From 9b965525afced37e99543e3a412218207a41e793 Mon Sep 17 00:00:00 2001 From: iximeow Date: Sat, 23 May 2020 19:37:58 -0700 Subject: add SHA, BMI1, and BMI2, complete XSAVE extension support additionally: cmpcxchg{8,16}b, rdrand, rdseed, rdpid, {rd,wr}{fs,gs}base --- src/long_mode/display.rs | 54 +++++++ src/long_mode/mod.rs | 316 ++++++++++++++++++++++++++++++++++------- src/long_mode/uarch.rs | 4 +- src/long_mode/vex.rs | 182 +++++++++++++++++++++++- src/protected_mode/display.rs | 46 ++++++ src/protected_mode/mod.rs | 272 ++++++++++++++++++++++++++++------- src/protected_mode/uarch.rs | 4 +- src/protected_mode/vex.rs | 166 +++++++++++++++++++++- test/long_mode/mod.rs | 122 +++++++++++++--- test/long_mode/operand.rs | 3 +- test/protected_mode/mod.rs | 92 ++++++++++-- test/protected_mode/operand.rs | 3 +- 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 > Colorize> Colorize> Colorize { 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 > Colorize> ShowContextual { 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>(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>(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>(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>(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>(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>(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>(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>(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>(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 > Colorize> Colorize> Colorize { 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 > Colorize> ShowContextual { 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>(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>(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>(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>(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>(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>(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>(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>(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>(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() { -- cgit v1.1