Skip to content

Commit

Permalink
Enable generation of inter-procedual control flow graph. (#4001)
Browse files Browse the repository at this point in the history
  • Loading branch information
Rot127 authored Dec 14, 2023
1 parent c7cd1a9 commit c0abdb8
Show file tree
Hide file tree
Showing 14 changed files with 243 additions and 16 deletions.
10 changes: 9 additions & 1 deletion librz/analysis/function.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,15 @@ RZ_API bool rz_analysis_function_delete(RzAnalysisFunction *fcn) {
return rz_list_delete_data(fcn->analysis->fcns, fcn);
}

RZ_API RzAnalysisFunction *rz_analysis_get_function_at(RzAnalysis *analysis, ut64 addr) {
/**
* \brief Returns the function which has its entrypoint at \p addr or NULL if non was found.
*
* \param analysis The current RzAnalysis.
* \param addr The address of the function to get.
*
* \return The function with an entrypoint at \p addr or NULL if non was found.
*/
RZ_API RzAnalysisFunction *rz_analysis_get_function_at(const RzAnalysis *analysis, ut64 addr) {
bool found = false;
RzAnalysisFunction *f = ht_up_find(analysis->ht_addr_fun, addr, &found);
if (f && found) {
Expand Down
6 changes: 3 additions & 3 deletions librz/analysis/xrefs.c
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ RZ_API ut64 rz_analysis_xrefs_count(RzAnalysis *analysis) {
return ret;
}

static RZ_OWN RzList /*<RzAnalysisXRef *>*/ *fcn_get_refs(RzAnalysisFunction *fcn, HtUP *ht) {
static RZ_OWN RzList /*<RzAnalysisXRef *>*/ *fcn_get_refs(const RzAnalysisFunction *fcn, HtUP *ht) {
RzListIter *iter;
RzAnalysisBlock *bb;
RzList *list = rz_analysis_xref_list_new();
Expand All @@ -293,12 +293,12 @@ static RZ_OWN RzList /*<RzAnalysisXRef *>*/ *fcn_get_refs(RzAnalysisFunction *fc
return list;
}

RZ_API RZ_OWN RzList /*<RzAnalysisXRef *>*/ *rz_analysis_function_get_xrefs_from(RzAnalysisFunction *fcn) {
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(RzAnalysisFunction *fcn) {
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);
}
Expand Down
97 changes: 96 additions & 1 deletion librz/core/cgraph.c
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
// SPDX-FileCopyrightText: 2022 imbillow <[email protected]>
// SPDX-FileCopyrightText: 2009-2020 pancake <[email protected]>
// SPDX-FileCopyrightText: 2009-2020 nibble <[email protected]>
// SPDX-FileCopyrightText: 2023 Rot127 <[email protected]>
// SPDX-License-Identifier: LGPL-3.0-only

#include <rz_analysis.h>
#include <rz_list.h>
#include <rz_core.h>
#include <rz_util/rz_graph_drawable.h>
#include "core_private.h"
#include <rz_util/ht_up.h>
#include <rz_util/rz_graph.h>
#include <rz_util/rz_th_ht.h>

static inline bool is_between(ut64 a, ut64 x, ut64 b) {
return (a == UT64_MAX && b == UT64_MAX) || RZ_BETWEEN(a, x, b);
Expand Down Expand Up @@ -484,6 +489,9 @@ RZ_API RZ_OWN RzGraph /*<RzGraphNodeInfo *>*/ *rz_core_graph(RzCore *core, RzCor
case RZ_CORE_GRAPH_TYPE_IL:
graph = rz_core_graph_il(core, addr);
break;
case RZ_CORE_GRAPH_TYPE_ICFG:
graph = rz_core_graph_icfg(core);
break;
case RZ_CORE_GRAPH_TYPE_DIFF:
default:
rz_warn_if_reached();
Expand Down Expand Up @@ -600,7 +608,7 @@ RZ_IPI bool rz_core_graph_print(RzCore *core, ut64 addr, RzCoreGraphType type, R
return false;
}
bool is_il = type == RZ_CORE_GRAPH_TYPE_IL;
core->graph->is_callgraph = type == RZ_CORE_GRAPH_TYPE_FUNCALL;
core->graph->is_callgraph = (type == RZ_CORE_GRAPH_TYPE_FUNCALL || type == RZ_CORE_GRAPH_TYPE_ICFG);
core->graph->is_il = is_il;
rz_core_graph_print_graph(core, g, format, !is_il);
rz_graph_free(g);
Expand Down Expand Up @@ -773,3 +781,90 @@ RZ_API RZ_OWN RzGraph /*<RzGraphNodeInfo *>*/ *rz_core_graph_il(RZ_NONNULL RzCor
}
return graph;
}

/**
* \brief Returns the graph node of a given \p fcn. If the function
* is not yet added as node to the graph, it adds it to the graph and returns its reference.
*
* \param icfg The iCFG to fill.
* \param graph_idx Hash table to track the graph indices of each function address.
* \param fcn The function to add.
* \param existed Is set to true if the node was already in the graph.
*
* \return The GraphNode.
*/
static RZ_OWN RzGraphNode *get_graph_node_of_fcn(RZ_BORROW RzGraph /*<RzGraphNodeInfo *>*/ *icfg, RZ_BORROW HtUU *graph_idx, const RzAnalysisFunction *fcn) {
rz_return_val_if_fail(icfg && graph_idx && fcn, NULL);
bool found = false;
ut64 i = ht_uu_find(graph_idx, fcn->addr, &found);
if (found) {
// Node already added, get it.
return rz_graph_get_node(icfg, i);
}
ht_uu_insert(graph_idx, fcn->addr, rz_list_length(rz_graph_get_nodes(icfg)));
return rz_graph_add_node_info(icfg, fcn->name, NULL, fcn->addr);
}

/**
* \brief Adds all call xrefs from \p fcn as edges to the iCFG
* and recurses into each of them.
*
* \param analysis The current RzAnalysis.
* \param icfg The iCFG to fill.
* \param graph_idx Hash table to track the graph node indices for each function address.
* \param fcn The function to add.
*/
static void extend_icfg(const RzAnalysis *analysis, RZ_BORROW RzGraph /*<RzGraphNodeInfo *>*/ *icfg, RZ_BORROW HtUU *graph_idx, const RzAnalysisFunction *fcn) {
rz_return_if_fail(analysis && icfg && graph_idx && fcn);
RzGraphNode *from_node = get_graph_node_of_fcn(icfg, graph_idx, fcn);
RzListIter *it;
const RzAnalysisXRef *xref;
rz_list_foreach (rz_analysis_function_get_xrefs_from(fcn), it, xref) {
if (xref->type != RZ_ANALYSIS_XREF_TYPE_CALL) {
continue;
}
const RzAnalysisFunction *called_fcn = rz_analysis_get_function_at(analysis, xref->to);
if (!called_fcn) {
// Either a faulty entry or a GOT entry
continue;
}
RzGraphNode *to_node = get_graph_node_of_fcn(icfg, graph_idx, called_fcn);
if (rz_graph_adjacent(icfg, from_node, to_node)) {
// Edge already added and walked. Don't recurse.
continue;
}
rz_graph_add_edge(icfg, from_node, to_node);
// Recurse into called function.
extend_icfg(analysis, icfg, graph_idx, called_fcn);
}
}

/**
* \brief Get the inter-procedual control flow graph of the binary.
* It uses the already discovered functions and their xrefs.
*
* \param core The current core.
*
* \return The iCFG of the binary or NULL in case of failure.
*/
RZ_API RZ_OWN RzGraph /*<RzGraphNodeInfo *>*/ *rz_core_graph_icfg(RZ_NONNULL RzCore *core) {
rz_return_val_if_fail(core && core->analysis, NULL);
const RzList *fcns = core->analysis->fcns;
RzGraph *graph = rz_graph_new();
if (!graph) {
return NULL;
}
if (rz_list_length(fcns) < 1) {
RZ_LOG_WARN("Cannot build iCFG without discovered functions. Did you run 'aac' and 'aap'?\n");
return NULL;
}

HtUU *graph_idx = ht_uu_new0();
RzListIter *it;
const RzAnalysisFunction *fcn;
rz_list_foreach (fcns, it, fcn) {
extend_icfg(core->analysis, graph, graph_idx, fcn);
}
ht_uu_free(graph_idx);
return graph;
}
5 changes: 5 additions & 0 deletions librz/core/cmd/cmd_analysis.c
Original file line number Diff line number Diff line change
Expand Up @@ -4710,6 +4710,11 @@ RZ_IPI RzCmdStatus rz_analysis_graph_il_handler(RzCore *core, int argc, const ch
return bool2status(rz_core_graph_print(core, core->offset, RZ_CORE_GRAPH_TYPE_IL, format));
}

RZ_IPI RzCmdStatus rz_analysis_graph_icfg_handler(RzCore *core, int argc, const char **argv) {
const RzCoreGraphFormat format = rz_core_graph_format_from_string(argv[1]);
return bool2status(rz_core_graph_print(core, core->offset, RZ_CORE_GRAPH_TYPE_ICFG, format));
}

RZ_IPI RzCmdStatus rz_analysis_graph_custom_handler(RzCore *core, int argc, const char **argv) {
const RzCoreGraphFormat format = rz_core_graph_format_from_string(argv[1]);
return bool2status(rz_core_agraph_print(core, format));
Expand Down
8 changes: 8 additions & 0 deletions librz/core/cmd_descs/cmd_analysis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1060,6 +1060,14 @@ commands:
type: RZ_CMD_ARG_TYPE_CHOICES
default_value: "ascii"
choices_cb: rz_analysis_graph_format_choices
- name: agCi
summary: Inter-procedual control flow graph
cname: analysis_graph_icfg
args:
- name: format
type: RZ_CMD_ARG_TYPE_CHOICES
default_value: "ascii"
choices_cb: rz_analysis_graph_format_choices
- name: agf
summary: Basic blocks function graph
cname: analysis_graph_bb_function
Expand Down
19 changes: 19 additions & 0 deletions librz/core/cmd_descs/cmd_descs.c
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ static const RzCmdDescArg analysis_graph_dataref_args[2];
static const RzCmdDescArg analysis_graph_dataref_global_args[2];
static const RzCmdDescArg analysis_graph_callgraph_function_args[2];
static const RzCmdDescArg analysis_graph_callgraph_global_args[2];
static const RzCmdDescArg analysis_graph_icfg_args[2];
static const RzCmdDescArg analysis_graph_bb_function_args[2];
static const RzCmdDescArg analysis_graph_imports_args[2];
static const RzCmdDescArg analysis_graph_refs_args[2];
Expand Down Expand Up @@ -4076,6 +4077,21 @@ static const RzCmdDescHelp analysis_graph_callgraph_global_help = {
.args = analysis_graph_callgraph_global_args,
};

static const RzCmdDescArg analysis_graph_icfg_args[] = {
{
.name = "format",
.type = RZ_CMD_ARG_TYPE_CHOICES,
.default_value = "ascii",
.choices.choices_cb = rz_analysis_graph_format_choices,

},
{ 0 },
};
static const RzCmdDescHelp analysis_graph_icfg_help = {
.summary = "Inter-procedual control flow graph",
.args = analysis_graph_icfg_args,
};

static const RzCmdDescArg analysis_graph_bb_function_args[] = {
{
.name = "format",
Expand Down Expand Up @@ -19366,6 +19382,9 @@ RZ_IPI void rzshell_cmddescs_init(RzCore *core) {
RzCmdDesc *analysis_graph_callgraph_global_cd = rz_cmd_desc_argv_new(core->rcmd, ag_cd, "agC", rz_analysis_graph_callgraph_global_handler, &analysis_graph_callgraph_global_help);
rz_warn_if_fail(analysis_graph_callgraph_global_cd);

RzCmdDesc *analysis_graph_icfg_cd = rz_cmd_desc_argv_new(core->rcmd, ag_cd, "agCi", rz_analysis_graph_icfg_handler, &analysis_graph_icfg_help);
rz_warn_if_fail(analysis_graph_icfg_cd);

RzCmdDesc *analysis_graph_bb_function_cd = rz_cmd_desc_argv_new(core->rcmd, ag_cd, "agf", rz_analysis_graph_bb_function_handler, &analysis_graph_bb_function_help);
rz_warn_if_fail(analysis_graph_bb_function_cd);

Expand Down
2 changes: 2 additions & 0 deletions librz/core/cmd_descs/cmd_descs.h
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,8 @@ RZ_IPI RzCmdStatus rz_analysis_graph_dataref_global_handler(RzCore *core, int ar
RZ_IPI RzCmdStatus rz_analysis_graph_callgraph_function_handler(RzCore *core, int argc, const char **argv);
// "agC"
RZ_IPI RzCmdStatus rz_analysis_graph_callgraph_global_handler(RzCore *core, int argc, const char **argv);
// "agCi"
RZ_IPI RzCmdStatus rz_analysis_graph_icfg_handler(RzCore *core, int argc, const char **argv);
// "agf"
RZ_IPI RzCmdStatus rz_analysis_graph_bb_function_handler(RzCore *core, int argc, const char **argv);
// "agi"
Expand Down
2 changes: 1 addition & 1 deletion librz/core/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -892,7 +892,7 @@ static const char *rizin_argv[] = {
"aei", "aeim", "aeip", "aek", "aek-", "aeli", "aelir", "aep?", "aep", "aep-", "aepc",
"aets?", "aets+", "aets-", "aes", "aesp", "aesb", "aeso", "aesou", "aess", "aesu", "aesue", "aetr", "aex",
"aF",
"ag?", "ag", "aga", "agA", "agc", "agC", "agd", "agf", "agi", "agr", "agR", "agx", "agg", "ag-",
"ag?", "ag", "aga", "agA", "agc", "agC", "agCi", "agd", "agf", "agi", "agr", "agR", "agx", "agg", "ag-",
"agn?", "agn", "agn-", "age?", "age", "age-",
"agl", "agfl",
"ah?", "ah", "ah.", "ah-", "ah*", "aha", "ahb", "ahc", "ahe", "ahf", "ahh", "ahi?", "ahi", "ahj", "aho",
Expand Down
7 changes: 3 additions & 4 deletions librz/include/rz_analysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -1486,8 +1486,7 @@ RZ_API RzAnalysisFunction *rz_analysis_create_function(RzAnalysis *analysis, con
// returns all functions that have a basic block containing the given address
RZ_API RzList /*<RzAnalysisFunction *>*/ *rz_analysis_get_functions_in(RzAnalysis *analysis, ut64 addr);

// returns the function that has its entrypoint at addr or NULL
RZ_API RzAnalysisFunction *rz_analysis_get_function_at(RzAnalysis *analysis, ut64 addr);
RZ_API RzAnalysisFunction *rz_analysis_get_function_at(const RzAnalysis *analysis, ut64 addr);

RZ_API bool rz_analysis_function_delete(RzAnalysisFunction *fcn);

Expand Down Expand Up @@ -1733,8 +1732,8 @@ RZ_API RzAnalysisXRefType rz_analysis_xrefs_type(char ch);
RZ_API RZ_OWN RzList /*<RzAnalysisXRef *>*/ *rz_analysis_xrefs_get_to(RzAnalysis *analysis, ut64 addr);
RZ_API RZ_OWN RzList /*<RzAnalysisXRef *>*/ *rz_analysis_xrefs_get_from(RzAnalysis *analysis, ut64 addr);
RZ_API RZ_OWN RzList /*<RzAnalysisXRef *>*/ *rz_analysis_xrefs_list(RzAnalysis *analysis);
RZ_API RZ_OWN RzList /*<RzAnalysisXRef *>*/ *rz_analysis_function_get_xrefs_from(RzAnalysisFunction *fcn);
RZ_API RZ_OWN RzList /*<RzAnalysisXRef *>*/ *rz_analysis_function_get_xrefs_to(RzAnalysisFunction *fcn);
RZ_API RZ_OWN RzList /*<RzAnalysisXRef *>*/ *rz_analysis_function_get_xrefs_from(const RzAnalysisFunction *fcn);
RZ_API RZ_OWN RzList /*<RzAnalysisXRef *>*/ *rz_analysis_function_get_xrefs_to(const RzAnalysisFunction *fcn);
RZ_API bool rz_analysis_xrefs_set(RzAnalysis *analysis, ut64 from, ut64 to, RzAnalysisXRefType type);
RZ_API bool rz_analysis_xrefs_deln(RzAnalysis *analysis, ut64 from, ut64 to, RzAnalysisXRefType type);
RZ_API bool rz_analysis_xref_del(RzAnalysis *analysis, ut64 from, ut64 to);
Expand Down
2 changes: 2 additions & 0 deletions librz/include/rz_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,7 @@ typedef enum {
RZ_CORE_GRAPH_TYPE_CUSTOM, ///< Custom graph
RZ_CORE_GRAPH_TYPE_NORMAL, ///< Normal graph
RZ_CORE_GRAPH_TYPE_IL, ///< RzIL graph
RZ_CORE_GRAPH_TYPE_ICFG, ///< Inter-procedual control flow graph
RZ_CORE_GRAPH_TYPE_UNK ///< Unknown graph
} RzCoreGraphType;

Expand All @@ -784,6 +785,7 @@ RZ_API RZ_OWN RzGraph /*<RzGraphNodeInfo *>*/ *rz_core_graph_function(RzCore *co
RZ_API RZ_OWN RzGraph /*<RzGraphNodeInfo *>*/ *rz_core_graph_line(RzCore *core, ut64 addr);
RZ_API RZ_OWN RzGraph /*<RzGraphNodeInfo *>*/ *rz_core_graph_il(RZ_NONNULL RzCore *core, ut64 addr);
RZ_API RZ_OWN RzGraph /*<RzGraphNodeInfo *>*/ *rz_core_graph(RzCore *core, RzCoreGraphType type, ut64 addr);
RZ_API RZ_OWN RzGraph /*<RzGraphNodeInfo *>*/ *rz_core_graph_icfg(RZ_NONNULL RzCore *core);

RZ_API RzCoreGraphFormat rz_core_graph_format_from_string(RZ_NULLABLE const char *x);
RZ_API RzCoreGraphType rz_core_graph_type_from_string(RZ_NULLABLE const char *x);
Expand Down
3 changes: 1 addition & 2 deletions librz/include/rz_util/rz_graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ RZ_API RzListIter *rz_graph_node_iter(const RzGraph *g, unsigned int idx);
RZ_API void rz_graph_reset(RzGraph *g);
RZ_API RzGraphNode *rz_graph_add_node(RzGraph *g, void *data);
RZ_API RzGraphNode *rz_graph_add_nodef(RzGraph *g, void *data, RzListFree user_free);
// XXX 'n' is destroyed after calling this function.
RZ_API void rz_graph_del_node(RzGraph *g, RzGraphNode *n);
RZ_API void rz_graph_del_node(RzGraph *g, RZ_OWN RzGraphNode *n);
RZ_API void rz_graph_add_edge(RzGraph *g, RzGraphNode *from, RzGraphNode *to);
RZ_API void rz_graph_add_edge_at(RzGraph *g, RzGraphNode *from, RzGraphNode *to, int nth);
RZ_API RzGraphNode *rz_graph_node_split_forward(RzGraph *g, RzGraphNode *split_me, void *data);
Expand Down
13 changes: 9 additions & 4 deletions librz/util/graph.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,12 +172,17 @@ RZ_API RzGraphNode *rz_graph_add_nodef(RzGraph *graph, void *data, RzListFree us
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 */
RZ_API void rz_graph_del_node(RzGraph *t, RzGraphNode *n) {
/**
* \brief Deletes the node \p n from the graph \p t and frees the \p n.
*
* \param t The graph to operate on.
* \param n The node to delete.
*/
RZ_API void rz_graph_del_node(RzGraph *t, RZ_OWN RzGraphNode *n) {
rz_return_if_fail(t);
RzGraphNode *gn;
RzListIter *it;
if (!n) {
if (!n || !rz_list_contains(t->nodes, n)) {
return;
}
rz_list_foreach (n->in_nodes, it, gn) {
Expand Down
Loading

0 comments on commit c0abdb8

Please sign in to comment.