1 /* radare - LGPL - Copyright 2008-2020 - pancake */
2
3 #include <r_debug.h>
4
5 // DO IT WITH SDB
6
r_debug_trace_new(void)7 R_API RDebugTrace *r_debug_trace_new (void) {
8 RDebugTrace *t = R_NEW0 (RDebugTrace);
9 if (!t) {
10 return NULL;
11 }
12 t->tag = 1; // UT32_MAX;
13 t->addresses = NULL;
14 t->enabled = false;
15 t->traces = r_list_new ();
16 if (!t->traces) {
17 r_debug_trace_free (t);
18 return NULL;
19 }
20 t->traces->free = free;
21 t->ht = ht_pp_new0 ();
22 if (!t->ht) {
23 r_debug_trace_free (t);
24 return NULL;
25 }
26 return t;
27 }
28
r_debug_trace_free(RDebugTrace * trace)29 R_API void r_debug_trace_free (RDebugTrace *trace) {
30 if (!trace) {
31 return;
32 }
33 r_list_purge (trace->traces);
34 free (trace->traces);
35 ht_pp_free (trace->ht);
36 R_FREE (trace);
37 }
38
39 // TODO: added overlap/mask support here... wtf?
40 // TODO: think about tagged traces
r_debug_trace_tag(RDebug * dbg,int tag)41 R_API int r_debug_trace_tag (RDebug *dbg, int tag) {
42 //if (tag>0 && tag<31) core->dbg->trace->tag = 1<<(sz-1);
43 return (dbg->trace->tag = (tag>0)? tag: UT32_MAX);
44 }
45
r_debug_trace_ins_before(RDebug * dbg)46 R_API bool r_debug_trace_ins_before(RDebug *dbg) {
47 RListIter *it, *it_tmp;
48 RAnalValue *val;
49 ut8 buf_pc[32];
50
51 // Analyze current instruction
52 ut64 pc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]);
53 if (!dbg->iob.read_at) {
54 return false;
55 }
56 if (!dbg->iob.read_at (dbg->iob.io, pc, buf_pc, sizeof (buf_pc))) {
57 return false;
58 }
59 dbg->cur_op = R_NEW0 (RAnalOp);
60 if (!dbg->cur_op) {
61 return false;
62 }
63 if (!r_anal_op (dbg->anal, dbg->cur_op, pc, buf_pc, sizeof (buf_pc), R_ANAL_OP_MASK_VAL)) {
64 r_anal_op_free (dbg->cur_op);
65 dbg->cur_op = NULL;
66 return false;
67 }
68
69 // resolve mem write address
70 r_list_foreach_safe (dbg->cur_op->access, it, it_tmp, val) {
71 switch (val->type) {
72 case R_ANAL_VAL_REG:
73 if (!(val->access & R_ANAL_ACC_W)) {
74 r_list_delete (dbg->cur_op->access, it);
75 }
76 break;
77 case R_ANAL_VAL_MEM:
78 if (val->memref > 32) {
79 eprintf ("Error: adding changes to %d bytes in memory.\n", val->memref);
80 r_list_delete (dbg->cur_op->access, it);
81 break;
82 }
83
84 if (val->access & R_ANAL_ACC_W) {
85 // resolve memory address
86 ut64 addr = 0;
87 addr += val->delta;
88 if (val->seg) {
89 addr += r_reg_get_value (dbg->reg, val->seg);
90 }
91 if (val->reg) {
92 addr += r_reg_get_value (dbg->reg, val->reg);
93 }
94 if (val->regdelta) {
95 int mul = val->mul ? val->mul : 1;
96 addr += mul * r_reg_get_value (dbg->reg, val->regdelta);
97 }
98 // resolve address into base for ins_after
99 val->base = addr;
100 } else {
101 r_list_delete (dbg->cur_op->access, it);
102 }
103 default:
104 break;
105 }
106 }
107 return true;
108 }
109
r_debug_trace_ins_after(RDebug * dbg)110 R_API bool r_debug_trace_ins_after(RDebug *dbg) {
111 RListIter *it;
112 RAnalValue *val;
113
114 // Add reg/mem write change
115 r_debug_reg_sync (dbg, R_REG_TYPE_ALL, false);
116 r_list_foreach (dbg->cur_op->access, it, val) {
117 if (!(val->access & R_ANAL_ACC_W)) {
118 continue;
119 }
120
121 switch (val->type) {
122 case R_ANAL_VAL_REG:
123 {
124 if (!val->reg) {
125 R_LOG_ERROR("invalid register, unable to trace register state\n");
126 continue;
127 }
128 ut64 data = r_reg_get_value (dbg->reg, val->reg);
129
130 // add reg write
131 r_debug_session_add_reg_change (dbg->session, val->reg->arena, val->reg->offset, data);
132 break;
133 }
134 case R_ANAL_VAL_MEM:
135 {
136 ut8 buf[32] = { 0 };
137 if (!dbg->iob.read_at (dbg->iob.io, val->base, buf, val->memref)) {
138 eprintf ("Error reading memory at 0x%"PFMT64x"\n", val->base);
139 break;
140 }
141
142 // add mem write
143 size_t i;
144 for (i = 0; i < val->memref; i++) {
145 r_debug_session_add_mem_change (dbg->session, val->base + i, buf[i]);
146 }
147 break;
148 }
149 default:
150 break;
151 }
152 }
153 r_anal_op_free (dbg->cur_op);
154 dbg->cur_op = NULL;
155 return true;
156 }
157
158 /*
159 * something happened at the given pc that we need to trace
160 */
r_debug_trace_pc(RDebug * dbg,ut64 pc)161 R_API int r_debug_trace_pc(RDebug *dbg, ut64 pc) {
162 ut8 buf[32];
163 RAnalOp op = {0};
164 if (!dbg->iob.is_valid_offset (dbg->iob.io, pc, 0)) {
165 eprintf ("trace_pc: cannot read memory at 0x%"PFMT64x"\n", pc);
166 return false;
167 }
168 (void)dbg->iob.read_at (dbg->iob.io, pc, buf, sizeof (buf));
169 if (r_anal_op (dbg->anal, &op, pc, buf, sizeof (buf), R_ANAL_OP_MASK_ESIL) < 1) {
170 eprintf ("trace_pc: cannot get opcode size at 0x%"PFMT64x"\n", pc);
171 return false;
172 }
173 r_debug_trace_op (dbg, &op);
174 r_anal_op_fini (&op);
175 return true;
176 }
177
r_debug_trace_op(RDebug * dbg,RAnalOp * op)178 R_API void r_debug_trace_op(RDebug *dbg, RAnalOp *op) {
179 static ut64 oldpc = UT64_MAX; // Must trace the previously traced instruction
180 if (dbg->trace->enabled) {
181 if (dbg->anal->esil) {
182 r_anal_esil_trace_op (dbg->anal->esil, op);
183 } else {
184 if (dbg->verbose) {
185 eprintf ("Run aeim to get dbg->anal->esil initialized\n");
186 }
187 }
188 }
189 if (oldpc != UT64_MAX) {
190 r_debug_trace_add (dbg, oldpc, op->size); //XXX review what this line really do
191 }
192 oldpc = op->addr;
193 }
194
r_debug_trace_at(RDebug * dbg,const char * str)195 R_API void r_debug_trace_at(RDebug *dbg, const char *str) {
196 // TODO: parse offsets and so use ut64 instead of strstr()
197 free (dbg->trace->addresses);
198 dbg->trace->addresses = (str&&*str)? strdup (str): NULL;
199 }
200
r_debug_trace_get(RDebug * dbg,ut64 addr)201 R_API RDebugTracepoint *r_debug_trace_get(RDebug *dbg, ut64 addr) {
202 int tag = dbg->trace->tag;
203 return ht_pp_find (dbg->trace->ht,
204 sdb_fmt ("trace.%d.%"PFMT64x, tag, addr), NULL);
205 }
206
cmpaddr(const void * _a,const void * _b)207 static int cmpaddr(const void *_a, const void *_b) {
208 const RListInfo *a = _a, *b = _b;
209 return (r_itv_begin (a->pitv) > r_itv_begin (b->pitv))? 1:
210 (r_itv_begin (a->pitv) < r_itv_begin (b->pitv))? -1: 0;
211 }
212
r_debug_trace_list(RDebug * dbg,int mode,ut64 offset)213 R_API void r_debug_trace_list(RDebug *dbg, int mode, ut64 offset) {
214 int tag = dbg->trace->tag;
215 RListIter *iter;
216 bool flag = false;
217 RList *info_list = r_list_new ();
218 if (!info_list && mode == '=') {
219 return;
220 }
221 RDebugTracepoint *trace;
222 r_list_foreach (dbg->trace->traces, iter, trace) {
223 if (!trace->tag || (tag & trace->tag)) {
224 switch (mode) {
225 case 'q':
226 dbg->cb_printf ("0x%"PFMT64x"\n", trace->addr);
227 break;
228 case '=': {
229 RListInfo *info = R_NEW0 (RListInfo);
230 if (!info) {
231 return;
232 }
233 info->pitv = (RInterval) {trace->addr, trace->size};
234 info->vitv = info->pitv;
235 info->perm = -1;
236 info->name = r_str_newf ("%d", trace->times);
237 info->extra = r_str_newf ("%d", trace->count);
238 r_list_append (info_list, info);
239 flag = true;
240 } break;
241 case 1:
242 case '*':
243 dbg->cb_printf ("dt+ 0x%"PFMT64x" %d\n", trace->addr, trace->times);
244 break;
245 default:
246 dbg->cb_printf ("0x%08"PFMT64x" size=%d count=%d times=%d tag=%d\n",
247 trace->addr, trace->size, trace->count, trace->times, trace->tag);
248 break;
249 }
250 }
251 }
252 if (flag) {
253 r_list_sort (info_list, cmpaddr);
254 RTable *table = r_table_new ("traces");
255 table->cons = r_cons_singleton();
256 RIO *io = dbg->iob.io;
257 r_table_visual_list (table, info_list, offset, 1,
258 r_cons_get_size (NULL), io->va);
259 io->cb_printf ("\n%s\n", r_table_tostring (table));
260 r_table_free (table);
261 r_list_free (info_list);
262 }
263 }
264
265 // XXX: find better name, make it public?
r_debug_trace_is_traceable(RDebug * dbg,ut64 addr)266 static int r_debug_trace_is_traceable(RDebug *dbg, ut64 addr) {
267 if (dbg->trace->addresses) {
268 char addr_str[32];
269 snprintf (addr_str, sizeof (addr_str), "0x%08"PFMT64x, addr);
270 if (!strstr (dbg->trace->addresses, addr_str)) {
271 return false;
272 }
273 }
274 return true;
275 }
276
r_debug_trace_add(RDebug * dbg,ut64 addr,int size)277 R_API RDebugTracepoint *r_debug_trace_add (RDebug *dbg, ut64 addr, int size) {
278 RDebugTracepoint *tp;
279 int tag = dbg->trace->tag;
280 if (!r_debug_trace_is_traceable (dbg, addr)) {
281 return NULL;
282 }
283 r_anal_trace_bb (dbg->anal, addr);
284 tp = R_NEW0 (RDebugTracepoint);
285 if (!tp) {
286 return NULL;
287 }
288 tp->stamp = r_time_now ();
289 tp->addr = addr;
290 tp->tags = tag;
291 tp->size = size;
292 tp->count = ++dbg->trace->count;
293 tp->times = 1;
294 r_list_append (dbg->trace->traces, tp);
295 ht_pp_update (dbg->trace->ht,
296 sdb_fmt ("trace.%d.%"PFMT64x, tag, addr), tp);
297 return tp;
298 }
299
r_debug_trace_reset(RDebug * dbg)300 R_API void r_debug_trace_reset (RDebug *dbg) {
301 RDebugTrace *t = dbg->trace;
302 r_list_purge (t->traces);
303 ht_pp_free (t->ht);
304 t->ht = ht_pp_new0 ();
305 t->traces = r_list_new ();
306 t->traces->free = free;
307 }
308