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

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;
}