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:
+41
-21
@@ -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
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user