diff --git a/CHANGELOG b/CHANGELOG index d420ed0b..26046bb4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,47 @@ +## 2.0.0 + +* upgrade to `yaxpeax-arch 0.3.1`, which brings with it a deprecation of the + `Colorize` and `ShowContextual` traits. +* because common use of yaxpeax-x86 involves using both this crate and + `yaxpeax-arch`, moving to a newer major version of `yaxpeax-arch` is a major + version bump of `yaxpeax-x86` as well. so, 2.0.0! + +changes: + +* `Operand` variants have had their naming made more consistent. + - many variants starting with "Reg" actually describe a memory access. they + now begin with "Mem" instead. + - several variants mentioned "Scale" in their name, but not "Index", even + though they use an index register. they now do. + - several variants mentioned their constituent parts out of order. for + example, "RegIndexBaseScaleDisp", even though the parts were specified as + base, then index, then scale, then displacement. these names have been + adjusted to reflect the order of their fields, which is roughly the order + those fields are shown when printed. + - `DisplacementU*` operands have always been access to memory at the absolute + address they specify. their names are now `AbsoluteU*` +* `Operand`, across the board, now uses struct-style enum variants, rather than tuple-style. +* the two changes together mean an operand that was + `RegIndexBaseScaleDisp(reg, reg, u8, i32)` + is now + `MemBaseIndexScaleDisp { base, index, scale, disp }` + and similar for other variants. +* two operand kinds, and their masked variants, were never actually constructed, and have been deleted. + - long ago yaxpeax-x86 returned different Operand variants when an index + register was used with scale 1, to hint that no scaling actually occurred. + this was eventually changed to return a scaling Operand variant with + scale==1, but the old variants remained. + - RegIndexBase has been removed + - RegIndexBaseDisp has been removed +* `Prefixes::selects_cs()` has been moved to `Prefixes::cs()`, and the old + useless functions are no more. `inst.prefixes().cs()` is finally a reasonable + way to determine if an instruction reads or writes through the cs prefix. + +fixes: + +* fix 32-bit call/jmp not respecting 66 prefix if set - such cases use 16-bit + operands, but decoded as if they used 32-bit operands. + ## 1.2.2 * fix `hreset` reporting two operands, with a second operand of `Nothing`. diff --git a/Cargo.toml b/Cargo.toml index e8734cc1..7b52dd9a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "yaxpeax-x86" -version = "1.2.2" +version = "2.0.0" authors = [ "iximeow " ] license = "0BSD" repository = "http://git.iximeow.net/yaxpeax-x86/" @@ -10,7 +10,7 @@ readme = "README.md" edition = "2018" [dependencies] -yaxpeax-arch = { version = "0.2.7", default-features = false, features = [] } +yaxpeax-arch = { version = "0.3.1", default-features = false, features = [] } "num-traits" = { version = "0.2", default-features = false } "serde" = { version = "1.0", optional = true } "serde_json" = { version = "1.0", optional = true } @@ -40,7 +40,13 @@ lto = true default = ["std", "colors", "use-serde", "fmt"] # opt-in for some apis that are really much nicer with String -std = ["yaxpeax-arch/std"] +std = ["alloc", "yaxpeax-arch/std"] + +# opt-in for some formatting-related helpers that require performing allocation +# +# this should only be useful with `fmt` currently, but in the future there could +# be other `fmt`-independent code gated on `alloc`. +alloc = ["yaxpeax-arch/alloc"] # feature for formatting instructions and their components fmt = [] @@ -52,3 +58,7 @@ colors = ["yaxpeax-arch/colors"] # This enables some capstone benchmarks over the same # instruction bytes used to bench this code. capstone_bench = [] + +# this disables a lot of inlining to make it easier for me to measure +# likelihood of codepaths for typical instruction sequences +profiling = [] diff --git a/build.rs b/build.rs index a9413754..116442ee 100644 --- a/build.rs +++ b/build.rs @@ -1,5 +1,5 @@ fn main() { - #[cfg(capstone_bench)] + #[cfg(feature="capstone_bench")] { println!("cargo:rustc-link-search=/usr/lib/"); println!("cargo:rustc-link-lib=capstone"); diff --git a/ffi/long_mode/Cargo.toml b/ffi/long_mode/Cargo.toml index 2df84502..0b6a529d 100644 --- a/ffi/long_mode/Cargo.toml +++ b/ffi/long_mode/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] yaxpeax-x86 = { path = "../../", default-features = false } -yaxpeax-arch = { version = "0.2.7", default-features = false } +yaxpeax-arch = { version = "0.3.1", default-features = false } [lib] name = "yaxpeax_x86_ffi_long_mode" diff --git a/ffi/multiarch/Cargo.toml b/ffi/multiarch/Cargo.toml index c15efbb3..1e2cd2db 100644 --- a/ffi/multiarch/Cargo.toml +++ b/ffi/multiarch/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] yaxpeax-x86 = { path = "../../", default-features = false } -yaxpeax-arch = { version = "0.2.7", default-features = false } +yaxpeax-arch = { version = "0.3.1", default-features = false } [lib] name = "yaxpeax_x86_ffi_multiarch" diff --git a/ffi/protected_mode/Cargo.toml b/ffi/protected_mode/Cargo.toml index 711845e2..ef99a397 100644 --- a/ffi/protected_mode/Cargo.toml +++ b/ffi/protected_mode/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] yaxpeax-x86 = { path = "../../", default-features = false } -yaxpeax-arch = { version = "0.2.7", default-features = false } +yaxpeax-arch = { version = "0.3.1", default-features = false } [lib] name = "yaxpeax_x86_ffi_protected_mode" diff --git a/ffi/real_mode/Cargo.toml b/ffi/real_mode/Cargo.toml index 689472ef..03b55734 100644 --- a/ffi/real_mode/Cargo.toml +++ b/ffi/real_mode/Cargo.toml @@ -6,9 +6,14 @@ edition = "2018" [dependencies] yaxpeax-x86 = { path = "../../", default-features = false } -yaxpeax-arch = { version = "0.2.7", default-features = false } +yaxpeax-arch = { version = "0.3.1", default-features = false } [lib] name = "yaxpeax_x86_ffi_real_mode" path = "src/lib.rs" -crate-type = ["staticlib"] +crate-type = ["staticlib", "cdylib"] + +[features] +default = ["fmt"] + +fmt = ["yaxpeax-x86/fmt"] diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 60690f6a..a1f871e4 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -10,6 +10,8 @@ cargo-fuzz = true [dependencies.yaxpeax-x86] path = ".." +[dependencies.yaxpeax-arch] +version = "0.3.1" [dependencies.libfuzzer-sys] git = "https://github.com/rust-fuzz/libfuzzer-sys.git" @@ -27,6 +29,18 @@ path = "fuzz_targets/display_does_not_panic.rs" test = false doc = false +[[bin]] +name = "displaysink_used_correctly" +path = "fuzz_targets/displaysink_used_correctly.rs" +test = false +doc = false + +[[bin]] +name = "instruction_text_buffer_size_ok" +path = "fuzz_targets/instruction_text_buffer_size_ok.rs" +test = false +doc = false + [[bin]] name = "display_c_does_not_panic" path = "fuzz_targets/display_c_does_not_panic.rs" diff --git a/fuzz/fuzz_targets/decode_does_not_panic.rs b/fuzz/fuzz_targets/decode_does_not_panic.rs index 5e6c15d0..fd6efecf 100644 --- a/fuzz/fuzz_targets/decode_does_not_panic.rs +++ b/fuzz/fuzz_targets/decode_does_not_panic.rs @@ -6,7 +6,7 @@ fuzz_target!(|data: &[u8]| { let x86_64_decoder = yaxpeax_x86::long_mode::InstDecoder::default(); let x86_32_decoder = yaxpeax_x86::protected_mode::InstDecoder::default(); let x86_16_decoder = yaxpeax_x86::real_mode::InstDecoder::default(); - drop(x86_64_decoder.decode_slice(data)); - drop(x86_32_decoder.decode_slice(data)); - drop(x86_16_decoder.decode_slice(data)); + x86_64_decoder.decode_slice(data).expect("is ok"); + x86_32_decoder.decode_slice(data).expect("is ok"); + x86_16_decoder.decode_slice(data).expect("is ok"); }); diff --git a/fuzz/fuzz_targets/display_does_not_panic.rs b/fuzz/fuzz_targets/display_does_not_panic.rs index 97a14b88..39f5753e 100644 --- a/fuzz/fuzz_targets/display_does_not_panic.rs +++ b/fuzz/fuzz_targets/display_does_not_panic.rs @@ -8,14 +8,26 @@ fuzz_target!(|data: &[u8]| { let x86_16_decoder = yaxpeax_x86::real_mode::InstDecoder::default(); if let Ok(inst) = x86_64_decoder.decode_slice(data) { - inst.write_to(&mut String::new()).expect("format does not panic"); + let mut out = String::new(); + inst.write_to(&mut out).expect("format does not panic"); + let mut text_buf = yaxpeax_x86::long_mode::InstructionTextBuffer::new(); + text_buf.format_inst(&inst.display_with(yaxpeax_x86::long_mode::DisplayStyle::Intel)).expect("can format"); + assert_eq!(text_buf.text_str(), out); }; if let Ok(inst) = x86_32_decoder.decode_slice(data) { - inst.write_to(&mut String::new()).expect("format does not panic"); + let mut out = String::new(); + inst.write_to(&mut out).expect("format does not panic"); + let mut text_buf = yaxpeax_x86::protected_mode::InstructionTextBuffer::new(); + text_buf.format_inst(&inst.display_with(yaxpeax_x86::protected_mode::DisplayStyle::Intel)).expect("can format"); + assert_eq!(text_buf.text_str(), out); }; if let Ok(inst) = x86_16_decoder.decode_slice(data) { - inst.write_to(&mut String::new()).expect("format does not panic"); + let mut out = String::new(); + inst.write_to(&mut out).expect("format does not panic"); + let mut text_buf = yaxpeax_x86::real_mode::InstructionTextBuffer::new(); + text_buf.format_inst(&inst.display_with(yaxpeax_x86::real_mode::DisplayStyle::Intel)).expect("can format"); + assert_eq!(text_buf.text_str(), out); }; }); diff --git a/fuzz/fuzz_targets/displaysink_used_correctly.rs b/fuzz/fuzz_targets/displaysink_used_correctly.rs new file mode 100644 index 00000000..bd8d1dbb --- /dev/null +++ b/fuzz/fuzz_targets/displaysink_used_correctly.rs @@ -0,0 +1,24 @@ +#![no_main] +#[macro_use] extern crate libfuzzer_sys; +extern crate yaxpeax_x86; +extern crate yaxpeax_arch; + +fuzz_target!(|data: &[u8]| { + let x86_64_decoder = yaxpeax_x86::long_mode::InstDecoder::default(); + let x86_32_decoder = yaxpeax_x86::protected_mode::InstDecoder::default(); + let x86_16_decoder = yaxpeax_x86::real_mode::InstDecoder::default(); + + use yaxpeax_arch::testkit::DisplaySinkValidator; + + if let Ok(inst) = x86_64_decoder.decode_slice(data) { + inst.display_into(&mut DisplaySinkValidator::new()).expect("instruction can be displayed"); + }; + + if let Ok(inst) = x86_32_decoder.decode_slice(data) { + inst.display_into(&mut DisplaySinkValidator::new()).expect("instruction can be displayed"); + }; + + if let Ok(inst) = x86_16_decoder.decode_slice(data) { + inst.display_into(&mut DisplaySinkValidator::new()).expect("instruction can be displayed"); + }; +}); diff --git a/fuzz/fuzz_targets/instruction_text_buffer_size_ok.rs b/fuzz/fuzz_targets/instruction_text_buffer_size_ok.rs new file mode 100644 index 00000000..2c88424e --- /dev/null +++ b/fuzz/fuzz_targets/instruction_text_buffer_size_ok.rs @@ -0,0 +1,51 @@ +#![no_main] +#[macro_use] extern crate libfuzzer_sys; +extern crate yaxpeax_x86; +extern crate yaxpeax_arch; + +use std::fmt::Write; + +fuzz_target!(|data: &[u8]| { + let x86_64_decoder = yaxpeax_x86::long_mode::InstDecoder::default(); + let x86_32_decoder = yaxpeax_x86::protected_mode::InstDecoder::default(); + let x86_16_decoder = yaxpeax_x86::real_mode::InstDecoder::default(); + + if let Ok(inst) = x86_64_decoder.decode_slice(data) { + use yaxpeax_x86::long_mode::DisplayStyle; + + let mut s = String::new(); + write!(s, "{}", inst.display_with(DisplayStyle::Intel)).expect("can write"); + // MAX_INSTRUCTION_LEN is not a public crate item yet... + assert!(s.len() < 512); + s.clear(); + write!(s, "{}", inst.display_with(DisplayStyle::C)).expect("can write"); + // MAX_INSTRUCTION_LEN is not a public crate item yet... + assert!(s.len() < 512); + }; + + if let Ok(inst) = x86_32_decoder.decode_slice(data) { + use yaxpeax_x86::protected_mode::DisplayStyle; + + let mut s = String::new(); + write!(s, "{}", inst.display_with(DisplayStyle::Intel)).expect("can write"); + // MAX_INSTRUCTION_LEN is not a public crate item yet... + assert!(s.len() < 512); + s.clear(); + write!(s, "{}", inst.display_with(DisplayStyle::C)).expect("can write"); + // MAX_INSTRUCTION_LEN is not a public crate item yet... + assert!(s.len() < 512); + }; + + if let Ok(inst) = x86_16_decoder.decode_slice(data) { + use yaxpeax_x86::real_mode::DisplayStyle; + + let mut s = String::new(); + write!(s, "{}", inst.display_with(DisplayStyle::Intel)).expect("can write"); + // MAX_INSTRUCTION_LEN is not a public crate item yet... + assert!(s.len() < 512); + s.clear(); + write!(s, "{}", inst.display_with(DisplayStyle::C)).expect("can write"); + // MAX_INSTRUCTION_LEN is not a public crate item yet... + assert!(s.len() < 512); + }; +}); diff --git a/fuzz/fuzz_targets/small_reg_is_always_old_bank_if_possible.rs b/fuzz/fuzz_targets/small_reg_is_always_old_bank_if_possible.rs index a1432052..b00ecb4b 100644 --- a/fuzz/fuzz_targets/small_reg_is_always_old_bank_if_possible.rs +++ b/fuzz/fuzz_targets/small_reg_is_always_old_bank_if_possible.rs @@ -12,8 +12,8 @@ extern crate yaxpeax_x86; // cases. leaving them in for fuzz targets to match other cases, and In Case Of Future Change. fuzz_target!(|data: &[u8]| { let x86_64_decoder = yaxpeax_x86::long_mode::InstDecoder::default(); - let x86_32_decoder = yaxpeax_x86::protected_mode::InstDecoder::default(); - let x86_16_decoder = yaxpeax_x86::real_mode::InstDecoder::default(); + // let x86_32_decoder = yaxpeax_x86::protected_mode::InstDecoder::default(); + // let x86_16_decoder = yaxpeax_x86::real_mode::InstDecoder::default(); if let Ok(inst) = x86_64_decoder.decode_slice(data) { for i in 0..inst.operand_count() { diff --git a/goodfile b/goodfile index 37db819a..cf93b869 100644 --- a/goodfile +++ b/goodfile @@ -28,7 +28,7 @@ Build.artifact(sopath) -- now run some perf numbers... Step.start("perf") -Build.run({"git", "clone", "https://github.com/athre0z/disas-bench.git", "disas-bench"}) +Build.run({"git", "clone", "https://github.com/iximeow/disas-bench.git", "disas-bench"}) Build.run({"git", "submodule", "update", "--recursive", "--init"}, {cwd="disas-bench"}) Build.run({"git", "remote", "add", "dev", "../../.."}, {cwd="disas-bench/libs/yaxpeax"}) Build.run({"git", "fetch", "-a", "dev"}, {cwd="disas-bench/libs/yaxpeax"}) diff --git a/src/lib.rs b/src/lib.rs index 188a37ac..93274f91 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,10 +35,10 @@ //! #[cfg(features="fmt")] //! assert_eq!("xor eax, dword [rcx]", inst.to_string()); //! -//! assert_eq!(Operand::Register(RegSpec::eax()), inst.operand(0)); +//! assert_eq!(Operand::Register { reg: RegSpec::eax() }, inst.operand(0)); //! #[cfg(features="fmt")] //! assert_eq!("eax", inst.operand(0).to_string()); -//! assert_eq!(Operand::RegDeref(RegSpec::rcx()), inst.operand(1)); +//! assert_eq!(Operand::MemDeref { base: RegSpec::rcx() }, inst.operand(1)); //! //! // an operand in isolation does not know the size of memory it references, if any //! #[cfg(features="fmt")] @@ -138,9 +138,44 @@ pub use protected_mode::Arch as x86_32; pub mod real_mode; pub use real_mode::Arch as x86_16; -mod safer_unchecked; +// this exists to size `InstructionTextBuffer`'s buffer. it ideally would come from an `Arch` +// impl, or something related to `Arch`, but i'm not yet sure how to wire that up into +// yaxpeax-arch. so instead calculate an appropriate max size for all of 16-bit/32-bit/64-bit +// instruction printing that `InstructionTextBuffer` can be used for. +// +// `InstructionTextBuffer` prints an `InstructionDisplayer`, which means either intel syntax or +// pseudo-C. in the future, at&t probably, as well. +// +// the pseudo-C syntax's max length would be something like: +// ``` +// xacquire xrelease lock { repnz qword if /* signed */ greater_or_equal(rflags) then jmp gs:[xmm31 + +// xmm31 * 8 + 0x12345678]{k7}{z}{rne-sae} } +// ``` +// (which is nonsensical) or for an unknown opcode, +// ``` +// xacquire xrelease lock { op0 = op(op0, op1, op2, op3) } +// ``` +// where `opN` is an operand. the longest operand, same as above, would be something like +// ``` +// gs:[xmm31 + xmm31 * 8 + 0x12345678]{k7}{z}{rne-sae} +// ``` +// for a length like 262 bytes of operand, 55 bytes of prefixes and syntax, and another up-to-20 +// bytes of opcode. +// +// the longest contextualize_c might write is around 337 bytes. round up to 512 because it's.. not +// much extra. +// +// the same reasoning for intel syntax yields a smaller instruction: +// ``` +// xacquire xrelease lock op op1, op2, op3, op4 +// ``` +// where the longest operands are the same as above. this comes out to closer to 307 bytes. 512 +// bytes is still the longest of the two options. +#[allow(dead_code)] // can be an unused constant in some library configurations +const MAX_INSTRUCTION_LEN: usize = 512; -const MEM_SIZE_STRINGS: [&'static str; 64] = [ +const MEM_SIZE_STRINGS: [&'static str; 65] = [ + "BUG", "byte", "word", "BUG", "dword", "ptr", "far", "BUG", "qword", "BUG", "mword", "BUG", "BUG", "BUG", "BUG", "BUG", "xmmword", "BUG", "BUG", "BUG", "BUG", "BUG", "BUG", "BUG", "BUG", @@ -194,7 +229,7 @@ impl MemoryAccessSize { /// "variable" accesses access a number of bytes dependent on the physical processor and its /// operating mode. this is particularly relevant for `xsave`/`xrstor`-style instructions. pub fn size_name(&self) -> &'static str { - MEM_SIZE_STRINGS[self.size as usize - 1] + MEM_SIZE_STRINGS[self.size as usize] } } diff --git a/src/long_mode/display.rs b/src/long_mode/display.rs index 9c6795ef..b9023ed1 100644 --- a/src/long_mode/display.rs +++ b/src/long_mode/display.rs @@ -1,11 +1,142 @@ use core::fmt; +// allowing these deprecated items for the time being, not yet breaking yaxpeax-x86 apis +#[allow(deprecated)] use yaxpeax_arch::{Colorize, ShowContextual, NoColors, YaxColors}; -use yaxpeax_arch::display::*; -use crate::safer_unchecked::GetSaferUnchecked as _; use crate::MEM_SIZE_STRINGS; -use crate::long_mode::{RegSpec, Opcode, Operand, MergeMode, InstDecoder, Instruction, Segment, PrefixRex, OperandSpec}; +use crate::long_mode::{RegSpec, Opcode, Operand, MergeMode, InstDecoder, Instruction, Segment, PrefixRex}; + +use yaxpeax_arch::display::DisplaySink; +use yaxpeax_arch::safer_unchecked::GetSaferUnchecked as _; +use yaxpeax_arch::safer_unchecked::unreachable_kinda_unchecked as unreachable_unchecked; + +trait DisplaySinkExt { + // `write_opcode` depends on all mnemonics being less than 32 bytes long. check that here, at + // compile time. referenced later to force evaluation of this const. + const MNEMONIC_LT_32: () = { + let mut i = 0; + while i < MNEMONICS.len() { + let name = &MNEMONICS[i]; + if name.len() >= 32 { + panic!("mnemonic too long"); + } + i += 1; + } + }; + + // `write_reg` depends on all register names being less than 8 bytes long. check that here, at + // compile time. referenced later to force evaluation of this const. + const REG_LABEL_LT_8: () = { + let mut i = 0; + while i < REG_NAMES.len() { + let name = ®_NAMES[i]; + if name.len() >= 8 { + panic!("register name too long"); + } + i += 1; + } + }; + + // `write_mem_size_label` depends on all memory size labels being less than 8 bytes long. check + // that here, at compile time. referenced later to force evaluation of this const. + const MEM_SIZE_LABEL_LT_8: () = { + let mut i = 0; + while i < crate::MEM_SIZE_STRINGS.len() { + let name = &MEM_SIZE_STRINGS[i]; + if name.len() >= 8 { + panic!("memory label name too long"); + } + i += 1; + } + }; + + // `write_sae_mode` depends on all sae mode labels being less than 16 bytes long. check that + // here, at compile time. referenced later to force evaluation of this const. + const SAE_LABEL_LT_16: () = { + let mut i = 0; + while i < super::SAE_MODES.len() { + let mode = &super::SAE_MODES[i]; + if mode.label().len() >= 16 { + panic!("sae mode label too long"); + } + i += 1; + } + }; + + fn write_opcode(&mut self, opcode: super::Opcode) -> Result<(), core::fmt::Error>; + fn write_reg(&mut self, reg: RegSpec) -> Result<(), core::fmt::Error>; + fn write_displacement(&mut self, disp: i32) -> Result<(), core::fmt::Error>; + fn write_scale(&mut self, scale: u8) -> Result<(), core::fmt::Error>; + fn write_mem_size_label(&mut self, mem_size: u8) -> Result<(), core::fmt::Error>; + fn write_sae_mode(&mut self, sae: super::SaeMode) -> Result<(), core::fmt::Error>; +} + +impl DisplaySinkExt for T { + #[inline(always)] + fn write_opcode(&mut self, opcode: super::Opcode) -> Result<(), core::fmt::Error> { + self.span_start_opcode(); + let name = opcode.name(); + + let _ = Self::MNEMONIC_LT_32; + // Safety: all opcode mnemonics are 31 bytes or fewer. + unsafe { self.write_lt_32(name)?; } + self.span_end_opcode(); + Ok(()) + } + + #[inline(always)] + fn write_reg(&mut self, reg: RegSpec) -> Result<(), core::fmt::Error> { + self.span_start_register(); + let label = regspec_label(®); + + let _ = Self::REG_LABEL_LT_8; + // Safety: all register labels are 7 bytes or fewer. + unsafe { self.write_lt_8(label)?; } + self.span_end_register(); + Ok(()) + } + + #[inline(always)] + fn write_displacement(&mut self, disp: i32) -> Result<(), core::fmt::Error> { + let mut v = disp as u32; + if disp < 0 { + self.write_fixed_size("- ")?; + v = disp.unsigned_abs(); + } else { + self.write_fixed_size("+ ")?; + } + self.span_start_number(); + self.write_prefixed_u32(v)?; + self.span_end_number(); + Ok(()) + } + + #[inline(always)] + fn write_scale(&mut self, scale: u8) -> Result<(), core::fmt::Error> { + self.span_start_number(); + self.write_char((0x30 + scale) as char)?; // translate scale=1 to '1', scale=2 to '2', etc + self.span_end_number(); + Ok(()) + } + + #[inline(always)] + fn write_mem_size_label(&mut self, mem_size: u8) -> Result<(), core::fmt::Error> { + let label = mem_size_label(mem_size); + let _ = Self::MEM_SIZE_LABEL_LT_8; + // Safety: all memory size labels are 7 bytes or fewer + unsafe { self.write_lt_8(label) } + } + + #[inline(always)] + fn write_sae_mode(&mut self, sae_mode: super::SaeMode) -> Result<(), core::fmt::Error> { + let label = sae_mode.label(); + + let _ = Self::SAE_LABEL_LT_16; + // Safety: all sae labels are 15 bytes or fewer. + unsafe { self.write_lt_16(label) } + } +} impl fmt::Display for InstDecoder { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -89,6 +220,19 @@ impl fmt::Display for PrefixRex { } } +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 { @@ -132,6 +276,10 @@ 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) } } +pub(crate) fn mem_size_label(size: u8) -> &'static str { + unsafe { MEM_SIZE_STRINGS.get_kinda_unchecked(size as usize) } +} + impl fmt::Display for RegSpec { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(regspec_label(self)) @@ -140,208 +288,325 @@ impl fmt::Display for RegSpec { impl fmt::Display for Operand { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + // to reuse one implementation, call the deprecated function for now. + #[allow(deprecated)] self.colorize(&NoColors, fmt) } } +// allowing these deprecated items for the time being, not yet breaking yaxpeax-x86 apis +#[allow(deprecated)] impl Colorize 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::ImmediateU64(imm) => { - write!(f, "{}", colors.number(u64_hex(imm))) - } - &Operand::ImmediateI64(imm) => { - write!(f, "{}", - colors.number(signed_i64_hex(imm))) - }, - &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::DisplacementU32(imm) => { - write!(f, "[{}]", colors.address(u32_hex(imm))) - } - &Operand::DisplacementU64(imm) => { - write!(f, "[{}]", colors.address(u64_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(()) }, + fn colorize(&self, _colors: &Y, f: &mut T) -> fmt::Result { + let mut f = yaxpeax_arch::display::FmtSink::new(f); + let mut visitor = DisplayingOperandVisitor { + f: &mut f + }; + self.visit(&mut visitor) + } +} + +struct DisplayingOperandVisitor<'a, T> { + f: &'a mut T, +} + +impl super::OperandVisitor for DisplayingOperandVisitor<'_, T> { + type Ok = (); + type Error = core::fmt::Error; + + #[cfg_attr(feature="profiling", inline(never))] + fn visit_u8(&mut self, imm: u8) -> Result { + self.f.span_start_immediate(); + self.f.write_fixed_size("0x")?; + self.f.write_u8(imm)?; + self.f.span_end_immediate(); + Ok(()) + } + #[cfg_attr(feature="profiling", inline(never))] + fn visit_i8(&mut self, imm: i8) -> Result { + self.f.span_start_immediate(); + let mut v = imm as u8; + if imm < 0 { + self.f.write_char('-')?; + v = imm.unsigned_abs(); } + self.f.write_fixed_size("0x")?; + self.f.write_u8(v)?; + self.f.span_end_immediate(); + Ok(()) + } + #[cfg_attr(feature="profiling", inline(never))] + fn visit_u16(&mut self, imm: u16) -> Result { + self.f.span_start_immediate(); + self.f.write_fixed_size("0x")?; + self.f.write_u16(imm)?; + self.f.span_end_immediate(); + Ok(()) + } + #[cfg_attr(feature="profiling", inline(never))] + fn visit_i16(&mut self, imm: i16) -> Result { + self.f.span_start_immediate(); + let mut v = imm as u16; + if imm < 0 { + self.f.write_char('-')?; + v = imm.unsigned_abs(); + } + self.f.write_fixed_size("0x")?; + self.f.write_u16(v)?; + self.f.span_end_immediate(); + Ok(()) + } + #[cfg_attr(feature="profiling", inline(never))] + fn visit_u32(&mut self, imm: u32) -> Result { + self.f.span_start_immediate(); + self.f.write_fixed_size("0x")?; + self.f.write_u32(imm)?; + self.f.span_end_immediate(); + Ok(()) + } + fn visit_i32(&mut self, imm: i32) -> Result { + self.f.span_start_immediate(); + let mut v = imm as u32; + if imm < 0 { + self.f.write_char('-')?; + v = imm.unsigned_abs(); + } + self.f.write_fixed_size("0x")?; + self.f.write_u32(v)?; + self.f.span_end_immediate(); + Ok(()) + } + #[cfg_attr(feature="profiling", inline(never))] + fn visit_u64(&mut self, imm: u64) -> Result { + self.f.span_start_immediate(); + self.f.write_fixed_size("0x")?; + self.f.write_u64(imm)?; + self.f.span_end_immediate(); + Ok(()) + } + #[cfg_attr(feature="profiling", inline(never))] + fn visit_i64(&mut self, imm: i64) -> Result { + self.f.span_start_immediate(); + let mut v = imm as u64; + if imm < 0 { + self.f.write_char('-')?; + v = imm.unsigned_abs(); + } + self.f.write_fixed_size("0x")?; + self.f.write_u64(v)?; + self.f.span_end_immediate(); + Ok(()) + } + #[cfg_attr(feature="profiling", inline(never))] + fn visit_reg(&mut self, reg: RegSpec) -> Result { + self.f.write_reg(reg)?; + Ok(()) + } + fn visit_reg_mask_merge(&mut self, spec: RegSpec, mask: RegSpec, merge_mode: MergeMode) -> Result { + self.f.write_reg(spec)?; + if mask.num != 0 { + self.f.write_fixed_size("{")?; + self.f.write_reg(mask)?; + self.f.write_fixed_size("}")?; + } + if let MergeMode::Zero = merge_mode { + self.f.write_fixed_size("{z}")?; + } + Ok(()) + } + fn visit_reg_mask_merge_sae(&mut self, spec: RegSpec, mask: RegSpec, merge_mode: MergeMode, sae_mode: crate::long_mode::SaeMode) -> Result { + self.f.write_reg(spec)?; + if mask.num != 0 { + self.f.write_fixed_size("{")?; + self.f.write_reg(mask)?; + self.f.write_fixed_size("}")?; + } + if let MergeMode::Zero = merge_mode { + self.f.write_fixed_size("{z}")?; + } + self.f.write_sae_mode(sae_mode)?; + Ok(()) + } + fn visit_reg_mask_merge_sae_noround(&mut self, spec: RegSpec, mask: RegSpec, merge_mode: MergeMode) -> Result { + self.f.write_reg(spec)?; + if mask.num != 0 { + self.f.write_fixed_size("{")?; + self.f.write_reg(mask)?; + self.f.write_fixed_size("}")?; + } + if let MergeMode::Zero = merge_mode { + self.f.write_fixed_size("{z}")?; + } + self.f.write_fixed_size("{sae}")?; + Ok(()) + } + fn visit_abs_u32(&mut self, imm: u32) -> Result { + self.f.write_fixed_size("[")?; + self.f.span_start_address(); + self.f.write_prefixed_u32(imm)?; + self.f.span_end_address(); + self.f.write_fixed_size("]")?; + Ok(()) + } + fn visit_abs_u64(&mut self, imm: u64) -> Result { + self.f.write_fixed_size("[")?; + self.f.span_start_address(); + self.f.write_prefixed_u64(imm)?; + self.f.span_end_address(); + self.f.write_fixed_size("]")?; + Ok(()) + } + #[cfg_attr(not(feature="profiling"), inline(always))] + #[cfg_attr(feature="profiling", inline(never))] + fn visit_disp(&mut self, base: RegSpec, disp: i32) -> Result { + self.f.write_char('[')?; + self.f.write_reg(base)?; + self.f.write_fixed_size(" ")?; + self.f.write_displacement(disp)?; + self.f.write_fixed_size("]") + } + fn visit_deref(&mut self, base: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size("]") + } + fn visit_index_scale(&mut self, index: RegSpec, scale: u8) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_fixed_size("]")?; + + Ok(()) + } + fn visit_index_scale_disp(&mut self, index: RegSpec, scale: u8, disp: i32) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_fixed_size(" ")?; + self.f.write_displacement(disp)?; + self.f.write_char(']') + } + fn visit_base_index_scale(&mut self, base: RegSpec, index: RegSpec, scale: u8) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size(" + ")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_fixed_size("]") + } + fn visit_base_index_scale_disp(&mut self, base: RegSpec, index: RegSpec, scale: u8, disp: i32) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size(" + ")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_fixed_size(" ")?; + self.f.write_displacement(disp)?; + self.f.write_fixed_size("]") + } + fn visit_disp_masked(&mut self, base: RegSpec, disp: i32, mask_reg: RegSpec) -> Result { + self.f.write_char('[')?; + self.f.write_reg(base)?; + self.f.write_char(' ')?; + self.f.write_displacement(disp)?; + self.f.write_char(']')?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_deref_masked(&mut self, base: RegSpec, mask_reg: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size("]")?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_index_scale_masked(&mut self, index: RegSpec, scale: u8, mask_reg: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_fixed_size("]")?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_index_scale_disp_masked(&mut self, index: RegSpec, scale: u8, disp: i32, mask_reg: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_fixed_size(" ")?; + self.f.write_displacement(disp)?; + self.f.write_char(']')?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_base_index_masked(&mut self, base: RegSpec, index: RegSpec, mask_reg: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size(" + ")?; + self.f.write_reg(index)?; + self.f.write_fixed_size("]")?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_base_index_disp_masked(&mut self, base: RegSpec, index: RegSpec, disp: i32, mask_reg: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size(" + ")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" ")?; + self.f.write_displacement(disp)?; + self.f.write_char(']')?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_base_index_scale_masked(&mut self, base: RegSpec, index: RegSpec, scale: u8, mask_reg: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size(" + ")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_fixed_size("]")?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_base_index_scale_disp_masked(&mut self, base: RegSpec, index: RegSpec, scale: u8, disp: i32, mask_reg: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size(" + ")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_char(' ')?; + self.f.write_displacement(disp)?; + self.f.write_char(']')?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + + fn visit_other(&mut self) -> Result { + Ok(()) } } @@ -380,6 +645,13 @@ const MNEMONICS: &[&'static str] = &[ "not", "xadd", "xchg", + "cmps", + "scas", + "movs", + "lods", + "stos", + "ins", + "outs", "invalid", "bt", "bsf", @@ -461,17 +733,10 @@ const MNEMONICS: &[&'static str] = &[ "cwd", "cdq", "cqo", - "lods", - "stos", "lahf", "sahf", - "cmps", - "scas", - "movs", "test", - "ins", "in", - "outs", "out", "imul", "jo", @@ -1845,8 +2110,18 @@ impl Opcode { } } +// allowing these deprecated items for the time being, not yet breaking yaxpeax-x86 apis +#[allow(deprecated)] impl Colorize for Opcode { - fn colorize(&self, colors: &Y, out: &mut T) -> fmt::Result { + fn colorize(&self, _colors: &Y, out: &mut T) -> fmt::Result { + out.write_str(self.name()) + // a way to preserve opcode colorization might be to see entering an opcode span, + // collecting text into a buffer, waiting for the span to exit, looking that up in a map + // for opcode types, and then picking a color. this really should be something like "opcode + // information" that can be looked up (including things like operand read/write behavior).. + // + // leaving this commented out as a reminder of what opcode to behavior mapping was like + /* match self { Opcode::VGF2P8AFFINEQB | Opcode::VGF2P8AFFINEINVQB | @@ -3272,17 +3547,22 @@ impl Colorize for Opcode { Opcode::UD2 | Opcode::Invalid => { write!(out, "{}", colors.invalid_op(self)) } } + */ } } impl fmt::Display for Instruction { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + // to reuse one implementation, call the deprecated function for now. + #[allow(deprecated)] self.display_with(DisplayStyle::Intel).colorize(&NoColors, fmt) } } impl<'instr> fmt::Display for InstructionDisplayer<'instr> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + // to reuse one implementation, call the deprecated function for now. + #[allow(deprecated)] self.colorize(&NoColors, fmt) } } @@ -3325,7 +3605,12 @@ pub struct InstructionDisplayer<'instr> { * * so write to some Write thing i guess. bite me. i really just want to * stop thinking about how to support printing instructions... + * + * UPDATE: really wish i thought of DisplaySink back then, really wish this was bounded as T: + * DisplaySink. */ +// allowing these deprecated items for the time being, not yet breaking yaxpeax-x86 apis +#[allow(deprecated)] impl <'instr, T: fmt::Write, Y: YaxColors> Colorize 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, @@ -3338,160 +3623,157 @@ impl <'instr, T: fmt::Write, Y: YaxColors> Colorize for InstructionDisplay struct NoContext; impl Instruction { + /// format this instruction into `out` as a plain text string. + #[cfg_attr(feature="profiling", inline(never))] pub fn write_to(&self, out: &mut T) -> fmt::Result { - self.display_with(DisplayStyle::Intel).contextualize(&NoColors, 0, Some(&NoContext), out) + let mut out = yaxpeax_arch::display::FmtSink::new(out); + contextualize_intel(self, &mut out) + } + + /// format this instruction into `out`, which may perform additional styling based on its + /// `DisplaySink` implementation. + #[cfg_attr(feature="profiling", inline(never))] + pub fn display_into(&self, out: &mut T) -> fmt::Result { + contextualize_intel(self, out) } } -fn contextualize_intel(instr: &Instruction, colors: &Y, _address: u64, _context: Option<&NoContext>, out: &mut T) -> fmt::Result { +#[cfg_attr(feature="profiling", inline(never))] +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 [Opcode::MOVS, Opcode::CMPS, Opcode::LODS, Opcode::STOS, Opcode::INS, Opcode::OUTS].contains(&instr.opcode) { + 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::JRCXZ, - 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 - 1])?; - 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 - 1])?; - 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(", ")?; + // hint that accessing `inster.operands[i]` can't panic: this is useful for + // `instr.operands` and the segment selector check after. + if i >= 4 { + // Safety: Instruction::operands is a four-element array; operand_count is always + // low enough that 0..operand_count is a valid index. + unsafe { unreachable_unchecked(); } + } + + if instr.operands[i as usize].is_memory() { + out.write_mem_size_label(instr.mem_size)?; + 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('}')?; } } } @@ -3499,7 +3781,7 @@ fn contextualize_intel(instr: &Instruction, colors: Ok(()) } -fn contextualize_c(instr: &Instruction, colors: &Y, _address: u64, _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; @@ -3523,7 +3805,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", @@ -3545,21 +3827,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))) + Operand::ImmediateI8 { imm: rel } => { + 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))) + Operand::ImmediateI32 { imm: 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) @@ -3722,87 +4012,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::JRCXZ => { out.write_str("if rcx == 0 then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::LOOP => { out.write_str("rcx--; if rcx != 0 then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::LOOPZ => { out.write_str("rcx--; if rcx != 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("rcx--; if rcx != 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")?; @@ -3833,37 +4123,49 @@ fn contextualize_c(instr: &Instruction, colors: &Y, Ok(()) } +// allowing these deprecated items for the time being, not yet breaking yaxpeax-x86 apis +#[allow(deprecated)] impl <'instr, T: fmt::Write, Y: YaxColors> ShowContextual for InstructionDisplayer<'instr> { - fn contextualize(&self, colors: &Y, address: u64, context: Option<&NoContext>, out: &mut T) -> fmt::Result { + fn contextualize(&self, _colors: &Y, _address: u64, _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) } } } } +// allowing these deprecated items for the time being, not yet breaking yaxpeax-x86 apis +#[allow(deprecated)] #[cfg(feature="std")] impl ShowContextual], T, Y> for Instruction { fn contextualize(&self, colors: &Y, _address: u64, context: Option<&[Option]>, out: &mut T) -> fmt::Result { + let mut out = yaxpeax_arch::display::FmtSink::new(out); + let out = &mut out; + use core::fmt::Write; + 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 ")?; + if self.prefixes.rep_any() { + if self.opcode.can_rep() { + // only a few of you actually use the prefix... + if self.prefixes.rep() { + write!(out, "rep ")?; + } else if self.prefixes.repnz() { + write!(out, "repnz ")?; + } } } @@ -3873,7 +4175,7 @@ impl ShowContextual { write!(out, " {}", s)?; }, None => { match self.operands[0] { - OperandSpec::Nothing => { + super::OperandSpec::Nothing => { return Ok(()); }, _ => { @@ -3883,8 +4185,11 @@ impl ShowContextual ShowContextual { write!(out, ", {}", s)? } None => { match &self.operands[i] { - &OperandSpec::Nothing => { + &super::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)? + let mut displayer = DisplayingOperandVisitor { + f: out, + }; + self.visit_operand(i as u8, &mut displayer)?; } } } @@ -3911,3 +4215,248 @@ 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, _base: RegSpec) -> Result { + Ok(false) + } + fn visit_disp(&mut self, _base: 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('$')?; + let mut v = rel as u8; + if rel < 0 { + self.out.write_char('-')?; + v = rel.unsigned_abs(); + } else { + self.out.write_char('+')?; + } + 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('$')?; + let mut v = rel as u32; + if rel < 0 { + self.out.write_char('-')?; + v = rel.unsigned_abs(); + } else { + self.out.write_char('+')?; + } + 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_i64(&mut self, _imm: i64) -> Result { + Ok(false) + } + fn visit_u64(&mut self, _imm: u64) -> Result { + Ok(false) + } + fn visit_abs_u32(&mut self, _imm: u32) -> Result { + Ok(false) + } + fn visit_abs_u64(&mut self, _imm: u64) -> Result { + Ok(false) + } + fn visit_index_scale(&mut self, _index: RegSpec, _scale: u8) -> Result { + Ok(false) + } + fn visit_base_index_scale(&mut self, _base: RegSpec, _index: RegSpec, _scale: u8) -> Result { + Ok(false) + } + fn visit_index_scale_disp(&mut self, _index: RegSpec, _scale: u8, _disp: i32) -> Result { + Ok(false) + } + fn visit_base_index_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_disp_masked(&mut self, _base: RegSpec, _disp: i32, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_deref_masked(&mut self, _base: RegSpec, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_index_scale_masked(&mut self, _index: RegSpec, _scale: u8, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_index_scale_disp_masked(&mut self, _index: RegSpec, _scale: u8, _disp: i32, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_base_index_masked(&mut self, _base: RegSpec, _index: RegSpec, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_base_index_disp_masked(&mut self, _base: RegSpec, _index: RegSpec, _disp: i32, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_base_index_scale_masked(&mut self, _base: RegSpec, _index: RegSpec, _scale: u8, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_base_index_scale_disp_masked(&mut self, _base: RegSpec, _index: RegSpec, _scale: u8, _disp: i32, _mask_reg: RegSpec) -> Result { + Ok(false) + } +} + +#[cfg(feature="alloc")] +mod buffer_sink { + use core::fmt; + use super::super::{DisplayStyle, InstructionDisplayer}; + use super::{contextualize_c, contextualize_intel}; + + /// helper to format `amd64` instructions with highest throughput and least configuration. this is + /// functionally a buffer for one x86 instruction's text. + /// + /// ### when to use this over `fmt::Display`? + /// + /// `fmt::Display` is a fair choice in most cases. in some cases, `InstructionTextBuffer` may + /// support formatting options that may be difficult to configure for a `Display` impl. + /// additionally, `InstructionTextBuffer` may be able to specialize more effectively where + /// `fmt::Display`, writing to a generic `fmt::Write`, may not. + /// + /// if your use case for `yaxpeax-x86` involves being bounded on the speed of disassembling and + /// formatting instructions, [`InstructionTextBuffer::format_inst`] has been measured as up to 11% + /// faster than an equivalent `write!(buf, "{}", inst)`. + /// + /// `InstructionTextBuffer` involves internal allocations; if your use case for `yaxpeax-x86` + /// requires allocations never occurring, it is not an appropriate tool. + /// + /// ### example + /// + /// ``` + /// use yaxpeax_x86::long_mode::InstDecoder; + /// use yaxpeax_x86::long_mode::InstructionTextBuffer; + /// use yaxpeax_x86::long_mode::DisplayStyle; + /// + /// let bytes = &[0x33, 0xc0]; + /// let inst = InstDecoder::default().decode_slice(bytes).expect("can decode"); + /// let mut text_buf = InstructionTextBuffer::new(); + /// assert_eq!( + /// text_buf.format_inst(&inst.display_with(DisplayStyle::Intel)).expect("can format"), + /// "xor eax, eax" + /// ); + /// + /// // or, getting the formatted instruction with `text_str`: + /// assert_eq!( + /// text_buf.text_str(), + /// "xor eax, eax" + /// ); + /// ``` + pub struct InstructionTextBuffer { + content: alloc::string::String, + } + + impl InstructionTextBuffer { + /// create an `InstructionTextBuffer` with default settings. `InstructionTextBuffer`'s default + /// settings format instructions identically to their corresponding `fmt::Display`. + pub fn new() -> Self { + let mut buf = alloc::string::String::new(); + buf.reserve(crate::MAX_INSTRUCTION_LEN); + Self { + content: buf, + } + } + + /// format `inst` into this buffer. returns a borrow of that same internal buffer for convenience. + /// + /// this clears and reuses an internal buffer; if an instruction had been previously formatted + /// through this buffer, it will be overwritten. + pub fn format_inst<'buf, 'instr>(&'buf mut self, display: &InstructionDisplayer<'instr>) -> Result<&'buf str, fmt::Error> { + // Safety: this sink is used to format exactly one instruction and then dropped. it can + // never escape `format_inst`. + let mut handle = unsafe { self.write_handle() }; + + match display.style { + DisplayStyle::Intel => { + contextualize_intel(&display.instr, &mut handle)?; + } + DisplayStyle::C => { + contextualize_c(&display.instr, &mut handle)?; + } + } + + Ok(self.text_str()) + } + + /// return a borrow of the internal buffer. if an instruction has been formatted, the + /// returned `&str` contains that instruction's buffered text. + pub fn text_str(&self) -> &str { + self.content.as_str() + } + + /// do the necessary bookkeeping and provide an `InstructionTextSink` to write an instruction + /// into. + /// + /// SAFETY: callers must print at most one instruction into this handle. + unsafe fn write_handle(&mut self) -> yaxpeax_arch::display::InstructionTextSink { + self.content.clear(); + // Safety: `content` was just cleared, so writing begins at the start of the buffer. + // `content`is large enough to hold a fully-formatted instruction (see + // `InstructionTextBuffer::new`). + yaxpeax_arch::display::InstructionTextSink::new(&mut self.content) + } + } +} +#[cfg(feature="alloc")] +pub use buffer_sink::InstructionTextBuffer; diff --git a/src/long_mode/mod.rs b/src/long_mode/mod.rs index 9bc9f0b0..0ce6a586 100644 --- a/src/long_mode/mod.rs +++ b/src/long_mode/mod.rs @@ -8,13 +8,15 @@ pub use crate::MemoryAccessSize; #[cfg(feature = "fmt")] pub use self::display::{DisplayStyle, InstructionDisplayer}; +#[cfg(all(feature = "fmt", feature = "alloc"))] +pub use self::display::InstructionTextBuffer; use core::cmp::PartialEq; -use crate::safer_unchecked::unreachable_kinda_unchecked as unreachable_unchecked; use yaxpeax_arch::{AddressDiff, Decoder, Reader, LengthedInstruction}; use yaxpeax_arch::annotation::{AnnotatingDecoder, DescriptionSink, NullSink}; use yaxpeax_arch::{DecodeError as ArchDecodeError}; +use yaxpeax_arch::safer_unchecked::unreachable_kinda_unchecked as unreachable_unchecked; use core::fmt; impl fmt::Display for DecodeError { @@ -366,91 +368,81 @@ enum SizeCode { #[non_exhaustive] pub enum Operand { /// a sign-extended byte - ImmediateI8(i8), + ImmediateI8 { imm: i8 }, /// a zero-extended byte - ImmediateU8(u8), + ImmediateU8 { imm: u8 }, /// a sign-extended word - ImmediateI16(i16), + ImmediateI16 { imm: i16 }, /// a zero-extended word - ImmediateU16(u16), + ImmediateU16 { imm: u16 }, /// a sign-extended dword - ImmediateI32(i32), + ImmediateI32 { imm: i32 }, /// a zero-extended dword - ImmediateU32(u32), + ImmediateU32 { imm: u32 }, /// a sign-extended qword - ImmediateI64(i64), + ImmediateI64 { imm: i64 }, /// a zero-extended qword - ImmediateU64(u64), + ImmediateU64 { imm: u64 }, /// a bare register operand, such as `rcx`. - Register(RegSpec), + Register { reg: RegSpec }, /// an `avx512` register operand with optional mask register and merge mode, such as /// `zmm3{k4}{z}`. /// /// if the mask register is `k0`, there is no masking applied, and the default x86 operation is /// `MergeMode::Merge`. - RegisterMaskMerge(RegSpec, RegSpec, MergeMode), + RegisterMaskMerge { reg: RegSpec, mask: RegSpec, merge: MergeMode }, /// an `avx512` register operand with optional mask register, merge mode, and suppressed /// exceptions, such as `zmm3{k4}{z}{rd-sae}`. /// /// if the mask register is `k0`, there is no masking applied, and the default x86 operation is /// `MergeMode::Merge`. - RegisterMaskMergeSae(RegSpec, RegSpec, MergeMode, SaeMode), + RegisterMaskMergeSae { reg: RegSpec, mask: RegSpec, merge: MergeMode, sae: SaeMode }, /// an `avx512` register operand with optional mask register, merge mode, and suppressed /// exceptions, with no overridden rounding mode, such as `zmm3{k4}{z}{sae}`. /// /// if the mask register is `k0`, there is no masking applied, and the default x86 operation is /// `MergeMode::Merge`. - RegisterMaskMergeSaeNoround(RegSpec, RegSpec, MergeMode), + RegisterMaskMergeSaeNoround { reg: RegSpec, mask: RegSpec, merge: MergeMode }, /// a memory access to a literal dword address. it's extremely rare that a well-formed x86 /// instruction uses this mode. as an example, `[0x1133]` - DisplacementU32(u32), + AbsoluteU32 { addr: u32 }, /// a memory access to a literal qword address. it's relatively rare that a well-formed x86 /// instruction uses this mode, but plausible. for example, `gs:[0x14]`. segment overrides, /// however, are maintained on the instruction itself. - DisplacementU64(u64), + AbsoluteU64 { addr: u64 }, /// a simple dereference of the address held in some register. for example: `[rsi]`. - RegDeref(RegSpec), + MemDeref { base: RegSpec }, /// a dereference of the address held in some register with offset. for example: `[rsi + 0x14]`. - RegDisp(RegSpec, i32), + Disp { base: RegSpec, disp: i32 }, /// a dereference of the address held in some register scaled by 1, 2, 4, or 8. this is almost always used with the `lea` instruction. for example: `[rdx * 4]`. - RegScale(RegSpec, u8), - /// a dereference of the address from summing two registers. for example: `[rbp + rax]` - RegIndexBase(RegSpec, RegSpec), - /// a dereference of the address from summing two registers with offset. for example: `[rdi + rcx + 0x40]` - RegIndexBaseDisp(RegSpec, RegSpec, i32), + MemIndexScale { index: RegSpec, scale: u8 }, /// a dereference of the address held in some register scaled by 1, 2, 4, or 8 with offset. this is almost always used with the `lea` instruction. for example: `[rax * 4 + 0x30]`. - RegScaleDisp(RegSpec, u8, i32), + MemIndexScaleDisp { index: RegSpec, scale: u8, disp: i32 }, /// a dereference of the address from summing a register and index register scaled by 1, 2, 4, /// or 8. for /// example: `[rsi + rcx * 4]` - RegIndexBaseScale(RegSpec, RegSpec, u8), + MemBaseIndexScale { base: RegSpec, index: RegSpec, scale: u8 }, /// a dereference of the address from summing a register and index register scaled by 1, 2, 4, /// or 8, with offset. for /// example: `[rsi + rcx * 4 + 0x1234]` - RegIndexBaseScaleDisp(RegSpec, RegSpec, u8, i32), + MemBaseIndexScaleDisp { base: RegSpec, index: RegSpec, scale: u8, disp: i32 }, /// an `avx512` dereference of register with optional masking. for example: `[rdx]{k3}` - RegDerefMasked(RegSpec, RegSpec), + MemDerefMasked { base: RegSpec, mask: RegSpec }, /// an `avx512` dereference of register plus offset, with optional masking. for example: `[rsp + 0x40]{k3}` - RegDispMasked(RegSpec, i32, RegSpec), + DispMasked { base: RegSpec, disp: i32, mask: RegSpec }, /// an `avx512` dereference of a register scaled by 1, 2, 4, or 8, with optional masking. this /// seems extraordinarily unlikely to occur in practice. for example: `[rsi * 4]{k2}` - RegScaleMasked(RegSpec, u8, RegSpec), - /// an `avx512` dereference of a register plus index scaled by 1, 2, 4, or 8, with optional masking. - /// for example: `[rsi + rax * 4]{k6}` - RegIndexBaseMasked(RegSpec, RegSpec, RegSpec), - /// an `avx512` dereference of a register plus offset, with optional masking. for example: - /// `[rsi + rax + 0x1313]{k6}` - RegIndexBaseDispMasked(RegSpec, RegSpec, i32, RegSpec), + MemIndexScaleMasked { index: RegSpec, scale: u8, mask: RegSpec }, /// an `avx512` dereference of a register scaled by 1, 2, 4, or 8 plus offset, with optional /// masking. this seems extraordinarily unlikely to occur in practice. for example: `[rsi * /// 4 + 0x1357]{k2}` - RegScaleDispMasked(RegSpec, u8, i32, RegSpec), + MemIndexScaleDispMasked { index: RegSpec, scale: u8, disp: i32, mask: RegSpec }, /// an `avx512` dereference of a register plus index scaled by 1, 2, 4, or 8, with optional /// masking. for example: `[rsi + rax * 4]{k6}` - RegIndexBaseScaleMasked(RegSpec, RegSpec, u8, RegSpec), + MemBaseIndexScaleMasked { base: RegSpec, index: RegSpec, scale: u8, mask: RegSpec }, /// an `avx512` dereference of a register plus index scaled by 1, 2, 4, or 8 and offset, with /// optional masking. for example: `[rsi + rax * 4 + 0x1313]{k6}` - RegIndexBaseScaleDispMasked(RegSpec, RegSpec, u8, i32, RegSpec), + MemBaseIndexScaleDispMasked { base: RegSpec, index: RegSpec, scale: u8, disp: i32, mask: RegSpec }, /// no operand. it is a bug for `yaxpeax-x86` to construct an `Operand` of this kind for public /// use; the instruction's `operand_count` should be reduced so as to make this invisible to /// library clients. @@ -464,11 +456,11 @@ impl OperandSpec { OperandSpec::RegMMM => OperandSpec::RegMMM_maskmerge, OperandSpec::RegVex => OperandSpec::RegVex_maskmerge, OperandSpec::Deref => OperandSpec::Deref_mask, - OperandSpec::RegDisp => OperandSpec::RegDisp_mask, - OperandSpec::RegScale => OperandSpec::RegScale_mask, - OperandSpec::RegScaleDisp => OperandSpec::RegScaleDisp_mask, - OperandSpec::RegIndexBaseScale => OperandSpec::RegIndexBaseScale_mask, - OperandSpec::RegIndexBaseScaleDisp => OperandSpec::RegIndexBaseScaleDisp_mask, + OperandSpec::Disp => OperandSpec::Disp_mask, + OperandSpec::MemIndexScale => OperandSpec::MemIndexScale_mask, + OperandSpec::MemIndexScaleDisp => OperandSpec::MemIndexScaleDisp_mask, + OperandSpec::MemBaseIndexScale => OperandSpec::MemBaseIndexScale_mask, + OperandSpec::MemBaseIndexScaleDisp => OperandSpec::MemBaseIndexScaleDisp_mask, o => o, } } @@ -521,7 +513,7 @@ impl SaeMode { /// assert_eq!(SaeMode::RoundUp.label(), "{ru-sae}"); /// assert_eq!(SaeMode::RoundZero.label(), "{rz-sae}"); /// ``` - pub fn label(&self) -> &'static str { + pub const fn label(&self) -> &'static str { match self { SaeMode::RoundNearest => "{rne-sae}", SaeMode::RoundDown => "{rd-sae}", @@ -541,6 +533,43 @@ impl SaeMode { SAE_MODES[idx] } } + +pub trait OperandVisitor { + type Ok; + type Error; + + fn visit_reg(&mut self, reg: RegSpec) -> Result; + fn visit_deref(&mut self, reg: RegSpec) -> Result; + fn visit_disp(&mut self, reg: RegSpec, disp: i32) -> Result; + fn visit_index_scale(&mut self, reg: RegSpec, scale: u8) -> Result; + fn visit_index_scale_disp(&mut self, reg: RegSpec, scale: u8, disp: i32) -> Result; + fn visit_base_index_scale(&mut self, base: RegSpec, index: RegSpec, scale: u8) -> Result; + fn visit_base_index_scale_disp(&mut self, base: RegSpec, index: RegSpec, scale: u8, disp: i32) -> Result; + fn visit_i8(&mut self, imm: i8) -> Result; + fn visit_u8(&mut self, imm: u8) -> Result; + fn visit_i16(&mut self, imm: i16) -> Result; + fn visit_u16(&mut self, imm: u16) -> Result; + fn visit_i32(&mut self, imm: i32) -> Result; + fn visit_u32(&mut self, imm: u32) -> Result; + fn visit_i64(&mut self, imm: i64) -> Result; + fn visit_u64(&mut self, imm: u64) -> Result; + fn visit_abs_u32(&mut self, imm: u32) -> Result; + fn visit_abs_u64(&mut self, imm: u64) -> Result; + fn visit_reg_mask_merge(&mut self, base: RegSpec, mask: RegSpec, merge_mode: MergeMode) -> Result; + fn visit_reg_mask_merge_sae(&mut self, base: RegSpec, mask: RegSpec, merge_mode: MergeMode, sae_mode: SaeMode) -> Result; + fn visit_reg_mask_merge_sae_noround(&mut self, base: RegSpec, mask: RegSpec, merge_mode: MergeMode) -> Result; + fn visit_disp_masked(&mut self, base: RegSpec, disp: i32, mask_reg: RegSpec) -> Result; + fn visit_deref_masked(&mut self, base: RegSpec, mask_reg: RegSpec) -> Result; + fn visit_index_scale_masked(&mut self, base: RegSpec, scale: u8, mask_reg: RegSpec) -> Result; + fn visit_index_scale_disp_masked(&mut self, base: RegSpec, scale: u8, disp: i32, mask_reg: RegSpec) -> Result; + fn visit_base_index_masked(&mut self, base: RegSpec, index: RegSpec, mask_reg: RegSpec) -> Result; + fn visit_base_index_disp_masked(&mut self, base: RegSpec, index: RegSpec, disp: i32, mask_reg: RegSpec) -> Result; + fn visit_base_index_scale_masked(&mut self, base: RegSpec, index: RegSpec, scale: u8, mask_reg: RegSpec) -> Result; + fn visit_base_index_scale_disp_masked(&mut self, base: RegSpec, index: RegSpec, scale: u8, disp: i32, mask_reg: RegSpec) -> Result; + + fn visit_other(&mut self) -> Result; +} + impl Operand { fn from_spec(inst: &Instruction, spec: OperandSpec) -> Operand { match spec { @@ -549,140 +578,140 @@ impl Operand { } // the register in modrm_rrr OperandSpec::RegRRR => { - Operand::Register(inst.regs[0]) + Operand::Register { reg: inst.regs[0] } } OperandSpec::RegRRR_maskmerge => { - Operand::RegisterMaskMerge( - inst.regs[0], - RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), - MergeMode::from(inst.prefixes.evex_unchecked().merge()), - ) + Operand::RegisterMaskMerge { + reg: inst.regs[0], + mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), + merge: MergeMode::from(inst.prefixes.evex_unchecked().merge()), + } } OperandSpec::RegRRR_maskmerge_sae => { - Operand::RegisterMaskMergeSae( - inst.regs[0], - RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), - MergeMode::from(inst.prefixes.evex_unchecked().merge()), - SaeMode::from(inst.prefixes.evex_unchecked().vex().l(), inst.prefixes.evex_unchecked().lp()), - ) + Operand::RegisterMaskMergeSae { + reg: inst.regs[0], + mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), + merge: MergeMode::from(inst.prefixes.evex_unchecked().merge()), + sae: SaeMode::from(inst.prefixes.evex_unchecked().vex().l(), inst.prefixes.evex_unchecked().lp()), + } } OperandSpec::RegRRR_maskmerge_sae_noround => { - Operand::RegisterMaskMergeSaeNoround( - inst.regs[0], - RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), - MergeMode::from(inst.prefixes.evex_unchecked().merge()), - ) + Operand::RegisterMaskMergeSaeNoround { + reg: inst.regs[0], + mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), + merge: MergeMode::from(inst.prefixes.evex_unchecked().merge()), + } } // the register in modrm_mmm (eg modrm mod bits were 11) OperandSpec::RegMMM => { - Operand::Register(inst.regs[1]) + Operand::Register { reg: inst.regs[1] } } OperandSpec::RegMMM_maskmerge => { - Operand::RegisterMaskMerge( - inst.regs[1], - RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), - MergeMode::from(inst.prefixes.evex_unchecked().merge()), - ) + Operand::RegisterMaskMerge { + reg: inst.regs[1], + mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), + merge: MergeMode::from(inst.prefixes.evex_unchecked().merge()), + } } OperandSpec::RegMMM_maskmerge_sae_noround => { - Operand::RegisterMaskMergeSaeNoround( - inst.regs[1], - RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), - MergeMode::from(inst.prefixes.evex_unchecked().merge()), - ) + Operand::RegisterMaskMergeSaeNoround { + reg: inst.regs[1], + mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), + merge: MergeMode::from(inst.prefixes.evex_unchecked().merge()), + } } OperandSpec::RegVex => { - Operand::Register(inst.regs[3]) + Operand::Register { reg: inst.regs[3]} } OperandSpec::RegVex_maskmerge => { - Operand::RegisterMaskMerge( - inst.regs[3], - RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), - MergeMode::from(inst.prefixes.evex_unchecked().merge()), - ) + Operand::RegisterMaskMerge { + reg: inst.regs[3], + mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), + merge: MergeMode::from(inst.prefixes.evex_unchecked().merge()), + } } OperandSpec::Reg4 => { - Operand::Register(RegSpec { num: inst.imm as u8, bank: inst.regs[3].bank }) - } - OperandSpec::ImmI8 => Operand::ImmediateI8(inst.imm as i8), - OperandSpec::ImmU8 => Operand::ImmediateU8(inst.imm as u8), - OperandSpec::ImmI16 => Operand::ImmediateI16(inst.imm as i16), - OperandSpec::ImmU16 => Operand::ImmediateU16(inst.imm as u16), - OperandSpec::ImmI32 => Operand::ImmediateI32(inst.imm as i32), - OperandSpec::ImmI64 => Operand::ImmediateI64(inst.imm as i64), - OperandSpec::ImmInDispField => Operand::ImmediateU16(inst.disp as u16), - OperandSpec::DispU32 => Operand::DisplacementU32(inst.disp as u32), - OperandSpec::DispU64 => Operand::DisplacementU64(inst.disp as u64), + Operand::Register { reg: RegSpec { num: inst.imm as u8, bank: inst.regs[3].bank }} + } + OperandSpec::ImmI8 => Operand::ImmediateI8 { imm: inst.imm as i8 }, + OperandSpec::ImmU8 => Operand::ImmediateU8 { imm: inst.imm as u8 }, + OperandSpec::ImmI16 => Operand::ImmediateI16 { imm: inst.imm as i16 }, + OperandSpec::ImmU16 => Operand::ImmediateU16 { imm: inst.imm as u16 }, + OperandSpec::ImmI32 => Operand::ImmediateI32 { imm: inst.imm as i32 }, + OperandSpec::ImmI64 => Operand::ImmediateI64 { imm: inst.imm as i64 }, + OperandSpec::ImmInDispField => Operand::ImmediateU16 { imm: inst.disp as u16 }, + OperandSpec::DispU32 => Operand::AbsoluteU32 { addr: inst.disp as u32 }, + OperandSpec::DispU64 => Operand::AbsoluteU64 { addr: inst.disp as u64 }, OperandSpec::Deref => { - Operand::RegDeref(inst.regs[1]) + Operand::MemDeref { base: inst.regs[1] } } OperandSpec::Deref_esi => { - Operand::RegDeref(RegSpec::esi()) + Operand::MemDeref { base: RegSpec::esi() } } OperandSpec::Deref_edi => { - Operand::RegDeref(RegSpec::edi()) + Operand::MemDeref { base: RegSpec::edi() } } OperandSpec::Deref_rsi => { - Operand::RegDeref(RegSpec::rsi()) + Operand::MemDeref { base: RegSpec::rsi() } } OperandSpec::Deref_rdi => { - Operand::RegDeref(RegSpec::rdi()) + Operand::MemDeref { base: RegSpec::rdi() } } - OperandSpec::RegDisp => { - Operand::RegDisp(inst.regs[1], inst.disp as i32) + OperandSpec::Disp => { + Operand::Disp { base: inst.regs[1], disp: inst.disp as i32 } } - OperandSpec::RegScale => { - Operand::RegScale(inst.regs[2], inst.scale) + OperandSpec::MemIndexScale => { + Operand::MemIndexScale { index: inst.regs[2], scale: inst.scale } } - OperandSpec::RegScaleDisp => { - Operand::RegScaleDisp(inst.regs[2], inst.scale, inst.disp as i32) + OperandSpec::MemIndexScaleDisp => { + Operand::MemIndexScaleDisp { index: inst.regs[2], scale: inst.scale, disp: inst.disp as i32 } } - OperandSpec::RegIndexBaseScale => { - Operand::RegIndexBaseScale(inst.regs[1], inst.regs[2], inst.scale) + OperandSpec::MemBaseIndexScale => { + Operand::MemBaseIndexScale { base: inst.regs[1], index: inst.regs[2], scale: inst.scale } } - OperandSpec::RegIndexBaseScaleDisp => { - Operand::RegIndexBaseScaleDisp(inst.regs[1], inst.regs[2], inst.scale, inst.disp as i32) + OperandSpec::MemBaseIndexScaleDisp => { + Operand::MemBaseIndexScaleDisp { base: inst.regs[1], index: inst.regs[2], scale: inst.scale, disp: inst.disp as i32 } } OperandSpec::Deref_mask => { if inst.prefixes.evex_unchecked().mask_reg() != 0 { - Operand::RegDerefMasked(inst.regs[1], RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg())) + Operand::MemDerefMasked { base: inst.regs[1], mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()) } } else { - Operand::RegDeref(inst.regs[1]) + Operand::MemDeref { base: inst.regs[1] } } } - OperandSpec::RegDisp_mask => { + OperandSpec::Disp_mask => { if inst.prefixes.evex_unchecked().mask_reg() != 0 { - Operand::RegDispMasked(inst.regs[1], inst.disp as i32, RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg())) + Operand::DispMasked { base: inst.regs[1], disp: inst.disp as i32, mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()) } } else { - Operand::RegDisp(inst.regs[1], inst.disp as i32) + Operand::Disp { base: inst.regs[1], disp: inst.disp as i32 } } } - OperandSpec::RegScale_mask => { + OperandSpec::MemIndexScale_mask => { if inst.prefixes.evex_unchecked().mask_reg() != 0 { - Operand::RegScaleMasked(inst.regs[2], inst.scale, RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg())) + Operand::MemIndexScaleMasked { index: inst.regs[2], scale: inst.scale, mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()) } } else { - Operand::RegScale(inst.regs[2], inst.scale) + Operand::MemIndexScale { index: inst.regs[2], scale: inst.scale } } } - OperandSpec::RegScaleDisp_mask => { + OperandSpec::MemIndexScaleDisp_mask => { if inst.prefixes.evex_unchecked().mask_reg() != 0 { - Operand::RegScaleDispMasked(inst.regs[2], inst.scale, inst.disp as i32, RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg())) + Operand::MemIndexScaleDispMasked { index: inst.regs[2], scale: inst.scale, disp: inst.disp as i32, mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()) } } else { - Operand::RegScaleDisp(inst.regs[2], inst.scale, inst.disp as i32) + Operand::MemIndexScaleDisp { index: inst.regs[2], scale: inst.scale, disp: inst.disp as i32 } } } - OperandSpec::RegIndexBaseScale_mask => { + OperandSpec::MemBaseIndexScale_mask => { if inst.prefixes.evex_unchecked().mask_reg() != 0 { - Operand::RegIndexBaseScaleMasked(inst.regs[1], inst.regs[2], inst.scale, RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg())) + Operand::MemBaseIndexScaleMasked { base: inst.regs[1], index: inst.regs[2], scale: inst.scale, mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()) } } else { - Operand::RegIndexBaseScale(inst.regs[1], inst.regs[2], inst.scale) + Operand::MemBaseIndexScale { base: inst.regs[1], index: inst.regs[2], scale: inst.scale } } } - OperandSpec::RegIndexBaseScaleDisp_mask => { + OperandSpec::MemBaseIndexScaleDisp_mask => { if inst.prefixes.evex_unchecked().mask_reg() != 0 { - Operand::RegIndexBaseScaleDispMasked(inst.regs[1], inst.regs[2], inst.scale, inst.disp as i32, RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg())) + Operand::MemBaseIndexScaleDispMasked { base: inst.regs[1], index: inst.regs[2], scale: inst.scale, disp: inst.disp as i32, mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()) } } else { - Operand::RegIndexBaseScaleDisp(inst.regs[1], inst.regs[2], inst.scale, inst.disp as i32) + Operand::MemBaseIndexScaleDisp { base: inst.regs[1], index: inst.regs[2], scale: inst.scale, disp: inst.disp as i32 } } } } @@ -694,38 +723,34 @@ impl Operand { /// memory. pub fn is_memory(&self) -> bool { match self { - Operand::DisplacementU32(_) | - Operand::DisplacementU64(_) | - Operand::RegDeref(_) | - Operand::RegDisp(_, _) | - Operand::RegScale(_, _) | - Operand::RegIndexBase(_, _) | - Operand::RegIndexBaseDisp(_, _, _) | - Operand::RegScaleDisp(_, _, _) | - Operand::RegIndexBaseScale(_, _, _) | - Operand::RegIndexBaseScaleDisp(_, _, _, _) | - Operand::RegDerefMasked(_, _) | - Operand::RegDispMasked(_, _, _) | - Operand::RegScaleMasked(_, _, _) | - Operand::RegIndexBaseMasked(_, _, _) | - Operand::RegIndexBaseDispMasked(_, _, _, _) | - Operand::RegScaleDispMasked(_, _, _, _) | - Operand::RegIndexBaseScaleMasked(_, _, _, _) | - Operand::RegIndexBaseScaleDispMasked(_, _, _, _, _) => { + Operand::AbsoluteU32 { .. } | + Operand::AbsoluteU64 { .. } | + Operand::MemDeref { .. } | + Operand::Disp { .. } | + Operand::MemIndexScale { .. } | + Operand::MemIndexScaleDisp { .. } | + Operand::MemBaseIndexScale { .. } | + Operand::MemBaseIndexScaleDisp { .. } | + Operand::MemDerefMasked { .. } | + Operand::DispMasked { .. } | + Operand::MemIndexScaleMasked { .. } | + Operand::MemIndexScaleDispMasked { .. } | + Operand::MemBaseIndexScaleMasked { .. } | + Operand::MemBaseIndexScaleDispMasked { .. } => { true }, - Operand::ImmediateI8(_) | - Operand::ImmediateU8(_) | - Operand::ImmediateI16(_) | - Operand::ImmediateU16(_) | - Operand::ImmediateU32(_) | - Operand::ImmediateI32(_) | - Operand::ImmediateU64(_) | - Operand::ImmediateI64(_) | - Operand::Register(_) | - Operand::RegisterMaskMerge(_, _, _) | - Operand::RegisterMaskMergeSae(_, _, _, _) | - Operand::RegisterMaskMergeSaeNoround(_, _, _) | + Operand::ImmediateI8 { .. } | + Operand::ImmediateU8 { .. } | + Operand::ImmediateI16 { .. } | + Operand::ImmediateU16 { .. } | + Operand::ImmediateU32 { .. } | + Operand::ImmediateI32 { .. } | + Operand::ImmediateU64 { .. } | + Operand::ImmediateI64 { .. } | + Operand::Register { .. } | + Operand::RegisterMaskMerge { .. } | + Operand::RegisterMaskMergeSae { .. } | + Operand::RegisterMaskMergeSaeNoround { .. } | Operand::Nothing => { false } @@ -737,26 +762,26 @@ impl Operand { /// `Operand` came from; `None` here means the authoritative width is `instr.mem_size()`. pub fn width(&self) -> Option { match self { - Operand::Register(reg) => { + Operand::Register { reg } => { Some(reg.width()) } - Operand::RegisterMaskMerge(reg, _, _) => { + Operand::RegisterMaskMerge { reg, .. } => { Some(reg.width()) } - Operand::ImmediateI8(_) | - Operand::ImmediateU8(_) => { + Operand::ImmediateI8 { .. } | + Operand::ImmediateU8 { .. } => { Some(1) } - Operand::ImmediateI16(_) | - Operand::ImmediateU16(_) => { + Operand::ImmediateI16 { .. } | + Operand::ImmediateU16 { .. } => { Some(2) } - Operand::ImmediateI32(_) | - Operand::ImmediateU32(_) => { + Operand::ImmediateI32 { .. } | + Operand::ImmediateU32 { .. } => { Some(4) } - Operand::ImmediateI64(_) | - Operand::ImmediateU64(_) => { + Operand::ImmediateI64 { .. } | + Operand::ImmediateU64 { .. } => { Some(8) } // memory operands or `Nothing` @@ -765,6 +790,49 @@ impl Operand { } } } + + /// provided for parity with [`Instruction::visit_operand`]. this has little utility other than + /// to reuse an `OperandVisitor` on an `Operand` directly. + #[allow(dead_code)] // in some configurations this is unused, but it is internal-only for now, so it would warn. + fn visit(&self, visitor: &mut T) -> Result { + match self { + Operand::Nothing => { + visitor.visit_other() + } + Operand::Register { reg } => { + visitor.visit_reg(*reg) + } + Operand::MemDeref { base } => { + visitor.visit_deref(*base) + } + Operand::Disp { base, disp } => { + visitor.visit_disp(*base, *disp) + } + Operand::ImmediateI8 { imm } => visitor.visit_i8(*imm), + Operand::ImmediateU8 { imm } => visitor.visit_u8(*imm), + Operand::ImmediateI16 { imm } => visitor.visit_i16(*imm), + Operand::ImmediateU16 { imm } => visitor.visit_u16(*imm), + Operand::ImmediateI32 { imm } => visitor.visit_i32(*imm), + Operand::ImmediateU32 { imm } => visitor.visit_u32(*imm), + Operand::ImmediateI64 { imm } => visitor.visit_i64(*imm), + Operand::ImmediateU64 { imm } => visitor.visit_u64(*imm), + Operand::AbsoluteU32 { addr } => visitor.visit_abs_u32(*addr), + Operand::AbsoluteU64 { addr } => visitor.visit_abs_u64(*addr), + Operand::MemIndexScale { index, scale } => visitor.visit_index_scale(*index, *scale), + Operand::MemIndexScaleDisp { index, scale, disp } => visitor.visit_index_scale_disp(*index, *scale, *disp), + Operand::MemBaseIndexScale { base, index, scale } => visitor.visit_base_index_scale(*base, *index, *scale), + Operand::MemBaseIndexScaleDisp { base, index, scale, disp } => visitor.visit_base_index_scale_disp(*base, *index, *scale, *disp), + Operand::RegisterMaskMerge { reg, mask, merge } => visitor.visit_reg_mask_merge(*reg, *mask, *merge), + Operand::RegisterMaskMergeSae { reg, mask, merge, sae } => visitor.visit_reg_mask_merge_sae(*reg, *mask, *merge, *sae), + Operand::RegisterMaskMergeSaeNoround { reg, mask, merge } => visitor.visit_reg_mask_merge_sae_noround(*reg, *mask, *merge), + Operand::MemDerefMasked { base, mask } => visitor.visit_deref_masked(*base, *mask), + Operand::DispMasked { base, disp, mask } => visitor.visit_disp_masked(*base, *disp, *mask), + Operand::MemIndexScaleMasked { index, scale, mask } => visitor.visit_index_scale_masked(*index, *scale, *mask), + Operand::MemIndexScaleDispMasked { index, scale, disp, mask } => visitor.visit_index_scale_disp_masked(*index, *scale, *disp, *mask), + Operand::MemBaseIndexScaleMasked { base, index, scale, mask } => visitor.visit_base_index_scale_masked(*base, *index, *scale, *mask), + Operand::MemBaseIndexScaleDispMasked { base, index, scale, disp, mask } => visitor.visit_base_index_scale_disp_masked(*base, *index, *scale, *disp, *mask), + } + } } #[test] @@ -855,16 +923,16 @@ const REGISTER_CLASS_NAMES: &[&'static str] = &[ /// } /// } /// -/// if let Operand::Register(regspec) = instruction.operand(0) { +/// if let Operand::Register { reg } = instruction.operand(0) { /// #[cfg(feature="fmt")] -/// println!("first operand is {}", regspec); -/// show_register_class_info(regspec.class()); +/// println!("first operand is {}", reg); +/// show_register_class_info(reg.class()); /// } /// -/// if let Operand::Register(regspec) = instruction.operand(1) { +/// if let Operand::Register { reg } = instruction.operand(1) { /// #[cfg(feature="fmt")] -/// println!("first operand is {}", regspec); -/// show_register_class_info(regspec.class()); +/// println!("second operand is {}", reg); +/// show_register_class_info(reg.class()); /// } /// ``` /// @@ -1064,7 +1132,16 @@ pub enum Opcode { NOT = 0x1019, XADD = 0x101a, XCHG = 0x101b, - Invalid = 0x1c, + + CMPS = 0x201c, + SCAS = 0x201d, + MOVS = 0x201e, + LODS = 0x201f, + STOS = 0x2020, + INS = 0x2021, + OUTS = 0x2022, + + Invalid = 0x23, // XADD, BT, // BTS, @@ -1149,17 +1226,10 @@ pub enum Opcode { CWD, CDQ, CQO, - LODS, - STOS, LAHF, SAHF, - CMPS, - SCAS, - MOVS, TEST, - INS, IN, - OUTS, OUT, IMUL, JO, @@ -2665,17 +2735,17 @@ enum OperandSpec { Deref_edi = 0x90, Deref_rsi = 0x91, Deref_rdi = 0x92, - RegDisp = 0x93, - RegScale = 0x94, - RegScaleDisp = 0x95, - RegIndexBaseScale = 0x96, - RegIndexBaseScaleDisp = 0x97, + Disp = 0x93, + MemIndexScale = 0x94, + MemIndexScaleDisp = 0x95, + MemBaseIndexScale = 0x96, + MemBaseIndexScaleDisp = 0x97, Deref_mask = 0xce, - RegDisp_mask = 0xd3, - RegScale_mask = 0xd4, - RegScaleDisp_mask = 0xd5, - RegIndexBaseScale_mask = 0xd6, - RegIndexBaseScaleDisp_mask = 0xd7, + Disp_mask = 0xd3, + MemIndexScale_mask = 0xd4, + MemIndexScaleDisp_mask = 0xd5, + MemBaseIndexScale_mask = 0xd6, + MemBaseIndexScaleDisp_mask = 0xd7, } // the Hash, Eq, and PartialEq impls here are possibly misleading. @@ -4301,6 +4371,17 @@ impl Opcode { _ => None, } } + + #[inline(always)] + fn can_lock(&self) -> bool { + (*self as u32) & 0x1000 != 0 + } + + #[inline(always)] + #[allow(dead_code)] // in some configurations this is unused, but it is internal-only for now, so it would warn. + fn can_rep(&self) -> bool { + (*self as u32) & 0x2000 != 0 + } } impl Default for Instruction { @@ -4323,6 +4404,167 @@ impl Instruction { Operand::from_spec(self, self.operands[i as usize]) } + // TODO: make public when this seems stable and worthwhile. currently only used for display + // and Displaysink 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(feature="profiling", inline(never))] + #[allow(dead_code)] // in some configurations this is unused, but it is internal-only for now, so it would warn. + fn visit_operand(&self, i: u8, visitor: &mut T) -> Result { + 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_esi => { +// visitor.visit_other() + visitor.visit_deref(RegSpec::esi()) + } + OperandSpec::Deref_edi => { +// visitor.visit_other() + visitor.visit_deref(RegSpec::edi()) + } + OperandSpec::Deref_rsi => { +// visitor.visit_other() + visitor.visit_deref(RegSpec::rsi()) + } + OperandSpec::Deref_rdi => { +// visitor.visit_other() + visitor.visit_deref(RegSpec::rdi()) + } + OperandSpec::Disp => { + 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::ImmI64 => visitor.visit_i64(self.imm as i64), + OperandSpec::ImmInDispField => visitor.visit_u16(self.disp as u16), + OperandSpec::DispU32 => visitor.visit_abs_u32(self.disp as u32), + OperandSpec::DispU64 => visitor.visit_abs_u64(self.disp as u64), + OperandSpec::MemIndexScale => { + visitor.visit_index_scale(self.regs[2], self.scale) + } + OperandSpec::MemIndexScaleDisp => { + visitor.visit_index_scale_disp(self.regs[2], self.scale, self.disp as i32) + } + OperandSpec::MemBaseIndexScale => { + visitor.visit_base_index_scale(self.regs[1], self.regs[2], self.scale) + } + OperandSpec::MemBaseIndexScaleDisp => { + visitor.visit_base_index_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_deref_masked(self.regs[1], RegSpec::mask(self.prefixes.evex_unchecked().mask_reg())) + } else { + visitor.visit_deref(self.regs[1]) + } + } + OperandSpec::Disp_mask => { + if self.prefixes.evex_unchecked().mask_reg() != 0 { + visitor.visit_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::MemIndexScale_mask => { + if self.prefixes.evex_unchecked().mask_reg() != 0 { + visitor.visit_index_scale_masked(self.regs[2], self.scale, RegSpec::mask(self.prefixes.evex_unchecked().mask_reg())) + } else { + visitor.visit_index_scale(self.regs[2], self.scale) + } + } + OperandSpec::MemIndexScaleDisp_mask => { + if self.prefixes.evex_unchecked().mask_reg() != 0 { + visitor.visit_index_scale_disp_masked(self.regs[2], self.scale, self.disp as i32, RegSpec::mask(self.prefixes.evex_unchecked().mask_reg())) + } else { + visitor.visit_index_scale_disp(self.regs[2], self.scale, self.disp as i32) + } + } + OperandSpec::MemBaseIndexScale_mask => { + if self.prefixes.evex_unchecked().mask_reg() != 0 { + visitor.visit_base_index_scale_masked(self.regs[1], self.regs[2], self.scale, RegSpec::mask(self.prefixes.evex_unchecked().mask_reg())) + } else { + visitor.visit_base_index_scale(self.regs[1], self.regs[2], self.scale) + } + } + OperandSpec::MemBaseIndexScaleDisp_mask => { + if self.prefixes.evex_unchecked().mask_reg() != 0 { + visitor.visit_base_index_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_base_index_scale_disp(self.regs[1], self.regs[2], self.scale, self.disp as i32) + } + } + } + } + /// get the number of operands in this instruction. useful in iterating an instruction's /// operands generically. pub fn operand_count(&self) -> u8 { @@ -4380,21 +4622,6 @@ impl Instruction { /// prefixes. pub fn segment_override_for_op(&self, op: u8) -> Option { match self.opcode { - Opcode::STOS | - Opcode::SCAS => { - if op == 0 { - Some(Segment::ES) - } else { - None - } - } - Opcode::LODS => { - if op == 1 { - Some(self.prefixes.segment) - } else { - None - } - } Opcode::MOVS => { if op == 0 { Some(Segment::ES) @@ -4415,8 +4642,8 @@ impl Instruction { }, _ => { // most operands are pretty simple: - if self.operands[op as usize].is_memory() && - self.prefixes.segment != Segment::DS { + if self.prefixes.segment != Segment::DS && + self.operands[op as usize].is_memory() { Some(self.prefixes.segment) } else { None @@ -4610,11 +4837,8 @@ impl Prefixes { fn set_lock(&mut self) { self.bits |= 0x4 } #[inline] pub fn lock(&self) -> bool { self.bits & 0x4 == 4 } - #[deprecated(since = "0.0.1", note = "pub fn cs has never returned `bool` indicating the current selector is `cs`. use `selects_cs` for this purpose, until 2.x that will correct `pub fn cs`.")] - #[inline] - pub fn cs(&mut self) {} #[inline] - pub fn selects_cs(&self) -> bool { self.segment == Segment::CS } + pub fn cs(&self) -> bool { self.segment == Segment::CS } #[inline] pub fn ds(&self) -> bool { self.segment == Segment::DS } #[inline] @@ -5811,7 +6035,8 @@ const OPCODES: [OpcodeRecord; 256] = [ ]; #[allow(non_snake_case)] -#[inline(always)] +#[cfg_attr(feature="profiling", inline(never))] +#[cfg_attr(not(feature="profiling"), inline(always))] pub(self) fn read_E< T: Reader<::Address, ::Word>, S: DescriptionSink, @@ -6007,7 +6232,7 @@ fn read_sib< InnerDescription::Misc("mod bits select no base register") .with_id(sib_start + 0) ); - OperandSpec::RegScale + OperandSpec::MemIndexScale } else { sink.record( modrm_start + 6, @@ -6015,7 +6240,7 @@ fn read_sib< InnerDescription::RegisterNumber("mod", 0b101, instr.regs[1]) .with_id(sib_start + 0) ); - OperandSpec::RegIndexBaseScale + OperandSpec::MemBaseIndexScale } } } else { @@ -6034,7 +6259,7 @@ fn read_sib< InnerDescription::RegisterNumber("iii", instr.regs[2].num & 0b111, instr.regs[2]) .with_id(sib_start + 0) ); - OperandSpec::RegIndexBaseScale + OperandSpec::MemBaseIndexScale } } @@ -6068,7 +6293,7 @@ fn read_sib< InnerDescription::RegisterNumber("mod", 0b101, instr.regs[1]) .with_id(sib_start + 0) ); - OperandSpec::RegDisp + OperandSpec::Disp } } else { sink.record( @@ -6084,7 +6309,7 @@ fn read_sib< InnerDescription::Misc("mod bits select no base register, [index+disp] only") .with_id(sib_start + 0) ); - OperandSpec::RegScaleDisp + OperandSpec::MemIndexScaleDisp } else { sink.record( modrm_start + 6, @@ -6092,7 +6317,7 @@ fn read_sib< InnerDescription::RegisterNumber("mod", 0b101, instr.regs[1]) .with_id(sib_start + 0) ); - OperandSpec::RegIndexBaseScaleDisp + OperandSpec::MemBaseIndexScaleDisp } } } else { @@ -6109,7 +6334,7 @@ fn read_sib< InnerDescription::Misc("iii + rex.x selects no index register") .with_id(sib_start + 0) ); - OperandSpec::RegDisp + OperandSpec::Disp } else { sink.record( sib_start + 3, @@ -6117,7 +6342,7 @@ fn read_sib< InnerDescription::RegisterNumber("iii", instr.regs[2].num & 0b111, instr.regs[2]) .with_id(sib_start + 0) ); - OperandSpec::RegIndexBaseScaleDisp + OperandSpec::MemBaseIndexScaleDisp } } }; @@ -6203,7 +6428,7 @@ fn read_M< OperandSpec::Deref } else { instr.disp = disp as i64 as u64; - OperandSpec::RegDisp + OperandSpec::Disp } } else { sink.record( @@ -6245,7 +6470,7 @@ fn read_M< OperandSpec::Deref } else { instr.disp = disp as i64 as u64; - OperandSpec::RegDisp + OperandSpec::Disp } } }; @@ -6440,6 +6665,7 @@ impl DecodeCtx { self.rb_size } +#[cfg_attr(feature="profiling", inline(never))] fn read_opc_hotpath< T: Reader<::Address, ::Word>, S: DescriptionSink, @@ -6496,7 +6722,8 @@ fn read_opc_hotpath< } } -#[inline(always)] +#[cfg_attr(feature="profiling", inline(never))] +#[cfg_attr(not(feature="profiling"), inline(always))] fn read_with_annotations< T: Reader<::Address, ::Word>, S: DescriptionSink, @@ -6651,7 +6878,7 @@ fn read_with_annotations< self.read_operands(decoder, words, instruction, record, sink)?; if self.check_lock { - if (instruction.opcode as u32) < 0x1000 || !instruction.operands[0].is_memory() { + if !instruction.opcode.can_lock() || !instruction.operands[0].is_memory() { return Err(DecodeError::InvalidPrefixes); } } @@ -6714,7 +6941,8 @@ fn read_avx_prefixed< return Ok(()); } -#[inline(always)] +#[cfg_attr(feature="profiling", inline(never))] +#[cfg_attr(not(feature="profiling"), inline(always))] fn read_operands< T: Reader<::Address, ::Word>, S: DescriptionSink @@ -7411,6 +7639,8 @@ fn read_operands< .with_id(modrm_start - 8) ); if instruction.operands[0] == OperandSpec::RegMMM { + // in 64-bit mode, operand size overrides do not actually shink the operand for + // `call`/`jmp`. if opcode == Opcode::CALL || opcode == Opcode::JMP { instruction.regs[1].bank = RegisterBank::Q; if opcode == Opcode::CALL { @@ -9002,6 +9232,7 @@ fn read_operands< } OperandCase::Yb_AL => { instruction.regs[0] = RegSpec::al(); + instruction.prefixes.segment = Segment::ES; if instruction.prefixes.address_size() { instruction.regs[1] = RegSpec::edi(); } else { @@ -9028,6 +9259,7 @@ fn read_operands< let bank = bank_from_prefixes_64(SizeCode::vqp, instruction.prefixes); instruction.regs[0].num = 0; instruction.regs[0].bank = bank; + instruction.prefixes.segment = Segment::ES; if instruction.prefixes.address_size() { instruction.regs[1] = RegSpec::edi(); } else { diff --git a/src/protected_mode/display.rs b/src/protected_mode/display.rs index 55cab9e2..81243375 100644 --- a/src/protected_mode/display.rs +++ b/src/protected_mode/display.rs @@ -1,11 +1,136 @@ use core::fmt; -use crate::safer_unchecked::GetSaferUnchecked as _; +// allowing these deprecated items for the time being, not yet breaking yaxpeax-x86 apis +#[allow(deprecated)] use yaxpeax_arch::{Colorize, ShowContextual, NoColors, YaxColors}; -use yaxpeax_arch::display::*; use crate::MEM_SIZE_STRINGS; -use crate::protected_mode::{RegSpec, Opcode, Operand, MergeMode, InstDecoder, Instruction, Segment, PrefixVex, OperandSpec}; +use crate::protected_mode::{RegSpec, Opcode, Operand, MergeMode, InstDecoder, Instruction, Segment, PrefixVex}; + +use yaxpeax_arch::display::DisplaySink; +use yaxpeax_arch::safer_unchecked::GetSaferUnchecked as _; +use yaxpeax_arch::safer_unchecked::unreachable_kinda_unchecked as unreachable_unchecked; + +trait DisplaySinkExt { + // `write_opcode` depends on all mnemonics being less than 32 bytes long. check that here, at + // compile time. referenced later to force evaluation of this const. + const MNEMONIC_LT_32: () = { + let mut i = 0; + while i < MNEMONICS.len() { + let name = &MNEMONICS[i]; + if name.len() >= 32 { + panic!("mnemonic too long"); + } + i += 1; + } + }; + + // `write_reg` depends on all register names being less than 8 bytes long. check that here, at + // compile time. referenced later to force evaluation of this const. + const REG_LABEL_LT_8: () = { + let mut i = 0; + while i < REG_NAMES.len() { + let name = ®_NAMES[i]; + if name.len() >= 8 { + panic!("register name too long"); + } + i += 1; + } + }; + + // `write_mem_size_label` depends on all memory size labels being less than 8 bytes long. check + // that here, at compile time. referenced later to force evaluation of this const. + const MEM_SIZE_LABEL_LT_8: () = { + let mut i = 0; + while i < crate::MEM_SIZE_STRINGS.len() { + let name = &MEM_SIZE_STRINGS[i]; + if name.len() >= 8 { + panic!("memory label name too long"); + } + i += 1; + } + }; + + // `write_sae_mode` depends on all sae mode labels being less than 16 bytes long. check that + // here, at compile time. referenced later to force evaluation of this const. + const SAE_LABEL_LT_16: () = { + let mut i = 0; + while i < super::SAE_MODES.len() { + let mode = &super::SAE_MODES[i]; + if mode.label().len() >= 16 { + panic!("sae mode label too long"); + } + i += 1; + } + }; + + fn write_opcode(&mut self, opcode: super::Opcode) -> Result<(), core::fmt::Error>; + fn write_reg(&mut self, reg: RegSpec) -> Result<(), core::fmt::Error>; + fn write_displacement(&mut self, disp: i32) -> Result<(), core::fmt::Error>; + fn write_scale(&mut self, scale: u8) -> Result<(), core::fmt::Error>; + fn write_mem_size_label(&mut self, mem_size: u8) -> Result<(), core::fmt::Error>; + fn write_sae_mode(&mut self, sae: super::SaeMode) -> Result<(), core::fmt::Error>; +} + +impl DisplaySinkExt for T { + #[inline(always)] + fn write_opcode(&mut self, opcode: super::Opcode) -> Result<(), core::fmt::Error> { + let name = opcode.name(); + + let _ = Self::MNEMONIC_LT_32; + // Safety: all opcode mnemonics are 31 bytes or fewer. + unsafe { self.write_lt_32(name) } + } + + #[inline(always)] + fn write_reg(&mut self, reg: RegSpec) -> Result<(), core::fmt::Error> { + let label = regspec_label(®); + + let _ = Self::REG_LABEL_LT_8; + // Safety: all register labels are 7 bytes or fewer. + unsafe { self.write_lt_8(label) } + } + + #[inline(always)] + fn write_displacement(&mut self, disp: i32) -> Result<(), core::fmt::Error> { + let mut v = disp as u32; + if disp < 0 { + self.write_fixed_size("- ")?; + v = disp.unsigned_abs(); + } else { + self.write_fixed_size("+ ")?; + } + self.span_start_number(); + self.write_prefixed_u32(v)?; + self.span_end_number(); + Ok(()) + } + + #[inline(always)] + fn write_scale(&mut self, scale: u8) -> Result<(), core::fmt::Error> { + self.span_start_number(); + self.write_char((0x30 + scale) as char)?; // translate scale=1 to '1', scale=2 to '2', etc + self.span_end_number(); + Ok(()) + } + + #[inline(always)] + fn write_mem_size_label(&mut self, mem_size: u8) -> Result<(), core::fmt::Error> { + let label = mem_size_label(mem_size); + let _ = Self::MEM_SIZE_LABEL_LT_8; + // Safety: all memory size labels are 7 bytes or fewer + unsafe { self.write_lt_8(label) } + } + + #[inline(always)] + fn write_sae_mode(&mut self, sae_mode: super::SaeMode) -> Result<(), core::fmt::Error> { + let label = sae_mode.label(); + + let _ = Self::SAE_LABEL_LT_16; + // Safety: all sae labels are 15 bytes or fewer. + unsafe { self.write_lt_16(label) } + } +} impl fmt::Display for InstDecoder { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -89,6 +214,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 { @@ -129,6 +267,10 @@ 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) } } +pub(crate) fn mem_size_label(size: u8) -> &'static str { + unsafe { MEM_SIZE_STRINGS.get_kinda_unchecked(size as usize) } +} + impl fmt::Display for RegSpec { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(regspec_label(self)) @@ -137,207 +279,313 @@ impl fmt::Display for RegSpec { impl fmt::Display for Operand { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + // to reuse one implementation, call the deprecated function for now. + #[allow(deprecated)] self.colorize(&NoColors, fmt) } } +// allowing these deprecated items for the time being, not yet breaking yaxpeax-x86 apis +#[allow(deprecated)] impl Colorize 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(()) }, + fn colorize(&self, _colors: &Y, f: &mut T) -> fmt::Result { + let mut f = yaxpeax_arch::display::FmtSink::new(f); + let mut visitor = DisplayingOperandVisitor { + f: &mut f + }; + self.visit(&mut visitor) + } +} + +struct DisplayingOperandVisitor<'a, T> { + f: &'a mut T, +} + +impl super::OperandVisitor for DisplayingOperandVisitor<'_, T> { + type Ok = (); + type Error = core::fmt::Error; + + #[cfg_attr(feature="profiling", inline(never))] + fn visit_u8(&mut self, imm: u8) -> Result { + self.f.span_start_immediate(); + self.f.write_fixed_size("0x")?; + self.f.write_u8(imm)?; + self.f.span_end_immediate(); + Ok(()) + } + #[cfg_attr(feature="profiling", inline(never))] + fn visit_i8(&mut self, imm: i8) -> Result { + self.f.span_start_immediate(); + let mut v = imm as u8; + if imm < 0 { + self.f.write_char('-')?; + v = imm.unsigned_abs(); + } + self.f.write_fixed_size("0x")?; + self.f.write_u8(v)?; + self.f.span_end_immediate(); + Ok(()) + } + #[cfg_attr(feature="profiling", inline(never))] + fn visit_u16(&mut self, imm: u16) -> Result { + self.f.span_start_immediate(); + self.f.write_fixed_size("0x")?; + self.f.write_u16(imm)?; + self.f.span_end_immediate(); + Ok(()) + } + #[cfg_attr(feature="profiling", inline(never))] + fn visit_i16(&mut self, imm: i16) -> Result { + self.f.span_start_immediate(); + let mut v = imm as u16; + if imm < 0 { + self.f.write_char('-')?; + v = imm.unsigned_abs(); } + self.f.write_fixed_size("0x")?; + self.f.write_u16(v)?; + self.f.span_end_immediate(); + Ok(()) + } + #[cfg_attr(feature="profiling", inline(never))] + fn visit_u32(&mut self, imm: u32) -> Result { + self.f.span_start_immediate(); + self.f.write_fixed_size("0x")?; + self.f.write_u32(imm)?; + self.f.span_end_immediate(); + Ok(()) + } + fn visit_i32(&mut self, imm: i32) -> Result { + self.f.span_start_immediate(); + let mut v = imm as u32; + if imm < 0 { + self.f.write_char('-')?; + v = imm.unsigned_abs(); + } + self.f.write_fixed_size("0x")?; + self.f.write_u32(v)?; + self.f.span_end_immediate(); + Ok(()) + } + #[cfg_attr(feature="profiling", inline(never))] + fn visit_reg(&mut self, reg: RegSpec) -> Result { + self.f.write_reg(reg)?; + Ok(()) + } + fn visit_reg_mask_merge(&mut self, spec: RegSpec, mask: RegSpec, merge_mode: MergeMode) -> Result { + self.f.write_reg(spec)?; + if mask.num != 0 { + self.f.write_fixed_size("{")?; + self.f.write_reg(mask)?; + self.f.write_fixed_size("}")?; + } + if let MergeMode::Zero = merge_mode { + self.f.write_fixed_size("{z}")?; + } + Ok(()) + } + fn visit_reg_mask_merge_sae(&mut self, spec: RegSpec, mask: RegSpec, merge_mode: MergeMode, sae_mode: super::SaeMode) -> Result { + self.f.write_reg(spec)?; + if mask.num != 0 { + self.f.write_fixed_size("{")?; + self.f.write_reg(mask)?; + self.f.write_fixed_size("}")?; + } + if let MergeMode::Zero = merge_mode { + self.f.write_fixed_size("{z}")?; + } + self.f.write_sae_mode(sae_mode)?; + Ok(()) + } + fn visit_reg_mask_merge_sae_noround(&mut self, spec: RegSpec, mask: RegSpec, merge_mode: MergeMode) -> Result { + self.f.write_reg(spec)?; + if mask.num != 0 { + self.f.write_fixed_size("{")?; + self.f.write_reg(mask)?; + self.f.write_fixed_size("}")?; + } + if let MergeMode::Zero = merge_mode { + self.f.write_fixed_size("{z}")?; + } + self.f.write_fixed_size("{sae}")?; + Ok(()) + } + fn visit_abs_u16(&mut self, imm: u16) -> Result { + self.f.write_fixed_size("[")?; + self.f.span_start_address(); + self.f.write_fixed_size("0x")?; + self.f.write_u16(imm)?; + self.f.span_end_address(); + self.f.write_fixed_size("]")?; + Ok(()) + } + fn visit_abs_u32(&mut self, imm: u32) -> Result { + self.f.write_fixed_size("[")?; + self.f.span_start_address(); + self.f.write_fixed_size("0x")?; + self.f.write_u32(imm)?; + self.f.span_end_address(); + self.f.write_fixed_size("]")?; + Ok(()) + } + #[cfg_attr(not(feature="profiling"), inline(always))] + #[cfg_attr(feature="profiling", inline(never))] + fn visit_disp(&mut self, base: RegSpec, disp: i32) -> Result { + self.f.write_char('[')?; + self.f.write_reg(base)?; + self.f.write_fixed_size(" ")?; + self.f.write_displacement(disp)?; + self.f.write_fixed_size("]") + } + fn visit_deref(&mut self, base: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size("]") + } + fn visit_index_scale(&mut self, index: RegSpec, scale: u8) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_fixed_size("]")?; + + Ok(()) + } + fn visit_index_scale_disp(&mut self, index: RegSpec, scale: u8, disp: i32) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_fixed_size(" ")?; + self.f.write_displacement(disp)?; + self.f.write_char(']') + } + fn visit_base_index_scale(&mut self, base: RegSpec, index: RegSpec, scale: u8) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size(" + ")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_fixed_size("]") + } + fn visit_base_index_scale_disp(&mut self, base: RegSpec, index: RegSpec, scale: u8, disp: i32) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size(" + ")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_fixed_size(" ")?; + self.f.write_displacement(disp)?; + self.f.write_fixed_size("]") + } + fn visit_disp_masked(&mut self, base: RegSpec, disp: i32, mask_reg: RegSpec) -> Result { + self.f.write_char('[')?; + self.f.write_reg(base)?; + self.f.write_char(' ')?; + self.f.write_displacement(disp)?; + self.f.write_char(']')?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_deref_masked(&mut self, base: RegSpec, mask_reg: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size("]")?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_index_scale_masked(&mut self, index: RegSpec, scale: u8, mask_reg: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_fixed_size("]")?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_index_scale_disp_masked(&mut self, index: RegSpec, scale: u8, disp: i32, mask_reg: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_fixed_size(" ")?; + self.f.write_displacement(disp)?; + self.f.write_char(']')?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_base_index_masked(&mut self, base: RegSpec, index: RegSpec, mask_reg: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size(" + ")?; + self.f.write_reg(index)?; + self.f.write_fixed_size("]")?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_base_index_disp_masked(&mut self, base: RegSpec, index: RegSpec, disp: i32, mask_reg: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size(" + ")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" ")?; + self.f.write_displacement(disp)?; + self.f.write_char(']')?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_base_index_scale_masked(&mut self, base: RegSpec, index: RegSpec, scale: u8, mask_reg: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size(" + ")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_fixed_size("]")?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_base_index_scale_disp_masked(&mut self, base: RegSpec, index: RegSpec, scale: u8, disp: i32, mask_reg: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size(" + ")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_char(' ')?; + self.f.write_displacement(disp)?; + self.f.write_char(']')?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_absolute_far_address(&mut self, segment: u16, address: u32) -> Result { + self.f.write_prefixed_u16(segment)?; + self.f.write_fixed_size(":")?; + self.f.write_prefixed_u32(address)?; + Ok(()) + } + + + fn visit_other(&mut self) -> Result { + Ok(()) } } @@ -376,6 +624,13 @@ const MNEMONICS: &[&'static str] = &[ "not", "xadd", "xchg", + "cmps", + "scas", + "movs", + "lods", + "stos", + "ins", + "outs", "invalid", "bt", "bsf", @@ -457,17 +712,10 @@ const MNEMONICS: &[&'static str] = &[ "cwd", "cdq", "cqo", - "lods", - "stos", "lahf", "sahf", - "cmps", - "scas", - "movs", "test", - "ins", "in", - "outs", "out", "imul", "jo", @@ -1854,1456 +2102,27 @@ impl Opcode { } } +// allowing these deprecated items for the time being, not yet breaking yaxpeax-x86 apis +#[allow(deprecated)] impl Colorize for Opcode { - fn colorize(&self, colors: &Y, out: &mut T) -> fmt::Result { - match self { - Opcode::VGF2P8AFFINEQB | - Opcode::VGF2P8AFFINEINVQB | - Opcode::VPSHRDQ | - Opcode::VPSHRDD | - Opcode::VPSHRDW | - Opcode::VPSHLDQ | - Opcode::VPSHLDD | - Opcode::VPSHLDW | - Opcode::VBROADCASTF32X8 | - Opcode::VBROADCASTF64X4 | - Opcode::VBROADCASTF32X4 | - Opcode::VBROADCASTF64X2 | - Opcode::VBROADCASTF32X2 | - Opcode::VBROADCASTI32X8 | - Opcode::VBROADCASTI64X4 | - Opcode::VBROADCASTI32X4 | - Opcode::VBROADCASTI64X2 | - Opcode::VBROADCASTI32X2 | - Opcode::VEXTRACTI32X8 | - Opcode::VEXTRACTF32X8 | - Opcode::VINSERTI32X8 | - Opcode::VINSERTF32X8 | - Opcode::VINSERTI32X4 | - Opcode::V4FNMADDSS | - Opcode::V4FNMADDPS | - Opcode::VCVTNEPS2BF16 | - Opcode::V4FMADDSS | - Opcode::V4FMADDPS | - Opcode::VCVTNE2PS2BF16 | - Opcode::VP2INTERSECTD | - Opcode::VP2INTERSECTQ | - Opcode::VP4DPWSSDS | - Opcode::VP4DPWSSD | - Opcode::VPDPWSSDS | - Opcode::VPDPWSSD | - Opcode::VPDPBUSDS | - Opcode::VDPBF16PS | - Opcode::VPBROADCASTMW2D | - Opcode::VPBROADCASTMB2Q | - Opcode::VPMOVD2M | - Opcode::VPMOVQD | - Opcode::VPMOVWB | - Opcode::VPMOVDB | - Opcode::VPMOVDW | - Opcode::VPMOVQB | - Opcode::VPMOVQW | - Opcode::VGF2P8MULB | - Opcode::VPMADD52HUQ | - Opcode::VPMADD52LUQ | - Opcode::VPSHUFBITQMB | - Opcode::VPERMB | - Opcode::VPEXPANDD | - Opcode::VPEXPANDQ | - Opcode::VPABSQ | - Opcode::VPRORVD | - Opcode::VPRORVQ | - Opcode::VPMULTISHIFTQB | - Opcode::VPERMT2B | - Opcode::VPERMT2W | - Opcode::VPSHRDVQ | - Opcode::VPSHRDVD | - Opcode::VPSHRDVW | - Opcode::VPSHLDVQ | - Opcode::VPSHLDVD | - Opcode::VPSHLDVW | - Opcode::VPCOMPRESSB | - Opcode::VPCOMPRESSW | - Opcode::VPEXPANDB | - Opcode::VPEXPANDW | - Opcode::VPOPCNTD | - Opcode::VPOPCNTQ | - Opcode::VPOPCNTB | - Opcode::VPOPCNTW | - Opcode::VSCALEFSS | - Opcode::VSCALEFSD | - Opcode::VSCALEFPS | - Opcode::VSCALEFPD | - Opcode::VPDPBUSD | - Opcode::VCVTUSI2SD | - Opcode::VCVTUSI2SS | - Opcode::VPXORD | - Opcode::VPXORQ | - Opcode::VPORD | - Opcode::VPORQ | - Opcode::VPANDND | - Opcode::VPANDNQ | - Opcode::VPANDD | - Opcode::VPANDQ | - - Opcode::VHADDPS | - Opcode::VHSUBPS | - Opcode::VADDSUBPS | - Opcode::VADDPD | - Opcode::VADDPS | - Opcode::VADDSD | - Opcode::VADDSS | - Opcode::VADDSUBPD | - Opcode::VFMADD132PD | - Opcode::VFMADD132PS | - Opcode::VFMADD132SD | - Opcode::VFMADD132SS | - Opcode::VFMADD213PD | - Opcode::VFMADD213PS | - Opcode::VFMADD213SD | - Opcode::VFMADD213SS | - Opcode::VFMADD231PD | - Opcode::VFMADD231PS | - Opcode::VFMADD231SD | - Opcode::VFMADD231SS | - Opcode::VFMADDSUB132PD | - Opcode::VFMADDSUB132PS | - Opcode::VFMADDSUB213PD | - Opcode::VFMADDSUB213PS | - Opcode::VFMADDSUB231PD | - Opcode::VFMADDSUB231PS | - Opcode::VFMSUB132PD | - Opcode::VFMSUB132PS | - Opcode::VFMSUB132SD | - Opcode::VFMSUB132SS | - Opcode::VFMSUB213PD | - Opcode::VFMSUB213PS | - Opcode::VFMSUB213SD | - Opcode::VFMSUB213SS | - Opcode::VFMSUB231PD | - Opcode::VFMSUB231PS | - Opcode::VFMSUB231SD | - Opcode::VFMSUB231SS | - Opcode::VFMSUBADD132PD | - Opcode::VFMSUBADD132PS | - Opcode::VFMSUBADD213PD | - Opcode::VFMSUBADD213PS | - Opcode::VFMSUBADD231PD | - Opcode::VFMSUBADD231PS | - Opcode::VFNMADD132PD | - Opcode::VFNMADD132PS | - Opcode::VFNMADD132SD | - Opcode::VFNMADD132SS | - Opcode::VFNMADD213PD | - Opcode::VFNMADD213PS | - Opcode::VFNMADD213SD | - Opcode::VFNMADD213SS | - Opcode::VFNMADD231PD | - Opcode::VFNMADD231PS | - Opcode::VFNMADD231SD | - Opcode::VFNMADD231SS | - Opcode::VFNMSUB132PD | - Opcode::VFNMSUB132PS | - Opcode::VFNMSUB132SD | - Opcode::VFNMSUB132SS | - Opcode::VFNMSUB213PD | - Opcode::VFNMSUB213PS | - Opcode::VFNMSUB213SD | - Opcode::VFNMSUB213SS | - Opcode::VFNMSUB231PD | - Opcode::VFNMSUB231PS | - Opcode::VFNMSUB231SD | - Opcode::VFNMSUB231SS | - Opcode::VDIVPD | - Opcode::VDIVPS | - Opcode::VDIVSD | - Opcode::VDIVSS | - Opcode::VHADDPD | - Opcode::VHSUBPD | - Opcode::HADDPD | - Opcode::HSUBPD | - Opcode::VMULPD | - Opcode::VMULPS | - Opcode::VMULSD | - Opcode::VMULSS | - Opcode::VPABSB | - Opcode::VPABSD | - Opcode::VPABSW | - Opcode::PABSB | - Opcode::PABSD | - Opcode::PABSW | - Opcode::VPSIGNB | - Opcode::VPSIGND | - Opcode::VPSIGNW | - Opcode::PSIGNB | - Opcode::PSIGND | - Opcode::PSIGNW | - Opcode::VPADDB | - Opcode::VPADDD | - Opcode::VPADDQ | - Opcode::VPADDSB | - Opcode::VPADDSW | - Opcode::VPADDUSB | - Opcode::VPADDUSW | - Opcode::VPADDW | - Opcode::VPAVGB | - Opcode::VPAVGW | - Opcode::VPMULDQ | - Opcode::VPMULHRSW | - Opcode::VPMULHUW | - Opcode::VPMULHW | - Opcode::VPMULLQ | - Opcode::VPMULLD | - Opcode::VPMULLW | - Opcode::VPMULUDQ | - Opcode::PCLMULQDQ | - Opcode::PMULDQ | - Opcode::PMULHRSW | - Opcode::PMULLD | - Opcode::VPSUBB | - Opcode::VPSUBD | - Opcode::VPSUBQ | - Opcode::VPSUBSB | - Opcode::VPSUBSW | - Opcode::VPSUBUSB | - Opcode::VPSUBUSW | - Opcode::VPSUBW | - Opcode::VROUNDPD | - Opcode::VROUNDPS | - Opcode::VEXP2PD | - Opcode::VEXP2PS | - Opcode::VEXP2SD | - Opcode::VEXP2SS | - Opcode::VRCP28PD | - Opcode::VRCP28PS | - Opcode::VRCP28SD | - Opcode::VRCP28SS | - Opcode::VRCP14PD | - Opcode::VRCP14PS | - Opcode::VRCP14SD | - Opcode::VRCP14SS | - Opcode::VRNDSCALEPD | - Opcode::VRNDSCALEPS | - Opcode::VRNDSCALESD | - Opcode::VRNDSCALESS | - Opcode::VRSQRT14PD | - Opcode::VRSQRT14PS | - Opcode::VRSQRT14SD | - Opcode::VRSQRT14SS | - Opcode::VSCALEDPD | - Opcode::VSCALEDPS | - Opcode::VSCALEDSD | - Opcode::VSCALEDSS | - Opcode::VRSQRT28PD | - Opcode::VRSQRT28PS | - Opcode::VRSQRT28SD | - Opcode::VRSQRT28SS | - Opcode::VRSQRTPS | - Opcode::VSQRTPD | - Opcode::VSQRTPS | - Opcode::VSUBPD | - Opcode::VSUBPS | - Opcode::VSUBSD | - Opcode::VSUBSS | - Opcode::VRCPSS | - Opcode::VROUNDSD | - Opcode::VROUNDSS | - Opcode::ROUNDPD | - Opcode::ROUNDPS | - Opcode::ROUNDSD | - Opcode::ROUNDSS | - Opcode::VRSQRTSS | - Opcode::VSQRTSD | - Opcode::VSQRTSS | - Opcode::VPSADBW | - Opcode::VMPSADBW | - Opcode::VDBPSADBW | - Opcode::VPHADDD | - Opcode::VPHADDSW | - Opcode::VPHADDW | - Opcode::VPHSUBD | - Opcode::VPHSUBSW | - Opcode::VPHSUBW | - Opcode::VPMADDUBSW | - Opcode::VPMADDWD | - Opcode::VDPPD | - Opcode::VDPPS | - Opcode::VRCPPS | - Opcode::VORPD | - Opcode::VORPS | - Opcode::VANDPD | - Opcode::VANDPS | - Opcode::VANDNPD | - Opcode::VANDNPS | - Opcode::VPAND | - Opcode::VPANDN | - Opcode::VPOR | - Opcode::VPXOR | - Opcode::VXORPD | - Opcode::VXORPS | - Opcode::VPSLLD | - Opcode::VPSLLDQ | - Opcode::VPSLLQ | - Opcode::VPSLLVD | - Opcode::VPSLLVQ | - Opcode::VPSLLW | - Opcode::VPROLD | - Opcode::VPROLQ | - Opcode::VPROLVD | - Opcode::VPROLVQ | - Opcode::VPRORD | - Opcode::VPRORQ | - Opcode::VPRORRD | - Opcode::VPRORRQ | - Opcode::VPSLLVW | - Opcode::VPSRAQ | - Opcode::VPSRAVQ | - Opcode::VPSRAVW | - Opcode::VPSRLVW | - Opcode::VPSRAD | - Opcode::VPSRAVD | - Opcode::VPSRAW | - Opcode::VPSRLD | - Opcode::VPSRLDQ | - Opcode::VPSRLQ | - Opcode::VPSRLVD | - Opcode::VPSRLVQ | - Opcode::VPSRLW | - Opcode::PHADDD | - Opcode::PHADDSW | - Opcode::PHADDW | - Opcode::PHSUBD | - Opcode::PHSUBSW | - Opcode::PHSUBW | - Opcode::PMADDUBSW | - Opcode::ADDSUBPD | - Opcode::DPPS | - Opcode::DPPD | - Opcode::MPSADBW | - Opcode::RCPSS | - Opcode::RSQRTSS | - Opcode::SQRTSD | - Opcode::ADDSD | - Opcode::SUBSD | - Opcode::MULSD | - Opcode::DIVSD | - Opcode::SQRTSS | - Opcode::ADDSS | - Opcode::SUBSS | - Opcode::MULSS | - Opcode::DIVSS | - Opcode::HADDPS | - Opcode::HSUBPS | - Opcode::ADDSUBPS | - Opcode::PMULHRW | - Opcode::PFRCP | - Opcode::PFRSQRT | - Opcode::PFSUB | - Opcode::PFADD | - Opcode::PFRCPIT1 | - Opcode::PFRSQIT1 | - Opcode::PFSUBR | - Opcode::PFACC | - Opcode::PFMUL | - Opcode::PFMULHRW | - Opcode::PFRCPIT2 | - Opcode::PFNACC | - Opcode::PFPNACC | - Opcode::PSWAPD | - Opcode::PAVGUSB | - Opcode::XADD| - Opcode::DIV | - Opcode::IDIV | - Opcode::MUL | - Opcode::MULX | - Opcode::NEG | - Opcode::NOT | - Opcode::SAR | - Opcode::SAL | - Opcode::SHR | - Opcode::SARX | - Opcode::SHLX | - Opcode::SHRX | - Opcode::SHRD | - Opcode::SHL | - Opcode::RCR | - Opcode::RCL | - Opcode::ROR | - Opcode::RORX | - Opcode::ROL | - Opcode::INC | - Opcode::DEC | - Opcode::SBB | - Opcode::AND | - Opcode::XOR | - Opcode::OR | - Opcode::LEA | - Opcode::ADD | - Opcode::ADC | - Opcode::ADCX | - Opcode::ADOX | - Opcode::SUB | - Opcode::POPCNT | - Opcode::LZCNT | - Opcode::VPLZCNTD | - Opcode::VPLZCNTQ | - Opcode::BT | - Opcode::BTS | - Opcode::BTR | - Opcode::BTC | - Opcode::BSF | - Opcode::BSR | - Opcode::BZHI | - Opcode::PDEP | - Opcode::PEXT | - Opcode::TZCNT | - Opcode::ANDN | - Opcode::BEXTR | - Opcode::BLSI | - Opcode::BLSMSK | - Opcode::BLSR | - Opcode::ADDPS | - Opcode::ADDPD | - Opcode::ANDNPS | - Opcode::ANDNPD | - Opcode::ANDPS | - Opcode::ANDPD | - Opcode::COMISD | - Opcode::COMISS | - Opcode::DIVPS | - Opcode::DIVPD | - Opcode::MULPS | - Opcode::MULPD | - Opcode::ORPS | - Opcode::ORPD | - Opcode::PADDB | - Opcode::PADDD | - Opcode::PADDQ | - Opcode::PADDSB | - Opcode::PADDSW | - Opcode::PADDUSB | - Opcode::PADDUSW | - Opcode::PADDW | - Opcode::PAND | - Opcode::PANDN | - Opcode::PAVGB | - Opcode::PAVGW | - Opcode::PMADDWD | - Opcode::PMULHUW | - Opcode::PMULHW | - Opcode::PMULLW | - Opcode::PMULUDQ | - Opcode::POR | - Opcode::PSADBW | - Opcode::PSHUFD | - Opcode::PSHUFW | - Opcode::PSHUFB | - Opcode::PSLLD | - Opcode::PSLLDQ | - Opcode::PSLLQ | - Opcode::PSLLW | - Opcode::PSRAD | - Opcode::PSRAW | - Opcode::PSRLD | - Opcode::PSRLDQ | - Opcode::PSRLQ | - Opcode::PSRLW | - Opcode::PSUBB | - Opcode::PSUBD | - Opcode::PSUBQ | - Opcode::PSUBSB | - Opcode::PSUBSW | - Opcode::PSUBUSB | - Opcode::PSUBUSW | - Opcode::PSUBW | - Opcode::PXOR | - Opcode::RSQRTPS | - Opcode::SQRTPS | - Opcode::SQRTPD | - Opcode::SUBPS | - Opcode::SUBPD | - Opcode::XORPS | - Opcode::XORPD | - Opcode::RCPPS | - Opcode::SHLD | - Opcode::SLHD | - Opcode::UCOMISD | - Opcode::UCOMISS | - Opcode::F2XM1 | - Opcode::FABS | - Opcode::FADD | - Opcode::FADDP | - Opcode::FCHS | - Opcode::FCOS | - Opcode::FDIV | - Opcode::FDIVP | - Opcode::FDIVR | - Opcode::FDIVRP | - Opcode::FIADD | - Opcode::FIDIV | - Opcode::FIDIVR | - Opcode::FIMUL | - Opcode::FISUB | - Opcode::FISUBR | - Opcode::FMUL | - Opcode::FMULP | - Opcode::FNCLEX | - Opcode::FNINIT | - Opcode::FPATAN | - Opcode::FPREM | - Opcode::FPREM1 | - Opcode::FPTAN | - Opcode::FRNDINT | - Opcode::FSCALE | - Opcode::FSIN | - Opcode::FSINCOS | - Opcode::FSQRT | - Opcode::FSUB | - Opcode::FSUBP | - Opcode::FSUBR | - Opcode::FSUBRP | - Opcode::FXTRACT | - Opcode::FYL2X | - Opcode::FYL2XP1 | - Opcode::AAA | - Opcode::AAS | - Opcode::DAS | - Opcode::DAA | - Opcode::AAD | - Opcode::AAM | - Opcode::KADDB | - Opcode::KANDB | - Opcode::KANDNB | - Opcode::KNOTB | - Opcode::KORB | - Opcode::KSHIFTLB | - Opcode::KSHIFTRB | - Opcode::KXNORB | - Opcode::KXORB | - Opcode::KADDW | - Opcode::KANDW | - Opcode::KANDNW | - Opcode::KNOTW | - Opcode::KORW | - Opcode::KSHIFTLW | - Opcode::KSHIFTRW | - Opcode::KXNORW | - Opcode::KXORW | - Opcode::KADDD | - Opcode::KANDD | - Opcode::KANDND | - Opcode::KNOTD | - Opcode::KORD | - Opcode::KSHIFTLD | - Opcode::KSHIFTRD | - Opcode::KXNORD | - Opcode::KXORD | - Opcode::KADDQ | - Opcode::KANDQ | - Opcode::KANDNQ | - Opcode::KNOTQ | - Opcode::KORQ | - Opcode::KSHIFTLQ | - Opcode::KSHIFTRQ | - Opcode::KXNORQ | - Opcode::KXORQ | - Opcode::IMUL => { write!(out, "{}", colors.arithmetic_op(self)) } - Opcode::POPF | - Opcode::PUSHF | - Opcode::ENTER | - Opcode::LEAVE | - Opcode::PUSHA | - Opcode::POPA | - Opcode::PUSH | - Opcode::POP => { write!(out, "{}", colors.stack_op(self)) } - Opcode::WAIT | - Opcode::FNOP | - Opcode::FDISI8087_NOP | - Opcode::FENI8087_NOP | - Opcode::FSETPM287_NOP | - Opcode::PREFETCHNTA | - Opcode::PREFETCH0 | - Opcode::PREFETCH1 | - Opcode::PREFETCH2 | - Opcode::PREFETCHW | - Opcode::NOP => { write!(out, "{}", colors.nop_op(self)) } - - /* Control flow */ - Opcode::HLT | - Opcode::INT | - Opcode::INTO | - Opcode::IRET | - Opcode::IRETD | - Opcode::IRETQ | - Opcode::RETF | - Opcode::RETURN => { write!(out, "{}", colors.stop_op(self)) } - Opcode::LOOPNZ | - Opcode::LOOPZ | - Opcode::LOOP | - Opcode::JECXZ | - Opcode::CALL | - Opcode::CALLF | - Opcode::JMP | - Opcode::JMPF | - Opcode::JO | - Opcode::JNO | - Opcode::JB | - Opcode::JNB | - Opcode::JZ | - Opcode::JNZ | - Opcode::JA | - Opcode::JNA | - Opcode::JS | - Opcode::JNS | - Opcode::JP | - Opcode::JNP | - Opcode::JL | - Opcode::JGE | - Opcode::JLE | - Opcode::JG => { write!(out, "{}", colors.control_flow_op(self)) } - - /* Data transfer */ - Opcode::PI2FW | - Opcode::PI2FD | - Opcode::PF2ID | - Opcode::PF2IW | - Opcode::VCVTDQ2PD | - Opcode::VCVTDQ2PS | - Opcode::VCVTPD2DQ | - Opcode::VCVTPD2PS | - Opcode::VCVTPH2PS | - Opcode::VCVTPS2DQ | - Opcode::VCVTPS2PD | - Opcode::VCVTPS2PH | - Opcode::VCVTTPD2DQ | - Opcode::VCVTTPS2DQ | - Opcode::VCVTSD2SI | - Opcode::VCVTSD2SS | - Opcode::VCVTSI2SD | - Opcode::VCVTSI2SS | - Opcode::VCVTSS2SD | - Opcode::VCVTSS2SI | - Opcode::VCVTTSD2SI | - Opcode::VCVTTSS2SI | - Opcode::VCVTPD2UDQ | - Opcode::VCVTTPD2UDQ | - Opcode::VCVTPS2UDQ | - Opcode::VCVTTPS2UDQ | - Opcode::VCVTQQ2PD | - Opcode::VCVTQQ2PS | - Opcode::VCVTSD2USI | - Opcode::VCVTTSD2USI | - Opcode::VCVTSS2USI | - Opcode::VCVTTSS2USI | - Opcode::VCVTUDQ2PD | - Opcode::VCVTUDQ2PS | - Opcode::VCVTUSI2USD | - Opcode::VCVTUSI2USS | - Opcode::VCVTTPD2QQ | - Opcode::VCVTPD2QQ | - Opcode::VCVTTPD2UQQ | - Opcode::VCVTPD2UQQ | - Opcode::VCVTTPS2QQ | - Opcode::VCVTPS2QQ | - Opcode::VCVTTPS2UQQ | - Opcode::VCVTPS2UQQ | - Opcode::VCVTUQQ2PD | - Opcode::VCVTUQQ2PS | - Opcode::VMOVDDUP | - Opcode::VPSHUFLW | - Opcode::VPSHUFHW | - Opcode::VBLENDMPD | - Opcode::VBLENDMPS | - Opcode::VPBLENDMD | - Opcode::VPBLENDMQ | - Opcode::VBLENDPD | - Opcode::VBLENDPS | - Opcode::VBLENDVPD | - Opcode::VBLENDVPS | - Opcode::VPBLENDMB | - Opcode::VPBLENDMW | - Opcode::PBLENDVB | - Opcode::PBLENDW | - Opcode::BLENDPD | - Opcode::BLENDPS | - Opcode::BLENDVPD | - Opcode::BLENDVPS | - Opcode::BLENDW | - Opcode::VBROADCASTF128 | - Opcode::VBROADCASTI128 | - Opcode::VBROADCASTSD | - Opcode::VBROADCASTSS | - Opcode::VPBROADCASTM | - Opcode::VEXTRACTF128 | - Opcode::VEXTRACTI128 | - Opcode::VEXTRACTPS | - Opcode::EXTRACTPS | - Opcode::VGATHERDPD | - Opcode::VGATHERDPS | - Opcode::VGATHERQPD | - Opcode::VGATHERQPS | - Opcode::VGATHERPF0DPD | - Opcode::VGATHERPF0DPS | - Opcode::VGATHERPF0QPD | - Opcode::VGATHERPF0QPS | - Opcode::VGATHERPF1DPD | - Opcode::VGATHERPF1DPS | - Opcode::VGATHERPF1QPD | - Opcode::VGATHERPF1QPS | - Opcode::VSCATTERDD | - Opcode::VSCATTERDQ | - Opcode::VSCATTERQD | - Opcode::VSCATTERQQ | - Opcode::VPSCATTERDD | - Opcode::VPSCATTERDQ | - Opcode::VPSCATTERQD | - Opcode::VPSCATTERQQ | - Opcode::VSCATTERPF0DPD | - Opcode::VSCATTERPF0DPS | - Opcode::VSCATTERPF0QPD | - Opcode::VSCATTERPF0QPS | - Opcode::VSCATTERPF1DPD | - Opcode::VSCATTERPF1DPS | - Opcode::VSCATTERPF1QPD | - Opcode::VSCATTERPF1QPS | - Opcode::VINSERTF128 | - Opcode::VINSERTI128 | - Opcode::VINSERTPS | - Opcode::INSERTPS | - Opcode::VEXTRACTF32X4 | - Opcode::VEXTRACTF64X2 | - Opcode::VEXTRACTF64X4 | - Opcode::VEXTRACTI32X4 | - Opcode::VEXTRACTI64X2 | - Opcode::VEXTRACTI64X4 | - Opcode::VINSERTF32X4 | - Opcode::VINSERTF64X2 | - Opcode::VINSERTF64X4 | - Opcode::VINSERTI64X2 | - Opcode::VINSERTI64X4 | - Opcode::VSHUFF32X4 | - Opcode::VSHUFF64X2 | - Opcode::VSHUFI32X4 | - Opcode::VSHUFI64X2 | - Opcode::VMASKMOVDQU | - Opcode::VMASKMOVPD | - Opcode::VMASKMOVPS | - Opcode::VMOVAPD | - Opcode::VMOVAPS | - Opcode::VMOVD | - Opcode::VMOVDQA | - Opcode::VMOVDQU | - Opcode::VMOVHLPS | - Opcode::VMOVHPD | - Opcode::VMOVHPS | - Opcode::VMOVLHPS | - Opcode::VMOVLPD | - Opcode::VMOVLPS | - Opcode::VMOVMSKPD | - Opcode::VMOVMSKPS | - Opcode::VMOVNTDQ | - Opcode::VMOVNTDQA | - Opcode::VMOVNTPD | - Opcode::VMOVNTPS | - Opcode::MOVDIR64B | - Opcode::MOVDIRI | - Opcode::MOVNTDQA | - Opcode::VMOVQ | - Opcode::VMOVSHDUP | - Opcode::VMOVSLDUP | - Opcode::VMOVUPD | - Opcode::VMOVUPS | - Opcode::VMOVSD | - Opcode::VMOVSS | - Opcode::VMOVDQA32 | - Opcode::VMOVDQA64 | - Opcode::VMOVDQU32 | - Opcode::VMOVDQU64 | - Opcode::VPMOVM2B | - Opcode::VPMOVM2W | - Opcode::VPMOVB2M | - Opcode::VPMOVW2M | - Opcode::VPMOVSWB | - Opcode::VPMOVUSWB | - Opcode::VPMOVSQB | - Opcode::VPMOVUSQB | - Opcode::VPMOVSQW | - Opcode::VPMOVUSQW | - Opcode::VPMOVSQD | - Opcode::VPMOVUSQD | - Opcode::VPMOVSDB | - Opcode::VPMOVUSDB | - Opcode::VPMOVSDW | - Opcode::VPMOVUSDW | - Opcode::VPMOVM2D | - Opcode::VPMOVM2Q | - Opcode::VPMOVB2D | - Opcode::VPMOVQ2M | - Opcode::VMOVDQU8 | - Opcode::VMOVDQU16 | - - Opcode::VPBLENDD | - Opcode::VPBLENDVB | - Opcode::VPBLENDW | - Opcode::VPBROADCASTB | - Opcode::VPBROADCASTD | - Opcode::VPBROADCASTQ | - Opcode::VPBROADCASTW | - Opcode::VPGATHERDD | - Opcode::VPGATHERDQ | - Opcode::VPGATHERQD | - Opcode::VPGATHERQQ | - Opcode::VPCLMULQDQ | - Opcode::VPMOVMSKB | - Opcode::VPMOVSXBD | - Opcode::VPMOVSXBQ | - Opcode::VPMOVSXBW | - Opcode::VPMOVSXDQ | - Opcode::VPMOVSXWD | - Opcode::VPMOVSXWQ | - Opcode::VPMOVZXBD | - Opcode::VPMOVZXBQ | - Opcode::VPMOVZXBW | - Opcode::VPMOVZXDQ | - Opcode::VPMOVZXWD | - Opcode::VPMOVZXWQ | - Opcode::PMOVSXBD | - Opcode::PMOVSXBQ | - Opcode::PMOVSXBW | - Opcode::PMOVSXDQ | - Opcode::PMOVSXWD | - Opcode::PMOVSXWQ | - Opcode::PMOVZXBD | - Opcode::PMOVZXBQ | - Opcode::PMOVZXBW | - Opcode::PMOVZXDQ | - Opcode::PMOVZXWD | - Opcode::PMOVZXWQ | - Opcode::KUNPCKBW | - Opcode::KUNPCKWD | - Opcode::KUNPCKDQ | - Opcode::VUNPCKHPD | - Opcode::VUNPCKHPS | - Opcode::VUNPCKLPD | - Opcode::VUNPCKLPS | - Opcode::VPUNPCKHBW | - Opcode::VPUNPCKHDQ | - Opcode::VPUNPCKHQDQ | - Opcode::VPUNPCKHWD | - Opcode::VPUNPCKLBW | - Opcode::VPUNPCKLDQ | - Opcode::VPUNPCKLQDQ | - Opcode::VPUNPCKLWD | - Opcode::VSHUFPD | - Opcode::VSHUFPS | - Opcode::VPACKSSDW | - Opcode::VPACKUSDW | - Opcode::PACKUSDW | - Opcode::VPACKSSWB | - Opcode::VPACKUSWB | - Opcode::VALIGND | - Opcode::VALIGNQ | - Opcode::VPALIGNR | - Opcode::PALIGNR | - Opcode::VPERM2F128 | - Opcode::VPERM2I128 | - Opcode::VPERMD | - Opcode::VPERMILPD | - Opcode::VPERMILPS | - Opcode::VPERMPD | - Opcode::VPERMPS | - Opcode::VPERMQ | - Opcode::VPERMI2D | - Opcode::VPERMI2Q | - Opcode::VPERMI2PD | - Opcode::VPERMI2PS | - Opcode::VPERMT2D | - Opcode::VPERMT2Q | - Opcode::VPERMT2PD | - Opcode::VPERMT2PS | - Opcode::VPERMI2B | - Opcode::VPERMI2W | - Opcode::VPERMW | - Opcode::VPEXTRB | - Opcode::VPEXTRD | - Opcode::VPEXTRQ | - Opcode::VPEXTRW | - Opcode::PEXTRB | - Opcode::PEXTRD | - Opcode::PEXTRQ | - Opcode::EXTRQ | - Opcode::PINSRB | - Opcode::PINSRD | - Opcode::PINSRQ | - Opcode::INSERTQ | - Opcode::VPINSRB | - Opcode::VPINSRD | - Opcode::VPINSRQ | - Opcode::VPINSRW | - Opcode::VPMASKMOVD | - Opcode::VPMASKMOVQ | - Opcode::VCOMPRESSPD | - Opcode::VCOMPRESSPS | - Opcode::VPCOMPRESSQ | - Opcode::VPCOMPRESSD | - Opcode::VEXPANDPD | - Opcode::VEXPANDPS | - Opcode::VPSHUFB | - Opcode::VPSHUFD | - Opcode::VPHMINPOSUW | - Opcode::PHMINPOSUW | - Opcode::VZEROUPPER | - Opcode::VZEROALL | - Opcode::VFIXUPIMMPD | - Opcode::VFIXUPIMMPS | - Opcode::VFIXUPIMMSD | - Opcode::VFIXUPIMMSS | - Opcode::VREDUCEPD | - Opcode::VREDUCEPS | - Opcode::VREDUCESD | - Opcode::VREDUCESS | - Opcode::VGETEXPPD | - Opcode::VGETEXPPS | - Opcode::VGETEXPSD | - Opcode::VGETEXPSS | - Opcode::VGETMANTPD | - Opcode::VGETMANTPS | - Opcode::VGETMANTSD | - Opcode::VGETMANTSS | - Opcode::VLDDQU | - Opcode::BSWAP | - Opcode::CVTDQ2PD | - Opcode::CVTDQ2PS | - Opcode::CVTPS2DQ | - Opcode::CVTPD2DQ | - Opcode::CVTPI2PS | - Opcode::CVTPI2PD | - Opcode::CVTPS2PD | - Opcode::CVTPD2PS | - Opcode::CVTPS2PI | - Opcode::CVTPD2PI | - Opcode::CVTSD2SI | - Opcode::CVTSD2SS | - Opcode::CVTSI2SD | - Opcode::CVTSI2SS | - Opcode::CVTSS2SD | - Opcode::CVTSS2SI | - Opcode::CVTTPD2DQ | - Opcode::CVTTPS2DQ | - Opcode::CVTTPS2PI | - Opcode::CVTTPD2PI | - Opcode::CVTTSD2SI | - Opcode::CVTTSS2SI | - Opcode::MASKMOVQ | - Opcode::MASKMOVDQU | - Opcode::MOVAPS | - Opcode::MOVAPD | - Opcode::MOVD | - Opcode::MOVHPS | - Opcode::MOVHPD | - Opcode::MOVHLPS | - Opcode::MOVLPS | - Opcode::MOVLPD | - Opcode::MOVLHPS | - Opcode::MOVMSKPS | - Opcode::MOVMSKPD | - Opcode::MOVNTI | - Opcode::MOVNTPS | - Opcode::MOVNTPD | - Opcode::MOVNTSS | - Opcode::MOVNTSD | - Opcode::MOVNTQ | - Opcode::MOVNTDQ | - Opcode::MOVSD | - Opcode::MOVSS | - Opcode::MOVUPD | - Opcode::PSHUFHW | - Opcode::PSHUFLW | - Opcode::PUNPCKHBW | - Opcode::PUNPCKHDQ | - Opcode::PUNPCKHWD | - Opcode::PUNPCKLBW | - Opcode::PUNPCKLDQ | - Opcode::PUNPCKLWD | - Opcode::PUNPCKLQDQ | - Opcode::PUNPCKHQDQ | - Opcode::PACKSSDW | - Opcode::PACKSSWB | - Opcode::PACKUSWB | - Opcode::UNPCKHPS | - Opcode::UNPCKHPD | - Opcode::UNPCKLPS | - Opcode::UNPCKLPD | - Opcode::SHUFPD | - Opcode::SHUFPS | - Opcode::PMOVMSKB | - Opcode::KMOVB | - Opcode::KMOVW | - Opcode::KMOVD | - Opcode::KMOVQ | - Opcode::BNDMOV | - Opcode::LDDQU | - Opcode::CMC | - Opcode::CLC | - Opcode::CLI | - Opcode::CLD | - Opcode::STC | - Opcode::STI | - Opcode::STD | - Opcode::CBW | - Opcode::CWDE | - Opcode::CDQE | - Opcode::CWD | - Opcode::CDQ | - Opcode::CQO | - Opcode::MOVDDUP | - Opcode::MOVSLDUP | - Opcode::MOVDQ2Q | - Opcode::MOVDQU | - Opcode::MOVDQA | - Opcode::MOVQ | - Opcode::MOVQ2DQ | - Opcode::MOVSHDUP | - Opcode::MOVUPS | - Opcode::PEXTRW | - Opcode::PINSRW | - Opcode::MOV | - Opcode::MOVBE | - Opcode::LODS | - Opcode::STOS | - Opcode::LAHF | - Opcode::SAHF | - Opcode::MOVS | - Opcode::INS | - Opcode::IN | - Opcode::OUTS | - Opcode::OUT | - Opcode::MOVZX | - Opcode::MOVSX | - Opcode::MOVSXD | - Opcode::FILD | - Opcode::FBLD | - Opcode::FBSTP | - Opcode::FIST | - Opcode::FISTP | - Opcode::FISTTP | - Opcode::FLD | - Opcode::FLD1 | - Opcode::FLDCW | - Opcode::FLDENV | - Opcode::FLDL2E | - Opcode::FLDL2T | - Opcode::FLDLG2 | - Opcode::FLDLN2 | - Opcode::FLDPI | - Opcode::FLDZ | - Opcode::FST | - Opcode::FSTP | - Opcode::FSTPNCE | - Opcode::FNSAVE | - Opcode::FNSTCW | - Opcode::FNSTENV | - Opcode::FNSTOR | - Opcode::FNSTSW | - Opcode::FRSTOR | - Opcode::FXCH | - Opcode::XCHG | - Opcode::XLAT | - Opcode::CMOVA | - Opcode::CMOVB | - Opcode::CMOVG | - Opcode::CMOVGE | - Opcode::CMOVL | - Opcode::CMOVLE | - Opcode::CMOVNA | - Opcode::CMOVNB | - Opcode::CMOVNO | - Opcode::CMOVNP | - Opcode::CMOVNS | - Opcode::CMOVNZ | - Opcode::CMOVO | - Opcode::CMOVP | - Opcode::CMOVS | - Opcode::CMOVZ | - Opcode::FCMOVB | - Opcode::FCMOVBE | - Opcode::FCMOVE | - Opcode::FCMOVNB | - Opcode::FCMOVNBE | - Opcode::FCMOVNE | - Opcode::FCMOVNU | - Opcode::FCMOVU | - Opcode::SALC | - Opcode::SETO | - Opcode::SETNO | - Opcode::SETB | - Opcode::SETAE | - Opcode::SETZ | - Opcode::SETNZ | - Opcode::SETBE | - Opcode::SETA | - Opcode::SETS | - Opcode::SETNS | - Opcode::SETP | - Opcode::SETNP | - Opcode::SETL | - Opcode::SETGE | - Opcode::SETLE | - Opcode::SETG => { write!(out, "{}", colors.data_op(self)) } - - Opcode::VCOMISD | - Opcode::VCOMISS | - Opcode::VUCOMISD | - Opcode::VUCOMISS | - Opcode::KORTESTB | - Opcode::KTESTB | - Opcode::KORTESTW | - Opcode::KTESTW | - Opcode::KORTESTD | - Opcode::KTESTD | - Opcode::KORTESTQ | - Opcode::KTESTQ | - Opcode::VPTESTNMD | - Opcode::VPTESTNMQ | - Opcode::VPTERNLOGD | - Opcode::VPTERNLOGQ | - Opcode::VPTESTMD | - Opcode::VPTESTMQ | - Opcode::VPTESTNMB | - Opcode::VPTESTNMW | - Opcode::VPTESTMB | - Opcode::VPTESTMW | - Opcode::VPCMPD | - Opcode::VPCMPUD | - Opcode::VPCMPQ | - Opcode::VPCMPUQ | - Opcode::VPCMPB | - Opcode::VPCMPUB | - Opcode::VPCMPW | - Opcode::VPCMPUW | - Opcode::VCMPPD | - Opcode::VCMPPS | - Opcode::VCMPSD | - Opcode::VCMPSS | - Opcode::VMAXPD | - Opcode::VMAXPS | - Opcode::VMAXSD | - Opcode::VMAXSS | - Opcode::VPMAXSQ | - Opcode::VPMAXUQ | - Opcode::VPMINSQ | - Opcode::VPMINUQ | - Opcode::VMINPD | - Opcode::VMINPS | - Opcode::VMINSD | - Opcode::VMINSS | - Opcode::VPCMPEQB | - Opcode::VPCMPEQD | - Opcode::VPCMPEQQ | - Opcode::VPCMPEQW | - Opcode::VPCMPGTB | - Opcode::VPCMPGTD | - Opcode::VPCMPGTQ | - Opcode::VPCMPGTW | - Opcode::VPCMPESTRI | - Opcode::VPCMPESTRM | - Opcode::VPCMPISTRI | - Opcode::VPCMPISTRM | - Opcode::VPMAXSB | - Opcode::VPMAXSD | - Opcode::VPMAXSW | - Opcode::VPMAXUB | - Opcode::VPMAXUW | - Opcode::VPMAXUD | - Opcode::VPMINSB | - Opcode::VPMINSW | - Opcode::VPMINSD | - Opcode::VPMINUB | - Opcode::VPMINUW | - Opcode::VPMINUD | - Opcode::VFPCLASSPD | - Opcode::VFPCLASSPS | - Opcode::VFPCLASSSD | - Opcode::VFPCLASSSS | - Opcode::VRANGEPD | - Opcode::VRANGEPS | - Opcode::VRANGESD | - Opcode::VRANGESS | - Opcode::VPCONFLICTD | - Opcode::VPCONFLICTQ | - Opcode::VPTEST | - Opcode::VTESTPD | - Opcode::VTESTPS | - Opcode::PCMPEQB | - Opcode::PCMPEQD | - Opcode::PCMPEQQ | - Opcode::PCMPEQW | - Opcode::PCMPESTRI | - Opcode::PCMPESTRM | - Opcode::PCMPGTB | - Opcode::PCMPGTD | - Opcode::PCMPGTQ | - Opcode::PCMPGTW | - Opcode::PCMPISTRI | - Opcode::PCMPISTRM | - Opcode::PTEST | - Opcode::MAXPD | - Opcode::MAXPS | - Opcode::MAXSD | - Opcode::MAXSS | - Opcode::MINPD | - Opcode::MINPS | - Opcode::MINSD | - Opcode::MINSS | - Opcode::PMAXSB | - Opcode::PMAXSD | - Opcode::PMAXSW | - Opcode::PMAXUB | - Opcode::PMAXUD | - Opcode::PMAXUW | - Opcode::PMINSB | - Opcode::PMINSD | - Opcode::PMINSW | - Opcode::PMINUB | - Opcode::PMINUD | - Opcode::PMINUW | - Opcode::PFCMPGE | - Opcode::PFMIN | - Opcode::PFCMPGT | - Opcode::PFMAX | - Opcode::PFCMPEQ | - Opcode::CMPS | - Opcode::SCAS | - Opcode::TEST | - Opcode::FTST | - Opcode::FXAM | - Opcode::FUCOM | - Opcode::FUCOMI | - Opcode::FUCOMIP | - Opcode::FUCOMP | - Opcode::FUCOMPP | - Opcode::FCOM | - Opcode::FCOMI | - Opcode::FCOMIP | - Opcode::FCOMP | - Opcode::FCOMPP | - Opcode::FICOM | - Opcode::FICOMP | - Opcode::CMPSD | - Opcode::CMPSS | - Opcode::CMP | - Opcode::CMPPS | - Opcode::CMPPD | - Opcode::CMPXCHG8B | - Opcode::CMPXCHG16B | - Opcode::CMPXCHG => { write!(out, "{}", colors.comparison_op(self)) } - - Opcode::WRMSR | - Opcode::RDMSR | - Opcode::RDTSC | - Opcode::RDPMC | - Opcode::RDPID | - Opcode::RDFSBASE | - Opcode::RDGSBASE | - Opcode::WRFSBASE | - Opcode::WRGSBASE | - Opcode::FXSAVE | - Opcode::FXRSTOR | - Opcode::LDMXCSR | - Opcode::STMXCSR | - Opcode::VLDMXCSR | - Opcode::VSTMXCSR | - Opcode::XSAVE | - Opcode::XSAVEC | - Opcode::XSAVES | - Opcode::XSAVEC64 | - Opcode::XSAVES64 | - Opcode::XRSTOR | - Opcode::XRSTORS | - Opcode::XRSTORS64 | - Opcode::XSAVEOPT | - Opcode::LFENCE | - Opcode::MFENCE | - Opcode::SFENCE | - Opcode::CLFLUSH | - Opcode::CLFLUSHOPT | - Opcode::CLWB | - Opcode::LDS | - Opcode::LES | - Opcode::SGDT | - Opcode::SIDT | - Opcode::LGDT | - Opcode::LIDT | - Opcode::SMSW | - Opcode::LMSW | - Opcode::SWAPGS | - Opcode::RDTSCP | - Opcode::INVEPT | - Opcode::INVVPID | - Opcode::INVPCID | - Opcode::INVLPG | - Opcode::INVLPGA | - Opcode::INVLPGB | - Opcode::TLBSYNC | - Opcode::PSMASH | - Opcode::PVALIDATE | - Opcode::RMPADJUST | - Opcode::RMPUPDATE | - Opcode::CPUID | - Opcode::WBINVD | - Opcode::INVD | - Opcode::SYSRET | - Opcode::CLTS | - Opcode::SYSCALL | - Opcode::TDCALL | - Opcode::SEAMRET | - Opcode::SEAMOPS | - Opcode::SEAMCALL | - Opcode::TPAUSE | - Opcode::UMONITOR | - Opcode::UMWAIT | - Opcode::LSL | - Opcode::SLDT | - Opcode::STR | - Opcode::LLDT | - Opcode::LTR | - Opcode::VERR | - Opcode::VERW | - Opcode::JMPE | - Opcode::EMMS | - Opcode::FEMMS | - Opcode::GETSEC | - Opcode::LFS | - Opcode::LGS | - Opcode::LSS | - Opcode::RSM | - Opcode::SYSENTER | - Opcode::SYSEXIT | - Opcode::VMREAD | - Opcode::VMWRITE | - Opcode::VMCLEAR | - Opcode::VMPTRLD | - Opcode::VMPTRST | - Opcode::VMXON | - Opcode::VMCALL | - Opcode::VMLAUNCH | - Opcode::VMRESUME | - Opcode::VMLOAD | - Opcode::VMMCALL | - Opcode::VMSAVE | - Opcode::VMRUN | - Opcode::VMXOFF | - Opcode::PCONFIG | - Opcode::MONITOR | - Opcode::MWAIT | - Opcode::MONITORX | - Opcode::MWAITX | - Opcode::SKINIT | - Opcode::CLGI | - Opcode::STGI | - Opcode::CLAC | - Opcode::STAC | - Opcode::ENCLS | - Opcode::ENCLV | - Opcode::XGETBV | - Opcode::XSETBV | - Opcode::VMFUNC | - Opcode::XEND | - Opcode::XTEST | - Opcode::XABORT | - Opcode::XBEGIN | - Opcode::ENCLU | - Opcode::RDPKRU | - Opcode::WRPKRU | - Opcode::RDPRU | - Opcode::CLZERO | - Opcode::ENQCMD | - Opcode::ENQCMDS | - Opcode::PTWRITE | - Opcode::UIRET | - Opcode::TESTUI | - Opcode::CLUI | - Opcode::STUI | - Opcode::SENDUIPI | - Opcode::XSUSLDTRK | - Opcode::XRESLDTRK | - Opcode::BOUND | - Opcode::ARPL | - Opcode::BNDMK | - Opcode::BNDCL | - Opcode::BNDCU | - Opcode::BNDCN | - Opcode::BNDLDX | - Opcode::BNDSTX | - Opcode::LAR => { write!(out, "{}", colors.platform_op(self)) } - - Opcode::CRC32 | - Opcode::RDSEED | - Opcode::RDRAND | - Opcode::SHA1RNDS4 | - Opcode::SHA1NEXTE | - Opcode::SHA1MSG1 | - Opcode::SHA1MSG2 | - Opcode::SHA256RNDS2 | - Opcode::SHA256MSG1 | - Opcode::SHA256MSG2 | - Opcode::FFREE | - Opcode::FFREEP | - Opcode::FDECSTP | - Opcode::FINCSTP | - Opcode::GF2P8MULB | - Opcode::GF2P8AFFINEQB | - Opcode::GF2P8AFFINEINVQB | - Opcode::AESDEC128KL | - Opcode::AESDEC256KL | - Opcode::AESDECWIDE128KL | - Opcode::AESDECWIDE256KL | - Opcode::AESENC128KL | - Opcode::AESENC256KL | - Opcode::AESENCWIDE128KL | - Opcode::AESENCWIDE256KL | - Opcode::ENCODEKEY128 | - Opcode::ENCODEKEY256 | - Opcode::LOADIWKEY | - Opcode::HRESET | - Opcode::WRUSS | - Opcode::WRSS | - Opcode::INCSSP | - Opcode::SAVEPREVSSP | - Opcode::SETSSBSY | - Opcode::CLRSSBSY | - Opcode::RSTORSSP | - Opcode::ENDBR64 | - Opcode::ENDBR32 | - Opcode::AESDEC | - Opcode::AESDECLAST | - Opcode::AESENC | - Opcode::AESENCLAST | - Opcode::AESIMC | - Opcode::AESKEYGENASSIST | - Opcode::VAESDEC | - Opcode::VAESDECLAST | - Opcode::VAESENC | - Opcode::VAESENCLAST | - Opcode::VAESIMC | - Opcode::VAESKEYGENASSIST => { write!(out, "{}", colors.misc_op(self)) } - - Opcode::UD0 | - Opcode::UD1 | - Opcode::UD2 | - Opcode::Invalid => { write!(out, "{}", colors.invalid_op(self)) } - } + fn colorize(&self, _colors: &Y, out: &mut T) -> fmt::Result { + // see `impl Colorize for long_mode::Opcode for more about this + out.write_str(self.name()) } } impl fmt::Display for Instruction { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + // to reuse one implementation, call the deprecated function for now. + #[allow(deprecated)] self.display_with(DisplayStyle::Intel).colorize(&NoColors, fmt) } } impl<'instr> fmt::Display for InstructionDisplayer<'instr> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + // to reuse one implementation, call the deprecated function for now. + #[allow(deprecated)] self.colorize(&NoColors, fmt) } } @@ -3347,6 +2166,8 @@ pub struct InstructionDisplayer<'instr> { * so write to some Write thing i guess. bite me. i really just want to * stop thinking about how to support printing instructions... */ +// allowing these deprecated items for the time being, not yet breaking yaxpeax-x86 apis +#[allow(deprecated)] impl <'instr, T: fmt::Write, Y: YaxColors> Colorize 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, @@ -3359,160 +2180,156 @@ impl <'instr, T: fmt::Write, Y: YaxColors> Colorize for InstructionDisplay struct NoContext; impl Instruction { + /// format this instruction into `out` as a plain text string. + #[cfg_attr(feature="profiling", inline(never))] pub fn write_to(&self, out: &mut T) -> fmt::Result { - self.display_with(DisplayStyle::Intel).contextualize(&NoColors, 0, Some(&NoContext), out) + let mut out = yaxpeax_arch::display::FmtSink::new(out); + contextualize_intel(self, &mut out) + } + + /// format this instruction into `out`, which may perform additional styling based on its + /// `DisplaySink` implementation. + #[cfg_attr(feature="profiling", inline(never))] + pub fn display_into(&self, out: &mut T) -> fmt::Result { + contextualize_intel(self, out) } } -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 [Opcode::MOVS, Opcode::CMPS, Opcode::LODS, Opcode::STOS, Opcode::INS, Opcode::OUTS].contains(&instr.opcode) { + 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 - 1])?; - 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 - 1])?; - 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(", ")?; + // hint that accessing `inster.operands[i]` can't panic: this is useful for + // `instr.operands` and the segment selector check after. + if i >= 4 { + // Safety: Instruction::operands is a four-element array; operand_count is always + // low enough that 0..operand_count is a valid index. + unsafe { unreachable_unchecked(); } + } + + if instr.operands[i as usize].is_memory() { + out.write_mem_size_label(instr.mem_size)?; + 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('}')?; } } } @@ -3520,7 +2337,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; @@ -3544,7 +2361,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", @@ -3566,21 +2383,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))) + Operand::ImmediateI8 { imm: rel } => { + 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))) + Operand::ImmediateI32 { imm: 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) @@ -3743,87 +2568,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")?; @@ -3854,24 +2679,30 @@ fn contextualize_c(instr: &Instruction, colors: &Y, Ok(()) } +// allowing these deprecated items for the time being, not yet breaking yaxpeax-x86 apis +#[allow(deprecated)] 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) } } } } +// allowing these deprecated items for the time being, not yet breaking yaxpeax-x86 apis +#[allow(deprecated)] #[cfg(feature="std")] impl ShowContextual], T, Y> for Instruction { fn contextualize(&self, colors: &Y, _address: u64, context: Option<&[Option]>, out: &mut T) -> fmt::Result { @@ -3894,7 +2725,7 @@ impl ShowContextual { write!(out, " {}", s)?; }, None => { match self.operands[0] { - OperandSpec::Nothing => { + super::OperandSpec::Nothing => { return Ok(()); }, _ => { @@ -3914,7 +2745,7 @@ impl ShowContextual { write!(out, ", {}", s)? } None => { match &self.operands[i] { - &OperandSpec::Nothing => { + &super::OperandSpec::Nothing => { return Ok(()); }, _ => { @@ -3932,3 +2763,245 @@ 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, _base: RegSpec) -> Result { + Ok(false) + } + fn visit_disp(&mut self, _base: 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('$')?; + let mut v = rel as u8; + if rel < 0 { + self.out.write_char('-')?; + v = rel.unsigned_abs(); + } else { + self.out.write_char('+')?; + } + 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('$')?; + let mut v = rel as u32; + if rel < 0 { + self.out.write_char('-')?; + v = rel.unsigned_abs(); + } else { + self.out.write_char('+')?; + } + 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_index_scale(&mut self, _index: RegSpec, _scale: u8) -> Result { + Ok(false) + } + fn visit_base_index_scale(&mut self, _base: RegSpec, _index: RegSpec, _scale: u8) -> Result { + Ok(false) + } + fn visit_index_scale_disp(&mut self, _index: RegSpec, _scale: u8, _disp: i32) -> Result { + Ok(false) + } + fn visit_base_index_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_disp_masked(&mut self, _base: RegSpec, _disp: i32, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_deref_masked(&mut self, _base: RegSpec, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_index_scale_masked(&mut self, _index: RegSpec, _scale: u8, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_index_scale_disp_masked(&mut self, _index: RegSpec, _scale: u8, _disp: i32, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_base_index_masked(&mut self, _base: RegSpec, _index: RegSpec, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_base_index_disp_masked(&mut self, _base: RegSpec, _index: RegSpec, _disp: i32, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_base_index_scale_masked(&mut self, _base: RegSpec, _index: RegSpec, _scale: u8, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_base_index_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) + } +} + +#[cfg(feature="alloc")] +mod buffer_sink { + use core::fmt; + use super::super::{DisplayStyle, InstructionDisplayer}; + use super::{contextualize_c, contextualize_intel}; + + /// helper to format `amd64` instructions with highest throughput and least configuration. this is + /// functionally a buffer for one x86 instruction's text. + /// + /// ### when to use this over `fmt::Display`? + /// + /// `fmt::Display` is a fair choice in most cases. in some cases, `InstructionTextBuffer` may + /// support formatting options that may be difficult to configure for a `Display` impl. + /// additionally, `InstructionTextBuffer` may be able to specialize more effectively where + /// `fmt::Display`, writing to a generic `fmt::Write`, may not. + /// + /// if your use case for `yaxpeax-x86` involves being bounded on the speed of disassembling and + /// formatting instructions, [`InstructionTextBuffer::format_inst`] has been measured as up to 11% + /// faster than an equivalent `write!(buf, "{}", inst)`. + /// + /// `InstructionTextBuffer` involves internal allocations; if your use case for `yaxpeax-x86` + /// requires allocations never occurring, it is not an appropriate tool. + /// + /// ### example + /// + /// ``` + /// use yaxpeax_x86::long_mode::InstDecoder; + /// use yaxpeax_x86::long_mode::InstructionTextBuffer; + /// use yaxpeax_x86::long_mode::DisplayStyle; + /// + /// let bytes = &[0x33, 0xc0]; + /// let inst = InstDecoder::default().decode_slice(bytes).expect("can decode"); + /// let mut text_buf = InstructionTextBuffer::new(); + /// assert_eq!( + /// text_buf.format_inst(&inst.display_with(DisplayStyle::Intel)).expect("can format"), + /// "xor eax, eax" + /// ); + /// + /// // or, getting the formatted instruction with `text_str`: + /// assert_eq!( + /// text_buf.text_str(), + /// "xor eax, eax" + /// ); + /// ``` + pub struct InstructionTextBuffer { + content: alloc::string::String, + } + + impl InstructionTextBuffer { + /// create an `InstructionTextBuffer` with default settings. `InstructionTextBuffer`'s default + /// settings format instructions identically to their corresponding `fmt::Display`. + pub fn new() -> Self { + let mut buf = alloc::string::String::new(); + buf.reserve(crate::MAX_INSTRUCTION_LEN); + Self { + content: buf, + } + } + + /// format `inst` into this buffer. returns a borrow of that same internal buffer for convenience. + /// + /// this clears and reuses an internal buffer; if an instruction had been previously formatted + /// through this buffer, it will be overwritten. + pub fn format_inst<'buf, 'instr>(&'buf mut self, display: &InstructionDisplayer<'instr>) -> Result<&'buf str, fmt::Error> { + // Safety: this sink is used to format exactly one instruction and then dropped. it can + // never escape `format_inst`. + let mut handle = unsafe { self.write_handle() }; + + match display.style { + DisplayStyle::Intel => { + contextualize_intel(&display.instr, &mut handle)?; + } + DisplayStyle::C => { + contextualize_c(&display.instr, &mut handle)?; + } + } + + Ok(self.text_str()) + } + + /// return a borrow of the internal buffer. if an instruction has been formatted, the + /// returned `&str` contains that instruction's buffered text. + pub fn text_str(&self) -> &str { + self.content.as_str() + } + + /// do the necessary bookkeeping and provide an `InstructionTextSink` to write an instruction + /// into. + /// + /// SAFETY: callers must print at most one instruction into this handle. + unsafe fn write_handle(&mut self) -> yaxpeax_arch::display::InstructionTextSink { + self.content.clear(); + // Safety: `content` was just cleared, so writing begins at the start of the buffer. + // `content`is large enough to hold a fully-formatted instruction (see + // `InstructionTextBuffer::new`). + yaxpeax_arch::display::InstructionTextSink::new(&mut self.content) + } + } +} +#[cfg(feature="alloc")] +pub use buffer_sink::InstructionTextBuffer; diff --git a/src/protected_mode/mod.rs b/src/protected_mode/mod.rs index 0d125ebe..081be20d 100644 --- a/src/protected_mode/mod.rs +++ b/src/protected_mode/mod.rs @@ -8,13 +8,15 @@ pub use crate::MemoryAccessSize; #[cfg(feature = "fmt")] pub use self::display::{DisplayStyle, InstructionDisplayer}; +#[cfg(all(feature = "fmt", feature = "alloc"))] +pub use self::display::InstructionTextBuffer; use core::cmp::PartialEq; -use crate::safer_unchecked::unreachable_kinda_unchecked as unreachable_unchecked; use yaxpeax_arch::{AddressDiff, Decoder, Reader, LengthedInstruction}; use yaxpeax_arch::annotation::{AnnotatingDecoder, DescriptionSink, NullSink}; use yaxpeax_arch::{DecodeError as ArchDecodeError}; +use yaxpeax_arch::safer_unchecked::unreachable_kinda_unchecked as unreachable_unchecked; use core::fmt; impl fmt::Display for DecodeError { @@ -304,87 +306,77 @@ enum SizeCode { #[non_exhaustive] pub enum Operand { /// a sign-extended byte - ImmediateI8(i8), + ImmediateI8 { imm: i8 }, /// a zero-extended byte - ImmediateU8(u8), + ImmediateU8 { imm: u8 }, /// a sign-extended word - ImmediateI16(i16), + ImmediateI16 { imm: i16 }, /// a zero-extended word - ImmediateU16(u16), + ImmediateU16 { imm: u16 }, /// a sign-extended dword - ImmediateI32(i32), + ImmediateI32 { imm: i32 }, /// a zero-extended dword - ImmediateU32(u32), + ImmediateU32 { imm: u32 }, /// a bare register operand, such as `rcx`. - Register(RegSpec), + Register { reg: RegSpec }, /// an `avx512` register operand with optional mask register and merge mode, such as /// `zmm3{k4}{z}`. /// /// if the mask register is `k0`, there is no masking applied, and the default x86 operation is /// `MergeMode::Merge`. - RegisterMaskMerge(RegSpec, RegSpec, MergeMode), + RegisterMaskMerge { reg: RegSpec, mask: RegSpec, merge: MergeMode }, /// an `avx512` register operand with optional mask register, merge mode, and suppressed /// exceptions, such as `zmm3{k4}{z}{rd-sae}`. /// /// if the mask register is `k0`, there is no masking applied, and the default x86 operation is /// `MergeMode::Merge`. - RegisterMaskMergeSae(RegSpec, RegSpec, MergeMode, SaeMode), + RegisterMaskMergeSae { reg: RegSpec, mask: RegSpec, merge: MergeMode, sae: SaeMode }, /// an `avx512` register operand with optional mask register, merge mode, and suppressed /// exceptions, with no overridden rounding mode, such as `zmm3{k4}{z}{sae}`. /// /// if the mask register is `k0`, there is no masking applied, and the default x86 operation is /// `MergeMode::Merge`. - RegisterMaskMergeSaeNoround(RegSpec, RegSpec, MergeMode), + RegisterMaskMergeSaeNoround { reg: RegSpec, mask: RegSpec, merge: MergeMode }, /// a memory access to a literal word address. it's extremely rare that a well-formed x86 /// instruction uses this mode. as an example, `[0x1133]` - DisplacementU16(u16), + AbsoluteU16 { addr: u16 }, /// a memory access to a literal qword address. it's relatively rare that a well-formed x86 /// instruction uses this mode, but plausible. for example, `fs:[0x14]`. segment overrides, /// however, are maintained on the instruction itself. - DisplacementU32(u32), + AbsoluteU32 { addr: u32 }, /// a simple dereference of the address held in some register. for example: `[esi]`. - RegDeref(RegSpec), + MemDeref { base: RegSpec }, /// a dereference of the address held in some register with offset. for example: `[esi + 0x14]`. - RegDisp(RegSpec, i32), + Disp { base: RegSpec, disp: i32 }, /// a dereference of the address held in some register scaled by 1, 2, 4, or 8. this is almost always used with the `lea` instruction. for example: `[edx * 4]`. - RegScale(RegSpec, u8), - /// a dereference of the address from summing two registers. for example: `[ebp + rax]` - RegIndexBase(RegSpec, RegSpec), - /// a dereference of the address from summing two registers with offset. for example: `[edi + ecx + 0x40]` - RegIndexBaseDisp(RegSpec, RegSpec, i32), + MemIndexScale { index: RegSpec, scale: u8 }, /// a dereference of the address held in some register scaled by 1, 2, 4, or 8 with offset. this is almost always used with the `lea` instruction. for example: `[eax * 4 + 0x30]`. - RegScaleDisp(RegSpec, u8, i32), + MemIndexScaleDisp { index: RegSpec, scale: u8, disp: i32 }, /// a dereference of the address from summing a register and index register scaled by 1, 2, 4, /// or 8. for /// example: `[esi + ecx * 4]` - RegIndexBaseScale(RegSpec, RegSpec, u8), + MemBaseIndexScale { base: RegSpec, index: RegSpec, scale: u8 }, /// a dereference of the address from summing a register and index register scaled by 1, 2, 4, /// or 8, with offset. for /// example: `[esi + ecx * 4 + 0x1234]` - RegIndexBaseScaleDisp(RegSpec, RegSpec, u8, i32), + MemBaseIndexScaleDisp { base: RegSpec, index: RegSpec, scale: u8, disp: i32 }, /// an `avx512` dereference of register with optional masking. for example: `[edx]{k3}` - RegDerefMasked(RegSpec, RegSpec), + MemDerefMasked { base: RegSpec, mask: RegSpec }, /// an `avx512` dereference of register plus offset, with optional masking. for example: `[esp + 0x40]{k3}` - RegDispMasked(RegSpec, i32, RegSpec), + DispMasked { base: RegSpec, disp: i32, mask: RegSpec }, /// an `avx512` dereference of a register scaled by 1, 2, 4, or 8, with optional masking. this /// seems extraordinarily unlikely to occur in practice. for example: `[esi * 4]{k2}` - RegScaleMasked(RegSpec, u8, RegSpec), - /// an `avx512` dereference of a register plus index scaled by 1, 2, 4, or 8, with optional masking. - /// for example: `[esi + eax * 4]{k6}` - RegIndexBaseMasked(RegSpec, RegSpec, RegSpec), - /// an `avx512` dereference of a register plus offset, with optional masking. for example: - /// `[esi + eax + 0x1313]{k6}` - RegIndexBaseDispMasked(RegSpec, RegSpec, i32, RegSpec), + MemIndexScaleMasked { index: RegSpec, scale: u8, mask: RegSpec }, /// an `avx512` dereference of a register scaled by 1, 2, 4, or 8 plus offset, with optional /// masking. this seems extraordinarily unlikely to occur in practice. for example: `[esi * /// 4 + 0x1357]{k2}` - RegScaleDispMasked(RegSpec, u8, i32, RegSpec), + MemIndexScaleDispMasked { index: RegSpec, scale: u8, disp: i32, mask: RegSpec }, /// an `avx512` dereference of a register plus index scaled by 1, 2, 4, or 8, with optional /// masking. for example: `[esi + eax * 4]{k6}` - RegIndexBaseScaleMasked(RegSpec, RegSpec, u8, RegSpec), + MemBaseIndexScaleMasked { base: RegSpec, index: RegSpec, scale: u8, mask: RegSpec }, /// an `avx512` dereference of a register plus index scaled by 1, 2, 4, or 8 and offset, with /// optional masking. for example: `[esi + eax * 4 + 0x1313]{k6}` - RegIndexBaseScaleDispMasked(RegSpec, RegSpec, u8, i32, RegSpec), + MemBaseIndexScaleDispMasked { base: RegSpec, index: RegSpec, scale: u8, disp: i32, mask: RegSpec }, /// no operand. it is a bug for `yaxpeax-x86` to construct an `Operand` of this kind for public /// use; the instruction's `operand_count` should be reduced so as to make this invisible to /// library clients. @@ -401,11 +393,11 @@ impl OperandSpec { OperandSpec::RegMMM => OperandSpec::RegMMM_maskmerge, OperandSpec::RegVex => OperandSpec::RegVex_maskmerge, OperandSpec::Deref => OperandSpec::Deref_mask, - OperandSpec::RegDisp => OperandSpec::RegDisp_mask, - OperandSpec::RegScale => OperandSpec::RegScale_mask, - OperandSpec::RegScaleDisp => OperandSpec::RegScaleDisp_mask, - OperandSpec::RegIndexBaseScale => OperandSpec::RegIndexBaseScale_mask, - OperandSpec::RegIndexBaseScaleDisp => OperandSpec::RegIndexBaseScaleDisp_mask, + OperandSpec::Disp => OperandSpec::Disp_mask, + OperandSpec::MemIndexScale => OperandSpec::MemIndexScale_mask, + OperandSpec::MemIndexScaleDisp => OperandSpec::MemIndexScaleDisp_mask, + OperandSpec::MemBaseIndexScale => OperandSpec::MemBaseIndexScale_mask, + OperandSpec::MemBaseIndexScaleDisp => OperandSpec::MemBaseIndexScaleDisp_mask, o => o, } } @@ -450,14 +442,14 @@ impl SaeMode { /// a human-friendly label for this `SaeMode`: /// /// ``` - /// use yaxpeax_x86::long_mode::SaeMode; + /// use yaxpeax_x86::protected_mode::SaeMode; /// /// assert_eq!(SaeMode::RoundNearest.label(), "{rne-sae}"); /// assert_eq!(SaeMode::RoundDown.label(), "{rd-sae}"); /// assert_eq!(SaeMode::RoundUp.label(), "{ru-sae}"); /// assert_eq!(SaeMode::RoundZero.label(), "{rz-sae}"); /// ``` - pub fn label(&self) -> &'static str { + pub const fn label(&self) -> &'static str { match self { SaeMode::RoundNearest => "{rne-sae}", SaeMode::RoundDown => "{rd-sae}", @@ -477,6 +469,42 @@ impl SaeMode { SAE_MODES[idx] } } + +pub trait OperandVisitor { + type Ok; + type Error; + + fn visit_reg(&mut self, reg: RegSpec) -> Result; + fn visit_deref(&mut self, reg: RegSpec) -> Result; + fn visit_disp(&mut self, reg: RegSpec, disp: i32) -> Result; + fn visit_index_scale(&mut self, index: RegSpec, scale: u8) -> Result; + fn visit_base_index_scale(&mut self, base: RegSpec, index: RegSpec, scale: u8) -> Result; + fn visit_base_index_scale_disp(&mut self, base: RegSpec, index: RegSpec, scale: u8, disp: i32) -> Result; + fn visit_index_scale_disp(&mut self, index: RegSpec, scale: u8, disp: i32) -> Result; + fn visit_i8(&mut self, imm: i8) -> Result; + fn visit_u8(&mut self, imm: u8) -> Result; + fn visit_i16(&mut self, imm: i16) -> Result; + fn visit_u16(&mut self, imm: u16) -> Result; + fn visit_i32(&mut self, imm: i32) -> Result; + fn visit_u32(&mut self, imm: u32) -> Result; + fn visit_abs_u16(&mut self, imm: u16) -> Result; + fn visit_abs_u32(&mut self, imm: u32) -> Result; + fn visit_reg_mask_merge(&mut self, base: RegSpec, mask: RegSpec, merge_mode: MergeMode) -> Result; + fn visit_reg_mask_merge_sae(&mut self, base: RegSpec, mask: RegSpec, merge_mode: MergeMode, sae_mode: SaeMode) -> Result; + fn visit_reg_mask_merge_sae_noround(&mut self, base: RegSpec, mask: RegSpec, merge_mode: MergeMode) -> Result; + fn visit_disp_masked(&mut self, base: RegSpec, disp: i32, mask_reg: RegSpec) -> Result; + fn visit_deref_masked(&mut self, base: RegSpec, mask_reg: RegSpec) -> Result; + fn visit_index_scale_masked(&mut self, index: RegSpec, scale: u8, mask_reg: RegSpec) -> Result; + fn visit_index_scale_disp_masked(&mut self, index: RegSpec, scale: u8, disp: i32, mask_reg: RegSpec) -> Result; + fn visit_base_index_masked(&mut self, base: RegSpec, index: RegSpec, mask_reg: RegSpec) -> Result; + fn visit_base_index_disp_masked(&mut self, base: RegSpec, index: RegSpec, disp: i32, mask_reg: RegSpec) -> Result; + fn visit_base_index_scale_masked(&mut self, base: RegSpec, index: RegSpec, scale: u8, mask_reg: RegSpec) -> Result; + fn visit_base_index_scale_disp_masked(&mut self, base: RegSpec, index: RegSpec, scale: u8, disp: i32, mask_reg: RegSpec) -> Result; + fn visit_absolute_far_address(&mut self, segment: u16, address: u32) -> Result; + + fn visit_other(&mut self) -> Result; +} + impl Operand { fn from_spec(inst: &Instruction, spec: OperandSpec) -> Operand { match spec { @@ -485,139 +513,139 @@ impl Operand { } // the register in regs[0] OperandSpec::RegRRR => { - Operand::Register(inst.regs[0]) + Operand::Register { reg: inst.regs[0] } } OperandSpec::RegRRR_maskmerge => { - Operand::RegisterMaskMerge( - inst.regs[0], - RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), - MergeMode::from(inst.prefixes.evex_unchecked().merge()), - ) + Operand::RegisterMaskMerge { + reg: inst.regs[0], + mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), + merge: MergeMode::from(inst.prefixes.evex_unchecked().merge()), + } } OperandSpec::RegRRR_maskmerge_sae => { - Operand::RegisterMaskMergeSae( - inst.regs[0], - RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), - MergeMode::from(inst.prefixes.evex_unchecked().merge()), - SaeMode::from(inst.prefixes.evex_unchecked().vex().l(), inst.prefixes.evex_unchecked().lp()), - ) + Operand::RegisterMaskMergeSae { + reg: inst.regs[0], + mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), + merge: MergeMode::from(inst.prefixes.evex_unchecked().merge()), + sae: SaeMode::from(inst.prefixes.evex_unchecked().vex().l(), inst.prefixes.evex_unchecked().lp()), + } } OperandSpec::RegRRR_maskmerge_sae_noround => { - Operand::RegisterMaskMergeSaeNoround( - inst.regs[0], - RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), - MergeMode::from(inst.prefixes.evex_unchecked().merge()), - ) + Operand::RegisterMaskMergeSaeNoround { + reg: inst.regs[0], + mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), + merge: MergeMode::from(inst.prefixes.evex_unchecked().merge()), + } } // the register in regs[1] (eg modrm mod bits were 11) OperandSpec::RegMMM => { - Operand::Register(inst.regs[1]) + Operand::Register { reg: inst.regs[1] } } OperandSpec::RegMMM_maskmerge => { - Operand::RegisterMaskMerge( - inst.regs[1], - RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), - MergeMode::from(inst.prefixes.evex_unchecked().merge()), - ) + Operand::RegisterMaskMerge { + reg: inst.regs[1], + mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), + merge: MergeMode::from(inst.prefixes.evex_unchecked().merge()), + } } OperandSpec::RegMMM_maskmerge_sae_noround => { - Operand::RegisterMaskMergeSaeNoround( - inst.regs[1], - RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), - MergeMode::from(inst.prefixes.evex_unchecked().merge()), - ) + Operand::RegisterMaskMergeSaeNoround { + reg: inst.regs[1], + mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), + merge: MergeMode::from(inst.prefixes.evex_unchecked().merge()), + } } OperandSpec::RegVex => { - Operand::Register(inst.regs[3]) + Operand::Register { reg: inst.regs[3] } } OperandSpec::RegVex_maskmerge => { - Operand::RegisterMaskMerge( - inst.regs[3], - RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), - MergeMode::from(inst.prefixes.evex_unchecked().merge()), - ) + Operand::RegisterMaskMerge { + reg: inst.regs[3], + mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), + merge: MergeMode::from(inst.prefixes.evex_unchecked().merge()), + } } OperandSpec::Reg4 => { - Operand::Register(RegSpec { num: inst.imm as u8, bank: inst.regs[3].bank }) - } - OperandSpec::ImmI8 => Operand::ImmediateI8(inst.imm as i8), - OperandSpec::ImmU8 => Operand::ImmediateU8(inst.imm as u8), - OperandSpec::ImmI16 => Operand::ImmediateI16(inst.imm as i16), - OperandSpec::ImmU16 => Operand::ImmediateU16(inst.imm as u16), - OperandSpec::ImmI32 => Operand::ImmediateI32(inst.imm as i32), - OperandSpec::ImmInDispField => Operand::ImmediateU16(inst.disp as u16), - OperandSpec::DispU16 => Operand::DisplacementU16(inst.disp as u16), - OperandSpec::DispU32 => Operand::DisplacementU32(inst.disp), + Operand::Register { reg: RegSpec { num: inst.imm as u8, bank: inst.regs[3].bank }} + } + OperandSpec::ImmI8 => Operand::ImmediateI8 { imm: inst.imm as i8 }, + OperandSpec::ImmU8 => Operand::ImmediateU8 { imm: inst.imm as u8 }, + OperandSpec::ImmI16 => Operand::ImmediateI16 { imm: inst.imm as i16 }, + OperandSpec::ImmU16 => Operand::ImmediateU16 { imm: inst.imm as u16 }, + OperandSpec::ImmI32 => Operand::ImmediateI32 { imm: inst.imm as i32 }, + OperandSpec::ImmInDispField => Operand::ImmediateU16 { imm: inst.disp as u16 }, + OperandSpec::DispU16 => Operand::AbsoluteU16 { addr: inst.disp as u16 }, + OperandSpec::DispU32 => Operand::AbsoluteU32 { addr: inst.disp }, OperandSpec::Deref => { - Operand::RegDeref(inst.regs[1]) + Operand::MemDeref { base: inst.regs[1] } } OperandSpec::Deref_si => { - Operand::RegDeref(RegSpec::si()) + Operand::MemDeref { base: RegSpec::si() } } OperandSpec::Deref_di => { - Operand::RegDeref(RegSpec::di()) + Operand::MemDeref { base: RegSpec::di() } } OperandSpec::Deref_esi => { - Operand::RegDeref(RegSpec::esi()) + Operand::MemDeref { base: RegSpec::esi() } } OperandSpec::Deref_edi => { - Operand::RegDeref(RegSpec::edi()) + Operand::MemDeref { base: RegSpec::edi() } } - OperandSpec::RegDisp => { - Operand::RegDisp(inst.regs[1], inst.disp as i32) + OperandSpec::Disp => { + Operand::Disp { base: inst.regs[1], disp: inst.disp as i32 } } - OperandSpec::RegScale => { - Operand::RegScale(inst.regs[2], inst.scale) + OperandSpec::MemIndexScale => { + Operand::MemIndexScale { index: inst.regs[2], scale: inst.scale } } - OperandSpec::RegScaleDisp => { - Operand::RegScaleDisp(inst.regs[2], inst.scale, inst.disp as i32) + OperandSpec::MemIndexScaleDisp => { + Operand::MemIndexScaleDisp { index: inst.regs[2], scale: inst.scale, disp: inst.disp as i32 } } - OperandSpec::RegIndexBaseScale => { - Operand::RegIndexBaseScale(inst.regs[1], inst.regs[2], inst.scale) + OperandSpec::MemBaseIndexScale => { + Operand::MemBaseIndexScale { base: inst.regs[1], index: inst.regs[2], scale: inst.scale } } - OperandSpec::RegIndexBaseScaleDisp => { - Operand::RegIndexBaseScaleDisp(inst.regs[1], inst.regs[2], inst.scale, inst.disp as i32) + OperandSpec::MemBaseIndexScaleDisp => { + Operand::MemBaseIndexScaleDisp { base: inst.regs[1], index: inst.regs[2], scale: inst.scale, disp: inst.disp as i32 } } OperandSpec::Deref_mask => { if inst.prefixes.evex_unchecked().mask_reg() != 0 { - Operand::RegDerefMasked(inst.regs[1], RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg())) + Operand::MemDerefMasked { base: inst.regs[1], mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()) } } else { - Operand::RegDeref(inst.regs[1]) + Operand::MemDeref { base: inst.regs[1] } } } - OperandSpec::RegDisp_mask => { + OperandSpec::Disp_mask => { if inst.prefixes.evex_unchecked().mask_reg() != 0 { - Operand::RegDispMasked(inst.regs[1], inst.disp as i32, RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg())) + Operand::DispMasked { base: inst.regs[1], disp: inst.disp as i32, mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()) } } else { - Operand::RegDisp(inst.regs[1], inst.disp as i32) + Operand::Disp { base: inst.regs[1], disp: inst.disp as i32 } } } - OperandSpec::RegScale_mask => { + OperandSpec::MemIndexScale_mask => { if inst.prefixes.evex_unchecked().mask_reg() != 0 { - Operand::RegScaleMasked(inst.regs[2], inst.scale, RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg())) + Operand::MemIndexScaleMasked { index: inst.regs[2], scale: inst.scale, mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()) } } else { - Operand::RegScale(inst.regs[2], inst.scale) + Operand::MemIndexScale { index: inst.regs[2], scale: inst.scale } } } - OperandSpec::RegScaleDisp_mask => { + OperandSpec::MemIndexScaleDisp_mask => { if inst.prefixes.evex_unchecked().mask_reg() != 0 { - Operand::RegScaleDispMasked(inst.regs[2], inst.scale, inst.disp as i32, RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg())) + Operand::MemIndexScaleDispMasked { index: inst.regs[2], scale: inst.scale, disp: inst.disp as i32, mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()) } } else { - Operand::RegScaleDisp(inst.regs[2], inst.scale, inst.disp as i32) + Operand::MemIndexScaleDisp { index: inst.regs[2], scale: inst.scale, disp: inst.disp as i32 } } } - OperandSpec::RegIndexBaseScale_mask => { + OperandSpec::MemBaseIndexScale_mask => { if inst.prefixes.evex_unchecked().mask_reg() != 0 { - Operand::RegIndexBaseScaleMasked(inst.regs[1], inst.regs[2], inst.scale, RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg())) + Operand::MemBaseIndexScaleMasked { base: inst.regs[1], index: inst.regs[2], scale: inst.scale, mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()) } } else { - Operand::RegIndexBaseScale(inst.regs[1], inst.regs[2], inst.scale) + Operand::MemBaseIndexScale { base: inst.regs[1], index: inst.regs[2], scale: inst.scale } } } - OperandSpec::RegIndexBaseScaleDisp_mask => { + OperandSpec::MemBaseIndexScaleDisp_mask => { if inst.prefixes.evex_unchecked().mask_reg() != 0 { - Operand::RegIndexBaseScaleDispMasked(inst.regs[1], inst.regs[2], inst.scale, inst.disp as i32, RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg())) + Operand::MemBaseIndexScaleDispMasked { base: inst.regs[1], index: inst.regs[2], scale: inst.scale, disp: inst.disp as i32, mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()) } } else { - Operand::RegIndexBaseScaleDisp(inst.regs[1], inst.regs[2], inst.scale, inst.disp as i32) + Operand::MemBaseIndexScaleDisp { base: inst.regs[1], index: inst.regs[2], scale: inst.scale, disp: inst.disp as i32 } } } OperandSpec::AbsoluteFarAddress => { @@ -634,36 +662,32 @@ impl Operand { /// memory. pub fn is_memory(&self) -> bool { match self { - Operand::DisplacementU16(_) | - Operand::DisplacementU32(_) | - Operand::RegDeref(_) | - Operand::RegDisp(_, _) | - Operand::RegScale(_, _) | - Operand::RegIndexBase(_, _) | - Operand::RegIndexBaseDisp(_, _, _) | - Operand::RegScaleDisp(_, _, _) | - Operand::RegIndexBaseScale(_, _, _) | - Operand::RegIndexBaseScaleDisp(_, _, _, _) | - Operand::RegDerefMasked(_, _) | - Operand::RegDispMasked(_, _, _) | - Operand::RegScaleMasked(_, _, _) | - Operand::RegIndexBaseMasked(_, _, _) | - Operand::RegIndexBaseDispMasked(_, _, _, _) | - Operand::RegScaleDispMasked(_, _, _, _) | - Operand::RegIndexBaseScaleMasked(_, _, _, _) | - Operand::RegIndexBaseScaleDispMasked(_, _, _, _, _) => { + Operand::AbsoluteU16 { .. } | + Operand::AbsoluteU32 { .. } | + Operand::MemDeref { .. } | + Operand::Disp { .. } | + Operand::MemIndexScale { .. } | + Operand::MemIndexScaleDisp { .. } | + Operand::MemBaseIndexScale { .. } | + Operand::MemBaseIndexScaleDisp { .. } | + Operand::MemDerefMasked { .. } | + Operand::DispMasked { .. } | + Operand::MemIndexScaleMasked { .. } | + Operand::MemIndexScaleDispMasked { .. } | + Operand::MemBaseIndexScaleMasked { .. } | + Operand::MemBaseIndexScaleDispMasked { .. } => { true }, - Operand::ImmediateI8(_) | - Operand::ImmediateU8(_) | - Operand::ImmediateI16(_) | - Operand::ImmediateU16(_) | - Operand::ImmediateU32(_) | - Operand::ImmediateI32(_) | - Operand::Register(_) | - Operand::RegisterMaskMerge(_, _, _) | - Operand::RegisterMaskMergeSae(_, _, _, _) | - Operand::RegisterMaskMergeSaeNoround(_, _, _) | + Operand::ImmediateI8 { .. } | + Operand::ImmediateU8 { .. } | + Operand::ImmediateI16 { .. } | + Operand::ImmediateU16 { .. } | + Operand::ImmediateU32 { .. } | + Operand::ImmediateI32 { .. } | + Operand::Register { .. } | + Operand::RegisterMaskMerge { .. } | + Operand::RegisterMaskMergeSae { .. } | + Operand::RegisterMaskMergeSaeNoround { .. } | Operand::AbsoluteFarAddress { .. } | Operand::Nothing => { false @@ -676,22 +700,22 @@ impl Operand { /// `Operand` came from; `None` here means the authoritative width is `instr.mem_size()`. pub fn width(&self) -> Option { match self { - Operand::Register(reg) => { + Operand::Register { reg } => { Some(reg.width()) } - Operand::RegisterMaskMerge(reg, _, _) => { + Operand::RegisterMaskMerge { reg, .. } => { Some(reg.width()) } - Operand::ImmediateI8(_) | - Operand::ImmediateU8(_) => { + Operand::ImmediateI8 { .. } | + Operand::ImmediateU8 { .. } => { Some(1) } - Operand::ImmediateI16(_) | - Operand::ImmediateU16(_) => { + Operand::ImmediateI16 { .. } | + Operand::ImmediateU16 { .. } => { Some(2) } - Operand::ImmediateI32(_) | - Operand::ImmediateU32(_) => { + Operand::ImmediateI32 { .. } | + Operand::ImmediateU32 { .. } => { Some(4) } // memory operands or `Nothing` @@ -700,6 +724,48 @@ impl Operand { } } } + + /// provided for parity with [`Instruction::visit_operand`]. this has little utility other than + /// to reuse an `OperandVisitor` on an `Operand` directly. + #[allow(dead_code)] // in some configurations this is unused, but it is internal-only for now, so it would warn. + fn visit(&self, visitor: &mut T) -> Result { + match self { + Operand::Nothing => { + visitor.visit_other() + } + Operand::Register { reg } => { + visitor.visit_reg(*reg) + } + Operand::MemDeref { base } => { + visitor.visit_deref(*base) + } + Operand::Disp { base, disp } => { + visitor.visit_disp(*base, *disp) + } + Operand::ImmediateI8 { imm } => visitor.visit_i8(*imm), + Operand::ImmediateU8 { imm } => visitor.visit_u8(*imm), + Operand::ImmediateI16 { imm } => visitor.visit_i16(*imm), + Operand::ImmediateU16 { imm } => visitor.visit_u16(*imm), + Operand::ImmediateI32 { imm } => visitor.visit_i32(*imm), + Operand::ImmediateU32 { imm } => visitor.visit_u32(*imm), + Operand::AbsoluteU16 { addr } => visitor.visit_abs_u16(*addr), + Operand::AbsoluteU32 { addr } => visitor.visit_abs_u32(*addr), + Operand::MemIndexScale { index, scale } => visitor.visit_index_scale(*index, *scale), + Operand::MemIndexScaleDisp { index, scale, disp } => visitor.visit_index_scale_disp(*index, *scale, *disp), + Operand::MemBaseIndexScale { base, index, scale } => visitor.visit_base_index_scale(*base, *index, *scale), + Operand::MemBaseIndexScaleDisp { base, index, scale, disp } => visitor.visit_base_index_scale_disp(*base, *index, *scale, *disp), + Operand::RegisterMaskMerge { reg, mask, merge } => visitor.visit_reg_mask_merge(*reg, *mask, *merge), + Operand::RegisterMaskMergeSae { reg, mask, merge, sae } => visitor.visit_reg_mask_merge_sae(*reg, *mask, *merge, *sae), + Operand::RegisterMaskMergeSaeNoround { reg, mask, merge } => visitor.visit_reg_mask_merge_sae_noround(*reg, *mask, *merge), + Operand::MemDerefMasked { base, mask } => visitor.visit_deref_masked(*base, *mask), + Operand::DispMasked { base, disp, mask } => visitor.visit_disp_masked(*base, *disp, *mask), + Operand::MemIndexScaleMasked { index, scale, mask } => visitor.visit_index_scale_masked(*index, *scale, *mask), + Operand::MemIndexScaleDispMasked { index, scale, disp, mask } => visitor.visit_index_scale_disp_masked(*index, *scale, *disp, *mask), + Operand::MemBaseIndexScaleMasked { base, index, scale, mask } => visitor.visit_base_index_scale_masked(*base, *index, *scale, *mask), + Operand::MemBaseIndexScaleDispMasked { base, index, scale, disp, mask } => visitor.visit_base_index_scale_disp_masked(*base, *index, *scale, *disp, *mask), + Operand::AbsoluteFarAddress { segment, address } => visitor.visit_absolute_far_address(*segment, *address), + } + } } #[test] @@ -781,16 +847,16 @@ const REGISTER_CLASS_NAMES: &[&'static str] = &[ /// } /// } /// -/// if let Operand::Register(regspec) = instruction.operand(0) { +/// if let Operand::Register { reg } = instruction.operand(0) { /// #[cfg(feature="fmt")] -/// println!("first operand is {}", regspec); -/// show_register_class_info(regspec.class()); +/// println!("first operand is {}", reg); +/// show_register_class_info(reg.class()); /// } /// -/// if let Operand::Register(regspec) = instruction.operand(1) { +/// if let Operand::Register { reg } = instruction.operand(1) { /// #[cfg(feature="fmt")] -/// println!("first operand is {}", regspec); -/// show_register_class_info(regspec.class()); +/// println!("second operand is {}", reg); +/// show_register_class_info(reg.class()); /// } /// ``` /// @@ -968,7 +1034,16 @@ pub enum Opcode { NOT = 0x1019, XADD = 0x101a, XCHG = 0x101b, - Invalid = 0x1c, + + CMPS = 0x201c, + SCAS = 0x201d, + MOVS = 0x201e, + LODS = 0x201f, + STOS = 0x2020, + INS = 0x2021, + OUTS = 0x2022, + + Invalid = 0x23, // XADD, BT, // BTS, @@ -1053,17 +1128,10 @@ pub enum Opcode { CWD, CDQ, CQO, - LODS, - STOS, LAHF, SAHF, - CMPS, - SCAS, - MOVS, TEST, - INS, IN, - OUTS, OUT, IMUL, JO, @@ -2581,19 +2649,19 @@ enum OperandSpec { Deref_di = 0x90, Deref_esi = 0x91, Deref_edi = 0x92, - RegDisp = 0x93, - RegScale = 0x94, - RegScaleDisp = 0x95, - RegIndexBaseScale = 0x96, - RegIndexBaseScaleDisp = 0x97, + Disp = 0x93, + MemIndexScale = 0x94, + MemIndexScaleDisp = 0x95, + MemBaseIndexScale = 0x96, + MemBaseIndexScaleDisp = 0x97, Deref_mask = 0xce, - RegDisp_mask = 0xd3, - RegScale_mask = 0xd4, - RegScaleDisp_mask = 0xd5, - RegIndexBaseScale_mask = 0xd6, - RegIndexBaseScaleDisp_mask = 0xd7, + Disp_mask = 0xd3, + MemIndexScale_mask = 0xd4, + MemIndexScaleDisp_mask = 0xd5, + MemBaseIndexScale_mask = 0xd6, + MemBaseIndexScaleDisp_mask = 0xd7, // u16:u{16,32} immediate address for a far call - AbsoluteFarAddress = 0x98, + AbsoluteFarAddress = 0x18, } // the Hash, Eq, and PartialEq impls here are possibly misleading. @@ -4217,6 +4285,17 @@ impl Opcode { _ => None, } } + + #[inline(always)] + fn can_lock(&self) -> bool { + (*self as u32) & 0x1000 != 0 + } + + #[inline(always)] + #[allow(dead_code)] // in some configurations this is unused, but it is internal-only for now, so it would warn. + fn can_rep(&self) -> bool { + (*self as u32) & 0x2000 != 0 + } } impl Default for Instruction { @@ -4239,6 +4318,169 @@ impl Instruction { Operand::from_spec(self, self.operands[i as usize]) } + // TODO: make public when this seems stable and worthwhile. currently only used for display + // and Displaysink 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(feature="profiling", inline(never))] + #[allow(dead_code)] // in some configurations this is unused, but it is internal-only for now, so it would warn. + fn visit_operand(&self, i: u8, visitor: &mut T) -> Result { + 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::Disp => { + 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::MemIndexScale => { + visitor.visit_index_scale(self.regs[2], self.scale) + } + OperandSpec::MemIndexScaleDisp => { + visitor.visit_index_scale_disp(self.regs[2], self.scale, self.disp as i32) + } + OperandSpec::MemBaseIndexScale => { + visitor.visit_base_index_scale(self.regs[1], self.regs[2], self.scale) + } + OperandSpec::MemBaseIndexScaleDisp => { + visitor.visit_base_index_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_deref_masked(self.regs[1], RegSpec::mask(self.prefixes.evex_unchecked().mask_reg())) + } else { + visitor.visit_deref(self.regs[1]) + } + } + OperandSpec::Disp_mask => { + if self.prefixes.evex_unchecked().mask_reg() != 0 { + visitor.visit_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::MemIndexScale_mask => { + if self.prefixes.evex_unchecked().mask_reg() != 0 { + visitor.visit_index_scale_masked(self.regs[2], self.scale, RegSpec::mask(self.prefixes.evex_unchecked().mask_reg())) + } else { + visitor.visit_index_scale(self.regs[2], self.scale) + } + } + OperandSpec::MemIndexScaleDisp_mask => { + if self.prefixes.evex_unchecked().mask_reg() != 0 { + visitor.visit_index_scale_disp_masked(self.regs[2], self.scale, self.disp as i32, RegSpec::mask(self.prefixes.evex_unchecked().mask_reg())) + } else { + visitor.visit_index_scale_disp(self.regs[2], self.scale, self.disp as i32) + } + } + OperandSpec::MemBaseIndexScale_mask => { + if self.prefixes.evex_unchecked().mask_reg() != 0 { + visitor.visit_base_index_scale_masked(self.regs[1], self.regs[2], self.scale, RegSpec::mask(self.prefixes.evex_unchecked().mask_reg())) + } else { + visitor.visit_base_index_scale(self.regs[1], self.regs[2], self.scale) + } + } + OperandSpec::MemBaseIndexScaleDisp_mask => { + if self.prefixes.evex_unchecked().mask_reg() != 0 { + visitor.visit_base_index_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_base_index_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 { @@ -4527,11 +4769,8 @@ impl Prefixes { fn set_lock(&mut self) { self.bits |= 0x4 } #[inline] pub fn lock(&self) -> bool { self.bits & 0x4 == 4 } - #[deprecated(since = "0.0.1", note = "pub fn cs has never returned `bool` indicating the current selector is `cs`. use `selects_cs` for this purpose, until 2.x that will correct `pub fn cs`.")] - #[inline] - pub fn cs(&mut self) {} #[inline] - pub fn selects_cs(&self) -> bool { self.segment == Segment::CS } + pub fn cs(&self) -> bool { self.segment == Segment::CS } #[inline] fn set_cs(&mut self) { self.segment = Segment::CS } #[inline] @@ -5706,7 +5945,8 @@ const OPCODES: [OpcodeRecord; 256] = [ ]; #[allow(non_snake_case)] -#[inline(always)] +#[cfg_attr(feature="profiling", inline(never))] +#[cfg_attr(not(feature="profiling"), inline(always))] pub(self) fn read_E< T: Reader<::Address, ::Word>, S: DescriptionSink, @@ -5894,7 +6134,7 @@ fn read_sib< InnerDescription::Misc("mod bits select no base register") .with_id(sib_start + 0) ); - OperandSpec::RegScale + OperandSpec::MemIndexScale } else { sink.record( modrm_start + 6, @@ -5902,7 +6142,7 @@ fn read_sib< InnerDescription::RegisterNumber("mod", 0b101, instr.regs[1]) .with_id(sib_start + 0) ); - OperandSpec::RegIndexBaseScale + OperandSpec::MemBaseIndexScale } } } else { @@ -5921,7 +6161,7 @@ fn read_sib< InnerDescription::RegisterNumber("iii", instr.regs[2].num, instr.regs[2]) .with_id(sib_start + 0) ); - OperandSpec::RegIndexBaseScale + OperandSpec::MemBaseIndexScale } } @@ -5955,7 +6195,7 @@ fn read_sib< InnerDescription::RegisterNumber("mod", 0b101, instr.regs[1]) .with_id(sib_start + 0) ); - OperandSpec::RegDisp + OperandSpec::Disp } } else { sink.record( @@ -5971,7 +6211,7 @@ fn read_sib< InnerDescription::Misc("mod bits select no base register, [index+disp] only") .with_id(sib_start + 0) ); - OperandSpec::RegScaleDisp + OperandSpec::MemIndexScaleDisp } else { sink.record( modrm_start + 6, @@ -5979,7 +6219,7 @@ fn read_sib< InnerDescription::RegisterNumber("mod", 0b101, instr.regs[1]) .with_id(sib_start + 0) ); - OperandSpec::RegIndexBaseScaleDisp + OperandSpec::MemBaseIndexScaleDisp } } } else { @@ -5996,7 +6236,7 @@ fn read_sib< InnerDescription::Misc("iii selects no index register") .with_id(sib_start + 0) ); - OperandSpec::RegDisp + OperandSpec::Disp } else { sink.record( sib_start + 3, @@ -6004,7 +6244,7 @@ fn read_sib< InnerDescription::RegisterNumber("iii", instr.regs[2].num, instr.regs[2]) .with_id(sib_start + 0) ); - OperandSpec::RegIndexBaseScaleDisp + OperandSpec::MemBaseIndexScaleDisp } } }; @@ -6114,7 +6354,7 @@ fn read_M_16bit< Ok(OperandSpec::Deref) } else { instr.scale = 1; - Ok(OperandSpec::RegIndexBaseScale) + Ok(OperandSpec::MemBaseIndexScale) } }, 0b01 => { @@ -6135,13 +6375,13 @@ fn read_M_16bit< ); if mmm > 3 { if instr.disp != 0 { - Ok(OperandSpec::RegDisp) + Ok(OperandSpec::Disp) } else { Ok(OperandSpec::Deref) } } else { instr.scale = 1; - Ok(OperandSpec::RegIndexBaseScaleDisp) + Ok(OperandSpec::MemBaseIndexScaleDisp) } }, 0b10 => { @@ -6162,13 +6402,13 @@ fn read_M_16bit< ); if mmm > 3 { if instr.disp != 0 { - Ok(OperandSpec::RegDisp) + Ok(OperandSpec::Disp) } else { Ok(OperandSpec::Deref) } } else { instr.scale = 1; - Ok(OperandSpec::RegIndexBaseScaleDisp) + Ok(OperandSpec::MemBaseIndexScaleDisp) } }, _ => { @@ -6262,7 +6502,7 @@ fn read_M< OperandSpec::Deref } else { instr.disp = disp as i32 as u32; - OperandSpec::RegDisp + OperandSpec::Disp } } }; @@ -6444,6 +6684,7 @@ impl DecodeCtx { } } +#[cfg_attr(feature="profiling", inline(never))] fn read_opc_hotpath< T: Reader<::Address, ::Word>, S: DescriptionSink, @@ -6494,6 +6735,8 @@ fn read_opc_hotpath< } } +#[cfg_attr(feature="profiling", inline(never))] +#[cfg_attr(not(feature="profiling"), inline(always))] fn read_with_annotations< T: Reader<::Address, ::Word>, S: DescriptionSink, @@ -6646,7 +6889,7 @@ fn read_with_annotations< self.read_operands(decoder, words, instruction, record, sink)?; if self.check_lock { - if (instruction.opcode as u32) < 0x1000 || !instruction.operands[0].is_memory() { + if !instruction.opcode.can_lock() || !instruction.operands[0].is_memory() { return Err(DecodeError::InvalidPrefixes); } } @@ -6654,6 +6897,8 @@ fn read_with_annotations< Ok(()) } +#[cfg_attr(feature="profiling", inline(never))] +#[cfg_attr(not(feature="profiling"), inline(always))] fn read_operands< T: Reader<::Address, ::Word>, S: DescriptionSink @@ -7265,11 +7510,8 @@ fn read_operands< .with_id(modrm_start - 8) ); if instruction.operands[0] == OperandSpec::RegMMM { - if opcode == Opcode::CALL || opcode == Opcode::JMP { - instruction.regs[1].bank = RegisterBank::D; - if opcode == Opcode::CALL { - instruction.mem_size = 4; - } + if opcode == Opcode::CALL { + instruction.mem_size = 4; } else if opcode == Opcode::PUSH || opcode == Opcode::POP { if instruction.prefixes.operand_size() { instruction.mem_size = 2; diff --git a/src/real_mode/display.rs b/src/real_mode/display.rs index 9450a39f..9607e9d2 100644 --- a/src/real_mode/display.rs +++ b/src/real_mode/display.rs @@ -1,11 +1,136 @@ use core::fmt; +// allowing these deprecated items for the time being, not yet breaking yaxpeax-x86 apis +#[allow(deprecated)] use yaxpeax_arch::{Colorize, ShowContextual, NoColors, YaxColors}; -use yaxpeax_arch::display::*; -use crate::safer_unchecked::GetSaferUnchecked as _; use crate::MEM_SIZE_STRINGS; -use crate::real_mode::{RegSpec, Opcode, Operand, MergeMode, InstDecoder, Instruction, Segment, PrefixVex, OperandSpec}; +use crate::real_mode::{RegSpec, Opcode, Operand, MergeMode, InstDecoder, Instruction, Segment, PrefixVex}; + +use yaxpeax_arch::display::DisplaySink; +use yaxpeax_arch::safer_unchecked::GetSaferUnchecked as _; +use yaxpeax_arch::safer_unchecked::unreachable_kinda_unchecked as unreachable_unchecked; + +trait DisplaySinkExt { + // `write_opcode` depends on all mnemonics being less than 32 bytes long. check that here, at + // compile time. referenced later to force evaluation of this const. + const MNEMONIC_LT_32: () = { + let mut i = 0; + while i < MNEMONICS.len() { + let name = &MNEMONICS[i]; + if name.len() >= 32 { + panic!("mnemonic too long"); + } + i += 1; + } + }; + + // `write_reg` depends on all register names being less than 8 bytes long. check that here, at + // compile time. referenced later to force evaluation of this const. + const REG_LABEL_LT_8: () = { + let mut i = 0; + while i < REG_NAMES.len() { + let name = ®_NAMES[i]; + if name.len() >= 8 { + panic!("register name too long"); + } + i += 1; + } + }; + + // `write_mem_size_label` depends on all memory size labels being less than 8 bytes long. check + // that here, at compile time. referenced later to force evaluation of this const. + const MEM_SIZE_LABEL_LT_8: () = { + let mut i = 0; + while i < crate::MEM_SIZE_STRINGS.len() { + let name = &MEM_SIZE_STRINGS[i]; + if name.len() >= 8 { + panic!("memory label name too long"); + } + i += 1; + } + }; + + // `write_sae_mode` depends on all sae mode labels being less than 16 bytes long. check that + // here, at compile time. referenced later to force evaluation of this const. + const SAE_LABEL_LT_16: () = { + let mut i = 0; + while i < super::SAE_MODES.len() { + let mode = &super::SAE_MODES[i]; + if mode.label().len() >= 16 { + panic!("sae mode label too long"); + } + i += 1; + } + }; + + fn write_opcode(&mut self, opcode: super::Opcode) -> Result<(), core::fmt::Error>; + fn write_reg(&mut self, reg: RegSpec) -> Result<(), core::fmt::Error>; + fn write_displacement(&mut self, disp: i32) -> Result<(), core::fmt::Error>; + fn write_scale(&mut self, scale: u8) -> Result<(), core::fmt::Error>; + fn write_mem_size_label(&mut self, mem_size: u8) -> Result<(), core::fmt::Error>; + fn write_sae_mode(&mut self, sae: super::SaeMode) -> Result<(), core::fmt::Error>; +} + +impl DisplaySinkExt for T { + #[inline(always)] + fn write_opcode(&mut self, opcode: super::Opcode) -> Result<(), core::fmt::Error> { + let name = opcode.name(); + + let _ = Self::MNEMONIC_LT_32; + // Safety: all opcode mnemonics are 31 bytes or fewer. + unsafe { self.write_lt_32(name) } + } + + #[inline(always)] + fn write_reg(&mut self, reg: RegSpec) -> Result<(), core::fmt::Error> { + let label = regspec_label(®); + + let _ = Self::REG_LABEL_LT_8; + // Safety: all register labels are 7 bytes or fewer. + unsafe { self.write_lt_8(label) } + } + + #[inline(always)] + fn write_displacement(&mut self, disp: i32) -> Result<(), core::fmt::Error> { + let mut v = disp as u32; + if disp < 0 { + self.write_fixed_size("- ")?; + v = disp.unsigned_abs(); + } else { + self.write_fixed_size("+ ")?; + } + self.span_start_number(); + self.write_prefixed_u32(v)?; + self.span_end_number(); + Ok(()) + } + + #[inline(always)] + fn write_scale(&mut self, scale: u8) -> Result<(), core::fmt::Error> { + self.span_start_number(); + self.write_char((0x30 + scale) as char)?; // translate scale=1 to '1', scale=2 to '2', etc + self.span_end_number(); + Ok(()) + } + + #[inline(always)] + fn write_mem_size_label(&mut self, mem_size: u8) -> Result<(), core::fmt::Error> { + let label = mem_size_label(mem_size); + let _ = Self::MEM_SIZE_LABEL_LT_8; + // Safety: all memory size labels are 7 bytes or fewer + unsafe { self.write_lt_8(label) } + } + + #[inline(always)] + fn write_sae_mode(&mut self, sae_mode: super::SaeMode) -> Result<(), core::fmt::Error> { + let label = sae_mode.label(); + + let _ = Self::SAE_LABEL_LT_16; + // Safety: all sae labels are 15 bytes or fewer. + unsafe { self.write_lt_16(label) } + } +} impl fmt::Display for InstDecoder { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -89,6 +214,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 { @@ -129,6 +267,10 @@ 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) } } +pub(crate) fn mem_size_label(size: u8) -> &'static str { + unsafe { MEM_SIZE_STRINGS.get_kinda_unchecked(size as usize) } +} + impl fmt::Display for RegSpec { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(regspec_label(self)) @@ -137,207 +279,315 @@ impl fmt::Display for RegSpec { impl fmt::Display for Operand { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + // to reuse one implementation, call the deprecated function for now. + #[allow(deprecated)] self.colorize(&NoColors, fmt) } } +// allowing these deprecated items for the time being, not yet breaking yaxpeax-x86 apis +#[allow(deprecated)] impl Colorize 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(()) }, + fn colorize(&self, _colors: &Y, f: &mut T) -> fmt::Result { + let mut f = yaxpeax_arch::display::FmtSink::new(f); + let mut visitor = DisplayingOperandVisitor { + f: &mut f + }; + self.visit(&mut visitor) + } +} + +struct DisplayingOperandVisitor<'a, T> { + f: &'a mut T, +} + +impl super::OperandVisitor for DisplayingOperandVisitor<'_, T> { + type Ok = (); + type Error = core::fmt::Error; + + #[cfg_attr(feature="profiling", inline(never))] + fn visit_u8(&mut self, imm: u8) -> Result { + self.f.span_start_immediate(); + self.f.write_fixed_size("0x")?; + self.f.write_u8(imm)?; + self.f.span_end_immediate(); + Ok(()) + } + #[cfg_attr(feature="profiling", inline(never))] + fn visit_i8(&mut self, imm: i8) -> Result { + self.f.span_start_immediate(); + let mut v = imm as u8; + if imm < 0 { + self.f.write_char('-')?; + v = imm.unsigned_abs(); + } + self.f.write_fixed_size("0x")?; + self.f.write_u8(v)?; + self.f.span_end_immediate(); + Ok(()) + } + #[cfg_attr(feature="profiling", inline(never))] + fn visit_u16(&mut self, imm: u16) -> Result { + self.f.span_start_immediate(); + self.f.write_fixed_size("0x")?; + self.f.write_u16(imm)?; + self.f.span_end_immediate(); + Ok(()) + } + #[cfg_attr(feature="profiling", inline(never))] + fn visit_i16(&mut self, imm: i16) -> Result { + self.f.span_start_immediate(); + let mut v = imm as u16; + if imm < 0 { + self.f.write_char('-')?; + v = imm.unsigned_abs(); } + self.f.write_fixed_size("0x")?; + self.f.write_u16(v)?; + self.f.span_end_immediate(); + Ok(()) + } + #[cfg_attr(feature="profiling", inline(never))] + fn visit_u32(&mut self, imm: u32) -> Result { + self.f.span_start_immediate(); + self.f.write_fixed_size("0x")?; + self.f.write_u32(imm)?; + self.f.span_end_immediate(); + Ok(()) + } + fn visit_i32(&mut self, imm: i32) -> Result { + self.f.span_start_immediate(); + let mut v = imm as u32; + if imm < 0 { + self.f.write_char('-')?; + v = imm.unsigned_abs(); + } + self.f.write_fixed_size("0x")?; + self.f.write_u32(v)?; + self.f.span_end_immediate(); + Ok(()) + } + #[cfg_attr(feature="profiling", inline(never))] + fn visit_reg(&mut self, reg: RegSpec) -> Result { + self.f.span_start_register(); + self.f.write_reg(reg)?; + self.f.span_end_register(); + Ok(()) + } + fn visit_reg_mask_merge(&mut self, spec: RegSpec, mask: RegSpec, merge_mode: MergeMode) -> Result { + self.f.span_start_register(); + self.f.write_reg(spec)?; + self.f.span_end_register(); + if mask.num != 0 { + self.f.write_fixed_size("{")?; + self.f.span_start_register(); + self.f.write_reg(mask)?; + self.f.span_end_register(); + self.f.write_fixed_size("}")?; + } + if let MergeMode::Zero = merge_mode { + self.f.write_fixed_size("{z}")?; + } + Ok(()) + } + fn visit_reg_mask_merge_sae(&mut self, spec: RegSpec, mask: RegSpec, merge_mode: MergeMode, sae_mode: super::SaeMode) -> Result { + self.f.write_reg(spec)?; + if mask.num != 0 { + self.f.write_fixed_size("{")?; + self.f.write_reg(mask)?; + self.f.write_fixed_size("}")?; + } + if let MergeMode::Zero = merge_mode { + self.f.write_fixed_size("{z}")?; + } + self.f.write_sae_mode(sae_mode)?; + Ok(()) + } + fn visit_reg_mask_merge_sae_noround(&mut self, spec: RegSpec, mask: RegSpec, merge_mode: MergeMode) -> Result { + self.f.write_reg(spec)?; + if mask.num != 0 { + self.f.write_fixed_size("{")?; + self.f.write_reg(mask)?; + self.f.write_fixed_size("}")?; + } + if let MergeMode::Zero = merge_mode { + self.f.write_fixed_size("{z}")?; + } + self.f.write_fixed_size("{sae}")?; + Ok(()) + } + fn visit_abs_u16(&mut self, imm: u16) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_fixed_size("0x")?; + self.f.write_u16(imm)?; + self.f.write_fixed_size("]")?; + Ok(()) + } + fn visit_abs_u32(&mut self, imm: u32) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_fixed_size("0x")?; + self.f.write_u32(imm)?; + self.f.write_fixed_size("]")?; + Ok(()) + } + #[cfg_attr(not(feature="profiling"), inline(always))] + #[cfg_attr(feature="profiling", inline(never))] + fn visit_disp(&mut self, base: RegSpec, disp: i32) -> Result { + self.f.write_char('[')?; + self.f.write_reg(base)?; + self.f.write_fixed_size(" ")?; + self.f.write_displacement(disp)?; + self.f.write_fixed_size("]") + } + fn visit_deref(&mut self, base: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size("]") + } + fn visit_index_scale(&mut self, index: RegSpec, scale: u8) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_fixed_size("]")?; + + Ok(()) + } + fn visit_index_scale_disp(&mut self, index: RegSpec, scale: u8, disp: i32) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_fixed_size(" ")?; + self.f.write_displacement(disp)?; + self.f.write_char(']') + } + fn visit_base_index_scale(&mut self, base: RegSpec, index: RegSpec, scale: u8) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size(" + ")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_fixed_size("]") + } + fn visit_base_index_scale_disp(&mut self, base: RegSpec, index: RegSpec, scale: u8, disp: i32) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size(" + ")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_fixed_size(" ")?; + self.f.write_displacement(disp)?; + self.f.write_fixed_size("]") + } + fn visit_disp_masked(&mut self, base: RegSpec, disp: i32, mask_reg: RegSpec) -> Result { + self.f.write_char('[')?; + self.f.write_reg(base)?; + self.f.write_char(' ')?; + self.f.write_displacement(disp)?; + self.f.write_char(']')?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_deref_masked(&mut self, base: RegSpec, mask_reg: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size("]")?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_index_scale_masked(&mut self, index: RegSpec, scale: u8, mask_reg: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_fixed_size("]")?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_index_scale_disp_masked(&mut self, index: RegSpec, scale: u8, disp: i32, mask_reg: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_fixed_size(" ")?; + self.f.write_displacement(disp)?; + self.f.write_char(']')?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_base_index_masked(&mut self, base: RegSpec, index: RegSpec, mask_reg: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size(" + ")?; + self.f.write_reg(index)?; + self.f.write_fixed_size("]")?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_base_index_disp_masked(&mut self, base: RegSpec, index: RegSpec, disp: i32, mask_reg: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size(" + ")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" ")?; + self.f.write_displacement(disp)?; + self.f.write_char(']')?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_base_index_scale_masked(&mut self, base: RegSpec, index: RegSpec, scale: u8, mask_reg: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size(" + ")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_fixed_size("]")?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_base_index_scale_disp_masked(&mut self, base: RegSpec, index: RegSpec, scale: u8, disp: i32, mask_reg: RegSpec) -> Result { + self.f.write_fixed_size("[")?; + self.f.write_reg(base)?; + self.f.write_fixed_size(" + ")?; + self.f.write_reg(index)?; + self.f.write_fixed_size(" * ")?; + self.f.write_scale(scale)?; + self.f.write_char(' ')?; + self.f.write_displacement(disp)?; + self.f.write_char(']')?; + self.f.write_char('{')?; + self.f.write_reg(mask_reg)?; + self.f.write_char('}')?; + Ok(()) + } + fn visit_absolute_far_address(&mut self, segment: u16, address: u32) -> Result { + self.f.write_prefixed_u16(segment)?; + self.f.write_fixed_size(":")?; + self.f.write_prefixed_u32(address)?; + Ok(()) + } + + + fn visit_other(&mut self) -> Result { + Ok(()) } } @@ -376,6 +626,13 @@ const MNEMONICS: &[&'static str] = &[ "not", "xadd", "xchg", + "cmps", + "scas", + "movs", + "lods", + "stos", + "ins", + "outs", "invalid", "bt", "bsf", @@ -457,17 +714,10 @@ const MNEMONICS: &[&'static str] = &[ "cwd", "cdq", "cqo", - "lods", - "stos", "lahf", "sahf", - "cmps", - "scas", - "movs", "test", - "ins", "in", - "outs", "out", "imul", "jo", @@ -1854,1456 +2104,27 @@ impl Opcode { } } +// allowing these deprecated items for the time being, not yet breaking yaxpeax-x86 apis +#[allow(deprecated)] impl Colorize for Opcode { - fn colorize(&self, colors: &Y, out: &mut T) -> fmt::Result { - match self { - Opcode::VGF2P8AFFINEQB | - Opcode::VGF2P8AFFINEINVQB | - Opcode::VPSHRDQ | - Opcode::VPSHRDD | - Opcode::VPSHRDW | - Opcode::VPSHLDQ | - Opcode::VPSHLDD | - Opcode::VPSHLDW | - Opcode::VBROADCASTF32X8 | - Opcode::VBROADCASTF64X4 | - Opcode::VBROADCASTF32X4 | - Opcode::VBROADCASTF64X2 | - Opcode::VBROADCASTF32X2 | - Opcode::VBROADCASTI32X8 | - Opcode::VBROADCASTI64X4 | - Opcode::VBROADCASTI32X4 | - Opcode::VBROADCASTI64X2 | - Opcode::VBROADCASTI32X2 | - Opcode::VEXTRACTI32X8 | - Opcode::VEXTRACTF32X8 | - Opcode::VINSERTI32X8 | - Opcode::VINSERTF32X8 | - Opcode::VINSERTI32X4 | - Opcode::V4FNMADDSS | - Opcode::V4FNMADDPS | - Opcode::VCVTNEPS2BF16 | - Opcode::V4FMADDSS | - Opcode::V4FMADDPS | - Opcode::VCVTNE2PS2BF16 | - Opcode::VP2INTERSECTD | - Opcode::VP2INTERSECTQ | - Opcode::VP4DPWSSDS | - Opcode::VP4DPWSSD | - Opcode::VPDPWSSDS | - Opcode::VPDPWSSD | - Opcode::VPDPBUSDS | - Opcode::VDPBF16PS | - Opcode::VPBROADCASTMW2D | - Opcode::VPBROADCASTMB2Q | - Opcode::VPMOVD2M | - Opcode::VPMOVQD | - Opcode::VPMOVWB | - Opcode::VPMOVDB | - Opcode::VPMOVDW | - Opcode::VPMOVQB | - Opcode::VPMOVQW | - Opcode::VGF2P8MULB | - Opcode::VPMADD52HUQ | - Opcode::VPMADD52LUQ | - Opcode::VPSHUFBITQMB | - Opcode::VPERMB | - Opcode::VPEXPANDD | - Opcode::VPEXPANDQ | - Opcode::VPABSQ | - Opcode::VPRORVD | - Opcode::VPRORVQ | - Opcode::VPMULTISHIFTQB | - Opcode::VPERMT2B | - Opcode::VPERMT2W | - Opcode::VPSHRDVQ | - Opcode::VPSHRDVD | - Opcode::VPSHRDVW | - Opcode::VPSHLDVQ | - Opcode::VPSHLDVD | - Opcode::VPSHLDVW | - Opcode::VPCOMPRESSB | - Opcode::VPCOMPRESSW | - Opcode::VPEXPANDB | - Opcode::VPEXPANDW | - Opcode::VPOPCNTD | - Opcode::VPOPCNTQ | - Opcode::VPOPCNTB | - Opcode::VPOPCNTW | - Opcode::VSCALEFSS | - Opcode::VSCALEFSD | - Opcode::VSCALEFPS | - Opcode::VSCALEFPD | - Opcode::VPDPBUSD | - Opcode::VCVTUSI2SD | - Opcode::VCVTUSI2SS | - Opcode::VPXORD | - Opcode::VPXORQ | - Opcode::VPORD | - Opcode::VPORQ | - Opcode::VPANDND | - Opcode::VPANDNQ | - Opcode::VPANDD | - Opcode::VPANDQ | - - Opcode::VHADDPS | - Opcode::VHSUBPS | - Opcode::VADDSUBPS | - Opcode::VADDPD | - Opcode::VADDPS | - Opcode::VADDSD | - Opcode::VADDSS | - Opcode::VADDSUBPD | - Opcode::VFMADD132PD | - Opcode::VFMADD132PS | - Opcode::VFMADD132SD | - Opcode::VFMADD132SS | - Opcode::VFMADD213PD | - Opcode::VFMADD213PS | - Opcode::VFMADD213SD | - Opcode::VFMADD213SS | - Opcode::VFMADD231PD | - Opcode::VFMADD231PS | - Opcode::VFMADD231SD | - Opcode::VFMADD231SS | - Opcode::VFMADDSUB132PD | - Opcode::VFMADDSUB132PS | - Opcode::VFMADDSUB213PD | - Opcode::VFMADDSUB213PS | - Opcode::VFMADDSUB231PD | - Opcode::VFMADDSUB231PS | - Opcode::VFMSUB132PD | - Opcode::VFMSUB132PS | - Opcode::VFMSUB132SD | - Opcode::VFMSUB132SS | - Opcode::VFMSUB213PD | - Opcode::VFMSUB213PS | - Opcode::VFMSUB213SD | - Opcode::VFMSUB213SS | - Opcode::VFMSUB231PD | - Opcode::VFMSUB231PS | - Opcode::VFMSUB231SD | - Opcode::VFMSUB231SS | - Opcode::VFMSUBADD132PD | - Opcode::VFMSUBADD132PS | - Opcode::VFMSUBADD213PD | - Opcode::VFMSUBADD213PS | - Opcode::VFMSUBADD231PD | - Opcode::VFMSUBADD231PS | - Opcode::VFNMADD132PD | - Opcode::VFNMADD132PS | - Opcode::VFNMADD132SD | - Opcode::VFNMADD132SS | - Opcode::VFNMADD213PD | - Opcode::VFNMADD213PS | - Opcode::VFNMADD213SD | - Opcode::VFNMADD213SS | - Opcode::VFNMADD231PD | - Opcode::VFNMADD231PS | - Opcode::VFNMADD231SD | - Opcode::VFNMADD231SS | - Opcode::VFNMSUB132PD | - Opcode::VFNMSUB132PS | - Opcode::VFNMSUB132SD | - Opcode::VFNMSUB132SS | - Opcode::VFNMSUB213PD | - Opcode::VFNMSUB213PS | - Opcode::VFNMSUB213SD | - Opcode::VFNMSUB213SS | - Opcode::VFNMSUB231PD | - Opcode::VFNMSUB231PS | - Opcode::VFNMSUB231SD | - Opcode::VFNMSUB231SS | - Opcode::VDIVPD | - Opcode::VDIVPS | - Opcode::VDIVSD | - Opcode::VDIVSS | - Opcode::VHADDPD | - Opcode::VHSUBPD | - Opcode::HADDPD | - Opcode::HSUBPD | - Opcode::VMULPD | - Opcode::VMULPS | - Opcode::VMULSD | - Opcode::VMULSS | - Opcode::VPABSB | - Opcode::VPABSD | - Opcode::VPABSW | - Opcode::PABSB | - Opcode::PABSD | - Opcode::PABSW | - Opcode::VPSIGNB | - Opcode::VPSIGND | - Opcode::VPSIGNW | - Opcode::PSIGNB | - Opcode::PSIGND | - Opcode::PSIGNW | - Opcode::VPADDB | - Opcode::VPADDD | - Opcode::VPADDQ | - Opcode::VPADDSB | - Opcode::VPADDSW | - Opcode::VPADDUSB | - Opcode::VPADDUSW | - Opcode::VPADDW | - Opcode::VPAVGB | - Opcode::VPAVGW | - Opcode::VPMULDQ | - Opcode::VPMULHRSW | - Opcode::VPMULHUW | - Opcode::VPMULHW | - Opcode::VPMULLQ | - Opcode::VPMULLD | - Opcode::VPMULLW | - Opcode::VPMULUDQ | - Opcode::PCLMULQDQ | - Opcode::PMULDQ | - Opcode::PMULHRSW | - Opcode::PMULLD | - Opcode::VPSUBB | - Opcode::VPSUBD | - Opcode::VPSUBQ | - Opcode::VPSUBSB | - Opcode::VPSUBSW | - Opcode::VPSUBUSB | - Opcode::VPSUBUSW | - Opcode::VPSUBW | - Opcode::VROUNDPD | - Opcode::VROUNDPS | - Opcode::VEXP2PD | - Opcode::VEXP2PS | - Opcode::VEXP2SD | - Opcode::VEXP2SS | - Opcode::VRCP28PD | - Opcode::VRCP28PS | - Opcode::VRCP28SD | - Opcode::VRCP28SS | - Opcode::VRCP14PD | - Opcode::VRCP14PS | - Opcode::VRCP14SD | - Opcode::VRCP14SS | - Opcode::VRNDSCALEPD | - Opcode::VRNDSCALEPS | - Opcode::VRNDSCALESD | - Opcode::VRNDSCALESS | - Opcode::VRSQRT14PD | - Opcode::VRSQRT14PS | - Opcode::VRSQRT14SD | - Opcode::VRSQRT14SS | - Opcode::VSCALEDPD | - Opcode::VSCALEDPS | - Opcode::VSCALEDSD | - Opcode::VSCALEDSS | - Opcode::VRSQRT28PD | - Opcode::VRSQRT28PS | - Opcode::VRSQRT28SD | - Opcode::VRSQRT28SS | - Opcode::VRSQRTPS | - Opcode::VSQRTPD | - Opcode::VSQRTPS | - Opcode::VSUBPD | - Opcode::VSUBPS | - Opcode::VSUBSD | - Opcode::VSUBSS | - Opcode::VRCPSS | - Opcode::VROUNDSD | - Opcode::VROUNDSS | - Opcode::ROUNDPD | - Opcode::ROUNDPS | - Opcode::ROUNDSD | - Opcode::ROUNDSS | - Opcode::VRSQRTSS | - Opcode::VSQRTSD | - Opcode::VSQRTSS | - Opcode::VPSADBW | - Opcode::VMPSADBW | - Opcode::VDBPSADBW | - Opcode::VPHADDD | - Opcode::VPHADDSW | - Opcode::VPHADDW | - Opcode::VPHSUBD | - Opcode::VPHSUBSW | - Opcode::VPHSUBW | - Opcode::VPMADDUBSW | - Opcode::VPMADDWD | - Opcode::VDPPD | - Opcode::VDPPS | - Opcode::VRCPPS | - Opcode::VORPD | - Opcode::VORPS | - Opcode::VANDPD | - Opcode::VANDPS | - Opcode::VANDNPD | - Opcode::VANDNPS | - Opcode::VPAND | - Opcode::VPANDN | - Opcode::VPOR | - Opcode::VPXOR | - Opcode::VXORPD | - Opcode::VXORPS | - Opcode::VPSLLD | - Opcode::VPSLLDQ | - Opcode::VPSLLQ | - Opcode::VPSLLVD | - Opcode::VPSLLVQ | - Opcode::VPSLLW | - Opcode::VPROLD | - Opcode::VPROLQ | - Opcode::VPROLVD | - Opcode::VPROLVQ | - Opcode::VPRORD | - Opcode::VPRORQ | - Opcode::VPRORRD | - Opcode::VPRORRQ | - Opcode::VPSLLVW | - Opcode::VPSRAQ | - Opcode::VPSRAVQ | - Opcode::VPSRAVW | - Opcode::VPSRLVW | - Opcode::VPSRAD | - Opcode::VPSRAVD | - Opcode::VPSRAW | - Opcode::VPSRLD | - Opcode::VPSRLDQ | - Opcode::VPSRLQ | - Opcode::VPSRLVD | - Opcode::VPSRLVQ | - Opcode::VPSRLW | - Opcode::PHADDD | - Opcode::PHADDSW | - Opcode::PHADDW | - Opcode::PHSUBD | - Opcode::PHSUBSW | - Opcode::PHSUBW | - Opcode::PMADDUBSW | - Opcode::ADDSUBPD | - Opcode::DPPS | - Opcode::DPPD | - Opcode::MPSADBW | - Opcode::RCPSS | - Opcode::RSQRTSS | - Opcode::SQRTSD | - Opcode::ADDSD | - Opcode::SUBSD | - Opcode::MULSD | - Opcode::DIVSD | - Opcode::SQRTSS | - Opcode::ADDSS | - Opcode::SUBSS | - Opcode::MULSS | - Opcode::DIVSS | - Opcode::HADDPS | - Opcode::HSUBPS | - Opcode::ADDSUBPS | - Opcode::PMULHRW | - Opcode::PFRCP | - Opcode::PFRSQRT | - Opcode::PFSUB | - Opcode::PFADD | - Opcode::PFRCPIT1 | - Opcode::PFRSQIT1 | - Opcode::PFSUBR | - Opcode::PFACC | - Opcode::PFMUL | - Opcode::PFMULHRW | - Opcode::PFRCPIT2 | - Opcode::PFNACC | - Opcode::PFPNACC | - Opcode::PSWAPD | - Opcode::PAVGUSB | - Opcode::XADD| - Opcode::DIV | - Opcode::IDIV | - Opcode::MUL | - Opcode::MULX | - Opcode::NEG | - Opcode::NOT | - Opcode::SAR | - Opcode::SAL | - Opcode::SHR | - Opcode::SARX | - Opcode::SHLX | - Opcode::SHRX | - Opcode::SHRD | - Opcode::SHL | - Opcode::RCR | - Opcode::RCL | - Opcode::ROR | - Opcode::RORX | - Opcode::ROL | - Opcode::INC | - Opcode::DEC | - Opcode::SBB | - Opcode::AND | - Opcode::XOR | - Opcode::OR | - Opcode::LEA | - Opcode::ADD | - Opcode::ADC | - Opcode::ADCX | - Opcode::ADOX | - Opcode::SUB | - Opcode::POPCNT | - Opcode::LZCNT | - Opcode::VPLZCNTD | - Opcode::VPLZCNTQ | - Opcode::BT | - Opcode::BTS | - Opcode::BTR | - Opcode::BTC | - Opcode::BSF | - Opcode::BSR | - Opcode::BZHI | - Opcode::PDEP | - Opcode::PEXT | - Opcode::TZCNT | - Opcode::ANDN | - Opcode::BEXTR | - Opcode::BLSI | - Opcode::BLSMSK | - Opcode::BLSR | - Opcode::ADDPS | - Opcode::ADDPD | - Opcode::ANDNPS | - Opcode::ANDNPD | - Opcode::ANDPS | - Opcode::ANDPD | - Opcode::COMISD | - Opcode::COMISS | - Opcode::DIVPS | - Opcode::DIVPD | - Opcode::MULPS | - Opcode::MULPD | - Opcode::ORPS | - Opcode::ORPD | - Opcode::PADDB | - Opcode::PADDD | - Opcode::PADDQ | - Opcode::PADDSB | - Opcode::PADDSW | - Opcode::PADDUSB | - Opcode::PADDUSW | - Opcode::PADDW | - Opcode::PAND | - Opcode::PANDN | - Opcode::PAVGB | - Opcode::PAVGW | - Opcode::PMADDWD | - Opcode::PMULHUW | - Opcode::PMULHW | - Opcode::PMULLW | - Opcode::PMULUDQ | - Opcode::POR | - Opcode::PSADBW | - Opcode::PSHUFD | - Opcode::PSHUFW | - Opcode::PSHUFB | - Opcode::PSLLD | - Opcode::PSLLDQ | - Opcode::PSLLQ | - Opcode::PSLLW | - Opcode::PSRAD | - Opcode::PSRAW | - Opcode::PSRLD | - Opcode::PSRLDQ | - Opcode::PSRLQ | - Opcode::PSRLW | - Opcode::PSUBB | - Opcode::PSUBD | - Opcode::PSUBQ | - Opcode::PSUBSB | - Opcode::PSUBSW | - Opcode::PSUBUSB | - Opcode::PSUBUSW | - Opcode::PSUBW | - Opcode::PXOR | - Opcode::RSQRTPS | - Opcode::SQRTPS | - Opcode::SQRTPD | - Opcode::SUBPS | - Opcode::SUBPD | - Opcode::XORPS | - Opcode::XORPD | - Opcode::RCPPS | - Opcode::SHLD | - Opcode::SLHD | - Opcode::UCOMISD | - Opcode::UCOMISS | - Opcode::F2XM1 | - Opcode::FABS | - Opcode::FADD | - Opcode::FADDP | - Opcode::FCHS | - Opcode::FCOS | - Opcode::FDIV | - Opcode::FDIVP | - Opcode::FDIVR | - Opcode::FDIVRP | - Opcode::FIADD | - Opcode::FIDIV | - Opcode::FIDIVR | - Opcode::FIMUL | - Opcode::FISUB | - Opcode::FISUBR | - Opcode::FMUL | - Opcode::FMULP | - Opcode::FNCLEX | - Opcode::FNINIT | - Opcode::FPATAN | - Opcode::FPREM | - Opcode::FPREM1 | - Opcode::FPTAN | - Opcode::FRNDINT | - Opcode::FSCALE | - Opcode::FSIN | - Opcode::FSINCOS | - Opcode::FSQRT | - Opcode::FSUB | - Opcode::FSUBP | - Opcode::FSUBR | - Opcode::FSUBRP | - Opcode::FXTRACT | - Opcode::FYL2X | - Opcode::FYL2XP1 | - Opcode::AAA | - Opcode::AAS | - Opcode::DAS | - Opcode::DAA | - Opcode::AAD | - Opcode::AAM | - Opcode::KADDB | - Opcode::KANDB | - Opcode::KANDNB | - Opcode::KNOTB | - Opcode::KORB | - Opcode::KSHIFTLB | - Opcode::KSHIFTRB | - Opcode::KXNORB | - Opcode::KXORB | - Opcode::KADDW | - Opcode::KANDW | - Opcode::KANDNW | - Opcode::KNOTW | - Opcode::KORW | - Opcode::KSHIFTLW | - Opcode::KSHIFTRW | - Opcode::KXNORW | - Opcode::KXORW | - Opcode::KADDD | - Opcode::KANDD | - Opcode::KANDND | - Opcode::KNOTD | - Opcode::KORD | - Opcode::KSHIFTLD | - Opcode::KSHIFTRD | - Opcode::KXNORD | - Opcode::KXORD | - Opcode::KADDQ | - Opcode::KANDQ | - Opcode::KANDNQ | - Opcode::KNOTQ | - Opcode::KORQ | - Opcode::KSHIFTLQ | - Opcode::KSHIFTRQ | - Opcode::KXNORQ | - Opcode::KXORQ | - Opcode::IMUL => { write!(out, "{}", colors.arithmetic_op(self)) } - Opcode::POPF | - Opcode::PUSHF | - Opcode::ENTER | - Opcode::LEAVE | - Opcode::PUSHA | - Opcode::POPA | - Opcode::PUSH | - Opcode::POP => { write!(out, "{}", colors.stack_op(self)) } - Opcode::WAIT | - Opcode::FNOP | - Opcode::FDISI8087_NOP | - Opcode::FENI8087_NOP | - Opcode::FSETPM287_NOP | - Opcode::PREFETCHNTA | - Opcode::PREFETCH0 | - Opcode::PREFETCH1 | - Opcode::PREFETCH2 | - Opcode::PREFETCHW | - Opcode::NOP => { write!(out, "{}", colors.nop_op(self)) } - - /* Control flow */ - Opcode::HLT | - Opcode::INT | - Opcode::INTO | - Opcode::IRET | - Opcode::IRETD | - Opcode::IRETQ | - Opcode::RETF | - Opcode::RETURN => { write!(out, "{}", colors.stop_op(self)) } - Opcode::LOOPNZ | - Opcode::LOOPZ | - Opcode::LOOP | - Opcode::JCXZ | - Opcode::CALL | - Opcode::CALLF | - Opcode::JMP | - Opcode::JMPF | - Opcode::JO | - Opcode::JNO | - Opcode::JB | - Opcode::JNB | - Opcode::JZ | - Opcode::JNZ | - Opcode::JA | - Opcode::JNA | - Opcode::JS | - Opcode::JNS | - Opcode::JP | - Opcode::JNP | - Opcode::JL | - Opcode::JGE | - Opcode::JLE | - Opcode::JG => { write!(out, "{}", colors.control_flow_op(self)) } - - /* Data transfer */ - Opcode::PI2FW | - Opcode::PI2FD | - Opcode::PF2ID | - Opcode::PF2IW | - Opcode::VCVTDQ2PD | - Opcode::VCVTDQ2PS | - Opcode::VCVTPD2DQ | - Opcode::VCVTPD2PS | - Opcode::VCVTPH2PS | - Opcode::VCVTPS2DQ | - Opcode::VCVTPS2PD | - Opcode::VCVTPS2PH | - Opcode::VCVTTPD2DQ | - Opcode::VCVTTPS2DQ | - Opcode::VCVTSD2SI | - Opcode::VCVTSD2SS | - Opcode::VCVTSI2SD | - Opcode::VCVTSI2SS | - Opcode::VCVTSS2SD | - Opcode::VCVTSS2SI | - Opcode::VCVTTSD2SI | - Opcode::VCVTTSS2SI | - Opcode::VCVTPD2UDQ | - Opcode::VCVTTPD2UDQ | - Opcode::VCVTPS2UDQ | - Opcode::VCVTTPS2UDQ | - Opcode::VCVTQQ2PD | - Opcode::VCVTQQ2PS | - Opcode::VCVTSD2USI | - Opcode::VCVTTSD2USI | - Opcode::VCVTSS2USI | - Opcode::VCVTTSS2USI | - Opcode::VCVTUDQ2PD | - Opcode::VCVTUDQ2PS | - Opcode::VCVTUSI2USD | - Opcode::VCVTUSI2USS | - Opcode::VCVTTPD2QQ | - Opcode::VCVTPD2QQ | - Opcode::VCVTTPD2UQQ | - Opcode::VCVTPD2UQQ | - Opcode::VCVTTPS2QQ | - Opcode::VCVTPS2QQ | - Opcode::VCVTTPS2UQQ | - Opcode::VCVTPS2UQQ | - Opcode::VCVTUQQ2PD | - Opcode::VCVTUQQ2PS | - Opcode::VMOVDDUP | - Opcode::VPSHUFLW | - Opcode::VPSHUFHW | - Opcode::VBLENDMPD | - Opcode::VBLENDMPS | - Opcode::VPBLENDMD | - Opcode::VPBLENDMQ | - Opcode::VBLENDPD | - Opcode::VBLENDPS | - Opcode::VBLENDVPD | - Opcode::VBLENDVPS | - Opcode::VPBLENDMB | - Opcode::VPBLENDMW | - Opcode::PBLENDVB | - Opcode::PBLENDW | - Opcode::BLENDPD | - Opcode::BLENDPS | - Opcode::BLENDVPD | - Opcode::BLENDVPS | - Opcode::BLENDW | - Opcode::VBROADCASTF128 | - Opcode::VBROADCASTI128 | - Opcode::VBROADCASTSD | - Opcode::VBROADCASTSS | - Opcode::VPBROADCASTM | - Opcode::VEXTRACTF128 | - Opcode::VEXTRACTI128 | - Opcode::VEXTRACTPS | - Opcode::EXTRACTPS | - Opcode::VGATHERDPD | - Opcode::VGATHERDPS | - Opcode::VGATHERQPD | - Opcode::VGATHERQPS | - Opcode::VGATHERPF0DPD | - Opcode::VGATHERPF0DPS | - Opcode::VGATHERPF0QPD | - Opcode::VGATHERPF0QPS | - Opcode::VGATHERPF1DPD | - Opcode::VGATHERPF1DPS | - Opcode::VGATHERPF1QPD | - Opcode::VGATHERPF1QPS | - Opcode::VSCATTERDD | - Opcode::VSCATTERDQ | - Opcode::VSCATTERQD | - Opcode::VSCATTERQQ | - Opcode::VPSCATTERDD | - Opcode::VPSCATTERDQ | - Opcode::VPSCATTERQD | - Opcode::VPSCATTERQQ | - Opcode::VSCATTERPF0DPD | - Opcode::VSCATTERPF0DPS | - Opcode::VSCATTERPF0QPD | - Opcode::VSCATTERPF0QPS | - Opcode::VSCATTERPF1DPD | - Opcode::VSCATTERPF1DPS | - Opcode::VSCATTERPF1QPD | - Opcode::VSCATTERPF1QPS | - Opcode::VINSERTF128 | - Opcode::VINSERTI128 | - Opcode::VINSERTPS | - Opcode::INSERTPS | - Opcode::VEXTRACTF32X4 | - Opcode::VEXTRACTF64X2 | - Opcode::VEXTRACTF64X4 | - Opcode::VEXTRACTI32X4 | - Opcode::VEXTRACTI64X2 | - Opcode::VEXTRACTI64X4 | - Opcode::VINSERTF32X4 | - Opcode::VINSERTF64X2 | - Opcode::VINSERTF64X4 | - Opcode::VINSERTI64X2 | - Opcode::VINSERTI64X4 | - Opcode::VSHUFF32X4 | - Opcode::VSHUFF64X2 | - Opcode::VSHUFI32X4 | - Opcode::VSHUFI64X2 | - Opcode::VMASKMOVDQU | - Opcode::VMASKMOVPD | - Opcode::VMASKMOVPS | - Opcode::VMOVAPD | - Opcode::VMOVAPS | - Opcode::VMOVD | - Opcode::VMOVDQA | - Opcode::VMOVDQU | - Opcode::VMOVHLPS | - Opcode::VMOVHPD | - Opcode::VMOVHPS | - Opcode::VMOVLHPS | - Opcode::VMOVLPD | - Opcode::VMOVLPS | - Opcode::VMOVMSKPD | - Opcode::VMOVMSKPS | - Opcode::VMOVNTDQ | - Opcode::VMOVNTDQA | - Opcode::VMOVNTPD | - Opcode::VMOVNTPS | - Opcode::MOVDIR64B | - Opcode::MOVDIRI | - Opcode::MOVNTDQA | - Opcode::VMOVQ | - Opcode::VMOVSHDUP | - Opcode::VMOVSLDUP | - Opcode::VMOVUPD | - Opcode::VMOVUPS | - Opcode::VMOVSD | - Opcode::VMOVSS | - Opcode::VMOVDQA32 | - Opcode::VMOVDQA64 | - Opcode::VMOVDQU32 | - Opcode::VMOVDQU64 | - Opcode::VPMOVM2B | - Opcode::VPMOVM2W | - Opcode::VPMOVB2M | - Opcode::VPMOVW2M | - Opcode::VPMOVSWB | - Opcode::VPMOVUSWB | - Opcode::VPMOVSQB | - Opcode::VPMOVUSQB | - Opcode::VPMOVSQW | - Opcode::VPMOVUSQW | - Opcode::VPMOVSQD | - Opcode::VPMOVUSQD | - Opcode::VPMOVSDB | - Opcode::VPMOVUSDB | - Opcode::VPMOVSDW | - Opcode::VPMOVUSDW | - Opcode::VPMOVM2D | - Opcode::VPMOVM2Q | - Opcode::VPMOVB2D | - Opcode::VPMOVQ2M | - Opcode::VMOVDQU8 | - Opcode::VMOVDQU16 | - - Opcode::VPBLENDD | - Opcode::VPBLENDVB | - Opcode::VPBLENDW | - Opcode::VPBROADCASTB | - Opcode::VPBROADCASTD | - Opcode::VPBROADCASTQ | - Opcode::VPBROADCASTW | - Opcode::VPGATHERDD | - Opcode::VPGATHERDQ | - Opcode::VPGATHERQD | - Opcode::VPGATHERQQ | - Opcode::VPCLMULQDQ | - Opcode::VPMOVMSKB | - Opcode::VPMOVSXBD | - Opcode::VPMOVSXBQ | - Opcode::VPMOVSXBW | - Opcode::VPMOVSXDQ | - Opcode::VPMOVSXWD | - Opcode::VPMOVSXWQ | - Opcode::VPMOVZXBD | - Opcode::VPMOVZXBQ | - Opcode::VPMOVZXBW | - Opcode::VPMOVZXDQ | - Opcode::VPMOVZXWD | - Opcode::VPMOVZXWQ | - Opcode::PMOVSXBD | - Opcode::PMOVSXBQ | - Opcode::PMOVSXBW | - Opcode::PMOVSXDQ | - Opcode::PMOVSXWD | - Opcode::PMOVSXWQ | - Opcode::PMOVZXBD | - Opcode::PMOVZXBQ | - Opcode::PMOVZXBW | - Opcode::PMOVZXDQ | - Opcode::PMOVZXWD | - Opcode::PMOVZXWQ | - Opcode::KUNPCKBW | - Opcode::KUNPCKWD | - Opcode::KUNPCKDQ | - Opcode::VUNPCKHPD | - Opcode::VUNPCKHPS | - Opcode::VUNPCKLPD | - Opcode::VUNPCKLPS | - Opcode::VPUNPCKHBW | - Opcode::VPUNPCKHDQ | - Opcode::VPUNPCKHQDQ | - Opcode::VPUNPCKHWD | - Opcode::VPUNPCKLBW | - Opcode::VPUNPCKLDQ | - Opcode::VPUNPCKLQDQ | - Opcode::VPUNPCKLWD | - Opcode::VSHUFPD | - Opcode::VSHUFPS | - Opcode::VPACKSSDW | - Opcode::VPACKUSDW | - Opcode::PACKUSDW | - Opcode::VPACKSSWB | - Opcode::VPACKUSWB | - Opcode::VALIGND | - Opcode::VALIGNQ | - Opcode::VPALIGNR | - Opcode::PALIGNR | - Opcode::VPERM2F128 | - Opcode::VPERM2I128 | - Opcode::VPERMD | - Opcode::VPERMILPD | - Opcode::VPERMILPS | - Opcode::VPERMPD | - Opcode::VPERMPS | - Opcode::VPERMQ | - Opcode::VPERMI2D | - Opcode::VPERMI2Q | - Opcode::VPERMI2PD | - Opcode::VPERMI2PS | - Opcode::VPERMT2D | - Opcode::VPERMT2Q | - Opcode::VPERMT2PD | - Opcode::VPERMT2PS | - Opcode::VPERMI2B | - Opcode::VPERMI2W | - Opcode::VPERMW | - Opcode::VPEXTRB | - Opcode::VPEXTRD | - Opcode::VPEXTRQ | - Opcode::VPEXTRW | - Opcode::PEXTRB | - Opcode::PEXTRD | - Opcode::PEXTRQ | - Opcode::EXTRQ | - Opcode::PINSRB | - Opcode::PINSRD | - Opcode::PINSRQ | - Opcode::INSERTQ | - Opcode::VPINSRB | - Opcode::VPINSRD | - Opcode::VPINSRQ | - Opcode::VPINSRW | - Opcode::VPMASKMOVD | - Opcode::VPMASKMOVQ | - Opcode::VCOMPRESSPD | - Opcode::VCOMPRESSPS | - Opcode::VPCOMPRESSQ | - Opcode::VPCOMPRESSD | - Opcode::VEXPANDPD | - Opcode::VEXPANDPS | - Opcode::VPSHUFB | - Opcode::VPSHUFD | - Opcode::VPHMINPOSUW | - Opcode::PHMINPOSUW | - Opcode::VZEROUPPER | - Opcode::VZEROALL | - Opcode::VFIXUPIMMPD | - Opcode::VFIXUPIMMPS | - Opcode::VFIXUPIMMSD | - Opcode::VFIXUPIMMSS | - Opcode::VREDUCEPD | - Opcode::VREDUCEPS | - Opcode::VREDUCESD | - Opcode::VREDUCESS | - Opcode::VGETEXPPD | - Opcode::VGETEXPPS | - Opcode::VGETEXPSD | - Opcode::VGETEXPSS | - Opcode::VGETMANTPD | - Opcode::VGETMANTPS | - Opcode::VGETMANTSD | - Opcode::VGETMANTSS | - Opcode::VLDDQU | - Opcode::BSWAP | - Opcode::CVTDQ2PD | - Opcode::CVTDQ2PS | - Opcode::CVTPS2DQ | - Opcode::CVTPD2DQ | - Opcode::CVTPI2PS | - Opcode::CVTPI2PD | - Opcode::CVTPS2PD | - Opcode::CVTPD2PS | - Opcode::CVTPS2PI | - Opcode::CVTPD2PI | - Opcode::CVTSD2SI | - Opcode::CVTSD2SS | - Opcode::CVTSI2SD | - Opcode::CVTSI2SS | - Opcode::CVTSS2SD | - Opcode::CVTSS2SI | - Opcode::CVTTPD2DQ | - Opcode::CVTTPS2DQ | - Opcode::CVTTPS2PI | - Opcode::CVTTPD2PI | - Opcode::CVTTSD2SI | - Opcode::CVTTSS2SI | - Opcode::MASKMOVQ | - Opcode::MASKMOVDQU | - Opcode::MOVAPS | - Opcode::MOVAPD | - Opcode::MOVD | - Opcode::MOVHPS | - Opcode::MOVHPD | - Opcode::MOVHLPS | - Opcode::MOVLPS | - Opcode::MOVLPD | - Opcode::MOVLHPS | - Opcode::MOVMSKPS | - Opcode::MOVMSKPD | - Opcode::MOVNTI | - Opcode::MOVNTPS | - Opcode::MOVNTPD | - Opcode::MOVNTSS | - Opcode::MOVNTSD | - Opcode::MOVNTQ | - Opcode::MOVNTDQ | - Opcode::MOVSD | - Opcode::MOVSS | - Opcode::MOVUPD | - Opcode::PSHUFHW | - Opcode::PSHUFLW | - Opcode::PUNPCKHBW | - Opcode::PUNPCKHDQ | - Opcode::PUNPCKHWD | - Opcode::PUNPCKLBW | - Opcode::PUNPCKLDQ | - Opcode::PUNPCKLWD | - Opcode::PUNPCKLQDQ | - Opcode::PUNPCKHQDQ | - Opcode::PACKSSDW | - Opcode::PACKSSWB | - Opcode::PACKUSWB | - Opcode::UNPCKHPS | - Opcode::UNPCKHPD | - Opcode::UNPCKLPS | - Opcode::UNPCKLPD | - Opcode::SHUFPD | - Opcode::SHUFPS | - Opcode::PMOVMSKB | - Opcode::KMOVB | - Opcode::KMOVW | - Opcode::KMOVD | - Opcode::KMOVQ | - Opcode::BNDMOV | - Opcode::LDDQU | - Opcode::CMC | - Opcode::CLC | - Opcode::CLI | - Opcode::CLD | - Opcode::STC | - Opcode::STI | - Opcode::STD | - Opcode::CBW | - Opcode::CWDE | - Opcode::CDQE | - Opcode::CWD | - Opcode::CDQ | - Opcode::CQO | - Opcode::MOVDDUP | - Opcode::MOVSLDUP | - Opcode::MOVDQ2Q | - Opcode::MOVDQU | - Opcode::MOVDQA | - Opcode::MOVQ | - Opcode::MOVQ2DQ | - Opcode::MOVSHDUP | - Opcode::MOVUPS | - Opcode::PEXTRW | - Opcode::PINSRW | - Opcode::MOV | - Opcode::MOVBE | - Opcode::LODS | - Opcode::STOS | - Opcode::LAHF | - Opcode::SAHF | - Opcode::MOVS | - Opcode::INS | - Opcode::IN | - Opcode::OUTS | - Opcode::OUT | - Opcode::MOVZX | - Opcode::MOVSX | - Opcode::MOVSXD | - Opcode::FILD | - Opcode::FBLD | - Opcode::FBSTP | - Opcode::FIST | - Opcode::FISTP | - Opcode::FISTTP | - Opcode::FLD | - Opcode::FLD1 | - Opcode::FLDCW | - Opcode::FLDENV | - Opcode::FLDL2E | - Opcode::FLDL2T | - Opcode::FLDLG2 | - Opcode::FLDLN2 | - Opcode::FLDPI | - Opcode::FLDZ | - Opcode::FST | - Opcode::FSTP | - Opcode::FSTPNCE | - Opcode::FNSAVE | - Opcode::FNSTCW | - Opcode::FNSTENV | - Opcode::FNSTOR | - Opcode::FNSTSW | - Opcode::FRSTOR | - Opcode::FXCH | - Opcode::XCHG | - Opcode::XLAT | - Opcode::CMOVA | - Opcode::CMOVB | - Opcode::CMOVG | - Opcode::CMOVGE | - Opcode::CMOVL | - Opcode::CMOVLE | - Opcode::CMOVNA | - Opcode::CMOVNB | - Opcode::CMOVNO | - Opcode::CMOVNP | - Opcode::CMOVNS | - Opcode::CMOVNZ | - Opcode::CMOVO | - Opcode::CMOVP | - Opcode::CMOVS | - Opcode::CMOVZ | - Opcode::FCMOVB | - Opcode::FCMOVBE | - Opcode::FCMOVE | - Opcode::FCMOVNB | - Opcode::FCMOVNBE | - Opcode::FCMOVNE | - Opcode::FCMOVNU | - Opcode::FCMOVU | - Opcode::SALC | - Opcode::SETO | - Opcode::SETNO | - Opcode::SETB | - Opcode::SETAE | - Opcode::SETZ | - Opcode::SETNZ | - Opcode::SETBE | - Opcode::SETA | - Opcode::SETS | - Opcode::SETNS | - Opcode::SETP | - Opcode::SETNP | - Opcode::SETL | - Opcode::SETGE | - Opcode::SETLE | - Opcode::SETG => { write!(out, "{}", colors.data_op(self)) } - - Opcode::VCOMISD | - Opcode::VCOMISS | - Opcode::VUCOMISD | - Opcode::VUCOMISS | - Opcode::KORTESTB | - Opcode::KTESTB | - Opcode::KORTESTW | - Opcode::KTESTW | - Opcode::KORTESTD | - Opcode::KTESTD | - Opcode::KORTESTQ | - Opcode::KTESTQ | - Opcode::VPTESTNMD | - Opcode::VPTESTNMQ | - Opcode::VPTERNLOGD | - Opcode::VPTERNLOGQ | - Opcode::VPTESTMD | - Opcode::VPTESTMQ | - Opcode::VPTESTNMB | - Opcode::VPTESTNMW | - Opcode::VPTESTMB | - Opcode::VPTESTMW | - Opcode::VPCMPD | - Opcode::VPCMPUD | - Opcode::VPCMPQ | - Opcode::VPCMPUQ | - Opcode::VPCMPB | - Opcode::VPCMPUB | - Opcode::VPCMPW | - Opcode::VPCMPUW | - Opcode::VCMPPD | - Opcode::VCMPPS | - Opcode::VCMPSD | - Opcode::VCMPSS | - Opcode::VMAXPD | - Opcode::VMAXPS | - Opcode::VMAXSD | - Opcode::VMAXSS | - Opcode::VPMAXSQ | - Opcode::VPMAXUQ | - Opcode::VPMINSQ | - Opcode::VPMINUQ | - Opcode::VMINPD | - Opcode::VMINPS | - Opcode::VMINSD | - Opcode::VMINSS | - Opcode::VPCMPEQB | - Opcode::VPCMPEQD | - Opcode::VPCMPEQQ | - Opcode::VPCMPEQW | - Opcode::VPCMPGTB | - Opcode::VPCMPGTD | - Opcode::VPCMPGTQ | - Opcode::VPCMPGTW | - Opcode::VPCMPESTRI | - Opcode::VPCMPESTRM | - Opcode::VPCMPISTRI | - Opcode::VPCMPISTRM | - Opcode::VPMAXSB | - Opcode::VPMAXSD | - Opcode::VPMAXSW | - Opcode::VPMAXUB | - Opcode::VPMAXUW | - Opcode::VPMAXUD | - Opcode::VPMINSB | - Opcode::VPMINSW | - Opcode::VPMINSD | - Opcode::VPMINUB | - Opcode::VPMINUW | - Opcode::VPMINUD | - Opcode::VFPCLASSPD | - Opcode::VFPCLASSPS | - Opcode::VFPCLASSSD | - Opcode::VFPCLASSSS | - Opcode::VRANGEPD | - Opcode::VRANGEPS | - Opcode::VRANGESD | - Opcode::VRANGESS | - Opcode::VPCONFLICTD | - Opcode::VPCONFLICTQ | - Opcode::VPTEST | - Opcode::VTESTPD | - Opcode::VTESTPS | - Opcode::PCMPEQB | - Opcode::PCMPEQD | - Opcode::PCMPEQQ | - Opcode::PCMPEQW | - Opcode::PCMPESTRI | - Opcode::PCMPESTRM | - Opcode::PCMPGTB | - Opcode::PCMPGTD | - Opcode::PCMPGTQ | - Opcode::PCMPGTW | - Opcode::PCMPISTRI | - Opcode::PCMPISTRM | - Opcode::PTEST | - Opcode::MAXPD | - Opcode::MAXPS | - Opcode::MAXSD | - Opcode::MAXSS | - Opcode::MINPD | - Opcode::MINPS | - Opcode::MINSD | - Opcode::MINSS | - Opcode::PMAXSB | - Opcode::PMAXSD | - Opcode::PMAXSW | - Opcode::PMAXUB | - Opcode::PMAXUD | - Opcode::PMAXUW | - Opcode::PMINSB | - Opcode::PMINSD | - Opcode::PMINSW | - Opcode::PMINUB | - Opcode::PMINUD | - Opcode::PMINUW | - Opcode::PFCMPGE | - Opcode::PFMIN | - Opcode::PFCMPGT | - Opcode::PFMAX | - Opcode::PFCMPEQ | - Opcode::CMPS | - Opcode::SCAS | - Opcode::TEST | - Opcode::FTST | - Opcode::FXAM | - Opcode::FUCOM | - Opcode::FUCOMI | - Opcode::FUCOMIP | - Opcode::FUCOMP | - Opcode::FUCOMPP | - Opcode::FCOM | - Opcode::FCOMI | - Opcode::FCOMIP | - Opcode::FCOMP | - Opcode::FCOMPP | - Opcode::FICOM | - Opcode::FICOMP | - Opcode::CMPSD | - Opcode::CMPSS | - Opcode::CMP | - Opcode::CMPPS | - Opcode::CMPPD | - Opcode::CMPXCHG8B | - Opcode::CMPXCHG16B | - Opcode::CMPXCHG => { write!(out, "{}", colors.comparison_op(self)) } - - Opcode::WRMSR | - Opcode::RDMSR | - Opcode::RDTSC | - Opcode::RDPMC | - Opcode::RDPID | - Opcode::RDFSBASE | - Opcode::RDGSBASE | - Opcode::WRFSBASE | - Opcode::WRGSBASE | - Opcode::FXSAVE | - Opcode::FXRSTOR | - Opcode::LDMXCSR | - Opcode::STMXCSR | - Opcode::VLDMXCSR | - Opcode::VSTMXCSR | - Opcode::XSAVE | - Opcode::XSAVEC | - Opcode::XSAVES | - Opcode::XSAVEC64 | - Opcode::XSAVES64 | - Opcode::XRSTOR | - Opcode::XRSTORS | - Opcode::XRSTORS64 | - Opcode::XSAVEOPT | - Opcode::LFENCE | - Opcode::MFENCE | - Opcode::SFENCE | - Opcode::CLFLUSH | - Opcode::CLFLUSHOPT | - Opcode::CLWB | - Opcode::LDS | - Opcode::LES | - Opcode::SGDT | - Opcode::SIDT | - Opcode::LGDT | - Opcode::LIDT | - Opcode::SMSW | - Opcode::LMSW | - Opcode::SWAPGS | - Opcode::RDTSCP | - Opcode::INVEPT | - Opcode::INVVPID | - Opcode::INVPCID | - Opcode::INVLPG | - Opcode::INVLPGA | - Opcode::INVLPGB | - Opcode::TLBSYNC | - Opcode::PSMASH | - Opcode::PVALIDATE | - Opcode::RMPADJUST | - Opcode::RMPUPDATE | - Opcode::CPUID | - Opcode::WBINVD | - Opcode::INVD | - Opcode::SYSRET | - Opcode::CLTS | - Opcode::SYSCALL | - Opcode::TDCALL | - Opcode::SEAMRET | - Opcode::SEAMOPS | - Opcode::SEAMCALL | - Opcode::TPAUSE | - Opcode::UMONITOR | - Opcode::UMWAIT | - Opcode::LSL | - Opcode::SLDT | - Opcode::STR | - Opcode::LLDT | - Opcode::LTR | - Opcode::VERR | - Opcode::VERW | - Opcode::JMPE | - Opcode::EMMS | - Opcode::FEMMS | - Opcode::GETSEC | - Opcode::LFS | - Opcode::LGS | - Opcode::LSS | - Opcode::RSM | - Opcode::SYSENTER | - Opcode::SYSEXIT | - Opcode::VMREAD | - Opcode::VMWRITE | - Opcode::VMCLEAR | - Opcode::VMPTRLD | - Opcode::VMPTRST | - Opcode::VMXON | - Opcode::VMCALL | - Opcode::VMLAUNCH | - Opcode::VMRESUME | - Opcode::VMLOAD | - Opcode::VMMCALL | - Opcode::VMSAVE | - Opcode::VMRUN | - Opcode::VMXOFF | - Opcode::PCONFIG | - Opcode::MONITOR | - Opcode::MWAIT | - Opcode::MONITORX | - Opcode::MWAITX | - Opcode::SKINIT | - Opcode::CLGI | - Opcode::STGI | - Opcode::CLAC | - Opcode::STAC | - Opcode::ENCLS | - Opcode::ENCLV | - Opcode::XGETBV | - Opcode::XSETBV | - Opcode::VMFUNC | - Opcode::XEND | - Opcode::XTEST | - Opcode::XABORT | - Opcode::XBEGIN | - Opcode::ENCLU | - Opcode::RDPKRU | - Opcode::WRPKRU | - Opcode::RDPRU | - Opcode::CLZERO | - Opcode::ENQCMD | - Opcode::ENQCMDS | - Opcode::PTWRITE | - Opcode::UIRET | - Opcode::TESTUI | - Opcode::CLUI | - Opcode::STUI | - Opcode::SENDUIPI | - Opcode::XSUSLDTRK | - Opcode::XRESLDTRK | - Opcode::BOUND | - Opcode::ARPL | - Opcode::BNDMK | - Opcode::BNDCL | - Opcode::BNDCU | - Opcode::BNDCN | - Opcode::BNDLDX | - Opcode::BNDSTX | - Opcode::LAR => { write!(out, "{}", colors.platform_op(self)) } - - Opcode::CRC32 | - Opcode::RDSEED | - Opcode::RDRAND | - Opcode::SHA1RNDS4 | - Opcode::SHA1NEXTE | - Opcode::SHA1MSG1 | - Opcode::SHA1MSG2 | - Opcode::SHA256RNDS2 | - Opcode::SHA256MSG1 | - Opcode::SHA256MSG2 | - Opcode::FFREE | - Opcode::FFREEP | - Opcode::FDECSTP | - Opcode::FINCSTP | - Opcode::GF2P8MULB | - Opcode::GF2P8AFFINEQB | - Opcode::GF2P8AFFINEINVQB | - Opcode::AESDEC128KL | - Opcode::AESDEC256KL | - Opcode::AESDECWIDE128KL | - Opcode::AESDECWIDE256KL | - Opcode::AESENC128KL | - Opcode::AESENC256KL | - Opcode::AESENCWIDE128KL | - Opcode::AESENCWIDE256KL | - Opcode::ENCODEKEY128 | - Opcode::ENCODEKEY256 | - Opcode::LOADIWKEY | - Opcode::HRESET | - Opcode::WRUSS | - Opcode::WRSS | - Opcode::INCSSP | - Opcode::SAVEPREVSSP | - Opcode::SETSSBSY | - Opcode::CLRSSBSY | - Opcode::RSTORSSP | - Opcode::ENDBR64 | - Opcode::ENDBR32 | - Opcode::AESDEC | - Opcode::AESDECLAST | - Opcode::AESENC | - Opcode::AESENCLAST | - Opcode::AESIMC | - Opcode::AESKEYGENASSIST | - Opcode::VAESDEC | - Opcode::VAESDECLAST | - Opcode::VAESENC | - Opcode::VAESENCLAST | - Opcode::VAESIMC | - Opcode::VAESKEYGENASSIST => { write!(out, "{}", colors.misc_op(self)) } - - Opcode::UD0 | - Opcode::UD1 | - Opcode::UD2 | - Opcode::Invalid => { write!(out, "{}", colors.invalid_op(self)) } - } + fn colorize(&self, _colors: &Y, out: &mut T) -> fmt::Result { + // see `impl Colorize for long_mode::Opcode for more about this + out.write_str(self.name()) } } impl fmt::Display for Instruction { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + // to reuse one implementation, call the deprecated function for now. + #[allow(deprecated)] self.display_with(DisplayStyle::Intel).colorize(&NoColors, fmt) } } impl<'instr> fmt::Display for InstructionDisplayer<'instr> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + // to reuse one implementation, call the deprecated function for now. + #[allow(deprecated)] self.colorize(&NoColors, fmt) } } @@ -3347,6 +2168,8 @@ pub struct InstructionDisplayer<'instr> { * so write to some Write thing i guess. bite me. i really just want to * stop thinking about how to support printing instructions... */ +// allowing these deprecated items for the time being, not yet breaking yaxpeax-x86 apis +#[allow(deprecated)] impl <'instr, T: fmt::Write, Y: YaxColors> Colorize 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, @@ -3359,160 +2182,156 @@ impl <'instr, T: fmt::Write, Y: YaxColors> Colorize for InstructionDisplay struct NoContext; impl Instruction { + /// format this instruction into `out` as a plain text string. + #[cfg_attr(feature="profiling", inline(never))] pub fn write_to(&self, out: &mut T) -> fmt::Result { - self.display_with(DisplayStyle::Intel).contextualize(&NoColors, 0, Some(&NoContext), out) + let mut out = yaxpeax_arch::display::FmtSink::new(out); + contextualize_intel(self, &mut out) + } + + /// format this instruction into `out`, which may perform additional styling based on its + /// `DisplaySink` implementation. + #[cfg_attr(feature="profiling", inline(never))] + pub fn display_into(&self, out: &mut T) -> fmt::Result { + contextualize_intel(self, out) } } -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 [Opcode::MOVS, Opcode::CMPS, Opcode::LODS, Opcode::STOS, Opcode::INS, Opcode::OUTS].contains(&instr.opcode) { + 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::JCXZ, - 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 - 1])?; - 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 - 1])?; - 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(", ")?; + // hint that accessing `inster.operands[i]` can't panic: this is useful for + // `instr.operands` and the segment selector check after. + if i >= 4 { + // Safety: Instruction::operands is a four-element array; operand_count is always + // low enough that 0..operand_count is a valid index. + unsafe { unreachable_unchecked(); } + } + + if instr.operands[i as usize].is_memory() { + out.write_mem_size_label(instr.mem_size)?; + 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('}')?; } } } @@ -3520,7 +2339,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; @@ -3544,7 +2363,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", @@ -3566,21 +2385,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))) + Operand::ImmediateI8 { imm: rel } => { + 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))) + Operand::ImmediateI32 { imm: 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) @@ -3743,87 +2570,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::JCXZ => { out.write_str("if cx == 0 then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::LOOP => { out.write_str("cx--; if cx != 0 then jmp ")?; - write_jmp_operand(instr.operand(0), colors, out)?; + write_jmp_operand(instr.operand(0), out)?; }, Opcode::LOOPZ => { out.write_str("cx--; if cx != 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("cx--; if cx != 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")?; @@ -3854,24 +2681,30 @@ fn contextualize_c(instr: &Instruction, colors: &Y, Ok(()) } +// allowing these deprecated items for the time being, not yet breaking yaxpeax-x86 apis +#[allow(deprecated)] 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) } } } } +// allowing these deprecated items for the time being, not yet breaking yaxpeax-x86 apis +#[allow(deprecated)] #[cfg(feature="std")] impl ShowContextual], T, Y> for Instruction { fn contextualize(&self, colors: &Y, _address: u64, context: Option<&[Option]>, out: &mut T) -> fmt::Result { @@ -3894,7 +2727,7 @@ impl ShowContextual { write!(out, " {}", s)?; }, None => { match self.operands[0] { - OperandSpec::Nothing => { + super::OperandSpec::Nothing => { return Ok(()); }, _ => { @@ -3914,7 +2747,7 @@ impl ShowContextual { write!(out, ", {}", s)? } None => { match &self.operands[i] { - &OperandSpec::Nothing => { + &super::OperandSpec::Nothing => { return Ok(()); }, _ => { @@ -3932,3 +2765,245 @@ 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, _base: RegSpec) -> Result { + Ok(false) + } + fn visit_disp(&mut self, _base: 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('$')?; + let mut v = rel as u8; + if rel < 0 { + self.out.write_char('-')?; + v = rel.unsigned_abs(); + } else { + self.out.write_char('+')?; + } + 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('$')?; + let mut v = rel as u32; + if rel < 0 { + self.out.write_char('-')?; + v = rel.unsigned_abs(); + } else { + self.out.write_char('+')?; + } + 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_index_scale(&mut self, _index: RegSpec, _scale: u8) -> Result { + Ok(false) + } + fn visit_base_index_scale(&mut self, _base: RegSpec, _index: RegSpec, _scale: u8) -> Result { + Ok(false) + } + fn visit_index_scale_disp(&mut self, _index: RegSpec, _scale: u8, _disp: i32) -> Result { + Ok(false) + } + fn visit_base_index_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_disp_masked(&mut self, _base: RegSpec, _disp: i32, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_deref_masked(&mut self, _base: RegSpec, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_index_scale_masked(&mut self, _index: RegSpec, _scale: u8, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_index_scale_disp_masked(&mut self, _index: RegSpec, _scale: u8, _disp: i32, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_base_index_masked(&mut self, _base: RegSpec, _index: RegSpec, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_base_index_disp_masked(&mut self, _base: RegSpec, _index: RegSpec, _disp: i32, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_base_index_scale_masked(&mut self, _base: RegSpec, _index: RegSpec, _scale: u8, _mask_reg: RegSpec) -> Result { + Ok(false) + } + fn visit_base_index_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) + } +} + +#[cfg(feature="alloc")] +mod buffer_sink { + use core::fmt; + use super::super::{DisplayStyle, InstructionDisplayer}; + use super::{contextualize_c, contextualize_intel}; + + /// helper to format `amd64` instructions with highest throughput and least configuration. this is + /// functionally a buffer for one x86 instruction's text. + /// + /// ### when to use this over `fmt::Display`? + /// + /// `fmt::Display` is a fair choice in most cases. in some cases, `InstructionTextBuffer` may + /// support formatting options that may be difficult to configure for a `Display` impl. + /// additionally, `InstructionTextBuffer` may be able to specialize more effectively where + /// `fmt::Display`, writing to a generic `fmt::Write`, may not. + /// + /// if your use case for `yaxpeax-x86` involves being bounded on the speed of disassembling and + /// formatting instructions, [`InstructionTextBuffer::format_inst`] has been measured as up to 11% + /// faster than an equivalent `write!(buf, "{}", inst)`. + /// + /// `InstructionTextBuffer` involves internal allocations; if your use case for `yaxpeax-x86` + /// requires allocations never occurring, it is not an appropriate tool. + /// + /// ### example + /// + /// ``` + /// use yaxpeax_x86::long_mode::InstDecoder; + /// use yaxpeax_x86::long_mode::InstructionTextBuffer; + /// use yaxpeax_x86::long_mode::DisplayStyle; + /// + /// let bytes = &[0x33, 0xc0]; + /// let inst = InstDecoder::default().decode_slice(bytes).expect("can decode"); + /// let mut text_buf = InstructionTextBuffer::new(); + /// assert_eq!( + /// text_buf.format_inst(&inst.display_with(DisplayStyle::Intel)).expect("can format"), + /// "xor eax, eax" + /// ); + /// + /// // or, getting the formatted instruction with `text_str`: + /// assert_eq!( + /// text_buf.text_str(), + /// "xor eax, eax" + /// ); + /// ``` + pub struct InstructionTextBuffer { + content: alloc::string::String, + } + + impl InstructionTextBuffer { + /// create an `InstructionTextBuffer` with default settings. `InstructionTextBuffer`'s default + /// settings format instructions identically to their corresponding `fmt::Display`. + pub fn new() -> Self { + let mut buf = alloc::string::String::new(); + buf.reserve(crate::MAX_INSTRUCTION_LEN); + Self { + content: buf, + } + } + + /// format `inst` into this buffer. returns a borrow of that same internal buffer for convenience. + /// + /// this clears and reuses an internal buffer; if an instruction had been previously formatted + /// through this buffer, it will be overwritten. + pub fn format_inst<'buf, 'instr>(&'buf mut self, display: &InstructionDisplayer<'instr>) -> Result<&'buf str, fmt::Error> { + // Safety: this sink is used to format exactly one instruction and then dropped. it can + // never escape `format_inst`. + let mut handle = unsafe { self.write_handle() }; + + match display.style { + DisplayStyle::Intel => { + contextualize_intel(&display.instr, &mut handle)?; + } + DisplayStyle::C => { + contextualize_c(&display.instr, &mut handle)?; + } + } + + Ok(self.text_str()) + } + + /// return a borrow of the internal buffer. if an instruction has been formatted, the + /// returned `&str` contains that instruction's buffered text. + pub fn text_str(&self) -> &str { + self.content.as_str() + } + + /// do the necessary bookkeeping and provide an `InstructionTextSink` to write an instruction + /// into. + /// + /// SAFETY: callers must print at most one instruction into this handle. + unsafe fn write_handle(&mut self) -> yaxpeax_arch::display::InstructionTextSink { + self.content.clear(); + // Safety: `content` was just cleared, so writing begins at the start of the buffer. + // `content`is large enough to hold a fully-formatted instruction (see + // `InstructionTextBuffer::new`). + yaxpeax_arch::display::InstructionTextSink::new(&mut self.content) + } + } +} +#[cfg(feature="alloc")] +pub use buffer_sink::InstructionTextBuffer; diff --git a/src/real_mode/mod.rs b/src/real_mode/mod.rs index 68ab6dd3..b06a5255 100644 --- a/src/real_mode/mod.rs +++ b/src/real_mode/mod.rs @@ -8,13 +8,15 @@ pub use crate::MemoryAccessSize; #[cfg(feature = "fmt")] pub use self::display::{DisplayStyle, InstructionDisplayer}; +#[cfg(all(feature = "fmt", feature = "alloc"))] +pub use self::display::InstructionTextBuffer; use core::cmp::PartialEq; -use crate::safer_unchecked::unreachable_kinda_unchecked as unreachable_unchecked; use yaxpeax_arch::{AddressDiff, Decoder, Reader, LengthedInstruction}; use yaxpeax_arch::annotation::{AnnotatingDecoder, DescriptionSink, NullSink}; use yaxpeax_arch::{DecodeError as ArchDecodeError}; +use yaxpeax_arch::safer_unchecked::unreachable_kinda_unchecked as unreachable_unchecked; use core::fmt; impl fmt::Display for DecodeError { @@ -304,87 +306,77 @@ enum SizeCode { #[non_exhaustive] pub enum Operand { /// a sign-extended byte - ImmediateI8(i8), + ImmediateI8 { imm: i8 }, /// a zero-extended byte - ImmediateU8(u8), + ImmediateU8 { imm: u8 }, /// a sign-extended word - ImmediateI16(i16), + ImmediateI16 { imm: i16 }, /// a zero-extended word - ImmediateU16(u16), + ImmediateU16 { imm: u16 }, /// a sign-extended dword - ImmediateI32(i32), + ImmediateI32 { imm: i32 }, /// a zero-extended dword - ImmediateU32(u32), + ImmediateU32 { imm: u32 }, /// a bare register operand, such as `rcx`. - Register(RegSpec), + Register { reg: RegSpec }, /// an `avx512` register operand with optional mask register and merge mode, such as /// `zmm3{k4}{z}`. /// /// if the mask register is `k0`, there is no masking applied, and the default x86 operation is /// `MergeMode::Merge`. - RegisterMaskMerge(RegSpec, RegSpec, MergeMode), + RegisterMaskMerge { reg: RegSpec, mask: RegSpec, merge: MergeMode }, /// an `avx512` register operand with optional mask register, merge mode, and suppressed /// exceptions, such as `zmm3{k4}{z}{rd-sae}`. /// /// if the mask register is `k0`, there is no masking applied, and the default x86 operation is /// `MergeMode::Merge`. - RegisterMaskMergeSae(RegSpec, RegSpec, MergeMode, SaeMode), + RegisterMaskMergeSae { reg: RegSpec, mask: RegSpec, merge: MergeMode, sae: SaeMode }, /// an `avx512` register operand with optional mask register, merge mode, and suppressed /// exceptions, with no overridden rounding mode, such as `zmm3{k4}{z}{sae}`. /// /// if the mask register is `k0`, there is no masking applied, and the default x86 operation is /// `MergeMode::Merge`. - RegisterMaskMergeSaeNoround(RegSpec, RegSpec, MergeMode), + RegisterMaskMergeSaeNoround { reg: RegSpec, mask: RegSpec, merge: MergeMode }, /// a memory access to a literal word address. it's extremely rare that a well-formed x86 /// instruction uses this mode. as an example, `[0x1133]` - DisplacementU16(u16), + AbsoluteU16 { addr: u16 }, /// a memory access to a literal qword address. it's relatively rare that a well-formed x86 /// instruction uses this mode, but plausible. for example, `fs:[0x14]`. segment overrides, /// however, are maintained on the instruction itself. - DisplacementU32(u32), + AbsoluteU32 { addr: u32 }, /// a simple dereference of the address held in some register. for example: `[esi]`. - RegDeref(RegSpec), + MemDeref { base: RegSpec }, /// a dereference of the address held in some register with offset. for example: `[esi + 0x14]`. - RegDisp(RegSpec, i32), + MemDisp { base: RegSpec, disp: i32 }, /// a dereference of the address held in some register scaled by 1, 2, 4, or 8. this is almost always used with the `lea` instruction. for example: `[edx * 4]`. - RegScale(RegSpec, u8), - /// a dereference of the address from summing two registers. for example: `[ebp + rax]` - RegIndexBase(RegSpec, RegSpec), - /// a dereference of the address from summing two registers with offset. for example: `[edi + ecx + 0x40]` - RegIndexBaseDisp(RegSpec, RegSpec, i32), + MemIndexScale { index: RegSpec, scale: u8 }, /// a dereference of the address held in some register scaled by 1, 2, 4, or 8 with offset. this is almost always used with the `lea` instruction. for example: `[eax * 4 + 0x30]`. - RegScaleDisp(RegSpec, u8, i32), + MemIndexScaleDisp { index: RegSpec, scale: u8, disp: i32 }, /// a dereference of the address from summing a register and index register scaled by 1, 2, 4, /// or 8. for /// example: `[esi + ecx * 4]` - RegIndexBaseScale(RegSpec, RegSpec, u8), + MemBaseIndexScale { base: RegSpec, index: RegSpec, scale: u8 }, /// a dereference of the address from summing a register and index register scaled by 1, 2, 4, /// or 8, with offset. for /// example: `[esi + ecx * 4 + 0x1234]` - RegIndexBaseScaleDisp(RegSpec, RegSpec, u8, i32), + MemBaseIndexScaleDisp { base: RegSpec, index: RegSpec, scale: u8, disp: i32 }, /// an `avx512` dereference of register with optional masking. for example: `[edx]{k3}` - RegDerefMasked(RegSpec, RegSpec), + MemDerefMasked { base: RegSpec, mask: RegSpec }, /// an `avx512` dereference of register plus offset, with optional masking. for example: `[esp + 0x40]{k3}` - RegDispMasked(RegSpec, i32, RegSpec), + MemDispMasked { base: RegSpec, disp: i32, mask: RegSpec }, /// an `avx512` dereference of a register scaled by 1, 2, 4, or 8, with optional masking. this /// seems extraordinarily unlikely to occur in practice. for example: `[esi * 4]{k2}` - RegScaleMasked(RegSpec, u8, RegSpec), - /// an `avx512` dereference of a register plus index scaled by 1, 2, 4, or 8, with optional masking. - /// for example: `[esi + eax * 4]{k6}` - RegIndexBaseMasked(RegSpec, RegSpec, RegSpec), - /// an `avx512` dereference of a register plus offset, with optional masking. for example: - /// `[esi + eax + 0x1313]{k6}` - RegIndexBaseDispMasked(RegSpec, RegSpec, i32, RegSpec), + MemIndexScaleMasked { index: RegSpec, scale: u8, mask: RegSpec }, /// an `avx512` dereference of a register scaled by 1, 2, 4, or 8 plus offset, with optional /// masking. this seems extraordinarily unlikely to occur in practice. for example: `[esi * /// 4 + 0x1357]{k2}` - RegScaleDispMasked(RegSpec, u8, i32, RegSpec), + MemIndexScaleDispMasked { index: RegSpec, scale: u8, disp: i32, mask: RegSpec }, /// an `avx512` dereference of a register plus index scaled by 1, 2, 4, or 8, with optional /// masking. for example: `[esi + eax * 4]{k6}` - RegIndexBaseScaleMasked(RegSpec, RegSpec, u8, RegSpec), + MemBaseIndexScaleMasked { base: RegSpec, index: RegSpec, scale: u8, mask: RegSpec }, /// an `avx512` dereference of a register plus index scaled by 1, 2, 4, or 8 and offset, with /// optional masking. for example: `[esi + eax * 4 + 0x1313]{k6}` - RegIndexBaseScaleDispMasked(RegSpec, RegSpec, u8, i32, RegSpec), + MemBaseIndexScaleDispMasked { base: RegSpec, index: RegSpec, scale: u8, disp: i32, mask: RegSpec }, /// no operand. it is a bug for `yaxpeax-x86` to construct an `Operand` of this kind for public /// use; the instruction's `operand_count` should be reduced so as to make this invisible to /// library clients. @@ -401,11 +393,11 @@ impl OperandSpec { OperandSpec::RegMMM => OperandSpec::RegMMM_maskmerge, OperandSpec::RegVex => OperandSpec::RegVex_maskmerge, OperandSpec::Deref => OperandSpec::Deref_mask, - OperandSpec::RegDisp => OperandSpec::RegDisp_mask, - OperandSpec::RegScale => OperandSpec::RegScale_mask, - OperandSpec::RegScaleDisp => OperandSpec::RegScaleDisp_mask, - OperandSpec::RegIndexBaseScale => OperandSpec::RegIndexBaseScale_mask, - OperandSpec::RegIndexBaseScaleDisp => OperandSpec::RegIndexBaseScaleDisp_mask, + OperandSpec::Disp => OperandSpec::Disp_mask, + OperandSpec::MemIndexScale => OperandSpec::MemIndexScale_mask, + OperandSpec::MemIndexScaleDisp => OperandSpec::MemIndexScaleDisp_mask, + OperandSpec::MemBaseIndexScale => OperandSpec::MemBaseIndexScale_mask, + OperandSpec::MemBaseIndexScaleDisp => OperandSpec::MemBaseIndexScaleDisp_mask, o => o, } } @@ -450,14 +442,14 @@ impl SaeMode { /// a human-friendly label for this `SaeMode`: /// /// ``` - /// use yaxpeax_x86::long_mode::SaeMode; + /// use yaxpeax_x86::real_mode::SaeMode; /// /// assert_eq!(SaeMode::RoundNearest.label(), "{rne-sae}"); /// assert_eq!(SaeMode::RoundDown.label(), "{rd-sae}"); /// assert_eq!(SaeMode::RoundUp.label(), "{ru-sae}"); /// assert_eq!(SaeMode::RoundZero.label(), "{rz-sae}"); /// ``` - pub fn label(&self) -> &'static str { + pub const fn label(&self) -> &'static str { match self { SaeMode::RoundNearest => "{rne-sae}", SaeMode::RoundDown => "{rd-sae}", @@ -477,6 +469,42 @@ impl SaeMode { SAE_MODES[idx] } } + +pub trait OperandVisitor { + type Ok; + type Error; + + fn visit_reg(&mut self, reg: RegSpec) -> Result; + fn visit_deref(&mut self, base: RegSpec) -> Result; + fn visit_disp(&mut self, base: RegSpec, disp: i32) -> Result; + fn visit_index_scale(&mut self, index: RegSpec, scale: u8) -> Result; + fn visit_base_index_scale(&mut self, base: RegSpec, index: RegSpec, scale: u8) -> Result; + fn visit_base_index_scale_disp(&mut self, base: RegSpec, index: RegSpec, scale: u8, disp: i32) -> Result; + fn visit_index_scale_disp(&mut self, index: RegSpec, scale: u8, disp: i32) -> Result; + fn visit_i8(&mut self, imm: i8) -> Result; + fn visit_u8(&mut self, imm: u8) -> Result; + fn visit_i16(&mut self, imm: i16) -> Result; + fn visit_u16(&mut self, imm: u16) -> Result; + fn visit_i32(&mut self, imm: i32) -> Result; + fn visit_u32(&mut self, imm: u32) -> Result; + fn visit_abs_u16(&mut self, imm: u16) -> Result; + fn visit_abs_u32(&mut self, imm: u32) -> Result; + fn visit_reg_mask_merge(&mut self, base: RegSpec, mask: RegSpec, merge_mode: MergeMode) -> Result; + fn visit_reg_mask_merge_sae(&mut self, base: RegSpec, mask: RegSpec, merge_mode: MergeMode, sae_mode: SaeMode) -> Result; + fn visit_reg_mask_merge_sae_noround(&mut self, base: RegSpec, mask: RegSpec, merge_mode: MergeMode) -> Result; + fn visit_disp_masked(&mut self, base: RegSpec, disp: i32, mask_reg: RegSpec) -> Result; + fn visit_deref_masked(&mut self, base: RegSpec, mask_reg: RegSpec) -> Result; + fn visit_index_scale_masked(&mut self, index: RegSpec, scale: u8, mask_reg: RegSpec) -> Result; + fn visit_index_scale_disp_masked(&mut self, index: RegSpec, scale: u8, disp: i32, mask_reg: RegSpec) -> Result; + fn visit_base_index_masked(&mut self, base: RegSpec, index: RegSpec, mask_reg: RegSpec) -> Result; + fn visit_base_index_disp_masked(&mut self, base: RegSpec, index: RegSpec, disp: i32, mask_reg: RegSpec) -> Result; + fn visit_base_index_scale_masked(&mut self, base: RegSpec, index: RegSpec, scale: u8, mask_reg: RegSpec) -> Result; + fn visit_base_index_scale_disp_masked(&mut self, base: RegSpec, index: RegSpec, scale: u8, disp: i32, mask_reg: RegSpec) -> Result; + fn visit_absolute_far_address(&mut self, segment: u16, address: u32) -> Result; + + fn visit_other(&mut self) -> Result; +} + impl Operand { fn from_spec(inst: &Instruction, spec: OperandSpec) -> Operand { match spec { @@ -485,139 +513,139 @@ impl Operand { } // the register in regs[0] OperandSpec::RegRRR => { - Operand::Register(inst.regs[0]) + Operand::Register { reg: inst.regs[0] } } OperandSpec::RegRRR_maskmerge => { - Operand::RegisterMaskMerge( - inst.regs[0], - RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), - MergeMode::from(inst.prefixes.evex_unchecked().merge()), - ) + Operand::RegisterMaskMerge { + reg: inst.regs[0], + mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), + merge: MergeMode::from(inst.prefixes.evex_unchecked().merge()), + } } OperandSpec::RegRRR_maskmerge_sae => { - Operand::RegisterMaskMergeSae( - inst.regs[0], - RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), - MergeMode::from(inst.prefixes.evex_unchecked().merge()), - SaeMode::from(inst.prefixes.evex_unchecked().vex().l(), inst.prefixes.evex_unchecked().lp()), - ) + Operand::RegisterMaskMergeSae { + reg: inst.regs[0], + mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), + merge: MergeMode::from(inst.prefixes.evex_unchecked().merge()), + sae: SaeMode::from(inst.prefixes.evex_unchecked().vex().l(), inst.prefixes.evex_unchecked().lp()), + } } OperandSpec::RegRRR_maskmerge_sae_noround => { - Operand::RegisterMaskMergeSaeNoround( - inst.regs[0], - RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), - MergeMode::from(inst.prefixes.evex_unchecked().merge()), - ) + Operand::RegisterMaskMergeSaeNoround { + reg: inst.regs[0], + mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), + merge: MergeMode::from(inst.prefixes.evex_unchecked().merge()), + } } // the register in regs[1] (eg modrm mod bits were 11) OperandSpec::RegMMM => { - Operand::Register(inst.regs[1]) + Operand::Register { reg: inst.regs[1] } } OperandSpec::RegMMM_maskmerge => { - Operand::RegisterMaskMerge( - inst.regs[1], - RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), - MergeMode::from(inst.prefixes.evex_unchecked().merge()), - ) + Operand::RegisterMaskMerge { + reg: inst.regs[1], + mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), + merge: MergeMode::from(inst.prefixes.evex_unchecked().merge()), + } } OperandSpec::RegMMM_maskmerge_sae_noround => { - Operand::RegisterMaskMergeSaeNoround( - inst.regs[1], - RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), - MergeMode::from(inst.prefixes.evex_unchecked().merge()), - ) + Operand::RegisterMaskMergeSaeNoround { + reg: inst.regs[1], + mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), + merge: MergeMode::from(inst.prefixes.evex_unchecked().merge()), + } } OperandSpec::RegVex => { - Operand::Register(inst.regs[3]) + Operand::Register { reg: inst.regs[3] } } OperandSpec::RegVex_maskmerge => { - Operand::RegisterMaskMerge( - inst.regs[3], - RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), - MergeMode::from(inst.prefixes.evex_unchecked().merge()), - ) + Operand::RegisterMaskMerge { + reg: inst.regs[3], + mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()), + merge: MergeMode::from(inst.prefixes.evex_unchecked().merge()), + } } OperandSpec::Reg4 => { - Operand::Register(RegSpec { num: inst.imm as u8, bank: inst.regs[3].bank }) - } - OperandSpec::ImmI8 => Operand::ImmediateI8(inst.imm as i8), - OperandSpec::ImmU8 => Operand::ImmediateU8(inst.imm as u8), - OperandSpec::ImmI16 => Operand::ImmediateI16(inst.imm as i16), - OperandSpec::ImmU16 => Operand::ImmediateU16(inst.imm as u16), - OperandSpec::ImmI32 => Operand::ImmediateI32(inst.imm as i32), - OperandSpec::ImmInDispField => Operand::ImmediateU16(inst.disp as u16), - OperandSpec::DispU16 => Operand::DisplacementU16(inst.disp as u16), - OperandSpec::DispU32 => Operand::DisplacementU32(inst.disp), + Operand::Register { reg: RegSpec { num: inst.imm as u8, bank: inst.regs[3].bank } } + } + OperandSpec::ImmI8 => Operand::ImmediateI8 { imm: inst.imm as i8 }, + OperandSpec::ImmU8 => Operand::ImmediateU8 { imm: inst.imm as u8 }, + OperandSpec::ImmI16 => Operand::ImmediateI16 { imm: inst.imm as i16 }, + OperandSpec::ImmU16 => Operand::ImmediateU16 { imm: inst.imm as u16 }, + OperandSpec::ImmI32 => Operand::ImmediateI32 { imm: inst.imm as i32 }, + OperandSpec::ImmInDispField => Operand::ImmediateU16 { imm: inst.disp as u16 }, + OperandSpec::DispU16 => Operand::AbsoluteU16 { addr: inst.disp as u16 }, + OperandSpec::DispU32 => Operand::AbsoluteU32 { addr: inst.disp }, OperandSpec::Deref => { - Operand::RegDeref(inst.regs[1]) + Operand::MemDeref { base: inst.regs[1] } } OperandSpec::Deref_si => { - Operand::RegDeref(RegSpec::si()) + Operand::MemDeref { base: RegSpec::si() } } OperandSpec::Deref_di => { - Operand::RegDeref(RegSpec::di()) + Operand::MemDeref { base: RegSpec::di() } } OperandSpec::Deref_esi => { - Operand::RegDeref(RegSpec::esi()) + Operand::MemDeref { base: RegSpec::esi() } } OperandSpec::Deref_edi => { - Operand::RegDeref(RegSpec::edi()) + Operand::MemDeref { base: RegSpec::edi() } } - OperandSpec::RegDisp => { - Operand::RegDisp(inst.regs[1], inst.disp as i32) + OperandSpec::Disp => { + Operand::MemDisp { base: inst.regs[1], disp: inst.disp as i32 } } - OperandSpec::RegScale => { - Operand::RegScale(inst.regs[2], inst.scale) + OperandSpec::MemIndexScale => { + Operand::MemIndexScale { index: inst.regs[2], scale: inst.scale } } - OperandSpec::RegScaleDisp => { - Operand::RegScaleDisp(inst.regs[2], inst.scale, inst.disp as i32) + OperandSpec::MemIndexScaleDisp => { + Operand::MemIndexScaleDisp { index: inst.regs[2], scale: inst.scale, disp: inst.disp as i32 } } - OperandSpec::RegIndexBaseScale => { - Operand::RegIndexBaseScale(inst.regs[1], inst.regs[2], inst.scale) + OperandSpec::MemBaseIndexScale => { + Operand::MemBaseIndexScale { base: inst.regs[1], index: inst.regs[2], scale: inst.scale } } - OperandSpec::RegIndexBaseScaleDisp => { - Operand::RegIndexBaseScaleDisp(inst.regs[1], inst.regs[2], inst.scale, inst.disp as i32) + OperandSpec::MemBaseIndexScaleDisp => { + Operand::MemBaseIndexScaleDisp { base: inst.regs[1], index: inst.regs[2], scale: inst.scale, disp: inst.disp as i32 } } OperandSpec::Deref_mask => { if inst.prefixes.evex_unchecked().mask_reg() != 0 { - Operand::RegDerefMasked(inst.regs[1], RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg())) + Operand::MemDerefMasked { base: inst.regs[1], mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()) } } else { - Operand::RegDeref(inst.regs[1]) + Operand::MemDeref { base: inst.regs[1] } } } - OperandSpec::RegDisp_mask => { + OperandSpec::Disp_mask => { if inst.prefixes.evex_unchecked().mask_reg() != 0 { - Operand::RegDispMasked(inst.regs[1], inst.disp as i32, RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg())) + Operand::MemDispMasked { base: inst.regs[1], disp: inst.disp as i32, mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()) } } else { - Operand::RegDisp(inst.regs[1], inst.disp as i32) + Operand::MemDisp { base: inst.regs[1], disp: inst.disp as i32 } } } - OperandSpec::RegScale_mask => { + OperandSpec::MemIndexScale_mask => { if inst.prefixes.evex_unchecked().mask_reg() != 0 { - Operand::RegScaleMasked(inst.regs[2], inst.scale, RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg())) + Operand::MemIndexScaleMasked { index: inst.regs[2], scale: inst.scale, mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()) } } else { - Operand::RegScale(inst.regs[2], inst.scale) + Operand::MemIndexScale { index: inst.regs[2], scale: inst.scale } } } - OperandSpec::RegScaleDisp_mask => { + OperandSpec::MemIndexScaleDisp_mask => { if inst.prefixes.evex_unchecked().mask_reg() != 0 { - Operand::RegScaleDispMasked(inst.regs[2], inst.scale, inst.disp as i32, RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg())) + Operand::MemIndexScaleDispMasked { index: inst.regs[2], scale: inst.scale, disp: inst.disp as i32, mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()) } } else { - Operand::RegScaleDisp(inst.regs[2], inst.scale, inst.disp as i32) + Operand::MemIndexScaleDisp { index: inst.regs[2], scale: inst.scale, disp: inst.disp as i32 } } } - OperandSpec::RegIndexBaseScale_mask => { + OperandSpec::MemBaseIndexScale_mask => { if inst.prefixes.evex_unchecked().mask_reg() != 0 { - Operand::RegIndexBaseScaleMasked(inst.regs[1], inst.regs[2], inst.scale, RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg())) + Operand::MemBaseIndexScaleMasked { base: inst.regs[1], index: inst.regs[2], scale: inst.scale, mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()) } } else { - Operand::RegIndexBaseScale(inst.regs[1], inst.regs[2], inst.scale) + Operand::MemBaseIndexScale { base: inst.regs[1], index: inst.regs[2], scale: inst.scale } } } - OperandSpec::RegIndexBaseScaleDisp_mask => { + OperandSpec::MemBaseIndexScaleDisp_mask => { if inst.prefixes.evex_unchecked().mask_reg() != 0 { - Operand::RegIndexBaseScaleDispMasked(inst.regs[1], inst.regs[2], inst.scale, inst.disp as i32, RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg())) + Operand::MemBaseIndexScaleDispMasked { base: inst.regs[1], index: inst.regs[2], scale: inst.scale, disp: inst.disp as i32, mask: RegSpec::mask(inst.prefixes.evex_unchecked().mask_reg()) } } else { - Operand::RegIndexBaseScaleDisp(inst.regs[1], inst.regs[2], inst.scale, inst.disp as i32) + Operand::MemBaseIndexScaleDisp { base: inst.regs[1], index: inst.regs[2], scale: inst.scale, disp: inst.disp as i32 } } } OperandSpec::AbsoluteFarAddress => { @@ -634,36 +662,32 @@ impl Operand { /// memory. pub fn is_memory(&self) -> bool { match self { - Operand::DisplacementU16(_) | - Operand::DisplacementU32(_) | - Operand::RegDeref(_) | - Operand::RegDisp(_, _) | - Operand::RegScale(_, _) | - Operand::RegIndexBase(_, _) | - Operand::RegIndexBaseDisp(_, _, _) | - Operand::RegScaleDisp(_, _, _) | - Operand::RegIndexBaseScale(_, _, _) | - Operand::RegIndexBaseScaleDisp(_, _, _, _) | - Operand::RegDerefMasked(_, _) | - Operand::RegDispMasked(_, _, _) | - Operand::RegScaleMasked(_, _, _) | - Operand::RegIndexBaseMasked(_, _, _) | - Operand::RegIndexBaseDispMasked(_, _, _, _) | - Operand::RegScaleDispMasked(_, _, _, _) | - Operand::RegIndexBaseScaleMasked(_, _, _, _) | - Operand::RegIndexBaseScaleDispMasked(_, _, _, _, _) => { + Operand::AbsoluteU16 { .. } | + Operand::AbsoluteU32 { .. } | + Operand::MemDeref { .. } | + Operand::MemDisp { .. } | + Operand::MemIndexScale { .. } | + Operand::MemIndexScaleDisp { .. } | + Operand::MemBaseIndexScale { .. } | + Operand::MemBaseIndexScaleDisp { .. } | + Operand::MemDerefMasked { .. } | + Operand::MemDispMasked { .. } | + Operand::MemIndexScaleMasked { .. } | + Operand::MemIndexScaleDispMasked { .. } | + Operand::MemBaseIndexScaleMasked { .. } | + Operand::MemBaseIndexScaleDispMasked { .. } => { true }, - Operand::ImmediateI8(_) | - Operand::ImmediateU8(_) | - Operand::ImmediateI16(_) | - Operand::ImmediateU16(_) | - Operand::ImmediateU32(_) | - Operand::ImmediateI32(_) | - Operand::Register(_) | - Operand::RegisterMaskMerge(_, _, _) | - Operand::RegisterMaskMergeSae(_, _, _, _) | - Operand::RegisterMaskMergeSaeNoround(_, _, _) | + Operand::ImmediateI8 { .. } | + Operand::ImmediateU8 { .. } | + Operand::ImmediateI16 { .. } | + Operand::ImmediateU16 { .. } | + Operand::ImmediateU32 { .. } | + Operand::ImmediateI32 { .. } | + Operand::Register { .. } | + Operand::RegisterMaskMerge { .. } | + Operand::RegisterMaskMergeSae { .. } | + Operand::RegisterMaskMergeSaeNoround { .. } | Operand::AbsoluteFarAddress { .. } | Operand::Nothing => { false @@ -676,22 +700,22 @@ impl Operand { /// `Operand` came from; `None` here means the authoritative width is `instr.mem_size()`. pub fn width(&self) -> Option { match self { - Operand::Register(reg) => { + Operand::Register { reg } => { Some(reg.width()) } - Operand::RegisterMaskMerge(reg, _, _) => { + Operand::RegisterMaskMerge { reg, .. } => { Some(reg.width()) } - Operand::ImmediateI8(_) | - Operand::ImmediateU8(_) => { + Operand::ImmediateI8 { .. } | + Operand::ImmediateU8 { .. } => { Some(1) } - Operand::ImmediateI16(_) | - Operand::ImmediateU16(_) => { + Operand::ImmediateI16 { .. } | + Operand::ImmediateU16 { .. } => { Some(2) } - Operand::ImmediateI32(_) | - Operand::ImmediateU32(_) => { + Operand::ImmediateI32 { .. } | + Operand::ImmediateU32 { .. } => { Some(4) } // memory operands or `Nothing` @@ -700,6 +724,48 @@ impl Operand { } } } + + /// provided for parity with [`Instruction::visit_operand`]. this has little utility other than + /// to reuse an `OperandVisitor` on an `Operand` directly. + #[allow(dead_code)] // in some configurations this is unused, but it is internal-only for now, so it would warn. + fn visit(&self, visitor: &mut T) -> Result { + match self { + Operand::Nothing => { + visitor.visit_other() + } + Operand::Register { reg } => { + visitor.visit_reg(*reg) + } + Operand::MemDeref { base } => { + visitor.visit_deref(*base) + } + Operand::MemDisp { base, disp } => { + visitor.visit_disp(*base, *disp) + } + Operand::ImmediateI8 { imm } => visitor.visit_i8(*imm), + Operand::ImmediateU8 { imm } => visitor.visit_u8(*imm), + Operand::ImmediateI16 { imm } => visitor.visit_i16(*imm), + Operand::ImmediateU16 { imm } => visitor.visit_u16(*imm), + Operand::ImmediateI32 { imm } => visitor.visit_i32(*imm), + Operand::ImmediateU32 { imm } => visitor.visit_u32(*imm), + Operand::AbsoluteU16 { addr } => visitor.visit_abs_u16(*addr), + Operand::AbsoluteU32 { addr } => visitor.visit_abs_u32(*addr), + Operand::MemIndexScale { index, scale } => visitor.visit_index_scale(*index, *scale), + Operand::MemIndexScaleDisp { index, scale, disp } => visitor.visit_index_scale_disp(*index, *scale, *disp), + Operand::MemBaseIndexScale { base, index, scale } => visitor.visit_base_index_scale(*base, *index, *scale), + Operand::MemBaseIndexScaleDisp { base, index, scale, disp } => visitor.visit_base_index_scale_disp(*base, *index, *scale, *disp), + Operand::RegisterMaskMerge { reg, mask, merge } => visitor.visit_reg_mask_merge(*reg, *mask, *merge), + Operand::RegisterMaskMergeSae { reg, mask, merge, sae } => visitor.visit_reg_mask_merge_sae(*reg, *mask, *merge, *sae), + Operand::RegisterMaskMergeSaeNoround { reg, mask, merge } => visitor.visit_reg_mask_merge_sae_noround(*reg, *mask, *merge), + Operand::MemDerefMasked { base, mask } => visitor.visit_deref_masked(*base, *mask), + Operand::MemDispMasked { base, disp, mask } => visitor.visit_disp_masked(*base, *disp, *mask), + Operand::MemIndexScaleMasked { index, scale, mask } => visitor.visit_index_scale_masked(*index, *scale, *mask), + Operand::MemIndexScaleDispMasked { index, scale, disp, mask } => visitor.visit_index_scale_disp_masked(*index, *scale, *disp, *mask), + Operand::MemBaseIndexScaleMasked { base, index, scale, mask } => visitor.visit_base_index_scale_masked(*base, *index, *scale, *mask), + Operand::MemBaseIndexScaleDispMasked { base, index, scale, disp, mask } => visitor.visit_base_index_scale_disp_masked(*base, *index, *scale, *disp, *mask), + Operand::AbsoluteFarAddress { segment, address } => visitor.visit_absolute_far_address(*segment, *address), + } + } } #[test] @@ -781,16 +847,16 @@ const REGISTER_CLASS_NAMES: &[&'static str] = &[ /// } /// } /// -/// if let Operand::Register(regspec) = instruction.operand(0) { +/// if let Operand::Register { reg } = instruction.operand(0) { /// #[cfg(feature="fmt")] -/// println!("first operand is {}", regspec); -/// show_register_class_info(regspec.class()); +/// println!("first operand is {}", reg); +/// show_register_class_info(reg.class()); /// } /// -/// if let Operand::Register(regspec) = instruction.operand(1) { +/// if let Operand::Register { reg } = instruction.operand(1) { /// #[cfg(feature="fmt")] -/// println!("first operand is {}", regspec); -/// show_register_class_info(regspec.class()); +/// println!("second operand is {}", reg); +/// show_register_class_info(reg.class()); /// } /// ``` /// @@ -968,7 +1034,16 @@ pub enum Opcode { NOT = 0x1019, XADD = 0x101a, XCHG = 0x101b, - Invalid = 0x1c, + + CMPS = 0x201c, + SCAS = 0x201d, + MOVS = 0x201e, + LODS = 0x201f, + STOS = 0x2020, + INS = 0x2021, + OUTS = 0x2022, + + Invalid = 0x23, // XADD, BT, // BTS, @@ -1053,17 +1128,10 @@ pub enum Opcode { CWD, CDQ, CQO, - LODS, - STOS, LAHF, SAHF, - CMPS, - SCAS, - MOVS, TEST, - INS, IN, - OUTS, OUT, IMUL, JO, @@ -2581,19 +2649,19 @@ enum OperandSpec { Deref_di = 0x90, Deref_esi = 0x91, Deref_edi = 0x92, - RegDisp = 0x93, - RegScale = 0x94, - RegScaleDisp = 0x95, - RegIndexBaseScale = 0x96, - RegIndexBaseScaleDisp = 0x97, + Disp = 0x93, + MemIndexScale = 0x94, + MemIndexScaleDisp = 0x95, + MemBaseIndexScale = 0x96, + MemBaseIndexScaleDisp = 0x97, Deref_mask = 0xce, - RegDisp_mask = 0xd3, - RegScale_mask = 0xd4, - RegScaleDisp_mask = 0xd5, - RegIndexBaseScale_mask = 0xd6, - RegIndexBaseScaleDisp_mask = 0xd7, + Disp_mask = 0xd3, + MemIndexScale_mask = 0xd4, + MemIndexScaleDisp_mask = 0xd5, + MemBaseIndexScale_mask = 0xd6, + MemBaseIndexScaleDisp_mask = 0xd7, // u16:u{16,32} immediate address for a far call - AbsoluteFarAddress = 0x98, + AbsoluteFarAddress = 0x18, } // the Hash, Eq, and PartialEq impls here are possibly misleading. @@ -4217,6 +4285,17 @@ impl Opcode { _ => None, } } + + #[inline(always)] + fn can_lock(&self) -> bool { + (*self as u32) & 0x1000 != 0 + } + + #[inline(always)] + #[allow(dead_code)] // in some configurations this is unused, but it is internal-only for now, so it would warn. + fn can_rep(&self) -> bool { + (*self as u32) & 0x2000 != 0 + } } impl Default for Instruction { @@ -4239,6 +4318,169 @@ impl Instruction { Operand::from_spec(self, self.operands[i as usize]) } + // TODO: make public when this seems stable and worthwhile. currently only used for display + // and Displaysink 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(feature="profiling", inline(never))] + #[allow(dead_code)] // in some configurations this is unused, but it is internal-only for now, so it would warn. + fn visit_operand(&self, i: u8, visitor: &mut T) -> Result { + 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::Disp => { + 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::MemIndexScale => { + visitor.visit_index_scale(self.regs[2], self.scale) + } + OperandSpec::MemIndexScaleDisp => { + visitor.visit_index_scale_disp(self.regs[2], self.scale, self.disp as i32) + } + OperandSpec::MemBaseIndexScale => { + visitor.visit_base_index_scale(self.regs[1], self.regs[2], self.scale) + } + OperandSpec::MemBaseIndexScaleDisp => { + visitor.visit_base_index_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_deref_masked(self.regs[1], RegSpec::mask(self.prefixes.evex_unchecked().mask_reg())) + } else { + visitor.visit_deref(self.regs[1]) + } + } + OperandSpec::Disp_mask => { + if self.prefixes.evex_unchecked().mask_reg() != 0 { + visitor.visit_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::MemIndexScale_mask => { + if self.prefixes.evex_unchecked().mask_reg() != 0 { + visitor.visit_index_scale_masked(self.regs[2], self.scale, RegSpec::mask(self.prefixes.evex_unchecked().mask_reg())) + } else { + visitor.visit_index_scale(self.regs[2], self.scale) + } + } + OperandSpec::MemIndexScaleDisp_mask => { + if self.prefixes.evex_unchecked().mask_reg() != 0 { + visitor.visit_index_scale_disp_masked(self.regs[2], self.scale, self.disp as i32, RegSpec::mask(self.prefixes.evex_unchecked().mask_reg())) + } else { + visitor.visit_index_scale_disp(self.regs[2], self.scale, self.disp as i32) + } + } + OperandSpec::MemBaseIndexScale_mask => { + if self.prefixes.evex_unchecked().mask_reg() != 0 { + visitor.visit_base_index_scale_masked(self.regs[1], self.regs[2], self.scale, RegSpec::mask(self.prefixes.evex_unchecked().mask_reg())) + } else { + visitor.visit_base_index_scale(self.regs[1], self.regs[2], self.scale) + } + } + OperandSpec::MemBaseIndexScaleDisp_mask => { + if self.prefixes.evex_unchecked().mask_reg() != 0 { + visitor.visit_base_index_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_base_index_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 { @@ -4527,11 +4769,8 @@ impl Prefixes { fn set_lock(&mut self) { self.bits |= 0x4 } #[inline] pub fn lock(&self) -> bool { self.bits & 0x4 == 4 } - #[deprecated(since = "0.0.1", note = "pub fn cs has never returned `bool` indicating the current selector is `cs`. use `selects_cs` for this purpose, until 2.x that will correct `pub fn cs`.")] - #[inline] - pub fn cs(&mut self) {} #[inline] - pub fn selects_cs(&self) -> bool { self.segment == Segment::CS } + pub fn cs(&self) -> bool { self.segment == Segment::CS } #[inline] fn set_cs(&mut self) { self.segment = Segment::CS } #[inline] @@ -5706,7 +5945,8 @@ const OPCODES: [OpcodeRecord; 256] = [ ]; #[allow(non_snake_case)] -#[inline(always)] +#[cfg_attr(feature="profiling", inline(never))] +#[cfg_attr(not(feature="profiling"), inline(always))] pub(self) fn read_E< T: Reader<::Address, ::Word>, S: DescriptionSink, @@ -5894,7 +6134,7 @@ fn read_sib< InnerDescription::Misc("mod bits select no base register") .with_id(sib_start + 0) ); - OperandSpec::RegScale + OperandSpec::MemIndexScale } else { sink.record( modrm_start + 6, @@ -5902,7 +6142,7 @@ fn read_sib< InnerDescription::RegisterNumber("mod", 0b101, instr.regs[1]) .with_id(sib_start + 0) ); - OperandSpec::RegIndexBaseScale + OperandSpec::MemBaseIndexScale } } } else { @@ -5921,7 +6161,7 @@ fn read_sib< InnerDescription::RegisterNumber("iii", instr.regs[2].num, instr.regs[2]) .with_id(sib_start + 0) ); - OperandSpec::RegIndexBaseScale + OperandSpec::MemBaseIndexScale } } @@ -5955,7 +6195,7 @@ fn read_sib< InnerDescription::RegisterNumber("mod", 0b101, instr.regs[1]) .with_id(sib_start + 0) ); - OperandSpec::RegDisp + OperandSpec::Disp } } else { sink.record( @@ -5971,7 +6211,7 @@ fn read_sib< InnerDescription::Misc("mod bits select no base register, [index+disp] only") .with_id(sib_start + 0) ); - OperandSpec::RegScaleDisp + OperandSpec::MemIndexScaleDisp } else { sink.record( modrm_start + 6, @@ -5979,7 +6219,7 @@ fn read_sib< InnerDescription::RegisterNumber("mod", 0b101, instr.regs[1]) .with_id(sib_start + 0) ); - OperandSpec::RegIndexBaseScaleDisp + OperandSpec::MemBaseIndexScaleDisp } } } else { @@ -5996,7 +6236,7 @@ fn read_sib< InnerDescription::Misc("iii selects no index register") .with_id(sib_start + 0) ); - OperandSpec::RegDisp + OperandSpec::Disp } else { sink.record( sib_start + 3, @@ -6004,7 +6244,7 @@ fn read_sib< InnerDescription::RegisterNumber("iii", instr.regs[2].num, instr.regs[2]) .with_id(sib_start + 0) ); - OperandSpec::RegIndexBaseScaleDisp + OperandSpec::MemBaseIndexScaleDisp } } }; @@ -6114,7 +6354,7 @@ fn read_M_16bit< Ok(OperandSpec::Deref) } else { instr.scale = 1; - Ok(OperandSpec::RegIndexBaseScale) + Ok(OperandSpec::MemBaseIndexScale) } }, 0b01 => { @@ -6135,13 +6375,13 @@ fn read_M_16bit< ); if mmm > 3 { if instr.disp != 0 { - Ok(OperandSpec::RegDisp) + Ok(OperandSpec::Disp) } else { Ok(OperandSpec::Deref) } } else { instr.scale = 1; - Ok(OperandSpec::RegIndexBaseScaleDisp) + Ok(OperandSpec::MemBaseIndexScaleDisp) } }, 0b10 => { @@ -6162,13 +6402,13 @@ fn read_M_16bit< ); if mmm > 3 { if instr.disp != 0 { - Ok(OperandSpec::RegDisp) + Ok(OperandSpec::Disp) } else { Ok(OperandSpec::Deref) } } else { instr.scale = 1; - Ok(OperandSpec::RegIndexBaseScaleDisp) + Ok(OperandSpec::MemBaseIndexScaleDisp) } }, _ => { @@ -6264,7 +6504,7 @@ fn read_M< OperandSpec::Deref } else { instr.disp = disp as i32 as u32; - OperandSpec::RegDisp + OperandSpec::Disp } } }; @@ -6445,6 +6685,7 @@ impl DecodeCtx { } } +#[cfg_attr(feature="profiling", inline(never))] fn read_opc_hotpath< T: Reader<::Address, ::Word>, S: DescriptionSink, @@ -6495,6 +6736,8 @@ fn read_opc_hotpath< } } +#[cfg_attr(feature="profiling", inline(never))] +#[cfg_attr(not(feature="profiling"), inline(always))] fn read_with_annotations< T: Reader<::Address, ::Word>, S: DescriptionSink, @@ -6647,7 +6890,7 @@ fn read_with_annotations< self.read_operands(decoder, words, instruction, record, sink)?; if self.check_lock { - if (instruction.opcode as u32) < 0x1000 || !instruction.operands[0].is_memory() { + if !instruction.opcode.can_lock() || !instruction.operands[0].is_memory() { return Err(DecodeError::InvalidPrefixes); } } @@ -6655,6 +6898,8 @@ fn read_with_annotations< Ok(()) } +#[cfg_attr(feature="profiling", inline(never))] +#[cfg_attr(not(feature="profiling"), inline(always))] fn read_operands< T: Reader<::Address, ::Word>, S: DescriptionSink diff --git a/src/safer_unchecked.rs b/src/safer_unchecked.rs deleted file mode 100644 index 34216bc5..00000000 --- a/src/safer_unchecked.rs +++ /dev/null @@ -1,30 +0,0 @@ -use core::slice::SliceIndex; - -pub trait GetSaferUnchecked { - unsafe fn get_kinda_unchecked(&self, index: I) -> &>::Output - where - I: SliceIndex<[T]>; -} - -impl GetSaferUnchecked for [T] { - #[inline(always)] - unsafe fn get_kinda_unchecked(&self, index: I) -> &>::Output - where - I: SliceIndex<[T]>, - { - if cfg!(debug_assertions) { - &self[index] - } else { - self.get_unchecked(index) - } - } -} - -#[inline(always)] -pub unsafe fn unreachable_kinda_unchecked() -> ! { - if cfg!(debug_assertions) { - panic!("UB: Unreachable unchecked was executed") - } else { - core::hint::unreachable_unchecked() - } -} diff --git a/src/shared/evex.in b/src/shared/evex.in index a43a97e3..0aa7d956 100644 --- a/src/shared/evex.in +++ b/src/shared/evex.in @@ -173,7 +173,7 @@ pub(crate) fn read_evex< let opc = words.next().ok().ok_or(DecodeError::ExhaustedInput)?; let table_idx = ((m << 2) | p) as usize; let table = generated::TABLES[table_idx]; - if table as *const [_] == &generated::DUMMY[..] as *const [_] { + if core::ptr::eq(table as *const [_], &generated::DUMMY[..] as *const [_]) { return Err(DecodeError::InvalidOpcode); } let mut index_lower = 0; diff --git a/test/long_mode/mod.rs b/test/long_mode/mod.rs index 8cb032cd..917d16a1 100644 --- a/test/long_mode/mod.rs +++ b/test/long_mode/mod.rs @@ -15,6 +15,21 @@ use std::fmt::Write; use yaxpeax_arch::{AddressBase, Decoder, LengthedInstruction}; use yaxpeax_x86::long_mode::InstDecoder; +/* +#[test] +#[cfg(feature="std")] +fn test_write_hex_specialization() { + use crate::yaxpeax_x86::long_mode::DisplaySink; + for i in 0..0xffu8 { + let mut out = yaxpeax_x86::long_mode::InstructionFormatter::new(); + out.write_char('0').unwrap(); + out.write_char('x').unwrap(); + out.write_u8(i).unwrap(); + assert_eq!(out.into_inner(), format!("0x{:x}", i)); + } +} +*/ + fn test_invalid(data: &[u8]) { test_invalid_under(&InstDecoder::default(), data); } @@ -62,6 +77,49 @@ fn test_display_under(decoder: &InstDecoder, data: &[u8], expected: &'static str text, expected ); + + let mut text2 = String::new(); + let mut out = yaxpeax_arch::display::FmtSink::new(&mut text2); + instr.write_to(&mut out).expect("printing succeeds"); + + assert!( + text2 == text, + "display error through FmtSink for {}:\n decoded: {:?} under decoder {}\n displayed: {}\n expected: {}\n", + hex, + instr, + decoder, + text2, + text, + ); + + #[cfg(feature="alloc")] + let mut formatter = yaxpeax_x86::long_mode::InstructionTextBuffer::new(); + #[cfg(feature="alloc")] + let text3 = formatter.format_inst(&instr.display_with(yaxpeax_x86::long_mode::DisplayStyle::Intel)).expect("printing succeeds"); + + #[cfg(feature="alloc")] + assert!( + text3 == text, + "display error through InstructionTextBuffer for {}:\n decoded: {:?} under decoder {}\n displayed: {}\n expected: {}\n", + hex, + instr, + decoder, + text3, + text, + ); + + let mut text4 = String::new(); + instr.write_to(&mut text4).expect("printing succeeds"); + + assert!( + text4 == text, + "display error through String for {}:\n decoded: {:?} under decoder {}\n displayed: {}\n expected: {}\n", + hex, + instr, + decoder, + text4, + text, + ); } else { eprintln!("non-fmt build cannot compare text equality") } @@ -1245,14 +1303,25 @@ fn test_control_flow() { test_display(&[0x73, 0x31], "jnb $+0x31"); test_display(&[0x72, 0x5a], "jb $+0x5a"); test_display(&[0x72, 0xf0], "jb $-0x10"); + test_display(&[0xe8, 0x01, 0x00, 0x00, 0x00], "call $+0x1"); + test_display(&[0xe8, 0x80, 0x00, 0x00, 0x00], "call $+0x80"); + test_display(&[0xe8, 0xff, 0xff, 0xff, 0xff], "call $-0x1"); + test_display(&[0xe9, 0x01, 0x00, 0x00, 0x00], "jmp $+0x1"); + test_display(&[0xe9, 0x80, 0x00, 0x00, 0x00], "jmp $+0x80"); + test_display(&[0xe9, 0xff, 0xff, 0xff, 0xff], "jmp $-0x1"); test_display(&[0x0f, 0x86, 0x8b, 0x01, 0x00, 0x00], "jna $+0x18b"); test_display(&[0x0f, 0x85, 0x3b, 0x25, 0x00, 0x00], "jnz $+0x253b"); test_display(&[0x74, 0x47], "jz $+0x47"); test_display(&[0xff, 0x15, 0x7e, 0x72, 0x24, 0x00], "call qword [rip + 0x24727e]"); + test_display(&[0xff, 0x15, 0x7e, 0x72, 0x24, 0x00], "call qword [rip + 0x24727e]"); test_display(&[0xff, 0x24, 0xcd, 0x70, 0xa0, 0xbc, 0x01], "jmp qword [rcx * 8 + 0x1bca070]"); + test_display(&[0xff, 0x14, 0xcd, 0x70, 0xa0, 0xbc, 0x01], "call qword [rcx * 8 + 0x1bca070]"); test_display(&[0xff, 0xe0], "jmp rax"); + test_display(&[0xff, 0xd0], "call rax"); test_display(&[0x66, 0xff, 0xe0], "jmp rax"); test_display(&[0x67, 0xff, 0xe0], "jmp rax"); + test_display(&[0x66, 0xff, 0xd0], "call rax"); + test_display(&[0x67, 0xff, 0xd0], "call rax"); test_invalid(&[0xff, 0xd8]); test_display(&[0xff, 0x18], "callf mword [rax]"); test_display(&[0xe0, 0x12], "loopnz $+0x12"); @@ -2751,7 +2820,7 @@ fn only_64bit() { test_display(&[0xae], "scas byte es:[rdi], al"); test_display(&[0xaf], "scas dword es:[rdi], eax"); test_display(&[0x67, 0xaf], "scas dword es:[edi], eax"); - test_display(&[0x67, 0xac], "lods al, byte ds:[esi]"); + test_display(&[0x67, 0xac], "lods al, byte [esi]"); test_display(&[0x67, 0xaa], "stos byte es:[edi], al"); // note that rax.b does *not* change the register test_display(&[0x4f, 0xa0, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34, 0x12, 0x34], "mov al, byte [0x3412341234123412]"); diff --git a/test/long_mode/operand.rs b/test/long_mode/operand.rs index a47e6c8c..0faa1c38 100644 --- a/test/long_mode/operand.rs +++ b/test/long_mode/operand.rs @@ -3,19 +3,19 @@ use yaxpeax_x86::MemoryAccessSize; #[test] fn register_widths() { - assert_eq!(Operand::Register(RegSpec::rsp()).width(), Some(8)); - assert_eq!(Operand::Register(RegSpec::esp()).width(), Some(4)); - assert_eq!(Operand::Register(RegSpec::sp()).width(), Some(2)); - assert_eq!(Operand::Register(RegSpec::cl()).width(), Some(1)); - assert_eq!(Operand::Register(RegSpec::ch()).width(), Some(1)); - assert_eq!(Operand::Register(RegSpec::gs()).width(), Some(2)); + assert_eq!(Operand::Register { reg: RegSpec::rsp() }.width(), Some(8)); + assert_eq!(Operand::Register { reg: RegSpec::esp() }.width(), Some(4)); + assert_eq!(Operand::Register { reg: RegSpec::sp() }.width(), Some(2)); + assert_eq!(Operand::Register { reg: RegSpec::cl() }.width(), Some(1)); + assert_eq!(Operand::Register { reg: RegSpec::ch() }.width(), Some(1)); + assert_eq!(Operand::Register { reg: RegSpec::gs() }.width(), Some(2)); } #[test] fn memory_widths() { // the register operand directly doesn't report a size - it comes from the `Instruction` for - // which this is an operand. - assert_eq!(Operand::RegDeref(RegSpec::rsp()).width(), None); + // which this is an operand . + assert_eq!(Operand::MemDeref { base: RegSpec::rsp() }.width(), None); fn mem_size_of(data: &[u8]) -> MemoryAccessSize { let decoder = InstDecoder::default(); diff --git a/test/protected_mode/mod.rs b/test/protected_mode/mod.rs index 0f3a6ffe..8fecdab9 100644 --- a/test/protected_mode/mod.rs +++ b/test/protected_mode/mod.rs @@ -1123,13 +1123,24 @@ fn test_control_flow() { test_display(&[0x73, 0x31], "jnb $+0x31"); test_display(&[0x72, 0x5a], "jb $+0x5a"); test_display(&[0x72, 0xf0], "jb $-0x10"); + test_display(&[0xe8, 0x01, 0x00, 0x00, 0x00], "call $+0x1"); + test_display(&[0xe8, 0x80, 0x00, 0x00, 0x00], "call $+0x80"); + test_display(&[0xe8, 0xff, 0xff, 0xff, 0xff], "call $-0x1"); + test_display(&[0xe9, 0x01, 0x00, 0x00, 0x00], "jmp $+0x1"); + test_display(&[0xe9, 0x80, 0x00, 0x00, 0x00], "jmp $+0x80"); + test_display(&[0xe9, 0xff, 0xff, 0xff, 0xff], "jmp $-0x1"); test_display(&[0x0f, 0x86, 0x8b, 0x01, 0x00, 0x00], "jna $+0x18b"); + test_display(&[0x0f, 0x85, 0x3b, 0x25, 0x00, 0x00], "jnz $+0x253b"); test_display(&[0x74, 0x47], "jz $+0x47"); test_display(&[0xff, 0x15, 0x7e, 0x72, 0x24, 0x00], "call dword [0x24727e]"); test_display(&[0xff, 0x24, 0xcd, 0x70, 0xa0, 0xbc, 0x01], "jmp dword [ecx * 8 + 0x1bca070]"); + test_display(&[0xff, 0x14, 0xcd, 0x70, 0xa0, 0xbc, 0x01], "call dword [ecx * 8 + 0x1bca070]"); test_display(&[0xff, 0xe0], "jmp eax"); - test_display(&[0x66, 0xff, 0xe0], "jmp eax"); + test_display(&[0xff, 0xd0], "call eax"); + test_display(&[0x66, 0xff, 0xe0], "jmp ax"); test_display(&[0x67, 0xff, 0xe0], "jmp eax"); + test_display(&[0x66, 0xff, 0xd0], "call ax"); + test_display(&[0x67, 0xff, 0xd0], "call eax"); test_invalid(&[0xff, 0xd8]); test_display(&[0xff, 0x18], "callf far [eax]"); test_display(&[0xe0, 0x12], "loopnz $+0x12"); diff --git a/test/protected_mode/operand.rs b/test/protected_mode/operand.rs index 6eb9ba52..78a34b4f 100644 --- a/test/protected_mode/operand.rs +++ b/test/protected_mode/operand.rs @@ -3,18 +3,18 @@ use yaxpeax_x86::MemoryAccessSize; #[test] fn register_widths() { - assert_eq!(Operand::Register(RegSpec::esp()).width(), Some(4)); - assert_eq!(Operand::Register(RegSpec::sp()).width(), Some(2)); - assert_eq!(Operand::Register(RegSpec::cl()).width(), Some(1)); - assert_eq!(Operand::Register(RegSpec::ch()).width(), Some(1)); - assert_eq!(Operand::Register(RegSpec::gs()).width(), Some(2)); + assert_eq!(Operand::Register { reg: RegSpec::esp() }.width(), Some(4)); + assert_eq!(Operand::Register { reg: RegSpec::sp() }.width(), Some(2)); + assert_eq!(Operand::Register { reg: RegSpec::cl() }.width(), Some(1)); + assert_eq!(Operand::Register { reg: RegSpec::ch() }.width(), Some(1)); + assert_eq!(Operand::Register { reg: RegSpec::gs() }.width(), Some(2)); } #[test] fn memory_widths() { // the register operand directly doesn't report a size - it comes from the `Instruction` for // which this is an operand. - assert_eq!(Operand::RegDeref(RegSpec::esp()).width(), None); + assert_eq!(Operand::MemDeref { base: RegSpec::esp() }.width(), None); fn mem_size_of(data: &[u8]) -> MemoryAccessSize { let decoder = InstDecoder::default(); diff --git a/test/real_mode/mod.rs b/test/real_mode/mod.rs index c5b15484..844a95e6 100644 --- a/test/real_mode/mod.rs +++ b/test/real_mode/mod.rs @@ -16950,6 +16950,7 @@ fn test_real_mode() { test_display(&[0x66, 0xf3, 0x0f, 0x01, 0xe8], "setssbsy"); test_display(&[0x66, 0xf3, 0x0f, 0x01, 0xea], "saveprevssp"); test_display(&[0x66, 0xf3, 0x0f, 0xbd, 0xc1], "lzcnt eax, ecx"); + test_display(&[0x66, 0xff, 0xd0], "call eax"); test_display(&[0x66, 0xff, 0xe0], "jmp eax"); test_display(&[0x67, 0x0f, 0x5b, 0x01], "cvtdq2ps xmm0, xmmword [ecx]"); test_display(&[0x67, 0x66, 0x0f, 0x28, 0x00], "movapd xmm0, xmmword [eax]"); @@ -16960,6 +16961,7 @@ fn test_real_mode() { test_display(&[0x67, 0x66, 0x0f, 0x38, 0xdf, 0x0f], "aesdeclast xmm1, xmmword [edi]"); test_display(&[0x67, 0x66, 0x65, 0x3e, 0x0f, 0x6d, 0xd1], "punpckhqdq xmm2, xmm1"); test_display(&[0x67, 0xe5, 0x99], "in ax, 0x99"); + test_display(&[0x67, 0xff, 0xd0], "call ax"); test_display(&[0x67, 0xff, 0xe0], "jmp ax"); test_display(&[0x68, 0x7f, 0x63], "push 0x637f"); test_display(&[0x6b, 0x43, 0x6f, 0x6d], "imul ax, word [bp + di * 1 + 0x6f], 0x6d"); @@ -17945,6 +17947,7 @@ fn test_real_mode() { test_display(&[0xff, 0x75, 0x08], "push word [di + 0x8]"); test_display(&[0xff, 0x75, 0xb8], "push word [di - 0x48]"); test_display(&[0xff, 0xe0], "jmp ax"); + test_display(&[0xff, 0xd0], "call ax"); } #[test]