2 * Copyright 2008-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
6 #include "elf_haiku_version.h"
12 #include <image_defs.h>
15 #include "elf_symbol_lookup.h"
18 // interim Haiku API versions
19 #define HAIKU_VERSION_PRE_GLUE_CODE 0x00000010
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
);
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
,
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",
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
,
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
;
73 if (commentSize
== 0) {
74 FATAL("%s: Could not find .comment section\n", image
->path
);
78 // read a part of the comment section
79 if (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
,
89 // the common prefix of the strings in the .comment section
90 static const char* kGCCVersionPrefix
= "GCC: (";
91 size_t gccVersionPrefixLen
= strlen(kGCCVersionPrefix
);
99 // Read up to 10 comments. The first three or four are usually from the
101 for (int i
= 0; i
< 10; i
++) {
103 while (index
< commentSize
&& buffer
[index
] == '\0')
105 char* stringStart
= buffer
+ index
;
108 while (index
< commentSize
&& buffer
[index
] != '\0')
111 // ignore the entry at the end of the buffer
112 if (index
== commentSize
)
115 // We have to analyze string like these:
116 // GCC: (GNU) 2.9-beos-991026
117 // GCC: (GNU) 2.95.3-haiku-080322
119 // GCC: (2016_02_29) 5.3.0
121 // FIXME this does not handle binaries generated with clang or other
124 // skip the common prefix
125 if (strncmp(stringStart
, kGCCVersionPrefix
, gccVersionPrefixLen
) != 0)
128 // Skip the build identifier, the closing parenthesis, and the space
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
) {
140 patchLevel
= strchr(gccPlatform
, '-');
141 if (patchLevel
!= NULL
) {
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
, '.');
156 version
[k
] = atoi(gccVersion
);
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")) {
188 image
->abi
= B_HAIKU_ABI_GCC_2_ANCIENT
;
190 image
->abi
= B_HAIKU_ABI_GCC_2_HAIKU
;
192 image
->abi
= B_HAIKU_ABI_GCC_2_BEOS
;
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
200 image
->abi
= gccMajor
<< 16;
208 analyze_image_haiku_version_and_abi(int fd
, image_t
* image
, elf_ehdr
& eheader
,
209 int32 sheaderSize
, char* buffer
, size_t bufferSize
)
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
)) {
219 = *(uint32
*)(symbol
->st_value
+ image
->regions
[0].delta
);
221 image
->api_version
= 0;
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
);
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
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
;