Skip to content

Commit

Permalink
Introduce a separate segment for rodata.
Browse files Browse the repository at this point in the history
  • Loading branch information
DirtyHairy committed Jul 13, 2024
1 parent e8cbfab commit 21c8098
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 65 deletions.
30 changes: 23 additions & 7 deletions src/emucore/CartELF.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}

Expand All @@ -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)";
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -299,6 +312,7 @@ void CartridgeELF::vcsCopyOverblankToRiotRam()
vcsWrite5(0x80 + i, OVERBLANK_PROGRAM[i]);
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeELF::vcsStartOverblank()
{
myTransactionQueue.injectROM(0x4c);
Expand All @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion src/emucore/elf/ElfEnvironment.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
188 changes: 140 additions & 48 deletions src/emucore/elf/ElfLinker.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Expand All @@ -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);
Expand All @@ -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;

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 ElfLinker::getTextSize() const
{
return myTextSize;
}
case SegmentType::data:
return myDataSize;

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const uInt8* ElfLinker::getTextData() const
{
return myTextData ? myTextData.get() : nullptr;
}
case SegmentType::rodata:
return myRodataSize;

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 ElfLinker::getDataBase() const
{
return myDataBase;
default:
throw runtime_error("unreachable");
}
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 ElfLinker::getDataSize() const
const uInt8* ElfLinker::getSegmentData(SegmentType type) const
{
return myDataSize;
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::getDataData() const
uInt32 ElfLinker::getSegmentBase(SegmentType type) const
{
return myDataData ? myDataData.get() : nullptr;
switch (type) {
case SegmentType::text:
return myTextBase;

case SegmentType::data:
return myDataBase;

case SegmentType::rodata:
return myRodataBase;

default:
throw runtime_error("unreachable");
}
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Expand Down Expand Up @@ -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
Expand All @@ -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;

auto& segmentData = getSegmentDataRef(segmentType);

std::memset(myTextData.get(), 0, myTextSize);
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++) {
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
);
Expand Down Expand Up @@ -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};
Expand Down Expand Up @@ -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:
Expand All @@ -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");
Expand Down
Loading

0 comments on commit 21c8098

Please sign in to comment.