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

View File

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

View File

@ -22,9 +22,35 @@
#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)
: 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<ExternalSymbol>& 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<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;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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<std::optional<ElfLinker::RelocatedSymbol>>& 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<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()
{
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<uInt8[]>(myTextSize);
myDataData = make_unique<uInt8[]>(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<uInt8[]>(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<ExternalSymbol>& 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");

View File

@ -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<ExternalSymbol>& 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<uInt32>& getInitArray() const;
const vector<uInt32>& getPreinitArray() const;
@ -98,6 +94,9 @@ class ElfLinker {
const vector<std::optional<RelocatedSymbol>>& getRelocatedSymbols() const;
private:
uInt32& getSegmentSizeRef(SegmentType type);
unique_ptr<uInt8[]>& getSegmentDataRef(SegmentType type);
void relocateSections();
void relocateInitArrays();
void relocateSymbols(const vector<ExternalSymbol>& 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<uInt8[]> myTextData;
unique_ptr<uInt8[]> myDataData;
unique_ptr<uInt8[]> myRodataData;
vector<optional<RelocatedSection>> myRelocatedSections;
vector<optional<RelocatedSymbol>> myRelocatedSymbols;