mirror of
https://github.com/radareorg/radare2.git
synced 2024-11-27 15:10:53 +00:00
7d6c66697a
Some checks failed
build / macos-acr (arm64, 13) (push) Waiting to run
build / macos-acr (x86_64, 12) (push) Waiting to run
build / ios (cydia32) (push) Waiting to run
build / ios (true, cydia) (push) Waiting to run
build / w32-meson (push) Waiting to run
build / w64-static-2022 (push) Waiting to run
build / w64-static (push) Waiting to run
build / w64-meson (push) Waiting to run
build / check_release (push) Blocked by required conditions
build / release (push) Blocked by required conditions
build / linux-wasi (push) Failing after 0s
CI / macos-test (push) Waiting to run
CI / macos-rpath (push) Waiting to run
CI / w64-make (push) Waiting to run
build / linux-wasi-api (push) Failing after 0s
build / linux-csnext (push) Failing after 0s
build / tarball (push) Failing after 0s
build / linux-static (push) Failing after 0s
build / check_abi_compatibility (push) Has been skipped
build / linux-acr-rpm-64 (push) Failing after 0s
build / linux-acr-deb (amd64) (push) Failing after 0s
build / linux-acr-deb (arm64, aarch64-linux-gnu) (push) Failing after 0s
build / linux-acr-deb (i386, multilib) (push) Failing after 0s
build / android-acr (16, arm) (push) Failing after 0s
build / android-acr (aarch64) (push) Failing after 0s
build / android-meson (x86_64) (push) Has been skipped
CI / linux-acr-oldlibsbug (push) Failing after 0s
CI / linux-nocs (push) Failing after 0s
CI / linux-acr-gperf (push) Failing after 0s
CI / linux-sys-capstone (push) Failing after 0s
CI / linux-acr-resymlink (push) Failing after 0s
CI / linux-test (push) Failing after 0s
CI / linux-static-meson (push) Failing after 0s
CI / linux-rpath (push) Failing after 0s
CI / linux-meson-spaces (push) Failing after 0s
CI / linux-tinyasan-fuzz (push) Failing after 0s
CI / linux-asan-fuzz (push) Failing after 0s
CI / w32-mingw (push) Failing after 0s
CI / w64-mingw (push) Failing after 0s
Code scanning - action / CodeQL-Build (push) Failing after 0s
tcc / ubuntu-tcc-newabi (push) Failing after 0s
tcc / ubuntu-tcc-test (push) Failing after 0s
tcc / ubuntu-tcc-nodbg (push) Failing after 0s
tcc / r2pm-tcc (push) Failing after 0s
tcc / ubuntu-tcc-syslibs (push) Failing after 0s
Coverity Scan / latest (push) Failing after 0s
475 lines
12 KiB
C
475 lines
12 KiB
C
/* radare - LGPL - Copyright 2007-2024 - pancake, ret2libc, condret */
|
|
|
|
#include <r_util.h>
|
|
|
|
enum {
|
|
WHITE_COLOR = 0,
|
|
GRAY_COLOR,
|
|
BLACK_COLOR
|
|
};
|
|
|
|
static RGraphNode *r_graph_node_new(void *data) {
|
|
RGraphNode *p = R_NEW0 (RGraphNode);
|
|
if (p) {
|
|
p->data = data;
|
|
// R_NEW0 already sets p->free to NULL
|
|
// p->free = NULL;
|
|
p->out_nodes = r_list_new ();
|
|
p->in_nodes = r_list_new ();
|
|
p->all_neighbours = r_list_new ();
|
|
}
|
|
return p;
|
|
}
|
|
|
|
static void r_graph_node_free(RGraphNode *n) {
|
|
if (!n) {
|
|
return;
|
|
}
|
|
if (n->free) {
|
|
n->free (n->data);
|
|
}
|
|
r_list_free (n->out_nodes);
|
|
r_list_free (n->in_nodes);
|
|
r_list_free (n->all_neighbours);
|
|
free (n);
|
|
}
|
|
|
|
static int node_cmp(unsigned int idx, RGraphNode *b) {
|
|
return idx == b->idx ? 0 : -1;
|
|
}
|
|
|
|
// direction == true => forwards
|
|
static void dfs_node(RGraph *g, RGraphNode *n, RGraphVisitor *vis, int color[], const bool direction) {
|
|
if (!n) {
|
|
return;
|
|
}
|
|
RStack *s = r_stack_new (2 * g->n_edges + 1);
|
|
if (!s) {
|
|
return;
|
|
}
|
|
RGraphEdge *edg = R_NEW0 (RGraphEdge);
|
|
if (!edg) {
|
|
r_stack_free (s);
|
|
return;
|
|
}
|
|
edg->from = NULL;
|
|
edg->to = n;
|
|
r_stack_push (s, edg);
|
|
while (!r_stack_is_empty (s)) {
|
|
RGraphEdge *cur_edge = (RGraphEdge *)r_stack_pop (s);
|
|
RGraphNode *v, *cur = cur_edge->to, *from = cur_edge->from;
|
|
RListIter *it;
|
|
int i;
|
|
|
|
if (from && cur) {
|
|
if (color[cur->idx] == WHITE_COLOR && vis->tree_edge) {
|
|
vis->tree_edge (cur_edge, vis);
|
|
} else if (color[cur->idx] == GRAY_COLOR && vis->back_edge) {
|
|
vis->back_edge (cur_edge, vis);
|
|
} else if (color[cur->idx] == BLACK_COLOR && vis->fcross_edge) {
|
|
vis->fcross_edge (cur_edge, vis);
|
|
}
|
|
} else if (!cur && from) {
|
|
if (color[from->idx] != BLACK_COLOR && vis->finish_node) {
|
|
vis->finish_node (from, vis);
|
|
}
|
|
color[from->idx] = BLACK_COLOR;
|
|
}
|
|
free (cur_edge);
|
|
if (!cur || color[cur->idx] != WHITE_COLOR) {
|
|
continue;
|
|
}
|
|
if (color[cur->idx] == WHITE_COLOR && vis->discover_node) {
|
|
vis->discover_node (cur, vis);
|
|
}
|
|
color[cur->idx] = GRAY_COLOR;
|
|
|
|
edg = R_NEW0 (RGraphEdge);
|
|
if (!edg) {
|
|
break;
|
|
}
|
|
edg->from = cur;
|
|
r_stack_push (s, edg);
|
|
|
|
i = 0;
|
|
const RList *neighbours = direction ? cur->out_nodes : cur->in_nodes;
|
|
r_list_foreach (neighbours, it, v) {
|
|
edg = R_NEW (RGraphEdge);
|
|
edg->from = cur;
|
|
edg->to = v;
|
|
edg->nth = i++;
|
|
r_stack_push (s, edg);
|
|
}
|
|
}
|
|
r_stack_free (s);
|
|
}
|
|
|
|
R_API RGraph *r_graph_new(void) {
|
|
RGraph *t = R_NEW0 (RGraph);
|
|
if (!t) {
|
|
return NULL;
|
|
}
|
|
t->nodes = r_list_new ();
|
|
if (!t->nodes) {
|
|
r_graph_free (t);
|
|
return NULL;
|
|
}
|
|
t->nodes->free = (RListFree)r_graph_node_free;
|
|
t->n_nodes = 0;
|
|
t->last_index = 0;
|
|
return t;
|
|
}
|
|
|
|
R_API void r_graph_free(RGraph* t) {
|
|
if (!t) {
|
|
return;
|
|
}
|
|
r_list_free (t->nodes);
|
|
free (t);
|
|
}
|
|
|
|
R_API RGraphNode *r_graph_get_node(const RGraph *t, unsigned int idx) {
|
|
RListIter *it = r_list_find (t->nodes, (void *)(size_t)idx, (RListComparator)node_cmp);
|
|
if (!it) {
|
|
return NULL;
|
|
}
|
|
return (RGraphNode *)it->data;
|
|
}
|
|
|
|
R_API RListIter *r_graph_node_iter(const RGraph *t, unsigned int idx) {
|
|
return r_list_find (t->nodes, (void *)(size_t)idx, (RListComparator)node_cmp);
|
|
}
|
|
|
|
R_API void r_graph_reset(RGraph *t) {
|
|
r_list_free (t->nodes);
|
|
t->nodes = r_list_new ();
|
|
if (!t->nodes) {
|
|
return;
|
|
}
|
|
t->nodes->free = (RListFree)r_graph_node_free;
|
|
t->n_nodes = 0; // XXX isnt r_list_length enough?
|
|
t->n_edges = 0;
|
|
t->last_index = 0;
|
|
}
|
|
|
|
R_API RGraphNode *r_graph_add_node(RGraph *t, void *data) {
|
|
R_RETURN_VAL_IF_FAIL (t && data, NULL);
|
|
RGraphNode *n = r_graph_node_new (data);
|
|
if (n) {
|
|
n->idx = t->last_index++;
|
|
r_list_append (t->nodes, n);
|
|
t->n_nodes++; /// istn r_list_length enough?
|
|
}
|
|
return n;
|
|
}
|
|
|
|
R_API RGraphNode *r_graph_add_nodef(RGraph *graph, void *data, RListFree user_free) {
|
|
RGraphNode *node = r_graph_add_node (graph, data);
|
|
if (node) {
|
|
node->free = user_free;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
/* remove the node from the graph and free the node */
|
|
/* users of this function should be aware they can't access n anymore */
|
|
R_API void r_graph_del_node(RGraph *t, RGraphNode *n) {
|
|
RGraphNode *gn;
|
|
RListIter *it;
|
|
if (!n) {
|
|
return;
|
|
}
|
|
r_list_foreach (n->in_nodes, it, gn) {
|
|
r_list_delete_data (gn->out_nodes, n);
|
|
r_list_delete_data (gn->all_neighbours, n);
|
|
t->n_edges--;
|
|
}
|
|
|
|
r_list_foreach (n->out_nodes, it, gn) {
|
|
r_list_delete_data (gn->in_nodes, n);
|
|
r_list_delete_data (gn->all_neighbours, n);
|
|
t->n_edges--;
|
|
}
|
|
|
|
r_list_delete_data (t->nodes, n);
|
|
t->n_nodes--;
|
|
}
|
|
|
|
R_API void r_graph_add_edge(RGraph *t, RGraphNode *from, RGraphNode *to) {
|
|
r_graph_add_edge_at (t, from, to, -1);
|
|
}
|
|
|
|
R_API void r_graph_add_edge_at(RGraph *t, RGraphNode *from, RGraphNode *to, int nth) {
|
|
if (from && to) {
|
|
r_list_insert (from->out_nodes, nth, to);
|
|
r_list_append (from->all_neighbours, to);
|
|
r_list_append (to->in_nodes, from);
|
|
r_list_append (to->all_neighbours, from);
|
|
t->n_edges++;
|
|
}
|
|
}
|
|
|
|
// splits the "split_me", so that new node has it's outnodes
|
|
R_API RGraphNode *r_graph_node_split_forward(RGraph *g, RGraphNode *split_me, void *data) {
|
|
RGraphNode *front = r_graph_add_node(g, data);
|
|
RList *tmp = front->out_nodes;
|
|
front->out_nodes = split_me->out_nodes;
|
|
split_me->out_nodes = tmp;
|
|
RListIter *iter;
|
|
RGraphNode *n;
|
|
r_list_foreach (front->out_nodes, iter, n) {
|
|
r_list_delete_data (n->in_nodes, split_me); // optimize me
|
|
r_list_delete_data (n->all_neighbours, split_me); // boy this all_neighbours is so retarding perf here
|
|
r_list_delete_data (split_me->all_neighbours, n);
|
|
r_list_append (n->all_neighbours, front);
|
|
r_list_append (n->in_nodes, front);
|
|
r_list_append (front->all_neighbours, n);
|
|
}
|
|
return front;
|
|
}
|
|
|
|
R_API void r_graph_del_edge(RGraph *t, RGraphNode *from, RGraphNode *to) {
|
|
if (!from || !to || !r_graph_adjacent (t, from, to)) {
|
|
return;
|
|
}
|
|
r_list_delete_data (from->out_nodes, to);
|
|
r_list_delete_data (from->all_neighbours, to);
|
|
r_list_delete_data (to->in_nodes, from);
|
|
r_list_delete_data (to->all_neighbours, from);
|
|
t->n_edges--;
|
|
}
|
|
|
|
// XXX remove comments and static inline all this crap
|
|
/* returns the list of nodes reachable from `n` */
|
|
R_API const RList *r_graph_get_neighbours(const RGraph *g, const RGraphNode *n) {
|
|
return n? n->out_nodes: NULL;
|
|
}
|
|
|
|
/* returns the n-th nodes reachable from the give node `n`.
|
|
* This, of course, depends on the order of the nodes. */
|
|
R_API RGraphNode *r_graph_nth_neighbour(const RGraph *g, const RGraphNode *n, int nth) {
|
|
return n? (RGraphNode *)r_list_get_n (n->out_nodes, nth): NULL;
|
|
}
|
|
|
|
/* returns the list of nodes that can reach `n` */
|
|
R_API const RList *r_graph_innodes(const RGraph *g, const RGraphNode *n) {
|
|
return n? n->in_nodes: NULL;
|
|
}
|
|
|
|
/* returns the list of nodes reachable from `n` and that can reach `n`. */
|
|
R_API const RList *r_graph_all_neighbours(const RGraph *g, const RGraphNode *n) {
|
|
return n? n->all_neighbours: NULL;
|
|
}
|
|
|
|
R_API const RList *r_graph_get_nodes(const RGraph *g) {
|
|
return g? g->nodes: NULL;
|
|
}
|
|
|
|
/* true if there is an edge from the node `from` to the node `to` */
|
|
R_API bool r_graph_adjacent(const RGraph *g, const RGraphNode *from, const RGraphNode *to) {
|
|
if (!g || !from) {
|
|
return false;
|
|
}
|
|
return r_list_contains (from->out_nodes, to);
|
|
}
|
|
|
|
R_API void r_graph_dfs_node(RGraph *g, RGraphNode *n, RGraphVisitor *vis) {
|
|
if (!g || !n || !vis) {
|
|
return;
|
|
}
|
|
int *color = R_NEWS0 (int, g->last_index);
|
|
if (color) {
|
|
dfs_node (g, n, vis, color, true);
|
|
free (color);
|
|
}
|
|
}
|
|
|
|
R_API void r_graph_dfs_node_reverse(RGraph *g, RGraphNode *n, RGraphVisitor *vis) {
|
|
if (!g || !n || !vis) {
|
|
return;
|
|
}
|
|
int *color = R_NEWS0 (int, g->last_index);
|
|
if (color) {
|
|
dfs_node (g, n, vis, color, false);
|
|
free (color);
|
|
}
|
|
}
|
|
|
|
R_API void r_graph_dfs(RGraph *g, RGraphVisitor *vis) {
|
|
R_RETURN_IF_FAIL (g && vis);
|
|
RGraphNode *n;
|
|
RListIter *it;
|
|
|
|
int *color = R_NEWS0 (int, g->last_index);
|
|
if (color) {
|
|
r_list_foreach (g->nodes, it, n) {
|
|
if (color[n->idx] == WHITE_COLOR) {
|
|
dfs_node (g, n, vis, color, true);
|
|
}
|
|
}
|
|
free (color);
|
|
}
|
|
}
|
|
|
|
typedef struct _dfs_inserter {
|
|
RGraph *g;
|
|
HtUP *reverse; //reverse lookup of nodes
|
|
// RList *mo; //multiple out nodes
|
|
RList *mi; //multiple in nodes
|
|
ut32 idx;
|
|
bool fail;
|
|
} DfsInserter;
|
|
|
|
static void _dfs_ins_node(RGraphNode *n, RGraphVisitor *vi) {
|
|
DfsInserter *di = (DfsInserter *)vi->data;
|
|
if (di->fail) {
|
|
return;
|
|
}
|
|
RGraphDomNode *dn = R_NEW0 (RGraphDomNode);
|
|
if (!dn) {
|
|
di->fail = true;
|
|
return;
|
|
}
|
|
RGraphNode *node = r_graph_add_nodef (di->g, dn, free);
|
|
if (!node) {
|
|
free (dn);
|
|
di->fail = true;
|
|
return;
|
|
}
|
|
dn->node = n;
|
|
ht_up_insert (di->reverse, (ut64)(size_t)n, node);
|
|
if ((r_list_length (n->in_nodes) > 1 ) && (di->idx)) {
|
|
r_list_append (di->mi, node);
|
|
}
|
|
dn->idx = di->idx++;
|
|
}
|
|
|
|
static void _dfs_ins_edge(const RGraphEdge *e, RGraphVisitor *vi) {
|
|
DfsInserter *di = (DfsInserter *)vi->data;
|
|
if (di->fail) {
|
|
return;
|
|
}
|
|
bool found;
|
|
RGraphNode *from = (RGraphNode *)ht_up_find (di->reverse, (ut64)(size_t)e->from, &found);
|
|
if (!found) {
|
|
_dfs_ins_node (e->from, vi);
|
|
if (di->fail) {
|
|
return;
|
|
}
|
|
from = (RGraphNode *)ht_up_find (di->reverse, (ut64)(size_t)e->from, &found);
|
|
if (!found) {
|
|
return;
|
|
}
|
|
}
|
|
RGraphNode *to = (RGraphNode *)ht_up_find (di->reverse, (ut64)(size_t)e->to, &found);
|
|
if (!found) {
|
|
_dfs_ins_node (e->to, vi);
|
|
if (di->fail) {
|
|
return;
|
|
}
|
|
to = (RGraphNode *)ht_up_find (di->reverse, (ut64)(size_t)e->to, &found);
|
|
if (!found) {
|
|
return;
|
|
}
|
|
}
|
|
r_graph_add_edge (di->g, from, to);
|
|
}
|
|
|
|
R_API RGraph *r_graph_dom_tree(RGraph *graph, RGraphNode *root) {
|
|
R_RETURN_VAL_IF_FAIL (graph && root, NULL);
|
|
RGraph *g = r_graph_new ();
|
|
if (!g) {
|
|
return NULL;
|
|
}
|
|
DfsInserter di = {g, ht_up_new0 (), r_list_new (), 0, false};
|
|
if (!di.mi) {
|
|
ht_up_free (di.reverse);
|
|
r_graph_free (g);
|
|
return NULL;
|
|
}
|
|
if (!di.reverse) {
|
|
r_list_free (di.mi);
|
|
r_graph_free (g);
|
|
return NULL;
|
|
}
|
|
RGraphVisitor vi = { NULL, NULL, _dfs_ins_edge, NULL, NULL, &di};
|
|
//create a spanning tree
|
|
r_graph_dfs_node (graph, root, &vi);
|
|
if (di.fail) {
|
|
r_list_free (di.mi);
|
|
ht_up_free (di.reverse);
|
|
r_graph_free (g);
|
|
return NULL;
|
|
}
|
|
while (r_list_length (di.mi)) {
|
|
RGraphNode *n = r_list_pop_head (di.mi);
|
|
RGraphNode *p = (RGraphNode *)r_list_get_n (n->in_nodes, 0);
|
|
if (p && ((RGraphDomNode *)(p->data))->idx == 0) {
|
|
//parent is root node
|
|
continue;
|
|
}
|
|
RGraphDomNode *dn = (RGraphDomNode *)n->data;
|
|
RGraphNode *max_n = NULL, *min_n = NULL;
|
|
RListIter *iter;
|
|
RGraphNode *nn;
|
|
r_list_foreach (dn->node->in_nodes, iter, nn) {
|
|
RGraphNode *in = (RGraphNode *)ht_up_find (di.reverse, (ut64)(size_t)nn, NULL);
|
|
if (nn == root) {
|
|
r_graph_del_edge (g, (RGraphNode *)r_list_get_n (n->in_nodes, 0), n);
|
|
r_graph_add_edge (g, in, n);
|
|
goto cont;
|
|
}
|
|
if (!max_n || (((RGraphDomNode *)(max_n->data))->idx < ((RGraphDomNode *)(in->data))->idx)) {
|
|
max_n = in;
|
|
}
|
|
if (!min_n || (((RGraphDomNode *)(min_n->data))->idx > ((RGraphDomNode *)(in->data))->idx)) {
|
|
min_n = in;
|
|
}
|
|
}
|
|
while (((RGraphDomNode *)max_n->data)->idx > dn->idx) {
|
|
max_n = (RGraphNode *)r_list_get_n (max_n->in_nodes, 0);
|
|
}
|
|
// at this point max_n refers to the semi dominator (i hope this is correct)
|
|
RGraphNode *dom = min_n;
|
|
while (((RGraphDomNode *)max_n->data)->idx < ((RGraphDomNode *)dom->data)->idx) {
|
|
dom = (RGraphNode *)r_list_get_n (dom->in_nodes, 0);
|
|
}
|
|
// dom <= sdom
|
|
r_graph_del_edge (g, p, n);
|
|
r_graph_add_edge (g, dom, n);
|
|
cont:;
|
|
}
|
|
r_list_free (di.mi);
|
|
ht_up_free (di.reverse);
|
|
RListIter *iter;
|
|
RGraphNode *n;
|
|
r_list_foreach (g->nodes, iter, n) {
|
|
RGraphDomNode *dn = (RGraphDomNode *)n->data;
|
|
n->free = NULL;
|
|
n->data = dn->node;
|
|
free (dn);
|
|
}
|
|
return g;
|
|
}
|
|
|
|
static void _invert_edges (RGraph *g) {
|
|
RListIter *iter;
|
|
RGraphNode *n;
|
|
r_list_foreach (g->nodes, iter, n) {
|
|
n->in_nodes = (RList *)(((size_t)n->in_nodes) ^ ((size_t)n->out_nodes));
|
|
n->out_nodes = (RList *)(((size_t)n->in_nodes) ^ ((size_t)n->out_nodes));
|
|
n->in_nodes = (RList *)(((size_t)n->in_nodes) ^ ((size_t)n->out_nodes));
|
|
}
|
|
}
|
|
|
|
R_API RGraph *r_graph_pdom_tree(RGraph *graph, RGraphNode *root) {
|
|
R_RETURN_VAL_IF_FAIL (graph && root, NULL);
|
|
_invert_edges (graph);
|
|
RGraph *g = r_graph_dom_tree (graph, root);
|
|
_invert_edges (graph);
|
|
if (g) {
|
|
_invert_edges (graph);
|
|
}
|
|
return g;
|
|
}
|