aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2020-08-15 11:38:15 -0700
committeriximeow <me@iximeow.net>2020-08-15 11:39:04 -0700
commita0b1fddbb55cbab9d482d4f8cffc7ffe87f73864 (patch)
tree0f94f4bb1809dbdf3ef01170884757d0488f78c1
parent6248d3c5f7dd196acdfce5c0da1608fed81b43cd (diff)
add register class constants to allow reasoning about register operands0.1.1
also bump to 0.1.1
-rw-r--r--CHANGELOG5
-rw-r--r--Cargo.toml2
-rw-r--r--src/long_mode/mod.rs89
-rw-r--r--src/protected_mode/mod.rs81
-rw-r--r--test/long_mode/regspec.rs18
-rw-r--r--test/protected_mode/regspec.rs18
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 <me@iximeow.net>" ]
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);
+ }
+ }
+}