aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2026-07-26 17:56:26 +0000
committeriximeow <me@iximeow.net>2026-07-28 07:28:15 +0000
commit9b3c56c0e62c3d1c27ffef4b8021663798b73ca7 (patch)
tree212b04bd20e3a1b1ec1e9e2296986389b70ef31d
parent019920be5aa0b3b129a4ecf3bc31c14fddf34eff (diff)
thumb: when nonconforming, allow it/cond=1111 like capstone
-rw-r--r--src/armv7/display.rs11
-rw-r--r--src/armv7/thumb.rs9
-rw-r--r--tests/armv7/thumb.rs22
3 files changed, 36 insertions, 6 deletions
diff --git a/src/armv7/display.rs b/src/armv7/display.rs
index 6f6fba1..d2198de 100644
--- a/src/armv7/display.rs
+++ b/src/armv7/display.rs
@@ -620,8 +620,15 @@ pub(crate) fn visit_inst<T: DisplaySink>(instr: &Instruction, out: &mut T) -> fm
panic!("impossible it operand");
};
- let inv = cond & 1 == 1;
- let condition = ConditionCode::build(*cond as u8);
+ let (inv, condition) = if *cond == 0b1111 {
+ // we've allowed *unpredictable* instruction encodings. capstone calls this condition
+ // "al" as well, so might as well follow along... and for this condition code it
+ // doesn't invert the fields either!
+ (false, ConditionCode::AL)
+ } else {
+ let inv = cond & 1 == 1;
+ (inv, ConditionCode::build(*cond as u8))
+ };
if mask & 0b0001 != 0 {
// three flags
out.write_char(if inv ^ ((mask & 0b1000) != 0) { 'e' } else { 't' })?;
diff --git a/src/armv7/thumb.rs b/src/armv7/thumb.rs
index 7aa3a5d..9e9c30a 100644
--- a/src/armv7/thumb.rs
+++ b/src/armv7/thumb.rs
@@ -3918,8 +3918,13 @@ pub fn decode_into<T: Reader<<ARMv7 as Arch>::Address, <ARMv7 as Arch>::Word>>(d
let firstcond = opa;
let mask = opb;
inst.opcode = Opcode::IT;
- if firstcond == 0b1111 {
- return Err(DecodeError::InvalidOperand);
+ if decoder.should_is_must {
+ // > if firstcond == ‘1111’ ||
+ // > (firstcond == ‘1110’ && BitCount(mask) != 1)
+ // > then UNPREDICTABLE;
+ if firstcond == 0b1111 {
+ return Err(DecodeError::Nonconforming);
+ }
}
inst.operands = [
Operand::Imm32(firstcond),
diff --git a/tests/armv7/thumb.rs b/tests/armv7/thumb.rs
index d9ecda6..c8b0b11 100644
--- a/tests/armv7/thumb.rs
+++ b/tests/armv7/thumb.rs
@@ -19,8 +19,8 @@ fn test_invalid_under(decoder: &InstDecoder, data: &[u8]) {
}
#[allow(dead_code)]
-fn test_display_under(decoder: &InstDecoder, data: [u8; 4], expected: &'static str) {
- let mut reader = yaxpeax_arch::U8Reader::new(&data[..]);
+fn test_display_under(decoder: &InstDecoder, data: &[u8], expected: &'static str) {
+ let mut reader = yaxpeax_arch::U8Reader::new(data);
let instr = match decoder.decode(&mut reader) {
Err(e) => {
panic!("failed to decode {:#x?}: {}", data, e)
@@ -76,6 +76,20 @@ fn test_display(data: &[u8], expected: &'static str) {
);
}
+fn test_nonconforming(data: &[u8], expected: &'static str) {
+ let conforming = InstDecoder::default_thumb().allow_nonconforming(false);
+ let nonconforming = InstDecoder::default_thumb().allow_nonconforming(true);
+
+ let mut reader = yaxpeax_arch::U8Reader::new(&data[..]);
+ let result = conforming.decode(&mut reader);
+ assert!(
+ result.is_err(),
+ "got bad result: {:?} from {:#x?}", result, data
+ );
+
+ test_display_under(&nonconforming, data, expected);
+}
+
#[test]
fn test_unpredictable_instructions() {
test_invalid(&[0x80, 0xfa, 0x40, 0x00]);
@@ -1951,6 +1965,10 @@ fn test_decode_it_cases() {
&[0xef, 0xbf],
"iteee al"
);
+ test_nonconforming(
+ &[0xf7, 0xbf],
+ "ittee al",
+ );
}
#[test]
fn test_decode_ldm_16b_cases() {