rizin/librz/debug/trace.c
Florian Märkl 245e198cf1
RzReg: Associate roles with RzRegItem instead of name strings (#6291)
Register roles (RzRegisterId) are now associated directly with an
RzRegItem in RzReg rather than mapping to name strings, which previously
needed an additional hashtable lookup to get more information about the
register. Conversely, if the name is needed from an RzRegItem, it is
available directly as a member.

This is not a pure refactor as there were cases before where a register
name was assigned to a role in the register profile, but no register
actually existed under that name. Such cases will now cause a warning to
be printed during profile load and the role association will be ignored.
Changes in register profiles in this commit are for fixing such cases.
2026-04-30 12:03:51 +02:00

309 lines
8.1 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->enabled = false;
t->traces = rz_list_new();
if (!t->traces) {
rz_debug_trace_free(t);
return NULL;
}
t->traces->free = free;
t->ht = ht_sp_new(HT_STR_DUP, NULL, NULL);
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_sp_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_by_role(dbg, RZ_REG_NAME_PC);
if (!dbg->iob.read_at) {
RZ_LOG_ERROR("dbg->iob.read_at missing\n");
return false;
}
if (!dbg->iob.read_at(dbg->iob.io, pc, buf_pc, sizeof(buf_pc))) {
RZ_LOG_ERROR("dbg->iob.read_at failure -- pc 0x%" PFMT64x "\n", pc);
return false;
}
dbg->cur_op = rz_analysis_op_new();
if (!dbg->cur_op) {
RZ_LOG_ERROR("rz_analysis_op_new failure\n");
return false;
}
if (rz_analysis_op(dbg->analysis, dbg->cur_op, pc, buf_pc, sizeof(buf_pc), RZ_ANALYSIS_OP_MASK_VAL) < 1) {
RZ_LOG_ERROR("rz_analysis_op failure -- pc 0x%" PFMT64x "\n", pc);
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;
}
/**
* \brief Add register/memory changes to the debug session.
*
* \param dbg RzDebug instance containing the session.
* \return false The current instruction is not known so no changes can be recorded.
* \return true Otherwise.
*/
RZ_API bool rz_debug_trace_ins_after(RZ_NONNULL RzDebug *dbg) {
rz_return_val_if_fail(dbg, false);
if (!dbg->cur_op) { // Can happen if hard stepping is available and code is unknown to Rizin
return 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)) {
RZ_LOG_ERROR("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) {
RZ_LOG_ERROR("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) {
RzAnalysisEsil *esil = rz_analysis_get_esil(dbg->analysis);
if (esil) {
const char *eexpr = rz_strbuf_get(&op->esil);
rz_analysis_esil_trace_op(esil, op->addr, eexpr);
} else {
if (dbg->verbose) {
RZ_LOG_WARN("debug: Run aeim to get 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 RzDebugTracepoint *rz_debug_trace_get(RzDebug *dbg, ut64 addr) {
char tmpbuf[64];
int tag = dbg->trace->tag;
return ht_sp_find(dbg->trace->ht,
rz_strf(tmpbuf, "trace.%d.%" PFMT64x, tag, addr), NULL);
}
static int cmpaddr(const void *_a, const void *_b, void *user) {
const RzDbgListInfo *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;
}
/***
* Get all trace info
* \param dbg core->dbg
* \param offset offset of address
* \return a RzList of RzDbgListInfo
*/
RZ_API RZ_OWN RzList /*<RzDbgListInfo *>*/ *rz_debug_traces_info(RzDebug *dbg, ut64 offset) {
rz_return_val_if_fail(dbg, NULL);
int tag = dbg->trace->tag;
RzListIter *iter;
RzList *info_list = rz_list_newf((RzListFree)rz_debug_listinfo_free);
if (!info_list) {
return NULL;
}
RzDebugTracepoint *trace;
rz_list_foreach (dbg->trace->traces, iter, trace) {
if (trace->tag && !(tag & trace->tag)) {
continue;
}
RzDbgListInfo *info = RZ_NEW0(RzDbgListInfo);
if (!info) {
rz_list_free(info_list);
return NULL;
}
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);
}
rz_list_sort(info_list, cmpaddr, NULL);
return info_list;
}
RZ_API RzDebugTracepoint *rz_debug_trace_add(RzDebug *dbg, ut64 addr, int size) {
RzDebugTracepoint *tp;
char tmpbuf[64];
int tag = dbg->trace->tag;
rz_analysis_trace_bb(dbg->analysis, addr);
tp = RZ_NEW0(RzDebugTracepoint);
if (!tp) {
return NULL;
}
tp->stamp = rz_time_now_mono();
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_sp_update(dbg->trace->ht,
rz_strf(tmpbuf, "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_sp_free(t->ht);
t->ht = ht_sp_new(HT_STR_DUP, NULL, NULL);
t->traces = rz_list_new();
t->traces->free = free;
}
RZ_API RZ_OWN RzDbgListInfo *rz_debug_listinfo_new(RZ_NULLABLE const char *name, RzInterval pitv, RzInterval vitv, int perm, RZ_NULLABLE const char *extra) {
RzDbgListInfo *info = RZ_NEW(RzDbgListInfo);
if (!info) {
return NULL;
}
info->name = rz_str_dup(name);
info->pitv = pitv;
info->vitv = vitv;
info->perm = perm;
info->extra = rz_str_dup(extra);
return info;
}
RZ_API void rz_debug_listinfo_free(RZ_NULLABLE RzDbgListInfo *info) {
if (!info) {
return;
}
free(info->name);
free(info->extra);
free(info);
}