Merge pull request #1117 from TideOPL/fix/relinker-tls-unreached-code

fix(relinker): find Windows guest TLS accesses in code no root reaches
This commit is contained in:
Adrià 2026-10-08 09:54:40 +02:00 committed by GitHub
commit f51eeff059
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 60 additions and 0 deletions

View File

@ -209,6 +209,44 @@ std::set<Domain::VirtualAddress> CodeInstructionCollector::Collect(const std::ve
address += info.Length;
}
}
for (const auto& header : headers) {
if (header.Type != 1 || (header.Flags & 1) == 0 || header.FileSize < 16) continue;
range(header.Offset, header.FileSize);
const auto* text = bytes.data() + header.Offset;
const auto size = static_cast<std::size_t>(header.FileSize);
const auto paddedStart = [&](std::size_t address) { return address % 16 == 0 && address >= 2 && text[address - 1] == 0xcc && text[address - 2] == 0xcc; };
const auto nextPaddedStart = [&](std::size_t address) {
address = (address + 16) & ~std::size_t{15};
while (address < size && !paddedStart(address)) address += 16;
return address;
};
for (std::size_t position = nextPaddedStart(0); position < size;) {
if (position + 1 < size && text[position] == 0 && text[position + 1] == 0) {
position = nextPaddedStart(position);
continue;
}
Codegen::DecodedInstructionInfo info;
try {
info = decoder.DecodeInstruction(text + position, size - position);
} catch (const Codegen::CodegenException&) {
position = nextPaddedStart(position);
continue;
}
if (info.Length == 0 || info.Length > size - position) {
position = nextPaddedStart(position);
continue;
}
const auto next = position + info.Length;
const auto crossed = (position & ~std::size_t{15}) + 16;
if (crossed < next && paddedStart(crossed)) {
position = crossed;
continue;
}
const auto opcode = position + info.OpcodeOffset;
if (info.SegmentPrefix == 0x64 && opcode + 2 < next && (text[opcode + 1] & 0xc7) == 0x04 && text[opcode + 2] == 0x25) addRoot(header.MappedAddress + position);
position = next;
}
}
for (const auto target : staticTargets) {
for (const auto& [skipBegin, skipEnd] : skipped) {
if (skipBegin <= target && target < skipEnd)

View File

@ -346,6 +346,28 @@ def main():
external[0x1300:0x1310] = external[0x1240:0x1250]
external[0x1240:0x1250] = b"\xe8" + struct.pack("<i", 0x1300 - 0x1245) + b"\xc3" + b"\x90" * 10
convert("direct-call-from-indirect-block", external, tls_address=0x1300)
def convert_padded(name, image):
source = work / (name + ".elf")
output = source.with_suffix(".exe")
source.write_bytes(image)
result = subprocess.run([str(relinker), "--skip-sce-module", "--windows", str(source), str(output)],
capture_output=True, text=True, timeout=30)
assert result.returncode == 0, (name, result.stdout, result.stderr)
return output.read_bytes()
unreferenced = make_image("register", "unwind")
unreferenced[0x18f0:0x1900] = b"\xcc" * 16
unreferenced[0x1900:0x1900 + len(TLS_LOAD) + 4] = TLS_LOAD + bytes.fromhex("8b 40 f0 c3")
pe = convert_padded("unreferenced-padded-function", unreferenced)
assert pe_bytes_at(pe, 0x11900, 1)[0] == 0xe9, "unreferenced-padded-function"
assert pe_bytes_at(pe, 0x11850, len(TLS_LOAD)) == TLS_LOAD, "unreferenced-padded-function"
zero_filled = make_image("register", "unwind")
zero_filled[0x18f0:0x1900] = b"\xcc" * 16
zero_filled[0x1900:0x1910] = bytes(16)
zero_filled[0x1910:0x1910 + len(TLS_LOAD)] = TLS_LOAD
pe = convert_padded("zero-filled-after-padding", zero_filled)
assert pe_bytes_at(pe, 0x11910, len(TLS_LOAD)) == TLS_LOAD, "zero-filled-after-padding"
for register in range(16):
offset, body = register_load(register)
image = make_image("register", "unwind", body=body)