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: ("; 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 // GCC: (2016_02_29) 5.3.0 120 // GCC: (2018_05_01) 7.3.0 121 // GCC: (GNU) 7.3.0 122 123 // FIXME this does not handle binaries generated with clang or other 124 // compilers. 125 126 // skip the common prefix 127 if (strncmp(stringStart, kGCCVersionPrefix, gccVersionPrefixLen) != 0) 128 continue; 129 130 // Skip the build identifier, the closing parenthesis, and the space 131 // that follows it. 132 // Hopefully no one is going to include nested parentheses in the 133 // version string, so we can save the need for a smarter parser. 134 char* gccVersion = strchr(stringStart + gccVersionPrefixLen, ')') + 2; 135 136 // the rest is the GCC version 137 char* gccPlatform = strchr(gccVersion, '-'); 138 char* patchLevel = NULL; 139 if (gccPlatform != NULL) { 140 *gccPlatform = '\0'; 141 gccPlatform++; 142 patchLevel = strchr(gccPlatform, '-'); 143 if (patchLevel != NULL) { 144 *patchLevel = '\0'; 145 patchLevel++; 146 } 147 } 148 149 // split the gcc version into major, middle, and minor 150 int version[3] = { 0, 0, 0 }; 151 152 for (int k = 0; gccVersion != NULL && k < 3; k++) { 153 char* dot = strchr(gccVersion, '.'); 154 if (dot) { 155 *dot = '\0'; 156 dot++; 157 } 158 version[k] = atoi(gccVersion); 159 gccVersion = dot; 160 } 161 162 // got any version? 163 if (version[0] == 0) 164 continue; 165 166 // Select the gcc version with the smallest major, but the greatest 167 // middle/minor. This should usually ignore the glue code version as 168 // well as cases where e.g. in a gcc 2 program a single C file has 169 // been compiled with gcc 4. 170 if (gccMajor == 0 || gccMajor > version[0] 171 || (gccMajor == version[0] 172 && (gccMiddle < version[1] 173 || (gccMiddle == version[1] && gccMinor < version[2])))) { 174 gccMajor = version[0]; 175 gccMiddle = version[1]; 176 gccMinor = version[2]; 177 } 178 179 if (gccMajor == 2 && gccPlatform != NULL 180 && strcmp(gccPlatform, "haiku")) { 181 isHaiku = false; 182 } 183 } 184 185 if (gccMajor == 0) 186 return false; 187 188 if (gccMajor == 2) { 189 if (gccMiddle < 95) 190 image->abi = B_HAIKU_ABI_GCC_2_ANCIENT; 191 else if (isHaiku) 192 image->abi = B_HAIKU_ABI_GCC_2_HAIKU; 193 else 194 image->abi = B_HAIKU_ABI_GCC_2_BEOS; 195 } else { 196 if (gccMajor >= 5) { 197 // The ABI changes in libstdc++ 5+ are optional, and currently we 198 // are using it in backwards compatible mode. So, it is still 199 // generating ABI version 4. 200 gccMajor = 4; 201 } 202 image->abi = gccMajor << 16; 203 } 204 205 return true; 206 } 207 208 209 void 210 analyze_image_haiku_version_and_abi(int fd, image_t* image, elf_ehdr& eheader, 211 int32 sheaderSize, char* buffer, size_t bufferSize) 212 { 213 // Haiku API version 214 elf_sym* symbol = find_symbol(image, 215 SymbolLookupInfo(B_SHARED_OBJECT_HAIKU_VERSION_VARIABLE_NAME, 216 B_SYMBOL_TYPE_DATA, true)); 217 if (symbol != NULL && symbol->st_shndx != SHN_UNDEF 218 && symbol->st_value > 0 219 && symbol->st_size >= sizeof(uint32)) { 220 image->api_version 221 = *(uint32*)(symbol->st_value + image->regions[0].delta); 222 } else 223 image->api_version = 0; 224 225 // Haiku ABI 226 symbol = find_symbol(image, 227 SymbolLookupInfo(B_SHARED_OBJECT_HAIKU_ABI_VARIABLE_NAME, 228 B_SYMBOL_TYPE_DATA)); 229 if (symbol != NULL && symbol->st_shndx != SHN_UNDEF 230 && symbol->st_value > 0 231 && symbol->Type() == STT_OBJECT 232 && symbol->st_size >= sizeof(uint32)) { 233 image->abi = *(uint32*)(symbol->st_value + image->regions[0].delta); 234 } else 235 image->abi = 0; 236 237 if (image->abi == 0) { 238 // No ABI version in the shared object, i.e. it has been built before 239 // that was introduced in Haiku. We have to try and analyze the gcc 240 // version. 241 if (!analyze_object_gcc_version(fd, image, eheader, sheaderSize, 242 buffer, bufferSize)) { 243 FATAL("%s: Failed to get gcc version.\n", image->path); 244 // not really fatal, actually 245 246 // assume ancient BeOS 247 image->abi = B_HAIKU_ABI_GCC_2_ANCIENT; 248 } 249 } 250 251 // guess the API version, if we couldn't figure it out yet 252 if (image->api_version == 0) { 253 image->api_version = image->abi > B_HAIKU_ABI_GCC_2_BEOS 254 ? HAIKU_VERSION_PRE_GLUE_CODE : B_HAIKU_VERSION_BEOS; 255 } 256 } 257