diff --git a/src/emucore/elf/ElfFile.hxx b/src/emucore/elf/ElfFile.hxx index 9cb145d15..4b133a7df 100644 --- a/src/emucore/elf/ElfFile.hxx +++ b/src/emucore/elf/ElfFile.hxx @@ -92,6 +92,7 @@ class ElfFile { static constexpr uInt32 SHN_UND = 0x00; static constexpr uInt32 STT_SECTION = 0x03; + static constexpr uInt32 STT_NOTYPE = 0x00; static constexpr uInt32 R_ARM_ABS32 = 0x02; static constexpr uInt32 R_ARM_THM_CALL = 0x0a; diff --git a/src/emucore/elf/LinkerTest.cxx b/src/emucore/elf/LinkerTest.cxx index 5095930cb..a20176fb6 100644 --- a/src/emucore/elf/LinkerTest.cxx +++ b/src/emucore/elf/LinkerTest.cxx @@ -28,6 +28,8 @@ namespace { public: explicit ElfFixture(size_t size) : mySize(size) { myData = make_unique(mySize); + + addSection("", 0, 0, 0); } const uInt8 *getData() const override { @@ -66,6 +68,22 @@ namespace { return *this; } + ElfFixture& addSymbol(string_view name, uInt32 value, uInt16 section, uInt8 type = ElfFile::STT_NOTYPE) { + mySymbols.push_back({ + .nameOffset = 0, + .value = value, + .size = 0, + .info = 0, + .visibility = 0, + .section = section, + .name = string(name), + .bind = 0, + .type = type + }); + + return *this; + } + ElfFixture& write8(size_t address, uInt8 value) { myData[address] = value; @@ -92,7 +110,7 @@ namespace { EXPECT_EQ(linker.getSegmentSize(ElfLinker::SegmentType::rodata), static_cast(0)); } - TEST(ElfLinker, TextSegmentsGoToText) { + TEST(ElfLinker, TextSectionsGoToText) { ElfFixture fixture(1000); ElfLinker linker(0x00100000, 0x00200000, 0x00300000, fixture); @@ -112,25 +130,25 @@ namespace { EXPECT_EQ(linker.getSegmentSize(ElfLinker::SegmentType::data), static_cast(0)); EXPECT_EQ(linker.getSegmentSize(ElfLinker::SegmentType::rodata), static_cast(0)); - EXPECT_EQ(linker.getRelocatedSections()[0]->offset, static_cast(0)); - EXPECT_EQ(linker.getRelocatedSections()[0]->segment, ElfLinker::SegmentType::text); - - EXPECT_EQ(linker.getRelocatedSections()[1]->offset, static_cast(12)); + EXPECT_EQ(linker.getRelocatedSections()[1]->offset, static_cast(0)); EXPECT_EQ(linker.getRelocatedSections()[1]->segment, ElfLinker::SegmentType::text); - EXPECT_EQ(linker.getRelocatedSections()[2]->offset, static_cast(34)); + EXPECT_EQ(linker.getRelocatedSections()[2]->offset, static_cast(12)); EXPECT_EQ(linker.getRelocatedSections()[2]->segment, ElfLinker::SegmentType::text); - EXPECT_EQ(linker.getRelocatedSections()[3]->offset, static_cast(67)); + EXPECT_EQ(linker.getRelocatedSections()[3]->offset, static_cast(34)); EXPECT_EQ(linker.getRelocatedSections()[3]->segment, ElfLinker::SegmentType::text); + EXPECT_EQ(linker.getRelocatedSections()[4]->offset, static_cast(67)); + EXPECT_EQ(linker.getRelocatedSections()[4]->segment, ElfLinker::SegmentType::text); + EXPECT_EQ(linker.getSegmentData(ElfLinker::SegmentType::text)[0], 0x01); EXPECT_EQ(linker.getSegmentData(ElfLinker::SegmentType::text)[12], 0x02); EXPECT_EQ(linker.getSegmentData(ElfLinker::SegmentType::text)[34], 0x03); EXPECT_EQ(linker.getSegmentData(ElfLinker::SegmentType::text)[67], 0x04); } - TEST(ElfLinker, DataSegmentsGoToData) { + TEST(ElfLinker, DataSectionsGoToData) { ElfFixture fixture(1000); ElfLinker linker(0x00100000, 0x00200000, 0x00300000, fixture); @@ -150,25 +168,25 @@ namespace { EXPECT_EQ(linker.getSegmentSize(ElfLinker::SegmentType::data), static_cast(78)); EXPECT_EQ(linker.getSegmentSize(ElfLinker::SegmentType::rodata), static_cast(0)); - EXPECT_EQ(linker.getRelocatedSections()[0]->offset, static_cast(0)); - EXPECT_EQ(linker.getRelocatedSections()[0]->segment, ElfLinker::SegmentType::data); - - EXPECT_EQ(linker.getRelocatedSections()[1]->offset, static_cast(12)); + EXPECT_EQ(linker.getRelocatedSections()[1]->offset, static_cast(0)); EXPECT_EQ(linker.getRelocatedSections()[1]->segment, ElfLinker::SegmentType::data); - EXPECT_EQ(linker.getRelocatedSections()[2]->offset, static_cast(34)); + EXPECT_EQ(linker.getRelocatedSections()[2]->offset, static_cast(12)); EXPECT_EQ(linker.getRelocatedSections()[2]->segment, ElfLinker::SegmentType::data); - EXPECT_EQ(linker.getRelocatedSections()[3]->offset, static_cast(67)); + EXPECT_EQ(linker.getRelocatedSections()[3]->offset, static_cast(34)); EXPECT_EQ(linker.getRelocatedSections()[3]->segment, ElfLinker::SegmentType::data); + EXPECT_EQ(linker.getRelocatedSections()[4]->offset, static_cast(67)); + EXPECT_EQ(linker.getRelocatedSections()[4]->segment, ElfLinker::SegmentType::data); + EXPECT_EQ(linker.getSegmentData(ElfLinker::SegmentType::data)[0], 0x01); EXPECT_EQ(linker.getSegmentData(ElfLinker::SegmentType::data)[12], 0x02); EXPECT_EQ(linker.getSegmentData(ElfLinker::SegmentType::data)[34], 0x03); EXPECT_EQ(linker.getSegmentData(ElfLinker::SegmentType::data)[67], 0x04); } -TEST(ElfLinker, RodataSegmentsGoToRodata) { +TEST(ElfLinker, RodataSectionsGoToRodata) { ElfFixture fixture(1000); ElfLinker linker(0x00100000, 0x00200000, 0x00300000, fixture); @@ -188,18 +206,18 @@ TEST(ElfLinker, RodataSegmentsGoToRodata) { EXPECT_EQ(linker.getSegmentSize(ElfLinker::SegmentType::data), static_cast(0)); EXPECT_EQ(linker.getSegmentSize(ElfLinker::SegmentType::rodata), static_cast(78)); - EXPECT_EQ(linker.getRelocatedSections()[0]->offset, static_cast(0)); - EXPECT_EQ(linker.getRelocatedSections()[0]->segment, ElfLinker::SegmentType::rodata); - - EXPECT_EQ(linker.getRelocatedSections()[1]->offset, static_cast(12)); + EXPECT_EQ(linker.getRelocatedSections()[1]->offset, static_cast(0)); EXPECT_EQ(linker.getRelocatedSections()[1]->segment, ElfLinker::SegmentType::rodata); - EXPECT_EQ(linker.getRelocatedSections()[2]->offset, static_cast(34)); + EXPECT_EQ(linker.getRelocatedSections()[2]->offset, static_cast(12)); EXPECT_EQ(linker.getRelocatedSections()[2]->segment, ElfLinker::SegmentType::rodata); - EXPECT_EQ(linker.getRelocatedSections()[3]->offset, static_cast(67)); + EXPECT_EQ(linker.getRelocatedSections()[3]->offset, static_cast(34)); EXPECT_EQ(linker.getRelocatedSections()[3]->segment, ElfLinker::SegmentType::rodata); + EXPECT_EQ(linker.getRelocatedSections()[4]->offset, static_cast(67)); + EXPECT_EQ(linker.getRelocatedSections()[4]->segment, ElfLinker::SegmentType::rodata); + EXPECT_EQ(linker.getSegmentData(ElfLinker::SegmentType::rodata)[0], 0x01); EXPECT_EQ(linker.getSegmentData(ElfLinker::SegmentType::rodata)[12], 0x02); EXPECT_EQ(linker.getSegmentData(ElfLinker::SegmentType::rodata)[34], 0x03); @@ -224,21 +242,205 @@ TEST(ElfLinker, RodataSegmentsGoToRodata) { EXPECT_EQ(linker.getSegmentSize(ElfLinker::SegmentType::data), static_cast(76)); EXPECT_EQ(linker.getSegmentSize(ElfLinker::SegmentType::rodata), static_cast(0)); - EXPECT_EQ(linker.getRelocatedSections()[0]->offset, static_cast(0)); - EXPECT_EQ(linker.getRelocatedSections()[0]->segment, ElfLinker::SegmentType::data); - - EXPECT_EQ(linker.getRelocatedSections()[1]->offset, static_cast(44)); + EXPECT_EQ(linker.getRelocatedSections()[1]->offset, static_cast(0)); EXPECT_EQ(linker.getRelocatedSections()[1]->segment, ElfLinker::SegmentType::data); - EXPECT_EQ(linker.getRelocatedSections()[2]->offset, static_cast(10)); + EXPECT_EQ(linker.getRelocatedSections()[2]->offset, static_cast(44)); EXPECT_EQ(linker.getRelocatedSections()[2]->segment, ElfLinker::SegmentType::data); - EXPECT_EQ(linker.getRelocatedSections()[3]->offset, static_cast(65)); + EXPECT_EQ(linker.getRelocatedSections()[3]->offset, static_cast(10)); EXPECT_EQ(linker.getRelocatedSections()[3]->segment, ElfLinker::SegmentType::data); + EXPECT_EQ(linker.getRelocatedSections()[4]->offset, static_cast(65)); + EXPECT_EQ(linker.getRelocatedSections()[4]->segment, ElfLinker::SegmentType::data); + EXPECT_EQ(linker.getSegmentData(ElfLinker::SegmentType::data)[0], 0x01); EXPECT_EQ(linker.getSegmentData(ElfLinker::SegmentType::data)[10], 0x02); EXPECT_EQ(linker.getSegmentData(ElfLinker::SegmentType::data)[44], 0); EXPECT_EQ(linker.getSegmentData(ElfLinker::SegmentType::data)[65], 0); } + + TEST(ElfLinker, SegmentsMayEndNextToEachOther) { + ElfFixture fixture(1000); + ElfLinker linker(100, 200, 300, fixture); + + fixture + .addSection(".text", ElfFile::SHT_PROGBITS, 0, 100, 4) + .addSection(".data", ElfFile::SHT_PROGBITS, 0, 100, 4) + .addSection(".rodata", ElfFile::SHT_PROGBITS, 0, 100, 4); + + EXPECT_NO_THROW(linker.link({})); + } + + TEST(ElfLinker, OverlappingTextAndDataSegmentsThrow) { + ElfFixture fixture(1000); + ElfLinker linker(100, 200, 300, fixture); + + fixture + .addSection(".text", ElfFile::SHT_PROGBITS, 0, 101, 4) + .addSection(".data", ElfFile::SHT_PROGBITS, 0, 100, 4) + .addSection(".rodata", ElfFile::SHT_PROGBITS, 0, 100, 4); + + EXPECT_THROW(linker.link({}), ElfLinker::ElfLinkError); + } + + TEST(ElfLinker, OverlappingTextAndRodataSegmentsThrow) { + ElfFixture fixture(1000); + ElfLinker linker(100, 300, 200, fixture); + + fixture + .addSection(".text", ElfFile::SHT_PROGBITS, 0, 101, 4) + .addSection(".data", ElfFile::SHT_PROGBITS, 0, 100, 4) + .addSection(".rodata", ElfFile::SHT_PROGBITS, 0, 100, 4); + + EXPECT_THROW(linker.link({}), ElfLinker::ElfLinkError); + } + + TEST(ElfLinker, OverlappingDataAndRodataSegmentsThrow) { + ElfFixture fixture(1000); + ElfLinker linker(100, 200, 300, fixture); + + fixture + .addSection(".text", ElfFile::SHT_PROGBITS, 0, 100, 4) + .addSection(".data", ElfFile::SHT_PROGBITS, 0, 101, 4) + .addSection(".rodata", ElfFile::SHT_PROGBITS, 0, 100, 4); + + EXPECT_THROW(linker.link({}), ElfLinker::ElfLinkError); + } + + TEST(ElfLinker, ABSSymbolsAreAcceptedUnchanged) { + ElfFixture fixture(1000); + ElfLinker linker(0x00100000, 0x00200000, 0x00300000, fixture); + + fixture + .addSymbol("foo", 0x12345678, ElfFile::SHN_ABS); + + linker.link({}); + + EXPECT_EQ(linker.getRelocatedSymbols().size(), static_cast(1)); + EXPECT_TRUE(linker.getRelocatedSymbols()[0].has_value()); + EXPECT_FALSE(linker.getRelocatedSymbols()[0]->undefined); + EXPECT_EQ(linker.getRelocatedSymbols()[0]->value, static_cast(0x12345678)); + } + + TEST(ElfLinker, UNDSymbolesAreTakenFromExternals) { + ElfFixture fixture(1000); + ElfLinker linker(0x00100000, 0x00200000, 0x00300000, fixture); + + fixture + .addSymbol("foo", 0, ElfFile::SHN_UND); + + linker.link({{"foo", 0x12345678}}); + + EXPECT_EQ(linker.getRelocatedSymbols().size(), static_cast(1)); + EXPECT_TRUE(linker.getRelocatedSymbols()[0].has_value()); + EXPECT_FALSE(linker.getRelocatedSymbols()[0]->undefined); + EXPECT_EQ(linker.getRelocatedSymbols()[0]->value, static_cast(0x12345678)); + } + + TEST(ElfLinker, UNDSymbolsAreResolvedWithTheDefaultIfSet) { + ElfFixture fixture(1000); + ElfLinker linker(0x00100000, 0x00200000, 0x00300000, fixture); + + fixture + .addSymbol("foo", 0, ElfFile::SHN_UND); + + linker + .setUndefinedSymbolDefault(0x12345678) + .link({}); + + EXPECT_EQ(linker.getRelocatedSymbols().size(), static_cast(1)); + EXPECT_TRUE(linker.getRelocatedSymbols()[0].has_value()); + EXPECT_TRUE(linker.getRelocatedSymbols()[0]->undefined); + EXPECT_EQ(linker.getRelocatedSymbols()[0]->value, static_cast(0x12345678)); + } + + TEST(ElfLinker, UNDSymbolsAreIgnoredIfTheyCannotBeResolved) { + ElfFixture fixture(1000); + ElfLinker linker(0x00100000, 0x00200000, 0x00300000, fixture); + + fixture + .addSymbol("foo", 0, ElfFile::SHN_UND); + + linker.link({}); + + EXPECT_EQ(linker.getRelocatedSymbols().size(), static_cast(1)); + EXPECT_FALSE(linker.getRelocatedSymbols()[0].has_value()); + } + + TEST(ElfLinker, SymbolsThatReferToTextAreResolvedRelativeToText) { + ElfFixture fixture(1000); + ElfLinker linker(0x00100000, 0x00200000, 0x00300000, fixture); + + fixture + .addSection(".text.1", ElfFile::SHT_PROGBITS, 0, 0x10) + .addSection(".text.2", ElfFile::SHT_PROGBITS, 0, 0xef) + .addSection(".data", ElfFile::SHT_PROGBITS, 0, 0xff) + .addSection(".rodata", ElfFile::SHT_PROGBITS, 0, 0xff) + .addSymbol("foo", 0x42, 2); + + linker.link({}); + + EXPECT_EQ(linker.getRelocatedSymbols().size(), static_cast(1)); + EXPECT_TRUE(linker.getRelocatedSymbols()[0].has_value()); + EXPECT_FALSE(linker.getRelocatedSymbols()[0]->undefined); + EXPECT_EQ(linker.getRelocatedSymbols()[0]->value, static_cast(0x00100052)); + } + + TEST(ElfLinker, SymbolsThatReferToDataAreResolvedRelativeToData) { + ElfFixture fixture(1000); + ElfLinker linker(0x00100000, 0x00200000, 0x00300000, fixture); + + fixture + .addSection(".text", ElfFile::SHT_PROGBITS, 0, 0xff) + .addSection(".data.1", ElfFile::SHT_PROGBITS, 0, 0x10) + .addSection(".data.2", ElfFile::SHT_PROGBITS, 0, 0xef) + .addSection(".rodata", ElfFile::SHT_PROGBITS, 0, 0xff) + .addSymbol("foo", 0x42, 3); + + linker.link({}); + + EXPECT_EQ(linker.getRelocatedSymbols().size(), static_cast(1)); + EXPECT_TRUE(linker.getRelocatedSymbols()[0].has_value()); + EXPECT_FALSE(linker.getRelocatedSymbols()[0]->undefined); + EXPECT_EQ(linker.getRelocatedSymbols()[0]->value, static_cast(0x00200052)); + } + + TEST(ElfLinker, SymbolsThatReferToRodataAreResolvedRelativeToRodata) { + ElfFixture fixture(1000); + ElfLinker linker(0x00100000, 0x00200000, 0x00300000, fixture); + + fixture + .addSection(".text", ElfFile::SHT_PROGBITS, 0, 0xff) + .addSection(".data", ElfFile::SHT_PROGBITS, 0, 0xff) + .addSection(".rodata.1", ElfFile::SHT_PROGBITS, 0, 0x10) + .addSection(".rodata.2", ElfFile::SHT_PROGBITS, 0, 0xef) + .addSymbol("foo", 0x42, 4); + + linker.link({}); + + EXPECT_EQ(linker.getRelocatedSymbols().size(), static_cast(1)); + EXPECT_TRUE(linker.getRelocatedSymbols()[0].has_value()); + EXPECT_FALSE(linker.getRelocatedSymbols()[0]->undefined); + EXPECT_EQ(linker.getRelocatedSymbols()[0]->value, static_cast(0x00300052)); + } + + TEST(ElfLinker, SymbolsThatReferToBssAreResolvedRelativeToBss) { + ElfFixture fixture(1000); + ElfLinker linker(0x00100000, 0x00200000, 0x00300000, fixture); + + fixture + .addSection(".text", ElfFile::SHT_PROGBITS, 0, 0xff) + .addSection(".data", ElfFile::SHT_PROGBITS, 0, 0x10) + .addSection(".bss", ElfFile::SHT_PROGBITS, 0, 0xef) + .addSection(".rodata", ElfFile::SHT_PROGBITS, 0, 0xff) + .addSymbol("foo", 0x42, 3); + + linker.link({}); + + EXPECT_EQ(linker.getRelocatedSymbols().size(), static_cast(1)); + EXPECT_TRUE(linker.getRelocatedSymbols()[0].has_value()); + EXPECT_FALSE(linker.getRelocatedSymbols()[0]->undefined); + EXPECT_EQ(linker.getRelocatedSymbols()[0]->value, static_cast(0x00200052)); + } }