diff options
| author | iximeow <me@iximeow.net> | 2026-06-19 03:02:35 +0000 |
|---|---|---|
| committer | iximeow <me@iximeow.net> | 2026-07-05 00:09:22 +0000 |
| commit | 97dbde69221127d2552cb4fc442b90a2c0ff2a95 (patch) | |
| tree | d31f0fb9c01d3729e8a491f12e59268b466d1178 /src/protected_mode/display/masm.rs | |
| parent | b35fd33629ab53925302f34747faf86ee2f90ace (diff) | |
add DisplayRules, docs, doc tests, ..
this includes `trait DisplayRules` as a generic mechanism to control
parts of instruction printing, a `DefaultRules` for the existing
formatting style, and `AbsoluteAddressFormatter` to print instructions
as at some location in an address space.
Diffstat (limited to 'src/protected_mode/display/masm.rs')
| -rw-r--r-- | src/protected_mode/display/masm.rs | 841 |
1 files changed, 472 insertions, 369 deletions
diff --git a/src/protected_mode/display/masm.rs b/src/protected_mode/display/masm.rs index 88fb275..61db308 100644 --- a/src/protected_mode/display/masm.rs +++ b/src/protected_mode/display/masm.rs @@ -4,25 +4,29 @@ use yaxpeax_arch::AddressBase; use yaxpeax_arch::LengthedInstruction; use crate::protected_mode::{ + DisplayRules, RegSpec, Opcode, Operand, OperandSpec, MergeMode, SaeMode, Instruction, RegisterBank, - display::DisplaySinkExt, OperandVisitor, + display::DisplaySinkExt, }; use yaxpeax_arch::display::DisplaySink; use yaxpeax_arch::safer_unchecked::GetSaferUnchecked as _; use yaxpeax_arch::safer_unchecked::unreachable_kinda_unchecked as unreachable_unchecked; -struct DisplayingOperandVisitor<'a, T> { +struct DisplayingOperandVisitor<'a, 'rules, T, R> { f: &'a mut T, - show_sae: bool, - sae_mode: Option<SaeMode>, + rules: &'rules R, } -impl<'a, T> DisplayingOperandVisitor<'a, T> { - pub fn new(f: &'a mut T) -> Self { - Self { f, show_sae: false, sae_mode: None } +impl<T: DisplaySink, R: DisplayRules<T>> DisplayingOperandVisitor<'_, '_, T, R> { + fn write_register(&mut self, reg: RegSpec) -> Result<(), core::fmt::Error> { + if self.rules.emit_register(reg, &mut self.f)? { + return Ok(()); + } + + self.f.write_reg(reg) } } @@ -64,12 +68,13 @@ static RELATIVE_BRANCHES: [Opcode; 23] = [ Opcode::JLE, Opcode::JG, ]; -struct RelativeBranchPrinter<'a, F: DisplaySink> { +struct RelativeBranchPrinter<'a, F: DisplaySink, Rules: DisplayRules<F>> { inst: &'a Instruction, out: &'a mut F, + rules: &'a Rules, } -impl<'a, F: DisplaySink> crate::protected_mode::OperandVisitor for RelativeBranchPrinter<'a, F> { +impl<'a, F: DisplaySink, Rules: DisplayRules<F>> crate::protected_mode::OperandVisitor for RelativeBranchPrinter<'a, F, Rules> { // return true if we printed a relative branch offset, false otherwise type Ok = bool; // but errors are errors @@ -96,12 +101,14 @@ impl<'a, F: DisplaySink> crate::protected_mode::OperandVisitor for RelativeBranc } else { self.out.write_char('+')?; } - if needs_leading_0(v as u64) { - self.out.write_char('0')?; - } - write!(self.out, "{:X}", v)?; - if hex_ambiguous(v as u64) { - self.out.write_char('h')?; + if !self.rules.emit_signed_immediate(rel as i32, self.out)? { + if needs_leading_0(v as u64) { + self.out.write_char('0')?; + } + write!(self.out, "{:X}", v)?; + if hex_ambiguous(v as u64) { + self.out.write_char('h')?; + } } Ok(true) } else { @@ -129,12 +136,14 @@ impl<'a, F: DisplaySink> crate::protected_mode::OperandVisitor for RelativeBranc } else { self.out.write_char('+')?; } - if needs_leading_0(v as u64) { - self.out.write_char('0')?; - } - write!(self.out, "{:X}", v)?; - if hex_ambiguous(v as u64) { - self.out.write_char('h')?; + if !self.rules.emit_signed_immediate(rel as i32, self.out)? { + if needs_leading_0(v as u64) { + self.out.write_char('0')?; + } + write!(self.out, "{:X}", v)?; + if hex_ambiguous(v as u64) { + self.out.write_char('h')?; + } } Ok(true) } else { @@ -232,12 +241,16 @@ fn masm_displacement<T: core::fmt::Write>(f: &mut T, disp: i32) -> Result<(), co Ok(()) } -impl <T: DisplaySink> crate::protected_mode::OperandVisitor for DisplayingOperandVisitor<'_, T> { +impl <T: DisplaySink, R: DisplayRules<T>> crate::protected_mode::OperandVisitor for DisplayingOperandVisitor<'_, '_, T, R> { type Ok = (); type Error = core::fmt::Error; #[cfg_attr(feature="profiling", inline(never))] fn visit_u8(&mut self, imm: u8) -> Result<Self::Ok, Self::Error> { + if self.rules.emit_unsigned_immediate(imm as u32, self.f)? { + return Ok(()); + } + self.f.span_start_immediate(); if needs_leading_0(imm as u64) { self.f.write_char('0')?; @@ -251,6 +264,10 @@ impl <T: DisplaySink> crate::protected_mode::OperandVisitor for DisplayingOperan } #[cfg_attr(feature="profiling", inline(never))] fn visit_i8(&mut self, imm: i8) -> Result<Self::Ok, Self::Error> { + if self.rules.emit_signed_immediate(imm as i32, self.f)? { + return Ok(()); + } + self.f.span_start_immediate(); let imm = imm as i32 as u32; if needs_leading_0(imm as u64) { @@ -265,6 +282,10 @@ impl <T: DisplaySink> crate::protected_mode::OperandVisitor for DisplayingOperan } #[cfg_attr(feature="profiling", inline(never))] fn visit_u16(&mut self, imm: u16) -> Result<Self::Ok, Self::Error> { + if self.rules.emit_unsigned_immediate(imm as u32, self.f)? { + return Ok(()); + } + self.f.span_start_immediate(); if needs_leading_0(imm as u64) { self.f.write_char('0')?; @@ -278,6 +299,10 @@ impl <T: DisplaySink> crate::protected_mode::OperandVisitor for DisplayingOperan } #[cfg_attr(feature="profiling", inline(never))] fn visit_i16(&mut self, imm: i16) -> Result<Self::Ok, Self::Error> { + if self.rules.emit_signed_immediate(imm as i32, self.f)? { + return Ok(()); + } + self.f.span_start_immediate(); let imm = imm as i32 as u32; if needs_leading_0(imm as u64) { @@ -292,6 +317,10 @@ impl <T: DisplaySink> crate::protected_mode::OperandVisitor for DisplayingOperan } #[cfg_attr(feature="profiling", inline(never))] fn visit_u32(&mut self, imm: u32) -> Result<Self::Ok, Self::Error> { + if self.rules.emit_unsigned_immediate(imm, self.f)? { + return Ok(()); + } + self.f.span_start_immediate(); if needs_leading_0(imm as u64) { self.f.write_char('0')?; @@ -304,6 +333,10 @@ impl <T: DisplaySink> crate::protected_mode::OperandVisitor for DisplayingOperan Ok(()) } fn visit_i32(&mut self, imm: i32) -> Result<Self::Ok, Self::Error> { + if self.rules.emit_signed_immediate(imm, self.f)? { + return Ok(()); + } + self.f.span_start_immediate(); let imm = imm as u32; if needs_leading_0(imm as u64) { @@ -333,7 +366,7 @@ impl <T: DisplaySink> crate::protected_mode::OperandVisitor for DisplayingOperan } Ok(()) } - fn visit_reg_mask_merge_sae(&mut self, spec: RegSpec, mask: RegSpec, merge_mode: MergeMode, sae_mode: crate::protected_mode::SaeMode) -> Result<Self::Ok, Self::Error> { + fn visit_reg_mask_merge_sae(&mut self, spec: RegSpec, mask: RegSpec, merge_mode: MergeMode, _sae_mode: crate::protected_mode::SaeMode) -> Result<Self::Ok, Self::Error> { self.f.write_reg(spec)?; if mask.num != 0 { self.f.write_fixed_size("{")?; @@ -343,8 +376,6 @@ impl <T: DisplaySink> crate::protected_mode::OperandVisitor for DisplayingOperan if let MergeMode::Zero = merge_mode { self.f.write_fixed_size("{z}")?; } - self.show_sae = true; - self.sae_mode = Some(sae_mode); Ok(()) } fn visit_reg_mask_merge_sae_noround(&mut self, spec: RegSpec, mask: RegSpec, merge_mode: MergeMode) -> Result<Self::Ok, Self::Error> { @@ -357,46 +388,50 @@ impl <T: DisplaySink> crate::protected_mode::OperandVisitor for DisplayingOperan if let MergeMode::Zero = merge_mode { self.f.write_fixed_size("{z}")?; } - self.show_sae = true; - self.sae_mode = None; Ok(()) } fn visit_abs_u16(&mut self, imm: u16) -> Result<Self::Ok, Self::Error> { self.f.write_fixed_size("[")?; - self.f.span_start_address(); - if imm >= 0x1000 && needs_leading_0(imm as u64) { - self.f.write_char('0')?; + if !self.rules.emit_absolute_address(imm as u32, self.f)? { + self.f.span_start_address(); + if imm >= 0x1000 && needs_leading_0(imm as u64) { + self.f.write_char('0')?; + } + write!(self.f, "{:04X}", imm)?; + self.f.write_char('h')?; + self.f.span_end_address(); } - write!(self.f, "{:04X}", imm)?; - self.f.write_char('h')?; - self.f.span_end_address(); self.f.write_fixed_size("]")?; Ok(()) } fn visit_abs_u32(&mut self, imm: u32) -> Result<Self::Ok, Self::Error> { self.f.write_fixed_size("[")?; - self.f.span_start_address(); - if imm >= 0x1000_0000 && needs_leading_0(imm as u64) { - self.f.write_char('0')?; + if !self.rules.emit_absolute_address(imm, self.f)? { + self.f.span_start_address(); + if imm >= 0x1000_0000 && needs_leading_0(imm as u64) { + self.f.write_char('0')?; + } + write!(self.f, "{:08X}", imm)?; + self.f.write_char('h')?; + self.f.span_end_address(); } - write!(self.f, "{:08X}", imm)?; - self.f.write_char('h')?; - self.f.span_end_address(); self.f.write_fixed_size("]")?; Ok(()) } fn visit_absolute_far_address(&mut self, segment: u16, address: u32) -> Result<Self::Ok, Self::Error> { - if needs_leading_0(segment as u64) { - self.f.write_char('0')?; - } - write!(self.f, "{:4X}", segment)?; - self.f.write_char('h')?; - self.f.write_fixed_size(":")?; - if needs_leading_0(address as u64) { - self.f.write_char('0')?; + if !self.rules.emit_far_address(segment, address, self.f)? { + if needs_leading_0(segment as u64) { + self.f.write_char('0')?; + } + write!(self.f, "{:4X}", segment)?; + self.f.write_char('h')?; + self.f.write_fixed_size(":")?; + if needs_leading_0(address as u64) { + self.f.write_char('0')?; + } + write!(self.f, "{:4X}", address)?; + self.f.write_char('h')?; } - write!(self.f, "{:4X}", address)?; - self.f.write_char('h')?; Ok(()) } #[cfg_attr(not(feature="profiling"), inline(always))] @@ -570,7 +605,7 @@ impl <T: DisplaySink> crate::protected_mode::OperandVisitor for DisplayingOperan } #[cfg_attr(feature="profiling", inline(never))] -pub(crate) fn contextualize<T: DisplaySink>(instr: &Instruction, out: &mut T) -> fmt::Result { +pub(crate) fn contextualize<T: DisplaySink, R: DisplayRules<T>>(instr: &Instruction, rules: &R, out: &mut T) -> fmt::Result { if instr.xacquire() { out.write_fixed_size("xacquire ")?; } @@ -612,133 +647,119 @@ pub(crate) fn contextualize<T: DisplaySink>(instr: &Instruction, out: &mut T) -> match instr.opcode { Opcode::HRESET => { // dumpbin shows, and MASM needs, the implicit "eax" operand as an explicit textual operand. - out.write_fixed_size(" ")?; - instr.visit_operand(0, &mut DisplayingOperandVisitor::new(out))?; - out.write_fixed_size(", eax")?; + let mut visitor = DisplayingOperandVisitor { + f: out, + rules, + }; + + visitor.f.write_fixed_size(" ")?; + if !rules.emit_operand(instr, 0, visitor.f)? { + instr.visit_operand(0, &mut visitor)?; + } + visitor.f.write_fixed_size(", ")?; + visitor.write_register(RegSpec::eax())?; return Ok(()); } Opcode::LSL => { // dumpbin shows, and MASM needs, the first and second operands to match in size. // this means `lsl eax, edx` is actually shown as `lsl eax, edx`. fix that up here. - let mut visitor = DisplayingOperandVisitor::new(out); + let mut visitor = DisplayingOperandVisitor { + f: out, + rules, + }; let Operand::Register { reg: dest } = instr.operand(0) else { panic!("impossible LSL dest"); }; if let Operand::Register { reg: mut src } = instr.operand(1) { visitor.f.write_fixed_size(" ")?; - instr.visit_operand(0, &mut visitor)?; + if !rules.emit_operand(instr, 0, visitor.f)? { + instr.visit_operand(0, &mut visitor)?; + } src.bank = dest.bank; visitor.f.write_fixed_size(", ")?; - visitor.visit_reg(src)?; + visitor.write_register(src)?; return Ok(()); } else { // don't need to do anything about memory sources }; } - Opcode::PREFETCHNTA => { - // dumpbin doesn't bother with the memory size here, same for masm. - out.write_char(' ')?; - let mut visitor = DisplayingOperandVisitor::new(out); - instr.visit_operand(0, &mut visitor)?; - - return Ok(()); - } - Opcode::PREFETCH0 => { - // dumpbin doesn't bother with the memory size here, same for masm. - out.write_char(' ')?; - let mut visitor = DisplayingOperandVisitor::new(out); - instr.visit_operand(0, &mut visitor)?; - - return Ok(()); - } - Opcode::PREFETCH1 => { - // dumpbin doesn't bother with the memory size here, same for masm. - out.write_char(' ')?; - let mut visitor = DisplayingOperandVisitor::new(out); - instr.visit_operand(0, &mut visitor)?; - - return Ok(()); - } - Opcode::PREFETCH2 => { - // dumpbin doesn't bother with the memory size here, same for masm. - out.write_char(' ')?; - let mut visitor = DisplayingOperandVisitor::new(out); - instr.visit_operand(0, &mut visitor)?; - - return Ok(()); - } - Opcode::PREFETCHW => { - // dumpbin doesn't bother with the memory size here, same for masm. - out.write_char(' ')?; - let mut visitor = DisplayingOperandVisitor::new(out); - instr.visit_operand(0, &mut visitor)?; - - return Ok(()); - } - Opcode::INVLPG => { - // dumpbin doesn't bother with the memory size here, same for masm. - out.write_char(' ')?; - let mut visitor = DisplayingOperandVisitor::new(out); - instr.visit_operand(0, &mut visitor)?; - - return Ok(()); - } - Opcode::CLFLUSH => { - // dumpbin doesn't bother with the memory size here, same for masm. - out.write_char(' ')?; - let mut visitor = DisplayingOperandVisitor::new(out); - instr.visit_operand(0, &mut visitor)?; - - return Ok(()); - } - Opcode::CLFLUSHOPT => { - // dumpbin doesn't bother with the memory size here, same for masm. - out.write_char(' ')?; - let mut visitor = DisplayingOperandVisitor::new(out); - instr.visit_operand(0, &mut visitor)?; - - return Ok(()); - } + Opcode::PREFETCHNTA | + Opcode::PREFETCH0 | + Opcode::PREFETCH1 | + Opcode::PREFETCH2 | + Opcode::PREFETCHW | + Opcode::INVLPG | + Opcode::CLFLUSH | + Opcode::CLFLUSHOPT | Opcode::CLWB => { // dumpbin doesn't bother with the memory size here, same for masm. out.write_char(' ')?; - let mut visitor = DisplayingOperandVisitor::new(out); - instr.visit_operand(0, &mut visitor)?; + let mut visitor = DisplayingOperandVisitor { + f: out, + rules, + }; + + if !rules.emit_operand(instr, 0, visitor.f)? { + instr.visit_operand(0, &mut visitor)?; + } return Ok(()); } Opcode::SGDT | Opcode::SIDT => { // masm uses "tbyte" as a memory size here. - out.write_fixed_size(" fword ptr ")?; - let mut visitor = DisplayingOperandVisitor::new(out); - instr.visit_operand(0, &mut visitor)?; + out.write_char(' ')?; + let mut visitor = DisplayingOperandVisitor { + f: out, + rules, + }; + + if !rules.emit_operand(instr, 0, visitor.f)? { + visitor.f.write_fixed_size("fword ptr ")?; + instr.visit_operand(0, &mut visitor)?; + } return Ok(()); } Opcode::LSS => { - let mut visitor = DisplayingOperandVisitor::new(out); + let mut visitor = DisplayingOperandVisitor { + f: out, + rules, + }; + visitor.f.write_char(' ')?; - instr.visit_operand(0, &mut visitor)?; - - match instr.mem_size { - 4 => { - visitor.f.write_fixed_size(", dword ptr ")?; - }, - 6 => { - visitor.f.write_fixed_size(", fword ptr ")?; - }, - o => { panic!("impossible memory size: {:?}", o); } + if !rules.emit_operand(instr, 0, visitor.f)? { + instr.visit_operand(0, &mut visitor)?; } + visitor.f.write_fixed_size(", ")?; + + if !rules.emit_operand(instr, 1, visitor.f)? { + match instr.mem_size { + 4 => { + visitor.f.write_fixed_size("dword ptr ")?; + }, + 6 => { + visitor.f.write_fixed_size("fword ptr ")?; + }, + o => { panic!("impossible memory size: {:?}", o); } + } - instr.visit_operand(1, &mut visitor)?; + instr.visit_operand(1, &mut visitor)?; + } return Ok(()); } Opcode::LGDT | Opcode::LIDT => { // masm uses "fword" as a memory size here. - out.write_fixed_size(" fword ptr ")?; - let mut visitor = DisplayingOperandVisitor::new(out); - instr.visit_operand(0, &mut visitor)?; + out.write_char(' ')?; + let mut visitor = DisplayingOperandVisitor { + f: out, + rules, + }; + + if !rules.emit_operand(instr, 0, visitor.f)? { + visitor.f.write_fixed_size("fword ptr ")?; + instr.visit_operand(0, &mut visitor)?; + } return Ok(()); } @@ -749,159 +770,171 @@ pub(crate) fn contextualize<T: DisplaySink>(instr: &Instruction, out: &mut T) -> return Ok(()); } }, - Opcode::VPSCATTERDD => { - // intel/xed/etc syntax has the mask register as an operand rather than normal memory masking. is xed wrong? - let mut visitor = DisplayingOperandVisitor::new(out); - visitor.f.write_str(" dword ptr ")?; - instr.visit_operand(0, &mut visitor)?; - visitor.f.write_char('{')?; - visitor.f.write_reg(instr.regs[3])?; - visitor.f.write_fixed_size("}, ")?; - instr.visit_operand(2, &mut visitor)?; - return Ok(()); - }, - Opcode::VPSCATTERQD => { - // intel/xed/etc syntax has the mask register as an operand rather than normal memory masking. is xed wrong? - let mut visitor = DisplayingOperandVisitor::new(out); - visitor.f.write_str(" dword ptr ")?; - instr.visit_operand(0, &mut visitor)?; - visitor.f.write_char('{')?; - visitor.f.write_reg(instr.regs[3])?; - visitor.f.write_fixed_size("}, ")?; - instr.visit_operand(2, &mut visitor)?; - return Ok(()); - }, - Opcode::VPSCATTERDQ => { + Opcode::VPSCATTERDD | Opcode::VPSCATTERQD => { // intel/xed/etc syntax has the mask register as an operand rather than normal memory masking. is xed wrong? - let mut visitor = DisplayingOperandVisitor::new(out); - visitor.f.write_str(" qword ptr ")?; - instr.visit_operand(0, &mut visitor)?; - visitor.f.write_char('{')?; - visitor.f.write_reg(instr.regs[3])?; - visitor.f.write_fixed_size("}, ")?; - instr.visit_operand(2, &mut visitor)?; + let mut visitor = DisplayingOperandVisitor { + f: out, + rules, + }; + + visitor.f.write_char(' ')?; + if !rules.emit_operand(instr, 0, visitor.f)? { + visitor.f.write_str("dword ptr ")?; + instr.visit_operand(0, &mut visitor)?; + visitor.f.write_char('{')?; + if !rules.emit_operand(instr, 1, visitor.f)? { + visitor.f.write_reg(instr.regs[3])?; + } + visitor.f.write_char('}')?; + } + visitor.f.write_fixed_size(", ")?; + if !rules.emit_operand(instr, 2, visitor.f)? { + instr.visit_operand(2, &mut visitor)?; + } return Ok(()); }, - Opcode::VPSCATTERQQ => { + Opcode::VPSCATTERDQ | Opcode::VPSCATTERQQ => { // intel/xed/etc syntax has the mask register as an operand rather than normal memory masking. is xed wrong? - let mut visitor = DisplayingOperandVisitor::new(out); - visitor.f.write_str(" qword ptr ")?; - instr.visit_operand(0, &mut visitor)?; - visitor.f.write_char('{')?; - visitor.f.write_reg(instr.regs[3])?; - visitor.f.write_fixed_size("}, ")?; - instr.visit_operand(2, &mut visitor)?; + let mut visitor = DisplayingOperandVisitor { + f: out, + rules, + }; + + visitor.f.write_char(' ')?; + if !rules.emit_operand(instr, 0, visitor.f)? { + visitor.f.write_str("qword ptr ")?; + instr.visit_operand(0, &mut visitor)?; + visitor.f.write_char('{')?; + if !rules.emit_operand(instr, 1, visitor.f)? { + visitor.f.write_reg(instr.regs[3])?; + } + visitor.f.write_char('}')?; + } + visitor.f.write_fixed_size(", ")?; + if !rules.emit_operand(instr, 2, visitor.f)? { + instr.visit_operand(2, &mut visitor)?; + } return Ok(()); }, - Opcode::MONITOR => { + Opcode::MONITOR | Opcode::MONITORX | Opcode::MWAITX => { // masm wants the implicit registers to all be ... explicit. - let visitor = DisplayingOperandVisitor::new(out); + let visitor = DisplayingOperandVisitor { + f: out, + rules, + }; + visitor.f.write_char(' ')?; - visitor.f.write_reg(RegSpec::eax())?; + if !rules.emit_register(RegSpec::eax(), visitor.f)? { + visitor.f.write_reg(RegSpec::eax())?; + } visitor.f.write_fixed_size(", ")?; - visitor.f.write_reg(RegSpec::ecx())?; + if !rules.emit_register(RegSpec::ecx(), visitor.f)? { + visitor.f.write_reg(RegSpec::ecx())?; + } visitor.f.write_fixed_size(", ")?; - visitor.f.write_reg(RegSpec::edx())?; + if !rules.emit_register(RegSpec::edx(), visitor.f)? { + visitor.f.write_reg(RegSpec::edx())?; + } return Ok(()); } Opcode::MWAIT => { // masm wants the implicit registers to all be ... explicit. - let visitor = DisplayingOperandVisitor::new(out); + let visitor = DisplayingOperandVisitor { + f: out, + rules, + }; + visitor.f.write_char(' ')?; - visitor.f.write_reg(RegSpec::eax())?; + if !rules.emit_register(RegSpec::eax(), visitor.f)? { + visitor.f.write_reg(RegSpec::eax())?; + } visitor.f.write_fixed_size(", ")?; - visitor.f.write_reg(RegSpec::ecx())?; + if !rules.emit_register(RegSpec::ecx(), visitor.f)? { + visitor.f.write_reg(RegSpec::ecx())?; + } return Ok(()); } Opcode::INVLPGB => { // masm bug: it doesn't tolerate the mention of the second operand?! - let mut visitor = DisplayingOperandVisitor::new(out); - visitor.f.write_char(' ')?; - instr.visit_operand(0, &mut visitor)?; - visitor.f.write_fixed_size(", ")?; - instr.visit_operand(2, &mut visitor)?; - return Ok(()); - } - Opcode::MONITORX => { - // masm wants the implicit registers to all be ... explicit. - let visitor = DisplayingOperandVisitor::new(out); - visitor.f.write_char(' ')?; - visitor.f.write_reg(RegSpec::eax())?; - visitor.f.write_fixed_size(", ")?; - visitor.f.write_reg(RegSpec::ecx())?; - visitor.f.write_fixed_size(", ")?; - visitor.f.write_reg(RegSpec::edx())?; - return Ok(()); - } - Opcode::MWAITX => { - // masm wants the implicit registers to all be ... explicit. - let visitor = DisplayingOperandVisitor::new(out); + let mut visitor = DisplayingOperandVisitor { + f: out, + rules, + }; + visitor.f.write_char(' ')?; - visitor.f.write_reg(RegSpec::eax())?; - visitor.f.write_fixed_size(", ")?; - visitor.f.write_reg(RegSpec::ecx())?; + if !rules.emit_operand(instr, 0, visitor.f)? { + instr.visit_operand(0, &mut visitor)?; + } visitor.f.write_fixed_size(", ")?; - visitor.f.write_reg(RegSpec::ebx())?; + if !rules.emit_operand(instr, 2, visitor.f)? { + instr.visit_operand(2, &mut visitor)?; + } return Ok(()); } Opcode::RDPRU => { // masm wants no implicit registers this time. return Ok(()); } - Opcode::SCAS => { + Opcode::SCAS | Opcode::STOS => { // masm does not want the implicit r/e/ax out.write_fixed_size(" ")?; - out.write_mem_size_label(instr.mem_size)?; - out.write_fixed_size(" ptr ")?; - if let Some(prefix) = instr.segment_override_for_op(0) { - let name = prefix.name(); - out.write_char(name[0] as char)?; - out.write_char(name[1] as char)?; - out.write_fixed_size(":")?; + if !rules.emit_operand(instr, 0, out)? { + out.write_mem_size_label(instr.mem_size)?; + out.write_fixed_size(" ptr ")?; + if let Some(prefix) = instr.segment_override_for_op(0) { + let name = prefix.name(); + out.write_char(name[0] as char)?; + out.write_char(name[1] as char)?; + out.write_fixed_size(":")?; + } + let mut visitor = DisplayingOperandVisitor { + f: out, + rules, + }; + instr.visit_operand(0, &mut visitor)?; } - let mut visitor = DisplayingOperandVisitor::new(out); - instr.visit_operand(0, &mut visitor)?; return Ok(()); } Opcode::LODS => { // masm does not want the implicit r/e/ax out.write_fixed_size(" ")?; - out.write_mem_size_label(instr.mem_size)?; - out.write_fixed_size(" ptr ")?; - let mut visitor = DisplayingOperandVisitor::new(out); - instr.visit_operand(1, &mut visitor)?; - return Ok(()); - } - Opcode::STOS => { - // masm does not want the implicit r/e/ax - out.write_fixed_size(" ")?; - out.write_mem_size_label(instr.mem_size)?; - out.write_fixed_size(" ptr ")?; - if let Some(prefix) = instr.segment_override_for_op(0) { - let name = prefix.name(); - out.write_char(name[0] as char)?; - out.write_char(name[1] as char)?; - out.write_fixed_size(":")?; + if !rules.emit_operand(instr, 1, out)? { + out.write_mem_size_label(instr.mem_size)?; + out.write_fixed_size(" ptr ")?; + let mut visitor = DisplayingOperandVisitor { + f: out, + rules, + }; + instr.visit_operand(1, &mut visitor)?; } - let mut visitor = DisplayingOperandVisitor::new(out); - instr.visit_operand(0, &mut visitor)?; return Ok(()); } Opcode::PSMASH => { // masm wants the implicit eax operand out.write_fixed_size(" ")?; - out.write_reg(RegSpec::eax())?; + if !rules.emit_register(RegSpec::eax(), out)? { + out.write_reg(RegSpec::eax())?; + } return Ok(()); } Opcode::PVALIDATE | Opcode::RMPADJUST | Opcode::RMPUPDATE => { // masm wants the implicit registers to all be ... explicit. - let visitor = DisplayingOperandVisitor::new(out); + let visitor = DisplayingOperandVisitor { + f: out, + rules, + }; visitor.f.write_char(' ')?; - visitor.f.write_reg(RegSpec::eax())?; + if !rules.emit_register(RegSpec::eax(), visitor.f)? { + visitor.f.write_reg(RegSpec::eax())?; + } visitor.f.write_fixed_size(", ")?; - visitor.f.write_reg(RegSpec::ecx())?; + if !rules.emit_register(RegSpec::ecx(), visitor.f)? { + visitor.f.write_reg(RegSpec::ecx())?; + } visitor.f.write_fixed_size(", ")?; - visitor.f.write_reg(RegSpec::edx())?; + if !rules.emit_register(RegSpec::edx(), visitor.f)? { + visitor.f.write_reg(RegSpec::edx())?; + } return Ok(()); } Opcode::INCSSP => { @@ -929,35 +962,50 @@ pub(crate) fn contextualize<T: DisplaySink>(instr: &Instruction, out: &mut T) -> Opcode::PBLENDVB | Opcode::BLENDVPS | Opcode::BLENDVPD | Opcode::SHA256RNDS2 => { // masm wants the implicit xmm0 operand as ... explicit. out.write_fixed_size(" ")?; - let mut visitor = DisplayingOperandVisitor::new(out); - instr.visit_operand(0, &mut visitor)?; + let mut visitor = DisplayingOperandVisitor { + f: out, + rules, + }; + if !rules.emit_operand(instr, 0, visitor.f)? { + instr.visit_operand(0, &mut visitor)?; + } visitor.f.write_str(", ")?; - if instr.operands[1].is_memory() { - visitor.f.write_mem_size_label(instr.mem_size)?; - visitor.f.write_fixed_size(" ptr")?; - visitor.f.write_char(' ')?; - if let Some(prefix) = instr.segment_override_for_op(1) { - let name = prefix.name(); - visitor.f.write_char(name[0] as char)?; - visitor.f.write_char(name[1] as char)?; - visitor.f.write_fixed_size(":")?; + if !rules.emit_operand(instr, 1, visitor.f)? { + if instr.operands[1].is_memory() { + visitor.f.write_mem_size_label(instr.mem_size)?; + visitor.f.write_fixed_size(" ptr")?; + visitor.f.write_char(' ')?; + if let Some(prefix) = instr.segment_override_for_op(1) { + let name = prefix.name(); + visitor.f.write_char(name[0] as char)?; + visitor.f.write_char(name[1] as char)?; + visitor.f.write_fixed_size(":")?; + } } + instr.visit_operand(1, &mut visitor)?; } - - instr.visit_operand(1, &mut visitor)?; visitor.f.write_str(", ")?; - visitor.f.write_reg(RegSpec::xmm0())?; + if !rules.emit_register(RegSpec::xmm0(), visitor.f)? { + visitor.f.write_reg(RegSpec::xmm0())?; + } return Ok(()); } Opcode::LEA => { // dumpbin/masm don't want the `<word> ptr` prefix on the memory access here.. out.write_fixed_size(" ")?; - let mut visitor = DisplayingOperandVisitor::new(out); - instr.visit_operand(0, &mut visitor)?; + let mut visitor = DisplayingOperandVisitor { + f: out, + rules, + }; + if !rules.emit_operand(instr, 0, visitor.f)? { + instr.visit_operand(0, &mut visitor)?; + } visitor.f.write_str(", ")?; - instr.visit_operand(1, &mut visitor)?; + if !rules.emit_operand(instr, 1, visitor.f)? { + instr.visit_operand(1, &mut visitor)?; + } return Ok(()); } Opcode::PUSHF => { @@ -975,21 +1023,26 @@ pub(crate) fn contextualize<T: DisplaySink>(instr: &Instruction, out: &mut T) -> Opcode::FCOM | Opcode::FCOMP | Opcode::FICOM | Opcode::FICOMP => { // masm does not want the first operand *ever*? out.write_fixed_size(" ")?; - let mut visitor = DisplayingOperandVisitor::new(out); + let mut visitor = DisplayingOperandVisitor { + f: out, + rules, + }; - if instr.operands[1].is_memory() { - visitor.f.write_mem_size_label(instr.mem_size)?; - visitor.f.write_fixed_size(" ptr")?; - visitor.f.write_char(' ')?; - if let Some(prefix) = instr.segment_override_for_op(1) { - let name = prefix.name(); - visitor.f.write_char(name[0] as char)?; - visitor.f.write_char(name[1] as char)?; - visitor.f.write_fixed_size(":")?; + if !rules.emit_operand(instr, 1, visitor.f)? { + if instr.operands[1].is_memory() { + visitor.f.write_mem_size_label(instr.mem_size)?; + visitor.f.write_fixed_size(" ptr")?; + visitor.f.write_char(' ')?; + if let Some(prefix) = instr.segment_override_for_op(1) { + let name = prefix.name(); + visitor.f.write_char(name[0] as char)?; + visitor.f.write_char(name[1] as char)?; + visitor.f.write_fixed_size(":")?; + } } - } - instr.visit_operand(1, &mut visitor)?; + instr.visit_operand(1, &mut visitor)?; + } return Ok(()); } Opcode::FADD | Opcode::FMUL | Opcode::FSUB | Opcode::FSUBR | Opcode::FDIV | Opcode::FDIVR | @@ -997,105 +1050,123 @@ pub(crate) fn contextualize<T: DisplaySink>(instr: &Instruction, out: &mut T) -> Opcode::FIADD | Opcode::FIMUL | Opcode::FISUB | Opcode::FISUBR | Opcode::FIDIV | Opcode::FIDIVR | Opcode::FCMOVB | Opcode::FCMOVE | Opcode::FCMOVBE | Opcode::FCMOVU | Opcode::FCMOVNB | Opcode::FCMOVNE | Opcode::FCMOVNBE | Opcode::FCMOVNU | Opcode::FUCOMI | Opcode::FCOMI | Opcode::FUCOMIP | Opcode::FCOMIP => { + out.write_fixed_size(" ")?; + let mut visitor = DisplayingOperandVisitor { + f: out, + rules, + }; + if instr.operands[1].is_memory() { // masm does not want to see the implicit st(0). - out.write_fixed_size(" ")?; - let mut visitor = DisplayingOperandVisitor::new(out); - visitor.f.write_mem_size_label(instr.mem_size)?; - visitor.f.write_fixed_size(" ptr")?; - visitor.f.write_char(' ')?; - if let Some(prefix) = instr.segment_override_for_op(1) { - let name = prefix.name(); - visitor.f.write_char(name[0] as char)?; - visitor.f.write_char(name[1] as char)?; - visitor.f.write_fixed_size(":")?; + if !rules.emit_operand(instr, 1, visitor.f)? { + visitor.f.write_mem_size_label(instr.mem_size)?; + visitor.f.write_fixed_size(" ptr")?; + visitor.f.write_char(' ')?; + if let Some(prefix) = instr.segment_override_for_op(1) { + let name = prefix.name(); + visitor.f.write_char(name[0] as char)?; + visitor.f.write_char(name[1] as char)?; + visitor.f.write_fixed_size(":")?; + } + instr.visit_operand(1, &mut visitor)?; } - instr.visit_operand(1, &mut visitor)?; - return Ok(()); } else { // dumpbin writes `st` instead of `st(0)` as the first operand in reg-reg ops, replicate this. masm doesn't care. - out.write_fixed_size(" ")?; - let mut visitor = DisplayingOperandVisitor::new(out); - if instr.operands[0] == OperandSpec::RegRRR { - if instr.regs[0] == RegSpec::st0() { - visitor.f.write_fixed_size("st")?; - } else { - instr.visit_operand(0, &mut visitor)?; + if !rules.emit_operand(instr, 0, visitor.f)? { + if instr.regs[0] == RegSpec::st0() { + visitor.f.write_fixed_size("st")?; + } else { + instr.visit_operand(0, &mut visitor)?; + } } visitor.f.write_fixed_size(", ")?; - instr.visit_operand(1, &mut visitor)?; + if !rules.emit_operand(instr, 1, visitor.f)? { + instr.visit_operand(1, &mut visitor)?; + } } else { debug_assert!(instr.operands[1] == OperandSpec::RegRRR); - instr.visit_operand(0, &mut visitor)?; + if !rules.emit_operand(instr, 0, visitor.f)? { + instr.visit_operand(0, &mut visitor)?; + } visitor.f.write_fixed_size(", ")?; - if instr.regs[0] == RegSpec::st0() { - visitor.f.write_fixed_size("st")?; - } else { - instr.visit_operand(1, &mut visitor)?; + if !rules.emit_operand(instr, 1, visitor.f)? { + if instr.regs[0] == RegSpec::st0() { + visitor.f.write_fixed_size("st")?; + } else { + instr.visit_operand(1, &mut visitor)?; + } } }; - - return Ok(()); } + + return Ok(()); } Opcode::FBLD | Opcode::FLD | Opcode::FILD | Opcode::FXCH | Opcode::FUCOM | Opcode::FUCOMP => { // masm does not want to see the implicit st(0). out.write_fixed_size(" ")?; - let mut visitor = DisplayingOperandVisitor::new(out); + let mut visitor = DisplayingOperandVisitor { + f: out, + rules, + }; - if instr.operands[1].is_memory() { - if instr.mem_size == 10 { - visitor.f.write_fixed_size("tbyte")?; - } else { - visitor.f.write_mem_size_label(instr.mem_size)?; - } - visitor.f.write_fixed_size(" ptr")?; - visitor.f.write_char(' ')?; - if let Some(prefix) = instr.segment_override_for_op(1) { - let name = prefix.name(); - visitor.f.write_char(name[0] as char)?; - visitor.f.write_char(name[1] as char)?; - visitor.f.write_fixed_size(":")?; - } - instr.visit_operand(1, &mut visitor)?; - return Ok(()); - } else { - if instr.regs[1] == RegSpec::st0() { - visitor.f.write_fixed_size("st")?; - } else { + if !rules.emit_operand(instr, 1, visitor.f)? { + if instr.operands[1].is_memory() { + if instr.mem_size == 10 { + visitor.f.write_fixed_size("tbyte")?; + } else { + visitor.f.write_mem_size_label(instr.mem_size)?; + } + visitor.f.write_fixed_size(" ptr")?; + visitor.f.write_char(' ')?; + if let Some(prefix) = instr.segment_override_for_op(1) { + let name = prefix.name(); + visitor.f.write_char(name[0] as char)?; + visitor.f.write_char(name[1] as char)?; + visitor.f.write_fixed_size(":")?; + } instr.visit_operand(1, &mut visitor)?; + } else { + if instr.regs[1] == RegSpec::st0() { + visitor.f.write_fixed_size("st")?; + } else { + instr.visit_operand(1, &mut visitor)?; + } } - return Ok(()); } + return Ok(()); } Opcode::FBSTP | Opcode::FST | Opcode::FSTP | Opcode::FIST | Opcode::FISTP | Opcode::FISTTP => { // masm does not want to see the implicit st(0). out.write_fixed_size(" ")?; - let mut visitor = DisplayingOperandVisitor::new(out); + let mut visitor = DisplayingOperandVisitor { + f: out, + rules, + }; - if instr.operands[0].is_memory() { - if instr.mem_size == 10 { - visitor.f.write_fixed_size("tbyte")?; + if !rules.emit_operand(instr, 0, visitor.f)? { + if instr.operands[0].is_memory() { + if instr.mem_size == 10 { + visitor.f.write_fixed_size("tbyte")?; + } else { + visitor.f.write_mem_size_label(instr.mem_size)?; + } + visitor.f.write_fixed_size(" ptr")?; + visitor.f.write_char(' ')?; + if let Some(prefix) = instr.segment_override_for_op(0) { + let name = prefix.name(); + visitor.f.write_char(name[0] as char)?; + visitor.f.write_char(name[1] as char)?; + visitor.f.write_fixed_size(":")?; + } + instr.visit_operand(0, &mut visitor)?; } else { - visitor.f.write_mem_size_label(instr.mem_size)?; - } - visitor.f.write_fixed_size(" ptr")?; - visitor.f.write_char(' ')?; - if let Some(prefix) = instr.segment_override_for_op(0) { - let name = prefix.name(); - visitor.f.write_char(name[0] as char)?; - visitor.f.write_char(name[1] as char)?; - visitor.f.write_fixed_size(":")?; + instr.visit_operand(0, &mut visitor)?; } - instr.visit_operand(0, &mut visitor)?; - return Ok(()); - } else { - instr.visit_operand(0, &mut visitor)?; - return Ok(()); } + return Ok(()); } _ => {} } @@ -1120,41 +1191,68 @@ pub(crate) fn contextualize<T: DisplaySink>(instr: &Instruction, out: &mut T) -> size_is_mmword = true; } - if instr.visit_operand(0, &mut RelativeBranchPrinter { - inst: instr, - out, - })? { - return Ok(()); - } - - if instr.operands[0 as usize].is_memory() { - // fxsave and friends get no "XXXword ptr" memory prefix, masm doesn't accept it - if instr.mem_size != 63 && instr.mem_size != 48 { // masm does not print "m384b" labels.. - if size_is_mmword && instr.mem_size == 8 { - out.write_fixed_size("mmword")?; - } else if instr.mem_size == 6 && (instr.opcode == Opcode::JMPF || instr.opcode == Opcode::CALLF) { - // "fword" in protected mode instead of "far".. - out.write_fixed_size("fword")?; - } else { - out.write_mem_size_label(instr.mem_size)?; + if instr.operands[0] == OperandSpec::ImmI8 || instr.operands[0] == OperandSpec::ImmI32 { + if RELATIVE_BRANCHES.contains(&instr.opcode) { + // relative branch instructions have only one operand, so print this one and we're + // done. relative branch instructions *also* have a ... relative branch ... as + // their only operand, so don't `emit_operand()` which would confuse these for a + // "normal" immediate. + if rules.emit_branch_addr(instr.len(), instr.imm as i32, out)? { + return Ok(()); } - out.write_fixed_size(" ptr")?; - out.write_char(' ')?; } - if let Some(prefix) = instr.segment_override_for_op(0) { - let name = prefix.name(); - out.write_char(name[0] as char)?; - out.write_char(name[1] as char)?; - out.write_fixed_size(":")?; + + if instr.visit_operand(0, &mut RelativeBranchPrinter { + inst: instr, + rules, + out, + })? { + return Ok(()); } } + let mut show_sae = false; + let mut sae_mode = None; let mut displayer = DisplayingOperandVisitor { f: out, - show_sae: false, - sae_mode: None, + rules, }; - instr.visit_operand(0 as u8, &mut displayer)?; + + if instr.operands[0] == OperandSpec::RegRRR_maskmerge_sae || + instr.operands[0] == OperandSpec::RegRRR_maskmerge_sae_noround || + instr.operands[0] == OperandSpec::RegMMM_maskmerge_sae_noround { + show_sae = true; + if instr.operands[0] == OperandSpec::RegRRR_maskmerge_sae { + let instr_evex = instr.prefixes.evex_unchecked(); + sae_mode = Some(SaeMode::from(instr_evex.vex().l(), instr_evex.lp())); + } + } + + if !rules.emit_operand(instr, 0, displayer.f)? { + if instr.operands[0 as usize].is_memory() { + // fxsave and friends get no "XXXword ptr" memory prefix, masm doesn't accept it + if instr.mem_size != 63 && instr.mem_size != 48 { // masm does not print "m384b" labels.. + if size_is_mmword && instr.mem_size == 8 { + displayer.f.write_fixed_size("mmword")?; + } else if instr.mem_size == 6 && (instr.opcode == Opcode::JMPF || instr.opcode == Opcode::CALLF) { + // "fword" in protected mode instead of "far".. + displayer.f.write_fixed_size("fword")?; + } else { + displayer.f.write_mem_size_label(instr.mem_size)?; + } + displayer.f.write_fixed_size(" ptr")?; + displayer.f.write_char(' ')?; + } + if let Some(prefix) = instr.segment_override_for_op(0) { + let name = prefix.name(); + displayer.f.write_char(name[0] as char)?; + displayer.f.write_char(name[1] as char)?; + displayer.f.write_fixed_size(":")?; + } + } + + instr.visit_operand(0 as u8, &mut displayer)?; + } for i in 1..instr.operand_count { // don't worry about checking for `instr.operands[i] != Nothing`, it would be a bug to @@ -1168,6 +1266,11 @@ pub(crate) fn contextualize<T: DisplaySink>(instr: &Instruction, out: &mut T) -> unsafe { unreachable_unchecked(); } } + if rules.emit_operand(instr, i, displayer.f)? { + // if the rule printed an operand out, continue on to the next one! + continue; + } + if instr.operands[i as usize].is_memory() { // fxsave and friends get no "XXXword ptr" memory prefix, masm doesn't accept it if instr.mem_size != 63 && instr.mem_size != 48 { // masm does not print "m384b" labels.. @@ -1251,9 +1354,9 @@ pub(crate) fn contextualize<T: DisplaySink>(instr: &Instruction, out: &mut T) -> } } - if displayer.show_sae { + if show_sae { displayer.f.write_char(' ')?; - if let Some(sae_mode) = displayer.sae_mode.as_ref() { + if let Some(sae_mode) = sae_mode.as_ref() { displayer.f.write_sae_mode(*sae_mode)?; } else { displayer.f.write_str("{sae}")?; |
