aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG8
-rw-r--r--src/armv7.rs25
-rw-r--r--tests/armv7/mod.rs13
3 files changed, 44 insertions, 2 deletions
diff --git a/CHANGELOG b/CHANGELOG
index b53d6b4..deb6edd 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -7,8 +7,14 @@
* support non-`*2` forms of coprocessor instructions
* collapse `*2` encoding choice for coprocessor instructions into a paramter
on the opcode (rather than distinct opcodes)
+
+several fixes from @Grond66:
* ARMv7: added `armv7::InstDecoder::in_thumb_mode` to read back if an ARMv7
- decoder is operating in thumb mode. thank you for the patch, @Grond66!
+ decoder is operating in thumb mode.
+* ARMv7: fix incorrect handling of registers lsr or asr by an immediate 32.
+ yaxpeax-arm incorrectly reported a shift of 0, but should have reported 32.
+
+thank you for the patches!
## 0.4.0
diff --git a/src/armv7.rs b/src/armv7.rs
index 2ce8122..f911388 100644
--- a/src/armv7.rs
+++ b/src/armv7.rs
@@ -389,8 +389,31 @@ pub struct RegImmShift {
impl RegImmShift {
/// the immediate this register is shifted by.
pub fn imm(&self) -> u8 {
- (self.data >> 7) as u8 & 0b11111
+ let raw = (self.data >> 7) as u8 & 0b11111;
+ // in the ARMv7m reference,
+ // `Instruction Details` ->
+ // `Shifts applied to a register` ->
+ // `Constant shifts`:
+ //
+ // > The assembler encodes <shift> into two type bits and five immediate bits, as follows:
+ // > ...
+ // > LSR #<n> type = 0b01
+ // > If <n> < 32, immediate = <n>.
+ // > If <n> == 32, immediate = 0.
+ // > ASR #<n> type = 0b10
+ // > If <n> < 32, immediate = <n>.
+ // > If <n> == 32, immediate = 0.
+ //
+ // so we have to fix this up here.
+ if raw == 0 {
+ let stype = self.stype();
+ if stype == ShiftStyle::LSR || stype == ShiftStyle::ASR {
+ return 32;
+ }
+ }
+ raw
}
+
/// the way in which this register is shifted.
pub fn stype(&self) -> ShiftStyle {
ShiftStyle::from((self.data >> 5) as u8 & 0b11)
diff --git a/tests/armv7/mod.rs b/tests/armv7/mod.rs
index 8f413ca..9a9e718 100644
--- a/tests/armv7/mod.rs
+++ b/tests/armv7/mod.rs
@@ -696,6 +696,19 @@ fn test_decode_mul() {
);
}
+#[test]
+fn test_register_shift_rotate() {
+ test_armv6([0xec, 0x02, 0x00, 0x00], "andeq r0, r0, ip, ror 5");
+ test_armv6([0xa0, 0x33, 0x0b, 0x00], "andeq r3, fp, r0, lsr 7");
+ test_armv6([0xa4, 0x33, 0x0b, 0x00], "andeq r3, fp, r4, lsr 7");
+ test_armv6([0xa0, 0x7d, 0x0b, 0x00], "andeq r7, fp, r0, lsr 27");
+
+ // When an LSR or ASR shift has an encoded immediate of zero, it actually means that the
+ // applied shift is 32.
+ test_armv6([0x21, 0x00, 0x20, 0x00], "eoreq r0, r0, r1, lsr 32");
+ test_armv6([0x41, 0x00, 0x20, 0x00], "eoreq r0, r0, r1, asr 32");
+}
+
static INSTRUCTION_BYTES: [u8; 4 * 60] = [
0x24, 0xc0, 0x9f, 0xe5,
0x00, 0xb0, 0xa0, 0xe3,