aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2021-08-21 14:03:08 -0700
committeriximeow <me@iximeow.net>2021-08-21 14:03:08 -0700
commitd7208834963c46a6da74a3837d9e82bad33dfd7f (patch)
tree6df2525647cc29b719ce1db376d271b801c47371
parente4131e4eb64595d9b24493eb31a9af4c5e21b1eb (diff)
fix incorrect decoding of 0x9*-series instructions with rex.b
-rw-r--r--CHANGELOG2
-rw-r--r--src/long_mode/mod.rs14
-rw-r--r--test/long_mode/mod.rs4
3 files changed, 16 insertions, 4 deletions
diff --git a/CHANGELOG b/CHANGELOG
index b10a6c0..0febd15 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -14,6 +14,8 @@
the *read*, not the corresponding write of pushing `{e}ip` to the stack.
documentation has been added to `mem_size` more specifically describing
this circumstance.
+* correct `rex.b` incorrectly applying to the `*ax` register - `4f91` is `xchg rax, r9`, not `xchg r8, r9`.
+* correct `nop` incorrectly ignoring `rex.b` - `4190` is `xchg rax, r8`, not `nop`.
## 1.0.4
diff --git a/src/long_mode/mod.rs b/src/long_mode/mod.rs
index a01e854..22b6a99 100644
--- a/src/long_mode/mod.rs
+++ b/src/long_mode/mod.rs
@@ -5118,7 +5118,7 @@ enum OperandCode {
Zv_R5 = OperandCodeBuilder::new().op0_is_rrr_and_Z_operand(ZOperandCategory::Zv_R, 5).bits(),
Zv_R6 = OperandCodeBuilder::new().op0_is_rrr_and_Z_operand(ZOperandCategory::Zv_R, 6).bits(),
Zv_R7 = OperandCodeBuilder::new().op0_is_rrr_and_Z_operand(ZOperandCategory::Zv_R, 7).bits(),
- // Zv_AX_R0 = 0x48,
+ Zv_AX_R0 = OperandCodeBuilder::new().op0_is_rrr_and_Z_operand(ZOperandCategory::Zv_AX, 0).bits(),
Zv_AX_R1 = OperandCodeBuilder::new().op0_is_rrr_and_Z_operand(ZOperandCategory::Zv_AX, 1).bits(),
Zv_AX_R2 = OperandCodeBuilder::new().op0_is_rrr_and_Z_operand(ZOperandCategory::Zv_AX, 2).bits(),
Zv_AX_R3 = OperandCodeBuilder::new().op0_is_rrr_and_Z_operand(ZOperandCategory::Zv_AX, 3).bits(),
@@ -5447,7 +5447,7 @@ const OPCODES: [OpcodeRecord; 256] = [
OpcodeRecord(Interpretation::Instruction(Opcode::LEA), OperandCode::Gv_M),
OpcodeRecord(Interpretation::Instruction(Opcode::MOV), OperandCode::Sw_Ew),
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::ModRM_0x8f_Ev),
- OpcodeRecord(Interpretation::Instruction(Opcode::NOP), OperandCode::Nothing),
+ OpcodeRecord(Interpretation::Instruction(Opcode::XCHG), OperandCode::Zv_AX_R0),
OpcodeRecord(Interpretation::Instruction(Opcode::XCHG), OperandCode::Zv_AX_R1),
OpcodeRecord(Interpretation::Instruction(Opcode::XCHG), OperandCode::Zv_AX_R2),
OpcodeRecord(Interpretation::Instruction(Opcode::XCHG), OperandCode::Zv_AX_R3),
@@ -7428,6 +7428,13 @@ fn read_operands<T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpe
}
1 => {
// Zv_AX
+ // in 64-bit mode, rex.b is able to make "nop" into an `xchg`, as in `4190`
+ // aka `xchg eax, r8d.
+ if reg == 0 && !instruction.prefixes.rex_unchecked().b() {
+ instruction.opcode = Opcode::NOP;
+ instruction.operand_count = 0;
+ return Ok(());
+ }
let opwidth = imm_width_from_prefixes_64(SizeCode::vqp, instruction.prefixes);
let bank = if opwidth == 4 {
RegisterBank::D
@@ -7436,8 +7443,9 @@ fn read_operands<T: Reader<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpe
} else {
RegisterBank::Q
};
+ // always *ax, but size is determined by prefixes (or lack thereof)
instruction.regs[0] =
- RegSpec::from_parts(0, instruction.prefixes.rex_unchecked().b(), bank);
+ RegSpec::from_parts(0, false, bank);
instruction.operands[1] = OperandSpec::RegMMM;
instruction.regs[1] =
RegSpec::from_parts(reg, instruction.prefixes.rex_unchecked().b(), bank);
diff --git a/test/long_mode/mod.rs b/test/long_mode/mod.rs
index 61ea63d..015f1c6 100644
--- a/test/long_mode/mod.rs
+++ b/test/long_mode/mod.rs
@@ -1176,8 +1176,10 @@ fn test_mov() {
fn test_xchg() {
test_display(&[0x90], "nop");
test_display(&[0x91], "xchg eax, ecx");
- test_display(&[0x4f, 0x91], "xchg r8, r9");
+ test_display(&[0x4f, 0x91], "xchg rax, r9");
test_display(&[0x66, 0x91], "xchg ax, cx");
+ test_display(&[0x4f, 0x90], "xchg rax, r8");
+ test_display(&[0x41, 0x90], "xchg eax, r8d");
}
#[test]