mirror of https://github.com/stella-emu/stella.git
Woefully untested ELF linker.
This commit is contained in:
parent
4c44735f40
commit
135349ed6b
|
@ -18,6 +18,8 @@
|
|||
#include <sstream>
|
||||
|
||||
#include "System.hxx"
|
||||
#include "ElfParser.hxx"
|
||||
#include "ElfLinker.hxx"
|
||||
#include "exception/FatalEmulationError.hxx"
|
||||
|
||||
#include "CartELF.hxx"
|
||||
|
@ -61,6 +63,24 @@ namespace {
|
|||
0x4c, 0x00, 0x10 // jmp $1000
|
||||
};
|
||||
|
||||
constexpr uInt32 ADDR_TEXT_BASE = 0x00100000;
|
||||
constexpr uInt32 ADDR_DATA_BASE = 0x00200000;
|
||||
|
||||
constexpr uInt32 ADDR_ADDR_IDR = 0xf0000000;
|
||||
constexpr uInt32 ADDR_DATA_IDR = 0xf0000004;
|
||||
constexpr uInt32 ADDR_DATA_ODR = 0xf0000008;
|
||||
constexpr uInt32 ADDR_DATA_MODER = 0xf0000010;
|
||||
|
||||
constexpr uInt32 ADDR_VCS_JSR6 = 0x1000;
|
||||
|
||||
const vector<ElfLinker::ExternalSymbol> EXTERNAL_SYMBOLS = {
|
||||
{"ADDR_IDR", ADDR_ADDR_IDR},
|
||||
{"DATA_IDR", ADDR_DATA_IDR},
|
||||
{"DATA_ODR", ADDR_DATA_ODR},
|
||||
{"DATA_MODER", ADDR_DATA_MODER},
|
||||
{"vcsJsr6", ADDR_VCS_JSR6}
|
||||
};
|
||||
|
||||
#ifdef DUMP_ELF
|
||||
void dumpElf(const ElfParser& elfParser) {
|
||||
cout << "ELF sections:" << std::endl << std::endl;
|
||||
|
@ -99,6 +119,8 @@ CartridgeELF::CartridgeELF(const ByteBuffer& image, size_t size, string_view md5
|
|||
const Settings& settings)
|
||||
: Cartridge(settings, md5), myImageSize(size)
|
||||
{
|
||||
ElfParser elfParser;
|
||||
|
||||
try {
|
||||
elfParser.parse(image.get(), size);
|
||||
} catch (ElfParser::ElfParseError& e) {
|
||||
|
@ -116,6 +138,27 @@ CartridgeELF::CartridgeELF(const ByteBuffer& image, size_t size, string_view md5
|
|||
#ifdef DUMP_ELF
|
||||
dumpElf(elfParser);
|
||||
#endif
|
||||
|
||||
ElfLinker elfLinker(ADDR_TEXT_BASE, ADDR_DATA_BASE, elfParser);
|
||||
try {
|
||||
elfLinker.link(EXTERNAL_SYMBOLS);
|
||||
} catch (const ElfLinker::ElfLinkError& e) {
|
||||
throw runtime_error("failed to link ELF: " + string(e.what()));
|
||||
}
|
||||
|
||||
try {
|
||||
myArmEntrypoint = elfLinker.findRelocatedSymbol("elf_main").value;
|
||||
} catch (const ElfLinker::ElfSymbolResolutionError& e) {
|
||||
throw runtime_error("failed to resolve ARM entrypoint" + string(e.what()));
|
||||
}
|
||||
|
||||
#ifdef DUMP_ELF
|
||||
cout
|
||||
<< std::endl
|
||||
<< "ARM entrypoint: 0x"
|
||||
<< std::hex << std::setw(8) << std::setfill('0') << myArmEntrypoint << std::dec
|
||||
<< std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
|
||||
#include "bspf.hxx"
|
||||
#include "Cart.hxx"
|
||||
#include "ElfParser.hxx"
|
||||
|
||||
class CartridgeELF: public Cartridge {
|
||||
public:
|
||||
|
@ -116,7 +115,7 @@ class CartridgeELF: public Cartridge {
|
|||
bool myIsBusDriven{false};
|
||||
uInt8 myDriveBusValue{0};
|
||||
|
||||
ElfParser elfParser;
|
||||
uInt32 myArmEntrypoint{0};
|
||||
};
|
||||
|
||||
#endif // CARTRIDGE_ELF
|
||||
|
|
|
@ -0,0 +1,264 @@
|
|||
//============================================================================
|
||||
//
|
||||
// SSSS tt lll lll
|
||||
// SS SS tt ll ll
|
||||
// SS tttttt eeee ll ll aaaa
|
||||
// SSSS tt ee ee ll ll aa
|
||||
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||
// SS SS tt ee ll ll aa aa
|
||||
// SSSS ttt eeeee llll llll aaaaa
|
||||
//
|
||||
// Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
|
||||
// and the Stella Team
|
||||
//
|
||||
// See the file "License.txt" for information on usage and redistribution of
|
||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//============================================================================
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "ElfUtil.hxx"
|
||||
|
||||
#include "ElfLinker.hxx"
|
||||
|
||||
ElfLinker::ElfLinker(uInt32 textBase, uInt32 dataBase, const ElfParser& parser)
|
||||
: myTextBase(textBase), myDataBase(dataBase), myParser(parser)
|
||||
{}
|
||||
|
||||
void ElfLinker::link(const vector<ExternalSymbol>& externalSymbols)
|
||||
{
|
||||
myTextSize = myDataSize = 0;
|
||||
myTextData.reset();
|
||||
myDataData.reset();
|
||||
myRelocatedSections.resize(0);
|
||||
myRelocatedSymbols.resize(0);
|
||||
|
||||
auto& sections = myParser.getSections();
|
||||
myRelocatedSections.resize(sections.size(), std::nullopt);
|
||||
|
||||
// relocate all .text and .data sections
|
||||
for (size_t i = 0; i < sections.size(); i++) {
|
||||
const auto& section = sections[i];
|
||||
|
||||
if (section.type == ElfParser::SHT_PROGBITS) {
|
||||
const bool isText = section.name.starts_with(".text");
|
||||
uInt32& segmentSize = isText ? myTextSize : myDataSize;
|
||||
|
||||
if (segmentSize % section.align)
|
||||
segmentSize = (segmentSize / section.align + 1) * section.align;
|
||||
|
||||
myRelocatedSections[i] = {isText ? SectionType::text : SectionType::data, segmentSize};
|
||||
segmentSize += section.size;
|
||||
}
|
||||
}
|
||||
|
||||
// relocate all .bss sections
|
||||
for (size_t i = 0; i < sections.size(); i++) {
|
||||
const auto& section = sections[i];
|
||||
|
||||
if (section.type == ElfParser::SHT_NOBITS) {
|
||||
if (myDataSize % section.align)
|
||||
myDataSize = (myDataSize / section.align + 1) * section.align;
|
||||
|
||||
myRelocatedSections[i] = {SectionType::data, myDataSize};
|
||||
myDataSize += section.size;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure that the segments don't overlap
|
||||
if (!(myTextBase + myTextSize <= myDataBase || myDataBase + myDataSize <= myTextBase))
|
||||
ElfLinkError::raise("segments overlap");
|
||||
|
||||
// allocate and copy section data
|
||||
myTextData = make_unique<uInt8[]>(myTextSize);
|
||||
myDataData = make_unique<uInt8[]>(myDataSize);
|
||||
|
||||
std::memset(myTextData.get(), 0, myTextSize);
|
||||
std::memset(myDataData.get(), 0, myDataSize);
|
||||
|
||||
for (size_t i = 0; i < sections.size(); i++) {
|
||||
const auto& relocatedSection = myRelocatedSections[i];
|
||||
if (!relocatedSection.has_value()) continue;
|
||||
|
||||
const auto& section = sections[i];
|
||||
if (section.type != ElfParser::SHT_PROGBITS) continue;
|
||||
|
||||
const bool isText = section.name.starts_with(".text");
|
||||
|
||||
std::memcpy(
|
||||
(isText ? myTextData : myDataData).get() + relocatedSection->offset,
|
||||
myParser.getData() + section.offset,
|
||||
section.size
|
||||
);
|
||||
}
|
||||
|
||||
std::unordered_map<string, const ExternalSymbol*> externalSymbolLookup;
|
||||
for (const auto& externalSymbol: externalSymbols)
|
||||
externalSymbolLookup[externalSymbol.name] = &externalSymbol;
|
||||
|
||||
// relocate symbols
|
||||
const auto& symbols = myParser.getSymbols();
|
||||
myRelocatedSymbols.resize(symbols.size(), std::nullopt);
|
||||
|
||||
for (size_t i = 0; i < symbols.size(); i++) {
|
||||
const auto& symbol = symbols[i];
|
||||
|
||||
if (symbol.section == ElfParser::SHN_ABS) {
|
||||
myRelocatedSymbols[i] = {std::nullopt, symbol.value};
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (symbol.section == ElfParser::SHN_UND) {
|
||||
if (externalSymbolLookup.contains(symbol.name))
|
||||
myRelocatedSymbols[i] = {std::nullopt, externalSymbolLookup[symbol.name]->value};
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& relocatedSection = myRelocatedSections[symbol.section];
|
||||
if (!relocatedSection) continue;
|
||||
|
||||
uInt32 value = relocatedSection->type == SectionType::text ? myTextBase : myDataBase;
|
||||
value += relocatedSection->offset;
|
||||
if (symbol.type != ElfParser::STT_SECTION) value += symbol.value;
|
||||
|
||||
myRelocatedSymbols[i] = {relocatedSection->type, value};
|
||||
}
|
||||
|
||||
// apply relocations
|
||||
for (size_t iSection = 0; iSection < sections.size(); iSection++) {
|
||||
const auto& relocations = myParser.getRelocations(iSection);
|
||||
if (!relocations) continue;
|
||||
if (!myRelocatedSections[iSection]) continue;
|
||||
|
||||
for (const auto& relocation: *relocations)
|
||||
applyRelocation(relocation, iSection);
|
||||
}
|
||||
}
|
||||
|
||||
uInt32 ElfLinker::getTextBase() const
|
||||
{
|
||||
return myTextBase;
|
||||
}
|
||||
|
||||
uInt32 ElfLinker::getTextSize() const
|
||||
{
|
||||
return myTextSize;
|
||||
}
|
||||
|
||||
const uInt8* ElfLinker::getTextData() const
|
||||
{
|
||||
return myTextData ? myTextData.get() : nullptr;
|
||||
}
|
||||
|
||||
uInt32 ElfLinker::getDataBase() const
|
||||
{
|
||||
return myDataBase;
|
||||
}
|
||||
|
||||
uInt32 ElfLinker::getDataSize() const
|
||||
{
|
||||
return myDataSize;
|
||||
}
|
||||
|
||||
const uInt8* ElfLinker::getDataData() const
|
||||
{
|
||||
return myDataData ? myDataData.get() : nullptr;
|
||||
}
|
||||
|
||||
const vector<uInt32> ElfLinker::getInitArray() const
|
||||
{
|
||||
throw runtime_error("not implemented");
|
||||
}
|
||||
|
||||
const vector<uInt32> ElfLinker::getPreinitArray() const
|
||||
{
|
||||
throw runtime_error("not implemented");
|
||||
}
|
||||
|
||||
ElfLinker::RelocatedSymbol ElfLinker::findRelocatedSymbol(string_view name) const
|
||||
{
|
||||
const auto& symbols = myParser.getSymbols();
|
||||
for (size_t i = 0; i < symbols.size(); i++) {
|
||||
if (symbols[i].name != name) continue;
|
||||
|
||||
if (!myRelocatedSymbols[i])
|
||||
ElfSymbolResolutionError::raise("symbol could not be relocated");
|
||||
|
||||
return *myRelocatedSymbols[i];
|
||||
}
|
||||
|
||||
ElfSymbolResolutionError::raise("symbol not found");
|
||||
}
|
||||
|
||||
void ElfLinker::applyRelocation(const ElfParser::Relocation& relocation, size_t iSection)
|
||||
{
|
||||
const auto& targetSection = myParser.getSections()[iSection];
|
||||
const auto& targetSectionRelocated = *myRelocatedSections[iSection];
|
||||
const auto& symbol = myParser.getSymbols()[relocation.symbol];
|
||||
const auto& relocatedSymbol = myRelocatedSymbols[relocation.symbol];
|
||||
|
||||
if (!relocatedSymbol)
|
||||
ElfLinkError::raise(
|
||||
"unable to relocate " + symbol.name + " in " + targetSection.name + ": symbol could not be relocated"
|
||||
);
|
||||
|
||||
uInt8* target =
|
||||
(targetSectionRelocated.type == SectionType::text ? myTextData : myDataData).get() +
|
||||
targetSectionRelocated.offset + relocation.offset;
|
||||
|
||||
switch (relocation.type) {
|
||||
case ElfParser::R_ARM_ABS32:
|
||||
case ElfParser::R_ARM_TARGET1:
|
||||
{
|
||||
if (relocation.offset + 4 > targetSection.size)
|
||||
ElfLinkError::raise(
|
||||
"unable to relocate " + symbol.name + " in " + targetSection.name + ": target out of range"
|
||||
);
|
||||
|
||||
const uInt32 value = relocatedSymbol->value + relocation.addend.value_or(read32(target));
|
||||
write32(target, value | (symbol.type == ElfParser::STT_FUNC ? 0x01 : 0));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ElfParser::R_ARM_THM_CALL:
|
||||
case ElfParser::R_ARM_THM_JUMP24:
|
||||
{
|
||||
if (relocation.offset + 4 > targetSection.size)
|
||||
ElfLinkError::raise(
|
||||
"unable to relocate " + symbol.name + " in " + targetSection.name + ": target out of range"
|
||||
);
|
||||
|
||||
const uInt32 op = read32(target);
|
||||
|
||||
Int32 offset = relocatedSymbol->value + relocation.addend.value_or(elfUtil::decode_B_BL(op)) -
|
||||
(targetSectionRelocated.type == SectionType::text ? myTextBase : myDataBase) -
|
||||
targetSectionRelocated.offset - relocation.offset - 4;
|
||||
|
||||
if ((offset >> 25) != -1 && (offset >> 25) != 0)
|
||||
ElfLinkError::raise("unable to relocate jump: offset out of bounds");
|
||||
|
||||
write32(target, elfUtil::encode_B_BL(offset, relocation.type == ElfParser::R_ARM_THM_CALL));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uInt32 ElfLinker::read32(const uInt8* address)
|
||||
{
|
||||
uInt32 value = *(address++);
|
||||
value |= *(address++) << 8;
|
||||
value |= *(address++) << 16;
|
||||
value |= *(address++) << 24;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void ElfLinker::write32(uInt8* address, uInt32 value)
|
||||
{
|
||||
*(address++) = value;
|
||||
*(address++) = value >> 8;
|
||||
*(address++) = value >> 16;
|
||||
*(address++) = value >> 24;
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
//============================================================================
|
||||
//
|
||||
// SSSS tt lll lll
|
||||
// SS SS tt ll ll
|
||||
// SS tttttt eeee ll ll aaaa
|
||||
// SSSS tt ee ee ll ll aa
|
||||
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||
// SS SS tt ee ll ll aa aa
|
||||
// SSSS ttt eeeee llll llll aaaaa
|
||||
//
|
||||
// Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
|
||||
// and the Stella Team
|
||||
//
|
||||
// See the file "License.txt" for information on usage and redistribution of
|
||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//============================================================================
|
||||
|
||||
#ifndef ELF_LINKER
|
||||
#define ELF_LINKER
|
||||
|
||||
#include "bspf.hxx"
|
||||
#include "ElfParser.hxx"
|
||||
|
||||
class ElfLinker {
|
||||
public:
|
||||
class ElfLinkError : public std::exception {
|
||||
friend ElfLinker;
|
||||
|
||||
public:
|
||||
const char* what() const noexcept override { return myReason.c_str(); }
|
||||
|
||||
[[noreturn]] static void raise(string_view message) {
|
||||
throw ElfLinkError(message);
|
||||
}
|
||||
|
||||
private:
|
||||
explicit ElfLinkError(string_view reason) : myReason(reason) {}
|
||||
|
||||
private:
|
||||
const string myReason;
|
||||
};
|
||||
|
||||
class ElfSymbolResolutionError : public std::exception {
|
||||
friend ElfLinker;
|
||||
|
||||
public:
|
||||
const char* what() const noexcept override { return myReason.c_str(); }
|
||||
|
||||
[[noreturn]] static void raise(string_view message) {
|
||||
throw ElfSymbolResolutionError(message);
|
||||
}
|
||||
|
||||
private:
|
||||
explicit ElfSymbolResolutionError(string_view reason) : myReason(reason) {}
|
||||
|
||||
private:
|
||||
const string myReason;
|
||||
};
|
||||
|
||||
enum class SectionType: uInt8 { text, data };
|
||||
|
||||
struct RelocatedSymbol {
|
||||
optional<SectionType> section;
|
||||
uInt32 value;
|
||||
};
|
||||
|
||||
struct ExternalSymbol {
|
||||
string name;
|
||||
uInt32 value;
|
||||
};
|
||||
|
||||
public:
|
||||
ElfLinker(uInt32 textBase, uInt32 dataBase, const ElfParser& parser);
|
||||
|
||||
void link(const vector<ExternalSymbol>& externalSymbols);
|
||||
|
||||
uInt32 getTextBase() const;
|
||||
uInt32 getTextSize() const;
|
||||
const uInt8* getTextData() const;
|
||||
|
||||
uInt32 getDataBase() const;
|
||||
uInt32 getDataSize() const;
|
||||
const uInt8* getDataData() const;
|
||||
|
||||
const vector<uInt32> getInitArray() const;
|
||||
const vector<uInt32> getPreinitArray() const;
|
||||
|
||||
RelocatedSymbol findRelocatedSymbol(string_view name) const;
|
||||
|
||||
private:
|
||||
struct RelocatedSection {
|
||||
SectionType type;
|
||||
uInt32 offset;
|
||||
};
|
||||
|
||||
private:
|
||||
void applyRelocation(const ElfParser::Relocation& relocation, size_t iSection);
|
||||
|
||||
uInt32 read32(const uInt8* address);
|
||||
void write32(uInt8* address, uInt32 value);
|
||||
|
||||
private:
|
||||
const uInt32 myTextBase{0};
|
||||
const uInt32 myDataBase{0};
|
||||
const ElfParser& myParser;
|
||||
|
||||
uInt32 myTextSize{0};
|
||||
uInt32 myDataSize{0};
|
||||
unique_ptr<uInt8[]> myTextData;
|
||||
unique_ptr<uInt8[]> myDataData;
|
||||
|
||||
vector<optional<RelocatedSection>> myRelocatedSections;
|
||||
vector<optional<RelocatedSymbol>> myRelocatedSymbols;
|
||||
|
||||
private:
|
||||
ElfLinker(const ElfLinker&) = delete;
|
||||
ElfLinker(ElfLinker&&) = delete;
|
||||
ElfLinker& operator=(const ElfLinker&) = delete;
|
||||
ElfLinker& operator=(ElfLinker&&) = delete;
|
||||
};
|
||||
|
||||
#endif // ELF_LINKER
|
|
@ -31,45 +31,45 @@ namespace {
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void ElfParser::parse(const uInt8 *elfData, size_t size)
|
||||
{
|
||||
data = elfData;
|
||||
this->size = size;
|
||||
myData = elfData;
|
||||
mySize = size;
|
||||
|
||||
sections.resize(0);
|
||||
symbols.resize(0);
|
||||
relocations.clear();
|
||||
bigEndian = true;
|
||||
mySections.resize(0);
|
||||
mySymbols.resize(0);
|
||||
myRelocations.clear();
|
||||
myBigEndian = true;
|
||||
|
||||
try {
|
||||
if (read32(0x00) != ELF_MAGIC) ElfParseError::raise("bad magic");
|
||||
if (read8(0x04) != ELF_CLASS_32) ElfParseError::raise("not 32bit ELF");
|
||||
if (read8(0x06) != ELF_VERSION) ElfParseError::raise("invalid ELF version");
|
||||
|
||||
header.endianess = read8(0x05);
|
||||
bigEndian = header.endianess == ENDIAN_BIG_ENDIAN;
|
||||
myHeader.endianess = read8(0x05);
|
||||
myBigEndian = myHeader.endianess == ENDIAN_BIG_ENDIAN;
|
||||
|
||||
if (read32(0x14) != ELF_VERSION) ElfParseError::raise("inconsistent ELF version");
|
||||
|
||||
header.type = read16(0x10);
|
||||
header.arch = read16(0x12);
|
||||
myHeader.type = read16(0x10);
|
||||
myHeader.arch = read16(0x12);
|
||||
|
||||
header.shOffset = read32(0x20);
|
||||
header.shSize = read16(0x2e);
|
||||
header.shNum = read16(0x30);
|
||||
header.shstrIndex = read16(0x32);
|
||||
myHeader.shOffset = read32(0x20);
|
||||
myHeader.shSize = read16(0x2e);
|
||||
myHeader.shNum = read16(0x30);
|
||||
myHeader.shstrIndex = read16(0x32);
|
||||
|
||||
if (header.shstrIndex >= header.shNum) ElfParseError::raise(".shstrtab out of range");
|
||||
if (myHeader.shstrIndex >= myHeader.shNum) ElfParseError::raise(".shstrtab out of range");
|
||||
|
||||
sections.reserve(header.shNum);
|
||||
mySections.reserve(myHeader.shNum);
|
||||
|
||||
for (size_t i = 0; i < header.shNum; i++)
|
||||
sections.push_back(
|
||||
readSection(header.shOffset + i * header.shSize));
|
||||
for (size_t i = 0; i < myHeader.shNum; i++)
|
||||
mySections.push_back(
|
||||
readSection(myHeader.shOffset + i * myHeader.shSize));
|
||||
|
||||
const Section &shrstrtab(sections[header.shstrIndex]);
|
||||
const Section &shrstrtab(mySections[myHeader.shstrIndex]);
|
||||
|
||||
if (shrstrtab.type != SHT_STRTAB) ElfParseError::raise(".shstrtab has wrong type");
|
||||
|
||||
for (Section §ion : sections)
|
||||
for (Section §ion : mySections)
|
||||
section.name = getName(shrstrtab, section.nameOffset);
|
||||
|
||||
const Section* symtab = getSymtab();
|
||||
|
@ -78,15 +78,15 @@ void ElfParser::parse(const uInt8 *elfData, size_t size)
|
|||
const Section* strtab = getStrtab();
|
||||
if (!strtab) ElfParseError::raise("no string table to resolve symbol names");
|
||||
|
||||
symbols.reserve(symtab->size / SYMBOL_ENTRY_SIZE);
|
||||
mySymbols.reserve(symtab->size / SYMBOL_ENTRY_SIZE);
|
||||
|
||||
for (size_t i = 0; i < symtab->size / SYMBOL_ENTRY_SIZE; i++)
|
||||
symbols.push_back(readSymbol(i, *symtab, *strtab));
|
||||
mySymbols.push_back(readSymbol(i, *symtab, *strtab));
|
||||
}
|
||||
|
||||
for (auto& section: sections) {
|
||||
for (const auto& section: mySections) {
|
||||
if (section.type != SHT_REL && section.type != SHT_RELA) continue;
|
||||
if (section.info >= sections.size()) ElfParseError::raise("relocation table for invalid section");
|
||||
if (section.info >= mySections.size()) ElfParseError::raise("relocation table for invalid section");
|
||||
|
||||
vector<Relocation> rels;
|
||||
const size_t relocationCount = section.size / (section.type == SHT_REL ? REL_ENTRY_SIZE : RELA_ENTRY_SIZE);
|
||||
|
@ -95,13 +95,13 @@ void ElfParser::parse(const uInt8 *elfData, size_t size)
|
|||
for (size_t i = 0; i < relocationCount; i++) {
|
||||
Relocation rel = readRelocation(i, section);
|
||||
|
||||
if (rel.symbol >= symbols.size()) ElfParseError::raise("invalid relocation symbol");
|
||||
rel.symbolName = symbols[rel.symbol].name;
|
||||
if (rel.symbol >= mySymbols.size()) ElfParseError::raise("invalid relocation symbol");
|
||||
rel.symbolName = mySymbols[rel.symbol].name;
|
||||
|
||||
rels.push_back(rel);
|
||||
}
|
||||
|
||||
relocations[section.info] = rels;
|
||||
myRelocations[section.info] = rels;
|
||||
}
|
||||
} catch (const ElfParseError &e) {
|
||||
ElfParseError::raise("failed to parse ELF: " + string(e.what()));
|
||||
|
@ -109,27 +109,27 @@ void ElfParser::parse(const uInt8 *elfData, size_t size)
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
const uInt8 *ElfParser::getData() const { return data; }
|
||||
const uInt8 *ElfParser::getData() const { return myData; }
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
size_t ElfParser::getSize() const { return size; }
|
||||
size_t ElfParser::getSize() const { return mySize; }
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
const vector<ElfParser::Section> &ElfParser::getSections() const
|
||||
{
|
||||
return sections;
|
||||
return mySections;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
const vector<ElfParser::Symbol>& ElfParser::getSymbols() const
|
||||
{
|
||||
return symbols;
|
||||
return mySymbols;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
const optional<ElfParser::Section>
|
||||
ElfParser::getSection(const string &name) const {
|
||||
for (const Section §ion : sections)
|
||||
for (const Section §ion : mySections)
|
||||
if (section.name == name)
|
||||
return section;
|
||||
|
||||
|
@ -139,29 +139,29 @@ ElfParser::getSection(const string &name) const {
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
const optional<vector<ElfParser::Relocation>> ElfParser::getRelocations(size_t section) const
|
||||
{
|
||||
return relocations.contains(section) ? relocations.at(section) : optional<vector<ElfParser::Relocation>>();
|
||||
return myRelocations.contains(section) ? myRelocations.at(section) : optional<vector<ElfParser::Relocation>>();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt8 ElfParser::read8(uInt32 offset) const
|
||||
{
|
||||
if (offset >= size)
|
||||
if (offset >= mySize)
|
||||
ElfParseError::raise("reference beyond bounds");
|
||||
|
||||
return data[offset];
|
||||
return myData[offset];
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt16 ElfParser::read16(uInt32 offset) const
|
||||
{
|
||||
return bigEndian ? ((read8(offset) << 8) | read8(offset + 1))
|
||||
return myBigEndian ? ((read8(offset) << 8) | read8(offset + 1))
|
||||
: ((read8(offset + 1) << 8) | read8(offset));
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 ElfParser::read32(uInt32 offset) const
|
||||
{
|
||||
return bigEndian ? ((read8(offset) << 24) | (read8(offset + 1) << 16) |
|
||||
return myBigEndian ? ((read8(offset) << 24) | (read8(offset + 1) << 16) |
|
||||
(read8(offset + 2) << 8) | read8(offset + 3))
|
||||
: ((read8(offset + 3) << 24) | (read8(offset + 2) << 16) |
|
||||
(read8(offset + 1) << 8) | read8(offset));
|
||||
|
@ -181,7 +181,7 @@ ElfParser::Section ElfParser::readSection(uInt32 offset) const {
|
|||
section.info = read32(offset + 0x1c);
|
||||
section.align = read32(offset + 0x20);
|
||||
|
||||
if (section.offset + section.size >= size)
|
||||
if (section.offset + section.size >= mySize)
|
||||
ElfParseError::raise("section exceeds bounds");
|
||||
} catch (const ElfParseError &e) {
|
||||
ElfParseError::raise("failed to read section: " + string(e.what()));
|
||||
|
@ -212,14 +212,14 @@ ElfParser::Symbol ElfParser::readSymbol(uInt32 index, const Section& symSec, con
|
|||
|
||||
if (
|
||||
((sym.section != SHN_ABS && sym.section != SHN_UND) || sym.type == STT_SECTION) &&
|
||||
sym.section >= sections.size()
|
||||
sym.section >= mySections.size()
|
||||
)
|
||||
ElfParseError::raise("symbol: section index out of range");
|
||||
|
||||
sym.bind = sym.info >> 4;
|
||||
sym.type = sym.info & 0x0f;
|
||||
|
||||
sym.name = sym.type == STT_SECTION ? sections[sym.section].name : getName(strSec, sym.nameOffset);
|
||||
sym.name = sym.type == STT_SECTION ? mySections[sym.section].name : getName(strSec, sym.nameOffset);
|
||||
|
||||
return sym;
|
||||
}
|
||||
|
@ -239,9 +239,9 @@ ElfParser::Relocation ElfParser::readRelocation(uInt32 index, const Section& sec
|
|||
Relocation rel;
|
||||
|
||||
try {
|
||||
rel.address = read32(offset);
|
||||
rel.offset = read32(offset);
|
||||
rel.info = read32(offset + 0x04);
|
||||
rel.addend = sec.type == SHT_RELA ? read32(offset + 0x08) : 0;
|
||||
rel.addend = sec.type == SHT_RELA ? read32(offset + 0x08) : optional<uInt32>();
|
||||
} catch (const ElfParseError& e) {
|
||||
ElfParseError::raise("failed to read relocation: " + string(e.what()));
|
||||
}
|
||||
|
@ -249,6 +249,9 @@ ElfParser::Relocation ElfParser::readRelocation(uInt32 index, const Section& sec
|
|||
rel.symbol = rel.info >> 8;
|
||||
rel.type = rel.info & 0x0f;
|
||||
|
||||
if (rel.symbol >=mySymbols.size())
|
||||
ElfParseError::raise("bad relocation: symbol out of bounds");
|
||||
|
||||
return rel;
|
||||
}
|
||||
|
||||
|
@ -258,9 +261,9 @@ const char* ElfParser::getName(const Section& section, uInt32 offset) const
|
|||
if (offset >= section.size) ElfParseError::raise("name out of bounds");
|
||||
const uInt32 imageOffset = offset + section.offset;
|
||||
|
||||
const char *name = reinterpret_cast<const char *>(data + imageOffset);
|
||||
const char *name = reinterpret_cast<const char *>(myData + imageOffset);
|
||||
|
||||
if (data[imageOffset + strnlen(name, section.size - offset)] != '\0')
|
||||
if (myData[imageOffset + strnlen(name, section.size - offset)] != '\0')
|
||||
ElfParseError::raise("unterminated section name");
|
||||
|
||||
return name;
|
||||
|
@ -269,7 +272,7 @@ const char* ElfParser::getName(const Section& section, uInt32 offset) const
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
const ElfParser::Section* ElfParser::getSymtab() const
|
||||
{
|
||||
for (auto& section: sections)
|
||||
for (auto& section: mySections)
|
||||
if (section.type == SHT_SYMTAB) return §ion;
|
||||
|
||||
return nullptr;
|
||||
|
@ -281,10 +284,10 @@ const ElfParser::Section* ElfParser::getStrtab() const
|
|||
const Section* strtab = nullptr;
|
||||
size_t count = 0;
|
||||
|
||||
for (size_t i = 0; i < sections.size(); i++) {
|
||||
if (sections[i].type != SHT_STRTAB || i == header.shstrIndex) continue;
|
||||
for (size_t i = 0; i < mySections.size(); i++) {
|
||||
if (mySections[i].type != SHT_STRTAB || i == myHeader.shstrIndex) continue;
|
||||
|
||||
strtab = §ions[i];
|
||||
strtab = &mySections[i];
|
||||
count++;
|
||||
}
|
||||
|
||||
|
@ -349,11 +352,13 @@ ostream& operator<<(ostream& os, const ElfParser::Relocation rel)
|
|||
os
|
||||
<< rel.symbolName << " :"
|
||||
<< std::hex << std::setw(4) << std::setfill('0')
|
||||
<< " address=0x" << rel.address
|
||||
<< " offset=0x" << rel.offset
|
||||
<< " info=0x" << rel.info
|
||||
<< " addend=0x" << rel.addend
|
||||
<< " type=0x" << (int)rel.type;
|
||||
|
||||
if (rel.addend.has_value())
|
||||
os << " addend=0x" << *rel.addend;
|
||||
|
||||
os.copyfmt(reset);
|
||||
|
||||
os << " symbol=" << (int)rel.symbol;
|
||||
|
|
|
@ -81,9 +81,9 @@ class ElfParser {
|
|||
};
|
||||
|
||||
struct Relocation {
|
||||
uInt32 address;
|
||||
uInt32 offset;
|
||||
uInt32 info;
|
||||
uInt32 addend;
|
||||
optional<uInt32> addend;
|
||||
|
||||
uInt32 symbol;
|
||||
uInt8 type;
|
||||
|
@ -113,6 +113,13 @@ class ElfParser {
|
|||
|
||||
static constexpr uInt32 STT_SECTION = 0x03;
|
||||
|
||||
static constexpr uInt32 R_ARM_ABS32 = 0x02;
|
||||
static constexpr uInt32 R_ARM_THM_CALL = 0x0a;
|
||||
static constexpr uInt32 R_ARM_THM_JUMP24 = 0x1e;
|
||||
static constexpr uInt32 R_ARM_TARGET1 = 0x26;
|
||||
|
||||
static constexpr uInt32 STT_FUNC = 0x02;
|
||||
|
||||
public:
|
||||
ElfParser() = default;
|
||||
|
||||
|
@ -141,15 +148,15 @@ class ElfParser {
|
|||
const Section* getStrtab() const;
|
||||
|
||||
private:
|
||||
const uInt8 *data{nullptr};
|
||||
size_t size;
|
||||
const uInt8 *myData{nullptr};
|
||||
size_t mySize;
|
||||
|
||||
bool bigEndian{true};
|
||||
bool myBigEndian{true};
|
||||
|
||||
Header header;
|
||||
vector<Section> sections;
|
||||
vector<Symbol> symbols;
|
||||
std::unordered_map<size_t, vector<Relocation>> relocations;
|
||||
Header myHeader;
|
||||
vector<Section> mySections;
|
||||
vector<Symbol> mySymbols;
|
||||
std::unordered_map<size_t, vector<Relocation>> myRelocations;
|
||||
|
||||
private:
|
||||
ElfParser(const ElfParser&) = delete;
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
//============================================================================
|
||||
//
|
||||
// SSSS tt lll lll
|
||||
// SS SS tt ll ll
|
||||
// SS tttttt eeee ll ll aaaa
|
||||
// SSSS tt ee ee ll ll aa
|
||||
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||
// SS SS tt ee ll ll aa aa
|
||||
// SSSS ttt eeeee llll llll aaaaa
|
||||
//
|
||||
// Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
|
||||
// and the Stella Team
|
||||
//
|
||||
// See the file "License.txt" for information on usage and redistribution of
|
||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//============================================================================
|
||||
|
||||
#include "ElfUtil.hxx"
|
||||
|
||||
Int32 elfUtil::decode_B_BL(uInt32 opcode)
|
||||
{
|
||||
// nomenclature follows Thumb32 BL / B.W encoding in Arm Architecture Reference
|
||||
|
||||
uInt16 hw1 = opcode;
|
||||
uInt16 hw2 = opcode >> 16;
|
||||
|
||||
const uInt8 s = (hw1 >> 10) & 0x01;
|
||||
const uInt8 i1 = ~((hw2 >> 13) ^ s) & 0x01;
|
||||
const uInt8 i2 = ~((hw2 >> 11) ^ s) & 0x01;
|
||||
const uInt32 imm11 = hw2 & 0x7ff;
|
||||
const uInt32 imm10 = hw1 & 0x3ff;
|
||||
|
||||
Int32 offset = imm11 | (imm10 << 11) | (i1 << 21) | (i2 << 22) | (s << 23);
|
||||
|
||||
offset <<= 8;
|
||||
offset >>= 7;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
uInt32 elfUtil::encode_B_BL(Int32 offset, bool link)
|
||||
{
|
||||
// nomenclature follows Thumb32 BL / B.W encoding in Arm Architecture Reference
|
||||
|
||||
offset >>= 1;
|
||||
|
||||
uInt8 s = (offset >> 23) & 0x01;
|
||||
uInt8 j2 = ((~offset >> 22) ^ s) & 0x01;
|
||||
uInt8 j1 = ((~offset >> 21) ^ s) & 0x01;
|
||||
uInt32 imm11 = offset & 0x7ff;
|
||||
uInt32 imm10 = (offset >> 11) & 0x3ff;
|
||||
|
||||
uInt16 hw1 = 0xf000 | (s << 10) | imm10;
|
||||
uInt16 hw2 = 0x9000 | (j1 << 13) | (j2 << 11) | imm11;
|
||||
if (link) hw2 |= 0x4000;
|
||||
|
||||
return hw1 | (hw2 << 16);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
//============================================================================
|
||||
//
|
||||
// SSSS tt lll lll
|
||||
// SS SS tt ll ll
|
||||
// SS tttttt eeee ll ll aaaa
|
||||
// SSSS tt ee ee ll ll aa
|
||||
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||
// SS SS tt ee ll ll aa aa
|
||||
// SSSS ttt eeeee llll llll aaaaa
|
||||
//
|
||||
// Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
|
||||
// and the Stella Team
|
||||
//
|
||||
// See the file "License.txt" for information on usage and redistribution of
|
||||
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||
//============================================================================
|
||||
|
||||
#ifndef ELF_UTIL
|
||||
#define ELF_UTIL
|
||||
|
||||
#include "bspf.hxx"
|
||||
|
||||
namespace elfUtil {
|
||||
Int32 decode_B_BL(uInt32 opcode);
|
||||
|
||||
uInt32 encode_B_BL(Int32 offset, bool link);
|
||||
}
|
||||
|
||||
#endif // ELF_UTIL
|
|
@ -1,7 +1,9 @@
|
|||
MODULE := src/emucore/elf
|
||||
|
||||
MODULE_OBJS = \
|
||||
src/emucore/elf/ElfParser.o
|
||||
src/emucore/elf/ElfParser.o \
|
||||
src/emucore/elf/ElfLinker.o \
|
||||
src/emucore/elf/ElfUtil.o
|
||||
|
||||
MODULE_DIRS += \
|
||||
src/emucore/elf
|
||||
|
|
|
@ -692,6 +692,8 @@
|
|||
<ClCompile Include="..\..\emucore\ControllerDetector.cxx" />
|
||||
<ClCompile Include="..\..\emucore\DispatchResult.cxx" />
|
||||
<ClCompile Include="..\..\emucore\elf\ElfParser.cxx" />
|
||||
<ClCompile Include="..\..\emucore\elf\ElfLinker.cxx" />
|
||||
<ClCompile Include="..\..\emucore\elf\ElfUtil.cxx" />
|
||||
<ClCompile Include="..\..\emucore\EmulationTiming.cxx" />
|
||||
<ClCompile Include="..\..\emucore\EmulationWorker.cxx" />
|
||||
<ClCompile Include="..\..\emucore\FBSurface.cxx" />
|
||||
|
@ -1663,6 +1665,8 @@
|
|||
<ClInclude Include="..\..\emucore\ControlLowLevel.hxx" />
|
||||
<ClInclude Include="..\..\emucore\DispatchResult.hxx" />
|
||||
<ClInclude Include="..\..\emucore\elf\ElfParser.hxx" />
|
||||
<ClInclude Include="..\..\emucore\elf\ElfLinker.hxx" />
|
||||
<ClInclude Include="..\..\emucore\elf\ElfUtil.hxx" />
|
||||
<ClInclude Include="..\..\emucore\EmulationTiming.hxx" />
|
||||
<ClInclude Include="..\..\emucore\EmulationWorker.hxx" />
|
||||
<ClInclude Include="..\..\emucore\EventHandlerConstants.hxx" />
|
||||
|
@ -1971,4 +1975,4 @@
|
|||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -1248,6 +1248,12 @@
|
|||
<ClCompile Include="..\..\emucore\elf\ElfParser.cxx">
|
||||
<Filter>Source Files\emucore\elf</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\emucore\elf\ElfLinker.cxx">
|
||||
<Filter>Source Files\emucore\elf</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\emucore\elf\ElfUtil.cxx">
|
||||
<Filter>Source Files\emucore\elf</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\emucore\AtariVox.hxx">
|
||||
|
@ -2525,6 +2531,12 @@
|
|||
<ClInclude Include="..\..\emucore\elf\ElfParser.hxx">
|
||||
<Filter>Header Files\emucore\elf</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\emucore\elf\ElfLinker.hxx">
|
||||
<Filter>Header Files\emucore\elf</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\emucore\elf\ElfUtil.hxx">
|
||||
<Filter>Header Files\emucore\elf</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="stella.ico">
|
||||
|
@ -2536,4 +2548,4 @@
|
|||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
Loading…
Reference in New Issue