diff options
| -rw-r--r-- | CHANGELOG | 15 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | Makefile | 14 | ||||
| -rw-r--r-- | src/lib.rs | 19 | ||||
| -rw-r--r-- | src/long_mode/mod.rs | 75 | ||||
| -rw-r--r-- | src/protected_mode/mod.rs | 75 | ||||
| -rw-r--r-- | src/real_mode/mod.rs | 74 | ||||
| -rw-r--r-- | test/long_mode/display.rs | 4 | ||||
| -rw-r--r-- | test/long_mode/evex_generated.rs | 50 | ||||
| -rw-r--r-- | test/long_mode/mod.rs | 49 | ||||
| -rw-r--r-- | test/long_mode/regspec.rs | 2 | ||||
| -rw-r--r-- | test/protected_mode/display.rs | 4 | ||||
| -rw-r--r-- | test/protected_mode/evex_generated.rs | 50 | ||||
| -rw-r--r-- | test/protected_mode/mod.rs | 49 | ||||
| -rw-r--r-- | test/protected_mode/regspec.rs | 2 | ||||
| -rw-r--r-- | test/real_mode/mod.rs | 49 | 
16 files changed, 376 insertions, 156 deletions
@@ -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 @@ -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" @@ -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); +                } +            } +          }      }  }  | 
