headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / runtime_loader / elf_haiku_version.cpp
blob7e61df133cdb3fdbe8941ef331a2626a19bbfbf9
1 /*
2 * Copyright 2008-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
6 #include "elf_haiku_version.h"
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
12 #include <image_defs.h>
13 #include <syscalls.h>
15 #include "elf_symbol_lookup.h"
18 // interim Haiku API versions
19 #define HAIKU_VERSION_PRE_GLUE_CODE 0x00000010
22 static bool
23 analyze_object_gcc_version(int fd, image_t* image, elf_ehdr& eheader,
24 int32 sheaderSize, char* buffer, size_t bufferSize)
26 if (sheaderSize > (int)bufferSize) {
27 FATAL("%s: Cannot handle section headers bigger than %lu bytes\n",
28 image->path, bufferSize);
29 return false;
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;
40 // load the string section
41 elf_shdr* sectionHeader
42 = (elf_shdr*)(buffer + eheader.e_shstrndx * eheader.e_shentsize);
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;
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;
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;
73 if (commentSize == 0) {
74 FATAL("%s: Could not find .comment section\n", image->path);
75 return false;
78 // read a part of the comment section
79 if (commentSize > 512)
80 commentSize = 512;
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;
89 // the common prefix of the strings in the .comment section
90 static const char* kGCCVersionPrefix = "GCC: (";
91 size_t gccVersionPrefixLen = strlen(kGCCVersionPrefix);
93 size_t index = 0;
94 int gccMajor = 0;
95 int gccMiddle = 0;
96 int gccMinor = 0;
97 bool isHaiku = true;
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;
107 // find string end
108 while (index < commentSize && buffer[index] != '\0')
109 index++;
111 // ignore the entry at the end of the buffer
112 if (index == commentSize)
113 break;
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
121 // FIXME this does not handle binaries generated with clang or other
122 // compilers.
124 // skip the common prefix
125 if (strncmp(stringStart, kGCCVersionPrefix, gccVersionPrefixLen) != 0)
126 continue;
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;
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++;
147 // split the gcc version into major, middle, and minor
148 int version[3] = { 0, 0, 0 };
150 for (int k = 0; gccVersion != NULL && k < 3; k++) {
151 char* dot = strchr(gccVersion, '.');
152 if (dot) {
153 *dot = '\0';
154 dot++;
156 version[k] = atoi(gccVersion);
157 gccVersion = dot;
160 // got any version?
161 if (version[0] == 0)
162 continue;
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];
177 if (gccMajor == 2 && gccPlatform != NULL
178 && strcmp(gccPlatform, "haiku")) {
179 isHaiku = false;
183 if (gccMajor == 0)
184 return false;
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;
200 image->abi = gccMajor << 16;
203 return true;
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)
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;
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;
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
244 // assume ancient BeOS
245 image->abi = B_HAIKU_ABI_GCC_2_ANCIENT;
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;