* added breakpoints and stepping * add link register to allow single-stepping a ret instruction, fix stacktraces * refactor to avoid passing the IO layer structs to the breakpoint function * add tests, refine the stacktrace to not include non-function * add register information for core file parsing * core file generation for RISC-V * make tests run under riscv-64 * make tests run under riscv-32
136 lines
4.1 KiB
C
136 lines
4.1 KiB
C
// SPDX-FileCopyrightText: 2015 pancake <pancake@nopcode.org>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
/* implementation */
|
|
|
|
static int iscallret(RzDebug *dbg, ut64 addr) {
|
|
ut8 buf[32];
|
|
if (addr == 0LL || addr == UT64_MAX)
|
|
return 0;
|
|
/* check if region is executable */
|
|
/* check if previous instruction is a call */
|
|
/* if x86 expect CALL to be 5 byte length */
|
|
if (dbg->arch && !strcmp(dbg->arch, "x86")) {
|
|
(void)dbg->iob.read_at(dbg->iob.io, addr - 5, buf, 5);
|
|
if (buf[0] == 0xe8) {
|
|
return 1;
|
|
}
|
|
if (buf[3] == 0xff /* bits 4-5 (from right) of next byte must be 01 */
|
|
&& ((buf[4] & 0xf0) == 0xd0 /* Mod is 11 */
|
|
|| ((buf[4] & 0xf0) == 0x10 /* Mod is 00 */
|
|
&& (buf[4] & 0x06) != 0x04))) { /* R/M not 10x */
|
|
return 1;
|
|
}
|
|
/* check for 7-byte CALL indirect (encoded by FF 14 25) */
|
|
(void)dbg->iob.read_at(dbg->iob.io, addr - 7, buf, 7);
|
|
if (!memcmp(buf, "\xff\x14\x25", 3)) {
|
|
return 1;
|
|
}
|
|
// IMMAMISSINGANYOP
|
|
} else if (dbg->arch && !strcmp(dbg->arch, "riscv")) {
|
|
RzAnalysisOp op = { 0 };
|
|
(void)dbg->iob.read_at(dbg->iob.io, addr - 4, buf, 4);
|
|
rz_analysis_op_init(&op);
|
|
|
|
(void)rz_analysis_op(dbg->analysis, &op, addr - 4, buf, 4, RZ_ANALYSIS_OP_MASK_ALL);
|
|
// is the operation precedeing the address a call ?
|
|
if (op.type == RZ_ANALYSIS_OP_TYPE_CALL || op.type == RZ_ANALYSIS_OP_TYPE_UCALL) {
|
|
bool target_known = op.jump != 0 && op.jump != UT64_MAX;
|
|
// The address is assumed to be a true return address when one of the following is true
|
|
// 1. The target of the call is known and is a valid function
|
|
// 2. The target of the call is unknown (register-indirect call or similar)
|
|
if (!target_known || rz_analysis_get_function_at(dbg->analysis, op.jump)) {
|
|
rz_analysis_op_fini(&op);
|
|
return 1;
|
|
}
|
|
}
|
|
rz_analysis_op_fini(&op);
|
|
} else {
|
|
RzAnalysisOp op = { 0 };
|
|
(void)dbg->iob.read_at(dbg->iob.io, addr - 8, buf, 8);
|
|
rz_analysis_op_init(&op);
|
|
(void)rz_analysis_op(dbg->analysis, &op, addr - 8, buf, 8, RZ_ANALYSIS_OP_MASK_BASIC);
|
|
if (op.type == RZ_ANALYSIS_OP_TYPE_CALL || op.type == RZ_ANALYSIS_OP_TYPE_UCALL) {
|
|
rz_analysis_op_fini(&op);
|
|
return 1;
|
|
}
|
|
rz_analysis_op_fini(&op);
|
|
/* delay slot */
|
|
rz_analysis_op_init(&op);
|
|
(void)rz_analysis_op(dbg->analysis, &op, addr - 4, buf, 4, RZ_ANALYSIS_OP_MASK_BASIC);
|
|
if (op.type == RZ_ANALYSIS_OP_TYPE_CALL || op.type == RZ_ANALYSIS_OP_TYPE_UCALL) {
|
|
rz_analysis_op_fini(&op);
|
|
return 1;
|
|
}
|
|
rz_analysis_op_fini(&op);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static RzList /*<RzDebugFrame *>*/ *backtrace_fuzzy(RzDebug *dbg, ut64 at) {
|
|
ut8 *stack, *ptr;
|
|
int wordsize = dbg->bits; // XXX, dbg->bits is wordsize not bits
|
|
ut64 sp;
|
|
RzIOBind *bio = &dbg->iob;
|
|
int i, stacksize;
|
|
ut64 *p64, addr = 0LL;
|
|
ut32 *p32;
|
|
ut16 *p16;
|
|
ut64 cursp, oldsp;
|
|
RzList *list;
|
|
|
|
stacksize = 1024 * 512; // 512KB .. should get the size from the regions if possible
|
|
stack = malloc(stacksize);
|
|
if (at == UT64_MAX) {
|
|
RzRegItem *ri;
|
|
RzReg *reg = dbg->reg;
|
|
const char *spname = rz_reg_get_name(reg, RZ_REG_NAME_SP);
|
|
if (!spname) {
|
|
eprintf("Cannot find stack pointer register\n");
|
|
free(stack);
|
|
return NULL;
|
|
}
|
|
ri = rz_reg_get(reg, spname, RZ_REG_TYPE_GPR);
|
|
if (!ri) {
|
|
eprintf("Cannot find stack pointer register\n");
|
|
free(stack);
|
|
return NULL;
|
|
}
|
|
sp = rz_reg_get_value(reg, ri);
|
|
} else {
|
|
sp = at;
|
|
}
|
|
|
|
list = rz_list_new();
|
|
list->free = free;
|
|
cursp = oldsp = sp;
|
|
(void)bio->read_at(bio->io, sp, stack, stacksize);
|
|
ptr = stack;
|
|
for (i = 0; i < dbg->btdepth; i++) {
|
|
p64 = (ut64 *)ptr;
|
|
p32 = (ut32 *)ptr;
|
|
p16 = (ut16 *)ptr;
|
|
switch (wordsize) {
|
|
case 8: addr = *p64; break;
|
|
case 4: addr = *p32; break;
|
|
case 2: addr = *p16; break;
|
|
default:
|
|
eprintf("Invalid word size with asm.bits\n");
|
|
rz_list_free(list);
|
|
return NULL;
|
|
}
|
|
if (iscallret(dbg, addr)) {
|
|
RzDebugFrame *frame = RZ_NEW0(RzDebugFrame);
|
|
frame->addr = addr;
|
|
frame->size = cursp - oldsp;
|
|
frame->sp = cursp;
|
|
frame->bp = oldsp; // addr + (i * wordsize); // -4 || -8
|
|
// eprintf ("--------------> 0x%" PFMT64x " (%d)\n", addr, frame->size);
|
|
rz_list_append(list, frame);
|
|
oldsp = cursp;
|
|
}
|
|
ptr += wordsize;
|
|
cursp += wordsize;
|
|
}
|
|
return list;
|
|
}
|