Files
manticore/core/cpu/arm.py
T
2017-02-13 12:04:15 -05:00

962 lines
31 KiB
Python

import struct
import sys
from abstractcpu import Cpu, RegisterFile, Operand
from abstractcpu import SymbolicPCException, InvalidPCException, Interruption
from abstractcpu import instruction as abstract_instruction
from register import Register
from core.smtlib import Operators, Expression
from functools import wraps
from bitwise import *
from capstone import *
from capstone.arm import *
from capstone.x86 import *
# no emulator by default
try:
from unicorn import *
from unicorn.x86_const import *
from unicorn.arm_const import *
except:
pass
MU = None
from core.smtlib import *
# Custom Constants (avoid conflicts with capstone's arm constants)
ARM_REG_APSR_N = 1000
ARM_REG_APSR_Z = 1001
ARM_REG_APSR_C = 1002
ARM_REG_APSR_V = 1003
import logging
logger = logging.getLogger("CPU")
# map different instructions to a single impl here
OP_NAME_MAP = {
'MOVW': 'MOV'
}
def HighBit(n):
return Bit(n, 31)
def instruction(body):
@wraps(body)
def instruction_implementation(cpu, *args, **kwargs):
ret = None
should_execute = cpu.shouldExecuteConditional()
if isinstance(should_execute, Expression):
i_size = cpu.address_bit_size / 8
cpu.PC = Operators.ITEBV(cpu.address_bit_size, should_execute, cpu.PC-i_size,
cpu.PC)
return
if should_execute:
ret = body(cpu, *args, **kwargs)
if cpu.shouldCommitFlags():
cpu.commitFlags()
return ret
return abstract_instruction(instruction_implementation)
class Armv7Operand(Operand):
def __init__(self, cpu, op, **kwargs):
super(Armv7Operand, self).__init__(cpu, op, **kwargs)
def size(self):
assert self.op.type == ARM_OP_REG
if self.op.reg >= ARM_REG_D0 and self.op.reg <= ARM_REG_D31:
return 8
else:
return 4
def read(self, nbits=None, withCarry=False):
carry = self.cpu.regfile.read(ARM_REG_APSR_C)
if self.op.type == ARM_OP_REG:
reg = self.cpu.regfile.read(self.op.reg)
# XXX This can be an offset of 8, depending on ARM mode
if self.op.reg == ARM_REG_R15:
reg += 4
if self.is_shifted():
shift = self.op.shift
reg, carry = self.cpu._Shift(reg, shift.type, shift.value, carry)
if self.op.subtracted:
reg = -reg
if withCarry:
return reg, carry
return reg
elif self.op.type == ARM_OP_IMM:
imm = self.op.imm
if self.op.subtracted:
imm = -imm
if withCarry:
return imm, self._getExpandImmCarry(carry)
return imm
elif self.op.type == ARM_OP_MEM:
val = self.cpu.read_int(self.address(), nbits)
if withCarry:
return (val, carry)
return val
else:
raise NotImplementedError("readOperand unknown type", self.op.type)
def write(self, value, nbits=None):
if self.op.type == ARM_OP_REG:
self.cpu.regfile.write(self.op.reg, value)
elif self.op.type == ARM_OP_MEM:
raise NotImplementedError('need to impl arm store mem')
else:
raise NotImplementedError("writeOperand unknown type", self.op.type)
def writeback(self, value):
if self.op.type == ARM_OP_REG:
self.write(value)
elif self.op.type == ARM_OP_MEM:
self.cpu.regfile.write(self.op.mem.base, value)
else:
raise NotImplementedError("writeback Operand unknown type", self.op.type)
def is_shifted(self):
return self.op.shift.type != ARM_SFT_INVALID
def address(self):
assert self.op.type == ARM_OP_MEM
mem = self.op.mem
addr = self.get_mem_base_addr() + self.get_mem_offset()
return addr & Mask(self.cpu.address_bit_size)
def get_mem_offset(self):
assert self.op.type == ARM_OP_MEM
mem = self.op.mem
off = 0
if mem.index:
idx = mem.scale * self.cpu.regfile.read(mem.index)
carry = self.cpu.regfile.read(ARM_REG_APSR_C)
if self.is_shifted():
shift = self.op.shift
idx, carry = self.cpu._Shift(idx, shift.type, shift.value, carry)
off = idx
else:
off = mem.disp
return -off if self.op.subtracted else off
def get_mem_base_addr(self):
assert self.op.type == ARM_OP_MEM
mem = self.op.mem
base = self.cpu.regfile.read(mem.base)
# If pc is the base, we need to correct for the fact that the ARM
# spec defines PC to point to the current insn + 8, which we are not
# compliant with (we do current insn + 4)
return base+4 if mem.base == ARM_REG_PC else base
def _getExpandImmCarry(self, carryIn):
'''Manually compute the carry bit produced by expanding an immediate
operand (see ARMExpandImm_C)
'''
insn = struct.unpack('<I', self.cpu.instruction.bytes)[0]
unrotated = insn & Mask(8)
shift = Operators.EXTRACT(insn, 8, 4)
_, carry = self.cpu._Shift(unrotated, ARM_SFT_ROR, 2*shift, carryIn)
return carry
class Armv7RegisterFile(RegisterFile):
REGMAP = {
ARM_REG_R0: 0,
ARM_REG_R1: 1,
ARM_REG_R2: 2,
ARM_REG_R3: 3,
ARM_REG_R4: 4,
ARM_REG_R5: 5,
ARM_REG_R6: 6,
ARM_REG_R7: 7,
ARM_REG_R8: 8,
ARM_REG_R9: 9,
ARM_REG_R10: 10,
ARM_REG_R11: 11,
ARM_REG_R12: 12,
ARM_REG_R13: 13, # alias: ARM_REG_SP
ARM_REG_R14: 14, # alias: ARM_REG_LR
ARM_REG_R15: 15, # alias: ARM_REG_PC
ARM_REG_D0: 16,
ARM_REG_D1: 17,
ARM_REG_D2: 18,
ARM_REG_D3: 19,
ARM_REG_D4: 20,
ARM_REG_D5: 21,
ARM_REG_D6: 22,
ARM_REG_D7: 23,
ARM_REG_D8: 24,
ARM_REG_D9: 25,
ARM_REG_D10: 26,
ARM_REG_D11: 27,
ARM_REG_D12: 28,
ARM_REG_D13: 29,
ARM_REG_D14: 30,
ARM_REG_D15: 31,
ARM_REG_D16: 32,
ARM_REG_D17: 33,
ARM_REG_D18: 34,
ARM_REG_D19: 35,
ARM_REG_D20: 36,
ARM_REG_D21: 37,
ARM_REG_D22: 38,
ARM_REG_D23: 39,
ARM_REG_D24: 40,
ARM_REG_D25: 41,
ARM_REG_D26: 42,
ARM_REG_D27: 43,
ARM_REG_D28: 44,
ARM_REG_D29: 45,
ARM_REG_D30: 46,
ARM_REG_D31: 47,
ARM_REG_APSR_N: 48,
ARM_REG_APSR_Z: 49,
ARM_REG_APSR_C: 50,
ARM_REG_APSR_V: 51
}
def __init__(self):
'''ARM Register file abstraction. GPRs use ints for read/write. APSR
flags allow writes of bool/{1, 0} but always read bools.
'''
super(Armv7RegisterFile, self).__init__( )
self.aliases = { 'STACK': 'SP',
'PC': 'PC', # these three lines are technically unnecessary, but explicit is
'SP': 'SP', # better than implicit
'LR': 'LR', }#)
gpr = [Register(32) for x in xrange(16)]
vec = [Register(64) for x in xrange(32)]
bit_flags = [Register(1) for x in xrange(4)]
self.regs = gpr + vec + bit_flags
def read(self, reg_id):
return self.regs[self.REGMAP[reg_id]].read()
def write(self, reg_id, val):
reg_offset = self.REGMAP[reg_id]
reg = self.regs[reg_offset]
reg.write(val)
@property
def all_registers(self):
return ('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',
'D21','D22','D23','D24','D25','D26','D27','D28','D29','D30','D31','APSR_N','APSR_Z','APSR_C','APSR_V') + ('STACK','PC','SP','LR')
@property
def canonical_registers(self):
return ('R0','R1','R2','R3','R4','R5','R6','R7','R8','R9','R10','R11','R12', 'SP', 'LR', 'PC')
def reg_name(self, reg_id):
reg_offset = self.REGMAP[reg_id]
return self.all_registers[reg_offset]
def reg_id(self, reg_name):
reg_name = self.aliases.get(reg_name, reg_name)
return globals()['ARM_REG_'+reg_name]
def __contains__(self, reg_id):
return reg_id in self.all_registers
class Armv7Cpu(Cpu):
'''Note: In this implementation, PC contains address of current
instruction + 4. However, official spec defines PC to be address of
current instruction + 8 (section A2.3).
'''
address_bit_size = 32
max_instr_width = 4
machine = 'armv7'
arch = CS_ARCH_ARM
mode = CS_MODE_ARM
def __init__(self, memory, *args, **kwargs):
super(Armv7Cpu, self).__init__(Armv7RegisterFile(), memory)
self._last_flags = {'C': 0, 'V': 0, 'N': 0, 'Z': 0}
self._force_next = False
def __getstate__(self):
state = super(Armv7Cpu, self).__getstate__()
state['_last_flags'] = self._last_flags
state['_force_next'] = self._force_next
return state
def __setstate__(self, state):
super(Armv7Cpu, self).__setstate__(state)
self._last_flags = state['_last_flags']
self._force_next = state['_force_next']
def _concretize_registers(cpu, instruction):
reg_values = {}
if hasattr(instruction, 'regs_access'):
(regs_read, regs_write) = instruction.regs_access()
regs = [ instruction.reg_name(r).upper() for r in regs_read ]
regs.append('R15')
else:
regs = self.canonical_registers
logger.debug("Emulator wants this regs %r", regs)
for reg in regs:
value = cpu.read_register(reg)
if isinstance(value, Expression):
raise ConcretizeRegister(reg, "Passing control to emulator") #FIXME improve exception to handle multiple registers at a time
reg_values[reg] = value
logger.info("Emulator wants this regs %r", reg_values)
return reg_values
def stack_get(self):
return self.STACK
def stack_set(self, value):
self.STACK = value
def pc_get(self):
return self.PC
def pc_set(self, value):
self.PC=value
def stack_sub(self, value):
self.STACK -= value
def stack_add(self, value):
self.STACK += value
# Flags that are the result of arithmetic instructions. Unconditionally
# set, but conditionally committed.
#
# Register file has the actual CPU flags
def setFlags(self, **flags):
'''
Note: For any unupdated flags, update _last_flags with the most recent
committed value. Otherwise, for example, this could happen:
overflow=0
instr1 computes overflow=1, updates _last_flags, doesn't commit
instr2 updates all flags in _last_flags except overflow (overflow remains 1 in _last_flags)
instr2 commits all in _last_flags
now overflow=1 even though it should still be 0
'''
unupdated_flags = self._last_flags.viewkeys() - flags.viewkeys()
for flag in unupdated_flags:
flag_name = globals()['ARM_REG_APSR_%s'%(flag,)]
self._last_flags[flag] = self.regfile.read(flag_name)
self._last_flags.update(flags)
def commitFlags(self):
# XXX: capstone incorrectly sets .update_flags for adc
if self.instruction.mnemonic == 'adc':
return
for flag, val in self._last_flags.iteritems():
flag_name = globals()['ARM_REG_APSR_%s'%(flag,)]
self.regfile.write(flag_name, val)
def _Shift(cpu, value, _type, amount, carry):
assert(_type > ARM_SFT_INVALID and _type <= ARM_SFT_RRX_REG)
# XXX: Capstone should set the value of an RRX shift to 1, which is
# asserted in the manual, but it sets it to 0, so we have to check
if _type in (ARM_SFT_RRX, ARM_SFT_RRX_REG) and amount != 1:
amount = 1
if _type in range(ARM_SFT_ASR_REG, ARM_SFT_RRX_REG + 1):
amount = Operators.EXTRACT(cpu.regfile.read(amount), 0, 8)
if amount == 0:
return value, carry
width = cpu.address_bit_size
if _type in (ARM_SFT_ASR, ARM_SFT_ASR_REG):
return ASR_C(value, amount, width)
elif _type in (ARM_SFT_LSL, ARM_SFT_LSL_REG):
return LSL_C(value, amount, width)
elif _type in (ARM_SFT_LSR, ARM_SFT_LSR_REG):
return LSR_C(value, amount, width)
elif _type in (ARM_SFT_ROR, ARM_SFT_ROR_REG):
return ROR_C(value, amount, width)
elif _type in (ARM_SFT_RRX, ARM_SFT_RRX_REG):
return RRX_C(value, carry, width)
raise NotImplementedError("Bad shift value")
# TODO add to abstract cpu, and potentially remove stacksub/add from it?
def stack_push(self, data):
if isinstance(data, (int, long)):
self.stack_sub(self.address_bit_size/8)
self.write_int(self.stack_get(), data, self.address_bit_size)
elif isinstance(data, BitVec):
self.stack_sub(data.size/8)
self.write_int(self.stack_get(), data, data.size)
elif isinstance(data, str):
self.stack_sub(len(data))
self.write(self.stack_get(), data)
else:
raise NotImplementedError('unsupported type for stack push data')
return self.stack_get()
def stack_peek(self, nbytes=4):
return self.read(self.stack_get(), nbytes)
def stack_pop(self, nbytes=4):
# TODO is the distinction between load and read really in the op size?
nbits = nbytes * 8
if nbits == self.address_bit_size:
val = self.read_int(self.stack_get(), nbits)
else:
val = self.read(self.stack_get(), nbytes)
self.stack_add(nbytes)
return val
def read(self, addr, nbytes):
return self.read_bytes(addr, nbytes)
def write(self, addr, data):
return self.write_bytes(addr, data)
@staticmethod
def canonicalize_instruction_name(instr):
name = instr.insn_name().upper()
# XXX bypass a capstone bug that incorrectly labels some insns as mov
if name == 'MOV':
if instr.mnemonic.startswith('lsr'):
return 'LSR'
elif instr.mnemonic.startswith('lsl'):
return 'LSL'
return OP_NAME_MAP.get(name, name)
def readOperand(self, op):
if op.type == ARM_OP_REG:
return self.regfile.read(op.reg)
elif op.type == ARM_OP_IMM:
return op.imm
elif op.type == ARM_OP_MEM:
raise NotImplementedError('need to impl arm load mem')
else:
raise NotImplementedError("readOperand unknown type", op.type)
def writeOperand(self, op, value):
if op.type == ARM_OP_REG:
self.regfile.write(op.reg, value)
elif op.type == ARM_OP_MEM:
raise NotImplementedError('need to impl arm store mem')
else:
raise NotImplementedError("writeOperand unknown type", op.type)
def getOperandAddress(self, op):
# TODO IMPLEMENT
return -1
def _wrap_operands(self, ops):
return [Armv7Operand(self, op) for op in ops]
def shouldCommitFlags(cpu):
return cpu.instruction.update_flags
def forceNextInstruction(cpu):
cpu._force_next = True
def shouldExecuteConditional(cpu):
cc = cpu.instruction.cc
ret = False
if cpu._force_next:
cpu._force_next = False
return True
C = cpu.regfile.read(ARM_REG_APSR_C)
N = cpu.regfile.read(ARM_REG_APSR_N)
V = cpu.regfile.read(ARM_REG_APSR_V)
Z = cpu.regfile.read(ARM_REG_APSR_Z)
if cc == ARM_CC_AL: ret = True
elif cc == ARM_CC_EQ: ret = Z
elif cc == ARM_CC_NE: ret = Operators.NOT(Z)
elif cc == ARM_CC_HS: ret = C
elif cc == ARM_CC_LO: ret = Operators.NOT(C)
elif cc == ARM_CC_MI: ret = N
elif cc == ARM_CC_PL: ret = Operators.NOT(N)
elif cc == ARM_CC_VS: ret = V
elif cc == ARM_CC_VC: ret = Operators.NOT(V)
elif cc == ARM_CC_HI:
ret = Operators.AND(C, Operators.NOT(Z))
elif cc == ARM_CC_LS:
ret = Operators.OR(Operators.NOT(C), Z)
elif cc == ARM_CC_GE: ret = N == V
elif cc == ARM_CC_LT: ret = N != V
elif cc == ARM_CC_GT:
ret = Operators.AND(Operators.NOT(Z), N == V)
elif cc == ARM_CC_LE:
ret = Operators.OR(Z, N != V)
else:
raise NotImplementedError("Bad conditional tag")
return ret
def getCanonicalRegisters(cpu):
#TODO: Clean up the way this interacts with the regfile
names = ['r%d'%(i,) for i in range(13)] + ['sp', 'lr', 'pc']
values = [cpu.regfile.regs[i].read() for i in range(16)]
d = dict(zip(names, values))
N = cpu.regfile.read(ARM_REG_APSR_N)
Z = cpu.regfile.read(ARM_REG_APSR_Z)
C = cpu.regfile.read(ARM_REG_APSR_C)
V = cpu.regfile.read(ARM_REG_APSR_V)
cpsr = 0
def make_cpsr_flag(flag_expr, offset):
'Helper for constructing an expression for the CPSR register'
return Operators.ITEBV(cpu.address_bit_size, flag_expr,
BitVecConstant(cpu.address_bit_size, 1 << offset),
BitVecConstant(cpu.address_bit_size, 0))
if any(isinstance(x, Expression) for x in [N, Z, C, V]):
cpsr = (make_cpsr_flag(N, 31) |
make_cpsr_flag(Z, 30) |
make_cpsr_flag(C, 29) |
make_cpsr_flag(V, 28))
else:
if N: cpsr |= 1 << 31
if Z: cpsr |= 1 << 30
if C: cpsr |= 1 << 29
if V: cpsr |= 1 << 28
d['cpsr'] = cpsr
return d
def getSyscallRetReg(self):
return 'r0'
def get_syscall_description(cpu):
# EABI standards:
# syscall # is in R7
# arguments are passed in R0-R6
# retval is passed in R0
index = cpu.regfile.read(ARM_REG_R7)
arg_indeces = [globals()['ARM_REG_R%d' % i] for i in range(0, 7)]
arguments = [cpu.regfile.read(idx) for idx in arg_indeces]
def writeResult(result, cpu = cpu):
cpu.regfile.write(ARM_REG_R0, result)
return (index, arguments, writeResult)
def getSyscallResult(self):
return self.regfile.read(ARM_REG_R0)
@instruction
def MOV(cpu, dest, src):
'''TODO: MOV imm should technically set carry bit.
XXX: We now set carry bit when it's a shift operation
Note: If src operand is PC, temporarily release our logical PC
view and conform to the spec, which dictates PC = curr instr + 8
'''
result, carry = src.read(withCarry=True)
dest.write(result)
# Setting flags in two separate setFlags calls clears earlier flags, set
# it once
flags = {'N' : HighBit(result),
'Z': (result == 0)}
if src.is_shifted():
flags['C'] = carry
cpu.setFlags(**flags)
def _handleWriteback(cpu, src, dest, offset):
# capstone bug doesn't set writeback correctly for postindex reg
if cpu.instruction.writeback or offset:
if offset:
off = offset.read()
else:
off = dest.get_mem_offset()
wbaddr = dest.get_mem_base_addr() + off
dest.writeback(wbaddr)
def _STR(cpu, width, src, dest, offset=None):
val = src.read()
cpu.write_int(dest.address(), val, width)
cpu._handleWriteback(src, dest, offset)
@instruction
def STR(cpu, *args): return cpu._STR(cpu.address_bit_size, *args)
@instruction
def STRB(cpu, *args): return cpu._STR(8, *args)
@instruction
def STRH(cpu, *args): return cpu._STR(16, *args)
def _LDR(cpu, dest, src, width, is_signed, offset):
mem = cpu.read_int(src.address(), width)
if is_signed:
word = Operators.SEXTEND(mem, width, cpu.address_bit_size)
else:
word = Operators.ZEXTEND(mem, cpu.address_bit_size)
dest.write(word)
cpu._handleWriteback(dest, src, offset)
@instruction
def LDR(cpu, dest, src, offset=None):
cpu._LDR(dest, src, 32, False, offset)
@instruction
def LDRH(cpu, dest, src, offset=None):
cpu._LDR(dest, src, 16, False, offset)
@instruction
def LDRSH(cpu, dest, src, offset=None):
cpu._LDR(dest, src, 16, True, offset)
@instruction
def LDRB(cpu, dest, src, offset=None):
cpu._LDR(dest, src, 8, False, offset)
@instruction
def LDRSB(cpu, dest, src, offset=None):
cpu._LDR(dest, src, 8, True, offset)
def _ADD(cpu, _op1, _op2, carry=0):
W = cpu.address_bit_size
# masking to 32 because sometimes capstone's op.imm field is negative.
# this converts it back to unsigned
_op2 = Operators.ZEXTEND(_op2, W)
uo1 = UInt(_op1, W*2)
uo2 = UInt(_op2, W*2)
c = UInt(carry, W*2)
unsigned_sum = uo1 + uo2 + c
so1 = SInt(Operators.SEXTEND(_op1, W, W*2), W*2)
so2 = SInt(Operators.SEXTEND(_op2, W, W*2), W*2)
signed_sum = so1 + so2 + c
result = GetNBits(unsigned_sum, W)
carry_out = UInt(result, W*2) != unsigned_sum
overflow = SInt(Operators.SEXTEND(result,W,W*2), W*2) != signed_sum
cpu.setFlags(C=carry_out,
V=overflow,
N=HighBit(result),
Z=result == 0)
return result, carry_out, overflow
@instruction
def ADC(cpu, dest, op1, op2):
carry = cpu.regfile.read(ARM_REG_APSR_C)
result, carry, overflow = cpu._ADD(op1.read(), op2.read(), carry)
dest.write(result)
return result, carry, overflow
@instruction
def ADD(cpu, dest, src, add):
result, carry, overflow = cpu._ADD(src.read(), add.read())
dest.write(result)
return result, carry, overflow
@instruction
def RSB(cpu, dest, src, add):
result, carry, overflow = cpu._ADD(~src.read(), add.read(), 1)
dest.write(result)
return result, carry, overflow
@instruction
def SUB(cpu, dest, src, add):
result, carry, overflow = cpu._ADD(src.read(), ~add.read(), 1)
dest.write(result)
return result, carry, overflow
@instruction
def SBC(cpu, dest, src, add):
carry = cpu.regfile.read(ARM_REG_APSR_C)
result, carry, overflow = cpu._ADD(src.read(), ~add.read(), carry)
dest.write(result)
return result, carry, overflow
@instruction
def B(cpu, dest):
cpu.PC = dest.read()
# XXX How should we deal with switching Thumb modes?
@instruction
def BX(cpu, dest):
cpu.PC = dest.read() & ~1
@instruction
def BLE(cpu, dest):
cpu.PC = Operators.ITEBV(cpu.address_bit_size,
cpu.regfile.read(ARM_REG_APSR_Z), dest.read(), cpu.PC)
@instruction
def BL(cpu, label):
next_instr_addr = cpu.regfile.read(ARM_REG_PC)
cpu.regfile.write(ARM_REG_LR, next_instr_addr)
cpu.regfile.write(ARM_REG_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
target = dest.read()
next_instr_addr = cpu.regfile.read(ARM_REG_PC) #- 2
cpu.regfile.write(ARM_REG_LR, next_instr_addr) # | 1)
cpu.regfile.write(ARM_REG_PC, target & ~1)
@instruction
def CMP(cpu, reg, cmp):
notcmp = ~cmp.read() & Mask(cpu.address_bit_size)
cpu._ADD(reg.read(), notcmp, 1)
@instruction
def POP(cpu, *regs):
sp = cpu.stack_get()
invalid = 0
# "The SP can only be in the list before ARMv7. ARM deprecates any use
# of ARM instructions that include the SP, and the value of the SP after
# such an instruction is UNKNOWN" (pg A8-537)
valid = lambda r: r.op.type == ARM_OP_REG and r.op.reg != ARM_REG_SP
for reg in regs:
assert valid(reg)
val = cpu.stack_pop(cpu.address_bit_size / 8)
reg.write(val)
@instruction
def PUSH(cpu, *regs):
# ARM deprecates the use of ARM instructions that include the PC in the
# list (pg A8-539)
valid = lambda r: r.op.type == ARM_OP_REG and r.op.reg != ARM_REG_PC
high_to_low_regs = regs[::-1]
for reg in high_to_low_regs:
assert valid(reg)
cpu.stack_push(reg.read())
@instruction
def CLZ(cpu, dest, src):
# Check if the |pos| bit is 1, pos being the offset from the MSB
value = src.read()
msb = cpu.address_bit_size - 1
result = 32
for pos in xrange(cpu.address_bit_size):
cond = Operators.EXTRACT(value, pos, 1) == 1
result = Operators.ITEBV(cpu.address_bit_size, cond, msb-pos, result)
dest.write(result)
@instruction
def NOP(cpu):
pass
def _LDM(cpu, insn_id, base, regs):
'''
It's technically UNKNOWN if you writeback to a register you loaded into,
but we let it slide.
'''
address = base.read()
if insn_id == ARM_INS_LDMIB:
address += cpu.address_bit_size/8
for reg in regs:
reg.write(cpu.read_int(address, cpu.address_bit_size))
address += cpu.address_bit_size/8
if insn_id == ARM_INS_LDMIB:
address -= cpu.address_bit_size/8
if cpu.instruction.writeback:
base.writeback(address)
@instruction
def LDM(cpu, base, *regs):
cpu._LDM(ARM_INS_LDM, base, regs)
@instruction
def LDMIB(cpu, base, *regs):
cpu._LDM(ARM_INS_LDMIB, base, regs)
def _STM(cpu, insn_id, base, regs):
address = base.read()
if insn_id == ARM_INS_STMIB:
address += 4
for reg in regs:
cpu.write_int(address, reg.read(), cpu.address_bit_size)
address += 4
# Undo the last addition if we're in STMIB
if insn_id == ARM_INS_STMIB:
address -= 4
if cpu.instruction.writeback:
base.writeback(address)
@instruction
def STM(cpu, base, *regs):
cpu._STM(ARM_INS_STM, base, regs)
@instruction
def STMIB(cpu, base, *regs):
cpu._STM(ARM_INS_STMIB, base, regs)
def _bitwise_instruction(cpu, operation, dest, op1, *op2):
if op2:
op2_val, carry = op2[0].read(withCarry=True)
result = operation(op1.read(), op2_val)
else:
op1_val, carry = op1.read(withCarry=True)
result = operation(op1_val)
if dest is not None:
dest.write(result)
cpu.setFlags(C=carry, N=HighBit(result), Z=(result == 0))
@instruction
def ORR(cpu, dest, op1, op2):
cpu._bitwise_instruction(lambda x, y: x | y, dest, op1, op2)
@instruction
def EOR(cpu, dest, op1, op2):
cpu._bitwise_instruction(lambda x, y: x ^ y, dest, op1, op2)
@instruction
def AND(cpu, dest, op1, op2):
cpu._bitwise_instruction(lambda x, y: x & y, dest, op1, op2)
@instruction
def TEQ(cpu, *operands):
cpu._bitwise_instruction(lambda x, y: x ^ y, None, *operands)
cpu.commitFlags()
@instruction
def TST(cpu, Rn, Rm):
shifted, carry = Rm.read(withCarry=True)
result = Rn.read() & shifted
cpu.setFlags(N=HighBit(result), Z=(result==0), C=carry)
@instruction
def SVC(cpu, op):
if (op.read() != 0):
logger.warning("Bad SVC number: {:08}".format(op.read()))
raise Interruption(0)
@instruction
def CMN(cpu, src, add):
result, carry, overflow = cpu._ADD(src.read(), add.read())
return result, carry, overflow
@instruction
def LSL(cpu, dest, op, *rest):
'''LSL reg has @rest, but LSL imm does not, its baked into @op
'''
carry = cpu.regfile.read(ARM_REG_APSR_C)
if rest:
amount_val = rest[0].op.reg
result, carry = cpu._Shift(op.read(), ARM_SFT_LSL_REG, amount_val, carry)
else:
result, carry = op.read(withCarry=True)
dest.write(result)
cpu.setFlags(N=HighBit(result), Z=(result==0), C=carry)
@instruction
def LSR(cpu, dest, op, *rest):
'''LSR reg has @rest, but LSR imm does not, its baked into @op
'''
carry = cpu.regfile.read(ARM_REG_APSR_C)
if rest:
amount_val = rest[0].op.reg
result, carry = cpu._Shift(op.read(), ARM_SFT_LSR_REG, amount_val, carry)
else:
result, carry = op.read(withCarry=True)
dest.write(result)
cpu.setFlags(N=HighBit(result), Z=(result==0), C=carry)
@instruction
def UMULL(cpu, rdlo, rdhi, rn, rm):
result = UInt(rn.read(), cpu.address_bit_size*2) * UInt(rm.read(), cpu.address_bit_size*2)
rdhi.write(Operators.EXTRACT(result, cpu.address_bit_size, cpu.address_bit_size))
rdlo.write(GetNBits(result, cpu.address_bit_size))
cpu.setFlags(N=Bit(result, 63), Z=(result==0))
@instruction
def MUL(cpu, dest, src1, src2):
width = cpu.address_bit_size
op1 = SInt(src1.read(), width)
op2 = SInt(src2.read(), width)
result = op1 * op2
dest.write(result & Mask(width))
cpu.setFlags(N=HighBit(result), Z=(result==0))
@instruction
def MVN(cpu,dest, op):
cpu._bitwise_instruction(lambda x: ~x, dest, op)
@instruction
def MLA(cpu, dest, op1, op2, addend):
width = cpu.address_bit_size
op1_val = SInt(op1.read(), width)
op2_val = SInt(op2.read(), width)
add_val = SInt(addend.read(), width)
result = op1_val * op2_val + add_val
dest.write(result & Mask(cpu.address_bit_size))
cpu.setFlags(N=HighBit(result), Z=(result==0))
@instruction
def BIC(cpu, dest, reg, imm):
result = (reg.read() & ~imm.read()) & Mask(cpu.address_bit_size)
dest.write(result)
cpu.setFlags(N=HighBit(result), Z=(result==0))
def _VSTM(cpu, address, *regs):
for reg in regs:
size = reg.size()
cpu.write_int(address, reg.read(), size * 8)
address += size
return address
@instruction
def VSTMIA(cpu, base, *regs):
updated_address = cpu._VSTM(base.read(), *regs)
if cpu.instruction.writeback:
base.writeback(updated_address)
@instruction
def VSTMDB(cpu, base, *regs):
address = base.read() - (cpu.address_bit_size / 8) * len(regs)
updated_address = cpu._VSTM(address, *regs)
if cpu.instruction.writeback:
base.writeback(updated_address)
@instruction
def STCL(cpu, *operands):
pass