arm: Cleanup br instruction handling

The cleaned up code fixes the following issues:
1. "bx pc" should always change to ARM mode (bits=32), never
   Thumb mode (bits=16), since PC is always even.
2. Use (addr & ~3LL) + pcdelta for all PC-relative arithmetic.

and removes checks for "b reg"/"bl reg"/"bx imm"/"bxj imm" as
these instructions don't exist.
This commit is contained in:
Andrew D'Addesio 2019-04-05 17:57:23 -05:00 committed by radare
parent 05998cdfbb
commit fcd10cc52d

View File

@ -1474,17 +1474,19 @@ static int analop_esil(RAnal *a, RAnalOp *op, ut64 addr, const ut8 *buf, int len
case ARM_INS_NOP:
r_strbuf_setf (&op->esil, ",");
break;
case ARM_INS_BL:
case ARM_INS_BLX:
r_strbuf_append (&op->esil, "pc,lr,=,");
/* fallthrough */
case ARM_INS_BX:
case ARM_INS_BXJ:
{
const char *op1 = ARG (0);
if (!strcmp (op1, "pc")) {
r_strbuf_setf (&op->esil, "%d,$$,+,pc,=", pcdelta);
case ARM_INS_B:
if (ISREG(0) && REGID(0) == ARM_REG_PC) {
r_strbuf_appendf (&op->esil, "0x%"PFMT64x",pc,=", (addr & ~3LL) + pcdelta);
} else {
r_strbuf_setf (&op->esil, "%s,pc,=", ARG (0));
r_strbuf_appendf (&op->esil, "%s,pc,=", ARG(0));
}
break;
}
case ARM_INS_UDF:
r_strbuf_setf (&op->esil, "%s,TRAP", ARG(0));
break;
@ -1592,13 +1594,6 @@ r4,r5,r6,3,sp,[*],12,sp,+=
case ARM_INS_CMN:
r_strbuf_appendf (&op->esil, "%s,%s,^,!,!,zf,=", ARG(1), ARG(0));
break;
case ARM_INS_B:
r_strbuf_appendf (&op->esil, "%s,pc,=", ARG(0));
break;
case ARM_INS_BL:
case ARM_INS_BLX:
r_strbuf_appendf (&op->esil, "pc,lr,=,%s,pc,=", ARG(0));
break;
case ARM_INS_MOVT:
r_strbuf_appendf (&op->esil, "16,%s,<<,%s,|=", ARG(1), REG(0));
break;
@ -2892,8 +2887,10 @@ jmp $$ + 4 + ( [delta] * 2 )
case ARM_INS_BLX:
op->cycles = 4;
if (ISREG(0)) {
/* blx reg */
op->type = R_ANAL_OP_TYPE_RCALL;
} else {
/* blx label */
op->type = R_ANAL_OP_TYPE_CALL;
op->jump = IMM(0) & UT32_MAX;
op->fail = addr + op->size;
@ -2903,14 +2900,12 @@ jmp $$ + 4 + ( [delta] * 2 )
}
break;
case ARM_INS_BL:
if (ISREG (0)) {
op->type = R_ANAL_OP_TYPE_RCALL;
} else {
op->type = R_ANAL_OP_TYPE_CALL;
op->jump = IMM(0) & UT32_MAX;
op->fail = addr + op->size;
op->hint.new_bits = a->bits;
}
/* bl label */
op->cycles = 4;
op->type = R_ANAL_OP_TYPE_CALL;
op->jump = IMM(0) & UT32_MAX;
op->fail = addr + op->size;
op->hint.new_bits = a->bits;
break;
case ARM_INS_CBZ:
case ARM_INS_CBNZ:
@ -2924,6 +2919,7 @@ jmp $$ + 4 + ( [delta] * 2 )
}
break;
case ARM_INS_B:
/* b.cc label */
op->cycles = 4;
if (insn->detail->arm.cc == ARM_CC_INVALID) {
op->type = R_ANAL_OP_TYPE_ILL;
@ -2941,31 +2937,25 @@ jmp $$ + 4 + ( [delta] * 2 )
break;
case ARM_INS_BX:
case ARM_INS_BXJ:
// BX LR == RET
/* bx reg */
op->cycles = 4;
if (ISREG (0)) {
switch (REGID(0)) {
case ARM_REG_LR:
op->type = R_ANAL_OP_TYPE_RET;
break;
case ARM_REG_IP:
op->type = R_ANAL_OP_TYPE_UJMP;
break;
case ARM_REG_PC:
// bx pc is well known without ESIL
op->type = R_ANAL_OP_TYPE_UJMP;
op->jump = op->addr + pcdelta;
op->hint.new_bits = (a->bits == 32)? 16 : 32;
break;
default:
op->type = R_ANAL_OP_TYPE_UJMP;
op->eob = true;
break;
}
} else {
op->type = R_ANAL_OP_TYPE_JMP;
op->jump = IMM(0);
op->fail = addr + op->size;
switch (REGID(0)) {
case ARM_REG_LR:
op->type = R_ANAL_OP_TYPE_RET;
break;
case ARM_REG_IP:
op->type = R_ANAL_OP_TYPE_UJMP;
break;
case ARM_REG_PC:
// bx pc is well known without ESIL
op->type = R_ANAL_OP_TYPE_UJMP;
op->jump = (addr & ~3LL) + pcdelta;
op->hint.new_bits = 32;
break;
default:
op->type = R_ANAL_OP_TYPE_UJMP;
op->eob = true;
break;
}
break;
case ARM_INS_ADR: