rizin/librz/arch/p/analysis/analysis_rx.c
2024-09-15 19:12:07 +08:00

387 lines
9.2 KiB
C

// SPDX-FileCopyrightText: 2024 heersin <teablearcher@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_types.h>
#include <rz_util.h>
#include <rz_lib.h>
#include <rz_asm.h>
#include <rz_analysis.h>
#include <rx/rx.h>
static void calculate_jmp_addr(RxInst *inst, RzAnalysisOp *op) {
if (inst->v0.kind == RX_OPERAND_COND) {
ut8 pcdsp_l = inst->v0.v.cond.pc_dsp_len;
ut32 pcdsp_val = inst->v0.v.cond.pc_dsp_val;
ut64 addr_inc = pcdsp_val;
if (pcdsp_l >= 8) {
// as SIMM, use signed extend
ut8 shift = pcdsp_l - 1;
if ((1 << shift) & pcdsp_val) {
// as negative
ut32 mask = 0xffffffff << shift;
addr_inc = abs((st32)(pcdsp_val | mask));
op->jump = op->addr - addr_inc;
return;
}
}
op->jump = op->addr + addr_inc;
}
op->fail = op->addr + op->size;
}
static int rx_operand_cnt(RxInst *inst) {
int cnt = 0;
if (inst->v0.kind != RX_OPERAND_NULL) {
cnt++;
}
if (inst->v1.kind != RX_OPERAND_NULL) {
cnt++;
}
if (inst->v2.kind != RX_OPERAND_NULL) {
cnt++;
}
return cnt;
}
static inline RxOperand *rx_operand_get(RxInst *inst, int idx) {
if (idx >= rx_operand_cnt(inst)) {
RZ_LOG_WARN("Failed to get operand%d of ISA Renesas Rx\n", idx);
rz_warn_if_reached();
return NULL;
}
switch (idx) {
case 0:
return &inst->v0;
case 1:
return &inst->v1;
case 2:
return &inst->v2;
default:
return NULL;
}
}
static int analysis_rx_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr,
const ut8 *buf, int len, RzAnalysisOpMask mask) {
op->addr = addr;
op->type = RZ_ANALYSIS_OP_TYPE_ILL;
RxInst inst = { 0 };
st32 bytes_read = 0;
if (!rx_dis(&inst, &bytes_read, buf, len)) {
return bytes_read;
} else {
op->type = RZ_ANALYSIS_OP_TYPE_UNK;
}
op->size = bytes_read;
switch (inst.op) {
// jump related instructions
case RX_OP_RTS:
op->type = RZ_ANALYSIS_OP_TYPE_RET;
op->stackop = RZ_ANALYSIS_STACK_INC;
op->stackptr = 4;
break;
case RX_OP_RTSD:
// use register to deallocate stack frames
op->type = RZ_ANALYSIS_OP_TYPE_RET;
break;
case RX_OP_BSR_A:
case RX_OP_BSR_W:
op->type = RZ_ANALYSIS_OP_TYPE_CALL;
calculate_jmp_addr(&inst, op);
break;
case RX_OP_BCND_W:
case RX_OP_BCND_B:
case RX_OP_BCND_S:
op->type = RZ_ANALYSIS_OP_TYPE_CJMP;
calculate_jmp_addr(&inst, op);
break;
case RX_OP_BRA_A:
case RX_OP_BRA_B:
case RX_OP_BRA_S:
case RX_OP_BRA_W:
op->type = RZ_ANALYSIS_OP_TYPE_JMP;
calculate_jmp_addr(&inst, op);
break;
case RX_OP_BRA_L:
case RX_OP_JMP:
// use register to jump unconditionally
op->type = RZ_ANALYSIS_OP_TYPE_IRJMP;
break;
case RX_OP_BSR_L:
case RX_OP_JSR:
// use register to call unconditionally
op->type = RZ_ANALYSIS_OP_TYPE_IRCALL;
break;
// stack related operations
case RX_OP_PUSH:
case RX_OP_PUSHC:
op->type = RZ_ANALYSIS_OP_TYPE_PUSH;
op->stackop = RZ_ANALYSIS_STACK_DEC;
op->stackptr = 4;
break;
case RX_OP_PUSHM:
op->type = RZ_ANALYSIS_OP_TYPE_PUSH;
op->stackop = RZ_ANALYSIS_STACK_DEC;
op->stackptr = 4 * (inst.v1.v.reg.reg - inst.v0.v.reg.reg + 1);
break;
case RX_OP_POP:
case RX_OP_POPM:
op->type = RZ_ANALYSIS_OP_TYPE_POP;
op->stackop = RZ_ANALYSIS_STACK_INC;
op->stackptr = 4;
break;
case RX_OP_POPC:
op->type = RZ_ANALYSIS_OP_TYPE_POP;
op->stackop = RZ_ANALYSIS_STACK_INC;
op->stackptr = 4 * (inst.v1.v.reg.reg - inst.v0.v.reg.reg + 1);
break;
// normal instruction
case RX_OP_ADD_UB:
case RX_OP_ADC:
// add imm, rn, rm
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
break;
case RX_OP_ADD:
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
if (rx_operand_cnt(&inst) == 2) {
// add imm, sp
const RxOperand *op0 = rx_operand_get(&inst, 0);
if (op0->kind == RX_OPERAND_IMM) {
const RxOperand *op1 = rx_operand_get(&inst, 1);
if (op1->kind == RX_OPERAND_REG && op1->v.reg.reg == RX_REG_R0) {
// modify SP
op->stackop = RZ_ANALYSIS_STACK_INC;
op->stackptr = op0->v.imm.imm;
}
}
}
break;
case RX_OP_SUB:
op->type = RZ_ANALYSIS_OP_TYPE_SUB;
if (rx_operand_cnt(&inst) == 2) {
// sub imm, sp
const RxOperand *op0 = rx_operand_get(&inst, 0);
if (op0->kind == RX_OPERAND_IMM) {
const RxOperand *op1 = rx_operand_get(&inst, 1);
if (op1->kind == RX_OPERAND_REG && op1->v.reg.reg == RX_REG_R0) {
// modify SP
op->stackop = RZ_ANALYSIS_STACK_DEC;
op->stackptr = op0->v.imm.imm;
}
}
}
break;
case RX_OP_SUB_UB:
op->type = RZ_ANALYSIS_OP_TYPE_SUB;
break;
case RX_OP_DIV:
case RX_OP_DIV_UB:
case RX_OP_DIVU:
case RX_OP_DIVU_UB:
op->type = RZ_ANALYSIS_OP_TYPE_DIV;
break;
case RX_OP_MUL:
case RX_OP_MULLO:
case RX_OP_EMUL:
case RX_OP_EMULU:
case RX_OP_EMULU_UB:
case RX_OP_EMUL_UB:
case RX_OP_MUL_UB:
case RX_OP_MULHI:
case RX_OP_MACHI:
case RX_OP_MACLO:
op->type = RZ_ANALYSIS_OP_TYPE_MUL;
break;
case RX_OP_AND:
case RX_OP_AND_UB:
op->type = RZ_ANALYSIS_OP_TYPE_AND;
break;
case RX_OP_OR:
case RX_OP_OR_UB:
op->type = RZ_ANALYSIS_OP_TYPE_OR;
break;
case RX_OP_NOT:
op->type = RZ_ANALYSIS_OP_TYPE_NOT;
break;
case RX_OP_NOP:
op->type = RZ_ANALYSIS_OP_TYPE_NOP;
break;
case RX_OP_NEG:
op->type = RZ_ANALYSIS_OP_TYPE_CPL;
break;
case RX_OP_CMP:
case RX_OP_CMP_UB:
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
break;
case RX_OP_ROTL:
case RX_OP_ROLC:
op->type = RZ_ANALYSIS_OP_TYPE_ROL;
break;
case RX_OP_ROTR:
case RX_OP_RORC:
op->type = RZ_ANALYSIS_OP_TYPE_ROR;
break;
case RX_OP_SHAR:
op->type = RZ_ANALYSIS_OP_TYPE_SAR;
break;
case RX_OP_SHLR:
op->type = RZ_ANALYSIS_OP_TYPE_SHR;
break;
case RX_OP_SHLL:
op->type = RZ_ANALYSIS_OP_TYPE_SHL;
break;
case RX_OP_XCHG_UB:
case RX_OP_XCHG:
op->type = RZ_ANALYSIS_OP_TYPE_XCHG;
break;
case RX_OP_XOR:
case RX_OP_XOR_UB:
op->type = RZ_ANALYSIS_OP_TYPE_XOR;
break;
case RX_OP_INT:
case RX_OP_RTE:
case RX_OP_RTFI:
op->type = RZ_ANALYSIS_OP_TYPE_SWI;
break;
case RX_OP_MOV:
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
if (rx_operand_cnt(&inst) == 2) {
// mov sp, rn
const RxOperand *op0 = rx_operand_get(&inst, 0);
const RxOperand *op1 = rx_operand_get(&inst, 1);
if (op0->kind == RX_OPERAND_IMM) {
if (op1->kind == RX_OPERAND_REG && op1->v.reg.reg == RX_REG_R0) {
// modify SP
op->stackop = RZ_ANALYSIS_STACK_SET;
op->stackptr = op0->v.imm.imm;
}
}
}
break;
case RX_OP_MOVU:
case RX_OP_MVTIPL:
case RX_OP_MVTC:
case RX_OP_MVTACLO:
case RX_OP_MVTACHI:
case RX_OP_MVFACMI:
case RX_OP_MVFACHI:
case RX_OP_MVFC:
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
break;
case RX_OP_ABS:
op->type = RZ_ANALYSIS_OP_TYPE_ABS;
break;
// FPU
case RX_OP_FADD:
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
op->family = RZ_ANALYSIS_OP_FAMILY_FPU;
break;
case RX_OP_FSUB:
op->type = RZ_ANALYSIS_OP_TYPE_SUB;
op->family = RZ_ANALYSIS_OP_FAMILY_FPU;
break;
case RX_OP_FMUL:
op->type = RZ_ANALYSIS_OP_TYPE_MUL;
op->family = RZ_ANALYSIS_OP_FAMILY_FPU;
break;
case RX_OP_FDIV:
op->type = RZ_ANALYSIS_OP_TYPE_DIV;
op->family = RZ_ANALYSIS_OP_FAMILY_FPU;
break;
case RX_OP_FCMP:
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
op->family = RZ_ANALYSIS_OP_FAMILY_FPU;
break;
case RX_OP_ITOF:
case RX_OP_FTOI:
case RX_OP_ITOF_UB:
op->type = RZ_ANALYSIS_OP_TYPE_CAST;
op->family = RZ_ANALYSIS_OP_FAMILY_FPU;
break;
default:
op->type = RZ_ANALYSIS_OP_TYPE_UNK;
break;
}
return op->size;
}
static char *analysis_rx_reg_profile(RzAnalysis *analysis) {
// check librz/reg/profile for register profile description
const char *p =
"=PC pc\n"
"=SP r0\n"
"=ZF zf\n"
"=CF cf\n"
"=SF sf\n"
"=OF of\n"
// ABI: https://www.renesas.com/us/en/document/mat/cc-rx-compiler-users-manual
"=R0 r1\n"
"=A0 r1\n"
"=A1 r2\n"
"=A2 r3\n"
"=A3 r4\n"
// general
"gpr r0 .32 0 0\n"
"gpr r1 .32 4 0\n"
"gpr r2 .32 8 0\n"
"gpr r3 .32 12 0\n"
"gpr r4 .32 16 0\n"
"gpr r5 .32 20 0\n"
"gpr r6 .32 24 0\n"
"gpr r7 .32 28 0\n"
"gpr r8 .32 32 0\n"
"gpr r9 .32 36 0\n"
"gpr r10 .32 40 0\n"
"gpr r11 .32 44 0\n"
"gpr r12 .32 48 0\n"
"gpr r13 .32 52 0\n"
"gpr r14 .32 56 0\n"
"gpr r15 .32 60 0\n"
// control register
"gpr isp .32 64 0\n"
"gpr usp .32 68 0\n"
"gpr intb .32 72 0\n"
"gpr pc .32 76 0\n"
// psw
"gpr psw .32 80 0\n"
"flg ipl .4 .644 0\n"
"flg pm .1 .651 0\n"
"flg u .1 .654 0\n"
"flg i .1 .655 0\n"
"flg of .1 .668 0\n"
"flg sf .1 .669 0\n"
"flg zf .1 .670 0\n"
"flg cf .1 .671 0\n"
"gpr bpc .32 84 0\n"
"gpr bpsw .32 88 0\n"
"gpr fintv .32 92 0\n"
// fpsw, contains multiple flags but ignore now
"gpr fpsw .32 96 0\n"
"flg fsf .1 .768 0\n"
"flg fxf .1 .769 0\n"
"flg fuf .1 .770 0\n"
"flg fzf .1 .771 0\n"
"flg fof .1 .772 0\n"
"flg fvf .1 .773 0\n"
"flg rmode .2 .798 0\n";
return rz_str_dup(p);
}
RzAnalysisPlugin rz_analysis_plugin_rx = {
.name = "rx",
.arch = "rx",
.desc = "Renesas RX Family analysis",
.license = "LGPL3",
.bits = 32,
.op = &analysis_rx_op,
.get_reg_profile = &analysis_rx_reg_profile,
};