aboutsummaryrefslogtreecommitdiff
path: root/src/generic/display.rs
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2022-08-27 17:11:19 -0700
committeriximeow <me@iximeow.net>2023-01-02 08:50:23 -0800
commit789797accee0caa6580fbba650c719a952945ac6 (patch)
tree85fffbcc13a525bbea1df0464a85096744f0b3de /src/generic/display.rs
parentd2b4f3d1a454c7bbcc487ddfb2839b01dc1c9c9e (diff)
add a `generic` module for x86 disassembly
this module generally attempts to decode as 64-bit x86 instructions, on the assumption they are the most likely-desired instructions, falling back to 32-bit and then 16-bit decoding, in order. translation from a 64-bit `long_mode::Instruction` to `generic::Instruction` is close to free, where `protected_mode::Instruction` and `real_mode::Instruction` may be a little more costly in time but should still not be too bad. docs still need much touching up. most docs reference the `long_mode` structures and enums they're strongly inspired by.
Diffstat (limited to 'src/generic/display.rs')
-rw-r--r--src/generic/display.rs977
1 files changed, 977 insertions, 0 deletions
diff --git a/src/generic/display.rs b/src/generic/display.rs
new file mode 100644
index 0000000..61e58ac
--- /dev/null
+++ b/src/generic/display.rs
@@ -0,0 +1,977 @@
+use core::fmt;
+use crate::safer_unchecked::GetSaferUnchecked as _;
+
+use yaxpeax_arch::{Colorize, ShowContextual, NoColors, YaxColors};
+use yaxpeax_arch::display::*;
+
+use crate::MEM_SIZE_STRINGS;
+use crate::generic::{RegSpec, Opcode, Operand, MergeMode, InstDecoder, Instruction, Segment, PrefixVex, OperandSpec};
+
+impl fmt::Display for InstDecoder {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if self == &InstDecoder::default() {
+ return write!(f, "<all features>");
+ } else if self == &InstDecoder::minimal() {
+ return write!(f, "<no features>");
+ }
+ if self.sse3() { write!(f, "sse3 ")? }
+ if self.ssse3() { write!(f, "ssse3 ")? }
+ if self.monitor() { write!(f, "monitor ")? }
+ if self.vmx() { write!(f, "vmx ")? }
+ if self.fma3() { write!(f, "fma3 ")? }
+ if self.cmpxchg16b() { write!(f, "cmpxchg16b ")? }
+ if self.sse4_1() { write!(f, "sse4_1 ")? }
+ if self.sse4_2() { write!(f, "sse4_2 ")? }
+ if self.movbe() { write!(f, "movbe ")? }
+ if self.popcnt() { write!(f, "popcnt ")? }
+ if self.aesni() { write!(f, "aesni ")? }
+ if self.xsave() { write!(f, "xsave ")? }
+ if self.rdrand() { write!(f, "rdrand ")? }
+ if self.sgx() { write!(f, "sgx ")? }
+ if self.bmi1() { write!(f, "bmi1 ")? }
+ if self.avx2() { write!(f, "avx2 ")? }
+ if self.bmi2() { write!(f, "bmi2 ")? }
+ if self.invpcid() { write!(f, "invpcid ")? }
+ if self.mpx() { write!(f, "mpx ")? }
+ if self.avx512_f() { write!(f, "avx512_f ")? }
+ if self.avx512_dq() { write!(f, "avx512_dq ")? }
+ if self.rdseed() { write!(f, "rdseed ")? }
+ if self.adx() { write!(f, "adx ")? }
+ if self.avx512_fma() { write!(f, "avx512_fma ")? }
+ if self.pcommit() { write!(f, "pcommit ")? }
+ if self.clflushopt() { write!(f, "clflushopt ")? }
+ if self.clwb() { write!(f, "clwb ")? }
+ if self.avx512_pf() { write!(f, "avx512_pf ")? }
+ if self.avx512_er() { write!(f, "avx512_er ")? }
+ if self.avx512_cd() { write!(f, "avx512_cd ")? }
+ if self.sha() { write!(f, "sha ")? }
+ if self.avx512_bw() { write!(f, "avx512_bw ")? }
+ if self.avx512_vl() { write!(f, "avx512_vl ")? }
+ if self.prefetchwt1() { write!(f, "prefetchwt1 ")? }
+ if self.avx512_vbmi() { write!(f, "avx512_vbmi ")? }
+ if self.avx512_vbmi2() { write!(f, "avx512_vbmi2 ")? }
+ if self.gfni() { write!(f, "gfni ")? }
+ if self.vaes() { write!(f, "vaes ")? }
+ if self.pclmulqdq() { write!(f, "pclmulqdq ")? }
+ if self.avx_vnni() { write!(f, "avx_vnni ")? }
+ if self.avx512_bitalg() { write!(f, "avx512_bitalg ")? }
+ if self.avx512_vpopcntdq() { write!(f, "avx512_vpopcntdq ")? }
+ if self.avx512_4vnniw() { write!(f, "avx512_4vnniw ")? }
+ if self.avx512_4fmaps() { write!(f, "avx512_4fmaps ")? }
+ if self.cx8() { write!(f, "cx8 ")? }
+ if self.syscall() { write!(f, "syscall ")? }
+ if self.rdtscp() { write!(f, "rdtscp ")? }
+ if self.abm() { write!(f, "abm ")? }
+ if self.sse4a() { write!(f, "sse4a ")? }
+ if self._3dnowprefetch() { write!(f, "_3dnowprefetch ")? }
+ if self.xop() { write!(f, "xop ")? }
+ if self.skinit() { write!(f, "skinit ")? }
+ if self.tbm() { write!(f, "tbm ")? }
+ if self.intel_quirks() { write!(f, "intel_quirks ")? }
+ if self.amd_quirks() { write!(f, "amd_quirks ")? }
+ if self.avx() { write!(f, "avx ")? }
+ Ok(())
+ }
+}
+
+impl fmt::Display for PrefixVex {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if self.present() {
+ write!(f, "vex:{}{}{}{}",
+ if self.w() { "w" } else { "-" },
+ if self.r() { "r" } else { "-" },
+ if self.x() { "x" } else { "-" },
+ if self.b() { "b" } else { "-" },
+ )
+ } else {
+ write!(f, "vex:none")
+ }
+ }
+}
+
+impl fmt::Display for Segment {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ Segment::CS => write!(f, "cs"),
+ Segment::DS => write!(f, "ds"),
+ Segment::ES => write!(f, "es"),
+ Segment::FS => write!(f, "fs"),
+ Segment::GS => write!(f, "gs"),
+ Segment::SS => write!(f, "ss"),
+ }
+ }
+}
+
+// register names are grouped by indices scaled by 16.
+// xmm, ymm, zmm all get two indices.
+const REG_NAMES: &[&'static str] = &[
+ "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
+ "ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
+ "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
+ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7",
+ "dr0", "dr1", "dr2", "dr3", "dr4", "dr5", "dr6", "dr7",
+ "es", "cs", "ss", "ds", "fs", "gs", "", "",
+ "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15",
+ "xmm16", "xmm17", "xmm18", "xmm19", "xmm20", "xmm21", "xmm22", "xmm23", "xmm24", "xmm25", "xmm26", "xmm27", "xmm28", "xmm29", "xmm30", "xmm31",
+ "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7", "ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15",
+ "ymm16", "ymm17", "ymm18", "ymm19", "ymm20", "ymm21", "ymm22", "ymm23", "ymm24", "ymm25", "ymm26", "ymm27", "ymm28", "ymm29", "ymm30", "ymm31",
+ "zmm0", "zmm1", "zmm2", "zmm3", "zmm4", "zmm5", "zmm6", "zmm7", "zmm8", "zmm9", "zmm10", "zmm11", "zmm12", "zmm13", "zmm14", "zmm15", "zmm16", "zmm17", "zmm18", "zmm19", "zmm20", "zmm21", "zmm22", "zmm23", "zmm24", "zmm25", "zmm26", "zmm27", "zmm28", "zmm29", "zmm30", "zmm31",
+ "st(0)", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)",
+ "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
+ "k0", "k1", "k2", "k3", "k4", "k5", "k6", "k7",
+ "eip", "BUG", "BUG", "BUG", "BUG", "BUG", "BUG", "BUG",
+ "eflags", "BUG", "BUG", "BUG", "BUG", "BUG", "BUG", "BUG",
+];
+
+pub(crate) fn regspec_label(spec: &RegSpec) -> &'static str {
+ unsafe { REG_NAMES.get_kinda_unchecked((spec.num as u16 + ((spec.bank as u16) << 3)) as usize) }
+}
+
+impl fmt::Display for RegSpec {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(regspec_label(self))
+ }
+}
+
+impl fmt::Display for Operand {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ self.colorize(&NoColors, fmt)
+ }
+}
+
+impl <T: fmt::Write, Y: YaxColors> Colorize<T, Y> for Operand {
+ fn colorize(&self, colors: &Y, f: &mut T) -> fmt::Result {
+ match self {
+ &Operand::ImmediateU8(imm) => {
+ write!(f, "{}", colors.number(u8_hex(imm)))
+ }
+ &Operand::ImmediateI8(imm) => {
+ write!(f, "{}",
+ colors.number(signed_i8_hex(imm)))
+ },
+ &Operand::ImmediateU16(imm) => {
+ write!(f, "{}", colors.number(u16_hex(imm)))
+ }
+ &Operand::ImmediateI16(imm) => {
+ write!(f, "{}",
+ colors.number(signed_i16_hex(imm)))
+ },
+ &Operand::ImmediateU32(imm) => {
+ write!(f, "{}", colors.number(u32_hex(imm)))
+ }
+ &Operand::ImmediateI32(imm) => {
+ write!(f, "{}",
+ colors.number(signed_i32_hex(imm)))
+ },
+ &Operand::AbsoluteFarAddress { segment, address } => {
+ write!(f, "{}:{}",
+ colors.number(u16_hex(segment as u16)),
+ colors.number(u32_hex(address as u32)),
+ )
+ },
+ &Operand::Register(ref spec) => {
+ f.write_str(regspec_label(spec))
+ }
+ &Operand::RegisterMaskMerge(ref spec, ref mask, merge_mode) => {
+ f.write_str(regspec_label(spec))?;
+ if mask.num != 0 {
+ f.write_str("{")?;
+ f.write_str(regspec_label(mask))?;
+ f.write_str("}")?;
+ }
+ if let MergeMode::Zero = merge_mode {
+ f.write_str("{z}")?;
+ }
+ Ok(())
+ }
+ &Operand::RegisterMaskMergeSae(ref spec, ref mask, merge_mode, sae_mode) => {
+ f.write_str(regspec_label(spec))?;
+ if mask.num != 0 {
+ f.write_str("{")?;
+ f.write_str(regspec_label(mask))?;
+ f.write_str("}")?;
+ }
+ if let MergeMode::Zero = merge_mode {
+ f.write_str("{z}")?;
+ }
+ f.write_str(sae_mode.label())?;
+ Ok(())
+ }
+ &Operand::RegisterMaskMergeSaeNoround(ref spec, ref mask, merge_mode) => {
+ f.write_str(regspec_label(spec))?;
+ if mask.num != 0 {
+ f.write_str("{")?;
+ f.write_str(regspec_label(mask))?;
+ f.write_str("}")?;
+ }
+ if let MergeMode::Zero = merge_mode {
+ f.write_str("{z}")?;
+ }
+ f.write_str("{sae}")?;
+ Ok(())
+ }
+ &Operand::DisplacementU16(imm) => {
+ write!(f, "[{}]", colors.address(u16_hex(imm)))
+ }
+ &Operand::DisplacementU32(imm) => {
+ write!(f, "[{}]", colors.address(u32_hex(imm)))
+ }
+ &Operand::RegDisp(ref spec, disp) => {
+ write!(f, "[{} ", regspec_label(spec))?;
+ format_number_i32(colors, f, disp, NumberStyleHint::HexSignedWithSignSplit)?;
+ write!(f, "]")
+ },
+ &Operand::RegDeref(ref spec) => {
+ f.write_str("[")?;
+ f.write_str(regspec_label(spec))?;
+ f.write_str("]")
+ },
+ &Operand::RegScale(ref spec, scale) => {
+ write!(f, "[{} * {}]",
+ regspec_label(spec),
+ colors.number(scale)
+ )
+ },
+ &Operand::RegScaleDisp(ref spec, scale, disp) => {
+ write!(f, "[{} * {} ",
+ regspec_label(spec),
+ colors.number(scale),
+ )?;
+ format_number_i32(colors, f, disp, NumberStyleHint::HexSignedWithSignSplit)?;
+ write!(f, "]")
+ },
+ &Operand::RegIndexBase(ref base, ref index) => {
+ f.write_str("[")?;
+ f.write_str(regspec_label(base))?;
+ f.write_str(" + ")?;
+ f.write_str(regspec_label(index))?;
+ f.write_str("]")
+ }
+ &Operand::RegIndexBaseDisp(ref base, ref index, disp) => {
+ write!(f, "[{} + {} ",
+ regspec_label(base),
+ regspec_label(index),
+ )?;
+ format_number_i32(colors, f, disp, NumberStyleHint::HexSignedWithSignSplit)?;
+ write!(f, "]")
+ },
+ &Operand::RegIndexBaseScale(ref base, ref index, scale) => {
+ write!(f, "[{} + {} * {}]",
+ regspec_label(base),
+ regspec_label(index),
+ colors.number(scale)
+ )
+ }
+ &Operand::RegIndexBaseScaleDisp(ref base, ref index, scale, disp) => {
+ write!(f, "[{} + {} * {} ",
+ regspec_label(base),
+ regspec_label(index),
+ colors.number(scale),
+ )?;
+ format_number_i32(colors, f, disp, NumberStyleHint::HexSignedWithSignSplit)?;
+ write!(f, "]")
+ },
+ &Operand::RegDispMasked(ref spec, disp, ref mask_reg) => {
+ write!(f, "[{} ", regspec_label(spec))?;
+ format_number_i32(colors, f, disp, NumberStyleHint::HexSignedWithSignSplit)?;
+ write!(f, "]")?;
+ write!(f, "{{{}}}", regspec_label(mask_reg))
+ },
+ &Operand::RegDerefMasked(ref spec, ref mask_reg) => {
+ f.write_str("[")?;
+ f.write_str(regspec_label(spec))?;
+ f.write_str("]")?;
+ write!(f, "{{{}}}", regspec_label(mask_reg))
+ },
+ &Operand::RegScaleMasked(ref spec, scale, ref mask_reg) => {
+ write!(f, "[{} * {}]",
+ regspec_label(spec),
+ colors.number(scale)
+ )?;
+ write!(f, "{{{}}}", regspec_label(mask_reg))
+ },
+ &Operand::RegScaleDispMasked(ref spec, scale, disp, ref mask_reg) => {
+ write!(f, "[{} * {} ",
+ regspec_label(spec),
+ colors.number(scale),
+ )?;
+ format_number_i32(colors, f, disp, NumberStyleHint::HexSignedWithSignSplit)?;
+ write!(f, "]")?;
+ write!(f, "{{{}}}", regspec_label(mask_reg))
+ },
+ &Operand::RegIndexBaseMasked(ref base, ref index, ref mask_reg) => {
+ f.write_str("[")?;
+ f.write_str(regspec_label(base))?;
+ f.write_str(" + ")?;
+ f.write_str(regspec_label(index))?;
+ f.write_str("]")?;
+ write!(f, "{{{}}}", regspec_label(mask_reg))
+ }
+ &Operand::RegIndexBaseDispMasked(ref base, ref index, disp, ref mask_reg) => {
+ write!(f, "[{} + {} ",
+ regspec_label(base),
+ regspec_label(index),
+ )?;
+ format_number_i32(colors, f, disp, NumberStyleHint::HexSignedWithSignSplit)?;
+ write!(f, "]")?;
+ write!(f, "{{{}}}", regspec_label(mask_reg))
+ },
+ &Operand::RegIndexBaseScaleMasked(ref base, ref index, scale, ref mask_reg) => {
+ write!(f, "[{} + {} * {}]",
+ regspec_label(base),
+ regspec_label(index),
+ colors.number(scale)
+ )?;
+ write!(f, "{{{}}}", regspec_label(mask_reg))
+ }
+ &Operand::RegIndexBaseScaleDispMasked(ref base, ref index, scale, disp, ref mask_reg) => {
+ write!(f, "[{} + {} * {} ",
+ regspec_label(base),
+ regspec_label(index),
+ colors.number(scale),
+ )?;
+ format_number_i32(colors, f, disp, NumberStyleHint::HexSignedWithSignSplit)?;
+ write!(f, "]")?;
+ write!(f, "{{{}}}", regspec_label(mask_reg))
+ },
+ &Operand::Nothing => { Ok(()) },
+ }
+ }
+}
+
+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 eax, [edx + ecx * 2 + 0x1234]`
+ Intel,
+ /// C-style syntax for instructions, like
+ /// `eax += [edx + ecx * 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,
+}
+
+/// implementation of [`Display`](fmt::Display) that renders instructions using a specified display
+/// style.
+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.
+ *
+ * can't write this as an intermediate struct because i refuse to copy
+ * all data into the struct, and having a function producing a struct with
+ * some lifetimes gets really hairy if it's from a trait - same GAT kind
+ * of nonsense as i saw with ContextRead, because someone could hold onto
+ * the dang intermediate struct forever.
+ *
+ * so write to some Write thing i guess. bite me. i really just want to
+ * stop thinking about how to support printing instructions...
+ */
+impl <'instr, T: fmt::Write, Y: YaxColors> Colorize<T, 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..
+ self.contextualize(colors, 0, Some(&NoContext), out)
+ }
+}
+
+/// No per-operand context when contextualizing an instruction!
+struct NoContext;
+
+impl Instruction {
+ pub fn write_to<T: fmt::Write>(&self, out: &mut T) -> fmt::Result {
+ self.display_with(DisplayStyle::Intel).contextualize(&NoColors, 0, Some(&NoContext), out)
+ }
+}
+
+fn contextualize_intel<T: fmt::Write, Y: YaxColors>(instr: &Instruction, colors: &Y, _address: u32, _context: Option<&NoContext>, out: &mut T) -> fmt::Result {
+ if instr.xacquire() {
+ out.write_str("xacquire ")?;
+ }
+ if instr.xrelease() {
+ out.write_str("xrelease ")?;
+ }
+ if instr.prefixes.lock() {
+ out.write_str("lock ")?;
+ }
+
+ if instr.prefixes.rep_any() {
+ if [Opcode::MOVS, Opcode::CMPS, Opcode::LODS, Opcode::STOS, Opcode::INS, Opcode::OUTS].contains(&instr.opcode) {
+ if instr.prefixes.rep() {
+ write!(out, "rep ")?;
+ } else if instr.prefixes.repnz() {
+ write!(out, "repnz ")?;
+ }
+ }
+ }
+
+ out.write_str(instr.opcode.name())?;
+
+ if instr.opcode == Opcode::XBEGIN {
+ if (instr.imm as i32) >= 0 {
+ return write!(out, " $+{}", colors.number(signed_i32_hex(instr.imm as i32)));
+ } else {
+ return write!(out, " ${}", colors.number(signed_i32_hex(instr.imm as i32)));
+ }
+ }
+
+ if instr.operand_count > 0 {
+ out.write_str(" ")?;
+
+ let x = Operand::from_spec(instr, instr.operands[0]);
+
+ const RELATIVE_BRANCHES: [Opcode; 21] = [
+ Opcode::JMP, Opcode::JECXZ,
+ Opcode::LOOP, Opcode::LOOPZ, Opcode::LOOPNZ,
+ Opcode::JO, Opcode::JNO,
+ Opcode::JB, Opcode::JNB,
+ Opcode::JZ, Opcode::JNZ,
+ Opcode::JNA, Opcode::JA,
+ Opcode::JS, Opcode::JNS,
+ Opcode::JP, Opcode::JNP,
+ Opcode::JL, Opcode::JGE,
+ Opcode::JLE, Opcode::JG,
+ ];
+
+ if instr.operands[0] == OperandSpec::ImmI8 || instr.operands[0] == OperandSpec::ImmI32 {
+ if RELATIVE_BRANCHES.contains(&instr.opcode) {
+ return match x {
+ Operand::ImmediateI8(rel) => {
+ if rel >= 0 {
+ write!(out, "$+{}", colors.number(signed_i32_hex(rel as i32)))
+ } else {
+ write!(out, "${}", colors.number(signed_i32_hex(rel as i32)))
+ }
+ }
+ Operand::ImmediateI32(rel) => {
+ if rel >= 0 {
+ write!(out, "$+{}", colors.number(signed_i32_hex(rel)))
+ } else {
+ write!(out, "${}", colors.number(signed_i32_hex(rel)))
+ }
+ }
+ _ => { unreachable!() }
+ };
+ }
+ }
+
+ if x.is_memory() {
+ out.write_str(MEM_SIZE_STRINGS[instr.mem_size as usize - 1])?;
+ out.write_str(" ")?;
+ }
+
+ if let Some(prefix) = instr.segment_override_for_op(0) {
+ write!(out, "{}:", prefix)?;
+ }
+ x.colorize(colors, out)?;
+
+ for i in 1..instr.operand_count {
+ match instr.opcode {
+ _ => {
+ match &instr.operands[i as usize] {
+ &OperandSpec::Nothing => {
+ return Ok(());
+ },
+ _ => {
+ out.write_str(", ")?;
+ let x = Operand::from_spec(instr, instr.operands[i as usize]);
+ if x.is_memory() {
+ out.write_str(MEM_SIZE_STRINGS[instr.mem_size as usize - 1])?;
+ out.write_str(" ")?;
+ }
+ if let Some(prefix) = instr.segment_override_for_op(i) {
+ write!(out, "{}:", prefix)?;
+ }
+ x.colorize(colors, out)?;
+ if let Some(evex) = instr.prefixes.evex() {
+ if evex.broadcast() && x.is_memory() {
+ let scale = if instr.opcode == Opcode::VCVTPD2PS || instr.opcode == Opcode::VCVTTPD2UDQ || instr.opcode == Opcode::VCVTPD2UDQ || instr.opcode == Opcode::VCVTUDQ2PD || instr.opcode == Opcode::VCVTPS2PD || instr.opcode == Opcode::VCVTQQ2PS || instr.opcode == Opcode::VCVTDQ2PD || instr.opcode == Opcode::VCVTTPD2DQ || instr.opcode == Opcode::VFPCLASSPS || instr.opcode == Opcode::VFPCLASSPD || instr.opcode == Opcode::VCVTNEPS2BF16 || instr.opcode == Opcode::VCVTUQQ2PS || instr.opcode == Opcode::VCVTPD2DQ || instr.opcode == Opcode::VCVTTPS2UQQ || instr.opcode == Opcode::VCVTPS2UQQ || instr.opcode == Opcode::VCVTTPS2QQ || instr.opcode == Opcode::VCVTPS2QQ {
+ if instr.opcode == Opcode::VFPCLASSPS || instr.opcode == Opcode::VCVTNEPS2BF16 {
+ if evex.vex().l() {
+ 8
+ } else if evex.lp() {
+ 16
+ } else {
+ 4
+ }
+ } else if instr.opcode == Opcode::VFPCLASSPD {
+ if evex.vex().l() {
+ 4
+ } else if evex.lp() {
+ 8
+ } else {
+ 2
+ }
+ } else {
+ // vcvtpd2ps is "cool": in broadcast mode, it can read a
+ // double-precision float (qword), resize to single-precision,
+ // then broadcast that to the whole destination register. this
+ // means we need to show `xmm, qword [addr]{1to4}` if vector
+ // size is 256. likewise, scale of 8 for the same truncation
+ // reason if vector size is 512.
+ // vcvtudq2pd is the same story.
+ // vfpclassp{s,d} is a mystery to me.
+ if evex.vex().l() {
+ 4
+ } else if evex.lp() {
+ 8
+ } else {
+ 2
+ }
+ }
+ } else {
+ // this should never be `None` - that would imply two
+ // memory operands for a broadcasted operation.
+ if let Some(width) = Operand::from_spec(instr, instr.operands[i as usize - 1]).width() {
+ width / instr.mem_size
+ } else {
+ 0
+ }
+ };
+ write!(out, "{{1to{}}}", scale)?;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ Ok(())
+}
+
+fn contextualize_c<T: fmt::Write, Y: YaxColors>(instr: &Instruction, colors: &Y, _address: u32, _context: Option<&NoContext>, out: &mut T) -> fmt::Result {
+ let mut brace_count = 0;
+
+ let mut prefixed = false;
+
+ if instr.xacquire() {
+ out.write_str("xacquire ")?;
+ prefixed = true;
+ }
+ if instr.xrelease() {
+ out.write_str("xrelease ")?;
+ prefixed = true;
+ }
+ if instr.prefixes.lock() {
+ out.write_str("lock ")?;
+ prefixed = true;
+ }
+
+ if prefixed {
+ out.write_str("{ ")?;
+ 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.repnz() {
+ out.write_str("repnz ")?;
+ } // TODO: other rep kinds?
+
+ out.write_str(word_str)?;
+ out.write_str(" { ")?;
+ brace_count += 1;
+ }
+ }
+
+ fn write_jmp_operand<T: fmt::Write, Y: YaxColors>(op: Operand, colors: &Y, out: &mut T) -> fmt::Result {
+ match op {
+ Operand::ImmediateI8(rel) => {
+ if rel >= 0 {
+ write!(out, "$+{}", colors.number(signed_i32_hex(rel as i32)))
+ } else {
+ write!(out, "${}", colors.number(signed_i32_hex(rel as i32)))
+ }
+ }
+ Operand::ImmediateI32(rel) => {
+ if rel >= 0 {
+ write!(out, "$+{}", colors.number(signed_i32_hex(rel)))
+ } else {
+ write!(out, "${}", colors.number(signed_i32_hex(rel)))
+ }
+ }
+ other => {
+ write!(out, "{}", other)
+ }
+ }
+ }
+
+ match instr.opcode {
+ Opcode::Invalid => { out.write_str("invalid")?; },
+ Opcode::MOVS => {
+ out.write_str("es:[edi++] = ds:[esi++]")?;
+ },
+ Opcode::CMPS => {
+ out.write_str("eflags = flags(ds:[esi++] - es:[edi++])")?;
+ },
+ Opcode::LODS => {
+ // TODO: size
+ out.write_str("rax = ds:[esi++]")?;
+ },
+ Opcode::STOS => {
+ // TODO: size
+ out.write_str("es:[edi++] = rax")?;
+ },
+ Opcode::INS => {
+ // TODO: size
+ out.write_str("es:[edi++] = port(dx)")?;
+ },
+ Opcode::OUTS => {
+ // TODO: size
+ out.write_str("port(dx) = ds:[esi++]")?;
+ }
+ Opcode::ADD => {
+ write!(out, "{} += {}", instr.operand(0), instr.operand(1))?;
+ }
+ Opcode::OR => {
+ write!(out, "{} |= {}", instr.operand(0), instr.operand(1))?;
+ }
+ Opcode::ADC => {
+ write!(out, "{} += {} + eflags.cf", instr.operand(0), instr.operand(1))?;
+ }
+ Opcode::ADCX => {
+ write!(out, "{} += {} + eflags.cf", instr.operand(0), instr.operand(1))?;
+ }
+ Opcode::ADOX => {
+ write!(out, "{} += {} + eflags.of", instr.operand(0), instr.operand(1))?;
+ }
+ Opcode::SBB => {
+ write!(out, "{} -= {} + eflags.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, "eflags = flags({} - {})", instr.operand(0), instr.operand(1))?;
+ }
+ Opcode::TEST => {
+ write!(out, "eflags = 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 => {
+ if instr.operand(0).is_memory() {
+ match instr.mem_size {
+ 1 => { write!(out, "byte {}++", instr.operand(0))?; },
+ 2 => { write!(out, "word {}++", instr.operand(0))?; },
+ 4 => { write!(out, "dword {}++", instr.operand(0))?; },
+ _ => { write!(out, "qword {}++", instr.operand(0))?; }, // sizes that are not 1, 2, or 4, *better* be 8.
+ }
+ } else {
+ write!(out, "{}++", instr.operand(0))?;
+ }
+ }
+ Opcode::DEC => {
+ if instr.operand(0).is_memory() {
+ match instr.mem_size {
+ 1 => { write!(out, "byte {}--", instr.operand(0))?; },
+ 2 => { write!(out, "word {}--", instr.operand(0))?; },
+ 4 => { write!(out, "dword {}--", instr.operand(0))?; },
+ _ => { write!(out, "qword {}--", instr.operand(0))?; }, // sizes that are not 1, 2, or 4, *better* be 8.
+ }
+ } else {
+ write!(out, "{}--", instr.operand(0))?;
+ }
+ }
+ Opcode::JMP => {
+ out.write_str("jmp ")?;
+ write_jmp_operand(instr.operand(0), colors, out)?;
+ },
+ Opcode::JECXZ => {
+ out.write_str("if ecx == 0 then jmp ")?;
+ write_jmp_operand(instr.operand(0), colors, out)?;
+ },
+ Opcode::LOOP => {
+ out.write_str("ecx--; if ecx != 0 then jmp ")?;
+ write_jmp_operand(instr.operand(0), colors, out)?;
+ },
+ Opcode::LOOPZ => {
+ out.write_str("ecx--; if ecx != 0 and zero(rflags) then jmp ")?;
+ write_jmp_operand(instr.operand(0), colors, out)?;
+ },
+ Opcode::LOOPNZ => {
+ out.write_str("ecx--; if ecx != 0 and !zero(rflags) then jmp ")?;
+ write_jmp_operand(instr.operand(0), colors, out)?;
+ },
+ Opcode::JO => {
+ out.write_str("if _(rflags) then jmp ")?;
+ write_jmp_operand(instr.operand(0), colors, out)?;
+ },
+ Opcode::JNO => {
+ out.write_str("if _(rflags) then jmp ")?;
+ write_jmp_operand(instr.operand(0), colors, out)?;
+ },
+ Opcode::JB => {
+ out.write_str("if /* unsigned */ below(rflags) then jmp ")?;
+ write_jmp_operand(instr.operand(0), colors, out)?;
+ },
+ Opcode::JNB => {
+ out.write_str("if /* unsigned */ above_or_equal(rflags) then jmp ")?;
+ write_jmp_operand(instr.operand(0), colors, out)?;
+ },
+ Opcode::JZ => {
+ out.write_str("if zero(rflags) then jmp ")?;
+ write_jmp_operand(instr.operand(0), colors, out)?;
+ },
+ Opcode::JNZ => {
+ out.write_str("if !zero(rflags) then jmp ")?;
+ write_jmp_operand(instr.operand(0), colors, out)?;
+ },
+ Opcode::JNA => {
+ out.write_str("if /* unsigned */ below_or_equal(rflags) then jmp ")?;
+ write_jmp_operand(instr.operand(0), colors, out)?;
+ },
+ Opcode::JA => {
+ out.write_str("if /* unsigned */ above(rflags) then jmp ")?;
+ write_jmp_operand(instr.operand(0), colors, out)?;
+ },
+ Opcode::JS => {
+ out.write_str("if signed(rflags) then jmp ")?;
+ write_jmp_operand(instr.operand(0), colors, out)?;
+ },
+ Opcode::JNS => {
+ out.write_str("if !signed(rflags) then jmp ")?;
+ write_jmp_operand(instr.operand(0), colors, out)?;
+ },
+ Opcode::JP => {
+ out.write_str("if parity(rflags) then jmp ")?;
+ write_jmp_operand(instr.operand(0), colors, out)?;
+ },
+ Opcode::JNP => {
+ out.write_str("if !parity(rflags) then jmp ")?;
+ write_jmp_operand(instr.operand(0), colors, out)?;
+ },
+ Opcode::JL => {
+ out.write_str("if /* signed */ less(rflags) then jmp ")?;
+ write_jmp_operand(instr.operand(0), colors, out)?;
+ },
+ Opcode::JGE => {
+ out.write_str("if /* signed */ greater_or_equal(rflags) then jmp ")?;
+ write_jmp_operand(instr.operand(0), colors, out)?;
+ },
+ Opcode::JLE => {
+ out.write_str("if /* signed */ less_or_equal(rflags) then jmp ")?;
+ write_jmp_operand(instr.operand(0), colors, out)?;
+ },
+ Opcode::JG => {
+ out.write_str("if /* signed */ greater(rflags) then jmp ")?;
+ write_jmp_operand(instr.operand(0), colors, out)?;
+ },
+ 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, Y: YaxColors> ShowContextual<u32, NoContext, T, Y> for InstructionDisplayer<'instr> {
+ fn contextualize(&self, colors: &Y, address: u32, 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)
+ }
+ }
+ }
+}
+
+#[cfg(feature="std")]
+impl <T: fmt::Write, Y: YaxColors> ShowContextual<u64, [Option<alloc::string::String>], T, Y> for Instruction {
+ fn contextualize(&self, colors: &Y, _address: u64, context: Option<&[Option<alloc::string::String>]>, out: &mut T) -> fmt::Result {
+ if self.prefixes.lock() {
+ write!(out, "lock ")?;
+ }
+
+ 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.repnz() {
+ write!(out, "repnz ")?;
+ }
+ }
+
+ self.opcode.colorize(colors, out)?;
+
+ match context.and_then(|xs| xs[0].as_ref()) {
+ Some(s) => { write!(out, " {}", s)?; },
+ None => {
+ match self.operands[0] {
+ OperandSpec::Nothing => {
+ return Ok(());
+ },
+ _ => {
+ write!(out, " ")?;
+ if let Some(prefix) = self.segment_override_for_op(0) {
+ write!(out, "{}:", prefix)?;
+ }
+ }
+ }
+ let x = Operand::from_spec(self, self.operands[0]);
+ x.colorize(colors, out)?;
+ }
+ };
+ for i in 1..self.operand_count {
+ let i = i as usize;
+ match context.and_then(|xs| xs[i].as_ref()) {
+ Some(s) => { write!(out, ", {}", s)? }
+ None => {
+ match &self.operands[i] {
+ &OperandSpec::Nothing => {
+ return Ok(());
+ },
+ _ => {
+ write!(out, ", ")?;
+ if let Some(prefix) = self.segment_override_for_op(1) {
+ write!(out, "{}:", prefix)?;
+ }
+ let x = Operand::from_spec(self, self.operands[i]);
+ x.colorize(colors, out)?
+ }
+ }
+ }
+ }
+ }
+ Ok(())
+ }
+}