Armv7 code refactor (#749)
* Armv7 code refactor Removes dead imports, makes code style more consistent and a bit more compliant with pep8 (yeah i seems we don't enforce any particular style, but this seems to be a reasonable default for me). * Review fix: add comment for armv7 _shift
This commit is contained in:
committed by
Mark Mossberg
parent
c6f457d72e
commit
680fc8f17e
+191
-179
@@ -18,15 +18,17 @@ 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()
|
||||
should_execute = cpu.should_execute_conditional()
|
||||
|
||||
if cpu._at_symbolic_conditional:
|
||||
cpu._at_symbolic_conditional = False
|
||||
@@ -36,20 +38,21 @@ def instruction(body):
|
||||
# Let's remember next time we get here we should not do this again
|
||||
cpu._at_symbolic_conditional = True
|
||||
i_size = cpu.address_bit_size / 8
|
||||
cpu.PC = Operators.ITEBV(cpu.address_bit_size, should_execute, cpu.PC-i_size,
|
||||
cpu.PC)
|
||||
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()
|
||||
if cpu.should_commit_flags():
|
||||
cpu.commit_flags()
|
||||
|
||||
return ret
|
||||
|
||||
return abstract_instruction(instruction_implementation)
|
||||
|
||||
|
||||
class Armv7Operand(Operand):
|
||||
def __init__(self, cpu, op, **kwargs):
|
||||
super(Armv7Operand, self).__init__(cpu, op, **kwargs)
|
||||
@@ -60,8 +63,8 @@ class Armv7Operand(Operand):
|
||||
cs.arm.ARM_OP_REG: 'register',
|
||||
cs.arm.ARM_OP_MEM: 'memory',
|
||||
cs.arm.ARM_OP_IMM: 'immediate',
|
||||
cs.arm.ARM_OP_PIMM:'coprocessor',
|
||||
cs.arm.ARM_OP_CIMM:'immediate'
|
||||
cs.arm.ARM_OP_PIMM: 'coprocessor',
|
||||
cs.arm.ARM_OP_CIMM: 'immediate'
|
||||
}
|
||||
|
||||
return type_map[self.op.type]
|
||||
@@ -69,13 +72,13 @@ class Armv7Operand(Operand):
|
||||
@property
|
||||
def size(self):
|
||||
assert self.type == 'register'
|
||||
if self.op.reg >= cs.arm.ARM_REG_D0 and self.op.reg <= cs.arm.ARM_REG_D31:
|
||||
if cs.arm.ARM_REG_D0 <= self.op.reg <= cs.arm.ARM_REG_D31:
|
||||
return 64
|
||||
else:
|
||||
#FIXME check other types of operand sizes
|
||||
# FIXME check other types of operand sizes
|
||||
return 32
|
||||
|
||||
def read(self, nbits=None, withCarry=False):
|
||||
def read(self, nbits=None, with_carry=False):
|
||||
carry = self.cpu.regfile.read('APSR_C')
|
||||
if self.type == 'register':
|
||||
value = self.cpu.regfile.read(self.reg)
|
||||
@@ -85,26 +88,26 @@ class Armv7Operand(Operand):
|
||||
value += cpu.instruction.size
|
||||
if self.is_shifted():
|
||||
shift = self.op.shift
|
||||
value, carry = self.cpu._Shift(value, shift.type, shift.value, carry)
|
||||
value, carry = self.cpu._shift(value, shift.type, shift.value, carry)
|
||||
if self.op.subtracted:
|
||||
value = -value
|
||||
if withCarry:
|
||||
if with_carry:
|
||||
return value, carry
|
||||
return value
|
||||
elif self.type == 'immediate':
|
||||
imm = self.op.imm
|
||||
if self.op.subtracted:
|
||||
imm = -imm
|
||||
if withCarry:
|
||||
return imm, self._getExpandImmCarry(carry)
|
||||
if with_carry:
|
||||
return imm, self._get_expand_imm_carry(carry)
|
||||
return imm
|
||||
elif self.type == 'coprocessor':
|
||||
imm = self.op.imm
|
||||
return imm
|
||||
elif self.type == 'memory':
|
||||
val = self.cpu.read_int(self.address(), nbits)
|
||||
if withCarry:
|
||||
return (val, carry)
|
||||
if with_carry:
|
||||
return val, carry
|
||||
return val
|
||||
else:
|
||||
raise NotImplementedError("readOperand unknown type", self.op.type)
|
||||
@@ -142,7 +145,7 @@ class Armv7Operand(Operand):
|
||||
carry = self.cpu.regfile.read('APSR_C')
|
||||
if self.is_shifted():
|
||||
shift = self.op.shift
|
||||
idx, carry = self.cpu._Shift(idx, shift.type, shift.value, carry)
|
||||
idx, carry = self.cpu._shift(idx, shift.type, shift.value, carry)
|
||||
off = -idx if self.op.subtracted else idx
|
||||
else:
|
||||
off = self.mem.disp
|
||||
@@ -174,58 +177,57 @@ class Armv7Operand(Operand):
|
||||
else:
|
||||
return base
|
||||
|
||||
def _getExpandImmCarry(self, carryIn):
|
||||
'''Manually compute the carry bit produced by expanding an immediate
|
||||
operand (see ARMExpandImm_C)
|
||||
'''
|
||||
def _get_expand_imm_carry(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, cs.arm.ARM_SFT_ROR, 2 * shift, carryIn)
|
||||
_, carry = self.cpu._shift(unrotated, cs.arm.ARM_SFT_ROR, 2 * shift, carryIn)
|
||||
return carry
|
||||
|
||||
|
||||
class Armv7RegisterFile(RegisterFile):
|
||||
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__({ 'SB':'R9',
|
||||
'SL':'R10',
|
||||
'FP':'R11',
|
||||
'IP': 'R12',
|
||||
'STACK': 'R13',
|
||||
'SP': 'R13',
|
||||
'LR': 'R14',
|
||||
'PC': 'R15', } )
|
||||
self._regs = { }
|
||||
#32 bit registers
|
||||
for reg_name in ( 'R0', 'R1', 'R2', 'R3', 'R4', 'R5', 'R6', 'R7', 'R8',
|
||||
'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15' ):
|
||||
"""
|
||||
super(Armv7RegisterFile, self).__init__({'SB': 'R9',
|
||||
'SL': 'R10',
|
||||
'FP': 'R11',
|
||||
'IP': 'R12',
|
||||
'STACK': 'R13',
|
||||
'SP': 'R13',
|
||||
'LR': 'R14',
|
||||
'PC': 'R15'})
|
||||
self._regs = {}
|
||||
# 32 bit registers
|
||||
for reg_name in ('R0', 'R1', 'R2', 'R3', 'R4', 'R5', 'R6', 'R7', 'R8',
|
||||
'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15'):
|
||||
self._regs[reg_name] = Register(32)
|
||||
#64 bit registers
|
||||
for reg_name in ( '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'):
|
||||
# 64 bit registers
|
||||
for reg_name in ('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'):
|
||||
self._regs[reg_name] = Register(64)
|
||||
#Flags
|
||||
# Flags
|
||||
self._regs['APSR_N'] = Register(1)
|
||||
self._regs['APSR_Z'] = Register(1)
|
||||
self._regs['APSR_C'] = Register(1)
|
||||
self._regs['APSR_V'] = Register(1)
|
||||
self._regs['APSR_GE'] = Register(4)
|
||||
|
||||
#MMU Coprocessor -- to support MCR/MRC for TLS
|
||||
# MMU Coprocessor -- to support MCR/MRC for TLS
|
||||
self._regs['P15_C13'] = Register(32)
|
||||
|
||||
def _read_APSR(self):
|
||||
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"""
|
||||
return Operators.ITEBV(32, flag_expr,
|
||||
BitVecConstant(32, 1 << offset),
|
||||
BitVecConstant(32, 0))
|
||||
BitVecConstant(32, 1 << offset),
|
||||
BitVecConstant(32, 0))
|
||||
|
||||
apsr = 0
|
||||
N = self.read('APSR_N')
|
||||
Z = self.read('APSR_Z')
|
||||
@@ -244,9 +246,8 @@ class Armv7RegisterFile(RegisterFile):
|
||||
if V: apsr |= 1 << 28
|
||||
return apsr
|
||||
|
||||
|
||||
def _write_APSR(self, apsr):
|
||||
''' Auxiliary function - Writes flags from a full APSR (only 4 msb used) '''
|
||||
"""Auxiliary function - Writes flags from a full APSR (only 4 msb used)"""
|
||||
V = Operators.EXTRACT(apsr, 28, 1)
|
||||
C = Operators.EXTRACT(apsr, 29, 1)
|
||||
Z = Operators.EXTRACT(apsr, 30, 1)
|
||||
@@ -274,19 +275,22 @@ class Armv7RegisterFile(RegisterFile):
|
||||
@property
|
||||
def all_registers(self):
|
||||
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',
|
||||
'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','APSR_GE',
|
||||
('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', 'APSR_N',
|
||||
'APSR_Z', 'APSR_C', 'APSR_V', 'APSR_GE',
|
||||
'P15_C13')
|
||||
|
||||
@property
|
||||
def canonical_registers(self):
|
||||
return ('R0','R1','R2','R3','R4','R5','R6','R7','R8','R9','R10','R11','R12','R13','R14','R15','APSR')
|
||||
return ('R0', 'R1', 'R2', 'R3', 'R4', 'R5', 'R6', 'R7', 'R8', 'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15',
|
||||
'APSR')
|
||||
|
||||
|
||||
class Armv7LinuxSyscallAbi(SyscallAbi):
|
||||
'''
|
||||
ARMv7 Linux system call ABI
|
||||
'''
|
||||
"""ARMv7 Linux system call ABI"""
|
||||
|
||||
# EABI standards:
|
||||
# syscall # is in R7
|
||||
# arguments are passed in R0-R6
|
||||
@@ -301,10 +305,10 @@ class Armv7LinuxSyscallAbi(SyscallAbi):
|
||||
def write_result(self, result):
|
||||
self._cpu.R0 = result
|
||||
|
||||
|
||||
class Armv7CdeclAbi(Abi):
|
||||
'''
|
||||
ARMv7 Cdecl function call ABI
|
||||
'''
|
||||
"""ARMv7 Cdecl function call ABI"""
|
||||
|
||||
def get_arguments(self):
|
||||
# First four passed via R0-R3, then on stack
|
||||
for reg in ('R0', 'R1', 'R2', 'R3'):
|
||||
@@ -319,14 +323,15 @@ class Armv7CdeclAbi(Abi):
|
||||
def ret(self):
|
||||
self._cpu.PC = self._cpu.LR
|
||||
|
||||
|
||||
class Armv7Cpu(Cpu):
|
||||
'''
|
||||
"""
|
||||
Cpu specialization handling the ARMv7 architecture.
|
||||
|
||||
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'
|
||||
@@ -382,20 +387,19 @@ class Armv7Cpu(Cpu):
|
||||
self.mode = new_mode
|
||||
|
||||
def _swap_mode(self):
|
||||
'Toggle between ARM and Thumb mode'
|
||||
"""Toggle between ARM and Thumb mode"""
|
||||
assert self.mode in (cs.CS_MODE_ARM, cs.CS_MODE_THUMB)
|
||||
if self.mode == cs.CS_MODE_ARM:
|
||||
self.mode = cs.CS_MODE_THUMB
|
||||
else:
|
||||
self.mode = cs.CS_MODE_ARM
|
||||
|
||||
|
||||
# Flags that are the result of arithmetic instructions. Unconditionally
|
||||
# set, but conditionally committed.
|
||||
#
|
||||
# Register file has the actual CPU flags
|
||||
def setFlags(self, **flags):
|
||||
'''
|
||||
def set_flags(self, **flags):
|
||||
"""
|
||||
Note: For any unmodified flags, update _last_flags with the most recent
|
||||
committed value. Otherwise, for example, this could happen:
|
||||
|
||||
@@ -404,14 +408,14 @@ class Armv7Cpu(Cpu):
|
||||
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 = 'APSR_{}'.format(flag)
|
||||
self._last_flags[flag] = self.regfile.read(flag_name)
|
||||
self._last_flags.update(flags)
|
||||
|
||||
def commitFlags(self):
|
||||
def commit_flags(self):
|
||||
# XXX: capstone incorrectly sets .update_flags for adc
|
||||
if self.instruction.mnemonic == 'adc':
|
||||
return
|
||||
@@ -419,8 +423,9 @@ class Armv7Cpu(Cpu):
|
||||
flag_name = 'APSR_{}'.format(flag)
|
||||
self.regfile.write(flag_name, val)
|
||||
|
||||
def _Shift(cpu, value, _type, amount, carry):
|
||||
assert(_type > cs.arm.ARM_SFT_INVALID and _type <= cs.arm.ARM_SFT_RRX_REG)
|
||||
def _shift(cpu, value, _type, amount, carry):
|
||||
"""See Shift() and Shift_C() in the ARM manual"""
|
||||
assert(cs.arm.ARM_SFT_INVALID < _type <= cs.arm.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
|
||||
@@ -440,7 +445,7 @@ class Armv7Cpu(Cpu):
|
||||
|
||||
width = cpu.address_bit_size
|
||||
|
||||
if _type in (cs.arm.ARM_SFT_ASR, cs.arm.ARM_SFT_ASR_REG):
|
||||
if _type in (cs.arm.ARM_SFT_ASR, cs.arm.ARM_SFT_ASR_REG):
|
||||
return ASR_C(value, amount, width)
|
||||
elif _type in (cs.arm.ARM_SFT_LSL, cs.arm.ARM_SFT_LSL_REG):
|
||||
return LSL_C(value, amount, width)
|
||||
@@ -456,11 +461,11 @@ class Armv7Cpu(Cpu):
|
||||
# TODO add to abstract cpu, and potentially remove stacksub/add from it?
|
||||
def stack_push(self, data, nbytes=None):
|
||||
if isinstance(data, (int, long)):
|
||||
nbytes = nbytes or self.address_bit_size/8
|
||||
nbytes = nbytes or self.address_bit_size / 8
|
||||
self.SP -= nbytes
|
||||
self.write_int(self.SP, data, nbytes * 8)
|
||||
elif isinstance(data, BitVec):
|
||||
self.SP -= data.size/8
|
||||
self.SP -= data.size / 8
|
||||
self.write_int(self.SP, data, data.size)
|
||||
elif isinstance(data, str):
|
||||
self.SP -= len(data)
|
||||
@@ -507,21 +512,21 @@ class Armv7Cpu(Cpu):
|
||||
def _wrap_operands(self, ops):
|
||||
return [Armv7Operand(self, op) for op in ops]
|
||||
|
||||
def shouldCommitFlags(cpu):
|
||||
#workaround for a capstone bug (issue #980);
|
||||
#the bug has been fixed the 'master' and 'next' branches of capstone as of 2017-07-31
|
||||
def should_commit_flags(cpu):
|
||||
# workaround for a capstone bug (issue #980);
|
||||
# the bug has been fixed the 'master' and 'next' branches of capstone as of 2017-07-31
|
||||
if cpu.instruction.id == cs.arm.ARM_INS_UADD8:
|
||||
return True
|
||||
|
||||
return cpu.instruction.update_flags
|
||||
|
||||
def shouldExecuteConditional(cpu):
|
||||
#for the IT instuction, the cc applies to the subsequent instructions,
|
||||
#so the IT instruction should be executed regardless of its cc
|
||||
def should_execute_conditional(cpu):
|
||||
# for the IT instruction, the cc applies to the subsequent instructions,
|
||||
# so the IT instruction should be executed regardless of its cc
|
||||
if cpu.instruction.id == cs.arm.ARM_INS_IT:
|
||||
return True
|
||||
|
||||
#support for the it[x[y[z]]] <op> instructions
|
||||
# support for the it[x[y[z]]] <op> instructions
|
||||
if cpu._it_conditional:
|
||||
return cpu._it_conditional.pop(0)
|
||||
|
||||
@@ -534,21 +539,32 @@ class Armv7Cpu(Cpu):
|
||||
V = cpu.regfile.read('APSR_V')
|
||||
Z = cpu.regfile.read('APSR_Z')
|
||||
|
||||
if cc == cs.arm.ARM_CC_AL: ret = True
|
||||
elif cc == cs.arm.ARM_CC_EQ: ret = Z
|
||||
elif cc == cs.arm.ARM_CC_NE: ret = Operators.NOT(Z)
|
||||
elif cc == cs.arm.ARM_CC_HS: ret = C
|
||||
elif cc == cs.arm.ARM_CC_LO: ret = Operators.NOT(C)
|
||||
elif cc == cs.arm.ARM_CC_MI: ret = N
|
||||
elif cc == cs.arm.ARM_CC_PL: ret = Operators.NOT(N)
|
||||
elif cc == cs.arm.ARM_CC_VS: ret = V
|
||||
elif cc == cs.arm.ARM_CC_VC: ret = Operators.NOT(V)
|
||||
if cc == cs.arm.ARM_CC_AL:
|
||||
ret = True
|
||||
elif cc == cs.arm.ARM_CC_EQ:
|
||||
ret = Z
|
||||
elif cc == cs.arm.ARM_CC_NE:
|
||||
ret = Operators.NOT(Z)
|
||||
elif cc == cs.arm.ARM_CC_HS:
|
||||
ret = C
|
||||
elif cc == cs.arm.ARM_CC_LO:
|
||||
ret = Operators.NOT(C)
|
||||
elif cc == cs.arm.ARM_CC_MI:
|
||||
ret = N
|
||||
elif cc == cs.arm.ARM_CC_PL:
|
||||
ret = Operators.NOT(N)
|
||||
elif cc == cs.arm.ARM_CC_VS:
|
||||
ret = V
|
||||
elif cc == cs.arm.ARM_CC_VC:
|
||||
ret = Operators.NOT(V)
|
||||
elif cc == cs.arm.ARM_CC_HI:
|
||||
ret = Operators.AND(C, Operators.NOT(Z))
|
||||
elif cc == cs.arm.ARM_CC_LS:
|
||||
ret = Operators.OR(Operators.NOT(C), Z)
|
||||
elif cc == cs.arm.ARM_CC_GE: ret = N == V
|
||||
elif cc == cs.arm.ARM_CC_LT: ret = N != V
|
||||
elif cc == cs.arm.ARM_CC_GE:
|
||||
ret = N == V
|
||||
elif cc == cs.arm.ARM_CC_LT:
|
||||
ret = N != V
|
||||
elif cc == cs.arm.ARM_CC_GT:
|
||||
ret = Operators.AND(Operators.NOT(Z), N == V)
|
||||
elif cc == cs.arm.ARM_CC_LE:
|
||||
@@ -562,8 +578,8 @@ class Armv7Cpu(Cpu):
|
||||
def IT(cpu):
|
||||
cc = cpu.instruction.cc
|
||||
true_case = cpu._evaluate_conditional(cc)
|
||||
#this is incredibly hacky--how else does capstone expose this?
|
||||
#TODO: find a better way than string parsing the mnemonic -GR, 2017-07-13
|
||||
# this is incredibly hacky--how else does capstone expose this?
|
||||
# TODO: find a better way than string parsing the mnemonic -GR, 2017-07-13
|
||||
for c in cpu.instruction.mnemonic[1:]:
|
||||
if c == 't':
|
||||
cpu._it_conditional.append(true_case)
|
||||
@@ -577,14 +593,14 @@ class Armv7Cpu(Cpu):
|
||||
sums = list()
|
||||
carries = list()
|
||||
for i in range(4):
|
||||
uo1 = UInt(Operators.ZEXTEND(Operators.EXTRACT(op1, (8*i), 8), 9), 9)
|
||||
uo2 = UInt(Operators.ZEXTEND(Operators.EXTRACT(op2, (8*i), 8), 9), 9)
|
||||
uo1 = UInt(Operators.ZEXTEND(Operators.EXTRACT(op1, (8 * i), 8), 9), 9)
|
||||
uo2 = UInt(Operators.ZEXTEND(Operators.EXTRACT(op2, (8 * i), 8), 9), 9)
|
||||
byte = uo1 + uo2
|
||||
carry = Operators.EXTRACT(byte, 8, 1)
|
||||
sums.append(Operators.EXTRACT(byte, 0, 8))
|
||||
carries.append(carry)
|
||||
dest.write(Operators.CONCAT(32, *reversed(sums)))
|
||||
cpu.setFlags(GE=Operators.CONCAT(4, *reversed(carries)))
|
||||
cpu.set_flags(GE=Operators.CONCAT(4, *reversed(carries)))
|
||||
|
||||
@instruction
|
||||
def SEL(cpu, dest, op1, op2):
|
||||
@@ -594,12 +610,13 @@ class Armv7Cpu(Cpu):
|
||||
GE = cpu.regfile.read('APSR_GE')
|
||||
for i in range(4):
|
||||
bit = Operators.EXTRACT(GE, i, 1)
|
||||
result.append(Operators.ITEBV(8, bit, Operators.EXTRACT(op1val, i*8, 8), Operators.EXTRACT(op2val, i*8, 8)))
|
||||
result.append(
|
||||
Operators.ITEBV(8, bit, Operators.EXTRACT(op1val, i * 8, 8), Operators.EXTRACT(op2val, i * 8, 8)))
|
||||
dest.write(Operators.CONCAT(32, *reversed(result)))
|
||||
|
||||
@instruction
|
||||
def MOV(cpu, dest, src):
|
||||
'''
|
||||
"""
|
||||
Implement the MOV{S} instruction.
|
||||
|
||||
Note: If src operand is PC, temporarily release our logical PC
|
||||
@@ -607,25 +624,25 @@ class Armv7Cpu(Cpu):
|
||||
|
||||
:param Armv7Operand dest: The destination operand; register.
|
||||
:param Armv7Operand src: The source operand; register or immediate.
|
||||
'''
|
||||
"""
|
||||
if cpu.mode == cs.CS_MODE_ARM:
|
||||
result, carry_out = src.read(withCarry=True)
|
||||
result, carry_out = src.read(with_carry=True)
|
||||
dest.write(result)
|
||||
cpu.setFlags(C=carry_out, N=HighBit(result), Z=(result == 0))
|
||||
cpu.set_flags(C=carry_out, N=HighBit(result), Z=(result == 0))
|
||||
else:
|
||||
# thumb mode cannot do wonky things to the operand, so no carry calculation
|
||||
result = src.read()
|
||||
dest.write(result)
|
||||
cpu.setFlags(N=HighBit(result), Z=(result == 0))
|
||||
cpu.set_flags(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)
|
||||
@@ -633,7 +650,7 @@ class Armv7Cpu(Cpu):
|
||||
|
||||
@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
|
||||
@@ -642,7 +659,7 @@ class Armv7Cpu(Cpu):
|
||||
: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'
|
||||
@@ -653,7 +670,7 @@ class Armv7Cpu(Cpu):
|
||||
coprocessor_n_name = coprocessor_reg_n.read()
|
||||
coprocessor_m_name = coprocessor_reg_m.read()
|
||||
|
||||
if 15 == imm_coprocessor: #MMU
|
||||
if 15 == imm_coprocessor: # MMU
|
||||
if 0 == imm_opcode1:
|
||||
if 13 == coprocessor_n_name:
|
||||
if 3 == imm_opcode2:
|
||||
@@ -663,14 +680,12 @@ class Armv7Cpu(Cpu):
|
||||
|
||||
@instruction
|
||||
def LDRD(cpu, dest1, dest2, src, offset=None):
|
||||
'''
|
||||
Loads double width data from memory.
|
||||
'''
|
||||
"""Loads double width data from memory."""
|
||||
assert dest1.type == 'register'
|
||||
assert dest2.type == 'register'
|
||||
assert src.type == 'memory'
|
||||
mem1 = cpu.read_int(src.address(), 32)
|
||||
mem2 = cpu.read_int(src.address()+4, 32)
|
||||
mem2 = cpu.read_int(src.address() + 4, 32)
|
||||
writeback = cpu._compute_writeback(src, offset)
|
||||
dest1.write(mem1)
|
||||
dest2.write(mem2)
|
||||
@@ -678,9 +693,7 @@ class Armv7Cpu(Cpu):
|
||||
|
||||
@instruction
|
||||
def STRD(cpu, src1, src2, dest, offset=None):
|
||||
'''
|
||||
Writes the contents of two registers to memory.
|
||||
'''
|
||||
"""Writes the contents of two registers to memory."""
|
||||
assert src1.type == 'register'
|
||||
assert src2.type == 'register'
|
||||
assert dest.type == 'memory'
|
||||
@@ -688,12 +701,12 @@ class Armv7Cpu(Cpu):
|
||||
val2 = src2.read()
|
||||
writeback = cpu._compute_writeback(dest, offset)
|
||||
cpu.write_int(dest.address(), val1, 32)
|
||||
cpu.write_int(dest.address()+4, val2, 32)
|
||||
cpu.write_int(dest.address() + 4, val2, 32)
|
||||
cpu._cs_hack_ldr_str_writeback(dest, offset, writeback)
|
||||
|
||||
@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
|
||||
@@ -704,38 +717,36 @@ class Armv7Cpu(Cpu):
|
||||
|
||||
: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
|
||||
"""
|
||||
# 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
|
||||
"""
|
||||
# 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.
|
||||
'''
|
||||
"""PLD instructs the cpu that the address at addr might be loaded soon."""
|
||||
pass
|
||||
|
||||
def _compute_writeback(cpu, operand, offset):
|
||||
@@ -758,13 +769,16 @@ class Armv7Cpu(Cpu):
|
||||
cpu._cs_hack_ldr_str_writeback(dest, offset, writeback)
|
||||
|
||||
@instruction
|
||||
def STR(cpu, *args): return cpu._STR(cpu.address_bit_size, *args)
|
||||
def STR(cpu, *args):
|
||||
return cpu._STR(cpu.address_bit_size, *args)
|
||||
|
||||
@instruction
|
||||
def STRB(cpu, *args): return cpu._STR(8, *args)
|
||||
def STRB(cpu, *args):
|
||||
return cpu._STR(8, *args)
|
||||
|
||||
@instruction
|
||||
def STRH(cpu, *args): return cpu._STR(16, *args)
|
||||
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)
|
||||
@@ -806,24 +820,24 @@ class Armv7Cpu(Cpu):
|
||||
# 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)
|
||||
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)
|
||||
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
|
||||
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)
|
||||
cpu.set_flags(C=carry_out,
|
||||
V=overflow,
|
||||
N=HighBit(result),
|
||||
Z=result == 0)
|
||||
|
||||
return result, carry_out, overflow
|
||||
|
||||
@@ -842,7 +856,7 @@ class Armv7Cpu(Cpu):
|
||||
if add is not None:
|
||||
result, carry, overflow = cpu._ADD(src.read(), add.read())
|
||||
else:
|
||||
#support for the thumb mode version of adds <dest>, <immediate>
|
||||
# support for the thumb mode version of adds <dest>, <immediate>
|
||||
result, carry, overflow = cpu._ADD(dest.read(), src.read())
|
||||
dest.write(result)
|
||||
return result, carry, overflow
|
||||
@@ -867,8 +881,9 @@ class Armv7Cpu(Cpu):
|
||||
if add is not None:
|
||||
result, carry, overflow = cpu._ADD(src.read(), ~add.read(), 1)
|
||||
else:
|
||||
#support for the thumb mode version of sub <dest>, <immediate>
|
||||
# support for the thumb mode version of sub <dest>, <immediate>
|
||||
result, carry, overflow = cpu._ADD(dest.read(), ~src.read(), 1)
|
||||
|
||||
dest.write(result)
|
||||
return result, carry, overflow
|
||||
|
||||
@@ -924,9 +939,9 @@ class Armv7Cpu(Cpu):
|
||||
cpu.regfile.write('LR', next_instr_addr)
|
||||
cpu.regfile.write('PC', target & ~1)
|
||||
|
||||
## 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':
|
||||
# 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 mode due to BLX at inst 0x{:x}".format(address))
|
||||
cpu._swap_mode()
|
||||
elif dest.type=='register':
|
||||
@@ -952,7 +967,6 @@ class Armv7Cpu(Cpu):
|
||||
for reg in high_to_low_regs:
|
||||
cpu.stack_push(reg)
|
||||
|
||||
|
||||
@instruction
|
||||
def CLZ(cpu, dest, src):
|
||||
|
||||
@@ -963,7 +977,7 @@ class Armv7Cpu(Cpu):
|
||||
|
||||
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)
|
||||
result = Operators.ITEBV(cpu.address_bit_size, cond, msb - pos, result)
|
||||
|
||||
dest.write(result)
|
||||
|
||||
@@ -976,7 +990,7 @@ class Armv7Cpu(Cpu):
|
||||
opval = op.read()
|
||||
_bytes = list()
|
||||
for i in range(4):
|
||||
_bytes.append(Operators.EXTRACT(opval, i*8, 8))
|
||||
_bytes.append(Operators.EXTRACT(opval, i * 8, 8))
|
||||
dest.write(Operators.CONCAT(32, *_bytes))
|
||||
|
||||
@instruction
|
||||
@@ -985,20 +999,20 @@ class Armv7Cpu(Cpu):
|
||||
dest.write(Operators.SEXTEND(Operators.EXTRACT(_op, 0, 16), 16, 32))
|
||||
|
||||
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 == cs.arm.ARM_INS_LDMIB:
|
||||
address += cpu.address_bit_size/8
|
||||
address += cpu.address_bit_size / 8
|
||||
|
||||
for reg in regs:
|
||||
reg.write(cpu.read_int(address, cpu.address_bit_size))
|
||||
address += reg.size/8
|
||||
address += reg.size / 8
|
||||
|
||||
if insn_id == cs.arm.ARM_INS_LDMIB:
|
||||
address -= reg.size/8
|
||||
address -= reg.size / 8
|
||||
|
||||
if cpu.instruction.writeback:
|
||||
base.writeback(address)
|
||||
@@ -1037,14 +1051,14 @@ class Armv7Cpu(Cpu):
|
||||
|
||||
def _bitwise_instruction(cpu, operation, dest, op1, *op2):
|
||||
if op2:
|
||||
op2_val, carry = op2[0].read(withCarry=True)
|
||||
op2_val, carry = op2[0].read(with_carry=True)
|
||||
result = operation(op1.read(), op2_val)
|
||||
else:
|
||||
op1_val, carry = op1.read(withCarry=True)
|
||||
op1_val, carry = op1.read(with_carry=True)
|
||||
result = operation(op1_val)
|
||||
if dest is not None:
|
||||
dest.write(result)
|
||||
cpu.setFlags(C=carry, N=HighBit(result), Z=(result == 0))
|
||||
cpu.set_flags(C=carry, N=HighBit(result), Z=(result == 0))
|
||||
|
||||
@instruction
|
||||
def ORR(cpu, dest, op1, op2=None):
|
||||
@@ -1077,13 +1091,13 @@ class Armv7Cpu(Cpu):
|
||||
@instruction
|
||||
def TEQ(cpu, *operands):
|
||||
cpu._bitwise_instruction(lambda x, y: x ^ y, None, *operands)
|
||||
cpu.commitFlags()
|
||||
cpu.commit_flags()
|
||||
|
||||
@instruction
|
||||
def TST(cpu, Rn, Rm):
|
||||
shifted, carry = Rm.read(withCarry=True)
|
||||
shifted, carry = Rm.read(with_carry=True)
|
||||
result = Rn.read() & shifted
|
||||
cpu.setFlags(N=HighBit(result), Z=(result==0), C=carry)
|
||||
cpu.set_flags(N=HighBit(result), Z=(result == 0), C=carry)
|
||||
|
||||
@instruction
|
||||
def SVC(cpu, op):
|
||||
@@ -1097,14 +1111,14 @@ class Armv7Cpu(Cpu):
|
||||
return result, carry, overflow
|
||||
|
||||
def _SR(cpu, insn_id, dest, op, *rest):
|
||||
'''
|
||||
"""
|
||||
Notes on Capstone behavior:
|
||||
- In ARM mode, _SR reg has `rest`, but _SR imm does not, its baked into `op`.
|
||||
- In ARM mode, `lsr r1, r2` will have a `rest[0]`
|
||||
- In Thumb mode, `lsr r1, r2` will have an empty `rest`
|
||||
- In ARM mode, something like `lsr r1, 3` will not have `rest` and op will be
|
||||
the immediate.
|
||||
'''
|
||||
"""
|
||||
assert insn_id in (cs.arm.ARM_INS_ASR, cs.arm.ARM_INS_LSL, cs.arm.ARM_INS_LSR)
|
||||
|
||||
if insn_id == cs.arm.ARM_INS_ASR:
|
||||
@@ -1124,19 +1138,19 @@ class Armv7Cpu(Cpu):
|
||||
srtype = cs.arm.ARM_SFT_LSR_REG
|
||||
|
||||
carry = cpu.regfile.read('APSR_C')
|
||||
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':
|
||||
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)
|
||||
result, carry = cpu._shift(op.read(), srtype, amount, carry)
|
||||
elif cpu.mode == cs.CS_MODE_THUMB:
|
||||
result, carry = cpu._Shift(dest.read(), srtype, op, carry)
|
||||
result, carry = cpu._shift(dest.read(), srtype, op, carry)
|
||||
else:
|
||||
result, carry = op.read(withCarry=True)
|
||||
result, carry = op.read(with_carry=True)
|
||||
dest.write(result)
|
||||
|
||||
cpu.setFlags(N=HighBit(result), Z=(result==0), C=carry)
|
||||
cpu.set_flags(N=HighBit(result), Z=(result == 0), C=carry)
|
||||
|
||||
@instruction
|
||||
def ASR(cpu, dest, op, *rest):
|
||||
@@ -1152,10 +1166,10 @@ class Armv7Cpu(Cpu):
|
||||
|
||||
@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)
|
||||
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))
|
||||
cpu.set_flags(N=Bit(result, 63), Z=(result == 0))
|
||||
|
||||
@instruction
|
||||
def MUL(cpu, dest, src1, src2):
|
||||
@@ -1164,10 +1178,10 @@ class Armv7Cpu(Cpu):
|
||||
op2 = SInt(src2.read(), width)
|
||||
result = op1 * op2
|
||||
dest.write(result & Mask(width))
|
||||
cpu.setFlags(N=HighBit(result), Z=(result==0))
|
||||
cpu.set_flags(N=HighBit(result), Z=(result == 0))
|
||||
|
||||
@instruction
|
||||
def MVN(cpu,dest, op):
|
||||
def MVN(cpu, dest, op):
|
||||
cpu._bitwise_instruction(lambda x: ~x, dest, op)
|
||||
|
||||
@instruction
|
||||
@@ -1179,7 +1193,7 @@ class Armv7Cpu(Cpu):
|
||||
result = op1_val * op2_val + add_val
|
||||
|
||||
dest.write(result & Mask(cpu.address_bit_size))
|
||||
cpu.setFlags(N=HighBit(result), Z=(result==0))
|
||||
cpu.set_flags(N=HighBit(result), Z=(result == 0))
|
||||
|
||||
@instruction
|
||||
def BIC(cpu, dest, op1, op2=None):
|
||||
@@ -1188,12 +1202,12 @@ class Armv7Cpu(Cpu):
|
||||
else:
|
||||
result = (dest.read() & ~op1.read()) & Mask(cpu.address_bit_size)
|
||||
dest.write(result)
|
||||
cpu.setFlags(N=HighBit(result), Z=(result==0))
|
||||
cpu.set_flags(N=HighBit(result), Z=(result == 0))
|
||||
|
||||
def _VSTM(cpu, address, *regs):
|
||||
for reg in regs:
|
||||
cpu.write_int(address, reg.read(), reg.size)
|
||||
address += reg.size/8
|
||||
address += reg.size / 8
|
||||
|
||||
return address
|
||||
|
||||
@@ -1220,15 +1234,13 @@ class Armv7Cpu(Cpu):
|
||||
|
||||
@instruction
|
||||
def DMB(cpu, *operands):
|
||||
'''
|
||||
"""
|
||||
Used by the the __kuser_dmb ARM Linux user-space handler. This is a nop
|
||||
under Manticore's memory and execution model.
|
||||
'''
|
||||
"""
|
||||
pass
|
||||
|
||||
@instruction
|
||||
def LDCL(cpu, *operands):
|
||||
'''
|
||||
Occasionally used in glibc (longjmp in ld.so). Nop under our execution model.
|
||||
'''
|
||||
"""Occasionally used in glibc (longjmp in ld.so). Nop under our execution model."""
|
||||
pass
|
||||
|
||||
@@ -1433,7 +1433,7 @@ class Armv7CpuInstructions(unittest.TestCase):
|
||||
self._checkFlagsNZCV(1, 0, 0, 0)
|
||||
|
||||
def test_flag_state_continuity(self):
|
||||
'''If an instruction only partially updates flags, cpu.setFlags should
|
||||
'''If an instruction only partially updates flags, cpu.set_flags should
|
||||
ensure unupdated flags are preserved.
|
||||
|
||||
For example:
|
||||
|
||||
@@ -1212,7 +1212,7 @@ class Armv7UnicornInstructions(unittest.TestCase):
|
||||
self.assertEqual(self.rf.read('R2'), 2)
|
||||
|
||||
def test_flag_state_continuity(self):
|
||||
'''If an instruction only partially updates flags, cpu.setFlags should
|
||||
'''If an instruction only partially updates flags, cpu.set_flags should
|
||||
ensure unupdated flags are preserved.
|
||||
|
||||
For example:
|
||||
|
||||
Reference in New Issue
Block a user