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 <noreply@anthropic.com>
This commit is contained in:
parent
7a84b22267
commit
cded9ba8c3
|
|
@ -181,7 +181,8 @@ std::set<Domain::VirtualAddress> 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);
|
||||
|
|
|
|||
|
|
@ -79,7 +79,12 @@ public:
|
|||
const auto slot = read<std::uint64_t>(*rela + offset);
|
||||
const auto info = read<std::uint64_t>(*rela + offset + 8);
|
||||
if (static_cast<std::uint32_t>(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<std::uint8_t>(*symbols + symbol * 24 + 4) & 15) != 3 || read<std::uint64_t>(*symbols + symbol * 24 + 8) != 0)
|
||||
throw RelinkerException("Strict filter: relative relocation has a symbol", slot);
|
||||
}
|
||||
const auto target = read<std::uint64_t>(*rela + offset + 16);
|
||||
if (input.ImportSlots.contains(slot) || !input.Pointers.emplace(slot, target).second)
|
||||
throw RelinkerException("Strict filter: overlapping pointer relocations", slot);
|
||||
|
|
|
|||
|
|
@ -219,6 +219,15 @@ RelinkResult RelinkerPipeline::Relink(const std::vector<std::uint8_t>& sourceElf
|
|||
return relaOff + off;
|
||||
};
|
||||
|
||||
auto isZeroSectionSymbol = [&](const std::uint32_t symIdx) {
|
||||
const FileByteOffset symOff = dynSymTabOffset + static_cast<FileByteOffset>(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<std::uint8_t>& 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<std::uint8_t>& 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<std::uint32_t>(rInfo >> 32);
|
||||
const std::uint32_t relType = static_cast<std::uint32_t>(rInfo & 0xffffffff);
|
||||
if (symIdx == 0 && relType == R_X86_64_RELATIVE)
|
||||
if (relType == R_X86_64_RELATIVE)
|
||||
appendRela(dynSection.RelaData, rOffset, static_cast<std::uint64_t>(R_X86_64_RELATIVE), rAddend);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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<std::uint64_t>(sectionRelative, 208, 0x70);
|
||||
write<std::uint64_t>(sectionRelative, 216, 0x70);
|
||||
write<std::int64_t>(sectionRelative, 0x330, 0x61000039);
|
||||
write<std::uint64_t>(sectionRelative, 0x338, 0x100);
|
||||
write<std::int64_t>(sectionRelative, 0x340, 0x6100003B);
|
||||
write<std::uint64_t>(sectionRelative, 0x348, 24);
|
||||
write<std::int64_t>(sectionRelative, 0x350, 0x6100003F);
|
||||
write<std::uint64_t>(sectionRelative, 0x358, 48);
|
||||
write<std::uint8_t>(sectionRelative, 0x61C, 3);
|
||||
write<std::uint64_t>(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<std::uint64_t>(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<std::uint8_t>(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<std::uint64_t>(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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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("<IBBHQQ", image, 0x638, 0, 3, 0, 0, 0, 0)
|
||||
struct.pack_into("<qQ", image, 0x470, 0x6100003f, 48)
|
||||
struct.pack_into("<QQ", image, 152, 0x90, 0x90)
|
||||
source = work / f"{sce}-section-relative.elf"
|
||||
source.write_bytes(image)
|
||||
for target, options in (("linux", []), ("windows", ["--windows"]), ("macos", ["--macos"])):
|
||||
for level in (0, 1):
|
||||
output = work / f"{sce}-section-relative-{target}-{level}.out"
|
||||
result = subprocess.run([str(relinker), "--skip-sce-module", f"unused-filter={level}", *options,
|
||||
str(source), str(output)], capture_output=True, text=True, timeout=20)
|
||||
assert result.returncode == 0, (sce, target, level, result.stdout, result.stderr)
|
||||
data = output.read_bytes()
|
||||
if target == "linux":
|
||||
tags, _ = dynamic_strings(data)
|
||||
start = file_offset(data, loads(data), tags[7])
|
||||
assert [struct.unpack_from("<QQq", data, offset) for offset in range(start, start + tags[8], 24)] == [relative]
|
||||
if target == "windows" and os.name == "nt":
|
||||
run = subprocess.run([str(output)], capture_output=True, timeout=20)
|
||||
assert run.returncode == 42, (sce, level, run.returncode, run.stderr)
|
||||
for name, symbol in (("notype", (0, 0, 0, 0, 0, 0)), ("valued-section", (0, 3, 0, 0, 0x10, 0))):
|
||||
image = image_with_relocations([noop, (0x300, (1 << 32) | 8, 0x210)], False, False)
|
||||
struct.pack_into("<IBBHQQ", image, 0x638, *symbol)
|
||||
source = work / f"invalid-relative-{name}.elf"
|
||||
source.write_bytes(image)
|
||||
output = work / f"invalid-relative-{name}.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, (name, result.stderr)
|
||||
assert not output.exists()
|
||||
print("No-op executable relocation tests passed")
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue