diff --git a/libr/anal/anal.c b/libr/anal/anal.c index 32da07cec611c..f25269648148d 100644 --- a/libr/anal/anal.c +++ b/libr/anal/anal.c @@ -69,6 +69,84 @@ static void r_meta_item_free(void *_item) { } } +#if USE_NEW_ESIL +static bool anal_esil_mem_switch (void *mem, ut32 idx) { + RAnal *anal = mem; + if (!anal || !anal->iob.init) { + R_LOG_WARN ("anal->iob is not setup"); + return false; + } + return anal->iob.bank_use (anal->iob.io, idx); +} + +static bool anal_esil_mem_read (void *mem, ut64 addr, ut8 *buf, int len) { + RAnal *anal = mem; + if (!anal || !anal->iob.init) { + R_LOG_WARN ("anal->iob is not setup"); + return false; + } + return anal->iob.read_at (anal->iob.io, addr, buf, len); +} + +static bool anal_esil_mem_write (void *mem, ut64 addr, const ut8 *buf, int len) { + RAnal *anal = mem; + if (!anal || !anal->iob.init) { + R_LOG_WARN ("anal->iob is not setup"); + return false; + } + return anal->iob.write_at (anal->iob.io, addr, buf, len); +} + +REsilMemInterface anal_esil_mem_if = { + .mem_switch = anal_esil_mem_switch, + .mem_read = anal_esil_mem_read, + .mem_write = anal_esil_mem_write +}; + +static bool anal_esil_is_reg (void *user, const char *name) { + RRegItem *ri = r_reg_get (((RAnal *)user)->reg, name, -1); + if (!ri) { + return false; + } + r_unref (ri); + return true; +} + +static bool anal_esil_reg_read(void *user, const char *name, ut64 *val) { + RRegItem *ri = r_reg_get (((RAnal *)user)->reg, name, -1); + if (!ri) { + return false; + } + ut64 v = r_reg_get_value (((RAnal *)user)->reg, ri); + if (val) { + *val = v; + } + r_unref (ri); + return true; +} + +static bool anal_esil_reg_write (void *user, const char *name, ut64 val) { + return r_reg_setv (((RAnal *)user)->reg, name, val); +} + +static ut32 anal_esil_reg_size (void *user, const char *name) { + RRegItem *ri = r_reg_get (((RAnal *)user)->reg, name, -1); + if (!ri) { + return 0; + } + const ut32 size = ri->size; + r_unref (ri); + return size; +} + +static REsilRegInterface anal_esil_reg_if = { + .is_reg = anal_esil_is_reg, + .reg_read = anal_esil_reg_read, + .reg_write = anal_esil_reg_write, + .reg_size = anal_esil_reg_size +}; +#endif + // Take nullable RArchConfig as argument? R_API RAnal *r_anal_new(void) { int i; @@ -113,7 +191,13 @@ R_API RAnal *r_anal_new(void) { anal->sdb_classes_attrs = sdb_ns (anal->sdb_classes, "attrs", 1); anal->zign_path = strdup (""); anal->cb_printf = (PrintfCallback) printf; +#if USE_NEW_ESIL + anal_esil_reg_if.user = anal; + anal_esil_mem_if.user = anal; + anal->esil = r_esil_new_ex (4096, 0, 1, &anal_esil_reg_if, &anal_esil_mem_if); +#else anal->esil = r_esil_new (4096, 0, 1); +#endif anal->esil->anal = anal; (void)r_anal_pin_init (anal); (void)r_anal_xrefs_init (anal); diff --git a/libr/anal/esil_dfg.c b/libr/anal/esil_dfg.c index 71a080e72867c..e47b888e4998f 100644 --- a/libr/anal/esil_dfg.c +++ b/libr/anal/esil_dfg.c @@ -1554,7 +1554,11 @@ R_API RAnalEsilDFG *r_anal_esil_dfg_new(RAnal* anal, bool use_map_info, bool use free (dfg); return NULL; } +#if USE_NEW_ESIL + dfg->esil = r_esil_new_simple (1, anal->reg, &anal->iob); +#else dfg->esil = r_esil_new (4096, 0, 1); +#endif if (!dfg->esil) { r_reg_free (dfg->reg); free (dfg); @@ -1644,13 +1648,17 @@ R_API void r_anal_esil_dfg_free(RAnalEsilDFG *dfg) { R_API RAnalEsilDFG *r_anal_esil_dfg_expr(RAnal *anal, RAnalEsilDFG * R_NULLABLE dfg, const char *expr, bool use_map_info, bool use_maps) { R_RETURN_VAL_IF_FAIL (anal && expr, NULL); +#if USE_NEW_ESIL + REsil *esil = r_esil_new_simple (1, anal->reg, &anal->iob); +#else REsil *esil = r_esil_new (4096, 0, 1); +#endif if (!esil) { return NULL; } esil->anal = anal; - RAnalEsilDFG *edf = dfg ? dfg : r_anal_esil_dfg_new (anal, use_map_info, use_maps); + RAnalEsilDFG *edf = dfg? dfg: r_anal_esil_dfg_new (anal, use_map_info, use_maps); if (!edf) { r_esil_free (esil); return NULL; diff --git a/libr/core/anal_tp.c b/libr/core/anal_tp.c index 6824987e8154f..44fdaf4c37b96 100644 --- a/libr/core/anal_tp.c +++ b/libr/core/anal_tp.c @@ -4,14 +4,504 @@ #include #define LOOP_MAX 10 +typedef struct type_trace_change_reg_t { + int idx; + ut32 cc; + char *name; + ut64 data; + ut64 odata; +} TypeTraceRegChange; + +typedef struct type_trace_change_mem_t { + int idx; + ut32 cc; + ut64 addr; + ut8 data; + ut8 odata; +} TypeTraceMemChange; + +typedef struct { + const char *name; + ut64 value; + // TODO: size +} TypeTraceRegAccess; + +typedef struct { + char *data; + ut64 addr; + // TODO: size +} TypeTraceMemoryAccess; + +typedef struct { + union { + TypeTraceRegAccess reg; + TypeTraceMemoryAccess mem; + }; + bool is_write; + bool is_reg; +} TypeTraceAccess; + +typedef struct { + ut64 addr; + ut32 start; + ut32 end; // 1 past the end of the op for this index +} TypeTraceOp; + +static inline void tt_fini_access(TypeTraceAccess *access) { + if (access->is_reg) { + return; + } + free (access->mem.data); +} + +R_VEC_TYPE(VecTraceOp, TypeTraceOp); +R_VEC_TYPE_WITH_FINI(VecAccess, TypeTraceAccess, tt_fini_access); + +typedef struct { + VecTraceOp ops; + VecAccess accesses; + HtUU *loop_counts; +} TypeTraceDB; + +typedef struct type_trace_t { + TypeTraceDB db; + int idx; + ut32 cc; + int end_idx; + int cur_idx; + RReg *reg; + HtUP *registers; + HtUP *memory; + ut32 voy[4]; +} TypeTrace; + +#define CMP_REG_CHANGE(x, y) ((x) - ((TypeTraceRegChange *)y)->idx) +#define CMP_MEM_CHANGE(x, y) ((x) - ((TypeTraceMemChange *)y)->idx) + +static void update_trace_db_op(TypeTraceDB *db) { + const ut32 trace_op_len = VecTraceOp_length (&db->ops); + if (!trace_op_len) { + return; + } + TypeTraceOp *last = VecTraceOp_at (&db->ops, trace_op_len - 1); + if (!last) { + return; + } + const ut32 vec_idx = VecAccess_length (&db->accesses); + if (!vec_idx) { + R_LOG_ERROR ("Invalid access database"); + return; + } + last->end = vec_idx; // - 1; +} + +static void type_trace_voyeur_reg_read (void *user, const char *name, ut64 val) { + R_RETURN_IF_FAIL (user && name); + char *name_dup = strdup (name); + if (!name_dup) { + R_LOG_ERROR ("Failed to allocate(strdup) memory for storing access"); + return; + } + TypeTraceDB *db = user; + TypeTraceAccess *access = VecAccess_emplace_back (&db->accesses); + if (!access) { + free (name_dup); + R_LOG_ERROR ("Failed to allocate memory for storing access"); + return; + } + access->reg.name = name_dup; + access->reg.value = val; + access->is_reg = true; + access->is_write = false; + update_trace_db_op (db); +} + +static void add_reg_change(TypeTrace *trace, RRegItem *ri, ut64 data, ut64 odata) { + R_RETURN_IF_FAIL (trace && ri); + ut64 addr = ri->offset | (ri->arena << 16); + RVector *vreg = ht_up_find (trace->registers, addr, NULL); + if (R_UNLIKELY (!vreg)) { + vreg = r_vector_new (sizeof (TypeTraceRegChange), NULL, NULL); + if (R_UNLIKELY (!vreg)) { + R_LOG_ERROR ("creating a register vector"); + return; + } + ht_up_insert (trace->registers, addr, vreg); + } + TypeTraceRegChange reg = {trace->cur_idx, trace->cc++, + strdup (ri->name), data, odata}; + r_vector_push (vreg, ®); +} + +static void type_trace_voyeur_reg_write (void *user, const char *name, ut64 old, ut64 val) { + R_RETURN_IF_FAIL (user && name); + TypeTrace *trace = user; + RRegItem *ri = r_reg_get (trace->reg, name, -1); + if (!ri) { + return; + } + char *name_dup = strdup (name); + if (!name_dup) { + R_LOG_ERROR ("Failed to allocate(strdup) memory for storing access"); + goto fail_name_dup; + } + TypeTraceAccess *access = VecAccess_emplace_back (&trace->db.accesses); + if (!access) { + R_LOG_ERROR ("Failed to allocate memory for storing access"); + goto fail_emplace_back; + } + access->is_reg = true; + access->reg.name = name_dup; + access->reg.value = val; + access->is_write = true; + + add_reg_change (trace, ri, val, old); + update_trace_db_op (&trace->db); + r_unref (ri); + return; +fail_emplace_back: + free (name_dup); +fail_name_dup: + r_unref (ri); +} + +static void type_trace_voyeur_mem_read (void *user, ut64 addr, const ut8 *buf, int len) { + R_RETURN_IF_FAIL (user && buf && (len > 0)); + char *hexbuf = r_hex_bin2strdup (buf, len); //why? + if (!hexbuf) { + R_LOG_ERROR ("Failed to allocate(r_hex_bin2strdup) memory for storing access"); + return; + } + TypeTraceDB *db = user; + TypeTraceAccess *access = VecAccess_emplace_back (&db->accesses); + if (!access) { + free (hexbuf); + R_LOG_ERROR ("Failed to allocate memory for storing access"); + return; + } + access->is_reg = false; + access->mem.data = hexbuf; + access->mem.addr = addr; + access->is_write = false; + update_trace_db_op (db); +} + +static void type_trace_voyeur_mem_write (void *user, ut64 addr, const ut8 *old, const ut8 *buf, int len) { + R_RETURN_IF_FAIL (user && buf && (len > 0)); + char *hexbuf = r_hex_bin2strdup (buf, len); //why? + if (!hexbuf) { + R_LOG_ERROR ("Failed to allocate(r_hex_bin2strdup) memory for storing access"); + return; + } + TypeTrace *trace = user; + TypeTraceAccess *access = VecAccess_emplace_back (&trace->db.accesses); + if (!access) { + free (hexbuf); + R_LOG_ERROR ("Failed to allocate memory for storing access"); + return; + } + access->is_reg = false; + access->mem.data = hexbuf; + access->mem.addr = addr; + access->is_write = true; + ut32 i; + for (i = 0; i < len; i++) { + //adding each byte one by one is utterly stupid, typical gsoc crap + //ideally this would use a tree structure, that splits nodes when necessary + RVector *vmem = ht_up_find (trace->memory, addr, NULL); + if (!vmem) { + vmem = r_vector_new (sizeof (TypeTraceMemChange), NULL, NULL); + if (!vmem) { + R_LOG_ERROR ("creating a memory vector"); + break; + } + ht_up_insert (trace->memory, addr, vmem); + } + TypeTraceMemChange mem = {trace->idx, trace->cc++, addr, buf[i], old[i]}; + r_vector_push (vmem, &mem); + } + update_trace_db_op (&trace->db); +} + +static void htup_vector_free(HtUPKv *kv) { + if (kv) { + r_vector_free (kv->value); + } +} + +static void trace_db_init(TypeTraceDB *db) { + VecTraceOp_init (&db->ops); + VecAccess_init (&db->accesses); + db->loop_counts = ht_uu_new0 (); +} + +static bool type_trace_init(TypeTrace *trace, REsil *esil, RReg *reg) { + R_RETURN_VAL_IF_FAIL (trace && esil && reg, false); + *trace = (const TypeTrace){0}; + trace_db_init (&trace->db); + trace->registers = ht_up_new (NULL, htup_vector_free, NULL); + if (!trace->registers) { + goto fail_registers_ht; + } + trace->memory = ht_up_new (NULL, htup_vector_free, NULL); + if (!trace->memory) { + goto fail_memory_ht; + } + trace->voy[R_ESIL_VOYEUR_REG_READ] = r_esil_add_voyeur (esil, &trace->db, + type_trace_voyeur_reg_read, R_ESIL_VOYEUR_REG_READ); + if (R_UNLIKELY (trace->voy[R_ESIL_VOYEUR_REG_READ] == R_ESIL_VOYEUR_ERR)) { + goto fail_regr_voy; + } + trace->voy[R_ESIL_VOYEUR_REG_WRITE] = r_esil_add_voyeur (esil, trace, + type_trace_voyeur_reg_write, R_ESIL_VOYEUR_REG_WRITE); + if (R_UNLIKELY (trace->voy[R_ESIL_VOYEUR_REG_WRITE] == R_ESIL_VOYEUR_ERR)) { + goto fail_regw_voy; + } + trace->voy[R_ESIL_VOYEUR_MEM_READ] = r_esil_add_voyeur (esil, &trace->db, + type_trace_voyeur_mem_read, R_ESIL_VOYEUR_MEM_READ); + if (R_UNLIKELY (trace->voy[R_ESIL_VOYEUR_MEM_READ] == R_ESIL_VOYEUR_ERR)) { + goto fail_memr_voy; + } + trace->voy[R_ESIL_VOYEUR_MEM_WRITE] = r_esil_add_voyeur (esil, trace, + type_trace_voyeur_mem_write, R_ESIL_VOYEUR_MEM_WRITE); + if (R_UNLIKELY (trace->voy[R_ESIL_VOYEUR_MEM_WRITE] == R_ESIL_VOYEUR_ERR)) { + goto fail_memw_voy; + } + trace->reg = reg; + return true; +fail_memw_voy: + r_esil_del_voyeur (esil, trace->voy[R_ESIL_VOYEUR_MEM_READ]); +fail_memr_voy: + r_esil_del_voyeur (esil, trace->voy[R_ESIL_VOYEUR_REG_WRITE]); +fail_regw_voy: + r_esil_del_voyeur (esil, trace->voy[R_ESIL_VOYEUR_REG_READ]); +fail_regr_voy: + ht_up_free (trace->memory); + trace->memory = NULL; +fail_memory_ht: + ht_up_free (trace->registers); + trace->registers = NULL; +fail_registers_ht: + return false; +} + +static ut64 type_trace_loopcount(TypeTrace *trace, ut64 addr) { + bool found = false; + const ut64 count = ht_uu_find (trace->db.loop_counts, addr, &found); + return found? count: 0; +} + +static void type_trace_loopcount_increment(TypeTrace *trace, ut64 addr) { + const ut64 count = type_trace_loopcount (trace, addr); + ht_uu_update (trace->db.loop_counts, addr, count + 1); +} + +//XXX: trace should be the only parameter +static void type_trace_fini(TypeTrace *trace, REsil *esil) { + R_RETURN_IF_FAIL (trace && esil); + VecTraceOp_fini (&trace->db.ops); + VecAccess_fini (&trace->db.accesses); + ht_uu_free (trace->db.loop_counts); + ht_up_free (trace->registers); + ht_up_free (trace->memory); + r_esil_del_voyeur (esil, trace->voy[R_ESIL_VOYEUR_MEM_WRITE]); + r_esil_del_voyeur (esil, trace->voy[R_ESIL_VOYEUR_MEM_READ]); + r_esil_del_voyeur (esil, trace->voy[R_ESIL_VOYEUR_REG_WRITE]); + r_esil_del_voyeur (esil, trace->voy[R_ESIL_VOYEUR_REG_READ]); + r_reg_free (trace->reg); + trace[0] = (const TypeTrace){0}; +} + +static bool type_trace_op(TypeTrace *trace, REsil *esil, RAnalOp *op) { + R_RETURN_VAL_IF_FAIL (trace && esil && op, false); + const char *expr = r_strbuf_get (&op->esil); + if (R_UNLIKELY (!expr || !strlen (expr))) { + R_LOG_WARN ("expr is empty or null"); + return false; + } + trace->cc = 0; + RRegItem *ri = r_reg_get (trace->reg, "PC", -1); + if (ri) { + const bool suc = r_esil_reg_write_silent (esil, ri->name, op->addr + op->size); + r_unref (ri); + if (!suc) { + return false; + } + } + + TypeTraceOp *to = VecTraceOp_emplace_back (&trace->db.ops); + if (R_LIKELY (to)) { + ut32 vec_idx = VecAccess_length (&trace->db.accesses); + to->start = vec_idx; + to->end = vec_idx; + to->addr = op->addr; + } else { + R_LOG_WARN ("Couldn't allocate(emplace_back) trace op"); + //anything to do here? + } + const bool ret = r_esil_parse (esil, expr); + r_esil_stack_free (esil); + trace->idx++; + trace->end_idx++; // should be vector length? + return ret; +} + +#if 0 +static bool count_changes_above_idx_cb (void *user, const ut64 key, const void *val) { + RVector *vec = val; + if (R_UNLIKELY (r_vector_empty (vec))) { + return true; + } + ut64 *v = user; + const int idx = v[0] >> 32; + ut32 count = v[0] & UT32_MAX; + v[0] &= UT64_MAX ^ UT64_MAX; + ut32 i = r_vector_length (vec) - 1; + TypeTraceMemChange *change = r_vector_index_ptr (vec, i); + //idx is guaranteed to be at struct offset 0 for MemChange and RegChange, so this hack is fine + while (change->idx >= idx) { + count++; + if (!i) { + break; + } + i--; + change = r_vector_index_ptr (vec, i); + } + v[0] |= count; + return true; +} + +typedef struct { + int idx; + union { + TypeTraceRegChange *rc_ptr; + TypeTraceMemChange *mc_ptr; + void *data; + }; +} TTChangeCollector; + +static bool collect_reg_changes_cb (void *user, const ut64 key, const void *val) { + RVector *vec = val; + if (R_UNLIKELY (r_vector_empty (vec))) { + return true; + } + TTChangeCollector *cc = user; + ut32 i = r_vector_length (vec) - 1; + TypeTraceRegChange *rc = r_vector_index_ptr (vec, i); + while (rc->idx >= cc->idx) { + r_vector_remove_at (vec, i, cc->rc_ptr); + cc->rc_ptr++; + if (!i) { + return true; + } + i--; + } + return true; +} + +static bool collect_mem_changes_cb (void *user, const ut64 key, const void *val) { + RVector *vec = val; + if (R_UNLIKELY (r_vector_empty (vec))) { + return true; + } + TTChangeCollector *cc = user; + ut32 i = r_vector_length (vec) - 1; + TypeTraceMemChange *rc = r_vector_index_ptr (vec, i); + while (rc->idx >= cc->idx) { + r_vector_remove_at (vec, i, cc->mc_ptr); + cc->mc_ptr++; + if (!i) { + return true; + } + i--; + } + return true; +} + +static int sort_reg_changes_cb (const void *v0, const void *v1) { + const TypeTraceRegChange *a = v0; + const TypeTraceRegChange *b = v1; + if (a->idx == b->idx) { + return (int)b->cc - (int)a->cc; + } + return b->idx - a->idx; +} + +static int sort_mem_changes_cb (const void *v0, const void *v1) { + const TypeTraceMemChange *a = v0; + const TypeTraceMemChange *b = v1; + if (a->idx == b->idx) { + return (int)b->cc - (int)a->cc; + } + return b->idx - a->idx; +} + +static void type_trace_restore(TypeTrace *trace, REsil *esil, int idx) { + R_RETURN_IF_FAIL (trace && esil && (idx < trace->idx)); + ut64 v = ((ut64)idx) << 32; + ht_up_foreach (trace->registers, count_changes_above_idx_cb, &v); + ut32 c_num = v & UT32_MAX; + void *data = NULL; + if (c_num) { + data = R_NEWS (TypeTraceRegChange, c_num); + TTChangeCollector collector = {.idx = idx, .data = data}; + ht_up_foreach (trace->registers, collect_reg_changes_cb, &collector); + //sort collected reg changes so that the newest come first + qsort (data, c_num, sizeof (TypeTraceRegChange), sort_reg_changes_cb); + collector.data = data; + ut32 i = 0; + for (; i < c_num; i++) { + r_esil_reg_write_silent (esil, collector.rc_ptr[i].name, collector.rc_ptr[i].odata); + R_FREE (collector.rc_ptr[i].name); + } + } + v &= UT64_MAX ^ UT32_MAX; + ht_up_foreach (trace->memory, count_changes_above_idx_cb, &v); + if (data && (((v & UT32_MAX) * sizeof (TypeTraceMemChange)) > + (c_num * sizeof (TypeTraceRegChange)))) { + c_num = v & UT32_MAX; + void *new_data = realloc (data, sizeof (TypeTraceMemChange) * c_num); + if (!new_data) { + free (data); + return; + } + data = new_data; + } else { + c_num = v & UT32_MAX; + } + if (!c_num) { + free (data); + return; + } + if (R_UNLIKELY (!data)) { + data = R_NEWS (TypeTraceMemChange, c_num); + if (!data) { + return; + } + } + TTChangeCollector collector = {.idx = idx, .data = data}; + ht_up_foreach (trace->memory, collect_mem_changes_cb, &collector); + //sort collected mem changes so that the newest come first + qsort (data, c_num, sizeof (TypeTraceMemChange), sort_mem_changes_cb); + collector.data = data; + ut32 i = 0; + for (;i < c_num; i++) { + r_esil_mem_write_silent (esil, collector.mc_ptr[i].addr, &collector.rc_ptr[i].odata, 1); + } +} +#endif + R_VEC_TYPE (RVecUT64, ut64); R_VEC_TYPE (RVecBuf, ut8); typedef struct { + REsil esil; + TypeTrace tt; + ut64 stack_base; RCore *core; - RAnal *anal; - REsilTrace *et; - REsilTrace *_et; + RReg *anal_reg; + int stack_fd; + ut32 stack_map; RConfigHold *hc; char *cfg_spec; bool cfg_breakoninvalid; @@ -20,23 +510,23 @@ typedef struct { /// BEGIN /////////////////// esil trace helpers /////////////////////// -static int etrace_index(REsilTrace *etrace) { - int len = RVecTraceOp_length (&etrace->db.ops); +static int etrace_index(TypeTrace *etrace) { + int len = VecTraceOp_length (&etrace->db.ops); etrace->cur_idx = len; // > 0? len -1: 0; - return etrace->cur_idx; // RVecTraceOp_length (&etrace->db.ops); + return etrace->cur_idx; // VecTraceOp_length (&etrace->db.ops); } -static ut64 etrace_addrof(REsilTrace *etrace, ut32 idx) { - REsilTraceOp *op = RVecTraceOp_at (&etrace->db.ops, idx); +static ut64 etrace_addrof(TypeTrace *etrace, ut32 idx) { + TypeTraceOp *op = VecTraceOp_at (&etrace->db.ops, idx); return op? op->addr: 0; } -static ut64 etrace_memwrite_addr(REsilTrace *etrace, ut32 idx) { - REsilTraceOp *op = RVecTraceOp_at (&etrace->db.ops, idx); +static ut64 etrace_memwrite_addr(TypeTrace *etrace, ut32 idx) { + TypeTraceOp *op = VecTraceOp_at (&etrace->db.ops, idx); R_LOG_DEBUG ("memwrite %d %d", etrace->idx, idx); if (op && op->start != op->end) { - REsilTraceAccess *start = RVecAccess_at (&etrace->db.accesses, op->start); - REsilTraceAccess *end = RVecAccess_at (&etrace->db.accesses, op->end - 1); + TypeTraceAccess *start = VecAccess_at (&etrace->db.accesses, op->start); + TypeTraceAccess *end = VecAccess_at (&etrace->db.accesses, op->end - 1); while (start <= end) { if (!start->is_reg && start->is_write) { return start->mem.addr; @@ -47,12 +537,12 @@ static ut64 etrace_memwrite_addr(REsilTrace *etrace, ut32 idx) { return 0; } -static bool etrace_have_memread(REsilTrace *etrace, ut32 idx) { - REsilTraceOp *op = RVecTraceOp_at (&etrace->db.ops, idx); +static bool etrace_have_memread(TypeTrace *etrace, ut32 idx) { + TypeTraceOp *op = VecTraceOp_at (&etrace->db.ops, idx); R_LOG_DEBUG ("memread %d %d", etrace->idx, idx); if (op && op->start != op->end) { - REsilTraceAccess *start = RVecAccess_at (&etrace->db.accesses, op->start); - REsilTraceAccess *end = RVecAccess_at (&etrace->db.accesses, op->end - 1); + TypeTraceAccess *start = VecAccess_at (&etrace->db.accesses, op->start); + TypeTraceAccess *end = VecAccess_at (&etrace->db.accesses, op->end - 1); while (start <= end) { if (!start->is_reg && !start->is_write) { return true; @@ -63,12 +553,12 @@ static bool etrace_have_memread(REsilTrace *etrace, ut32 idx) { return false; } -static ut64 etrace_regread_value(REsilTrace *etrace, ut32 idx, const char *rname) { +static ut64 etrace_regread_value(TypeTrace *etrace, ut32 idx, const char *rname) { R_LOG_DEBUG ("regread %d %d", etrace->idx, idx); - REsilTraceOp *op = RVecTraceOp_at (&etrace->db.ops, idx); + TypeTraceOp *op = VecTraceOp_at (&etrace->db.ops, idx); if (op && op->start != op->end) { - REsilTraceAccess *start = RVecAccess_at (&etrace->db.accesses, op->start); - REsilTraceAccess *end = RVecAccess_at (&etrace->db.accesses, op->end - 1); + TypeTraceAccess *start = VecAccess_at (&etrace->db.accesses, op->start); + TypeTraceAccess *end = VecAccess_at (&etrace->db.accesses, op->end - 1); while (start <= end) { if (start->is_reg && !start->is_write) { if (!strcmp (rname, start->reg.name)) { @@ -81,12 +571,12 @@ static ut64 etrace_regread_value(REsilTrace *etrace, ut32 idx, const char *rname return 0; } -static const char *etrace_regwrite(REsilTrace *etrace, ut32 idx) { +static const char *etrace_regwrite(TypeTrace *etrace, ut32 idx) { R_LOG_DEBUG ("regwrite %d %d", etrace->idx, idx); - REsilTraceOp *op = RVecTraceOp_at (&etrace->db.ops, idx); + TypeTraceOp *op = VecTraceOp_at (&etrace->db.ops, idx); if (op && op->start != op->end) { - REsilTraceAccess *start = RVecAccess_at (&etrace->db.accesses, op->start); - REsilTraceAccess *end = RVecAccess_at (&etrace->db.accesses, op->end - 1); + TypeTraceAccess *start = VecAccess_at (&etrace->db.accesses, op->start); + TypeTraceAccess *end = VecAccess_at (&etrace->db.accesses, op->end - 1); while (start <= end) { if (start->is_reg && start->is_write) { return start->reg.name; @@ -99,13 +589,13 @@ static const char *etrace_regwrite(REsilTrace *etrace, ut32 idx) { /// END ///////////////////// esil trace helpers /////////////////////// -static bool etrace_regwrite_contains(REsilTrace *etrace, ut32 idx, const char *rname) { +static bool etrace_regwrite_contains(TypeTrace *etrace, ut32 idx, const char *rname) { R_LOG_DEBUG ("regwrite contains %d %s", idx, rname); R_RETURN_VAL_IF_FAIL (etrace && rname, false); - REsilTraceOp *op = RVecTraceOp_at (&etrace->db.ops, idx); // AAA + 1); + TypeTraceOp *op = VecTraceOp_at (&etrace->db.ops, idx); // AAA + 1); if (op && op->start != op->end) { - REsilTraceAccess *start = RVecAccess_at (&etrace->db.accesses, op->start); - REsilTraceAccess *end = RVecAccess_at (&etrace->db.accesses, op->end - 1); + TypeTraceAccess *start = VecAccess_at (&etrace->db.accesses, op->start); + TypeTraceAccess *end = VecAccess_at (&etrace->db.accesses, op->end - 1); while (start <= end) { if (start->is_reg && start->is_write) { if (!strcmp (rname, start->reg.name)) { @@ -121,11 +611,11 @@ static bool etrace_regwrite_contains(REsilTrace *etrace, ut32 idx, const char *r static bool type_pos_hit(TPState *tps, bool in_stack, int idx, int size, const char *place) { R_LOG_DEBUG ("Type pos hit %d %d %d %s", in_stack, idx, size, place); if (in_stack) { - ut64 sp = r_reg_getv (tps->core->anal->reg, "SP"); // XXX this is slow too and we can cache - const ut64 write_addr = etrace_memwrite_addr (tps->et, idx); // AAA -1 + ut64 sp = r_reg_getv (tps->tt.reg, "SP"); // XXX this is slow too and we can cache + const ut64 write_addr = etrace_memwrite_addr (&tps->tt, idx); // AAA -1 return (write_addr == sp + size); } - return place && etrace_regwrite_contains (tps->et, idx, place); + return place && etrace_regwrite_contains (&tps->tt, idx, place); } static void var_rename(RAnal *anal, RAnalVar *v, const char *name, ut64 addr) { @@ -257,7 +747,7 @@ static void get_src_regname(RCore *core, ut64 addr, char *regname, int size) { r_anal_op_free (op); } -static ut64 get_addr(REsilTrace *et, const char *regname, int idx) { +static ut64 get_addr(TypeTrace *et, const char *regname, int idx) { if (R_STR_ISEMPTY (regname)) { return 0; } @@ -378,9 +868,9 @@ static void type_match(TPState *tps, char *fcn_name, ut64 addr, ut64 baddr, cons int prev_idx, bool userfnc, ut64 caddr) { RAnal *anal = tps->core->anal; RCons *cons = tps->core->cons; - REsilTrace *et= tps->et; + TypeTrace *tt = &tps->tt; Sdb *TDB = anal->sdb_types; - const int idx = etrace_index (et) -1; + const int idx = etrace_index (tt) -1; const bool verbose = r_config_get_b (tps->core->config, "anal.types.verbose"); // XXX bool stack_rev = false, in_stack = false, format = false; R_LOG_DEBUG ("type_match %s %"PFMT64x" %"PFMT64x" %s %d", fcn_name, addr, baddr, cc, prev_idx); @@ -456,7 +946,7 @@ static void type_match(TPState *tps, char *fcn_name, ut64 addr, ut64 baddr, cons for (j = idx; j >= prev_idx; j--) { // r_strf_var (k, 32, "%d.addr", j); // ut64 instr_addr = sdb_num_get (trace, k, 0); - ut64 instr_addr = etrace_addrof (et, j); + ut64 instr_addr = etrace_addrof (tt, j); R_LOG_DEBUG ("0x%08"PFMT64x" back traceing %d", instr_addr, j); if (instr_addr < baddr) { break; @@ -473,7 +963,7 @@ static void type_match(TPState *tps, char *fcn_name, ut64 addr, ut64 baddr, cons break; } RAnalVar *var = r_anal_get_used_function_var (anal, op->addr); - if (op->type == R_ANAL_OP_TYPE_MOV && etrace_have_memread (et, j)) { + if (op->type == R_ANAL_OP_TYPE_MOV && etrace_have_memread (tt, j)) { memref = ! (!memref && var && (var->kind != R_ANAL_VAR_KIND_REG)); } // Match type from function param to instr @@ -513,11 +1003,11 @@ static void type_match(TPState *tps, char *fcn_name, ut64 addr, ut64 baddr, cons res = true; } else { get_src_regname (tps->core, instr_addr, regname, sizeof (regname)); - xaddr = get_addr (et, regname, j); + xaddr = get_addr (tt, regname, j); } } // Type propagate by following source reg - if (!res && *regname && etrace_regwrite_contains (et, j, regname)) { + if (!res && *regname && etrace_regwrite_contains (tt, j, regname)) { if (var) { if (!userfnc) { // not a userfunction, propagate the callee's arg types into our function's vars @@ -544,7 +1034,7 @@ static void type_match(TPState *tps, char *fcn_name, ut64 addr, ut64 baddr, cons } else if (var && res && (xaddr && xaddr != UT64_MAX)) { // Type progation using value char tmp[REGNAME_SIZE] = {0}; get_src_regname (tps->core, instr_addr, tmp, sizeof (tmp)); - ut64 ptr = get_addr (et, tmp, j); + ut64 ptr = get_addr (tt, tmp, j); if (ptr == xaddr) { var_retype (anal, var, name, r_str_get_fail (type, "int"), memref, false); } @@ -561,44 +1051,152 @@ static void type_match(TPState *tps, char *fcn_name, ut64 addr, ut64 baddr, cons static int bb_cmpaddr(const void *_a, const void *_b) { const RAnalBlock *a = _a, *b = _b; - return a->addr > b->addr ? 1 : (a->addr < b->addr ? -1 : 0); + return a->addr > b->addr? 1: (a->addr < b->addr? -1: 0); } static void tps_fini(TPState *tps) { R_RETURN_IF_FAIL (tps); + tps->core->anal->reg = tps->anal_reg; + type_trace_fini (&tps->tt, &tps->esil); + r_esil_fini (&tps->esil); + r_io_fd_close (tps->core->io, tps->stack_fd); free (tps->cfg_spec); r_config_hold_restore (tps->hc); r_config_hold_free (tps->hc); - r_esil_trace_free (tps->et); - tps->core->anal->esil->trace = tps->_et; free (tps); } +static bool tt_is_reg(void *reg, const char *name) { + RRegItem *ri = r_reg_get ((RReg *)reg, name, -1); + if (!ri) { + return false; + } + r_unref (ri); + return true; +} + +static bool tt_reg_read(void *reg, const char *name, ut64 *val) { + RRegItem *ri = r_reg_get ((RReg *)reg, name, -1); + if (!ri) { + return false; + } + if (val) { + *val = r_reg_get_value ((RReg *)reg, ri); + } + r_unref (ri); + return true; +} + +static ut32 tt_reg_size(void *reg, const char *name) { + RRegItem *ri = r_reg_get ((RReg *)reg, name, -1); + if (!ri) { + return 0; + } + ut32 size = ri->size; + r_unref (ri); + return size; +} + +static REsilRegInterface type_trace_reg_if = { + .is_reg = tt_is_reg, + .reg_read = tt_reg_read, + .reg_write = (REsilRegWrite)r_reg_setv, + .reg_size = tt_reg_size, + // .reg_alias = default_reg_alias +}; + +static bool tt_mem_read (void *mem, ut64 addr, ut8 *buf, int len) { + TPState *tps = (TPState *)mem; + return r_io_read_at (tps->core->io, addr, buf, len); +} + +// ensures type trace esil engine only writes to it's designated stack map. +// writes outside of that itv will be assumed as valid and return true. +// this function assumes, that stack map has highest priority, +// or does not overlap with any other map. +static bool tt_mem_write (void *mem, ut64 addr, const ut8 *buf, int len) { + TPState *tps = (TPState *)mem; + RIOMap *map = r_io_map_get (tps->core->io, tps->stack_map); + RInterval itv = {addr, len}; + if (!r_itv_overlap (map->itv, itv)) { + return true; + } + itv = r_itv_intersect (map->itv, itv); + return r_io_write_at (tps->core->io, itv.addr, &buf[itv.addr - addr], (int)itv.size); +} + +static REsilMemInterface type_trace_mem_if = { + .mem_read = tt_mem_read, + .mem_write = tt_mem_write +}; + +//XXX: this name is wrong static TPState *tps_init(RCore *core) { - R_RETURN_VAL_IF_FAIL (core && core->anal && core->anal->esil, NULL); + R_RETURN_VAL_IF_FAIL (core && core->io && core->anal && core->anal->esil, NULL); TPState *tps = R_NEW0 (TPState); RConfig *cfg = core->config; tps->core = core; + int align = r_arch_info (core->anal->arch, R_ARCH_INFO_DATA_ALIGN); + align = R_MAX (r_arch_info (core->anal->arch, R_ARCH_INFO_CODE_ALIGN), align); + align = R_MAX (align, 1); + tps->stack_base = r_config_get_i (core->config, "esil.stack.addr"); + ut64 stack_size = r_config_get_i (core->config, "esil.stack.size"); + //ideally this all would happen in a dedicated temporal io bank + if (!r_io_map_locate (core->io, &tps->stack_base, stack_size, align)) { + free (tps); + return NULL; + } + char *uri = r_str_newf ("malloc://0x%"PFMT64x, stack_size); + if (!uri) { + free (tps); + return NULL; + } + tps->stack_fd = r_io_fd_open (core->io, uri, R_PERM_RW, 0); + free (uri); + RIOMap *map = r_io_map_add (core->io, tps->stack_fd, R_PERM_RW, 0, tps->stack_base, stack_size); + if (!map) { + r_io_fd_close (core->io, tps->stack_fd); + free (tps); + return NULL; + } + //XXX: r_reg_clone should be invoked in type_trace_init + RReg *reg = r_reg_clone (core->anal->reg); + if (!reg) { + r_io_fd_close (core->io, tps->stack_fd); + free (tps); + return NULL; + } + tps->stack_map = map->id; + //todo fix addrsize + type_trace_reg_if.reg = reg; + type_trace_mem_if.mem = tps; + ut64 sp = tps->stack_base + stack_size - (stack_size % align) - align * 8; + //todo: this probably needs some boundary checks + r_reg_setv (reg, "SP", sp); + r_reg_setv (reg, "BP", sp); + if (!r_esil_init (&tps->esil, 4096, false, 64, &type_trace_reg_if, &type_trace_mem_if)) { + r_reg_free (reg); + r_io_fd_close (core->io, tps->stack_fd); + free (tps); + return NULL; + } + if (!type_trace_init (&tps->tt, &tps->esil, reg)) { + r_esil_fini (&tps->esil); + r_reg_free (reg); + r_io_fd_close (core->io, tps->stack_fd); + free (tps); + return NULL; + } +//XXX: HACK + tps->anal_reg = core->anal->reg; + tps->esil.anal = core->anal; + core->anal->reg = reg; tps->hc = r_config_hold_new (cfg); - // tps->_dt = core->dbg->trace; - tps->_et = core->anal->esil->trace; tps->cfg_spec = strdup (r_config_get (cfg, "anal.types.spec")); tps->cfg_breakoninvalid = r_config_get_b (cfg, "esil.breakoninvalid"); tps->cfg_chk_constraint = r_config_get_b (cfg, "anal.types.constraint"); - tps->et = r_esil_trace_new (core->anal->esil); - // tps->dt = r_debug_trace_new (); - core->anal->esil->trace = tps->et; - // core->dbg->trace = tps->dt; - r_config_hold (tps->hc, "esil.romem", "esil.nonull", "dbg.follow", NULL); - r_config_set_b (cfg, "esil.romem", true); - r_config_set_b (cfg, "esil.nonull", true); + r_config_hold (tps->hc, "dbg.follow", NULL); r_config_set_i (cfg, "dbg.follow", 0); - RReg *reg = core->anal->reg; - if (!r_reg_getv (reg, "BP") && !r_reg_getv (reg, "SP")) { - R_LOG_WARN ("The virtual stack is not yet available. Run aeim or aei and try again"); - tps_fini (tps); - return NULL; - } return tps; } @@ -606,7 +1204,7 @@ R_API void r_core_anal_type_match(RCore *core, RAnalFunction *fcn) { R_RETURN_IF_FAIL (core && core->anal && fcn); // const int op_tions = R_ARCH_OP_MASK_BASIC ;//| R_ARCH_OP_MASK_VAL | R_ARCH_OP_MASK_ESIL | R_ARCH_OP_MASK_HINT; - const int op_tions = R_ARCH_OP_MASK_BASIC | R_ARCH_OP_MASK_HINT; + const int op_tions = R_ARCH_OP_MASK_BASIC | R_ARCH_OP_MASK_HINT | R_ARCH_OP_MASK_ESIL; RAnalBlock *bb; RListIter *it; RAnalOp aop = {0}; @@ -621,14 +1219,8 @@ R_API void r_core_anal_type_match(RCore *core, RAnalFunction *fcn) { if (!tps) { return; } - // TODO: maybe move into tps - RDebugTrace *dtrace = core->dbg->trace; // tps->dt; // core->dbg->trace; - HtPPOptions opt = dtrace->ht->opt; - ht_pp_free (dtrace->ht); - dtrace->ht = ht_pp_new_size (fcn->ninstr, opt.dupvalue, opt.freefn, opt.calcsizeV); - dtrace->ht->opt = opt; - tps->et->cur_idx = 0; + tps->tt.cur_idx = 0; const bool be = R_ARCH_CONFIG_IS_BIG_ENDIAN (core->rasm->config); char *fcn_name = NULL; char *ret_type = NULL; @@ -660,8 +1252,7 @@ R_API void r_core_anal_type_match(RCore *core, RAnalFunction *fcn) { RVecUT64_push_back (&bblist, &bb->addr); } int i, j; - r_config_set_b (core->config, "dbg.trace.eval", false); - REsilTrace *etrace = tps->et; + TypeTrace *etrace = &tps->tt; for (j = 0; j < bblist_size; j++) { const ut64 bbat = *RVecUT64_at (&bblist, j); bb = r_anal_get_block_at (core->anal, bbat); @@ -687,45 +1278,47 @@ R_API void r_core_anal_type_match(RCore *core, RAnalFunction *fcn) { } // XXX fail sometimes /// addr = bb_addr + i; - r_reg_setv (core->dbg->reg, "PC", addr); + r_reg_setv (etrace->reg, "PC", addr); ut64 bb_left = bb_size - i; if ((addr >= bb_addr + bb_size) || (addr < bb_addr)) { // stop emulating this bb if pc is outside the basic block boundaries break; } - if (0&&next_op->addr == addr) { - memcpy (&aop, next_op, sizeof (aop)); - ret = next_op->size; - } else { - ret = r_anal_op (anal, &aop, addr, buf_ptr + i, bb_left, op_tions); - } + ret = r_anal_op (anal, &aop, addr, buf_ptr + i, bb_left, op_tions); if (ret <= 0) { i += minopcode; addr += minopcode; - r_reg_setv (core->dbg->reg, "PC", addr); + r_reg_setv (etrace->reg, "PC", addr); r_anal_op_fini (&aop); continue; } - const int loop_count = r_esil_trace_loopcount (etrace, addr); + const int loop_count = type_trace_loopcount (etrace, addr); #if 1 if (loop_count > LOOP_MAX || aop.type == R_ANAL_OP_TYPE_RET) { r_anal_op_fini (&aop); break; } #endif - r_esil_trace_loopcount_increment (etrace, addr); - if (r_anal_op_nonlinear (aop.type)) { // skip jmp/cjmp/trap/ret/call ops - // eprintf ("%x nonlinear\n", pcval); - r_reg_setv (core->dbg->reg, "PC", addr + aop.size); - } else { - // eprintf ("STEP 0x%"PFMT64x"\n", addr); - int res = r_core_esil_step (core, UT64_MAX, NULL, NULL, false); - if (tps->cfg_breakoninvalid && !res) { + type_trace_loopcount_increment (etrace, addr); + r_reg_setv (etrace->reg, "PC", addr + aop.size); + if (!r_anal_op_nonlinear (aop.type)) { // skip jmp/cjmp/trap/ret/call ops +//this shit probably needs further refactoring. i hate this code + if (aop.type == R_ANAL_OP_TYPE_ILL || aop.type == R_ANAL_OP_TYPE_UNK) { + if (tps->cfg_breakoninvalid) { + R_LOG_ERROR ("step failed at 0x%08"PFMT64x, addr); + r_anal_op_fini (&aop); + retries = -1; + goto repeat; + } + goto bla; + } + if ((type_trace_op (etrace, &tps->esil, &aop)) && tps->cfg_breakoninvalid) { R_LOG_ERROR ("step failed at 0x%08"PFMT64x, addr); retries--; goto repeat; } } +bla: #if 1 // XXX this code looks wrong and slow maybe is not needed // maybe the basic block is gone after the step @@ -743,7 +1336,7 @@ R_API void r_core_anal_type_match(RCore *core, RAnalFunction *fcn) { if (cur_idx < 0) { cur_idx = 0; } - tps->et->cur_idx = etrace_index (etrace); + tps->tt.cur_idx = etrace_index (etrace); RAnalVar *var = r_anal_get_used_function_var (anal, aop.addr); // XXX this is analyzing the same op twice wtf this is so wrong @@ -795,8 +1388,8 @@ R_API void r_core_anal_type_match(RCore *core, RAnalFunction *fcn) { if (Cc && r_anal_cc_exist (anal, Cc)) { char *cc = strdup (Cc); type_match (tps, fcn_name, addr, bb->addr, cc, prev_idx, userfnc, callee_addr); - // prev_idx = tps->et->cur_idx; - prev_idx = tps->core->anal->esil->trace->cur_idx; + // prev_idx = tps->tt.cur_idx; + prev_idx = etrace->cur_idx; R_FREE (ret_type); const char *rt = r_type_func_ret (TDB, fcn_name); if (rt) { @@ -813,8 +1406,8 @@ R_API void r_core_anal_type_match(RCore *core, RAnalFunction *fcn) { if (!strcmp (fcn_name, "__stack_chk_fail")) { // r_strf_var (query, 32, "%d.addr", cur_idx - 1); // ut64 mov_addr = sdb_num_get (trace, query, 0); - // cur_idx = tps->et->cur_idx - 2; - cur_idx = tps->core->anal->esil->trace->cur_idx - 2; + // cur_idx = tps->tt.cur_idx - 2; + cur_idx = etrace->cur_idx - 2; // eprintf (Color_GREEN"ADDROF %d\n"Color_RESET, cur_idx); ut64 mov_addr = etrace_addrof (etrace, cur_idx); RAnalOp *mop = r_core_anal_op (core, mov_addr, R_ARCH_OP_MASK_VAL | R_ARCH_OP_MASK_BASIC); @@ -835,8 +1428,8 @@ R_API void r_core_anal_type_match(RCore *core, RAnalFunction *fcn) { // r_strf_var (query, 32, "%d.reg.write", cur_idx); // const char *cur_dest = sdb_const_get (trace, query, 0); // sdb_const_get (trace, query, 0); - // cur_idx = tps->et->cur_idx - 1; - cur_idx = tps->core->anal->esil->trace->cur_idx - 1; + // cur_idx = tps->tt.cur_idx - 1; + cur_idx = etrace->cur_idx - 1; const char *cur_dest = etrace_regwrite (etrace, cur_idx); get_src_regname (core, aop.addr, src, sizeof (src)); if (ret_reg && *src && strstr (ret_reg, src)) { @@ -978,7 +1571,6 @@ R_API void r_core_anal_type_match(RCore *core, RAnalFunction *fcn) { } } R_FREE (next_op); - r_config_set_b (core->config, "dbg.trace.eval", true); RVecBuf_fini (&buf); RVecUT64_fini (&bblist); diff --git a/libr/core/cconfig.c b/libr/core/cconfig.c index 4a57dee0c1dff..b51c975e79f43 100644 --- a/libr/core/cconfig.c +++ b/libr/core/cconfig.c @@ -464,9 +464,15 @@ static bool cb_arch_platform(void *user, void *data) { static bool cb_archbits(void *user, void *data) { R_RETURN_VAL_IF_FAIL (user && data, false); - RCore *core = (RCore *)user; + RCore *core = user; +#if USE_NEW_ESIL + r_core_esil_unload_arch (core); +#endif RConfigNode *node = (RConfigNode *)data; r_arch_set_bits (core->anal->arch, node->i_value); +#if USE_NEW_ESIL + r_core_esil_load_arch (core); +#endif return true; } @@ -479,15 +485,27 @@ static bool cb_archbits_getter(RCore *core, RConfigNode *node) { } static bool cb_archendian(void *user, void *data) { - RCore *core = (RCore *)user; - RConfigNode *node = (RConfigNode *)data; + RCore *core = user; + RConfigNode *node = data; R_RETURN_VAL_IF_FAIL (node && core && core->anal && core->anal->arch, false); if (!strcmp (node->value, "big") || !strcmp (node->value, "bigswap")) { +#if USE_NEW_ESIL + r_core_esil_unload_arch (core); +#endif r_arch_set_endian (core->anal->arch, R_SYS_ENDIAN_BIG); +#if USE_NEW_ESIL + r_core_esil_load_arch (core); +#endif return true; } if (!strcmp (node->value, "little") || !strcmp (node->value, "littleswap")) { +#if USE_NEW_ESIL + r_core_esil_unload_arch (core); +#endif r_arch_set_endian (core->anal->arch, R_SYS_ENDIAN_LITTLE); +#if USE_NEW_ESIL + r_core_esil_load_arch (core); +#endif return true; } return false; @@ -684,7 +702,13 @@ static bool cb_asmcpu(void *user, void *data) { #endif return 0; } +#if USE_NEW_ESIL + r_core_esil_unload_arch (core); +#endif r_arch_config_set_cpu (core->rasm->config, node->value); +#if USE_NEW_ESIL + r_core_esil_load_arch (core); +#endif const int v = r_anal_archinfo (core->anal, R_ARCH_INFO_CODE_ALIGN); if (v >= 0) { core->anal->config->codealign = v; @@ -1285,7 +1309,13 @@ static bool cb_bigendian(void *user, void *data) { } core->rasm->config->endian = endianType; +#if USE_NEW_ESIL + r_core_esil_unload_arch (core); +#endif r_arch_set_endian (core->anal->arch, endianType); +#if USE_NEW_ESIL + r_core_esil_load_arch (core); +#endif return true; } @@ -1877,11 +1907,11 @@ static bool cb_gotolimit(void *user, void *data) { return true; } -static bool cb_esilverbose(void *user, void *data) { +static bool cb_esilmaxbacksteps(void *user, void *data) { RCore *core = user; RConfigNode *node = data; - if (core->anal->esil) { - core->anal->esil->verbose = node->i_value; + if (core->cesil.reg) { //hack + r_core_esil_set_max_stepback (core, node->i_value); } return true; } @@ -1899,9 +1929,9 @@ static bool cb_esiltraprevert(void *user, void *data) { RCore *core = user; RConfigNode *node = data; if (node->i_value) { - core->esil.cfg |= R_CORE_ESIL_TRAP_REVERT_CONFIG; + core->cesil.cfg |= R_CORE_ESIL_TRAP_REVERT_CONFIG; } else { - core->esil.cfg &= ~R_CORE_ESIL_TRAP_REVERT_CONFIG; + core->cesil.cfg &= ~R_CORE_ESIL_TRAP_REVERT_CONFIG; } return true; } @@ -2131,12 +2161,18 @@ R_API bool r_core_esil_cmd(REsil *esil, const char *cmd, ut64 a1, ut64 a2) { } static bool cb_cmd_esil_ioer(void *user, void *data) { - RCore *core = (RCore *) user; - RConfigNode *node = (RConfigNode *) data; + RCore *core = user; + RConfigNode *node = data; +#if USE_NEW_ESIL + if (core) { + free (core->cesil.cmd_ioer); + core->cesil.cmd_ioer = strdup (node->value); +#else if (core && core->anal && core->anal->esil) { core->anal->esil->cmd = r_core_esil_cmd; free (core->anal->esil->cmd_ioer); core->anal->esil->cmd_ioer = strdup (node->value); +#endif } return true; } @@ -2164,12 +2200,18 @@ static bool cb_cmd_esil_intr(void *user, void *data) { } static bool cb_mdevrange(void *user, void *data) { - RCore *core = (RCore *) user; - RConfigNode *node = (RConfigNode *) data; + RCore *core = user; + RConfigNode *node = data; +#if USE_NEW_ESIL + if (core) { + free (core->cesil.mdev_range); + core->cesil.mdev_range = strdup (node->value); +#else if (core && core->anal && core->anal->esil) { core->anal->esil->cmd = r_core_esil_cmd; free (core->anal->esil->mdev_range); core->anal->esil->mdev_range = strdup (node->value); +#endif } return true; } @@ -2185,34 +2227,52 @@ static bool cb_cmd_esil_pin(void *user, void *data) { } static bool cb_cmd_esil_step(void *user, void *data) { - RCore *core = (RCore *) user; - RConfigNode *node = (RConfigNode *) data; + RCore *core = user; + RConfigNode *node = data; +#if USE_NEW_ESIL + if (core) { + free (core->cesil.cmd_step); + core->cesil.cmd_step = strdup (node->value); +#else if (core && core->anal && core->anal->esil) { core->anal->esil->cmd = r_core_esil_cmd; free (core->anal->esil->cmd_step); core->anal->esil->cmd_step = strdup (node->value); +#endif } return true; } static bool cb_cmd_esil_step_out(void *user, void *data) { - RCore *core = (RCore *) user; - RConfigNode *node = (RConfigNode *) data; + RCore *core = user; + RConfigNode *node = data; +#if USE_NEW_ESIL + if (core) { + free (core->cesil.cmd_step_out); + core->cesil.cmd_step_out = strdup (node->value); +#else if (core && core->anal && core->anal->esil) { core->anal->esil->cmd = r_core_esil_cmd; free (core->anal->esil->cmd_step_out); core->anal->esil->cmd_step_out = strdup (node->value); +#endif } return true; } static bool cb_cmd_esil_mdev(void *user, void *data) { - RCore *core = (RCore *) user; - RConfigNode *node = (RConfigNode *) data; + RCore *core = user; + RConfigNode *node = data; +#if USE_NEW_ESIL + if (core) { + free (core->cesil.cmd_mdev); + core->cesil.cmd_mdev = strdup (node->value); +#else if (core && core->anal && core->anal->esil) { core->anal->esil->cmd = r_core_esil_cmd; free (core->anal->esil->cmd_mdev); core->anal->esil->cmd_mdev = strdup (node->value); +#endif } return true; } @@ -2654,9 +2714,9 @@ static bool cb_romem(void *user, void *data) { RConfigNode *node = (RConfigNode *)data; RCore *core = (RCore*)user; if (node->i_value) { - core->esil.cfg |= R_CORE_ESIL_RO; + core->cesil.cfg |= R_CORE_ESIL_RO; } else { - core->esil.cfg &= ~R_CORE_ESIL_RO; + core->cesil.cfg &= ~R_CORE_ESIL_RO; } return true; } @@ -2665,9 +2725,9 @@ static bool cb_esilnonull(void *user, void *data) { RConfigNode *node = (RConfigNode *)data; RCore *core = (RCore*)user; if (node->i_value) { - core->esil.cfg |= R_CORE_ESIL_NONULL; + core->cesil.cfg |= R_CORE_ESIL_NONULL; } else { - core->esil.cfg &= ~R_CORE_ESIL_NONULL; + core->cesil.cfg &= ~R_CORE_ESIL_NONULL; } return true; } @@ -3747,8 +3807,8 @@ R_API int r_core_config_init(RCore *core) { SETB ("dbg.glibc.demangle", "false", "demangle linked-lists pointers introduced in glibc 2.32"); SETB ("esil.prestep", "true", "step before esil evaluation in `de` commands"); SETI ("esil.maxsteps", 0, "If !=0 defines the maximum amount of steps to perform on aesu/aec/.."); + SETICB ("esil.maxbacksteps", 256, &cb_esilmaxbacksteps, "esil back step capacity"); SETS ("esil.fillstack", "", "initialize ESIL stack with (random, debruijn, sequence, zeros, ...)"); - SETICB ("esil.verbose", 0, &cb_esilverbose, "show ESIL verbose level (0, 1, 2)"); SETICB ("esil.gotolimit", core->anal->esil_goto_limit, &cb_gotolimit, "maximum number of gotos per ESIL expression"); SETICB ("esil.stack.depth", 256, &cb_esilstackdepth, "number of elements that can be pushed on the esilstack"); SETI ("esil.stack.size", 0xf0000, "set stack size in ESIL VM"); diff --git a/libr/core/cmd_anal.inc.c b/libr/core/cmd_anal.inc.c index 41fc144c1da56..8e1cd314ff26c 100644 --- a/libr/core/cmd_anal.inc.c +++ b/libr/core/cmd_anal.inc.c @@ -2446,6 +2446,71 @@ R_API char *cmd_syscall_dostr(RCore *core, st64 n, ut64 addr) { r_syscall_item_free (item); return r_str_append (res, ")"); } +#if USE_NEW_ESIL +static bool mw(int *ec, ut64 addr, const ut8 *old, const ut8 *buf, int len) { + *ec += (len * 2); + return true; +} + +static bool anal_rr(void *_reg, const char *name, ut64 *val) { + RReg *reg = _reg; + RRegItem *ri = r_reg_get (reg, name, -1); + if (ri) { +#if 0 + if (len) { + *len = ri->size; + } +#endif + if (val) { + *val = r_reg_get_value (reg, ri); + } + return true; + } + return false; +} + +static bool anal_rw(void *_reg, const char *name, ut64 val) { + RReg *reg = _reg; + return r_reg_setv (reg, name, val); +} + +static bool anal_mw(void *user, ut64 addr, const ut8 *buf, int len) { + RCore *core = user; + r_io_write_at (core->io, addr, buf, len); + return true; +} + +static bool anal_mr(void *user, ut64 addr, ut8 *buf, int len) { + RCore *core = user; + (void)r_io_read_at (core->io, addr, (ut8 *)buf, len); + return true; +} + +static bool anal_ir(void *_reg, const char *name) { + RReg *reg = _reg; + RRegItem *ri = r_reg_get (reg, name, -1); + if (ri) { + return true; + } + return false; +} + +#if 0 +static bool rw(void *null, const char *regname, ut64 old, ut64 num) { + return true; +} + +static bool rr(void *null, const char *regname, ut64 num) { + return true; +} +#endif + +static bool mr(int *ec, ut64 addr, const ut8 *buf, int len) { + *ec += len; + return true; +} + +#else static bool mw(REsil *esil, ut64 addr, const ut8 *buf, int len) { int *ec = (int*)esil->user; @@ -2466,12 +2531,24 @@ static bool mr(REsil *esil, ut64 addr, ut8 *buf, int len) { *ec += len; return true; } +#endif static int esil_cost(RCore *core, ut64 addr, const char *expr) { if (R_STR_ISEMPTY (expr)) { return 0; } int ec = 0; +#if USE_NEW_ESIL + REsil *e = r_esil_new_simple (0, core->anal->reg, &core->anal->iob); + e->anal = core->anal; //XXX + //preserve regs? enforce ro mem access? + r_esil_add_voyeur (e, &ec, mr, R_ESIL_VOYEUR_MEM_READ); + r_esil_add_voyeur (e, &ec, mw, R_ESIL_VOYEUR_MEM_WRITE); +#if 0 + r_esil_add_voyeur (e, NULL, rr, R_ESIL_VOYEUR_REG_READ); + r_esil_add_voyeur (e, NULL, rw, R_ESIL_VOYEUR_REG_WRITE); +#endif +#else REsil *e = r_esil_new (256, 0, 0); r_esil_setup (e, core->anal, false, false, false); e->user = &ec; @@ -2479,6 +2556,7 @@ static int esil_cost(RCore *core, ut64 addr, const char *expr) { e->cb.mem_write = mw; e->cb.reg_write = rw; e->cb.reg_read = rr; +#endif r_esil_parse (e, expr); r_esil_free (e); return ec; @@ -2504,11 +2582,24 @@ static inline REsil *esil_new_setup(RCore *core) { bool stats = r_config_get_b (core->config, "esil.stats"); bool nonull = r_config_get_b (core->config, "esil.nonull"); r_esil_setup (esil, core->anal, romem, stats, nonull); - esil->verbose = r_config_get_i (core->config, "esil.verbose"); esil->cmd = r_core_esil_cmd; const char *et = r_config_get (core->config, "cmd.esil.trap"); esil->cmd_trap = R_STR_ISNOTEMPTY (et)? strdup (et): NULL; } + esil->user = core; + // reg + esil->reg_if.reg = core->anal->reg; + esil->reg_if.reg_write = anal_rw; + esil->reg_if.reg_read = anal_rr; + esil->reg_if.is_reg = anal_ir; + // mem + esil->mem_if.user = core; + esil->mem_if.mem_read = anal_mr; + esil->mem_if.mem_write = anal_mw; +#if 0 + esil->cb.hook_mem_write = anal_mw; + esil->cb.hook_mem_read = anal_mr; +#endif // run the esilcb from arch if (core->anal->arch) { r_arch_esilcb (core->anal->arch, R_ARCH_ESIL_ACTION_INIT); @@ -2540,22 +2631,36 @@ static void val_tojson(PJ *pj, RAnalValue *val) { pj_end (pj); } - +#if USE_NEW_ESIL +static bool mw2(void *null, ut64 addr, const ut8 *old, const ut8 *buf, int len) { +#else static bool mw2(REsil *esil, ut64 addr, const ut8 *buf, int len) { R_LOG_INFO ("WRITE 0x%08"PFMT64x" %d", addr, len); +#endif return true; } +#if USE_NEW_ESIL +static bool mr2(void *null, ut64 addr, const ut8 *buf, int len) { +#else static bool mr2(REsil *esil, ut64 addr, ut8 *buf, int len) { R_LOG_INFO ("READ 0x%08"PFMT64x" %d", addr, len); +#endif return true; } static void esilmemrefs(RCore *core, const char *expr) { +#if USE_NEW_ESIL + REsil *e = r_esil_new_simple (0, core->anal->reg, &core->anal->iob); + r_esil_add_voyeur (e, NULL, mw2, R_ESIL_VOYEUR_MEM_WRITE); + r_esil_add_voyeur (e, NULL, mr2, R_ESIL_VOYEUR_MEM_READ); + e->anal = core->anal; //XXX +#else REsil *e = r_esil_new (256, 0, 0); r_esil_setup (e, core->anal, false, false, false); e->cb.mem_read = mr2; e->cb.mem_write = mw2; +#endif r_esil_parse (e, expr); r_esil_free (e); } @@ -2573,20 +2678,7 @@ static void core_anal_bytes(RCore *core, const ut8 *buf, int len, int nops, int ut64 addr; PJ *pj = NULL; int totalsize = 0; -#if 1 - REsil *esil = r_esil_new (256, 0, 0); - r_esil_setup (esil, core->anal, false, false, false); - esil->user = &core; - esil->cb.mem_read = mr; - esil->cb.mem_write = mw; -#else - REsil *esil = core->anal->esil; - //esil->user = &ec; - esil->cb.mem_read = mr; - esil->cb.mem_write = mw; -#endif - // Variables required for setting up ESIL to REIL conversion if (use_color) { color = core->cons->context->pal.label; } @@ -3065,7 +3157,6 @@ static void core_anal_bytes(RCore *core, const ut8 *buf, int len, int nops, int r_cons_println (core->cons, pj_string (pj)); pj_free (pj); } - r_esil_free (esil); } static int bb_cmp(const void *a, const void *b) { @@ -7187,6 +7278,95 @@ void cmd_anal_reg(RCore *core, const char *str) { } } +#if USE_NEW_ESIL +R_API int r_core_esil_step(RCore *core, ut64 until_addr, const char *until_expr, ut64 *prev_addr, bool stepOver) { +// const bool is_x86 = r_str_startswith (r_config_get (core->config, "asm.arch"), "x86"); +// const bool breakoninvalid = r_config_get_b (core->config, "esil.breakoninvalid"); + const int esiltimeout = r_config_get_i (core->config, "esil.timeout"); + int ret = true; + ut64 startTime = 0; + + if (esiltimeout > 0) { + startTime = r_time_now_mono (); + } + ut64 addr = r_reg_getv (core->cesil.reg, "PC"); + r_cons_break_push (core->cons, NULL, NULL); + while (addr != until_addr) { + if (esiltimeout > 0) { + ut64 elapsedTime = r_time_now_mono () - startTime; + elapsedTime >>= 20; + if (elapsedTime >= esiltimeout) { + R_LOG_INFO ("[ESIL] Timeout exceeded"); + ret = false; + break; + } + } + RAnalOp op; + if (until_expr || stepOver) { + r_anal_op_init (&op); + ut8 buf[64]; + r_io_read_at (core->io, addr, buf, 64); + r_anal_op_set_bytes (&op, addr, buf, 64); + r_arch_decode (core->anal->arch, &op, R_ARCH_OP_MASK_BASIC | R_ARCH_OP_MASK_ESIL); + } + if (until_expr) { + RAnalHint *hint = r_anal_hint_get (core->anal, addr); + if (hint && hint->esil) { + r_strbuf_set (&op.esil, hint->esil); + } + if (!strcmp (r_strbuf_get (&op.esil), until_expr)) { + r_anal_op_fini (&op); + break; + } + } + if (prev_addr) { + prev_addr[0] = addr; + } + if (stepOver) { + switch (op.type) { + case R_ANAL_OP_TYPE_SWI: + case R_ANAL_OP_TYPE_UCALL: + case R_ANAL_OP_TYPE_CALL: + case R_ANAL_OP_TYPE_JMP: + case R_ANAL_OP_TYPE_RCALL: + case R_ANAL_OP_TYPE_RJMP: + case R_ANAL_OP_TYPE_CJMP: + case R_ANAL_OP_TYPE_RET: + case R_ANAL_OP_TYPE_CRET: + case R_ANAL_OP_TYPE_UJMP: + if (addr % R_MAX (r_arch_info (core->anal->arch, R_ARCH_INFO_CODE_ALIGN), 1)) { + if (core->cesil.cmd_trap) { + r_core_cmd0 (core, core->cesil.cmd_trap); + } + r_anal_op_fini (&op); + goto out; + } + r_reg_setv (core->cesil.reg, "PC", op.addr + op.size); + r_anal_op_fini (&op); + continue; + } + } + if (until_expr || stepOver) { + r_anal_op_fini (&op); + } + if (!r_core_esil_single_step (core)) { + ret = false; + break; + } + // addr = r_reg_getv (core->cesil.reg, "PC"); + addr = r_reg_getv (core->anal->reg, "PC"); + // ESIL TODO: use StepOptions with .amount + if (until_addr == UT64_MAX) { + break; + } + } +out: + r_cons_break_pop (core->cons); + return ret; +} + +#else + R_API int r_core_esil_step(RCore *core, ut64 until_addr, const char *until_expr, ut64 *prev_addr, bool stepOver) { #define return_tail(x) { tail_return_value = x; goto tail_return; } int tail_return_value = 0; @@ -7294,12 +7474,12 @@ R_API int r_core_esil_step(RCore *core, ut64 until_addr, const char *until_expr, R_ARCH_OP_MASK_BASIC | R_ARCH_OP_MASK_ESIL | R_ARCH_OP_MASK_HINT); } #if 1 - if (core->dbg->anal->esil->trace) { + if (core->dbg->anal->esil->trace) { // ut64 pc = r_debug_reg_get (core->dbg, "PC"); // ut64 mask = R_ARCH_OP_MASK_BASIC | R_ARCH_OP_MASK_ESIL | R_ARCH_OP_MASK_VAL; // RAnalOp *op = r_core_anal_op (core, pc, mask); - r_esil_trace_op (core->dbg->anal->esil, &op); - } + r_esil_trace_op (core->dbg->anal->esil, &op); + } #endif // if type is JMP then we execute the next N instructions // update the esil pointer because RAnal.op() can change it @@ -7436,7 +7616,6 @@ R_API int r_core_esil_step(RCore *core, ut64 until_addr, const char *until_expr, } tail_return_value = 1; } - // esil->verbose ? // eprintf ("REPE 0x%"PFMT64x" %s => 0x%"PFMT64x"\n", addr, R_STRBUF_SAFEGET (&op.esil), r_reg_getv (core->anal->reg, "PC")); ut64 pc = r_reg_getv (core->anal->reg, "PC"); if (pc == UT64_MAX || pc == UT32_MAX) { @@ -7488,6 +7667,17 @@ R_API int r_core_esil_step(RCore *core, ut64 until_addr, const char *until_expr, r_cons_break_pop (core->cons); return tail_return_value; } +#endif + +#if USE_NEW_ESIL +R_API bool r_core_esil_step_back(RCore *core) { + R_RETURN_VAL_IF_FAIL (core && core->io && core->cesil.reg && + r_list_length (&core->cesil.stepback), false); + r_core_esil_stepback (core); + return true; +} + +#else R_API bool r_core_esil_step_back(RCore *core) { R_RETURN_VAL_IF_FAIL (core && core->anal, false); @@ -7504,6 +7694,7 @@ R_API bool r_core_esil_step_back(RCore *core) { } return false; } +#endif static void cmd_address_info(RCore *core, const char *addrstr, int fmt) { ut64 addr = R_STR_ISEMPTY (addrstr)? core->addr: r_num_math (core->num, addrstr); @@ -7915,11 +8106,9 @@ static bool mymemread(REsil *esil, ut64 addr, ut8 *buf, int len) { return false; } n = R_NEW (AeaMemItem); - if (n) { - n->addr = addr; - n->size = len; - r_list_push (mymemxsr, n); - } + n->addr = addr; + n->size = len; + r_list_push (mymemxsr, n); return true; } @@ -8287,7 +8476,7 @@ static void cmd_aespc(RCore *core, ut64 addr, ut64 until_addr, int ninstr) { r_reg_setv (core->dbg->reg, "PC", aop.addr + aop.size); const char *e = R_STRBUF_SAFEGET (&aop.esil); if (R_STR_ISNOTEMPTY (e)) { - // eprintf (" 0x%08llx %d %s\n", aop.addr, ret, aop.mnemonic); + // eprintf (" 0x%08llx %d %s\n", aop.addr, ret, aop.mnemonic); (void)r_esil_parse (esil, e); } break; @@ -8427,6 +8616,7 @@ static void cmd_debug_stack_init(RCore *core, int argc, char **argv, char **envp } R_IPI void cmd_aei(RCore *core) { +//TODO use core_esil here REsil *esil = esil_new_setup (core); if (esil) { r_esil_free (core->anal->esil); diff --git a/libr/core/cmd_search.inc.c b/libr/core/cmd_search.inc.c index 41ed0430a42e1..c683b99eba047 100644 --- a/libr/core/cmd_search.inc.c +++ b/libr/core/cmd_search.inc.c @@ -1829,8 +1829,79 @@ static int r_core_search_rop(RCore *core, RInterval search_itv, int opt, const c return result; } +#if USE_NEW_ESIL +static bool search_esil_mem_read (void *mem, ut64 addr, ut8 *buf, int len) { + RCore *core = mem; + if (!addr && r_config_get_b (core->config, "esil.nonull")) { + return false; + } + return r_io_read_at (core->io, addr, buf, len); +} + +static bool search_esil_mem_write_ro (void *mem, ut64 addr, const ut8 *buf, int len) { + RCore *core = mem; + if (!addr && r_config_get_b (core->config, "esil.nonull")) { + return false; + } + RIORegion region; + if (!r_io_get_region_at (core->io, ®ion, addr)) { + //maybe check voidwrites config here + return true; + } + if (!(region.perm & R_PERM_W)) { + return false; + } + if (r_itv_contain (region.itv, addr + len - 1)) { + return true; + } + return search_esil_mem_write_ro (mem, r_itv_end (region.itv), + NULL, addr + len - r_itv_end (region.itv)); //no need to pass buf, because this is RO mode +} + +REsilMemInterface search_esil_mem_if = { + .mem_read = search_esil_mem_read, + .mem_write = search_esil_mem_write_ro, +}; + +static bool search_esil_is_reg (void *reg, const char *name) { + RRegItem *ri = r_reg_get ((RReg *)reg, name, -1); + if (!ri) { + return false; + } + r_unref (ri); + return true; +} + +static bool search_esil_reg_read (void *reg, const char *name, ut64 *val) { + RRegItem *ri = r_reg_get ((RReg *)reg, name, -1); + if (!ri) { + return false; + } + *val = r_reg_get_value ((RReg *)reg, ri); + r_unref (ri); + return true; +} + +static ut32 search_esil_reg_size (void *reg, const char *name) { + RRegItem *ri = r_reg_get ((RReg *)reg, name, -1); + if (!ri) { + return 0; + } + const ut32 size = ri->size; + r_unref (ri); + return size; +} + +REsilRegInterface search_esil_reg_if = { + .is_reg = search_esil_is_reg, + .reg_read = search_esil_reg_read, + .reg_write = (REsilRegWrite)r_reg_setv, + .reg_size = search_esil_reg_size, +}; +#endif + static bool esil_addrinfo(REsil *esil) { - RCore *core = (RCore *) esil->cb.user; + RCore *core = esil->user; ut64 num = 0; char *src = r_esil_pop (esil); if (src && *src && r_esil_get_parm (esil, src, &num)) { @@ -1865,26 +1936,43 @@ static void do_esil_search(RCore *core, struct search_parameters *param, const c r_core_cmd_help (core, help_msg_slash_esil); return; } - const unsigned int addrsize = r_config_get_i (core->config, "esil.addr.size"); + const ut32 addrsize = r_config_get_i (core->config, "esil.addr.size"); const int iotrap = r_config_get_i (core->config, "esil.iotrap"); - int stacksize = r_config_get_i (core->config, "esil.stack.size"); - const int nonull = r_config_get_i (core->config, "esil.nonull"); - const int romem = r_config_get_i (core->config, "esil.romem"); - const int stats = r_config_get_i (core->config, "esil.stats"); - if (stacksize < 16) { - stacksize = 16; + const int stacksize = R_MAX (r_config_get_i (core->config, "esil.stack.size"), 16); +#if USE_NEW_ESIL + search_esil_reg_if.user = core->anal->reg; + search_esil_mem_if.user = core; + REsil stack_located_esil = {0}; + if (!r_esil_init (&stack_located_esil, stacksize, iotrap, addrsize, + &search_esil_reg_if, &search_esil_mem_if)) { + R_LOG_ERROR ("Cannot initialize search esil instance"); + return; } + if (!r_reg_arena_push (core->anal->reg)) { + r_esil_fini (&stack_located_esil); + R_LOG_ERROR ("Cannot push reg arena instance"); + return; + } + REsil *esil = &stack_located_esil; +#else + const int nonull = r_config_get_i (core->config, "esil.nonull"); +// const int romem = r_config_get_i (core->config, "esil.romem"); +// const int stats = r_config_get_i (core->config, "esil.stats"); REsil *esil = r_esil_new (stacksize, iotrap, addrsize); if (!esil) { R_LOG_ERROR ("Cannot create an esil instance"); return; } +// r_esil_setup (esil, core->anal, romem, stats, nonull); + r_esil_setup (esil, core->anal, true, false, nonull); +#endif + esil->user = core; //this is fine, because no arch plugin custom ops are registered r_esil_set_op (esil, "$$", esil_address, 0, 1, R_ESIL_OP_TYPE_UNKNOWN, "current address"); - esil->cb.user = core; + /* hook addrinfo */ + r_esil_set_op (esil, "AddrInfo", esil_addrinfo, 1, 1, R_ESIL_OP_TYPE_UNKNOWN, NULL); // TODO:? cmd_aei (core); RIOMap *map; RListIter *iter; - r_esil_setup (esil, core->anal, romem, stats, nonull); r_list_foreach (param->boundaries, iter, map) { bool hit_happens = false; size_t hit_combo = 0; @@ -1892,12 +1980,8 @@ static void do_esil_search(RCore *core, struct search_parameters *param, const c ut64 nres, addr; ut64 from = r_io_map_begin (map); ut64 to = r_io_map_end (map); - /* hook addrinfo */ - r_esil_set_op (esil, "AddrInfo", esil_addrinfo, 1, 1, R_ESIL_OP_TYPE_UNKNOWN, NULL); - /* hook addrinfo */ - r_esil_setup (esil, core->anal, 1, 0, nonull); + //r_esil_setup (esil, core->anal, 1, 0, nonull); r_esil_stack_free (esil); - esil->verbose = 0; r_cons_break_push (core->cons, NULL, NULL); for (addr = from; addr < to; addr++) { @@ -1921,7 +2005,12 @@ static void do_esil_search(RCore *core, struct search_parameters *param, const c R_LOG_INFO ("Breaked at 0x%08"PFMT64x, addr); break; } +#if USE_NEW_ESIL + const char *pc_name = r_reg_alias_getname (core->anal->reg, R_REG_ALIAS_PC); + r_reg_setv (core->anal->reg, pc_name, core->addr); +#else r_esil_set_pc (esil, addr); +#endif if (!r_esil_parse (esil, input + 2)) { // XXX: return value doesnt seems to be correct here R_LOG_ERROR ("Cannot parse esil (%s)", input + 2); @@ -1978,7 +2067,12 @@ static void do_esil_search(RCore *core, struct search_parameters *param, const c if (param->outmode == R_MODE_JSON) { pj_end (param->pj); } +#if USE_NEW_ESIL + r_esil_fini (&stack_located_esil); + r_reg_arena_pop (core->anal->reg); +#else r_esil_free (esil); +#endif } #define MAXINSTR 8 @@ -2069,29 +2163,20 @@ static void do_syscall_search(RCore *core, struct search_parameters *param) { RAnalOp aop = {0}; int i, ret, bsize = R_MAX (64, core->blocksize); int kwidx = core->search->n_kws; - RIOMap* map; + RIOMap *map; RListIter *iter; const int mininstrsz = r_anal_archinfo (core->anal, R_ARCH_INFO_MINOP_SIZE); const int minopcode = R_MAX (1, mininstrsz); - REsil *esil; int align = core->search->align; - int stacksize = r_config_get_i (core->config, "esil.stack.depth"); - int iotrap = r_config_get_i (core->config, "esil.iotrap"); - unsigned int addrsize = r_config_get_i (core->config, "esil.addr.size"); const bool isx86 = r_str_startswith (r_config_get (core->config, "asm.arch"), "x86"); - if (!(esil = r_esil_new (stacksize, iotrap, addrsize))) { - return; - } int *previnstr = calloc (MAXINSTR + 1, sizeof (int)); if (!previnstr) { - r_esil_free (esil); return; } ut8 *buf = malloc (bsize); if (!buf) { R_LOG_ERROR ("Cannot allocate %d byte(s)", bsize); - r_esil_free (esil); free (previnstr); return; } @@ -2264,7 +2349,7 @@ static void do_syscall_search(RCore *core, struct search_parameters *param) { pj_end (param->pj); } r_core_seek (core, oldoff, true); - r_esil_free (esil); + // r_esil_free (esil); r_cons_break_pop (core->cons); free (buf); free (esp32); diff --git a/libr/core/core.c b/libr/core/core.c index 859aff7634c14..c6a3f96d8a677 100644 --- a/libr/core/core.c +++ b/libr/core/core.c @@ -2740,6 +2740,10 @@ R_API bool r_core_init(RCore *core) { core->io->cb_printf = r_cons_gprintf; core->dbg->cb_printf = r_cons_gprintf; core->dbg->ev = core->ev; +#if USE_NEW_ESIL + r_core_esil_init (core); + // core->esil.reg = core->anal->reg; +#endif r_core_config_init (core); r_core_loadlibs_init (core); //r_core_loadlibs (core); @@ -2845,6 +2849,9 @@ R_API void r_core_fini(RCore *c) { /* r_unref (c->anal->config); */ +#if USE_NEW_ESIL + r_core_esil_fini (&c->cesil); +#endif if (c->anal->esil) { c->anal->esil->anal = NULL; } diff --git a/libr/core/core_esil.c b/libr/core/core_esil.c index 0a0007101b52e..5afe1f51ba6dd 100644 --- a/libr/core/core_esil.c +++ b/libr/core/core_esil.c @@ -1,4 +1,4 @@ -/* radare - LGPL - Copyright 2024 - condret */ +/* radare - LGPL - Copyright 2024-2025 - condret */ #define R_LOG_ORIGIN "core.esil" #include @@ -8,8 +8,8 @@ static bool core_esil_op_todo(REsil *esil) { RCore *core = esil->user; - if (core->esil.cmd_todo) { - r_core_cmd0 (core, core->esil.cmd_todo); + if (R_STR_ISNOTEMPTY (core->cesil.cmd_todo)) { + r_core_cmd0 (core, core->cesil.cmd_todo); } return true; } @@ -23,14 +23,15 @@ static bool core_esil_op_interrupt(REsil *esil) { } free (str); RCore *core = esil->user; - if (core->esil.cmd_intr) { - r_core_cmd0 (core, core->esil.cmd_intr); + if (R_STR_ISNOTEMPTY (core->cesil.cmd_intr)) { + r_core_cmd0 (core, core->cesil.cmd_intr); } return r_esil_fire_interrupt (esil, (ut32)interrupt); } -static bool core_esil_is_reg (void *core, const char *name) { - RRegItem *ri = r_reg_get (((RCore *)core)->esil.reg, name, -1); +static bool core_esil_is_reg(void *_core, const char *name) { + RCore *core = _core; + RRegItem *ri = r_reg_get (core->anal->reg, name, -1); if (!ri) { return false; } @@ -38,27 +39,31 @@ static bool core_esil_is_reg (void *core, const char *name) { return true; } -static bool core_esil_reg_read (void *core, const char *name, ut64 *val) { - RRegItem *ri = r_reg_get (((RCore *)core)->esil.reg, name, -1); - if (!ri) { - return false; +static bool core_esil_reg_read(void *_core, const char *name, ut64 *val) { + RCore *core = _core; + RRegItem *ri = r_reg_get (core->anal->reg, name, -1); + if (R_LIKELY (ri)) { + *val = r_reg_get_value (core->anal->reg, ri); + r_unref (ri); + return true; } - *val = r_reg_get_value (((RCore *)core)->esil.reg, ri); - r_unref (ri); - return true; + return false; } -static bool core_esil_reg_write (void *core, const char *name, ut64 val) { - return r_reg_setv (((RCore *)core)->esil.reg, name, val); +static bool core_esil_reg_write (void *_core, const char *name, ut64 val) { + RCore *core = _core; + return r_reg_setv (core->cesil.reg, name, val); } -static ut32 core_esil_reg_size (void *core, const char *name) { - RRegItem *ri = r_reg_get (((RCore *)core)->esil.reg, name, -1); - if (!ri) { - return 0; - } - r_unref (ri); - return ri->size; +static ut32 core_esil_reg_size(void *_core, const char *name) { + RCore *core = _core; + RRegItem *ri = r_reg_get (core->anal->reg, name, -1); + if (R_LIKELY (ri)) { + const ut32 size = ri->size; + r_unref (ri); + return size; + } + return 0; } static REsilRegInterface core_esil_reg_if = { @@ -72,22 +77,27 @@ static bool core_esil_mem_switch (void *core, ut32 idx) { return r_io_bank_use (((RCore *)core)->io, idx); } -static bool core_esil_mem_read (void *core, ut64 addr, ut8 *buf, int len) { +static bool core_esil_mem_read (void *_core, ut64 addr, ut8 *buf, int len) { if ((UT64_MAX - len + 1) < addr) { - if (!core_esil_mem_read (core, 0ULL, buf + (UT64_MAX - addr + 1), + if (!core_esil_mem_read (_core, 0ULL, buf + (UT64_MAX - addr + 1), len - (UT64_MAX - addr + 1))) { return false; } len = UT64_MAX - addr + 1; } - RCore *c = core; - if (!addr && c->esil.cfg & R_CORE_ESIL_NONULL) { + RCore *core = _core; + if (!addr && core->cesil.cfg & R_CORE_ESIL_NONULL) { return false; } - return r_io_read_at (c->io, addr, buf, len); + if (R_STR_ISNOTEMPTY (core->cesil.cmd_mdev) && core->cesil.mdev_range && r_str_range_in (core->cesil.mdev_range, addr)) { + r_core_cmdf (core, "%s %"PFMT64d" 0", core->cesil.cmd_mdev, core->cesil.old_pc); + return core->num->value; + } + return r_io_read_at (core->io, addr, buf, len); } -static bool core_esil_mem_write (void *core, ut64 addr, const ut8 *buf, int len) { +static bool core_esil_mem_write (void *_core, ut64 addr, const ut8 *buf, int len) { + RCore *core = _core; if ((UT64_MAX - len + 1) < addr) { if (!core_esil_mem_write (core, 0ULL, buf + (UT64_MAX - addr + 1), len - (UT64_MAX - addr + 1))) { @@ -95,13 +105,16 @@ static bool core_esil_mem_write (void *core, ut64 addr, const ut8 *buf, int len) } len = UT64_MAX - addr + 1; } - RCore *c = core; - if (!addr && c->esil.cfg & R_CORE_ESIL_NONULL) { + if (!addr && core->cesil.cfg & R_CORE_ESIL_NONULL) { return false; } - if (c->esil.cfg & R_CORE_ESIL_RO) { + if (R_STR_ISNOTEMPTY (core->cesil.cmd_mdev) && core->cesil.mdev_range && r_str_range_in (core->cesil.mdev_range, addr)) { + r_core_cmdf (core, "%s %"PFMT64d" 1", core->cesil.cmd_mdev, core->cesil.old_pc); + return core->num->value; + } + if (core->cesil.cfg & R_CORE_ESIL_RO) { RIORegion region; - if (!r_io_get_region_at (c->io, ®ion, addr)) { + if (!r_io_get_region_at (core->io, ®ion, addr)) { //maybe check voidwrites config here return true; } @@ -114,7 +127,7 @@ static bool core_esil_mem_write (void *core, ut64 addr, const ut8 *buf, int len) return core_esil_mem_write (core, r_itv_end (region.itv), NULL, addr + len - r_itv_end (region.itv)); //no need to pass buf, because this is RO mode } - return r_io_write_at (c->io, addr, buf, len); + return r_io_write_at (core->io, addr, buf, len); } static REsilMemInterface core_esil_mem_if = { @@ -123,8 +136,7 @@ static REsilMemInterface core_esil_mem_if = { .mem_write = core_esil_mem_write }; -static void core_esil_voyeur_trap_revert_reg_write (void *user, const char *name, - ut64 old, ut64 val) { +static void core_esil_voyeur_trap_revert_reg_write(void *user, const char *name, ut64 old, ut64 val) { RCoreEsil *cesil = user; if (!(cesil->cfg & R_CORE_ESIL_TRAP_REVERT)) { return; @@ -132,12 +144,15 @@ static void core_esil_voyeur_trap_revert_reg_write (void *user, const char *name r_strbuf_prependf (&cesil->trap_revert, "0x%"PFMT64x",%s,:=,", old, name); } -static void core_esil_voyeur_trap_revert_mem_write (void *user, ut64 addr, +static void core_esil_voyeur_trap_revert_mem_write(void *user, ut64 addr, const ut8 *old, const ut8 *buf, int len) { RCoreEsil *cesil = user; if (!(cesil->cfg & R_CORE_ESIL_TRAP_REVERT)) { return; } + if (cesil->cfg & R_CORE_ESIL_RO) { + return; + } int i; for (i = 0; i < len; i++) { //TODO: optimize this after breaking @@ -146,34 +161,48 @@ static void core_esil_voyeur_trap_revert_mem_write (void *user, ut64 addr, } } +static void core_esil_stepback_free(void *data) { + if (data) { + RCoreEsilStepBack *cesb = data; + free (cesb->expr); + free (data); + } +} + R_API bool r_core_esil_init(RCore *core) { R_RETURN_VAL_IF_FAIL (core && core->io, false); - core->esil.reg = r_reg_new (); - if (!core->esil.reg) { + core->cesil = (const RCoreEsil){0}; +#if 0 // hack + core->cesil.reg = core->anal->reg; +#else + core->cesil.reg = r_reg_new (); + if (!core->cesil.reg) { return false; } +#endif core_esil_reg_if.reg = core; core_esil_mem_if.mem = core; - if (!r_esil_init (&core->esil.esil, 4096, false, 64, + if (!r_esil_init (&core->cesil.esil, 4096, false, 64, &core_esil_reg_if, &core_esil_mem_if)) { goto init_fail; } - if (!r_esil_set_op (&core->esil.esil, "TODO", core_esil_op_todo, 0, 0, - R_ESIL_OP_TYPE_UNKNOWN, NULL) || !r_esil_set_op (&core->esil.esil, + if (!r_esil_set_op (&core->cesil.esil, "TODO", core_esil_op_todo, 0, 0, + R_ESIL_OP_TYPE_UNKNOWN, NULL) || !r_esil_set_op (&core->cesil.esil, "$", core_esil_op_interrupt, 0, 1, R_ESIL_OP_TYPE_UNKNOWN, NULL)) { goto op_fail; } - r_strbuf_init (&core->esil.trap_revert); - core->esil.esil.user = core; - core->esil.tr_reg = r_esil_add_voyeur (&core->esil.esil, &core->esil, + r_strbuf_init (&core->cesil.trap_revert); + core->cesil.esil.user = core; + core->cesil.tr_reg = r_esil_add_voyeur (&core->cesil.esil, &core->cesil, core_esil_voyeur_trap_revert_reg_write, R_ESIL_VOYEUR_REG_WRITE); - core->esil.tr_mem = r_esil_add_voyeur (&core->esil.esil, &core->esil, + core->cesil.tr_mem = r_esil_add_voyeur (&core->cesil.esil, &core->cesil, core_esil_voyeur_trap_revert_mem_write, R_ESIL_VOYEUR_MEM_WRITE); + core->cesil.stepback.free = core_esil_stepback_free; return true; op_fail: - r_esil_fini (&core->esil.esil); + r_esil_fini (&core->cesil.esil); init_fail: - r_reg_free (core->esil.reg); + r_reg_free (core->cesil.reg); return false; } @@ -187,7 +216,7 @@ R_API void r_core_esil_load_arch(RCore *core) { } //This is awful. TODO: massage r_arch api REsil *arch_esil = core->anal->arch->esil; - core->anal->arch->esil = &core->esil.esil; + core->anal->arch->esil = &core->cesil.esil; r_arch_esilcb (core->anal->arch, R_ARCH_ESIL_ACTION_INIT); core->anal->arch->esil = arch_esil; char *rp = core->anal->arch->session->plugin->regs (core->anal->arch->session); @@ -195,7 +224,7 @@ R_API void r_core_esil_load_arch(RCore *core) { R_LOG_WARN ("Couldn't set reg profile"); return; } - r_reg_set_profile_string (core->esil.reg, rp); + r_reg_set_profile_string (core->cesil.reg, rp); free (rp); } @@ -206,34 +235,127 @@ R_API void r_core_esil_unload_arch(RCore *core) { return; } REsil *arch_esil = core->anal->arch->esil; - core->anal->arch->esil = &core->esil.esil; + core->anal->arch->esil = &core->cesil.esil; r_arch_esilcb (core->anal->arch, R_ARCH_ESIL_ACTION_FINI); core->anal->arch->esil = arch_esil; } -R_API void r_core_esil_single_step(RCore *core) { - R_RETURN_IF_FAIL (core && core->anal && core->anal->arch && core->io && core->esil.reg); - const char *pc_name = r_reg_alias_getname (core->esil.reg, R_REG_ALIAS_PC); +R_API bool r_core_esil_run_expr_at(RCore *core, const char *expr, ut64 addr) { + R_RETURN_VAL_IF_FAIL (expr && core && core->anal && core->anal->arch && core->io && core->cesil.reg, false); + const char *pc_name = r_reg_alias_getname (core->cesil.reg, R_REG_ALIAS_PC); if (!pc_name) { R_LOG_ERROR ("CoreEsil reg profile has no pc register"); - return; + return false; } ut64 pc; - if (!r_esil_reg_read_silent (&core->esil.esil, pc_name, &pc, NULL)) { + if (!r_esil_reg_read_silent (&core->cesil.esil, pc_name, &pc, NULL)) { R_LOG_ERROR ("Couldn't read from PC register"); - return; + return false; + } + if ((core->cesil.cfg & R_CORE_ESIL_TRAP_REVERT_CONFIG) || core->cesil.max_stepback) { + core->cesil.cfg |= R_CORE_ESIL_TRAP_REVERT; + r_strbuf_initf (&core->cesil.trap_revert, + "0x%"PFMT64x",%s,:=", pc, pc_name); + } else { + core->cesil.cfg &= ~R_CORE_ESIL_TRAP_REVERT; + } + core->cesil.old_pc = pc; + r_esil_reg_write_silent (&core->cesil.esil, pc_name, addr); + if (r_esil_parse (&core->cesil.esil, expr)) { + if (core->cesil.cfg & R_CORE_ESIL_TRAP_REVERT) { + if (core->cesil.max_stepback) { + if (core->cesil.max_stepback > r_list_length (&core->cesil.stepback)) { + RCoreEsilStepBack *cesb = R_NEW (RCoreEsilStepBack); + if (!cesb) { + R_LOG_WARN ("RCoreEsilStepBack allocation failed"); + r_strbuf_fini (&core->cesil.trap_revert); + } else { + if (!r_list_push (&core->cesil.stepback, cesb)) { + R_LOG_WARN ("Pushing RCoreEsilStepBack failed"); + } else { + cesb->expr = r_strbuf_drain_nofree (&core->cesil.trap_revert); + cesb->addr = core->cesil.old_pc; + } + } + } else { + //this is like r_list_pop_head + r_list_push, + //but without expensive calls to malloc and free + RListIter *iter = core->cesil.stepback.head; + iter->p->n = NULL; + core->cesil.stepback.head = iter->p; + iter->p = NULL; + iter->n = core->cesil.stepback.tail; + core->cesil.stepback.tail->p = iter; + core->cesil.stepback.tail = iter; + RCoreEsilStepBack *cesb = iter->data; + free (cesb->expr); + cesb->expr = r_strbuf_drain_nofree (&core->cesil.trap_revert); + cesb->addr = core->cesil.old_pc; + } + } else { + r_strbuf_fini (&core->cesil.trap_revert); + } + } + return true; + } + if (core->cesil.cfg & R_CORE_ESIL_TRAP_REVERT) { + //disable trap_revert voyeurs + core->cesil.cfg &= ~R_CORE_ESIL_TRAP_REVERT; + char *expr = r_strbuf_drain_nofree (&core->cesil.trap_revert); + //revert all changes + r_esil_parse (&core->cesil.esil, expr); + free (expr); + } else { + r_esil_reg_write_silent (&core->cesil.esil, pc_name, core->cesil.old_pc); + } + if (R_STR_ISNOTEMPTY (core->cesil.cmd_trap)) { + r_core_cmd0 (core, core->cesil.cmd_trap); + } + switch (core->cesil.esil.trap_code) { + case R_ANAL_TRAP_WRITE_ERR: + case R_ANAL_TRAP_READ_ERR: + if (R_STR_ISNOTEMPTY (core->cesil.cmd_ioer)) { + r_core_cmdf (core, "%s %"PFMT64d" 0", core->cesil.cmd_ioer, + core->cesil.old_pc); + } + break; } + return false; +} + +R_API bool r_core_esil_single_step(RCore *core) { + R_RETURN_VAL_IF_FAIL (core && core->anal && core->anal->arch && core->io && core->cesil.reg, false); + RReg *reg = core->anal->reg; // core->cesil.reg NO + const char *pc_name = r_reg_alias_getname (reg, R_REG_ALIAS_PC); + if (!pc_name) { + R_LOG_ERROR ("CoreEsil reg profile has no pc register"); + return false; + } + ut64 pc; + if (!r_esil_reg_read_silent (&core->cesil.esil, pc_name, &pc, NULL)) { + R_LOG_ERROR ("Couldn't read from PC register"); + return false; + } + ut32 trap_code = R_ANAL_TRAP_UNALIGNED; + const int align = R_MAX (1, r_arch_info (core->anal->arch, R_ARCH_INFO_CODE_ALIGN)); + if (pc % align) { + R_LOG_ERROR ("Unaligned execution at PC=0x%08"PFMT64x, pc); + goto trap; + } + trap_code = R_ANAL_TRAP_READ_ERR; //check if pc is in mapped rx area, //or in case io is pa //check if pc is within desc and desc is at least readable RIORegion region; if (!r_io_get_region_at (core->io, ®ion, pc)) { + R_LOG_ERROR ("pc not in region %s = 0x%"PFMT64x, pc_name, pc); goto trap; } if ((region.perm & (R_PERM_R | R_PERM_X)) != (R_PERM_R | R_PERM_X) || (!core->io->va && !(region.perm & R_PERM_R))) { goto trap; } + trap_code = R_ANAL_TRAP_NONE; int max_opsize = R_MIN (64, r_arch_info (core->anal->arch, R_ARCH_INFO_MAXOP_SIZE)); if (R_UNLIKELY (max_opsize < 1)) { @@ -243,7 +365,7 @@ R_API void r_core_esil_single_step(RCore *core) { ut8 buf[64]; if (R_UNLIKELY (!r_io_read_at (core->io, pc, buf, max_opsize))) { R_LOG_ERROR ("Couldn't read data to decode from 0x%"PFMT64x, pc); - return; + return false; } RAnalOp op; r_anal_op_init (&op); @@ -254,7 +376,7 @@ R_API void r_core_esil_single_step(RCore *core) { R_ARCH_OP_MASK_BASIC | R_ARCH_OP_MASK_ESIL)) { R_LOG_ERROR ("COuldn't decode instruction at 0x%"PFMT64x, pc); r_anal_op_fini (&op); - return; + return false; } RAnalHint *hint = r_anal_hint_get (core->anal, pc); if (hint) { @@ -280,47 +402,129 @@ R_API void r_core_esil_single_step(RCore *core) { if (!r_itv_contain (region.itv, pc + op.size)) { goto op_trap; } - if (core->esil.cfg & R_CORE_ESIL_TRAP_REVERT_CONFIG) { - core->esil.cfg |= R_CORE_ESIL_TRAP_REVERT; - r_strbuf_initf (&core->esil.trap_revert, + if ((core->cesil.cfg & R_CORE_ESIL_TRAP_REVERT_CONFIG) || core->cesil.max_stepback) { + core->cesil.cfg |= R_CORE_ESIL_TRAP_REVERT; + r_strbuf_initf (&core->cesil.trap_revert, "0x%"PFMT64x",%s,:=", pc, pc_name); } else { - core->esil.cfg &= ~R_CORE_ESIL_TRAP_REVERT; - core->esil.old_pc = pc; + core->cesil.cfg &= ~R_CORE_ESIL_TRAP_REVERT; } + core->cesil.old_pc = pc; pc += op.size; char *expr = r_strbuf_drain_nofree (&op.esil); + r_esil_reg_write_silent (&core->cesil.esil, pc_name, pc); + r_reg_setv (core->anal->reg, pc_name, pc); // XXX r_anal_op_fini (&op); - r_esil_reg_write_silent (&core->esil.esil, pc_name, pc); - const bool suc = r_esil_parse (&core->esil.esil, expr); + if (R_STR_ISNOTEMPTY (core->cesil.cmd_step)) { + r_core_cmdf (core, "%s %"PFMT64d" 0", core->cesil.cmd_step, core->cesil.old_pc); + if (core->num->value) { + free (expr); + goto skip; + } + } + if (R_STR_ISEMPTY (expr)) { + // nops, endbrp ,.. + goto skip; + } + const bool suc = r_esil_parse (core->anal->esil, expr); + //const bool suc = r_esil_parse (&core->cesil.esil, expr); free (expr); if (suc) { - if (core->esil.cfg & R_CORE_ESIL_TRAP_REVERT) { - r_strbuf_fini (&core->esil.trap_revert); +skip: + if (core->cesil.cfg & R_CORE_ESIL_TRAP_REVERT) { + if (core->cesil.max_stepback) { + if (core->cesil.max_stepback > r_list_length (&core->cesil.stepback)) { + RCoreEsilStepBack *cesb = R_NEW0 (RCoreEsilStepBack); + if (!cesb) { + R_LOG_WARN ("RCoreEsilStepBack allocation failed"); + r_strbuf_fini (&core->cesil.trap_revert); + } else { + if (!r_list_push (&core->cesil.stepback, cesb)) { + R_LOG_WARN ("Pushing RCoreEsilStepBack failed"); + } else { + cesb->expr = r_strbuf_drain_nofree (&core->cesil.trap_revert); + cesb->addr = core->cesil.old_pc; + } + } + } else { + //this is like r_list_pop_head + r_list_push, + //but without expensive calls to malloc and free + RListIter *iter = core->cesil.stepback.head; + if (iter->p) { + iter->p->n = NULL; + } else { + R_LOG_ERROR ("iter->p shouldnt be null"); + } + core->cesil.stepback.head = iter->p; + iter->p = NULL; + iter->n = core->cesil.stepback.tail; + core->cesil.stepback.tail->p = iter; + core->cesil.stepback.tail = iter; + RCoreEsilStepBack *cesb = iter->data; + free (cesb->expr); + cesb->expr = r_strbuf_drain_nofree (&core->cesil.trap_revert); + cesb->addr = core->cesil.old_pc; + } + } else { + r_strbuf_fini (&core->cesil.trap_revert); + } } - return; + if (R_STR_ISNOTEMPTY (core->cesil.cmd_step_out)) { + r_core_cmdf (core, "%s %"PFMT64d" 0", core->cesil.cmd_step_out, core->cesil.old_pc); + } + return true; } - if (core->esil.cfg & R_CORE_ESIL_TRAP_REVERT) { + trap_code = core->cesil.esil.trap_code; + if (core->cesil.cfg & R_CORE_ESIL_TRAP_REVERT) { //disable trap_revert voyeurs - core->esil.cfg &= ~R_CORE_ESIL_TRAP_REVERT; - char *expr = r_strbuf_drain_nofree (&core->esil.trap_revert); - //TODO: check trap codes here + core->cesil.cfg &= ~R_CORE_ESIL_TRAP_REVERT; + char *expr = r_strbuf_drain_nofree (&core->cesil.trap_revert); //revert all changes - r_esil_parse (&core->esil.esil, expr); + // r_esil_parse (&core->cesil.esil, expr); + r_esil_parse (core->anal->esil, expr); free (expr); goto trap; } //restore pc - r_esil_reg_write_silent (&core->esil.esil, pc_name, core->esil.old_pc); - //TODO: check trap codes here + // eprintf ("PC WRITE %llx\n", core->cesil.old_pc); + r_esil_reg_write_silent (&core->cesil.esil, pc_name, core->cesil.old_pc); goto trap; op_trap: r_anal_op_fini (&op); trap: - if (core->esil.cmd_trap) { - r_core_cmd0 (core, core->esil.cmd_trap); + if (R_STR_ISNOTEMPTY (core->cesil.cmd_trap)) { + r_core_cmd0 (core, core->cesil.cmd_trap); + } + switch (trap_code) { + case R_ANAL_TRAP_WRITE_ERR: + case R_ANAL_TRAP_READ_ERR: + if (R_STR_ISNOTEMPTY (core->cesil.cmd_ioer)) { + r_core_cmdf (core, "%s %"PFMT64d" 0", core->cesil.cmd_ioer, + core->cesil.old_pc); + } + break; + } + return false; +} + +R_API void r_core_esil_stepback(RCore *core) { + R_RETURN_IF_FAIL (core && core->io && core->cesil.reg); + if (!r_list_length (&core->cesil.stepback)) { + //not an error + return; + } + RCoreEsilStepBack *cesb = r_list_pop (&core->cesil.stepback); + core->cesil.cfg &= ~R_CORE_ESIL_TRAP_REVERT; + r_esil_parse (&core->cesil.esil, cesb->expr); + core_esil_stepback_free (cesb); +} + +R_API void r_core_esil_set_max_stepback(RCore *core, ut32 max_stepback) { + R_RETURN_IF_FAIL (core && core->cesil.stepback.free); + core->cesil.max_stepback = max_stepback; + while (r_list_length (&core->cesil.stepback) > max_stepback) { + core_esil_stepback_free (r_list_pop_head (&core->cesil.stepback)); } - return; } R_API void r_core_esil_fini(RCoreEsil *cesil) { @@ -329,10 +533,12 @@ R_API void r_core_esil_fini(RCoreEsil *cesil) { r_esil_del_voyeur (&cesil->esil, cesil->tr_mem); r_esil_fini (&cesil->esil); r_strbuf_fini (&cesil->trap_revert); +#if 1 if (cesil->reg) { r_reg_free (cesil->reg); cesil->reg = NULL; } +#endif R_FREE (cesil->cmd_step); R_FREE (cesil->cmd_step_out); R_FREE (cesil->cmd_intr); @@ -341,5 +547,6 @@ R_API void r_core_esil_fini(RCoreEsil *cesil) { R_FREE (cesil->cmd_todo); R_FREE (cesil->cmd_ioer); R_FREE (cesil->mdev_range); + r_list_purge (&cesil->stepback); cesil->esil = (const REsil){0}; } diff --git a/libr/core/vmenus.c b/libr/core/vmenus.c index 7f5dcf2bc4908..ab8dd74df1a61 100644 --- a/libr/core/vmenus.c +++ b/libr/core/vmenus.c @@ -237,10 +237,16 @@ R_API bool r_core_visual_esil(RCore *core, const char *input) { } RCons *cons = core->cons; r_reg_arena_push (core->anal->reg); +#if USE_NEW_ESIL + REsil *esil = r_esil_new_simple (addrsize, core->anal->reg, &core->anal->iob); + const char *pc_name = r_reg_alias_getname (core->anal->reg, R_REG_ALIAS_PC); + r_reg_setv (core->anal->reg, pc_name, core->addr); +#else REsil *esil = r_esil_new (20, 0, addrsize); r_esil_setup (esil, core->anal, false, false, false); // esil->anal = core->anal; r_esil_set_pc (esil, core->addr); +#endif char *expr = NULL; bool refresh = false; for (;;) { @@ -336,19 +342,29 @@ R_API bool r_core_visual_esil(RCore *core, const char *input) { case 'P': x = 0; r_esil_free (esil); - esil = r_esil_new (20, 0, addrsize); - esil->anal = core->anal; r_core_cmd0 (core, "so+1"); +#if USE_NEW_ESIL + esil = r_esil_new_simple (addrsize, core->anal->reg, &core->anal->iob); + r_reg_setv (core->anal->reg, pc_name, core->addr); +#else + esil = r_esil_new (20, 0, addrsize); r_esil_set_pc (esil, core->addr); +#endif + esil->anal = core->anal; break; case 'N': case 'p': x = 0; r_esil_free (esil); - esil = r_esil_new (20, 0, addrsize); - esil->anal = core->anal; r_core_cmd0 (core, "so-1"); +#if USE_NEW_ESIL + esil = r_esil_new_simple (addrsize, core->anal->reg, &core->anal->iob); + r_reg_setv (core->anal->reg, pc_name, core->addr); +#else + esil = r_esil_new (20, 0, addrsize); r_esil_set_pc (esil, core->addr); +#endif + esil->anal = core->anal; break; case '=': { // TODO: edit diff --git a/libr/esil/esil.c b/libr/esil/esil.c index 5661d839fe0f8..a56d54f99372a 100644 --- a/libr/esil/esil.c +++ b/libr/esil/esil.c @@ -1,4 +1,4 @@ -/* radare - LGPL - Copyright 2014-2024 - pancake, condret */ +/* radare - LGPL - Copyright 2014-2025 - pancake, condret */ #define R_LOG_ORIGIN "esil" @@ -35,7 +35,6 @@ R_API REsil *r_esil_new(int stacksize, int iotrap, unsigned int addrsize) { free (esil); return NULL; } - esil->verbose = false; esil->stacksize = stacksize; esil->parse_goto_count = R_ESIL_GOTO_LIMIT; esil->ops = ht_pp_new (NULL, esil_ops_free, NULL); @@ -44,8 +43,10 @@ R_API REsil *r_esil_new(int stacksize, int iotrap, unsigned int addrsize) { r_esil_plugins_init (esil); esil->addrmask = r_num_genmask (addrsize - 1); esil->trace = r_esil_trace_new (esil); +#if USE_NEW_ESIL == 0 int stats = 1; r_esil_stats (esil, NULL, stats); +#endif r_esil_setup_ops (esil); return esil; } @@ -75,6 +76,7 @@ R_API bool r_esil_init(REsil *esil, int stacksize, bool iotrap, if (r_id_storage_init (&esil->voyeur[i], 0, MAX_VOYEURS)) { continue; } + R_LOG_ERROR ("voyeur init failed"); do { r_id_storage_fini (&esil->voyeur[i]); i--; @@ -129,21 +131,25 @@ static bool default_reg_alias(void *reg, int kind, const char *name) { static bool default_reg_read(void *reg, const char *name, ut64 *val) { RRegItem *ri = r_reg_get ((RReg *)reg, name, -1); - if (!ri) { - return false; + if (R_LIKELY (ri)) { + ut64 v = r_reg_get_value ((RReg *)reg, ri); + if (val) { + *val = v; + } + r_unref (ri); + return true; } - *val = r_reg_get_value ((RReg *)reg, ri); - r_unref (ri); - return true; + return false; } static ut32 default_reg_size(void *reg, const char *name) { RRegItem *ri = r_reg_get ((RReg *)reg, name, -1); - if (!ri) { - return 0; + if (R_LIKELY (ri)) { + ut32 size = ri->size; + r_unref (ri); + return size; } - r_unref (ri); - return ri->size; + return 0; } static REsilRegInterface simple_reg_if = { @@ -399,9 +405,9 @@ R_API bool r_esil_mem_write(REsil *esil, ut64 addr, const ut8 *buf, int len) { return true; } do { - REsilVoyeur *voy = r_id_storage_get (&esil->voyeur[R_ESIL_OP_TYPE_MEM_WRITE], i); + REsilVoyeur *voy = r_id_storage_get (&esil->voyeur[R_ESIL_VOYEUR_MEM_WRITE], i); voy->mem_write (voy->user, addr, o.buf, buf, len); - } while (r_id_storage_get_next (&esil->voyeur[R_ESIL_OP_TYPE_MEM_WRITE], &i)); + } while (r_id_storage_get_next (&esil->voyeur[R_ESIL_VOYEUR_MEM_WRITE], &i)); return true; } o.ptr = R_NEWS (ut8, len); @@ -413,12 +419,15 @@ R_API bool r_esil_mem_write(REsil *esil, ut64 addr, const ut8 *buf, int len) { esil->trap = R_ANAL_TRAP_NONE; } if (R_UNLIKELY (!r_esil_mem_write_silent (esil, addr, buf, len))) { + free (o.ptr); return false; } - do { - REsilVoyeur *voy = r_id_storage_get (&esil->voyeur[R_ESIL_OP_TYPE_MEM_WRITE], i); - voy->mem_write (voy->user, addr, o.ptr, buf, len); - } while (r_id_storage_get_next (&esil->voyeur[R_ESIL_OP_TYPE_MEM_WRITE], &i)); + if (!r_id_storage_get_lowest (&esil->voyeur[R_ESIL_VOYEUR_MEM_WRITE], &i)) { + do { + REsilVoyeur *voy = r_id_storage_get (&esil->voyeur[R_ESIL_VOYEUR_MEM_WRITE], i); + voy->mem_write (voy->user, addr, o.ptr, buf, len); + } while (r_id_storage_get_next (&esil->voyeur[R_ESIL_VOYEUR_MEM_WRITE], &i)); + } free (o.ptr); return true; #else @@ -462,9 +471,7 @@ static bool internal_esil_reg_read(REsil *esil, const char *regname, ut64 *num, } if (num) { *num = r_reg_get_value (esil->anal->reg, ri); - if (esil->verbose) { - eprintf ("%s < %x\n", regname, (int)*num); - } + R_LOG_DEBUG ("%s < %x", regname, (int)*num); } r_unref (ri); return true; @@ -551,9 +558,14 @@ R_API char *r_esil_pop(REsil *esil) { } static int not_a_number(REsil *esil, const char *str) { +#if USE_NEW_ESIL + R_RETURN_VAL_IF_FAIL (esil && str && esil->reg_if.is_reg, R_ESIL_PARM_INVALID); + if (esil->reg_if.is_reg (esil->reg_if.reg, str)) { +#else RRegItem *ri = r_reg_get (esil->anal->reg, str, -1); if (ri) { r_unref (ri); +#endif return R_ESIL_PARM_REG; } return R_ESIL_PARM_INVALID; @@ -567,7 +579,7 @@ R_API int r_esil_get_parm_type(REsil *esil, const char *str) { if (r_str_startswith (str, "0x")) { return R_ESIL_PARM_NUM; } - if (!((isdigit(str[0])) || str[0] == '-')) { + if (!((isdigit (str[0])) || str[0] == '-')) { return not_a_number (esil, str); } size_t i; @@ -656,7 +668,7 @@ R_API bool r_esil_reg_read_nocallback(REsil *esil, const char *regname, ut64 *nu R_API bool r_esil_reg_read(REsil *esil, const char *regname, ut64 *val, ut32 *size) { #if USE_NEW_ESIL - R_RETURN_VAL_IF_FAIL (esil && regname && val, false); + R_RETURN_VAL_IF_FAIL (esil && regname, false); if (R_UNLIKELY (!r_esil_reg_read_silent (esil, regname, val, size))) { return false; } @@ -666,7 +678,9 @@ R_API bool r_esil_reg_read(REsil *esil, const char *regname, ut64 *val, ut32 *si } do { REsilVoyeur *voy = r_id_storage_get (&esil->voyeur[R_ESIL_VOYEUR_REG_READ], i); - voy->reg_read (voy->user, regname, *val); + if (val) { + voy->reg_read (voy->user, regname, *val); + } } while (r_id_storage_get_next (&esil->voyeur[R_ESIL_VOYEUR_REG_READ], &i)); return true; #else @@ -677,7 +691,7 @@ R_API bool r_esil_reg_read(REsil *esil, const char *regname, ut64 *val, ut32 *si val = &localnum; } *val = 0LL; - if (size) { + if (size && esil->anal && esil->anal->config) { *size = esil->anal->config->bits; } if (esil->cb.hook_reg_read) { @@ -691,14 +705,15 @@ R_API bool r_esil_reg_read(REsil *esil, const char *regname, ut64 *val, ut32 *si } R_API bool r_esil_reg_read_silent(REsil *esil, const char *name, ut64 *val, ut32 *size) { - R_RETURN_VAL_IF_FAIL (esil && esil->reg_if.reg_read && name && val, false); - if (!esil->reg_if.reg_read (esil->reg_if.reg, name, val)) { - return false; - } + R_RETURN_VAL_IF_FAIL (esil && esil->reg_if.reg_read && name, false); if (esil->reg_if.reg_size && size) { *size = esil->reg_if.reg_size (esil->reg_if.reg, name); } - return true; + if (!val) { + return esil->reg_if.is_reg (esil->reg_if.reg, name); + } + *val = 0; + return esil->reg_if.reg_read (esil->reg_if.reg, name, val); } R_API const char *r_esil_trapstr(int type) { @@ -853,9 +868,7 @@ static int eval_word(REsil *esil, const char *ostr, const char **str) { esil->parse_goto = -1; return 2; } - if (esil->verbose) { - R_LOG_ERROR ("Cannot find word %d", esil->parse_goto); - } + R_LOG_DEBUG ("Cannot find word %d", esil->parse_goto); return 1; } if (esil->parse_stop) { @@ -1105,7 +1118,9 @@ R_API bool r_esil_setup(REsil *esil, RAnal *anal, bool romem, bool stats, bool n esil->cb.mem_write = internal_esil_mem_write; } r_esil_mem_ro (esil, romem); +#if USE_NEW_ESIL == 0 r_esil_stats (esil, NULL, stats); +#endif r_esil_setup_ops (esil); // Try arch esil init cb first, then anal as fallback diff --git a/libr/esil/esil_ops.c b/libr/esil/esil_ops.c index 34e8fb74f27f3..a992e0ca404c6 100644 --- a/libr/esil/esil_ops.c +++ b/libr/esil/esil_ops.c @@ -1,4 +1,4 @@ -/* radare - LGPL - Copyright 2024 - pancake, condret */ +/* radare - LGPL - Copyright 2024-2025 - pancake, condret */ #include #include @@ -11,7 +11,6 @@ #include #include -#define IFDBG if (esil->verbose > 1) #define OP(v, w, x, y, z) r_esil_set_op (esil, v, w, x, y, z, NULL) #define OP2(v, w, x, y, z, i) r_esil_set_op (esil, v, w, x, y, z, i) #define OT_UNK R_ESIL_OP_TYPE_UNKNOWN @@ -23,6 +22,17 @@ #define OT_FLAG R_ESIL_OP_TYPE_FLAG #define OT_TRAP R_ESIL_OP_TYPE_TRAP +static bool isreg(REsil *esil, const char* name) { + return r_esil_reg_read (esil, name, NULL, NULL); +} +static ut64 reg_getv(REsil *esil, const char* name) { + ut64 v = UT64_MAX; + if (r_esil_reg_read (esil, name, &v, NULL)) { + return v; + } + return UT64_MAX; +} + R_IPI bool alignCheck(REsil *esil, ut64 addr); /// XXX R2_600 - must be internal imho @@ -63,14 +73,6 @@ R_API bool r_esil_mem_read(REsil *esil, ut64 addr, ut8 *buf, int len) { esil->trap_code = addr; } } - IFDBG { - size_t i; - eprintf ("0x%08" PFMT64x " R> ", addr); - for (i = 0; i < len; i++) { - eprintf ("%02x", buf[i]); - } - eprintf ("\n"); - } return ret; #endif } @@ -125,6 +127,7 @@ static bool popRN(REsil *esil, ut64 *n) { } static ut8 esil_internal_sizeof_reg(REsil *esil, const char *r) { +#if 0 R_RETURN_VAL_IF_FAIL (esil && esil->anal && esil->anal->reg && r, 0); RRegItem *ri = r_reg_get (esil->anal->reg, r, -1); if (ri) { @@ -132,6 +135,14 @@ static ut8 esil_internal_sizeof_reg(REsil *esil, const char *r) { r_unref (ri); return reg_size; } +#else + R_RETURN_VAL_IF_FAIL (esil && r, 0); + ut32 size = 0; + ut64 val = 0; // XXX esil_reg_read cant take val as null + if (r_esil_reg_read (esil, r, &val, &size)) { + return size; + } +#endif return 0; } @@ -334,7 +345,7 @@ static bool esil_js(REsil *esil) { } static bool esil_weak_eq(REsil *esil) { - R_RETURN_VAL_IF_FAIL (esil && esil->anal, false); + R_RETURN_VAL_IF_FAIL (esil, false); char *dst = r_esil_pop (esil); char *src = r_esil_pop (esil); @@ -371,6 +382,7 @@ static bool esil_eq(REsil *esil) { } bool is128reg = false; bool ispacked = false; +#if 0 RRegItem *ri = r_reg_get (esil->anal->reg, dst, -1); if (ri) { is128reg = ri->size == 128; @@ -379,6 +391,9 @@ static bool esil_eq(REsil *esil) { } else { R_LOG_DEBUG ("esil_eq: %s is not a register", dst); } +#else + // TODO: r_esil_reg can get regsize, but not the packed size +#endif if (is128reg && esil->stackptr > 0) { char *src2 = r_esil_pop (esil); // pop the higher 64bit value ut64 n0 = r_num_get (NULL, src); @@ -595,8 +610,8 @@ static bool esil_bits(REsil *esil) { if (popRN (esil, &s)) { if (esil->anal && esil->anal->coreb.setArchBits) { esil->anal->coreb.setArchBits (esil->anal->coreb.core, NULL, s); + return true; } - return true; } R_LOG_DEBUG ("esil_bits: missing parameters in stack"); return false; @@ -621,9 +636,11 @@ static bool esil_syscall(REsil *esil) { static bool esil_cmd(REsil *esil) { char *str = r_esil_pop (esil); if (str) { - if (esil->anal && esil->anal->coreb.setArchBits) { + if (esil->anal && esil->anal->coreb.core) { esil->anal->coreb.cmd (esil->anal->coreb.core, str); + return true; } + R_LOG_WARN ("Cannot run RCoreBind.cmd"); } return false; } @@ -657,20 +674,13 @@ static void pushnums(REsil *esil, const char *src, ut64 num2, const char *dst, u R_RETURN_IF_FAIL (esil); esil->old = num; esil->cur = num - num2; - RReg *reg = esil->anal->reg; - RRegItem *ri = r_reg_get (reg, dst, -1); - if (ri) { + if (isreg (esil, dst)) { esil->lastsz = esil_internal_sizeof_reg (esil, dst); - r_unref (ri); - return; - } - if (ri = r_reg_get (reg, src, -1), ri) { + } else if (isreg (esil, src)) { esil->lastsz = esil_internal_sizeof_reg (esil, src); - r_unref (ri); - return; + } else { + esil->lastsz = 64; } - // default size is set to 64 as internally operands are ut64 - esil->lastsz = 64; } // This function also sets internal vars which is used in flag calculations. @@ -703,7 +713,11 @@ static bool esil_regalias(REsil *esil) { ret = true; int kind = r_reg_alias_fromstring (dst); if (kind != -1) { - r_reg_alias_setname (esil->anal->reg, kind, src); + if (esil->anal) { + r_reg_alias_setname (esil->anal->reg, kind, src); + } else { + R_LOG_ERROR ("ESIL_ANAL_REQUIRED"); + } } } free (dst); @@ -879,9 +893,7 @@ static bool esil_lsreq(REsil *esil) { if (dst && r_esil_reg_read (esil, dst, &num, NULL)) { if (src && r_esil_get_parm (esil, src, &num2)) { if (num2 > 63) { - if (esil->verbose) { - R_LOG_WARN ("Invalid shift at 0x%08"PFMT64x, esil->addr); - } + R_LOG_DEBUG ("Invalid shift at 0x%08"PFMT64x, esil->addr); num2 = 63; } esil->old = num; @@ -932,23 +944,17 @@ static bool esil_asreq(REsil *esil) { ut64 left_bits = 0; int shift = regsize - 1; if (shift < 0 || shift > regsize - 1) { - if (esil->verbose) { - R_LOG_WARN ("Invalid asreq shift of %d at 0x%"PFMT64x, shift, esil->addr); - } + R_LOG_DEBUG ("Invalid asreq shift of %d at 0x%"PFMT64x, shift, esil->addr); shift = 0; } if (param_num > regsize - 1) { // capstone bug? - if (esil->verbose) { - R_LOG_WARN ("Invalid asreq shift of %"PFMT64d" at 0x%"PFMT64x, param_num, esil->addr); - } + R_LOG_DEBUG ("Invalid asreq shift of %"PFMT64d" at 0x%"PFMT64x, param_num, esil->addr); param_num = 30; } if (shift >= 63) { // LL can't handle LShift of 63 or more - if (esil->verbose) { - R_LOG_WARN ("Invalid asreq shift of %d at 0x%08"PFMT64x, shift, esil->addr); - } + R_LOG_DEBUG ("Invalid asreq shift of %d at 0x%08"PFMT64x, shift, esil->addr); } else if (op_num & (1LL << shift)) { left_bits = (1 << param_num) - 1; left_bits <<= regsize - param_num; @@ -965,9 +971,7 @@ static bool esil_asreq(REsil *esil) { // r_esil_pushnum (esil, res); ret = true; } else { - if (esil->verbose) { - R_LOG_WARN ("esil_asr: empty stack"); - } + R_LOG_DEBUG ("esil_asr: empty stack"); } } free (param); @@ -985,9 +989,7 @@ static bool esil_asr(REsil *esil) { if (param && r_esil_get_parm (esil, param, ¶m_num)) { if (param_num > regsize - 1) { // capstone bug? - if (esil->verbose) { - R_LOG_WARN ("Invalid asr shift of %"PFMT64d" at 0x%"PFMT64x, param_num, esil->addr); - } + R_LOG_DEBUG ("Invalid asr shift of %"PFMT64d" at 0x%"PFMT64x, param_num, esil->addr); param_num = 30; } bool isNegative; @@ -1543,6 +1545,8 @@ static bool esil_deceq(REsil *esil) { /* POKE */ static bool esil_poke_n(REsil *esil, int bits) { + R_RETURN_VAL_IF_FAIL (esil, false); + const bool be = (esil->anal)? R_ARCH_CONFIG_IS_BIG_ENDIAN (esil->anal->config): false; ut64 bitmask = r_num_genmask (bits - 1); ut64 num, addr; ut8 b[8] = {0}; @@ -1566,9 +1570,9 @@ static bool esil_poke_n(REsil *esil, int bits) { size_t last = strlen (reg); reg[last + 1] = 0; reg[last] = 'l'; - ut64 loow = r_reg_getv (esil->anal->reg, reg); + ut64 loow = reg_getv (esil, reg); // r_reg_getv (esil->anal->reg, reg); reg[last] = 'h'; - ut64 high = r_reg_getv (esil->anal->reg, reg); + ut64 high = reg_getv (esil, reg); // r_reg_getv (esil->anal->reg, reg); ret = r_esil_mem_write (esil, addr, (const ut8*)&loow, 8); ret = r_esil_mem_write (esil, addr + 8, (const ut8*)&high, 8); #if 0 @@ -1592,12 +1596,12 @@ static bool esil_poke_n(REsil *esil, int bits) { esil->cb.hook_mem_read = NULL; r_esil_mem_read (esil, addr, b, bytes); esil->cb.hook_mem_read = oldhook; - n = r_read_ble64 (b, R_ARCH_CONFIG_IS_BIG_ENDIAN (esil->anal->config)); + n = r_read_ble64 (b, be); esil->old = n; esil->cur = num; esil->lastsz = bits; num = num & bitmask; - r_write_ble (b, num, R_ARCH_CONFIG_IS_BIG_ENDIAN (esil->anal->config), bits); + r_write_ble (b, num, be, bits); ret = r_esil_mem_write (esil, addr, b, bytes); } } @@ -1639,6 +1643,8 @@ static bool esil_poke_some(REsil *esil) { char *count, *dst = r_esil_pop (esil); if (dst && r_esil_get_parm_size (esil, dst, &tmp, ®size)) { + // ESIL TODO - do not depend on esil->anal + const bool be = esil->anal? R_ARCH_CONFIG_IS_BIG_ENDIAN (esil->anal->config): false; // reg isregornum (esil, dst, &ptr); count = r_esil_pop (esil); @@ -1657,7 +1663,7 @@ static bool esil_poke_some(REsil *esil) { } r_esil_get_parm_size (esil, foo, &tmp, ®size); isregornum (esil, foo, &num64); - r_write_ble (b, num64, R_ARCH_CONFIG_IS_BIG_ENDIAN (esil->anal->config), regsize); + r_write_ble (b, num64, be, regsize); const int size_bytes = regsize / 8; const ut32 written = r_esil_mem_write (esil, ptr, b, size_bytes); if (written != size_bytes) { @@ -1683,7 +1689,7 @@ static bool esil_peek_n(REsil *esil, int bits) { if (bits & 7) { return false; } - bool be = R_ARCH_CONFIG_IS_BIG_ENDIAN (esil->anal->config); + bool be = (esil->anal)? R_ARCH_CONFIG_IS_BIG_ENDIAN (esil->anal->config): false; // XXX esil cant determine endian without anal bool ret = false; char res[SDB_NUM_BUFSZ]; ut64 addr; @@ -1761,6 +1767,7 @@ static bool esil_peek_some(REsil *esil) { isregornum (esil, dst, &ptr); count = r_esil_pop (esil); if (count) { + const bool be = esil->anal? R_ARCH_CONFIG_IS_BIG_ENDIAN (esil->anal->config): false; isregornum (esil, count, ®s); if (regs > 0) { ut8 a[4]; @@ -1774,14 +1781,12 @@ static bool esil_peek_some(REsil *esil) { } bool oks = r_esil_mem_read (esil, ptr, a, 4); if (!oks) { - if (esil->verbose) { - R_LOG_ERROR ("Cannot peek from 0x%08" PFMT64x, ptr); - } + R_LOG_DEBUG ("Cannot peek from 0x%08" PFMT64x, ptr); free (dst); free (count); return false; } - ut32 num32 = r_read_ble32 (a, R_ARCH_CONFIG_IS_BIG_ENDIAN (esil->anal->config)); + ut32 num32 = r_read_ble32 (a, be); r_esil_reg_write (esil, foo, num32); ptr += 4; free (foo); diff --git a/libr/esil/esil_stats.c b/libr/esil/esil_stats.c index 4dc47472e8c2b..6d4e206fdf89c 100644 --- a/libr/esil/esil_stats.c +++ b/libr/esil/esil_stats.c @@ -1,9 +1,7 @@ -/* radare - LGPL - Copyright 2014-2024 - pancake, condret */ +/* radare - LGPL - Copyright 2014-2025 - pancake, condret */ #include -static void esil_stats_old(REsil *esil, bool enable); - #if USE_NEW_ESIL static void stats_voyeur_reg_read (void *user, const char *name, ut64 val) { const char *key = (*name>='0' && *name<='9')? "num.load": "reg.read"; @@ -25,6 +23,8 @@ static void stats_voyeur_mem_write (void *user, ut64 addr, const ut8 *old, const static void stats_voyeur_op (void *user, const char *op) { sdb_array_add ((Sdb *)user, "ops.list", op, 0); } +#else +static void esil_stats_old(REsil *esil, bool enable); #endif R_API void r_esil_stats(REsil *esil, REsilStats *stats, bool enable) { @@ -65,6 +65,9 @@ R_API void r_esil_stats(REsil *esil, REsilStats *stats, bool enable) { #endif } +#if USE_NEW_ESIL +// +#else static bool hook_command(REsil *esil, const char *op) { sdb_array_add (esil->stats, "ops.list", op, 0); return false; @@ -90,6 +93,7 @@ static bool hook_reg_write(REsil *esil, const char *name, ut64 *val) { sdb_array_add (esil->stats, "reg.write", name, 0); return false; } +#endif static bool hook_NOP_mem_write(REsil *esil, ut64 addr, const ut8 *buf, int len) { eprintf ("NOP WRITE AT 0x%08"PFMT64x"\n", addr); @@ -104,6 +108,8 @@ R_API void r_esil_mem_ro(REsil *esil, bool mem_readonly) { } } +#if USE_NEW_ESIL +#else static void esil_stats_old(REsil *esil, bool enable) { if (enable) { if (esil->stats) { @@ -124,3 +130,4 @@ static void esil_stats_old(REsil *esil, bool enable) { esil->stats = NULL; } } +#endif diff --git a/libr/esil/esil_toc.c b/libr/esil/esil_toc.c index 0bbd0cbea14cc..49a05ca0810e7 100644 --- a/libr/esil/esil_toc.c +++ b/libr/esil/esil_toc.c @@ -1,6 +1,7 @@ -/* radare - LGPL - Copyright 2021-2024 - pancake */ +/* radare - LGPL - Copyright 2021-2025 - pancake */ #include +#include static bool esil2c_eq(REsil *esil) { REsilC *user = esil->user; @@ -184,7 +185,11 @@ static void esil2c_free(REsilC *user) { free (user); } +#if USE_NEW_ESIL +static bool esil2c_mw(void *null, ut64 addr, const ut8 *old, const ut8 *buf, int len) { +#else static bool esil2c_mw(REsil *esil, ut64 addr, const ut8 *buf, int len) { +#endif R_LOG_TODO ("poke%d 0x%08"PFMT64x" %d", len, addr, *buf); return true; } @@ -198,9 +203,13 @@ static void esil2c_setup(REsil *esil) { R_RETURN_IF_FAIL (esil); REsilC *user = R_NEW (REsilC); esil->user = user; - esil->verbose = true; // r_config_get_b (core->config, "esil.verbose"); +#if USE_NEW_ESIL + r_esil_add_voyeur (esil, NULL, esil2c_mw, R_ESIL_VOYEUR_MEM_WRITE); + r_esil_add_voyeur (esil, NULL, esil2c_mr, R_ESIL_VOYEUR_MEM_READ); +#else esil->cb.mem_read = esil2c_mr; esil->cb.mem_write = esil2c_mw; +#endif r_esil_set_op (esil, "=", esil2c_eq, 0, 2, R_ESIL_OP_TYPE_REG_WRITE, NULL); r_esil_set_op (esil, ":=", esil2c_eq, 0, 2, R_ESIL_OP_TYPE_REG_WRITE, NULL); r_esil_set_op (esil, "-", esil2c_sub, 1, 2, R_ESIL_OP_TYPE_REG_WRITE, NULL); @@ -222,8 +231,13 @@ static void esil2c_setup(REsil *esil) { R_API REsilC *r_esil_toc_new(struct r_anal_t *anal, const int bits) { REsilC *ec = R_NEW0 (REsilC); if (ec) { +#if USE_NEW_ESIL + REsil *esil = r_esil_new_simple (bits, anal->reg, &anal->iob); + esil->anal = anal; +#else int ss = 16 * 1024; REsil *esil = r_esil_new (ss, 0, bits); +#endif if (esil) { esil2c_setup (esil); ec->anal = anal; diff --git a/libr/esil/esil_trace.c b/libr/esil/esil_trace.c index 48aa6b64b1e67..f23de7873051f 100644 --- a/libr/esil/esil_trace.c +++ b/libr/esil/esil_trace.c @@ -339,11 +339,8 @@ R_API void r_esil_trace_op(REsil *esil, struct r_anal_op_t *op) { esil->cb.hook_mem_read = trace_hook_mem_read; esil->cb.hook_mem_write = trace_hook_mem_write; /* evaluate esil expression */ - const int esil_verbose = esil->verbose; - esil->verbose = 0; // disable verbose logs when tracing r_esil_parse (esil, expr); r_esil_stack_free (esil); - esil->verbose = esil_verbose; /* restore hooks */ esil->cb = esil->ocb; esil->ocb_set = false; diff --git a/libr/include/r_core.h b/libr/include/r_core.h index 9ef28704a549a..d2b3f73e4f50c 100644 --- a/libr/include/r_core.h +++ b/libr/include/r_core.h @@ -307,12 +307,16 @@ typedef struct { int y; } VisualMark; +typedef struct r_core_esil_stepback_t { + char *expr; + ut64 addr; +} RCoreEsilStepBack; + typedef struct r_core_esil_t { REsil esil; - union { - RStrBuf trap_revert; - ut64 old_pc; - }; + RList stepback; + RStrBuf trap_revert; + ut64 old_pc; ut32 tr_reg; ut32 tr_mem; RReg *reg; @@ -324,6 +328,7 @@ typedef struct r_core_esil_t { char *cmd_todo; // command to run when esil expr contains TODO char *cmd_ioer; // command to run when esil fails to IO char *mdev_range; // string containing the r_str_range to match for read/write accesses + ut32 max_stepback; ut8 cfg; } RCoreEsil; @@ -359,7 +364,7 @@ struct r_core_t { RList/**/ *cmd_descriptors; RAnal *anal; RAsm *rasm; - RCoreEsil esil; + RCoreEsil cesil; /* ^^ */ RCoreTimes *times; // RParse *parser; @@ -789,7 +794,10 @@ R_API bool r_core_esil_init(RCore *core); R_API void r_core_esil_fini(RCoreEsil *cesil); R_API void r_core_esil_load_arch(RCore *core); R_API void r_core_esil_unload_arch(RCore *core); -R_API void r_core_esil_single_step(RCore *core); +R_API bool r_core_esil_run_expr_at(RCore *core, const char *expr, ut64 addr); +R_API bool r_core_esil_single_step(RCore *core); +R_API void r_core_esil_stepback(RCore *core); //replacement for r_core_esil_step_back; rename later +R_API void r_core_esil_set_max_stepback(RCore *core, ut32 max_stepback); // both do the same, we should get rid of one of them R_API bool r_core_bin_raise(RCore *core, ut32 bfid); diff --git a/libr/include/r_esil.h b/libr/include/r_esil.h index 60cbec23167c1..23b29ef6b555d 100644 --- a/libr/include/r_esil.h +++ b/libr/include/r_esil.h @@ -1,4 +1,4 @@ -/* radare2 - LGPL - Copyright 2022-2024 - pancake */ +/* radare2 - LGPL - Copyright 2022-2025 - pancake */ #ifndef R_ESIL_H #define R_ESIL_H @@ -14,7 +14,7 @@ extern "C" { #endif -#define USE_NEW_ESIL 0 +#define USE_NEW_ESIL 1 #define esilprintf(op, fmt, ...) r_strbuf_setf (&op->esil, fmt, ##__VA_ARGS__) // only flags that affect control flow @@ -220,7 +220,6 @@ typedef struct r_esil_t { int parse_stop; int parse_goto; int parse_goto_count; - int verbose; ut64 flags; ut64 addr; ut64 stack_addr; @@ -252,9 +251,9 @@ typedef struct r_esil_t { REsilRegInterface reg_if; REsilMemInterface mem_if; RIDStorage voyeur[R_ESIL_VOYEUR_LAST]; - REsilCallbacks cb; - REsilCallbacks ocb; - bool ocb_set; + REsilCallbacks cb; // DEPRECATED + REsilCallbacks ocb; // DEPRECATED + bool ocb_set; // DEPRECATED // this is so cursed, can we please remove external commands from esil internals. // Function pointers are fine, but not commands char *cmd_step; // r2 (external) command to run before a step is performed @@ -266,7 +265,7 @@ typedef struct r_esil_t { char *cmd_ioer; // r2 (external) command to run when esil fails to IO char *mdev_range; // string containing the r_str_range to match for read/write accesses bool (*cmd)(ESIL *esil, const char *name, ut64 a0, ut64 a1); - void *user; + void *user; // RCore * int stack_fd; // ahem, let's not do this bool in_cmd_step; #if 0 diff --git a/test/unit/test_esil_dfg_filter.c b/test/unit/test_esil_dfg_filter.c index 0cb3fb7820412..8e7f0772a3cc1 100644 --- a/test/unit/test_esil_dfg_filter.c +++ b/test/unit/test_esil_dfg_filter.c @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include "minunit.h" @@ -8,7 +10,13 @@ bool test_filter_regs(void) { r_anal_use (anal, "x86"); r_anal_set_bits (anal, 32); r_anal_set_reg_profile (anal, NULL); +#if USE_NEW_ESIL + RIO *io = r_io_new (); + r_io_bind (io, &anal->iob); + REsil *esil = r_esil_new_simple (1, anal->reg, &anal->iob); +#else REsil *esil = r_esil_new (4096, 0, 1); +#endif esil->anal = anal; // create expected results @@ -43,6 +51,9 @@ bool test_filter_regs(void) { r_anal_esil_dfg_free (dfg); r_esil_free (esil); r_anal_free (anal); +#if USE_NEW_ESIL + r_io_free (io); +#endif mu_assert ("filtering for ax failed", ax == filtered_ax); mu_assert ("filtering for ah failed", ah == filtered_ah); @@ -56,6 +67,11 @@ bool test_lemon_const_folder(void) { r_anal_set_bits (anal, 32); r_anal_set_reg_profile (anal, NULL); +#if USE_NEW_ESIL + RIO *io = r_io_new (); + r_io_bind (io, &anal->iob); +#endif + RAnalEsilDFG *dfg = r_anal_esil_dfg_expr (anal, NULL, "4,!,3,ebx,:=,!,1,+,eax,:=", false, false); r_anal_esil_dfg_fold_const (anal, dfg); RStrBuf *filtered = r_anal_esil_dfg_filter (dfg, "eax"); @@ -63,6 +79,9 @@ bool test_lemon_const_folder(void) { r_strbuf_free (filtered); r_anal_esil_dfg_free (dfg); r_anal_free (anal); +#if USE_NEW_ESIL + r_io_free (io); +#endif mu_assert_true (cmp_result, "esil dfg const folding is broken"); mu_end;