Woefully untested ELF linker.

This commit is contained in:
Christian Speckner 2024-07-09 20:11:35 +02:00
parent 4c44735f40
commit 135349ed6b
11 changed files with 610 additions and 65 deletions

View File

@ -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
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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 &section : sections)
for (Section &section : 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 &section : sections)
for (const Section &section : 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 &section;
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 = &sections[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;

View File

@ -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;

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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" />

View File

@ -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">