diff --git a/src/emucore/CartELF.cxx b/src/emucore/CartELF.cxx index b3b86f92d..8cfd46de9 100644 --- a/src/emucore/CartELF.cxx +++ b/src/emucore/CartELF.cxx @@ -18,6 +18,8 @@ #include #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 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 } diff --git a/src/emucore/CartELF.hxx b/src/emucore/CartELF.hxx index 85a03f3e9..288b83a37 100644 --- a/src/emucore/CartELF.hxx +++ b/src/emucore/CartELF.hxx @@ -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 diff --git a/src/emucore/elf/ElfLinker.cxx b/src/emucore/elf/ElfLinker.cxx new file mode 100644 index 000000000..31e91f1b2 --- /dev/null +++ b/src/emucore/elf/ElfLinker.cxx @@ -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 + +#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& 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(myTextSize); + myDataData = make_unique(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 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 ElfLinker::getInitArray() const +{ + throw runtime_error("not implemented"); +} + +const vector 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; +} diff --git a/src/emucore/elf/ElfLinker.hxx b/src/emucore/elf/ElfLinker.hxx new file mode 100644 index 000000000..8c98fbe21 --- /dev/null +++ b/src/emucore/elf/ElfLinker.hxx @@ -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 section; + uInt32 value; + }; + + struct ExternalSymbol { + string name; + uInt32 value; + }; + + public: + ElfLinker(uInt32 textBase, uInt32 dataBase, const ElfParser& parser); + + void link(const vector& externalSymbols); + + uInt32 getTextBase() const; + uInt32 getTextSize() const; + const uInt8* getTextData() const; + + uInt32 getDataBase() const; + uInt32 getDataSize() const; + const uInt8* getDataData() const; + + const vector getInitArray() const; + const vector 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 myTextData; + unique_ptr myDataData; + + vector> myRelocatedSections; + vector> myRelocatedSymbols; + + private: + ElfLinker(const ElfLinker&) = delete; + ElfLinker(ElfLinker&&) = delete; + ElfLinker& operator=(const ElfLinker&) = delete; + ElfLinker& operator=(ElfLinker&&) = delete; +}; + +#endif // ELF_LINKER diff --git a/src/emucore/elf/ElfParser.cxx b/src/emucore/elf/ElfParser.cxx index 99d977d7e..5577ff635 100644 --- a/src/emucore/elf/ElfParser.cxx +++ b/src/emucore/elf/ElfParser.cxx @@ -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 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::getSections() const { - return sections; + return mySections; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - const vector& ElfParser::getSymbols() const { - return symbols; + return mySymbols; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - const optional 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> ElfParser::getRelocations(size_t section) const { - return relocations.contains(section) ? relocations.at(section) : optional>(); + return myRelocations.contains(section) ? myRelocations.at(section) : optional>(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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(); } 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(data + imageOffset); + const char *name = reinterpret_cast(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; diff --git a/src/emucore/elf/ElfParser.hxx b/src/emucore/elf/ElfParser.hxx index 6f3dbd224..b6021622b 100644 --- a/src/emucore/elf/ElfParser.hxx +++ b/src/emucore/elf/ElfParser.hxx @@ -81,9 +81,9 @@ class ElfParser { }; struct Relocation { - uInt32 address; + uInt32 offset; uInt32 info; - uInt32 addend; + optional 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
sections; - vector symbols; - std::unordered_map> relocations; + Header myHeader; + vector
mySections; + vector mySymbols; + std::unordered_map> myRelocations; private: ElfParser(const ElfParser&) = delete; diff --git a/src/emucore/elf/ElfUtil.cxx b/src/emucore/elf/ElfUtil.cxx new file mode 100644 index 000000000..233dbf32c --- /dev/null +++ b/src/emucore/elf/ElfUtil.cxx @@ -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); + } diff --git a/src/emucore/elf/ElfUtil.hxx b/src/emucore/elf/ElfUtil.hxx new file mode 100644 index 000000000..c47056249 --- /dev/null +++ b/src/emucore/elf/ElfUtil.hxx @@ -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 diff --git a/src/emucore/elf/module.mk b/src/emucore/elf/module.mk index fc3b44e64..671451272 100644 --- a/src/emucore/elf/module.mk +++ b/src/emucore/elf/module.mk @@ -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 diff --git a/src/os/windows/Stella.vcxproj b/src/os/windows/Stella.vcxproj index 5b2cca742..20005bfa0 100755 --- a/src/os/windows/Stella.vcxproj +++ b/src/os/windows/Stella.vcxproj @@ -692,6 +692,8 @@ + + @@ -1663,6 +1665,8 @@ + + @@ -1971,4 +1975,4 @@ - \ No newline at end of file + diff --git a/src/os/windows/Stella.vcxproj.filters b/src/os/windows/Stella.vcxproj.filters index 60be923c8..a0cfc8a6a 100644 --- a/src/os/windows/Stella.vcxproj.filters +++ b/src/os/windows/Stella.vcxproj.filters @@ -1248,6 +1248,12 @@ Source Files\emucore\elf + + Source Files\emucore\elf + + + Source Files\emucore\elf + @@ -2525,6 +2531,12 @@ Header Files\emucore\elf + + Header Files\emucore\elf + + + Header Files\emucore\elf + @@ -2536,4 +2548,4 @@ Resource Files - \ No newline at end of file +