Introduce a separate segment for rodata.

This commit is contained in:
Christian Speckner 2024-07-13 21:43:57 +02:00
parent e8cbfabb79
commit 21c80981cd
4 changed files with 176 additions and 65 deletions

View File

@ -70,9 +70,11 @@ namespace {
cout cout
<< std::endl << 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 << 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; << std::endl;
cout << std::endl << "relocated sections:" << std::endl << std::endl; cout << std::endl << "relocated sections:" << std::endl << std::endl;
@ -85,9 +87,8 @@ namespace {
cout cout
<< sections[i].name << sections[i].name
<< " @ 0x"<< std::setw(8) << (relocatedSections[i]->offset + << " @ 0x"<< std::setw(8)
(relocatedSections[i]->segment == ElfLinker::SegmentType::text ? ADDR_TEXT_BASE : ADDR_DATA_BASE) << (relocatedSections[i]->offset + linker.getSegmentBase(relocatedSections[i]->segment))
)
<< " size 0x" << std::setw(8) << sections[i].size << std::endl; << " size 0x" << std::setw(8) << sections[i].size << std::endl;
} }
@ -104,7 +105,19 @@ namespace {
<< " = 0x" << std::setw(8) << relocatedSymbols[i]->value; << " = 0x" << std::setw(8) << relocatedSymbols[i]->value;
if (relocatedSymbols[i]->segment) { 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 { } else {
cout << " (abs)"; cout << " (abs)";
} }
@ -159,7 +172,7 @@ CartridgeELF::CartridgeELF(const ByteBuffer& image, size_t size, string_view md5
dumpElf(elfParser); dumpElf(elfParser);
#endif #endif
ElfLinker elfLinker(ADDR_TEXT_BASE, ADDR_DATA_BASE, elfParser); ElfLinker elfLinker(ADDR_TEXT_BASE, ADDR_DATA_BASE, ADDR_RODATA_BASE, elfParser);
try { try {
elfLinker.link(externalSymbols(Palette::ntsc)); elfLinker.link(externalSymbols(Palette::ntsc));
} catch (const ElfLinker::ElfLinkError& e) { } catch (const ElfLinker::ElfLinkError& e) {
@ -299,6 +312,7 @@ void CartridgeELF::vcsCopyOverblankToRiotRam()
vcsWrite5(0x80 + i, OVERBLANK_PROGRAM[i]); vcsWrite5(0x80 + i, OVERBLANK_PROGRAM[i]);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeELF::vcsStartOverblank() void CartridgeELF::vcsStartOverblank()
{ {
myTransactionQueue.injectROM(0x4c); myTransactionQueue.injectROM(0x4c);
@ -307,12 +321,14 @@ void CartridgeELF::vcsStartOverblank()
myTransactionQueue.yield(0x0080); myTransactionQueue.yield(0x0080);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeELF::BusTransaction CartridgeELF::BusTransaction::transactionYield(uInt16 address) CartridgeELF::BusTransaction CartridgeELF::BusTransaction::transactionYield(uInt16 address)
{ {
address &= 0x1fff; address &= 0x1fff;
return {.address = address, .value = 0, .yield = true}; return {.address = address, .value = 0, .yield = true};
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeELF::BusTransaction CartridgeELF::BusTransaction::transactionDrive(uInt16 address, uInt8 value) CartridgeELF::BusTransaction CartridgeELF::BusTransaction::transactionDrive(uInt16 address, uInt8 value)
{ {
address &= 0x1fff; address &= 0x1fff;

View File

@ -24,7 +24,8 @@
namespace elfEnvironment { namespace elfEnvironment {
constexpr uInt32 ADDR_TEXT_BASE = 0x00100000; constexpr uInt32 ADDR_TEXT_BASE = 0x00100000;
constexpr uInt32 ADDR_DATA_BASE = 0x00200000; 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_ADDR_IDR = 0xf0000000;
constexpr uInt32 ADDR_DATA_IDR = 0xf0000004; constexpr uInt32 ADDR_DATA_IDR = 0xf0000004;

View File

@ -22,9 +22,35 @@
#include "ElfLinker.hxx" #include "ElfLinker.hxx"
namespace {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
std::optional<ElfLinker::SegmentType> 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) ElfLinker::ElfLinker(uInt32 textBase, uInt32 dataBase, uInt32 rodataBase, const ElfParser& parser)
: myTextBase(textBase), myDataBase(dataBase), myParser(parser) : myTextBase(textBase), myDataBase(dataBase), myRodataBase(rodataBase), myParser(parser)
{} {}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -38,9 +64,10 @@ ElfLinker& ElfLinker::setUndefinedSymbolDefault(uInt32 defaultValue)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void ElfLinker::link(const vector<ExternalSymbol>& externalSymbols) void ElfLinker::link(const vector<ExternalSymbol>& externalSymbols)
{ {
myTextSize = myDataSize = 0; myTextSize = myDataSize = myRodataSize = 0;
myTextData.reset(); myTextData.reset();
myDataData.reset(); myDataData.reset();
myRodataData.reset();
myRelocatedSections.resize(0); myRelocatedSections.resize(0);
myRelocatedSymbols.resize(0); myRelocatedSymbols.resize(0);
myInitArray.resize(0); myInitArray.resize(0);
@ -53,39 +80,57 @@ void ElfLinker::link(const vector<ExternalSymbol>& 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;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - case SegmentType::data:
uInt32 ElfLinker::getDataBase() const return myDataBase;
{
return myDataBase;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - case SegmentType::rodata:
uInt32 ElfLinker::getDataSize() const return myRodataBase;
{
return myDataSize;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - default:
const uInt8* ElfLinker::getDataData() const throw runtime_error("unreachable");
{ }
return myDataData ? myDataData.get() : nullptr;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -128,26 +173,62 @@ const vector<std::optional<ElfLinker::RelocatedSymbol>>& ElfLinker::getRelocated
return myRelocatedSymbols; 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<uInt8[]>& 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() void ElfLinker::relocateSections()
{ {
auto& sections = myParser.getSections(); auto& sections = myParser.getSections();
myRelocatedSections.resize(sections.size(), std::nullopt); 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++) { for (size_t i = 0; i < sections.size(); i++) {
const auto& section = sections[i]; const auto& section = sections[i];
if (section.type == ElfParser::SHT_PROGBITS) { const auto segmentType = determineSegmentType(section);
const bool isText = section.name.starts_with(".text"); if (!segmentType || section.type == ElfParser::SHT_NOBITS) continue;
uInt32& segmentSize = isText ? myTextSize : myDataSize;
if (segmentSize % section.align) uInt32& segmentSize = getSegmentSizeRef(*segmentType);
segmentSize = (segmentSize / section.align + 1) * section.align;
myRelocatedSections[i] = {isText ? SegmentType::text : SegmentType::data, segmentSize}; if (segmentSize % section.align)
segmentSize += section.size; segmentSize = (segmentSize / section.align + 1) * section.align;
}
myRelocatedSections[i] = {*segmentType, segmentSize};
segmentSize += section.size;
} }
// relocate all .bss sections // relocate all .bss sections
@ -164,27 +245,37 @@ void ElfLinker::relocateSections()
} }
// ensure that the segments don't overlap // 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"); ElfLinkError::raise("segments overlap");
// allocate and copy section data // allocate segment data
myTextData = make_unique<uInt8[]>(myTextSize); for (SegmentType segmentType: {SegmentType::text, SegmentType::data, SegmentType::rodata}) {
myDataData = make_unique<uInt8[]>(myDataSize); const uInt32 segmentSize = getSegmentSize(segmentType);
if (segmentSize == 0) continue;
std::memset(myTextData.get(), 0, myTextSize); auto& segmentData = getSegmentDataRef(segmentType);
std::memset(myDataData.get(), 0, myDataSize);
segmentData = make_unique<uInt8[]>(segmentSize);
std::memset(segmentData.get(), 0, segmentSize);
}
// copy segment data
for (size_t i = 0; i < sections.size(); i++) { for (size_t i = 0; i < sections.size(); i++) {
const auto& relocatedSection = myRelocatedSections[i]; const auto& relocatedSection = myRelocatedSections[i];
if (!relocatedSection) continue; if (!relocatedSection) continue;
const auto& section = sections[i]; 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( std::memcpy(
(isText ? myTextData : myDataData).get() + relocatedSection->offset, getSegmentDataRef(*segmentType).get() + relocatedSection->offset,
myParser.getData() + section.offset, myParser.getData() + section.offset,
section.size section.size
); );
@ -268,8 +359,7 @@ void ElfLinker::relocateSymbols(const vector<ExternalSymbol>& externalSymbols)
const auto& relocatedSection = myRelocatedSections[symbol.section]; const auto& relocatedSection = myRelocatedSections[symbol.section];
if (!relocatedSection) continue; if (!relocatedSection) continue;
uInt32 value = relocatedSection->segment == SegmentType::text ? myTextBase : myDataBase; uInt32 value = getSegmentBase(relocatedSection->segment) + relocatedSection->offset;
value += relocatedSection->offset;
if (symbol.type != ElfParser::STT_SECTION) value += symbol.value; if (symbol.type != ElfParser::STT_SECTION) value += symbol.value;
myRelocatedSymbols[i] = {relocatedSection->segment, value, false}; myRelocatedSymbols[i] = {relocatedSection->segment, value, false};
@ -327,8 +417,9 @@ void ElfLinker::applyRelocationToSection(const ElfParser::Relocation& relocation
); );
uInt8* target = uInt8* target =
(targetSectionRelocated.segment == SegmentType::text ? myTextData : myDataData).get() + getSegmentDataRef(targetSectionRelocated.segment).get() +
targetSectionRelocated.offset + relocation.offset; targetSectionRelocated.offset +
relocation.offset;
switch (relocation.type) { switch (relocation.type) {
case ElfParser::R_ARM_ABS32: case ElfParser::R_ARM_ABS32:
@ -346,8 +437,9 @@ void ElfLinker::applyRelocationToSection(const ElfParser::Relocation& relocation
const uInt32 op = read32(target); const uInt32 op = read32(target);
Int32 offset = relocatedSymbol->value + relocation.addend.value_or(elfUtil::decode_B_BL(op)) - Int32 offset = relocatedSymbol->value + relocation.addend.value_or(elfUtil::decode_B_BL(op)) -
(targetSectionRelocated.segment == SegmentType::text ? myTextBase : myDataBase) - getSegmentBase(targetSectionRelocated.segment) -
targetSectionRelocated.offset - relocation.offset - 4; targetSectionRelocated.offset -
relocation.offset - 4;
if ((offset >> 24) != -1 && (offset >> 24) != 0) if ((offset >> 24) != -1 && (offset >> 24) != 0)
ElfLinkError::raise("unable to relocate jump: offset out of bounds"); ElfLinkError::raise("unable to relocate jump: offset out of bounds");

View File

@ -57,7 +57,7 @@ class ElfLinker {
const string myReason; const string myReason;
}; };
enum class SegmentType: uInt8 { text, data }; enum class SegmentType: uInt8 { text, data, rodata };
struct RelocatedSection { struct RelocatedSection {
SegmentType segment; SegmentType segment;
@ -76,18 +76,14 @@ class ElfLinker {
}; };
public: public:
ElfLinker(uInt32 textBase, uInt32 dataBase, const ElfParser& parser); ElfLinker(uInt32 textBase, uInt32 dataBase, uInt32 rodataBase, const ElfParser& parser);
ElfLinker& setUndefinedSymbolDefault(uInt32 defaultValue); ElfLinker& setUndefinedSymbolDefault(uInt32 defaultValue);
void link(const vector<ExternalSymbol>& externalSymbols); void link(const vector<ExternalSymbol>& externalSymbols);
uInt32 getTextBase() const; uInt32 getSegmentSize(SegmentType type) const;
uInt32 getTextSize() const; const uInt8* getSegmentData(SegmentType type) const;
const uInt8* getTextData() const; uInt32 getSegmentBase(SegmentType type) const;
uInt32 getDataBase() const;
uInt32 getDataSize() const;
const uInt8* getDataData() const;
const vector<uInt32>& getInitArray() const; const vector<uInt32>& getInitArray() const;
const vector<uInt32>& getPreinitArray() const; const vector<uInt32>& getPreinitArray() const;
@ -98,6 +94,9 @@ class ElfLinker {
const vector<std::optional<RelocatedSymbol>>& getRelocatedSymbols() const; const vector<std::optional<RelocatedSymbol>>& getRelocatedSymbols() const;
private: private:
uInt32& getSegmentSizeRef(SegmentType type);
unique_ptr<uInt8[]>& getSegmentDataRef(SegmentType type);
void relocateSections(); void relocateSections();
void relocateInitArrays(); void relocateInitArrays();
void relocateSymbols(const vector<ExternalSymbol>& externalSymbols); void relocateSymbols(const vector<ExternalSymbol>& externalSymbols);
@ -116,12 +115,15 @@ class ElfLinker {
const uInt32 myTextBase{0}; const uInt32 myTextBase{0};
const uInt32 myDataBase{0}; const uInt32 myDataBase{0};
const uInt32 myRodataBase{0};
const ElfParser& myParser; const ElfParser& myParser;
uInt32 myTextSize{0}; uInt32 myTextSize{0};
uInt32 myDataSize{0}; uInt32 myDataSize{0};
uInt32 myRodataSize{0};
unique_ptr<uInt8[]> myTextData; unique_ptr<uInt8[]> myTextData;
unique_ptr<uInt8[]> myDataData; unique_ptr<uInt8[]> myDataData;
unique_ptr<uInt8[]> myRodataData;
vector<optional<RelocatedSection>> myRelocatedSections; vector<optional<RelocatedSection>> myRelocatedSections;
vector<optional<RelocatedSymbol>> myRelocatedSymbols; vector<optional<RelocatedSymbol>> myRelocatedSymbols;