rizin/librz/arch/xrefs.c
Giovanni b5fee3471c
Move RzAnalysis as private and force usage of C API (#6123)
* Hide RzAnalysis structure to force the usage of the C APIs.

* Fix rop code & test

* Fix linter

* Fix rz_analysis_free to return void
2026-04-06 16:07:53 +00:00

311 lines
8.1 KiB
C

// SPDX-FileCopyrightText: 2009-2019 pancake <pancake@nopcode.org>
// SPDX-FileCopyrightText: 2009-2019 nibble <nibble.ds@gmail.com>
// SPDX-FileCopyrightText: 2009-2019 defragger <rlaemmert@gmail.com>
// SPDX-FileCopyrightText: 2009-2019 ret2libc <sirmy15@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#include "analysis_private.h"
#include <rz_cons.h>
/*
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
*/
// TODO: is it possible to have multiple type for the same (from, to) pair?
// if it is, things need to be adjusted
static RzAnalysisXRef *rz_analysis_xref_new(ut64 from, ut64 to, ut64 type) {
RzAnalysisXRef *xref = RZ_NEW(RzAnalysisXRef);
if (xref) {
xref->from = from;
xref->to = to;
xref->type = (type == -1) ? RZ_ANALYSIS_XREF_TYPE_CODE : type;
}
return xref;
}
static void rz_analysis_xref_free(RzAnalysisXRef *xref) {
free(xref);
}
RZ_API RZ_OWN RzList /*<RzAnalysisXRef *>*/ *rz_analysis_xref_list_new() {
return rz_list_newf((RzListFree)free);
}
static bool appendRef(void *u, const ut64 k, const void *v) {
RzList *list = (RzList *)u;
RzAnalysisXRef *xref = (RzAnalysisXRef *)v;
RzAnalysisXRef *cloned = rz_analysis_xref_new(xref->from, xref->to, xref->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 RzAnalysisXRef *a, const RzAnalysisXRef *b, void *user) {
if (a->from < b->from) {
return -1;
}
if (a->from > b->from) {
return 1;
}
if (a->to < b->to) {
return -1;
}
if (a->to > b->to) {
return 1;
}
return 0;
}
static void sortxrefs(RzList /*<RzAnalysisXRef *>*/ *list) {
rz_list_sort(list, (RzListComparator)ref_cmp, NULL);
}
static void listxrefs(HtUP *m, ut64 addr, RzList /*<RzAnalysisXRef *>*/ *list) {
if (addr == UT64_MAX) {
ht_up_foreach(m, mylistrefs_cb, list);
} else {
HtUP *d = ht_up_find(m, addr, NULL);
if (d) {
ht_up_foreach(d, appendRef, list);
}
}
}
static bool set_xref(HtUP *m, RzAnalysisXRef *xref, bool from2to) {
ut64 key1 = from2to ? xref->from : xref->to;
HtUP *ht = ht_up_find(m, key1, NULL);
if (!ht) {
// RzAnalysis::ht_xrefs_to is responsible for releasing of pointers.
HtUPFreeValue cb = from2to ? NULL : (HtUPFreeValue)rz_analysis_xref_free;
ht = ht_up_new(NULL, cb);
if (!ht) {
return false;
}
if (!ht_up_insert(m, key1, ht)) {
return false;
}
}
ut64 key2 = from2to ? xref->to : xref->from;
return ht_up_update(ht, key2, xref);
}
// Set a cross reference from FROM to TO.
RZ_API bool rz_analysis_xrefs_set(RzAnalysis *analysis, ut64 from, ut64 to, RzAnalysisXRefType 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;
}
}
RzAnalysisXRef *xref = rz_analysis_xref_new(from, to, type);
if (!xref) {
return false;
}
if (!set_xref(analysis->ht_xrefs_from, xref, true)) {
// Pointer isn't added to <ht_xrefs_from> so we have to release it
rz_analysis_xref_free(xref);
return false;
}
if (!set_xref(analysis->ht_xrefs_to, xref, false)) {
// Delete the entry in <ht_xrefs_from>
rz_analysis_xrefs_deln(analysis, from, to, type);
// Pointer isn't added to <ht_xrefs_to> so we have to release it
rz_analysis_xref_free(xref);
return false;
}
return true;
}
RZ_API bool rz_analysis_xrefs_deln(RzAnalysis *analysis, ut64 from, ut64 to, RzAnalysisXRefType type) {
if (!analysis) {
return false;
}
HtUP *ht1 = ht_up_find(analysis->ht_xrefs_from, from, NULL);
if (ht1) {
ht_up_delete(ht1, to);
}
HtUP *ht2 = ht_up_find(analysis->ht_xrefs_to, to, NULL);
if (ht2) {
ht_up_delete(ht2, from);
}
return true;
}
RZ_API bool rz_analysis_xref_del(RzAnalysis *analysis, ut64 from, ut64 to) {
bool res = false;
res |= rz_analysis_xrefs_deln(analysis, from, to, RZ_ANALYSIS_XREF_TYPE_NULL);
res |= rz_analysis_xrefs_deln(analysis, from, to, RZ_ANALYSIS_XREF_TYPE_CODE);
res |= rz_analysis_xrefs_deln(analysis, from, to, RZ_ANALYSIS_XREF_TYPE_CALL);
res |= rz_analysis_xrefs_deln(analysis, from, to, RZ_ANALYSIS_XREF_TYPE_DATA);
res |= rz_analysis_xrefs_deln(analysis, from, to, RZ_ANALYSIS_XREF_TYPE_STRING);
return res;
}
RZ_API RZ_OWN RzList /*<RzAnalysisXRef *>*/ *rz_analysis_xrefs_get_to(RzAnalysis *analysis, ut64 addr) {
RzList *list = rz_analysis_xref_list_new();
if (!list) {
return NULL;
}
listxrefs(analysis->ht_xrefs_to, addr, list);
sortxrefs(list);
if (rz_list_empty(list)) {
rz_list_free(list);
list = NULL;
}
return list;
}
RZ_API RZ_OWN RzList /*<RzAnalysisXRef *>*/ *rz_analysis_xrefs_get_from(RzAnalysis *analysis, ut64 addr) {
RzList *list = rz_analysis_xref_list_new();
if (!list) {
return NULL;
}
listxrefs(analysis->ht_xrefs_from, addr, list);
sortxrefs(list);
if (rz_list_empty(list)) {
rz_list_free(list);
list = NULL;
}
return list;
}
/**
* \brief Get list of all xrefs.
* \param analysis RzAnalysis instance
* \return RzList <RzAnalysisXRef *>
*/
RZ_API RZ_OWN RzList /*<RzAnalysisXRef *>*/ *rz_analysis_xrefs_list(RzAnalysis *analysis) {
rz_return_val_if_fail(analysis, NULL);
RzList *list = rz_analysis_xref_list_new();
if (list) {
listxrefs(analysis->ht_xrefs_from, UT64_MAX, list);
sortxrefs(list);
}
return list;
}
RZ_API const char *rz_analysis_xrefs_type_tostring(RzAnalysisXRefType type) {
switch (type) {
case RZ_ANALYSIS_XREF_TYPE_CODE:
return "CODE";
case RZ_ANALYSIS_XREF_TYPE_CALL:
return "CALL";
case RZ_ANALYSIS_XREF_TYPE_DATA:
return "DATA";
case RZ_ANALYSIS_XREF_TYPE_STRING:
return "STRING";
case RZ_ANALYSIS_XREF_TYPE_NULL:
default:
return "UNKNOWN";
}
}
RZ_API RzAnalysisXRefType rz_analysis_xrefs_type(char ch) {
switch (ch) {
case RZ_ANALYSIS_XREF_TYPE_CODE:
case RZ_ANALYSIS_XREF_TYPE_CALL:
case RZ_ANALYSIS_XREF_TYPE_DATA:
case RZ_ANALYSIS_XREF_TYPE_STRING:
case RZ_ANALYSIS_XREF_TYPE_NULL:
return (RzAnalysisXRefType)ch;
default:
return RZ_ANALYSIS_XREF_TYPE_NULL;
}
}
RZ_API bool rz_analysis_xrefs_init(RzAnalysis *analysis) {
ht_up_free(analysis->ht_xrefs_from);
analysis->ht_xrefs_from = NULL;
ht_up_free(analysis->ht_xrefs_to);
analysis->ht_xrefs_to = NULL;
HtUP *tmp = ht_up_new(NULL, (HtUPFreeValue)ht_up_free);
if (!tmp) {
return false;
}
analysis->ht_xrefs_from = tmp;
tmp = ht_up_new(NULL, (HtUPFreeValue)ht_up_free);
if (!tmp) {
ht_up_free(analysis->ht_xrefs_from);
analysis->ht_xrefs_from = NULL;
return false;
}
analysis->ht_xrefs_to = tmp;
return true;
}
static bool count_cb(void *user, const ut64 k, const void *v) {
(*(ut64 *)user) += ht_up_size((HtUP *)v);
return true;
}
RZ_API ut64 rz_analysis_xrefs_count(RzAnalysis *analysis) {
ut64 ret = 0;
ht_up_foreach(analysis->ht_xrefs_to, count_cb, &ret);
return ret;
}
static RZ_OWN RzList /*<RzAnalysisXRef *>*/ *fcn_get_refs(const RzAnalysisFunction *fcn, HtUP *ht) {
void **it;
RzAnalysisBlock *bb;
RzList *list = rz_analysis_xref_list_new();
if (!list) {
return NULL;
}
rz_pvector_foreach (fcn->bbs, it) {
bb = (RzAnalysisBlock *)*it;
for (size_t 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 RZ_OWN RzList /*<RzAnalysisXRef *>*/ *rz_analysis_function_get_xrefs_from(const RzAnalysisFunction *fcn) {
rz_return_val_if_fail(fcn, NULL);
return fcn_get_refs(fcn, fcn->analysis->ht_xrefs_from);
}
RZ_API RZ_OWN RzList /*<RzAnalysisXRef *>*/ *rz_analysis_function_get_xrefs_to(const RzAnalysisFunction *fcn) {
rz_return_val_if_fail(fcn, NULL);
return fcn_get_refs(fcn, fcn->analysis->ht_xrefs_to);
}
RZ_API const char *rz_analysis_ref_type_tostring(RzAnalysisXRefType t) {
switch (t) {
case RZ_ANALYSIS_XREF_TYPE_NULL:
return "null";
case RZ_ANALYSIS_XREF_TYPE_CODE:
return "code";
case RZ_ANALYSIS_XREF_TYPE_CALL:
return "call";
case RZ_ANALYSIS_XREF_TYPE_DATA:
return "data";
case RZ_ANALYSIS_XREF_TYPE_STRING:
return "string";
}
return "unknown";
}