From d64c0605ea07128a7bbcb648dab5ad8a242ffef5 Mon Sep 17 00:00:00 2001 From: Oguzhan Ozdemir <48760055+oguzhanozfe@users.noreply.github.com> Date: Wed, 7 Oct 2026 18:51:56 +0300 Subject: [PATCH] fix(relinker): let REX.W override 0x66 when sizing decoder immediates In 64-bit mode REX.W takes precedence over the 0x66 operand-size prefix, so the immediate of 81, C7, 69, 68, A9, the 05..3D accumulator forms and the F7 TEST form stays imm32. The decoder only applied this to B8+r and returned a length two bytes short for the others. Co-Authored-By: Claude Opus 5.5 --- .../relinker/codegen/src/x86/X64InstructionDecoder.cpp | 10 ++++++---- .../relinker/codegen/tests/Amd64OnlyConverterTests.cpp | 4 ++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp b/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp index 065c7f9dd..9cc934523 100644 --- a/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp +++ b/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp @@ -39,6 +39,9 @@ std::size_t X64InstructionDecoder::Decode(const std::uint8_t* data, std::size_t pos += 1; } + const bool rexW = rexPresent && (rex & RexWBit) != 0; + const std::size_t operandImmediateSize = operandSizeOverride && !rexW ? ImmSize16 : ImmSize32; + if (pos >= available) { throw CodegenException("Instruction truncated after prefixes"); } @@ -172,10 +175,9 @@ std::size_t X64InstructionDecoder::Decode(const std::uint8_t* data, std::size_t opcode == OneByteTestEaxImm32 || (opcode >= OneByteMovImm32RegMin && opcode <= OneByteMovImm32RegMax)) { if (opcode >= OneByteMovImm32RegMin && opcode <= OneByteMovImm32RegMax) { - immediateSize = (rexPresent && (rex & RexWBit) != 0) ? ImmSize64 : - (operandSizeOverride ? ImmSize16 : ImmSize32); + immediateSize = rexW ? ImmSize64 : operandImmediateSize; } else { - immediateSize = operandSizeOverride ? ImmSize16 : ImmSize32; + immediateSize = operandImmediateSize; } } else if (opcode == OneByteImm8Grp1 || opcode == OneByteImulRm32Imm8) { immediateSize = ImmSize8; @@ -279,7 +281,7 @@ std::size_t X64InstructionDecoder::Decode(const std::uint8_t* data, std::size_t reg <= Grp3RegTestMax) { immediateSize = (opcode == OneByteTestGrp3Rm8) ? ImmSize8 - : (operandSizeOverride ? ImmSize16 : ImmSize32); + : operandImmediateSize; } if (mod != ModRmModRegister && rm == ModRmRmSibPresent) { diff --git a/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp b/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp index c4fab89f4..65e297c29 100644 --- a/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp +++ b/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp @@ -127,6 +127,10 @@ void decoderLengths() { {0x0F, 0x38, 0xCB, 0xCA}, {0x45, 0x0F, 0x38, 0xCC, 0xE1}, {0x0F, 0x38, 0xCD, 0x08}, {0x0F, 0x38, 0xCB, 0x0D, 0x10, 0x00, 0x00, 0x00}, {0x0F, 0x3A, 0xCC, 0xD5, 0x03}, {0x45, 0x0F, 0x3A, 0xCC, 0xE1, 0x00}, {0x0F, 0x3A, 0xCC, 0x08, 0x01}, {0x0F, 0x38, 0xC8, 0xCA}, {0x0F, 0x38, 0xC9, 0xCA}, {0x0F, 0x38, 0xCA, 0x08}, {0x48, 0x66, 0xB8, 0x34, 0x12}, {0x66, 0x48, 0xB8, 1, 2, 3, 4, 5, 6, 7, 8}, {0x41, 0x48, 0xB8, 1, 2, 3, 4, 5, 6, 7, 8}, + {0x66, 0x48, 0x81, 0xC0, 1, 2, 3, 4}, {0x66, 0x48, 0x05, 1, 2, 3, 4}, {0x66, 0x48, 0xC7, 0xC0, 1, 2, 3, 4}, + {0x66, 0x48, 0x69, 0xC0, 1, 2, 3, 4}, {0x66, 0x48, 0x68, 1, 2, 3, 4}, {0x66, 0x48, 0xA9, 1, 2, 3, 4}, + {0x66, 0x48, 0xF7, 0xC0, 1, 2, 3, 4}, {0x66, 0x81, 0xC0, 0x34, 0x12}, {0x48, 0x66, 0x81, 0xC0, 0x34, 0x12}, + {0x66, 0x41, 0x81, 0xC0, 0x34, 0x12}, {0x66, 0xF7, 0xC0, 0x34, 0x12}, {0x48, 0x64, 0x8B, 0x00}, {0x48, 0xF3, 0x0F, 0x2B, 0x00}, {0x48, 0x67, 0x0F, 0x01, 0xFC}, {0x48, 0x48, 0x0F, 0x01, 0xFC}}; Bytes padded; for (const auto& instruction : instructions) {