aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2020-05-23 19:37:58 -0700
committeriximeow <me@iximeow.net>2020-05-23 19:37:58 -0700
commit9b965525afced37e99543e3a412218207a41e793 (patch)
treed82cb429eb2b7f230249769f935bc24d60a6158f
parenta0fd5a24cb0aa0b697f680c451d928cefe8323b4 (diff)
add SHA, BMI1, and BMI2, complete XSAVE extension support
additionally: cmpcxchg{8,16}b, rdrand, rdseed, rdpid, {rd,wr}{fs,gs}base
-rw-r--r--src/long_mode/display.rs54
-rw-r--r--src/long_mode/mod.rs316
-rw-r--r--src/long_mode/uarch.rs4
-rw-r--r--src/long_mode/vex.rs182
-rw-r--r--src/protected_mode/display.rs46
-rw-r--r--src/protected_mode/mod.rs272
-rw-r--r--src/protected_mode/uarch.rs4
-rw-r--r--src/protected_mode/vex.rs166
-rw-r--r--test/long_mode/mod.rs122
-rw-r--r--test/long_mode/operand.rs3
-rw-r--r--test/protected_mode/mod.rs92
-rw-r--r--test/protected_mode/operand.rs3
12 files changed, 1123 insertions, 141 deletions
diff --git a/src/long_mode/display.rs b/src/long_mode/display.rs
index 5318ebb..807565c 100644
--- a/src/long_mode/display.rs
+++ b/src/long_mode/display.rs
@@ -306,6 +306,9 @@ impl fmt::Display for Opcode {
&Opcode::BTC => write!(f, "btc"),
&Opcode::BSF => write!(f, "bsf"),
&Opcode::BSR => write!(f, "bsr"),
+ &Opcode::BZHI => write!(f, "bzhi"),
+ &Opcode::PDEP => write!(f, "pdep"),
+ &Opcode::PEXT => write!(f, "pext"),
&Opcode::TZCNT => write!(f, "tzcnt"),
&Opcode::MOVSS => write!(f, "movss"),
&Opcode::SQRTSS => write!(f, "sqrtss"),
@@ -354,17 +357,30 @@ impl fmt::Display for Opcode {
&Opcode::RDMSR => write!(f, "rdmsr"),
&Opcode::RDTSC => write!(f, "rdtsc"),
&Opcode::RDPMC => write!(f, "rdpmc"),
+ &Opcode::RDPID => write!(f, "rdpid"),
+ &Opcode::RDFSBASE => write!(f, "rdfsbase"),
+ &Opcode::RDGSBASE => write!(f, "rdgsbase"),
+ &Opcode::WRFSBASE => write!(f, "wrfsbase"),
+ &Opcode::WRGSBASE => write!(f, "wrgsbase"),
&Opcode::FXSAVE => write!(f, "fxsave"),
&Opcode::FXRSTOR => write!(f, "fxrstor"),
&Opcode::LDMXCSR => write!(f, "ldmxcsr"),
&Opcode::STMXCSR => write!(f, "stmxcsr"),
&Opcode::XSAVE => write!(f, "xsave"),
+ &Opcode::XSAVEC => write!(f, "xsavec"),
+ &Opcode::XSAVES => write!(f, "xsaves"),
+ &Opcode::XSAVEC64 => write!(f, "xsavec64"),
+ &Opcode::XSAVES64 => write!(f, "xsaves64"),
&Opcode::XRSTOR => write!(f, "xrstor"),
+ &Opcode::XRSTORS => write!(f, "xrstors"),
+ &Opcode::XRSTORS64 => write!(f, "xrstors64"),
&Opcode::XSAVEOPT => write!(f, "xsaveopt"),
&Opcode::LFENCE => write!(f, "lfence"),
&Opcode::MFENCE => write!(f, "mfence"),
&Opcode::SFENCE => write!(f, "sfence"),
&Opcode::CLFLUSH => write!(f, "clflush"),
+ &Opcode::CLFLUSHOPT => write!(f, "clflushopt"),
+ &Opcode::CLWB => write!(f, "clwb"),
&Opcode::SGDT => write!(f, "sgdt"),
&Opcode::SIDT => write!(f, "sidt"),
&Opcode::LGDT => write!(f, "lgdt"),
@@ -450,11 +466,15 @@ impl fmt::Display for Opcode {
&Opcode::SAR => write!(f, "sar"),
&Opcode::SAL => write!(f, "sal"),
&Opcode::SHR => write!(f, "shr"),
+ &Opcode::SARX => write!(f, "sarx"),
+ &Opcode::SHLX => write!(f, "shlx"),
+ &Opcode::SHRX => write!(f, "shrx"),
&Opcode::SHRD => write!(f, "shrd"),
&Opcode::SHL => write!(f, "shl"),
&Opcode::RCR => write!(f, "rcr"),
&Opcode::RCL => write!(f, "rcl"),
&Opcode::ROR => write!(f, "ror"),
+ &Opcode::RORX => write!(f, "rorx"),
&Opcode::ROL => write!(f, "rol"),
&Opcode::CMOVA => write!(f, "cmova"),
&Opcode::CMOVB => write!(f, "cmovb"),
@@ -475,9 +495,12 @@ impl fmt::Display for Opcode {
&Opcode::NEG => write!(f, "neg"),
&Opcode::NOT => write!(f, "not"),
&Opcode::MUL => write!(f, "mul"),
+ &Opcode::MULX => write!(f, "mulx"),
&Opcode::DIV => write!(f, "div"),
&Opcode::IDIV => write!(f, "idiv"),
&Opcode::CMPXCHG => write!(f, "cmpxchg"),
+ &Opcode::CMPXCHG8B => write!(f, "cmpxchg8b"),
+ &Opcode::CMPXCHG16B => write!(f, "cmpxchg16b"),
&Opcode::MOVSX_b => write!(f, "movsx"),
&Opcode::MOVSX_w => write!(f, "movsx"),
&Opcode::MOVZX_b => write!(f, "movzx"),
@@ -652,6 +675,8 @@ impl fmt::Display for Opcode {
&Opcode::BLSMSK => write!(f, "blsmsk"),
&Opcode::BLSR => write!(f, "blsr"),
&Opcode::VMCLEAR => write!(f, "vmclear"),
+ &Opcode::VMPTRLD => write!(f, "vmptrld"),
+ &Opcode::VMPTRST => write!(f, "vmptrst"),
&Opcode::VMXON => write!(f, "vmxon"),
&Opcode::VMCALL => write!(f, "vmcall"),
&Opcode::VMLAUNCH => write!(f, "vmlaunch"),
@@ -1310,16 +1335,21 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
Opcode::DIV |
Opcode::IDIV |
Opcode::MUL |
+ Opcode::MULX |
Opcode::NEG |
Opcode::NOT |
Opcode::SAR |
Opcode::SAL |
Opcode::SHR |
+ Opcode::SARX |
+ Opcode::SHLX |
+ Opcode::SHRX |
Opcode::SHRD |
Opcode::SHL |
Opcode::RCR |
Opcode::RCL |
Opcode::ROR |
+ Opcode::RORX |
Opcode::ROL |
Opcode::INC |
Opcode::DEC |
@@ -1341,6 +1371,9 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
Opcode::BTC |
Opcode::BSF |
Opcode::BSR |
+ Opcode::BZHI |
+ Opcode::PDEP |
+ Opcode::PEXT |
Opcode::TZCNT |
Opcode::ANDN |
Opcode::BEXTR |
@@ -1833,23 +1866,38 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
Opcode::CMP |
Opcode::CMPPS |
Opcode::CMPPD |
+ Opcode::CMPXCHG8B |
+ Opcode::CMPXCHG16B |
Opcode::CMPXCHG => { write!(out, "{}", colors.comparison_op(self)) }
Opcode::WRMSR |
Opcode::RDMSR |
Opcode::RDTSC |
Opcode::RDPMC |
+ Opcode::RDPID |
+ Opcode::RDFSBASE |
+ Opcode::RDGSBASE |
+ Opcode::WRFSBASE |
+ Opcode::WRGSBASE |
Opcode::FXSAVE |
Opcode::FXRSTOR |
Opcode::LDMXCSR |
Opcode::STMXCSR |
Opcode::XSAVE |
+ Opcode::XSAVEC |
+ Opcode::XSAVES |
+ Opcode::XSAVEC64 |
+ Opcode::XSAVES64 |
Opcode::XRSTOR |
+ Opcode::XRSTORS |
+ Opcode::XRSTORS64 |
Opcode::XSAVEOPT |
Opcode::LFENCE |
Opcode::MFENCE |
Opcode::SFENCE |
Opcode::CLFLUSH |
+ Opcode::CLFLUSHOPT |
+ Opcode::CLWB |
Opcode::SGDT |
Opcode::SIDT |
Opcode::LGDT |
@@ -1886,6 +1934,8 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
Opcode::VMREAD |
Opcode::VMWRITE |
Opcode::VMCLEAR |
+ Opcode::VMPTRLD |
+ Opcode::VMPTRST |
Opcode::VMXON |
Opcode::VMCALL |
Opcode::VMLAUNCH |
@@ -1993,6 +2043,10 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> ShowContextual<u6
self.opcode.colorize(colors, out)?;
+ if self.opcode == Opcode::XBEGIN {
+ return write!(out, " $+{}", colors.number(signed_i32_hex(self.imm as i32)));
+ }
+
match self.operands[0] {
OperandSpec::Nothing => {
return Ok(());
diff --git a/src/long_mode/mod.rs b/src/long_mode/mod.rs
index 20abe1f..58d4c57 100644
--- a/src/long_mode/mod.rs
+++ b/src/long_mode/mod.rs
@@ -528,17 +528,36 @@ const BMI1: [Opcode; 6] = [
Opcode::TZCNT,
];
+const BMI2: [Opcode; 8] = [
+ Opcode::BZHI,
+ Opcode::MULX,
+ Opcode::PDEP,
+ Opcode::PEXT,
+ Opcode::RORX,
+ Opcode::SARX,
+ Opcode::SHRX,
+ Opcode::SHLX,
+];
+
+#[allow(dead_code)]
+const XSAVE: [Opcode; 10] = [
+ Opcode::XGETBV,
+ Opcode::XRSTOR,
+ Opcode::XRSTORS,
+ Opcode::XSAVE,
+ Opcode::XSAVEC,
+ Opcode::XSAVEC64,
+ Opcode::XSAVEOPT,
+ Opcode::XSAVES,
+ Opcode::XSAVES64,
+ Opcode::XSETBV,
+];
+
// TODO:
// PTWRITE
-// RDFSBASE
-// RDGSBASE
-// WRFSBASE
-// WRGSBASE
// TPAUSE
// UMONITOR
// UMWAIT
-// CLFLUSHOPT
-// CLWB
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Opcode {
@@ -740,6 +759,8 @@ pub enum Opcode {
MFENCE,
SFENCE,
CLFLUSH,
+ CLFLUSHOPT,
+ CLWB,
WRMSR,
RDTSC,
RDMSR,
@@ -1376,6 +1397,32 @@ pub enum Opcode {
ADOX,
PREFETCHW,
+
+ RDPID,
+ CMPXCHG8B,
+ CMPXCHG16B,
+ VMPTRLD,
+ VMPTRST,
+
+ BZHI,
+ MULX,
+ SHLX,
+ SHRX,
+ SARX,
+ PDEP,
+ PEXT,
+ RORX,
+ XRSTORS,
+ XRSTORS64,
+ XSAVEC,
+ XSAVEC64,
+ XSAVES,
+ XSAVES64,
+
+ RDFSBASE,
+ RDGSBASE,
+ WRFSBASE,
+ WRGSBASE,
}
#[derive(Debug)]
@@ -2094,7 +2141,7 @@ impl InstDecoder {
/// the clearest documentation on when these instructions were reintroduced into 64-bit
/// architectures seems to be
/// [wikipedia](https://en.wikipedia.org/wiki/X86-64#Older_implementations):
- /// ```
+ /// ```text
/// Early AMD64 and Intel 64 CPUs lacked LAHF and SAHF instructions in 64-bit mode. AMD
/// introduced these instructions (also in 64-bit mode) with their Athlon 64, Opteron and
/// Turion 64 revision D processors in March 2005[48][49][50] while Intel introduced the
@@ -2686,9 +2733,8 @@ impl InstDecoder {
* (if CPUID.01H:ECX.POPCNT[bit 23] = 1).
* ```
*/
- if self.intel_quirks() && (!self.sse4_2() || !self.popcnt()) {
- inst.opcode = Opcode::Invalid;
- return Err(DecodeError::InvalidOpcode);
+ if self.intel_quirks() && (self.sse4_2() || self.popcnt()) {
+ return Ok(());
} else if !self.popcnt() {
/*
* elsewhere from the amd APM:
@@ -2795,6 +2841,11 @@ impl InstDecoder {
return Err(DecodeError::InvalidOpcode);
}
}
+ if !self.bmi2() {
+ if BMI2.contains(&other) {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
}
}
Ok(())
@@ -3137,6 +3188,8 @@ pub enum OperandCode {
ModRM_0x0fae,
ModRM_0x0fba,
ModRM_0xf238,
+ ModRM_0xf30fae,
+ ModRM_0x660fae,
ModRM_0xf30fc7,
ModRM_0x660f38,
ModRM_0xf30f38,
@@ -3513,7 +3566,7 @@ const OPCODE_660F_MAP: [OpcodeRecord; 256] = [
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
- OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
+ OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::ModRM_0x660fae),
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
// 0xb0
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
@@ -4075,7 +4128,7 @@ const OPCODE_F30F_MAP: [OpcodeRecord; 256] = [
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
- OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
+ OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::ModRM_0xf30fae),
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
// 0xb0
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
@@ -5379,13 +5432,12 @@ fn read_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter: T,
return Ok(());
} else {
instruction.opcode = Opcode::XBEGIN;
- instruction.disp = if opwidth == 2 {
+ instruction.imm = if opwidth == 2 {
read_imm_signed(&mut bytes_iter, 2, length)? as i16 as i64 as u64
} else {
read_imm_signed(&mut bytes_iter, 4, length)? as i32 as i64 as u64
};
- instruction.modrm_mmm = RegSpec::rip();
- instruction.operands[0] = OperandSpec::RegDisp;
+ instruction.operands[0] = OperandSpec::ImmI32;
instruction.operand_count = 1;
return Ok(());
}
@@ -5628,6 +5680,9 @@ fn read_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter: T,
instruction.imm =
read_num(&mut bytes_iter, 1)? as u8 as u64;
*length += 1;
+ if instruction.operands[1] == OperandSpec::RegMMM {
+ instruction.modrm_mmm.bank = RegisterBank::MM;
+ }
instruction.operands[2] = OperandSpec::ImmI8;
instruction.operand_count = 3;
},
@@ -5776,7 +5831,7 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter
}
OperandCode::ModRM_0x0f0d => {
let modrm = read_modrm(&mut bytes_iter, length)?;
- let r = modrm & 0b111;
+ let r = (modrm >> 3) & 0b111;
let opwidth = imm_width_from_prefixes_64(SizeCode::vq, instruction.prefixes);
@@ -5842,46 +5897,94 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter
return read_operands(decoder, bytes_iter, instruction, operands, length);
},
OperandCode::ModRM_0x0f3a => {
+ let opcode = read_modrm(&mut bytes_iter, length)?;
+ if opcode == 0xcc {
+ instruction.opcode = Opcode::SHA1RNDS4;
+ return read_operands(decoder, bytes_iter, instruction, OperandCode::G_E_xmm_Ib, length);
+ } else if opcode == 0x0f {
+ instruction.opcode = Opcode::PALIGNR;
+ return read_operands(decoder, bytes_iter, instruction, OperandCode::G_E_mm_Ib, length);
+ }
},
OperandCode::ModRM_0x0fc7 => {
let modrm = read_modrm(&mut bytes_iter, length)?;
- if modrm >> 6 == 0b11 {
- match (modrm >> 3) & 0b111 {
- 0b111 => {
- instruction.opcode = Opcode::RDSEED;
- instruction.operand_count = 1;
- instruction.operands[0] = OperandSpec::RegRRR;
- let opwidth = imm_width_from_prefixes_64(SizeCode::vq, instruction.prefixes);
- instruction.modrm_rrr =
- RegSpec::from_parts(modrm & 7, instruction.prefixes.rex().r(), match opwidth {
- 8 => RegisterBank::Q,
- 4 => RegisterBank::D,
- 2 => RegisterBank::W,
- _ => unreachable!()
- });
+ let is_reg = (modrm & 0xc0) == 0xc0;
+
+ let r = (modrm >> 3) & 0b111;
+
+ let opcode = match r {
+ 0b001 => {
+ if is_reg {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOperand);
+ } else {
+ if instruction.prefixes.rex().w() {
+ Opcode::CMPXCHG16B
+ } else {
+ Opcode::CMPXCHG8B
+ }
}
- 0b110 => {
- instruction.opcode = Opcode::RDRAND;
- instruction.operand_count = 1;
- instruction.operands[0] = OperandSpec::RegRRR;
- let opwidth = imm_width_from_prefixes_64(SizeCode::vq, instruction.prefixes);
- instruction.modrm_rrr =
- RegSpec::from_parts(modrm & 7, instruction.prefixes.rex().r(), match opwidth {
- 8 => RegisterBank::Q,
- 4 => RegisterBank::D,
- 2 => RegisterBank::W,
- _ => unreachable!()
- });
+ }
+ 0b011 => {
+ if is_reg {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOperand);
+ } else {
+ if instruction.prefixes.rex().w() {
+ Opcode::XRSTORS64
+ } else {
+ Opcode::XRSTORS
+ }
}
- _ => {
+ }
+ 0b100 => {
+ if is_reg {
instruction.opcode = Opcode::Invalid;
- return Err(DecodeError::InvalidOpcode);
+ return Err(DecodeError::InvalidOperand);
+ } else {
+ if instruction.prefixes.rex().w() {
+ Opcode::XSAVEC64
+ } else {
+ Opcode::XSAVEC
+ }
}
}
- } else {
- instruction.opcode = Opcode::Invalid;
- return Err(DecodeError::InvalidOpcode);
- }
+ 0b101 => {
+ if is_reg {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOperand);
+ } else {
+ if instruction.prefixes.rex().w() {
+ Opcode::XSAVES64
+ } else {
+ Opcode::XSAVES
+ }
+ }
+ }
+ 0b110 => {
+ if is_reg {
+ Opcode::RDRAND
+ } else {
+ Opcode::VMPTRLD
+ }
+ }
+ 0b111 => {
+ if is_reg {
+ Opcode::RDSEED
+ } else {
+ Opcode::VMPTRST
+ }
+ }
+ _ => {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOperand);
+ }
+ };
+
+ instruction.opcode = opcode;
+ instruction.operand_count = 1;
+ let opwidth = imm_width_from_prefixes_64(SizeCode::vqp, instruction.prefixes);
+ instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, opwidth, length)?;
},
OperandCode::ModRM_0x0f71 => {
instruction.operand_count = 2;
@@ -6168,15 +6271,33 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter
instruction.operands[1] = OperandSpec::ImmU8;
},
OperandCode::ModRM_0x660fc7 => {
+ let opwidth = imm_width_from_prefixes_64(SizeCode::vqp, instruction.prefixes);
let modrm = read_modrm(&mut bytes_iter, length)?;
let r = (modrm >> 3) & 7;
match r {
6 => {
instruction.opcode = Opcode::VMCLEAR;
- instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, 1 /* doesn't matter, something using this width is invalid */, length)?;
+ instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, opwidth, length)?;
if instruction.operands[0] == OperandSpec::RegMMM {
- return Err(DecodeError::InvalidOperand);
+ // this would be invalid as `vmclear`, so fall back to the parse as
+ // 66-prefixed rdrand. this is a register operand, so just demote it to the
+ // word-form operand:
+ instruction.modrm_mmm = RegSpec { bank: RegisterBank::W, num: instruction.modrm_mmm.num };
+ instruction.opcode = Opcode::RDRAND;
+ }
+ instruction.operand_count = 1;
+ }
+ 7 => {
+ instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, opwidth, length)?;
+ if instruction.operands[0] == OperandSpec::RegMMM {
+ // this would be invalid as `vmclear`, so fall back to the parse as
+ // 66-prefixed rdrand. this is a register operand, so just demote it to the
+ // word-form operand:
+ instruction.modrm_mmm = RegSpec { bank: RegisterBank::W, num: instruction.modrm_mmm.num };
+ instruction.opcode = Opcode::RDSEED;
+ } else {
+ return Err(DecodeError::InvalidOpcode);
}
instruction.operand_count = 1;
}
@@ -6185,15 +6306,108 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter
}
}
},
+ OperandCode::ModRM_0x660fae => {
+ let modrm = read_modrm(&mut bytes_iter, length)?;
+ if modrm < 0xc0 {
+ instruction.opcode = match (modrm >> 3) & 7 {
+ 6 => {
+ Opcode::CLWB
+ }
+ 7 => {
+ Opcode::CLFLUSHOPT
+ }
+ _ => {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ }
+ };
+ instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, 1 /* opwidth */, length)?;
+ instruction.operand_count = 1;
+ } else {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ }
+ },
+ OperandCode::ModRM_0xf30fae => {
+ let modrm = read_modrm(&mut bytes_iter, length)?;
+
+ if (modrm & 0xc0) == 0xc0 {
+ let r = (modrm >> 3) & 7;
+ let m = modrm & 7;
+ match r {
+ 0 => {
+ instruction.opcode = Opcode::RDFSBASE;
+ let opwidth = if instruction.prefixes.rex().w() {
+ RegisterBank::Q
+ } else {
+ RegisterBank::D
+ };
+ instruction.modrm_mmm = RegSpec::from_parts(m, instruction.prefixes.rex().x(), opwidth);
+ instruction.operands[0] = OperandSpec::RegMMM;
+ instruction.operand_count = 1;
+ }
+ 1 => {
+ instruction.opcode = Opcode::RDGSBASE;
+ let opwidth = if instruction.prefixes.rex().w() {
+ RegisterBank::Q
+ } else {
+ RegisterBank::D
+ };
+ instruction.modrm_mmm = RegSpec::from_parts(m, instruction.prefixes.rex().x(), opwidth);
+ instruction.operands[0] = OperandSpec::RegMMM;
+ instruction.operand_count = 1;
+
+ }
+ 2 => {
+ instruction.opcode = Opcode::WRFSBASE;
+ let opwidth = if instruction.prefixes.rex().w() {
+ RegisterBank::Q
+ } else {
+ RegisterBank::D
+ };
+ instruction.modrm_mmm = RegSpec::from_parts(m, instruction.prefixes.rex().x(), opwidth);
+ instruction.operands[0] = OperandSpec::RegMMM;
+ instruction.operand_count = 1;
+ }
+ 3 => {
+ instruction.opcode = Opcode::WRGSBASE;
+ let opwidth = if instruction.prefixes.rex().w() {
+ RegisterBank::Q
+ } else {
+ RegisterBank::D
+ };
+ instruction.modrm_mmm = RegSpec::from_parts(m, instruction.prefixes.rex().x(), opwidth);
+ instruction.operands[0] = OperandSpec::RegMMM;
+ instruction.operand_count = 1;
+
+ }
+ _ => {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
+ }
+ }
OperandCode::ModRM_0xf30fc7 => {
+ let opwidth = imm_width_from_prefixes_64(SizeCode::vqp, instruction.prefixes);
let modrm = read_modrm(&mut bytes_iter, length)?;
let r = (modrm >> 3) & 7;
match r {
6 => {
instruction.opcode = Opcode::VMXON;
- instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, 1 /* doesn't matter, something using this width is invalid */, length)?;
+ instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, opwidth, length)?;
if instruction.operands[0] == OperandSpec::RegMMM {
+ // this would be invalid as `vmxon`, so fall back to the parse as
+ // f3-prefixed rdrand
+ instruction.opcode = Opcode::RDRAND;
+ }
+ instruction.operand_count = 1;
+ }
+ 7 => {
+ instruction.opcode = Opcode::RDPID;
+ instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, opwidth, length)?;
+ if instruction.operands[0] != OperandSpec::RegMMM {
return Err(DecodeError::InvalidOperand);
}
instruction.operand_count = 1;
diff --git a/src/long_mode/uarch.rs b/src/long_mode/uarch.rs
index b2b1201..94b6b45 100644
--- a/src/long_mode/uarch.rs
+++ b/src/long_mode/uarch.rs
@@ -203,7 +203,9 @@ pub mod intel {
/// common denominator: if you want a `Skylake` decoder with AVX512, something like the
/// following:
/// ```
- /// InstDecoder::skylake().with_avx512_f().with_avx512_dq()
+ /// yaxpeax_x86::long_mode::uarch::intel::skylake()
+ /// .with_avx512_f()
+ /// .with_avx512_dq();
/// ```
/// is likely your best option.
pub fn skylake() -> InstDecoder {
diff --git a/src/long_mode/vex.rs b/src/long_mode/vex.rs
index fe50c4e..cf30622 100644
--- a/src/long_mode/vex.rs
+++ b/src/long_mode/vex.rs
@@ -83,6 +83,10 @@ enum VEXOperandCode {
Ed_G_xmm,
G_xmm_Ed,
G_xmm_Eq,
+ G_E_V,
+ G_V_E,
+ G_E_Ib,
+ BMI1_F3,
}
#[inline(never)]
@@ -694,6 +698,88 @@ fn read_vex_operands<T: Iterator<Item=u8>>(bytes: &mut T, instruction: &mut Inst
instruction.operands[2] = OperandSpec::RegVex;
Ok(())
}
+ VEXOperandCode::G_V_E => {
+ let modrm = read_modrm(bytes, length)?;
+ let (opwidth, bank) = if instruction.prefixes.vex().w() {
+ (8, RegisterBank::Q)
+ } else {
+ (4, RegisterBank::D)
+ };
+ instruction.modrm_rrr =
+ RegSpec::from_parts((modrm >> 3) & 7,instruction.prefixes.vex().x(), bank);
+ instruction.vex_reg.bank = bank;
+ let mem_oper = read_E(bytes, instruction, modrm, opwidth, length)?;
+ instruction.operands[0] = OperandSpec::RegRRR;
+ instruction.operands[1] = OperandSpec::RegVex;
+ instruction.operands[2] = mem_oper;
+ instruction.operand_count = 3;
+ Ok(())
+ }
+ VEXOperandCode::G_E_V => {
+ let modrm = read_modrm(bytes, length)?;
+ let (opwidth, bank) = if instruction.prefixes.vex().w() {
+ (8, RegisterBank::Q)
+ } else {
+ (4, RegisterBank::D)
+ };
+ instruction.modrm_rrr =
+ RegSpec::from_parts((modrm >> 3) & 7,instruction.prefixes.vex().x(), bank);
+ instruction.vex_reg.bank = bank;
+ let mem_oper = read_E(bytes, instruction, modrm, opwidth, length)?;
+ instruction.operands[0] = OperandSpec::RegRRR;
+ instruction.operands[1] = mem_oper;
+ instruction.operands[2] = OperandSpec::RegVex;
+ instruction.operand_count = 3;
+ Ok(())
+ }
+ VEXOperandCode::G_E_Ib => {
+ let modrm = read_modrm(bytes, length)?;
+ let (opwidth, bank) = if instruction.prefixes.vex().w() {
+ (8, RegisterBank::Q)
+ } else {
+ (4, RegisterBank::D)
+ };
+ instruction.modrm_rrr =
+ RegSpec::from_parts((modrm >> 3) & 7,instruction.prefixes.vex().x(), bank);
+ let mem_oper = read_E(bytes, instruction, modrm, opwidth, length)?;
+ instruction.operands[0] = OperandSpec::RegRRR;
+ instruction.operands[1] = mem_oper;
+ instruction.imm = read_imm_unsigned(bytes, 1, length)?;
+ instruction.operands[2] = OperandSpec::ImmI8;
+ instruction.operand_count = 3;
+ Ok(())
+ }
+ VEXOperandCode::BMI1_F3 => {
+ let modrm = read_modrm(bytes, length)?;
+ instruction.opcode = match (modrm >> 3) & 7 {
+ 1 => {
+ Opcode::BLSR
+ }
+ 2 => {
+ Opcode::BLSMSK
+ }
+ 3 => {
+ Opcode::BLSI
+ }
+ _ => {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ }
+ };
+ let (opwidth, bank) = if instruction.prefixes.vex().w() {
+ (8, RegisterBank::Q)
+ } else {
+ (4, RegisterBank::D)
+ };
+ instruction.modrm_rrr =
+ RegSpec::from_parts((modrm >> 3) & 7,instruction.prefixes.vex().x(), bank);
+ let mem_oper = read_E(bytes, instruction, modrm, opwidth, length)?;
+ instruction.operands[0] = OperandSpec::RegVex;
+ instruction.operands[1] = mem_oper;
+ instruction.operand_count = 2;
+ instruction.vex_reg.bank = bank;
+ Ok(())
+ }
VEXOperandCode::G_E_ymm_imm8 |
VEXOperandCode::G_V_E_xmm_xmm4 |
@@ -2270,15 +2356,92 @@ fn read_vex_instruction<T: Iterator<Item=u8>>(opcode_map: VEXOpcodeMap, bytes: &
} else {
VEXOperandCode::G_V_E_xmm
}),
+ 0xF7 => (Opcode::SHLX, if L {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ } else {
+ VEXOperandCode::G_E_V
+ }),
+ _ => {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
+ } else if let VEXOpcodePrefix::PrefixF2 = p {
+ match opc {
+ 0xF5 => (Opcode::PDEP, if L {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ } else {
+ VEXOperandCode::G_V_E
+ }),
+ 0xF6 => (Opcode::MULX, if L {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ } else {
+ VEXOperandCode::G_V_E
+ }),
+ 0xF7 => (Opcode::SHRX, if L {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ } else {
+ VEXOperandCode::G_E_V
+ }),
+ _ => {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
+ } else if let VEXOpcodePrefix::PrefixF3 = p {
+ match opc {
+ 0xF5 => (Opcode::PEXT, if L {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ } else {
+ VEXOperandCode::G_V_E
+ }),
+ 0xF7 => (Opcode::SARX, if L {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ } else {
+ VEXOperandCode::G_E_V
+ }),
_ => {
instruction.opcode = Opcode::Invalid;
return Err(DecodeError::InvalidOpcode);
}
}
} else {
- // the only VEX* 0f38 instructions have an implied 66 prefix.
- instruction.opcode = Opcode::Invalid;
- return Err(DecodeError::InvalidOpcode);
+ match opc {
+ 0xF2 => (Opcode::ANDN, if L {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ } else {
+ VEXOperandCode::G_V_E
+ }),
+ 0xF3 => (Opcode::Invalid, if L {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ } else {
+ VEXOperandCode::BMI1_F3
+ }),
+ 0xF5 => (Opcode::BZHI, if L {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ } else {
+ VEXOperandCode::G_E_V
+ }),
+ 0xF7 => (Opcode::BEXTR, if L {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ } else {
+ VEXOperandCode::G_E_V
+ }),
+ _ => {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
}
}
VEXOpcodeMap::Map0F3A => {
@@ -2504,6 +2667,19 @@ fn read_vex_instruction<T: Iterator<Item=u8>>(opcode_map: VEXOpcodeMap, bytes: &
return Err(DecodeError::InvalidOpcode);
}
}
+ } else if let VEXOpcodePrefix::PrefixF2 = p {
+ match opc {
+ 0xF0 => (Opcode::RORX, if L {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ } else {
+ VEXOperandCode::G_E_Ib
+ }),
+ _ => {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
} else {
// the only VEX* 0f3a instructions have an implied 66 prefix.
instruction.opcode = Opcode::Invalid;
diff --git a/src/protected_mode/display.rs b/src/protected_mode/display.rs
index ed8d44c..77d39ff 100644
--- a/src/protected_mode/display.rs
+++ b/src/protected_mode/display.rs
@@ -291,6 +291,9 @@ impl fmt::Display for Opcode {
&Opcode::BTC => write!(f, "btc"),
&Opcode::BSF => write!(f, "bsf"),
&Opcode::BSR => write!(f, "bsr"),
+ &Opcode::BZHI => write!(f, "bzhi"),
+ &Opcode::PDEP => write!(f, "pdep"),
+ &Opcode::PEXT => write!(f, "pext"),
&Opcode::TZCNT => write!(f, "tzcnt"),
&Opcode::MOVSS => write!(f, "movss"),
&Opcode::SQRTSS => write!(f, "sqrtss"),
@@ -339,17 +342,27 @@ impl fmt::Display for Opcode {
&Opcode::RDMSR => write!(f, "rdmsr"),
&Opcode::RDTSC => write!(f, "rdtsc"),
&Opcode::RDPMC => write!(f, "rdpmc"),
+ &Opcode::RDPID => write!(f, "rdpid"),
+ &Opcode::RDFSBASE => write!(f, "rdfsbase"),
+ &Opcode::RDGSBASE => write!(f, "rdgsbase"),
+ &Opcode::WRFSBASE => write!(f, "wrfsbase"),
+ &Opcode::WRGSBASE => write!(f, "wrgsbase"),
&Opcode::FXSAVE => write!(f, "fxsave"),
&Opcode::FXRSTOR => write!(f, "fxrstor"),
&Opcode::LDMXCSR => write!(f, "ldmxcsr"),
&Opcode::STMXCSR => write!(f, "stmxcsr"),
&Opcode::XSAVE => write!(f, "xsave"),
+ &Opcode::XSAVEC => write!(f, "xsavec"),
+ &Opcode::XSAVES => write!(f, "xsaves"),
&Opcode::XRSTOR => write!(f, "xrstor"),
+ &Opcode::XRSTORS => write!(f, "xrstors"),
&Opcode::XSAVEOPT => write!(f, "xsaveopt"),
&Opcode::LFENCE => write!(f, "lfence"),
&Opcode::MFENCE => write!(f, "mfence"),
&Opcode::SFENCE => write!(f, "sfence"),
&Opcode::CLFLUSH => write!(f, "clflush"),
+ &Opcode::CLFLUSHOPT => write!(f, "clflushopt"),
+ &Opcode::CLWB => write!(f, "clwb"),
&Opcode::LDS => write!(f, "lds"),
&Opcode::LES => write!(f, "les"),
&Opcode::SGDT => write!(f, "sgdt"),
@@ -437,11 +450,15 @@ impl fmt::Display for Opcode {
&Opcode::SAR => write!(f, "sar"),
&Opcode::SAL => write!(f, "sal"),
&Opcode::SHR => write!(f, "shr"),
+ &Opcode::SARX => write!(f, "sarx"),
+ &Opcode::SHLX => write!(f, "shlx"),
+ &Opcode::SHRX => write!(f, "shrx"),
&Opcode::SHRD => write!(f, "shrd"),
&Opcode::SHL => write!(f, "shl"),
&Opcode::RCR => write!(f, "rcr"),
&Opcode::RCL => write!(f, "rcl"),
&Opcode::ROR => write!(f, "ror"),
+ &Opcode::RORX => write!(f, "rorx"),
&Opcode::ROL => write!(f, "rol"),
&Opcode::CMOVA => write!(f, "cmova"),
&Opcode::CMOVB => write!(f, "cmovb"),
@@ -462,9 +479,11 @@ impl fmt::Display for Opcode {
&Opcode::NEG => write!(f, "neg"),
&Opcode::NOT => write!(f, "not"),
&Opcode::MUL => write!(f, "mul"),
+ &Opcode::MULX => write!(f, "mulx"),
&Opcode::DIV => write!(f, "div"),
&Opcode::IDIV => write!(f, "idiv"),
&Opcode::CMPXCHG => write!(f, "cmpxchg"),
+ &Opcode::CMPXCHG8B => write!(f, "cmpxchg8b"),
&Opcode::MOVSX_b => write!(f, "movsx"),
&Opcode::MOVSX_w => write!(f, "movsx"),
&Opcode::MOVZX_b => write!(f, "movzx"),
@@ -639,6 +658,8 @@ impl fmt::Display for Opcode {
&Opcode::BLSMSK => write!(f, "blsmsk"),
&Opcode::BLSR => write!(f, "blsr"),
&Opcode::VMCLEAR => write!(f, "vmclear"),
+ &Opcode::VMPTRLD => write!(f, "vmptrld"),
+ &Opcode::VMPTRST => write!(f, "vmptrst"),
&Opcode::VMXON => write!(f, "vmxon"),
&Opcode::VMCALL => write!(f, "vmcall"),
&Opcode::VMLAUNCH => write!(f, "vmlaunch"),
@@ -1297,16 +1318,21 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
Opcode::DIV |
Opcode::IDIV |
Opcode::MUL |
+ Opcode::MULX |
Opcode::NEG |
Opcode::NOT |
Opcode::SAR |
Opcode::SAL |
Opcode::SHR |
+ Opcode::SARX |
+ Opcode::SHLX |
+ Opcode::SHRX |
Opcode::SHRD |
Opcode::SHL |
Opcode::RCR |
Opcode::RCL |
Opcode::ROR |
+ Opcode::RORX |
Opcode::ROL |
Opcode::INC |
Opcode::DEC |
@@ -1328,6 +1354,9 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
Opcode::BTC |
Opcode::BSF |
Opcode::BSR |
+ Opcode::BZHI |
+ Opcode::PDEP |
+ Opcode::PEXT |
Opcode::TZCNT |
Opcode::ANDN |
Opcode::BEXTR |
@@ -1819,23 +1848,34 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
Opcode::CMP |
Opcode::CMPPS |
Opcode::CMPPD |
+ Opcode::CMPXCHG8B |
Opcode::CMPXCHG => { write!(out, "{}", colors.comparison_op(self)) }
Opcode::WRMSR |
Opcode::RDMSR |
Opcode::RDTSC |
Opcode::RDPMC |
+ Opcode::RDPID |
+ Opcode::RDFSBASE |
+ Opcode::RDGSBASE |
+ Opcode::WRFSBASE |
+ Opcode::WRGSBASE |
Opcode::FXSAVE |
Opcode::FXRSTOR |
Opcode::LDMXCSR |
Opcode::STMXCSR |
Opcode::XSAVE |
+ Opcode::XSAVEC |
+ Opcode::XSAVES |
Opcode::XRSTOR |
+ Opcode::XRSTORS |
Opcode::XSAVEOPT |
Opcode::LFENCE |
Opcode::MFENCE |
Opcode::SFENCE |
Opcode::CLFLUSH |
+ Opcode::CLFLUSHOPT |
+ Opcode::CLWB |
Opcode::LDS |
Opcode::LES |
Opcode::SGDT |
@@ -1875,6 +1915,8 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
Opcode::VMREAD |
Opcode::VMWRITE |
Opcode::VMCLEAR |
+ Opcode::VMPTRST |
+ Opcode::VMPTRLD |
Opcode::VMXON |
Opcode::VMCALL |
Opcode::VMLAUNCH |
@@ -1982,6 +2024,10 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> ShowContextual<u6
self.opcode.colorize(colors, out)?;
+ if self.opcode == Opcode::XBEGIN {
+ return write!(out, " $+{}", colors.number(signed_i32_hex(self.imm as i32)));
+ }
+
match self.operands[0] {
OperandSpec::Nothing => {
return Ok(());
diff --git a/src/protected_mode/mod.rs b/src/protected_mode/mod.rs
index 89c485f..2fe3b3e 100644
--- a/src/protected_mode/mod.rs
+++ b/src/protected_mode/mod.rs
@@ -478,17 +478,34 @@ const BMI1: [Opcode; 6] = [
Opcode::TZCNT,
];
+const BMI2: [Opcode; 8] = [
+ Opcode::BZHI,
+ Opcode::MULX,
+ Opcode::PDEP,
+ Opcode::PEXT,
+ Opcode::RORX,
+ Opcode::SARX,
+ Opcode::SHRX,
+ Opcode::SHLX,
+];
+
+#[allow(dead_code)]
+const XSAVE: [Opcode; 8] = [
+ Opcode::XGETBV,
+ Opcode::XRSTOR,
+ Opcode::XRSTORS,
+ Opcode::XSAVE,
+ Opcode::XSAVEC,
+ Opcode::XSAVEOPT,
+ Opcode::XSAVES,
+ Opcode::XSETBV,
+];
+
// TODO:
// PTWRITE
-// RDFSBASE
-// RDGSBASE
-// WRFSBASE
-// WRGSBASE
// TPAUSE
// UMONITOR
// UMWAIT
-// CLFLUSHOPT
-// CLWB
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Opcode {
@@ -692,6 +709,8 @@ pub enum Opcode {
MFENCE,
SFENCE,
CLFLUSH,
+ CLFLUSHOPT,
+ CLWB,
WRMSR,
RDTSC,
RDMSR,
@@ -1328,6 +1347,28 @@ pub enum Opcode {
ADOX,
PREFETCHW,
+
+ RDPID,
+ CMPXCHG8B,
+ VMPTRLD,
+ VMPTRST,
+
+ BZHI,
+ MULX,
+ SHLX,
+ SHRX,
+ SARX,
+ PDEP,
+ PEXT,
+ RORX,
+ XRSTORS,
+ XSAVEC,
+ XSAVES,
+
+ RDFSBASE,
+ RDGSBASE,
+ WRFSBASE,
+ WRGSBASE,
}
#[derive(Debug)]
@@ -2044,7 +2085,7 @@ impl InstDecoder {
/// the clearest documentation on when these instructions were reintroduced into 64-bit
/// architectures seems to be
/// [wikipedia](https://en.wikipedia.org/wiki/X86-64#Older_implementations):
- /// ```
+ /// ```text
/// Early AMD64 and Intel 64 CPUs lacked LAHF and SAHF instructions in 64-bit mode. AMD
/// introduced these instructions (also in 64-bit mode) with their Athlon 64, Opteron and
/// Turion 64 revision D processors in March 2005[48][49][50] while Intel introduced the
@@ -2635,9 +2676,8 @@ impl InstDecoder {
* (if CPUID.01H:ECX.POPCNT[bit 23] = 1).
* ```
*/
- if self.intel_quirks() && (!self.sse4_2() || !self.popcnt()) {
- inst.opcode = Opcode::Invalid;
- return Err(DecodeError::InvalidOpcode);
+ if self.intel_quirks() && (self.sse4_2() || self.popcnt()) {
+ return Ok(());
} else if !self.popcnt() {
/*
* elsewhere from the amd APM:
@@ -2744,6 +2784,11 @@ impl InstDecoder {
return Err(DecodeError::InvalidOpcode);
}
}
+ if !self.bmi2() {
+ if BMI2.contains(&other) {
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
}
}
Ok(())
@@ -3066,6 +3111,8 @@ pub enum OperandCode {
ModRM_0x0fae,
ModRM_0x0fba,
ModRM_0xf238,
+ ModRM_0xf30fae,
+ ModRM_0x660fae,
ModRM_0xf30fc7,
ModRM_0x660f38,
ModRM_0xf30f38,
@@ -3444,7 +3491,7 @@ const OPCODE_660F_MAP: [OpcodeRecord; 256] = [
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
- OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
+ OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::ModRM_0x660fae),
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
// 0xb0
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
@@ -4006,7 +4053,7 @@ const OPCODE_F30F_MAP: [OpcodeRecord; 256] = [
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
- OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
+ OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::ModRM_0xf30fae),
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
// 0xb0
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
@@ -5371,13 +5418,12 @@ fn read_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter: T,
return Ok(());
} else {
instruction.opcode = Opcode::XBEGIN;
- instruction.disp = if opwidth == 2 {
+ instruction.imm = if opwidth == 2 {
read_imm_signed(&mut bytes_iter, 2, length)? as i16 as i32 as u32
} else {
read_imm_signed(&mut bytes_iter, 4, length)? as i32 as u32
};
- instruction.modrm_mmm = RegSpec::eip();
- instruction.operands[0] = OperandSpec::RegDisp;
+ instruction.operands[0] = OperandSpec::ImmI32;
instruction.operand_count = 1;
return Ok(());
}
@@ -5618,6 +5664,9 @@ fn read_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter: T,
instruction.imm =
read_num(&mut bytes_iter, 1)? as u8 as u32;
*length += 1;
+ if instruction.operands[1] == OperandSpec::RegMMM {
+ instruction.modrm_mmm.bank = RegisterBank::MM;
+ }
instruction.operands[2] = OperandSpec::ImmI8;
instruction.operand_count = 3;
},
@@ -5747,7 +5796,7 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter
}
OperandCode::ModRM_0x0f0d => {
let modrm = read_modrm(&mut bytes_iter, length)?;
- let r = modrm & 0b111;
+ let r = (modrm >> 3) & 0b111;
let opwidth = imm_width_from_prefixes(SizeCode::vd, instruction.prefixes);
@@ -5813,44 +5862,78 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter
return read_operands(decoder, bytes_iter, instruction, operands, length);
},
OperandCode::ModRM_0x0f3a => {
+ let opcode = read_modrm(&mut bytes_iter, length)?;
+ if opcode == 0xcc {
+ instruction.opcode = Opcode::SHA1RNDS4;
+ return read_operands(decoder, bytes_iter, instruction, OperandCode::G_E_xmm_Ib, length);
+ } else if opcode == 0x0f {
+ instruction.opcode = Opcode::PALIGNR;
+ return read_operands(decoder, bytes_iter, instruction, OperandCode::G_E_mm_Ib, length);
+ }
},
OperandCode::ModRM_0x0fc7 => {
let modrm = read_modrm(&mut bytes_iter, length)?;
- if modrm >> 6 == 0b11 {
- match (modrm >> 3) & 0b111 {
- 0b111 => {
- instruction.opcode = Opcode::RDSEED;
- instruction.operand_count = 1;
- instruction.operands[0] = OperandSpec::RegRRR;
- let opwidth = imm_width_from_prefixes(SizeCode::vd, instruction.prefixes);
- instruction.modrm_rrr =
- RegSpec::from_parts(modrm & 7, match opwidth {
- 4 => RegisterBank::D,
- 2 => RegisterBank::W,
- _ => unreachable!()
- });
+ let is_reg = (modrm & 0xc0) == 0xc0;
+
+ let r = (modrm >> 3) & 0b111;
+
+ let opcode = match r {
+ 0b001 => {
+ if is_reg {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOperand);
+ } else {
+ Opcode::CMPXCHG8B
}
- 0b110 => {
- instruction.opcode = Opcode::RDRAND;
- instruction.operand_count = 1;
- instruction.operands[0] = OperandSpec::RegRRR;
- let opwidth = imm_width_from_prefixes(SizeCode::vd, instruction.prefixes);
- instruction.modrm_rrr =
- RegSpec::from_parts(modrm & 7, match opwidth {
- 4 => RegisterBank::D,
- 2 => RegisterBank::W,
- _ => unreachable!()
- });
+ }
+ 0b011 => {
+ if is_reg {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOperand);
+ } else {
+ Opcode::XRSTORS
}
- _ => {
+ }
+ 0b100 => {
+ if is_reg {
instruction.opcode = Opcode::Invalid;
- return Err(DecodeError::InvalidOpcode);
+ return Err(DecodeError::InvalidOperand);
+ } else {
+ Opcode::XSAVEC
}
}
- } else {
- instruction.opcode = Opcode::Invalid;
- return Err(DecodeError::InvalidOpcode);
- }
+ 0b101 => {
+ if is_reg {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOperand);
+ } else {
+ Opcode::XSAVES
+ }
+ }
+ 0b110 => {
+ if is_reg {
+ Opcode::RDRAND
+ } else {
+ Opcode::VMPTRLD
+ }
+ }
+ 0b111 => {
+ if is_reg {
+ Opcode::RDSEED
+ } else {
+ Opcode::VMPTRST
+ }
+ }
+ _ => {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOperand);
+ }
+ };
+
+ instruction.opcode = opcode;
+ instruction.operand_count = 1;
+ let opwidth = imm_width_from_prefixes(SizeCode::vd, instruction.prefixes);
+ instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, opwidth, length)?;
},
OperandCode::ModRM_0x0f71 => {
instruction.operand_count = 2;
@@ -6137,15 +6220,33 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter
instruction.operands[1] = OperandSpec::ImmU8;
},
OperandCode::ModRM_0x660fc7 => {
+ let opwidth = imm_width_from_prefixes(SizeCode::vd, instruction.prefixes);
let modrm = read_modrm(&mut bytes_iter, length)?;
let r = (modrm >> 3) & 7;
match r {
6 => {
instruction.opcode = Opcode::VMCLEAR;
- instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, 1 /* doesn't matter, something using this width is invalid */, length)?;
+ instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, opwidth, length)?;
if instruction.operands[0] == OperandSpec::RegMMM {
- return Err(DecodeError::InvalidOperand);
+ // this would be invalid as `vmclear`, so fall back to the parse as
+ // 66-prefixed rdrand. this is a register operand, so just demote it to the
+ // word-form operand:
+ instruction.modrm_mmm = RegSpec { bank: RegisterBank::W, num: instruction.modrm_mmm.num };
+ instruction.opcode = Opcode::RDRAND;
+ }
+ instruction.operand_count = 1;
+ }
+ 7 => {
+ instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, opwidth, length)?;
+ if instruction.operands[0] == OperandSpec::RegMMM {
+ // this would be invalid as `vmclear`, so fall back to the parse as
+ // 66-prefixed rdrand. this is a register operand, so just demote it to the
+ // word-form operand:
+ instruction.modrm_mmm = RegSpec { bank: RegisterBank::W, num: instruction.modrm_mmm.num };
+ instruction.opcode = Opcode::RDSEED;
+ } else {
+ return Err(DecodeError::InvalidOpcode);
}
instruction.operand_count = 1;
}
@@ -6154,15 +6255,88 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter
}
}
},
+ OperandCode::ModRM_0x660fae => {
+ let modrm = read_modrm(&mut bytes_iter, length)?;
+ if modrm < 0xc0 {
+ instruction.opcode = match (modrm >> 3) & 7 {
+ 6 => {
+ Opcode::CLWB
+ }
+ 7 => {
+ Opcode::CLFLUSHOPT
+ }
+ _ => {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ }
+ };
+ instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, 1 /* opwidth */, length)?;
+ instruction.operand_count = 1;
+ } else {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ }
+ },
+ OperandCode::ModRM_0xf30fae => {
+ let modrm = read_modrm(&mut bytes_iter, length)?;
+
+ if (modrm & 0xc0) == 0xc0 {
+ let r = (modrm >> 3) & 7;
+ let m = modrm & 7;
+ match r {
+ 0 => {
+ instruction.opcode = Opcode::RDFSBASE;
+ instruction.modrm_mmm = RegSpec::from_parts(m, RegisterBank::D);
+ instruction.operands[0] = OperandSpec::RegMMM;
+ instruction.operand_count = 1;
+ }
+ 1 => {
+ instruction.opcode = Opcode::RDGSBASE;
+ instruction.modrm_mmm = RegSpec::from_parts(m, RegisterBank::D);
+ instruction.operands[0] = OperandSpec::RegMMM;
+ instruction.operand_count = 1;
+
+ }
+ 2 => {
+ instruction.opcode = Opcode::WRFSBASE;
+ instruction.modrm_mmm = RegSpec::from_parts(m, RegisterBank::D);
+ instruction.operands[0] = OperandSpec::RegMMM;
+ instruction.operand_count = 1;
+ }
+ 3 => {
+ instruction.opcode = Opcode::WRGSBASE;
+ instruction.modrm_mmm = RegSpec::from_parts(m, RegisterBank::D);
+ instruction.operands[0] = OperandSpec::RegMMM;
+ instruction.operand_count = 1;
+
+ }
+ _ => {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
+ }
+ }
OperandCode::ModRM_0xf30fc7 => {
+ let opwidth = imm_width_from_prefixes(SizeCode::vd, instruction.prefixes);
let modrm = read_modrm(&mut bytes_iter, length)?;
let r = (modrm >> 3) & 7;
match r {
6 => {
instruction.opcode = Opcode::VMXON;
- instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, 1 /* doesn't matter, something using this width is invalid */, length)?;
+ instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, opwidth, length)?;
if instruction.operands[0] == OperandSpec::RegMMM {
+ // this would be invalid as `vmxon`, so fall back to the parse as
+ // f3-prefixed rdrand
+ instruction.opcode = Opcode::RDRAND;
+ }
+ instruction.operand_count = 1;
+ }
+ 7 => {
+ instruction.opcode = Opcode::RDPID;
+ instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, opwidth, length)?;
+ if instruction.operands[0] != OperandSpec::RegMMM {
return Err(DecodeError::InvalidOperand);
}
instruction.operand_count = 1;
diff --git a/src/protected_mode/uarch.rs b/src/protected_mode/uarch.rs
index b2b1201..20c434f 100644
--- a/src/protected_mode/uarch.rs
+++ b/src/protected_mode/uarch.rs
@@ -203,7 +203,9 @@ pub mod intel {
/// common denominator: if you want a `Skylake` decoder with AVX512, something like the
/// following:
/// ```
- /// InstDecoder::skylake().with_avx512_f().with_avx512_dq()
+ /// yaxpeax_x86::protected_mode::uarch::intel::skylake()
+ /// .with_avx512_f()
+ /// .with_avx512_dq();
/// ```
/// is likely your best option.
pub fn skylake() -> InstDecoder {
diff --git a/src/protected_mode/vex.rs b/src/protected_mode/vex.rs
index b4716d7..b2a845e 100644
--- a/src/protected_mode/vex.rs
+++ b/src/protected_mode/vex.rs
@@ -83,6 +83,10 @@ enum VEXOperandCode {
Ed_G_xmm,
G_xmm_Ed,
G_xmm_Eq,
+ G_E_V,
+ G_V_E,
+ G_E_Ib,
+ BMI1_F3,
}
#[inline(never)]
@@ -691,6 +695,72 @@ fn read_vex_operands<T: Iterator<Item=u8>>(bytes: &mut T, instruction: &mut Inst
instruction.operands[2] = OperandSpec::RegVex;
Ok(())
}
+ VEXOperandCode::G_V_E => {
+ let modrm = read_modrm(bytes, length)?;
+ let (opwidth, bank) = (4, RegisterBank::D);
+ instruction.modrm_rrr =
+ RegSpec::from_parts((modrm >> 3) & 7, bank);
+ instruction.vex_reg.bank = bank;
+ let mem_oper = read_E(bytes, instruction, modrm, opwidth, length)?;
+ instruction.operands[0] = OperandSpec::RegRRR;
+ instruction.operands[1] = OperandSpec::RegVex;
+ instruction.operands[2] = mem_oper;
+ instruction.operand_count = 3;
+ Ok(())
+ }
+ VEXOperandCode::G_E_V => {
+ let modrm = read_modrm(bytes, length)?;
+ let (opwidth, bank) = (4, RegisterBank::D);
+ instruction.modrm_rrr =
+ RegSpec::from_parts((modrm >> 3) & 7, bank);
+ instruction.vex_reg.bank = bank;
+ let mem_oper = read_E(bytes, instruction, modrm, opwidth, length)?;
+ instruction.operands[0] = OperandSpec::RegRRR;
+ instruction.operands[1] = mem_oper;
+ instruction.operands[2] = OperandSpec::RegVex;
+ instruction.operand_count = 3;
+ Ok(())
+ }
+ VEXOperandCode::G_E_Ib => {
+ let modrm = read_modrm(bytes, length)?;
+ let (opwidth, bank) = (4, RegisterBank::D);
+ instruction.modrm_rrr =
+ RegSpec::from_parts((modrm >> 3) & 7, bank);
+ let mem_oper = read_E(bytes, instruction, modrm, opwidth, length)?;
+ instruction.operands[0] = OperandSpec::RegRRR;
+ instruction.operands[1] = mem_oper;
+ instruction.imm = read_imm_unsigned(bytes, 1, length)?;
+ instruction.operands[2] = OperandSpec::ImmI8;
+ instruction.operand_count = 3;
+ Ok(())
+ }
+ VEXOperandCode::BMI1_F3 => {
+ let modrm = read_modrm(bytes, length)?;
+ instruction.opcode = match (modrm >> 3) & 7 {
+ 1 => {
+ Opcode::BLSR
+ }
+ 2 => {
+ Opcode::BLSMSK
+ }
+ 3 => {
+ Opcode::BLSI
+ }
+ _ => {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ }
+ };
+ let (opwidth, bank) = (4, RegisterBank::D);
+ instruction.modrm_rrr =
+ RegSpec::from_parts((modrm >> 3) & 7, bank);
+ let mem_oper = read_E(bytes, instruction, modrm, opwidth, length)?;
+ instruction.operands[0] = OperandSpec::RegVex;
+ instruction.operands[1] = mem_oper;
+ instruction.operand_count = 2;
+ instruction.vex_reg.bank = bank;
+ Ok(())
+ }
VEXOperandCode::G_E_ymm_imm8 |
VEXOperandCode::G_V_E_xmm_xmm4 |
@@ -2267,15 +2337,92 @@ fn read_vex_instruction<T: Iterator<Item=u8>>(opcode_map: VEXOpcodeMap, bytes: &
} else {
VEXOperandCode::G_V_E_xmm
}),
+ 0xF7 => (Opcode::SHLX, if L {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ } else {
+ VEXOperandCode::G_E_V
+ }),
+ _ => {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
+ } else if let VEXOpcodePrefix::PrefixF2 = p {
+ match opc {
+ 0xF5 => (Opcode::PDEP, if L {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ } else {
+ VEXOperandCode::G_V_E
+ }),
+ 0xF6 => (Opcode::MULX, if L {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ } else {
+ VEXOperandCode::G_V_E
+ }),
+ 0xF7 => (Opcode::SHRX, if L {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ } else {
+ VEXOperandCode::G_E_V
+ }),
+ _ => {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
+ } else if let VEXOpcodePrefix::PrefixF3 = p {
+ match opc {
+ 0xF5 => (Opcode::PEXT, if L {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ } else {
+ VEXOperandCode::G_V_E
+ }),
+ 0xF7 => (Opcode::SARX, if L {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ } else {
+ VEXOperandCode::G_E_V
+ }),
_ => {
instruction.opcode = Opcode::Invalid;
return Err(DecodeError::InvalidOpcode);
}
}
} else {
- // the only VEX* 0f38 instructions have an implied 66 prefix.
- instruction.opcode = Opcode::Invalid;
- return Err(DecodeError::InvalidOpcode);
+ match opc {
+ 0xF2 => (Opcode::ANDN, if L {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ } else {
+ VEXOperandCode::G_V_E
+ }),
+ 0xF3 => (Opcode::Invalid, if L {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ } else {
+ VEXOperandCode::BMI1_F3
+ }),
+ 0xF5 => (Opcode::BZHI, if L {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ } else {
+ VEXOperandCode::G_E_V
+ }),
+ 0xF7 => (Opcode::BEXTR, if L {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ } else {
+ VEXOperandCode::G_E_V
+ }),
+ _ => {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
}
}
VEXOpcodeMap::Map0F3A => {
@@ -2501,6 +2648,19 @@ fn read_vex_instruction<T: Iterator<Item=u8>>(opcode_map: VEXOpcodeMap, bytes: &
return Err(DecodeError::InvalidOpcode);
}
}
+ } else if let VEXOpcodePrefix::PrefixF2 = p {
+ match opc {
+ 0xF0 => (Opcode::RORX, if L {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ } else {
+ VEXOperandCode::G_E_Ib
+ }),
+ _ => {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ }
+ }
} else {
// the only VEX* 0f3a instructions have an implied 66 prefix.
instruction.opcode = Opcode::Invalid;
diff --git a/test/long_mode/mod.rs b/test/long_mode/mod.rs
index b37581e..f6f02a1 100644
--- a/test/long_mode/mod.rs
+++ b/test/long_mode/mod.rs
@@ -62,6 +62,7 @@ fn test_mmx() {
test_display(&[0x4f, 0x0f, 0xd1, 0x00], "psrlw mm0, [r8]");
test_invalid(&[0x4f, 0x0f, 0xd7, 0x00]);
test_display(&[0x4f, 0x0f, 0xd7, 0xcf], "pmovmskb r9d, mm7");
+ test_display(&[0x0f, 0x3a, 0x0f, 0xc1, 0x23], "palignr mm0, mm1, 0x23");
}
#[test]
@@ -510,14 +511,14 @@ fn test_0f01() {
test_display(&[0x0f, 0x01, 0xd5], "xend");
test_display(&[0x0f, 0x01, 0xd6], "xtest");
test_display(&[0x0f, 0x01, 0xd7], "enclu");
- test_invalid(&[0x0f, 0x01, 0xd8]);
- test_invalid(&[0x0f, 0x01, 0xd9]);
- test_invalid(&[0x0f, 0x01, 0xda]);
- test_invalid(&[0x0f, 0x01, 0xdb]);
- test_invalid(&[0x0f, 0x01, 0xdc]);
- test_invalid(&[0x0f, 0x01, 0xdd]);
- test_invalid(&[0x0f, 0x01, 0xde]);
- test_invalid(&[0x0f, 0x01, 0xdf]);
+ test_display(&[0x0f, 0x01, 0xd8], "vmrun rax");
+ test_display(&[0x0f, 0x01, 0xd9], "vmmcall");
+ test_display(&[0x0f, 0x01, 0xda], "vmload rax");
+ test_display(&[0x0f, 0x01, 0xdb], "vmsave rax");
+ test_display(&[0x0f, 0x01, 0xdc], "stgi");
+ test_display(&[0x0f, 0x01, 0xdd], "clgi");
+ test_display(&[0x0f, 0x01, 0xde], "skinit eax");
+ test_display(&[0x0f, 0x01, 0xdf], "invlpga rax, ecx");
test_display(&[0x0f, 0x01, 0xee], "rdpkru");
test_display(&[0x0f, 0x01, 0xef], "wrpkru");
test_display(&[0x0f, 0x01, 0xf8], "swapgs");
@@ -771,8 +772,59 @@ fn test_bmi1() {
test_display_under(&bmi1, &[0x41, 0x0f, 0xbc, 0xd3], "tzcnt edx, r11d");
test_display_under(&no_bmi1, &[0x41, 0x0f, 0xbc, 0xd3], "bsf edx, r11d");
- test_display_under(&bmi1, &[0xf3, 0x0f, 0xb8, 0xc1], "popcnt eax, ecx");
- test_display_under(&bmi1, &[0xf3, 0x4f, 0x0f, 0xb8, 0xc1], "popcnt r8, r9");
+ // just 0f38
+ test_display_under(&bmi1, &[0xc4, 0xc2, 0x60, 0xf2, 0x01], "andn eax, ebx, [r9]");
+ test_display_under(&bmi1, &[0xc4, 0xc2, 0xe0, 0xf2, 0x01], "andn rax, rbx, [r9]");
+ test_display_under(&bmi1, &[0xc4, 0xc2, 0x78, 0xf3, 0x09], "blsr eax, [r9]");
+ test_display_under(&bmi1, &[0xc4, 0xc2, 0xf8, 0xf3, 0x09], "blsr rax, [r9]");
+ test_display_under(&bmi1, &[0xc4, 0xc2, 0x78, 0xf3, 0x11], "blsmsk eax, [r9]");
+ test_display_under(&bmi1, &[0xc4, 0xc2, 0xf8, 0xf3, 0x11], "blsmsk rax, [r9]");
+ test_display_under(&bmi1, &[0xc4, 0xc2, 0x78, 0xf3, 0x19], "blsi eax, [r9]");
+ test_display_under(&bmi1, &[0xc4, 0xc2, 0xf8, 0xf3, 0x19], "blsi rax, [r9]");
+ test_display_under(&bmi1, &[0xc4, 0xc2, 0x60, 0xf7, 0x01], "bextr eax, [r9], ebx");
+ test_display_under(&bmi1, &[0xc4, 0xc2, 0xe0, 0xf7, 0x01], "bextr rax, [r9], rbx");
+}
+
+#[test]
+fn test_bmi2() {
+ let bmi2 = InstDecoder::minimal().with_bmi2();
+ // f2 0f3a
+ test_display_under(&bmi2, &[0xc4, 0xc3, 0x7b, 0xf0, 0x01, 0x05], "rorx eax, [r9], 0x5");
+ test_display_under(&bmi2, &[0xc4, 0xc3, 0xfb, 0xf0, 0x01, 0x05], "rorx rax, [r9], 0x5");
+
+ // f2 0f38 map
+ test_display_under(&bmi2, &[0xc4, 0xe2, 0x63, 0xf5, 0x07], "pdep eax, ebx, [rdi]");
+ test_display_under(&bmi2, &[0xc4, 0xe2, 0xe3, 0xf5, 0x07], "pdep rax, rbx, [rdi]");
+ test_display_under(&bmi2, &[0xc4, 0xe2, 0x63, 0xf6, 0x07], "mulx eax, ebx, [rdi]");
+ test_display_under(&bmi2, &[0xc4, 0xe2, 0xe3, 0xf6, 0x07], "mulx rax, rbx, [rdi]");
+ test_display_under(&bmi2, &[0xc4, 0xc2, 0x63, 0xf7, 0x01], "shrx eax, [r9], ebx");
+ test_display_under(&bmi2, &[0xc4, 0xc2, 0xe3, 0xf7, 0x01], "shrx rax, [r9], rbx");
+
+ // f3 0f38 map
+ test_display_under(&bmi2, &[0xc4, 0xe2, 0x62, 0xf5, 0x07], "pext eax, ebx, [rdi]");
+ test_display_under(&bmi2, &[0xc4, 0xe2, 0xe2, 0xf5, 0x07], "pext rax, rbx, [rdi]");
+ test_display_under(&bmi2, &[0xc4, 0xc2, 0x62, 0xf7, 0x01], "sarx eax, [r9], ebx");
+ test_display_under(&bmi2, &[0xc4, 0xc2, 0xe2, 0xf7, 0x01], "sarx rax, [r9], rbx");
+
+ // just 0f38
+ test_display_under(&bmi2, &[0xc4, 0xe2, 0x60, 0xf5, 0x07], "bzhi eax, [rdi], ebx");
+ test_display_under(&bmi2, &[0xc4, 0xe2, 0xe0, 0xf5, 0x07], "bzhi rax, [rdi], rbx");
+
+ // 66 0f38
+ test_display_under(&bmi2, &[0xc4, 0xc2, 0x61, 0xf7, 0x01], "shlx eax, [r9], ebx");
+ test_display_under(&bmi2, &[0xc4, 0xc2, 0xe1, 0xf7, 0x01], "shlx rax, [r9], rbx");
+}
+
+#[test]
+fn test_popcnt() {
+ let popcnt = InstDecoder::minimal().with_popcnt();
+ let intel_popcnt = InstDecoder::minimal().with_intel_quirks().with_sse4_2();
+ let no_popcnt = InstDecoder::minimal();
+ test_display_under(&popcnt, &[0xf3, 0x0f, 0xb8, 0xc1], "popcnt eax, ecx");
+ test_display_under(&intel_popcnt, &[0xf3, 0x0f, 0xb8, 0xc1], "popcnt eax, ecx");
+ test_display_under(&popcnt, &[0xf3, 0x4f, 0x0f, 0xb8, 0xc1], "popcnt r8, r9");
+ test_display_under(&intel_popcnt, &[0xf3, 0x4f, 0x0f, 0xb8, 0xc1], "popcnt r8, r9");
+ test_invalid_under(&no_popcnt, &[0xf3, 0x4f, 0x0f, 0xb8, 0xc1]);
}
#[test]
@@ -821,12 +873,18 @@ fn test_misc() {
test_display(&[0xf3, 0x48, 0xa5], "rep movs es:[rdi], ds:[rsi]");
test_display(&[0xf3, 0x45, 0x0f, 0xbc, 0xd7], "tzcnt r10d, r15d");
- // this is actually vmx
- // test_invalid(&[0x66, 0x0f, 0xc7, 0x03]);
- test_display(&[0x66, 0x4f, 0x0f, 0xc7, 0x33], "vmclear [r11]");
- test_display(&[0x66, 0x0f, 0xc7, 0x33], "vmclear [rbx]");
- test_display(&[0xf3, 0x4f, 0x0f, 0xc7, 0x33], "vmxon [r11]");
- test_display(&[0xf3, 0x0f, 0xc7, 0x33], "vmxon [rbx]");
+ test_display(&[0xf3, 0x0f, 0xae, 0xc4], "rdfsbase esp");
+ test_display(&[0xf3, 0x4f, 0x0f, 0xae, 0xc4], "rdfsbase r12");
+ test_display(&[0xf3, 0x0f, 0xae, 0xcc], "rdgsbase esp");
+ test_display(&[0xf3, 0x4f, 0x0f, 0xae, 0xcc], "rdgsbase r12");
+ test_display(&[0xf3, 0x0f, 0xae, 0xd4], "wrfsbase esp");
+ test_display(&[0xf3, 0x4f, 0x0f, 0xae, 0xd4], "wrfsbase r12");
+ test_display(&[0xf3, 0x0f, 0xae, 0xdc], "wrgsbase esp");
+ test_display(&[0xf3, 0x4f, 0x0f, 0xae, 0xdc], "wrgsbase r12");
+ test_display(&[0x66, 0x0f, 0xae, 0x3f], "clflushopt [rdi]"); // or clflush without 66
+ test_invalid(&[0x66, 0x0f, 0xae, 0xff]);
+ test_display(&[0x66, 0x0f, 0xae, 0x37], "clwb [rdi]");
+ test_invalid(&[0x66, 0x0f, 0xae, 0xf7]);
}
#[test]
@@ -1139,8 +1197,8 @@ fn test_movbe() {
#[test]
fn test_tsx() {
test_display(&[0xc6, 0xf8, 0x10], "xabort 0x10");
- test_display(&[0xc7, 0xf8, 0x10, 0x12, 0x34, 0x56, 0x78], "xbegin 0x78563412");
- test_display(&[0x66, 0xc7, 0xf8, 0x10, 0x12, 0x34], "xbegin 0x3412");
+ test_display(&[0xc7, 0xf8, 0x10, 0x12, 0x34, 0x56], "xbegin $+0x56341210");
+ test_display(&[0x66, 0xc7, 0xf8, 0x10, 0x12], "xbegin $+0x1210");
test_display(&[0x0f, 0x01, 0xd5], "xend");
test_display(&[0x0f, 0x01, 0xd6], "xtest");
}
@@ -1165,3 +1223,31 @@ fn test_sha() {
test_display(&[0x0f, 0x38, 0xcc, 0x12], "sha256msg1 xmm2, [rdx]");
test_display(&[0x0f, 0x38, 0xcd, 0x12], "sha256msg2 xmm2, [rdx]");
}
+
+#[test]
+fn test_vmx() {
+ test_display(&[0x0f, 0xc7, 0x3f], "vmptrst [rdi]");
+ test_display(&[0x0f, 0xc7, 0x37], "vmptrld [rdi]");
+ test_display(&[0xf3, 0x0f, 0xc7, 0xf7], "rdrand edi");
+ test_display(&[0xf3, 0x0f, 0xc7, 0x37], "vmxon [rdi]");
+ test_display(&[0x66, 0x0f, 0xc7, 0xf7], "rdrand di");
+ test_display(&[0x66, 0x0f, 0xc7, 0x37], "vmclear [rdi]");
+
+ // this is actually vmx
+ // test_invalid(&[0x66, 0x0f, 0xc7, 0x03]);
+ test_display(&[0x66, 0x4f, 0x0f, 0xc7, 0x33], "vmclear [r11]");
+ test_display(&[0x66, 0x0f, 0xc7, 0x33], "vmclear [rbx]");
+ test_display(&[0xf3, 0x4f, 0x0f, 0xc7, 0x33], "vmxon [r11]");
+ test_display(&[0xf3, 0x0f, 0xc7, 0x33], "vmxon [rbx]");
+}
+
+#[test]
+fn test_rdpid() {
+ test_display(&[0xf3, 0x0f, 0xc7, 0xfd], "rdpid ebp");
+}
+
+#[test]
+fn test_cmpxchg8b() {
+ test_display(&[0x0f, 0xc7, 0x0f], "cmpxchg8b [rdi]");
+ test_display(&[0x4f, 0x0f, 0xc7, 0x0f], "cmpxchg16b [r15]");
+}
diff --git a/test/long_mode/operand.rs b/test/long_mode/operand.rs
index 1250b8a..f0300d6 100644
--- a/test/long_mode/operand.rs
+++ b/test/long_mode/operand.rs
@@ -1,5 +1,4 @@
-use yaxpeax_arch::{Decoder, LengthedInstruction};
-use yaxpeax_x86::long_mode::{DecodeError, InstDecoder, Opcode, Operand, RegSpec};
+use yaxpeax_x86::long_mode::{Operand, RegSpec};
#[test]
fn register_widths() {
diff --git a/test/protected_mode/mod.rs b/test/protected_mode/mod.rs
index 9fc603d..fc1db2d 100644
--- a/test/protected_mode/mod.rs
+++ b/test/protected_mode/mod.rs
@@ -482,14 +482,14 @@ fn test_0f01() {
test_display(&[0x0f, 0x01, 0xd5], "xend");
test_display(&[0x0f, 0x01, 0xd6], "xtest");
test_display(&[0x0f, 0x01, 0xd7], "enclu");
- test_invalid(&[0x0f, 0x01, 0xd8]);
- test_invalid(&[0x0f, 0x01, 0xd9]);
- test_invalid(&[0x0f, 0x01, 0xda]);
- test_invalid(&[0x0f, 0x01, 0xdb]);
- test_invalid(&[0x0f, 0x01, 0xdc]);
- test_invalid(&[0x0f, 0x01, 0xdd]);
- test_invalid(&[0x0f, 0x01, 0xde]);
- test_invalid(&[0x0f, 0x01, 0xdf]);
+ test_display(&[0x0f, 0x01, 0xd8], "vmrun eax");
+ test_display(&[0x0f, 0x01, 0xd9], "vmmcall");
+ test_display(&[0x0f, 0x01, 0xda], "vmload eax");
+ test_display(&[0x0f, 0x01, 0xdb], "vmsave eax");
+ test_display(&[0x0f, 0x01, 0xdc], "stgi");
+ test_display(&[0x0f, 0x01, 0xdd], "clgi");
+ test_display(&[0x0f, 0x01, 0xde], "skinit eax");
+ test_display(&[0x0f, 0x01, 0xdf], "invlpga eax, ecx");
test_display(&[0x0f, 0x01, 0xee], "rdpkru");
test_display(&[0x0f, 0x01, 0xef], "wrpkru");
test_display(&[0x0f, 0x01, 0xf8], "swapgs");
@@ -716,7 +716,68 @@ fn test_bmi1() {
test_display_under(&bmi1, &[0x0f, 0xbc, 0xd3], "tzcnt edx, ebx");
test_display_under(&no_bmi1, &[0x0f, 0xbc, 0xd3], "bsf edx, ebx");
- test_display_under(&bmi1, &[0xf3, 0x0f, 0xb8, 0xc1], "popcnt eax, ecx");
+ // from the intel manual [`ANDN`, though this is true for `BMI1` generally]:
+ // ```
+ // VEX.W1 is ignored in non-64-bit modes.
+ // ```
+
+ // just 0f38
+ test_display_under(&bmi1, &[0xc4, 0xe2, 0x60, 0xf2, 0x01], "andn eax, ebx, [ecx]");
+ test_display_under(&bmi1, &[0xc4, 0xe2, 0xe0, 0xf2, 0x01], "andn eax, ebx, [ecx]");
+ test_display_under(&bmi1, &[0xc4, 0xe2, 0x78, 0xf3, 0x09], "blsr eax, [ecx]");
+ test_display_under(&bmi1, &[0xc4, 0xe2, 0xf8, 0xf3, 0x09], "blsr eax, [ecx]");
+ test_display_under(&bmi1, &[0xc4, 0xe2, 0x78, 0xf3, 0x11], "blsmsk eax, [ecx]");
+ test_display_under(&bmi1, &[0xc4, 0xe2, 0xf8, 0xf3, 0x11], "blsmsk eax, [ecx]");
+ test_display_under(&bmi1, &[0xc4, 0xe2, 0x78, 0xf3, 0x19], "blsi eax, [ecx]");
+ test_display_under(&bmi1, &[0xc4, 0xe2, 0xf8, 0xf3, 0x19], "blsi eax, [ecx]");
+ test_display_under(&bmi1, &[0xc4, 0xe2, 0x60, 0xf7, 0x01], "bextr eax, [ecx], ebx");
+ test_display_under(&bmi1, &[0xc4, 0xe2, 0xe0, 0xf7, 0x01], "bextr eax, [ecx], ebx");
+}
+
+#[test]
+fn test_bmi2() {
+ let bmi2 = InstDecoder::minimal().with_bmi2();
+
+ // from the intel manual [`PDEP`, though this is true for `BMI2` generally]:
+ // ```
+ // VEX.W1 is ignored in non-64-bit modes.
+ // ```
+
+ // f2 0f3a
+ test_display_under(&bmi2, &[0xc4, 0xe3, 0x7b, 0xf0, 0x01, 0x05], "rorx eax, [ecx], 0x5");
+ test_display_under(&bmi2, &[0xc4, 0xe3, 0xfb, 0xf0, 0x01, 0x05], "rorx eax, [ecx], 0x5");
+
+ // f2 0f38 map
+ test_display_under(&bmi2, &[0xc4, 0xe2, 0x63, 0xf5, 0x07], "pdep eax, ebx, [edi]");
+ test_display_under(&bmi2, &[0xc4, 0xe2, 0xe3, 0xf5, 0x07], "pdep eax, ebx, [edi]");
+ test_display_under(&bmi2, &[0xc4, 0xe2, 0x63, 0xf6, 0x07], "mulx eax, ebx, [edi]");
+ test_display_under(&bmi2, &[0xc4, 0xe2, 0xe3, 0xf6, 0x07], "mulx eax, ebx, [edi]");
+ test_display_under(&bmi2, &[0xc4, 0xe2, 0x63, 0xf7, 0x01], "shrx eax, [ecx], ebx");
+ test_display_under(&bmi2, &[0xc4, 0xe2, 0xe3, 0xf7, 0x01], "shrx eax, [ecx], ebx");
+
+ // f3 0f38 map
+ test_display_under(&bmi2, &[0xc4, 0xe2, 0x62, 0xf5, 0x07], "pext eax, ebx, [edi]");
+ test_display_under(&bmi2, &[0xc4, 0xe2, 0xe2, 0xf5, 0x07], "pext eax, ebx, [edi]");
+ test_display_under(&bmi2, &[0xc4, 0xe2, 0x62, 0xf7, 0x01], "sarx eax, [ecx], ebx");
+ test_display_under(&bmi2, &[0xc4, 0xe2, 0xe2, 0xf7, 0x01], "sarx eax, [ecx], ebx");
+
+ // just 0f38
+ test_display_under(&bmi2, &[0xc4, 0xe2, 0x60, 0xf5, 0x07], "bzhi eax, [edi], ebx");
+ test_display_under(&bmi2, &[0xc4, 0xe2, 0xe0, 0xf5, 0x07], "bzhi eax, [edi], ebx");
+
+ // 66 0f38
+ test_display_under(&bmi2, &[0xc4, 0xe2, 0x61, 0xf7, 0x01], "shlx eax, [ecx], ebx");
+ test_display_under(&bmi2, &[0xc4, 0xe2, 0xe1, 0xf7, 0x01], "shlx eax, [ecx], ebx");
+}
+
+#[test]
+fn test_popcnt() {
+ let popcnt = InstDecoder::minimal().with_popcnt();
+ let intel_popcnt = InstDecoder::minimal().with_intel_quirks().with_sse4_2();
+ let no_popcnt = InstDecoder::minimal();
+ test_display_under(&popcnt, &[0xf3, 0x0f, 0xb8, 0xc1], "popcnt eax, ecx");
+ test_display_under(&intel_popcnt, &[0xf3, 0x0f, 0xb8, 0xc1], "popcnt eax, ecx");
+ test_invalid_under(&no_popcnt, &[0xf3, 0x0f, 0xb8, 0xc1]);
}
#[test]
@@ -768,6 +829,15 @@ fn test_misc() {
// test_invalid(&[0x66, 0x0f, 0xc7, 0x03]);
test_display(&[0x66, 0x0f, 0xc7, 0x33], "vmclear [ebx]");
test_display(&[0xf3, 0x0f, 0xc7, 0x33], "vmxon [ebx]");
+
+ test_display(&[0xf3, 0x0f, 0xae, 0xc4], "rdfsbase esp");
+ test_display(&[0xf3, 0x0f, 0xae, 0xcc], "rdgsbase esp");
+ test_display(&[0xf3, 0x0f, 0xae, 0xd4], "wrfsbase esp");
+ test_display(&[0xf3, 0x0f, 0xae, 0xdc], "wrgsbase esp");
+ test_display(&[0x66, 0x0f, 0xae, 0x3f], "clflushopt [edi]"); // or clflush without 66
+ test_invalid(&[0x66, 0x0f, 0xae, 0xff]);
+ test_display(&[0x66, 0x0f, 0xae, 0x37], "clwb [edi]");
+ test_invalid(&[0x66, 0x0f, 0xae, 0xf7]);
}
#[test]
@@ -1075,8 +1145,8 @@ fn test_movbe() {
#[test]
fn test_tsx() {
test_display(&[0xc6, 0xf8, 0x10], "xabort 0x10");
- test_display(&[0xc7, 0xf8, 0x10, 0x12, 0x34, 0x56, 0x78], "xbegin 0x78563412");
- test_display(&[0x66, 0xc7, 0xf8, 0x10, 0x12, 0x34], "xbegin 0x3412");
+ test_display(&[0xc7, 0xf8, 0x10, 0x12, 0x34, 0x56], "xbegin $+0x56341210");
+ test_display(&[0x66, 0xc7, 0xf8, 0x10, 0x12], "xbegin $+0x1210");
test_display(&[0x0f, 0x01, 0xd5], "xend");
test_display(&[0x0f, 0x01, 0xd6], "xtest");
}
diff --git a/test/protected_mode/operand.rs b/test/protected_mode/operand.rs
index 8fda181..8ffd446 100644
--- a/test/protected_mode/operand.rs
+++ b/test/protected_mode/operand.rs
@@ -1,5 +1,4 @@
-use yaxpeax_arch::{Decoder, LengthedInstruction};
-use yaxpeax_x86::protected_mode::{DecodeError, InstDecoder, Opcode, Operand, RegSpec};
+use yaxpeax_x86::protected_mode::{Operand, RegSpec};
#[test]
fn register_widths() {