From f23d12996aeb4f6e1dbf65e4c8e39376303425c8 Mon Sep 17 00:00:00 2001 From: iximeow Date: Mon, 16 Dec 2019 00:30:10 -0800 Subject: support missing sse3 instructions, add tests for sse3 instructions --- src/lib.rs | 32 +++++++++++++++++------------ test/test.rs | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 83 insertions(+), 16 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1d798ab..15cee14 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2433,7 +2433,6 @@ pub enum OperandCode { Gv_Eb = 0x60, Gv_Ew = 0x61, Gdq_Ed = 0x62, - G_E_xmm = 0x63, G_E_mm_Ib = 0x64, G_E_xmm_Ib = 0x65, AL_Ib = 0x66, @@ -2490,6 +2489,8 @@ pub enum OperandCode { G_U_mm = 0xf1, Ev_Gv_Ib = 0xf3, Ev_Gv_CL = 0xf5, + G_M_xmm = 0xf7, + G_E_xmm = 0xf9, } fn base_opcode_map(v: u8) -> Opcode { @@ -2649,8 +2650,8 @@ 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::Nothing), + OpcodeRecord(Interpretation::Instruction(Opcode::HADDPD), OperandCode::G_E_xmm), + OpcodeRecord(Interpretation::Instruction(Opcode::HSUBPD), OperandCode::G_E_xmm), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), // 0x80 @@ -2739,7 +2740,7 @@ const OPCODE_660F_MAP: [OpcodeRecord; 256] = [ OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), // 0xd0 - OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), + OpcodeRecord(Interpretation::Instruction(Opcode::ADDSUBPD), OperandCode::G_E_xmm), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), @@ -3054,7 +3055,7 @@ const OPCODE_F20F_MAP: [OpcodeRecord; 256] = [ OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), // 0xf0 - OpcodeRecord(Interpretation::Instruction(Opcode::LDDQU), OperandCode::G_E_xmm), + OpcodeRecord(Interpretation::Instruction(Opcode::LDDQU), OperandCode::G_M_xmm), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing), @@ -4758,16 +4759,21 @@ pub fn read_operands>(decoder: &InstDecoder, mut bytes_iter instruction.modrm_mmm.num &= 0b111; instruction.operand_count = 2; }, - OperandCode::G_E_xmm => { - let modrm = read_modrm(&mut bytes_iter, instruction, length)?; - bytes_read = 1; - -// println!("mod_bits: {:2b}, r: {:3b}, m: {:3b}", mod_bits, r, m); - instruction.operands[1] = read_E_xmm(&mut bytes_iter, instruction, modrm, length)?; - instruction.modrm_rrr = - RegSpec::from_parts((modrm >> 3) & 7, instruction.prefixes.rex().r(), RegisterBank::X); + op @ OperandCode::G_M_xmm | + op @ OperandCode::G_E_xmm => { + instruction.modrm_rrr.bank = RegisterBank::X; instruction.operands[0] = OperandSpec::RegRRR; + instruction.operands[1] = mem_oper; instruction.operand_count = 2; + if instruction.operands[1] == OperandSpec::RegMMM { + if op == OperandCode::G_M_xmm { + instruction.opcode = Opcode::Invalid; + return Err(()); + } else { + // fix the register to XMM + instruction.modrm_mmm.bank = RegisterBank::X; + } + } }, OperandCode::G_E_mm_Ib => { let modrm = read_modrm(&mut bytes_iter, instruction, length)?; diff --git a/test/test.rs b/test/test.rs index b8c14a7..e2c52fe 100644 --- a/test/test.rs +++ b/test/test.rs @@ -28,7 +28,7 @@ fn test_invalid(data: &[u8]) { fn test_invalid_under(decoder: &InstDecoder, data: &[u8]) { if let Some(inst) = decoder.decode(data.into_iter().cloned()) { - assert_eq!(inst.opcode, yaxpeax_x86::Opcode::Invalid); + assert_eq!(inst.opcode, yaxpeax_x86::Opcode::Invalid, "decoded {:?} from {:02x?} under decoder {}", inst.opcode, data, decoder); } else { // this is fine } @@ -48,15 +48,16 @@ fn test_display_under(decoder: &InstDecoder, data: &[u8], expected: &'static str let text = format!("{}", instr); assert!( text == expected, - "display error for {}:\n decoded: {:?}\n displayed: {}\n expected: {}\n", + "display error for {}:\n decoded: {:?} under decoder {}\n displayed: {}\n expected: {}\n", hex, instr, + decoder, text, expected ); }, None => { - assert!(false, "decode error for {}:\n expected: {}\n", hex, expected); + assert!(false, "decode error for {} under decoder {}:\n expected: {}\n", hex, decoder, expected); } } } @@ -97,6 +98,66 @@ fn test_cvt() { } #[test] +fn test_sse3() { + fn test_instr(bytes: &[u8], text: &'static str) { + test_display_under(&InstDecoder::minimal().with_sse3(), bytes, text); + test_invalid_under(&InstDecoder::minimal(), bytes); + // avx doesn't imply older instructions are necessarily valid + test_invalid_under(&InstDecoder::minimal().with_avx(), bytes); + // sse4 doesn't imply older instructions are necessarily valid + test_invalid_under(&InstDecoder::minimal().with_sse4_1(), bytes); + test_invalid_under(&InstDecoder::minimal().with_sse4_2(), bytes); + } + + fn test_instr_invalid(bytes: &[u8]) { + test_invalid_under(&InstDecoder::minimal().with_sse3(), bytes); + test_invalid_under(&InstDecoder::default(), bytes); + } + + test_instr(&[0xf2, 0x0f, 0xf0, 0x0f], "lddqu xmm1, [rdi]"); + test_instr_invalid(&[0xf2, 0x0f, 0xf0, 0xcf]); + test_instr(&[0xf2, 0x0f, 0xd0, 0x0f], "addsubps xmm1, [rdi]"); + test_instr(&[0xf2, 0x0f, 0xd0, 0xcf], "addsubps xmm1, xmm7"); + test_instr(&[0xf2, 0x4f, 0x0f, 0xd0, 0xcf], "addsubps xmm9, xmm15"); + test_instr(&[0x66, 0x0f, 0xd0, 0x0f], "addsubpd xmm1, [rdi]"); + test_instr(&[0x66, 0x0f, 0xd0, 0xcf], "addsubpd xmm1, xmm7"); + test_instr(&[0x66, 0x4f, 0x0f, 0xd0, 0xcf], "addsubpd xmm9, xmm15"); + + test_instr(&[0xf2, 0x0f, 0x7c, 0x0f], "haddps xmm1, [rdi]"); + test_instr(&[0xf2, 0x0f, 0x7c, 0xcf], "haddps xmm1, xmm7"); + test_instr(&[0xf2, 0x4f, 0x0f, 0x7c, 0xcf], "haddps xmm9, xmm15"); + test_instr(&[0x66, 0x0f, 0x7c, 0x0f], "haddpd xmm1, [rdi]"); + test_instr(&[0x66, 0x0f, 0x7c, 0xcf], "haddpd xmm1, xmm7"); + test_instr(&[0x66, 0x4f, 0x0f, 0x7c, 0xcf], "haddpd xmm9, xmm15"); + + test_instr(&[0xf2, 0x0f, 0x7d, 0x0f], "hsubps xmm1, [rdi]"); + test_instr(&[0xf2, 0x0f, 0x7d, 0xcf], "hsubps xmm1, xmm7"); + test_instr(&[0xf2, 0x4f, 0x0f, 0x7d, 0xcf], "hsubps xmm9, xmm15"); + test_instr(&[0x66, 0x0f, 0x7d, 0x0f], "hsubpd xmm1, [rdi]"); + test_instr(&[0x66, 0x0f, 0x7d, 0xcf], "hsubpd xmm1, xmm7"); + test_instr(&[0x66, 0x4f, 0x0f, 0x7d, 0xcf], "hsubpd xmm9, xmm15"); + + test_instr(&[0xf3, 0x0f, 0x12, 0x0f], "movsldup xmm1, [rdi]"); + test_instr(&[0xf3, 0x0f, 0x12, 0xcf], "movsldup xmm1, xmm7"); + test_instr(&[0xf3, 0x4f, 0x0f, 0x12, 0xcf], "movsldup xmm9, xmm15"); + test_instr(&[0xf3, 0x0f, 0x16, 0x0f], "movshdup xmm1, [rdi]"); + test_instr(&[0xf3, 0x0f, 0x16, 0xcf], "movshdup xmm1, xmm7"); + test_instr(&[0xf3, 0x4f, 0x0f, 0x16, 0xcf], "movshdup xmm9, xmm15"); + + test_instr(&[0xf2, 0x0f, 0x12, 0x0f], "movddup xmm1, [rdi]"); + test_instr(&[0xf2, 0x0f, 0x12, 0xcf], "movddup xmm1, xmm7"); + test_instr(&[0xf2, 0x4f, 0x0f, 0x12, 0xcf], "movddup xmm9, xmm15"); + + test_instr(&[0x66, 0x0f, 0x01, 0xc8], "monitor"); + test_instr(&[0xf2, 0x0f, 0x01, 0xc8], "monitor"); + test_instr(&[0xf3, 0x0f, 0x01, 0xc8], "monitor"); + + test_instr(&[0x66, 0x0f, 0x01, 0xc9], "mwait"); + test_instr(&[0xf2, 0x0f, 0x01, 0xc9], "mwait"); + test_instr(&[0xf3, 0x0f, 0x01, 0xc9], "mwait"); +} + +#[test] fn test_0f01() { // drawn heavily from "Table A-6. Opcode Extensions for One- and Two-byte Opcodes by Group // Number" -- cgit v1.1