1746 lines
60 KiB
C++
1746 lines
60 KiB
C++
/*
|
|
* Interactive disassembler (IDA).
|
|
* Copyright (c) 1990-2002 by Ilfak Guilfanov, Datarescue.
|
|
* ALL RIGHTS RESERVED.
|
|
*
|
|
*/
|
|
|
|
#ifndef _ARM_HPP
|
|
#define _ARM_HPP
|
|
|
|
#include "../idaidp.hpp"
|
|
#include <idd.hpp>
|
|
#include <dbg.hpp>
|
|
#include <segregs.hpp>
|
|
#include <typeinf.hpp>
|
|
#include <fixup.hpp>
|
|
#include "../calcrel_common.hpp"
|
|
struct arm_t;
|
|
|
|
#define PROCMOD_NAME arm
|
|
#define PROCMOD_NODE_NAME " $arm"
|
|
|
|
//------------------------------------------------------------------
|
|
struct fptr_info_t
|
|
{
|
|
ea_t addr; // address where the fp register is set
|
|
ushort reg; // frame pointer for current function (usually R11 or R7)
|
|
};
|
|
|
|
//---------------------------------
|
|
// ARM insn.auxpref bits
|
|
#define aux_cond 0x0001 // set condition codes (S postfix is required)
|
|
#define aux_byte 0x0002 // byte transfer (B postfix is required)
|
|
#define aux_npriv 0x0004 // non-privileged transfer (T postfix is required)
|
|
#define aux_regsh 0x0008 // shift count is held in a register (see o_shreg)
|
|
#define aux_negoff 0x0010 // memory offset is negated in LDR,STR
|
|
#define aux_immcarry 0x0010 // carry flag is set to bit 31 of the immediate operand (see may_set_carry)
|
|
#define aux_wback 0x0020 // write back (! postfix is required)
|
|
#define aux_wbackldm 0x0040 // write back for LDM/STM (! postfix is required)
|
|
#define aux_postidx 0x0080 // post-indexed mode in LDR,STR
|
|
#define aux_ltrans 0x0100 // long transfer in LDC/STC (L postfix is required)
|
|
#define aux_wimm 0x0200 // thumb32 wide encoding of immediate constant (MOVW)
|
|
#define aux_sb 0x0400 // signed byte (SB postfix)
|
|
#define aux_sh 0x0800 // signed halfword (SH postfix)
|
|
#define aux_sw (aux_sb|aux_sh) // signed word (SW postfix)
|
|
#define aux_h 0x1000 // halfword (H postfix)
|
|
#define aux_x (aux_h|aux_byte) // doubleword (X postfix in A64)
|
|
#define aux_d aux_x // dual (D postfix in A32/T32)
|
|
#define aux_p 0x2000 // priviledged (P postfix)
|
|
#define aux_coproc 0x4000 // coprocessor instruction
|
|
#define aux_wide 0x8000 // wide (32-bit) thumb instruction (.W suffix)
|
|
#define aux_pac 0x10000 // Pointer Authentication Code instruction (see PAC_ flags)
|
|
#define aux_ns 0x20000 // non-secure branch (NS suffix)
|
|
#define aux_thumb32 0x40000 // (aux_wide is sometimes turned off for pretty-printing)
|
|
|
|
// assembler flags
|
|
#define UAS_GNU 0x0001 // GNU assembler
|
|
#define UAS_LEGACY 0x0002 // Legacy (pre-UAL) assembler
|
|
|
|
//---------------------------------
|
|
|
|
#define it_mask insnpref // mask field of IT-insn
|
|
|
|
// data type of NEON and vector VFP instructions (for the suffix)
|
|
enum neon_datatype_t ENUM_SIZE(char)
|
|
{
|
|
DT_NONE = 0,
|
|
DT_8,
|
|
DT_16,
|
|
DT_32,
|
|
DT_64,
|
|
DT_S8,
|
|
DT_S16,
|
|
DT_S32,
|
|
DT_S64,
|
|
DT_U8,
|
|
DT_U16,
|
|
DT_U32,
|
|
DT_U64,
|
|
DT_I8,
|
|
DT_I16,
|
|
DT_I32,
|
|
DT_I64,
|
|
DT_P8,
|
|
DT_P16,
|
|
DT_F16,
|
|
DT_F32,
|
|
DT_F64,
|
|
DT_P32, // unused?
|
|
DT_P64, // for 128-bit form of PMULL instruction
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
// we will store the suffix in insnpref, since it's used only by the IT instruction
|
|
// if we need two suffixes (VCVTxx), we'll store the second one in Op1.specflag1
|
|
inline void set_neon_suffix(insn_t &insn, neon_datatype_t suf1, neon_datatype_t suf2 = DT_NONE)
|
|
{
|
|
if ( suf1 != DT_NONE )
|
|
{
|
|
insn.insnpref = char(0x80 | suf1);
|
|
if ( suf2 != DT_NONE )
|
|
insn.Op1.specflag1 = suf2;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
inline neon_datatype_t get_neon_suffix(const insn_t &insn)
|
|
{
|
|
if ( insn.insnpref & 0x80 )
|
|
return neon_datatype_t(insn.insnpref & 0x7F);
|
|
else
|
|
return DT_NONE;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
inline neon_datatype_t get_neon_suffix2(const insn_t &insn)
|
|
{
|
|
return neon_datatype_t(insn.Op1.specflag1);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
inline char dtype_from_dt(neon_datatype_t dt)
|
|
{
|
|
switch ( dt )
|
|
{
|
|
case DT_8:
|
|
case DT_S8:
|
|
case DT_U8:
|
|
case DT_I8:
|
|
case DT_P8:
|
|
return dt_byte;
|
|
case DT_16:
|
|
case DT_S16:
|
|
case DT_U16:
|
|
case DT_I16:
|
|
case DT_P16:
|
|
return dt_word;
|
|
case DT_32:
|
|
case DT_S32:
|
|
case DT_U32:
|
|
case DT_I32:
|
|
return dt_dword;
|
|
case DT_64:
|
|
case DT_S64:
|
|
case DT_U64:
|
|
case DT_I64:
|
|
case DT_NONE:
|
|
default:
|
|
return dt_qword;
|
|
case DT_F16:
|
|
return dt_half;
|
|
case DT_F32:
|
|
return dt_float;
|
|
case DT_F64:
|
|
return dt_double;
|
|
}
|
|
}
|
|
|
|
#define PROC_MAXOP 5
|
|
CASSERT(PROC_MAXOP <= UA_MAXOP);
|
|
|
|
// Operand types:
|
|
#define o_shreg o_idpspec0 // Shifted register
|
|
// op.reg - register
|
|
#define shtype specflag2 // op.shtype - shift type
|
|
#define shreg(x) uchar(x.specflag1) // op.shreg - shift register
|
|
#define shcnt value // op.shcnt - shift counter
|
|
|
|
#define ishtype specflag2 // o_imm - shift type
|
|
#define ishcnt specval // o_imm - shift counter
|
|
|
|
#define secreg(x) uchar(x.specflag1) // o_phrase: the second register is here
|
|
#define ralign specflag3 // o_phrase, o_displ: NEON alignment (power-of-two bytes, i.e. 8*(1<<a))
|
|
// minimal alignment is 16 (a==1)
|
|
|
|
#define simd_sz specflag1 // o_reg: SIMD vector element size
|
|
// 0=scalar, 1=8 bits, 2=16 bits, 3=32 bits, 4=64 bits, 5=128 bits)
|
|
// number of lanes is derived from the vector size (dtype)
|
|
#define simd_idx specflag3 // o_reg: SIMD scalar index plus 1 (Vn.H[i])
|
|
|
|
// o_phrase: the second register is held in secreg (specflag1)
|
|
// the shift type is in shtype (specflag2)
|
|
// the shift counter is in shcnt (value)
|
|
|
|
#define o_reglist o_idpspec1 // Register list (for LDM/STM)
|
|
#define reglist specval // The list is in op.specval
|
|
#define uforce specflag1 // PSR & force user bit (^ suffix)
|
|
|
|
#define o_creglist o_idpspec2 // Coprocessor register list (for CDP)
|
|
#define CRd reg //
|
|
#define CRn specflag1 //
|
|
#define CRm specflag2 //
|
|
|
|
#define o_creg o_idpspec3 // Coprocessor register (for LDC/STC)
|
|
|
|
#define o_fpreglist o_idpspec4 // Floating point register list
|
|
#define fpregstart reg // First register
|
|
#define fpregcnt value // number of registers; 0: single register (NEON scalar)
|
|
#define fpregstep specflag2 // register spacing (0: {Dd, Dd+1,... }, 1: {Dd, Dd+2, ...} etc)
|
|
#define fpregindex specflag3 // NEON scalar index plus 1 (Dd[x])
|
|
#define NOINDEX (char)254 // no index - all lanes (Dd[])
|
|
|
|
#define o_text o_idpspec5 // Arbitrary text stored in the operand
|
|
// structure starting at the 'value' field
|
|
// up to 16 bytes (with terminating zero)
|
|
#define o_cond o_idpspec5+1 // ARM condition as an operand
|
|
// condition is stored in 'value' field
|
|
|
|
// The processor number of coprocessor instructions is held in cmd.Op1.specflag1:
|
|
#define procnum specflag1
|
|
|
|
// bits stored in specflag1 for APSR register
|
|
#define APSR_nzcv 0x01
|
|
#define APSR_q 0x02
|
|
#define APSR_g 0x04
|
|
// for SPSR/CPSR
|
|
#define CPSR_c 0x01
|
|
#define CPSR_x 0x02
|
|
#define CPSR_s 0x04
|
|
#define CPSR_f 0x08
|
|
// for banked registers (R8-R12, SP, LR/ELR, SPSR), this flag is set
|
|
#define BANKED_MODE 0x80 // the mode is in low 5 bits (arm_mode_t)
|
|
|
|
//------------------------------------------------------------------
|
|
// Shift types:
|
|
enum shift_t
|
|
{
|
|
LSL, // logical left LSL #0 - don't shift
|
|
LSR, // logical right LSR #0 means LSR #32
|
|
ASR, // arithmetic right ASR #0 means ASR #32
|
|
ROR, // rotate right ROR #0 means RRX
|
|
RRX, // extended rotate right
|
|
|
|
// ARMv8 shifts
|
|
MSL, // masked shift left (ones are shifted in from the right)
|
|
|
|
// extending register operations
|
|
UXTB,
|
|
UXTH,
|
|
UXTW,
|
|
UXTX, // alias for LSL
|
|
SXTB,
|
|
SXTH,
|
|
SXTW,
|
|
SXTX,
|
|
};
|
|
|
|
//------------------------------------------------------------------
|
|
// Bit definitions. Just for convenience:
|
|
#define BIT0 0x00000001L
|
|
#define BIT1 0x00000002L
|
|
#define BIT2 0x00000004L
|
|
#define BIT3 0x00000008L
|
|
#define BIT4 0x00000010L
|
|
#define BIT5 0x00000020L
|
|
#define BIT6 0x00000040L
|
|
#define BIT7 0x00000080L
|
|
#define BIT8 0x00000100L
|
|
#define BIT9 0x00000200L
|
|
#define BIT10 0x00000400L
|
|
#define BIT11 0x00000800L
|
|
#define BIT12 0x00001000L
|
|
#define BIT13 0x00002000L
|
|
#define BIT14 0x00004000L
|
|
#define BIT15 0x00008000L
|
|
#define BIT16 0x00010000L
|
|
#define BIT17 0x00020000L
|
|
#define BIT18 0x00040000L
|
|
#define BIT19 0x00080000L
|
|
#define BIT20 0x00100000L
|
|
#define BIT21 0x00200000L
|
|
#define BIT22 0x00400000L
|
|
#define BIT23 0x00800000L
|
|
#define BIT24 0x01000000L
|
|
#define BIT25 0x02000000L
|
|
#define BIT26 0x04000000L
|
|
#define BIT27 0x08000000L
|
|
#define BIT28 0x10000000L
|
|
#define BIT29 0x20000000L
|
|
#define BIT30 0x40000000L
|
|
#define BIT31 0x80000000L
|
|
|
|
#define HEX__(n) 0x##n##LU
|
|
|
|
/* 8-bit conversion function */
|
|
#define B8__(x) ((x&0x0000000FLU)?1:0) \
|
|
+((x&0x000000F0LU)?2:0) \
|
|
+((x&0x00000F00LU)?4:0) \
|
|
+((x&0x0000F000LU)?8:0) \
|
|
+((x&0x000F0000LU)?16:0) \
|
|
+((x&0x00F00000LU)?32:0) \
|
|
+((x&0x0F000000LU)?64:0) \
|
|
+((x&0xF0000000LU)?128:0)
|
|
|
|
// for upto 8-bit binary constants
|
|
#define B8(d) ((unsigned char)B8__(HEX__(d)))
|
|
|
|
// for upto 16-bit binary constants, MSB first
|
|
#define B16(dmsb,dlsb) (((uint16)B8(dmsb)<< 8) | (uint16)B8(dlsb))
|
|
|
|
// for upto 32-bit binary constants, MSB first
|
|
#define B32(dmsb,db2,db3,dlsb) (((uint32)B8(dmsb)<<24) \
|
|
| ((uint32)B8(db2 )<<16) \
|
|
| ((uint32)B8(db3 )<< 8) \
|
|
| (uint32)B8(dlsb))
|
|
|
|
// extract bit numbers high..low from val (inclusive, start from 0)
|
|
#define BITS(val, high, low) ( ((val)>>low) & ( (1<<(high-low+1))-1) )
|
|
|
|
// extract one bit
|
|
#define BIT(val, bit) ( ((val)>>bit) & 1 )
|
|
|
|
// return if mask matches the value
|
|
// mask has 1s for important bits and 0s for don't-care bits
|
|
// match has actual values for important bits
|
|
// e.g. : xx0x0 means mask is 11010 and match is aa0a0
|
|
#define MATCH(value, mask, match) ( ((value) & (mask)) == (match) )
|
|
|
|
//------------------------------------------------------------------
|
|
// The condition code of instruction will be kept in cmd.segpref:
|
|
|
|
#define cond segpref
|
|
|
|
//---------------------------------
|
|
// PAC (Pointer Authentication Code) instruction suffix flags are stored in insnpref
|
|
// NB: we have only 8 bits!
|
|
#define pac_flags insnpref
|
|
|
|
// bits 0..2: key used
|
|
// The Pointer Authentication specification defines five keys. Four
|
|
// keys for PAC* and AUT* instructions (combination of instruction/data
|
|
// and A/B keys), and a fifth key for use with the general purpose PACGA
|
|
// instruction
|
|
#define PAC_KEYMASK 0x07
|
|
#define PAC_KEY_IA 0x00 // Instruction address, using key A
|
|
#define PAC_KEY_IB 0x01 // Instruction address, using key B
|
|
#define PAC_KEY_DA 0x02 // Data address, using key A
|
|
#define PAC_KEY_DB 0x03 // Data address, using key B
|
|
#define PAC_KEY_GA 0x04 // Generic key (PACGA)
|
|
#define PAC_KEY_RES5 0x05 // reserved for future
|
|
#define PAC_KEY_RES6 0x06 // reserved for future
|
|
// bits 3..4: address used
|
|
#define PAC_ADRMASK (3<<3) // address is:
|
|
#define PAC_ADR_GPR (0<<3) // in general-purpose register that is specified by <Xd>
|
|
#define PAC_ADR_X17 (1<<3) // in X17
|
|
#define PAC_ADR_X30 (2<<3) // in X30(XLR)
|
|
// bits 5..7: modifier used
|
|
#define PAC_MODMASK (3<<5) // modifier is :
|
|
#define PAC_MOD_GPR (0<<5) // in general-purpose register or stack pointer that is specified by <Xn|SP>
|
|
#define PAC_MOD_ZR (1<<5) // zero
|
|
#define PAC_MOD_X16 (2<<5) // in X16
|
|
#define PAC_MOD_SP (3<<5) // in SP
|
|
|
|
//----------------------------------------------------------------------
|
|
// build a suffix for a PAC instruction
|
|
static inline bool get_pac_suffix(qstring *suf, const insn_t &insn)
|
|
{
|
|
//
|
|
int key = insn.pac_flags & PAC_KEYMASK;
|
|
const char *id = NULL, *ab = NULL;
|
|
switch ( key )
|
|
{
|
|
case PAC_KEY_IA: id= "I"; ab="A"; break;
|
|
case PAC_KEY_IB: id= "I"; ab="B"; break;
|
|
case PAC_KEY_DA: id= "D"; ab="A"; break;
|
|
case PAC_KEY_DB: id= "D"; ab="B"; break;
|
|
case PAC_KEY_GA: id= "G"; ab="A"; break;
|
|
default: return false;
|
|
}
|
|
int adr = insn.pac_flags & PAC_ADRMASK;
|
|
int mod = insn.pac_flags & PAC_MODMASK;
|
|
bool pre_ab = adr == PAC_ADR_X30 || adr == PAC_ADR_X17;
|
|
switch ( insn.itype )
|
|
{
|
|
case ARM_pac:
|
|
case ARM_aut:
|
|
suf->append(id);
|
|
if ( pre_ab )
|
|
suf->append(ab);
|
|
|
|
if ( adr == PAC_ADR_X17 )
|
|
suf->append("17");
|
|
else if ( adr != PAC_ADR_X30 && adr != PAC_ADR_GPR )
|
|
return false;
|
|
|
|
if ( mod == PAC_MOD_SP )
|
|
suf->append("SP");
|
|
else if ( mod == PAC_MOD_ZR )
|
|
suf->append("Z");
|
|
else if ( mod == PAC_MOD_X16 )
|
|
suf->append("16");
|
|
else if ( mod != PAC_MOD_GPR )
|
|
return false;
|
|
if ( !pre_ab )
|
|
suf->append(ab);
|
|
break;
|
|
case ARM_xpac:
|
|
if ( adr == PAC_ADR_X30 )
|
|
suf->append("LR");// XPACLRI
|
|
else if ( adr != PAC_ADR_GPR )
|
|
return 0;
|
|
suf->append(id); // XPACD, XPACI,
|
|
break;
|
|
default:
|
|
// AA, AB, AAZ, ABZ
|
|
suf->append("A");
|
|
suf->append(ab);
|
|
if ( mod == PAC_MOD_ZR )
|
|
suf->append("Z");
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------
|
|
// is insn PACIASP or PACIBSP ?
|
|
static inline bool is_paci_sp(const insn_t &insn)
|
|
{
|
|
if ( insn.itype != ARM_pac )
|
|
return false;
|
|
int adr = insn.pac_flags & PAC_ADRMASK;
|
|
int mod = insn.pac_flags & PAC_MODMASK;
|
|
int key = insn.pac_flags & PAC_KEYMASK;
|
|
if ( adr != PAC_ADR_X30
|
|
|| mod != PAC_MOD_SP )
|
|
return false;
|
|
return key == PAC_KEY_IA || key == PAC_KEY_IB;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
enum RegNo
|
|
{
|
|
R0, R1, R2, R3, R4, R5, R6, R7,
|
|
R8, R9, R10, R11, R12, R13, R14, R15,
|
|
CPSR, CPSR_flg,
|
|
SPSR, SPSR_flg,
|
|
T, rVcs, rVds, // virtual registers for code and data segments
|
|
Racc0, // Intel xScale coprocessor accumulator
|
|
FPSID, FPSCR, FPEXC, // VFP system registers
|
|
FPINST, FPINST2, MVFR0, MVFR1,
|
|
// msr system registers
|
|
SYSM_APSR,
|
|
SYSM_IAPSR,
|
|
SYSM_EAPSR,
|
|
SYSM_XPSR,
|
|
SYSM_IPSR,
|
|
SYSM_EPSR,
|
|
SYSM_IEPSR,
|
|
SYSM_MSP,
|
|
SYSM_PSP,
|
|
SYSM_PRIMASK,
|
|
SYSM_BASEPRI,
|
|
SYSM_BASEPRI_MAX,
|
|
SYSM_FAULTMASK,
|
|
SYSM_CONTROL,
|
|
Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7,
|
|
Q8, Q9, Q10, Q11, Q12, Q13, Q14, Q15,
|
|
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,
|
|
S0, S1, S2, S3, S4, S5, S6, S7,
|
|
S8, S9, S10, S11, S12, S13, S14, S15,
|
|
S16, S17, S18, S19, S20, S21, S22, S23,
|
|
S24, S25, S26, S27, S28, S29, S30, S31,
|
|
FIRST_FPREG=Q0,
|
|
LAST_FPREG=S31,
|
|
CF, ZF, NF, VF,
|
|
|
|
// AArch64 registers
|
|
// general-purpose registers
|
|
X0, X1, X2, X3, X4, X5, X6, X7,
|
|
X8, X9, X10, X11, X12, X13, X14, X15,
|
|
X16, X17, X18, X19, X20, X21, X22, X23,
|
|
X24, X25, X26, X27, X28,
|
|
X29, XFP = X29, // frame pointer
|
|
X30, XLR = X30, // link register
|
|
|
|
XZR, // zero register (special case of GPR=31)
|
|
|
|
XSP, // stack pointer (special case of GPR=31)
|
|
|
|
XPC, // PC (not available as actual register)
|
|
|
|
// 128-bit SIMD registers
|
|
V0, V1, V2, V3, V4, V5, V6, V7,
|
|
V8, V9, V10, V11, V12, V13, V14, V15,
|
|
V16, V17, V18, V19, V20, V21, V22, V23,
|
|
V24, V25, V26, V27, V28, V29, V30, V31,
|
|
|
|
ARM_MAXREG, // must be the last entry
|
|
};
|
|
|
|
// we use specflag1 to store register values
|
|
// so they must fit into a byte
|
|
CASSERT(ARM_MAXREG < 0x100);
|
|
|
|
extern const char *const arm_regnames[];
|
|
|
|
//------------------------------------------------------------------
|
|
// r0 * argument word/integer result
|
|
// r1-r3 argument word
|
|
//
|
|
// r4-r8 S register variable
|
|
// r9 S (rfp) register variable (real frame pointer)
|
|
//
|
|
// r10 F S (sl) stack limit (used by -mapcs-stack-check)
|
|
// r11 F S (fp) argument pointer
|
|
// r12 (ip) temp workspace
|
|
// r13 F S (sp) lower end of current stack frame
|
|
// r14 (lr) link address/workspace
|
|
// r15 F (pc) program counter
|
|
//
|
|
// f0 floating point result
|
|
// f1-f3 floating point scratch
|
|
//
|
|
// f4-f7 S floating point variable
|
|
|
|
#define PC R15
|
|
#define LR R14
|
|
#define SP R13
|
|
#define FP R11
|
|
#define FP2 R7 // in thumb mode
|
|
|
|
inline int getreg(const op_t &x)
|
|
{
|
|
return x.type == o_reg
|
|
|| x.type == o_shreg
|
|
&& x.shtype == LSL
|
|
&& x.shcnt == 0 ? x.reg : -1;
|
|
}
|
|
|
|
inline bool isreg(const op_t &x, int reg)
|
|
{
|
|
return getreg(x) == reg;
|
|
}
|
|
|
|
// is it simply [Rx, Ry]?
|
|
// no shift, no negation, no post-index, no writeback
|
|
inline bool is_simple_phrase(const insn_t &insn, const op_t &x)
|
|
{
|
|
return x.type == o_phrase
|
|
&& x.shtype == LSL
|
|
&& x.shcnt == 0
|
|
&& (insn.auxpref & (aux_negoff|aux_postidx|aux_wback)) == 0;
|
|
}
|
|
|
|
inline bool issp(const op_t &x) { return isreg(x, SP) || isreg(x, XSP); }
|
|
inline bool issp(int reg) { return reg == SP || reg == XSP; }
|
|
|
|
#define is_a64reg(reg) ((reg) >= X0 && (reg) < ARM_MAXREG)
|
|
|
|
inline bool is_gr(int reg)
|
|
{
|
|
return reg >= R0 && reg <= R14
|
|
|| reg >= X0 && reg <= X30;
|
|
}
|
|
|
|
inline bool is_thread_id_sysreg(const insn_t &insn, int op)
|
|
{
|
|
// assert: insn.ops[op+0].type == o_imm
|
|
// && insn.ops[op+1].type == o_creg
|
|
// && insn.ops[op+2].type == o_creg
|
|
// && insn.ops[op+3].type == o_imm
|
|
// TPIDR_EL0
|
|
return insn.ops[op+0].value == 3
|
|
&& insn.ops[op+1].reg == 13
|
|
&& insn.ops[op+2].reg == 0
|
|
&& insn.ops[op+3].value == 2;
|
|
}
|
|
|
|
// is callee-saved (preserved) register? (according to Procedure Call Standard)
|
|
/* Procedure Call Standard for the ARM Architecture:
|
|
5.1.1. A subroutine must preserve the contents of the registers r4-r8,
|
|
r10, r11 and SP (and r9 in PCS variants that designate r9 as v6).
|
|
Procedure Call Standard for the ARM 64-bit Architecture:
|
|
5.1.1. A subroutine invocation must preserve the contents of the
|
|
registers r19-r29 and SP.
|
|
*/
|
|
inline bool is_callee_saved_gr(int reg)
|
|
{
|
|
return reg >= R4 && reg <= R11
|
|
|| reg >= X19 && reg <= X29;
|
|
}
|
|
/* Procedure Call Standard for the ARM Architecture:
|
|
5.1.2.1 Registers s16-s31 (d8-d15, q4-q7) must be preserved across
|
|
subroutine calls; registers s0-s15 (d0-d7, q0-q3) do not need to be
|
|
preserved (and can be used for passing arguments or returning results in
|
|
standard procedure-call variants)
|
|
Procedure Call Standard for the ARM 64-bit Architecture:
|
|
5.1.2. Registers v8-v15 must be preserved by a callee across subroutine
|
|
calls.
|
|
*/
|
|
inline bool is_callee_saved_vr(int reg)
|
|
{
|
|
return reg >= S16 && reg <= S31
|
|
|| reg >= D8 && reg <= D15
|
|
|| reg >= Q4 && reg <= Q7
|
|
|| reg >= V8 && reg <= V15;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// get full value of the immediate operand
|
|
// (performing optional shift operator)
|
|
inline uval_t get_immfull(const op_t &x)
|
|
{
|
|
// FIXME support other types of shift
|
|
return x.type != o_imm ? 0
|
|
: x.value == 0 ? 0
|
|
: x.ishcnt == 0 ? x.value
|
|
: x.ishtype == LSL ? x.value << x.ishcnt
|
|
: x.ishtype == MSL ? ((x.value + 1) << x.ishcnt) - 1
|
|
: 0;
|
|
}
|
|
//----------------------------------------------------------------------
|
|
// check if 'reg' is present in 'reglist' (only ARM32 GPRs supported!)
|
|
inline bool in_reglist(uint32 reglist, int reg)
|
|
{
|
|
return (reg <= R15) && (reglist & (1u << reg)) != 0;
|
|
}
|
|
//----------------------------------------------------------------------
|
|
// calculate the total number of bytes represented by a register list
|
|
inline uval_t calc_reglist_size(uint32 reglist)
|
|
{
|
|
return uval_t(4) * bitcount(reglist & 0xFFFF);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// find out pre- or post-indexed addressing mode of given operand <op>;
|
|
// if <delta> arg is specified than we check o_displ operand only and
|
|
// return register offset in <delta>;
|
|
// returns base register or -1
|
|
inline int get_pre_post_delta(const insn_t &insn, const op_t &op, sval_t *delta = NULL)
|
|
{
|
|
if ( (insn.auxpref & (aux_wback | aux_postidx)) != 0
|
|
&& (op.type == o_displ && op.addr != 0
|
|
|| op.type == o_phrase && delta == NULL) )
|
|
{
|
|
if ( delta != NULL )
|
|
*delta = op.addr;
|
|
return op.reg;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
//------------------------------------------------------------------
|
|
// PSR format:
|
|
// bit name description
|
|
// 0 M0 M4..M0 are mode bits:
|
|
// 1 M1 10000 User
|
|
// 2 M2 10001 FIQ (fast interrupt request)
|
|
// 3 M3 10010 IRQ (interrupt request)
|
|
// 4 M4 10011 Supervisor
|
|
// 10110 Monitor (security extensions)
|
|
// 10111 Abort
|
|
// 11010 Hyp (Hypervisor; virtualization extensions)
|
|
// 11011 Undefined
|
|
// 11111 System
|
|
// 5 T Thumb state
|
|
// 6 F FIQ disable
|
|
// 7 I IRQ disable
|
|
// 8 A Asynchronous abort disable
|
|
// 9 E Endianness (0=little endian, 1=big endian)
|
|
// 10 IT2 IT7...IT0 If-Then execution state bits (ITSTATE)
|
|
// 11 IT3
|
|
// 12 IT4
|
|
// 13 IT5
|
|
// 14 IT6
|
|
// 15 IT7
|
|
// 16 GE0 GE3..GE0 Greater than or Equal flags (for SIMD instructions)
|
|
// 17 GE1
|
|
// 18 GE2
|
|
// 19 GE3
|
|
// 24 J Jazelle state
|
|
// 25 IT0
|
|
// 26 IT1
|
|
// 27 Q Cumulative saturation flag
|
|
// 28 V Overflow
|
|
// 29 C Carry/Borrow/Extend
|
|
// 30 Z Zero
|
|
// 31 N Negative/Less Than
|
|
|
|
enum arm_mode_t
|
|
{
|
|
M_usr = B8(10000),
|
|
M_fiq = B8(10001),
|
|
M_irq = B8(10010),
|
|
M_svc = B8(10011),
|
|
M_mon = B8(10110),
|
|
M_abt = B8(10111),
|
|
M_hyp = B8(11010),
|
|
M_und = B8(11011),
|
|
M_sys = B8(11111),
|
|
};
|
|
|
|
//------------------------------------------------------------------
|
|
// Vector summary:
|
|
// Address Exception Mode on Entry
|
|
// ------- --------- -------------
|
|
// 0000 Reset Supervisor
|
|
// 0004 Undefined instruction Undefined
|
|
// 0008 Software interrupt Supervisor
|
|
// 000C Abort (prefetch) Abort
|
|
// 0010 Abort (data) Abort
|
|
// 0014 Hypervisor trap Hyp
|
|
// 0018 IRQ IRQ
|
|
// 001C FIQ FIQ
|
|
|
|
//------------------------------------------------------------------
|
|
// Condition codes:
|
|
enum cond_t
|
|
{
|
|
cEQ, // 0000 Z Equal
|
|
cNE, // 0001 !Z Not equal
|
|
cCS, // 0010 C Unsigned higher or same
|
|
cCC, // 0011 !C Unsigned lower
|
|
cMI, // 0100 N Negative
|
|
cPL, // 0101 !N Positive or Zero
|
|
cVS, // 0110 V Overflow
|
|
cVC, // 0111 !V No overflow
|
|
cHI, // 1000 C & !Z Unsigned higher
|
|
cLS, // 1001 !C | Z Unsigned lower or same
|
|
cGE, // 1010 (N & V) | (!N & !V) Greater or equal
|
|
cLT, // 1011 (N & !V) | (!N & V) Less than
|
|
cGT, // 1100 !Z & ((N & V)|(!N & !V)) Greater than
|
|
cLE, // 1101 Z | (N & !V) | (!N & V) Less than or equal
|
|
cAL, // 1110 Always
|
|
cNV, // 1111 Never
|
|
cLAST
|
|
};
|
|
inline cond_t get_cond(const insn_t &insn)
|
|
{
|
|
return cond_t(insn.cond);
|
|
}
|
|
inline bool has_cond(const insn_t &insn)
|
|
{
|
|
return insn.cond != cAL;
|
|
}
|
|
inline bool is_negated_cond(cond_t cond)
|
|
{
|
|
return (cond & 1) != 0;
|
|
}
|
|
inline cond_t invert_cond(cond_t cond)
|
|
{
|
|
if ( cond < cLAST )
|
|
return cond_t(cond ^ 1);
|
|
return cLAST;
|
|
}
|
|
inline cond_t get_op_cond(const op_t &x)
|
|
{
|
|
// assert: x.type == o_cond
|
|
return cond_t(x.value & 0xF);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// see ARMExpandImm_C/ThumbExpandImm_C in ARM ARM
|
|
inline bool may_set_carry(ushort itype)
|
|
{
|
|
switch ( itype )
|
|
{
|
|
case ARM_and:
|
|
case ARM_bic:
|
|
case ARM_eor:
|
|
case ARM_mov:
|
|
case ARM_mvn:
|
|
case ARM_orn:
|
|
case ARM_orr:
|
|
case ARM_teq:
|
|
case ARM_tst:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// if true, then ASPR.C is set to bit 31 of the immediate constant
|
|
inline bool imm_sets_carry(const insn_t &insn)
|
|
{
|
|
switch ( insn.itype )
|
|
{
|
|
case ARM_and:
|
|
case ARM_bic:
|
|
case ARM_eor:
|
|
case ARM_mov:
|
|
case ARM_mvn:
|
|
case ARM_orn:
|
|
case ARM_orr:
|
|
// flags are updated if S suffix is used
|
|
return (insn.auxpref & (aux_immcarry|aux_cond)) == (aux_immcarry|aux_cond);
|
|
case ARM_teq:
|
|
case ARM_tst:
|
|
// these two always update flags
|
|
return (insn.auxpref & aux_immcarry) != 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
struct pushreg_t
|
|
{
|
|
ea_t ea; // instruction ea
|
|
uval_t off; // offset from the frame top (sp delta)
|
|
uval_t width; // size of allocated area in bytes
|
|
int reg; // register number (-1 means stack space allocation)
|
|
DECLARE_COMPARISONS(pushreg_t);
|
|
};
|
|
typedef qvector<pushreg_t> pushregs_t;
|
|
|
|
struct pushinfo_t : public pushregs_t
|
|
{
|
|
enum { PUSHINFO_VERSION = 2 };
|
|
uint32 flags;
|
|
#define APSI_VARARG 0x01 // is vararg function?
|
|
#define APSI_FIRST_VARG_MASK 0x06 // index of the first register in push {rx..r3}
|
|
#define APSI_HAVE_SSIZE 0x08 // pushinfo_t structure contains its own size (field 'cb')
|
|
#define APSI_OFFSET_WO_DELTA 0x10 // do not use delta-coding for <off>
|
|
inline int get_first_vararg_reg(void) { return (flags & APSI_FIRST_VARG_MASK) >> 1; }
|
|
uval_t savedregs; // size of the 'saved regs' area
|
|
eavec_t prolog_insns; // additional prolog instruction addresses
|
|
// (in addition to instructions from pushregs_t)
|
|
uval_t fpd; // frame pointer delta
|
|
|
|
int cb; // size of this structure (it would be better if
|
|
// this field was the first one)
|
|
|
|
// vararg info
|
|
uval_t gr_top; // offset from the frame top general registers
|
|
// vararg save area
|
|
uval_t vr_top; // offset from the frame top FP/SIMD registers
|
|
// vararg save area
|
|
uval_t gr_width; // size of general registers vararg save area
|
|
uval_t vr_width; // size of FP/SIMD registers vararg save area
|
|
|
|
pushinfo_t(void)
|
|
: flags(APSI_HAVE_SSIZE),
|
|
savedregs(0), fpd(0),
|
|
cb(sizeof(pushinfo_t)),
|
|
gr_top(0), vr_top(0), gr_width(0), vr_width(0)
|
|
{}
|
|
DECLARE_COMPARISONS(pushinfo_t);
|
|
|
|
void serialize(bytevec_t *packed, ea_t ea) const;
|
|
bool deserialize(memory_deserializer_t *mmdsr, ea_t ea);
|
|
|
|
void save_to_idb(arm_t &pm, ea_t ea) const;
|
|
bool restore_from_idb(arm_t &pm, ea_t ea);
|
|
|
|
void mark_prolog_insns(arm_t &pm);
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
enum arm_base_arch_t
|
|
{
|
|
// values taken from ARM IHI 0045C, Tag_CPU_arch
|
|
arch_ARM_old = 0, // Pre-v4
|
|
arch_ARMv4 = 1, // e.g. SA110
|
|
arch_ARMv4T = 2, // e.g. ARM7TDMI
|
|
arch_ARMv5T = 3, // e.g. ARM9TDMI
|
|
arch_ARMv5TE = 4, // e.g. ARM946E-S
|
|
arch_ARMv5TEJ = 5, // e.g. ARM926EJ-S
|
|
arch_ARMv6 = 6, // e.g. ARM1136J-S
|
|
arch_ARMv6KZ = 7, // e.g. ARM1176JZ-S
|
|
arch_ARMv6T2 = 8, // e.g. ARM1156T2F-S
|
|
arch_ARMv6K = 9, // e.g. ARM1136J-S
|
|
arch_ARMv7 = 10, // e.g. Cortex A8, Cortex M3
|
|
arch_ARMv6M = 11, // e.g. Cortex M1
|
|
arch_ARMv6SM = 12, // v6-M with the System extensions
|
|
arch_ARMv7EM = 13, // v7-M with DSP extensions
|
|
arch_ARMv8 = 14, // v8
|
|
arch_curr_max = arch_ARMv8,
|
|
arch_ARM_meta = 9999, // decode everything
|
|
};
|
|
|
|
enum arm_arch_profile_t
|
|
{
|
|
arch_profile_unkn = 0, // Architecture profile is not applicable (e.g. pre v7, or cross-profile code)
|
|
arch_profile_A = 'A', // The application profile (e.g. for Cortex A8)
|
|
arch_profile_R = 'R', // The real-time profile (e.g. for Cortex R4)
|
|
arch_profile_M = 'M', // The microcontroller profile (e.g. for Cortex M3)
|
|
arch_profile_S = 'S', // Application or real-time profile (i.e. the 'classic' programmer's model)
|
|
};
|
|
|
|
enum fp_arch_t
|
|
{
|
|
fp_arch_none = 0, // The user did not permit this entity to use instructions requiring FP hardware
|
|
fp_arch_v1 = 1, // The user permitted use of instructions from v1 of the floating point (FP) ISA
|
|
fp_arch_v2 = 2, // Use of the v2 FP ISA was permitted (implies use of the v1 FP ISA)
|
|
fp_arch_v3 = 3, // Use of the v3 FP ISA was permitted (implies use of the v2 FP ISA)
|
|
fp_arch_v3_16 = 4, // Use of the v3 FP ISA was permitted, but only citing registers D0-D15, S0-S31
|
|
fp_arch_v4 = 5, // Use of the v4 FP ISA was permitted (implies use of the non-vector v3 FP ISA)
|
|
fp_arch_v4_16 = 6, // Use of the v4 FP ISA was permitted, but only citing registers D0-D15, S0-S31
|
|
fp_arch_v8 = 7, // Use of the ARM v8-A FP ISA was permitted
|
|
fp_arch_v8_16 = 8, // Use of the ARM v8-A FP ISA was permitted, but only citing registers D0-D15, S0-S31
|
|
};
|
|
|
|
enum adv_simd_arch_t
|
|
{
|
|
adv_simd_arch_none = 0, // The user did not permit this entity to use the Advanced SIMD Architecture (Neon)
|
|
adv_simd_arch_base = 1, // Use of the Advanced SIMD Architecture (Neon) was permitted
|
|
adv_simd_arch_fma = 2, // Use of Advanced SIMD Architecture (Neon) with fused MAC operations was permitted
|
|
adv_simd_arch_v8 = 3, // Use of the ARM v8-A Advanced SIMD Architecture (Neon) was permitted
|
|
};
|
|
|
|
struct arm_arch_t
|
|
{
|
|
arm_base_arch_t base_arch;
|
|
arm_arch_profile_t profile;
|
|
fp_arch_t fp_arch;
|
|
adv_simd_arch_t neon_arch;
|
|
|
|
int arm_isa_use; // 0 = no ARM instructions (e.g. v7-M)
|
|
// 1 = allow ARM instructions
|
|
int thumb_isa_use; // 0 = no Thumb instructions
|
|
// 1 = 16-bit Thumb instructions + BL
|
|
// 2 = plus 32-bit Thumb instructions
|
|
int xscale_arch; // 0 = no XScale extension
|
|
// 1 = XScale extension (MAR/MRA etc)
|
|
int wmmx_arch; // 0 = no WMMX
|
|
// 1 = WMMX v1
|
|
// 2 = WMMX v2
|
|
int hp_ext; // 0 = no half-precision extension
|
|
// 1 = VFPv3/Advanced SIMD optional half-precision extension
|
|
int t2_ee; // 0 = no Thumb2-EE extension
|
|
// 1 = Thumb2-EE extension (ENTERX and LEAVEX)
|
|
qstring arch_name; // e.g. ARMv7-M
|
|
qstring core_name; // e.g. ARM1176JZF-S
|
|
|
|
bool be8; // image is BE-8, i.e. little-endian code but big-endian data
|
|
bool arm64_32; // 32-bit address space, ARM64 instructions
|
|
|
|
static const char *get_canonical_name(unsigned archno);
|
|
bool set_from_name(const char *name); // arch name or core name
|
|
bool set_options(const char *opts); // semicolon-delimited option string
|
|
void save_to_idb(arm_t &pm) const;
|
|
bool restore_from_idb(arm_t &pm);
|
|
qstring to_string() const;
|
|
arm_arch_t(void)
|
|
: base_arch(arch_ARM_old),
|
|
profile(arch_profile_unkn),
|
|
fp_arch(fp_arch_none),
|
|
neon_arch(adv_simd_arch_none),
|
|
arm_isa_use(0),
|
|
thumb_isa_use(0),
|
|
xscale_arch(0),
|
|
wmmx_arch(0),
|
|
hp_ext(0),
|
|
t2_ee(0),
|
|
be8(false),
|
|
arm64_32(false)
|
|
{}
|
|
|
|
bool is_mprofile()
|
|
{
|
|
if ( profile == arch_profile_unkn )
|
|
{
|
|
return base_arch >= arch_ARMv6M && base_arch <= arch_ARMv7EM;
|
|
}
|
|
return profile == arch_profile_M;
|
|
}
|
|
};
|
|
|
|
//------------------------------------------------------------------
|
|
struct mmtype_t
|
|
{
|
|
const char *name;
|
|
const type_t *type;
|
|
const type_t *fields;
|
|
tinfo_t tif;
|
|
};
|
|
|
|
//------------------------------------------------------------------
|
|
void term_ana(void);
|
|
void move_it_blocks(ea_t from, ea_t to, asize_t size);
|
|
int get_it_size(const insn_t &insn);
|
|
|
|
uint64 expand_imm_vfp(uint8 imm8, int sz);
|
|
bool idaapi equal_ops(const op_t &x, const op_t &y);
|
|
|
|
int may_be_func(const insn_t &insn);
|
|
bool is_branch_insn(const insn_t &insn);
|
|
bool is_push_insn(const insn_t &insn, uint32 *reglist=NULL);
|
|
bool is_pop_insn(const insn_t &insn, uint32 *reglist=NULL, bool allow_ed=false);
|
|
int arm_create_switch_xrefs(ea_t insn_ea, const switch_info_t &si);
|
|
void mark_arm_codeseqs(void);
|
|
bool can_resolve_seg(ea_t ea);
|
|
#if defined(NALT_HPP) && defined(_XREF_HPP)
|
|
int arm_calc_switch_cases(casevec_t *casevec, eavec_t *targets, ea_t insn_ea, const switch_info_t &si);
|
|
#endif
|
|
|
|
bool create_func_frame64(func_t *pfn, bool reanalyze);
|
|
int arm_get_frame_retsize(const func_t *pfn);
|
|
bool get_insn_op_literal(const insn_t &insn, const op_t &x, ea_t ea, void *value, bool force=false);
|
|
|
|
void use_arm_arg_types(
|
|
ea_t ea,
|
|
func_type_data_t *fti,
|
|
funcargvec_t *rargs);
|
|
|
|
//----------------------------------------------------------------------
|
|
typedef const regval_t &idaapi getreg_t(const char *name, const regval_t *regvalues);
|
|
|
|
ea_t arm_calc_step_over(ea_t ip);
|
|
ea_t arm_get_macro_insn_head(ea_t ip);
|
|
int arm_get_dbr_opnum(const insn_t &insn);
|
|
ssize_t arm_get_reg_name(qstring *buf, int _reg, size_t width, int reghi);
|
|
ssize_t arm_get_one_reg_name(qstring *buf, int _reg, size_t width);
|
|
bool ana_neon(insn_t &insn, uint32 code, bool thumb);
|
|
void opimm_vfp(op_t &x, uint32 imm8, int sz);
|
|
void ana_hint(insn_t &insn, int hint);
|
|
int get_it_info(ea_t ea);
|
|
int arm_get_reg_index(const char *name, bool as_mainreg, bitrange_t *pbitrange, bool is_a64);
|
|
|
|
//======================================================================
|
|
// common inline functions used by analyzer
|
|
//----------------------------------------------------------------------
|
|
inline void oreglist(op_t &x, int regs)
|
|
{
|
|
x.type = o_reglist;
|
|
x.dtype = dt_dword;
|
|
x.reglist = regs;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
inline void onear(op_t &x, uval_t target, bool arm64=false)
|
|
{
|
|
x.type = o_near;
|
|
x.dtype = dt_code;
|
|
x.addr = arm64 ? target : uint32(target);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
inline void otext(op_t &x, const char *txt)
|
|
{
|
|
x.type = o_text;
|
|
qstrncpy((char *)&x.value, txt, sizeof(x) - qoffsetof(op_t, value));
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Get register number
|
|
inline uchar getreg(uint32 code, int lbit)
|
|
{
|
|
return uchar((code >> lbit) & 0xF);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create operand of register type
|
|
inline void fillreg(op_t &x, uint32 code, int lbit)
|
|
{
|
|
x.reg = getreg(code, lbit);
|
|
x.type = o_reg;
|
|
x.dtype = dt_dword;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
inline void opreg(op_t &x, int rgnum)
|
|
{
|
|
x.reg = uint16(rgnum);
|
|
x.type = o_reg;
|
|
x.dtype = dt_dword;
|
|
}
|
|
|
|
struct reg_mode_t
|
|
{
|
|
uint16 reg;
|
|
uchar mode;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create operand of banked_reg type
|
|
extern const reg_mode_t banked0[32];
|
|
extern const reg_mode_t banked1[32];
|
|
inline bool opbanked(op_t &x, int R, uchar sysm)
|
|
{
|
|
const reg_mode_t &rm = R ? banked1[sysm] : banked0[sysm];
|
|
if ( rm.reg == 0xFFFF )
|
|
return false;
|
|
x.reg = rm.reg;
|
|
x.specflag1 = rm.mode | BANKED_MODE;
|
|
x.type = o_reg;
|
|
x.dtype = dt_dword;
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create operand of immediate type
|
|
inline void op_imm(op_t &x, uval_t value)
|
|
{
|
|
x.type = o_imm;
|
|
x.dtype = dt_dword;
|
|
x.value = value;
|
|
x.ishtype = LSL;
|
|
x.ishcnt = 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// Create operand of immediate type (4 bits)
|
|
inline void op_imm4(op_t &x, uint32 code, int lbit)
|
|
{
|
|
op_imm(x, getreg(code, lbit));
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
inline void barrier_op(op_t &x, int code)
|
|
{
|
|
const char *op = NULL;
|
|
switch ( code )
|
|
{
|
|
case B8(0001): op = "OSHLD"; break;
|
|
case B8(0010): op = "OSHST"; break;
|
|
case B8(0011): op = "OSH"; break;
|
|
case B8(0101): op = "NSHLD"; break;
|
|
case B8(0110): op = "NSHST"; break;
|
|
case B8(0111): op = "NSH"; break;
|
|
case B8(1001): op = "ISHLD"; break;
|
|
case B8(1010): op = "ISHST"; break;
|
|
case B8(1011): op = "ISH"; break;
|
|
case B8(1101): op = "LD"; break;
|
|
case B8(1110): op = "ST"; break;
|
|
case B8(1111): op = "SY"; break;
|
|
}
|
|
if ( op != NULL )
|
|
otext(x, op);
|
|
else
|
|
op_imm(x, code & 0xF);
|
|
}
|
|
|
|
// is the current insn inside an it-block?
|
|
inline bool inside_itblock(int itcnd)
|
|
{
|
|
return itcnd != -1;
|
|
}
|
|
|
|
//======================================================================
|
|
// common data sructures and functions for emulator
|
|
//----------------------------------------------------------------------
|
|
|
|
// since these is a lot of recursion in this module, we will keep
|
|
// all data as local as possible. no static data since we will have
|
|
// to save/restore it a lot
|
|
struct arm_saver_t
|
|
{
|
|
arm_t ±
|
|
insn_t insn; // current instruction
|
|
bool flow;
|
|
|
|
arm_saver_t(arm_t &_pm) : pm(_pm), insn(), flow(true) {}
|
|
arm_saver_t(arm_t &_pm, const insn_t &insn_) : pm(_pm), insn(insn_), flow(true) {}
|
|
|
|
void handle_operand(const op_t &x, bool isload);
|
|
void emulate(void);
|
|
void handle_code_ref(const op_t &x, ea_t ea, bool iscall);
|
|
ea_t match_arm64_glue(void);
|
|
bool detect_glue_code(
|
|
ea_t ea,
|
|
int flags,
|
|
#define DGC_HANDLE 0x0001 // create offsets and names
|
|
#define DGC_FIND 0x0002 // ea points to the end of the glue code
|
|
ea_t *p_target = NULL,
|
|
ea_t *p_fptr = NULL,
|
|
size_t *p_glue_size = NULL);
|
|
bool detect_glue_code(
|
|
int flags,
|
|
ea_t *p_target = NULL,
|
|
ea_t *p_fptr = NULL,
|
|
size_t *p_glue_size = NULL);
|
|
bool arm_is_switch(void);
|
|
// try to decode instruction at ea as arm or thumb
|
|
//-V:try_decode:501 identical sub-expressions
|
|
bool try_decode(ea_t ea, bool is_thumb, bool check_sane = true);
|
|
};
|
|
|
|
// SPD value for security_push_cookie/security_pop_cookie
|
|
inline sval_t security_cookie_spd()
|
|
{
|
|
return inf_is_64bit() ? 0x10 : 0x4;
|
|
}
|
|
|
|
// SPD value for a special function
|
|
inline sval_t special_func_spd(special_func_t spf)
|
|
{
|
|
return spf == SPF_GNU_MCOUNT_NC ? 4
|
|
: spf == SPF_SECURITY_POP_COOKIE ? security_cookie_spd()
|
|
: spf == SPF_SECURITY_PUSH_COOKIE ? -security_cookie_spd()
|
|
: 0;
|
|
}
|
|
|
|
int calc_fpreglist_size(const insn_t &ins);
|
|
int calc_advsimdlist_size(const insn_t &ins);
|
|
|
|
// return true if 'x' is 'const' in LDR Rd, =const
|
|
inline bool is_ldr_literal(const insn_t &insn, const op_t &x)
|
|
{
|
|
return x.type == o_mem
|
|
&& (insn.itype == ARM_ldr || insn.itype == ARM_ldrpc
|
|
|| insn.itype == ARM_ldur || insn.itype == ARM_ldxr
|
|
|| insn.itype == ARM_fldd || insn.itype == ARM_flds
|
|
|| insn.itype == ARM_vldr);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
inline bool creglist_is_crd_cref(const insn_t &insn)
|
|
{
|
|
return insn.itype == ARM_cdp || insn.itype == ARM_cdp2;
|
|
}
|
|
inline bool creglist_is_crn_cref(const insn_t &insn)
|
|
{
|
|
return insn.itype != ARM_mcrr
|
|
&& insn.itype != ARM_mrrc
|
|
&& insn.itype != ARM_mcrr2
|
|
&& insn.itype != ARM_mrrc2;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// tuning parameters for reg_tracker_t
|
|
struct regtrack_info_t
|
|
{
|
|
uint16 vals_size; // max number of possible values returned by
|
|
// find_op_values, find_reg_values;
|
|
// 0 - no limit;
|
|
uint16 op_recursion; // max level of recursion when calculating
|
|
// operands of the emulated instruction, e.g.
|
|
// LDR R0, =addr
|
|
// LDR R0, [R0,#4]
|
|
// ADD R0, #4
|
|
// needs 2 levels (one for addressing, one for
|
|
// addition);
|
|
// 0 - no limit;
|
|
uint16 bblk_recursion; // max level of recursion when reconstructing
|
|
// the execution flow;
|
|
// 0 - no limit;
|
|
uint16 cache_size; // max size of the cache for one register;
|
|
// 0 - no limit;
|
|
uint16 max_xrefs; // max number of xrefs to analyze
|
|
// 0 - no limit;
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
// Since we have to know if an instruction belongs to an IT-block or not,
|
|
// we keep all this information in a map
|
|
struct it_info_t
|
|
{
|
|
uint8 size;
|
|
uint8 cond;
|
|
uint8 mask;
|
|
it_info_t(void) : size(0), cond(0), mask(0) {}
|
|
it_info_t(uval_t x) :
|
|
size(x&0xFF),
|
|
cond((x >> 8)&0xFF),
|
|
mask((x >> 16)&0xFF)
|
|
{
|
|
}
|
|
it_info_t(uint8 s, uint8 c, uint8 m) : size(s), cond(c), mask(m) {}
|
|
operator uval_t(void) const { return size | (cond << 8) | (mask << 16); }
|
|
};
|
|
using it_blocks_t = std::map<ea_t, it_info_t>; // first_it_block_ea -> info
|
|
|
|
//------------------------------------------------------------------
|
|
#if defined(__LINUX__) && defined(__ARM__) || defined(ENABLE_LOWCNDS)
|
|
inline bool is_arm64_ea(ea_t ea)
|
|
{
|
|
qnotused(ea);
|
|
#if defined(__EA64__)
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
//------------------------------------------------------------------
|
|
#define BL_FORCE_JUMP 0
|
|
#define BL_FORCE_CALL 1
|
|
bool idaapi arm_bl_force(ea_t ea, int option, bool hide_errors);
|
|
|
|
struct bl_force_flow_ah_t : public action_handler_t
|
|
{
|
|
bool is_jump;
|
|
bl_force_flow_ah_t(bool _is_jump) : is_jump(_is_jump) {}
|
|
virtual int idaapi activate(action_activation_ctx_t *) override
|
|
{
|
|
return arm_bl_force(
|
|
get_screen_ea(),
|
|
is_jump ? BL_FORCE_JUMP : BL_FORCE_CALL,
|
|
false);
|
|
}
|
|
|
|
virtual action_state_t idaapi update(action_update_ctx_t *) override
|
|
{
|
|
return AST_ENABLE_ALWAYS;
|
|
}
|
|
};
|
|
|
|
//------------------------------------------------------------------
|
|
struct reg_tracker_t;
|
|
struct opinfo_helpers_t;
|
|
struct cfh_t;
|
|
struct rvi_vec_t;
|
|
|
|
DECLARE_PROC_LISTENER(pm_idb_listener_t, struct arm_t);
|
|
|
|
#define PROCMOD_T arm_t
|
|
struct arm_t : public procmod_t
|
|
{
|
|
arm_t();
|
|
~arm_t();
|
|
|
|
pm_idb_listener_t idb_listener = pm_idb_listener_t(*this);
|
|
struct eh_parse_t *eh_parse = nullptr;
|
|
|
|
//----------------------------------------------------------------------
|
|
netnode helper; // altval(-1): idp flags
|
|
#define CALLEE_TAG 'A' // altval(ea): callee address for indirect calls
|
|
#define DXREF_TAG 'd' // altval(ea): resolved address for complex calculation (e.g. ADD R1, PC)
|
|
#define DELAY_TAG 'D' // altval(ea) == DELAY_MARK: analyze ea for a possible offset
|
|
#define ITBLOCK_TAG 'I' // altval(ea): packed it_info_t
|
|
#define FPTR_REG_TAG 'F' // supval(ea): frame pointer info fptr_info_t
|
|
#define FIXED_STKPNT 'x' // charval(ea): may not modify sp value at this address
|
|
#define PUSHINFO_TAG 's' // blob(ea): packed pushinfo_t
|
|
#define ARCHINFO_TAG 'a' // blob(0): packed arm_arch_t
|
|
#define LITERAL_EA 'L' // charval(insn_ea): =const operand number of load literal instruction
|
|
#define MAYBE_STKVAR 't' // charval(ea) == opno+1: may create stkvar for the operand
|
|
|
|
#define DELAY_MARK 1 // possible offset marked for reanalysis
|
|
#define DELAY_DONE 2 // reanalysis completed, don't mark again to prevent endless loops
|
|
|
|
inline void set_callee(ea_t ea, ea_t callee) { helper.easet(ea, callee, CALLEE_TAG); }
|
|
inline ea_t get_callee(ea_t ea) { return helper.eaget(ea, CALLEE_TAG); }
|
|
inline void del_callee(ea_t ea) { helper.eadel(ea, CALLEE_TAG); }
|
|
|
|
inline void set_dxref(ea_t ea, ea_t dxref) { helper.easet(ea, dxref, DXREF_TAG); }
|
|
inline ea_t get_dxref(ea_t ea) { return helper.eaget(ea, DXREF_TAG); }
|
|
inline void del_dxref(ea_t ea) { helper.eadel(ea, DXREF_TAG); }
|
|
|
|
// literal intsruction
|
|
inline void set_lit_opnum(ea_t insn_ea, uchar nop) { helper.charset_ea(insn_ea, nop, LITERAL_EA); }
|
|
inline uchar get_lit_opnum(ea_t insn_ea) { return helper.charval_ea(insn_ea, LITERAL_EA); }
|
|
inline void del_lit_opnum(ea_t insn_ea) { helper.chardel_ea(insn_ea, LITERAL_EA); }
|
|
|
|
// possible stkvar
|
|
inline void set_possible_stkvar(ea_t insn_ea, int nop) { helper.charset_ea(insn_ea, uchar(nop + 1), MAYBE_STKVAR); }
|
|
inline int get_possible_stkvar(ea_t insn_ea) { return helper.charval_ea(insn_ea, MAYBE_STKVAR) - 1; }
|
|
inline void del_possible_stkvar(ea_t insn_ea) { helper.chardel_ea(insn_ea, MAYBE_STKVAR); }
|
|
|
|
//----------------------------------------------------------------------
|
|
#define IDP_SIMPLIFY 0x0001
|
|
#define IDP_NO_PTR_DEREF 0x0002
|
|
// 0x0004 // reserved, see reg.cpp
|
|
#define IDP_ARM5 0x0008
|
|
#define IDP_NO_SETSGR 0x0010
|
|
#define IDP_NO_BL_JUMPS 0x0020
|
|
#define IDP_MOVT_MASK 0x0F00 // handling of MOV(W)/MOVT pairs
|
|
#define IDP_MOVT_SHIFT 8 // see MOVT_
|
|
// MOVT pair handling options
|
|
#define MOVT_NONE 0 // do nothing (old behavior)
|
|
#define MOVT_ADDR 1 // only convert if resulting value is valid address
|
|
#define MOVT_ALL 2 // convert all pairs regardless of the value
|
|
|
|
// - simplify instructions and replace them by pseudo-instructions
|
|
// - disable detection of BL instructions used for long jumps (not calls)
|
|
// in Thumb mode
|
|
// - convert valid addresses
|
|
ushort idpflags = IDP_SIMPLIFY
|
|
| IDP_ARM5
|
|
| IDP_NO_BL_JUMPS
|
|
| (MOVT_ADDR<<IDP_MOVT_SHIFT);
|
|
|
|
inline bool simplify(void) { return (idpflags & IDP_SIMPLIFY) != 0; }
|
|
inline bool deref_ptrs(void) { return (idpflags & IDP_NO_PTR_DEREF) == 0; }
|
|
inline bool may_setsgr(void) { return (idpflags & IDP_NO_SETSGR) == 0; }
|
|
inline bool no_bl_jumps(void) { return (idpflags & IDP_NO_BL_JUMPS) != 0; }
|
|
inline uint8 arm_movt_convert(void) { return (idpflags & IDP_MOVT_MASK) >> 8; }
|
|
|
|
bool convert_movw_movt(const insn_t &_insn, ea_t ea_movt, ea_t ea_movw, ea_t base);
|
|
|
|
//----------------------------------------------------------------------
|
|
arm_arch_t arch;
|
|
arm_arch_t tarch; // temporary (unsaved) arch info that's being changed by the user
|
|
qstring default_arch;
|
|
qstring arm_arch;
|
|
|
|
inline bool has_arm() { return arch.arm_isa_use != 0; }
|
|
inline bool has_thumb() { return arch.thumb_isa_use != 0; }
|
|
inline bool has_thumb2() { return arch.thumb_isa_use >= 2; }
|
|
inline bool has_xscale() { return arch.xscale_arch != 0; }
|
|
inline bool has_vfp() { return arch.fp_arch != fp_arch_none; }
|
|
inline bool has_neon() { return arch.neon_arch != adv_simd_arch_none; }
|
|
#ifndef ENABLE_LOWCNDS
|
|
inline bool has_armv5() { return arch.base_arch >= arch_ARMv5T; }
|
|
inline bool has_armv7a() { return arch.base_arch == arch_ARMv7 || arch.base_arch > arch_ARMv7EM; }
|
|
inline bool has_armv8() { return arch.base_arch >= arch_ARMv8; }
|
|
inline bool is_mprofile() { return arch.is_mprofile(); }
|
|
#endif
|
|
inline bool is_be8() { return arch.be8 != 0; }
|
|
inline bool is_thumb_ea(ea_t ea)
|
|
{
|
|
if ( !has_arm() )
|
|
return true;
|
|
sel_t t = get_sreg(ea, T);
|
|
return t != BADSEL && t != 0;
|
|
}
|
|
|
|
intvec_t custom_format_fids;
|
|
bl_force_flow_ah_t bl_force_jump = bl_force_flow_ah_t(true);
|
|
bl_force_flow_ah_t bl_force_call = bl_force_flow_ah_t(false);
|
|
ea_t got_ea = BADADDR; // .got start address
|
|
int mnem_width = 0;
|
|
bool file_loaded = 0;
|
|
bool initing = false;
|
|
bool warn_about_max_xrefs = true;
|
|
bool recursing = false;
|
|
|
|
it_blocks_t it_blocks;
|
|
|
|
//----------------------------------------------------------------------
|
|
// tuning parameters for reg_tracker_t (set from configuration file by
|
|
// set_idp_options)
|
|
regtrack_info_t regtrack_inf =
|
|
{
|
|
100, // vals_size
|
|
100, // op_recursion
|
|
2000, // bblk_recursion
|
|
5000, // cache_size
|
|
256, // max_xrefs
|
|
};
|
|
reg_tracker_t *reg_tracker = nullptr;
|
|
|
|
//----------------------------------------------------------------------
|
|
// clear the cache of calculated register values from <ea> until the
|
|
// next function;
|
|
// if <ea> is BADADDR the clear the whole cache
|
|
void clear_reg_cache(ea_t ea, int msgid);
|
|
// clear all
|
|
void term_reg_cache();
|
|
// find all values loaded into register
|
|
void find_reg_values(rvi_vec_t *ret, ea_t ea, int reg);
|
|
|
|
//----------------------------------------------------------------------
|
|
mmtype_t *mmtypes = nullptr;
|
|
size_t mmtypes_cnt = 0;
|
|
mmtype_t *mm_array_types = nullptr;
|
|
size_t mm_array_types_cnt = 0;
|
|
|
|
int get_arm_simd_types(
|
|
simd_info_vec_t *outtypes,
|
|
const simd_info_t *pattern,
|
|
const argloc_t *argloc,
|
|
bool do_create);
|
|
void term_arm_simdtypes();
|
|
void register_custom_formats(void);
|
|
void unregister_custom_formats(void);
|
|
|
|
//----------------------------------------------------------------------
|
|
cfh_t *cfh = nullptr;
|
|
#ifdef __EA64__
|
|
fixup_type_t cfh_pg21_id = 0; // ids of fixup handlers
|
|
fixup_type_t cfh_hi12_id = 0;
|
|
fixup_type_t cfh_lo12_id = 0;
|
|
fixup_type_t cfh_lo21_id = 0;
|
|
fixup_type_t cfh_b14_id = 0;
|
|
fixup_type_t cfh_b19_id = 0;
|
|
fixup_type_t cfh_b26_id = 0;
|
|
int ref_pg21_id = 0; // ids of refinfo handlers
|
|
int ref_hi12_id = 0;
|
|
int ref_lo12_id = 0;
|
|
int ref_lo21_id = 0;
|
|
|
|
bool emulate_ldr_str_add_operand(const insn_t &insn, const op_t &op);
|
|
#else
|
|
// ids of fixup handlers
|
|
fixup_type_t cfh_prel31_id = 0;
|
|
// ids of refinfo handlers
|
|
int ref_prel31_id = 0;
|
|
#endif
|
|
|
|
void init_custom_refs();
|
|
void term_custom_refs();
|
|
|
|
//----------------------------------------------------------------------
|
|
#include "../msvc_common.hpp"
|
|
|
|
//----------------------------------------------------------------------
|
|
inline bool is_gas(void) const
|
|
{
|
|
return (ash.uflag & UAS_GNU) != 0;
|
|
}
|
|
inline bool is_ual(void) const
|
|
{
|
|
return (ash.uflag & UAS_LEGACY) == 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
virtual ssize_t idaapi on_event(ssize_t msgid, va_list va) override;
|
|
|
|
void restore_arch();
|
|
void load_from_idb();
|
|
void arm_set_gotea(ea_t ea);
|
|
void arm_get_abi_info(qstrvec_t *abi_names, qstrvec_t *abi_opts, comp_t);
|
|
void arm_set_compiler(bool init_abibits);
|
|
void header(outctx_t &ctx);
|
|
void assumes(outctx_t &ctx);
|
|
void segstart(outctx_t &ctx, segment_t *seg);
|
|
void segend(outctx_t &ctx, segment_t *seg);
|
|
void del_function_marks(const func_t *pfn);
|
|
const char *get_regname(int rn);
|
|
|
|
void init_ana(void);
|
|
void term_ana(void);
|
|
int ana64(insn_t &insn, calcrel_helper_t *crh);
|
|
int ana_arm(insn_t &insn, calcrel_helper_t *crh=nullptr);
|
|
int ana_thumb(insn_t &insn, calcrel_helper_t *crh=nullptr);
|
|
bool ana_coproc(insn_t &insn, uint32 code);
|
|
bool ana_vfp_insns(insn_t &insn, uint32 code);
|
|
bool ana_neon(insn_t &insn, uint32 code, bool thumb);
|
|
inline bool is_undef_vfp(uint32 code, bool thumb);
|
|
inline bool supported_vfp_neon_insn(uint32 code);
|
|
void check_displ(const insn_t &insn, op_t &x, bool alignPC=false);
|
|
void addressing_mode(
|
|
insn_t &insn,
|
|
op_t &x,
|
|
uint32 code,
|
|
calcrel_helper_t *crh);
|
|
bool thumb32(insn_t &insn, int code1, int code2, calcrel_helper_t *crh);
|
|
int do_ana(insn_t &insn, calcrel_helper_t *crh);
|
|
int ana(insn_t *out_ins);
|
|
int get_it_info(ea_t ea, ea_t *it_insn_ea=nullptr);
|
|
void move_it_blocks(ea_t from, ea_t to, asize_t size);
|
|
bool build_movl_macro(insn_t *s, const insn_t &insn);
|
|
bool build_adrl_macro(insn_t *s, const insn_t &insn);
|
|
bool simplify_to_mov(insn_t *s);
|
|
bool build_macro(insn_t &insn, bool may_go_forward);
|
|
bool is_arm64(const insn_t &insn) const;
|
|
bool is_arm64(void) const;
|
|
#if defined(__LINUX__) && defined(__ARM__)
|
|
// see above, global def
|
|
#else
|
|
bool is_arm64_ea(ea_t ea) const;
|
|
#endif
|
|
bool check_for_t_changes(ea_t start, size_t size);
|
|
int a64_branch(insn_t &insn, uint32 code, calcrel_helper_t *crh);
|
|
int decode64(insn_t &insn, calcrel_helper_t *crh);
|
|
|
|
int emu(const insn_t &insn);
|
|
void add_it_block(const insn_t &insn);
|
|
void del_it_block(ea_t ea);
|
|
void del_insn_info(ea_t ea);
|
|
bool copy_insn_optype(const insn_t &insn, const op_t &x, ea_t ea, bool force=false);
|
|
inline void fix_stkpnt(ea_t ea);
|
|
inline bool is_fixed_stkpnt(ea_t ea) const;
|
|
inline void add_stkpnt(const insn_t &insn, func_t *pfn, sval_t v) const;
|
|
void trace_sp(const insn_t &insn);
|
|
bool verify_sp(func_t *pfn);
|
|
inline void save_idpflags() { helper.altset(-1, idpflags); }
|
|
void add_dxref(ea_t from, ea_t target);
|
|
ea_t find_callee(const insn_t &insn, const op_t &x);
|
|
void emulate_ldr_str_base_offset(
|
|
const insn_t &insn,
|
|
ea_t base,
|
|
const struct rvi_vec_t &offs,
|
|
char op_dtyp,
|
|
bool swap_base_off,
|
|
bool only_dxref = false);
|
|
bool emulate_ldr_str_reg_based(const insn_t &insn, const op_t &op);
|
|
bool emulate_ldr_str_with_displ(const insn_t &insn, const op_t &op);
|
|
bool emulate_add_reg_based(const insn_t &insn, int reg1, int reg2);
|
|
void arm_splitSRrange1(ea_t from, ea_t to, sel_t v, uchar tag);
|
|
bool is_arm_call_insn(const insn_t &insn);
|
|
bool range_uses_lr(ea_t ea1, ea_t ea2);
|
|
bool is_bl_call(const insn_t &_insn, ea_t ea);
|
|
void propagate_t_bit_to(const insn_t &insn, ea_t ea, bool use_low_bit);
|
|
bool same_reg_value(
|
|
ea_t ea,
|
|
const op_t &op,
|
|
int r1,
|
|
const op_t *r1_moved,
|
|
bool ignore_r1_changes = false);
|
|
void set_arch_from_loader_arch_info();
|
|
bool is_sp_alias(ea_t ea, int reg);
|
|
int arm_calc_next_eas(eavec_t *res, const insn_t &insn, bool over);
|
|
bool is_return_insn(const insn_t &insn, bool only_lr=false);
|
|
bool try_code_start(ea_t ea, bool respect_low_bit);
|
|
int try_offset(ea_t ea);
|
|
ea_t calc_next_exec_insn(
|
|
const insn_t &ins,
|
|
const regval_t *regvalues,
|
|
const opinfo_helpers_t &oh,
|
|
bool is_mprofile);
|
|
bool try_switch_branch_stmt2(switch_info_t *_si, const insn_t &strt);
|
|
bool try_switch_branch_stmt3(switch_info_t *_si, const insn_t &strt);
|
|
|
|
//----------------------------------------------------------------------
|
|
// Is register 'reg' spoiled by the current instruction?
|
|
// If flag <use_pcs> is set then it is assumed that call instruction
|
|
// doesn't spoil callee-saved registers (according to Procedure Call
|
|
// Standard the are r4-r8, r10, r11, SP for AArch32, r19-r29, SP for
|
|
// AArch64). If this flag is not set then this function assumes that
|
|
// call instruction spoils LR and r0 (return value).
|
|
bool spoils(const insn_t &insn, int reg, bool use_pcs = false);
|
|
// If flag <use_pcs> is set then it is assumed that call instruction
|
|
// doesn't spoil callee-saved registers else this function assumes that
|
|
// call instruction spoils everything (why?).
|
|
int spoils(const insn_t &insn, const uint32 *regs, int n, bool use_pcs = false);
|
|
// is PSR (Program Status Register) spoiled by the instruction ?
|
|
bool spoils_psr(const insn_t &insn) const;
|
|
|
|
|
|
bool get_fptr_info(fptr_info_t *fpi, ea_t func_ea) const;
|
|
inline ushort get_fptr_reg(ea_t func_ea)
|
|
{
|
|
fptr_info_t fpi;
|
|
return get_fptr_info(&fpi, func_ea) ? fpi.reg : ushort(-1);
|
|
}
|
|
inline ea_t get_fp_ea(ea_t func_ea)
|
|
{
|
|
fptr_info_t fpi;
|
|
return get_fptr_info(&fpi, func_ea) ? fpi.addr : BADADDR;
|
|
}
|
|
void set_fptr_info(ea_t func_ea, ushort reg, ea_t addr);
|
|
|
|
bool is_sub_rn_fp(const insn_t &insn);
|
|
sval_t calc_sp_delta(func_t *pfn, const insn_t &insn);
|
|
bool arm_calc_spdelta(sval_t *spdelta, const insn_t &insn);
|
|
inline bool isfp(const insn_t &insn, const op_t &x);
|
|
inline bool is_sp_based(const insn_t &insn, const op_t &x);
|
|
static bool is_start_func_hint(const insn_t &_insn);
|
|
void try_delay_offset(ea_t ea);
|
|
int sp_based(const insn_t &insn, const op_t &x);
|
|
bool uses(const insn_t &insn, int reg) const;
|
|
bool arm_set_op_type(
|
|
const insn_t &insn,
|
|
const op_t &x,
|
|
const tinfo_t &tif,
|
|
const char *name);
|
|
bool arm_set_op_type(
|
|
const insn_t &insn,
|
|
const op_t &x,
|
|
const tinfo_t &tif,
|
|
const char *name,
|
|
eavec_t *visited);
|
|
int use_arm_regarg_type(ea_t ea, const funcargvec_t &rargs);
|
|
void use_arm_arg_types(ea_t ea, func_type_data_t *fti, funcargvec_t *rargs);
|
|
bool is_glue_adr_insn(const insn_t &insn);
|
|
flags_t set_immd_bit(const insn_t &insn, int n, flags_t F);
|
|
bool good_target(ea_t from, ea_t target);
|
|
ea_t get_thumb_glue_target(ea_t ea);
|
|
bool handle_thunk(const insn_t &insn);
|
|
void handle_pc_offset(ea_t ea, int reg);
|
|
int is_jump_func(func_t *pfn, ea_t *jump_target, ea_t *function_pointer);
|
|
int is_arm_sane_insn(const insn_t &insn, int asn_flags);
|
|
#define ASN_NOCREFS 0x01 // there are no crefs to the insn
|
|
#define ASN_THUMB 0x02 // use thumb mode
|
|
#define ASN_CHECK_MODE 0x04 // check thumb/arm mode of the next insn
|
|
int arm_is_align_insn(ea_t ea);
|
|
bool is_rt_switch8(switch_info_t *_si, const insn_t &insn);
|
|
bool lr_points_to_next_insn(const insn_t &insn, ea_t *value=nullptr);
|
|
bool arm_get_operand_info(
|
|
idd_opinfo_t *opinf,
|
|
ea_t ea,
|
|
int n,
|
|
int tid,
|
|
getreg_t *getreg,
|
|
const regval_t *rv);
|
|
ea_t arm_next_exec_insn(
|
|
ea_t ea,
|
|
int tid,
|
|
getreg_t *getreg,
|
|
const regval_t *regvalues);
|
|
bool arm_update_call_stack(call_stack_t *stack, int tid, getreg_t *getreg, const regval_t *rv);
|
|
bool get_call_addr(call_stack_info_t *si, ea_t pc, bool is_a64);
|
|
void force_offset(ea_t ea, int n, ea_t base) const;
|
|
int may_be_func(const insn_t &_insn);
|
|
void get_reg_accesses(reg_accesses_t *accvec, const insn_t &insn) const;
|
|
void test_get_reg_accesses() const;
|
|
bool find_reg_diff(sval_t *delta, const insn_t &insn);
|
|
bool get_arm_callregs(callregs_t *callregs, cm_t cc);
|
|
bool is_big_udt(const tinfo_t &tif, bool as_retval);
|
|
bool alloc_args(func_type_data_t *fti, int nfixed);
|
|
bool calc_arm_arglocs(func_type_data_t *fti);
|
|
bool calc_arm_varglocs(func_type_data_t *fti, regobjs_t *regargs, int nfixed);
|
|
bool calc_arm_retloc(argloc_t *retloc, const tinfo_t &rettype, cm_t cc);
|
|
bool adjust_arm_argloc(argloc_t *argloc, const tinfo_t *tif, int size);
|
|
bool alloc_args64(func_type_data_t *fti, int nfixed);
|
|
void arm_lower_func_arg_types(intvec_t *argnums, const func_type_data_t &fti);
|
|
bool arm_get_reg_info(const char **main_name, bitrange_t *pbitrange, const char *name);
|
|
bool create_func_frame32(func_t *pfn, bool reanalyze);
|
|
bool create_func_frame64(func_t *pfn, bool reanalyze);
|
|
bool create_func_frame(func_t *pfn, bool reanalyze = false);
|
|
sval_t find_subsp_ofs(const insn_t &insn);
|
|
sval_t check_fp_changes(const insn_t &strt, int fpreg, int *finalreg);
|
|
bool adjust_frame_size(
|
|
func_t *pfn,
|
|
const insn_t &insn,
|
|
int reg,
|
|
sval_t spoff);
|
|
int is_cfguard_reg(int *reg, ea_t ea, bool is_indirect) const;
|
|
int is_cfguard_thunk(const insn_t &insn, int *p_reg);
|
|
|
|
void arm_move_segm(ea_t from, const segment_t *s, bool changed_netmap);
|
|
void arm_erase_info(ea_t ea1, ea_t ea2);
|
|
const char *set_idp_options(
|
|
const char *keyword,
|
|
int value_type,
|
|
const void *value,
|
|
bool idb_loaded);
|
|
void correct_code_sequences(void);
|
|
bool is_be_proc() const;
|
|
|
|
ushort basearch_to_field() const;
|
|
ushort vfp_to_field() const;
|
|
|
|
void arm_upgrade_fbase_to700();
|
|
void arm_upgrade_fptr_to700();
|
|
|
|
const char *cond2str(cond_t cond) const;
|
|
void footer(outctx_t &ctx) const;
|
|
bool outspec(outctx_t &ctx, uchar stype) const;
|
|
};
|
|
extern int data_id;
|
|
|
|
//----------------------------------------------------------------------
|
|
class reg_formatter_t
|
|
{
|
|
const processor_t &ph;
|
|
qstring outbuf;
|
|
|
|
const char *regname(int rn) const;
|
|
void format_with_suffix(int rn, int fields);
|
|
|
|
public:
|
|
reg_formatter_t(const processor_t &_ph) : ph(_ph) {}
|
|
void format_a64(int regnum, char dtype, int elsize = 0, int index = -1);
|
|
void format_with_index(int rn, int index=-1);
|
|
void format_any_reg(int rn, const op_t &x, bool ignore_suffix=false);
|
|
op_dtype_t format_phreg(const op_t &x, int regidx);
|
|
const qstring &get_regname() const { return outbuf; }
|
|
};
|
|
|
|
//------------------------------------------------------------------
|
|
reg_tracker_t *alloc_reg_tracker(arm_t &pm);
|
|
void free_reg_tracker(reg_tracker_t *rt);
|
|
cfh_t *alloc_cfh();
|
|
void free_cfh(cfh_t *ptr);
|
|
|
|
#endif // _ARM_HPP
|