From 570c3297d334c4f4a9070da5a442d2a21ef9e9c9 Mon Sep 17 00:00:00 2001 From: aasishdairel Date: Mon, 19 Oct 2026 23:34:36 +0530 Subject: [PATCH 1/5] fix(relinker): trap only GS memory accesses in Windows and macOS output --- .../codegen/include/codegen/CodegenTypes.hpp | 1 + .../codegen/src/x86/X64InstructionDecoder.cpp | 8 ++++++++ core/relinker/codegen/tests/DecoderLengthTests.cpp | 13 +++++++++++++ core/relinker/elfpatcher/src/macos/MacOsImage.cpp | 3 ++- .../elfpatcher/src/windows/WindowsTlsBuilder.cpp | 3 ++- .../relinker/relinker/tests/test_macos_gs_traps.py | 14 ++++++++++++++ .../relinker/tests/test_tls_function_coverage.py | 13 +++++++++++++ docs/dev/debt/relinker.md | 2 +- 8 files changed, 54 insertions(+), 3 deletions(-) diff --git a/core/relinker/codegen/include/codegen/CodegenTypes.hpp b/core/relinker/codegen/include/codegen/CodegenTypes.hpp index 72bace4267..021f2bf059 100644 --- a/core/relinker/codegen/include/codegen/CodegenTypes.hpp +++ b/core/relinker/codegen/include/codegen/CodegenTypes.hpp @@ -68,6 +68,7 @@ struct DecodedInstructionInfo { bool AddressSize32; std::size_t RipRelativeDispOffset; bool HasModRm; + bool HasMemoryOperand; std::uint8_t ModRmByte; bool HasBranchTarget; std::int64_t BranchDisp; diff --git a/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp b/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp index c6cc9de4ac..5c93d00390 100644 --- a/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp +++ b/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp @@ -393,6 +393,8 @@ DecodedInstructionInfo X64InstructionDecoder::DecodeInstruction( info.ModRmRegField = (modrm >> ModRmRegShift) & ModRmRegMask; const std::uint8_t mod = registerOnly ? ModRmModRegister : (modrm >> ModRmModShift) & ModRmModMask; const std::uint8_t rm = modrm & ModRmRmMask; + if (mod != ModRmModRegister) + info.HasMemoryOperand = true; if (mod == ModRmModIndirect && rm == ModRmRmRipRelative) { info.HasRipRelativeDisp = true; info.RipRelativeDispOffset = modrmPos + 1; @@ -480,6 +482,8 @@ DecodedInstructionInfo X64InstructionDecoder::DecodeInstruction( info.ModRmRegField = reg; std::uint8_t mod = (modrm >> ModRmModShift) & ModRmModMask; std::uint8_t rm = modrm & ModRmRmMask; + if (mod != ModRmModRegister) + info.HasMemoryOperand = true; if (reg == 2) { if (mod == ModRmModIndirect && rm == ModRmRmRipRelative) { info.FlowKind = ControlFlowKind::Call; @@ -592,6 +596,10 @@ DecodedInstructionInfo X64InstructionDecoder::DecodeInstruction( if (hasModRm && pos < info.Length) readModRm(pos, twoByteOpcode && op >= TwoByteMovCrDrMin && op <= TwoByteMovCrDrMax); + if (!twoByteOpcode && ((op >= 0x6c && op <= 0x6f) || (op >= 0xa0 && op <= 0xa7) || + (op >= 0xaa && op <= 0xaf) || op == 0xd7)) + info.HasMemoryOperand = true; + info.FlowKind = ControlFlowKind::Sequential; return info; } diff --git a/core/relinker/codegen/tests/DecoderLengthTests.cpp b/core/relinker/codegen/tests/DecoderLengthTests.cpp index 63bca4bfe0..e7c7340696 100644 --- a/core/relinker/codegen/tests/DecoderLengthTests.cpp +++ b/core/relinker/codegen/tests/DecoderLengthTests.cpp @@ -117,6 +117,19 @@ int main() { const auto threeDNowInfo = decoder.DecodeInstruction(threeDNow.data(), threeDNow.size()); require(threeDNowInfo.HasModRm && threeDNowInfo.ModRmByte == 0x0d, "3DNow! ModRM was not reported"); require(threeDNowInfo.HasRipRelativeDisp && threeDNowInfo.RipRelativeDispOffset == 3, "3DNow! RIP-relative displacement was not reported"); + for (const auto& testCase : std::vector>{ + {"6590", false}, + {"654889c8", false}, + {"65488b0c2578030000", true}, + {"65a4", true}, + {"650fa2", false}, + {"ff10", true}, + {"ffe0", false}, + }) { + const auto bytes = parse(testCase.first); + require(decoder.DecodeInstruction(bytes.data(), bytes.size()).HasMemoryOperand == testCase.second, + "Wrong memory operand presence for " + std::string(testCase.first)); + } std::cout << "Decoder length tests passed\n"; } catch (const std::exception& error) { std::cerr << error.what() << '\n'; diff --git a/core/relinker/elfpatcher/src/macos/MacOsImage.cpp b/core/relinker/elfpatcher/src/macos/MacOsImage.cpp index 91a7665452..6af3a39fb6 100644 --- a/core/relinker/elfpatcher/src/macos/MacOsImage.cpp +++ b/core/relinker/elfpatcher/src/macos/MacOsImage.cpp @@ -245,7 +245,8 @@ TlsScan ScanTls(const std::vector& source, const std::vector& source, co } if (info.SegmentPrefix == 0x65 && !insidePrevious) { - gsTraps.emplace_back(rva, info.Length); + if (info.HasMemoryOperand) + gsTraps.emplace_back(rva, info.Length); continue; } if (info.SegmentPrefix != 0 && !(insidePrevious && info.SegmentPrefix != 0x64)) { diff --git a/core/relinker/relinker/tests/test_macos_gs_traps.py b/core/relinker/relinker/tests/test_macos_gs_traps.py index 8b9c21512d..9f67360693 100644 --- a/core/relinker/relinker/tests/test_macos_gs_traps.py +++ b/core/relinker/relinker/tests/test_macos_gs_traps.py @@ -17,6 +17,12 @@ FORMS = { "gs-short-register-address": bytes.fromhex("65 48 8b 00"), } +NON_MEMORY_FORMS = { + "gs-nop": bytes.fromhex("65 90"), + "gs-register-move": bytes.fromhex("65 48 89 c8"), + "gs-cpuid": bytes.fromhex("65 0f a2"), +} + def convert(relinker, directory, name, image): source = Path(directory) / f"{name}.elf" @@ -38,12 +44,20 @@ def check_trapped(name, code, instruction): raise SystemExit(f"{name}: the GS access was left in the code") +def check_untouched(name, code, instruction): + if instruction + b"\xc3" not in code: + raise SystemExit(f"{name}: a non-memory GS instruction was replaced") + + def main(): relinker = Path(sys.argv[1]).resolve() with tempfile.TemporaryDirectory(prefix="anyps5-macos-gs-") as directory: for name, instruction in FORMS.items(): image = make_image("register", "unwind", body=instruction + b"\xc3") check_trapped(name, convert(relinker, directory, name, image), instruction) + for name, instruction in NON_MEMORY_FORMS.items(): + image = make_image("register", "unwind", body=instruction + b"\xc3") + check_untouched(name, convert(relinker, directory, name, image), instruction) instruction = FORMS["gs-load-rcx"] image = make_image("register", "unwind", body=instruction + b"\xc3") struct.pack_into(" Date: Tue, 20 Oct 2026 10:13:29 +0530 Subject: [PATCH 2/5] fix(relinker): trap GS overrides on implicit memory operands MASKMOVQ, MASKMOVDQU, VMASKMOVDQU, MONITOR and MONITORX use a register-form ModRM byte but still access memory at DS:rDI or DS:rAX. Mark them as memory operands and cover the encodings in the Windows and macOS GS trap tests. --- .../codegen/x86/X64OpcodeConstants.hpp | 1 + .../codegen/src/x86/X64InstructionDecoder.cpp | 19 +++++++++++++++++-- .../codegen/tests/DecoderLengthTests.cpp | 7 +++++++ .../relinker/tests/test_macos_gs_traps.py | 6 ++++++ .../tests/test_tls_function_coverage.py | 6 ++++++ 5 files changed, 37 insertions(+), 2 deletions(-) diff --git a/core/relinker/codegen/include/codegen/x86/X64OpcodeConstants.hpp b/core/relinker/codegen/include/codegen/x86/X64OpcodeConstants.hpp index 7a9a14a364..c6db778a9e 100644 --- a/core/relinker/codegen/include/codegen/x86/X64OpcodeConstants.hpp +++ b/core/relinker/codegen/include/codegen/x86/X64OpcodeConstants.hpp @@ -146,6 +146,7 @@ inline constexpr std::uint8_t TwoByteMovdMovq = 0x7E; inline constexpr std::uint8_t TwoByteMovqMmxXmm = 0x6F; inline constexpr std::uint8_t TwoByteMovdqu = 0x7F; inline constexpr std::uint8_t TwoByteGrp7 = 0x01; +inline constexpr std::uint8_t TwoByteMaskmov = 0xF7; inline constexpr std::uint8_t TwoByteShiftImm8Min = 0x70; inline constexpr std::uint8_t TwoByteShiftImm8Max = 0x73; inline constexpr std::uint8_t ThreeByteEscape38 = 0x38; diff --git a/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp b/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp index 5c93d00390..25c18b4f46 100644 --- a/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp +++ b/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp @@ -406,9 +406,17 @@ DecodedInstructionInfo X64InstructionDecoder::DecodeInstruction( const bool xop = op == XopPrefix && pos < info.Length && (data[pos] & Vex3MapMask) >= XopMapMin; if (op == OneByteVex2 || op == OneByteVex3 || op == EvexPrefix || xop) { - pos += (op == OneByteVex2 ? 1 : (op == OneByteVex3 || xop ? 2 : EvexPrefixLength - 1)) + 1; - if (pos < info.Length) + const bool vex = op == OneByteVex2 || op == OneByteVex3; + const std::size_t vectorOpcodeOffset = pos + (op == OneByteVex2 ? 1 : (op == OneByteVex3 || xop ? 2 : EvexPrefixLength - 1)); + const std::uint8_t vexMap = op == OneByteVex2 ? 1 : (data[pos] & (op == EvexPrefix ? EvexMapMask : Vex3MapMask)); + const std::uint8_t vectorOpcode = vectorOpcodeOffset < info.Length ? data[vectorOpcodeOffset] : 0; + pos = vectorOpcodeOffset + 1; + if (pos < info.Length) { readModRm(pos, false); + if (vex && vexMap == 1 && vectorOpcode == TwoByteMaskmov && + ((info.ModRmByte >> ModRmModShift) & ModRmModMask) == ModRmModRegister) + info.HasMemoryOperand = true; + } info.FlowKind = ControlFlowKind::Sequential; return info; } @@ -596,6 +604,13 @@ DecodedInstructionInfo X64InstructionDecoder::DecodeInstruction( if (hasModRm && pos < info.Length) readModRm(pos, twoByteOpcode && op >= TwoByteMovCrDrMin && op <= TwoByteMovCrDrMax); + if (info.HasModRm && twoByteOpcode) { + if (op == TwoByteMaskmov && ((info.ModRmByte >> ModRmModShift) & ModRmModMask) == ModRmModRegister) + info.HasMemoryOperand = true; + else if (op == TwoByteGrp7 && (info.ModRmByte == 0xC8 || info.ModRmByte == 0xFA)) + info.HasMemoryOperand = true; + } + if (!twoByteOpcode && ((op >= 0x6c && op <= 0x6f) || (op >= 0xa0 && op <= 0xa7) || (op >= 0xaa && op <= 0xaf) || op == 0xd7)) info.HasMemoryOperand = true; diff --git a/core/relinker/codegen/tests/DecoderLengthTests.cpp b/core/relinker/codegen/tests/DecoderLengthTests.cpp index e7c7340696..433468fa56 100644 --- a/core/relinker/codegen/tests/DecoderLengthTests.cpp +++ b/core/relinker/codegen/tests/DecoderLengthTests.cpp @@ -123,6 +123,13 @@ int main() { {"65488b0c2578030000", true}, {"65a4", true}, {"650fa2", false}, + {"650ff7c1", true}, + {"65660ff7c1", true}, + {"65c5f9f7c1", true}, + {"65c4e179f7c1", true}, + {"650f01c8", true}, + {"650f01fa", true}, + {"650f01c9", false}, {"ff10", true}, {"ffe0", false}, }) { diff --git a/core/relinker/relinker/tests/test_macos_gs_traps.py b/core/relinker/relinker/tests/test_macos_gs_traps.py index 9f67360693..2cc7d050ed 100644 --- a/core/relinker/relinker/tests/test_macos_gs_traps.py +++ b/core/relinker/relinker/tests/test_macos_gs_traps.py @@ -15,12 +15,18 @@ FORMS = { "gs-store": bytes.fromhex("65 48 89 04 25 30 00 00 00"), "gs-alu": bytes.fromhex("65 48 33 04 25 28 00 00 00"), "gs-short-register-address": bytes.fromhex("65 48 8b 00"), + "gs-maskmovdqu": bytes.fromhex("65 66 0f f7 c1"), + "gs-maskmovq": bytes.fromhex("65 0f f7 c1"), + "gs-vmaskmovdqu": bytes.fromhex("65 c5 f9 f7 c1"), + "gs-monitor": bytes.fromhex("65 0f 01 c8"), + "gs-monitorx": bytes.fromhex("65 0f 01 fa"), } NON_MEMORY_FORMS = { "gs-nop": bytes.fromhex("65 90"), "gs-register-move": bytes.fromhex("65 48 89 c8"), "gs-cpuid": bytes.fromhex("65 0f a2"), + "gs-mwait": bytes.fromhex("65 0f 01 c9"), } diff --git a/core/relinker/relinker/tests/test_tls_function_coverage.py b/core/relinker/relinker/tests/test_tls_function_coverage.py index 7786496c9e..9d59c63b5d 100644 --- a/core/relinker/relinker/tests/test_tls_function_coverage.py +++ b/core/relinker/relinker/tests/test_tls_function_coverage.py @@ -783,6 +783,11 @@ def main(): "gs-store": bytes.fromhex("65 48 89 04 25 30 00 00 00"), "gs-alu": bytes.fromhex("65 48 33 04 25 28 00 00 00"), "gs-short-register-address": bytes.fromhex("65 48 8b 00"), + "gs-maskmovdqu": bytes.fromhex("65 66 0f f7 c1"), + "gs-maskmovq": bytes.fromhex("65 0f f7 c1"), + "gs-vmaskmovdqu": bytes.fromhex("65 c5 f9 f7 c1"), + "gs-monitor": bytes.fromhex("65 0f 01 c8"), + "gs-monitorx": bytes.fromhex("65 0f 01 fa"), }.items(): source = work / (name + ".elf") output = source.with_suffix(".exe") @@ -796,6 +801,7 @@ def main(): "gs-nop": bytes.fromhex("65 90"), "gs-register-move": bytes.fromhex("65 48 89 c8"), "gs-cpuid": bytes.fromhex("65 0f a2"), + "gs-mwait": bytes.fromhex("65 0f 01 c9"), }.items(): source = work / (name + ".elf") output = source.with_suffix(".exe") From a58d560c9bf505e14a86c71061f3d834b62d7708 Mon Sep 17 00:00:00 2001 From: aasishdairel Date: Tue, 20 Oct 2026 10:19:04 +0530 Subject: [PATCH 3/5] fix(relinker): trap UMONITOR GS overrides UMONITOR (F3 0F AE /6) takes its effective address from the register operand and uses DS with segment overrides, so mark its register-form encoding as a memory operand. UMWAIT and TPAUSE use the same opcode with F2/66 and have no memory operand. --- core/relinker/codegen/src/x86/X64InstructionDecoder.cpp | 9 ++++++++- core/relinker/codegen/tests/DecoderLengthTests.cpp | 4 ++++ core/relinker/relinker/tests/test_macos_gs_traps.py | 2 ++ .../relinker/tests/test_tls_function_coverage.py | 2 ++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp b/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp index 25c18b4f46..7082938042 100644 --- a/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp +++ b/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp @@ -356,6 +356,7 @@ DecodedInstructionInfo X64InstructionDecoder::DecodeInstruction( std::size_t pos = 0; bool operandSizeOverride = false; + bool repPrefix = false; while (pos < info.Length) { std::uint8_t b = data[pos]; @@ -374,6 +375,8 @@ DecodedInstructionInfo X64InstructionDecoder::DecodeInstruction( info.AddressSize32 = true; if (b == PrefixOperandSize) operandSizeOverride = true; + if (b == PrefixRep) + repPrefix = true; info.RexPrefix = 0; ++pos; continue; @@ -605,10 +608,14 @@ DecodedInstructionInfo X64InstructionDecoder::DecodeInstruction( readModRm(pos, twoByteOpcode && op >= TwoByteMovCrDrMin && op <= TwoByteMovCrDrMax); if (info.HasModRm && twoByteOpcode) { - if (op == TwoByteMaskmov && ((info.ModRmByte >> ModRmModShift) & ModRmModMask) == ModRmModRegister) + const std::uint8_t mod = (info.ModRmByte >> ModRmModShift) & ModRmModMask; + const std::uint8_t reg = (info.ModRmByte >> ModRmRegShift) & ModRmRegMask; + if (op == TwoByteMaskmov && mod == ModRmModRegister) info.HasMemoryOperand = true; else if (op == TwoByteGrp7 && (info.ModRmByte == 0xC8 || info.ModRmByte == 0xFA)) info.HasMemoryOperand = true; + else if (op == TwoByteGrp15 && repPrefix && reg == 6 && mod == ModRmModRegister) + info.HasMemoryOperand = true; } if (!twoByteOpcode && ((op >= 0x6c && op <= 0x6f) || (op >= 0xa0 && op <= 0xa7) || diff --git a/core/relinker/codegen/tests/DecoderLengthTests.cpp b/core/relinker/codegen/tests/DecoderLengthTests.cpp index 433468fa56..697c200fa3 100644 --- a/core/relinker/codegen/tests/DecoderLengthTests.cpp +++ b/core/relinker/codegen/tests/DecoderLengthTests.cpp @@ -130,6 +130,10 @@ int main() { {"650f01c8", true}, {"650f01fa", true}, {"650f01c9", false}, + {"65f30faef0", true}, + {"f20faef0", false}, + {"660faef0", false}, + {"f30faee8", false}, {"ff10", true}, {"ffe0", false}, }) { diff --git a/core/relinker/relinker/tests/test_macos_gs_traps.py b/core/relinker/relinker/tests/test_macos_gs_traps.py index 2cc7d050ed..368c0a60c8 100644 --- a/core/relinker/relinker/tests/test_macos_gs_traps.py +++ b/core/relinker/relinker/tests/test_macos_gs_traps.py @@ -20,6 +20,7 @@ FORMS = { "gs-vmaskmovdqu": bytes.fromhex("65 c5 f9 f7 c1"), "gs-monitor": bytes.fromhex("65 0f 01 c8"), "gs-monitorx": bytes.fromhex("65 0f 01 fa"), + "gs-umonitor": bytes.fromhex("65 f3 0f ae f0"), } NON_MEMORY_FORMS = { @@ -27,6 +28,7 @@ NON_MEMORY_FORMS = { "gs-register-move": bytes.fromhex("65 48 89 c8"), "gs-cpuid": bytes.fromhex("65 0f a2"), "gs-mwait": bytes.fromhex("65 0f 01 c9"), + "gs-umwait": bytes.fromhex("65 f2 0f ae f0"), } diff --git a/core/relinker/relinker/tests/test_tls_function_coverage.py b/core/relinker/relinker/tests/test_tls_function_coverage.py index 9d59c63b5d..99fec2aae4 100644 --- a/core/relinker/relinker/tests/test_tls_function_coverage.py +++ b/core/relinker/relinker/tests/test_tls_function_coverage.py @@ -788,6 +788,7 @@ def main(): "gs-vmaskmovdqu": bytes.fromhex("65 c5 f9 f7 c1"), "gs-monitor": bytes.fromhex("65 0f 01 c8"), "gs-monitorx": bytes.fromhex("65 0f 01 fa"), + "gs-umonitor": bytes.fromhex("65 f3 0f ae f0"), }.items(): source = work / (name + ".elf") output = source.with_suffix(".exe") @@ -802,6 +803,7 @@ def main(): "gs-register-move": bytes.fromhex("65 48 89 c8"), "gs-cpuid": bytes.fromhex("65 0f a2"), "gs-mwait": bytes.fromhex("65 0f 01 c9"), + "gs-umwait": bytes.fromhex("65 f2 0f ae f0"), }.items(): source = work / (name + ".elf") output = source.with_suffix(".exe") From a0e591c9323814af8f75333a941bbc6b0a03c2f4 Mon Sep 17 00:00:00 2001 From: aasishdairel Date: Tue, 20 Oct 2026 10:53:42 +0530 Subject: [PATCH 4/5] fix(relinker): trap CLZERO GS overrides CLZERO (0F 01 FC) clears the cache line at the logical address in rAX and applies a GS override, so mark its register-form encoding as a memory operand. RDPRU (0F 01 FD) has no memory operand and stays unchanged. --- core/relinker/codegen/src/x86/X64InstructionDecoder.cpp | 2 +- core/relinker/codegen/tests/DecoderLengthTests.cpp | 2 ++ core/relinker/relinker/tests/test_macos_gs_traps.py | 2 ++ core/relinker/relinker/tests/test_tls_function_coverage.py | 2 ++ 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp b/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp index 7082938042..b22c3f8726 100644 --- a/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp +++ b/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp @@ -612,7 +612,7 @@ DecodedInstructionInfo X64InstructionDecoder::DecodeInstruction( const std::uint8_t reg = (info.ModRmByte >> ModRmRegShift) & ModRmRegMask; if (op == TwoByteMaskmov && mod == ModRmModRegister) info.HasMemoryOperand = true; - else if (op == TwoByteGrp7 && (info.ModRmByte == 0xC8 || info.ModRmByte == 0xFA)) + else if (op == TwoByteGrp7 && (info.ModRmByte == 0xC8 || info.ModRmByte == 0xFA || info.ModRmByte == 0xFC)) info.HasMemoryOperand = true; else if (op == TwoByteGrp15 && repPrefix && reg == 6 && mod == ModRmModRegister) info.HasMemoryOperand = true; diff --git a/core/relinker/codegen/tests/DecoderLengthTests.cpp b/core/relinker/codegen/tests/DecoderLengthTests.cpp index 697c200fa3..3ad1299b0d 100644 --- a/core/relinker/codegen/tests/DecoderLengthTests.cpp +++ b/core/relinker/codegen/tests/DecoderLengthTests.cpp @@ -129,7 +129,9 @@ int main() { {"65c4e179f7c1", true}, {"650f01c8", true}, {"650f01fa", true}, + {"650f01fc", true}, {"650f01c9", false}, + {"650f01fd", false}, {"65f30faef0", true}, {"f20faef0", false}, {"660faef0", false}, diff --git a/core/relinker/relinker/tests/test_macos_gs_traps.py b/core/relinker/relinker/tests/test_macos_gs_traps.py index 368c0a60c8..de0426bc5f 100644 --- a/core/relinker/relinker/tests/test_macos_gs_traps.py +++ b/core/relinker/relinker/tests/test_macos_gs_traps.py @@ -20,6 +20,7 @@ FORMS = { "gs-vmaskmovdqu": bytes.fromhex("65 c5 f9 f7 c1"), "gs-monitor": bytes.fromhex("65 0f 01 c8"), "gs-monitorx": bytes.fromhex("65 0f 01 fa"), + "gs-clzero": bytes.fromhex("65 0f 01 fc"), "gs-umonitor": bytes.fromhex("65 f3 0f ae f0"), } @@ -28,6 +29,7 @@ NON_MEMORY_FORMS = { "gs-register-move": bytes.fromhex("65 48 89 c8"), "gs-cpuid": bytes.fromhex("65 0f a2"), "gs-mwait": bytes.fromhex("65 0f 01 c9"), + "gs-rdpru": bytes.fromhex("65 0f 01 fd"), "gs-umwait": bytes.fromhex("65 f2 0f ae f0"), } diff --git a/core/relinker/relinker/tests/test_tls_function_coverage.py b/core/relinker/relinker/tests/test_tls_function_coverage.py index 99fec2aae4..dfe3918c87 100644 --- a/core/relinker/relinker/tests/test_tls_function_coverage.py +++ b/core/relinker/relinker/tests/test_tls_function_coverage.py @@ -788,6 +788,7 @@ def main(): "gs-vmaskmovdqu": bytes.fromhex("65 c5 f9 f7 c1"), "gs-monitor": bytes.fromhex("65 0f 01 c8"), "gs-monitorx": bytes.fromhex("65 0f 01 fa"), + "gs-clzero": bytes.fromhex("65 0f 01 fc"), "gs-umonitor": bytes.fromhex("65 f3 0f ae f0"), }.items(): source = work / (name + ".elf") @@ -803,6 +804,7 @@ def main(): "gs-register-move": bytes.fromhex("65 48 89 c8"), "gs-cpuid": bytes.fromhex("65 0f a2"), "gs-mwait": bytes.fromhex("65 0f 01 c9"), + "gs-rdpru": bytes.fromhex("65 0f 01 fd"), "gs-umwait": bytes.fromhex("65 f2 0f ae f0"), }.items(): source = work / (name + ".elf") From 0d3f6f7262b5e878762cb35668397cb5ece3731b Mon Sep 17 00:00:00 2001 From: aasishdairel Date: Tue, 20 Oct 2026 21:54:30 +0530 Subject: [PATCH 5/5] test(relinker): split GS trap coverage groups The Windows gs_traps group grew to 21 relinker invocations and ran close to its 30 s CTest timeout on a loaded host. Split the implicit-memory cases into gs_traps_implicit so each group stays well under the limit, and share the per-case check between them. --- core/relinker/CMakeLists.txt | 2 +- .../tests/test_tls_function_coverage.py | 79 ++++++++++--------- 2 files changed, 41 insertions(+), 40 deletions(-) diff --git a/core/relinker/CMakeLists.txt b/core/relinker/CMakeLists.txt index d2a3387683..ec14856671 100644 --- a/core/relinker/CMakeLists.txt +++ b/core/relinker/CMakeLists.txt @@ -474,7 +474,7 @@ if(BUILD_TESTING) foreach(opcode IN ITEMS add or adc sbb and sub xor cmp) list(APPEND tlsCoverageGroups alu_${opcode}) endforeach() - list(APPEND tlsCoverageGroups gs_traps) + list(APPEND tlsCoverageGroups gs_traps gs_traps_implicit) foreach(group IN LISTS tlsCoverageGroups) add_test(NAME tls_function_coverage_${group} COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/relinker/tests/test_tls_function_coverage.py $ ${group}) diff --git a/core/relinker/relinker/tests/test_tls_function_coverage.py b/core/relinker/relinker/tests/test_tls_function_coverage.py index dfe3918c87..7de43350f9 100644 --- a/core/relinker/relinker/tests/test_tls_function_coverage.py +++ b/core/relinker/relinker/tests/test_tls_function_coverage.py @@ -530,7 +530,7 @@ def add_alias(image, size, begin=0x1200): def main(): groups = ("metadata", "padding", "register_loads", "displacement_bounds", "alu_execution", - "register_address_loads", "moved_branch", "rejected", "gs_traps") + "register_address_loads", "moved_branch", "rejected", "gs_traps", "gs_traps_implicit") groups += tuple(f"displacement_r{register}" for register in range(16) if register != 4) groups += tuple(f"alu_{name}" for name in ALU_READ) parser = argparse.ArgumentParser(description="Run Windows guest TLS function coverage fixtures") @@ -776,45 +776,33 @@ def main(): conflicting[232:344] = unwind[232:344] struct.pack_into("