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:
Disconnect3d
2018-02-17 17:51:58 +01:00
committed by Mark Mossberg
parent c6f457d72e
commit 680fc8f17e
3 changed files with 193 additions and 181 deletions
+191 -179
View File
@@ -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
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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: