251 lines
6.3 KiB
C++
251 lines
6.3 KiB
C++
|
|
#include "fr.hpp"
|
|
|
|
// Analyze an instruction
|
|
static ea_t next_insn(insn_t *insn, ea_t ea)
|
|
{
|
|
if ( decode_insn(insn, ea) == 0 )
|
|
return 0;
|
|
ea += insn->size;
|
|
return ea;
|
|
}
|
|
|
|
// Emulate an operand.
|
|
static void handle_operand(const insn_t &insn, const op_t &op)
|
|
{
|
|
bool offset = false;
|
|
switch ( op.type )
|
|
{
|
|
case o_near:
|
|
insn.add_cref(to_ea(insn.cs, op.addr), op.offb, (insn.itype == fr_call) ? fl_CN : fl_JN);
|
|
break;
|
|
|
|
case o_mem:
|
|
{
|
|
enum dref_t mode = dr_U;
|
|
|
|
if ( op.specflag1 & OP_ADDR_R )
|
|
mode = dr_R;
|
|
else if ( op.specflag1 & OP_ADDR_W )
|
|
mode = dr_W;
|
|
|
|
ea_t ea = to_ea(insn.cs, op.addr);
|
|
insn.add_dref(ea, op.offb, mode);
|
|
insn.create_op_data(ea, op);
|
|
}
|
|
break;
|
|
|
|
case o_imm:
|
|
// if current insn is ldi:32 #imm, r1
|
|
// and next insn is call @r1,
|
|
// replace the immediate value with an offset.
|
|
if ( insn.itype == fr_ldi_32
|
|
&& insn.Op1.type == o_imm
|
|
&& insn.Op2.type == o_reg )
|
|
{
|
|
const int callreg = insn.Op2.reg;
|
|
insn_t nexti;
|
|
if ( next_insn(&nexti, insn.ea + insn.size ) > 0
|
|
&& nexti.itype == fr_call
|
|
&& nexti.Op1.type == o_phrase
|
|
&& nexti.Op1.specflag2 == fIGR
|
|
&& nexti.Op1.reg == callreg )
|
|
{
|
|
offset = true;
|
|
}
|
|
if ( !is_defarg(get_flags(insn.ea), 0) && offset )
|
|
op_plain_offset(insn.ea, 0, 0);
|
|
}
|
|
set_immd(insn.ea);
|
|
// if the value was converted to an offset, then create a data xref:
|
|
if ( !offset && op_adds_xrefs(get_flags(insn.ea), op.n) )
|
|
insn.add_off_drefs(op, dr_O, 0);
|
|
|
|
// create stack variables if necessary
|
|
{
|
|
bool ok = false;
|
|
// ldi8 #our_value, R1
|
|
// extsb R1
|
|
// addn R14, R1
|
|
if ( insn.itype == fr_ldi_8
|
|
&& insn.Op2.type == o_reg
|
|
&& insn.Op2.reg == rR1 )
|
|
{
|
|
insn_t nexti;
|
|
next_insn(&nexti, insn.ea + insn.size);
|
|
if ( nexti.itype == fr_extsb
|
|
&& nexti.Op1.type == o_reg
|
|
&& nexti.Op1.reg == rR1 )
|
|
{
|
|
ok = true;
|
|
}
|
|
if ( ok )
|
|
{
|
|
ok = false;
|
|
next_insn(&nexti, nexti.ea + insn.size);
|
|
if ( nexti.itype == fr_addn
|
|
&& nexti.Op1.type == o_reg
|
|
&& nexti.Op1.reg == rR14
|
|
&& nexti.Op2.type == o_reg
|
|
&& nexti.Op2.reg == rR1 )
|
|
{
|
|
ok = true;
|
|
}
|
|
}
|
|
}
|
|
// ldi32 #our_value, Ri
|
|
// addn R14, Ri
|
|
//
|
|
// (where Ri is either R1 or R2)
|
|
else if ( insn.itype == fr_ldi_32
|
|
&& insn.Op2.type == o_reg
|
|
&& (insn.Op2.reg == rR1 || insn.Op2.reg == rR2) )
|
|
{
|
|
ushort the_reg = insn.Op2.reg;
|
|
insn_t nexti;
|
|
next_insn(&nexti, insn.ea + insn.size);
|
|
if ( nexti.itype == fr_addn
|
|
&& nexti.Op1.type == o_reg
|
|
&& nexti.Op1.reg == rR14
|
|
&& nexti.Op2.type == o_reg
|
|
&& nexti.Op2.reg == the_reg )
|
|
{
|
|
ok = true;
|
|
}
|
|
}
|
|
|
|
if ( ok && may_create_stkvars()
|
|
&& !is_defarg(get_flags(insn.ea), op.n) )
|
|
{
|
|
func_t *pfn = get_func(insn.ea);
|
|
if ( pfn != NULL && pfn->flags & FUNC_FRAME )
|
|
{
|
|
if ( insn.create_stkvar(op, op.value, 0) )
|
|
op_stkvar(insn.ea, op.n);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case o_displ:
|
|
case o_phrase: // XXX
|
|
case o_reglist:
|
|
case o_void:
|
|
case o_reg:
|
|
break;
|
|
|
|
default:
|
|
INTERR(10017);
|
|
}
|
|
}
|
|
|
|
inline bool fr_t::is_stop(const insn_t &insn) const
|
|
{
|
|
uint32 feature = insn.get_canon_feature(ph);
|
|
return (feature & CF_STOP) != 0;
|
|
}
|
|
|
|
// Emulate an instruction.
|
|
int fr_t::emu(const insn_t &insn) const
|
|
{
|
|
bool flow = !is_stop(insn) || (insn.auxpref & INSN_DELAY_SHOT);
|
|
if ( flow )
|
|
{
|
|
insn_t previ;
|
|
if ( decode_prev_insn(&previ, insn.ea) != BADADDR )
|
|
flow = !(is_stop(previ) && (previ.auxpref & INSN_DELAY_SHOT));
|
|
}
|
|
|
|
if ( insn.Op1.type != o_void ) handle_operand(insn, insn.Op1);
|
|
if ( insn.Op2.type != o_void ) handle_operand(insn, insn.Op2);
|
|
if ( insn.Op3.type != o_void ) handle_operand(insn, insn.Op3);
|
|
if ( insn.Op4.type != o_void ) handle_operand(insn, insn.Op4);
|
|
|
|
if ( flow )
|
|
add_cref(insn.ea, insn.ea + insn.size, fl_F);
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Create a function frame
|
|
bool idaapi create_func_frame(func_t *pfn)
|
|
{
|
|
ushort savedreg_size = 0;
|
|
uint32 args_size = 0;
|
|
uint32 localvar_size;
|
|
|
|
ea_t ea = pfn->start_ea;
|
|
|
|
// detect multiple ``st Ri, @-R15'' instructions.
|
|
insn_t insn;
|
|
while ( (ea=next_insn(&insn, ea)) != 0
|
|
&& insn.itype == fr_st
|
|
&& insn.Op1.type == o_reg
|
|
&& insn.Op2.type == o_phrase
|
|
&& insn.Op2.reg == rR15
|
|
&& insn.Op2.specflag2 == fIGRM )
|
|
{
|
|
savedreg_size += 4;
|
|
#if defined(__DEBUG__)
|
|
msg("0x%a: detected st Rx, @-R15\n", ea);
|
|
#endif /* __DEBUG__ */
|
|
}
|
|
|
|
// detect enter #nn
|
|
if ( insn.itype == fr_enter )
|
|
{
|
|
// R14 is automatically pushed by fr_enter
|
|
savedreg_size += 4;
|
|
localvar_size = uint32(insn.Op1.value - 4);
|
|
#if defined(__DEBUG__)
|
|
msg("0x%a: detected enter #0x%a\n", ea, insn.Op1.value);
|
|
#endif /* __DEBUG__ */
|
|
}
|
|
// detect mov R15, R14 + ldi #imm, R0 instructions
|
|
else
|
|
{
|
|
if ( insn.itype != fr_mov
|
|
|| insn.Op1.type != o_reg
|
|
|| insn.Op1.reg != rR15
|
|
|| insn.Op2.type != o_reg
|
|
|| insn.Op2.reg != rR14 )
|
|
{
|
|
goto BAD_FUNC;
|
|
}
|
|
/*ea = */next_insn(&insn, ea);
|
|
if ( (insn.itype == fr_ldi_20 || insn.itype == fr_ldi_32)
|
|
&& insn.Op1.type == o_imm
|
|
&& insn.Op2.type == o_reg
|
|
&& insn.Op2.reg == rR0 )
|
|
{
|
|
localvar_size = uint32(insn.Op1.value);
|
|
}
|
|
else
|
|
{
|
|
goto BAD_FUNC;
|
|
}
|
|
#if defined(__DEBUG__)
|
|
msg("0x%a: detected ldi #0x%a, R0\n", ea, insn.Op1.value);
|
|
#endif /* __DEBUG__ */
|
|
}
|
|
|
|
// XXX we don't care about near/far functions, because currently
|
|
// we don't know how to detect them ;-)
|
|
|
|
pfn->flags |= FUNC_FRAME;
|
|
return add_frame(pfn, localvar_size, savedreg_size, args_size);
|
|
|
|
BAD_FUNC:
|
|
return 0;
|
|
}
|
|
|
|
int idaapi is_sp_based(const insn_t &, const op_t &)
|
|
{
|
|
return OP_SP_ADD | OP_FP_BASED;
|
|
}
|
|
|
|
int idaapi is_align_insn(ea_t ea)
|
|
{
|
|
return get_byte(ea) == 0;
|
|
}
|