* Copyright 2008-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "elf_haiku_version.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <image_defs.h>
#include <syscalls.h>
#include "elf_symbol_lookup.h"
#define HAIKU_VERSION_PRE_GLUE_CODE 0x00000010
static bool
analyze_object_gcc_version(int fd, image_t* image, elf_ehdr& eheader,
int32 sheaderSize, char* buffer, size_t bufferSize)
{
if (sheaderSize <= 0)
return false;
if (sheaderSize > (int)bufferSize) {
FATAL("%s: Cannot handle section headers bigger than %lu bytes\n",
image->path, bufferSize);
return false;
}
ssize_t length = _kern_read(fd, eheader.e_shoff, buffer, sheaderSize);
if (length != sheaderSize) {
FATAL("%s: Could not read section headers: %s\n", image->path,
strerror(length));
return false;
}
elf_shdr* sectionHeader
= (elf_shdr*)(buffer + eheader.e_shstrndx * eheader.e_shentsize);
if (sheaderSize + sectionHeader->sh_size > bufferSize) {
FATAL("%s: Buffer not big enough for section string section\n",
image->path);
return false;
}
char* sectionStrings = buffer + bufferSize - sectionHeader->sh_size;
length = _kern_read(fd, sectionHeader->sh_offset, sectionStrings,
sectionHeader->sh_size);
if (length != (int)sectionHeader->sh_size) {
FATAL("%s: Could not read section string section: %s\n", image->path,
strerror(length));
return false;
}
off_t commentOffset = 0;
size_t commentSize = 0;
for (uint32 i = 0; i < eheader.e_shnum; i++) {
sectionHeader = (elf_shdr*)(buffer + i * eheader.e_shentsize);
const char* sectionName = sectionStrings + sectionHeader->sh_name;
if (sectionHeader->sh_name != 0
&& strcmp(sectionName, ".comment") == 0) {
commentOffset = sectionHeader->sh_offset;
commentSize = sectionHeader->sh_size;
break;
}
}
if (commentSize == 0) {
FATAL("%s: Could not find .comment section\n", image->path);
return false;
}
if (commentSize > 512)
commentSize = 512;
length = _kern_read(fd, commentOffset, buffer, commentSize);
if (length != (int)commentSize) {
FATAL("%s: Could not read .comment section: %s\n", image->path,
strerror(length));
return false;
}
static const char* kGCCVersionPrefix = "GCC: (";
size_t gccVersionPrefixLen = strlen(kGCCVersionPrefix);
size_t index = 0;
int gccMajor = 0;
int gccMiddle = 0;
int gccMinor = 0;
bool isHaiku = true;
for (int i = 0; i < 10; i++) {
while (index < commentSize && buffer[index] == '\0')
index++;
char* stringStart = buffer + index;
while (index < commentSize && buffer[index] != '\0')
index++;
if (index == commentSize)
break;
if (strncmp(stringStart, kGCCVersionPrefix, gccVersionPrefixLen) != 0)
continue;
char* gccVersion = strchr(stringStart + gccVersionPrefixLen, ')') + 2;
char* gccPlatform = strchr(gccVersion, '-');
char* patchLevel = NULL;
if (gccPlatform != NULL) {
*gccPlatform = '\0';
gccPlatform++;
patchLevel = strchr(gccPlatform, '-');
if (patchLevel != NULL) {
*patchLevel = '\0';
patchLevel++;
}
}
int version[3] = { 0, 0, 0 };
for (int k = 0; gccVersion != NULL && k < 3; k++) {
char* dot = strchr(gccVersion, '.');
if (dot) {
*dot = '\0';
dot++;
}
version[k] = atoi(gccVersion);
gccVersion = dot;
}
if (version[0] == 0)
continue;
if (gccMajor == 0 || gccMajor > version[0]
|| (gccMajor == version[0]
&& (gccMiddle < version[1]
|| (gccMiddle == version[1] && gccMinor < version[2])))) {
gccMajor = version[0];
gccMiddle = version[1];
gccMinor = version[2];
}
if (gccMajor == 2 && gccPlatform != NULL
&& strcmp(gccPlatform, "haiku")) {
isHaiku = false;
}
}
if (gccMajor == 0)
return false;
if (gccMajor == 2) {
if (gccMiddle < 95)
image->abi = B_HAIKU_ABI_GCC_2_ANCIENT;
else if (isHaiku)
image->abi = B_HAIKU_ABI_GCC_2_HAIKU;
else
image->abi = B_HAIKU_ABI_GCC_2_BEOS;
} else {
if (gccMajor >= 5) {
gccMajor = 4;
}
image->abi = gccMajor << 16;
}
return true;
}
void
analyze_image_haiku_version_and_abi(int fd, image_t* image, elf_ehdr& eheader,
int32 sheaderSize, char* buffer, size_t bufferSize)
{
elf_sym* symbol = find_symbol(image,
SymbolLookupInfo(B_SHARED_OBJECT_HAIKU_VERSION_VARIABLE_NAME,
B_SYMBOL_TYPE_DATA));
if (symbol != NULL && symbol->st_shndx != SHN_UNDEF
&& symbol->st_value > 0
&& symbol->Type() == STT_OBJECT
&& symbol->st_size >= sizeof(uint32)) {
image->api_version
= *(uint32*)(symbol->st_value + image->regions[0].delta);
} else
image->api_version = 0;
symbol = find_symbol(image,
SymbolLookupInfo(B_SHARED_OBJECT_HAIKU_ABI_VARIABLE_NAME,
B_SYMBOL_TYPE_DATA));
if (symbol != NULL && symbol->st_shndx != SHN_UNDEF
&& symbol->st_value > 0
&& symbol->Type() == STT_OBJECT
&& symbol->st_size >= sizeof(uint32)) {
image->abi = *(uint32*)(symbol->st_value + image->regions[0].delta);
} else
image->abi = 0;
if (image->abi == 0) {
if (!analyze_object_gcc_version(fd, image, eheader, sheaderSize,
buffer, bufferSize)) {
FATAL("%s: Failed to get gcc version.\n", image->path);
image->abi = B_HAIKU_ABI_GCC_2_ANCIENT;
}
}
if (image->api_version == 0) {
image->api_version = image->abi > B_HAIKU_ABI_GCC_2_BEOS
? HAIKU_VERSION_PRE_GLUE_CODE : B_HAIKU_VERSION_BEOS;
}
}