From be06139418380397791d157efa74e5b1468e2ab1 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 16:09:37 -0400 Subject: [PATCH 01/40] Add unit tests for ARMv7 MVN instruction --- tests/native/test_armv7cpu.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/native/test_armv7cpu.py b/tests/native/test_armv7cpu.py index 59adc2a3a..0ea78a3d9 100644 --- a/tests/native/test_armv7cpu.py +++ b/tests/native/test_armv7cpu.py @@ -224,6 +224,23 @@ def _checkFlagsNZCV(self, n, z, c, v): self.assertEqual(self.rf.read("APSR_C"), c) self.assertEqual(self.rf.read("APSR_V"), v) + # MVN + @itest("mvn r0, #0x0") + def test_mvn_imm_min(self): + self.assertEqual(self.rf.read("R0"), 0xFFFFFFFF) + + @itest("mvn r0, #0xFFFFFFFF") + def test_mvn_imm_max(self): + self.assertEqual(self.rf.read("R0"), 0x0) + + @itest("mvn r0, #0x18000") + def test_mvn_mod_imm_1(self): + self.assertEqual(self.rf.read("R0"), 0xFFFE7FFF) + + @itest("mvn r0, #24, 20") + def test_mvn_mod_imm_2(self): + self.assertEqual(self.rf.read("R0"), 0xFFFE7FFF) + # MOV @itest("mov r0, 0x0") From ee3952efd95dcf65daa765b4c7f20130f57ec978 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 16:10:00 -0400 Subject: [PATCH 02/40] Add unit tests for ARMv7 MOV instruction --- tests/native/test_armv7cpu.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/native/test_armv7cpu.py b/tests/native/test_armv7cpu.py index 0ea78a3d9..a1ae8b050 100644 --- a/tests/native/test_armv7cpu.py +++ b/tests/native/test_armv7cpu.py @@ -259,6 +259,14 @@ def test_mov_imm_modified_imm_min(self): def test_mov_imm_modified_imm_max(self): self.assertEqual(self.rf.read("R0"), 0xFF000000) + @itest("mov r0, #0x18000") + def test_mov_mod_imm_1(self): + self.assertEqual(self.rf.read("R0"), 0x18000) + + @itest("mov r0, #24, 20") + def test_mov_mod_imm_2(self): + self.assertEqual(self.rf.read("R0"), 0x18000) + @itest_custom("mov r0, r1") def test_mov_immreg(self): self.rf.write("R1", 0) From 88506f0b985607f3c629a8e14e36c87c1dd24ee4 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 16:10:51 -0400 Subject: [PATCH 03/40] Add unit tests for ARMv7 ADD instruction --- tests/native/test_armv7cpu.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/native/test_armv7cpu.py b/tests/native/test_armv7cpu.py index a1ae8b050..911728060 100644 --- a/tests/native/test_armv7cpu.py +++ b/tests/native/test_armv7cpu.py @@ -391,6 +391,18 @@ def test_add_imm_mod_imm_min(self): self.cpu.execute() self.assertEqual(self.rf.read("R3"), 44 + 0x100) + @itest_custom("add r3, r1, 0x18000") + def test_add_imm_mod_imm_case1(self): + self.rf.write("R1", 44) + self.cpu.execute() + self.assertEqual(self.rf.read("R3"), 44 + 0x18000) + + @itest_custom("add r3, r1, 24, 20") + def test_add_imm_mod_imm_case2(self): + self.rf.write("R1", 44) + self.cpu.execute() + self.assertEqual(self.rf.read("R3"), 44 + 0x18000) + @itest_custom("add r3, r1, 0xff000000") def test_add_imm_mod_imm_max(self): self.rf.write("R1", 44) From 950dc13fbb9970a7868931bd02a8e728bbae9bd4 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 16:11:14 -0400 Subject: [PATCH 04/40] Add unit tests for ARMv7 ADC instruction --- tests/native/test_armv7cpu.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/native/test_armv7cpu.py b/tests/native/test_armv7cpu.py index 911728060..a2a11d718 100644 --- a/tests/native/test_armv7cpu.py +++ b/tests/native/test_armv7cpu.py @@ -498,11 +498,13 @@ def test_add_reg_sft_ror(self): self.cpu.execute() self.assertEqual(self.rf.read("R3"), 0x60000000) + # ADCS @itest_setregs("R3=0xfffffff6", "R4=10") @itest_thumb("adcs r3, r4") def test_thumb_adc_basic(self): self.assertEqual(self.rf.read("R3"), 0) + # ADC @itest_custom("adc r3, r1, r2") @itest_setregs("R1=1", "R2=2", "APSR_C=1") def test_adc_basic(self): @@ -517,6 +519,18 @@ def test_adc_reg_sft_ror(self): self.cpu.execute() self.assertEqual(self.rf.read("R3"), 0x60000001) + @itest_custom("adc r3, r1, #0x18000") + @itest_setregs("R1=1", "APSR_C=1") + def test_adc_mod_imm_1(self): + self.cpu.execute() + self.assertEqual(self.rf.read("R3"), 0x18002) + + @itest_custom("adc r3, r1, #24, 20") + @itest_setregs("R1=1", "APSR_C=1") + def test_adc_mod_imm_2(self): + self.cpu.execute() + self.assertEqual(self.rf.read("R3"), 0x18002) + # TODO what is shifter_carry_out in the manual, A8-291? it gets set to # Bit[0] presumably, but i have no clue what it is. Not mentioned again in # manual. From ae583117b2c4452b0b8d1fbcfbc68067e145e4a8 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 16:11:25 -0400 Subject: [PATCH 05/40] Add unit tests for ARMv7 ADR instruction --- tests/native/test_armv7cpu.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tests/native/test_armv7cpu.py b/tests/native/test_armv7cpu.py index a2a11d718..d2c1b2f7f 100644 --- a/tests/native/test_armv7cpu.py +++ b/tests/native/test_armv7cpu.py @@ -1034,8 +1034,28 @@ def test_str(self): # ADR - @itest_custom("adr r0, #16", mode=CS_MODE_THUMB) + @itest_custom("adr r0, #16") def test_adr(self): + pre_pc = self.rf.read("PC") + self.cpu.execute() + self.assertEqual(self.rf.read("R0"), (pre_pc + 8) + 16) + + # Note, ARM ARM says that the following is an alternative encoding for a form of ADR + @itest_custom("add r0, PC, #0x10") + def test_adr_mod_imm_1(self): + pre_pc = self.rf.read("PC") + self.cpu.execute() + self.assertEqual(self.rf.read("R0"), (pre_pc + 8) + 0x10) + + # Note, ARM ARM says that the following is an alternative encoding for a form of ADR + @itest_custom("add r0, PC, #1, 28") + def test_adr_mod_imm_2(self): + pre_pc = self.rf.read("PC") + self.cpu.execute() + self.assertEqual(self.rf.read("R0"), (pre_pc + 8) + 0x10) + + @itest_custom("adr r0, #16", mode=CS_MODE_THUMB) + def test_adr_thumb(self): pre_pc = self.rf.read("PC") self.cpu.execute() self.assertEqual(self.rf.read("R0"), (pre_pc + 4) + 16) # adr is 4 bytes long From ee1abc2ca74818705423d22640ba06a8f7870c89 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 16:11:34 -0400 Subject: [PATCH 06/40] Add unit tests for ARMv7 CMN instruction --- tests/native/test_armv7cpu.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/native/test_armv7cpu.py b/tests/native/test_armv7cpu.py index d2c1b2f7f..249f72bd9 100644 --- a/tests/native/test_armv7cpu.py +++ b/tests/native/test_armv7cpu.py @@ -1182,6 +1182,18 @@ def test_tbh_pc_relative(self): self.cpu.execute() self.assertEqual(self.rf.read("PC"), (pre_pc + 4) + 42) # tbh is 4 bytes long + # CMN + + @itest_setregs("R0=-0x18000") + @itest("cmn r0, #0x18000") + def test_cmn_eq_mod_imm_1(self): + self._checkFlagsNZCV(0, 1, 1, 0) + + @itest_setregs("R0=-0x18000") + @itest("cmn r0, #24, 20") + def test_cmn_eq_mod_imm_2(self): + self._checkFlagsNZCV(0, 1, 1, 0) + # CMP @itest_setregs("R0=3") From 8c242bbf012bb1565653e0b614f8a0197fcd5f81 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 16:11:47 -0400 Subject: [PATCH 07/40] Add unit tests for ARMv7 CMP instruction --- tests/native/test_armv7cpu.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/native/test_armv7cpu.py b/tests/native/test_armv7cpu.py index 249f72bd9..0e05e40f7 100644 --- a/tests/native/test_armv7cpu.py +++ b/tests/native/test_armv7cpu.py @@ -1201,6 +1201,16 @@ def test_cmn_eq_mod_imm_2(self): def test_cmp_eq(self): self._checkFlagsNZCV(0, 1, 1, 0) + @itest_setregs("R0=0x18000") + @itest("cmp r0, #0x18000") + def test_cmp_eq_mod_imm_1(self): + self._checkFlagsNZCV(0, 1, 1, 0) + + @itest_setregs("R0=0x18000") + @itest("cmp r0, #24, 20") + def test_cmp_eq_mod_imm_2(self): + self._checkFlagsNZCV(0, 1, 1, 0) + @itest_setregs("R0=3") @itest("cmp r0, 5") def test_cmp_lt(self): From 5e6161cdf97711b6ad3268eed9eed8d2d9a5e29c Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 16:11:55 -0400 Subject: [PATCH 08/40] Add unit tests for ARMv7 SUB instruction --- tests/native/test_armv7cpu.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/native/test_armv7cpu.py b/tests/native/test_armv7cpu.py index 0e05e40f7..ea0928b31 100644 --- a/tests/native/test_armv7cpu.py +++ b/tests/native/test_armv7cpu.py @@ -1268,6 +1268,8 @@ def test_clz_lead_zero_then_more_zeroes(self): self.cpu.execute() self.assertEqual(self.rf.read("R1"), 1) + # SUB + @itest_custom("sub r3, r1, r2") @itest_setregs("R1=4", "R2=2") def test_sub_basic(self): @@ -1285,6 +1287,18 @@ def test_sub_imm(self): self.cpu.execute() self.assertEqual(self.rf.read("R3"), 5) + @itest_custom("sub r3, r1, #0x18000") + @itest_setregs("R1=0x18000") + def test_sub_mod_imm_1(self): + self.cpu.execute() + self.assertEqual(self.rf.read("R3"), 0) + + @itest_custom("sub r3, r1, #24, 20") + @itest_setregs("R1=0x18000") + def test_sub_mod_imm_2(self): + self.cpu.execute() + self.assertEqual(self.rf.read("R3"), 0) + @itest_custom("uqsub8 r3, r1, r2") @itest_setregs("R1=0x04030201", "R2=0x01010101") def test_uqsub8_concrete(self): From 4c20478901fbde2d19f352025e4d8fb2e2cb843d Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 16:12:37 -0400 Subject: [PATCH 09/40] Add unit tests for ARMv7 RSC instruction --- tests/native/test_armv7cpu.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/native/test_armv7cpu.py b/tests/native/test_armv7cpu.py index ea0928b31..30004631d 100644 --- a/tests/native/test_armv7cpu.py +++ b/tests/native/test_armv7cpu.py @@ -1322,6 +1322,32 @@ def test_uqsub8_sym(self): all_vals = solver.get_all_values(self.cpu.memory.constraints, self.cpu.R3) self.assertIn(0x03020100, all_vals) + # RSC + + @itest_custom("rsc r3, r1, #0x18000") + @itest_setregs("R1=0x18000", "APSR_C=1") + def test_rsc_mod_imm_1(self): + self.cpu.execute() + self.assertEqual(self.rf.read("R3"), 0) + + @itest_custom("rsc r3, r1, #0x18000") + @itest_setregs("R1=0x17fff", "APSR_C=0") + def test_rsc_mod_imm_2(self): + self.cpu.execute() + self.assertEqual(self.rf.read("R3"), 0) + + @itest_custom("rsc r3, r1, #24, 20") + @itest_setregs("R1=0x18000", "APSR_C=1") + def test_rsc_mod_imm_3(self): + self.cpu.execute() + self.assertEqual(self.rf.read("R3"), 0) + + @itest_custom("rsc r3, r1, #24, 20") + @itest_setregs("R1=0x17fff", "APSR_C=0") + def test_rsc_mod_imm_4(self): + self.cpu.execute() + self.assertEqual(self.rf.read("R3"), 0) + @itest_custom("sbc r3, r1, #5") @itest_setregs("R1=10") def test_sbc_imm(self): From db1792f064e35d081862ac649f983f42225e88ed Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 16:12:46 -0400 Subject: [PATCH 10/40] Add unit tests for ARMv7 SBC instruction --- tests/native/test_armv7cpu.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/native/test_armv7cpu.py b/tests/native/test_armv7cpu.py index 30004631d..c69b26721 100644 --- a/tests/native/test_armv7cpu.py +++ b/tests/native/test_armv7cpu.py @@ -1348,6 +1348,8 @@ def test_rsc_mod_imm_4(self): self.cpu.execute() self.assertEqual(self.rf.read("R3"), 0) + # SBC + @itest_custom("sbc r3, r1, #5") @itest_setregs("R1=10") def test_sbc_imm(self): @@ -1359,6 +1361,30 @@ def test_sbc_imm(self): def test_sbc_thumb(self): self.assertEqual(self.rf.read("R0"), 0) + @itest_custom("sbc r3, r1, #0x18000") + @itest_setregs("R1=0x18010", "APSR_C=1") + def test_sbc_mod_imm_1(self): + self.cpu.execute() + self.assertEqual(self.rf.read("R3"), 0x10) + + @itest_custom("sbc r3, r1, #0x18000") + @itest_setregs("R1=0x18010", "APSR_C=0") + def test_sbc_mod_imm_2(self): + self.cpu.execute() + self.assertEqual(self.rf.read("R3"), 0xf) + + @itest_custom("sbc r3, r1, #24, 20") + @itest_setregs("R1=0x18010", "APSR_C=1") + def test_sbc_mod_imm_3(self): + self.cpu.execute() + self.assertEqual(self.rf.read("R3"), 0x10) + + @itest_custom("sbc r3, r1, #24, 20") + @itest_setregs("R1=0x18010", "APSR_C=0") + def test_sbc_mod_imm_4(self): + self.cpu.execute() + self.assertEqual(self.rf.read("R3"), 0xf) + # LDM/LDMIB/LDMDA/LDMDB @itest_custom("ldm sp, {r1, r2, r3}") From 78c36cd1b416bb1ba691a44df3d4743162578898 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 16:12:54 -0400 Subject: [PATCH 11/40] Add unit tests for ARMv7 ORR instruction --- tests/native/test_armv7cpu.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/native/test_armv7cpu.py b/tests/native/test_armv7cpu.py index c69b26721..7bccac84c 100644 --- a/tests/native/test_armv7cpu.py +++ b/tests/native/test_armv7cpu.py @@ -1573,6 +1573,18 @@ def test_orr_imm(self): def test_thumb_orr_imm(self): self.assertEqual(self.rf.read("R3"), 0x1005) + @itest_custom("orr r2, r3, #0x18000") + @itest_setregs("R3=0x1000") + def test_orr_mod_imm_1(self): + self.cpu.execute() + self.assertEqual(self.rf.read("R2"), 0x19000) + + @itest_custom("orr r2, r3, #24, 20") + @itest_setregs("R3=0x1000") + def test_orr_mod_imm_2(self): + self.cpu.execute() + self.assertEqual(self.rf.read("R2"), 0x19000) + @itest_custom("orrs r2, r3") @itest_setregs("R2=0x5", "R3=0x80000000") def test_orrs_imm_flags(self): From 98341cda9c69e3621112a55a8289600b7ad8f2b0 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 16:13:06 -0400 Subject: [PATCH 12/40] Add unit tests for ARMv7 EOR instruction --- tests/native/test_armv7cpu.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/native/test_armv7cpu.py b/tests/native/test_armv7cpu.py index 7bccac84c..e3bb1839e 100644 --- a/tests/native/test_armv7cpu.py +++ b/tests/native/test_armv7cpu.py @@ -1657,6 +1657,18 @@ def test_eor_reg_two_op_shifted(self): self.cpu.execute() self.assertEqual(self.rf.read("R2"), 0x5) + @itest_custom("eor r2, r3, #0x18000") + @itest_setregs("R3=0xA") + def test_eor_mod_imm_1(self): + self.cpu.execute() + self.assertEqual(self.rf.read("R2"), 0x1800a) + + @itest_custom("eor r2, r3, #24, 20") + @itest_setregs("R3=0xA") + def test_eor_mod_imm_2(self): + self.cpu.execute() + self.assertEqual(self.rf.read("R2"), 0x1800a) + # LDRH - see also LDR tests @itest_custom("ldrh r1, [sp]") From 77a3eece28c2e630780777029e566413a2845a4e Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 16:14:32 -0400 Subject: [PATCH 13/40] Add unit tests for ARMv7 TST instruction --- tests/native/test_armv7cpu.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tests/native/test_armv7cpu.py b/tests/native/test_armv7cpu.py index e3bb1839e..53da01da7 100644 --- a/tests/native/test_armv7cpu.py +++ b/tests/native/test_armv7cpu.py @@ -1764,7 +1764,27 @@ def test_ldrsb_reg_off_pos(self): # TST @itest_setregs("R1=1", "R3=0") @itest("tst r3, r1") - def test_tst(self): + def test_tst_1(self): + self._checkFlagsNZCV(0, 1, 0, 0) + + @itest_setregs("R1=1", "R3=1") + @itest("tst r3, r1") + def test_tst_2(self): + self._checkFlagsNZCV(0, 0, 0, 0) + + @itest_setregs("R1=1", "R3=3") + @itest("tst r3, r1") + def test_tst_3(self): + self._checkFlagsNZCV(0, 0, 0, 0) + + @itest_setregs("R3=0") + @itest("tst r3, #0x18000") + def test_tst_mod_imm_1(self): + self._checkFlagsNZCV(0, 1, 0, 0) + + @itest_setregs("R3=0") + @itest("tst r3, #24, 20") + def test_tst_mod_imm_2(self): self._checkFlagsNZCV(0, 1, 0, 0) # AND From 9f174713213b6ac82f1ce400d720c9f74d0ef5ea Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 16:14:43 -0400 Subject: [PATCH 14/40] Add unit tests for ARMv7 TEQ instruction --- tests/native/test_armv7cpu.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/native/test_armv7cpu.py b/tests/native/test_armv7cpu.py index 53da01da7..35946b937 100644 --- a/tests/native/test_armv7cpu.py +++ b/tests/native/test_armv7cpu.py @@ -1787,6 +1787,37 @@ def test_tst_mod_imm_1(self): def test_tst_mod_imm_2(self): self._checkFlagsNZCV(0, 1, 0, 0) + # TEQ + @itest_setregs("R1=1", "R3=0") + @itest("teq r3, r1") + def test_teq_1(self): + self._checkFlagsNZCV(0, 0, 0, 0) + + @itest_setregs("R1=1", "R3=1") + @itest("teq r3, r1") + def test_teq_2(self): + self._checkFlagsNZCV(0, 1, 0, 0) + + @itest_setregs("R3=0") + @itest("teq r3, #0x18000") + def test_teq_mod_imm_1(self): + self._checkFlagsNZCV(0, 0, 0, 0) + + @itest_setregs("R3=0x18000") + @itest("teq r3, #0x18000") + def test_teq_mod_imm_2(self): + self._checkFlagsNZCV(0, 1, 0, 0) + + @itest_setregs("R3=0") + @itest("teq r3, #24, 20") + def test_teq_mod_imm_3(self): + self._checkFlagsNZCV(0, 0, 0, 0) + + @itest_setregs("R3=0x18000") + @itest("teq r3, #24, 20") + def test_teq_mod_imm_4(self): + self._checkFlagsNZCV(0, 1, 0, 0) + # AND @itest_setregs("R2=5") @itest("and r2, r2, #1") From c496230898101da15aed270bc679515a002c6fc5 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 16:14:52 -0400 Subject: [PATCH 15/40] Add unit tests for ARMv7 AND instruction --- tests/native/test_armv7cpu.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/native/test_armv7cpu.py b/tests/native/test_armv7cpu.py index 35946b937..61c922ac4 100644 --- a/tests/native/test_armv7cpu.py +++ b/tests/native/test_armv7cpu.py @@ -1835,6 +1835,16 @@ def test_and_reg_carry(self): self.assertEqual(self.rf.read("R1"), 3 & 5) self.assertEqual(self.rf.read("APSR_C"), 1) + @itest_setregs("R2=5") + @itest("and r2, r2, #0x18000") + def test_and_mod_imm_1(self): + self.assertEqual(self.rf.read("R2"), 0) + + @itest_setregs("R2=5") + @itest("and r2, r2, #24, 20") + def test_and_mod_imm_2(self): + self.assertEqual(self.rf.read("R2"), 0) + # svc def test_svc(self): From 0568546e9d6f8dcb87fc601297d22a6db11320ee Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 16:15:00 -0400 Subject: [PATCH 16/40] Add unit tests for ARMv7 RSB instruction --- tests/native/test_armv7cpu.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/native/test_armv7cpu.py b/tests/native/test_armv7cpu.py index 61c922ac4..bc21bb9cb 100644 --- a/tests/native/test_armv7cpu.py +++ b/tests/native/test_armv7cpu.py @@ -1921,12 +1921,24 @@ def test_lsrw_thumb_reg(self): def test_asrw_thumb(self): self.assertEqual(self.cpu.R5, 16 >> 3) + # RSB + @itest_setregs("R2=29") @itest("RSB r2, r2, #31") def test_rsb_imm(self): # Diverging instruction from trace self.assertEqual(self.rf.read("R2"), 2) + @itest_setregs("R2=0x17000") + @itest("RSB r2, r2, #0x18000") + def test_rsb_mod_imm_1(self): + self.assertEqual(self.rf.read("R2"), 0x1000) + + @itest_setregs("R2=0x17000") + @itest("RSB r2, r2, #24, 20") + def test_rsb_mod_imm_2(self): + self.assertEqual(self.rf.read("R2"), 0x1000) + @itest_setregs("R6=2", "R8=0xfffffffe") @itest("RSBS r8, r6, #0") def test_rsbs_carry(self): From bdefd8da59550989f4bdde1eb2ed496be83ed3c6 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 16:15:08 -0400 Subject: [PATCH 17/40] Add unit tests for ARMv7 BIC instruction --- tests/native/test_armv7cpu.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/native/test_armv7cpu.py b/tests/native/test_armv7cpu.py index bc21bb9cb..1eceee0a6 100644 --- a/tests/native/test_armv7cpu.py +++ b/tests/native/test_armv7cpu.py @@ -1992,6 +1992,16 @@ def test_bic_reg_imm(self): def test_thumb_bic_reg_imm(self): self.assertEqual(self.rf.read("R1"), 0xEF) + @itest_setregs("R1=0x18002") + @itest("BIC R2, R1, #0x18000") + def test_bic_reg_mod_imm_1(self): + self.assertEqual(self.rf.read("R2"), 0x2) + + @itest_setregs("R1=0x18002") + @itest("BIC R2, R1, #24, 20") + def test_bic_reg_mod_imm_2(self): + self.assertEqual(self.rf.read("R2"), 0x2) + @itest_setregs("R1=0x1008") @itest("BLX R1") def test_blx_reg(self): From 4eae487d42b9290ce1c8de5cc26171dd9cf8be75 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 16:15:51 -0400 Subject: [PATCH 18/40] blacken test_armv7cpu.py --- tests/native/test_armv7cpu.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/native/test_armv7cpu.py b/tests/native/test_armv7cpu.py index 1eceee0a6..e601febf2 100644 --- a/tests/native/test_armv7cpu.py +++ b/tests/native/test_armv7cpu.py @@ -1371,7 +1371,7 @@ def test_sbc_mod_imm_1(self): @itest_setregs("R1=0x18010", "APSR_C=0") def test_sbc_mod_imm_2(self): self.cpu.execute() - self.assertEqual(self.rf.read("R3"), 0xf) + self.assertEqual(self.rf.read("R3"), 0xF) @itest_custom("sbc r3, r1, #24, 20") @itest_setregs("R1=0x18010", "APSR_C=1") @@ -1383,7 +1383,7 @@ def test_sbc_mod_imm_3(self): @itest_setregs("R1=0x18010", "APSR_C=0") def test_sbc_mod_imm_4(self): self.cpu.execute() - self.assertEqual(self.rf.read("R3"), 0xf) + self.assertEqual(self.rf.read("R3"), 0xF) # LDM/LDMIB/LDMDA/LDMDB @@ -1661,13 +1661,13 @@ def test_eor_reg_two_op_shifted(self): @itest_setregs("R3=0xA") def test_eor_mod_imm_1(self): self.cpu.execute() - self.assertEqual(self.rf.read("R2"), 0x1800a) + self.assertEqual(self.rf.read("R2"), 0x1800A) @itest_custom("eor r2, r3, #24, 20") @itest_setregs("R3=0xA") def test_eor_mod_imm_2(self): self.cpu.execute() - self.assertEqual(self.rf.read("R2"), 0x1800a) + self.assertEqual(self.rf.read("R2"), 0x1800A) # LDRH - see also LDR tests From b7756370c3a27e01e053ece20daeb851f7485776 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 20:17:00 -0400 Subject: [PATCH 19/40] Enhance the ARMv7 `instruction` decorator to normalize modified immediate values that are split --- manticore/native/cpu/arm.py | 121 +++++++++++++++++++++++++++++------- 1 file changed, 98 insertions(+), 23 deletions(-) diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index d38bc3017..e0d479b6a 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -1,4 +1,5 @@ from functools import wraps +from inspect import signature as inspect_signature import logging import struct @@ -10,6 +11,8 @@ from .register import Register from ...core.smtlib import Operators, BitVecConstant, issymbolic +from typing import NamedTuple + logger = logging.getLogger(__name__) # map different instructions to a single impl here @@ -20,35 +23,107 @@ def HighBit(n): return Bit(n, 31) -def instruction(body): - @wraps(body) - def instruction_implementation(cpu, *args, **kwargs): - ret = None +ARMV7_CPU_ADDRESS_BIT_SIZE = 32 - should_execute = cpu.should_execute_conditional() - if cpu._at_symbolic_conditional == cpu.instruction.address: - cpu._at_symbolic_conditional = None - should_execute = True +def instruction(body=None, *, can_take_denormalized_mod_imm: bool = False): + """ + This decorator is used to annotate Armv7Cpu methods as + instruction-implementing methods. + + This centralizes some common ARM-specific logic about CPU flags in one place. + + Additionally, this optionally adds /modified immediate normalization/ logic + to the wrapped method. It turns out that Capstone will sometimes disassemble + an ARM instruction that take an immediate constant value as its final operand + into /two/ immediate operand values, explicitly representing the immediate + constant as an 8-bit unsigned number and a 4-bit rotation value. The + modified immediate normalization logic that this decorator adds will convert + the explicitly-represented unsigned number and rotation into a single + immediate operand-like value that has the appropriate integer value. + + See https://stackoverflow.com/questions/3888158/making-decorators-with-optional-arguments + for some decorator-fu specific to making this work both when used both like + `@instruction` and like `@instruction(can_take_denormalized_mod_imm=True)`. + """ + def decorator(body): + if can_take_denormalized_mod_imm: + # Need to possibly normalize a modified immediate argument that's + # been explicitly split by Capstone into (number, rotation) + # components. + + body_sig = inspect_signature(body) + num_body_params = len(body_sig.parameters) - 1 + assert num_body_params > 0 + address_width = ARMV7_CPU_ADDRESS_BIT_SIZE + + def normalize_mod_imm_arg(args): + if len(args) == num_body_params + 1: + args = list(args) + rot = args.pop() + assert rot.type == "immediate" + num = args.pop() + assert num.type == "immediate" + imm = ROR(num.imm, rot.imm, address_width) + args.append(_ImmediatePseudoOperand(imm)) + return args + else: - if issymbolic(should_execute): - # Let's remember next time we get here we should not do this again - cpu._at_symbolic_conditional = cpu.instruction.address - i_size = cpu.instruction.size - cpu.PC = Operators.ITEBV( - cpu.address_bit_size, should_execute, cpu.PC - i_size, cpu.PC - ) - return + # No normalization of a modified immediate required; return what's given. + def normalize_mod_imm_arg(args): + return args - if should_execute: - ret = body(cpu, *args, **kwargs) + @wraps(body) + def instruction_implementation(cpu, *args, **kwargs): + should_execute = cpu.should_execute_conditional() - if cpu.should_commit_flags(): - cpu.commit_flags() + if cpu._at_symbolic_conditional == cpu.instruction.address: + cpu._at_symbolic_conditional = None + should_execute = True + else: + if issymbolic(should_execute): + # Let's remember next time we get here we should not do this again + cpu._at_symbolic_conditional = cpu.instruction.address + i_size = cpu.instruction.size + cpu.PC = Operators.ITEBV( + cpu.address_bit_size, should_execute, cpu.PC - i_size, cpu.PC + ) + return + + if should_execute: + ret = body(cpu, *normalize_mod_imm_arg(args), **kwargs) + else: + ret = None - return ret + if cpu.should_commit_flags(): + cpu.commit_flags() + + return ret - return abstract_instruction(instruction_implementation) + return abstract_instruction(instruction_implementation) + + if body is not None: + return decorator(body) + else: + return decorator + + +class _ImmediatePseudoOperand(NamedTuple): + """ + This is a hacky class that is used to represent an object that looks close + enough to an Armv7Operand to be used in the places where immediate operands + are used. + + See the `instruction` decorator for more detail. + """ + + imm: int + + def read(self, with_carry: bool = False): + if with_carry: + return self.imm, 0 + else: + return self.imm _TYPE_MAP = { @@ -482,7 +557,7 @@ class Armv7Cpu(Cpu): current instruction + 8 (section A2.3). """ - address_bit_size = 32 + address_bit_size = ARMV7_CPU_ADDRESS_BIT_SIZE max_instr_width = 4 machine = "armv7" arch = cs.CS_ARCH_ARM From b7e8eae1b4fd757437a9bde4a653f6f5f9c7f043 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 20:17:48 -0400 Subject: [PATCH 20/40] Simplify the `Armv7Cpu._bitwise_instruction` method --- manticore/native/cpu/arm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index e0d479b6a..5cad78ca3 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -1482,9 +1482,9 @@ def STMDA(cpu, base, *regs): def STMDB(cpu, base, *regs): cpu._STM(cs.arm.ARM_INS_STMDB, base, regs) - def _bitwise_instruction(cpu, operation, dest, op1, *op2): + def _bitwise_instruction(cpu, operation, dest, op1, op2=None): if op2: - op2_val, carry = op2[0].read(with_carry=True) + op2_val, carry = op2.read(with_carry=True) result = operation(op1.read(), op2_val) else: op1_val, carry = op1.read(with_carry=True) From 564aa5a670d0aa9f886ee1cf73cba84340cf6180 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 20:18:47 -0400 Subject: [PATCH 21/40] Fix ARMv7 MOV implementation for modified immediates --- manticore/native/cpu/arm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index 5cad78ca3..6e04696f3 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -843,7 +843,7 @@ def SEL(cpu, dest, op1, op2): ) dest.write(Operators.CONCAT(32, *reversed(result))) - @instruction + @instruction(can_take_denormalized_mod_imm=True) def MOV(cpu, dest, src): """ Implement the MOV{S} instruction. From 107eacb53f008a1da4211a7d10a453a803fa62ce Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 20:18:54 -0400 Subject: [PATCH 22/40] Fix ARMv7 ADC implementation for modified immediates --- manticore/native/cpu/arm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index 6e04696f3..2a80db87b 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -1089,7 +1089,7 @@ def _ADD(cpu, _op1, _op2, carry=0): return result, carry_out, overflow - @instruction + @instruction(can_take_denormalized_mod_imm=True) def ADC(cpu, dest, op1, op2=None): carry = cpu.regfile.read("APSR_C") if op2 is not None: From 2f4b096b3408f77628a32890729f17953c597853 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 20:19:05 -0400 Subject: [PATCH 23/40] Fix ARMv7 ADD implementation for modified immediates --- manticore/native/cpu/arm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index 2a80db87b..f89055660 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -1099,7 +1099,7 @@ def ADC(cpu, dest, op1, op2=None): dest.write(result) return result, carry, overflow - @instruction + @instruction(can_take_denormalized_mod_imm=True) def ADD(cpu, dest, src, add=None): if add is not None: result, carry, overflow = cpu._ADD(src.read(), add.read()) From b1faa4d85fa8b498adff4daba84797a286904481 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 20:19:14 -0400 Subject: [PATCH 24/40] Fix ARMv7 RSB implementation for modified immediates --- manticore/native/cpu/arm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index f89055660..5758f114d 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -1109,7 +1109,7 @@ def ADD(cpu, dest, src, add=None): dest.write(result) return result, carry, overflow - @instruction + @instruction(can_take_denormalized_mod_imm=True) def RSB(cpu, dest, src, add): inv_src = GetNBits(~src.read(), cpu.address_bit_size) result, carry, overflow = cpu._ADD(inv_src, add.read(), 1) From ef5c1a977221585982d626c6b0d88b6a7d302c48 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 20:19:26 -0400 Subject: [PATCH 25/40] Fix ARMv7 RSC implementation for modified immediates --- manticore/native/cpu/arm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index 5758f114d..03a367114 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -1116,7 +1116,7 @@ def RSB(cpu, dest, src, add): dest.write(result) return result, carry, overflow - @instruction + @instruction(can_take_denormalized_mod_imm=True) def RSC(cpu, dest, src, add): carry = cpu.regfile.read("APSR_C") inv_src = GetNBits(~src.read(), cpu.address_bit_size) From 9e2add06ff725a7948d8dcd73f2fc2142113bf1a Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 20:19:34 -0400 Subject: [PATCH 26/40] Fix ARMv7 SUB implementation for modified immediates --- manticore/native/cpu/arm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index 03a367114..6a3eef36c 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -1124,7 +1124,7 @@ def RSC(cpu, dest, src, add): dest.write(result) return result, carry, overflow - @instruction + @instruction(can_take_denormalized_mod_imm=True) def SUB(cpu, dest, src, add=None): if add is not None: result, carry, overflow = cpu._ADD(src.read(), ~add.read(), 1) From a0650af24bd916003c3123e67d3e376162f426d7 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 20:19:43 -0400 Subject: [PATCH 27/40] Fix ARMv7 SBC implementation for modified immediates --- manticore/native/cpu/arm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index 6a3eef36c..4c6173d1b 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -1135,7 +1135,7 @@ def SUB(cpu, dest, src, add=None): dest.write(result) return result, carry, overflow - @instruction + @instruction(can_take_denormalized_mod_imm=True) def SBC(cpu, dest, op1, op2=None): carry = cpu.regfile.read("APSR_C") if op2 is not None: From ba3a5654cad1684a5f7178c6b312619f5cac3b11 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 20:19:53 -0400 Subject: [PATCH 28/40] Fix ARMv7 CMP implementation for modified immediates --- manticore/native/cpu/arm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index 4c6173d1b..ffd46f3bb 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -1323,7 +1323,7 @@ def TBH(cpu, dest): cpu.PC += offset << 1 - @instruction + @instruction(can_take_denormalized_mod_imm=True) def CMP(cpu, reg, compare): notcmp = ~compare.read() & Mask(cpu.address_bit_size) cpu._ADD(reg.read(), notcmp, 1) From 8d423391d3dc878609497a0571c15a9246aab1c7 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 20:20:01 -0400 Subject: [PATCH 29/40] Fix ARMv7 ORR implementation for modified immediates --- manticore/native/cpu/arm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index ffd46f3bb..6401a4a4a 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -1493,7 +1493,7 @@ def _bitwise_instruction(cpu, operation, dest, op1, op2=None): dest.write(result) cpu.set_flags(C=carry, N=HighBit(result), Z=(result == 0)) - @instruction + @instruction(can_take_denormalized_mod_imm=True) def ORR(cpu, dest, op1, op2=None): if op2 is not None: cpu._bitwise_instruction(lambda x, y: x | y, dest, op1, op2) From 4758249137396d4ffc2a5035b470c1d5f5dab7a5 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 20:20:11 -0400 Subject: [PATCH 30/40] Fix ARMv7 ORN implementation for modified immediates --- manticore/native/cpu/arm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index 6401a4a4a..2c1606325 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -1500,7 +1500,7 @@ def ORR(cpu, dest, op1, op2=None): else: cpu._bitwise_instruction(lambda x, y: x | y, dest, dest, op1) - @instruction + @instruction(can_take_denormalized_mod_imm=True) def ORN(cpu, dest, op1, op2=None): if op2 is not None: cpu._bitwise_instruction(lambda x, y: x | ~y, dest, op1, op2) From 42193fd65eeba4fd84dba3d9a4c08c63828bb1cd Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 20:20:22 -0400 Subject: [PATCH 31/40] Fix ARMv7 EOR implementation for modified immediates --- manticore/native/cpu/arm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index 2c1606325..ca9fe3db5 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -1507,7 +1507,7 @@ def ORN(cpu, dest, op1, op2=None): else: cpu._bitwise_instruction(lambda x, y: x | ~y, dest, dest, op1) - @instruction + @instruction(can_take_denormalized_mod_imm=True) def EOR(cpu, dest, op1, op2=None): if op2 is not None: cpu._bitwise_instruction(lambda x, y: x ^ y, dest, op1, op2) From 1fe99aa3868af626498acb9bde13b316fb146e21 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 20:20:30 -0400 Subject: [PATCH 32/40] Fix ARMv7 AND implementation for modified immediates --- manticore/native/cpu/arm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index ca9fe3db5..85f5eebc0 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -1514,7 +1514,7 @@ def EOR(cpu, dest, op1, op2=None): else: cpu._bitwise_instruction(lambda x, y: x ^ y, dest, dest, op1) - @instruction + @instruction(can_take_denormalized_mod_imm=True) def AND(cpu, dest, op1, op2=None): if op2 is not None: cpu._bitwise_instruction(lambda x, y: x & y, dest, op1, op2) From 3a2d397250e1fb11e16bc3f166d34eeadc15a931 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 20:20:37 -0400 Subject: [PATCH 33/40] Fix ARMv7 TEQ implementation for modified immediates --- manticore/native/cpu/arm.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index 85f5eebc0..15b4241a1 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -1521,9 +1521,9 @@ def AND(cpu, dest, op1, op2=None): else: cpu._bitwise_instruction(lambda x, y: x & y, dest, dest, op1) - @instruction - def TEQ(cpu, *operands): - cpu._bitwise_instruction(lambda x, y: x ^ y, None, *operands) + @instruction(can_take_denormalized_mod_imm=True) + def TEQ(cpu, op1, op2=None): + cpu._bitwise_instruction(lambda x, y: x ^ y, None, op1, op2) cpu.commit_flags() @instruction From a0d6629401132ed66473ff49313a4630648e79dd Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 20:20:50 -0400 Subject: [PATCH 34/40] Fix ARMv7 TST implementation for modified immediates --- manticore/native/cpu/arm.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index 15b4241a1..db5ba57d3 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -1526,10 +1526,10 @@ def TEQ(cpu, op1, op2=None): cpu._bitwise_instruction(lambda x, y: x ^ y, None, op1, op2) cpu.commit_flags() - @instruction - def TST(cpu, Rn, Rm): - shifted, carry = Rm.read(with_carry=True) - result = Rn.read() & shifted + @instruction(can_take_denormalized_mod_imm=True) + def TST(cpu, op1, op2): + shifted, carry = op2.read(with_carry=True) + result = op1.read() & shifted cpu.set_flags(N=HighBit(result), Z=(result == 0), C=carry) @instruction From 6232fa650ac6a2a548a74c2bd11c0a4e900a1269 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 20:20:56 -0400 Subject: [PATCH 35/40] Fix ARMv7 CMN implementation for modified immediates --- manticore/native/cpu/arm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index db5ba57d3..61e3e6ae1 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -1538,7 +1538,7 @@ def SVC(cpu, op): logger.warning(f"Bad SVC number: {op.read():08}") raise Interruption(0) - @instruction + @instruction(can_take_denormalized_mod_imm=True) def CMN(cpu, src, add): result, carry, overflow = cpu._ADD(src.read(), add.read()) return result, carry, overflow From d3908ec18c25910f5565256cc555e7c4605769de Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 20:21:04 -0400 Subject: [PATCH 36/40] Fix ARMv7 MVN implementation for modified immediates --- manticore/native/cpu/arm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index 61e3e6ae1..b12926c2c 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -1618,7 +1618,7 @@ def MUL(cpu, dest, src1, src2): dest.write(result & Mask(width)) cpu.set_flags(N=HighBit(result), Z=(result == 0)) - @instruction + @instruction(can_take_denormalized_mod_imm=True) def MVN(cpu, dest, op): cpu._bitwise_instruction(lambda x: ~x, dest, op) From dc0563384451a9ab79d0a816a839869ca99da30a Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 20:21:10 -0400 Subject: [PATCH 37/40] Fix ARMv7 BIC implementation for modified immediates --- manticore/native/cpu/arm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index b12926c2c..61474a2a8 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -1633,7 +1633,7 @@ def MLA(cpu, dest, op1, op2, addend): dest.write(result & Mask(cpu.address_bit_size)) cpu.set_flags(N=HighBit(result), Z=(result == 0)) - @instruction + @instruction(can_take_denormalized_mod_imm=True) def BIC(cpu, dest, op1, op2=None): if op2 is not None: result = (op1.read() & ~op2.read()) & Mask(cpu.address_bit_size) From e4116ee5de345197061beb883d3fe190d2cc9078 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Fri, 20 Mar 2020 20:21:54 -0400 Subject: [PATCH 38/40] blacken manticore/native/cpu/arm.py --- manticore/native/cpu/arm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index 61474a2a8..3337dd6a2 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -46,6 +46,7 @@ def instruction(body=None, *, can_take_denormalized_mod_imm: bool = False): for some decorator-fu specific to making this work both when used both like `@instruction` and like `@instruction(can_take_denormalized_mod_imm=True)`. """ + def decorator(body): if can_take_denormalized_mod_imm: # Need to possibly normalize a modified immediate argument that's From ac902c483fde474550609cdae9fdf64ce8458554 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Mon, 23 Mar 2020 15:18:28 -0400 Subject: [PATCH 39/40] Expand comments about modified immediates; simplify decorator slightly --- manticore/native/cpu/arm.py | 53 ++++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index 3337dd6a2..bdf575f8f 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -34,17 +34,35 @@ def instruction(body=None, *, can_take_denormalized_mod_imm: bool = False): This centralizes some common ARM-specific logic about CPU flags in one place. Additionally, this optionally adds /modified immediate normalization/ logic - to the wrapped method. It turns out that Capstone will sometimes disassemble - an ARM instruction that take an immediate constant value as its final operand - into /two/ immediate operand values, explicitly representing the immediate - constant as an 8-bit unsigned number and a 4-bit rotation value. The - modified immediate normalization logic that this decorator adds will convert - the explicitly-represented unsigned number and rotation into a single - immediate operand-like value that has the appropriate integer value. - - See https://stackoverflow.com/questions/3888158/making-decorators-with-optional-arguments - for some decorator-fu specific to making this work both when used both like - `@instruction` and like `@instruction(can_take_denormalized_mod_imm=True)`. + to the wrapped method. + + This decorator works both as `@instruction` and as + `@instruction(can_take_denormalized_mod_imm=True)`. + + + What is this normalization logic? + + First, it helps to understand how ARM encodes immediate constants. + In encoded ARM instructions, immediate constant values are encoded as an + 8-bit unsigned number and a 4-bit rotation value; you can read about the + details in the ARM Architecture Reference Manual, ARMv7-A and ARMv7-R + edition, section A5.2.3, "Data-processing (immediate)". + + Second, it turns out that the Capstone disassembler we use will sometimes + disassemble an ARM immediate constant value into /two/ immediate operand + values, explicitly representing the 8-bit unsigned number and two times the + 4-bit shift. In particular, it seems that Capstone uses this explicit + representation when the modified immediate value is encoded in a + non-canonical form. A blog post has some more explanation around this: + + https://alisdair.mcdiarmid.org/arm-immediate-value-encoding/ + + So, finally, the /modified immediate normalization/ logic that this + decorator adds converts an explicitly-represented unsigned number and + rotation into a single immediate operand-like value (`_ImmediatePseudoOperand`) + that has the appropriate integer value, so that the actual implementation + of an ARM instruction here can expect only normalized immediates, and not + have to concern itself with this quirk of Capstone. """ def decorator(body): @@ -54,18 +72,23 @@ def decorator(body): # components. body_sig = inspect_signature(body) + # subtract 1 to account for the first parameter (`cpu`), present in + # all instruction methods. num_body_params = len(body_sig.parameters) - 1 assert num_body_params > 0 - address_width = ARMV7_CPU_ADDRESS_BIT_SIZE def normalize_mod_imm_arg(args): if len(args) == num_body_params + 1: + # We got 1 more argument than the wrapped function expects; + # this is the case of a modified immediate represented + # explicitly as 2 immediate operands. + # Normalize the two into one! args = list(args) rot = args.pop() assert rot.type == "immediate" num = args.pop() assert num.type == "immediate" - imm = ROR(num.imm, rot.imm, address_width) + imm = ROR(num.imm, rot.imm, ARMV7_CPU_ADDRESS_BIT_SIZE) args.append(_ImmediatePseudoOperand(imm)) return args @@ -103,6 +126,10 @@ def instruction_implementation(cpu, *args, **kwargs): return abstract_instruction(instruction_implementation) + # Here's where we support using this decorator both like + # `@instruction` and like `@instruction(can_take_denormalized_mod_imm=True)`. + # See https://stackoverflow.com/questions/3888158/making-decorators-with-optional-arguments + # for some decorator-fu. if body is not None: return decorator(body) else: From 9b5b5083bee87eb16bea81248335abeb29ce44f2 Mon Sep 17 00:00:00 2001 From: Brad Larsen Date: Mon, 23 Mar 2020 15:35:07 -0400 Subject: [PATCH 40/40] Rename parameter to avoid shadowing --- manticore/native/cpu/arm.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/manticore/native/cpu/arm.py b/manticore/native/cpu/arm.py index bdf575f8f..0def2f980 100644 --- a/manticore/native/cpu/arm.py +++ b/manticore/native/cpu/arm.py @@ -26,7 +26,7 @@ def HighBit(n): ARMV7_CPU_ADDRESS_BIT_SIZE = 32 -def instruction(body=None, *, can_take_denormalized_mod_imm: bool = False): +def instruction(instruction_body=None, *, can_take_denormalized_mod_imm: bool = False): """ This decorator is used to annotate Armv7Cpu methods as instruction-implementing methods. @@ -130,8 +130,8 @@ def instruction_implementation(cpu, *args, **kwargs): # `@instruction` and like `@instruction(can_take_denormalized_mod_imm=True)`. # See https://stackoverflow.com/questions/3888158/making-decorators-with-optional-arguments # for some decorator-fu. - if body is not None: - return decorator(body) + if instruction_body is not None: + return decorator(instruction_body) else: return decorator