lsl.w in thumb mode fixed, plus tests (#350)

- blx <Rm> swaps mode appropriately
- asr.w and lsr.w in thumb mode handle immediates for op2 correctly
- unit tests for thumb mode behavior for shifting instructions
- unit tests check for correct mode on BX and BLX instructions
This commit is contained in:
Garret Reece
2017-06-27 13:52:20 -05:00
committed by GitHub
parent e931992abb
commit 7d3d9a9ed1
2 changed files with 88 additions and 26 deletions
+41 -21
View File
@@ -335,9 +335,18 @@ class Armv7Cpu(Cpu):
self._at_symbolic_conditional = state['at_symbolic_conditional']
def _set_mode(self, new_mode):
assert new_mode in (CS_MODE_ARM, CS_MODE_THUMB)
self.mode = new_mode
self._md.mode = new_mode
assert new_mode in (CS_MODE_ARM, CS_MODE_THUMB)
self.mode = new_mode
self._md.mode = new_mode
def _swap_mode(self):
#swap from arm to thumb or back
assert self.mode in (CS_MODE_ARM, CS_MODE_THUMB)
if self.mode == CS_MODE_ARM:
self._set_mode(CS_MODE_THUMB)
else:
self._set_mode(CS_MODE_ARM)
# Flags that are the result of arithmetic instructions. Unconditionally
# set, but conditionally committed.
@@ -755,9 +764,12 @@ class Armv7Cpu(Cpu):
def B(cpu, dest):
cpu.PC = dest.read()
# XXX How should we deal with switching Thumb modes?
@instruction
def BX(cpu, dest):
if dest.read() & 0x1:
cpu._set_mode(CS_MODE_THUMB)
else:
cpu._set_mode(CS_MODE_ARM)
cpu.PC = dest.read() & ~1
@instruction
@@ -771,25 +783,21 @@ class Armv7Cpu(Cpu):
cpu.regfile.write('LR', next_instr_addr)
cpu.regfile.write('PC', label.read())
@instruction
def BLX(cpu, dest):
## XXX: Technically, this should use the values that are commented (sub
## 2 and LSB of LR set, but we currently do not distinguish between
## THUMB and regular modes, so we use the addresses as is. TODO: Handle
## thumb correctly and fix this
address = cpu.PC
target = dest.read()
next_instr_addr = cpu.regfile.read('PC') #- 2
cpu.regfile.write('LR', next_instr_addr) # | 1)
next_instr_addr = cpu.regfile.read('PC')
cpu.regfile.write('LR', next_instr_addr)
cpu.regfile.write('PC', target & ~1)
## The `blx <label>` form of this instruction forces a state swap
## The `blx <label>` form of this instruction forces a mode swap
## Otherwise check the lsb of the destination and set the mode
if dest.type=='immediate':
logger.debug("swapping ds mode due to BLX at inst 0x{:x}".format(address))
#swap from arm to thumb or back
assert cpu.mode in (CS_MODE_ARM, CS_MODE_THUMB)
if cpu.mode == CS_MODE_ARM:
logger.debug("swapping mode due to BLX at inst 0x{:x}".format(address))
cpu._swap_mode()
elif dest.type=='register':
if dest.read() & 0x1:
cpu._set_mode(CS_MODE_THUMB)
else:
cpu._set_mode(CS_MODE_ARM)
@@ -927,21 +935,33 @@ class Armv7Cpu(Cpu):
return result, carry, overflow
def _SR(cpu, insn_id, dest, op, *rest):
'''_SR reg has @rest, but _SR imm does not, its baked into @op
'''In ARM mode, _SR reg has @rest, but _SR imm does not, its baked into @op.
'''
assert insn_id in (ARM_INS_ASR, ARM_INS_LSL, ARM_INS_LSR)
if insn_id == ARM_INS_ASR:
srtype = ARM_SFT_ASR_REG
if rest and rest[0].type == 'immediate':
srtype = ARM_SFT_ASR
else:
srtype = ARM_SFT_ASR_REG
elif insn_id == ARM_INS_LSL:
srtype = ARM_SFT_LSL_REG
if rest and rest[0].type == 'immediate':
srtype = ARM_SFT_LSL
else:
srtype = ARM_SFT_LSL_REG
elif insn_id == ARM_INS_LSR:
srtype = ARM_SFT_LSR_REG
if rest and rest[0].type == 'immediate':
srtype = ARM_SFT_LSR
else:
srtype = ARM_SFT_LSR_REG
carry = cpu.regfile.read('APSR_C')
if rest:
if rest and rest[0].type=='register':
#FIXME we should make Operand.op private (and not accessible)
result, carry = cpu._Shift(op.read(), srtype, rest[0].op.reg, carry)
elif rest and rest[0].type=='immediate':
amount = rest[0].read()
result, carry = cpu._Shift(op.read(), srtype, amount, carry)
else:
result, carry = op.read(withCarry=True)
dest.write(result)
+47 -5
View File
@@ -6,17 +6,24 @@ from manticore.core.cpu.arm import Armv7Cpu as Cpu, Mask, Interruption
from manticore.core.memory import Memory32
from capstone.arm import *
from keystone import Ks, KS_ARCH_ARM, KS_MODE_ARM
from capstone import CS_MODE_THUMB, CS_MODE_ARM
from keystone import Ks, KS_ARCH_ARM, KS_MODE_ARM, KS_MODE_THUMB
ks = Ks(KS_ARCH_ARM, KS_MODE_ARM)
ks_thumb = Ks(KS_ARCH_ARM, KS_MODE_THUMB)
import logging
logger = logging.getLogger("ARM_TESTS")
def assemble(asm):
ords = ks.asm(asm)[0]
def assemble(asm, mode=CS_MODE_ARM):
if CS_MODE_ARM == mode:
ords = ks.asm(asm)[0]
elif CS_MODE_THUMB == mode:
ords = ks_thumb.asm(asm)[0]
else:
raise Exception('bad processor mode for assembly: {}'.format(mode))
if not ords:
raise Exception('bad assembly: {}'.format(asm))
return ''.join(map(chr, ords))
@@ -133,6 +140,17 @@ def itest_custom(asm):
return instr_dec
def itest_custom_thumb(asm):
def instr_dec(custom_func):
@wraps(custom_func)
def wrapper(self):
self._setupCpu(asm, mode=CS_MODE_THUMB)
custom_func(self)
return wrapper
return instr_dec
class Armv7CpuInstructions(unittest.TestCase):
def setUp(self):
@@ -140,14 +158,15 @@ class Armv7CpuInstructions(unittest.TestCase):
self.mem = self.cpu.memory
self.rf = self.cpu.regfile
def _setupCpu(self, asm):
def _setupCpu(self, asm, mode=CS_MODE_ARM):
self.code = self.mem.mmap(0x1000, 0x1000, 'rwx')
self.data = self.mem.mmap(0xd000, 0x1000, 'rw')
self.stack = self.mem.mmap(0xf000, 0x1000, 'rw')
start = self.code + 4
self.mem.write(start, assemble(asm))
self.mem.write(start, assemble(asm, mode))
self.rf.write('PC', start)
self.rf.write('SP', self.stack + 0x1000)
self.cpu._set_mode(mode)
def _checkFlagsNZCV(self, n, z, c, v):
self.assertEqual(self.rf.read('APSR_N'), n)
@@ -1045,6 +1064,7 @@ class Armv7CpuInstructions(unittest.TestCase):
def test_bx_basic(self):
self.cpu.execute()
self.assertEqual(self.rf.read('PC'), 0x1008)
self.assertEqual(self.cpu.mode, CS_MODE_ARM)
@itest_custom("bx r1")
@itest_setregs("R1=0x1009")
@@ -1052,6 +1072,7 @@ class Armv7CpuInstructions(unittest.TestCase):
pre_pc = self.rf.read('PC')
self.cpu.execute()
self.assertEqual(self.rf.read('PC'), pre_pc + 4)
self.assertEqual(self.cpu.mode, CS_MODE_THUMB)
# ORR
@@ -1276,6 +1297,13 @@ class Armv7CpuInstructions(unittest.TestCase):
self.assertEqual(self.cpu.R2, 0x1 << 31)
self._checkFlagsNZCV(1, 0, 1, 0)
@itest_setregs("R5=1", "R6=2")
@itest_custom_thumb("lsl.w r5, r6, #3")
def test_lslw_thumb(self):
'''thumb mode specific behavior'''
self.cpu.execute()
self.assertEqual(self.cpu.R5, 0x2 << 3)
# lsr
@itest_setregs("R0=0x1000", "R2=3")
@itest("lsr r0, r0, r2")
@@ -1287,6 +1315,18 @@ class Armv7CpuInstructions(unittest.TestCase):
def test_lsr_reg_imm(self):
self.assertEqual(self.rf.read('R0'), 0x1000 >> 3)
@itest_setregs("R5=0", "R6=16")
@itest_custom_thumb("lsr.w R5, R6, #3")
def test_lsrw_thumb(self):
self.cpu.execute()
self.assertEqual(self.cpu.R5, 16>>3)
@itest_setregs("R5=0", "R6=16")
@itest_custom_thumb("asr.w R5, R6, #3")
def test_asrw_thumb(self):
self.cpu.execute()
self.assertEqual(self.cpu.R5, 16>>3)
@itest_setregs("R2=29")
@itest("RSB r2, r2, #31")
def test_rsb_imm(self):
@@ -1346,12 +1386,14 @@ class Armv7CpuInstructions(unittest.TestCase):
def test_blx_reg(self):
self.assertEqual(self.rf.read('PC'), 0x1008)
self.assertEqual(self.rf.read('LR'), 0x1008)
self.assertEqual(self.cpu.mode, CS_MODE_ARM)
@itest_setregs("R1=0x1009")
@itest("BLX R1")
def test_blx_reg_thumb(self):
self.assertEqual(self.rf.read('PC'), 0x1008)
self.assertEqual(self.rf.read('LR'), 0x1008)
self.assertEqual(self.cpu.mode, CS_MODE_THUMB)
@itest_setregs("R1=0xffffffff", "R2=2")
@itest("UMULLS R1, R2, R1, R2")