Refactor to prepare for unit tests.

This commit is contained in:
Christian Speckner 2024-07-13 22:05:02 +02:00
parent 0978806ae4
commit 4d722c4622
8 changed files with 170 additions and 140 deletions

View File

@ -33,17 +33,17 @@ namespace {
constexpr size_t TRANSACTION_QUEUE_CAPACITY = 16384;
#ifdef DUMP_ELF
void dumpElf(const ElfParser& parser)
void dumpElf(const ElfFile& elf)
{
cout << std::endl << "ELF sections:" << std::endl << std::endl;
size_t i = 0;
for (auto& section: parser.getSections()) {
for (auto& section: elf.getSections()) {
if (section.type != 0x00) cout << i << " " << section << std::endl;
i++;
}
auto symbols = parser.getSymbols();
auto symbols = elf.getSymbols();
cout << std::endl << "ELF symbols:" << std::endl << std::endl;
if (symbols.size() > 0) {
i = 0;
@ -52,8 +52,8 @@ namespace {
}
i = 0;
for (auto& section: parser.getSections()) {
auto rels = parser.getRelocations(i++);
for (auto& section: elf.getSections()) {
auto rels = elf.getRelocations(i++);
if (!rels) continue;
cout

104
src/emucore/elf/ElfFile.hxx Normal file
View File

@ -0,0 +1,104 @@
//============================================================================
//
// 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_FILE
#define ELF_FILE
#include "bspf.hxx"
class ElfFile {
public:
struct Section {
uInt32 nameOffset;
string name;
uInt32 type;
uInt32 flags;
uInt32 virtualAddress;
uInt32 offset;
uInt32 size;
uInt32 info;
uInt32 align;
};
struct Symbol {
uInt32 nameOffset;
uInt32 value;
uInt32 size;
uInt8 info;
uInt8 visibility;
uInt16 section;
string name;
uInt8 bind;
uInt8 type;
};
struct Relocation {
uInt32 offset;
uInt32 info;
optional<uInt32> addend;
uInt32 symbol;
uInt8 type;
string symbolName;
};
public:
virtual ~ElfFile() = default;
virtual const uInt8 *getData() const = 0;
virtual size_t getSize() const = 0;
virtual const vector<Section>& getSections() const = 0;
virtual const vector<Symbol>& getSymbols() const = 0;
virtual const optional<vector<Relocation>> getRelocations(size_t section) const = 0;
public:
static constexpr uInt8 ENDIAN_LITTLE_ENDIAN = 0x01;
static constexpr uInt8 ENDIAN_BIG_ENDIAN = 0x02;
static constexpr uInt16 ET_REL = 0x01;
static constexpr uInt16 ARCH_ARM32 = 0x28;
static constexpr uInt32 SHT_NULL = 0x00;
static constexpr uInt32 SHT_PROGBITS = 0x01;
static constexpr uInt32 SHT_SYMTAB = 0x02;
static constexpr uInt32 SHT_STRTAB = 0x03;
static constexpr uInt32 SHT_RELA = 0x04;
static constexpr uInt32 SHT_NOBITS = 0x08;
static constexpr uInt32 SHT_REL = 0x09;
static constexpr uInt32 SHT_INIT_ARRAY = 0x0e;
static constexpr uInt32 SHT_PREINIT_ARRAY = 0x10;
static constexpr uInt32 SHN_ABS = 0xfff1;
static constexpr uInt32 SHN_UND = 0x00;
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;
};
#endif // ELF_FILE

View File

@ -24,17 +24,17 @@
namespace {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
std::optional<ElfLinker::SegmentType> determineSegmentType(const ElfParser::Section& section)
std::optional<ElfLinker::SegmentType> determineSegmentType(const ElfFile::Section& section)
{
switch (section.type) {
case ElfParser::SHT_PROGBITS:
case ElfFile::SHT_PROGBITS:
if (section.name.starts_with(".text")) return ElfLinker::SegmentType::text;
if (section.name.starts_with(".rodata")) return ElfLinker::SegmentType::rodata;
return ElfLinker::SegmentType::data;
case ElfParser::SHT_NOBITS:
case ElfFile::SHT_NOBITS:
return ElfLinker::SegmentType::data;
default:
@ -49,8 +49,8 @@ namespace {
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ElfLinker::ElfLinker(uInt32 textBase, uInt32 dataBase, uInt32 rodataBase, const ElfParser& parser)
: myTextBase(textBase), myDataBase(dataBase), myRodataBase(rodataBase), myParser(parser)
ElfLinker::ElfLinker(uInt32 textBase, uInt32 dataBase, uInt32 rodataBase, const ElfFile& elf)
: myTextBase(textBase), myDataBase(dataBase), myRodataBase(rodataBase), myElf(elf)
{}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -148,7 +148,7 @@ const vector<uInt32>& ElfLinker::getPreinitArray() const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ElfLinker::RelocatedSymbol ElfLinker::findRelocatedSymbol(string_view name) const
{
const auto& symbols = myParser.getSymbols();
const auto& symbols = myElf.getSymbols();
for (size_t i = 0; i < symbols.size(); i++) {
if (symbols[i].name != name) continue;
@ -212,7 +212,7 @@ unique_ptr<uInt8[]>& ElfLinker::getSegmentDataRef(SegmentType type)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ElfLinker::relocateSections()
{
auto& sections = myParser.getSections();
auto& sections = myElf.getSections();
myRelocatedSections.resize(sections.size(), std::nullopt);
// relocate everything that is not .bss
@ -220,7 +220,7 @@ void ElfLinker::relocateSections()
const auto& section = sections[i];
const auto segmentType = determineSegmentType(section);
if (!segmentType || section.type == ElfParser::SHT_NOBITS) continue;
if (!segmentType || section.type == ElfFile::SHT_NOBITS) continue;
uInt32& segmentSize = getSegmentSizeRef(*segmentType);
@ -235,7 +235,7 @@ void ElfLinker::relocateSections()
for (size_t i = 0; i < sections.size(); i++) {
const auto& section = sections[i];
if (section.type == ElfParser::SHT_NOBITS) {
if (section.type == ElfFile::SHT_NOBITS) {
if (myDataSize % section.align)
myDataSize = (myDataSize / section.align + 1) * section.align;
@ -269,14 +269,14 @@ void ElfLinker::relocateSections()
if (!relocatedSection) continue;
const auto& section = sections[i];
if (section.type == ElfParser::SHT_NOBITS) continue;
if (section.type == ElfFile::SHT_NOBITS) continue;
const auto segmentType = determineSegmentType(section);
if (!segmentType) continue;
std::memcpy(
getSegmentDataRef(*segmentType).get() + relocatedSection->offset,
myParser.getData() + section.offset,
myElf.getData() + section.offset,
section.size
);
}
@ -285,7 +285,7 @@ void ElfLinker::relocateSections()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ElfLinker::relocateInitArrays()
{
const auto& sections = myParser.getSections();
const auto& sections = myElf.getSections();
uInt32 initArraySize = 0;
uInt32 preinitArraySize = 0;
@ -297,7 +297,7 @@ void ElfLinker::relocateInitArrays()
const auto& section = sections[i];
switch (section.type) {
case ElfParser::SHT_INIT_ARRAY:
case ElfFile::SHT_INIT_ARRAY:
if (section.size % 4) ElfLinkError::raise("invalid init array");
relocatedInitArrays[i] = initArraySize;
@ -305,7 +305,7 @@ void ElfLinker::relocateInitArrays()
break;
case ElfParser::SHT_PREINIT_ARRAY:
case ElfFile::SHT_PREINIT_ARRAY:
if (section.size % 4) ElfLinkError::raise("invalid preinit array");
relocatedPreinitArrays[i] = preinitArraySize;
@ -321,8 +321,8 @@ void ElfLinker::relocateInitArrays()
copyInitArrays(myInitArray, relocatedInitArrays);
copyInitArrays(myPreinitArray, relocatedPreinitArrays);
applyRelocationsToInitArrays(ElfParser::SHT_INIT_ARRAY, myInitArray, relocatedInitArrays);
applyRelocationsToInitArrays(ElfParser::SHT_PREINIT_ARRAY, myPreinitArray, relocatedPreinitArrays);
applyRelocationsToInitArrays(ElfFile::SHT_INIT_ARRAY, myInitArray, relocatedInitArrays);
applyRelocationsToInitArrays(ElfFile::SHT_PREINIT_ARRAY, myPreinitArray, relocatedPreinitArrays);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -334,19 +334,19 @@ void ElfLinker::relocateSymbols(const vector<ExternalSymbol>& externalSymbols)
externalSymbolLookup[externalSymbol.name] = &externalSymbol;
// relocate symbols
const auto& symbols = myParser.getSymbols();
const auto& symbols = myElf.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) {
if (symbol.section == ElfFile::SHN_ABS) {
myRelocatedSymbols[i] = {std::nullopt, symbol.value, false};
continue;
}
if (symbol.section == ElfParser::SHN_UND) {
if (symbol.section == ElfFile::SHN_UND) {
if (externalSymbolLookup.contains(symbol.name))
myRelocatedSymbols[i] = {std::nullopt, externalSymbolLookup[symbol.name]->value, false};
@ -360,7 +360,7 @@ void ElfLinker::relocateSymbols(const vector<ExternalSymbol>& externalSymbols)
if (!relocatedSection) continue;
uInt32 value = getSegmentBase(relocatedSection->segment) + relocatedSection->offset;
if (symbol.type != ElfParser::STT_SECTION) value += symbol.value;
if (symbol.type != ElfFile::STT_SECTION) value += symbol.value;
myRelocatedSymbols[i] = {relocatedSection->segment, value, false};
}
@ -369,11 +369,11 @@ void ElfLinker::relocateSymbols(const vector<ExternalSymbol>& externalSymbols)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ElfLinker::applyRelocationsToSections()
{
auto& sections = myParser.getSections();
auto& sections = myElf.getSections();
// apply relocations
for (size_t iSection = 0; iSection < sections.size(); iSection++) {
const auto& relocations = myParser.getRelocations(iSection);
const auto& relocations = myElf.getRelocations(iSection);
if (!relocations) continue;
if (!myRelocatedSections[iSection]) continue;
@ -385,8 +385,8 @@ void ElfLinker::applyRelocationsToSections()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ElfLinker::copyInitArrays(vector<uInt32>& initArray, std::unordered_map<uInt32, uInt32> relocatedInitArrays)
{
const uInt8* elfData = myParser.getData();
const auto& sections = myParser.getSections();
const uInt8* elfData = myElf.getData();
const auto& sections = myElf.getSections();
// Copy init arrays
for (const auto [iSection, offset]: relocatedInitArrays) {
@ -398,11 +398,11 @@ void ElfLinker::copyInitArrays(vector<uInt32>& initArray, std::unordered_map<uI
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ElfLinker::applyRelocationToSection(const ElfParser::Relocation& relocation, size_t iSection)
void ElfLinker::applyRelocationToSection(const ElfFile::Relocation& relocation, size_t iSection)
{
const auto& targetSection = myParser.getSections()[iSection];
const auto& targetSection = myElf.getSections()[iSection];
const auto& targetSectionRelocated = *myRelocatedSections[iSection];
const auto& symbol = myParser.getSymbols()[relocation.symbol];
const auto& symbol = myElf.getSymbols()[relocation.symbol];
const auto& relocatedSymbol = myRelocatedSymbols[relocation.symbol];
if (!relocatedSymbol) ElfLinkError::raise(
@ -422,17 +422,17 @@ void ElfLinker::applyRelocationToSection(const ElfParser::Relocation& relocation
relocation.offset;
switch (relocation.type) {
case ElfParser::R_ARM_ABS32:
case ElfParser::R_ARM_TARGET1:
case ElfFile::R_ARM_ABS32:
case ElfFile::R_ARM_TARGET1:
{
const uInt32 value = relocatedSymbol->value + relocation.addend.value_or(read32(target));
write32(target, value | (symbol.type == ElfParser::STT_FUNC ? 0x01 : 0));
write32(target, value | (symbol.type == ElfFile::STT_FUNC ? 0x01 : 0));
break;
}
case ElfParser::R_ARM_THM_CALL:
case ElfParser::R_ARM_THM_JUMP24:
case ElfFile::R_ARM_THM_CALL:
case ElfFile::R_ARM_THM_JUMP24:
{
const uInt32 op = read32(target);
@ -444,7 +444,7 @@ void ElfLinker::applyRelocationToSection(const ElfParser::Relocation& relocation
if ((offset >> 24) != -1 && (offset >> 24) != 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));
write32(target, elfUtil::encode_B_BL(offset, relocation.type == ElfFile::R_ARM_THM_CALL));
}
}
}
@ -453,18 +453,18 @@ void ElfLinker::applyRelocationToSection(const ElfParser::Relocation& relocation
void ElfLinker::applyRelocationsToInitArrays(uInt8 initArrayType, vector<uInt32>& initArray,
const std::unordered_map<uInt32, uInt32>& relocatedInitArrays)
{
const auto& symbols = myParser.getSymbols();
const auto& sections = myParser.getSections();
const auto& symbols = myElf.getSymbols();
const auto& sections = myElf.getSections();
for (size_t iSection = 0; iSection < sections.size(); iSection++) {
const auto& section = sections[iSection];
if (section.type != initArrayType) continue;
const auto& relocations = myParser.getRelocations(iSection);
const auto& relocations = myElf.getRelocations(iSection);
if (!relocations) continue;
for (const auto& relocation: *relocations) {
if (relocation.type != ElfParser::R_ARM_ABS32 && relocation.type != ElfParser::R_ARM_TARGET1)
if (relocation.type != ElfFile::R_ARM_ABS32 && relocation.type != ElfFile::R_ARM_TARGET1)
ElfLinkError::raise("unsupported relocation for init array");
const auto& relocatedSymbol = myRelocatedSymbols[relocation.symbol];
@ -481,7 +481,7 @@ void ElfLinker::applyRelocationsToInitArrays(uInt8 initArrayType, vector<uInt32>
const uInt32 index = (relocatedInitArrays.at(iSection) + relocation.offset) >> 2;
const uInt32 value = relocatedSymbol->value + relocation.addend.value_or(initArray[index]);
initArray[index] = value | (symbols[relocation.symbol].type == ElfParser::STT_FUNC ? 1 : 0);
initArray[index] = value | (symbols[relocation.symbol].type == ElfFile::STT_FUNC ? 1 : 0);
}
}
}

View File

@ -19,7 +19,7 @@
#define ELF_LINKER
#include "bspf.hxx"
#include "ElfParser.hxx"
#include "ElfFile.hxx"
class ElfLinker {
public:
@ -76,7 +76,7 @@ class ElfLinker {
};
public:
ElfLinker(uInt32 textBase, uInt32 dataBase, uInt32 rodataBase, const ElfParser& parser);
ElfLinker(uInt32 textBase, uInt32 dataBase, uInt32 rodataBase, const ElfFile& elf);
ElfLinker& setUndefinedSymbolDefault(uInt32 defaultValue);
void link(const vector<ExternalSymbol>& externalSymbols);
@ -103,7 +103,7 @@ class ElfLinker {
void applyRelocationsToSections();
void copyInitArrays(vector<uInt32>& initArray, std::unordered_map<uInt32, uInt32> relocatedInitArrays);
void applyRelocationToSection(const ElfParser::Relocation& relocation, size_t iSection);
void applyRelocationToSection(const ElfFile::Relocation& relocation, size_t iSection);
void applyRelocationsToInitArrays(uInt8 initArrayType, vector<uInt32>& initArray,
const std::unordered_map<uInt32, uInt32>& relocatedInitArrays);
@ -116,7 +116,7 @@ class ElfLinker {
const uInt32 myTextBase{0};
const uInt32 myDataBase{0};
const uInt32 myRodataBase{0};
const ElfParser& myParser;
const ElfFile& myElf;
uInt32 myTextSize{0};
uInt32 myDataSize{0};

View File

@ -126,16 +126,6 @@ const vector<ElfParser::Symbol>& ElfParser::getSymbols() const
return mySymbols;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const optional<ElfParser::Section>
ElfParser::getSection(const string &name) const {
for (const Section &section : mySections)
if (section.name == name)
return section;
return optional<Section>();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const optional<vector<ElfParser::Relocation>> ElfParser::getRelocations(size_t section) const
{

View File

@ -20,9 +20,10 @@
#include <unordered_map>
#include "ElfFile.hxx"
#include "bspf.hxx"
class ElfParser {
class ElfParser : public ElfFile {
public:
class ElfParseError : public std::exception {
friend ElfParser;
@ -41,6 +42,19 @@ class ElfParser {
const string myReason;
};
public:
ElfParser() = default;
void parse(const uInt8 *elfData, size_t size);
const uInt8 *getData() const override;
size_t getSize() const override;
const vector<Section>& getSections() const override;
const vector<Symbol>& getSymbols() const override;
const optional<vector<Relocation>> getRelocations(size_t section) const override;
private:
struct Header {
uInt16 type;
uInt16 arch;
@ -52,88 +66,6 @@ class ElfParser {
uInt16 shstrIndex;
};
struct Section {
uInt32 nameOffset;
string name;
uInt32 type;
uInt32 flags;
uInt32 virtualAddress;
uInt32 offset;
uInt32 size;
uInt32 info;
uInt32 align;
};
struct Symbol {
uInt32 nameOffset;
uInt32 value;
uInt32 size;
uInt8 info;
uInt8 visibility;
uInt16 section;
string name;
uInt8 bind;
uInt8 type;
};
struct Relocation {
uInt32 offset;
uInt32 info;
optional<uInt32> addend;
uInt32 symbol;
uInt8 type;
string symbolName;
};
public:
static constexpr uInt8 ENDIAN_LITTLE_ENDIAN = 0x01;
static constexpr uInt8 ENDIAN_BIG_ENDIAN = 0x02;
static constexpr uInt16 ET_REL = 0x01;
static constexpr uInt16 ARCH_ARM32 = 0x28;
static constexpr uInt32 SHT_NULL = 0x00;
static constexpr uInt32 SHT_PROGBITS = 0x01;
static constexpr uInt32 SHT_SYMTAB = 0x02;
static constexpr uInt32 SHT_STRTAB = 0x03;
static constexpr uInt32 SHT_RELA = 0x04;
static constexpr uInt32 SHT_NOBITS = 0x08;
static constexpr uInt32 SHT_REL = 0x09;
static constexpr uInt32 SHT_INIT_ARRAY = 0x0e;
static constexpr uInt32 SHT_PREINIT_ARRAY = 0x10;
static constexpr uInt32 SHN_ABS = 0xfff1;
static constexpr uInt32 SHN_UND = 0x00;
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;
void parse(const uInt8 *elfData, size_t size);
const uInt8 *getData() const;
size_t getSize() const;
const Header& getHeader() const;
const vector<Section>& getSections() const;
const vector<Symbol>& getSymbols() const;
const optional<Section> getSection(const std::string &name) const;
const optional<vector<Relocation>> getRelocations(size_t section) const;
private:
uInt8 read8(uInt32 offset) const;
uInt16 read16(uInt32 offset) const;

View File

@ -1665,6 +1665,7 @@
<ClInclude Include="..\..\emucore\ControllerDetector.hxx" />
<ClInclude Include="..\..\emucore\ControlLowLevel.hxx" />
<ClInclude Include="..\..\emucore\DispatchResult.hxx" />
<ClInclude Include="..\..\emucore\elf\ElfFile.hxx" />
<ClInclude Include="..\..\emucore\elf\ElfParser.hxx" />
<ClInclude Include="..\..\emucore\elf\ElfLinker.hxx" />
<ClInclude Include="..\..\emucore\elf\ElfUtil.hxx" />

View File

@ -2531,6 +2531,9 @@
<ClInclude Include="..\..\debugger\gui\CartJANEWidget.hxx">
<Filter>Header Files\debugger\gui</Filter>
</ClInclude>
<ClInclude Include="..\..\emucore\elf\ElfFile.hxx">
<Filter>Header Files\emucore\elf</Filter>
</ClInclude>
<ClInclude Include="..\..\emucore\elf\ElfParser.hxx">
<Filter>Header Files\emucore\elf</Filter>
</ClInclude>