1 /* 2 * Copyright 2008-2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include "elf_haiku_version.h" 7 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 12 #include <image_defs.h> 13 #include <syscalls.h> 14 15 #include "elf_symbol_lookup.h" 16 17 18 // interim Haiku API versions 19 #define HAIKU_VERSION_PRE_GLUE_CODE 0x00000010 20 21 22 static bool 23 analyze_object_gcc_version(int fd, image_t* image, elf_ehdr& eheader, 24 int32 sheaderSize, char* buffer, size_t bufferSize) 25 { 26 if (sheaderSize > (int)bufferSize) { 27 FATAL("%s: Cannot handle section headers bigger than %lu bytes\n", 28 image->path, bufferSize); 29 return false; 30 } 31 32 // read section headers 33 ssize_t length = _kern_read(fd, eheader.e_shoff, buffer, sheaderSize); 34 if (length != sheaderSize) { 35 FATAL("%s: Could not read section headers: %s\n", image->path, 36 strerror(length)); 37 return false; 38 } 39 40 // load the string section 41 elf_shdr* sectionHeader 42 = (elf_shdr*)(buffer + eheader.e_shstrndx * eheader.e_shentsize); 43 44 if (sheaderSize + sectionHeader->sh_size > bufferSize) { 45 FATAL("%s: Buffer not big enough for section string section\n", 46 image->path); 47 return false; 48 } 49 50 char* sectionStrings = buffer + bufferSize - sectionHeader->sh_size; 51 length = _kern_read(fd, sectionHeader->sh_offset, sectionStrings, 52 sectionHeader->sh_size); 53 if (length != (int)sectionHeader->sh_size) { 54 FATAL("%s: Could not read section string section: %s\n", image->path, 55 strerror(length)); 56 return false; 57 } 58 59 // find the .comment section 60 off_t commentOffset = 0; 61 size_t commentSize = 0; 62 for (uint32 i = 0; i < eheader.e_shnum; i++) { 63 sectionHeader = (elf_shdr*)(buffer + i * eheader.e_shentsize); 64 const char* sectionName = sectionStrings + sectionHeader->sh_name; 65 if (sectionHeader->sh_name != 0 66 && strcmp(sectionName, ".comment") == 0) { 67 commentOffset = sectionHeader->sh_offset; 68 commentSize = sectionHeader->sh_size; 69 break; 70 } 71 } 72 73 if (commentSize == 0) { 74 FATAL("%s: Could not find .comment section\n", image->path); 75 return false; 76 } 77 78 // read a part of the comment section 79 if (commentSize > 512) 80 commentSize = 512; 81 82 length = _kern_read(fd, commentOffset, buffer, commentSize); 83 if (length != (int)commentSize) { 84 FATAL("%s: Could not read .comment section: %s\n", image->path, 85 strerror(length)); 86 return false; 87 } 88 89 // the common prefix of the strings in the .comment section 90 static const char* kGCCVersionPrefix = "GCC: (GNU) "; 91 size_t gccVersionPrefixLen = strlen(kGCCVersionPrefix); 92 93 size_t index = 0; 94 int gccMajor = 0; 95 int gccMiddle = 0; 96 int gccMinor = 0; 97 bool isHaiku = true; 98 99 // Read up to 10 comments. The first three or four are usually from the 100 // glue code. 101 for (int i = 0; i < 10; i++) { 102 // skip '\0' 103 while (index < commentSize && buffer[index] == '\0') 104 index++; 105 char* stringStart = buffer + index; 106 107 // find string end 108 while (index < commentSize && buffer[index] != '\0') 109 index++; 110 111 // ignore the entry at the end of the buffer 112 if (index == commentSize) 113 break; 114 115 // We have to analyze string like these: 116 // GCC: (GNU) 2.9-beos-991026 117 // GCC: (GNU) 2.95.3-haiku-080322 118 // GCC: (GNU) 4.1.2 119 120 // skip the common prefix 121 if (strncmp(stringStart, kGCCVersionPrefix, gccVersionPrefixLen) != 0) 122 continue; 123 124 // the rest is the GCC version 125 char* gccVersion = stringStart + gccVersionPrefixLen; 126 char* gccPlatform = strchr(gccVersion, '-'); 127 char* patchLevel = NULL; 128 if (gccPlatform != NULL) { 129 *gccPlatform = '\0'; 130 gccPlatform++; 131 patchLevel = strchr(gccPlatform, '-'); 132 if (patchLevel != NULL) { 133 *patchLevel = '\0'; 134 patchLevel++; 135 } 136 } 137 138 // split the gcc version into major, middle, and minor 139 int version[3] = { 0, 0, 0 }; 140 141 for (int k = 0; gccVersion != NULL && k < 3; k++) { 142 char* dot = strchr(gccVersion, '.'); 143 if (dot) { 144 *dot = '\0'; 145 dot++; 146 } 147 version[k] = atoi(gccVersion); 148 gccVersion = dot; 149 } 150 151 // got any version? 152 if (version[0] == 0) 153 continue; 154 155 // Select the gcc version with the smallest major, but the greatest 156 // middle/minor. This should usually ignore the glue code version as 157 // well as cases where e.g. in a gcc 2 program a single C file has 158 // been compiled with gcc 4. 159 if (gccMajor == 0 || gccMajor > version[0] 160 || (gccMajor == version[0] 161 && (gccMiddle < version[1] 162 || (gccMiddle == version[1] && gccMinor < version[2])))) { 163 gccMajor = version[0]; 164 gccMiddle = version[1]; 165 gccMinor = version[2]; 166 } 167 168 if (gccMajor == 2 && gccPlatform != NULL 169 && strcmp(gccPlatform, "haiku")) { 170 isHaiku = false; 171 } 172 } 173 174 if (gccMajor == 0) 175 return false; 176 177 if (gccMajor == 2) { 178 if (gccMiddle < 95) 179 image->abi = B_HAIKU_ABI_GCC_2_ANCIENT; 180 else if (isHaiku) 181 image->abi = B_HAIKU_ABI_GCC_2_HAIKU; 182 else 183 image->abi = B_HAIKU_ABI_GCC_2_BEOS; 184 } else 185 image->abi = gccMajor << 16; 186 187 return true; 188 } 189 190 191 void 192 analyze_image_haiku_version_and_abi(int fd, image_t* image, elf_ehdr& eheader, 193 int32 sheaderSize, char* buffer, size_t bufferSize) 194 { 195 // Haiku API version 196 elf_sym* symbol = find_symbol(image, 197 SymbolLookupInfo(B_SHARED_OBJECT_HAIKU_VERSION_VARIABLE_NAME, 198 B_SYMBOL_TYPE_DATA)); 199 if (symbol != NULL && symbol->st_shndx != SHN_UNDEF 200 && symbol->st_value > 0 201 && symbol->Type() == STT_OBJECT 202 && symbol->st_size >= sizeof(uint32)) { 203 image->api_version 204 = *(uint32*)(symbol->st_value + image->regions[0].delta); 205 } else 206 image->api_version = 0; 207 208 // Haiku ABI 209 symbol = find_symbol(image, 210 SymbolLookupInfo(B_SHARED_OBJECT_HAIKU_ABI_VARIABLE_NAME, 211 B_SYMBOL_TYPE_DATA)); 212 if (symbol != NULL && symbol->st_shndx != SHN_UNDEF 213 && symbol->st_value > 0 214 && symbol->Type() == STT_OBJECT 215 && symbol->st_size >= sizeof(uint32)) { 216 image->abi = *(uint32*)(symbol->st_value + image->regions[0].delta); 217 } else 218 image->abi = 0; 219 220 if (image->abi == 0) { 221 // No ABI version in the shared object, i.e. it has been built before 222 // that was introduced in Haiku. We have to try and analyze the gcc 223 // version. 224 if (!analyze_object_gcc_version(fd, image, eheader, sheaderSize, 225 buffer, bufferSize)) { 226 FATAL("%s: Failed to get gcc version.\n", image->path); 227 // not really fatal, actually 228 229 // assume ancient BeOS 230 image->abi = B_HAIKU_ABI_GCC_2_ANCIENT; 231 } 232 } 233 234 // guess the API version, if we couldn't figure it out yet 235 if (image->api_version == 0) { 236 image->api_version = image->abi > B_HAIKU_ABI_GCC_2_BEOS 237 ? HAIKU_VERSION_PRE_GLUE_CODE : B_HAIKU_VERSION_BEOS; 238 } 239 } 240