From b1213132953ff18efaa126e0b3f9f22e7ea44ed3 Mon Sep 17 00:00:00 2001 From: iximeow Date: Sun, 23 Jun 2024 12:53:41 -0700 Subject: adapt the rest of formating changes to protected_mode --- src/protected_mode/display.rs | 469 ++++++++++++++++++++++++++++-------------- 1 file changed, 312 insertions(+), 157 deletions(-) (limited to 'src/protected_mode/display.rs') diff --git a/src/protected_mode/display.rs b/src/protected_mode/display.rs index 635893c..46bb185 100644 --- a/src/protected_mode/display.rs +++ b/src/protected_mode/display.rs @@ -187,6 +187,19 @@ impl fmt::Display for PrefixVex { } } +impl Segment { + fn name(&self) -> &'static [u8; 2] { + match self { + Segment::CS => b"cs", + Segment::DS => b"ds", + Segment::ES => b"es", + Segment::FS => b"fs", + Segment::GS => b"gs", + Segment::SS => b"ss", + } + } +} + impl fmt::Display for Segment { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { @@ -257,7 +270,7 @@ struct DisplayingOperandVisitor<'a, T> { f: &'a mut T, } -impl crate::protected_mode::OperandVisitor for DisplayingOperandVisitor<'_, T> { +impl super::OperandVisitor for DisplayingOperandVisitor<'_, T> { type Ok = (); type Error = core::fmt::Error; @@ -3628,155 +3641,140 @@ impl Instruction { } } -fn contextualize_intel(instr: &Instruction, colors: &Y, _address: u32, _context: Option<&NoContext>, out: &mut T) -> fmt::Result { +pub(crate) fn contextualize_intel(instr: &Instruction, out: &mut T) -> fmt::Result { if instr.xacquire() { - out.write_str("xacquire ")?; + out.write_fixed_size("xacquire ")?; } if instr.xrelease() { - out.write_str("xrelease ")?; + out.write_fixed_size("xrelease ")?; } if instr.prefixes.lock() { - out.write_str("lock ")?; + out.write_fixed_size("lock ")?; } if instr.prefixes.rep_any() { if instr.opcode.can_rep() { if instr.prefixes.rep() { - write!(out, "rep ")?; + out.write_fixed_size("rep ")?; } else if instr.prefixes.repnz() { - write!(out, "repnz ")?; + out.write_fixed_size("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))); - } - } + out.write_opcode(instr.opcode)?; 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!() } - }; - } - } + out.write_fixed_size(" ")?; - if x.is_memory() { - out.write_str(MEM_SIZE_STRINGS[instr.mem_size as usize])?; - out.write_str(" ")?; + if instr.visit_operand(0, &mut RelativeBranchPrinter { + inst: instr, + out, + })? { + return Ok(()); } - if let Some(prefix) = instr.segment_override_for_op(0) { - write!(out, "{}:", prefix)?; + if instr.operands[0 as usize].is_memory() { + out.write_mem_size_label(instr.mem_size)?; + if let Some(prefix) = instr.segment_override_for_op(0) { + let name = prefix.name(); + out.write_char(' ')?; + out.write_char(name[0] as char)?; + out.write_char(name[1] as char)?; + out.write_fixed_size(":")?; + } else { + out.write_fixed_size(" ")?; + } } - x.colorize(colors, out)?; + + let mut displayer = DisplayingOperandVisitor { + f: out, + }; + instr.visit_operand(0 as u8, &mut displayer)?; 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])?; - out.write_str(" ")?; + // don't worry about checking for `instr.operands[i] != Nothing`, it would be a bug to + // reach that while iterating only to `operand_count`.. + out.write_fixed_size(", ")?; + if i >= 4 { + unsafe { core::hint::unreachable_unchecked(); } + } + + if instr.operands[i as usize].is_memory() { + out.write_mem_size_label(instr.mem_size)?; + if i >= 4 { + unsafe { core::hint::unreachable_unchecked(); } + } + if let Some(prefix) = instr.segment_override_for_op(i) { + let name = prefix.name(); + out.write_char(' ')?; + out.write_char(name[0] as char)?; + out.write_char(name[1] as char)?; + out.write_fixed_size(":")?; + } else { + out.write_fixed_size(" ")?; + } + } + + let mut displayer = DisplayingOperandVisitor { + f: out, + }; + + instr.visit_operand(i as u8, &mut displayer)?; + if let Some(evex) = instr.prefixes.evex() { + if evex.broadcast() && instr.operands[i as usize].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 } - if let Some(prefix) = instr.segment_override_for_op(i) { - write!(out, "{}:", prefix)?; + } else if instr.opcode == Opcode::VFPCLASSPD { + if evex.vex().l() { + 4 + } else if evex.lp() { + 8 + } else { + 2 } - 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)?; - } + } 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 + } + }; + out.write_fixed_size("{1to")?; + static STRING_LUT: &'static [&'static str] = &[ + "0", "1", "2", "3", "4", "5", "6", "7", "8", + "9", "10", "11", "12", "13", "14", "15", "16", + ]; + unsafe { + out.write_lt_16(STRING_LUT.get_kinda_unchecked(scale as usize))?; } + out.write_char('}')?; } } } @@ -3784,7 +3782,7 @@ fn contextualize_intel(instr: &Instruction, colors: Ok(()) } -fn contextualize_c(instr: &Instruction, colors: &Y, _address: u32, _context: Option<&NoContext>, out: &mut T) -> fmt::Result { +pub(crate) fn contextualize_c(instr: &Instruction, out: &mut T) -> fmt::Result { let mut brace_count = 0; let mut prefixed = false; @@ -3808,7 +3806,7 @@ fn contextualize_c(instr: &Instruction, colors: &Y, } if instr.prefixes.rep_any() { - if [Opcode::MOVS, Opcode::CMPS, Opcode::LODS, Opcode::STOS, Opcode::INS, Opcode::OUTS].contains(&instr.opcode) { + if instr.opcode.can_rep() { let word_str = match instr.mem_size { 1 => "byte", 2 => "word", @@ -3830,21 +3828,29 @@ fn contextualize_c(instr: &Instruction, colors: &Y, } } - fn write_jmp_operand(op: Operand, colors: &Y, out: &mut T) -> fmt::Result { + fn write_jmp_operand(op: Operand, out: &mut T) -> fmt::Result { + let mut out = yaxpeax_arch::display::FmtSink::new(out); + use core::fmt::Write; match op { Operand::ImmediateI8(rel) => { - if rel >= 0 { - write!(out, "$+{}", colors.number(signed_i32_hex(rel as i32))) + let rel = if rel >= 0 { + out.write_str("$+")?; + rel as u8 } else { - write!(out, "${}", colors.number(signed_i32_hex(rel as i32))) - } + out.write_str("$-")?; + rel.unsigned_abs() + }; + out.write_prefixed_u8(rel) } Operand::ImmediateI32(rel) => { - if rel >= 0 { - write!(out, "$+{}", colors.number(signed_i32_hex(rel))) + let rel = if rel >= 0 { + out.write_str("$+")?; + rel as u32 } else { - write!(out, "${}", colors.number(signed_i32_hex(rel))) - } + out.write_str("$-")?; + rel.unsigned_abs() + }; + out.write_prefixed_u32(rel) } other => { write!(out, "{}", other) @@ -4007,87 +4013,87 @@ fn contextualize_c(instr: &Instruction, colors: &Y, } Opcode::JMP => { out.write_str("jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::JECXZ => { out.write_str("if ecx == 0 then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::LOOP => { out.write_str("ecx--; if ecx != 0 then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::LOOPZ => { out.write_str("ecx--; if ecx != 0 and zero(rflags) then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::LOOPNZ => { out.write_str("ecx--; if ecx != 0 and !zero(rflags) then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::JO => { out.write_str("if _(rflags) then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::JNO => { out.write_str("if _(rflags) then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::JB => { out.write_str("if /* unsigned */ below(rflags) then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::JNB => { out.write_str("if /* unsigned */ above_or_equal(rflags) then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::JZ => { out.write_str("if zero(rflags) then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::JNZ => { out.write_str("if !zero(rflags) then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::JNA => { out.write_str("if /* unsigned */ below_or_equal(rflags) then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::JA => { out.write_str("if /* unsigned */ above(rflags) then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::JS => { out.write_str("if signed(rflags) then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::JNS => { out.write_str("if !signed(rflags) then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::JP => { out.write_str("if parity(rflags) then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::JNP => { out.write_str("if !parity(rflags) then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::JL => { out.write_str("if /* signed */ less(rflags) then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::JGE => { out.write_str("if /* signed */ greater_or_equal(rflags) then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::JLE => { out.write_str("if /* signed */ less_or_equal(rflags) then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::JG => { out.write_str("if /* signed */ greater(rflags) then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::NOP => { write!(out, "nop")?; @@ -4119,18 +4125,20 @@ fn contextualize_c(instr: &Instruction, colors: &Y, } impl <'instr, T: fmt::Write, Y: YaxColors> ShowContextual for InstructionDisplayer<'instr> { - fn contextualize(&self, colors: &Y, address: u32, context: Option<&NoContext>, out: &mut T) -> fmt::Result { + fn contextualize(&self, _colors: &Y, _address: u32, _context: Option<&NoContext>, out: &mut T) -> fmt::Result { let InstructionDisplayer { instr, style, } = self; + let mut out = yaxpeax_arch::display::FmtSink::new(out); + match style { DisplayStyle::Intel => { - contextualize_intel(instr, colors, address, context, out) + contextualize_intel(instr, &mut out) } DisplayStyle::C => { - contextualize_c(instr, colors, address, context, out) + contextualize_c(instr, &mut out) } } } @@ -4196,3 +4204,150 @@ impl ShowContextual { + inst: &'a Instruction, + out: &'a mut F, +} + +impl<'a, F: DisplaySink> super::OperandVisitor for RelativeBranchPrinter<'a, F> { + // return true if we printed a relative branch offset, false otherwise + type Ok = bool; + // but errors are errors + type Error = fmt::Error; + + fn visit_reg(&mut self, _reg: RegSpec) -> Result { + Ok(false) + } + fn visit_deref(&mut self, _reg: RegSpec) -> Result { + Ok(false) + } + fn visit_disp(&mut self, _reg: RegSpec, _disp: i32) -> Result { + Ok(false) + } + #[cfg_attr(feature="profiling", inline(never))] + fn visit_i8(&mut self, rel: i8) -> Result { + if RELATIVE_BRANCHES.contains(&self.inst.opcode) { + self.out.write_char('$')?; + // danger_anguished_string_write(self.out, "$"); + let mut v = rel as u8; + if rel < 0 { + self.out.write_char('-')?; + //danger_anguished_string_write(&mut self.out, "-"); + v = -rel as u8; + } else { + self.out.write_char('+')?; + // danger_anguished_string_write(&mut self.out, "+"); + } + self.out.write_fixed_size("0x")?; + self.out.write_u8(v)?; + Ok(true) + } else { + Ok(false) + } + } + #[cfg_attr(feature="profiling", inline(never))] + fn visit_i32(&mut self, rel: i32) -> Result { + if RELATIVE_BRANCHES.contains(&self.inst.opcode) || self.inst.opcode == Opcode::XBEGIN { + self.out.write_char('$')?; + // danger_anguished_string_write(self.out, "$"); + let mut v = rel as u32; + if rel < 0 { + self.out.write_char('-')?; + // danger_anguished_string_write(&mut self.out, "-"); + v = -rel as u32; + } else { + self.out.write_char('+')?; + // danger_anguished_string_write(&mut self.out, "+"); + } + self.out.write_fixed_size("0x")?; + self.out.write_u32(v)?; + Ok(true) + } else { + Ok(false) + } + } + fn visit_u8(&mut self, _imm: u8) -> Result { + Ok(false) + } + fn visit_i16(&mut self, _imm: i16) -> Result { + Ok(false) + } + fn visit_u16(&mut self, _imm: u16) -> Result { + Ok(false) + } + fn visit_u32(&mut self, _imm: u32) -> Result { + Ok(false) + } + fn visit_abs_u16(&mut self, _imm: u16) -> Result { + Ok(false) + } + fn visit_abs_u32(&mut self, _imm: u32) -> Result { + Ok(false) + } + fn visit_reg_scale(&mut self, _reg: RegSpec, _scale: u8) -> Result { + Ok(false) + } + fn visit_index_base_scale(&mut self, _base: RegSpec, _index: RegSpec, _scale: u8) -> Result { + Ok(false) + } + fn visit_reg_scale_disp(&mut self, _reg: RegSpec, _scale: u8, _disp: i32) -> Result { + Ok(false) + } + fn visit_index_base_scale_disp(&mut self, _base: RegSpec, _index: RegSpec, _scale: u8, _disp: i32) -> Result { + Ok(false) + } + fn visit_other(&mut self) -> Result { + Ok(false) + } + fn visit_reg_mask_merge(&mut self, _spec: RegSpec, _mask: RegSpec, _merge_mode: MergeMode) -> Result { + Ok(false) + } + fn visit_reg_mask_merge_sae(&mut self, _spec: RegSpec, _mask: RegSpec, _merge_mode: MergeMode, _sae_mode: super::SaeMode) -> Result { + Ok(false) + } + fn visit_reg_mask_merge_sae_noround(&mut self, _spec: RegSpec, _mask: RegSpec, _merge_mode: MergeMode) -> Result { + Ok(false) + } + fn visit_reg_disp_masked(&mut self, _spec: RegSpec, _disp: i32, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_reg_deref_masked(&mut self, _spec: RegSpec, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_reg_scale_masked(&mut self, _spec: RegSpec, _scale: u8, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_reg_scale_disp_masked(&mut self, _spec: RegSpec, _scale: u8, _disp: i32, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_index_base_masked(&mut self, _base: RegSpec, _index: RegSpec, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_index_base_disp_masked(&mut self, _base: RegSpec, _index: RegSpec, _disp: i32, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_index_base_scale_masked(&mut self, _base: RegSpec, _index: RegSpec, _scale: u8, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_index_base_scale_disp_masked(&mut self, _base: RegSpec, _index: RegSpec, _scale: u8, _disp: i32, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_absolute_far_address(&mut self, _segment: u16, _address: u32) -> Result { + Ok(false) + } +} -- cgit v1.1