Merge pull request #3543 from Lorkydey/fix/relinker-none-relocations

fix(relinker): ignore no-op executable relocations
This commit is contained in:
Adrià 2026-10-11 23:32:26 +02:00 committed by GitHub
commit 2ebe73da53
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 98 additions and 0 deletions

View File

@ -245,6 +245,8 @@ if(BUILD_TESTING)
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_dynamic_segment.py $<TARGET_FILE:relinker>)
add_test(NAME rela_size COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_rela_size.py $<TARGET_FILE:relinker>)
add_test(NAME none_relocations COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_none_relocations.py $<TARGET_FILE:relinker>)
add_test(NAME optional_plt COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_optional_plt.py $<TARGET_FILE:relinker>)
add_test(NAME empty_tls COMMAND ${Python3_EXECUTABLE}

View File

@ -231,6 +231,9 @@ RelinkResult RelinkerPipeline::Relink(const std::vector<std::uint8_t>& sourceElf
const std::uint32_t symIdx = static_cast<std::uint32_t>(rInfo >> 32);
const std::uint32_t relType = static_cast<std::uint32_t>(rInfo & 0xffffffff);
if (relType == R_X86_64_NONE)
continue;
if (relType == 8) {
if (symIdx != 0)
throw RelinkerException("RELATIVE relocation has a nonzero symbol index", pos);

View File

@ -0,0 +1,93 @@
import json
import os
from pathlib import Path
import struct
import subprocess
import sys
import tempfile
from test_dynamic_segment import fixture
from test_linux_dynamic_strings import dynamic_strings, file_offset
from test_linux_load_alignment import loads
def image_with_relocations(relocations, sce, imported):
image = fixture()
struct.pack_into("<Q", image, 0x458, len(relocations) * 24)
for index, relocation in enumerate(relocations):
struct.pack_into("<QQq", image, 0x700 + index * 24, *relocation)
if not any(info & 0xffffffff == 8 for _, info, _ in relocations):
image[0x200:0x206] = bytes.fromhex("b82a000000c3")
if imported:
strings = b"\0symbol\0libc.prx\0"
image[0x600:0x600 + len(strings)] = strings
struct.pack_into("<Q", image, 0x418, len(strings))
struct.pack_into("<IBBHQQ", image, 0x638, 1, 0x12, 0, 0, 0, 0)
image[0x200:0x20d] = bytes.fromhex("488b0501010000ff25f3000000")
struct.pack_into("<qQ", image, 0x470, 0x6100003f, 48)
struct.pack_into("<qQ", image, 0x480, 1, 8)
struct.pack_into("<QQ", image, 152, 0xa0, 0xa0)
if sce:
struct.pack_into("<IIQQQQQQ", image, 176, 0x61000000, 0, 0x500, 0, 0, 0xb00, 0xb00, 1)
variants = {5: 0x61000035, 10: 0x61000037, 6: 0x61000039, 11: 0x6100003b,
7: 0x6100002f, 8: 0x61000031, 9: 0x61000033}
for offset in range(0x400, 0x470, 16):
tag, value = struct.unpack_from("<qQ", image, offset)
struct.pack_into("<qQ", image, offset, variants[tag], value - 0x500 if tag in (5, 6, 7) else value)
return image
def main():
relinker = Path(sys.argv[1]).resolve()
relative = (0x300, 8, 0x210)
noop = (0, 0, 0)
ignored_fields = (0xffffffffffffffff, 0xffffffff00000000, -1)
imported = (0x308, (1 << 32) | 6, 0)
cases = (("baseline", [relative]), ("before", [noop, relative]), ("after", [relative, noop]),
("ignored-fields", [ignored_fields, relative, noop]),
("only-none", [noop, ignored_fields]),
("with-import", [noop, imported, ignored_fields, relative]))
with tempfile.TemporaryDirectory(prefix="anyps5-none-relocations-") as directory:
work = Path(directory)
for sce in (False, True):
for name, relocations in cases:
has_import = imported in relocations
image = image_with_relocations(relocations, sce, has_import)
source = work / f"{sce}-{name}.elf"
source.write_bytes(image)
for target, options, magic in (("linux", [], b"\x7fELF"), ("windows", ["--windows"], b"MZ"),
("macos", ["--macos"], b"\xcf\xfa\xed\xfe")):
for level in (0, 1):
output = work / f"{sce}-{name}-{target}-{level}.out"
result = subprocess.run([str(relinker), "--skip-sce-module", "--registry",
f"unused-filter={level}", *options, str(source), str(output)],
capture_output=True, text=True, timeout=20)
assert result.returncode == 0, (sce, name, target, level, result.stdout, result.stderr)
data = output.read_bytes()
assert data.startswith(magic), (name, target)
registry = json.loads(output.with_suffix(".registry.json").read_text(encoding="utf-8"))
assert [(entry["nid"], entry["relocationType"], entry["targetOffset"]) for entry in registry] == (
[("symbol", "R_X86_64_GLOB_DAT", "0x308")] if has_import else [])
assert source.read_bytes() == image
if target == "linux":
tags, _ = dynamic_strings(data)
actual = []
if tags.get(8, 0):
start = file_offset(data, loads(data), tags[7])
actual = [struct.unpack_from("<QQq", data, offset) for offset in range(start, start + tags[8], 24)]
assert actual == [relocation for relocation in relocations if relocation in (imported, relative)]
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()
print("No-op executable relocation tests passed")
if __name__ == "__main__":
main()