aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG15
-rw-r--r--Cargo.toml1
-rw-r--r--Makefile14
-rw-r--r--src/lib.rs19
-rw-r--r--src/long_mode/mod.rs75
-rw-r--r--src/protected_mode/mod.rs75
-rw-r--r--src/real_mode/mod.rs74
-rw-r--r--test/long_mode/display.rs4
-rw-r--r--test/long_mode/evex_generated.rs50
-rw-r--r--test/long_mode/mod.rs49
-rw-r--r--test/long_mode/regspec.rs2
-rw-r--r--test/protected_mode/display.rs4
-rw-r--r--test/protected_mode/evex_generated.rs50
-rw-r--r--test/protected_mode/mod.rs49
-rw-r--r--test/protected_mode/regspec.rs2
-rw-r--r--test/real_mode/mod.rs49
16 files changed, 376 insertions, 156 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 74ee269..4227196 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,18 @@
+## 1.1.5
+* `Makefile` at the crate root now exercises `yaxpeax-x86` builds and tests under:
+ - default features (fmt, std)
+ - no-std + fmt
+ - no-std and no-fmt
+* fix several issues prohibiting builds of the crate with no-std +fmt
+ configurations; the required Display impl for field annotation would simply not
+ compile in no-fmt builds. it is now a minimal implementation to comply with the
+ goals of `no-fmt`: "avoid as much extra code and data for formatting
+ instructions as possible". two dozen bytes for a likely-DCE'd codepath should
+ be ok.
+* adjust test structure so that exhaustive tests can be `#[ignored]` and
+ explicitly run anyway for completeness. this means the ignored at&t tests now
+ are both ignored and appear to succeed when run.
+
## 1.1.4
* fix reachable unreachable under `DisplayStyle::C` in 64-, 32-, and 16-bit modes
* add fuzz target to cover `DisplayStyle::C` formatter for 64-, 32-, and 16-bit modes
diff --git a/Cargo.toml b/Cargo.toml
index 877d99c..a440d12 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,6 +15,7 @@ yaxpeax-arch = { version = "0.2.7", default-features = false, features = [] }
"serde" = { version = "1.0", optional = true }
"serde_json" = { version = "1.0", optional = true }
"serde_derive" = { version = "1.0", optional = true }
+"cfg-if" = "1.0.0"
[dev-dependencies]
rand = "0.8.4"
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..c045d31
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,14 @@
+test: test-fast test-exhaustive
+
+test-fast: test-std test-no-std test-fmt-no-std
+
+test-exhaustive:
+ cargo test -- --ignored
+ cargo test --no-default-features -- --ignored
+
+test-std:
+ cargo test
+test-no-std:
+ cargo test --no-default-features
+test-fmt-no-std:
+ cargo test --no-default-features --features "fmt"
diff --git a/src/lib.rs b/src/lib.rs
index b8bd4f7..9825fd2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -12,6 +12,7 @@
//!
//! let inst = decoder.decode_slice(&[0x33, 0xc0]).unwrap();
//!
+//! #[cfg(features="fmt")]
//! assert_eq!("xor eax, eax", inst.to_string());
//! ```
//!
@@ -31,13 +32,16 @@
//!
//! let inst = decoder.decode_slice(&[0x33, 0x01]).unwrap();
//!
+//! #[cfg(features="fmt")]
//! assert_eq!("xor eax, dword [rcx]", inst.to_string());
//!
//! assert_eq!(Operand::Register(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));
//!
//! // an operand in isolation does not know the size of memory it references, if any
+//! #[cfg(features="fmt")]
//! assert_eq!("[rcx]", inst.operand(1).to_string());
//!
//! // and for memory operands, the size must be read from the instruction itself:
@@ -45,6 +49,7 @@
//! assert_eq!("dword", mem_size.size_name());
//!
//! // `MemoryAccessSize::size_name()` is how its `Display` impl works, as well:
+//! #[cfg(features="fmt")]
//! assert_eq!("dword", mem_size.to_string());
//! ```
//!
@@ -54,12 +59,23 @@
//! mod decoder {
//! use yaxpeax_arch::{Arch, AddressDisplay, Decoder, Reader, ReaderBuilder};
//!
+//! // have to play some games so this example works right even without `fmt` enabled!
+//! #[cfg(feature="fmt")]
+//! trait InstBound: std::fmt::Display {}
+//! #[cfg(not(feature="fmt"))]
+//! trait InstBound {}
+//!
+//! #[cfg(feature="fmt")]
+//! impl <T: std::fmt::Display> InstBound for T {}
+//! #[cfg(not(feature="fmt"))]
+//! impl <T> InstBound for T {}
+//!
//! pub fn decode_stream<
//! 'data,
//! A: yaxpeax_arch::Arch,
//! U: ReaderBuilder<A::Address, A::Word>,
//! >(data: U) where
-//! A::Instruction: std::fmt::Display,
+//! A::Instruction: InstBound,
//! {
//! let mut reader = ReaderBuilder::read_from(data);
//! let mut address: A::Address = reader.total_offset();
@@ -69,6 +85,7 @@
//! loop {
//! match decode_res {
//! Ok(ref inst) => {
+//! #[cfg(feature="fmt")]
//! println!("{}: {}", address.show(), inst);
//! decode_res = decoder.decode(&mut reader);
//! address = reader.total_offset();
diff --git a/src/long_mode/mod.rs b/src/long_mode/mod.rs
index d2cb2f7..6475d4e 100644
--- a/src/long_mode/mod.rs
+++ b/src/long_mode/mod.rs
@@ -888,11 +888,13 @@ const REGISTER_CLASS_NAMES: &[&'static str] = &[
/// }
///
/// if let Operand::Register(regspec) = instruction.operand(0) {
+/// #[cfg(feature="fmt")]
/// println!("first operand is {}", regspec);
/// show_register_class_info(regspec.class());
/// }
///
/// if let Operand::Register(regspec) = instruction.operand(1) {
+/// #[cfg(feature="fmt")]
/// println!("first operand is {}", regspec);
/// show_register_class_info(regspec.class());
/// }
@@ -7422,43 +7424,54 @@ impl InnerDescription {
}
}
-impl fmt::Display for InnerDescription {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- InnerDescription::RexPrefix(bits) => {
- write!(f, "rex prefix: {}{}{}{}",
- if bits & 0x8 != 0 { "w" } else { "-" },
- if bits & 0x4 != 0 { "r" } else { "-" },
- if bits & 0x2 != 0 { "x" } else { "-" },
- if bits & 0x1 != 0 { "b" } else { "-" },
- )
- }
- InnerDescription::SegmentPrefix(segment) => {
- write!(f, "segment override: {}", segment)
- }
- InnerDescription::Misc(text) => {
- f.write_str(text)
- }
- InnerDescription::Number(text, num) => {
- write!(f, "{}: {:#x}", text, num)
- }
- InnerDescription::Opcode(opc) => {
- write!(f, "opcode `{}`", opc)
- }
- InnerDescription::OperandCode(OperandCodeWrapper { code }) => {
- write!(f, "operand code `{:?}`", code)
- }
- InnerDescription::RegisterNumber(name, num, reg) => {
- write!(f, "`{}` (`{}` selects register number {})", reg, name, num)
+cfg_if::cfg_if! {
+ if #[cfg(feature="fmt")] {
+ impl fmt::Display for InnerDescription {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ InnerDescription::RexPrefix(bits) => {
+ write!(f, "rex prefix: {}{}{}{}",
+ if bits & 0x8 != 0 { "w" } else { "-" },
+ if bits & 0x4 != 0 { "r" } else { "-" },
+ if bits & 0x2 != 0 { "x" } else { "-" },
+ if bits & 0x1 != 0 { "b" } else { "-" },
+ )
+ }
+ InnerDescription::SegmentPrefix(segment) => {
+ write!(f, "segment override: {}", segment)
+ }
+ InnerDescription::Misc(text) => {
+ f.write_str(text)
+ }
+ InnerDescription::Number(text, num) => {
+ write!(f, "{}: {:#x}", text, num)
+ }
+ InnerDescription::Opcode(opc) => {
+ write!(f, "opcode `{}`", opc)
+ }
+ InnerDescription::OperandCode(OperandCodeWrapper { code }) => {
+ write!(f, "operand code `{:?}`", code)
+ }
+ InnerDescription::RegisterNumber(name, num, reg) => {
+ write!(f, "`{}` (`{}` selects register number {})", reg, name, num)
+ }
+ InnerDescription::Boundary(desc) => {
+ write!(f, "{}", desc)
+ }
+ }
}
- InnerDescription::Boundary(desc) => {
- write!(f, "{}", desc)
+ }
+ } else {
+ impl fmt::Display for InnerDescription {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str("non-fmt build")
}
}
}
}
-#[derive(Clone, PartialEq, Eq, Debug)]
+#[cfg_attr(feature="fmt", derive(Debug))]
+#[derive(Clone, PartialEq, Eq)]
pub struct FieldDescription {
desc: InnerDescription,
id: u32,
diff --git a/src/protected_mode/mod.rs b/src/protected_mode/mod.rs
index 0a6fcf8..dd63d32 100644
--- a/src/protected_mode/mod.rs
+++ b/src/protected_mode/mod.rs
@@ -833,11 +833,13 @@ const REGISTER_CLASS_NAMES: &[&'static str] = &[
/// }
///
/// if let Operand::Register(regspec) = instruction.operand(0) {
+/// #[cfg(feature="fmt")]
/// println!("first operand is {}", regspec);
/// show_register_class_info(regspec.class());
/// }
///
/// if let Operand::Register(regspec) = instruction.operand(1) {
+/// #[cfg(feature="fmt")]
/// println!("first operand is {}", regspec);
/// show_register_class_info(regspec.class());
/// }
@@ -7386,7 +7388,8 @@ fn read_0f3a_opcode(opcode: u8, prefixes: &mut Prefixes) -> OpcodeRecord {
};
}
-/// the actual description for a selection of bits involved in decoding an [`long_mode::Instruction`].
+/// the actual description for a selection of bits involved in decoding a
+/// [`protected_mode::Instruction`].
///
/// some prefixes are only identified as an `InnerDescription::Misc` string, while some are full
/// `InnerDescription::SegmentPrefix(Segment)`. generally, strings should be considered unstable
@@ -7439,37 +7442,47 @@ impl InnerDescription {
}
}
-impl fmt::Display for InnerDescription {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- InnerDescription::RexPrefix(bits) => {
- write!(f, "rex prefix: {}{}{}{}",
- if bits & 0x8 != 0 { "w" } else { "-" },
- if bits & 0x4 != 0 { "r" } else { "-" },
- if bits & 0x2 != 0 { "x" } else { "-" },
- if bits & 0x1 != 0 { "b" } else { "-" },
- )
- }
- InnerDescription::SegmentPrefix(segment) => {
- write!(f, "segment override: {}", segment)
- }
- InnerDescription::Misc(text) => {
- f.write_str(text)
- }
- InnerDescription::Number(text, num) => {
- write!(f, "{}: {:#x}", text, num)
- }
- InnerDescription::Opcode(opc) => {
- write!(f, "opcode `{}`", opc)
- }
- InnerDescription::OperandCode(OperandCodeWrapper { code }) => {
- write!(f, "operand code `{:?}`", code)
- }
- InnerDescription::RegisterNumber(name, num, reg) => {
- write!(f, "`{}` (`{}` selects register number {})", reg, name, num)
+cfg_if::cfg_if! {
+ if #[cfg(feature = "fmt")] {
+ impl fmt::Display for InnerDescription {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ InnerDescription::RexPrefix(bits) => {
+ write!(f, "rex prefix: {}{}{}{}",
+ if bits & 0x8 != 0 { "w" } else { "-" },
+ if bits & 0x4 != 0 { "r" } else { "-" },
+ if bits & 0x2 != 0 { "x" } else { "-" },
+ if bits & 0x1 != 0 { "b" } else { "-" },
+ )
+ }
+ InnerDescription::SegmentPrefix(segment) => {
+ write!(f, "segment override: {}", segment)
+ }
+ InnerDescription::Misc(text) => {
+ f.write_str(text)
+ }
+ InnerDescription::Number(text, num) => {
+ write!(f, "{}: {:#x}", text, num)
+ }
+ InnerDescription::Opcode(opc) => {
+ write!(f, "opcode `{}`", opc)
+ }
+ InnerDescription::OperandCode(OperandCodeWrapper { code }) => {
+ write!(f, "operand code `{:?}`", code)
+ }
+ InnerDescription::RegisterNumber(name, num, reg) => {
+ write!(f, "`{}` (`{}` selects register number {})", reg, name, num)
+ }
+ InnerDescription::Boundary(desc) => {
+ write!(f, "{}", desc)
+ }
+ }
}
- InnerDescription::Boundary(desc) => {
- write!(f, "{}", desc)
+ }
+ } else {
+ impl fmt::Display for InnerDescription {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str("non-fmt build")
}
}
}
diff --git a/src/real_mode/mod.rs b/src/real_mode/mod.rs
index 4b8ad6d..d7fda17 100644
--- a/src/real_mode/mod.rs
+++ b/src/real_mode/mod.rs
@@ -833,11 +833,13 @@ const REGISTER_CLASS_NAMES: &[&'static str] = &[
/// }
///
/// if let Operand::Register(regspec) = instruction.operand(0) {
+/// #[cfg(feature="fmt")]
/// println!("first operand is {}", regspec);
/// show_register_class_info(regspec.class());
/// }
///
/// if let Operand::Register(regspec) = instruction.operand(1) {
+/// #[cfg(feature="fmt")]
/// println!("first operand is {}", regspec);
/// show_register_class_info(regspec.class());
/// }
@@ -7388,7 +7390,7 @@ fn read_0f3a_opcode(opcode: u8, prefixes: &mut Prefixes) -> OpcodeRecord {
};
}
-/// the actual description for a selection of bits involved in decoding an [`long_mode::Instruction`].
+/// the actual description for a selection of bits involved in decoding a [`real_mode::Instruction`].
///
/// some prefixes are only identified as an `InnerDescription::Misc` string, while some are full
/// `InnerDescription::SegmentPrefix(Segment)`. generally, strings should be considered unstable
@@ -7441,37 +7443,47 @@ impl InnerDescription {
}
}
-impl fmt::Display for InnerDescription {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- InnerDescription::RexPrefix(bits) => {
- write!(f, "rex prefix: {}{}{}{}",
- if bits & 0x8 != 0 { "w" } else { "-" },
- if bits & 0x4 != 0 { "r" } else { "-" },
- if bits & 0x2 != 0 { "x" } else { "-" },
- if bits & 0x1 != 0 { "b" } else { "-" },
- )
- }
- InnerDescription::SegmentPrefix(segment) => {
- write!(f, "segment override: {}", segment)
- }
- InnerDescription::Misc(text) => {
- f.write_str(text)
- }
- InnerDescription::Number(text, num) => {
- write!(f, "{}: {:#x}", text, num)
- }
- InnerDescription::Opcode(opc) => {
- write!(f, "opcode `{}`", opc)
- }
- InnerDescription::OperandCode(OperandCodeWrapper { code }) => {
- write!(f, "operand code `{:?}`", code)
- }
- InnerDescription::RegisterNumber(name, num, reg) => {
- write!(f, "`{}` (`{}` selects register number {})", reg, name, num)
+cfg_if::cfg_if! {
+ if #[cfg(feature = "fmt")] {
+ impl fmt::Display for InnerDescription {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ InnerDescription::RexPrefix(bits) => {
+ write!(f, "rex prefix: {}{}{}{}",
+ if bits & 0x8 != 0 { "w" } else { "-" },
+ if bits & 0x4 != 0 { "r" } else { "-" },
+ if bits & 0x2 != 0 { "x" } else { "-" },
+ if bits & 0x1 != 0 { "b" } else { "-" },
+ )
+ }
+ InnerDescription::SegmentPrefix(segment) => {
+ write!(f, "segment override: {}", segment)
+ }
+ InnerDescription::Misc(text) => {
+ f.write_str(text)
+ }
+ InnerDescription::Number(text, num) => {
+ write!(f, "{}: {:#x}", text, num)
+ }
+ InnerDescription::Opcode(opc) => {
+ write!(f, "opcode `{}`", opc)
+ }
+ InnerDescription::OperandCode(OperandCodeWrapper { code }) => {
+ write!(f, "operand code `{:?}`", code)
+ }
+ InnerDescription::RegisterNumber(name, num, reg) => {
+ write!(f, "`{}` (`{}` selects register number {})", reg, name, num)
+ }
+ InnerDescription::Boundary(desc) => {
+ write!(f, "{}", desc)
+ }
+ }
}
- InnerDescription::Boundary(desc) => {
- write!(f, "{}", desc)
+ }
+ } else {
+ impl fmt::Display for InnerDescription {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str("non-fmt build")
}
}
}
diff --git a/test/long_mode/display.rs b/test/long_mode/display.rs
index fedb183..22fd787 100644
--- a/test/long_mode/display.rs
+++ b/test/long_mode/display.rs
@@ -41,9 +41,13 @@ fn test_display_under(decoder: &InstDecoder, style: DisplayStyle, data: &[u8], e
// decided i do not like at&t syntax much at all. not going to write a formatter for it. some test
// cases will live on in case someone else feels like adding one, or i get mad enough to do it.
+#[allow(unreachable_code)]
#[ignore]
#[test]
fn test_instructions_atnt() {
+ // `ignore` is now used to avoid running (slow!) exhaustive tests in a default `cargo test`.
+ // running exhaustive tests now runs these tests, which fail. so instead, return early.
+ return;
// just modrm
test_display(&[0x33, 0x08], "xor (%rax), %ecx");
test_display(&[0x33, 0x20], "xor (%rax), %esp");
diff --git a/test/long_mode/evex_generated.rs b/test/long_mode/evex_generated.rs
index dcb4e01..46d99a7 100644
--- a/test/long_mode/evex_generated.rs
+++ b/test/long_mode/evex_generated.rs
@@ -11,7 +11,17 @@ fn test_invalid(data: &[u8]) {
fn test_invalid_under(decoder: &InstDecoder, data: &[u8]) {
let mut reader = U8Reader::new(data);
if let Ok(inst) = decoder.decode(&mut reader) {
- panic!("decoded {:?} from {:02x?} under decoder {}", inst.opcode(), data, decoder);
+ // realistically, the chances an error only shows up under non-fmt builds seems unlikely,
+ // but try to report *something* in such cases.
+ cfg_if::cfg_if! {
+ if #[cfg(feature="fmt")] {
+ panic!("decoded {:?} from {:02x?} under decoder {}", inst.opcode(), data, decoder);
+ } else {
+ // don't warn about the unused inst here
+ let _ = inst;
+ panic!("decoded instruction <non-fmt build> from {:02x?} under decoder <non-fmt build>", data);
+ }
+ }
} else {
// this is fine
}
@@ -27,25 +37,39 @@ fn test_display_under(decoder: &InstDecoder, data: &[u8], expected: &'static str
for b in data {
write!(hex, "{:02x}", b).unwrap();
}
- let mut reader = U8Reader::new(data);
+ let mut reader = yaxpeax_arch::U8Reader::new(data);
match decoder.decode(&mut reader) {
Ok(instr) => {
- let text = format!("{}", instr);
- assert!(
- text == expected,
- "display error for {}:\n decoded: {:?} under decoder {}\n displayed: {}\n expected: {}\n",
- hex,
- instr,
- decoder,
- text,
- expected
- );
+ cfg_if::cfg_if! {
+ if #[cfg(feature="fmt")] {
+ let text = format!("{}", instr);
+ assert!(
+ text == expected,
+ "display error for {}:\n decoded: {:?} under decoder {}\n displayed: {}\n expected: {}\n",
+ hex,
+ instr,
+ decoder,
+ text,
+ expected
+ );
+ } else {
+ eprintln!("non-fmt build cannot compare text equality")
+ }
+ }
// while we're at it, test that the instruction is as long, and no longer, than its
// input
assert_eq!((0u64.wrapping_offset(instr.len()).to_linear()) as usize, data.len(), "instruction length is incorrect, wanted instruction {}", expected);
},
Err(e) => {
- assert!(false, "decode error ({}) for {} under decoder {}:\n expected: {}\n", e, hex, decoder, expected);
+ cfg_if::cfg_if! {
+ if #[cfg(feature="fmt")] {
+ assert!(false, "decode error ({}) for {} under decoder {}:\n expected: {}\n", e, hex, decoder, expected);
+ } else {
+ // avoid the unused `e` warning
+ let _ = e;
+ assert!(false, "decode error (<non-fmt build>) for {} under decoder <non-fmt build>:\n expected: {}\n", hex, expected);
+ }
+ }
}
}
}
diff --git a/test/long_mode/mod.rs b/test/long_mode/mod.rs
index 97e9a74..2a11b79 100644
--- a/test/long_mode/mod.rs
+++ b/test/long_mode/mod.rs
@@ -2,6 +2,7 @@ extern crate rand;
mod regspec;
mod operand;
+#[cfg(feature="fmt")]
mod display;
mod evex_generated;
mod reuse_test;
@@ -18,7 +19,17 @@ fn test_invalid(data: &[u8]) {
fn test_invalid_under(decoder: &InstDecoder, data: &[u8]) {
let mut reader = yaxpeax_arch::U8Reader::new(data);
if let Ok(inst) = decoder.decode(&mut reader) {
- panic!("decoded {:?} from {:02x?} under decoder {}", inst.opcode(), data, decoder);
+ // realistically, the chances an error only shows up under non-fmt builds seems unlikely,
+ // but try to report *something* in such cases.
+ cfg_if::cfg_if! {
+ if #[cfg(feature="fmt")] {
+ panic!("decoded {:?} from {:02x?} under decoder {}", inst.opcode(), data, decoder);
+ } else {
+ // don't warn about the unused inst here
+ let _ = inst;
+ panic!("decoded instruction <non-fmt build> from {:02x?} under decoder <non-fmt build>", data);
+ }
+ }
} else {
// this is fine
}
@@ -36,22 +47,36 @@ fn test_display_under(decoder: &InstDecoder, data: &[u8], expected: &'static str
let mut reader = yaxpeax_arch::U8Reader::new(data);
match decoder.decode(&mut reader) {
Ok(instr) => {
- let text = format!("{}", instr);
- assert!(
- text == expected,
- "display error for {}:\n decoded: {:?} under decoder {}\n displayed: {}\n expected: {}\n",
- hex,
- instr,
- decoder,
- text,
- expected
- );
+ cfg_if::cfg_if! {
+ if #[cfg(feature="fmt")] {
+ let text = format!("{}", instr);
+ assert!(
+ text == expected,
+ "display error for {}:\n decoded: {:?} under decoder {}\n displayed: {}\n expected: {}\n",
+ hex,
+ instr,
+ decoder,
+ text,
+ expected
+ );
+ } else {
+ eprintln!("non-fmt build cannot compare text equality")
+ }
+ }
// while we're at it, test that the instruction is as long, and no longer, than its
// input
assert_eq!((0u64.wrapping_offset(instr.len()).to_linear()) as usize, data.len(), "instruction length is incorrect, wanted instruction {}", expected);
},
Err(e) => {
- assert!(false, "decode error ({}) for {} under decoder {}:\n expected: {}\n", e, hex, decoder, expected);
+ cfg_if::cfg_if! {
+ if #[cfg(feature="fmt")] {
+ assert!(false, "decode error ({}) for {} under decoder {}:\n expected: {}\n", e, hex, decoder, expected);
+ } else {
+ // avoid the unused `e` warning
+ let _ = e;
+ assert!(false, "decode error (<non-fmt build>) for {} under decoder <non-fmt build>:\n expected: {}\n", hex, expected);
+ }
+ }
}
}
}
diff --git a/test/long_mode/regspec.rs b/test/long_mode/regspec.rs
index f92ec89..220435f 100644
--- a/test/long_mode/regspec.rs
+++ b/test/long_mode/regspec.rs
@@ -11,6 +11,7 @@ fn test_hash() {
let _: HashMap<RegSpec, u64> = HashMap::new();
}
+#[cfg(features="fmt")]
#[test]
fn test_labels() {
assert_eq!(RegSpec::rip().name(), "rip");
@@ -21,6 +22,7 @@ fn test_labels() {
assert_eq!(RegSpec::al().name(), "al");
}
+#[cfg(features="fmt")]
#[test]
fn test_bank_names() {
assert_eq!(RegSpec::al().class().name(), "byte");
diff --git a/test/protected_mode/display.rs b/test/protected_mode/display.rs
index 5f0c68d..f9e0d6a 100644
--- a/test/protected_mode/display.rs
+++ b/test/protected_mode/display.rs
@@ -41,9 +41,13 @@ fn test_display_under(decoder: &InstDecoder, style: DisplayStyle, data: &[u8], e
// decided i do not like at&t syntax much at all. not going to write a formatter for it. some test
// cases will live on in case someone else feels like adding one, or i get mad enough to do it.
+#[allow(unreachable_code)]
#[ignore]
#[test]
fn test_instructions_atnt() {
+ // `ignore` is now used to avoid running (slow!) exhaustive tests in a default `cargo test`.
+ // running exhaustive tests now runs these tests, which fail. so instead, return early.
+ return;
// just modrm
test_display(&[0x33, 0x08], "xor (%eax), %ecx");
test_display(&[0x33, 0x20], "xor (%eax), %esp");
diff --git a/test/protected_mode/evex_generated.rs b/test/protected_mode/evex_generated.rs
index 236edec..9c3a06e 100644
--- a/test/protected_mode/evex_generated.rs
+++ b/test/protected_mode/evex_generated.rs
@@ -11,7 +11,17 @@ fn test_invalid(data: &[u8]) {
fn test_invalid_under(decoder: &InstDecoder, data: &[u8]) {
let mut reader = U8Reader::new(data);
if let Ok(inst) = decoder.decode(&mut reader) {
- panic!("decoded {:?} from {:02x?} under decoder {}", inst.opcode(), data, decoder);
+ // realistically, the chances an error only shows up under non-fmt builds seems unlikely,
+ // but try to report *something* in such cases.
+ cfg_if::cfg_if! {
+ if #[cfg(feature="fmt")] {
+ panic!("decoded {:?} from {:02x?} under decoder {}", inst.opcode(), data, decoder);
+ } else {
+ // don't warn about the unused inst here
+ let _ = inst;
+ panic!("decoded instruction <non-fmt build> from {:02x?} under decoder <non-fmt build>", data);
+ }
+ }
} else {
// this is fine
}
@@ -27,25 +37,39 @@ fn test_display_under(decoder: &InstDecoder, data: &[u8], expected: &'static str
for b in data {
write!(hex, "{:02x}", b).unwrap();
}
- let mut reader = U8Reader::new(data);
+ let mut reader = yaxpeax_arch::U8Reader::new(data);
match decoder.decode(&mut reader) {
Ok(instr) => {
- let text = format!("{}", instr);
- assert!(
- text == expected,
- "display error for {}:\n decoded: {:?} under decoder {}\n displayed: {}\n expected: {}\n",
- hex,
- instr,
- decoder,
- text,
- expected
- );
+ cfg_if::cfg_if! {
+ if #[cfg(feature="fmt")] {
+ let text = format!("{}", instr);
+ assert!(
+ text == expected,
+ "display error for {}:\n decoded: {:?} under decoder {}\n displayed: {}\n expected: {}\n",
+ hex,
+ instr,
+ decoder,
+ text,
+ expected
+ );
+ } else {
+ eprintln!("non-fmt build cannot compare text equality")
+ }
+ }
// while we're at it, test that the instruction is as long, and no longer, than its
// input
assert_eq!((0u32.wrapping_offset(instr.len()).to_linear()) as usize, data.len(), "instruction length is incorrect, wanted instruction {}", expected);
},
Err(e) => {
- assert!(false, "decode error ({}) for {} under decoder {}:\n expected: {}\n", e, hex, decoder, expected);
+ cfg_if::cfg_if! {
+ if #[cfg(feature="fmt")] {
+ assert!(false, "decode error ({}) for {} under decoder {}:\n expected: {}\n", e, hex, decoder, expected);
+ } else {
+ // avoid the unused `e` warning
+ let _ = e;
+ assert!(false, "decode error (<non-fmt build>) for {} under decoder <non-fmt build>:\n expected: {}\n", hex, expected);
+ }
+ }
}
}
}
diff --git a/test/protected_mode/mod.rs b/test/protected_mode/mod.rs
index 744d006..9cda6be 100644
--- a/test/protected_mode/mod.rs
+++ b/test/protected_mode/mod.rs
@@ -1,5 +1,6 @@
mod regspec;
mod operand;
+#[cfg(feature="fmt")]
mod display;
mod evex_generated;
@@ -15,7 +16,17 @@ fn test_invalid(data: &[u8]) {
fn test_invalid_under(decoder: &InstDecoder, data: &[u8]) {
let mut reader = yaxpeax_arch::U8Reader::new(data);
if let Ok(inst) = decoder.decode(&mut reader) {
- panic!("decoded {:?} from {:02x?} under decoder {}", inst.opcode(), data, decoder);
+ // realistically, the chances an error only shows up under non-fmt builds seems unlikely,
+ // but try to report *something* in such cases.
+ cfg_if::cfg_if! {
+ if #[cfg(feature="fmt")] {
+ panic!("decoded {:?} from {:02x?} under decoder {}", inst.opcode(), data, decoder);
+ } else {
+ // don't warn about the unused inst here
+ let _ = inst;
+ panic!("decoded instruction <non-fmt build> from {:02x?} under decoder <non-fmt build>", data);
+ }
+ }
} else {
// this is fine
}
@@ -33,22 +44,36 @@ fn test_display_under(decoder: &InstDecoder, data: &[u8], expected: &'static str
let mut reader = yaxpeax_arch::U8Reader::new(data);
match decoder.decode(&mut reader) {
Ok(instr) => {
- let text = format!("{}", instr);
- assert!(
- text == expected,
- "display error for {}:\n decoded: {:?} under decoder {}\n displayed: {}\n expected: {}\n",
- hex,
- instr,
- decoder,
- text,
- expected
- );
+ cfg_if::cfg_if! {
+ if #[cfg(feature="fmt")] {
+ let text = format!("{}", instr);
+ assert!(
+ text == expected,
+ "display error for {}:\n decoded: {:?} under decoder {}\n displayed: {}\n expected: {}\n",
+ hex,
+ instr,
+ decoder,
+ text,
+ expected
+ );
+ } else {
+ eprintln!("non-fmt build cannot compare text equality")
+ }
+ }
// while we're at it, test that the instruction is as long, and no longer, than its
// input
assert_eq!((0u32.wrapping_offset(instr.len()).to_linear()) as usize, data.len(), "instruction length is incorrect, wanted instruction {}", expected);
},
Err(e) => {
- assert!(false, "decode error ({}) for {} under decoder {}:\n expected: {}\n", e, hex, decoder, expected);
+ cfg_if::cfg_if! {
+ if #[cfg(feature="fmt")] {
+ assert!(false, "decode error ({}) for {} under decoder {}:\n expected: {}\n", e, hex, decoder, expected);
+ } else {
+ // avoid the unused `e` warning
+ let _ = e;
+ assert!(false, "decode error (<non-fmt build>) for {} under decoder <non-fmt build>:\n expected: {}\n", hex, expected);
+ }
+ }
}
}
}
diff --git a/test/protected_mode/regspec.rs b/test/protected_mode/regspec.rs
index 7d43c7a..aeca086 100644
--- a/test/protected_mode/regspec.rs
+++ b/test/protected_mode/regspec.rs
@@ -11,6 +11,7 @@ fn test_hash() {
let _: HashMap<RegSpec, u64> = HashMap::new();
}
+#[cfg(features="fmt")]
#[test]
fn test_labels() {
assert_eq!(RegSpec::eip().name(), "eip");
@@ -19,6 +20,7 @@ fn test_labels() {
assert_eq!(RegSpec::al().name(), "al");
}
+#[cfg(features="fmt")]
#[test]
fn test_bank_names() {
assert_eq!(RegSpec::al().class().name(), "byte");
diff --git a/test/real_mode/mod.rs b/test/real_mode/mod.rs
index 60f4c47..dcd124c 100644
--- a/test/real_mode/mod.rs
+++ b/test/real_mode/mod.rs
@@ -12,7 +12,17 @@ fn test_invalid(data: &[u8]) {
fn test_invalid_under(decoder: &InstDecoder, data: &[u8]) {
let mut reader = U8Reader::new(data);
if let Ok(inst) = decoder.decode(&mut reader) {
- panic!("decoded {:?} from {:02x?} under decoder {}", inst.opcode(), data, decoder);
+ // realistically, the chances an error only shows up under non-fmt builds seems unlikely,
+ // but try to report *something* in such cases.
+ cfg_if::cfg_if! {
+ if #[cfg(feature="fmt")] {
+ panic!("decoded {:?} from {:02x?} under decoder {}", inst.opcode(), data, decoder);
+ } else {
+ // don't warn about the unused inst here
+ let _ = inst;
+ panic!("decoded instruction <non-fmt build> from {:02x?} under decoder <non-fmt build>", data);
+ }
+ }
} else {
// this is fine
}
@@ -31,22 +41,37 @@ fn test_display_under(decoder: &InstDecoder, data: &[u8], expected: &'static str
let mut reader = U8Reader::new(data);
match decoder.decode(&mut reader) {
Ok(instr) => {
- let text = format!("{}", instr);
- assert!(
- text == expected,
- "display error for {}:\n decoded: {:?} under decoder {}\n displayed: {}\n expected: {}\n",
- hex,
- instr,
- decoder,
- text,
- expected
- );
+ cfg_if::cfg_if! {
+ if #[cfg(feature="fmt")] {
+ let text = format!("{}", instr);
+ assert!(
+ text == expected,
+ "display error for {}:\n decoded: {:?} under decoder {}\n displayed: {}\n expected: {}\n",
+ hex,
+ instr,
+ decoder,
+ text,
+ expected
+ );
+ } else {
+ eprintln!("non-fmt build cannot compare text equality")
+ }
+ }
// while we're at it, test that the instruction is as long, and no longer, than its
// input
assert_eq!((0u32.wrapping_offset(instr.len()).to_linear()) as usize, data.len(), "instruction length is incorrect, wanted instruction {}", expected);
},
Err(e) => {
- assert!(false, "decode error ({}) for {} under decoder {}:\n expected: {}\n", e, hex, decoder, expected);
+ cfg_if::cfg_if! {
+ if #[cfg(feature="fmt")] {
+ assert!(false, "decode error ({}) for {} under decoder {}:\n expected: {}\n", e, hex, decoder, expected);
+ } else {
+ // avoid the unused `e` warning
+ let _ = e;
+ assert!(false, "decode error (<non-fmt build>) for {} under decoder <non-fmt build>:\n expected: {}\n", hex, expected);
+ }
+ }
+
}
}
}