Dev arm regfile refactor (#57)
* RegisterFile refactor everywhere * Update arm_rf tests * Refactor Operand in both x86 and arm * Add APSR support (#53) * Add APSR support * #issuecomment-284826572 * APSR tests * cspr to aspr * RegisterFile refactor everywhere * Update arm_rf tests * Refactor Operand in both x86 and arm * Rebased and NotImplementedError fix * ARM register initialization compressed * Fix merging bugs * Assimilate PR comments * Assimilate PR comments2 * The new style. future is now * WIP * Improve quick start ,add asciinema (#58) * Towards a helloworld world * Rm requirements.txt (#56) * Print workspace dir by default (#55) * Print workspace dir by default * Change initial cli output * refactor or or or * WIP * Add arm rf testcase * ARM aliases fix * debug print removed * fix tests * Remove unicorn script from travis build (#64) * remove unicorn script from travis build * remove unicorn script entirely * Rename test -> tests (#66) * rename test -> tests * re-add ignored tests * Switch instructions to prefer virtualenv (#65) * switch instructions to prefer virtualenv * document use of virtualenvwrapper * Make cpuid more forgiving (#67) * Make cpuid more forgiving * error() to warning() * Add CPUID EAX=0xd ECX=0,1 * Let logger handle the format string iff needed * to hex * Rm unused files (#76) * Move state into own file (#75) * Move State from executor.py * Fix import * relative import * Rm unnecessary code (#80) * Add example crackme (#77) * add example crackme * make filename and directory structure more accurate * Fix state.branches (#74) * init * rm old code pretty sure it's wrong * Clean * rm * Rename record_fork, move to ConcretizeRegister exception handler It doesn't need to be in fork() because it's only relevant for when we fork due to PC. * Add test for record_branches * Add back record_branches which got removed in rebase Rebase went oddly because State got moved to a new file * unnecesary tuple() * necesary tuple * remove unnecessary keys() * Use OrderedDict (#84) It can be useful to know elements in `branches` towards the end are more recently discovered branches * arm: Fix broken dynamic APSR (#78) * Refactor Register (#82) * Fix register read bug If we store a BitVec, we do NOT want to return bool() of it, which returns True, which is totally incorrect. We do however, want to return a symbolic Bool of it, if it is a 1 bit register (flag). * Improve * Raise error * Fix register tests Changed the interfaces: - removed nbits param (never used) - no longer raise AssertionError when overflowing a flag reg, just truncate - rename test funcs to be more descriptive * rm unused import * Add symbolic tests * Rm no longer applicable test * Add docs (#61) * Add sphinx-quickstart generated docs * Add api.rst * Edit index.rst * Add autodocumenting Manticore class * Update Manticore docstring * Doc verbosity * Doc hook and add_hook * More docs * Add Sphinx dev dep For building docs * RegisterFile refactor everywhere * Update arm_rf tests * Refactor Operand in both x86 and arm * RegisterFile refactor everywhere * Refactor Operand in both x86 and arm * Rebased and NotImplementedError fix * ARM register initialization compressed * Fix merging bugs * Assimilate PR comments * Assimilate PR comments2 * The new style. future is now * WIP * Towards a helloworld world * refactor or or or * WIP * Add arm rf testcase * ARM aliases fix * debug print removed * fix tests * unnecesary tuple() * necesary tuple * remove unnecessary keys() * rebased * Remove test * https://github.com/trailofbits/manticore/pull/57#pullrequestreview-27971778 * https://github.com/trailofbits/manticore/pull/57#discussion_r107820815, https://github.com/trailofbits/manticore/pull/57#discussion_r107820331 * https://github.com/trailofbits/manticore/pull/57#discussion_r107821090 * https://github.com/trailofbits/manticore/pull/57#discussion_r107821066 * https://github.com/trailofbits/manticore/pull/57#discussion_r107821919 * OOps search and replace * (invalid) -> None * The (invalid) -> None * None vs. invalid * In armpy we know that STACK is SP so lets try to eliminate cpu.STACK in favor of x.SP * remove ugly hex * Removed redundant import and comment
This commit is contained in:
@@ -26,19 +26,16 @@ MU = {
|
||||
SANE_SIZES = {8, 16, 32, 64, 80, 128, 256}
|
||||
# This encapsulates how to acccess operands (regs/mem/immediates) for differents cpus
|
||||
class Operand(object):
|
||||
__metaclass__ = ABCMeta
|
||||
def _reg_name(self, reg_id):
|
||||
return reg_id
|
||||
|
||||
class MemSpec(object):
|
||||
''' Auxiliary class wraps capstone operand 'mem' attribute. This will return register names instead of Ids '''
|
||||
def __init__(self, parent):
|
||||
self.parent = parent
|
||||
segment = property( lambda self: self.parent._reg_name(self.parent.op.mem.segment) )
|
||||
base = property( lambda self: self.parent._reg_name(self.parent.op.mem.base) )
|
||||
index = property( lambda self: self.parent._reg_name(self.parent.op.mem.index) )
|
||||
scale = property( lambda self: self.parent._reg_name(self.parent.op.mem.scale) )
|
||||
disp = property( lambda self: self.parent._reg_name(self.parent.op.mem.disp) )
|
||||
|
||||
scale = property( lambda self: self.parent.op.mem.scale )
|
||||
disp = property( lambda self: self.parent.op.mem.disp )
|
||||
|
||||
def __init__(self, cpu, op, **kwargs):
|
||||
'''
|
||||
@@ -50,87 +47,84 @@ class Operand(object):
|
||||
@param cpu: A Cpu oinstance
|
||||
@param op: a Capstone operand (eew)
|
||||
'''
|
||||
self.cpu=cpu
|
||||
self.op=op
|
||||
if op.type == X86_OP_MEM:
|
||||
self.mem = self.__class__.MemSpec(self)
|
||||
assert isinstance(cpu, Cpu)
|
||||
assert isinstance(op, (X86Op, ArmOp))
|
||||
self.cpu = cpu
|
||||
self.op = op
|
||||
self.mem = Operand.MemSpec(self)
|
||||
|
||||
def _reg_name(self, reg_id):
|
||||
''' Translates a capstone register ID into the register name '''
|
||||
cs_reg_name = self.cpu.instruction.reg_name(reg_id)
|
||||
if cs_reg_name is None or cs_reg_name.lower() == '(invalid)':
|
||||
return None
|
||||
return self.cpu._regfile._alias(cs_reg_name.upper())
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.op, name)
|
||||
|
||||
@abstractmethod
|
||||
@property
|
||||
def size(self):
|
||||
''' Return bit size of operand '''
|
||||
raise NotImplementedError
|
||||
|
||||
def address(self):
|
||||
''' On a memory operand it returns the effective address '''
|
||||
pass
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def read(self):
|
||||
''' It reads the operand value from the registers or memory '''
|
||||
pass
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def write(self, value):
|
||||
''' It writes the value ofspecific type to the registers or memory '''
|
||||
pass
|
||||
raise NotImplementedError
|
||||
|
||||
# Basic register file structure not actully need to abstract as it's used only from the cpu implementation
|
||||
class RegisterFile(object):
|
||||
|
||||
|
||||
def __init__(self, aliases=None):
|
||||
if aliases is None:
|
||||
aliases = {}
|
||||
self._aliases = aliases
|
||||
''''dict mapping from alias register name ('PC') to actual register name
|
||||
('RSP'), which can be passed into reg_id()
|
||||
'''
|
||||
''''dict mapping from alias register name ('PC') to actual register name ('RIP') '''
|
||||
|
||||
def _alias(self, register):
|
||||
'''Get register canonical alias. ex. PC->RIP or PC->R15 '''
|
||||
return self._aliases.get(register, register)
|
||||
|
||||
#@abstractmethod
|
||||
def write(self, reg_id, value):
|
||||
''' Write value to the register reg_id
|
||||
@param reg_id: a register id. Must be listed on all_registers
|
||||
def write(self, register, value):
|
||||
''' Write value to the specified register
|
||||
@param register: a register id. Must be listed on all_registers
|
||||
@param value: a value of the expected type
|
||||
@return the value actually written to the register
|
||||
'''
|
||||
pass
|
||||
|
||||
#@abstractmethod
|
||||
def read(self, reg_id):
|
||||
''' Read value from the register identified by reg_id
|
||||
@param reg_id: a register id. Must be listed on all_registers
|
||||
def read(self, register):
|
||||
''' Read value from specified register
|
||||
@param register: a register name. Must be listed on all_registers
|
||||
@return the register value
|
||||
'''
|
||||
pass
|
||||
|
||||
#@abstractmethod
|
||||
def reg_name(self, reg_id):
|
||||
''' Gives a string representation (name) of a register (ID->name)
|
||||
@param reg_id: a register ID
|
||||
'''
|
||||
pass
|
||||
|
||||
#@abstractmethod
|
||||
def reg_id(self, reg_name):
|
||||
''' Gives the register ID for a string representation of a register (name->ID)
|
||||
@param reg_name: a string representation of reg_id register'''
|
||||
pass
|
||||
|
||||
@property
|
||||
def all_registers(self):
|
||||
''' Lists all possible register names (Including aliases) '''
|
||||
pass
|
||||
return tuple(self._aliases)
|
||||
|
||||
@property
|
||||
def canonical_registers(self):
|
||||
''' List the minimal most beautiful set of registers needed '''
|
||||
pass
|
||||
|
||||
def __contains__(self, reg_id):
|
||||
def __contains__(self, register):
|
||||
''' Check for register validity
|
||||
@param reg_id: a register ID
|
||||
@param register: a register name
|
||||
'''
|
||||
return reg_id in self.all_registers
|
||||
|
||||
return self._alias(register) in self.all_registers
|
||||
############################################################################
|
||||
# Abstract cpu encapsulating common cpu methods used by models and executor.
|
||||
class Cpu(object):
|
||||
@@ -192,43 +186,36 @@ class Cpu(object):
|
||||
return self._regfile.all_registers
|
||||
|
||||
#this operates on names
|
||||
def write_register(self, name, value):
|
||||
def write_register(self, register, value):
|
||||
''' A convenient method to write a register by name (this accepts alias)
|
||||
@param name a register name as listed in all_registers
|
||||
@param register a register name as listed in all_registers
|
||||
@param value a value
|
||||
@return It will return the written value possibly croped
|
||||
'''
|
||||
reg_id = self._regfile.reg_id(name)
|
||||
return self._regfile.write(reg_id, value)
|
||||
return self._regfile.write(register, value)
|
||||
|
||||
def read_register(self, name):
|
||||
def read_register(self, register):
|
||||
''' A convenient method to read a register by name (this accepts alias)
|
||||
@param name a register name as listed in all_registers
|
||||
@param register a register name as listed in all_registers
|
||||
@param value a value
|
||||
@return It will return the written value possibly croped
|
||||
'''
|
||||
reg_id = self._regfile.reg_id(name)
|
||||
return self._regfile.read(reg_id)
|
||||
return self._regfile.read(register)
|
||||
|
||||
# Pythonic acces to registers and aliases
|
||||
def __getattr__(self, name):
|
||||
''' A pythonic version of read_register '''
|
||||
assert name != '_regfile'
|
||||
if hasattr(self, '_regfile') and name in self.all_registers:
|
||||
if hasattr(self, '_regfile') and name in self._regfile:
|
||||
return self.read_register(name)
|
||||
|
||||
raise AttributeError(name)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
''' A pythonic version of write_register '''
|
||||
if hasattr(self, '_regfile') and name in self.all_registers:
|
||||
if hasattr(self, '_regfile') and name in self._regfile:
|
||||
return self.write_register(name, value)
|
||||
object.__setattr__(self, name, value)
|
||||
|
||||
def getCanonicalRegisters(self):
|
||||
values = [self.read_register(rname) for rname in self.canonical_registers]
|
||||
d = dict(zip(self.canonical_registers, values))
|
||||
return d
|
||||
|
||||
#############################
|
||||
# Memory access
|
||||
@@ -372,9 +359,7 @@ class Cpu(object):
|
||||
if logger.level == logging.DEBUG :
|
||||
for l in str(self).split('\n'):
|
||||
logger.debug(l)
|
||||
|
||||
implementation(*instruction.operands)
|
||||
|
||||
self._icount+=1
|
||||
|
||||
@abstractmethod
|
||||
@@ -533,7 +518,7 @@ class InvalidPCException(Exception):
|
||||
super(InvalidPCException, self).__init__("Trying to execute invalid memory @%08x"%pc)
|
||||
self.pc=pc
|
||||
|
||||
class InstructionNotImplemented(Exception):
|
||||
class InstructionNotImplementedError(Exception):
|
||||
''' Exception raised when you try to execute an instruction that is
|
||||
not yet implemented in the emulator.
|
||||
Go to cpu.py and add it!
|
||||
|
||||
+111
-263
@@ -12,25 +12,6 @@ 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
|
||||
|
||||
|
||||
|
||||
# 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")
|
||||
@@ -70,28 +51,31 @@ class Armv7Operand(Operand):
|
||||
def __init__(self, cpu, op, **kwargs):
|
||||
super(Armv7Operand, self).__init__(cpu, op, **kwargs)
|
||||
|
||||
@property
|
||||
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
|
||||
return 64
|
||||
else:
|
||||
return 4
|
||||
#FIXME check other types of operand sizes
|
||||
return 32
|
||||
|
||||
def read(self, nbits=None, withCarry=False):
|
||||
carry = self.cpu.regfile.read(ARM_REG_APSR_C)
|
||||
carry = self.cpu.regfile.read('APSR_C')
|
||||
if self.op.type == ARM_OP_REG:
|
||||
reg = self.cpu.regfile.read(self.op.reg)
|
||||
register = self._reg_name(self.op.reg)
|
||||
value = self.cpu.regfile.read(register)
|
||||
# XXX This can be an offset of 8, depending on ARM mode
|
||||
if self.op.reg == ARM_REG_R15:
|
||||
reg += 4
|
||||
if register in ('PC', 'R15'):
|
||||
value += 4
|
||||
if self.is_shifted():
|
||||
shift = self.op.shift
|
||||
reg, carry = self.cpu._Shift(reg, shift.type, shift.value, carry)
|
||||
value, carry = self.cpu._Shift(value, shift.type, shift.value, carry)
|
||||
if self.op.subtracted:
|
||||
reg = -reg
|
||||
value = -value
|
||||
if withCarry:
|
||||
return reg, carry
|
||||
return reg
|
||||
return value, carry
|
||||
return value
|
||||
elif self.op.type == ARM_OP_IMM:
|
||||
imm = self.op.imm
|
||||
if self.op.subtracted:
|
||||
@@ -110,7 +94,8 @@ class Armv7Operand(Operand):
|
||||
|
||||
def write(self, value, nbits=None):
|
||||
if self.op.type == ARM_OP_REG:
|
||||
self.cpu.regfile.write(self.op.reg, value)
|
||||
register = self._reg_name(self.op.reg)
|
||||
self.cpu.regfile.write(register, value)
|
||||
elif self.op.type == ARM_OP_MEM:
|
||||
raise NotImplementedError('need to impl arm store mem')
|
||||
else:
|
||||
@@ -120,7 +105,8 @@ class Armv7Operand(Operand):
|
||||
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)
|
||||
register = self._reg_name(self.op.mem.base)
|
||||
self.cpu.regfile.write(register, value)
|
||||
else:
|
||||
raise NotImplementedError("writeback Operand unknown type", self.op.type)
|
||||
|
||||
@@ -136,29 +122,27 @@ class Armv7Operand(Operand):
|
||||
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.mem.index is not None:
|
||||
idx = self.mem.scale * self.cpu.regfile.read(self.mem.index)
|
||||
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)
|
||||
off = idx
|
||||
else:
|
||||
off = mem.disp
|
||||
off = self.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)
|
||||
base = self.cpu.regfile.read(self.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
|
||||
return base+4 if self.mem.base in ('PC', 'R15') else base
|
||||
|
||||
def _getExpandImmCarry(self, carryIn):
|
||||
'''Manually compute the carry bit produced by expanding an immediate
|
||||
@@ -172,90 +156,47 @@ class Armv7Operand(Operand):
|
||||
|
||||
|
||||
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
|
||||
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'):
|
||||
self._regs[reg_name] = Register(64)
|
||||
#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)
|
||||
|
||||
def _read_APSR(self):
|
||||
N = self.read(ARM_REG_APSR_N)
|
||||
Z = self.read(ARM_REG_APSR_Z)
|
||||
C = self.read(ARM_REG_APSR_C)
|
||||
V = self.read(ARM_REG_APSR_V)
|
||||
apsr = 0
|
||||
|
||||
def make_apsr_flag(flag_expr, offset):
|
||||
'Helper for constructing an expression for the APSR register'
|
||||
return Operators.ITEBV(32, flag_expr,
|
||||
BitVecConstant(32, 1 << offset),
|
||||
BitVecConstant(32, 0))
|
||||
apsr = 0
|
||||
N = self.read('APSR_N')
|
||||
Z = self.read('APSR_Z')
|
||||
C = self.read('APSR_C')
|
||||
V = self.read('APSR_V')
|
||||
|
||||
if any(issymbolic(x) for x in [N, Z, C, V]):
|
||||
apsr = (make_apsr_flag(N, 31) |
|
||||
make_apsr_flag(Z, 30) |
|
||||
@@ -268,50 +209,44 @@ class Armv7RegisterFile(RegisterFile):
|
||||
if V: apsr |= 1 << 28
|
||||
return apsr
|
||||
|
||||
|
||||
def _write_APSR(self, apsr):
|
||||
''' Auxiliar 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)
|
||||
N = Operators.EXTRACT(apsr, 31, 1)
|
||||
|
||||
self.write(ARM_REG_APSR_V, V)
|
||||
self.write(ARM_REG_APSR_C, C)
|
||||
self.write(ARM_REG_APSR_Z, Z)
|
||||
self.write(ARM_REG_APSR_N, N)
|
||||
self.write('APSR_V', V)
|
||||
self.write('APSR_C', C)
|
||||
self.write('APSR_Z', Z)
|
||||
self.write('APSR_N', N)
|
||||
|
||||
|
||||
def read(self, reg_id):
|
||||
if reg_id == ARM_REG_APSR:
|
||||
def read(self, register):
|
||||
assert register in self
|
||||
if register == 'APSR':
|
||||
return self._read_APSR()
|
||||
return self.regs[self.REGMAP[reg_id]].read()
|
||||
register = self._alias(register)
|
||||
return self._regs[register].read()
|
||||
|
||||
def write(self, reg_id, val):
|
||||
if reg_id == ARM_REG_APSR:
|
||||
return self._write_APSR(val)
|
||||
reg_offset = self.REGMAP[reg_id]
|
||||
reg = self.regs[reg_offset]
|
||||
reg.write(val)
|
||||
def write(self, register, value):
|
||||
assert register in self
|
||||
if register == 'APSR':
|
||||
return self._write_APSR(value)
|
||||
register = self._alias(register)
|
||||
self._regs[register].write(value)
|
||||
|
||||
@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',
|
||||
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_N','APSR_Z','APSR_C','APSR_V') + ('STACK','PC','SP','LR')
|
||||
'D21','D22','D23','D24','D25','D26','D27','D28','D29','D30','D31','APSR','APSR_N','APSR_Z','APSR_C','APSR_V')
|
||||
|
||||
@property
|
||||
def canonical_registers(self):
|
||||
return ('R0','R1','R2','R3','R4','R5','R6','R7','R8','R9','R10','R11','R12', 'SP', 'LR', 'PC', 'APSR')
|
||||
return ('R0','R1','R2','R3','R4','R5','R6','R7','R8','R9','R10','R11','R12','R13','R14','R15','APSR')
|
||||
|
||||
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
|
||||
@@ -361,22 +296,6 @@ class Armv7Cpu(Cpu):
|
||||
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.
|
||||
@@ -395,7 +314,7 @@ class Armv7Cpu(Cpu):
|
||||
'''
|
||||
unupdated_flags = self._last_flags.viewkeys() - flags.viewkeys()
|
||||
for flag in unupdated_flags:
|
||||
flag_name = globals()['ARM_REG_APSR_%s'%(flag,)]
|
||||
flag_name = 'APSR_{}'.format(flag)
|
||||
self._last_flags[flag] = self.regfile.read(flag_name)
|
||||
self._last_flags.update(flags)
|
||||
|
||||
@@ -404,7 +323,7 @@ class Armv7Cpu(Cpu):
|
||||
if self.instruction.mnemonic == 'adc':
|
||||
return
|
||||
for flag, val in self._last_flags.iteritems():
|
||||
flag_name = globals()['ARM_REG_APSR_%s'%(flag,)]
|
||||
flag_name = 'APSR_{}'.format(flag)
|
||||
self.regfile.write(flag_name, val)
|
||||
|
||||
|
||||
@@ -416,7 +335,8 @@ class Armv7Cpu(Cpu):
|
||||
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):
|
||||
elif _type in range(ARM_SFT_ASR_REG, ARM_SFT_RRX_REG + 1):
|
||||
amount = cpu.instruction.reg_name(amount).upper()
|
||||
amount = Operators.EXTRACT(cpu.regfile.read(amount), 0, 8)
|
||||
|
||||
if amount == 0:
|
||||
@@ -441,29 +361,29 @@ class Armv7Cpu(Cpu):
|
||||
# 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)
|
||||
self.SP -= self.address_bit_size/8
|
||||
self.write_int(self.SP, data, self.address_bit_size)
|
||||
elif isinstance(data, BitVec):
|
||||
self.stack_sub(data.size/8)
|
||||
self.write_int(self.stack_get(), data, data.size)
|
||||
self.SP -= data.size/8
|
||||
self.write_int(self.SP, data, data.size)
|
||||
elif isinstance(data, str):
|
||||
self.stack_sub(len(data))
|
||||
self.write(self.stack_get(), data)
|
||||
self.SP -= len(data)
|
||||
self.write(self.SP, data)
|
||||
else:
|
||||
raise NotImplementedError('unsupported type for stack push data')
|
||||
return self.stack_get()
|
||||
return self.SP
|
||||
|
||||
def stack_peek(self, nbytes=4):
|
||||
return self.read(self.stack_get(), nbytes)
|
||||
return self.read(self.SP, 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)
|
||||
val = self.read_int(self.SP, nbits)
|
||||
else:
|
||||
val = self.read(self.stack_get(), nbytes)
|
||||
self.stack_add(nbytes)
|
||||
val = self.read(self.SP, nbytes)
|
||||
self.SP += nbytes
|
||||
return val
|
||||
|
||||
def read(self, addr, nbytes):
|
||||
@@ -485,28 +405,6 @@ class Armv7Cpu(Cpu):
|
||||
return 'ASR'
|
||||
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]
|
||||
|
||||
@@ -524,10 +422,10 @@ class Armv7Cpu(Cpu):
|
||||
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)
|
||||
C = cpu.regfile.read('APSR_C')
|
||||
N = cpu.regfile.read('APSR_N')
|
||||
V = cpu.regfile.read('APSR_V')
|
||||
Z = cpu.regfile.read('APSR_Z')
|
||||
|
||||
if cc == ARM_CC_AL: ret = True
|
||||
elif cc == ARM_CC_EQ: ret = Z
|
||||
@@ -553,58 +451,23 @@ class Armv7Cpu(Cpu):
|
||||
|
||||
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(issymbolic(x) 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)
|
||||
index = cpu.regfile.read('R7')
|
||||
|
||||
arg_indeces = [globals()['ARM_REG_R%d' % i] for i in range(0, 7)]
|
||||
arg_indeces = ['R{}'.format(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)
|
||||
cpu.regfile.write('R0', result)
|
||||
|
||||
return (index, arguments, writeResult)
|
||||
|
||||
def getSyscallResult(self):
|
||||
return self.regfile.read(ARM_REG_R0)
|
||||
return self.regfile.read('R0')
|
||||
|
||||
@instruction
|
||||
def MOV(cpu, dest, src):
|
||||
@@ -707,7 +570,7 @@ class Armv7Cpu(Cpu):
|
||||
|
||||
@instruction
|
||||
def ADC(cpu, dest, op1, op2):
|
||||
carry = cpu.regfile.read(ARM_REG_APSR_C)
|
||||
carry = cpu.regfile.read('APSR_C')
|
||||
result, carry, overflow = cpu._ADD(op1.read(), op2.read(), carry)
|
||||
dest.write(result)
|
||||
return result, carry, overflow
|
||||
@@ -732,7 +595,7 @@ class Armv7Cpu(Cpu):
|
||||
|
||||
@instruction
|
||||
def SBC(cpu, dest, src, add):
|
||||
carry = cpu.regfile.read(ARM_REG_APSR_C)
|
||||
carry = cpu.regfile.read('APSR_C')
|
||||
result, carry, overflow = cpu._ADD(src.read(), ~add.read(), carry)
|
||||
dest.write(result)
|
||||
return result, carry, overflow
|
||||
@@ -749,13 +612,13 @@ class Armv7Cpu(Cpu):
|
||||
@instruction
|
||||
def BLE(cpu, dest):
|
||||
cpu.PC = Operators.ITEBV(cpu.address_bit_size,
|
||||
cpu.regfile.read(ARM_REG_APSR_Z), dest.read(), cpu.PC)
|
||||
cpu.regfile.read('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())
|
||||
next_instr_addr = cpu.regfile.read('PC')
|
||||
cpu.regfile.write('LR', next_instr_addr)
|
||||
cpu.regfile.write('PC', label.read())
|
||||
|
||||
|
||||
@instruction
|
||||
@@ -765,9 +628,9 @@ class Armv7Cpu(Cpu):
|
||||
## 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)
|
||||
next_instr_addr = cpu.regfile.read('PC') #- 2
|
||||
cpu.regfile.write('LR', next_instr_addr) # | 1)
|
||||
cpu.regfile.write('PC', target & ~1)
|
||||
|
||||
@instruction
|
||||
def CMP(cpu, reg, cmp):
|
||||
@@ -776,28 +639,14 @@ class Armv7Cpu(Cpu):
|
||||
|
||||
@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())
|
||||
|
||||
|
||||
@@ -927,10 +776,9 @@ class Armv7Cpu(Cpu):
|
||||
elif insn_id == ARM_INS_LSR:
|
||||
srtype = ARM_SFT_LSR_REG
|
||||
|
||||
carry = cpu.regfile.read(ARM_REG_APSR_C)
|
||||
carry = cpu.regfile.read('APSR_C')
|
||||
if rest:
|
||||
amount_val = rest[0].op.reg
|
||||
result, carry = cpu._Shift(op.read(), srtype, amount_val, carry)
|
||||
result, carry = cpu._Shift(op.read(), srtype, rest[0].op.reg, carry)
|
||||
else:
|
||||
result, carry = op.read(withCarry=True)
|
||||
dest.write(result)
|
||||
@@ -988,9 +836,8 @@ class Armv7Cpu(Cpu):
|
||||
|
||||
def _VSTM(cpu, address, *regs):
|
||||
for reg in regs:
|
||||
size = reg.size()
|
||||
cpu.write_int(address, reg.read(), size * 8)
|
||||
address += size
|
||||
cpu.write_int(address, reg.read(), reg.size)
|
||||
address += reg.size/8
|
||||
|
||||
return address
|
||||
|
||||
@@ -1010,3 +857,4 @@ class Armv7Cpu(Cpu):
|
||||
@instruction
|
||||
def STCL(cpu, *operands):
|
||||
pass
|
||||
|
||||
|
||||
+16
-31
@@ -443,16 +443,11 @@ class AMD64RegFile(RegisterFile):
|
||||
|
||||
for reg in ('FPSW', 'FPTAG', 'FPCW'):
|
||||
self._registers[reg] = 0
|
||||
|
||||
self._cache = {}
|
||||
for name in ('AF', 'CF', 'DF', 'IF', 'OF', 'PF', 'SF', 'ZF'):
|
||||
self.write(name, False)
|
||||
|
||||
def reg_name(self, reg_id):
|
||||
return reg_id
|
||||
|
||||
def reg_id(self, reg_name):
|
||||
return self._aliases.get(reg_name, reg_name)
|
||||
|
||||
@property
|
||||
def all_registers(self):
|
||||
return tuple( self._table.keys() +
|
||||
@@ -462,8 +457,8 @@ class AMD64RegFile(RegisterFile):
|
||||
def canonical_registers(self):
|
||||
return self._canonical_registers
|
||||
|
||||
def __contains__(self, reg_id):
|
||||
return reg_id in self.all_registers
|
||||
def __contains__(self, register):
|
||||
return register in self.all_registers
|
||||
|
||||
def _set_bv(self, register_id, register_size, offset, size, reset, value):
|
||||
if isinstance(value, (int,long)):
|
||||
@@ -550,9 +545,8 @@ class AMD64RegFile(RegisterFile):
|
||||
for flag, offset in self._flags.iteritems():
|
||||
self.write(flag, Operators.EXTRACT(res, offset, 1))
|
||||
|
||||
def write(self, reg_id, value):
|
||||
name = self.reg_name(reg_id)
|
||||
|
||||
def write(self, name, value):
|
||||
name = self._alias(name)
|
||||
if name in ('ST0', 'ST1', 'ST2', 'ST3', 'ST4', 'ST5', 'ST6', 'ST7'):
|
||||
name = 'FP%d' % ((self.read('TOP') + int(name[2]) ) & 7)
|
||||
|
||||
@@ -574,15 +568,14 @@ class AMD64RegFile(RegisterFile):
|
||||
self._update_cache(name, value)
|
||||
return value
|
||||
|
||||
def _update_cache(self,name, value):
|
||||
def _update_cache(self, name, value):
|
||||
self._cache[name] = value
|
||||
for affected in self._affects[name]:
|
||||
assert affected != name
|
||||
self._cache.pop(affected, None)
|
||||
|
||||
def read(self, reg_id):
|
||||
name = self.reg_name(reg_id)
|
||||
|
||||
def read(self, name):
|
||||
name = self._alias(name)
|
||||
if name in ('ST0', 'ST1', 'ST2', 'ST3', 'ST4', 'ST5', 'ST6', 'ST7'):
|
||||
name = 'FP%d' % ((self.read('TOP') + int(name[2]) ) & 7)
|
||||
if name in self._cache:
|
||||
@@ -605,24 +598,16 @@ class AMD64RegFile(RegisterFile):
|
||||
###########################
|
||||
# Operand Wrapper
|
||||
class AMD64Operand(Operand):
|
||||
def _reg_name(self, reg_id):
|
||||
if reg_id <= 0 :
|
||||
return '(invalid)'
|
||||
return self.cpu.instruction.reg_name(reg_id).upper()
|
||||
|
||||
''' This class deals with capstone X86 operands '''
|
||||
def __init__(self, cpu, op, **kwargs):
|
||||
super(AMD64Operand, self).__init__(cpu, op, **kwargs)
|
||||
self.cpu=cpu
|
||||
self.op=op
|
||||
if op.type == X86_OP_MEM:
|
||||
self.mem = AMD64Operand.MemSpec(self)
|
||||
|
||||
#################################3
|
||||
# Operand access
|
||||
def address(self):
|
||||
cpu, o = self.cpu, self.op
|
||||
address = 0
|
||||
if o.mem.segment != 0:
|
||||
if self.mem.segment is not None:
|
||||
seg = self.mem.segment
|
||||
base, size, ty = cpu.get_descriptor(cpu.read_register(seg))
|
||||
address += base #todo check limits and perms
|
||||
@@ -630,18 +615,18 @@ class AMD64Operand(Operand):
|
||||
#FIXME inspect operand or cpu.instruction and decide
|
||||
# the correct default segment for instruction
|
||||
seg = 'DS'
|
||||
if o.mem.base != 0 and self.mem.base in ['SP', 'ESP', 'EBP']:
|
||||
if self.mem.base is not None and self.mem.base in ['SP', 'ESP', 'EBP']:
|
||||
seg = 'SS'
|
||||
base, size, ty = cpu.get_descriptor(cpu.read_register(seg))
|
||||
address += base #todo check limits and perms
|
||||
if o.mem.base != 0:
|
||||
if self.mem.base is not None:
|
||||
base = self.mem.base
|
||||
address += cpu.read_register(base)
|
||||
if o.mem.index != 0:
|
||||
if self.mem.index is not None:
|
||||
index = self.mem.index
|
||||
address += o.mem.scale*cpu.read_register(index)
|
||||
if o.mem.disp != 0:
|
||||
address += o.mem.disp
|
||||
address += self.mem.scale*cpu.read_register(index)
|
||||
|
||||
address += self.mem.disp
|
||||
|
||||
return address & ((1<<cpu.address_bit_size)-1)
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ from ..core.cpu.arm import *
|
||||
from ..core.executor import SyscallNotImplemented, ProcessExit
|
||||
logger = logging.getLogger("MODEL")
|
||||
|
||||
|
||||
class RestartSyscall(Exception):
|
||||
pass
|
||||
|
||||
@@ -1125,10 +1126,10 @@ class Linux(object):
|
||||
@param path: the "link path id"
|
||||
@param buf: the buffer where the bytes will be putted.
|
||||
@param bufsize: the max size for read the link.
|
||||
@todo: Out eax number of bytes actually sent | EAGAIN | EBADF | EFAULT | EINTR | EINVAL | EIO | ENOSPC | EPIPE
|
||||
@todo: Out eax number of bytes actually sent | EAGAIN | EBADF | EFAULT | EINTR | errno.EINVAL | EIO | ENOSPC | EPIPE
|
||||
'''
|
||||
if bufsize <= 0:
|
||||
return -EINVAL
|
||||
return -errno.EINVAL
|
||||
filename = self._read_string(cpu, path)
|
||||
data = os.readlink(filename)[:bufsize]
|
||||
cpu.write_bytes(buf, data)
|
||||
@@ -1598,23 +1599,24 @@ class Linux(object):
|
||||
self.awake(procid)
|
||||
|
||||
def handleInvalidPC(self, e):
|
||||
#FIXME THIS IS ARM SPECIFIC
|
||||
cpu = self.current
|
||||
if cpu.PC == self.ARM_GET_TLS:
|
||||
if hasattr(self, 'tls_value'):
|
||||
cpu.regfile.write(ARM_REG_R0, self.tls_value)
|
||||
cpu.regfile.write('R0', self.tls_value)
|
||||
elif cpu.PC == self.ARM_CMPXCHG:
|
||||
oldval = cpu.regfile.read(ARM_REG_R0)
|
||||
newval = cpu.regfile.read(ARM_REG_R1)
|
||||
ptr = cpu.regfile.read(ARM_REG_R2)
|
||||
oldval = cpu.regfile.read('R0')
|
||||
newval = cpu.regfile.read('R1')
|
||||
ptr = cpu.regfile.read('R2')
|
||||
|
||||
existing = cpu.read_int(ptr, cpu.address_bit_size)
|
||||
ret = 1
|
||||
if existing == oldval:
|
||||
ret = 0
|
||||
cpu.regfile.write(ARM_REG_APSR_C, 1)
|
||||
cpu.regfile.write('APSR_C', 1)
|
||||
cpu.write_int(ptr, newval, cpu.address_bit_size)
|
||||
|
||||
cpu.regfile.write(ARM_REG_R0, ret)
|
||||
cpu.regfile.write('R0', ret)
|
||||
elif cpu.PC == self.ARM_MEM_BARRIER:
|
||||
# Apply any needed memory barrier to preserve consistency with data
|
||||
# modified manually and __kuser_cmpxchg usage. Nop in our case, just
|
||||
@@ -1624,7 +1626,7 @@ class Linux(object):
|
||||
raise e
|
||||
|
||||
# Return normally
|
||||
lr = cpu.regfile.read(ARM_REG_R14) # ARM_REG_LR
|
||||
lr = cpu.regfile.read('R14') # 'LR'
|
||||
cpu.PC = lr
|
||||
|
||||
|
||||
|
||||
+329
-348
File diff suppressed because it is too large
Load Diff
+45
-36
@@ -10,57 +10,66 @@ class Armv7RF(unittest.TestCase):
|
||||
self.r = RF()
|
||||
|
||||
def test_init_state(self):
|
||||
self.assertEqual(self.r.read(ARM_REG_R0), 0)
|
||||
self.assertEqual(self.r.read('R0'), 0)
|
||||
|
||||
def test_write_read(self):
|
||||
self.r.write(ARM_REG_R0, 1)
|
||||
self.assertEqual(self.r.read(ARM_REG_R0), 1)
|
||||
self.r.write('R0', 1)
|
||||
self.assertEqual(self.r.read('R0'), 1)
|
||||
|
||||
def test_write_read_sp(self):
|
||||
self.r.write(ARM_REG_SP, 1)
|
||||
self.assertEqual(self.r.read(ARM_REG_SP), 1)
|
||||
self.r.write('SP', 1)
|
||||
self.assertEqual(self.r.read('SP'), 1)
|
||||
|
||||
def test_flag_wr(self):
|
||||
self.r.write(ARM_REG_APSR_Z, True)
|
||||
self.assertEqual(self.r.read(ARM_REG_APSR_Z), True)
|
||||
self.r.write('APSR_Z', True)
|
||||
self.assertEqual(self.r.read('APSR_Z'), True)
|
||||
|
||||
def test_flag_wr_f(self):
|
||||
self.r.write(ARM_REG_APSR_Z, False)
|
||||
self.assertEqual(self.r.read(ARM_REG_APSR_Z), False)
|
||||
|
||||
def test_reg_name(self):
|
||||
self.assertEqual(self.r.reg_name(ARM_REG_R0), 'R0')
|
||||
self.r.write('APSR_Z', False)
|
||||
self.assertEqual(self.r.read('APSR_Z'), False)
|
||||
|
||||
def test_bad_reg_name(self):
|
||||
with self.assertRaises(KeyError):
|
||||
nonexistant_id = 9999
|
||||
self.r.reg_name(nonexistant_id)
|
||||
|
||||
def test_reg_id(self):
|
||||
self.assertEqual(self.r.reg_id('R0'), ARM_REG_R0)
|
||||
|
||||
def test_bad_reg_id(self):
|
||||
with self.assertRaises(KeyError):
|
||||
self.r.reg_id('XXX')
|
||||
with self.assertRaises(AssertionError):
|
||||
nonexistant_reg = "Pc"
|
||||
self.r.read(nonexistant_reg)
|
||||
|
||||
def test_flag_wr(self):
|
||||
self.r.write(ARM_REG_APSR, 0xffffffff)
|
||||
self.assertEqual(self.r.read(ARM_REG_APSR), 0xf0000000) #4 more significant bits used
|
||||
self.assertEqual(self.r.read(ARM_REG_APSR_V), True)
|
||||
self.assertEqual(self.r.read(ARM_REG_APSR_C), True)
|
||||
self.assertEqual(self.r.read(ARM_REG_APSR_Z), True)
|
||||
self.assertEqual(self.r.read(ARM_REG_APSR_N), True)
|
||||
self.r.write('APSR', 0xffffffff)
|
||||
self.assertEqual(self.r.read('APSR'), 0xf0000000) #4 more significant bits used
|
||||
self.assertEqual(self.r.read('APSR_V'), True)
|
||||
self.assertEqual(self.r.read('APSR_C'), True)
|
||||
self.assertEqual(self.r.read('APSR_Z'), True)
|
||||
self.assertEqual(self.r.read('APSR_N'), True)
|
||||
|
||||
self.r.write(ARM_REG_APSR_N, False)
|
||||
self.assertEqual(self.r.read(ARM_REG_APSR), 0x70000000)
|
||||
self.r.write('APSR_N', False)
|
||||
self.assertEqual(self.r.read('APSR'), 0x70000000)
|
||||
|
||||
self.r.write(ARM_REG_APSR_Z, False)
|
||||
self.assertEqual(self.r.read(ARM_REG_APSR), 0x30000000)
|
||||
self.r.write('APSR_Z', False)
|
||||
self.assertEqual(self.r.read('APSR'), 0x30000000)
|
||||
|
||||
self.r.write(ARM_REG_APSR_C, False)
|
||||
self.assertEqual(self.r.read(ARM_REG_APSR), 0x10000000)
|
||||
self.r.write('APSR_C', False)
|
||||
self.assertEqual(self.r.read('APSR'), 0x10000000)
|
||||
|
||||
self.r.write(ARM_REG_APSR_V, False)
|
||||
self.assertEqual(self.r.read(ARM_REG_APSR), 0x00000000)
|
||||
self.r.write('APSR_V', False)
|
||||
self.assertEqual(self.r.read('APSR'), 0x00000000)
|
||||
|
||||
def test_register_independence_wr(self):
|
||||
regs = ( 'R0', 'R1', 'R2', 'R3', 'R4', 'R5', 'R6', 'R7', 'R8',
|
||||
'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15' )
|
||||
aliases = {'SB':'R9', 'SL':'R10', 'FP':'R11', 'IP': 'R12', 'STACK': 'R13', 'SP': 'R13', 'LR': 'R14', 'PC': 'R15' }
|
||||
|
||||
for j in xrange(16):
|
||||
for i in xrange(16):
|
||||
if i == j:
|
||||
self.r.write(regs[i], 0x41424344)
|
||||
else:
|
||||
self.r.write(regs[i], 0)
|
||||
for a,b in aliases.items():
|
||||
self.assertEqual(self.r.read(a), self.r.read(b))
|
||||
|
||||
for i in xrange(16):
|
||||
if i == j:
|
||||
self.assertEqual(self.r.read(regs[i]), 0x41424344 )
|
||||
else:
|
||||
self.assertEqual(self.r.read(regs[i]), 0x00000000 )
|
||||
|
||||
|
||||
Reference in New Issue
Block a user