diff --git a/core/relinker/codegen/include/codegen/x86/X64OpcodeConstants.hpp b/core/relinker/codegen/include/codegen/x86/X64OpcodeConstants.hpp index 265725367..ba63d071c 100644 --- a/core/relinker/codegen/include/codegen/x86/X64OpcodeConstants.hpp +++ b/core/relinker/codegen/include/codegen/x86/X64OpcodeConstants.hpp @@ -119,7 +119,7 @@ inline constexpr std::uint8_t TwoByteMovImm8ModRm = 0xBA; inline constexpr std::uint8_t TwoByteCmovRangeMin = 0x40; inline constexpr std::uint8_t TwoByteCmovRangeMax = 0x4F; inline constexpr std::uint8_t TwoByteModRmRangeAMin = 0xA3; -inline constexpr std::uint8_t TwoByteModRmRangeAMax = 0xAB; +inline constexpr std::uint8_t TwoByteModRmRangeAMax = 0xA7; inline constexpr std::uint8_t TwoByteModRmRangeBMin = 0xB0; inline constexpr std::uint8_t TwoByteModRmRangeBMax = 0xB7; inline constexpr std::uint8_t TwoByteModRmRangeCMin = 0xBC; @@ -174,6 +174,7 @@ inline constexpr std::uint8_t TwoByteMovCrDrMin = 0x20; inline constexpr std::uint8_t TwoByteMovCrDrMax = 0x23; inline constexpr std::uint8_t TwoBytePopcnt = 0xB8; inline constexpr std::uint8_t TwoByteUd1 = 0xB9; +inline constexpr std::uint8_t TwoByteBts = 0xAB; inline constexpr std::uint8_t TwoByteBtc = 0xBB; inline constexpr std::uint8_t TwoByteXaddRm8 = 0xC0; inline constexpr std::uint8_t TwoByteMovnti = 0xC3; diff --git a/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp b/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp index f1ba6ddf6..0e5ebec82 100644 --- a/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp +++ b/core/relinker/codegen/src/x86/X64InstructionDecoder.cpp @@ -246,6 +246,7 @@ std::size_t X64InstructionDecoder::Decode(const std::uint8_t* data, std::size_t hasModRm = true; } else if ((opcode >= TwoByteCmovRangeMin && opcode <= TwoByteCmovRangeMax) || (opcode >= TwoByteModRmRangeAMin && opcode <= TwoByteModRmRangeAMax) || + opcode == TwoByteBts || (opcode >= TwoByteModRmRangeBMin && opcode <= TwoByteModRmRangeBMax) || (opcode >= TwoByteModRmRangeCMin && opcode <= TwoByteModRmRangeCMax) || (opcode >= TwoByteModRmRangeDMin && opcode <= TwoByteModRmRangeDMax) || @@ -279,7 +280,8 @@ std::size_t X64InstructionDecoder::Decode(const std::uint8_t* data, std::size_t const std::uint8_t modrm = data[pos]; pos += 1; - const auto mod = static_cast((modrm >> ModRmModShift) & ModRmModMask); + const bool registerOnlyModRm = twoByteOpcode && !threeByteEscape && opcode >= TwoByteMovCrDrMin && opcode <= TwoByteMovCrDrMax; + const auto mod = registerOnlyModRm ? ModRmModRegister : static_cast((modrm >> ModRmModShift) & ModRmModMask); const auto reg = static_cast((modrm >> ModRmRegShift) & ModRmRegMask); const auto rm = static_cast(modrm & ModRmRmMask); @@ -361,12 +363,12 @@ DecodedInstructionInfo X64InstructionDecoder::DecodeInstruction( if (pos >= info.Length) return info; - const auto readModRm = [&](const std::size_t modrmPos) { + const auto readModRm = [&](const std::size_t modrmPos, const bool registerOnly) { const std::uint8_t modrm = data[modrmPos]; info.HasModRm = true; info.ModRmByte = modrm; info.ModRmRegField = (modrm >> ModRmRegShift) & ModRmRegMask; - const std::uint8_t mod = (modrm >> ModRmModShift) & ModRmModMask; + const std::uint8_t mod = registerOnly ? ModRmModRegister : (modrm >> ModRmModShift) & ModRmModMask; const std::uint8_t rm = modrm & ModRmRmMask; if (mod == ModRmModIndirect && rm == ModRmRmRipRelative) { info.HasRipRelativeDisp = true; @@ -380,7 +382,7 @@ DecodedInstructionInfo X64InstructionDecoder::DecodeInstruction( if (op == OneByteVex2 || op == OneByteVex3 || op == EvexPrefix) { pos += (op == OneByteVex2 ? 1 : (op == OneByteVex3 ? 2 : EvexPrefixLength - 1)) + 1; if (pos < info.Length) - readModRm(pos); + readModRm(pos, false); info.FlowKind = ControlFlowKind::Sequential; return info; } @@ -516,6 +518,7 @@ DecodedInstructionInfo X64InstructionDecoder::DecodeInstruction( } else { if ((op >= TwoByteCmovRangeMin && op <= TwoByteCmovRangeMax) || (op >= TwoByteModRmRangeAMin && op <= TwoByteModRmRangeAMax) || + op == TwoByteBts || (op >= TwoByteModRmRangeBMin && op <= TwoByteModRmRangeBMax) || (op >= TwoByteModRmRangeCMin && op <= TwoByteModRmRangeCMax) || (op >= TwoByteModRmRangeDMin && op <= TwoByteModRmRangeDMax) || @@ -544,7 +547,7 @@ DecodedInstructionInfo X64InstructionDecoder::DecodeInstruction( } if (hasModRm && pos < info.Length) - readModRm(pos); + readModRm(pos, twoByteOpcode && op >= TwoByteMovCrDrMin && op <= TwoByteMovCrDrMax); info.FlowKind = ControlFlowKind::Sequential; return info; diff --git a/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp b/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp index 813d60c31..ba078f150 100644 --- a/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp +++ b/core/relinker/codegen/tests/Amd64OnlyConverterTests.cpp @@ -169,6 +169,33 @@ void decoderRipRelative() { require(!decoder.DecodeInstruction(instruction.data(), instruction.size()).HasRipRelativeDisp, "Vector instruction without a RIP-relative operand was reported as RIP-relative"); } +void decoderTwoByteOpcodeLengths() { + const Codegen::X64InstructionDecoder decoder; + const std::vector instructions = { + {0x0F, 0xA8}, {0x0F, 0xA9}, {0x0F, 0xAA}, {0x66, 0x0F, 0xA8}, {0x41, 0x0F, 0xA9}, + {0x0F, 0xAB, 0xC8}, {0x0F, 0xAB, 0x05, 0x10, 0x00, 0x00, 0x00}, {0x48, 0x0F, 0xAB, 0x44, 0x24, 0x08}, + {0x0F, 0x20, 0xC0}, {0x0F, 0x20, 0x05}, {0x0F, 0x22, 0x04}, {0x0F, 0x21, 0x45}, {0x0F, 0x23, 0x85}, + {0x66, 0x0F, 0x38, 0x20, 0x05, 0x10, 0x00, 0x00, 0x00}, {0x66, 0x0F, 0x38, 0x23, 0x44, 0x24, 0x08}}; + Bytes padded; + for (const auto& instruction : instructions) { + padded = instruction; + padded.insert(padded.end(), 8, 0x90); + require(decoder.Decode(padded.data(), padded.size()) == instruction.size(), "Two-byte opcode was decoded with operand bytes it does not have"); + require(decoder.DecodeInstruction(padded.data(), padded.size()).Length == instruction.size(), "Two-byte opcode was described with operand bytes it does not have"); + } + const Bytes controlRegister = {0x0F, 0x20, 0x05, 0x90, 0x90, 0x90, 0x90}; + const auto control = decoder.DecodeInstruction(controlRegister.data(), controlRegister.size()); + require(control.Length == 3 && control.HasModRm && control.ModRmByte == 0x05 && !control.HasRipRelativeDisp, "MOV from a control register was reported as RIP-relative"); + const Bytes debugRegister = {0x0F, 0x23, 0x05, 0x90, 0x90, 0x90, 0x90}; + require(!decoder.DecodeInstruction(debugRegister.data(), debugRegister.size()).HasRipRelativeDisp, "MOV to a debug register was reported as RIP-relative"); + const Bytes extend = {0x66, 0x0F, 0x38, 0x20, 0x05, 0x10, 0x00, 0x00, 0x00}; + const auto extended = decoder.DecodeInstruction(extend.data(), extend.size()); + require(extended.HasRipRelativeDisp && read(extend, extended.RipRelativeDispOffset) == 0x10, "PMOVSXBW lost its RIP-relative operand"); + const Bytes bitTest = {0x0F, 0xAB, 0x05, 0x10, 0x00, 0x00, 0x00}; + const auto bts = decoder.DecodeInstruction(bitTest.data(), bitTest.size()); + require(bts.HasRipRelativeDisp && read(bitTest, bts.RipRelativeDispOffset) == 0x10, "BTS lost its RIP-relative operand"); +} + void sse4aOperands() { const auto check = [](const Bytes& site, const bool insertq, const int dst, const int src, const int length, const int index) { const auto operands = Codegen::DecodeSse4a(site.data(), site.size()); @@ -1120,6 +1147,7 @@ int main() { try { decoderLengths(); decoderRipRelative(); + decoderTwoByteOpcodeLengths(); sse4aOperands(); sha256Operands(); sha1Operands();