From a0b1fddbb55cbab9d482d4f8cffc7ffe87f73864 Mon Sep 17 00:00:00 2001 From: iximeow Date: Sat, 15 Aug 2020 11:38:15 -0700 Subject: add register class constants to allow reasoning about register operands also bump to 0.1.1 --- CHANGELOG | 5 +++ Cargo.toml | 2 +- src/long_mode/mod.rs | 89 ++++++++++++++++++++++++++++++++++++++++++ src/protected_mode/mod.rs | 81 ++++++++++++++++++++++++++++++++++++++ test/long_mode/regspec.rs | 18 ++++++++- test/protected_mode/regspec.rs | 18 ++++++++- 6 files changed, 210 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 3fd659a..944e607 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +## 0.1.1 +* add `long_mode::register_class` and `protected_mode::register_class` where + `RegisterClass` constants for each register class are defined. + - without these, the only way to distinguish register classes would be string compares. bad. sorry! + ## 0.1.0 * port `long_mode` improvements to `protected_mode` decoder - real mode will wait until another day diff --git a/Cargo.toml b/Cargo.toml index 889b54a..672055d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "yaxpeax-x86" -version = "0.1.0" +version = "0.1.1" authors = [ "iximeow " ] license = "0BSD" repository = "http://git.iximeow.net/yaxpeax-x86/" diff --git a/src/long_mode/mod.rs b/src/long_mode/mod.rs index f328c72..e8bc646 100644 --- a/src/long_mode/mod.rs +++ b/src/long_mode/mod.rs @@ -558,6 +558,95 @@ const REGISTER_CLASS_NAMES: &[&'static str] = &[ "rflags", ]; +/// high-level register classes in an x86 machine, such as "8-byte general purpose", "xmm", "x87", +/// and so on. constants in this module are useful for inspecting the register class of a decoded +/// instruction. as an example: +/// ``` +/// extern crate yaxpeax_arch; +/// use yaxpeax_x86::long_mode::{self as amd64}; +/// use yaxpeax_x86::long_mode::{Opcode, Operand, RegisterClass}; +/// use yaxpeax_arch::Decoder; +/// +/// let movsx_eax_cl = &[0x0f, 0xbe, 0xc1]; +/// let decoder = amd64::InstDecoder::default(); +/// let instruction = decoder +/// .decode(movsx_eax_cl.into_iter().cloned()) +/// .expect("can decode"); +/// +/// assert_eq!(instruction.opcode(), Opcode::MOVSX_b); +/// +/// fn show_register_class_info(regclass: RegisterClass) { +/// match regclass { +/// amd64::register_class::D => { +/// println!(" and is a dword register"); +/// } +/// amd64::register_class::B => { +/// println!(" and is a byte register"); +/// } +/// other => { +/// panic!("unexpected and invalid register class {:?}", other); +/// } +/// } +/// } +/// +/// if let Operand::Register(regspec) = instruction.operand(0) { +/// println!("first operand is {}", regspec); +/// show_register_class_info(regspec.class()); +/// } +/// +/// if let Operand::Register(regspec) = instruction.operand(1) { +/// println!("first operand is {}", regspec); +/// show_register_class_info(regspec.class()); +/// } +/// ``` +/// +/// this is preferable to alternatives like checking register names against a known list: a +/// register class is one byte and "is qword general-purpose" can then be a simple one-byte +/// compare, instead of 16 string compares. +/// +/// `yaxpeax-x86` does not attempt to further distinguish between, for example, register +/// suitability as operands. as an example, `cl` is only a byte register, with no additional +/// register class to describe its use as an implicit shift operand. +pub mod register_class { + use super::{RegisterBank, RegisterClass}; + /// quadword registers: rax through r15 + pub const Q: RegisterClass = RegisterClass { kind: RegisterBank::Q }; + /// doubleword registers: eax through r15d + pub const D: RegisterClass = RegisterClass { kind: RegisterBank::D }; + /// word registers: ax through r15w + pub const W: RegisterClass = RegisterClass { kind: RegisterBank::W }; + /// byte registers: al, cl, dl, bl, ah, ch, dh, bh. `B` registers do *not* have a rex prefix. + pub const B: RegisterClass = RegisterClass { kind: RegisterBank::B }; + /// byte registers with rex prefix present: al through r15b. `RB` registers have a rex prefix. + pub const RB: RegisterClass = RegisterClass { kind: RegisterBank::rB }; + /// control registers cr0 through cr15. + pub const CR: RegisterClass = RegisterClass { kind: RegisterBank::CR}; + /// debug registers dr0 through dr15. + pub const DR: RegisterClass = RegisterClass { kind: RegisterBank::DR }; + /// segment registers es, cs, ss, ds, fs, gs. + pub const S: RegisterClass = RegisterClass { kind: RegisterBank::S }; + /// xmm registers xmm0 through xmm31. + pub const X: RegisterClass = RegisterClass { kind: RegisterBank::X }; + /// ymm registers ymm0 through ymm31. + pub const Y: RegisterClass = RegisterClass { kind: RegisterBank::Y }; + /// zmm registers zmm0 through zmm31. + pub const Z: RegisterClass = RegisterClass { kind: RegisterBank::Z }; + /// x87 floating point stack entries st(0) through st(7). + pub const ST: RegisterClass = RegisterClass { kind: RegisterBank::ST }; + /// mmx registers mm0 through mm7. + pub const MM: RegisterClass = RegisterClass { kind: RegisterBank::MM }; + /// `AVX512` mask registers k0 through k7. + pub const K: RegisterClass = RegisterClass { kind: RegisterBank::K }; + /// the full instruction pointer register. + pub const RIP: RegisterClass = RegisterClass { kind: RegisterBank::RIP }; + /// the low 32 bits of `rip`. + pub const EIP: RegisterClass = RegisterClass { kind: RegisterBank::EIP }; + /// the full cpu flags register. + pub const RFLAGS: RegisterClass = RegisterClass { kind: RegisterBank::RFlags }; + /// the low 32 bits of rflags. + pub const EFLAGS: RegisterClass = RegisterClass { kind: RegisterBank::EFlags }; +} + impl RegisterClass { /// return a human-friendly name for this register class pub fn name(&self) -> &'static str { diff --git a/src/protected_mode/mod.rs b/src/protected_mode/mod.rs index 315cb58..b9d626c 100644 --- a/src/protected_mode/mod.rs +++ b/src/protected_mode/mod.rs @@ -506,6 +506,87 @@ const REGISTER_CLASS_NAMES: &[&'static str] = &[ "eflags", ]; +/// high-level register classes in an x86 machine, such as "8-byte general purpose", "xmm", "x87", +/// and so on. constants in this module are useful for inspecting the register class of a decoded +/// instruction. as an example: +/// ``` +/// extern crate yaxpeax_arch; +/// use yaxpeax_x86::protected_mode::{self as amd64}; +/// use yaxpeax_x86::protected_mode::{Opcode, Operand, RegisterClass}; +/// use yaxpeax_arch::Decoder; +/// +/// let movsx_eax_cl = &[0x0f, 0xbe, 0xc1]; +/// let decoder = amd64::InstDecoder::default(); +/// let instruction = decoder +/// .decode(movsx_eax_cl.into_iter().cloned()) +/// .expect("can decode"); +/// +/// assert_eq!(instruction.opcode(), Opcode::MOVSX_b); +/// +/// fn show_register_class_info(regclass: RegisterClass) { +/// match regclass { +/// amd64::register_class::D => { +/// println!(" and is a dword register"); +/// } +/// amd64::register_class::B => { +/// println!(" and is a byte register"); +/// } +/// other => { +/// panic!("unexpected and invalid register class {:?}", other); +/// } +/// } +/// } +/// +/// if let Operand::Register(regspec) = instruction.operand(0) { +/// println!("first operand is {}", regspec); +/// show_register_class_info(regspec.class()); +/// } +/// +/// if let Operand::Register(regspec) = instruction.operand(1) { +/// println!("first operand is {}", regspec); +/// show_register_class_info(regspec.class()); +/// } +/// ``` +/// +/// this is preferable to alternatives like checking register names against a known list: a +/// register class is one byte and "is qword general-purpose" can then be a simple one-byte +/// compare, instead of 16 string compares. +/// +/// `yaxpeax-x86` does not attempt to further distinguish between, for example, register +/// suitability as operands. as an example, `cl` is only a byte register, with no additional +/// register class to describe its use as an implicit shift operand. +pub mod register_class { + use super::{RegisterBank, RegisterClass}; + /// doubleword registers: eax through edi. + pub const D: RegisterClass = RegisterClass { kind: RegisterBank::D }; + /// word registers: ax through di. + pub const W: RegisterClass = RegisterClass { kind: RegisterBank::W }; + /// byte registers: al, cl, dl, bl, ah, ch, dh, bh. + pub const B: RegisterClass = RegisterClass { kind: RegisterBank::B }; + /// control registers cr0 through cr7. + pub const CR: RegisterClass = RegisterClass { kind: RegisterBank::CR}; + /// debug registers dr0 through dr7. + pub const DR: RegisterClass = RegisterClass { kind: RegisterBank::DR }; + /// segment registers es, cs, ss, ds, fs, gs. + pub const S: RegisterClass = RegisterClass { kind: RegisterBank::S }; + /// xmm registers xmm0 through xmm31. + pub const X: RegisterClass = RegisterClass { kind: RegisterBank::X }; + /// ymm registers ymm0 through ymm31. + pub const Y: RegisterClass = RegisterClass { kind: RegisterBank::Y }; + /// zmm registers zmm0 through zmm31. + pub const Z: RegisterClass = RegisterClass { kind: RegisterBank::Z }; + /// x87 floating point stack entries st(0) through st(7). + pub const ST: RegisterClass = RegisterClass { kind: RegisterBank::ST }; + /// mmx registers mm0 through mm7. + pub const MM: RegisterClass = RegisterClass { kind: RegisterBank::MM }; + /// `AVX512` mask registers k0 through k7. + pub const K: RegisterClass = RegisterClass { kind: RegisterBank::K }; + /// the full instruction pointer register. + pub const EIP: RegisterClass = RegisterClass { kind: RegisterBank::EIP }; + /// the full cpu flags register. + pub const EFLAGS: RegisterClass = RegisterClass { kind: RegisterBank::EFlags }; +} + impl RegisterClass { /// return a human-friendly name for this register class pub fn name(&self) -> &'static str { diff --git a/test/long_mode/regspec.rs b/test/long_mode/regspec.rs index e126ac3..f92ec89 100644 --- a/test/long_mode/regspec.rs +++ b/test/long_mode/regspec.rs @@ -1,4 +1,4 @@ -use yaxpeax_x86::long_mode::RegSpec; +use yaxpeax_x86::long_mode::{register_class, RegSpec}; use std::collections::{BTreeMap, HashMap}; #[test] @@ -39,3 +39,19 @@ fn test_bank_names() { assert_eq!(RegSpec::ymm0().class().name(), "ymm"); assert_eq!(RegSpec::zmm0().class().name(), "zmm"); } + +// this should compile. +#[test] +fn match_bank_kind() { + match RegSpec::al().class() { + register_class::X => { + panic!("al is an xmm register? don't think so"); + } + register_class::B => { + println!("al is a byte register"); + } + other => { + panic!("unknown register kind: {:?}", other); + } + } +} diff --git a/test/protected_mode/regspec.rs b/test/protected_mode/regspec.rs index eb8d018..7d43c7a 100644 --- a/test/protected_mode/regspec.rs +++ b/test/protected_mode/regspec.rs @@ -1,4 +1,4 @@ -use yaxpeax_x86::protected_mode::RegSpec; +use yaxpeax_x86::protected_mode::{register_class, RegSpec}; use std::collections::{BTreeMap, HashMap}; #[test] @@ -33,3 +33,19 @@ fn test_bank_names() { assert_eq!(RegSpec::ymm0().class().name(), "ymm"); assert_eq!(RegSpec::zmm0().class().name(), "zmm"); } + +// this should compile. +#[test] +fn match_bank_kind() { + match RegSpec::al().class() { + register_class::X => { + panic!("al is an xmm register? don't think so"); + } + register_class::B => { + println!("al is a byte register"); + } + other => { + panic!("unknown register kind: {:?}", other); + } + } +} -- cgit v1.1