From 2097524c851b15e89091fd3775817a06f0eeae4f Mon Sep 17 00:00:00 2001 From: iximeow Date: Sun, 24 Apr 2022 17:39:21 -0700 Subject: fix a few issues preventing no-std builds from ... building this includes a `Makefile` that exercises the various crate configs. most annoyingly, several doc comments needed to grow `#[cfg(feature="fmt")]` blocks so docs continue to build with that feature enabled or disabled. carved out a way to run exhaustive tests; they should be written as `#[ignore]`, and then the makefile will run even ignored tests on the expectation that this will run the exhaustive (but slower) suite. exhaustive tests are not yet written. they'll probably involve spanning 4 byte sequences from 0 to 2^32-1. --- test/long_mode/display.rs | 4 +++ test/long_mode/evex_generated.rs | 50 ++++++++++++++++++++++++++--------- test/long_mode/mod.rs | 49 +++++++++++++++++++++++++--------- test/long_mode/regspec.rs | 2 ++ test/protected_mode/display.rs | 4 +++ test/protected_mode/evex_generated.rs | 50 ++++++++++++++++++++++++++--------- test/protected_mode/mod.rs | 49 +++++++++++++++++++++++++--------- test/protected_mode/regspec.rs | 2 ++ test/real_mode/mod.rs | 49 +++++++++++++++++++++++++--------- 9 files changed, 197 insertions(+), 62 deletions(-) (limited to 'test') 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 from {:02x?} under decoder ", 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 () for {} under decoder :\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 from {:02x?} under decoder ", 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 () for {} under decoder :\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 = 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 from {:02x?} under decoder ", 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 () for {} under decoder :\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 from {:02x?} under decoder ", 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 () for {} under decoder :\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 = 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 from {:02x?} under decoder ", 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 () for {} under decoder :\n expected: {}\n", hex, expected); + } + } + } } } -- cgit v1.1