From f314ecafcdf3f80cce2d79214bda046cd1535e8c Mon Sep 17 00:00:00 2001 From: iximeow Date: Sun, 14 Mar 2021 23:27:14 -0700 Subject: alternate display mode for c-style expressions --- src/long_mode/display.rs | 400 +++++++++++++++++++++++++++++++++++++--------- src/long_mode/mod.rs | 24 ++- test/long_mode/display.rs | 184 +++++++++++++++++++++ test/long_mode/mod.rs | 1 + 4 files changed, 531 insertions(+), 78 deletions(-) create mode 100644 test/long_mode/display.rs diff --git a/src/long_mode/display.rs b/src/long_mode/display.rs index 1be4a32..52018da 100644 --- a/src/long_mode/display.rs +++ b/src/long_mode/display.rs @@ -2201,10 +2201,40 @@ impl > Colorize fmt::Result { + self.display_with(DisplayStyle::Intel).colorize(&NoColors, fmt) + } +} + +impl<'instr> fmt::Display for InstructionDisplayer<'instr> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { self.colorize(&NoColors, fmt) } } +/// enum controlling how `Instruction::display_with` renders instructions. `Intel` is more or less +/// intel syntax, though memory operand sizes are elided if they can be inferred from other +/// operands. +#[derive(Copy, Clone)] +pub enum DisplayStyle { + /// intel-style syntax for instructions, like + /// `add rax, [rdx + rcx * 2 + 0x1234]` + Intel, + /// C-style syntax for instructions, like + /// `rax += [rdx + rcx * 2 + 0x1234]` + C, + // one might imagine an ATT style here, which is mostly interesting for reversing operand + // order. + // well. + // it also complicates memory operands in an offset-only operand, and is just kind of awful, so + // it's just not implemented yet. + // ATT, +} + +pub struct InstructionDisplayer<'instr> { + pub(crate) instr: &'instr Instruction, + pub(crate) style: DisplayStyle, +} + /* * Can't implement this as accepting a formatter because rust * doesn't let me build one outside println! or write! or whatever. @@ -2218,7 +2248,7 @@ impl fmt::Display for Instruction { * so write to some Write thing i guess. bite me. i really just want to * stop thinking about how to support printing instructions... */ -impl > Colorize for Instruction { +impl <'instr, T: fmt::Write, Color: fmt::Display, Y: YaxColors> Colorize for InstructionDisplayer<'instr> { fn colorize(&self, colors: &Y, out: &mut T) -> fmt::Result { // TODO: I DONT LIKE THIS, there is no address i can give contextualize here, // the address operand maybe should be optional.. @@ -2231,104 +2261,320 @@ struct NoContext; impl Instruction { pub fn write_to(&self, out: &mut T) -> fmt::Result { - self.contextualize(&NoColors, 0, Some(&NoContext), out) + self.display_with(DisplayStyle::Intel).contextualize(&NoColors, 0, Some(&NoContext), out) } } -impl > ShowContextual for Instruction { - fn contextualize(&self, colors: &Y, _address: u64, _context: Option<&NoContext>, out: &mut T) -> fmt::Result { - if self.prefixes.lock() { - write!(out, "lock ")?; - } +fn contextualize_intel>(instr: &Instruction, colors: &Y, _address: u64, _context: Option<&NoContext>, out: &mut T) -> fmt::Result { + if instr.prefixes.lock() { + write!(out, "lock ")?; + } - if self.prefixes.rep_any() { - if [Opcode::MOVS, Opcode::CMPS, Opcode::LODS, Opcode::STOS, Opcode::INS, Opcode::OUTS].contains(&self.opcode) { - // only a few of you actually use the prefix... - if self.prefixes.rep() { - write!(out, "rep ")?; - } else if self.prefixes.repz() { - write!(out, "repz ")?; - } else if self.prefixes.repnz() { - write!(out, "repnz ")?; - } + if instr.prefixes.rep_any() { + if [Opcode::MOVS, Opcode::CMPS, Opcode::LODS, Opcode::STOS, Opcode::INS, Opcode::OUTS].contains(&instr.opcode) { + // only a few of you actually use the prefix... + if instr.prefixes.rep() { + write!(out, "rep ")?; + } else if instr.prefixes.repz() { + write!(out, "repz ")?; + } else if instr.prefixes.repnz() { + write!(out, "repnz ")?; } } + } - out.write_str(self.opcode.name())?; + out.write_str(instr.opcode.name())?; - if self.opcode == Opcode::XBEGIN { - return write!(out, " $+{}", colors.number(signed_i32_hex(self.imm as i32))); - } + if instr.opcode == Opcode::XBEGIN { + return write!(out, " $+{}", colors.number(signed_i32_hex(instr.imm as i32))); + } - if self.operand_count > 0 { - out.write_str(" ")?; + if instr.operand_count > 0 { + out.write_str(" ")?; - if let Some(prefix) = self.segment_override_for_op(0) { - write!(out, "{}:", prefix)?; - } + if let Some(prefix) = instr.segment_override_for_op(0) { + write!(out, "{}:", prefix)?; + } - let x = Operand::from_spec(self, self.operands[0]); - x.colorize(colors, out)?; + let x = Operand::from_spec(instr, instr.operands[0]); + x.colorize(colors, out)?; - for i in 1..self.operand_count { - match self.opcode { - Opcode::MOVSX_b | - Opcode::MOVZX_b => { - match &self.operands[i as usize] { - &OperandSpec::Nothing => { - return Ok(()); - }, - &OperandSpec::RegMMM => { - out.write_str(", ")?; - } - _ => { - out.write_str(", byte ")?; - if let Some(prefix) = self.segment_override_for_op(i) { - write!(out, "{}:", prefix)?; - } - } + for i in 1..instr.operand_count { + match instr.opcode { + Opcode::MOVSX_b | + Opcode::MOVZX_b => { + match &instr.operands[i as usize] { + &OperandSpec::Nothing => { + return Ok(()); + }, + &OperandSpec::RegMMM => { + out.write_str(", ")?; } - let x = Operand::from_spec(self, self.operands[i as usize]); - x.colorize(colors, out)? - }, - Opcode::MOVSX_w | - Opcode::MOVZX_w => { - match &self.operands[i as usize] { - &OperandSpec::Nothing => { - return Ok(()); - }, - &OperandSpec::RegMMM => { - out.write_str(", ")?; + _ => { + out.write_str(", byte ")?; + if let Some(prefix) = instr.segment_override_for_op(i) { + write!(out, "{}:", prefix)?; } - _ => { - out.write_str(", word ")?; - if let Some(prefix) = self.segment_override_for_op(1) { - write!(out, "{}:", prefix)?; - } + } + } + let x = Operand::from_spec(instr, instr.operands[i as usize]); + x.colorize(colors, out)? + }, + Opcode::MOVSX_w | + Opcode::MOVZX_w => { + match &instr.operands[i as usize] { + &OperandSpec::Nothing => { + return Ok(()); + }, + &OperandSpec::RegMMM => { + out.write_str(", ")?; + } + _ => { + out.write_str(", word ")?; + if let Some(prefix) = instr.segment_override_for_op(1) { + write!(out, "{}:", prefix)?; } } - let x = Operand::from_spec(self, self.operands[i as usize]); - x.colorize(colors, out)? - }, - _ => { - match &self.operands[i as usize] { - &OperandSpec::Nothing => { - return Ok(()); - }, - _ => { - out.write_str(", ")?; - if let Some(prefix) = self.segment_override_for_op(1) { - write!(out, "{}:", prefix)?; - } - let x = Operand::from_spec(self, self.operands[i as usize]); - x.colorize(colors, out)? + } + let x = Operand::from_spec(instr, instr.operands[i as usize]); + x.colorize(colors, out)? + }, + _ => { + match &instr.operands[i as usize] { + &OperandSpec::Nothing => { + return Ok(()); + }, + _ => { + out.write_str(", ")?; + if let Some(prefix) = instr.segment_override_for_op(1) { + write!(out, "{}:", prefix)?; } + let x = Operand::from_spec(instr, instr.operands[i as usize]); + x.colorize(colors, out)? } } } } } - Ok(()) + } + Ok(()) +} + +fn contextualize_c>(instr: &Instruction, _colors: &Y, _address: u64, _context: Option<&NoContext>, out: &mut T) -> fmt::Result { + let mut brace_count = 0; + + if instr.prefixes.lock() { + out.write_str("lock { ")?; + brace_count += 1; + } + + if instr.prefixes.rep_any() { + if [Opcode::MOVS, Opcode::CMPS, Opcode::LODS, Opcode::STOS, Opcode::INS, Opcode::OUTS].contains(&instr.opcode) { + let word_str = match instr.mem_size { + 1 => "byte", + 2 => "word", + 4 => "dword", + 8 => "qword", + _ => { unreachable!("invalid word size") } + }; + + // only a few of you actually use the prefix... + if instr.prefixes.rep() { + out.write_str("rep ")?; + } else if instr.prefixes.repz() { + out.write_str("repz ")?; + } else if instr.prefixes.repnz() { + out.write_str("repnz ")?; + } // TODO: other rep kinds? + + out.write_str(word_str)?; + out.write_str(" { ")?; + brace_count += 1; + } + } + + match instr.opcode { + Opcode::Invalid => { out.write_str("invalid")?; }, + Opcode::MOVS => { + out.write_str("es:[rdi++] = ds:[rsi++]")?; + }, + Opcode::CMPS => { + out.write_str("rflags = flags(ds:[rsi++] - es:[rdi++])")?; + }, + Opcode::LODS => { + // TODO: size + out.write_str("rax = ds:[rsi++]")?; + }, + Opcode::STOS => { + // TODO: size + out.write_str("es:[rdi++] = rax")?; + }, + Opcode::INS => { + // TODO: size + out.write_str("es:[rdi++] = port(dx)")?; + }, + Opcode::OUTS => { + // TODO: size + out.write_str("port(dx) = ds:[rsi++]")?; + } + Opcode::ADD => { + write!(out, "{} += {}", instr.operand(0), instr.operand(1))?; + } + Opcode::OR => { + write!(out, "{} |= {}", instr.operand(0), instr.operand(1))?; + } + Opcode::ADC => { + write!(out, "{} += {} + rflags.cf", instr.operand(0), instr.operand(1))?; + } + Opcode::ADCX => { + write!(out, "{} += {} + rflags.cf", instr.operand(0), instr.operand(1))?; + } + Opcode::ADOX => { + write!(out, "{} += {} + rflags.of", instr.operand(0), instr.operand(1))?; + } + Opcode::SBB => { + write!(out, "{} -= {} + rflags.cf", instr.operand(0), instr.operand(1))?; + } + Opcode::AND => { + write!(out, "{} &= {}", instr.operand(0), instr.operand(1))?; + } + Opcode::XOR => { + write!(out, "{} ^= {}", instr.operand(0), instr.operand(1))?; + } + Opcode::SUB => { + write!(out, "{} -= {}", instr.operand(0), instr.operand(1))?; + } + Opcode::CMP => { + write!(out, "rflags = flags({} - {})", instr.operand(0), instr.operand(1))?; + } + Opcode::TEST => { + write!(out, "rflags = flags({} & {})", instr.operand(0), instr.operand(1))?; + } + Opcode::XADD => { + write!(out, "({}, {}) = ({} + {}, {})", instr.operand(0), instr.operand(1), instr.operand(0), instr.operand(1), instr.operand(0))?; + } + Opcode::BT => { + write!(out, "bt")?; + } + Opcode::BTS => { + write!(out, "bts")?; + } + Opcode::BTC => { + write!(out, "btc")?; + } + Opcode::BSR => { + write!(out, "{} = msb({})", instr.operand(0), instr.operand(1))?; + } + Opcode::BSF => { + write!(out, "{} = lsb({}) (x86 bsf)", instr.operand(0), instr.operand(1))?; + } + Opcode::TZCNT => { + write!(out, "{} = lsb({})", instr.operand(0), instr.operand(1))?; + } + Opcode::MOV => { + write!(out, "{} = {}", instr.operand(0), instr.operand(1))?; + } + Opcode::SAR => { + write!(out, "{} = {} >>> {}", instr.operand(0), instr.operand(0), instr.operand(1))?; + } + Opcode::SAL => { + write!(out, "{} = {} <<< {}", instr.operand(0), instr.operand(0), instr.operand(1))?; + } + Opcode::SHR => { + write!(out, "{} = {} >> {}", instr.operand(0), instr.operand(0), instr.operand(1))?; + } + Opcode::SHRX => { + write!(out, "{} = {} >> {} (x86 shrx)", instr.operand(0), instr.operand(1), instr.operand(2))?; + } + Opcode::SHL => { + write!(out, "{} = {} << {}", instr.operand(0), instr.operand(0), instr.operand(1))?; + } + Opcode::SHLX => { + write!(out, "{} = {} << {} (x86 shlx)", instr.operand(0), instr.operand(1), instr.operand(2))?; + } + Opcode::ROR => { + write!(out, "{} = {} ror {}", instr.operand(0), instr.operand(0), instr.operand(1))?; + } + Opcode::RORX => { + write!(out, "{} = {} ror {} (x86 rorx)", instr.operand(0), instr.operand(1), instr.operand(2))?; + } + Opcode::ROL => { + write!(out, "{} = {} rol {}", instr.operand(0), instr.operand(0), instr.operand(1))?; + } + Opcode::RCR => { + write!(out, "{} = {} rcr {}", instr.operand(0), instr.operand(0), instr.operand(1))?; + } + Opcode::RCL => { + write!(out, "{} = {} rcl {}", instr.operand(0), instr.operand(0), instr.operand(1))?; + } + Opcode::PUSH => { + write!(out, "push({})", instr.operand(0))?; + } + Opcode::POP => { + write!(out, "{} = pop()", instr.operand(0))?; + } + Opcode::MOVD => { + write!(out, "{} = movd({})", instr.operand(0), instr.operand(1))?; + } + Opcode::MOVQ => { + write!(out, "{} = movq({})", instr.operand(0), instr.operand(1))?; + } + Opcode::MOVNTQ => { + write!(out, "{} = movntq({})", instr.operand(0), instr.operand(1))?; + } + Opcode::INC => { + write!(out, "{}++", instr.operand(0))?; + } + Opcode::DEC => { + write!(out, "{}--", instr.operand(0))?; + } + Opcode::JG => { + write!(out, "if greater(rflags) then jmp {}", instr.operand(0))?; + } + Opcode::NOP => { + write!(out, "nop")?; + } + _ => { + if instr.operand_count() == 0 { + write!(out, "{}()", instr.opcode())?; + } else { + write!(out, "{} = {}({}", instr.operand(0), instr.opcode(), instr.operand(0))?; + let mut comma = true; + for i in 1..instr.operand_count() { + if comma { + write!(out, ", ")?; + } + write!(out, "{}", instr.operand(i))?; + comma = true; + } + write!(out, ")")?; + } + } + } + + while brace_count > 0 { + out.write_str(" }")?; + brace_count -= 1; + } + + Ok(()) +} + +impl <'instr, T: fmt::Write, Color: fmt::Display, Y: YaxColors> ShowContextual for InstructionDisplayer<'instr> { + fn contextualize(&self, colors: &Y, address: u64, context: Option<&NoContext>, out: &mut T) -> fmt::Result { + let InstructionDisplayer { + instr, + style, + } = self; + + match style { + DisplayStyle::Intel => { + contextualize_intel(instr, colors, address, context, out) + } + DisplayStyle::C => { + contextualize_c(instr, colors, address, context, out) + } + } } } diff --git a/src/long_mode/mod.rs b/src/long_mode/mod.rs index ff5e43b..6349aa4 100644 --- a/src/long_mode/mod.rs +++ b/src/long_mode/mod.rs @@ -3,6 +3,9 @@ mod vex; mod display; pub mod uarch; +#[cfg(feature = "fmt")] +pub use self::display::DisplayStyle; + use core::hint::unreachable_unchecked; use yaxpeax_arch::{AddressDiff, Decoder, LengthedInstruction}; @@ -1891,6 +1894,7 @@ pub struct Instruction { imm: u64, disp: u64, opcode: Opcode, + mem_size: u8, } impl yaxpeax_arch::Instruction for Instruction { @@ -3449,6 +3453,7 @@ impl Instruction { Instruction { prefixes: Prefixes::new(0), opcode: Opcode::Invalid, + mem_size: 1, modrm_rrr: RegSpec::rax(), modrm_mmm: RegSpec::rax(), // doubles as sib_base sib_index: RegSpec::rax(), @@ -3514,6 +3519,14 @@ impl Instruction { } } } + + #[cfg(feature = "fmt")] + pub fn display_with<'a>(&'a self, style: display::DisplayStyle) -> display::InstructionDisplayer<'a> { + display::InstructionDisplayer { + style, + instr: self, + } + } } #[derive(Debug, Copy, Clone)] @@ -5855,6 +5868,7 @@ fn read_instr>(decoder: &InstDecoder, mut bytes_iter: T, in // into one `mov 0, dword [instruction + modrm_mmm_offset]` instruction.modrm_mmm = RegSpec::rax(); instruction.sib_index = RegSpec::rax(); + instruction.mem_size = 0; fn escapes_are_prefixes_actually(prefixes: &mut Prefixes, opc_map: &mut Option) { match opc_map { @@ -6161,6 +6175,7 @@ fn read_operands>(decoder: &InstDecoder, mut bytes_iter: T, if !operand_code.has_byte_operands() { // further, this is an vdq E opwidth = imm_width_from_prefixes_64(SizeCode::vqp, instruction.prefixes); + instruction.mem_size = opwidth; if opwidth == 4 { bank = RegisterBank::D; } else if opwidth == 2 { @@ -6168,6 +6183,7 @@ fn read_operands>(decoder: &InstDecoder, mut bytes_iter: T, } } else { opwidth = 1; + instruction.mem_size = opwidth; if instruction.prefixes.rex().present() { bank = RegisterBank::rB; } else { @@ -7709,11 +7725,13 @@ fn unlikely_operands>(decoder: &InstDecoder, mut bytes_iter instruction.modrm_mmm = RegSpec::rsi(); instruction.operands[0] = OperandSpec::RegRRR; instruction.operands[1] = OperandSpec::Deref; + instruction.mem_size = 1; instruction.operand_count = 2; } OperandCode::Yb_Xb => { instruction.operands[0] = OperandSpec::Deref_rdi; instruction.operands[1] = OperandSpec::Deref_rsi; + instruction.mem_size = 1; instruction.operand_count = 2; } OperandCode::Yb_AL => { @@ -7721,6 +7739,7 @@ fn unlikely_operands>(decoder: &InstDecoder, mut bytes_iter instruction.modrm_mmm = RegSpec::rsi(); instruction.operands[0] = OperandSpec::Deref; instruction.operands[1] = OperandSpec::RegRRR; + instruction.mem_size = 1; instruction.operand_count = 2; } OperandCode::AX_Xv => { @@ -7733,6 +7752,7 @@ fn unlikely_operands>(decoder: &InstDecoder, mut bytes_iter }; instruction.modrm_mmm = RegSpec::rsi(); instruction.operands[1] = OperandSpec::Deref; + instruction.mem_size = opwidth; } OperandCode::Yv_AX => { let opwidth = imm_width_from_prefixes_64(SizeCode::vqp, instruction.prefixes); @@ -7745,9 +7765,11 @@ fn unlikely_operands>(decoder: &InstDecoder, mut bytes_iter instruction.modrm_mmm = RegSpec::rdi(); instruction.operands[0] = OperandSpec::Deref; instruction.operands[1] = OperandSpec::RegRRR; + instruction.mem_size = opwidth; } OperandCode::Yv_Xv => { - // TODO: repsect prefixes + let opwidth = imm_width_from_prefixes_64(SizeCode::vqp, instruction.prefixes); + instruction.mem_size = opwidth; instruction.operands[0] = OperandSpec::Deref_rdi; instruction.operands[1] = OperandSpec::Deref_rsi; } diff --git a/test/long_mode/display.rs b/test/long_mode/display.rs new file mode 100644 index 0000000..988e360 --- /dev/null +++ b/test/long_mode/display.rs @@ -0,0 +1,184 @@ +use std::fmt::Write; + +use yaxpeax_arch::{AddressBase, Decoder, LengthedInstruction}; +use yaxpeax_x86::long_mode::{DisplayStyle, InstDecoder, Opcode}; + +fn test_display(data: &[u8], expected: &'static str) { + test_display_under(&InstDecoder::default(), data, expected); +} + +fn test_display_under(decoder: &InstDecoder, data: &[u8], expected: &'static str) { + let mut hex = String::new(); + for b in data { + write!(hex, "{:02x}", b).unwrap(); + } + match decoder.decode(data.into_iter().map(|x| *x)) { + Ok(instr) => { + let text = format!("{}", instr.display_with(DisplayStyle::C)); + assert!( + text == expected, + "display error for {}:\n decoded: {:?} under decoder {}\n displayed: {}\n expected: {}\n", + hex, + instr, + decoder, + text, + expected + ); + // while we're at it, test that the instruction is as long, and no longer, than its + // input + assert_eq!((0u64.wrapping_offset(instr.len()).to_linear()) as usize, data.len(), "instruction length is incorrect, wanted instruction {}", expected); + }, + Err(e) => { + assert!(false, "decode error ({}) for {} under decoder {}:\n expected: {}\n", e, hex, decoder, expected); + } + } +} + +// decided i do not like at&t syntax much at all. not going to write a formatter for it. some test +// cases will live on in case someone else feels like adding one, or i get mad enough to do it. +#[ignore] +#[test] +fn test_instructions_atnt() { + // just modrm + test_display(&[0x33, 0x08], "xor (%rax), %ecx"); + test_display(&[0x33, 0x20], "xor (%rax), %esp"); + test_display(&[0x33, 0x05, 0x78, 0x56, 0x34, 0x12], "xor 0x12345678(%rip), %eax"); + test_display(&[0x33, 0x41, 0x23], "xor 0x23(%rcx), %eax"); + test_display(&[0x33, 0x81, 0x23, 0x01, 0x65, 0x43], "xor %0x43650123, %eax"); + test_display(&[0x33, 0xc1], "xor %ecx, %eax"); + + // modrm + rex.w + test_display(&[0x48, 0x33, 0x08], "xor (%rax), %rcx"); + test_display(&[0x48, 0x33, 0x20], "xor (%rax), %rsp"); + test_display(&[0x48, 0x33, 0x05, 0x78, 0x56, 0x34, 0x12], "xor 0x12345678(%rip), %rax"); + test_display(&[0x48, 0x33, 0x41, 0x23], "xor 0x23(%rcx), %rax"); + test_display(&[0x48, 0x33, 0x81, 0x23, 0x01, 0x65, 0x43], "xor 0x43650123(%rcx), %rax"); + test_display(&[0x48, 0x33, 0xc1], "xor %rcx, %rax"); + + // modrm + rex.r + test_display(&[0x44, 0x33, 0x08], "xor (%rax), %r9d"); + test_display(&[0x44, 0x33, 0x20], "xor (%rax), %r12d"); + test_display(&[0x44, 0x33, 0x05, 0x78, 0x56, 0x34, 0x12], "xor 0x12345678(%rip), %r8d"); + test_display(&[0x44, 0x33, 0x41, 0x23], "xor 0x23(%rcx), %r8d"); + test_display(&[0x44, 0x33, 0x81, 0x23, 0x01, 0x65, 0x43], "xor 0x43650123(%rcx), %r8d"); + test_display(&[0x44, 0x33, 0xc1], "xor %ecx, %r8d"); + + // modrm + rex.rb + test_display(&[0x45, 0x33, 0x08], "xor (%r8), %r9d"); + test_display(&[0x45, 0x33, 0x20], "xor (%r8), %r12d"); + test_display(&[0x45, 0x33, 0x05, 0x78, 0x56, 0x34, 0x12], "xor 0x12345678(%rip), %r8d"); + test_display(&[0x45, 0x33, 0x41, 0x23], "xor 0x23(%r9), %r8d"); + test_display(&[0x45, 0x33, 0x81, 0x23, 0x01, 0x65, 0x43], "xor 0x43650123(%r9), %r8d"); + test_display(&[0x45, 0x33, 0xc1], "xor %r9d, %r8d"); + + // sib + test_display(&[0x33, 0x04, 0x25, 0x11, 0x22, 0x33, 0x44], "xor (0x44332211), %eax"); + test_display(&[0x41, 0x33, 0x04, 0x25, 0x11, 0x22, 0x33, 0x44], "xor (0x44332211), %eax"); + + test_display(&[0x41, 0x33, 0x44, 0x65, 0x11], "xor 0x11(%r13), %eax"); + + test_display(&[0x42, 0x33, 0x34, 0x25, 0x20, 0x30, 0x40, 0x50], "xor 0x50403020(,%r12,1), %esi"); + + test_display(&[0x4f, 0x0f, 0xe7, 0x03], "movntq %mm0, (%r11)"); + test_display(&[0x0f, 0xe7, 0x03], "movntq %mm0, (%rbx)"); + + test_display(&[0x4f, 0x0f, 0x7f, 0x0f], "movq %mm1, (%r15)"); + test_display(&[0x0f, 0xc4, 0xc0, 0x14], "pinsrw $0x14, %eax, %mm0"); + + test_display(&[0x4f, 0x0f, 0xd1, 0x00], "psrlw (%r8), %mm0"); + test_display(&[0x0f, 0xe5, 0x3d, 0xaa, 0xbb, 0xcc, 0x77], "pmulhw 0x77ccbbaa(%rip), %mm7"); +} + +#[test] +fn test_instructions_c() { + // just modrm + test_display(&[0x33, 0x08], "ecx ^= [rax]"); + test_display(&[0x33, 0x20], "esp ^= [rax]"); + test_display(&[0x33, 0x05, 0x78, 0x56, 0x34, 0x12], "eax ^= [rip + 0x12345678]"); + test_display(&[0x33, 0x41, 0x23], "eax ^= [rcx + 0x23]"); + test_display(&[0x33, 0x81, 0x23, 0x01, 0x65, 0x43], "eax ^= [rcx + 0x43650123]"); + test_display(&[0x33, 0xc1], "eax ^= ecx"); + + // modrm + rex.w + test_display(&[0x48, 0x33, 0x08], "rcx ^= [rax]"); + test_display(&[0x48, 0x33, 0x20], "rsp ^= [rax]"); + test_display(&[0x48, 0x33, 0x05, 0x78, 0x56, 0x34, 0x12], "rax ^= [rip + 0x12345678]"); + test_display(&[0x48, 0x33, 0x41, 0x23], "rax ^= [rcx + 0x23]"); + test_display(&[0x48, 0x33, 0x81, 0x23, 0x01, 0x65, 0x43], "rax ^= [rcx + 0x43650123]"); + test_display(&[0x48, 0x33, 0xc1], "rax ^= rcx"); + + // modrm + rex.r + test_display(&[0x44, 0x33, 0x08], "r9d ^= [rax]"); + test_display(&[0x44, 0x33, 0x20], "r12d ^= [rax]"); + test_display(&[0x44, 0x33, 0x05, 0x78, 0x56, 0x34, 0x12], "r8d ^= [rip + 0x12345678]"); + test_display(&[0x44, 0x33, 0x41, 0x23], "r8d ^= [rcx + 0x23]"); + test_display(&[0x44, 0x33, 0x81, 0x23, 0x01, 0x65, 0x43], "r8d ^= [rcx + 0x43650123]"); + test_display(&[0x44, 0x33, 0xc1], "r8d ^= ecx"); + + // modrm + rex.rb + test_display(&[0x45, 0x33, 0x08], "r9d ^= [r8]"); + test_display(&[0x45, 0x33, 0x20], "r12d ^= [r8]"); + test_display(&[0x45, 0x33, 0x05, 0x78, 0x56, 0x34, 0x12], "r8d ^= [rip + 0x12345678]"); + test_display(&[0x45, 0x33, 0x41, 0x23], "r8d ^= [r9 + 0x23]"); + test_display(&[0x45, 0x33, 0x81, 0x23, 0x01, 0x65, 0x43], "r8d ^= [r9 + 0x43650123]"); + test_display(&[0x45, 0x33, 0xc1], "r8d ^= r9d"); + + // sib + test_display(&[0x33, 0x04, 0x25, 0x11, 0x22, 0x33, 0x44], "eax ^= [0x44332211]"); + test_display(&[0x41, 0x33, 0x04, 0x25, 0x11, 0x22, 0x33, 0x44], "eax ^= [0x44332211]"); + + test_display(&[0x41, 0x33, 0x44, 0x65, 0x11], "eax ^= [r13 + 0x11]"); + + test_display(&[0x42, 0x33, 0x34, 0x25, 0x20, 0x30, 0x40, 0x50], "esi ^= [r12 * 1 + 0x50403020]"); + + test_display(&[0x4f, 0x0f, 0xe7, 0x03], "[r11] = movntq(mm0)"); + test_display(&[0x0f, 0xe7, 0x03], "[rbx] = movntq(mm0)"); + + test_display(&[0x4f, 0x0f, 0x7f, 0x0f], "[r15] = movq(mm1)"); + test_display(&[0x0f, 0xc4, 0xc0, 0x14], "mm0 = pinsrw(mm0, eax, 0x14)"); + + test_display(&[0x4f, 0x0f, 0xd1, 0x00], "mm0 = psrlw(mm0, [r8])"); + test_display(&[0x0f, 0xe5, 0x3d, 0xaa, 0xbb, 0xcc, 0x77], "mm7 = pmulhw(mm7, [rip + 0x77ccbbaa])"); + + test_display(&[0xf3, 0x48, 0xa5], "rep qword { es:[rdi++] = ds:[rsi++] }"); + test_display(&[0xf3, 0xa5], "rep dword { es:[rdi++] = ds:[rsi++] }"); + test_display(&[0xf3, 0x66, 0xa5], "rep word { es:[rdi++] = ds:[rsi++] }"); + test_display(&[0xf3, 0xa4], "rep byte { es:[rdi++] = ds:[rsi++] }"); + + test_display(&[0xf6, 0xc2, 0x18], "rflags = flags(dl & 0x18)"); + test_display(&[0xf6, 0xc2, 0x18], "rflags = flags(dl & 0x18)"); + test_display(&[0x84, 0xc0], "rflags = flags(al & al)"); + test_display(&[0x85, 0xc0], "rflags = flags(eax & eax)"); + test_display(&[0x3a, 0xc0], "rflags = flags(al - al)"); + test_display(&[0x3b, 0xc0], "rflags = flags(eax - eax)"); + + test_display(&[0x41, 0x0f, 0xbc, 0xd3], "edx = lsb(r11d)"); + // test_display(&[0x41, 0x0f, 0xbc, 0xd3], "edx = lsb(r11d) (x86 bsf"); // for non-bm1 + test_display(&[0x41, 0x0f, 0xbd, 0xd3], "edx = msb(r11d)"); + // test_display(&[0x41, 0x0f, 0xbc, 0xd3], "edx = lsb(r11d) (x86 bsr"); // for non-bm1 + test_display(&[0xd2, 0xc0], "al = al rol cl"); + test_display(&[0xd2, 0xc8], "al = al ror cl"); + test_display(&[0xd2, 0xd0], "al = al rcl cl"); + test_display(&[0xd2, 0xd8], "al = al rcr cl"); + test_display(&[0xd2, 0xe0], "al = al << cl"); + test_display(&[0xd2, 0xe8], "al = al >> cl"); + test_display(&[0xd2, 0xf0], "al = al <<< cl"); + test_display(&[0xd2, 0xf8], "al = al >>> cl"); + + test_display(&[0xc4, 0xc3, 0x7b, 0xf0, 0x01, 0x05], "eax = [r9] ror 0x5 (x86 rorx)"); + test_display(&[0xc4, 0xc2, 0xe3, 0xf7, 0x01], "rax = [r9] >> rbx (x86 shrx)"); + test_display(&[0xc4, 0xc2, 0xe1, 0xf7, 0x01], "rax = [r9] << rbx (x86 shlx)"); + + test_display(&[0xd2, 0xe0], "al = al << cl"); + + test_display(&[0x66, 0x0f, 0xac, 0xcf, 0x11], "di = shrd(di, cx, 0x11)"); + test_display(&[0x0f, 0xa5, 0xc9], "ecx = shld(ecx, ecx, cl)"); + + test_display(&[0x66, 0x0f, 0x38, 0xf6, 0x01], "eax += [rcx] + rflags.cf"); + test_display(&[0xf3, 0x4f, 0x0f, 0x38, 0xf6, 0x01], "r8 += [r9] + rflags.of"); + + test_display(&[0xfe, 0x00], "[rax]++"); // TODO: byte + test_display(&[0xfe, 0x08], "[rax]--"); // TODO: byte + test_display(&[0xff, 0x00], "[rax]++"); // TODO: dword + test_display(&[0x48, 0xff, 0x00], "[rax]++"); // TODO: qword +} diff --git a/test/long_mode/mod.rs b/test/long_mode/mod.rs index c3933ab..2fb8833 100644 --- a/test/long_mode/mod.rs +++ b/test/long_mode/mod.rs @@ -1,5 +1,6 @@ mod regspec; mod operand; +mod display; use std::fmt::Write; -- cgit v1.1