radare2/libr/anal/xrefs.c

667 lines
17 KiB
C
Raw Normal View History

/* radare - LGPL - Copyright 2009-2023 - pancake, nibble, defragger, ret2libc */
// R2R db/cmd/cmd_aflxj db/cmd/cmd_aflxv db/cmd/cmd_ax
#include <r_anal.h>
2016-12-05 17:46:45 +00:00
#include <r_cons.h>
#include <r_vec.h>
#include <sdb/ht_up.h>
#include <sdb/ht_uu.h>
2023-07-18 09:03:44 +00:00
R_VEC_TYPE (RVecAnalRef, RAnalRef);
2018-02-09 21:25:30 +00:00
// xrefs are stored as an adjacency list (in both directions),
// as a hastable mapping at (from) to hashtables mapping addr (at) to a ref type.
typedef HtUU Edges;
typedef HtUP AdjacencyList;
// NOTE: this is heavy in memory usage, but needed due to performance reasons for large amounts of xrefs..
typedef struct r_ref_manager_t {
AdjacencyList *refs; // forward refs
AdjacencyList *xrefs; // backward refs
} RefManager;
static inline int compare_ref(const RAnalRef *a, const RAnalRef *b) {
if (a->at < b->at) {
return -1;
}
if (a->at > b->at) {
return 1;
}
if (a->addr < b->addr) {
return -1;
}
if (a->addr > b->addr) {
return 1;
}
return 0;
}
static void adjacency_list_free(HtUPKv *kv) {
ht_uu_free (kv->value);
}
static RefManager *ref_manager_new(void) {
RefManager *rm = R_NEW0 (RefManager);
if (R_LIKELY (rm)) {
rm->refs = ht_up_new (NULL, adjacency_list_free, NULL);
if (R_UNLIKELY (!rm->refs)) {
free (rm);
return NULL;
2017-12-14 11:40:31 +00:00
}
rm->xrefs = ht_up_new (NULL, adjacency_list_free, NULL);
if (R_UNLIKELY (!rm->xrefs)) {
free (rm->refs);
free (rm);
return NULL;
}
}
return rm;
}
static void ref_manager_free(RefManager *rm) {
if (R_LIKELY (rm)) {
ht_up_free (rm->refs);
ht_up_free (rm->xrefs);
}
free (rm);
}
static void _add_ref(AdjacencyList *adj_list, ut64 from, ut64 to, RAnalRefType type) {
2018-02-09 21:25:30 +00:00
bool found;
Edges *edges = ht_up_find (adj_list, from, &found);
2018-02-09 21:25:30 +00:00
if (!found) {
// optionally add a hashtable if missing
edges = ht_uu_new0 ();
if (!edges) {
R_LOG_WARN ("failed to create hashtable for xrefs");
2018-02-09 21:25:30 +00:00
return;
}
ht_up_insert (adj_list, from, edges); // adds the new (empty) hashtable
}
ht_uu_update (edges, to, (ut64) type); // and adds the ref
}
static void ref_manager_add_entry(RefManager *rm, ut64 from, ut64 to, RAnalRefType type) {
_add_ref (rm->refs, from, to, type);
_add_ref (rm->xrefs, to, from, type);
}
static void _delete_ref(AdjacencyList *adj_list, ut64 from, ut64 to) {
bool found;
Edges *edges = ht_up_find (adj_list, from, &found);
if (found && edges) {
if (edges->count == 1) {
// ht_uu_delete (edges, to); // delete the reference
ht_up_delete (adj_list, from); // delete rest of hashtable
} else {
ht_uu_delete (edges, to); // delete only a reference
}
}
}
// TODO add extra R_API call for deleting all refs, can be implemented in a more performant way
static void ref_manager_remove_entry(RefManager *rm, ut64 from, ut64 to) {
_delete_ref (rm->refs, from, to);
_delete_ref (rm->xrefs, to, from);
}
static inline bool count_edges(void *user, const ut64 k, const void *v) {
(*(ut64 *)user) += ((HtUU*) v)->count;
return true;
}
static ut64 ref_manager_count_xrefs(RefManager *rm) {
r_return_val_if_fail (rm, 0);
ut64 count = 0;
ht_up_foreach (rm->xrefs, count_edges, &count);
return count;
}
typedef struct r_collect_refs_state_t {
RVecAnalRef *result;
bool success;
} CollectRefsState;
typedef struct r_collect_ref_state_t {
CollectRefsState refs_state;
ut64 at;
} CollectRefState;
static inline bool _collect_ref_cb(void *user, const ut64 k, const ut64 v) {
CollectRefState *ref_state = user;
RAnalRef *ref = RVecAnalRef_emplace_back (ref_state->refs_state.result);
if (R_UNLIKELY (!ref)) {
ref_state->refs_state.success = false;
return false;
}
ref->at = ref_state->at;
ref->addr = k;
ref->type = (RAnalRefType) v;
return true;
}
static inline bool _collect_refs_cb(void *user, const ut64 k, const void *v) {
CollectRefState ref_state = {
.refs_state = *((CollectRefsState*) user),
.at = k
};
Edges *edges = (Edges*) v;
ht_uu_foreach (edges, _collect_ref_cb, &ref_state);
return ref_state.refs_state.success;
}
static RVecAnalRef *_collect_all_refs(RefManager *rm, const AdjacencyList *adj_list) {
RVecAnalRef *result = RVecAnalRef_new ();
if (R_UNLIKELY (!result)) {
return NULL;
}
const ut64 length = ref_manager_count_xrefs (rm);
if (!RVecAnalRef_reserve (result, length)) {
RVecAnalRef_free (result, NULL, NULL);
return NULL;
}
CollectRefsState state = { .result = result, .success = true };
ht_up_foreach ((AdjacencyList*) adj_list, _collect_refs_cb, &state);
if (!state.success) {
RVecAnalRef_free (result, NULL, NULL);
}
return result;
}
static RVecAnalRef *_collect_refs_from(const AdjacencyList *adj_list, ut64 from) {
// only finds entries with matching "from"
RVecAnalRef *result = RVecAnalRef_new ();
if (R_UNLIKELY (!result)) {
return NULL;
}
bool found;
Edges *edges = ht_up_find ((AdjacencyList*)adj_list, from, &found);
if (!found) {
RVecAnalRef_free (result, NULL, NULL);
return NULL;
}
CollectRefState state = { .refs_state = { .result = result, .success = true }, .at = from };
ht_uu_foreach (edges, _collect_ref_cb, &state);
if (!state.refs_state.success) {
RVecAnalRef_free (result, NULL, NULL);
return NULL;
}
return result;
}
static RVecAnalRef *_collect_refs(RefManager *rm, const AdjacencyList *adj_list, ut64 addr) {
return addr == UT64_MAX
? _collect_all_refs (rm, adj_list)
: _collect_refs_from (adj_list, addr);
}
static inline RVecAnalRef *ref_manager_get_refs(RefManager *rm, ut64 from) {
r_return_val_if_fail (rm, NULL);
return _collect_refs (rm, rm->refs, from);
}
static inline RVecAnalRef *ref_manager_get_xrefs(RefManager *rm, ut64 to) {
r_return_val_if_fail (rm, NULL);
return _collect_refs (rm, rm->xrefs, to);
}
R_API bool r_anal_xrefs_init(RAnal *anal) {
r_return_val_if_fail (anal, false);
r_anal_xrefs_free (anal);
anal->rm = ref_manager_new ();
return !!anal->rm;
}
R_API void r_anal_xrefs_free(RAnal *anal) {
r_return_if_fail (anal);
ref_manager_free (anal->rm);
}
2018-02-09 21:25:30 +00:00
// set a reference from FROM to TO and a cross-reference(xref) from TO to FROM.
R_API bool r_anal_xrefs_set(RAnal *anal, ut64 from, ut64 to, const RAnalRefType _type) {
r_return_val_if_fail (anal && anal->rm, false);
if (from == to || from == UT64_MAX || to == UT64_MAX) {
2015-09-14 00:08:31 +00:00
return false;
}
if (anal->iob.is_valid_offset) {
if (!anal->iob.is_valid_offset (anal->iob.io, from, 0)) {
return false;
}
if (!anal->iob.is_valid_offset (anal->iob.io, to, 0)) {
return false;
}
2014-10-20 23:02:25 +00:00
}
RAnalRefType type = _type;
if (!R_ANAL_REF_TYPE_PERM (type)) {
// type |= R_ANAL_REF_TYPE_READ;
switch (R_ANAL_REF_TYPE_MASK (type)) {
case R_ANAL_REF_TYPE_CODE:
case R_ANAL_REF_TYPE_CALL:
case R_ANAL_REF_TYPE_JUMP:
type |= R_ANAL_REF_TYPE_EXEC;
break;
default:
type |= R_ANAL_REF_TYPE_READ;
break;
}
}
ref_manager_add_entry (anal->rm, from, to, type);
R_DIRTY (anal);
2015-09-14 00:08:31 +00:00
return true;
}
// R2_590 deprecate, does same as r_anal_xref_del?
R_API bool r_anal_xrefs_deln(RAnal *anal, ut64 from, ut64 to, const RAnalRefType type) {
r_return_val_if_fail (anal && anal->rm, false);
ref_manager_remove_entry (anal->rm, from, to);
R_DIRTY (anal);
2015-09-14 00:08:31 +00:00
return true;
}
R_API bool r_anal_xref_del(RAnal *anal, ut64 from, ut64 to) {
r_return_val_if_fail (anal, false);
ref_manager_remove_entry (anal->rm, from, to);
R_DIRTY (anal);
2017-12-14 11:40:31 +00:00
return true;
2017-12-14 10:24:46 +00:00
}
R_API RVecAnalRef *r_anal_refs_get(RAnal *anal, ut64 from) {
r_return_val_if_fail (anal && anal->rm, NULL);
RVecAnalRef *anal_refs = ref_manager_get_refs (anal->rm, from);
if (!anal_refs || RVecAnalRef_empty (anal_refs)) {
RVecAnalRef_free (anal_refs, NULL, NULL);
return NULL;
}
RVecAnalRef_sort (anal_refs, compare_ref); // XXX not needed?
return anal_refs;
}
R_API RVecAnalRef *r_anal_xrefs_get(RAnal *anal, ut64 to) {
r_return_val_if_fail (anal && anal->rm, NULL);
RVecAnalRef *anal_refs = ref_manager_get_xrefs(anal->rm, to);
if (!anal_refs || RVecAnalRef_empty (anal_refs)) {
RVecAnalRef_free (anal_refs, NULL, NULL);
2017-02-20 11:15:58 +00:00
return NULL;
}
RVecAnalRef_sort (anal_refs, compare_ref); // XXX not needed?
return anal_refs;
2017-02-20 11:15:58 +00:00
}
R_API RVecAnalRef *r_anal_xrefs_get_from(RAnal *anal, ut64 to) {
r_return_val_if_fail (anal && anal->rm, NULL);
RVecAnalRef *anal_refs = ref_manager_get_refs (anal->rm, to);
if (!anal_refs || RVecAnalRef_empty (anal_refs)) {
RVecAnalRef_free (anal_refs, NULL, NULL);
return NULL;
}
RVecAnalRef_sort (anal_refs, compare_ref); // XXX not needed?
return anal_refs;
}
static void r_anal_xrefs_list_table(RAnal *anal, RVecAnalRef *anal_refs, const char *arg) {
RTable *table = r_table_new ("xrefs");
r_table_set_columnsf (table, "ddssss", "from", "to", "type", "perm", "fromname", "toname");
RAnalRef *ref;
R_VEC_FOREACH (anal_refs, ref) {
int t = R_ANAL_REF_TYPE_MASK (ref->type);
if (!t) {
t = ' ';
}
char *fromname = anal->coreb.getNameDelta (anal->coreb.core, ref->addr);
char *toname = anal->coreb.getNameDelta (anal->coreb.core, ref->at);
r_table_add_rowf (table, "ddssss",
ref->at, ref->addr,
r_anal_ref_type_tostring (t),
r_anal_ref_perm_tostring (ref),
toname, fromname
);
free (fromname);
free (toname);
}
bool show_table = true;
if (R_STR_ISNOTEMPTY (arg)) {
show_table = r_table_query (table, arg);
}
if (show_table) {
char *s = r_table_tofancystring (table);
r_cons_println (s);
free (s);
}
r_table_free (table);
}
static void r_anal_xrefs_list_json(RAnal *anal, RVecAnalRef *anal_refs) {
PJ *pj = anal->coreb.pjWithEncoding (anal->coreb.core);
if (!pj) {
return;
}
pj_a (pj);
2018-02-09 21:25:30 +00:00
RAnalRef *ref;
R_VEC_FOREACH (anal_refs, ref) {
int t = R_ANAL_REF_TYPE_MASK (ref->type);
if (!t) {
t = ' ';
}
pj_o (pj);
char *name = anal->coreb.getNameDelta (anal->coreb.core, ref->at);
if (name) {
r_str_replace_ch (name, ' ', 0, true);
pj_ks (pj, "name", name);
free (name);
}
pj_kn (pj, "from", ref->at);
pj_ks (pj, "type", r_anal_ref_type_tostring (t));
pj_ks (pj, "perm", r_anal_ref_perm_tostring (ref));
pj_kn (pj, "addr", ref->addr);
name = anal->coreb.getNameDelta (anal->coreb.core, ref->addr);
if (name) {
r_str_replace_ch (name, ' ', 0, true);
pj_ks (pj, "refname", name);
free (name);
}
pj_end (pj);
}
pj_end (pj);
anal->cb_printf ("%s\n", pj_string (pj));
pj_free (pj);
}
static void r_anal_xrefs_list_hex(RAnal *anal, RVecAnalRef *anal_refs) {
RAnalRef *ref;
R_VEC_FOREACH (anal_refs, ref) {
int t = R_ANAL_REF_TYPE_MASK (ref->type);
if (!t) {
t = ' ';
}
// TODO: export/import the read-write-exec information
anal->cb_printf ("ax%c 0x%"PFMT64x" 0x%"PFMT64x"\n", t, ref->addr, ref->at);
}
}
static void r_anal_xrefs_list_mapping(RAnal *anal, RVecAnalRef *anal_refs) {
RAnalRef *ref;
R_VEC_FOREACH (anal_refs, ref) {
2023-07-17 21:23:25 +00:00
RAnalRefType t = R_ANAL_REF_TYPE_MASK (ref->type);
if (!t) {
t = ' ';
}
anal->cb_printf ("0x%08"PFMT64x" -> 0x%08"PFMT64x" %s:%s\n", ref->at, ref->addr,
r_anal_ref_type_tostring (t), r_anal_ref_perm_tostring (ref));
}
}
static void r_anal_xrefs_list_plaintext(RAnal *anal, RVecAnalRef *anal_refs) {
RAnalRef *ref;
R_VEC_FOREACH (anal_refs, ref) {
int t = R_ANAL_REF_TYPE_MASK (ref->type);
if (!t) {
t = ' ';
}
char *name = anal->coreb.getNameDelta (anal->coreb.core, ref->at);
if (name) {
r_str_replace_ch (name, ' ', 0, true);
anal->cb_printf ("%40s", name);
free (name);
} else {
anal->cb_printf ("%40s", "?");
}
anal->cb_printf (" 0x%"PFMT64x" > %4s:%s > 0x%"PFMT64x, ref->at,
r_anal_ref_type_tostring (t), r_anal_ref_perm_tostring (ref), ref->addr);
name = anal->coreb.getNameDelta (anal->coreb.core, ref->addr);
if (name) {
r_str_replace_ch (name, ' ', 0, true);
anal->cb_printf (" %s\n", name);
free (name);
} else {
anal->cb_printf ("\n");
}
}
}
R_API void r_anal_xrefs_list(RAnal *anal, int rad, const char *arg) {
r_return_if_fail (anal && anal->rm);
RVecAnalRef *anal_refs = ref_manager_get_refs (anal->rm, UT64_MAX);
if (!anal_refs) {
R_LOG_DEBUG ("Could not list xrefs");
return;
}
RVecAnalRef_sort (anal_refs, compare_ref); // XXX not needed?
switch (rad) {
case ',':
r_anal_xrefs_list_table (anal, anal_refs, arg);
break;
case 'j':
r_anal_xrefs_list_json (anal, anal_refs);
break;
case '*':
r_anal_xrefs_list_hex (anal, anal_refs);
break;
case 'q':
r_anal_xrefs_list_mapping (anal, anal_refs);
break;
case '\0':
r_anal_xrefs_list_plaintext (anal, anal_refs);
default:
R_LOG_DEBUG ("Unsupported xrefs list format: %c", rad);
break;
}
RVecAnalRef_free (anal_refs, NULL, NULL);
}
R_API ut64 r_anal_xrefs_count(RAnal *anal) {
r_return_val_if_fail (anal && anal->rm, 0);
return ref_manager_count_xrefs (anal->rm);
}
R_API RVecAnalRef *r_anal_function_get_xrefs(RAnalFunction *fcn) {
r_return_val_if_fail (fcn, NULL);
RefManager *rm = fcn->anal->rm;
// XXX assume first basic block is the entrypoint
RVecAnalRef *anal_refs = ref_manager_get_xrefs (rm, fcn->addr);
if (anal_refs) {
RVecAnalRef_sort (anal_refs, compare_ref); // XXX not needed?
}
return anal_refs;
}
typedef RVecAnalRef *(*CollectFn)(RefManager *rm, ut64 addr);
static RVecAnalRef *fcn_get_all_refs(RAnalFunction *fcn, RefManager *rm, CollectFn collect_refs) {
RVecAnalRef *anal_refs = RVecAnalRef_new ();
if (R_LIKELY (anal_refs)) {
RListIter *iter;
RAnalBlock *bb;
r_list_foreach (fcn->bbs, iter, bb) {
int i;
for (i = 0; i < bb->ninstr; i++) {
ut64 instr_addr = bb->addr + r_anal_bb_offset_inst (bb, i);
RVecAnalRef *refs = collect_refs (rm, instr_addr);
if (!refs) {
continue;
}
RVecAnalRef_append (anal_refs, refs);
RVecAnalRef_free (refs, NULL, NULL);
}
}
RVecAnalRef_sort (anal_refs, compare_ref);
}
return anal_refs;
}
// XXX rename to r_anal_function_get_all_refs?
R_API RVecAnalRef *r_anal_function_get_refs(RAnalFunction *fcn) {
r_return_val_if_fail (fcn, NULL);
return fcn_get_all_refs (fcn, fcn->anal->rm, ref_manager_get_refs);
}
R_API RVecAnalRef *r_anal_function_get_all_xrefs(RAnalFunction *fcn) {
r_return_val_if_fail (fcn, NULL);
return fcn_get_all_refs (fcn, fcn->anal->rm, ref_manager_get_xrefs);
}
R_API char r_anal_ref_perm_tochar(RAnalRef *ref) {
if (ref->type & R_ANAL_REF_TYPE_WRITE) {
return 'w';
}
if (ref->type & R_ANAL_REF_TYPE_READ) {
return 'r';
}
if (ref->type & R_ANAL_REF_TYPE_EXEC) {
return 'x';
}
switch (R_ANAL_REF_TYPE_MASK (ref->type)) {
case R_ANAL_REF_TYPE_CODE:
case R_ANAL_REF_TYPE_CALL:
case R_ANAL_REF_TYPE_JUMP:
return 'x';
}
return '-';
}
R_API const char *r_anal_ref_perm_tostring(RAnalRef *ref) {
int perm = R_ANAL_REF_TYPE_PERM (ref->type);
if (!perm) {
switch (R_ANAL_REF_TYPE_MASK (ref->type)) {
case R_ANAL_REF_TYPE_CODE:
case R_ANAL_REF_TYPE_CALL:
case R_ANAL_REF_TYPE_JUMP:
perm = R_ANAL_REF_TYPE_EXEC;
break;
}
}
return r_str_rwx_i (perm);
}
R_API const char *r_anal_ref_type_tostring(RAnalRefType type) {
switch (R_ANAL_REF_TYPE_MASK (type)) {
case ' ':
case R_ANAL_REF_TYPE_NULL:
return "NULL";
case R_ANAL_REF_TYPE_ICOD:
return "ICOD";
case R_ANAL_REF_TYPE_CODE:
return "CODE";
case R_ANAL_REF_TYPE_CALL:
return "CALL";
case R_ANAL_REF_TYPE_JUMP:
return "JUMP";
case R_ANAL_REF_TYPE_DATA:
return "DATA";
case R_ANAL_REF_TYPE_STRN:
return "STRN";
2014-09-24 11:03:03 +00:00
default:
// R_LOG_ERROR("Invalid unknown ref type %c", R_ANAL_REF_TYPE_MASK (type));
return "UNKN";
}
}
R_API RAnalRefType r_anal_xrefs_type_from_string(const char *s) {
RAnalRefType rt = R_ANAL_REF_TYPE_NULL;
if (strchr (s, 'r')) {
rt |= (R_ANAL_REF_TYPE_DATA | R_ANAL_REF_TYPE_READ);
}
if (strchr (s, 'w')) {
rt |= (R_ANAL_REF_TYPE_DATA | R_ANAL_REF_TYPE_WRITE);
}
if (strchr (s, 'x')) {
rt |= R_ANAL_REF_TYPE_EXEC;
}
if (strchr (s, 'c')) {
rt |= R_ANAL_REF_TYPE_CODE;
}
if (strchr (s, 'C')) {
rt |= R_ANAL_REF_TYPE_CALL;
}
if (strchr (s, 'j')) {
rt |= R_ANAL_REF_TYPE_JUMP;
}
if (strchr (s, 'd')) {
rt |= R_ANAL_REF_TYPE_DATA;
}
if (strchr (s, 's')) {
rt |= R_ANAL_REF_TYPE_STRN;
}
return rt;
}
R_API int r_anal_ref_typemask(int x) {
const int maskedType = x & 0xff;
switch (maskedType) {
case R_ANAL_REF_TYPE_NULL:
case R_ANAL_REF_TYPE_CODE | R_ANAL_REF_TYPE_DATA: // 'g' // XXX R2_590 - this is a conflictive type
case R_ANAL_REF_TYPE_CODE: // 'c' // code ref
case R_ANAL_REF_TYPE_CALL: // 'C' // code ref (call)
case R_ANAL_REF_TYPE_JUMP: // 'j' // code ref (call)
case R_ANAL_REF_TYPE_DATA: // 'd' // mem ref
case R_ANAL_REF_TYPE_STRN: // 's' // string ref
case R_ANAL_REF_TYPE_ICOD: // 'i' // indirect cod reference
return maskedType;
case ' ':
return R_ANAL_REF_TYPE_NULL;
}
R_LOG_ERROR ("Invalid reftype mask '%c' (0x%02x)", x, x);
// SHOULD NEVER HAPPEN MAYBE WARN HERE
return 0;
}
// TODO: deprecate
2018-02-09 21:25:30 +00:00
R_API RAnalRefType r_anal_xrefs_type(char ch) {
switch (ch) {
case R_ANAL_REF_TYPE_CODE:
case R_ANAL_REF_TYPE_CALL:
case R_ANAL_REF_TYPE_DATA:
case R_ANAL_REF_TYPE_STRN:
case R_ANAL_REF_TYPE_ICOD:
2018-02-09 21:25:30 +00:00
case R_ANAL_REF_TYPE_NULL:
return (RAnalRefType)ch;
default:
return R_ANAL_REF_TYPE_NULL;
}
}