Add mrc and movt

* Add MOVT instruction

* Add new ARM operand types for MRC

* mrc implementation seems to work

* UXTB inst implemented, provisional implementations of LDREX and STREX

* Added unit tests for MRC, MOVT, and UXTB

* Added mode swapping from blx instruction on arm cpu

* Make tests pass
This commit is contained in:
Garret Reece 2017-06-13 13:21:19 -05:00 committed by GitHub
parent 5ea4a87ddb
commit 7f550becc6
4 changed files with 143 additions and 4 deletions

View File

@ -55,7 +55,9 @@ class Armv7Operand(Operand):
def type(self): def type(self):
type_map = { ARM_OP_REG: 'register', type_map = { ARM_OP_REG: 'register',
ARM_OP_MEM: 'memory', ARM_OP_MEM: 'memory',
ARM_OP_IMM: 'immediate'} ARM_OP_IMM: 'immediate',
ARM_OP_PIMM:'coprocessor',
ARM_OP_CIMM:'immediate'}
return type_map[self.op.type] return type_map[self.op.type]
@ -90,7 +92,9 @@ class Armv7Operand(Operand):
if withCarry: if withCarry:
return imm, self._getExpandImmCarry(carry) return imm, self._getExpandImmCarry(carry)
return imm return imm
elif self.type == 'coprocessor':
imm = self.op.imm
return imm
elif self.type == 'memory': elif self.type == 'memory':
val = self.cpu.read_int(self.address(), nbits) val = self.cpu.read_int(self.address(), nbits)
if withCarry: if withCarry:
@ -190,6 +194,9 @@ class Armv7RegisterFile(RegisterFile):
self._regs['APSR_C'] = Register(1) self._regs['APSR_C'] = Register(1)
self._regs['APSR_V'] = Register(1) self._regs['APSR_V'] = Register(1)
#MMU Coprocessor -- to support MCR/MRC for TLS
self._regs['P15_C13'] = Register(32)
def _read_APSR(self): def _read_APSR(self):
def make_apsr_flag(flag_expr, offset): def make_apsr_flag(flag_expr, offset):
'Helper for constructing an expression for the APSR register' 'Helper for constructing an expression for the APSR register'
@ -246,7 +253,8 @@ class Armv7RegisterFile(RegisterFile):
return super(Armv7RegisterFile, self).all_registers + \ return super(Armv7RegisterFile, self).all_registers + \
('R0','R1','R2','R3','R4','R5','R6','R7','R8','R9','R10','R11','R12','R13','R14','R15','D0','D1','D2', ('R0','R1','R2','R3','R4','R5','R6','R7','R8','R9','R10','R11','R12','R13','R14','R15','D0','D1','D2',
'D3','D4','D5','D6','D7','D8','D9','D10','D11','D12','D13','D14','D15','D16','D17','D18','D19','D20', 'D3','D4','D5','D6','D7','D8','D9','D10','D11','D12','D13','D14','D15','D16','D17','D18','D19','D20',
'D21','D22','D23','D24','D25','D26','D27','D28','D29','D30','D31','APSR','APSR_N','APSR_Z','APSR_C','APSR_V') 'D21','D22','D23','D24','D25','D26','D27','D28','D29','D30','D31','APSR','APSR_N','APSR_Z','APSR_C','APSR_V',
'P15_C13')
@property @property
def canonical_registers(self): def canonical_registers(self):
@ -322,6 +330,11 @@ class Armv7Cpu(Cpu):
self._last_flags = state['_last_flags'] self._last_flags = state['_last_flags']
self._force_next = state['_force_next'] self._force_next = state['_force_next']
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
# Flags that are the result of arithmetic instructions. Unconditionally # Flags that are the result of arithmetic instructions. Unconditionally
# set, but conditionally committed. # set, but conditionally committed.
# #
@ -418,6 +431,9 @@ class Armv7Cpu(Cpu):
def write(self, addr, data): def write(self, addr, data):
return self.write_bytes(addr, data) return self.write_bytes(addr, data)
def set_arm_tls(self, data):
self.regfile.write('P15_C13', data)
@staticmethod @staticmethod
def canonicalize_instruction_name(instr): def canonicalize_instruction_name(instr):
name = instr.insn_name().upper() name = instr.insn_name().upper()
@ -492,6 +508,96 @@ class Armv7Cpu(Cpu):
dest.write(result) dest.write(result)
cpu.setFlags(C=carry_out, N=HighBit(result), Z=(result == 0)) cpu.setFlags(C=carry_out, N=HighBit(result), Z=(result == 0))
@instruction
def MOVT(cpu, dest, src):
'''
MOVT writes imm16 to Rd[31:16]. The write does not affect Rd[15:0].
:param Armv7Operand dest: The destination operand; register
:param Armv7Operand src: The source operand; 16-bit immediate
'''
assert src.type == 'immediate'
imm = src.read()
low_halfword = dest.read() & Mask(16)
dest.write((imm << 16) | low_halfword)
@instruction
def MRC(cpu, coprocessor, opcode1, dest, coprocessor_reg_n, coprocessor_reg_m, opcode2):
'''
MRC moves to ARM register from coprocessor.
:param Armv7Operand coprocessor: The name of the coprocessor; immediate
:param Armv7Operand opcode1: coprocessor specific opcode; 3-bit immediate
:param Armv7Operand dest: the destination operand: register
:param Armv7Operand coprocessor_reg_n: the coprocessor register; immediate
:param Armv7Operand coprocessor_reg_m: the coprocessor register; immediate
:param Armv7Operand opcode2: coprocessor specific opcode; 3-bit immediate
'''
assert coprocessor.type == 'coprocessor'
assert opcode1.type == 'immediate'
assert opcode2.type == 'immediate'
assert dest.type == 'register'
imm_coprocessor = coprocessor.read()
imm_opcode1 = opcode1.read()
imm_opcode2 = opcode2.read()
coprocessor_n_name = coprocessor_reg_n.read()
coprocessor_m_name = coprocessor_reg_m.read()
if 15 == imm_coprocessor: #MMU
if 0 == imm_opcode1:
if 13 == coprocessor_n_name:
if 3 == imm_opcode2:
dest.write(cpu.regfile.read('P15_C13'))
return
raise NotImplementedError("MRC: unimplemented combination of coprocessor, opcode, and coprocessor register")
@instruction
def LDREX(cpu, dest, src, offset=None):
'''
LDREX loads data from memory.
* If the physical address has the shared TLB attribute, LDREX
tags the physical address as exclusive access for the current
processor, and clears any exclusive access tag for this
processor for any other physical address.
* Otherwise, it tags the fact that the executing processor has
an outstanding tagged physical address.
:param Armv7Operand dest: the destination register; register
:param Armv7Operand src: the source operand: register
'''
#TODO: add lock mechanism to underlying memory --GR, 2017-06-06
cpu._LDR(dest, src, 32, False, offset)
@instruction
def STREX(cpu, status, *args):
'''
STREX performs a conditional store to memory.
:param Armv7Operand status: the destination register for the returned status; register
'''
#TODO: implement conditional return with appropriate status --GR, 2017-06-06
status.write(0)
return cpu._STR(cpu.address_bit_size, *args)
@instruction
def UXTB(cpu, dest, src):
'''
UXTB extracts an 8-bit value from a register, zero-extends
it to the size of the register, and writes the result to the destination register.
:param ARMv7Operand dest: the destination register; register
:param ARMv7Operand dest: the source register; register
'''
val = GetNBits(src.read(), 8)
word = Operators.ZEXTEND(val, cpu.address_bit_size)
dest.write(word)
@instruction
def PLD(cpu, addr, offset=None):
'''
PLD instructs the cpu that the address at addr might be loaded soon.
'''
pass
def _compute_writeback(cpu, operand, offset): def _compute_writeback(cpu, operand, offset):
if offset: if offset:
off = offset.read() off = offset.read()
@ -645,11 +751,22 @@ class Armv7Cpu(Cpu):
## 2 and LSB of LR set, but we currently do not distinguish between ## 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 and regular modes, so we use the addresses as is. TODO: Handle
## thumb correctly and fix this ## thumb correctly and fix this
address = cpu.PC
target = dest.read() target = dest.read()
next_instr_addr = cpu.regfile.read('PC') #- 2 next_instr_addr = cpu.regfile.read('PC') #- 2
cpu.regfile.write('LR', next_instr_addr) # | 1) cpu.regfile.write('LR', next_instr_addr) # | 1)
cpu.regfile.write('PC', target & ~1) cpu.regfile.write('PC', target & ~1)
## The `blx <label>` form of this instruction forces a state swap
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:
cpu._set_mode(CS_MODE_THUMB)
else:
cpu._set_mode(CS_MODE_ARM)
@instruction @instruction
def CMP(cpu, reg, cmp): def CMP(cpu, reg, cmp):
notcmp = ~cmp.read() & Mask(cpu.address_bit_size) notcmp = ~cmp.read() & Mask(cpu.address_bit_size)

View File

@ -1244,6 +1244,7 @@ class Linux(Platform):
def sys_ARM_NR_set_tls(self, val): def sys_ARM_NR_set_tls(self, val):
if hasattr(self, '_arm_tls_memory'): if hasattr(self, '_arm_tls_memory'):
self.current.write_int(self._arm_tls_memory, val) self.current.write_int(self._arm_tls_memory, val)
self.current.set_arm_tls(val)
return 0 return 0
#Signals.. #Signals..

View File

@ -41,7 +41,10 @@ class UnicornEmulator(object):
def _unicorn(self): def _unicorn(self):
if self._cpu.arch == CS_ARCH_ARM: if self._cpu.arch == CS_ARCH_ARM:
if self._cpu.mode == CS_MODE_ARM:
return Uc(UC_ARCH_ARM, UC_MODE_ARM) return Uc(UC_ARCH_ARM, UC_MODE_ARM)
elif self._cpu.mode == CS_MODE_THUMB:
return Uc(UC_ARCH_ARM, UC_MODE_THUMB)
elif self._cpu.arch == CS_ARCH_X86: elif self._cpu.arch == CS_ARCH_X86:
if self._cpu.mode == CS_MODE_32: if self._cpu.mode == CS_MODE_32:
return Uc(UC_ARCH_X86, UC_MODE_32) return Uc(UC_ARCH_X86, UC_MODE_32)

View File

@ -1390,3 +1390,21 @@ class Armv7CpuInstructions(unittest.TestCase):
self.assertEqual(self.cpu.D9, 21) self.assertEqual(self.cpu.D9, 21)
self.assertEqual(self.cpu.D10, 20) self.assertEqual(self.cpu.D10, 20)
self.assertEqual(self.cpu.R1, pre) self.assertEqual(self.cpu.R1, pre)
@itest_setregs("R3=3")
@itest("movt R3, #9")
def test_movt(self):
self.assertEqual(self.cpu.R3, 0x90003)
@itest_custom("mrc p15, #0, r2, c13, c0, #3")
def test_mrc(self):
self.cpu.set_arm_tls(0x55555)
self.cpu.write_register('R2', 0)
self.cpu.execute()
self.assertEqual(self.cpu.R2, 0x55555)
@itest_setregs("R1=0x45", "R2=0x55555555")
@itest("uxtb r1, r2")
def test_uxtb(self):
self.assertEqual(self.cpu.R2, 0x55555555)
self.assertEqual(self.cpu.R1, 0x55)