aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2021-03-14 23:27:14 -0700
committeriximeow <me@iximeow.net>2021-03-14 23:27:14 -0700
commitf314ecafcdf3f80cce2d79214bda046cd1535e8c (patch)
tree392a6e3e2942ea9fcee37b50c36a578a58e90208
parent93c53657c2289e979672ee6c4612af7e9eac109c (diff)
alternate display mode for c-style expressions
-rw-r--r--src/long_mode/display.rs400
-rw-r--r--src/long_mode/mod.rs24
-rw-r--r--test/long_mode/display.rs184
-rw-r--r--test/long_mode/mod.rs1
4 files changed, 531 insertions, 78 deletions
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 <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
impl fmt::Display for Instruction {
fn fmt(&self, fmt: &mut fmt::Formatter) -> 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 <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color, Y> for Instruction {
+impl <'instr, T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color, Y> 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<T: fmt::Write>(&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 <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> ShowContextual<u64, NoContext, Color, T, Y> 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<T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>>(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<T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>>(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<Color>> ShowContextual<u64, NoContext, Color, T, Y> 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<T: Iterator<Item=u8>>(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<OpcodeMap>) {
match opc_map {
@@ -6161,6 +6175,7 @@ fn read_operands<T: Iterator<Item=u8>>(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<T: Iterator<Item=u8>>(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<T: Iterator<Item=u8>>(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<T: Iterator<Item=u8>>(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<T: Iterator<Item=u8>>(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<T: Iterator<Item=u8>>(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;