From cded9ba8c373f0b149fd335e2ac8f73267531aeb Mon Sep 17 00:00:00 2001 From: saxopa <99569665+saxopa@users.noreply.github.com> Date: Mon, 12 Oct 2026 03:12:02 +0200 Subject: [PATCH] fix(relinker): accept RELATIVE relocations against a zero-value section symbol OpenOrbis create-fself adds one to the symbol index of every relocation it copies from .rela.dyn, so each R_X86_64_RELATIVE entry of a homebrew built with the OpenOrbis toolchain names symbol 1, a local STT_SECTION symbol with value 0. FreeBSD rtld computes RELATIVE as base + addend without reading the symbol. The pipeline, the code analysis and the strict filter now accept index 0 or a STT_SECTION symbol with value 0, and still throw for any other symbol. The relocation is written with symbol index 0, where the pipeline previously dropped RELATIVE entries with a nonzero index. Co-Authored-By: Claude Opus 5.5 --- .../src/analysis/CodeInstructionCollector.cpp | 3 +- .../UnusedNidFilter/StrictNidFilter.cpp | 7 +++- .../src/pipeline/RelinkerPipeline.cpp | 14 +++++-- .../relinker/tests/StrictNidFilterTests.cpp | 26 +++++++++++++ .../relinker/tests/test_none_relocations.py | 38 +++++++++++++++---- 5 files changed, 76 insertions(+), 12 deletions(-) diff --git a/core/relinker/relinker/src/analysis/CodeInstructionCollector.cpp b/core/relinker/relinker/src/analysis/CodeInstructionCollector.cpp index 3d78157a2..876fa53ea 100644 --- a/core/relinker/relinker/src/analysis/CodeInstructionCollector.cpp +++ b/core/relinker/relinker/src/analysis/CodeInstructionCollector.cpp @@ -181,7 +181,8 @@ std::set CodeInstructionCollector::Collect(const std::ve const auto index = info >> 32; const auto addend = Io::ReadU64(bytes, offset + 16); if (type == 8) { - if (index != 0 || !pointers.Values.emplace(slot, addend).second) throw Domain::RelinkerException("Code analysis: invalid relative relocation", slot); + const auto zeroSectionSymbol = index != 0 && index < symbolCount && (bytes[symbols + index * 24 + 4] & 15) == 3 && Io::ReadU64(bytes, symbols + index * 24 + 8) == 0; + if ((index != 0 && !zeroSectionSymbol) || !pointers.Values.emplace(slot, addend).second) throw Domain::RelinkerException("Code analysis: invalid relative relocation", slot); addRoot(addend); } else if (type == 1 || type == 6 || type == 7) { if (index == 0 || index >= symbolCount) throw Domain::RelinkerException("Code analysis: invalid relocation symbol", slot); diff --git a/core/relinker/relinker/src/analysis/UnusedNidFilter/StrictNidFilter.cpp b/core/relinker/relinker/src/analysis/UnusedNidFilter/StrictNidFilter.cpp index cf163ac55..3eede671e 100644 --- a/core/relinker/relinker/src/analysis/UnusedNidFilter/StrictNidFilter.cpp +++ b/core/relinker/relinker/src/analysis/UnusedNidFilter/StrictNidFilter.cpp @@ -79,7 +79,12 @@ public: const auto slot = read(*rela + offset); const auto info = read(*rela + offset + 8); if (static_cast(info) != 8) continue; - if (info >> 32) throw RelinkerException("Strict filter: relative relocation has a symbol", slot); + if (const auto symbol = info >> 32; symbol != 0) { + const auto symbols = table(6, 0x61000039); + const auto size = tags.find(0x6100003F); + if (!symbols || size == tags.end() || symbol >= size->second / 24 || (read(*symbols + symbol * 24 + 4) & 15) != 3 || read(*symbols + symbol * 24 + 8) != 0) + throw RelinkerException("Strict filter: relative relocation has a symbol", slot); + } const auto target = read(*rela + offset + 16); if (input.ImportSlots.contains(slot) || !input.Pointers.emplace(slot, target).second) throw RelinkerException("Strict filter: overlapping pointer relocations", slot); diff --git a/core/relinker/relinker/src/pipeline/RelinkerPipeline.cpp b/core/relinker/relinker/src/pipeline/RelinkerPipeline.cpp index a486d3d64..d1ff243f4 100644 --- a/core/relinker/relinker/src/pipeline/RelinkerPipeline.cpp +++ b/core/relinker/relinker/src/pipeline/RelinkerPipeline.cpp @@ -219,6 +219,15 @@ RelinkResult RelinkerPipeline::Relink(const std::vector& sourceElf return relaOff + off; }; + auto isZeroSectionSymbol = [&](const std::uint32_t symIdx) { + const FileByteOffset symOff = dynSymTabOffset + static_cast(symIdx) * symEntSize; + if (symOff < dynSymTabOffset || symOff > raw.size() || raw.size() - symOff < symEntSize) + throw RelinkerException("Symbol table entry out of bounds", symOff); + std::uint64_t value = 0; + std::memcpy(&value, raw.data() + symOff + 8, sizeof(value)); + return (raw[symOff + 4] & 0xf) == 3 && value == 0; + }; + auto extractRela = [&](const FileByteOffset relaOff, const ByteCount relaSize) { for (ByteCount off = 0; relaSize >= relaEntSize && off <= relaSize - relaEntSize; off += relaEntSize) { const FileByteOffset pos = relaEntryPos(relaOff, off); @@ -236,7 +245,7 @@ RelinkResult RelinkerPipeline::Relink(const std::vector& sourceElf continue; if (relType == 8) { - if (symIdx != 0) + if (symIdx != 0 && !isZeroSectionSymbol(symIdx)) throw RelinkerException("RELATIVE relocation has a nonzero symbol index", pos); continue; } @@ -405,9 +414,8 @@ RelinkResult RelinkerPipeline::Relink(const std::vector& sourceElf std::memcpy(&rOffset, raw.data() + pos, 8); std::memcpy(&rInfo, raw.data() + pos + 8, 8); std::memcpy(&rAddend, raw.data() + pos + 16, 8); - const std::uint32_t symIdx = static_cast(rInfo >> 32); const std::uint32_t relType = static_cast(rInfo & 0xffffffff); - if (symIdx == 0 && relType == R_X86_64_RELATIVE) + if (relType == R_X86_64_RELATIVE) appendRela(dynSection.RelaData, rOffset, static_cast(R_X86_64_RELATIVE), rAddend); } }; diff --git a/core/relinker/relinker/tests/StrictNidFilterTests.cpp b/core/relinker/relinker/tests/StrictNidFilterTests.cpp index f62ec3f09..4d3f25755 100644 --- a/core/relinker/relinker/tests/StrictNidFilterTests.cpp +++ b/core/relinker/relinker/tests/StrictNidFilterTests.cpp @@ -576,6 +576,32 @@ void sceDynlibDataOffsets() { "Duplicate PT_SCE_DYNLIBDATA segments were accepted"); requireFailure([&] { (void)Relinker::MakeStrictUnusedNidFilter()->Filter(references, duplicate, input.Text, input.TextVaddr); }, "Strict filter accepted duplicate PT_SCE_DYNLIBDATA segments"); + + auto sectionRelative = bytes; + write(sectionRelative, 208, 0x70); + write(sectionRelative, 216, 0x70); + write(sectionRelative, 0x330, 0x61000039); + write(sectionRelative, 0x338, 0x100); + write(sectionRelative, 0x340, 0x6100003B); + write(sectionRelative, 0x348, 24); + write(sectionRelative, 0x350, 0x6100003F); + write(sectionRelative, 0x358, 48); + write(sectionRelative, 0x61C, 3); + write(sectionRelative, 0x908, (std::uint64_t{1} << 32) | 8); + require(Relinker::MakeStrictUnusedNidFilter()->Filter(references, sectionRelative, input.Text, input.TextVaddr).size() == 1, + "Strict filter rejected a RELATIVE relocation against a zero-value section symbol"); + auto valuedSection = sectionRelative; + write(valuedSection, 0x620, 0x10); + requireFailure([&] { (void)Relinker::MakeStrictUnusedNidFilter()->Filter(references, valuedSection, input.Text, input.TextVaddr); }, + "Strict filter accepted a RELATIVE relocation against a section symbol with a value"); + auto untyped = sectionRelative; + write(untyped, 0x61C, 0); + requireFailure([&] { (void)Relinker::MakeStrictUnusedNidFilter()->Filter(references, untyped, input.Text, input.TextVaddr); }, + "Strict filter accepted a RELATIVE relocation against a non-section symbol"); + auto outside = sectionRelative; + write(outside, 0x908, (std::uint64_t{2} << 32) | 8); + requireFailure([&] { (void)Relinker::MakeStrictUnusedNidFilter()->Filter(references, outside, input.Text, input.TextVaddr); }, + "Strict filter accepted a RELATIVE relocation symbol outside the symbol table"); } } diff --git a/core/relinker/relinker/tests/test_none_relocations.py b/core/relinker/relinker/tests/test_none_relocations.py index 5a0d06fc3..cbf3d4cb3 100644 --- a/core/relinker/relinker/tests/test_none_relocations.py +++ b/core/relinker/relinker/tests/test_none_relocations.py @@ -79,13 +79,37 @@ def main(): if target == "windows" and os.name == "nt" and not has_import: run = subprocess.run([str(output)], capture_output=True, timeout=20) assert run.returncode == 42, (name, level, run.returncode, run.stderr) - source = work / "invalid-relative.elf" - source.write_bytes(image_with_relocations([noop, (0x300, (1 << 32) | 8, 0x210)], False, False)) - output = work / "invalid-relative.exe" - result = subprocess.run([str(relinker), "--skip-sce-module", "--windows", str(source), str(output)], - capture_output=True, text=True, timeout=20) - assert result.returncode == 2 and "RELATIVE relocation has a nonzero symbol index" in result.stderr - assert not output.exists() + for sce in (False, True): + image = image_with_relocations([noop, (0x300, (1 << 32) | 8, 0x210)], sce, False) + struct.pack_into("