From ecec44cbee7be32e632b24ae0e4fe3f84e6e8ef8 Mon Sep 17 00:00:00 2001 From: iximeow Date: Fri, 11 Sep 2026 16:08:55 +0000 Subject: fix issues around unpredictable ldrd and offset printing --- CHANGELOG | 9 +++++++++ src/armv7/display.rs | 10 ++++------ src/armv7/thumb.rs | 16 ++++++++++++++-- tests/armv7/thumb.rs | 21 ++++++++++++++++++++- 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 61e29c0..19dd69b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -27,6 +27,15 @@ rather than pc * thumb2: several instructions which decode an explicit rotate operand now do so into a distinct Operand::Ror (sxth, uxth, stxb16, utxb16, sxtb, uxtb) +* thumb2: have unpredictable ldrd (literal) match a more "typical" interpretation + when ldrd is encoded as post-index with writeback, the manual describes the + behavior as "unpredictable", but capstone and others report this as a + post-index as the bits imply. yaxpeax will still reject this encoding if + configured to reject unpredictable instructions. +* ARMv7: do not try to elide memory offset when 0 in postindex operands. + this makes a post-index (and implied writeback!) ambiguous with a pre-indexed + access with offset zero. that is, `[r1, 0]` may be written as `[r1]`, but + `[r1], 0` has a different meaning than `[r1]`. * ARMv8: support thumb2 encoding of `Test Target` instruction, `Opcode::TT` (formatted as any of `tt`, `ttt`, `tta`, `ttat`) * ARMv8: formerly-coprocessor instructions have been defined into SIMD extensions, diff --git a/src/armv7/display.rs b/src/armv7/display.rs index 2d2a77c..3f7162b 100644 --- a/src/armv7/display.rs +++ b/src/armv7/display.rs @@ -245,13 +245,11 @@ impl crate::armv7::OperandVisitor for DisplayingOperandVisitor<' self.f.write_reg(base.number())?; self.f.write_char(']')?; - if offset != 0 { - self.f.write_fixed_size(", ")?; - if !add { - self.f.write_char('-')?; - } - self.f.write_prefixed_u16(offset)?; + self.f.write_fixed_size(", ")?; + if !add { + self.f.write_char('-')?; } + self.f.write_prefixed_u16(offset)?; Ok(()) } diff --git a/src/armv7/thumb.rs b/src/armv7/thumb.rs index 1d9d96a..077834a 100644 --- a/src/armv7/thumb.rs +++ b/src/armv7/thumb.rs @@ -286,7 +286,13 @@ pub fn decode_into::Address, ::Word>>(d inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::Reg(Reg::from_u8(rd)), - Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm8 << 2, u, false), + if p { + Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm8 << 2, u, w) + } else { + decoder.unpredictable()?; + // p == 0 and w == 0 is impossible, would be tbb/tbh + Operand::RegDerefPostindexOffset(Reg::from_u8(rn), imm8 << 2, u, false) + }, Operand::Nothing, ]; } @@ -595,7 +601,13 @@ pub fn decode_into::Address, ::Word>>(d inst.operands = [ Operand::Reg(Reg::from_u8(rt)), Operand::Reg(Reg::from_u8(rd)), - Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm8 << 2, u, false), + if p { + Operand::RegDerefPreindexOffset(Reg::from_u8(rn), imm8 << 2, u, w) + } else { + decoder.unpredictable()?; + // p == 0 and w == 0 is impossible, would be tbb/tbh + Operand::RegDerefPostindexOffset(Reg::from_u8(rn), imm8 << 2, u, false) + }, Operand::Nothing, ]; } diff --git a/tests/armv7/thumb.rs b/tests/armv7/thumb.rs index f03b046..b0eb262 100644 --- a/tests/armv7/thumb.rs +++ b/tests/armv7/thumb.rs @@ -3616,6 +3616,25 @@ fn test_decode_ldr_32b_cases() { &[0xf3, 0xe9, 0x7e, 0x5a], "ldrd r5, r10, [r3, 0x1f8]!" ); + // this is LDRD (literal) .. kind of. P == 0 but Rn == 0b1111 so following the manual we + // actually get to LDRD (literal) with P == 0 and maybe even W == 1. additionally: + // > if W == '1' then UNPREDICTABLE; + // + // yax at one point ignored W (and P) in this particular configuration, but capstone operates + // as if these are effectual. that seems ... fine ... particularly given yax is set up to + // reject this encoding as `unpredictable` if requested anyway. + test_display( + &[0x7f, 0xe8, 0x80, 0x3e], + "ldrd r3, lr, [pc], -0x200" + ); + + // i'd had a bird-brained idea to write a post-index operand with offset 0, like `[r1], #0`, as + // simply `[r1]`. the problem here is that post-index access implies writeback, which at best + // is `[r1]!`. but it's better to not do any of this. + test_display( + &[0x18, 0xf9, 0x00, 0x99], + "ldrsb.w sb, [r8], -0x0" + ); } #[test] @@ -3729,7 +3748,7 @@ fn test_decode_str_32b_cases() { ); test_display( &[0x41, 0xf8, 0x00, 0x2b], - "str.w r2, [r1]" + "str.w r2, [r1], 0x0" ); } -- cgit v1.1