#include "m32r.hpp" // handle immediate values static void handle_imm(const insn_t &insn) { set_immd(insn.ea); } //---------------------------------------------------------------------- // handle the custom switch format // .... // bl.s next || nop <- insn_ea // next: // add lr, R0 // jmp lr // si.jumps: // bra.s case0 || nop // bra.l case1 // ... int m32r_create_switch_xrefs(ea_t insn_ea, const switch_info_t &si) { if ( (si.flags & SWI_CUSTOM) != 0 ) { insn_t insn; decode_insn(&insn, insn_ea); ea_t ea = si.jumps; for ( int i = 0; i < si.ncases; i++, ea += insn.size ) { add_cref(insn_ea, ea, fl_JN); decode_insn(&insn, ea); if ( insn.Op1.type == o_near ) { ea_t target = to_ea(insn.cs, insn.Op1.addr); // xrefs are from "bl" -> branch target. add_cref(insn_ea, target, fl_JN); } } } return 1; // ok } //---------------------------------------------------------------------- int m32r_calc_switch_cases(casevec_t *casevec, eavec_t *targets, ea_t insn_ea, const switch_info_t &si) { if ( (si.flags & SWI_CUSTOM) == 0 ) return 0; insn_t insn; decode_insn(&insn, insn_ea); ea_t ea = si.jumps; svalvec_t vals; vals.push_back(0); // add one item for ( int i=0; i < si.ncases; i++, ea += insn.size ) { decode_insn(&insn, ea); if ( targets != NULL ) { if ( insn.itype == m32r_bra && insn.Op1.type == o_near ) targets->push_back(insn.Op1.addr); else targets->push_back(insn.ea); } if ( casevec != NULL ) { vals[0] = i; casevec->push_back(vals); } } return 1; // ok } //---------------------------------------------------------------------- static bool handle_switch(const insn_t &insn) { switch_info_t si; bool was_switch = (get_flags(insn.ea) & FF_JUMP) != 0 && get_switch_info(&si, insn.ea) > 0; // do not overwrite the existing switch // FIXME: reanalyze non user defined switches if ( was_switch ) return true; // ldi8 R1, #0x21 ; '!' // cmpu R1, R0 // bc.l loc_67F8C // slli R0, #2 // addi R0, #4 // bl.s next || nop // next: // add lr, R0 // jmp lr // bra.s loc_67CDC || nop // bra.s loc_67D34 || nop // bra.l loc_67F8C // ... if ( insn.itype != m32r_bl ) return false; // bl should be to next address ea_t tgt = to_ea(insn.cs, insn.Op1.addr); if ( tgt != insn.ea + insn.size ) return false; insn_t insn2; // check for add lr, R0; jmp lr if ( decode_insn(&insn2, tgt) == 0 || insn2.itype != m32r_add || !insn2.Op1.is_reg(rLR) || insn2.Op2.type != o_reg ) { BAD_MATCH: return false; } int switch_reg = insn2.Op2.reg; // jmp lr if ( decode_insn(&insn2, insn2.ea + insn2.size) == 0 || insn2.itype != m32r_jmp || !insn2.Op1.is_reg(rLR) ) { goto BAD_MATCH; } // addi R0, #4 if ( decode_prev_insn(&insn2, insn.ea) == BADADDR || insn2.itype != m32r_addi || !insn2.Op1.is_reg(switch_reg) || insn2.Op2.type != o_imm ) { goto BAD_MATCH; } ea_t jumps = insn.ea + insn.size + insn2.Op2.value; // slli R0, #2 if ( decode_prev_insn(&insn2, insn2.ea) == BADADDR || insn2.itype != m32r_slli || !insn2.Op1.is_reg(switch_reg) || !insn2.Op2.is_imm(2) ) { goto BAD_MATCH; } // bc.l default if ( decode_prev_insn(&insn2, insn2.ea) == BADADDR || insn2.itype != m32r_bc ) { goto BAD_MATCH; } ea_t defea = to_ea(insn2.cs, insn2.Op1.addr); // cmpu R1, R0 if ( decode_prev_insn(&insn2, insn2.ea) == BADADDR || insn2.itype != m32r_cmpu || !insn2.Op2.is_reg(switch_reg) || insn2.Op1.type != o_reg ) { goto BAD_MATCH; } int cmpreg = insn2.Op1.reg; // ldi8 R1, #max if ( decode_prev_insn(&insn2, insn2.ea) == BADADDR || insn2.itype != m32r_ldi || !insn2.Op1.is_reg(cmpreg) || insn2.Op2.type != o_imm ) { goto BAD_MATCH; } // looks good si.flags |= SWI_CUSTOM | SWI_J32; si.ncases = insn2.Op2.value + 1; si.jumps = jumps; si.lowcase = 0; si.startea = insn2.ea; si.set_expr(switch_reg, dt_dword); si.defjump = defea; set_switch_info(insn.ea, si); create_switch_table(insn.ea, si); return true; } //---------------------------------------------------------------------- // emulate operand void m32r_t::handle_operand(const insn_t &insn, const op_t &op, bool loading) { flags_t F = get_flags(insn.ea); switch ( op.type ) { // Address case o_near: // branch label - create code reference (call or jump // according to the instruction) { ea_t ea = to_ea(insn.cs, op.addr); cref_t ftype = fl_JN; if ( insn.itype == m32r_bl && !handle_switch(insn) ) { if ( !func_does_return(ea) ) flow = false; ftype = fl_CN; } insn.add_cref(ea, op.offb, ftype); } break; // Immediate case o_imm: QASSERT(10135, loading); handle_imm(insn); // if the value was converted to an offset, then create a data xref: if ( op_adds_xrefs(F, op.n) ) insn.add_off_drefs(op, dr_O, OOFW_IMM|OOF_SIGNED); // create a comment if this immediate is represented in the .cfg file { const ioport_t *port = find_sym(op.value); if ( port != NULL && !has_cmt(F) ) set_cmt(insn.ea, port->cmt.c_str(), false); } break; // Displ case o_displ: handle_imm(insn); // if the value was converted to an offset, then create a data xref: if ( op_adds_xrefs(F, op.n) ) insn.add_off_drefs(op, loading ? dr_R : dr_W, OOF_SIGNED|OOF_ADDR|OOFW_32); // create stack variables if required if ( may_create_stkvars() && !is_defarg(F, op.n) ) { func_t *pfn = get_func(insn.ea); if ( pfn != NULL && (op.reg == rFP || op.reg == rSP) && pfn->flags & FUNC_FRAME ) { if ( insn.create_stkvar(op, op.addr, STKVAR_VALID_SIZE) ) op_stkvar(insn.ea, op.n); } } break; case o_phrase: /* create stack variables if required */ if ( op.specflag1 == fRI && may_create_stkvars() && !is_defarg(F, op.n) ) { func_t *pfn = get_func(insn.ea); if ( pfn != NULL && (op.reg == rFP || op.reg == rSP) && (pfn->flags & FUNC_FRAME) != 0 ) { if ( insn.create_stkvar(op, 0, STKVAR_VALID_SIZE) ) op_stkvar(insn.ea, op.n); } } break; // Phrase - register - void : do nothing case o_reg: case o_void: break; // Others types should never be called default: INTERR(10136); } } //---------------------------------------------------------------------------- // emulate an instruction int m32r_t::emu(const insn_t &insn) { uint32 feature = insn.get_canon_feature(ph); flow = ((feature & CF_STOP) == 0); if ( feature & CF_USE1 ) handle_operand(insn, insn.Op1, true); if ( feature & CF_USE2 ) handle_operand(insn, insn.Op2, true); if ( feature & CF_USE3 ) handle_operand(insn, insn.Op3, true); if ( feature & CF_JUMP ) remember_problem(PR_JUMP, insn.ea); if ( feature & CF_CHG1 ) handle_operand(insn, insn.Op1, false); if ( feature & CF_CHG2 ) handle_operand(insn, insn.Op2, false); if ( feature & CF_CHG3 ) handle_operand(insn, insn.Op3, false); if ( flow ) add_cref(insn.ea, insn.ea + insn.size, fl_F); return 1; } //---------------------------------------------------------------------------- bool idaapi create_func_frame(func_t *pfn) { if ( pfn == NULL ) return 0; ea_t ea = pfn->start_ea; insn_t insn[4]; int i; for ( i = 0; i < 4; i++ ) { decode_insn(&insn[i], ea); ea += insn[i].size; } i = 0; ushort regsize = 0; // number of saved registers // first insn is not either push fp OR st fp, @-sp if ( (insn[i].itype != m32r_push || insn[i].Op1.reg != rFP) && (insn[i].itype != m32r_st || insn[i].Op1.reg != rFP || insn[i].Op2.reg != rSP || insn[i].Op2.specflag1 != fRIAS) ) { return 0; } regsize += 4; i++; // next insn is push lr OR st lr, @-sp if ( (insn[i].itype == m32r_push && insn[i].Op1.reg == rLR) || (insn[i].itype == m32r_st && insn[i].Op1.reg == rFP && insn[i].Op2.reg == rLR && insn[i].Op2.specflag1 != fRIAS) ) { regsize += 4; i++; } // next insn is not addi sp, #imm if ( insn[i].itype != m32r_addi || insn[i].Op1.reg != rSP ) return 0; sval_t offset = - (sval_t) insn[i].Op2.value; // toggle to the negative sign of the immediate operand of the addi insn if ( !is_invsign(insn[i].ea, get_flags(insn[i].ea), 2) ) toggle_sign(insn[i].ea, 2); i++; // next insn is not mv fp, sp if ( insn[i].itype != m32r_mv || insn[i].Op1.reg != rFP || insn[i].Op2.reg != rSP ) return 0; pfn->flags |= (FUNC_FRAME | FUNC_BOTTOMBP); return add_frame(pfn, offset, regsize, 0); } //---------------------------------------------------------------------------- // should always returns 0 int idaapi m32r_get_frame_retsize(const func_t *) { return 0; } //---------------------------------------------------------------------------- // check is the specified operand is relative to the SP register int idaapi is_sp_based(const insn_t &/*insn*/, const op_t &op) { return OP_SP_ADD | (op.reg == rSP ? OP_SP_BASED : OP_FP_BASED); } //---------------------------------------------------------------------------- bool idaapi can_have_type(const op_t &x) { switch ( x.type ) { case o_imm: case o_displ: return 1; case o_phrase: if ( x.specflag1 == fRI ) return 1; break; } return 0; }