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