2023-06-13 10:03:16 +00:00
|
|
|
/* radare - LGPL - Copyright 2009-2023 - pancake, nibble, defragger, ret2libc */
|
2013-04-18 01:58:44 +00:00
|
|
|
|
2023-07-07 14:01:48 +00:00
|
|
|
// R2R db/cmd/cmd_aflxj db/cmd/cmd_aflxv db/cmd/cmd_ax
|
|
|
|
|
2013-04-18 01:58:44 +00:00
|
|
|
#include <r_anal.h>
|
2016-12-05 17:46:45 +00:00
|
|
|
#include <r_cons.h>
|
2023-07-07 14:01:48 +00:00
|
|
|
#include <r_vec.h>
|
|
|
|
#include <sdb/ht_up.h>
|
|
|
|
#include <sdb/ht_uu.h>
|
2013-04-18 01:58:44 +00:00
|
|
|
|
2023-07-18 09:03:44 +00:00
|
|
|
R_VEC_TYPE (RVecAnalRef, RAnalRef);
|
2018-02-09 21:25:30 +00:00
|
|
|
|
2023-07-07 14:01:48 +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;
|
2017-12-11 17:00:14 +00:00
|
|
|
|
2023-07-07 14:01:48 +00:00
|
|
|
// 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;
|
2017-12-11 17:00:14 +00:00
|
|
|
|
2023-07-07 14:01:48 +00:00
|
|
|
static inline int compare_ref(const RAnalRef *a, const RAnalRef *b) {
|
2018-05-16 08:18:12 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-07-07 14:01:48 +00:00
|
|
|
static void adjacency_list_free(HtUPKv *kv) {
|
|
|
|
ht_uu_free (kv->value);
|
2019-07-06 14:34:31 +00:00
|
|
|
}
|
|
|
|
|
2023-07-07 14:01:48 +00:00
|
|
|
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
|
|
|
}
|
2023-07-07 14:01:48 +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);
|
2017-12-11 17:00:14 +00:00
|
|
|
}
|
2023-07-07 14:01:48 +00:00
|
|
|
free (rm);
|
2017-12-11 17:00:14 +00:00
|
|
|
}
|
|
|
|
|
2023-07-07 14:01:48 +00:00
|
|
|
static void _add_ref(AdjacencyList *adj_list, ut64 from, ut64 to, RAnalRefType type) {
|
2018-02-09 21:25:30 +00:00
|
|
|
bool found;
|
2023-07-07 14:01:48 +00:00
|
|
|
Edges *edges = ht_up_find (adj_list, from, &found);
|
2018-02-09 21:25:30 +00:00
|
|
|
if (!found) {
|
2023-07-07 14:01:48 +00:00
|
|
|
// 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;
|
2017-12-11 17:00:14 +00:00
|
|
|
}
|
2023-07-07 14:01:48 +00:00
|
|
|
|
|
|
|
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);
|
2017-12-11 17:00:14 +00:00
|
|
|
}
|
2023-07-07 14:01:48 +00:00
|
|
|
|
|
|
|
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;
|
2018-09-21 22:11:44 +00:00
|
|
|
}
|
2023-07-07 14:01:48 +00:00
|
|
|
|
|
|
|
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);
|
2017-12-11 17:00:14 +00:00
|
|
|
}
|
|
|
|
|
2018-02-09 21:25:30 +00:00
|
|
|
// set a reference from FROM to TO and a cross-reference(xref) from TO to FROM.
|
2022-04-25 20:19:23 +00:00
|
|
|
R_API bool r_anal_xrefs_set(RAnal *anal, ut64 from, ut64 to, const RAnalRefType _type) {
|
2023-07-07 14:01:48 +00:00
|
|
|
r_return_val_if_fail (anal && anal->rm, false);
|
|
|
|
|
2023-06-13 10:03:16 +00:00
|
|
|
if (from == to || from == UT64_MAX || to == UT64_MAX) {
|
2015-09-14 00:08:31 +00:00
|
|
|
return false;
|
2016-09-26 21:13:49 +00:00
|
|
|
}
|
2020-04-12 14:42:19 +00:00
|
|
|
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
|
|
|
}
|
2023-07-07 14:01:48 +00:00
|
|
|
|
|
|
|
RAnalRefType type = _type;
|
2022-04-25 20:19:23 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2023-07-07 14:01:48 +00:00
|
|
|
|
|
|
|
ref_manager_add_entry (anal->rm, from, to, type);
|
2022-01-25 09:39:15 +00:00
|
|
|
R_DIRTY (anal);
|
2015-09-14 00:08:31 +00:00
|
|
|
return true;
|
2013-04-18 01:58:44 +00:00
|
|
|
}
|
|
|
|
|
2023-07-07 14:01:48 +00:00
|
|
|
// R2_590 deprecate, does same as r_anal_xref_del?
|
2021-07-30 10:34:13 +00:00
|
|
|
R_API bool r_anal_xrefs_deln(RAnal *anal, ut64 from, ut64 to, const RAnalRefType type) {
|
2023-07-07 14:01:48 +00:00
|
|
|
r_return_val_if_fail (anal && anal->rm, false);
|
|
|
|
ref_manager_remove_entry (anal->rm, from, to);
|
2022-01-25 09:39:15 +00:00
|
|
|
R_DIRTY (anal);
|
2015-09-14 00:08:31 +00:00
|
|
|
return true;
|
2013-04-18 01:58:44 +00:00
|
|
|
}
|
|
|
|
|
2021-07-30 10:34:13 +00:00
|
|
|
R_API bool r_anal_xref_del(RAnal *anal, ut64 from, ut64 to) {
|
|
|
|
r_return_val_if_fail (anal, false);
|
2023-07-07 14:01:48 +00:00
|
|
|
ref_manager_remove_entry (anal->rm, from, to);
|
2022-01-25 09:39:15 +00:00
|
|
|
R_DIRTY (anal);
|
2017-12-14 11:40:31 +00:00
|
|
|
return true;
|
2017-12-14 10:24:46 +00:00
|
|
|
}
|
2013-04-18 01:58:44 +00:00
|
|
|
|
2023-07-07 14:01:48 +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);
|
2016-08-04 20:52:33 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2023-07-07 14:01:48 +00:00
|
|
|
|
|
|
|
RVecAnalRef_sort (anal_refs, compare_ref); // XXX not needed?
|
|
|
|
return anal_refs;
|
2013-04-18 01:58:44 +00:00
|
|
|
}
|
|
|
|
|
2023-07-07 14:01:48 +00:00
|
|
|
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;
|
|
|
|
}
|
2023-07-07 14:01:48 +00:00
|
|
|
|
|
|
|
RVecAnalRef_sort (anal_refs, compare_ref); // XXX not needed?
|
|
|
|
return anal_refs;
|
2017-02-20 11:15:58 +00:00
|
|
|
}
|
|
|
|
|
2023-07-07 14:01:48 +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);
|
2016-12-19 11:30:17 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2023-07-07 14:01:48 +00:00
|
|
|
|
|
|
|
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);
|
2014-09-03 21:40:57 +00:00
|
|
|
}
|
2023-07-07 14:01:48 +00:00
|
|
|
r_table_free (table);
|
2014-09-03 21:40:57 +00:00
|
|
|
}
|
|
|
|
|
2023-07-07 14:01:48 +00:00
|
|
|
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;
|
2023-07-07 14:01:48 +00:00
|
|
|
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);
|
2019-02-14 00:43:42 +00:00
|
|
|
}
|
2023-07-07 14:01:48 +00:00
|
|
|
|
|
|
|
pj_end (pj);
|
2013-10-24 11:59:19 +00:00
|
|
|
}
|
2023-07-07 14:01:48 +00:00
|
|
|
|
|
|
|
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) {
|
2022-05-18 07:24:09 +00:00
|
|
|
int t = R_ANAL_REF_TYPE_MASK (ref->type);
|
|
|
|
if (!t) {
|
|
|
|
t = ' ';
|
|
|
|
}
|
2023-07-07 14:01:48 +00:00
|
|
|
|
|
|
|
// 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);
|
2023-07-07 14:01:48 +00:00
|
|
|
if (!t) {
|
|
|
|
t = ' ';
|
2013-10-24 11:59:19 +00:00
|
|
|
}
|
2023-07-07 14:01:48 +00:00
|
|
|
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));
|
2013-10-24 11:59:19 +00:00
|
|
|
}
|
2023-07-07 14:01:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 = ' ';
|
2023-03-31 11:26:21 +00:00
|
|
|
}
|
2023-07-07 14:01:48 +00:00
|
|
|
|
|
|
|
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");
|
2022-05-30 19:13:29 +00:00
|
|
|
}
|
2013-07-19 01:35:45 +00:00
|
|
|
}
|
2023-07-07 14:01:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2013-07-19 01:35:45 +00:00
|
|
|
}
|
2014-09-23 08:15:19 +00:00
|
|
|
|
2022-04-25 20:19:23 +00:00
|
|
|
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)) {
|
2022-05-18 08:29:11 +00:00
|
|
|
case ' ':
|
2022-04-25 20:19:23 +00:00
|
|
|
case R_ANAL_REF_TYPE_NULL:
|
|
|
|
return "NULL";
|
2023-06-28 18:47:27 +00:00
|
|
|
case R_ANAL_REF_TYPE_ICOD:
|
|
|
|
return "ICOD";
|
2014-09-23 08:15:19 +00:00
|
|
|
case R_ANAL_REF_TYPE_CODE:
|
2018-05-08 06:41:53 +00:00
|
|
|
return "CODE";
|
2014-09-23 08:15:19 +00:00
|
|
|
case R_ANAL_REF_TYPE_CALL:
|
|
|
|
return "CALL";
|
2022-12-13 22:49:38 +00:00
|
|
|
case R_ANAL_REF_TYPE_JUMP:
|
|
|
|
return "JUMP";
|
2014-09-23 08:15:19 +00:00
|
|
|
case R_ANAL_REF_TYPE_DATA:
|
|
|
|
return "DATA";
|
2023-06-28 18:47:27 +00:00
|
|
|
case R_ANAL_REF_TYPE_STRN:
|
2022-04-25 20:19:23 +00:00
|
|
|
return "STRN";
|
2014-09-24 11:03:03 +00:00
|
|
|
default:
|
2023-06-28 18:47:27 +00:00
|
|
|
// R_LOG_ERROR("Invalid unknown ref type %c", R_ANAL_REF_TYPE_MASK (type));
|
2022-04-25 20:19:23 +00:00
|
|
|
return "UNKN";
|
2014-09-23 08:15:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-25 20:19:23 +00:00
|
|
|
R_API RAnalRefType r_anal_xrefs_type_from_string(const char *s) {
|
|
|
|
RAnalRefType rt = R_ANAL_REF_TYPE_NULL;
|
|
|
|
if (strchr (s, 'r')) {
|
2023-06-27 17:47:14 +00:00
|
|
|
rt |= (R_ANAL_REF_TYPE_DATA | R_ANAL_REF_TYPE_READ);
|
2022-04-25 20:19:23 +00:00
|
|
|
}
|
|
|
|
if (strchr (s, 'w')) {
|
2023-06-27 17:47:14 +00:00
|
|
|
rt |= (R_ANAL_REF_TYPE_DATA | R_ANAL_REF_TYPE_WRITE);
|
2022-04-25 20:19:23 +00:00
|
|
|
}
|
|
|
|
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')) {
|
2023-07-07 14:01:48 +00:00
|
|
|
rt |= R_ANAL_REF_TYPE_STRN;
|
2022-04-25 20:19:23 +00:00
|
|
|
}
|
|
|
|
return rt;
|
|
|
|
}
|
|
|
|
|
2023-06-27 17:47:14 +00:00
|
|
|
R_API int r_anal_ref_typemask(int x) {
|
|
|
|
const int maskedType = x & 0xff;
|
|
|
|
switch (maskedType) {
|
|
|
|
case R_ANAL_REF_TYPE_NULL:
|
2023-06-28 18:47:27 +00:00
|
|
|
case R_ANAL_REF_TYPE_CODE | R_ANAL_REF_TYPE_DATA: // 'g' // XXX R2_590 - this is a conflictive type
|
2023-06-27 17:47:14 +00:00
|
|
|
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
|
2023-06-28 18:47:27 +00:00
|
|
|
case R_ANAL_REF_TYPE_STRN: // 's' // string ref
|
|
|
|
case R_ANAL_REF_TYPE_ICOD: // 'i' // indirect cod reference
|
2023-06-27 17:47:14 +00:00
|
|
|
return maskedType;
|
2023-06-28 18:47:27 +00:00
|
|
|
case ' ':
|
|
|
|
return R_ANAL_REF_TYPE_NULL;
|
2023-06-27 17:47:14 +00:00
|
|
|
}
|
|
|
|
R_LOG_ERROR ("Invalid reftype mask '%c' (0x%02x)", x, x);
|
|
|
|
// SHOULD NEVER HAPPEN MAYBE WARN HERE
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-04-25 20:19:23 +00:00
|
|
|
// 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:
|
2023-06-27 17:47:14 +00:00
|
|
|
case R_ANAL_REF_TYPE_STRN:
|
2023-06-28 18:47:27 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|