Files
2021-10-31 21:20:46 +02:00

503 lines
17 KiB
C++

/*
* Interactive disassembler (IDA).
* Copyright (c) 2012-2021 Hex-Rays
* ALL RIGHTS RESERVED.
*
* ARC (Argonaut RISC Core) processor module
*
* Based on code contributed by by Felix Domke <tmbinc@gmx.net>
*/
#ifndef _ARC_HPP
#define _ARC_HPP
#include "../idaidp.hpp"
#include "ins.hpp"
#include <typeinf.hpp>
#include <diskio.hpp>
#include "../iohandler.hpp"
#define PROCMOD_NAME arc
#define PROCMOD_NODE_NAME "$ arc"
//------------------------------------------------------------------------
// customization of the 'cmd' structure:
enum processor_subtype_t
{
prc_arc = 0, // ARCTangent-A4 (old 32-bit ISA)
prc_arcompact = 1, // ARCtangent-A5 and later (32/16-bit mixed)
prc_arcv2 = 2, // ARC EM (ARCompact successor)
};
//------------------------------------------------------------------------
enum RegNo
{
R0, R1, R2, R3, R4, R5, R6, R7,
R8, R9, R10, R11, R12, R13, R14, R15,
R16, R17, R18, R19, R20, R21, R22, R23,
R24, R25, R26, R27, R28, R29, R30, R31,
R32, R33, R34, R35, R36, R37, R38, R39,
R40, R41, R42, R43, R44, R45, R46, R47,
R48, R49, R50, R51, R52, R53, R54, R55,
R56, R57, R58, R59, R60, R61, R62, R63,
CF, ZF, NF, VF,
// registers used for indexed instructions
// keep these consecutive
NEXT_PC,
LDI_BASE, JLI_BASE, EI_BASE,
GP_SEG, // virtual segment register for global pointer value
rVcs, rVds, // virtual registers for code and data segments
// aliases
GP = R26, // Global Pointer
FP = R27, // Frame Pointer
SP = R28, // Stack Pointer
ILINK1 = R29, // Level 1 interrupt link register
ILINK2 = R30, // Level 2 interrupt link register
BLINK = R31, // Branch link register
LP_COUNT = R60, // Loop count register
PCL = R63, // 32-bit aligned PC value (ARCompact)
// optional extension
MLO = R57, // Multiply low 32 bits, read only
MMID = R58, // Multiply middle 32 bits, read only
MHI = R59, // Multiply high 32 bits, read only
};
#define SHIMM_F 61 // Short immediate data indicator setting flags
#define LIMM 62 // Long immediate data indicator
#define SHIMM 63 // Short immediate data indicator not setting flags (NB: not used in ARCompact)
#define LIMM5 30 // 5-bit long immediate data indicator (used in ARCv2)
//------------------------------------------------------------------------
const ioport_t *find_port(ea_t address);
#define PROC_MAXOP 4 // max number of operands
CASSERT(PROC_MAXOP <= UA_MAXOP);
//---------------------------------
inline int getreg(const op_t &x)
{
return x.type == o_reg ? x.reg : -1;
}
inline bool isreg(const op_t &x, int reg)
{
return getreg(x) == reg;
}
inline bool issp(const op_t &x) { return isreg(x, SP); }
//---------------------------------
// cmd.auxpref bits
// instructions that use condition flags (Bcc, Jcc)
#define aux_f 0x0100 // Flags set field (.f postfix)
#define aux_nmask 0x0060 // Jump/Call nullify instruction mode
#define aux_nd 0x00 // No Delayed instruction slot (only execute next instruction when not jumping)
#define aux_d 0x20 // Delayed instruction slot (always execute next instruction)
#define aux_jd 0x40 // Jump Delayed instruction slot (only execute next instruction when jumping)
#define aux_cmask 0x001F // condition code mask
// load/store instructions flags (Di.AA.ZZ.X)
#define aux_di 0x0020 // direct to memory (cache bypass) (.di suffix)
#define aux_amask 0x0018 // Address write-back
#define aux_anone 0x00 // no writeback
#define aux_a 0x08 // pre-increment (.a or .aw)
#define aux_ab 0x10 // post-increment (.ab)
#define aux_as 0x18 // scaled access (.as)
#define aux_zmask 0x0006 // size mask
#define aux_l 0x0 // long size (no suffix)
#define aux_w 0x4 // word size (.w suffix)
#define aux_b 0x2 // byte size (.b suffix)
#define aux_x 0x0001 // Sign extend field (.x suffix)
#define aux_pcload 0x0200 // converted pc-relative to memory load (used when ARC_INLINECONST is set)
#define aux_bhint 0x0400 // non-default static branch prediction hint (.t or .nt suffix)
#define aux_s 0x0800 // 16-bit encoded instruction
// Operand types:
#define o_reglist o_idpspec0 // register list for enter/leave
#define reglist specval // o_reglist: registers to save/restore
#define REGLIST_REGS 0x0F // number of core registers to save/restore
#define REGLISTR_MAX 0x0E // max number of core registers to save/restore
#define REGLIST_FP 0x10 // save/restore stack frame
#define REGLIST_BLINK 0x20 // save/restore blink register
#define REGLIST_PCL 0x40 // jump to blink register after restore (leave only)
// o_phrase
#define secreg specflag1 // o_phrase: the second register is here: [op.phrase, op.secreg]
// o_displ
#define membase specflag1 // o_displ: if set, displacement is the base value: [op.addr, op.reg]
// this is important for scaled loads, e.g. ld.as r1, [0x23445, r2]
// o_reg
#define regpair specflag1 // o_reg: if set, this operand is the second register of a register pair
// the previous operand contains the other register of the pair
// o_mem
#define immdisp specval // o_mem: immediate displacement to immediate address.
// addr contains the already displaced address.
// addr - get_scale_factor(insn) * immdisp is the base address for immdisp
//------------------------------------------------------------------
// Condition codes:
enum cond_t
{
cAL=0, cRA=0, // Always 1 0x00
cEQ=1, cZ=1, // Zero Z 0x01
cNE=2, cNZ=2, // Non-Zero /Z 0x02
cPL=3, cP=3, // Positive /N 0x03
cMI=4, cN=4, // Negative N 0x04
cCS=5, cC=5, cLO=5, // Carry set, lower than (unsigned) C 0x05
cCC=6, cNC=6, cHS=6, // Carry clear, higher or same (unsigned) /C 0x06
cVS=7, cV=7, // Over-flow set V 0x07
cVC=8, cNV=8, // Over-flow clear /V 0x08
cGT=9, // Greater than (signed) (N and V and /Z) or (/N and /V and /Z) 0x09
cGE=0x0A, // Greater than or equal to (signed) (N and V) or (/N and /V) 0x0A
cLT=0x0B, // Less than (signed) (N and /V) or (/N and V) 0x0B
cLE=0x0C, // Less than or equal to (signed) Z or (N and /V) or (/N and V) 0x0C
cHI=0x0D, // Higher than (unsigned) /C and /Z 0x0D
cLS=0x0E, // Lower than or same (unsigned) C or Z 0x0E
cPNZ=0x0F, // Positive non-zero /N and /Z 0x0F
cLAST
};
inline uint8 get_cond(const insn_t &insn)
{
if ( insn.itype <= ARC_store_instructions )
return cAL;
return uint8(insn.auxpref & aux_cmask);
}
inline bool has_cond(const insn_t &insn)
{
if ( insn.itype <= ARC_store_instructions )
return false;
return (insn.auxpref & aux_cmask) != cAL;
}
inline cond_t get_core_cond(const insn_t &insn)
{
if ( insn.itype <= ARC_store_instructions )
return cAL;
uint8 cond = insn.auxpref & aux_cmask;
if ( cond >= cLAST )
return cLAST;
return cond_t(cond);
}
inline bool has_core_cond(const insn_t &insn)
{
if ( insn.itype <= ARC_store_instructions )
return false;
uint8 cond = insn.auxpref & aux_cmask;
return cond != cAL && cond < cLAST;
}
inline cond_t invert_cond(cond_t cond)
{
switch ( cond )
{
case cNE: return cEQ;
case cEQ: return cNE;
case cCC: return cCS;
case cCS: return cCC;
case cPL: return cMI;
case cMI: return cPL;
case cVC: return cVS;
case cVS: return cVC;
case cHI: return cLS;
case cLS: return cHI;
case cGE: return cLT;
case cLT: return cGE;
case cGT: return cLE;
case cLE: return cGT;
default: return cLAST;
}
}
// ARC ABI conventions from gdb/arc-tdep.h
#define ARC_ABI_GLOBAL_POINTER 26
#define ARC_ABI_FRAME_POINTER 27
#define ARC_ABI_STACK_POINTER 28
#define ARC_ABI_FIRST_CALLEE_SAVED_REGISTER 13
#define ARC_ABI_LAST_CALLEE_SAVED_REGISTER 26
#define ARC_ABI_FIRST_ARGUMENT_REGISTER 0
#define ARC_ABI_LAST_ARGUMENT_REGISTER 7
#define ARC_ABI_RETURN_REGNUM 0
#define ARC_ABI_RETURN_LOW_REGNUM 0
#define ARC_ABI_RETURN_HIGH_REGNUM 1
//------------------------------------------------------------------------
// does 'ins' have a delay slot? (next instruction is executed before branch/jump)
inline bool has_dslot(const insn_t &ins)
{
// EXCEPTION: jl.jd <addr> uses delay slot to
// hide the long immediate used for the address
if ( ins.itype == ARC_jl
&& (ins.auxpref & aux_nmask) == aux_jd
&& ins.Op1.type == o_near )
return false;
return ins.itype > ARC_store_instructions && (ins.auxpref & aux_nmask) != 0;
}
//------------------------------------------------------------------------
// Scale factor for indexed memory access
inline int get_scale_factor(const insn_t &ins)
{
switch ( ins.itype )
{
case ARC_st:
case ARC_ld:
if ( (ins.auxpref & aux_amask) == aux_as )
{
if ( (ins.auxpref & aux_zmask) == aux_w )
return 2;
if ( (ins.auxpref & aux_zmask) == aux_l )
return 4;
}
break;
case ARC_bih:
return 2;
case ARC_bi:
case ARC_ldi:
case ARC_jli:
case ARC_ei:
return 4;
}
return 1;
}
//------------------------------------------------------------------------
// Should the register be hidden when used as base in o_displ/o_phrase?
//
// 0 output normally
// 1 hide base reg
// -1 hide base reg and output as immediate
inline int is_hidden_base_reg(int reg)
{
if ( reg >= NEXT_PC && reg <= EI_BASE )
{
return reg == JLI_BASE || reg == EI_BASE ? -1 : 1;
}
return 0;
}
//------------------------------------------------------------------------
// The sreg that contains the current value for the given register
//
// Returns -1 if there is no such sreg
inline int get_base_sreg(int reg)
{
if ( reg == GP )
return GP_SEG;
else if ( reg >= LDI_BASE && reg <= GP_SEG )
return reg;
return -1;
}
//------------------------------------------------------------------------
void idaapi arc_header(outctx_t &ctx);
void idaapi arc_footer(outctx_t &ctx);
int idaapi is_sp_based(const insn_t &insn, const op_t & x);
bool idaapi create_func_frame(func_t * pfn);
int idaapi arc_get_frame_retsize(const func_t * pfn);
bool is_arc_return_insn(const insn_t &insn);
bool arc_is_switch(switch_info_t *si, const insn_t &insn);
inline bool is_arc_simple_branch(uint16 itype)
{
return itype == ARC_bl
|| itype == ARC_jl
|| itype == ARC_b
|| itype == ARC_j;
}
int get_arc_fastcall_regs(const int **regs);
bool calc_arc_arglocs(func_type_data_t *fti);
bool calc_arc_varglocs(
func_type_data_t *fti,
regobjs_t *regargs,
int nfixed);
bool calc_arc_retloc(argloc_t *retloc, const tinfo_t &tif, cm_t cc);
void use_arc_arg_types(
ea_t ea,
func_type_data_t *fti,
funcargvec_t *rargs);
//------------------------------------------------------------------------
struct arc_iohandler_t : public iohandler_t
{
struct arc_t &pm;
arc_iohandler_t(arc_t &_pm, netnode &nn) : iohandler_t(nn), pm(_pm) {}
virtual const char *iocallback(const ioports_t &iop, const char *line) override;
virtual void get_cfg_filename(char *buf, size_t bufsize) override;
};
DECLARE_PROC_LISTENER(pm_idb_listener_t, struct arc_t);
struct arc_t : public procmod_t
{
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 DSLOT_TAG 's' // altval(ea): 1: delay slot of an unconditional jump/branch
// 2: delay slot of a conditional jump/branch
// 3: delay slot of a jl/bl
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); }
instruc_t Instructions[ARC_last];
ioports_t auxregs;
arc_iohandler_t ioh = arc_iohandler_t(*this, helper);
pm_idb_listener_t idb_listener = pm_idb_listener_t(*this);
processor_subtype_t ptype = prc_arc;
inline bool is_a4() { return ptype == prc_arc; }
inline bool is_arcv2() { return ptype == prc_arcv2; }
int arc_respect_info = IORESP_ALL;
int ref_arcsoh_id = 0;
int ref_arcsol_id = 0;
#define ARC_SIMPLIFY 0x01
#define ARC_INLINECONST 0x02
#define ARC_TRACKREGS 0x04
ushort idpflags = ARC_SIMPLIFY | ARC_INLINECONST | ARC_TRACKREGS;
int g_limm = 0;
bool got_limm = false;
std::set<ea_t> renamed;
int islast = 0;
// is 'ea' in a delay slot of a branch/jump?
inline bool is_dslot(ea_t ea, bool including_calls = true)
{
nodeidx_t v = helper.altval_ea(ea, DSLOT_TAG);
if ( including_calls )
return v != 0;
else
return v == 1 || v == 2;
}
inline bool is_imm(int regno)
{
if ( regno == LIMM )
return true;
if ( regno == SHIMM_F || regno == SHIMM )
return is_a4();
return false;
}
arc_t();
virtual ssize_t idaapi on_event(ssize_t msgid, va_list va) override;
void save_idpflags() { helper.altset(-1, idpflags); }
void load_from_idb();
bool select_device(int resp_info);
inline void add_dxref(const insn_t &insn, ea_t target);
int emu(const insn_t &insn);
bool is_arc_basic_block_end(
const insn_t &insn,
bool call_insn_stops_block);
void del_insn_info(ea_t ea);
const char *set_idp_options(
const char *keyword,
int value_type,
const void *value,
bool idb_loaded);
void set_codeseqs() const;
void set_instruc_names();
void ptype_changed();
void doIndirectOperand(const insn_t &insn, int b, int c, op_t &op, int d, int li, bool special);
void doRegisterInstruction(insn_t &insn, uint32 code);
int ana_old(insn_t &insn);
void decode_operand(
insn_t &insn,
uint32 code,
int &op_pos,
uint32 opkind);
int analyze_compact(insn_t &insn, uint32 code, int idx, const struct arcompact_opcode_t *table);
int ana_compact(insn_t &insn);
int ana(insn_t *_insn);
int is_align_insn(ea_t ea) const;
bool good_target(const insn_t &insn, ea_t target) const;
bool copy_insn_optype(const insn_t &insn, const op_t &x, ea_t ea, void *value = NULL, bool force = false) const;
void handle_operand(const insn_t &insn, const op_t & x, bool loading);
int get_limm(insn_t &insn);
inline void opreg(insn_t &insn, op_t &x, int rgnum, int limm=LIMM);
inline void opdisp(insn_t &insn, op_t &x, int rgnum, ea_t disp);
void rename_if_not_set(ea_t ea, const char *name);
bool check_ac_pop_chain(int *regno, ea_t ea);
bool detect_millicode(qstring *mname, ea_t ea);
bool is_millicode(ea_t ea, sval_t *spdelta=nullptr);
sval_t calc_sp_delta(const insn_t &insn);
void trace_sp(const insn_t &insn);
bool arc_calc_spdelta(sval_t *spdelta, const insn_t &insn);
int arc_may_be_func(const insn_t &insn, int state);
void force_offset(
ea_t ea,
int n,
ea_t base,
bool issub = false,
int scale = 1);
bool spoils(const insn_t &insn, int reg) const;
int spoils(const insn_t &insn, const uint32 *regs, int n) const;
bool is_arc_call_insn(const insn_t &insn);
bool find_op_value_ex(
const insn_t &insn,
const op_t &x,
struct ldr_value_info_t *lvi,
bool /*check_fbase_reg*/);
bool find_ldr_value_ex(
const insn_t &insn,
ea_t ea,
int reg,
struct ldr_value_info_t *lvi,
bool /*check_fbase_reg*/);
bool find_op_value(
const insn_t &insn,
const op_t &x,
uval_t *p_val,
ea_t *p_val_ea=NULL,
bool check_fbase_reg=true,
bool *was_const_load=NULL);
bool find_ldr_value(
const insn_t &insn,
ea_t ea,
int reg,
uval_t *p_val,
ea_t *p_val_ea=NULL,
bool check_fbase_reg=true,
bool *was_const_load=NULL);
int use_arc_regarg_type(ea_t ea, const funcargvec_t &rargs);
bool arc_set_op_type(
const insn_t &insn,
const op_t &x,
const tinfo_t &tif,
const char *name,
eavec_t *visited);
void use_arc_arg_types(ea_t ea, func_type_data_t *fti, funcargvec_t *rargs);
void arc_segstart(outctx_t &ctx, segment_t *Sarea) const;
};
extern int data_id;
#endif