radare2/libr/anal/p/anal_sh.c
2014-05-19 23:57:19 +02:00

510 lines
16 KiB
C

/* radare - LGPL - Copyright 2010-2013 eloi<limited-entropy.com> */
#include <string.h>
#include <r_types.h>
#include <r_lib.h>
#include <r_asm.h>
#include <r_anal.h>
#define API static
#define LONG_SIZE 4
#define WORD_SIZE 2
#define BYTE_SIZE 1
//Macros for different instruction types
#define IS_RTE(x) ((x) == 0x002b)
#define IS_RTS(x) ((x) == 0x000b)
#define IS_BSRF(x) (((x) & 0xf0ff) == 0x0003)
#define IS_BRAF(x) (((x) & 0xf0ff) == 0x0023)
#define IS_MOVB_REG_TO_R0REL(x) (((x) & 0xF00F) == 0x0004)
#define IS_MOVW_REG_TO_R0REL(x) (((x) & 0xF00F) == 0x0005)
#define IS_MOVL_REG_TO_R0REL(x) (((x) & 0xF00F) == 0x0006)
#define IS_MOVB_R0REL_TO_REG(x) (((x) & 0xF00F) == 0x000C)
#define IS_MOVW_R0REL_TO_REG(x) (((x) & 0xF00F) == 0x000D)
#define IS_MOVL_R0REL_TO_REG(x) (((x) & 0xF00F) == 0x000E)
#define IS_MOVB_REG_TO_REGREF(x) (((x) & 0xF00F) == 0x2000)
#define IS_MOVW_REG_TO_REGREF(x) (((x) & 0xF00F) == 0x2001)
#define IS_MOVL_REG_TO_REGREF(x) (((x) & 0xF00F) == 0x2002)
#define IS_PUSHB(x) (((x) & 0xF00F) == 0x2004)
#define IS_PUSHW(x) (((x) & 0xF00F) == 0x2005)
#define IS_PUSHL(x) (((x) & 0xF00F) == 0x2006)
#define IS_AND_REGS(x) (((x) & 0xF00F) == 0x2009)
#define IS_XOR_REGS(x) (((x) & 0xF00F) == 0x200A)
#define IS_OR_REGS(x) (((x) & 0xF00F) == 0x200B)
#define IS_ADD(x) (((x) & 0xF00F) == 0x300C)
#define IS_ADDC(x) (((x) & 0xF00F) == 0x300E)
#define IS_ADDV(x) (((x) & 0xF00F) == 0x300F)
#define IS_SUB(x) (((x) & 0xF00F) == 0x3008)
#define IS_SUBC(x) (((x) & 0xF00F) == 0x300A)
#define IS_SUBV(x) (((x) & 0xF00F) == 0x300B)
#define IS_JSR(x) (((x) & 0xf0ff) == 0x400b)
#define IS_JMP(x) (((x) & 0xf0ff) == 0x402b)
#define IS_MOV_REGS(x) (((x) & 0xf00f) == 0x6003)
#define IS_MOVB_REGREF_TO_REG(x) (((x) & 0xF00F) == 0x6000)
#define IS_MOVW_REGREF_TO_REG(x) (((x) & 0xF00F) == 0x6001)
#define IS_MOVL_REGREF_TO_REG(x) (((x) & 0xF00F) == 0x6002)
#define IS_BF(x) (((x) & 0xff00) == 0x8B00)
#define IS_BFS(x) (((x) & 0xff00) == 0x8F00)
#define IS_BT(x) (((x) & 0xff00) == 0x8900)
#define IS_BTS(x) (((x) & 0xff00) == 0x8D00)
#define IS_BT_OR_BF(x) IS_BT(x)||IS_BTS(x)||IS_BF(x)||IS_BFS(x)
#define IS_MOVB_REGDISP_R0(x) (((x) & 0xFF00) == 0x8400)
#define IS_MOVW_REGDISP_R0(x) (((x) & 0xFF00) == 0x8500)
#define IS_TRAP(x) (((x) & 0xFF00) == 0xC300)
#define IS_MOVA_PCREL_R0(x) (((x) & 0xFF00) == 0xC700)
#define IS_AND_IMM_R0(x) (((x) & 0xFF00) == 0xC900)
#define IS_XOR_IMM_R0(x) (((x) & 0xFF00) == 0xCA00)
#define IS_OR_IMM_R0(x) (((x) & 0xFF00) == 0xCB00)
/* Compute PC-relative displacement for branch instructions */
#define GET_BRA_OFFSET(x) ((x) & 0x0fff)
#define GET_BTF_OFFSET(x) ((x) & 0x00ff)
/* Compute reg nr for BRAF,BSR,BSRF,JMP,JSR */
#define GET_TARGET_REG(x) ((x >> 8) & 0x0f)
#define GET_SOURCE_REG(x) ((x >> 4) & 0x0f)
#define PC_IDX 16
static ut64 disarm_12bit_offset (RAnalOp *op, unsigned int insoff) {
ut64 off = insoff;
/* sign extend if higher bit is 1 (0x0800) */
if ((off & 0x0800) == 0x0800)
off |= ~0xFFF;
return (op->addr<<1) + off + 4;
}
#if unused
static ut64 disarm_8bit_offset (unsigned int pc, unsigned int insoff) {
ut64 add = insoff;
/* sign extend if higher bit is 1 (0x08) */
if ((add & 0x80) == 0x80)
add |= 0x00;
return (add<<1) + pc + 4; //2*sign_extend(displacement) + 4
}
#endif
static char *regs[]={"r0","r1","r2","r3","r4","r5","r6","r7","r8","r9","r10","r11","r12","r13","r14","r15","pc"};
static RAnalValue *anal_fill_ai_rg(RAnal *anal, int idx) {
RAnalValue *ret = r_anal_value_new ();
ret->reg = r_reg_get (anal->reg,regs[idx],R_REG_TYPE_GPR);
return ret;
}
static RAnalValue *anal_fill_im(RAnal *anal, st32 v) {
RAnalValue *ret = r_anal_value_new ();
ret->imm = v;
return ret;
}
/* Implements @(disp,Rn) , size=1 for .b, 2 for .w, 4 for .l */
static RAnalValue *anal_fill_reg_disp_mem(RAnal *anal, int reg, st64 delta, st64 size) {
RAnalValue *ret = anal_fill_ai_rg(anal,reg);
ret->memref = size;
ret->delta = delta*size;
return ret;
}
static RAnalValue *anal_fill_reg_ref(RAnal *anal, int reg, st64 size){
RAnalValue *ret = anal_fill_ai_rg(anal,reg);
ret->memref = size;
return ret;
}
/* @(R0,Rx) references for all sizes */
static RAnalValue *anal_fill_r0_reg_ref(RAnal *anal, int reg,st64 size){
RAnalValue *ret = anal_fill_ai_rg(anal,0);
ret->regdelta = r_reg_get(anal->reg,regs[reg],R_REG_TYPE_GPR);
ret->memref = size;
return ret;
}
#if unused
static st32 sign_extend_12b(st32 v){
if( v & 0x800 ){
v |= 0xFFFFF000;
}
return v;
}
#endif
static RAnalValue *anal_pcrel_disp_mov(RAnal* anal,RAnalOp* op,st8 disp){
RAnalValue *ret = r_anal_value_new ();
ret->base = op->addr;
ret->delta = (op->addr & 0x02)?WORD_SIZE:LONG_SIZE;
ret->delta = ret->delta + disp*LONG_SIZE;
return ret;
}
static RAnalValue *anal_regrel_jump(RAnal* anal,RAnalOp* op, ut8 reg){
RAnalValue *ret = r_anal_value_new ();
ret->reg = r_reg_get(anal->reg,regs[reg],R_REG_TYPE_GPR);
ret->delta = op->addr+4;
return ret;
}
/* 16 decoder routines, based on 1st nibble value */
static int first_nibble_is_0(RAnal* anal, RAnalOp* op, ut16 code){
if(IS_BSRF(code)){
/* Call 'far' subroutine Rn+PC+4 */
op->type = R_ANAL_OP_TYPE_UCALL;
op->delay = 1;
op->dst = anal_regrel_jump(anal,op,GET_TARGET_REG(code));
} else if (IS_BRAF(code)){
/* Unconditional branch to Rn+PC+4, no delay slot */
op->type = R_ANAL_OP_TYPE_UJMP;
op->dst = anal_regrel_jump(anal,op,GET_TARGET_REG(code));
op->eob = R_TRUE;
} else if( IS_RTS(code) ){
/* Ret from subroutine. Returns to pr */
//TODO Convert into jump pr?
op->type = R_ANAL_OP_TYPE_RET;
op->delay = 1;
op->eob = R_TRUE;
} else if (IS_RTE(code)){
//TODO Convert into jmp spc? Indicate ssr->sr as well?
op->type = R_ANAL_OP_TYPE_RET;
op->delay = 1;
op->eob = R_TRUE;
} else if (IS_MOVB_REG_TO_R0REL(code)){
op->type = R_ANAL_OP_TYPE_MOV;
op->src[0] = anal_fill_ai_rg(anal,GET_SOURCE_REG(code));
op->dst = anal_fill_r0_reg_ref(anal,GET_TARGET_REG(code),BYTE_SIZE);
} else if (IS_MOVW_REG_TO_R0REL(code)){
op->type = R_ANAL_OP_TYPE_MOV;
op->src[0] = anal_fill_ai_rg(anal,GET_SOURCE_REG(code));
op->dst = anal_fill_r0_reg_ref(anal,GET_TARGET_REG(code),WORD_SIZE);
} else if (IS_MOVL_REG_TO_R0REL(code)){
op->type = R_ANAL_OP_TYPE_MOV;
op->src[0] = anal_fill_ai_rg(anal,GET_SOURCE_REG(code));
op->dst = anal_fill_r0_reg_ref(anal,GET_TARGET_REG(code),LONG_SIZE);
} else if (IS_MOVB_R0REL_TO_REG(code)){
op->type = R_ANAL_OP_TYPE_MOV;
op->src[0] = anal_fill_r0_reg_ref(anal,GET_SOURCE_REG(code),BYTE_SIZE);
op->dst = anal_fill_ai_rg(anal,GET_TARGET_REG(code));
} else if (IS_MOVW_R0REL_TO_REG(code)){
op->type = R_ANAL_OP_TYPE_MOV;
op->src[0] = anal_fill_r0_reg_ref(anal,GET_SOURCE_REG(code),WORD_SIZE);
op->dst = anal_fill_ai_rg(anal,GET_TARGET_REG(code));
} else if (IS_MOVL_R0REL_TO_REG(code)){
op->type = R_ANAL_OP_TYPE_MOV;
op->src[0] = anal_fill_r0_reg_ref(anal,GET_SOURCE_REG(code),LONG_SIZE);
op->dst = anal_fill_ai_rg(anal,GET_TARGET_REG(code));
}
//TODO Check missing insns, specially STC might be interesting
return op->size;
}
static int movl_reg_rdisp(RAnal* anal, RAnalOp* op, ut16 code){
op->type = R_ANAL_OP_TYPE_MOV;
op->src[0] = anal_fill_ai_rg(anal,GET_SOURCE_REG(code));
op->dst = anal_fill_reg_disp_mem(anal,GET_TARGET_REG(code),code&0x0F,LONG_SIZE);
return op->size;
}
static int first_nibble_is_2(RAnal* anal, RAnalOp* op, ut16 code){
//TODO handle mov.x Rm, @Rn, (X)OR/AND regs,
if (IS_MOVB_REG_TO_REGREF(code)){
op->type = R_ANAL_OP_TYPE_MOV;
op->src[0] = anal_fill_ai_rg(anal,GET_SOURCE_REG(code));
op->dst = anal_fill_reg_ref(anal,GET_TARGET_REG(code),BYTE_SIZE);
} else if (IS_MOVW_REG_TO_REGREF(code)){
op->type = R_ANAL_OP_TYPE_MOV;
op->src[0] = anal_fill_ai_rg(anal,GET_SOURCE_REG(code));
op->dst = anal_fill_reg_ref(anal,GET_TARGET_REG(code),WORD_SIZE);
} else if (IS_MOVL_REG_TO_REGREF(code)){
op->type = R_ANAL_OP_TYPE_MOV;
op->src[0] = anal_fill_ai_rg(anal,GET_SOURCE_REG(code));
op->dst = anal_fill_reg_ref(anal,GET_TARGET_REG(code),LONG_SIZE);
} else if (IS_AND_REGS(code)){
op->type = R_ANAL_OP_TYPE_AND;
op->src[0] = anal_fill_ai_rg(anal,GET_SOURCE_REG(code));
op->dst = anal_fill_ai_rg(anal,GET_TARGET_REG(code));
} else if (IS_XOR_REGS(code)){
op->type = R_ANAL_OP_TYPE_XOR;
op->src[0] = anal_fill_ai_rg(anal,GET_SOURCE_REG(code));
op->dst = anal_fill_ai_rg(anal,GET_TARGET_REG(code));
} else if (IS_OR_REGS(code)){
op->type = R_ANAL_OP_TYPE_OR;
op->src[0] = anal_fill_ai_rg(anal,GET_SOURCE_REG(code));
op->dst = anal_fill_ai_rg(anal,GET_TARGET_REG(code));
}
//TODO Handle 'pushes' (mov Rm,@-Rn)
//TODO Handle CMP/STR ??
return op->size;
}
static int first_nibble_is_3(RAnal* anal, RAnalOp* op, ut16 code){
//TODO Handle carry/overflow , CMP/xx?
if( IS_ADD(code) || IS_ADDC(code) || IS_ADDV(code) ){
op->type = R_ANAL_OP_TYPE_ADD;
op->src[0] = anal_fill_ai_rg(anal,GET_SOURCE_REG(code));
op->dst = anal_fill_ai_rg(anal,GET_TARGET_REG(code));
} else if ( IS_SUB(code) || IS_SUBC(code) || IS_SUBV(code)){
op->type = R_ANAL_OP_TYPE_SUB;
op->src[0] = anal_fill_ai_rg(anal,GET_SOURCE_REG(code));
op->dst = anal_fill_ai_rg(anal,GET_TARGET_REG(code));
}
return op->size;
}
static int first_nibble_is_4(RAnal* anal, RAnalOp* op, ut16 code){
if(IS_JSR(code)){
op->type = R_ANAL_OP_TYPE_UCALL; //call to reg
op->delay = 1;
op->dst = anal_fill_ai_rg(anal,GET_TARGET_REG(code));
} else if ( IS_JMP(code) ){
op->type = R_ANAL_OP_TYPE_UJMP; //jmp to reg
op->dst = anal_fill_ai_rg(anal,GET_TARGET_REG(code));
op->delay = 1;
op->eob = R_TRUE;
}
//TODO shifts + many system insns + CMP/P[L|Z]??
return op->size;
}
static int movl_rdisp_reg(RAnal* anal, RAnalOp* op, ut16 code){
op->type = R_ANAL_OP_TYPE_MOV;
op->dst = anal_fill_ai_rg(anal,GET_TARGET_REG(code));
op->src[0] = anal_fill_reg_disp_mem(anal,GET_SOURCE_REG(code),code&0x0F,LONG_SIZE);
return op->size;
}
static int first_nibble_is_6(RAnal* anal, RAnalOp* op, ut16 code){
if(IS_MOV_REGS(code)){
op->type = R_ANAL_OP_TYPE_MOV;
op->src[0] = anal_fill_ai_rg(anal,GET_SOURCE_REG(code));
op->dst = anal_fill_ai_rg(anal,GET_TARGET_REG(code));
} else if (IS_MOVB_REGREF_TO_REG(code)){
op->type = R_ANAL_OP_TYPE_MOV;
op->src[0] = anal_fill_reg_ref(anal,GET_SOURCE_REG(code),BYTE_SIZE);
op->dst = anal_fill_ai_rg(anal,GET_TARGET_REG(code));
} else if (IS_MOVW_REGREF_TO_REG(code)){
op->type = R_ANAL_OP_TYPE_MOV;
op->src[0] = anal_fill_reg_ref(anal,GET_SOURCE_REG(code),WORD_SIZE);
op->dst = anal_fill_ai_rg(anal,GET_TARGET_REG(code));
} else if (IS_MOVL_REGREF_TO_REG(code)){
op->type = R_ANAL_OP_TYPE_MOV;
op->src[0] = anal_fill_reg_ref(anal,GET_SOURCE_REG(code),LONG_SIZE);
op->dst = anal_fill_ai_rg(anal,GET_TARGET_REG(code));
}
//TODO neg(c) + MOV.L @Rm+,Rn
return op->size;
}
static int add_imm(RAnal* anal, RAnalOp* op, ut16 code){
op->type = R_ANAL_OP_TYPE_ADD;
op->src[0] = anal_fill_im(anal, (st8)(code&0xFF)); //Casting to (st8) forces sign-extension.
op->dst = anal_fill_ai_rg(anal,GET_TARGET_REG(code));
return op->size;
}
static int first_nibble_is_8(RAnal* anal, RAnalOp* op, ut16 code){
if (IS_BT_OR_BF(code)){
op->type = R_ANAL_OP_TYPE_CJMP; //Jump if true or jump if false insns
op->jump = (op->addr << 1) +4 + (st8)GET_BTF_OFFSET(code);
op->fail = op->addr + 2 ;
op->eob = R_TRUE;
if (IS_BTS(code) || IS_BFS(code))
op->delay = 1; //Only /S versions have a delay slot
} else if (IS_MOVB_REGDISP_R0(code)){
op->type = R_ANAL_OP_TYPE_MOV;
op->dst = anal_fill_ai_rg(anal,GET_TARGET_REG(code));
op->src[0] = anal_fill_reg_disp_mem(anal,GET_SOURCE_REG(code),code&0x0F,BYTE_SIZE);
} else if (IS_MOVW_REGDISP_R0(code)){
op->type = R_ANAL_OP_TYPE_MOV;
op->dst = anal_fill_ai_rg(anal,GET_TARGET_REG(code));
op->src[0] = anal_fill_reg_disp_mem(anal,GET_SOURCE_REG(code),code&0x0F,WORD_SIZE);
}
//TODO some movs + CMP/EQ??
return op->size;
}
static int movw_pcdisp_reg(RAnal* anal, RAnalOp* op, ut16 code){
op->type = R_ANAL_OP_TYPE_MOV;
op->dst = anal_fill_ai_rg(anal, GET_TARGET_REG(code));
op->src[0] = anal_fill_reg_disp_mem(anal,PC_IDX,code&0xFF,WORD_SIZE);
return op->size;
}
static int bra(RAnal* anal, RAnalOp* op, ut16 code){
/* Unconditional branch, relative to PC */
op->type = R_ANAL_OP_TYPE_JMP;
op->delay = 1;
op->jump = disarm_12bit_offset(op,GET_BRA_OFFSET(code));
op->eob = R_TRUE;
return op->size;
}
static int bsr(RAnal* anal, RAnalOp* op, ut16 code){
/* Subroutine call, relative to PC */
op->type = R_ANAL_OP_TYPE_CALL;
op->jump = disarm_12bit_offset(op,GET_BRA_OFFSET(code));
op->delay = 1;
return op->size;
}
static int first_nibble_is_c(RAnal* anal, RAnalOp* op, ut16 code){
if (IS_TRAP(code)){
op->type = R_ANAL_OP_TYPE_SWI;
op->val = (ut8)(code&0xFF);
} else if (IS_MOVA_PCREL_R0(code)){
op->type = R_ANAL_OP_TYPE_MOV;
op->src[0] = anal_pcrel_disp_mov(anal,op,code&0xFF);
op->dst = anal_fill_ai_rg(anal,0); //Always R0
} else if (IS_AND_IMM_R0(code)){
op->type = R_ANAL_OP_TYPE_AND;
op->src[0] = anal_fill_im(anal,code&0xFF);
op->dst = anal_fill_ai_rg(anal,0); //Always R0
} else if (IS_OR_IMM_R0(code)){
op->type = R_ANAL_OP_TYPE_OR;
op->src[0] = anal_fill_im(anal,code&0xFF);
op->dst = anal_fill_ai_rg(anal,0); //Always R0
} else if (IS_XOR_IMM_R0(code)){
op->type = R_ANAL_OP_TYPE_XOR;
op->src[0] = anal_fill_im(anal,code&0xFF);
op->dst = anal_fill_ai_rg(anal,0); //Always R0
}
//TODO Logic insns referencing GBR
return op->size;
}
static int movl_pcdisp_reg(RAnal* anal, RAnalOp* op, ut16 code){
op->type = R_ANAL_OP_TYPE_MOV;
op->src[0] = anal_pcrel_disp_mov(anal,op,code&0x0F);
op->dst = anal_fill_ai_rg(anal,GET_TARGET_REG(code));
return op->size;
}
static int mov_imm_reg(RAnal* anal, RAnalOp* op, ut16 code){
op->type = R_ANAL_OP_TYPE_MOV;
op->dst = anal_fill_ai_rg(anal,GET_TARGET_REG(code));
op->src[0] = anal_fill_im(anal,(st8)(code & 0xFF));
return op->size;
}
static int fpu_insn(RAnal* anal, RAnalOp* op, ut16 code){
//Not interested on FPU stuff for now
op->family = R_ANAL_OP_FAMILY_FPU;
return op->size;
}
/* Table of routines for further analysis based on 1st nibble */
static int (*first_nibble_decode[])(RAnal*,RAnalOp*,ut16) = {
first_nibble_is_0,
movl_reg_rdisp,
first_nibble_is_2,
first_nibble_is_3,
first_nibble_is_4,
movl_rdisp_reg,
first_nibble_is_6,
add_imm,
first_nibble_is_8,
movw_pcdisp_reg,
bra,
bsr,
first_nibble_is_c,
movl_pcdisp_reg,
mov_imm_reg,
fpu_insn
};
/* This is the basic operation analysis. Just initialize and jump to
* routines defined in first_nibble_decode table
*/
static int sh_op(RAnal *anal, RAnalOp *op, ut64 addr, const ut8 *data, int len) {
//TODO Check if we must do big or little endian. Also fix in RAsm
ut8 b;
int ret;
if (data == NULL)
return 0;
b = data[1]; //First byte, little endian
memset (op, '\0', sizeof (RAnalOp));
op->addr = addr;
op->type = R_ANAL_OP_TYPE_UNK;
op->jump = op->fail = -1;
op->ptr = op->val = -1;
op->size = 2;
ret = first_nibble_decode[(b>>4) & 0x0F](anal, op, *((ut16 *)data));
return ret;
}
/* Set the profile register */
static int sh_set_reg_profile(RAnal* anal){
//TODO Add system ( ssr, spc ) + fpu regs
int ret = r_reg_set_profile_string(anal->reg,
"=pc pc\n"
"=sp r15\n"
"=bp r14\n"
"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"
"gpr pc .32 64 0\n"
"gpr pr .32 68 0\n"
"gpr sr .32 72 0\n"
"gpr gbr .32 76 0\n"
"gpr mach .32 80 0\n"
"gpr macl .32 84 0\n"
);
return ret;
}
struct r_anal_plugin_t r_anal_plugin_sh = {
.name = "sh",
.desc = "SH-4 code analysis plugin",
.license = "LGPL3",
.arch = R_SYS_ARCH_SH,
.bits = 32,
.init = NULL,
.fini = NULL,
.op = &sh_op,
.set_reg_profile = &sh_set_reg_profile,
.fingerprint_bb = NULL,
.fingerprint_fcn = NULL,
.diff_bb = NULL,
.diff_fcn = NULL,
.diff_eval = NULL
};
#ifndef CORELIB
struct r_lib_struct_t radare_plugin = {
.type = R_LIB_TYPE_ANAL,
.data = &r_anal_plugin_sh
};
#endif