rizin/librz/analysis/xrefs.c
2021-01-21 22:05:44 +08:00

376 lines
9 KiB
C

// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_analysis.h>
#include <rz_cons.h>
#if 0
DICT
====
refs 10->20 C 16->10 J 20->10 C
xrefs 20->[10 C] 10 -> [16 J, 20 C]
10 : call 20 16 : jmp 10 20 : call 10
#endif
// XXX: is it possible to have multiple type for the same (from, to) pair?
// if it is, things need to be adjusted
static RzAnalysisRef *rz_analysis_ref_new(ut64 addr, ut64 at, ut64 type) {
RzAnalysisRef *ref = RZ_NEW(RzAnalysisRef);
if (ref) {
ref->addr = addr;
ref->at = at;
ref->type = (type == -1) ? RZ_ANALYSIS_REF_TYPE_CODE : type;
}
return ref;
}
static void rz_analysis_ref_free(void *ref) {
free(ref);
}
RZ_API RzList *rz_analysis_ref_list_new(void) {
return rz_list_newf(rz_analysis_ref_free);
}
static void xrefs_ht_free(HtUPKv *kv) {
ht_up_free(kv->value);
}
static void xrefs_ref_free(HtUPKv *kv) {
rz_analysis_ref_free(kv->value);
}
static bool appendRef(void *u, const ut64 k, const void *v) {
RzList *list = (RzList *)u;
RzAnalysisRef *ref = (RzAnalysisRef *)v;
RzAnalysisRef *cloned = rz_analysis_ref_new(ref->addr, ref->at, ref->type);
if (cloned) {
rz_list_append(list, cloned);
return true;
}
return false;
}
static bool mylistrefs_cb(void *list, const ut64 k, const void *v) {
HtUP *ht = (HtUP *)v;
ht_up_foreach(ht, appendRef, list);
return true;
}
static int ref_cmp(const RzAnalysisRef *a, const RzAnalysisRef *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 sortxrefs(RzList *list) {
rz_list_sort(list, (RzListComparator)ref_cmp);
}
static void listxrefs(HtUP *m, ut64 addr, RzList *list) {
if (addr == UT64_MAX) {
ht_up_foreach(m, mylistrefs_cb, list);
} else {
bool found;
HtUP *d = ht_up_find(m, addr, &found);
if (!found) {
return;
}
ht_up_foreach(d, appendRef, list);
}
}
static void setxref(HtUP *m, ut64 from, ut64 to, int type) {
bool found;
HtUP *ht = ht_up_find(m, from, &found);
if (!found) {
ht = ht_up_new(NULL, xrefs_ref_free, NULL);
if (!ht) {
return;
}
ht_up_insert(m, from, ht);
}
RzAnalysisRef *ref = rz_analysis_ref_new(to, from, type);
if (ref) {
ht_up_update(ht, to, ref);
}
}
// set a reference from FROM to TO and a cross-reference(xref) from TO to FROM.
RZ_API int rz_analysis_xrefs_set(RzAnalysis *analysis, ut64 from, ut64 to, const RzAnalysisRefType type) {
if (!analysis || from == to) {
return false;
}
if (analysis->iob.is_valid_offset) {
if (!analysis->iob.is_valid_offset(analysis->iob.io, from, 0)) {
return false;
}
if (!analysis->iob.is_valid_offset(analysis->iob.io, to, 0)) {
return false;
}
}
setxref(analysis->dict_xrefs, to, from, type);
setxref(analysis->dict_refs, from, to, type);
return true;
}
RZ_API int rz_analysis_xrefs_deln(RzAnalysis *analysis, ut64 from, ut64 to, const RzAnalysisRefType type) {
if (!analysis) {
return false;
}
ht_up_delete(analysis->dict_refs, from);
ht_up_delete(analysis->dict_xrefs, to);
return true;
}
RZ_API int rz_analysis_xref_del(RzAnalysis *analysis, ut64 from, ut64 to) {
bool res = false;
res |= rz_analysis_xrefs_deln(analysis, from, to, RZ_ANALYSIS_REF_TYPE_NULL);
res |= rz_analysis_xrefs_deln(analysis, from, to, RZ_ANALYSIS_REF_TYPE_CODE);
res |= rz_analysis_xrefs_deln(analysis, from, to, RZ_ANALYSIS_REF_TYPE_CALL);
res |= rz_analysis_xrefs_deln(analysis, from, to, RZ_ANALYSIS_REF_TYPE_DATA);
res |= rz_analysis_xrefs_deln(analysis, from, to, RZ_ANALYSIS_REF_TYPE_STRING);
return res;
}
RZ_API int rz_analysis_xrefs_from(RzAnalysis *analysis, RzList *list, const char *kind, const RzAnalysisRefType type, ut64 addr) {
listxrefs(analysis->dict_refs, addr, list);
sortxrefs(list);
return true;
}
RZ_API RzList *rz_analysis_xrefs_get(RzAnalysis *analysis, ut64 to) {
RzList *list = rz_analysis_ref_list_new();
if (!list) {
return NULL;
}
listxrefs(analysis->dict_xrefs, to, list);
sortxrefs(list);
if (rz_list_empty(list)) {
rz_list_free(list);
list = NULL;
}
return list;
}
RZ_API RzList *rz_analysis_refs_get(RzAnalysis *analysis, ut64 from) {
RzList *list = rz_analysis_ref_list_new();
if (!list) {
return NULL;
}
listxrefs(analysis->dict_refs, from, list);
sortxrefs(list);
if (rz_list_empty(list)) {
rz_list_free(list);
list = NULL;
}
return list;
}
RZ_API RzList *rz_analysis_xrefs_get_from(RzAnalysis *analysis, ut64 to) {
RzList *list = rz_analysis_ref_list_new();
if (!list) {
return NULL;
}
listxrefs(analysis->dict_refs, to, list);
sortxrefs(list);
if (rz_list_empty(list)) {
rz_list_free(list);
list = NULL;
}
return list;
}
RZ_API void rz_analysis_xrefs_list(RzAnalysis *analysis, int rad) {
RzListIter *iter;
RzAnalysisRef *ref;
PJ *pj = NULL;
RzList *list = rz_analysis_ref_list_new();
listxrefs(analysis->dict_refs, UT64_MAX, list);
sortxrefs(list);
if (rad == 'j') {
pj = analysis->coreb.pjWithEncoding(analysis->coreb.core);
if (!pj) {
return;
}
pj_a(pj);
}
rz_list_foreach (list, iter, ref) {
int t = ref->type ? ref->type : ' ';
switch (rad) {
case '*':
analysis->cb_printf("ax%c 0x%" PFMT64x " 0x%" PFMT64x "\n", t, ref->addr, ref->at);
break;
case '\0': {
char *name = analysis->coreb.getNameDelta(analysis->coreb.core, ref->at);
if (name) {
rz_str_replace_ch(name, ' ', 0, true);
analysis->cb_printf("%40s", name);
free(name);
} else {
analysis->cb_printf("%40s", "?");
}
analysis->cb_printf(" 0x%" PFMT64x " -> %9s -> 0x%" PFMT64x, ref->at, rz_analysis_xrefs_type_tostring(t), ref->addr);
name = analysis->coreb.getNameDelta(analysis->coreb.core, ref->addr);
if (name) {
rz_str_replace_ch(name, ' ', 0, true);
analysis->cb_printf(" %s\n", name);
free(name);
} else {
analysis->cb_printf("\n");
}
} break;
case 'q':
analysis->cb_printf("0x%08" PFMT64x " -> 0x%08" PFMT64x " %s\n", ref->at, ref->addr, rz_analysis_xrefs_type_tostring(t));
break;
case 'j': {
pj_o(pj);
char *name = analysis->coreb.getNameDelta(analysis->coreb.core, ref->at);
if (name) {
rz_str_replace_ch(name, ' ', 0, true);
pj_ks(pj, "name", name);
free(name);
}
pj_kn(pj, "from", ref->at);
pj_ks(pj, "type", rz_analysis_xrefs_type_tostring(t));
pj_kn(pj, "addr", ref->addr);
name = analysis->coreb.getNameDelta(analysis->coreb.core, ref->addr);
if (name) {
rz_str_replace_ch(name, ' ', 0, true);
pj_ks(pj, "refname", name);
free(name);
}
pj_end(pj);
} break;
default:
break;
}
}
if (rad == 'j') {
pj_end(pj);
analysis->cb_printf("%s\n", pj_string(pj));
pj_free(pj);
}
rz_list_free(list);
}
RZ_API const char *rz_analysis_xrefs_type_tostring(RzAnalysisRefType type) {
switch (type) {
case RZ_ANALYSIS_REF_TYPE_CODE:
return "CODE";
case RZ_ANALYSIS_REF_TYPE_CALL:
return "CALL";
case RZ_ANALYSIS_REF_TYPE_DATA:
return "DATA";
case RZ_ANALYSIS_REF_TYPE_STRING:
return "STRING";
case RZ_ANALYSIS_REF_TYPE_NULL:
default:
return "UNKNOWN";
}
}
RZ_API RzAnalysisRefType rz_analysis_xrefs_type(char ch) {
switch (ch) {
case RZ_ANALYSIS_REF_TYPE_CODE:
case RZ_ANALYSIS_REF_TYPE_CALL:
case RZ_ANALYSIS_REF_TYPE_DATA:
case RZ_ANALYSIS_REF_TYPE_STRING:
case RZ_ANALYSIS_REF_TYPE_NULL:
return (RzAnalysisRefType)ch;
default:
return RZ_ANALYSIS_REF_TYPE_NULL;
}
}
RZ_API bool rz_analysis_xrefs_init(RzAnalysis *analysis) {
ht_up_free(analysis->dict_refs);
analysis->dict_refs = NULL;
ht_up_free(analysis->dict_xrefs);
analysis->dict_xrefs = NULL;
HtUP *tmp = ht_up_new(NULL, xrefs_ht_free, NULL);
if (!tmp) {
return false;
}
analysis->dict_refs = tmp;
tmp = ht_up_new(NULL, xrefs_ht_free, NULL);
if (!tmp) {
ht_up_free(analysis->dict_refs);
analysis->dict_refs = NULL;
return false;
}
analysis->dict_xrefs = tmp;
return true;
}
static bool count_cb(void *user, const ut64 k, const void *v) {
(*(ut64 *)user) += ((HtUP *)v)->count;
return true;
}
RZ_API ut64 rz_analysis_xrefs_count(RzAnalysis *analysis) {
ut64 ret = 0;
ht_up_foreach(analysis->dict_xrefs, count_cb, &ret);
return ret;
}
static RzList *fcn_get_refs(RzAnalysisFunction *fcn, HtUP *ht) {
RzListIter *iter;
RzAnalysisBlock *bb;
RzList *list = rz_analysis_ref_list_new();
if (!list) {
return NULL;
}
rz_list_foreach (fcn->bbs, iter, bb) {
int i;
for (i = 0; i < bb->ninstr; i++) {
ut64 at = bb->addr + rz_analysis_block_get_op_offset(bb, i);
listxrefs(ht, at, list);
}
}
sortxrefs(list);
return list;
}
RZ_API RzList *rz_analysis_function_get_refs(RzAnalysisFunction *fcn) {
rz_return_val_if_fail(fcn, NULL);
return fcn_get_refs(fcn, fcn->analysis->dict_refs);
}
RZ_API RzList *rz_analysis_function_get_xrefs(RzAnalysisFunction *fcn) {
rz_return_val_if_fail(fcn, NULL);
return fcn_get_refs(fcn, fcn->analysis->dict_xrefs);
}
RZ_API const char *rz_analysis_ref_type_tostring(RzAnalysisRefType t) {
switch (t) {
case RZ_ANALYSIS_REF_TYPE_NULL:
return "null";
case RZ_ANALYSIS_REF_TYPE_CODE:
return "code";
case RZ_ANALYSIS_REF_TYPE_CALL:
return "call";
case RZ_ANALYSIS_REF_TYPE_DATA:
return "data";
case RZ_ANALYSIS_REF_TYPE_STRING:
return "string";
}
return "unknown";
}