diff --git a/core/relinker/relinker/src/analysis/CodeInstructionCollector.cpp b/core/relinker/relinker/src/analysis/CodeInstructionCollector.cpp index 915607b420..d0bad17e5d 100644 --- a/core/relinker/relinker/src/analysis/CodeInstructionCollector.cpp +++ b/core/relinker/relinker/src/analysis/CodeInstructionCollector.cpp @@ -208,8 +208,13 @@ std::set CodeInstructionCollector::Collect(const std::ve const auto index = info >> 32; const auto addend = Io::ReadU64(bytes, offset + 16); if (type == 8) { - 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); + const auto hasSymbol = index != 0 && index < symbolCount; + const auto symbol = hasSymbol ? symbols + index * 24 : 0; + const auto zeroSectionSymbol = hasSymbol && (bytes[symbol + 4] & 15) == 3 && Io::ReadU64(bytes, symbol + 8) == 0; + const auto matchingDefinedSymbol = hasSymbol && Io::ReadU16(bytes, symbol + 6) != 0 + && addend <= static_cast(std::numeric_limits::max()) + && Io::ReadU64(bytes, symbol + 8) == addend; + if ((index != 0 && !zeroSectionSymbol && !matchingDefinedSymbol) || !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/guest/GuestImageReader.cpp b/core/relinker/relinker/src/guest/GuestImageReader.cpp index 7986b1fd31..aa62dc7163 100644 --- a/core/relinker/relinker/src/guest/GuestImageReader.cpp +++ b/core/relinker/relinker/src/guest/GuestImageReader.cpp @@ -233,7 +233,16 @@ GuestImage GuestImageReader::Read(const std::filesystem::path& path, std::vector if ((next != relocationTargets.end() && next->first < target + 8) || (next != relocationTargets.begin() && std::prev(next)->second > target)) fail("Overlapping guest relocations"); relocationTargets.emplace(target, target + 8); if (relocation == 8) { - if ((info >> 32) != 0) fail("RELATIVE relocation has a symbol"); + if ((info >> 32) != 0) { + const auto& symbol = image.Symbols[info >> 32]; + if (symbol.Section == 0 || addend > static_cast(std::numeric_limits::max()) || symbol.Value != addend) + fail("RELATIVE relocation has a symbol"); + mapped(addend, 0, 0); + Io::AppendU64(output, target); + Io::AppendU64(output, relocation); + Io::AppendU64(output, addend); + continue; + } mapped(addend, 0, 0); } else if (relocation == 1 || relocation == 6 || relocation == 7) { const auto& symbol = image.Symbols[info >> 32]; diff --git a/core/relinker/relinker/tests/test_guest_none_relocations.py b/core/relinker/relinker/tests/test_guest_none_relocations.py index f11f796693..44d55ed646 100644 --- a/core/relinker/relinker/tests/test_guest_none_relocations.py +++ b/core/relinker/relinker/tests/test_guest_none_relocations.py @@ -18,11 +18,13 @@ NOOP = (0, 0, 0) IGNORED = (0xffffffffffffffff, 0xffffffff00000000, -1) OVERLAP = (TARGET + 1, 0xffffffff00000000, -1) RELATIVE = (TARGET, 8, FUNCTION) +RELATIVE_DEFINED = (TARGET, (1 << 32) | 8, FUNCTION) JUMP_SLOT = (TARGET, (1 << 32) | 7, 0) -def module(rela=(), plt=(), sce=False, lifecycle=False, initial=UNCHANGED): - image = module_with_symbol(True) +def module(rela=(), plt=(), sce=False, lifecycle=False, initial=UNCHANGED, exported=True, symbol_info=0x12): + image = module_with_symbol(exported) + image[0x89c] = symbol_info image.extend(bytes(0xE00 - len(image))) struct.pack_into("> 32 else entry + for entry in rela if entry[1] & 0xffffffff] expected_plt = [entry for entry in plt if entry[1] & 0xffffffff] relocated = bool(expected_rela or expected_plt) if system == "linux": @@ -108,7 +112,8 @@ def check_output(data, system, rela, plt, initial): def invalid_cases(sce): - yield "relative-symbol", module([NOOP, (TARGET, (1 << 32) | 8, FUNCTION)], sce=sce), "RELATIVE relocation has a symbol" + yield "relative-symbol-mismatched-value", module([(TARGET, (1 << 32) | 8, FUNCTION + 8)], sce=sce), "RELATIVE relocation has a symbol" + yield "relative-symbol-undefined", module([(TARGET, (1 << 32) | 8, 0)], sce=sce, exported=False), "RELATIVE relocation has a symbol" yield "invalid-symbol", module([NOOP, (TARGET, (99 << 32) | 6, 0)], sce=sce), "Unsupported relocation or symbol index" yield "invalid-target", module([NOOP, (0xffffffffffffffff, 8, FUNCTION)], sce=sce), "Unmapped or inaccessible guest address" yield "overlapping-real", module([NOOP, RELATIVE, RELATIVE], sce=sce), "Overlapping guest relocations" @@ -142,6 +147,8 @@ def main(): return relinker = Path(sys.argv[1]).resolve() cases = (("baseline", [RELATIVE], [], False, UNCHANGED), + ("relative-defined-symbol", [RELATIVE_DEFINED], [], False, UNCHANGED), + ("relative-defined-object", [RELATIVE_DEFINED], [], False, UNCHANGED), ("mixed", [NOOP, OVERLAP, RELATIVE, IGNORED, (TARGET, 0, -1)], [], False, UNCHANGED), ("only-none", [NOOP, IGNORED, OVERLAP], [], False, UNCHANGED), ("plt-mixed", [], [NOOP, OVERLAP, JUMP_SLOT, IGNORED], False, UNCHANGED), @@ -168,7 +175,8 @@ def main(): for sce in (False, True): for name, rela, plt, lifecycle, initial in cases: - image = module(rela, plt, sce, lifecycle, initial) + symbol_info = 0x11 if name == "relative-defined-object" else 0x12 + image = module(rela, plt, sce, lifecycle, initial, symbol_info=symbol_info) for system in ("linux", "windows", "macos"): result, artifact, output = convert(f"{sce}-{name}", system, image) assert result.returncode == 0, (sce, name, system, result.stdout, result.stderr)