diff --git a/src/emucore/CartELF.cxx b/src/emucore/CartELF.cxx index 0a86023b4..41064fd53 100644 --- a/src/emucore/CartELF.cxx +++ b/src/emucore/CartELF.cxx @@ -70,9 +70,11 @@ namespace { cout << std::endl - << "text segment size: 0x" << std::setw(8) << linker.getTextSize() + << "text segment size: 0x" << std::setw(8) << linker.getSegmentSize(ElfLinker::SegmentType::text) << std::endl - << "data segment size: 0x" << std::setw(8) << linker.getDataSize() + << "data segment size: 0x" << std::setw(8) << linker.getSegmentSize(ElfLinker::SegmentType::data) + << std::endl + << "rodata segment size: 0x" << std::setw(8) << linker.getSegmentSize(ElfLinker::SegmentType::rodata) << std::endl; cout << std::endl << "relocated sections:" << std::endl << std::endl; @@ -85,9 +87,8 @@ namespace { cout << sections[i].name - << " @ 0x"<< std::setw(8) << (relocatedSections[i]->offset + - (relocatedSections[i]->segment == ElfLinker::SegmentType::text ? ADDR_TEXT_BASE : ADDR_DATA_BASE) - ) + << " @ 0x"<< std::setw(8) + << (relocatedSections[i]->offset + linker.getSegmentBase(relocatedSections[i]->segment)) << " size 0x" << std::setw(8) << sections[i].size << std::endl; } @@ -104,7 +105,19 @@ namespace { << " = 0x" << std::setw(8) << relocatedSymbols[i]->value; if (relocatedSymbols[i]->segment) { - cout << (*relocatedSymbols[i]->segment == ElfLinker::SegmentType::text ? " (text)" : " (data)"); + switch (*relocatedSymbols[i]->segment) { + case ElfLinker::SegmentType::text: + cout << " (text)"; + break; + + case ElfLinker::SegmentType::data: + cout << " (data)"; + break; + + case ElfLinker::SegmentType::rodata: + cout << " (rodata)"; + break; + } } else { cout << " (abs)"; } @@ -159,7 +172,7 @@ CartridgeELF::CartridgeELF(const ByteBuffer& image, size_t size, string_view md5 dumpElf(elfParser); #endif - ElfLinker elfLinker(ADDR_TEXT_BASE, ADDR_DATA_BASE, elfParser); + ElfLinker elfLinker(ADDR_TEXT_BASE, ADDR_DATA_BASE, ADDR_RODATA_BASE, elfParser); try { elfLinker.link(externalSymbols(Palette::ntsc)); } catch (const ElfLinker::ElfLinkError& e) { @@ -299,6 +312,7 @@ void CartridgeELF::vcsCopyOverblankToRiotRam() vcsWrite5(0x80 + i, OVERBLANK_PROGRAM[i]); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeELF::vcsStartOverblank() { myTransactionQueue.injectROM(0x4c); @@ -307,12 +321,14 @@ void CartridgeELF::vcsStartOverblank() myTransactionQueue.yield(0x0080); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeELF::BusTransaction CartridgeELF::BusTransaction::transactionYield(uInt16 address) { address &= 0x1fff; return {.address = address, .value = 0, .yield = true}; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeELF::BusTransaction CartridgeELF::BusTransaction::transactionDrive(uInt16 address, uInt8 value) { address &= 0x1fff; diff --git a/src/emucore/elf/ElfEnvironment.hxx b/src/emucore/elf/ElfEnvironment.hxx index 3044fc8cb..841d13e66 100644 --- a/src/emucore/elf/ElfEnvironment.hxx +++ b/src/emucore/elf/ElfEnvironment.hxx @@ -24,7 +24,8 @@ namespace elfEnvironment { constexpr uInt32 ADDR_TEXT_BASE = 0x00100000; constexpr uInt32 ADDR_DATA_BASE = 0x00200000; - constexpr uInt32 ADDR_TABLES_BASE = 0x00300000; + constexpr uInt32 ADDR_RODATA_BASE = 0x00300000; + constexpr uInt32 ADDR_TABLES_BASE = 0x00400000; constexpr uInt32 ADDR_ADDR_IDR = 0xf0000000; constexpr uInt32 ADDR_DATA_IDR = 0xf0000004; diff --git a/src/emucore/elf/ElfLinker.cxx b/src/emucore/elf/ElfLinker.cxx index 581115885..6b89e7375 100644 --- a/src/emucore/elf/ElfLinker.cxx +++ b/src/emucore/elf/ElfLinker.cxx @@ -22,9 +22,35 @@ #include "ElfLinker.hxx" +namespace { + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + std::optional determineSegmentType(const ElfParser::Section& section) + { + switch (section.type) { + case ElfParser::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: + return ElfLinker::SegmentType::data; + + default: + return std::nullopt; + } + } + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + bool checkSegmentOverlap(uInt32 segmentBase1, uInt32 segmentSize1, uInt32 segmentBase2, uInt32 segmentSize2) { + return !(segmentBase1 + segmentSize1 <= segmentBase2 || segmentBase2 + segmentSize2 <= segmentBase1); + } +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -ElfLinker::ElfLinker(uInt32 textBase, uInt32 dataBase, const ElfParser& parser) - : myTextBase(textBase), myDataBase(dataBase), myParser(parser) +ElfLinker::ElfLinker(uInt32 textBase, uInt32 dataBase, uInt32 rodataBase, const ElfParser& parser) + : myTextBase(textBase), myDataBase(dataBase), myRodataBase(rodataBase), myParser(parser) {} // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -38,9 +64,10 @@ ElfLinker& ElfLinker::setUndefinedSymbolDefault(uInt32 defaultValue) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void ElfLinker::link(const vector& externalSymbols) { - myTextSize = myDataSize = 0; + myTextSize = myDataSize = myRodataSize = 0; myTextData.reset(); myDataData.reset(); + myRodataData.reset(); myRelocatedSections.resize(0); myRelocatedSymbols.resize(0); myInitArray.resize(0); @@ -53,39 +80,57 @@ void ElfLinker::link(const vector& externalSymbols) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 ElfLinker::getTextBase() const +uInt32 ElfLinker::getSegmentSize(SegmentType type) const { - return myTextBase; + switch (type) { + case SegmentType::text: + return myTextSize; + + case SegmentType::data: + return myDataSize; + + case SegmentType::rodata: + return myRodataSize; + + default: + throw runtime_error("unreachable"); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 ElfLinker::getTextSize() const +const uInt8* ElfLinker::getSegmentData(SegmentType type) const { - return myTextSize; + switch (type) { + case SegmentType::text: + return myTextData.get(); + + case SegmentType::data: + return myDataData.get(); + + case SegmentType::rodata: + return myRodataData.get(); + + default: + throw runtime_error("unreachable"); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* ElfLinker::getTextData() const +uInt32 ElfLinker::getSegmentBase(SegmentType type) const { - return myTextData ? myTextData.get() : nullptr; -} + switch (type) { + case SegmentType::text: + return myTextBase; -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 ElfLinker::getDataBase() const -{ - return myDataBase; -} + case SegmentType::data: + return myDataBase; -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 ElfLinker::getDataSize() const -{ - return myDataSize; -} + case SegmentType::rodata: + return myRodataBase; -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* ElfLinker::getDataData() const -{ - return myDataData ? myDataData.get() : nullptr; + default: + throw runtime_error("unreachable"); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -128,26 +173,62 @@ const vector>& ElfLinker::getRelocated return myRelocatedSymbols; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt32& ElfLinker::getSegmentSizeRef(SegmentType type) +{ + switch (type) { + case SegmentType::text: + return myTextSize; + + case SegmentType::data: + return myDataSize; + + case SegmentType::rodata: + return myRodataSize; + + default: + throw runtime_error("unreachable"); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +unique_ptr& ElfLinker::getSegmentDataRef(SegmentType type) +{ + switch (type) { + case SegmentType::text: + return myTextData; + + case SegmentType::data: + return myDataData; + + case SegmentType::rodata: + return myRodataData; + + default: + throw runtime_error("unreachable"); + } +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void ElfLinker::relocateSections() { auto& sections = myParser.getSections(); myRelocatedSections.resize(sections.size(), std::nullopt); - // relocate all .text and .data sections + // relocate everything that is not .bss 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; + const auto segmentType = determineSegmentType(section); + if (!segmentType || section.type == ElfParser::SHT_NOBITS) continue; - if (segmentSize % section.align) - segmentSize = (segmentSize / section.align + 1) * section.align; + uInt32& segmentSize = getSegmentSizeRef(*segmentType); - myRelocatedSections[i] = {isText ? SegmentType::text : SegmentType::data, segmentSize}; - segmentSize += section.size; - } + if (segmentSize % section.align) + segmentSize = (segmentSize / section.align + 1) * section.align; + + myRelocatedSections[i] = {*segmentType, segmentSize}; + segmentSize += section.size; } // relocate all .bss sections @@ -164,27 +245,37 @@ void ElfLinker::relocateSections() } // ensure that the segments don't overlap - if (!(myTextBase + myTextSize <= myDataBase || myDataBase + myDataSize <= myTextBase)) + if ( + checkSegmentOverlap(myTextBase, myTextSize, myDataBase, myDataSize) || + checkSegmentOverlap(myTextBase, myTextSize, myRodataBase, myRodataSize) || + checkSegmentOverlap(myDataBase, myDataSize, myRodataBase, myRodataSize) + ) ElfLinkError::raise("segments overlap"); - // allocate and copy section data - myTextData = make_unique(myTextSize); - myDataData = make_unique(myDataSize); + // allocate segment data + for (SegmentType segmentType: {SegmentType::text, SegmentType::data, SegmentType::rodata}) { + const uInt32 segmentSize = getSegmentSize(segmentType); + if (segmentSize == 0) continue; - std::memset(myTextData.get(), 0, myTextSize); - std::memset(myDataData.get(), 0, myDataSize); + auto& segmentData = getSegmentDataRef(segmentType); + segmentData = make_unique(segmentSize); + std::memset(segmentData.get(), 0, segmentSize); + } + + // copy segment data for (size_t i = 0; i < sections.size(); i++) { const auto& relocatedSection = myRelocatedSections[i]; if (!relocatedSection) continue; const auto& section = sections[i]; - if (section.type != ElfParser::SHT_PROGBITS) continue; + if (section.type == ElfParser::SHT_NOBITS) continue; - const bool isText = section.name.starts_with(".text"); + const auto segmentType = determineSegmentType(section); + if (!segmentType) continue; std::memcpy( - (isText ? myTextData : myDataData).get() + relocatedSection->offset, + getSegmentDataRef(*segmentType).get() + relocatedSection->offset, myParser.getData() + section.offset, section.size ); @@ -268,8 +359,7 @@ void ElfLinker::relocateSymbols(const vector& externalSymbols) const auto& relocatedSection = myRelocatedSections[symbol.section]; if (!relocatedSection) continue; - uInt32 value = relocatedSection->segment == SegmentType::text ? myTextBase : myDataBase; - value += relocatedSection->offset; + uInt32 value = getSegmentBase(relocatedSection->segment) + relocatedSection->offset; if (symbol.type != ElfParser::STT_SECTION) value += symbol.value; myRelocatedSymbols[i] = {relocatedSection->segment, value, false}; @@ -327,8 +417,9 @@ void ElfLinker::applyRelocationToSection(const ElfParser::Relocation& relocation ); uInt8* target = - (targetSectionRelocated.segment == SegmentType::text ? myTextData : myDataData).get() + - targetSectionRelocated.offset + relocation.offset; + getSegmentDataRef(targetSectionRelocated.segment).get() + + targetSectionRelocated.offset + + relocation.offset; switch (relocation.type) { case ElfParser::R_ARM_ABS32: @@ -346,8 +437,9 @@ void ElfLinker::applyRelocationToSection(const ElfParser::Relocation& relocation const uInt32 op = read32(target); Int32 offset = relocatedSymbol->value + relocation.addend.value_or(elfUtil::decode_B_BL(op)) - - (targetSectionRelocated.segment == SegmentType::text ? myTextBase : myDataBase) - - targetSectionRelocated.offset - relocation.offset - 4; + getSegmentBase(targetSectionRelocated.segment) - + targetSectionRelocated.offset - + relocation.offset - 4; if ((offset >> 24) != -1 && (offset >> 24) != 0) ElfLinkError::raise("unable to relocate jump: offset out of bounds"); diff --git a/src/emucore/elf/ElfLinker.hxx b/src/emucore/elf/ElfLinker.hxx index e16ac9a45..91370441b 100644 --- a/src/emucore/elf/ElfLinker.hxx +++ b/src/emucore/elf/ElfLinker.hxx @@ -57,7 +57,7 @@ class ElfLinker { const string myReason; }; - enum class SegmentType: uInt8 { text, data }; + enum class SegmentType: uInt8 { text, data, rodata }; struct RelocatedSection { SegmentType segment; @@ -76,18 +76,14 @@ class ElfLinker { }; public: - ElfLinker(uInt32 textBase, uInt32 dataBase, const ElfParser& parser); + ElfLinker(uInt32 textBase, uInt32 dataBase, uInt32 rodataBase, const ElfParser& parser); ElfLinker& setUndefinedSymbolDefault(uInt32 defaultValue); 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; + uInt32 getSegmentSize(SegmentType type) const; + const uInt8* getSegmentData(SegmentType type) const; + uInt32 getSegmentBase(SegmentType type) const; const vector& getInitArray() const; const vector& getPreinitArray() const; @@ -98,6 +94,9 @@ class ElfLinker { const vector>& getRelocatedSymbols() const; private: + uInt32& getSegmentSizeRef(SegmentType type); + unique_ptr& getSegmentDataRef(SegmentType type); + void relocateSections(); void relocateInitArrays(); void relocateSymbols(const vector& externalSymbols); @@ -116,12 +115,15 @@ class ElfLinker { const uInt32 myTextBase{0}; const uInt32 myDataBase{0}; + const uInt32 myRodataBase{0}; const ElfParser& myParser; uInt32 myTextSize{0}; uInt32 myDataSize{0}; + uInt32 myRodataSize{0}; unique_ptr myTextData; unique_ptr myDataData; + unique_ptr myRodataData; vector> myRelocatedSections; vector> myRelocatedSymbols;