pub struct SpecializedFormatter<TraitOptions: SpecializedFormatterTraitOptions> { /* private fields */ }
Expand description

Fast specialized formatter with less formatting options and with a masm-like syntax. Use it if formatting speed is more important than being able to re-assemble formatted instructions.

The TraitOptions generic parameter is a SpecializedFormatterTraitOptions trait. It can be used to hard code options so the compiler can create a smaller and faster formatter. See also FastFormatter which allows changing the options at runtime at the cost of being a little bit slower and using a little bit more code.

This formatter is ~3.3x faster than the gas/intel/masm/nasm formatters (the time includes decoding + formatting).

Examples

use iced_x86::*;

let bytes = b"\x62\xF2\x4F\xDD\x72\x50\x01";
let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
let instr = decoder.decode();

// If you like the default options, you can also use DefaultSpecializedFormatterTraitOptions
// instead of impl the options trait.
struct MyTraitOptions;
impl SpecializedFormatterTraitOptions for MyTraitOptions {
    fn space_after_operand_separator(_options: &FastFormatterOptions) -> bool {
        // We hard code the value to `true` which means it's not possible to
        // change this option at runtime, i.e., this will do nothing:
        //      formatter.options_mut().set_space_after_operand_separator(false);
        true
    }
    fn rip_relative_addresses(options: &FastFormatterOptions) -> bool {
        // Since we return the input, we can change this value at runtime, i.e.,
        // this works:
        //      formatter.options_mut().set_rip_relative_addresses(false);
        options.rip_relative_addresses()
    }
}
type MyFormatter = SpecializedFormatter<MyTraitOptions>;

let mut output = String::new();
let mut formatter = MyFormatter::new();
formatter.format(&instr, &mut output);
assert_eq!(output, "vcvtne2ps2bf16 zmm2{k5}{z}, zmm6, dword bcst [rax+0x4]");

Fastest possible disassembly

For fastest possible disassembly, you should not enable the db feature (or you should set ENABLE_DB_DW_DD_DQ to false) and you should also override the unsafe verify_output_has_enough_bytes_left() and return false.

use iced_x86::*;

struct MyTraitOptions;
impl SpecializedFormatterTraitOptions for MyTraitOptions {
    // If you never create a db/dw/dd/dq 'instruction', we don't need this feature.
    const ENABLE_DB_DW_DD_DQ: bool = false;
    // For a few percent faster code, you can also override `verify_output_has_enough_bytes_left()` and return `false`
    // unsafe fn verify_output_has_enough_bytes_left() -> bool {
    //     false
    // }
}
type MyFormatter = SpecializedFormatter<MyTraitOptions>;

// Assume this is a big slice and not just one instruction
let bytes = b"\x62\xF2\x4F\xDD\x72\x50\x01";
let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);

let mut output = String::new();
let mut instruction = Instruction::default();
let mut formatter = MyFormatter::new();
while decoder.can_decode() {
    decoder.decode_out(&mut instruction);
    output.clear();
    formatter.format(&instruction, &mut output);
    // do something with 'output' here, eg.:
    //     println!("{}", output);
}

Also add this to your Cargo.toml file:

[profile.release]
codegen-units = 1
lto = true
opt-level = 3

Using a symbol resolver

The symbol resolver is disabled by default, but it’s easy to enable it (or you can just use FastFormatter)

use iced_x86::*;
use std::collections::HashMap;

let bytes = b"\x48\x8B\x8A\xA5\x5A\xA5\x5A";
let mut decoder = Decoder::new(64, bytes, DecoderOptions::NONE);
let instr = decoder.decode();

struct MyTraitOptions;
impl SpecializedFormatterTraitOptions for MyTraitOptions {
    const ENABLE_SYMBOL_RESOLVER: bool = true;
}
type MyFormatter = SpecializedFormatter<MyTraitOptions>;

struct MySymbolResolver { map: HashMap<u64, String> }
impl SymbolResolver for MySymbolResolver {
    fn symbol(&mut self, _instruction: &Instruction, _operand: u32, _instruction_operand: Option<u32>,
         address: u64, _address_size: u32) -> Option<SymbolResult> {
        if let Some(symbol_string) = self.map.get(&address) {
            // The 'address' arg is the address of the symbol and doesn't have to be identical
            // to the 'address' arg passed to symbol(). If it's different from the input
            // address, the formatter will add +N or -N, eg. '[rax+symbol+123]'
            Some(SymbolResult::with_str(address, symbol_string.as_str()))
        } else {
            None
        }
    }
}

// Hard code the symbols, it's just an example!😄
let mut sym_map: HashMap<u64, String> = HashMap::new();
sym_map.insert(0x5AA55AA5, String::from("my_data"));

let mut output = String::new();
let resolver = Box::new(MySymbolResolver { map: sym_map });
let mut formatter = MyFormatter::try_with_options(Some(resolver)).unwrap();
formatter.format(&instr, &mut output);
assert_eq!("mov rcx,[rdx+my_data]", output);

Implementations§

source§

impl<TraitOptions: SpecializedFormatterTraitOptions> SpecializedFormatter<TraitOptions>

source

pub fn new() -> Self

Creates a new instance of this formatter

source

pub fn try_with_options( symbol_resolver: Option<Box<dyn SymbolResolver>> ) -> Result<Self, IcedError>

Creates a new instance of this formatter

Errors

Fails if TraitOptions::ENABLE_SYMBOL_RESOLVER is false and symbol_resolver.is_some()

Arguments
  • symbol_resolver: Symbol resolver or None
source

pub fn options(&self) -> &FastFormatterOptions

Gets the formatter options (immutable)

Note that the TraitOptions generic parameter can override any option and hard code them, see SpecializedFormatterTraitOptions

source

pub fn options_mut(&mut self) -> &mut FastFormatterOptions

Gets the formatter options (mutable)

Note that the TraitOptions generic parameter can override any option and hard code them, see SpecializedFormatterTraitOptions

source

pub fn format(&mut self, instruction: &Instruction, output: &mut String)

Formats the whole instruction: prefixes, mnemonic, operands

Arguments
  • instruction: Instruction
  • output: Output

Trait Implementations§

source§

impl<TraitOptions: SpecializedFormatterTraitOptions> Default for SpecializedFormatter<TraitOptions>

source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl<TraitOptions> !RefUnwindSafe for SpecializedFormatter<TraitOptions>

§

impl<TraitOptions> !Send for SpecializedFormatter<TraitOptions>

§

impl<TraitOptions> !Sync for SpecializedFormatter<TraitOptions>

§

impl<TraitOptions> Unpin for SpecializedFormatter<TraitOptions>

§

impl<TraitOptions> !UnwindSafe for SpecializedFormatter<TraitOptions>

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.