From 4136ceed1f9bfee86666cae3df02ec206072ca57 Mon Sep 17 00:00:00 2001 From: atlas0fd00m Date: Sat, 23 Mar 2024 19:49:07 -0400 Subject: [PATCH] Arm improvements (#631) * new emulation for orn instruction and unittest enhancement for archModXref mrs, msr register enhancements (supporting Cortex-M registers like BASEPRI) fix for a minor example_gui_extension.py bug (import bug) * enhancement for archModXref a few minor logging messages * tweaks (thanks unittests!) * improvements to ARM disasm, emu, and unittests :) * more touchups * touch-ups, thumb bugfixes, and unittests * more improvements: arm/thumb and unittests * bugfixes and test improvements * more updates and improvements for arm/thumb, especially MSR/MRS special registers * more work done on t32 and a32 MSR/MRS registers. more work to be done, and yet to have unittest parity with binja. but we're getting there. * more improvements for arm/thumb * dramatic improvements on ARM/THUMB unittests and decoding * unittest count and completion (almost. watch CPSR access unittest) * support None type registers for enum lists. this is important for ARM banked registers and the indirection required. * lots of work correcting register layouts and CPSR/SPSR functionality and unittests * tweak to make test_PSRs() pass :) * CPSR/SPSR correctness, and working unittests * initial changes per @rakuy0 * changes per @rakuy0 (and a little cleanup) * minor tweak * Symhints for "built" pointers * ADV SIMD LDST bugfixes for parsing VST/VLD type instructions * lots of VLD decode improvements * VLD all channels decoding implementation and unittests (and missed additions in Opers to support indexes and alignment) * check for MOVT and mark xrefs * cleanup and updates per @rakuy0 * update my musings with real docstrs * Apply suggestions from code review getAllReads/Writes support mods * cleanup errors for switchcase analysis for architectures which don't yet implemented a SymbolikTranslator. * updates to getAllReads() and getAllWrites, updating to use the actual node properties API, as well as documenting how the Paths work. * slight touchups * move the troubleshooting notes out of the unittests and into an ARM-specific note. * remove old comments --- envi/archs/arm/__init__.py | 5 +- envi/archs/arm/const.py | 37 +- envi/archs/arm/disasm.py | 778 +- envi/archs/arm/emu.py | 133 +- envi/archs/arm/regs.py | 203 +- envi/archs/thumb16/disasm.py | 645 +- envi/codeflow.py | 1 + envi/registers.py | 3 + envi/tests/test_arch_arm.py | 579 +- envi/tests/thumb_tests.py | 9075 ++++++++++++++++++ vivisect/__init__.py | 3 + vivisect/analysis/arm/emulation.py | 94 +- vivisect/analysis/generic/symswitchcase.py | 13 +- vivisect/base.py | 1 + vivisect/extensions/example_gui_extension.py | 4 +- vivisect/impemu/platarch/arm.py | 2 +- vivisect/notes/envi/arm/registers.md | 101 + vivisect/renderers/__init__.py | 2 +- vqt/common.py | 1 + 19 files changed, 11077 insertions(+), 603 deletions(-) create mode 100644 envi/tests/thumb_tests.py create mode 100644 vivisect/notes/envi/arm/registers.md diff --git a/envi/archs/arm/__init__.py b/envi/archs/arm/__init__.py index a52afbf61..eb9ad2d2c 100644 --- a/envi/archs/arm/__init__.py +++ b/envi/archs/arm/__init__.py @@ -71,8 +71,9 @@ def archModifyFuncAddr(self, va, info): return va & -2, {'arch' : envi.ARCH_THUMB} return va, info - def archModifyXrefAddr(self, tova, reftype, rflags): - if tova & 1: + def archModifyXrefAddr(self, tova, reftype, rflags=0): + # if rflags is non-zero, it indicates that the target is Code + if tova & 3 and rflags: return tova & -2, reftype, rflags return tova, reftype, rflags diff --git a/envi/archs/arm/const.py b/envi/archs/arm/const.py index 57f19830d..8c30ee148 100644 --- a/envi/archs/arm/const.py +++ b/envi/archs/arm/const.py @@ -83,6 +83,9 @@ ARCH_REVS['thumbee'] = REV_THUMBEE ARCH_REVSLEN = len(ARCH_REVS) +OP_SYMHINT_IDX = 5 + + # IFLAGS - keep bottom 8-bits for cross-platform flags like envi.IF_NOFALL and envi.IF_BRFALL IF_PSR_S = 1 << 32 # This DP instruciton can update CPSR (as in, add vs. adds) IF_B = 1 << 33 # Byte @@ -100,6 +103,7 @@ IF_THUMB32 = 1 << 50 # thumb32 IF_ADV_SIMD = 1 << 51 # Advanced SIMD instructions... it matters IF_SYS_MODE = 1 << 52 +IF_WIDE = 1 << 53 IF_DAIB_SHFT = 56 # shift-bits to get DAIB bits down to 0. this chops off the "is DAIB present" bit that the following store. IF_DAIB_MASK = 7 << (IF_DAIB_SHFT - 1) @@ -260,7 +264,7 @@ PM_und = 0b11011 PM_sys = 0b11111 -REGS_PER_MODE = 18 +REGS_PER_MODE = 19 # reg stuff stolen from regs.py to support proc_modes # these are in context of reg_table, not reg_data. @@ -274,18 +278,19 @@ REG_OFFSET_HYP = REGS_PER_MODE * (PM_hyp&0xf) REG_OFFSET_UND = REGS_PER_MODE * (PM_und&0xf) REG_OFFSET_SYS = REGS_PER_MODE * (PM_sys&0xf) -#REG_OFFSET_CPSR = REGS_PER_MODE * 16 -REG_OFFSET_CPSR = 16 # CPSR is available in every mode, and PM_usr and PM_sys don't have an SPSR. -REG_SPSR_usr = REG_OFFSET_USR + REGS_PER_MODE -REG_SPSR_fiq = REG_OFFSET_FIQ + REGS_PER_MODE -REG_SPSR_irq = REG_OFFSET_IRQ + REGS_PER_MODE -REG_SPSR_svc = REG_OFFSET_SVC + REGS_PER_MODE -REG_SPSR_mon = REG_OFFSET_MON + REGS_PER_MODE -REG_SPSR_abt = REG_OFFSET_ABT + REGS_PER_MODE -REG_SPSR_hyp = REG_OFFSET_HYP + REGS_PER_MODE -REG_SPSR_und = REG_OFFSET_UND + REGS_PER_MODE -REG_SPSR_sys = REG_OFFSET_SYS + REGS_PER_MODE +REG_OFFSET_CPSR = 16 # CPSR is available in every mode, and PM_usr and PM_sys don't have an SPSR. +REG_OFFSET_SPSR = 18 + +REG_SPSR_usr = REG_OFFSET_USR + REG_OFFSET_SPSR +REG_SPSR_fiq = REG_OFFSET_FIQ + REG_OFFSET_SPSR +REG_SPSR_irq = REG_OFFSET_IRQ + REG_OFFSET_SPSR +REG_SPSR_svc = REG_OFFSET_SVC + REG_OFFSET_SPSR +REG_SPSR_mon = REG_OFFSET_MON + REG_OFFSET_SPSR +REG_SPSR_abt = REG_OFFSET_ABT + REG_OFFSET_SPSR +REG_SPSR_hyp = REG_OFFSET_HYP + REG_OFFSET_SPSR +REG_SPSR_und = REG_OFFSET_UND + REG_OFFSET_SPSR +REG_SPSR_sys = REG_OFFSET_SYS + REG_OFFSET_SPSR REG_PC = 0xf REG_LR = 0xe @@ -310,6 +315,9 @@ PM_sys: ("System Processor Mode", "sys", "Runs privileged operating system tasks (ARMv4 and above)", REG_OFFSET_SYS, 15, REG_SPSR_sys, 1), } +MODE_COUNT = 17 + + PM_LNAME = 0 PM_SNAME = 1 PM_DESC = 2 @@ -318,9 +326,10 @@ PM_PSROFF = 5 PM_PRIVLVL = 6 -PSR_APSR = 2 -PSR_SPSR = 1 PSR_CPSR = 0 +PSR_SPSR = 1 +PSR_APSR = 2 + INST_ENC_DP_IMM = 0 # Data Processing Immediate Shift INST_ENC_MISC = 1 # Misc Instructions diff --git a/envi/archs/arm/disasm.py b/envi/archs/arm/disasm.py index 527c620ed..2e1f518be 100644 --- a/envi/archs/arm/disasm.py +++ b/envi/archs/arm/disasm.py @@ -278,7 +278,7 @@ def p_misc(opval, va): Rn = (opval) & 0xf mask = (opval>>16) & 0xf olist = ( - ArmPgmStatRegOper(r, mask), + ArmPgmStatRegOper(r, mask=mask), ArmRegOper(Rn, va=va), ) @@ -361,7 +361,7 @@ def p_misc(opval, va): Rd = (opval>>12) & 0xf olist = ( ArmRegOper(Rd, va=va), - ArmPgmStatRegOper(r), + ArmPgmStatRegOper(r, mask=0), ) else: raise envi.InvalidInstruction( @@ -772,7 +772,7 @@ def p_dp_movw(opval, va): iflags = 0 imm = ((opval >>4) &0xf000) + (opval & 0xfff) Rd = (opval >> 12) & 0xf - opcode = INS_MOV + opcode = INS_MOVW olist = ( ArmRegOper(Rd, va=va), ArmImmOper(imm), @@ -783,7 +783,7 @@ def p_dp_movt(opval, va): iflags = 0 imm = ((opval >>4) &0xf000) + (opval & 0xfff) Rd = (opval >> 12) & 0xf - opcode = INS_MOV + opcode = INS_MOVT olist = ( ArmRegOper(Rd, va=va), ArmImmOper(imm), @@ -832,7 +832,7 @@ def p_mov_imm_stat(opval, va): # only one instruction: "msr" iflags |= IF_SYS_MODE olist = ( - ArmPgmStatRegOper(r, mask), + ArmPgmStatRegOper(r, mask=mask, priv_mask=True), ArmImmOper(immed), ) @@ -1677,12 +1677,12 @@ def p_vpop(opval, va): return INS_VPUSH, 'vpush', opers, 0, simdflags def p_vdup(opval, va): - q = (opva >> 21) & 1 - b = (opva >> 22) & 1 - d = ((opva >> 3) & 0x10) - vd = (opva >> 16) & 0xf | d - rt = (opva >> 12) & 0xf - e = (opva >> 5) & 1 + q = (opval >> 21) & 1 + b = (opval >> 22) & 1 + d = ((opval >> 3) & 0x10) + vd = (opval >> 16) & 0xf | d + rt = (opval >> 12) & 0xf + e = (opval >> 5) & 1 # q# regs are two d# regs @@ -1768,7 +1768,7 @@ def p_uncond(opval, va, psize = 4): mesg="p_uncond (ontop=0): invalid instruction", bytez=struct.pack(">24)&1 R = (opval>>22)&1 # For w. Is pldw if R is 1 U = (opval>>23) & 1 @@ -1791,13 +1791,14 @@ def p_uncond(opval, va, psize = 4): olist = (ArmScaledOffsetOper(Rn, Rm, shtype, shval, va, (U<<3) | 0x10, psize=psize), ) return (opcode, mnem, olist, 0, 0) - elif (opval & 0xff000f0) == 0x5700010: + elif (opval & 0x0ff000f0) == 0x05700010: #clrex mnem = "clrex" olist =() opcode = INS_CLREX return (opcode, mnem, olist, 0, 0) - elif (opval & 0xff000e0) == 0x5700040: + + elif (opval & 0x0ff000e0) == 0x05700040: #dmb/dsb option = opval & 0xf if (opval & 0x10 )== 0x10: @@ -1808,17 +1809,24 @@ def p_uncond(opval, va, psize = 4): opcode = INS_DSB olist = (ArmBarrierOption(option),) return (opcode, mnem, olist, 0, 0) - elif (opval & 0xff000f0) == 0x5700060: + + elif (opval & 0x0ff000f0) == 0x05700060: #isb option = opval & 0xf mnem = 'isb' olist = (ArmBarrierOption(option),) opcode = INS_ISB return (opcode, mnem, olist, 0, 0) + + elif (opval & 0x0f100000) == 0x04000000: + # Advanced SIMD element or structure load/store + return adv_simd_ldst_32(opval, va) + else: raise envi.InvalidInstruction( mesg="p_uncond (ontop=1): invalid instruction", bytez=struct.pack(">24) & 1 return _do_adv_simd_ldst_32(val, va, u) @@ -2889,119 +2900,443 @@ def _do_adv_simd_ldst_32(val, va, u): a = (val >> 23) & 1 l = (val >> 21) & 1 - optype = (val >> 8) & 0xf - d = (val >> 22) & 1 rn = (val >> 16) & 0xf vd = (val >> 12) & 0xf rm = val & 0xf dd = vd | (d << 4) - sflag, size = ((IFS_8,1), (IFS_16,2), (IFS_32,4), (IFS_64,8))[(val >> 6) & 3] - align = (1,8,16,32)[(val >> 4) & 3] + wback = (rm != 15) + pubwl = PUxWL_DFLT | (wback<<1) + iflags = (0, IF_W)[wback] - simdflags = sflag - writeback = (rm != 15) - pubwl = PUxWL_DFLT | (writeback<<1) - iflags = (0, IF_W)[writeback] - opers = () + reg_index = rm not in (0xd, 0xf) if l == 0: # store if a == 0: - count = (1,2,4,2,3,3,3,1,1,1,2) [ optype ] + optype = (val >> 8) & 0xf + count = (4,4,4,4,3,3,3,1,2,2,2) [ optype ] + + szidx = (val>>6) & 3 # different location for a=0 and a=1 + sflag, size = ((IFS_8,1), (IFS_16,2), (IFS_32,4), (IFS_64,8))[szidx] + align = (1,8,16,32)[(val >> 4) & 3] + + # multiple elements if optype in (0b0010, 0b0110, 0b0111, 0b1010): # vst1 mnem = 'vst1' - opers = ( - ArmExtRegListOper(dd, count, 1), # size in this context means use "D#" registers - ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=size, tsize=size), - ) + if reg_index: + pubwl &= ~0x10 + opers = ( + ArmExtRegListOper(dd, count, 1), + ArmRegOffsetOper(rn, rm, va, pubwl=pubwl, psize=size, tsize=size, align=align), + ) + else: + if rm == 0xd: + pubwl |= 0x2 + opers = ( + ArmExtRegListOper(dd, count, 1), + ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=size, tsize=size, align=align), + ) + elif optype in (0b0011, 0b1000, 0b1001): # vst2 mnem = 'vst2' + inc = (1, 2)[optype==9] + align= (1, 4)[bool(align)] + if reg_index: + pubwl &= ~0x10 + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc), # size in this context means use "D#" registers + ArmRegOffsetOper(rn, rm, va, pubwl=pubwl, psize=size, tsize=size, align=align), + ) + else: + if rm == 0xd: + pubwl |= 0x2 + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc), + ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=size, tsize=size, align=align), + ) + elif optype in (0b0100, 0b0101): # vst3 mnem = 'vst3' inc = 1 + (optype&1) - opers = ( - ArmExtRegListOper(dd, count, 1, inc), - ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=size, tsize=size), - ) + if reg_index: + pubwl &= ~0x10 + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc), # size in this context means use "D#" registers + ArmRegOffsetOper(rn, rm, va, pubwl=pubwl, psize=size, tsize=size, align=align), + ) + else: + if rm == 0xd: + pubwl |= 0x2 + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc), + ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=size, tsize=size, align=align), + ) + elif optype in (0b0000, 0b0001): # vst4 mnem = 'vst4' + inc = 1 + (optype&1) + if reg_index: + pubwl &= ~0x10 + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc), # size in this context means use "D#" registers + ArmRegOffsetOper(rn, rm, va, pubwl=pubwl, psize=size, tsize=size, align=align), + ) + else: + if rm == 0xd: + pubwl |= 0x2 + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc), + ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=size, tsize=size, align=align), + ) + + else: # single elements + optype = (val >> 8) & 0x3 + szidx = (val>>10) & 3 # different location for a=0 and a=1 + sflag, ebytes = ((IFS_8,1), (IFS_16,2), (IFS_32,4), (None, None))[szidx] + index_align = (val >> 4) & 0xf - size = (val >> 10) & 3 + count = (1,2,3,4,) [ optype ] + index = index_align >> (1+szidx) - if optype in (0b0000, 0b0100, 0b1000): - # vst1 + # set increment size + if (index_align >> szidx) == 0: + inc = 1 + else: + inc = (1,2,2)[szidx] + + if optype == 0b00: + # vst1 - single 1-element structure from One lane mnem = 'vst1' - opers = ( - ArmExtRegListOper(dd, count, 1), - ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=size, tsize=size), - ) - elif optype in (0b0001, 0b0101, 0b1001): - # vst2 + if index_align & 1 == 0: + align = 1 + else: + align = ebytes + + if reg_index: + pubwl &= ~0x10 + opers = ( + ArmExtRegListOper(dd, count, 1, index=index), + ArmRegOffsetOper(rn, rm, va, pubwl=pubwl, psize=ebytes, tsize=ebytes, align=align), + ) + else: + if rm == 0xd: + pubwl |= 0x2 + opers = ( + ArmExtRegListOper(dd, count, 1, index=index), + ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=ebytes, tsize=ebytes, align=align), + ) + + elif optype == 0b01: + # vst2 - single 2-element structure from One lane mnem = 'vst2' - elif optype in (0b0010, 0b0110, 0b1010): - # vst3 + if index_align & 1 == 0: + align = 1 + else: + align = (ebytes << 1) + + if reg_index: + pubwl &= ~0x10 + opers = ( + ArmExtRegListOper(dd, count, 1, index=index), + ArmRegOffsetOper(rn, rm, va, pubwl=pubwl, psize=ebytes, tsize=ebytes, align=align), + ) + else: + if rm == 0xd: + pubwl |= 0x2 + opers = ( + ArmExtRegListOper(dd, count, 1, index=index), + ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=ebytes, tsize=ebytes, align=align), + ) + + elif optype == 0b10: + # vst3 - single 3-element structure from One lane mnem = 'vst3' - elif optype in (0b0011, 0b0111, 0b1011): - # vst4 + if reg_index: + pubwl &= ~0x10 + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc, index=index), + ArmRegOffsetOper(rn, rm, va, pubwl=pubwl, psize=ebytes, tsize=ebytes), + ) + else: + if rm == 0xd: + pubwl |= 0x2 + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc, index=index), + ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=ebytes, tsize=ebytes), + ) + + elif optype == 0b11: + # vst4 - single 4-element structure from One lane mnem = 'vst4' + if index_align & 1 == 0: + align = 1 + else: + align = (ebytes << 2) + + if reg_index: + pubwl &= ~0x10 + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc, index=index), + ArmRegOffsetOper(rn, rm, va, pubwl=pubwl, psize=ebytes, tsize=ebytes, align=align), + ) + else: + if rm == 0xd: + pubwl |= 0x2 + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc, index=index), + ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=ebytes, tsize=ebytes, align=align), + ) + + else: # load - if a: + if a == 0: + optype = (val >> 8) & 0xf + count = (4,4,4,4,3,3,3,1,2,2,2,3,3,3,4,4) [ optype ] + align = (1,8,16,32)[(val >> 4) & 3] + szidx = (val>>6) & 3 # different location for a=0 and a=1 + sflag, size = ((IFS_8,1), (IFS_16,2), (IFS_32,4), (IFS_64,8))[szidx] + # multiple elements if optype in (0b0010, 0b0110, 0b0111, 0b1010): # vld1 multiple single element mnem = 'vld1' + if reg_index: + pubwl &= ~0x10 + opers = ( + ArmExtRegListOper(dd, count, 1), + ArmRegOffsetOper(rn, rm, va, pubwl=pubwl, psize=size, tsize=size, align=align), + ) + else: + if rm == 0xd: + pubwl |= 0x2 + opers = ( + ArmExtRegListOper(dd, count, 1), + ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=size, tsize=size, align=align), + ) + elif optype in (0b0011, 0b1000, 0b1001): # vld2 multiple 2-element structures mnem = 'vld2' + inc = (1, 2)[optype==9] + align = (1, 4)[bool(align)] # FIXME + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc), # size in this context means use "D#" registers + ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=size, tsize=size), + ) + if reg_index: + pubwl &= ~0x10 + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc), + ArmRegOffsetOper(rn, rm, va, pubwl=pubwl, psize=size, tsize=size, align=align), + ) + else: + if rm == 0xd: + pubwl |= 0x2 + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc), + ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=size, tsize=size, align=align), + ) + + elif optype in (0b0100, 0b0101): # vld3 multiple 3-element structures mnem = 'vld3' + inc = 1 + (optype&1) + if reg_index: + pubwl &= ~0x10 + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc), + ArmRegOffsetOper(rn, rm, va, pubwl=pubwl, psize=size, tsize=size), + ) + else: + if rm == 0xd: + pubwl |= 0x2 + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc), + ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=size, tsize=size), + ) + + elif optype in (0b0000, 0b0001): # vld4 multiple 4-element structures mnem = 'vld4' + inc = 1 + (optype&1) + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc), # size in this context means use "D#" registers + ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=size, tsize=size), + ) + + + else: # a==1 + optype = (val >> 8) & 0x3 + pivot = szidx = (val>>10) & 3 # different location for a=0 and a=1 + if szidx != 0b11: + count = (1,2,3,4,3,3,3,1,2,3,2,1,1,1,1,1,1,) [ optype ] + index_align = (val >> 4) & 0xf + index = index_align >> (1+szidx) + + # set increment size + if (index_align >> szidx) == 0: + inc = 1 + else: + inc = (1,2,2)[szidx] + + else: #special case for "to all lanes" + szidx = (val>>6) & 3 + ebytes = 1 << szidx + t = (val>>5) & 1 + a = (val>>4) & 1 + count = optype + 1 + + sflag, size = ((IFS_8,1), (IFS_16,2), (IFS_32,4), (IFS_64,8))[szidx] - else: # single elements - if optype in (0b0000, 0b0100, 0b1000): - # vld1 single element to one lane + if optype == 0b00: mnem = 'vld1' - elif optype == 0b1100: - # vld1 single element to all lanes - mnem = 'vld1' - elif optype in (0b0001, 0b0101, 0b1001): + + if pivot == 0b11: + # vld1 single element to all lanes + count = t+1 + if a == 0: + alignment = 1 + else: + alignment = ebytes + + opers = ( + ArmExtRegListOper(dd, count, 1, index=INDEX_ALL), + ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=size, tsize=size), + ) + + else: + if reg_index: + # vld1 single element to one lane (register index) + pubwl &= ~0x10 + opers = ( + ArmExtRegListOper(dd, count, 1, index=index), + ArmRegOffsetOper(rn, rm, va, pubwl=pubwl, psize=size, tsize=size), + ) + else: + # vld1 single element to one lane (NOT register index) + if rm == 0xd: + pubwl |= 0x2 + opers = ( + ArmExtRegListOper(dd, count, 1, index=index), + ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=size, tsize=size), + ) + + elif optype == 0b01: # vld2 single 2-element structure to one lane mnem = 'vld2' - elif optype == 0b1101: - # vld2 single 2-element structure to all lanes - mnem = 'vld2' - elif optype in (0b0010, 0b0110, 0b1010): + if pivot == 0b11: + # vld2 single element to all lanes (special encoding) + inc = t+1 + if a == 0: + alignment = 1 + else: + alignment = ebytes << 1 + + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc, index=INDEX_ALL), + ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=size, tsize=size, align=alignment), + ) + + else: + if reg_index: + pubwl &= ~0x10 + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc, index=index), + ArmRegOffsetOper(rn, rm, va, pubwl=pubwl, psize=size, tsize=size), + ) + else: + if rm == 0xd: + pubwl |= 0x2 + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc, index=index), + ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=size, tsize=size), + ) + + elif optype == 0b10: # vld3 single 3-element structure to one lane mnem = 'vld3' - elif optype == 0b1110: - # vld3 single 3-element structure to all lanes - mnem = 'vld3' - elif optype in (0b0011, 0b0111, 0b1011): + if pivot == 0b11: + # vld3 single 3-element structure to all lanes + inc = t+1 + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc, index=INDEX_ALL), + ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=size, tsize=size), + ) + + else: + if reg_index: + pubwl &= ~0x10 + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc, index=index), + ArmRegOffsetOper(rn, rm, va, pubwl=pubwl, psize=size, tsize=size), + ) + else: + if rm == 0xd: + pubwl |= 0x2 + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc, index=index), + ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=size, tsize=size), + ) + + elif optype == 0b11: # vld4 single 4-element structure to one lane mnem = 'vld4' - elif optype == 0b1111: - # vld4 single 4-element structure to all lanes - mnem = 'vld4' + if pivot == 0b11: + # vld4 single 4-element structure to all lanes + inc = t+1 + if szidx == 0b11: + ebytes = 4 + alignment = 16 + else: + if a == 0: + alignment = 1 + else: + ebytes = 1 << szidx + if szidx == 0b10: + alignement = 8 + else: + alignment = ebytes << 2 + + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc, index=INDEX_ALL), + ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=size, tsize=size), + ) + + else: + if reg_index: + pubwl &= ~0x10 + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc, index=index), + ArmRegOffsetOper(rn, rm, va, pubwl=pubwl, psize=size, tsize=size), + ) + else: + if rm == 0xd: + pubwl |= 0x2 + opers = ( + ArmExtRegListOper(dd, count, 1, inc=inc, index=index), + ArmImmOffsetOper(rn, 0, va, pubwl=pubwl, psize=size, tsize=size), + ) + + simdflags = sflag return opcode, mnem, opers, iflags, simdflags # no iflags, only simdflags for this one def adv_simd_32(val, va): @@ -4022,7 +4357,9 @@ def render(self, mcanv): mnem += IFS[self.simdflags] #FIXME: Advanced SIMD modifiers (IF_V*) - if self.iflags & IF_THUMB32: + if self.iflags & IF_WIDE: + mnem += "w" + elif self.iflags & IF_THUMB32: mnem += ".w" mcanv.addNameText(mnem, typename="mnemonic") @@ -4038,6 +4375,14 @@ def render(self, mcanv): mcanv.addText(",") #if self.iflags & IF_W: # handled in operand. still keeping flag to indicate this instruction writes back # mcanc.addText(" !") + hint = mcanv.syms.getSymHint(self.va, OP_SYMHINT_IDX) + if hint: + mcanv.addText(' ; ') + if type(hint) == int: + name = addrToName(mcanv, hint) + mcanv.addVaText(name, hint) + else: + mcanv.addNameText(hint) def __repr__(self): mnem = self.mnem + cond_codes.get(self.prefixes) @@ -4068,7 +4413,9 @@ def __repr__(self): if self.simdflags: mnem += IFS[self.simdflags] - if self.iflags & IF_THUMB32: + if self.iflags & IF_WIDE: + mnem += "w" + elif self.iflags & IF_THUMB32: mnem += ".w" x = [] for o in self.opers: @@ -4140,7 +4487,6 @@ def getFloatValue(self, emu, elmtsz=None, elmtidx=None): if elmtsz is None: elmtsz = self.getWidth() - # TODO: signed? ifmt = e_bits.getFormat(elmtsz, big_endian=emu.getEndian()==ENDIAN_MSB) ffmt = e_bits.getFloatFormat(elmntsz, big_endian=emu.getEndian()==ENDIAN_MSB) @@ -4193,8 +4539,14 @@ def setFloatValue(self, emu, val, elmtsz=None, elmtidx=None): return regval def render(self, mcanv, op, idx): - rname = rctx.getRegisterName(self.reg) - mcanv.addNameText(rname, typename='registers') + hint = mcanv.syms.getSymHint(op.va, idx) + if hint is not None: + # FIXME: bug? what should this be? + mcanv.addNameText(hint, typename="registers") + else: + rname = rctx.getRegisterName(self.reg) + mcanv.addNameText(rname, typename='registers') + if self.oflags & OF_W: mcanv.addText( "!" ) @@ -4390,9 +4742,20 @@ def getOperValue(self, op, emu=None, codeflow=False): return shifters[self.shtype](self.val, self.shval, self.size, emu=emu) def render(self, mcanv, op, idx): - val = self.getOperValue(op) - mcanv.addText('#') - mcanv.addNameText('0x%.2x' % (val)) + value = self.val + hint = mcanv.syms.getSymHint(op.va, idx) + if hint is not None: + if mcanv.mem.isValidPointer(value): + mcanv.addVaText(hint, value) + else: + mcanv.addNameText(hint) + elif mcanv.mem.isValidPointer(value): + name = addrToName(mcanv, value) + mcanv.addVaText(name, value) + else: + val = self.getOperValue(op) + mcanv.addText('#') + mcanv.addNameText('0x%.2x' % (val)) def repr(self, op): val = self.getOperValue(op) @@ -4580,21 +4943,22 @@ def repr(self, op): else: shval = "" if (idxing&0x10) == 0: # post-indexed - tname = '[%s], %s%s %s' % (basereg, pom, offreg, shval) + tname = '[%s], %s%s%s' % (basereg, pom, offreg, shval) elif idxing == 0x10: - tname = '[%s, %s%s %s]' % (basereg, pom, offreg, shval) + tname = '[%s, %s%s%s]' % (basereg, pom, offreg, shval) else: # pre-indexed - tname = '[%s, %s%s %s]!' % (basereg, pom, offreg, shval) + tname = '[%s, %s%s%s]!' % (basereg, pom, offreg, shval) return tname class ArmRegOffsetOper(ArmOperand): ''' register offset operand. see "addressing mode 2 - load and store word or unsigned byte - register *" dereference address mode using the combination of two register values ''' - def __init__(self, base_reg, offset_reg, va, pubwl=PUxWL_DFLT, psize=4, tsize=None): + def __init__(self, base_reg, offset_reg, va, pubwl=PUxWL_DFLT, psize=4, tsize=None, align=None): self.base_reg = base_reg self.offset_reg = offset_reg self.pubwl = pubwl self.psize = psize + self.align = align if tsize is None: b = (self.pubwl >> 2) & 1 @@ -4613,6 +4977,8 @@ def __eq__(self, oper): return False if self.psize != oper.psize: return False + if self.align != oper.align: + return False return True def involvesPC(self): @@ -4622,6 +4988,7 @@ def isDeref(self): return True def setOperValue(self, op, emu=None, val=None): + #FIXME: alignment if emu is None: return None @@ -4629,6 +4996,7 @@ def setOperValue(self, op, emu=None, val=None): return emu.writeMemValue(addr, val, self.tsize) def getOperValue(self, op, emu=None, codeflow=False): + #FIXME: alignment if emu is None: return None @@ -4667,6 +5035,9 @@ def render(self, mcanv, op, idx): mcanv.addText(', ') mcanv.addText(pom) mcanv.addNameText(offreg, typename='registers') + + if self.align is not None: + mcanv.addNameText("@%d" % self.align) if idxing == 0x10: mcanv.addText(']') elif idxing&0x10 != 0: @@ -4677,12 +5048,18 @@ def repr(self, op): idxing = self.pubwl & 0x12 basereg = rctx.getRegisterName(self.base_reg) offreg = rctx.getRegisterName(self.offset_reg) + + if self.align is None: + alignstr = "" + else: + alignstr = "@%d" % self.align + if (idxing&0x10) == 0: # post-indexed tname = '[%s], %s%s' % (basereg, pom, offreg) elif idxing == 0x10: # offset addressing, not updated - tname = '[%s, %s%s]' % (basereg, pom, offreg) + tname = '[%s, %s%s%s]' % (basereg, pom, offreg, alignstr) else: # pre-indexed - tname = '[%s, %s%s]!' % (basereg, pom, offreg) + tname = '[%s, %s%s%s]!' % (basereg, pom, offreg, alignstr) return tname class ArmImmOffsetOper(ArmOperand): @@ -4693,7 +5070,7 @@ class ArmImmOffsetOper(ArmOperand): possibly with indexing, pre/post for faster rolling through arrays and such if the base_reg is PC, we'll dig in and hopefully grab the data being referenced. ''' - def __init__(self, base_reg, offset, va, pubwl=PUxWL_DFLT, psize=4, tsize=None): + def __init__(self, base_reg, offset, va, pubwl=PUxWL_DFLT, psize=4, tsize=None, align=None): ''' psize is pointer-size, since we want to increment base_reg that size when indexing tsize is the target size (4 or 1 bytes) @@ -4702,6 +5079,7 @@ def __init__(self, base_reg, offset, va, pubwl=PUxWL_DFLT, psize=4, tsize=None): self.offset = offset self.pubwl = pubwl self.psize = psize + self.align = align self.va = va if tsize is None: @@ -4721,6 +5099,8 @@ def __eq__(self, oper): return False if self.psize != oper.psize: return False + if self.align != oper.align: + return False return True def involvesPC(self): @@ -4753,7 +5133,7 @@ def getOperAddr(self, op, emu=None): # there are certain circumstances where we can survive without an emulator # if we don't have an emulator, we must be PC-based since we know it if self.base_reg == REG_PC: - base = self.va + base = self.va & 0xfffffffc elif emu is None: return None else: @@ -4879,13 +5259,17 @@ def getOperValue(self, op, emu=None, codeflow=False): return self.va + self.val def render(self, mcanv, op, idx): + hint = mcanv.syms.getSymHint(op.va, idx) value = self.getOperValue(op) - va = value & -2 - if mcanv.mem.isValidPointer(va): - name = addrToName(mcanv, va) - mcanv.addVaText(name, va) + if hint is not None: + mcanv.addVaText(hint, value) else: - mcanv.addVaText('0x%.8x' % va, va) + va = value & -2 + if mcanv.mem.isValidPointer(va): + name = addrToName(mcanv, va) + mcanv.addVaText(name, va) + else: + mcanv.addVaText('0x%.8x' % va, va) def repr(self, op): targ = self.getOperValue(op) @@ -4893,22 +5277,91 @@ def repr(self, op): return tname -psrs = ("CPSR", "SPSR", 'APSR', 'inval', 'inval', 'inval', 'inval', 'inval',) +REGx_APSR = (REG_CPSR, 0xf8000000) +REGx_IAPSR = ((REG_CPSR, 0xf8000000), (REG_IPSR, 0x000001ff)) +REGx_EAPSR = ((REG_CPSR, 0xf8000000), (REG_EPSR, 0x0700fc00)) +REGx_PSR = ((REG_CPSR, 0xf8000000), (REG_IPSR, 0x000001ff), (REG_EPSR, 0x0700fc00)) +REGx_IEPSR = ((REG_IPSR, 0x000001ff), (REG_EPSR, 0x0700fc00)) +REGx_MSPLIM = () # can't find definitions +REGx_PSPLIM = () # can't find definitions +CORE_PSRS = (("CPSR", REG_CPSR), ("SPSR", REG_SPSR), ('APSR', REGx_APSR), (None,None),) +SPEC_REGS_SPR = ( + (None, None), + ('IAPSR', REGx_IAPSR), + ('EAPSR', REGx_EAPSR), + ('PSR', REGx_PSR), + ('', None), + ('IPSR', REG_IPSR), + ('EPSR', REG_EPSR), + ('IEPSR', REGx_IEPSR), +) +SPEC_REGS_SP = ( + ('MSP', REG_MSP), + ('PSP', REG_PSP), + ('MSPLIM', REGx_MSPLIM), + ('PSPLIM', REGx_PSPLIM), + ('', None), + ('', None), + ('', None), + ('', None), +) +SPEC_REGS_PRICTL = ( + ('PRIMASK', REG_PRIMASK), + ('BASEPRI', REG_PRIMASK), + ('BASEPRI_MAX', REG_BASEPRI_MAX), + ('FAULTMASK', REG_FAULTMASK), + ('CONTROL', REG_CONTROL), +) + + +rt_data = (SPEC_REGS_SPR, SPEC_REGS_SP, SPEC_REGS_PRICTL) + fields = (None, 'c', 'x', 'cx', 's', 'cs', 'xs', 'cxs', 'f', 'fc', 'fx', 'fcx', 'fs', 'fcs', 'fxs', 'fcxs') +apsr_fields = [None for x in range(16)] +apsr_fields[8] = 'nzcvq' +apsr_fields[4] = 'g' +apsr_fields[0xc] = 'nzcvqg' + +expanded_mask = [] +for x in range(16): + temp = 0 + for y in range(4): + if x & (1<= REGS_VECTOR_TABLE_IDX: - return reg_table[idx] - - ridx = idx + (mode*REGS_PER_MODE) # account for different banks of registers - ridx = reg_table[ridx] # magic pointers allowing overlapping banks of registers - return ridx - ZC_bits = PSR_Z_bit | PSR_C_bit NC_bits = PSR_N_bit | PSR_C_bit @@ -380,15 +373,12 @@ def doPop(self): self.setRegister(REG_SP, esp+4) return val - def getProcMode(self): - ''' - get current ARM processor mode. see proc_modes (const.py) - ''' - return self._rctx_vals[REG_CPSR] & 0x1f # obfuscated for speed. could call getCPSR but it's not as fast - def getCPSR(self): ''' return the Current Program Status Register. + There can be only one. (CPSR, that is). Each processor mode has a slot + for a CPSR, and they all must point to the one and only CPSR data + in the RegisterContext, where emu values are stored. ''' return self._rctx_vals[REG_CPSR] @@ -422,94 +412,17 @@ def getSPSR(self, mode): ''' get the SPSR for the given ARM processor mode ''' - ridx = _getRegIdx(REG_OFFSET_CPSR, mode) + ridx = a_regs._getRegIdx(REG_OFFSET_SPSR, mode) return self._rctx_vals[ridx] def setSPSR(self, mode, psr, mask=0xffffffff): ''' set the SPSR for the given ARM processor mode ''' - ridx = _getRegIdx(REG_OFFSET_CPSR, mode) + ridx = a_regs._getRegIdx(REG_OFFSET_SPSR, mode) psr = self._rctx_vals[ridx] & (~mask) | (psr & mask) self._rctx_vals[ridx] = psr - def setProcMode(self, mode): - ''' - set the current processor mode. stored in CPSR - ''' - # write current psr to the saved psr register for target mode - # but not for USR or SYS modes, which don't have their own SPSR - if mode not in (PM_usr, PM_sys): - curSPSRidx = proc_modes[mode] - self._rctx_vals[curSPSRidx] = self.getCPSR() - - # set current processor mode - cpsr = self._rctx_vals[REG_CPSR] & 0xffffffe0 - self._rctx_vals[REG_CPSR] = cpsr | mode - - def getRegister(self, index, mode=None): - """ - Return the current value of the specified register index. - """ - if mode is None: - mode = self.getProcMode() & 0xf - else: - mode &= 0xf - - idx = (index & 0xffff) - ridx = _getRegIdx(idx, mode) - if idx == index: - return self._rctx_vals[ridx] - - offset = (index >> 24) & 0xff - width = (index >> 16) & 0xff - - mask = (2**width)-1 - return (self._rctx_vals[ridx] >> offset) & mask - - def setRegister(self, index, value, mode=None): - """ - Set a register value by index. - """ - if mode is None: - mode = self.getProcMode() & 0xf - else: - mode &= 0xf - - self._rctx_dirty = True - - # the raw index (in case index is a metaregister) - idx = (index & 0xffff) - - # we only keep separate register banks per mode for general registers, not vectors - if idx >= REGS_VECTOR_TABLE_IDX: - ridx = idx - else: - ridx = _getRegIdx(idx, mode) - - if idx == index: # not a metaregister - self._rctx_vals[ridx] = (value & self._rctx_masks[ridx]) # FIXME: hack. should look up index in proc_modes dict? - return - - # If we get here, it's a meta register index. - # NOTE: offset/width are in bits... - offset = (index >> 24) & 0xff - width = (index >> 16) & 0xff - - mask = e_bits.b_masks[width] - mask = mask << offset - - # NOTE: basewidth is in *bits* - basewidth = self._rctx_widths[ridx] - basemask = (2**basewidth)-1 - - # cut a whole in basemask at the size/offset of mask - finalmask = basemask ^ mask - - curval = self._rctx_vals[ridx] - - self._rctx_vals[ridx] = (curval & finalmask) | (value << offset) - def integerSubtraction(self, op): """ Do the core of integer subtraction but only *return* the @@ -685,6 +598,24 @@ def i_orr(self, op): self.setFlag(PSR_C_bit, e_bits.is_unsigned_carry(val, tsize)) self.setFlag(PSR_V_bit, e_bits.is_signed_overflow(val, tsize)) + def i_orn(self, op): + tsize = op.opers[0].tsize + if len(op.opers) == 3: + val1 = self.getOperValue(op, 1) + val2 = self.getOperValue(op, 2) + else: + val1 = self.getOperValue(op, 0) + val2 = self.getOperValue(op, 1) + val = val1 | (~val2) + self.setOperValue(op, 0, val) + + Sflag = op.iflags & IF_PSR_S + if Sflag: + self.setFlag(PSR_N_bit, e_bits.is_signed(val, tsize)) + self.setFlag(PSR_Z_bit, not val) + self.setFlag(PSR_C_bit, e_bits.is_unsigned_carry(val, tsize)) + self.setFlag(PSR_V_bit, e_bits.is_signed_overflow(val, tsize)) + def i_stm(self, op): if len(op.opers) == 2: srcreg = op.opers[0].reg @@ -1986,7 +1917,7 @@ def i_tbb(self, op): loc = self.vw.getLocation(va) if loc is not None: logger.warning("Terminating TB at Location/Reference") - logger.warning("%x, %d, %x, %r", loc) + logger.warning("%x, %d, %x, %r", *loc) break tbl.append(nexttgt) @@ -2001,7 +1932,7 @@ def i_tbb(self, op): if idx > 0x40000000: self.setRegister(idxreg, 0) # args handed in can be replaced with index 0 - jmptblbase = op.opers[0]._getOperBase(emu) + jmptblbase = op.opers[0]._getOperBase(self) jmptblval = self.getOperAddr(op, 0) jmptbltgt = (self.getOperValue(op, 0) * 2) + base logger.debug("0x%x: 0x%r\njmptblbase: 0x%x\njmptblval: 0x%x\njmptbltgt: 0x%x", op.va, op, jmptblbase, jmptblval, jmptbltgt) @@ -2020,6 +1951,18 @@ def i_ubfx(self, op): self.setOperValue(op, 0, val) + def i_sbfx(self, op): + tsize = op.opers[0].tsize + src = self.getOperValue(op, 1) + lsb = self.getOperValue(op, 2) + width = self.getOperValue(op, 3) + mask = (1 << width) - 1 + + val = e_bits.sign_extend((src>>lsb) & mask, width, tsize) + + self.setOperValue(op, 0, val) + + def i_umull(self, op): rn = self.getOperValue(op, 2) rm = self.getOperValue(op, 3) diff --git a/envi/archs/arm/regs.py b/envi/archs/arm/regs.py index b07a6456c..1571233e4 100644 --- a/envi/archs/arm/regs.py +++ b/envi/archs/arm/regs.py @@ -1,6 +1,8 @@ -from envi.archs.arm.const import * +import envi.bits as e_bits import envi.registers as e_reg +from envi.archs.arm.const import * + ''' Strategy: * Use only distinct register for Register Context (unique in each bank) @@ -27,7 +29,8 @@ ('lr', 32), # also r14 ('pc', 32), # also r15 ('cpsr', 32), - ('nil', 32), # place holder + ('nil', 32), # place holder + ('spsr', 32), # updated for each level # FIXME: need to deal with ELR_hyp ] @@ -41,21 +44,65 @@ ("r15", REG_PC, 0, 32), ] +REG_PC = 15 +REG_CPSR = 16 +REG_NIL = 17 +REG_SPSR = 18 + REG_APSR_MASK = 0xffff0000 # build a translation table to allow for fast access of banked registers modes = list(proc_modes.keys()) modes.sort() -reg_table = [ x for x in range(17 * REGS_PER_MODE) ] +# ARMv7/A32/T32 registers as complicated. each processor mode accesses a particular set of +# registers. some regs are shared with other modes, and some are unique to a particular mode. +# to achieve this using high-performance disassembly and emulation, and the power of the Viv +# RegisterContext model, we've had to get creative. + +# the solution we've chosen is to use a big lookup table and an extra layer to access the actual +# emulated register data. the larger list of registers (below, reg_table_data) defines a large +# list of registers, grouped into an equal size list of register definitions per processor mode. +# the smaller list (reg_data) defines a collapsed list of registers, including only the register +# definitions which have unique data storage. +# we use another large list (reg_table) for mapping indexes into +# reg_table_data to indexes into reg_data + +# so, thats: +# reg_table is the redirection table between MATH-COUNT-Index and Real Register Index: #index +# reg_data is that compressed "only what's needed" table of register entries: ('name', size) +# reg_table_data is the expanded reg_data completing the all modes, etc: ('name', size') + +# other details: +# emulator get/setRegister() access the emulator data as per *reg_data* +# disassembly creates *RegOper*s using indexes into *reg_table_data* +# the ArmRegisterContext class override get/setRegister() to handle translation +# as far as the rest of the ArmRegisterContext is concern, there is only reg_data +# *RegOper* operands only care about the *reg_tables_data* since they only care about render/repr +# and the getOperValue() defers to the ArmRegisterContext() for actually accessing the emu data + +# this setup allows us to have ranges of "registers" to match the different processor modes +# where some of those registers are unique to a given mode, and others are shared (banked regs) + +# all modes' CPSR's point to the one and only CPSR +# all modes` SPSR's are their own thing, and should get a copy of the current CPSR's data on +# mode-change. # NOTE: THIS IS IMPORTANT FOR FULL EMULATION + + +# start off creating the data for the first mode (USR mode) +# and then pre-populating the reg_table and reg_table_data reg_data = [ (reg, sz) for reg,sz in arm_regs_tups ] -reg_table_data = [ (None, 32) for x in range(17 * REGS_PER_MODE) ] +reg_table = [ x for x in range(MODE_COUNT * REGS_PER_MODE) ] +reg_table_data = [ (None, 32) for x in range(MODE_COUNT * REGS_PER_MODE) ] + for idx,data in enumerate(reg_data): reg_table_data[idx] = data # banked registers for different processor modes for modenum in modes[1:]: # skip first since we're already done (mname, msname, desc, offset, mode_reg_count, PSR_offset, priv_level) = proc_modes.get(modenum) + #print("Building %s (%x): offset=0x%x PSR_offset: 0x%x" % (msname, modenum, offset, PSR_offset)) + #import envi.interactive as ei; ei.dbg_interact(locals(), globals()) # shared regs for ridx in range(mode_reg_count): # don't create new entries for this register, use the usr-mode reg @@ -74,18 +121,18 @@ reg_table_data[ridx+offset] = (regname, rsz) - # PC - reg_table[PSR_offset-3] = 15 + # PC - always the original PC + reg_table[PSR_offset-3] = REG_PC reg_table_data[PSR_offset-3] = ('pc_%s' % (msname), 32) - # CPSR - reg_table[PSR_offset-2] = 16 # SPSR....?? + # CPSR - always the original CPSR + reg_table[PSR_offset-2] = REG_CPSR # SPSR....?? reg_table_data[PSR_offset-2] = ('CPSR_%s' % (msname), 32) # NIL - reg_table[PSR_offset-1] = 17 + reg_table[PSR_offset-1] = REG_NIL reg_table_data[PSR_offset-1] = ('NIL_%s' % (msname), 32) # PSR - reg_table[PSR_offset] = len(reg_data) - reg_table_data[PSR_offset] = ('SPSR_%s' % (msname), 32) + reg_table[PSR_offset] = len(reg_data) # -2 because we want SPSR_foo to point at the CPSR_foo. i think? + reg_table_data[PSR_offset] = ('SPSR_%s' % (msname), 32) reg_data.append(("SPSR_"+msname, 32)) # done with banked register translation table @@ -123,12 +170,40 @@ REG_FPSCR = len(reg_table) reg_table.append(len(reg_data)) reg_data.append(('fpscr', 32)) +reg_table_data.append(('fpscr', 32)) +reg_table.append(len(reg_data)) +reg_data.append(('IPSR', 32)) +reg_table_data.append(('IPSR', 32)) +reg_table.append(len(reg_data)) +reg_data.append(('EPSR', 32)) +reg_table_data.append(('EPSR', 32)) +reg_table.append(len(reg_data)) +reg_data.append(('PRIMASK', 32)) +reg_table_data.append(('PRIMASK', 32)) +reg_table.append(len(reg_data)) +reg_data.append(('FAULTMASK', 32)) +reg_table_data.append(('FAULTMASK', 32)) +reg_table.append(len(reg_data)) +reg_data.append(('BASEPRI', 32)) +reg_table_data.append(('BASEPRI', 32)) +reg_table.append(len(reg_data)) +reg_data.append(('BASEPRI_MAX', 32)) +reg_table_data.append(('BASEPRI_MAX', 32)) +reg_table.append(len(reg_data)) +reg_data.append(('CONTROL', 32)) +reg_table_data.append(('CONTROL', 32)) +reg_table.append(len(reg_data)) +reg_data.append(('MSP', 32)) +reg_table_data.append(('MSP', 32)) +reg_table.append(len(reg_data)) +reg_data.append(('PSP', 32)) +reg_table_data.append(('PSP', 32)) MAX_TABLE_SIZE = len(reg_table_data) l = locals() -e_reg.addLocalEnums(l, arm_regs_tups) +e_reg.addLocalEnums(l, reg_table_data) PSR_N = 31 # negative PSR_Z = 30 # zero @@ -229,4 +304,108 @@ def __init__(self): self.loadRegMetas(arm_metas, statmetas=arm_status_metas) self.setRegisterIndexes(REG_PC, REG_SP) + def getProcMode(self): + ''' + get current ARM processor mode. see proc_modes (const.py) + ''' + return self._rctx_vals[REG_CPSR] & 0x1f + + def setProcMode(self, mode): + ''' + set the current processor mode. stored in CPSR + ''' + # write current psr to the saved psr register for target mode + # but not for USR or SYS modes, which don't have their own SPSR + if mode not in (PM_usr, PM_sys): + curSPSRidx = proc_modes[mode] + self._rctx_vals[curSPSRidx] = self.getCPSR() + + # set current processor mode + cpsr = self._rctx_vals[REG_CPSR] & 0xffffffe0 + self._rctx_vals[REG_CPSR] = cpsr | mode + + def getRegister(self, index, mode=None): + """ + Return the current value of the specified register index. + """ + if mode is None: + mode = self.getProcMode() & 0xf + else: + mode &= 0xf + + idx = (index & 0xffff) + ridx = _getRegIdx(idx, mode) + + if (index & 0xffff) == index: # not a metaregister + return self._rctx_vals[ridx] + + offset = (index >> 24) & 0xff + width = (index >> 16) & 0xff + + mask = (2**width)-1 + return (self._rctx_vals[ridx] >> offset) & mask + + def setRegister(self, index, value, mode=None): + """ + Set a register value by index. + """ + if mode is None: + mode = self.getProcMode() & 0xf + else: + mode &= 0xf + + self._rctx_dirty = True + + # the raw index (in case index is a metaregister) + idx = (index & 0xffff) + + # have to translate every register index to find the right home + ridx = _getRegIdx(idx, mode) + + if (index & 0xffff) == index: # not a metaregister + self._rctx_vals[ridx] = (value & self._rctx_masks[ridx]) # FIXME: hack. should look up index in proc_modes dict? + return + + # If we get here, it's a meta register index. + # NOTE: offset/width are in bits... + offset = (index >> 24) & 0xff + width = (index >> 16) & 0xff + + mask = e_bits.b_masks[width] + mask = mask << offset + + # NOTE: basewidth is in *bits* + basewidth = self._rctx_widths[ridx] + basemask = (2**basewidth)-1 + + # cut a whole in basemask at the size/offset of mask + finalmask = basemask ^ mask + + curval = self._rctx_vals[ridx] + + self._rctx_vals[ridx] = (curval & finalmask) | (value << offset) + + +def _getRegIdx(idx, mode): + if idx >= REGS_VECTOR_TABLE_IDX: + return reg_table[idx] + + mode &= 0xf + ridx = idx + (mode*REGS_PER_MODE) # account for different banks of registers + ridx = reg_table[ridx] # magic pointers allowing overlapping banks of registers + return ridx + + +def reg_mode_base(mode): + ''' + Get the base register index for a given mode (full-sized table) + ''' + mdata = proc_modes.get(mode) + if mdata is None: + raise Exception("Invalid Mode access (reg_mode_base): %d" % mode) + + return mdata[3] + + + rctx = ArmRegisterContext() diff --git a/envi/archs/thumb16/disasm.py b/envi/archs/thumb16/disasm.py index 3f3984066..873fc6ebe 100644 --- a/envi/archs/thumb16/disasm.py +++ b/envi/archs/thumb16/disasm.py @@ -38,13 +38,23 @@ def __call__(self, va, value): imm3_rn_rd = simpleops((O_REG, 0, 0x7), (O_REG, 3, 0x7), (O_IMM, 6, 0x7)) imm8_rd = simpleops((O_REG, 8, 0x7), (O_IMM, 0, 0xff)) rm_rd = simpleops((O_REG, 0, 0x7), (O_REG, 3, 0x7)) -rn_rdm = simpleops((O_REG, 0, 0x7), (O_REG, 3, 0x7)) rm_rdn = simpleops((O_REG, 0, 0x7), (O_REG, 3, 0x7)) +rn_rdm = simpleops((O_REG, 0, 0x7), (O_REG, 3, 0x7), (O_REG, 0, 0x7)) rm_rd_imm0 = simpleops((O_REG, 0, 0x7), (O_REG, 3, 0x7), (O_IMM, 0, 0)) -imm8 = simpleops((O_IMM, 8, 0xff)) +imm8 = simpleops((O_IMM, 0, 0xff)) sh4_imm1 = simpleops((O_IMM, 3, 0x1)) +def d1_rm4_rd3_dbl(va, value): + # 0 1 0 0 0 1 0 0 DN(1) Rm(4) Rdn(3) + rdbit = shmaskval(value, 4, 0x8) + rd = shmaskval(value, 0, 0x7) + rdbit + rm = shmaskval(value, 3, 0xf) + if rm == REG_SP: + return COND_AL, (ArmRegOper(rd, va=va), ArmRegOper(rm, va=va), ArmRegOper(rd, va=va)), None + return COND_AL, (ArmRegOper(rd, va=va), ArmRegOper(rm, va=va)), None + + def d1_rm4_rd3(va, value): # 0 1 0 0 0 1 0 0 DN(1) Rm(4) Rdn(3) rdbit = shmaskval(value, 4, 0x8) @@ -90,6 +100,13 @@ def imm56_rn_rt(va, value): return COND_AL, (oper0, oper1), None +def sp_sp_imm8(va, value): # add + imm = shmaskval(value, 0, 0x7f) * 4 + oper0 = ArmRegOper(RSG_SP, va=va) + oper1 = ArmImmOper(imm, va=va) + return COND_AL, (oper0, oper1), None + + def rd_sp_imm8(va, value): # add rd = shmaskval(value, 8, 0x7) imm = shmaskval(value, 0, 0xff) * 4 @@ -104,25 +121,25 @@ def rd_sp_imm8d(va, value): # add imm = shmaskval(value, 0, 0xff) * 4 oper0 = ArmRegOper(rd, va=va) # pre-compute PC relative addr - oper1 = ArmImmOffsetOper(REG_SP, imm, (va & 0xfffffffc)+4, pubwl=0x18) + oper1 = ArmImmOffsetOper(REG_SP, imm, (va & 0xfffffffc), pubwl=0x18) return COND_AL, (oper0, oper1), None def rd_pc_imm8(va, value): # add rd = shmaskval(value, 8, 0x7) - imm = e_bits.signed(shmaskval(value, 0, 0xff), 1) * 4 + imm = e_bits.signed(shmaskval(value, 0, 0xff), 1) << 2 oper0 = ArmRegOper(rd, va=va) # pre-compute PC relative addr - oper1 = ArmImmOper((va & 0xfffffffc) + 4 + imm) + oper1 = ArmImmOper((va & 0xfffffffc) + imm) return COND_AL, (oper0, oper1), None def rd_pc_imm8d(va, value): # add rd = shmaskval(value, 8, 0x7) - imm = e_bits.signed(shmaskval(value, 0, 0xff), 1) * 4 + imm = e_bits.signed(shmaskval(value, 0, 0xff), 1) << 2 oper0 = ArmRegOper(rd, va=va) # pre-compute PC relative addr - oper1 = ArmImmOffsetOper(REG_PC, imm, (va & 0xfffffffc)+4, pubwl=0x18) + oper1 = ArmImmOffsetOper(REG_PC, imm, (va & 0xfffffffc), pubwl=0x18) return COND_AL, (oper0, oper1), None @@ -203,6 +220,9 @@ def rm4_shift3(va, value): # bx/blx None) +def t32_undef(va, val1, val2): + return COND_AL, None, None, (), None, None + def branch_misc(va, val, val2): # bl and misc control op = (val >> 4) & 0b1111111 op1 = (val2 >> 12) & 0b111 @@ -210,6 +230,14 @@ def branch_misc(va, val, val2): # bl and misc control imm8 = val2 & 0b1111 if (op1 & 0b101 == 0): + if op1 == 0b010 and op == 0b1111111: + flags = 0 + imm4 = val & 0xf + imm12 = val2 & 0xfff + immval = (imm4 << 12) | imm12 + oper0 = ArmImmOper(immval) + return COND_AL, INS_UNDEF, 'udf', (oper0, ), flags, 0 + if not (op & 0b0111000) == 0b0111000: # Bcc - T3 encoding - conditional cond = (val >> 6) & 0xf opcode, mnem, nflags, cond = bcc_ops.get(cond) @@ -294,21 +322,33 @@ def branch_misc(va, val, val2): # bl and misc control # xx0xxxxx and others if op & 0b1111110 == 0b0111000: - tmp = op2 & 3 + priv_mask = op2 & 3 Rn = val & 0xf mask = (val2 >> 8) & 0xf - if not (op & 1) and tmp == 0: - # MSR(register) p A8-498 - R = PSR_APSR + ##### Other Special Registers (magic encoding) + R = (op & 1) # CPSR==0, SPSR==1 + # also handle (seemingly) Thumb-only accesses: + xregs = val2 & 0b11111 + rtype = xregs >> 3 - else: # op==0111000 and op2==01/10/11 or op==0111001 - # MSR(register) p B9-1968 - R = (val >> 4) & 1 - # System Level Only... - opers = (ArmPgmStatRegOper(R, mask), - ArmRegOper(Rn)) + #if R == 0 and priv_mask == 0: # R == CPSR and mask&3 == 0 + # # MSR(register) p A8-498 + # R = PSR_APSR + + # also op==0111000 and op2==01/10/11 or op==0111001 + # MSR(register) p B9-1968 + # System Level Only... + + #if rtype: # special special regs (Cortex-M) + # opers = (ArmPgmStatRegOper(xregs&7, rtype, mask, priv_mask), + # ArmRegOper(Rn)) + + #else: # traditional PSR + opers = (ArmPgmStatRegOper(R, mask, xregs, rtype, priv_mask), + ArmRegOper(Rn)) + return COND_AL, None, 'msr', opers, None, 0 elif op == 0b0111010: @@ -365,15 +405,23 @@ def branch_misc(va, val, val2): # bl and misc control elif op == 0b0111110: Rd = (val2 >> 8) & 0xf + R = (val >> 4) & 1 + # handle special regs: + xregs = val2 & 0xff # per ARMv7-M profile + rtype = xregs >> 3 + opers = (ArmRegOper(Rd), - ArmPgmStatRegOper(PSR_CPSR),) + ArmPgmStatRegOper(R, mask=0, xregs=xregs, rtype=rtype),) return COND_AL, None, 'mrs', opers, None, 0 elif op == 0b0111111: Rd = (val2 >> 8) & 0xf R = (val >> 4) & 1 + # handle special regs: + R |= (val2 << 2) & 0b1111100 + opers = (ArmRegOper(Rd), - ArmPgmStatRegOper(R)) + ArmPgmStatRegOper(R, mask=0)) return COND_AL, None, 'mrs', opers, None, 0 @@ -423,15 +471,6 @@ def branch_misc(va, val, val2): # bl and misc control oper0 = ArmPcOffsetOper(e_bits.signed(imm, 4), va=va) return COND_AL, opcode, 'b', (oper0, ), flags, 0 - elif op1 == 0b010: - if op == 0b1111111: - flags = 0 - imm4 = val & 0xf - imm12 = val2 & 0xfff - immval = (imm4 << 12) | imm12 - oper0 = ArmImmOper(immval) - return COND_AL, INS_UDF, 'udf', (oper0, ), flags, 0 - raise InvalidInstruction(mesg="branch_misc subsection 6", bytez=struct.pack("> 10) & 1 mnem = ('blx', 'bl')[notx] opcode = (INS_BLX, INS_BL)[notx] - flags = envi.IF_CALL | IF_W + flags = envi.IF_CALL # need next two bytes - j1 = not ((val2 >> 13) & 1 ^ s) - j2 = not ((val2 >> 11) & 1 ^ s) + j1 = not (((val2 >> 13) & 1) ^ s) + j2 = not (((val2 >> 11) & 1) ^ s) imm = (s << 24) | (j1 << 23) | (j2 << 22) | ((val & 0x3ff) << 12) | ((val2 & 0x7ff) << 1) + #print("s=%d j1=%d j2=%d imm=%x" % (s, j1, j2, imm)) # sign extend a 25-bit number if s: @@ -481,30 +521,26 @@ def pc_imm8(va, value): # b oper0 = ArmPcOffsetOper(imm, va=va) return cond, (oper0,), None - -def ldmia(va, value): - rd = shmaskval(value, 8, 0x7) - reg_list = value & 0xff - oper0 = ArmRegOper(rd, va=va) - oper1 = ArmRegListOper(reg_list) - oper0.oflags |= OF_W - return COND_AL, (oper0, oper1), None +def udf_imm8(va, value): # udf + imm = value & 0xff + oper0 = ArmImmOper(imm, va=va) + return COND_AL, (oper0,), None def sp_sp_imm7(va, value): imm = shmaskval(value, 0, 0x7f) o0 = ArmRegOper(REG_SP) - o1 = ArmRegOper(REG_SP) - o2 = ArmImmOper(imm*4) - return COND_AL, (o0, o1, o2), None + o1 = ArmImmOper(imm*4) + return COND_AL, (o0, o1), None def rm_reglist(va, value): rm = shmaskval(value, 8, 0x7) - reglist = value & 0xff + reg_list = value & 0xff oper0 = ArmRegOper(rm, va=va) - oper1 = ArmRegListOper(reglist) - oper0.oflags |= OF_W + oper1 = ArmRegListOper(reg_list) + if not ((1<>= 1 return x - 1 + def getSuffix(self): + count, cond, data = self.getCondData() + return it_strs[count][data] + def getFlags(self): fiz = self.firstcond & 1 flags = 1 @@ -619,28 +657,28 @@ def getFlags(self): return flags def getCondData(self): - ''' - deprecated: 2020-06-24 - ''' mask = self.mask cond = self.firstcond - count = 0 - go = 0 + count = 4 cond0 = cond & 1 - data = 0 - for idx in range(4): - mbit = (mask >> idx) & 1 - if go: - bit = bool(mbit == cond0) - data <<= 1 - data |= bit - count += 1 + done = False + while mask: + count -= 1 + done = mask & 1 - if mbit: - go = 1 + # kill the first discovered 1 + mask >>= 1 + if done: + break + + if count == 4: + count = 0 - return count, self.firstcond, data + if not cond0: + mask ^= e_bits.bu_maxes[count] + + return count, self.firstcond, mask def getITSTATEdata(self): ''' @@ -866,64 +904,64 @@ def shift_or_ext_32(va, val1, val2): parallel_misc_info = ( { - 0b000: (INS_UADD8, 'uadd8', IF_THUMB32), - 0b001: (INS_UADD16, 'uadd16', IF_THUMB32), - 0b010: (INS_UASX, 'uasx', IF_THUMB32), - 0b110: (INS_USAX, 'usax', IF_THUMB32), - 0b101: (INS_USUB16, 'usub16', IF_THUMB32), - 0b100: (INS_USUB8, 'usub8', IF_THUMB32), - - 0b1000: (INS_UQADD8, 'uqadd8', IF_THUMB32), - 0b1001: (INS_UQADD16, 'uqadd16', IF_THUMB32), - 0b1010: (INS_UQASX, 'uqasx', IF_THUMB32), - 0b1110: (INS_UQSAX, 'uqsax', IF_THUMB32), - 0b1101: (INS_UQSUB16, 'uqsub16', IF_THUMB32), - 0b1100: (INS_UQSUB8, 'uqsub8', IF_THUMB32), - - 0b10000: (INS_UHADD8, 'uhadd8', IF_THUMB32), - 0b10001: (INS_UHADD16, 'uhadd16', IF_THUMB32), - 0b10010: (INS_UHASX, 'uhasx', IF_THUMB32), - 0b10110: (INS_UHSAX, 'uhsax', IF_THUMB32), - 0b10101: (INS_UHSUB16, 'uhsub16', IF_THUMB32), - 0b10100: (INS_UHSUB8, 'uhsub8', IF_THUMB32), + 0b000: (INS_UADD8, 'uadd8', IF_THUMB32, 3), + 0b001: (INS_UADD16, 'uadd16', IF_THUMB32, 3), + 0b010: (INS_UASX, 'uasx', IF_THUMB32, 3), + 0b110: (INS_USAX, 'usax', IF_THUMB32, 3), + 0b101: (INS_USUB16, 'usub16', IF_THUMB32, 3), + 0b100: (INS_USUB8, 'usub8', IF_THUMB32, 3), + + 0b1000: (INS_UQADD8, 'uqadd8', IF_THUMB32, 3), + 0b1001: (INS_UQADD16,'uqadd16', IF_THUMB32, 3), + 0b1010: (INS_UQASX, 'uqasx', IF_THUMB32, 3), + 0b1110: (INS_UQSAX, 'uqsax', IF_THUMB32, 3), + 0b1101: (INS_UQSUB16,'uqsub16', IF_THUMB32, 3), + 0b1100: (INS_UQSUB8, 'uqsub8', IF_THUMB32, 3), + + 0b10000: (INS_UHADD8, 'uhadd8', IF_THUMB32, 3), + 0b10001: (INS_UHADD16,'uhadd16', IF_THUMB32, 3), + 0b10010: (INS_UHASX, 'uhasx', IF_THUMB32, 3), + 0b10110: (INS_UHSAX, 'uhsax', IF_THUMB32, 3), + 0b10101: (INS_UHSUB16,'uhsub16', IF_THUMB32, 3), + 0b10100: (INS_UHSUB8, 'uhsub8', IF_THUMB32, 3), }, { - 0b000: (INS_SADD8, 'sadd8', IF_THUMB32), - 0b001: (INS_SADD16, 'sadd16', IF_THUMB32), - 0b010: (INS_SASX, 'sasx', IF_THUMB32), - 0b110: (INS_SSAX, 'ssax', IF_THUMB32), - 0b101: (INS_SSUB16, 'ssub16', IF_THUMB32), - 0b100: (INS_SSUB8, 'ssub8', IF_THUMB32), - - 0b1000: (INS_QADD8, 'qadd8', IF_THUMB32), - 0b1001: (INS_QADD16, 'qadd16', IF_THUMB32), - 0b1010: (INS_QASX, 'qasx', IF_THUMB32), - 0b1110: (INS_QSAX, 'qsax', IF_THUMB32), - 0b1101: (INS_QSUB16, 'qsub16', IF_THUMB32), - 0b1100: (INS_QSUB8, 'qsub8', IF_THUMB32), - - 0b10000: (INS_SHADD8, 'shadd8', IF_THUMB32), - 0b10001: (INS_SHADD16, 'shadd16', IF_THUMB32), - 0b10010: (INS_SHASX, 'shasx', IF_THUMB32), - 0b10110: (INS_SHSAX, 'shsax', IF_THUMB32), - 0b10101: (INS_SHSUB16, 'shsub16', IF_THUMB32), - 0b10100: (INS_SHSUB8, 'shsub8', IF_THUMB32), + 0b000: (INS_SADD8, 'sadd8', IF_THUMB32, 3), + 0b001: (INS_SADD16, 'sadd16', IF_THUMB32, 3), + 0b010: (INS_SASX, 'sasx', IF_THUMB32, 3), + 0b110: (INS_SSAX, 'ssax', IF_THUMB32, 3), + 0b101: (INS_SSUB16, 'ssub16', IF_THUMB32, 3), + 0b100: (INS_SSUB8, 'ssub8', IF_THUMB32, 3), + + 0b1000: (INS_QADD8, 'qadd8', IF_THUMB32, 3), + 0b1001: (INS_QADD16, 'qadd16', IF_THUMB32, 3), + 0b1010: (INS_QASX, 'qasx', IF_THUMB32, 3), + 0b1110: (INS_QSAX, 'qsax', IF_THUMB32, 3), + 0b1101: (INS_QSUB16, 'qsub16', IF_THUMB32, 3), + 0b1100: (INS_QSUB8, 'qsub8', IF_THUMB32, 3), + + 0b10000: (INS_SHADD8, 'shadd8', IF_THUMB32, 3), + 0b10001: (INS_SHADD16,'shadd16', IF_THUMB32, 3), + 0b10010: (INS_SHASX, 'shasx', IF_THUMB32, 3), + 0b10110: (INS_SHSAX, 'shsax', IF_THUMB32, 3), + 0b10101: (INS_SHSUB16,'shsub16', IF_THUMB32, 3), + 0b10100: (INS_SHSUB8, 'shsub8', IF_THUMB32, 3), }, { - 0b00000: (INS_QADD, 'qadd', IF_THUMB32), - 0b01000: (INS_QDADD, 'qdadd', IF_THUMB32), - 0b10000: (INS_QADD, 'qsub', IF_THUMB32), - 0b11000: (INS_QDADD, 'qdsub', IF_THUMB32), + 0b00000: (INS_QADD, 'qadd', IF_THUMB32, 3), + 0b01000: (INS_QDADD, 'qdadd', IF_THUMB32, 3), + 0b10000: (INS_QADD, 'qsub', IF_THUMB32, 3), + 0b11000: (INS_QDADD, 'qdsub', IF_THUMB32, 3), - 0b00001: (INS_REV, 'rev', IF_THUMB32), - 0b01001: (INS_REV16, 'rev16', IF_THUMB32), + 0b00001: (INS_REV, 'rev', IF_THUMB32, 2), + 0b01001: (INS_REV16, 'rev16', IF_THUMB32, 2), # rd, rm - 0b10001: (INS_RBIT, 'rbit', IF_THUMB32), - 0b11001: (INS_REVSH, 'revsh', IF_THUMB32), + 0b10001: (INS_RBIT, 'rbit', IF_THUMB32, 2), + 0b11001: (INS_REVSH, 'revsh', IF_THUMB32, 2), # rd, rn, rm - 0b00010: (INS_SEL, 'sel', IF_THUMB32), - 0b00011: (INS_CLZ, 'clz', IF_THUMB32), + 0b00010: (INS_SEL, 'sel', IF_THUMB32, 3), + 0b00011: (INS_CLZ, 'clz', IF_THUMB32, 2), }, ) @@ -941,14 +979,18 @@ def parallel_misc_32(va, val1, val2): if pardata is None: return shift_or_ext_32(va, val1, val2) - opcode, mnem, flags = pardata + opcode, mnem, flags, opercount = pardata rn = (val1 & 0xf) rd = (val2 >> 8) & 0xf rm = (val2 & 0xf) - opers = (ArmRegOper(rd), - ArmRegOper(rn), - ArmRegOper(rm)) + if opercount == 3: + opers = (ArmRegOper(rd), + ArmRegOper(rn), + ArmRegOper(rm)) + else: + opers = (ArmRegOper(rd), + ArmRegOper(rn)) return COND_AL, opcode, mnem, opers, flags, 0 @@ -971,11 +1013,14 @@ def ubfx_32(va, val1, val2): return COND_AL, None, None, opers, None, 0 -def dp_bin_imm_32(va, val1, val2): # p232 +def dp_mov_imm_32(va, val1, val2): # p232 + ''' + data processing (plain binary immediate) + MOV, MOVT + ''' if val2 & 0x8000: return branch_misc(va, val1, val2) - flags = IF_THUMB32 Rd = (val2 >> 8) & 0xf imm4 = val1 & 0xf @@ -990,20 +1035,92 @@ def dp_bin_imm_32(va, val1, val2): # p232 oper2 = ArmImmOper(const) opers = [oper0, oper2] - if op in (0b00100, 0b01100): # movw, movt - return COND_AL, None, None, opers, 0, 0 + return COND_AL, None, None, opers, None, 0 + + +def dp_bin_imm_32(va, val1, val2): # p232 + ''' + data processing (plain binary immediate) + ADD, SUB, ... + ''' + if val2 & 0x8000: + return branch_misc(va, val1, val2) + flags = IF_WIDE | IF_THUMB32 + Rd = (val2 >> 8) & 0xf Rn = val1 & 0xf + + imm4 = val1 & 0xf + i = (val1 >> 10) & 1 + imm3 = (val2 >> 12) & 0x7 + const = val2 & 0xff + + op = (val1 >> 4) & 0x1f + const |= (i << 11) | (imm3 << 8) + + + if Rn == 15 and op in (0, 0b1010): # add/sub # adr + oper0 = ArmRegOper(Rd) + oper2 = ArmImmOper(const) + opers = (oper0, oper2) return COND_AL, None, 'adr', opers, None, 0 + oper0 = ArmRegOper(Rd) oper1 = ArmRegOper(Rn) - opers.insert(1, oper1) + oper2 = ArmImmOper(const) + opers = (oper0, oper1, oper2) + + return COND_AL, None, None, opers, flags, 0 + + +def dp_sat_imm_32(va, val1, val2): # p232 + ''' + data processing (plain binary immediate) + SSAT, USAT, etc.... + ''' + if val2 & 0x8000: + return branch_misc(va, val1, val2) + + flags = IF_THUMB32 + Rd = (val2 >> 8) & 0xf + Rn = val1 & 0xf + + imm2 = (val2 >> 6) & 0x3 + imm3 = (val2 >> 12) & 0x7 + sh = (val1>>5) & 1 + sat_imm = val2 & 0x1f + + imm = (imm3<<2) | imm2 + if sh and not imm: + # SSAT16 + oper0 = ArmRegOper(Rd) + oper1 = ArmImmOper(sat_imm+1) + oper2 = ArmRegOper(Rn) + opers = (oper0, oper1, oper2) + + else: + shift_t, shift_n = DecodeImmShift(sh<<1, imm) + + oper0 = ArmRegOper(Rd) + oper1 = ArmImmOper(sat_imm+1) + oper2 = ArmRegShiftImmOper(Rn, shift_t, shift_n, va) + opers = (oper0, oper1, oper2) return COND_AL, None, None, opers, flags, 0 +def DecodeImmShift(sht, imm): + if sht == 0b11: + if imm == 0: + imm = 1 + + elif sht: # any non-zero + if imm == 0: + imm = 32 + return sht, imm + def dp_bfi_imm_32(va, val1, val2): # p232 if val2 & 0x8000: return branch_misc(va, val1, val2) @@ -1022,7 +1139,7 @@ def dp_bfi_imm_32(va, val1, val2): # p232 oper0 = ArmRegOper(Rd) oper2 = ArmImmOper(const) - if op in (0b00100, 0b01100): # movw, movt + if op in (0b00100, 0b01100): # mov, movt return COND_AL, None, None, (oper0, oper2), 0, 0 Rn = val1 & 0xf @@ -1131,30 +1248,76 @@ def strex_32(va, val1, val2): def ldr_32(va, val1, val2): + # handle immediates and registers + bitsbits = (val1 >> 4) & 0x7 tsize = (1, 0, 2, 2, 4, 4, 0, 0)[bitsbits] rn = val1 & 0xf rt = (val2 >> 12) & 0xf - imm12 = val2 & 0xfff + immtype = (val1 >> 7) & 1 + isimmed = (val2 >> 6) & 0x3f | immtype oper0 = ArmRegOper(rt, va=va) - oper1 = ArmImmOffsetOper(rn, imm12, va=va, tsize=tsize) + # there are register, imm12, or imm8 options + if isimmed: + if immtype: + imm12 = val2 & 0xfff + oper1 = ArmImmOffsetOper(rn, imm12, va=va, tsize=tsize) + + else: + imm8 = val2 & 0xff + pubwl = ((val2 >> 6) & 0x18) | ((val2 >> 7) & 0x2) + oper1 = ArmImmOffsetOper(rn, imm8, va=va, pubwl=pubwl, tsize=tsize) + + else: + # register type + rm = val2 & 0xf + imm2 = (val2 >> 4) & 3 + oper1 = ArmScaledOffsetOper(rn, rm, S_LSL, imm2, va=va, tsize=tsize) + opers = (oper0, oper1) return COND_AL, None, None, opers, None, 0 +def popone_32(va, val1, val2): + # handle immediates and registers + rt = (val2 >> 12) & 0xf + + oper0 = ArmRegListOper(1<> 12) & 0xf + + Sbit = (val1 >> 8) & 1 + + # ldrh, ldrsh + opcode, mnem, flags = ldrh_instrs[Sbit] + imm12 = val2 & 0xfff + opers = (ArmRegOper(rt), + ArmImmOffsetOper(rn, imm12, va, tsize=1)) + return COND_AL, opcode, mnem, opers, flags, 0 + def ldrb_memhints_32(va, val1, val2): op1 = (val1 >> 7) & 3 op2 = (val2 >> 6) & 0x3f @@ -1222,25 +1385,6 @@ def ldrb_memhints_32(va, val1, val2): return COND_AL, opcode, mnem, opers, flags, 0 -def ldr_shift_32(va, val1, val2): - #b11 = (val2>>11) & 1 - # if not b11: - # raise Exception("ldr_shift_32 parsing non-ldrb") - bitsbits = (val1 >> 4) & 0x7 - tsize = (1, 0, 2, 2, 4, 4, 0, 0)[bitsbits] - - rn = val1 & 0xf - rm = val2 & 0xf - rt = (val2 >> 12) & 0xf - imm2 = (val2 >> 4) & 3 - - oper0 = ArmRegOper(rt, va=va) - oper1 = ArmScaledOffsetOper(rn, rm, S_LSL, imm2, va=va, tsize=tsize) - - opers = (oper0, oper1) - return COND_AL, None, None, opers, None, 0 - - def ldrex_32(va, val1, val2): rn = val1 & 0xf rt = (val2 >> 12) & 0xf @@ -1290,6 +1434,12 @@ def strexn_32(va, val1, val2): flags = 0 return COND_AL, None, mnem, opers, flags, 0 +mla_instrs = ( + (INS_MLA, 'mla'), + (INS_MLS, 'mls'), + (0, 'ERROR: mla_32 fail'), + (0, 'ERROR: mla_32 fail'), +) def mla_32(va, val1, val2): rn = val1 & 0xf @@ -1297,55 +1447,81 @@ def mla_32(va, val1, val2): rd = (val2 >> 8) & 0xf ra = (val2 >> 12) & 0xf - mnem = 'mla' - opcode = INS_MLA + if ra == 0b1111: + opcode = INS_MUL + mnem = 'mul' + + opers = (ArmRegOper(rd, va=va), + ArmRegOper(rn, va=va), + ArmRegOper(rm, va=va), + ) - opers = (ArmRegOper(rd, va=va), - ArmRegOper(rn, va=va), - ArmRegOper(rm, va=va), - ArmRegOper(ra, va=va)) + else: + instr_idx = (val2>>4) & 3 + opcode, mnem = mla_instrs[instr_idx] + + opers = (ArmRegOper(rd, va=va), + ArmRegOper(rn, va=va), + ArmRegOper(rm, va=va), + ArmRegOper(ra, va=va) + ) return COND_AL, None, mnem, opers, None, 0 def smul_32(va, val1, val2): + ra = val2 >> 12 rn = val1 & 0xf rm = val2 & 0xf rd = (val2 >> 8) & 0xf - nm = (val2 >> 4) & 0x3 - opcode, mnem = ((INS_SMULBB, 'smulbb'), - (INS_SMULBT, 'smulbt'), - (INS_SMULTB, 'smultb'), - (INS_SMULTT, 'smultt'))[nm] + if ra == 0b1111: + nm = (val2 >> 4) & 0x3 + opcode, mnem = ((INS_SMULBB, 'smulbb'), + (INS_SMULBT, 'smulbt'), + (INS_SMULTB, 'smultb'), + (INS_SMULTT, 'smultt'))[nm] + + opers = (ArmRegOper(rd, va=va), + ArmRegOper(rn, va=va), + ArmRegOper(rm, va=va)) + else: + nm = (val2 >> 4) & 0x3 + opcode, mnem = ((INS_SMLABB, 'smlabb'), + (INS_SMLABT, 'smlabt'), + (INS_SMLATB, 'smlatb'), + (INS_SMLATT, 'smlatt'))[nm] + + opers = (ArmRegOper(rd, va=va), + ArmRegOper(rn, va=va), + ArmRegOper(rm, va=va), + ArmRegOper(ra, va=va)) - opers = (ArmRegOper(rd, va=va), - ArmRegOper(rn, va=va), - ArmRegOper(rm, va=va)) return COND_AL, opcode, mnem, opers, None, 0 + smulls_info = { - 0: {0x0: (INS_SMULL, 'smull',)}, - 1: {0xf: (INS_SDIV, 'sdiv',)}, - 2: {0x0: (INS_UMULL, 'umull',)}, - 3: {0xf: (INS_UDIV, 'udiv',)}, - 4: {0x0: (INS_SMLAL, 'smlal',), - 0x8: (INS_SMLAL, 'smlalbb',), - 0x9: (INS_SMLAL, 'smlalbt',), - 0xa: (INS_SMLAL, 'smlaltb',), - 0xb: (INS_SMLAL, 'smlaltt',), - 0xc: (INS_SMLALD, 'smlald',), - 0xd: (INS_SMLALDX, 'smlaldx',)}, - 5: {0xc: (INS_SMLSLD, 'smlsld',), - 0xd: (INS_SMLSLDX, 'smlsldx',)}, - 6: {0x0: (INS_UMLAL, 'umlal',), - 0x6: (INS_UMAAL, 'umaal',)}, + 0: {0x0: (INS_SMULL, 'smull', 4)}, + 1: {0xf: (INS_SDIV, 'sdiv', 3)}, + 2: {0x0: (INS_UMULL, 'umull', 4)}, + 3: {0xf: (INS_UDIV, 'udiv', 3)}, + 4: {0x0: (INS_SMLAL, 'smlal', 4), + 0x8: (INS_SMLAL, 'smlalbb', 4), + 0x9: (INS_SMLAL, 'smlalbt', 4), + 0xa: (INS_SMLAL, 'smlaltb', 4), + 0xb: (INS_SMLAL, 'smlaltt', 4), + 0xc: (INS_SMLALD, 'smlald', 4), + 0xd: (INS_SMLALDX, 'smlaldx', 4)}, + 5: {0xc: (INS_SMLSLD, 'smlsld', 4), + 0xd: (INS_SMLSLDX, 'smlsldx', 4)}, + 6: {0x0: (INS_UMLAL, 'umlal', 4), + 0x6: (INS_UMAAL, 'umaal', 4)}, } def smull_32(va, val1, val2): - # TODO: does this exist in thumb? + mnem = None rn = val1 & 0xf rm = val2 & 0xf rdhi = (val2 >> 8) & 0xf @@ -1368,10 +1544,16 @@ def smull_32(va, val1, val2): bytez=struct.pack(" ,, ('0100001101', (INS_MUL, 'mul', rn_rdm, IF_PSR_S)), ('0100001110', (INS_BIC, 'bic', rm_rdn, IF_PSR_S)), # BIC , - ('0100001111', (INS_MVN, 'mvn', rm_rd, 0)), # MVN , + ('0100001111', (INS_MVN, 'mvn', rm_rd, IF_PSR_S)), # MVN , # Special data in2tructions and branch and exchange - ('0100010000', (INS_ADD, 'add', d1_rm4_rd3, 0)), # ADD , - ('0100010001', (INS_ADD, 'add', d1_rm4_rd3, 0)), # ADD , - ('010001001', (INS_ADD, 'add', d1_rm4_rd3, 0)), # ADD , + ('0100010000', (INS_ADD, 'add', d1_rm4_rd3_dbl, 0)), # ADD , + ('0100010001', (INS_ADD, 'add', d1_rm4_rd3_dbl, 0)), # ADD , + ('010001001', (INS_ADD, 'add', d1_rm4_rd3_dbl, 0)), # ADD , ('010001010', (INS_CMP, 'cmp', d1_rm4_rd3, 0)), # CMP , ('010001011', (INS_CMP, 'cmp', d1_rm4_rd3, 0)), # CMP , ('01000110', (INS_MOV, 'mov', d1_rm4_rd3, 0)), # MOV , # BX # FIXME: check for IF_RET - ('010001110', (INS_BX, 'bx', rm4_shift3, 0)), + ('010001110', (INS_BX, 'bx', rm4_shift3, 0)), ('010001111', (INS_BLX, 'blx', rm4_shift3, envi.IF_CALL)), # BLX # Load from Litera7 Pool ('01001', (INS_LDR, 'ldr', rt_pc_imm8d, 0)), # LDR ,