diff --git a/src/emucore/elf/ElfFile.hxx b/src/emucore/elf/ElfFile.hxx index bd409bf4a..f2b111173 100644 --- a/src/emucore/elf/ElfFile.hxx +++ b/src/emucore/elf/ElfFile.hxx @@ -96,6 +96,7 @@ class ElfFile { static constexpr uInt32 STT_NOTYPE = 0x00; static constexpr uInt32 R_ARM_ABS32 = 0x02; + static constexpr uInt32 R_ARM_REL32 = 0x03; static constexpr uInt32 R_ARM_THM_CALL = 0x0a; static constexpr uInt32 R_ARM_THM_JUMP24 = 0x1e; static constexpr uInt32 R_ARM_TARGET1 = 0x26; diff --git a/src/emucore/elf/ElfLinker.cxx b/src/emucore/elf/ElfLinker.cxx index 6fcfc4467..bcb5b351b 100644 --- a/src/emucore/elf/ElfLinker.cxx +++ b/src/emucore/elf/ElfLinker.cxx @@ -438,7 +438,12 @@ void ElfLinker::applyRelocationToSection(const ElfFile::Relocation& relocation, "unable to relocate " + symbol.name + " in " + targetSection.name + ": target out of range" ); - uInt8* target = + const uInt32 targetAddress = + getSegmentBase(targetSectionRelocated.segment) + + targetSectionRelocated.offset + + relocation.offset; + + uInt8* const target = getSegmentDataRef(targetSectionRelocated.segment).get() + targetSectionRelocated.offset + relocation.offset; @@ -453,6 +458,16 @@ void ElfLinker::applyRelocationToSection(const ElfFile::Relocation& relocation, break; } + case ElfFile::R_ARM_REL32: + { + uInt32 value = relocatedSymbol->value + relocation.addend.value_or(read32(target)); + value |= (symbol.type == ElfFile::STT_FUNC ? 0x01 : 0); + + write32(target, value - targetAddress); + + break; + } + case ElfFile::R_ARM_THM_CALL: case ElfFile::R_ARM_THM_JUMP24: { diff --git a/src/emucore/elf/LinkerTest.cxx b/src/emucore/elf/LinkerTest.cxx index 2531e1819..b74773bb3 100644 --- a/src/emucore/elf/LinkerTest.cxx +++ b/src/emucore/elf/LinkerTest.cxx @@ -688,6 +688,102 @@ TEST(ElfLinker, RodataSectionsGoToRodata) { EXPECT_EQ(segmentRead32(linker, SegmentType::rodata, 0x14), static_cast(0x12345679)); } + + TEST(ElfLinker, R_ARM_REL32_InsertsTheValueRelativeToTheTargetPosition_text) { + ElfFixture fixture(1000); + ElfLinker linker(0x00100000, 0x00200000, 0x00300000, fixture); + + fixture + .addSection(".text.1", ElfFile::SHT_PROGBITS, 0, 0x10) + .addSection(".text.2", ElfFile::SHT_PROGBITS, 0x10, 0x10) + .addSymbol("foo", 0x12345678, ElfFile::SHN_ABS) + .addRelocation(2, 0, 0x04, ElfFile::R_ARM_REL32); + + linker.link({}); + + EXPECT_EQ(segmentRead32(linker, SegmentType::text, 0x14), static_cast(0x12345678 - 0x00100014)); + } + + TEST(ElfLinker, R_ARM_REL32_InsertsTheValueRelativeToTheTargetPosition_data) { + ElfFixture fixture(1000); + ElfLinker linker(0x00100000, 0x00200000, 0x00300000, fixture); + + fixture + .addSection(".data.1", ElfFile::SHT_PROGBITS, 0, 0x10) + .addSection(".data.2", ElfFile::SHT_PROGBITS, 0x10, 0x10) + .addSymbol("foo", 0x12345678, ElfFile::SHN_ABS) + .addRelocation(2, 0, 0x04, ElfFile::R_ARM_REL32); + + linker.link({}); + + EXPECT_EQ(segmentRead32(linker, SegmentType::data, 0x14), static_cast(0x12345678 - 0x00200014)); + } + + TEST(ElfLinker, R_ARM_REL32_InsertsTheValueRelativeToTheTargetPosition_rodata) { + ElfFixture fixture(1000); + ElfLinker linker(0x00100000, 0x00200000, 0x00300000, fixture); + + fixture + .addSection(".rodata.1", ElfFile::SHT_PROGBITS, 0, 0x10) + .addSection(".rodata.2", ElfFile::SHT_PROGBITS, 0x10, 0x10) + .addSymbol("foo", 0x12345678, ElfFile::SHN_ABS) + .addRelocation(2, 0, 0x04, ElfFile::R_ARM_REL32); + + linker.link({}); + + EXPECT_EQ(segmentRead32(linker, SegmentType::rodata, 0x14), static_cast(0x12345678 - 0x00300014)); + } + + TEST(ElfLinker, R_ARM_REL32_AddsAddendToTarget) { + ElfFixture fixture(1000); + ElfLinker linker(0x00100000, 0x00200000, 0x00300000, fixture); + + fixture + .addSection(".rodata.1", ElfFile::SHT_PROGBITS, 0, 0x10) + .addSection(".rodata.2", ElfFile::SHT_PROGBITS, 0x10, 0x10) + .addSymbol("foo", 0x12345678, ElfFile::SHN_ABS) + .addRelocation(2, 0, 0x04, ElfFile::R_ARM_REL32, -4); + + + linker.link({}); + + EXPECT_EQ(segmentRead32(linker, SegmentType::rodata, 0x14), static_cast(0x12345674 - 0x00300014)); + } + + TEST(ElfLinker, R_ARM_REL32_UsesExistingValueAsAddend) { + ElfFixture fixture(1000); + + ElfLinker linker(0x00100000, 0x00200000, 0x00300000, fixture); + + fixture + .addSection(".rodata.1", ElfFile::SHT_PROGBITS, 0, 0x10) + .addSection(".rodata.2", ElfFile::SHT_PROGBITS, 0x10, 0x10) + .addSymbol("foo", 0x12345678, ElfFile::SHN_ABS) + .addRelocation(2, 0, 0x04, ElfFile::R_ARM_REL32) + .write32(0x14, -4); + + + linker.link({}); + + EXPECT_EQ(segmentRead32(linker, SegmentType::rodata, 0x14), static_cast(0x12345674 - 0x00300014)); + } + + TEST(ElfLinker, R_ARM_REL32_SetsBit0IfTargetIsFunction) { + ElfFixture fixture(1000); + ElfLinker linker(0x00100000, 0x00200000, 0x00300000, fixture); + + fixture + .addSection(".rodata.1", ElfFile::SHT_PROGBITS, 0, 0x10) + .addSection(".rodata.2", ElfFile::SHT_PROGBITS, 0x10, 0x10) + .addSymbol("foo", 0x12345678, ElfFile::SHN_ABS, ElfFile::STT_FUNC) + .addRelocation(2, 0, 0x04, ElfFile::R_ARM_REL32); + + + linker.link({}); + + EXPECT_EQ(segmentRead32(linker, SegmentType::rodata, 0x14), static_cast(0x12345679 - 0x00300014)); + } + TEST(ElfLinker, R_ARM_THM_CALL_PatchesOffset) { ElfFixture fixture(1000); ElfLinker linker(0x00100000, 0x00200000, 0x00300000, fixture); @@ -854,7 +950,7 @@ TEST(ElfLinker, RodataSectionsGoToRodata) { } INSTANTIATE_TEST_SUITE_P(RelocationExceptionSuite, RelocationExceptionTest, testing::Values( - ElfFile::R_ARM_ABS32, ElfFile::R_ARM_TARGET1, + ElfFile::R_ARM_ABS32, ElfFile::R_ARM_TARGET1, ElfFile::R_ARM_REL32, ElfFile::R_ARM_THM_CALL, ElfFile::R_ARM_THM_JUMP24 ));