diff --git a/core/relinker/elfpatcher/src/windows/WindowsTlsBuilder.cpp b/core/relinker/elfpatcher/src/windows/WindowsTlsBuilder.cpp index 34a111d8e..54414f33e 100644 --- a/core/relinker/elfpatcher/src/windows/WindowsTlsBuilder.cpp +++ b/core/relinker/elfpatcher/src/windows/WindowsTlsBuilder.cpp @@ -135,7 +135,8 @@ PeDirectory WindowsTlsBuilder::Build(const std::vector& source, co const bool storeImmediate = supportedPrefixes && !hasOperandSizePrefix && (info.RexPrefix == 0 || info.RexPrefix == 0x40) && info.Length - position == 11 && bytes[position] == 0xc7 && bytes[position + 1] == 0x04 && bytes[position + 2] == 0x25 && Io::ReadU32(source, header.Offset + offset + position + 3) == 0x28; const bool aluRead = supportedPrefixes && (info.RexPrefix == 0x48 || info.RexPrefix == 0x4c) && loadRegister != 4 && info.Length - position == 7 && isAluReadOpcode(bytes[position]) && (bytes[position + 1] & 0xc7) == 0x04 && bytes[position + 2] == 0x25; const bool wide = (info.RexPrefix & 8) != 0; - const auto address = supportedPrefixes && !loadValue && !aluRead && bytes[position] == 0x8b && (wide || !hasOperandSizePrefix) ? registerAddress(bytes, position, info.Length, info.RexPrefix) : std::nullopt; + const bool isAlu = isAluReadOpcode(bytes[position]); + const auto address = supportedPrefixes && !loadValue && !aluRead && (bytes[position] == 0x8b || isAlu) && (wide || !hasOperandSizePrefix) ? registerAddress(bytes, position, info.Length, info.RexPrefix) : std::nullopt; const auto addressRegister = static_cast(((bytes[position + 1] >> 3) & 7) | ((info.RexPrefix & 4) << 1)); if (address && addressRegister != 4) { std::vector moved; @@ -149,7 +150,7 @@ PeDirectory WindowsTlsBuilder::Build(const std::vector& source, co moved.push_back({CheckedRva(rva + length), std::vector(bytes + length, bytes + length + next.Length), next.HasRipRelativeDisp ? std::optional(next.RipRelativeDispOffset) : std::nullopt}); length += next.Length; } - accesses.push_back({rva, header.Offset + offset, length, false, 0, addressRegister, 0, 0, *address, wide, std::move(moved)}); + accesses.push_back({rva, header.Offset + offset, length, false, 0, addressRegister, 0, isAlu ? bytes[position] : std::uint8_t{0}, *address, wide, std::move(moved)}); continue; } if (!loadValue && !storeImmediate && !aluRead) { @@ -221,26 +222,84 @@ PeDirectory WindowsTlsBuilder::Build(const std::vector& source, co patchAccess(sections, access, code.GetRva()); code.Emit({0x48, 0x8d, 0x64, 0x24, 0x80}); if (!access.Address.empty()) { - code.Emit({0x51}); - code.Emit({0x50}); - for (const auto byte : access.Address) - code.Emit({byte}); - code.Emit({0x50}); - loadPointer(); - code.Emit({0x59}); - const auto rex = static_cast((access.Wide ? 0x48 : 0x40) | ((access.Register >> 3) << 2)); - if (rex != 0x40) - code.Emit({rex}); - code.Emit({0x8b, static_cast(0x04 | ((access.Register & 7) << 3)), 0x08}); - if (access.Register == 0) { - code.Emit({0x48, 0x8d, 0x64, 0x24, 0x08}); + if (access.AluOpcode != 0 && access.Register == 0) { + code.Emit({0x51}); + code.Emit({0x52}); + code.Emit({0x50}); + for (const auto byte : access.Address) + code.Emit({byte}); + code.Emit({0x50}); + loadPointer(); code.Emit({0x59}); - } else if (access.Register == 1) { + const auto rex = static_cast((access.Wide ? 0x48 : 0x40) | 0x00); + if (rex != 0x40) + code.Emit({rex}); + code.Emit({0x8b, 0x14, 0x08}); + code.Emit({0x58}); + if (access.Wide) + code.Emit({0x48, access.AluOpcode, 0xc2}); + else + code.Emit({access.AluOpcode, 0xc2}); + code.Emit({0x5a}); + code.Emit({0x59}); + } else if (access.AluOpcode != 0 && access.Register == 1) { + code.Emit({0x51}); + code.Emit({0x50}); + for (const auto byte : access.Address) + code.Emit({byte}); + code.Emit({0x50}); + loadPointer(); + code.Emit({0x59}); + const auto rex = static_cast((access.Wide ? 0x48 : 0x40) | 0x00); + if (rex != 0x40) + code.Emit({rex}); + code.Emit({0x8b, 0x04, 0x08}); + if (access.Wide) + code.Emit({0x48, 0x8b, 0x4c, 0x24, 0x08, 0x48, access.AluOpcode, 0xc8}); + else + code.Emit({0x48, 0x8b, 0x4c, 0x24, 0x08, access.AluOpcode, 0xc8}); code.Emit({0x58}); code.Emit({0x48, 0x8d, 0x64, 0x24, 0x08}); + } else if (access.AluOpcode != 0) { + code.Emit({0x51}); + code.Emit({0x50}); + for (const auto byte : access.Address) + code.Emit({byte}); + code.Emit({0x50}); + loadPointer(); + code.Emit({0x59}); + const auto rexLoad = static_cast((access.Wide ? 0x48 : 0x40) | 0x00); + if (rexLoad != 0x40) + code.Emit({rexLoad}); + code.Emit({0x8b, 0x04, 0x08}); + const auto rexAlu = static_cast((access.Wide ? 0x48 : 0x40) | ((access.Register >> 3) << 2)); + if (rexAlu != 0x40) + code.Emit({rexAlu}); + code.Emit({access.AluOpcode, static_cast(0xc0 | ((access.Register & 7) << 3))}); + code.Emit({0x58}); + code.Emit({0x59}); } else { - code.Emit({0x58}); + code.Emit({0x51}); + code.Emit({0x50}); + for (const auto byte : access.Address) + code.Emit({byte}); + code.Emit({0x50}); + loadPointer(); code.Emit({0x59}); + const auto rex = static_cast((access.Wide ? 0x48 : 0x40) | ((access.Register >> 3) << 2)); + if (rex != 0x40) + code.Emit({rex}); + code.Emit({0x8b, static_cast(0x04 | ((access.Register & 7) << 3)), 0x08}); + if (access.Register == 0) { + code.Emit({0x48, 0x8d, 0x64, 0x24, 0x08}); + code.Emit({0x59}); + } else if (access.Register == 1) { + code.Emit({0x58}); + code.Emit({0x48, 0x8d, 0x64, 0x24, 0x08}); + } else { + code.Emit({0x58}); + code.Emit({0x59}); + } } } else if (access.AluOpcode != 0 && access.Register == 0) { code.Emit({0x51}); diff --git a/core/relinker/relinker/tests/test_tls_function_coverage.py b/core/relinker/relinker/tests/test_tls_function_coverage.py index c675d2aa3..48625d298 100644 --- a/core/relinker/relinker/tests/test_tls_function_coverage.py +++ b/core/relinker/relinker/tests/test_tls_function_coverage.py @@ -234,6 +234,18 @@ REGISTER_LOAD_FOLLOWERS = ( ("rax-rbx-rip-follower", bytes.fromhex("64 48 8b 03"), 0, True, {3: -8}, -8, b"", 0x0fedcba987654321), ("edx-rbx-nop-follower", bytes.fromhex("64 8b 13"), 2, False, {3: 0x10}, 0x10, b"\x90", None), ) +REGISTER_ALUS = ( + ("xor-rax-rbx", bytes.fromhex("64 48 33 03"), 0x33, 0, True, {3: -8}, -8), + ("xor-rcx-rax", bytes.fromhex("64 48 33 08"), 0x33, 1, True, {0: 0x10}, 0x10), + ("xor-rbx-rax", bytes.fromhex("64 48 33 18"), 0x33, 3, True, {0: 0x28}, 0x28), + ("add-rax-rbx", bytes.fromhex("64 48 03 03"), 0x03, 0, True, {3: -8}, -8), + ("cmp-rax-rbx", bytes.fromhex("64 48 3b 03"), 0x3b, 0, True, {3: -8}, -8), + ("xor-r13-rbx-rcx-disp8", bytes.fromhex("64 4c 33 6c 0b f0"), 0x33, 13, True, {3: 0x18, 1: -0x10}, -8), + ("xor-edx-rbx", bytes.fromhex("64 33 13"), 0x33, 2, False, {3: 0x10}, 0x10), + ("cmp-ecx-rax", bytes.fromhex("64 3b 08"), 0x3b, 1, False, {0: 0x10}, 0x10), + ("adc-rax-rbx", bytes.fromhex("64 48 13 03"), 0x13, 0, True, {3: -8}, -8), + ("sbb-rcx-rax", bytes.fromhex("64 48 1b 08"), 0x1b, 1, True, {0: 0x10}, 0x10), +) def register_load_body(instruction, register, wide, address, offset, flags, follower=b"", rip_constant=None): @@ -323,6 +335,114 @@ def register_load_stub(instruction, register, wide): return bytes.fromhex("48 8d 64 24 80 51 50") + address + b"\x50", b"\x59" + move + restore + bytes.fromhex("48 8d a4 24 80 00 00 00") +def register_alu_body(instruction, opcode, register, wide, address, offset, flags, follower=b"\x90"): + saved = (3, 5, 6, 7, 12, 13, 14, 15) + mask = (1 << 64) - 1 + code = bytearray() + failures = [] + + def emit(data): + code.extend(data) + + def immediate(target, value): + emit(bytes([0x48 | (target >> 3), 0xb8 | (target & 7)]) + struct.pack("= 8 else b"") + bytes([0x50 | (target & 7)])) + emit(bytes.fromhex("48 8d a4 24 00 ff ff ff")) + emit(fs_load(0, 0)) + for displacement, value in REGISTER_VALUES.items(): + immediate(1, value) + emit(bytes([0x48, 0x89, 0x48, displacement & 255])) + markers = [0x1020304050607000 + target * 0x101 for target in range(16)] + expected = list(markers) + for target in range(16): + if target != 4: + immediate(target, markers[target]) + for target, value in address.items(): + immediate(target, value) + expected[target] = value & mask + value = REGISTER_VALUES[offset] + base_val = expected[register] if wide else expected[register] & 0xffffffff + operand_val = value if wide else value & 0xffffffff + if opcode == 0x33: + res = base_val ^ operand_val + elif opcode == 0x23: + res = base_val & operand_val + elif opcode == 0x0b: + res = base_val | operand_val + elif opcode == 0x03: + res = (base_val + operand_val) & (mask if wide else 0xffffffff) + elif opcode == 0x2b: + res = (base_val - operand_val) & (mask if wide else 0xffffffff) + elif opcode == 0x3b: + res = base_val + elif opcode == 0x13: + res = (base_val + operand_val + (flags & 1)) & (mask if wide else 0xffffffff) + elif opcode == 0x1b: + res = (base_val - operand_val - (flags & 1)) & (mask if wide else 0xffffffff) + else: + raise ValueError(f"Unknown opcode: {opcode}") + if opcode != 0x3b: + expected[register] = res if wide else res & 0xffffffff + emit(b"\x68" + struct.pack("> 3) << 2, 0x89, 0x84 | (target & 7) << 3, 0x24]) + struct.pack("= 8 else b"") + bytes([0x58 | (target & 7)])) + emit(b"\xc3") + for position in failures: + struct.pack_into("> 3) << 2]) if wide or register >= 8 else b"") + alu = rex_alu + bytes([opcode, 0xc0 | (register & 7) << 3]) + return bytes.fromhex("48 8d 64 24 80 51 50") + address + b"\x50", b"\x59" + load + alu + bytes.fromhex("58 59 48 8d a4 24 80 00 00 00") + + + def make_image(transfer, metadata, extent=None, body=None): image = fixture() image.extend(b"\x90" * 0x1000) @@ -534,10 +654,18 @@ def main(): stub = pe_bytes_at(pe, 0x10000 + address + 5 + struct.unpack_from("