libroot_debug: Merge guarded heap into libroot_debug.
[haiku.git] / src / system / runtime_loader / elf_haiku_version.cpp
blob75c01c8abe6cc4d5a4287f8fb50ba0bce66d8fe2
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: (GNU) ";
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
120 // skip the common prefix
121 if (strncmp(stringStart, kGCCVersionPrefix, gccVersionPrefixLen) != 0)
122 continue;
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++;
138 // split the gcc version into major, middle, and minor
139 int version[3] = { 0, 0, 0 };
141 for (int k = 0; gccVersion != NULL && k < 3; k++) {
142 char* dot = strchr(gccVersion, '.');
143 if (dot) {
144 *dot = '\0';
145 dot++;
147 version[k] = atoi(gccVersion);
148 gccVersion = dot;
151 // got any version?
152 if (version[0] == 0)
153 continue;
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];
168 if (gccMajor == 2 && gccPlatform != NULL
169 && strcmp(gccPlatform, "haiku")) {
170 isHaiku = false;
174 if (gccMajor == 0)
175 return false;
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;
187 return true;
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)
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;
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;
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
229 // assume ancient BeOS
230 image->abi = B_HAIKU_ABI_GCC_2_ANCIENT;
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;