rizin/librz/debug/trace.c
Florian Märkl f9e01ba07d [RzIL] Rewrite RzAnalysisRzil as RzAnalysisILVM with config
RzAnalysisILVM wraps around the low-level RzILVM and enables emulation
of real code from disassembly, rather than raw IL.
Analysis plugins now don't actively initialize the vm anymore, but
return a fully declarative description RzAnalysisILConfig of how to set
up the vm and optionally its initial state.
This also enables multiple IL vms to exist at the same time as plugins
can not mess with the global vm anymore. See the added integration test
for an example.
2022-01-17 16:50:40 +01:00

307 lines
7.9 KiB
C

// SPDX-FileCopyrightText: 2008-2020 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_debug.h>
/* Old debug trace implementation */
RZ_API RzDebugTrace *rz_debug_trace_new(void) {
RzDebugTrace *t = RZ_NEW0(RzDebugTrace);
if (!t) {
return NULL;
}
t->tag = 1; // UT32_MAX;
t->addresses = NULL;
t->enabled = false;
t->traces = rz_list_new();
if (!t->traces) {
rz_debug_trace_free(t);
return NULL;
}
t->traces->free = free;
t->ht = ht_pp_new0();
if (!t->ht) {
rz_debug_trace_free(t);
return NULL;
}
return t;
}
RZ_API void rz_debug_trace_free(RzDebugTrace *trace) {
if (!trace) {
return;
}
rz_list_purge(trace->traces);
free(trace->traces);
ht_pp_free(trace->ht);
RZ_FREE(trace);
}
// TODO: added overlap/mask support here...
// TODO: think about tagged traces
RZ_API int rz_debug_trace_tag(RzDebug *dbg, int tag) {
// if (tag>0 && tag<31) core->dbg->trace->tag = 1<<(sz-1);
return (dbg->trace->tag = (tag > 0) ? tag : UT32_MAX);
}
RZ_API bool rz_debug_trace_ins_before(RzDebug *dbg) {
RzListIter *it, *it_tmp;
RzAnalysisValue *val;
ut8 buf_pc[32];
// Analyze current instruction
ut64 pc = rz_debug_reg_get(dbg, dbg->reg->name[RZ_REG_NAME_PC]);
if (!dbg->iob.read_at) {
return false;
}
if (!dbg->iob.read_at(dbg->iob.io, pc, buf_pc, sizeof(buf_pc))) {
return false;
}
dbg->cur_op = RZ_NEW0(RzAnalysisOp);
if (!dbg->cur_op) {
return false;
}
if (!rz_analysis_op(dbg->analysis, dbg->cur_op, pc, buf_pc, sizeof(buf_pc), RZ_ANALYSIS_OP_MASK_VAL)) {
rz_analysis_op_free(dbg->cur_op);
dbg->cur_op = NULL;
return false;
}
// resolve mem write address
rz_list_foreach_safe (dbg->cur_op->access, it, it_tmp, val) {
switch (val->type) {
case RZ_ANALYSIS_VAL_REG:
if (!(val->access & RZ_ANALYSIS_ACC_W)) {
rz_list_delete(dbg->cur_op->access, it);
}
break;
case RZ_ANALYSIS_VAL_MEM:
if (val->memref > 32) {
eprintf("Error: adding changes to %d bytes in memory.\n", val->memref);
rz_list_delete(dbg->cur_op->access, it);
break;
}
if (val->access & RZ_ANALYSIS_ACC_W) {
// resolve memory address
ut64 addr = 0;
addr += val->delta;
if (val->seg) {
addr += rz_reg_get_value(dbg->reg, val->seg);
}
if (val->reg) {
addr += rz_reg_get_value(dbg->reg, val->reg);
}
if (val->regdelta) {
int mul = val->mul ? val->mul : 1;
addr += mul * rz_reg_get_value(dbg->reg, val->regdelta);
}
// resolve address into base for ins_after
val->base = addr;
} else {
rz_list_delete(dbg->cur_op->access, it);
}
default:
break;
}
}
return true;
}
RZ_API bool rz_debug_trace_ins_after(RzDebug *dbg) {
rz_return_val_if_fail(dbg->cur_op, false);
RzListIter *it;
RzAnalysisValue *val;
// Add reg/mem write change
rz_debug_reg_sync(dbg, RZ_REG_TYPE_ANY, false);
rz_list_foreach (dbg->cur_op->access, it, val) {
if (!(val->access & RZ_ANALYSIS_ACC_W)) {
continue;
}
switch (val->type) {
case RZ_ANALYSIS_VAL_REG: {
if (!val->reg) {
RZ_LOG_ERROR("invalid register, unable to trace register state\n");
continue;
}
ut64 data = rz_reg_get_value(dbg->reg, val->reg);
// add reg write
rz_debug_session_add_reg_change(dbg->session, val->reg->arena, val->reg->offset, data);
break;
}
case RZ_ANALYSIS_VAL_MEM: {
ut8 buf[32] = { 0 };
if (!dbg->iob.read_at(dbg->iob.io, val->base, buf, val->memref)) {
eprintf("Error reading memory at 0x%" PFMT64x "\n", val->base);
break;
}
// add mem write
size_t i;
for (i = 0; i < val->memref; i++) {
rz_debug_session_add_mem_change(dbg->session, val->base + i, buf[i]);
}
break;
}
default:
break;
}
}
rz_analysis_op_free(dbg->cur_op);
dbg->cur_op = NULL;
return true;
}
/*
* something happened at the given pc that we need to trace
*/
RZ_API int rz_debug_trace_pc(RzDebug *dbg, ut64 pc) {
ut8 buf[32];
RzAnalysisOp op = { 0 };
if (!dbg->iob.is_valid_offset(dbg->iob.io, pc, 0)) {
eprintf("trace_pc: cannot read memory at 0x%" PFMT64x "\n", pc);
return false;
}
(void)dbg->iob.read_at(dbg->iob.io, pc, buf, sizeof(buf));
if (rz_analysis_op(dbg->analysis, &op, pc, buf, sizeof(buf), RZ_ANALYSIS_OP_MASK_ESIL) < 1) {
eprintf("trace_pc: cannot get opcode size at 0x%" PFMT64x "\n", pc);
return false;
}
rz_debug_trace_op(dbg, &op);
rz_analysis_op_fini(&op);
return true;
}
RZ_API void rz_debug_trace_op(RzDebug *dbg, RzAnalysisOp *op) {
static ut64 oldpc = UT64_MAX; // Must trace the previously traced instruction
if (dbg->trace->enabled) {
if (dbg->analysis->esil) {
rz_analysis_esil_trace_op(dbg->analysis->esil, op);
} else {
if (dbg->verbose) {
eprintf("Run aeim to get dbg->analysis->esil initialized\n");
}
}
}
if (oldpc != UT64_MAX) {
rz_debug_trace_add(dbg, oldpc, op->size); // XXX review what this line really do
}
oldpc = op->addr;
}
RZ_API void rz_debug_trace_at(RzDebug *dbg, const char *str) {
// TODO: parse offsets and so use ut64 instead of strstr()
free(dbg->trace->addresses);
dbg->trace->addresses = (str && *str) ? strdup(str) : NULL;
}
RZ_API RzDebugTracepoint *rz_debug_trace_get(RzDebug *dbg, ut64 addr) {
int tag = dbg->trace->tag;
return ht_pp_find(dbg->trace->ht,
sdb_fmt("trace.%d.%" PFMT64x, tag, addr), NULL);
}
static int cmpaddr(const void *_a, const void *_b) {
const RzListInfo *a = _a, *b = _b;
return (rz_itv_begin(a->pitv) > rz_itv_begin(b->pitv)) ? 1 : (rz_itv_begin(a->pitv) < rz_itv_begin(b->pitv)) ? -1
: 0;
}
RZ_API void rz_debug_trace_list(RzDebug *dbg, int mode, ut64 offset) {
int tag = dbg->trace->tag;
RzListIter *iter;
bool flag = false;
RzList *info_list = rz_list_new();
if (!info_list && mode == '=') {
return;
}
RzDebugTracepoint *trace;
rz_list_foreach (dbg->trace->traces, iter, trace) {
if (!trace->tag || (tag & trace->tag)) {
switch (mode) {
case 'q':
dbg->cb_printf("0x%" PFMT64x "\n", trace->addr);
break;
case '=': {
RzListInfo *info = RZ_NEW0(RzListInfo);
if (!info) {
rz_list_free(info_list);
return;
}
info->pitv = (RzInterval){ trace->addr, trace->size };
info->vitv = info->pitv;
info->perm = -1;
info->name = rz_str_newf("%d", trace->times);
info->extra = rz_str_newf("%d", trace->count);
rz_list_append(info_list, info);
flag = true;
} break;
case 1:
case '*':
dbg->cb_printf("dt+ 0x%" PFMT64x " %d\n", trace->addr, trace->times);
break;
default:
dbg->cb_printf("0x%08" PFMT64x " size=%d count=%d times=%d tag=%d\n",
trace->addr, trace->size, trace->count, trace->times, trace->tag);
break;
}
}
}
if (flag) {
rz_list_sort(info_list, cmpaddr);
RzTable *table = rz_table_new();
table->cons = rz_cons_singleton();
RzIO *io = dbg->iob.io;
rz_table_visual_list(table, info_list, offset, 1,
rz_cons_get_size(NULL), io->va);
io->cb_printf("\n%s\n", rz_table_tostring(table));
rz_table_free(table);
rz_list_free(info_list);
}
}
// XXX: find better name, make it public?
static int rz_debug_trace_is_traceable(RzDebug *dbg, ut64 addr) {
if (dbg->trace->addresses) {
char addr_str[32];
snprintf(addr_str, sizeof(addr_str), "0x%08" PFMT64x, addr);
if (!strstr(dbg->trace->addresses, addr_str)) {
return false;
}
}
return true;
}
RZ_API RzDebugTracepoint *rz_debug_trace_add(RzDebug *dbg, ut64 addr, int size) {
RzDebugTracepoint *tp;
int tag = dbg->trace->tag;
if (!rz_debug_trace_is_traceable(dbg, addr)) {
return NULL;
}
rz_analysis_trace_bb(dbg->analysis, addr);
tp = RZ_NEW0(RzDebugTracepoint);
if (!tp) {
return NULL;
}
tp->stamp = rz_time_now();
tp->addr = addr;
tp->tags = tag;
tp->size = size;
tp->count = ++dbg->trace->count;
tp->times = 1;
rz_list_append(dbg->trace->traces, tp);
ht_pp_update(dbg->trace->ht,
sdb_fmt("trace.%d.%" PFMT64x, tag, addr), tp);
return tp;
}
RZ_API void rz_debug_trace_reset(RzDebug *dbg) {
RzDebugTrace *t = dbg->trace;
rz_list_purge(t->traces);
ht_pp_free(t->ht);
t->ht = ht_pp_new0();
t->traces = rz_list_new();
t->traces->free = free;
}