xref: /haiku/src/system/runtime_loader/elf_haiku_version.cpp (revision 909af08f4328301fbdef1ffb41f566c3b5bec0c7)
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