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