#include <new>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ElfFile.h"
static const uint32 kMaxELFHeaderSize = sizeof(Elf_Ehdr) + 32;
static
status_t
read_exactly(BPositionIO &file, off_t position, void *buffer, size_t size,
const char *errorMessage = NULL)
{
status_t error = B_OK;
ssize_t read = file.ReadAt(position, buffer, size);
if (read < 0)
error = read;
else if ((size_t)read != size)
error = B_ERROR;
if (error != B_OK && errorMessage)
puts(errorMessage);
return error;
}
class SymbolPatcher::ElfSection {
public:
ElfSection();
~ElfSection();
void SetTo(ElfFile* file, Elf_Shdr* header);
void Unset();
bool IsInitialized() const { return fHeader; }
ElfFile* GetFile() const;
Elf_Shdr* GetHeader() const { return fHeader; }
const char* GetName() const;
uint8* GetData() const { return fData; }
size_t GetSize() const;
Elf_Word GetType() const;
Elf_Word GetLink() const;
Elf_Word GetInfo() const;
size_t GetEntrySize() const;
int32 CountEntries() const;
status_t Load();
void Unload();
void Dump();
private:
ElfFile* fFile;
Elf_Shdr* fHeader;
uint8* fData;
};
ElfSection::ElfSection()
: fFile(NULL),
fHeader(NULL),
fData(NULL)
{
}
ElfSection::~ElfSection()
{
Unset();
}
void
ElfSection::SetTo(ElfFile* file, Elf_Shdr* header)
{
Unset();
fFile = file;
fHeader = header;
}
void
ElfSection::Unset()
{
Unload();
fFile = NULL;
fHeader = NULL;
}
ElfFile*
ElfSection::GetFile() const
{
return fFile;
}
const char*
ElfSection::GetName() const
{
const char* name = NULL;
if (fHeader && fFile) {
size_t size = 0;
const char* nameSection = fFile->GetSectionHeaderStrings(&size);
if (nameSection && fHeader->sh_name < size)
name = nameSection + fHeader->sh_name;
}
return name;
}
size_t
ElfSection::GetSize() const
{
return fHeader->sh_size;
}
Elf_Word
ElfSection::GetType() const
{
return fHeader->sh_type;
}
Elf_Word
ElfSection::GetLink() const
{
return fHeader->sh_link;
}
Elf_Word
ElfSection::GetInfo() const
{
return fHeader->sh_info;
}
size_t
ElfSection::GetEntrySize() const
{
return fHeader->sh_entsize;
}
int32
ElfSection::CountEntries() const
{
int32 count = 0;
if (fHeader) {
if (GetEntrySize() == 0)
return 0;
count = GetSize() / GetEntrySize();
}
return count;
}
status_t
ElfSection::Load()
{
status_t error = B_ERROR;
if (fHeader && !fData && fHeader->sh_type != SHT_NULL
&& fHeader->sh_type != SHT_NOBITS) {
BFile* file = fFile->GetFile();
fData = new uint8[fHeader->sh_size];
if (!fData)
return B_NO_MEMORY;
error = read_exactly(*file, fHeader->sh_offset, fData,
fHeader->sh_size, "Failed to read section!\n");
if (error != B_OK)
Unload();
}
return error;
}
void
ElfSection::Unload()
{
if (fData) {
delete[] fData;
fData = NULL;
}
}
void
ElfSection::Dump()
{
printf("section %32s: size: %lu\n", GetName(), GetSize());
}
ElfSymbol::ElfSymbol(ElfSection* section, int32 index)
: fSection(section),
fIndex(index),
fSymbol(NULL)
{
}
ElfSymbol::~ElfSymbol()
{
Unset();
}
void
ElfSymbol::SetTo(ElfSection* section, int32 index)
{
Unset();
fSection = section;
fIndex = index;
}
void
ElfSymbol::Unset()
{
fSection = NULL;
fIndex = -1;
fSymbol = NULL;
}
const Elf_Sym*
ElfSymbol::GetSymbolStruct()
{
Elf_Sym* symbol = fSymbol;
if (!symbol && fSection && fSection->GetData()) {
size_t symbolSize = fSection->GetEntrySize();
if (symbolSize == 0)
return NULL;
int32 symbolCount = fSection->GetSize() / symbolSize;
if (fIndex >= 0 && fIndex < symbolCount)
symbol = (Elf_Sym*)(fSection->GetData() + fIndex * symbolSize);
}
return symbol;
}
const char*
ElfSymbol::GetName()
{
const char* name = NULL;
if (const Elf_Sym* symbol = GetSymbolStruct()) {
size_t size = 0;
const char* data = fSection->GetFile()->GetStringSectionStrings(
fSection->GetLink(), &size);
if (data && symbol->st_name < size)
name = data + symbol->st_name;
}
return name;
}
uint32
ElfSymbol::GetBinding()
{
uint32 binding = STB_LOCAL;
if (const Elf_Sym* symbol = GetSymbolStruct())
binding = ELF_ST_BIND(symbol->st_info);
return binding;
}
uint32
ElfSymbol::GetType()
{
uint32 type = STT_NOTYPE;
if (const Elf_Sym* symbol = GetSymbolStruct())
type = ELF_ST_TYPE(symbol->st_info);
return type;
}
uint32
ElfSymbol::GetTargetSectionIndex()
{
uint32 index = SHN_UNDEF;
if (const Elf_Sym* symbol = GetSymbolStruct())
index = symbol->st_shndx;
return index;
}
ElfRelocation::ElfRelocation(ElfSection* section, int32 index)
: fSection(section),
fIndex(index),
fRelocation(NULL)
{
}
ElfRelocation::~ElfRelocation()
{
Unset();
}
void
ElfRelocation::SetTo(ElfSection* section, int32 index)
{
Unset();
fSection = section;
fIndex = index;
}
void
ElfRelocation::Unset()
{
fSection = NULL;
fIndex = -1;
fRelocation = NULL;
}
Elf_Rel*
ElfRelocation::GetRelocationStruct()
{
Elf_Rel* relocation = fRelocation;
if (!relocation && fSection) {
if (!fSection->GetData()) {
if (fSection->Load() != B_OK)
return NULL;
}
size_t entrySize = fSection->GetEntrySize();
if (entrySize == 0 || entrySize < sizeof(Elf_Rel))
return NULL;
int32 entryCount = fSection->GetSize() / entrySize;
if (fIndex >= 0 && fIndex < entryCount) {
relocation = (Elf_Rel*)(fSection->GetData()
+ fIndex * entrySize);
}
}
return relocation;
}
uint32
ElfRelocation::GetType()
{
uint32 type = R_NONE;
if (Elf_Rel* relocation = GetRelocationStruct())
type = ELF_R_TYPE(relocation->r_info);
return type;
}
uint32
ElfRelocation::GetSymbolIndex()
{
uint32 index = 0;
if (Elf_Rel* relocation = GetRelocationStruct())
index = ELF_R_SYM(relocation->r_info);
return index;
}
Elf_Addr
ElfRelocation::GetOffset()
{
Elf_Addr offset = 0;
if (Elf_Rel* relocation = GetRelocationStruct())
offset = relocation->r_offset;
return offset;
}
status_t
ElfRelocation::GetSymbol(ElfSymbol* symbol)
{
status_t error = B_BAD_VALUE;
if (symbol && fSection) {
uint32 index = GetSymbolIndex();
if (ElfSection* symbols
= fSection->GetFile()->SectionAt(fSection->GetLink(), true)) {
symbol->SetTo(symbols, index);
if (symbol->GetSymbolStruct())
error = B_OK;
}
}
return error;
}
ElfRelocationIterator::ElfRelocationIterator(ElfFile* file)
: fFile(file),
fSectionIndex(-1),
fEntryIndex(-1)
{
}
ElfRelocationIterator::~ElfRelocationIterator()
{
}
bool
ElfRelocationIterator::GetNext(ElfRelocation* relocation)
{
bool result = false;
if (fFile && relocation) {
ElfSection* section = NULL;
if (fSectionIndex < 0) {
fSectionIndex = 0;
fEntryIndex = 0;
section = _FindNextSection();
} else {
fEntryIndex++;
section = fFile->SectionAt(fSectionIndex);
}
while (section && fEntryIndex >= section->CountEntries()) {
fSectionIndex++;
section = _FindNextSection();
fEntryIndex = 0;
}
if (section) {
relocation->SetTo(section, fEntryIndex);
result = true;
}
}
return result;
}
ElfSection*
ElfRelocationIterator::_FindNextSection()
{
if (fFile) {
for (; fSectionIndex < fFile->CountSections(); fSectionIndex++) {
ElfSection* section = fFile->SectionAt(fSectionIndex);
if (section && (section->GetType() == SHT_REL || section->GetType() == SHT_RELA))
return section;
}
}
return NULL;
}
ElfFile::ElfFile()
: fFile(),
fSectionHeaders(NULL),
fSections(NULL),
fSectionCount(0),
fSectionHeaderSize(0)
{
}
ElfFile::~ElfFile()
{
Unset();
}
status_t
ElfFile::SetTo(const char *filename)
{
Unset();
status_t error = _SetTo(filename);
if (error)
Unset();
return error;
}
void
ElfFile::Unset()
{
if (fSections) {
delete[] fSections;
fSections = NULL;
}
if (fSectionHeaders) {
delete[] fSectionHeaders;
fSectionHeaders = NULL;
}
fSectionCount = 0;
fSectionHeaderSize = 0;
fFile.Unset();
}
void
ElfFile::Unload()
{
for (int i = 0; i < fSectionCount; i++)
fSections[i].Unload();
}
const char*
ElfFile::GetSectionHeaderStrings(size_t* size)
{
return GetStringSectionStrings(fHeader.e_shstrndx, size);
}
const char*
ElfFile::GetStringSectionStrings(int32 index, size_t* _size)
{
const char* data = NULL;
size_t size = 0;
if (ElfSection* section = SectionAt(index, true)) {
data = (const char*)section->GetData();
size = (data ? section->GetSize() : 0);
}
if (_size)
*_size = size;
return data;
}
ElfSection*
ElfFile::SectionAt(int32 index, bool load)
{
ElfSection* section = NULL;
if (fSections && index >= 0 && index < fSectionCount) {
section = fSections + index;
if (load && !section->GetData()) {
if (section->Load() != B_OK) {
section = NULL;
printf("Failed to load section %" B_PRId32 "\n", index);
}
}
}
return section;
}
void
ElfFile::Dump()
{
printf("%" B_PRId32 " sections\n", fSectionCount);
for (int i = 0; i < fSectionCount; i++)
fSections[i].Dump();
}
status_t
ElfFile::_SetTo(const char *filename)
{
if (!filename)
return B_BAD_VALUE;
status_t error = fFile.SetTo(filename, B_READ_ONLY);
off_t fileSize = 0;
error = fFile.GetSize(&fileSize);
if (error != B_OK) {
printf("Failed to get file size!\n");
return error;
}
error = read_exactly(fFile, 0, &fHeader, sizeof(Elf_Ehdr),
"Failed to read ELF object header!\n");
if (error != B_OK)
return error;
if (fHeader.e_ident[EI_MAG0] != ELFMAG0
|| fHeader.e_ident[EI_MAG1] != ELFMAG1
|| fHeader.e_ident[EI_MAG2] != ELFMAG2
|| fHeader.e_ident[EI_MAG3] != ELFMAG3) {
printf("Bad ELF file magic!\n");
return B_BAD_VALUE;
}
if (fHeader.e_ident[EI_CLASS] != ELFCLASS) {
printf("Wrong ELF class!\n");
return B_BAD_VALUE;
}
if (fHeader.e_ident[EI_DATA] != ELFDATA2LSB) {
printf("Wrong data encoding!\n");
return B_BAD_VALUE;
}
if (fHeader.e_ident[EI_VERSION] != EV_CURRENT) {
printf("Wrong data encoding!\n");
return B_BAD_VALUE;
}
uint32 headerSize = fHeader.e_ehsize;
uint32 sectionHeaderTableOffset = fHeader.e_shoff;
uint32 sectionHeaderSize = fHeader.e_shentsize;
uint32 sectionHeaderCount = fHeader.e_shnum;
if (headerSize < sizeof(Elf_Ehdr) || headerSize > kMaxELFHeaderSize) {
printf("Invalid ELF header: invalid ELF header size: %" B_PRIu32 ".", headerSize);
return B_BAD_VALUE;
}
if (sectionHeaderTableOffset == 0) {
printf("ELF file has no section header table!\n");
return B_BAD_VALUE;
}
uint32 sectionHeaderTableSize = 0;
if (sectionHeaderTableOffset < headerSize
|| sectionHeaderTableOffset > fileSize) {
printf("Invalid ELF header: invalid section header table offset: %" B_PRIu32 ".",
sectionHeaderTableOffset);
return B_BAD_VALUE;
}
sectionHeaderTableSize = sectionHeaderSize * sectionHeaderCount;
if (sectionHeaderSize < sizeof(Elf_Shdr)
|| sectionHeaderTableOffset + sectionHeaderTableSize > fileSize) {
printf("Invalid ELF header: section header table exceeds file: %" B_PRIu32 ".",
sectionHeaderTableOffset + sectionHeaderTableSize);
return B_BAD_VALUE;
}
fSectionHeaders = new(std::nothrow) uint8[sectionHeaderTableSize];
fSectionCount = sectionHeaderCount;
fSectionHeaderSize = sectionHeaderSize;
if (!fSectionHeaders)
return B_NO_MEMORY;
error = read_exactly(fFile, sectionHeaderTableOffset, fSectionHeaders,
sectionHeaderTableSize,
"Failed to read section headers!\n");
if (error != B_OK)
return error;
fSections = new(std::nothrow) ElfSection[fSectionCount];
if (!fSections)
return B_NO_MEMORY;
for (int i = 0; i < fSectionCount; i++)
fSections[i].SetTo(this, _SectionHeaderAt(i));
return error;
}
Elf_Shdr*
ElfFile::_SectionHeaderAt(int32 index)
{
Elf_Shdr* header = NULL;
if (fSectionHeaders && index >= 0 && index < fSectionCount)
header = (Elf_Shdr*)(fSectionHeaders + index * fSectionHeaderSize);
return header;
}
status_t
ElfFile::_LoadSection(int32 index)
{
status_t error = B_OK;
if (fSections && index >= 0 && index < fSectionCount) {
ElfSection& section = fSections[index];
error = section.Load();
} else
error = B_BAD_VALUE;
return error;
}