Merge pull request #1381 from overlordxrz-source/fix/relinker-decoder-0f-no-modrm

fix(relinker): decode 0F A8-AA without a ModRM and MOV CR/DR without a memory operand
This commit is contained in:
Dean 2026-10-08 22:35:44 +13:00 committed by GitHub
commit 9e5ade70fb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 38 additions and 6 deletions

View File

@ -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;

View File

@ -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<std::uint8_t>((modrm >> ModRmModShift) & ModRmModMask);
const bool registerOnlyModRm = twoByteOpcode && !threeByteEscape && opcode >= TwoByteMovCrDrMin && opcode <= TwoByteMovCrDrMax;
const auto mod = registerOnlyModRm ? ModRmModRegister : static_cast<std::uint8_t>((modrm >> ModRmModShift) & ModRmModMask);
const auto reg = static_cast<std::uint8_t>((modrm >> ModRmRegShift) & ModRmRegMask);
const auto rm = static_cast<std::uint8_t>(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;

View File

@ -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<Bytes> 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<std::int32_t>(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<std::int32_t>(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();