diff options
| author | iximeow <me@iximeow.net> | 2024-06-23 12:53:41 -0700 | 
|---|---|---|
| committer | iximeow <me@iximeow.net> | 2024-06-23 12:53:41 -0700 | 
| commit | b1213132953ff18efaa126e0b3f9f22e7ea44ed3 (patch) | |
| tree | df09affb52bbe8ac2ef7e2308dc3f37db23844c3 /src | |
| parent | eec945bb991989a9dfcef2d5334746ba863e0abc (diff) | |
adapt the rest of formating changes to protected_mode
Diffstat (limited to 'src')
| -rw-r--r-- | src/long_mode/display.rs | 6 | ||||
| -rw-r--r-- | src/protected_mode/display.rs | 469 | ||||
| -rw-r--r-- | src/protected_mode/mod.rs | 165 | 
3 files changed, 480 insertions, 160 deletions
diff --git a/src/long_mode/display.rs b/src/long_mode/display.rs index bcea94a..869ac0b 100644 --- a/src/long_mode/display.rs +++ b/src/long_mode/display.rs @@ -272,7 +272,7 @@ struct DisplayingOperandVisitor<'a, T> {      f: &'a mut T,  } -impl <T: DisplaySink> crate::long_mode::OperandVisitor for DisplayingOperandVisitor<'_, T> { +impl <T: DisplaySink> super::OperandVisitor for DisplayingOperandVisitor<'_, T> {      type Ok = ();      type Error = core::fmt::Error; @@ -4237,7 +4237,7 @@ struct RelativeBranchPrinter<'a, F: DisplaySink> {      out: &'a mut F,  } -impl<'a, F: DisplaySink> crate::long_mode::OperandVisitor for RelativeBranchPrinter<'a, 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 @@ -4336,7 +4336,7 @@ impl<'a, F: DisplaySink> crate::long_mode::OperandVisitor for RelativeBranchPrin      fn visit_reg_mask_merge(&mut self, _spec: RegSpec, _mask: RegSpec, _merge_mode: MergeMode) -> Result<Self::Ok, Self::Error> {          Ok(false)      } -    fn visit_reg_mask_merge_sae(&mut self, _spec: RegSpec, _mask: RegSpec, _merge_mode: MergeMode, _sae_mode: crate::long_mode::SaeMode) -> Result<Self::Ok, Self::Error> { +    fn visit_reg_mask_merge_sae(&mut self, _spec: RegSpec, _mask: RegSpec, _merge_mode: MergeMode, _sae_mode: super::SaeMode) -> Result<Self::Ok, Self::Error> {          Ok(false)      }      fn visit_reg_mask_merge_sae_noround(&mut self, _spec: RegSpec, _mask: RegSpec, _merge_mode: MergeMode) -> Result<Self::Ok, Self::Error> { 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 <T: DisplaySink> crate::protected_mode::OperandVisitor for DisplayingOperandVisitor<'_, T> { +impl <T: DisplaySink> super::OperandVisitor for DisplayingOperandVisitor<'_, T> {      type Ok = ();      type Error = core::fmt::Error; @@ -3628,155 +3641,140 @@ impl Instruction {      }  } -fn contextualize_intel<T: fmt::Write, Y: YaxColors>(instr: &Instruction, colors: &Y, _address: u32, _context: Option<&NoContext>, out: &mut T) -> fmt::Result { +pub(crate) fn contextualize_intel<T: DisplaySink>(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<T: fmt::Write, Y: YaxColors>(instr: &Instruction, colors:      Ok(())  } -fn contextualize_c<T: fmt::Write, Y: YaxColors>(instr: &Instruction, colors: &Y, _address: u32, _context: Option<&NoContext>, out: &mut T) -> fmt::Result { +pub(crate) fn contextualize_c<T: DisplaySink>(instr: &Instruction, out: &mut T) -> fmt::Result {      let mut brace_count = 0;      let mut prefixed = false; @@ -3808,7 +3806,7 @@ fn contextualize_c<T: fmt::Write, Y: YaxColors>(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<T: fmt::Write, Y: YaxColors>(instr: &Instruction, colors: &Y,          }      } -    fn write_jmp_operand<T: fmt::Write, Y: YaxColors>(op: Operand, colors: &Y, out: &mut T) -> fmt::Result { +    fn write_jmp_operand<T: fmt::Write>(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<T: fmt::Write, Y: YaxColors>(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<T: fmt::Write, Y: YaxColors>(instr: &Instruction, colors: &Y,  }  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 { +    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 <T: fmt::Write, Y: YaxColors> ShowContextual<u64, [Option<alloc::string::St          Ok(())      }  } + +// TODO: should include CALL +static 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, +]; + +struct RelativeBranchPrinter<'a, F: DisplaySink> { +    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<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_deref(&mut self, _reg: RegSpec) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_disp(&mut self, _reg: RegSpec, _disp: i32) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    #[cfg_attr(feature="profiling", inline(never))] +    fn visit_i8(&mut self, rel: i8) -> Result<Self::Ok, Self::Error> { +        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<Self::Ok, Self::Error> { +        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<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_i16(&mut self, _imm: i16) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_u16(&mut self, _imm: u16) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_u32(&mut self, _imm: u32) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_abs_u16(&mut self, _imm: u16) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_abs_u32(&mut self, _imm: u32) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_reg_scale(&mut self, _reg: RegSpec, _scale: u8) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_index_base_scale(&mut self, _base: RegSpec, _index: RegSpec, _scale: u8) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_reg_scale_disp(&mut self, _reg: RegSpec, _scale: u8, _disp: i32) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_index_base_scale_disp(&mut self, _base: RegSpec, _index: RegSpec, _scale: u8, _disp: i32) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_other(&mut self) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_reg_mask_merge(&mut self, _spec: RegSpec, _mask: RegSpec, _merge_mode: MergeMode) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_reg_mask_merge_sae(&mut self, _spec: RegSpec, _mask: RegSpec, _merge_mode: MergeMode, _sae_mode: super::SaeMode) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_reg_mask_merge_sae_noround(&mut self, _spec: RegSpec, _mask: RegSpec, _merge_mode: MergeMode) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_reg_disp_masked(&mut self, _spec: RegSpec, _disp: i32, _mask_reg: RegSpec) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_reg_deref_masked(&mut self, _spec: RegSpec, _mask_reg: RegSpec) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_reg_scale_masked(&mut self, _spec: RegSpec, _scale: u8, _mask_reg: RegSpec) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_reg_scale_disp_masked(&mut self, _spec: RegSpec, _scale: u8, _disp: i32, _mask_reg: RegSpec) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_index_base_masked(&mut self, _base: RegSpec, _index: RegSpec, _mask_reg: RegSpec) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_index_base_disp_masked(&mut self, _base: RegSpec, _index: RegSpec, _disp: i32, _mask_reg: RegSpec) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_index_base_scale_masked(&mut self, _base: RegSpec, _index: RegSpec, _scale: u8, _mask_reg: RegSpec) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_index_base_scale_disp_masked(&mut self, _base: RegSpec, _index: RegSpec, _scale: u8, _disp: i32, _mask_reg: RegSpec) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +    fn visit_absolute_far_address(&mut self, _segment: u16, _address: u32) -> Result<Self::Ok, Self::Error> { +        Ok(false) +    } +} diff --git a/src/protected_mode/mod.rs b/src/protected_mode/mod.rs index 7c7a4c0..74522d9 100644 --- a/src/protected_mode/mod.rs +++ b/src/protected_mode/mod.rs @@ -4332,6 +4332,171 @@ impl Instruction {          Operand::from_spec(self, self.operands[i as usize])      } +    /// TODO: make public, document, etc... +    /// +    /// `visit_operand` allows code using operands to better specialize and inline with the logic +    /// that would construct an [`Operand`] variant, without having to necessarily construct an +    /// `Operand` (including the attendant move of the enum). +    /// +    /// if the work you expect to do per-operand is very small, constructing an `Operand` and +    /// dispatching on tags may be a substantial factor of overall runtime. `visit_operand` can +    /// reduce total overhead in such cases. +    #[cfg_attr(features="profiling", inline(never))] +    fn visit_operand<T: OperandVisitor>(&self, i: u8, visitor: &mut T) -> Result<T::Ok, T::Error> { +        assert!(i < 4); +        let spec = self.operands[i as usize]; +        match spec { +            OperandSpec::Nothing => { +                visitor.visit_other() +            } +            OperandSpec::RegRRR => { +                visitor.visit_reg(self.regs[0]) +            } +            OperandSpec::RegMMM => { +                visitor.visit_reg(self.regs[1]) +            } +            OperandSpec::RegVex => { +                visitor.visit_reg(self.regs[3]) +            } +            OperandSpec::Reg4 => { +                visitor.visit_reg(RegSpec { num: self.imm as u8, bank: self.regs[3].bank }) +            } +            OperandSpec::Deref => { +                visitor.visit_deref(self.regs[1]) +            } +            OperandSpec::Deref_si => { +//                visitor.visit_other() +                visitor.visit_deref(RegSpec::si()) +            } +            OperandSpec::Deref_di => { +//                visitor.visit_other() +                visitor.visit_deref(RegSpec::di()) +            } +            OperandSpec::Deref_esi => { +//                visitor.visit_other() +                visitor.visit_deref(RegSpec::esi()) +            } +            OperandSpec::Deref_edi => { +//                visitor.visit_other() +                visitor.visit_deref(RegSpec::edi()) +            } +            OperandSpec::RegDisp => { +                visitor.visit_disp(self.regs[1], self.disp as i32) +            } +            OperandSpec::RegRRR_maskmerge => { +                visitor.visit_reg_mask_merge( +                    self.regs[0], +                    RegSpec::mask(self.prefixes.evex_unchecked().mask_reg()), +                    MergeMode::from(self.prefixes.evex_unchecked().merge()), +                ) +            } +            OperandSpec::RegRRR_maskmerge_sae => { +                visitor.visit_reg_mask_merge_sae( +                    self.regs[0], +                    RegSpec::mask(self.prefixes.evex_unchecked().mask_reg()), +                    MergeMode::from(self.prefixes.evex_unchecked().merge()), +                    SaeMode::from(self.prefixes.evex_unchecked().vex().l(), self.prefixes.evex_unchecked().lp()), +                ) +            } +            OperandSpec::RegRRR_maskmerge_sae_noround => { +                visitor.visit_reg_mask_merge_sae_noround( +                    self.regs[0], +                    RegSpec::mask(self.prefixes.evex_unchecked().mask_reg()), +                    MergeMode::from(self.prefixes.evex_unchecked().merge()), +                ) +            } +            OperandSpec::RegMMM_maskmerge => { +                visitor.visit_reg_mask_merge( +                    self.regs[1], +                    RegSpec::mask(self.prefixes.evex_unchecked().mask_reg()), +                    MergeMode::from(self.prefixes.evex_unchecked().merge()), +                ) +            } +            OperandSpec::RegMMM_maskmerge_sae_noround => { +                visitor.visit_reg_mask_merge_sae_noround( +                    self.regs[1], +                    RegSpec::mask(self.prefixes.evex_unchecked().mask_reg()), +                    MergeMode::from(self.prefixes.evex_unchecked().merge()), +                ) +            } +            OperandSpec::RegVex_maskmerge => { +                visitor.visit_reg_mask_merge( +                    self.regs[3], +                    RegSpec::mask(self.prefixes.evex_unchecked().mask_reg()), +                    MergeMode::from(self.prefixes.evex_unchecked().merge()), +                ) +            } +            OperandSpec::ImmI8 => visitor.visit_i8(self.imm as i8), +            OperandSpec::ImmU8 => visitor.visit_u8(self.imm as u8), +            OperandSpec::ImmI16 => visitor.visit_i16(self.imm as i16), +            OperandSpec::ImmU16 => visitor.visit_u16(self.imm as u16), +            OperandSpec::ImmI32 => visitor.visit_i32(self.imm as i32), +            OperandSpec::ImmInDispField => visitor.visit_u16(self.disp as u16), +            OperandSpec::DispU16 => visitor.visit_abs_u16(self.disp as u16), +            OperandSpec::DispU32 => visitor.visit_abs_u32(self.disp as u32), +            OperandSpec::RegScale => { +                visitor.visit_reg_scale(self.regs[2], self.scale) +            } +            OperandSpec::RegScaleDisp => { +                visitor.visit_reg_scale_disp(self.regs[2], self.scale, self.disp as i32) +            } +            OperandSpec::RegIndexBaseScale => { +                visitor.visit_index_base_scale(self.regs[1], self.regs[2], self.scale) +                    /* +                Operand::RegIndexBaseScale(self.regs[1], self.regs[2], self.scale) +                */ +            } +            OperandSpec::RegIndexBaseScaleDisp => { +                visitor.visit_index_base_scale_disp(self.regs[1], self.regs[2], self.scale, self.disp as i32) +            } +            OperandSpec::Deref_mask => { +                if self.prefixes.evex_unchecked().mask_reg() != 0 { +                    visitor.visit_reg_deref_masked(self.regs[1], RegSpec::mask(self.prefixes.evex_unchecked().mask_reg())) +                } else { +                    visitor.visit_deref(self.regs[1]) +                } +            } +            OperandSpec::RegDisp_mask => { +                if self.prefixes.evex_unchecked().mask_reg() != 0 { +                    visitor.visit_reg_disp_masked(self.regs[1], self.disp as i32, RegSpec::mask(self.prefixes.evex_unchecked().mask_reg())) +                } else { +                    visitor.visit_disp(self.regs[1], self.disp as i32) +                } +            } +            OperandSpec::RegScale_mask => { +                if self.prefixes.evex_unchecked().mask_reg() != 0 { +                    visitor.visit_reg_scale_masked(self.regs[2], self.scale, RegSpec::mask(self.prefixes.evex_unchecked().mask_reg())) +                } else { +                    visitor.visit_reg_scale(self.regs[2], self.scale) +                } +            } +            OperandSpec::RegScaleDisp_mask => { +                if self.prefixes.evex_unchecked().mask_reg() != 0 { +                    visitor.visit_reg_scale_disp_masked(self.regs[2], self.scale, self.disp as i32, RegSpec::mask(self.prefixes.evex_unchecked().mask_reg())) +                } else { +                    visitor.visit_reg_scale_disp(self.regs[2], self.scale, self.disp as i32) +                } +            } +            OperandSpec::RegIndexBaseScale_mask => { +                if self.prefixes.evex_unchecked().mask_reg() != 0 { +                    visitor.visit_index_base_scale_masked(self.regs[1], self.regs[2], self.scale, RegSpec::mask(self.prefixes.evex_unchecked().mask_reg())) +                } else { +                    visitor.visit_index_base_scale(self.regs[1], self.regs[2], self.scale) +                } +            } +            OperandSpec::RegIndexBaseScaleDisp_mask => { +                if self.prefixes.evex_unchecked().mask_reg() != 0 { +                    visitor.visit_index_base_scale_disp_masked(self.regs[1], self.regs[2], self.scale, self.disp as i32, RegSpec::mask(self.prefixes.evex_unchecked().mask_reg())) +                } else { +                    visitor.visit_index_base_scale_disp(self.regs[1], self.regs[2], self.scale, self.disp as i32) +                } +            } +            OperandSpec::AbsoluteFarAddress => { +                visitor.visit_absolute_far_address(self.disp as u16, self.imm as u32) +            } +        } +    } +      /// get the number of operands in this instruction. useful in iterating an instruction's      /// operands generically.      pub fn operand_count(&self) -> u8 {  | 
