Graph refactor ##refactor (#17631)

* Support more output modes for class inheritence graph.
* Refactor codexrefs and importxrefs graphs.
* Add ag_w for the commands using new mechanism.
* Separate drawable graph from r_graph.
This commit is contained in:
karliss 2020-10-06 05:57:51 +03:00 committed by GitHub
parent 02edb130d7
commit bdb88df38c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 827 additions and 168 deletions

View File

@ -2,6 +2,7 @@
#include <r_anal.h>
#include <r_vector.h>
#include <r_util/r_graph_drawable.h>
#include "../include/r_anal.h"
#include "../include/r_util/r_graph.h"
@ -1264,15 +1265,10 @@ R_API RGraph *r_anal_class_get_inheritance_graph(RAnal *anal) {
// create nodes
RGraphNode *curr_node = ht_pp_find (hashmap, name, NULL);
if (!curr_node) {
RGraphNodeInfo *data = r_graph_create_node_info (strdup (name), NULL, 0);
if (!data) {
goto failure;
}
curr_node = r_graph_add_node (class_graph, data);
curr_node = r_graph_add_node_info (class_graph, name, NULL, 0);
if (!curr_node) {
goto failure;
}
curr_node->free = r_graph_free_node_info;
ht_pp_insert (hashmap, name, curr_node);
}
// create edges between node and it's parents
@ -1283,15 +1279,10 @@ R_API RGraph *r_anal_class_get_inheritance_graph(RAnal *anal) {
RGraphNode *base_node = ht_pp_find (hashmap, base->class_name, &base_found);
// If base isn't processed, do it now
if (!base_found) {
RGraphNodeInfo *data = r_graph_create_node_info (strdup (base->class_name), NULL, 0);
if (!data) {
goto failure;
}
base_node = r_graph_add_node (class_graph, data);
base_node = r_graph_add_node_info (class_graph, base->class_name, NULL, 0);
if (!base_node) {
goto failure;
}
base_node->free = r_graph_free_node_info;
ht_pp_insert (hashmap, base->class_name, base_node);
}
r_graph_add_edge (class_graph, base_node, curr_node);

View File

@ -2,6 +2,7 @@
#include <r_core.h>
#include <r_cons.h>
#include <r_util/r_graph_drawable.h>
#include <ctype.h>
#include <limits.h>
@ -4935,6 +4936,7 @@ R_API RAGraph *create_agraph_from_graph(const RGraph/*<RGraphNodeInfo>*/ *graph)
if (!result_agraph) {
return NULL;
}
result_agraph->need_reload_nodes = false;
// Cache lookup to build edges
HtPP /*<RGraphNode *node, RANode *anode>*/ *hashmap = ht_pp_new0 ();
if (!hashmap) {

View File

@ -6,6 +6,7 @@
#include <r_core.h>
#include <r_bin.h>
#include <ht_uu.h>
#include <r_util/r_graph_drawable.h>
#include <string.h>
@ -2280,43 +2281,64 @@ R_API void r_core_anal_coderefs(RCore *core, ut64 addr) {
}
}
R_API void r_core_anal_importxrefs(RCore *core) {
RBinInfo *info = r_bin_get_info (core->bin);
RBinObject *obj = r_bin_cur_object (core->bin);
bool lit = info ? info->has_lit: false;
bool va = core->io->va || core->bin->is_debugger;
RListIter *iter;
RBinImport *imp;
if (!obj) {
return;
}
r_list_foreach (obj->imports, iter, imp) {
ut64 addr = lit ? r_core_bin_impaddr (core->bin, va, imp->name): 0;
if (addr) {
r_core_anal_codexrefs (core, addr);
} else {
r_cons_printf ("agn %s\n", imp->name);
}
}
}
R_API void r_core_anal_codexrefs(RCore *core, ut64 addr) {
static void add_single_addr_xrefs(RCore *core, ut64 addr, RGraph *graph) {
r_return_if_fail (graph);
RFlagItem *f = r_flag_get_at (core->flags, addr, false);
char *me = (f && f->offset == addr)
? r_str_new (f->name) : r_str_newf ("0x%"PFMT64x, addr);
r_cons_printf ("agn %s\n", me);
? r_str_new (f->name)
: r_str_newf ("0x%" PFMT64x, addr);
RGraphNode *curr_node = r_graph_add_node_info (graph, me, NULL, addr);
R_FREE (me);
if (!curr_node) {
return;
}
RListIter *iter;
RAnalRef *ref;
RList *list = r_anal_xrefs_get (core->anal, addr);
r_list_foreach (list, iter, ref) {
RFlagItem *item = r_flag_get_i (core->flags, ref->addr);
const char *src = item? item->name: sdb_fmt ("0x%08"PFMT64x, ref->addr);
r_cons_printf ("agn %s\n", src);
r_cons_printf ("age %s %s\n", src, me);
char *src = item? r_str_new (item->name): r_str_newf ("0x%08" PFMT64x, ref->addr);
RGraphNode *reference_from = r_graph_add_node_info (graph, src, NULL, ref->addr);
free (src);
r_graph_add_edge (graph, reference_from, curr_node);
}
r_list_free (list);
free (me);
}
R_API RGraph *r_core_anal_importxrefs(RCore *core) {
RBinInfo *info = r_bin_get_info (core->bin);
RBinObject *obj = r_bin_cur_object (core->bin);
bool lit = info? info->has_lit: false;
bool va = core->io->va || core->bin->is_debugger;
RListIter *iter;
RBinImport *imp;
if (!obj) {
return NULL;
}
RGraph *graph = r_graph_new ();
if (!graph) {
return NULL;
}
r_list_foreach (obj->imports, iter, imp) {
ut64 addr = lit ? r_core_bin_impaddr (core->bin, va, imp->name): 0;
if (addr) {
add_single_addr_xrefs (core, addr, graph);
} else {
r_graph_add_node_info (graph, imp->name, NULL, 0);
}
}
return graph;
}
R_API RGraph *r_core_anal_codexrefs(RCore *core, ut64 addr) {
RGraph *graph = r_graph_new ();
if (!graph) {
return NULL;
}
add_single_addr_xrefs (core, addr, graph);
return graph;
}
static int RAnalRef_cmp(const RAnalRef* ref1, const RAnalRef* ref2) {

View File

@ -1,6 +1,7 @@
/* radare - LGPL - Copyright 2009-2020 - pancake, maijin */
#include <r_core.h>
#include <r_util/r_graph_drawable.h>
#define MAX_SCAN_SIZE 0x7ffffff
@ -25,7 +26,7 @@ static const char *help_msg_a[] = {
"ai", " [addr]", "address information (show perms, stack, heap, ...)",
"aj", "", "same as a* but in json (aflj)",
"aL", "", "list all asm/anal plugins (e asm.arch=?)",
"an"," [name] [@addr]","show/rename/create whatever flag/function is used at addr",
"an", " [name] [@addr]", "show/rename/create whatever flag/function is used at addr",
"ao", "[?] [len]", "analyze Opcodes (or emulate it)",
"aO", "[?] [len]", "Analyze N instructions in M bytes",
"ap", "", "find prelude for current offset",
@ -8312,10 +8313,8 @@ static char *getViewerPath(void) {
return NULL;
}
static char* graph_cmd(RCore *core, char *r2_cmd, const char *save_path) {
static char *dot_executable_path(void) {
const char *dot = "dot";
char *cmd = NULL;
const char *ext = r_config_get (core->config, "graph.gv.format");
char *dotPath = r_file_path (dot);
if (!strcmp (dotPath, dot)) {
free (dotPath);
@ -8323,24 +8322,62 @@ static char* graph_cmd(RCore *core, char *r2_cmd, const char *save_path) {
dotPath = r_file_path (dot);
if (!strcmp (dotPath, dot)) {
free (dotPath);
return r_str_new ("agf");
return NULL;
}
}
return dotPath;
}
static bool convert_dot_to_image(RCore *core, const char *dot_file, const char *save_path) {
char *dot = dot_executable_path ();
bool result = false;
if (!dot) {
eprintf ("Graphviz not found\n");
return false;
}
const char *ext = r_config_get (core->config, "graph.gv.format");
char *cmd = NULL;
if (save_path && *save_path) {
cmd = r_str_newf ("%s > a.dot;!%s -T%s -o%s a.dot;",
r2_cmd, dot, ext, save_path);
cmd = r_str_newf ("!%s -T%s -o%s a.dot;", dot, ext, save_path);
} else {
char *viewer = getViewerPath();
if (viewer) {
cmd = r_str_newf ("%s > a.dot;!%s -T%s -oa.%s a.dot;!%s a.%s",
r2_cmd, dot, ext, ext, viewer, ext);
cmd = r_str_newf ("!%s -T%s -oa.%s a.dot;!%s a.%s",
dot, ext, ext, viewer, ext);
free (viewer);
} else {
eprintf ("Cannot find a valid picture viewer\n");
goto end;
}
}
free (dotPath);
return cmd;
r_core_cmd0 (core, cmd);
result = true;
end:
free (cmd);
free (dot);
return result;
}
static bool convert_dotcmd_to_image(RCore *core, char *r2_cmd, const char *save_path) {
char *cmd = NULL;
if (save_path && *save_path) {
r_cons_printf ("Saving to file '%s'...\n", save_path);
r_cons_flush ();
}
r_core_cmdf (core, "%s > a.dot", r2_cmd); // TODO: check error here
return convert_dot_to_image (core, "a.dot", save_path);
}
static bool convert_dot_str_to_image(RCore *core, char *str, const char *save_path) {
if (save_path && *save_path) {
r_cons_printf ("Saving to file '%s'...\n", save_path);
r_cons_flush ();
}
if (!r_file_dump ("a.dot", (const unsigned char *)str, -1, false)) {
return false;
}
return convert_dot_to_image (core, "a.dot", save_path);
}
static void agraph_print_edge_dot(RANode *from, RANode *to, void *user) {
@ -8381,7 +8418,7 @@ static void cmd_agraph_node(RCore *core, const char *input) {
}
body = r_str_append (body, "\n");
if (n_args > 2) {
color = atoi(args[2]);
color = atoi (args[2]);
}
} else {
body = strdup ("");
@ -8455,7 +8492,7 @@ static void cmd_agraph_edge(RCore *core, const char *input) {
}
}
R_API void r_core_agraph_print (RCore *core, int use_utf, const char *input) {
R_API void r_core_agraph_print(RCore *core, int use_utf, const char *input) {
if (use_utf != -1) {
r_config_set_i (core->config, "scr.utf8", use_utf);
}
@ -8467,7 +8504,7 @@ R_API void r_core_agraph_print (RCore *core, int use_utf, const char *input) {
r_config_get (core->config, "graph.title"));
r_agraph_print (core->graph);
break;
case 't':{ // "aggt" - tiny graph
case 't': { // "aggt" - tiny graph
core->graph->is_tiny = true;
int e = r_config_get_i (core->config, "graph.edges");
r_config_set_i (core->config, "graph.edges", 0);
@ -8475,7 +8512,7 @@ R_API void r_core_agraph_print (RCore *core, int use_utf, const char *input) {
r_config_set_i (core->config, "graph.edges", e);
core->graph->is_tiny = false;
break;
}
}
case 'k': // "aggk"
{
Sdb *db = r_agraph_get_sdb (core->graph);
@ -8495,7 +8532,7 @@ R_API void r_core_agraph_print (RCore *core, int use_utf, const char *input) {
core->graph->force_update_seek = true;
core->graph->need_set_layout = true;
core->graph->layout = r_config_get_i (core->config, "graph.layout");
int ov = r_cons_is_interactive ();
bool ov = r_cons_is_interactive ();
core->graph->need_update_dim = true;
int update_seek = r_core_visual_graph (core, core->graph, NULL, true);
r_config_set_i (core->config, "scr.interactive", ov);
@ -8514,12 +8551,13 @@ R_API void r_core_agraph_print (RCore *core, int use_utf, const char *input) {
r_cons_printf ("digraph code {\nrankdir=LR;\noutputorder=edgesfirst\ngraph [bgcolor=azure];\n"
"edge [arrowhead=normal, color=\"#3030c0\" style=bold weight=2];\n"
"node [fillcolor=white, style=filled shape=box "
"fontname=\"%s\" fontsize=\"8\"];\n", font);
"fontname=\"%s\" fontsize=\"8\"];\n",
font);
r_agraph_foreach (core->graph, agraph_print_node_dot, NULL);
r_agraph_foreach_edge (core->graph, agraph_print_edge_dot, NULL);
r_cons_printf ("}\n");
break;
}
}
case '*': // "agg*" -
r_agraph_foreach (core->graph, agraph_print_node, NULL);
r_agraph_foreach_edge (core->graph, agraph_print_edge, NULL);
@ -8540,25 +8578,185 @@ R_API void r_core_agraph_print (RCore *core, int use_utf, const char *input) {
pj_free (pj);
} break;
case 'g':
r_cons_printf ("graph\n[\n" "hierarchic 1\n" "label \"\"\n" "directed 1\n");
r_cons_printf ("graph\n[\n"
"hierarchic 1\n"
"label \"\"\n"
"directed 1\n");
r_agraph_foreach (core->graph, agraph_print_node_gml, NULL);
r_agraph_foreach_edge (core->graph, agraph_print_edge_gml, NULL);
r_cons_print ("]\n");
break;
case 'w':{ // "aggw"
case 'w': { // "aggw"
if (r_config_get_i (core->config, "graph.web")) {
r_core_cmd0 (core, "=H /graph/");
} else {
const char *filename = r_str_trim_head_ro (input + 1);
char *cmd = graph_cmd (core, "aggd", filename);
if (cmd && *cmd) {
if (input[1] == ' ') {
r_cons_printf ("Saving to file '%s'...\n", filename);
r_cons_flush ();
}
r_core_cmd0 (core, cmd);
convert_dotcmd_to_image (core, "aggd", filename);
}
break;
}
default:
eprintf ("Usage: see ag?\n");
}
}
static void print_graph_agg(RGraph /*RGraphNodeInfo*/ *graph) {
RGraphNodeInfo *print_node;
RGraphNode *node, *target;
RListIter *it, *edge_it;
r_list_foreach (graph->nodes, it, node) {
char *encbody;
int len;
print_node = node->data;
if (R_STR_ISNOTEMPTY (print_node->body)) {
len = strlen (print_node->body);
if (len > 0 && print_node->body[len - 1] == '\n') {
len--;
}
free (cmd);
encbody = r_base64_encode_dyn (print_node->body, len);
r_cons_printf ("agn \"%s\" base64:%s\n", print_node->title, encbody);
free (encbody);
} else {
r_cons_printf ("agn \"%s\"\n", print_node->title);
}
}
r_list_foreach (graph->nodes, it, node) {
print_node = node->data;
r_list_foreach (node->out_nodes, edge_it, target) {
RGraphNodeInfo *to = target->data;
r_cons_printf ("age \"%s\" \"%s\"\n", print_node->title, to->title);
}
}
}
static char *print_graph_dot(RCore *core, RGraph /*<RGraphNodeInfo>*/ *graph) {
const char *font = r_config_get (core->config, "graph.font");
char *node_properties = r_str_newf ("fontname=\"%s\"", font);
char *result = r_graph_drawable_to_dot (graph, node_properties, NULL);
free (node_properties);
return result;
}
static void r_core_graph_print(RCore *core, RGraph /*<RGraphNodeInfo>*/ *graph, int use_utf, bool use_offset, const char *input) {
RAGraph *agraph = NULL;
RListIter *it;
RListIter *edge_it;
RGraphNode *graphNode, *target;
RGraphNodeInfo *print_node;
if (use_utf != -1) {
r_config_set_i (core->config, "scr.utf8", use_utf);
}
switch (*input) {
case 0:
case 't':
case 'k':
case 'v':
case 'i': {
agraph = create_agraph_from_graph (graph);
switch (*input) {
case 0:
agraph->can->linemode = r_config_get_i (core->config, "graph.linemode");
agraph->can->color = r_config_get_i (core->config, "scr.color");
r_agraph_set_title (agraph,
r_config_get (core->config, "graph.title"));
r_agraph_print (agraph);
break;
case 't': { // "ag_t" - tiny graph
agraph->is_tiny = true;
int e = r_config_get_i (core->config, "graph.edges");
r_config_set_i (core->config, "graph.edges", 0);
r_core_visual_graph (core, agraph, NULL, false);
r_config_set_i (core->config, "graph.edges", e);
break;
}
case 'k': // "ag_k"
{
Sdb *db = r_agraph_get_sdb (agraph);
char *o = sdb_querys (db, "null", 0, "*");
r_cons_print (o);
free (o);
break;
}
case 'v': // "ag_v"
case 'i': // "ag_i" - open current core->graph in interactive mode
{
RANode *ran = r_agraph_get_first_node (agraph);
if (ran) {
ut64 oseek = core->offset;
r_agraph_set_title (agraph, r_config_get (core->config, "graph.title"));
r_agraph_set_curnode (agraph, ran);
agraph->force_update_seek = true;
agraph->need_set_layout = true;
agraph->layout = r_config_get_i (core->config, "graph.layout");
bool ov = r_cons_is_interactive ();
agraph->need_update_dim = true;
int update_seek = r_core_visual_graph (core, agraph, NULL, true);
r_config_set_i (core->config, "scr.interactive", ov);
r_cons_show_cursor (true);
r_cons_enable_mouse (false);
if (update_seek != -1) {
r_core_seek (core, oseek, false);
}
} else {
eprintf ("This graph contains no nodes\n");
}
break;
}
}
break;
}
case 'd': { // "ag_d" - dot format
char *dot_text = print_graph_dot (core, graph);
if (dot_text) {
r_cons_print (dot_text);
free (dot_text);
}
break;
}
case '*': // "ag_*" -
print_graph_agg (graph);
break;
case 'J':
case 'j': {
PJ *pj = pj_new ();
if (pj) {
r_graph_drawable_to_json (graph, pj, use_offset);
r_cons_println (pj_string (pj));
pj_free (pj);
}
} break;
case 'g':
r_cons_printf ("graph\n[\n"
"hierarchic 1\n"
"label \"\"\n"
"directed 1\n");
r_list_foreach (graph->nodes, it, graphNode) {
print_node = graphNode->data;
r_cons_printf (" node [\n"
" id %d\n"
" label \"%s\"\n"
" ]\n",
graphNode->idx, print_node->title);
}
r_list_foreach (graph->nodes, it, graphNode) {
print_node = graphNode->data;
r_list_foreach (graphNode->out_nodes, edge_it, target) {
r_cons_printf (" edge [\n"
" source %d\n"
" target %d\n"
" ]\n",
graphNode->idx, target->idx);
}
}
r_cons_print ("]\n");
break;
case 'w': { // "ag_w"
const char *filename = r_str_trim_head_ro (input + 1);
char *dot_text = print_graph_dot (core, graph);
if (dot_text) {
convert_dot_str_to_image (core, dot_text, filename);
free (dot_text);
}
break;
}
@ -8576,11 +8774,11 @@ static void cmd_anal_graph(RCore *core, const char *input) {
case 0: // "agf"
r_core_visual_graph (core, NULL, NULL, false);
break;
case ' ':{ // "agf "
case ' ': { // "agf "
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, 0);
r_core_visual_graph (core, NULL, fcn, false);
break;
}
}
case 'v': // "agfv"
eprintf ("\rRendering graph...");
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, R_ANAL_FCN_TYPE_ROOT);
@ -8610,7 +8808,7 @@ static void cmd_anal_graph(RCore *core, const char *input) {
case 'j': // "agfj"
r_core_anal_graph (core, r_num_math (core->num, input + 2), R_CORE_ANAL_JSON);
break;
case 'J': {// "agfJ"
case 'J': { // "agfJ"
// Honor asm.graph=false in json as well
RConfigHold *hc = r_config_hold_new (core->config);
r_config_hold_i (hc, "asm.offset", NULL);
@ -8621,7 +8819,7 @@ static void cmd_anal_graph(RCore *core, const char *input) {
r_config_hold_restore (hc);
r_config_hold_free (hc);
break;
}
}
case 'g':{ // "agfg"
RAnalFunction *fcn = r_anal_get_fcn_in (core->anal, core->offset, 0);
r_core_print_bb_gml (core, fcn);
@ -8641,15 +8839,7 @@ static void cmd_anal_graph(RCore *core, const char *input) {
r_core_cmd0 (core, "=H /graph/");
} else {
char *cmdargs = r_str_newf ("agfd @ 0x%"PFMT64x, core->offset);
char *cmd = graph_cmd (core, cmdargs, input + 2);
if (cmd && *cmd) {
if (*(input + 2)) {
r_cons_printf ("Saving to file %s ...\n", input + 2);
r_cons_flush ();
}
r_core_cmd0 (core, cmd);
}
free (cmd);
convert_dotcmd_to_image (core, cmdargs, input + 2);
free (cmdargs);
}
break;
@ -8743,30 +8933,26 @@ static void cmd_anal_graph(RCore *core, const char *input) {
}
}
break;
case 'x': // "agx" cross refs
switch (input[1]) {
case '*': {
r_core_anal_codexrefs (core, core->offset);
}
break;
default: {
r_core_cmdf (core, "ag-; .agx* @ %"PFMT64u";", core->offset);
r_core_agraph_print(core, -1, input + 1);
break;
}
}
break;
case 'i': // "agi" import graph
switch (input[1]) {
case '*':
r_core_anal_importxrefs (core);
break;
default:
r_core_cmdf (core, "ag-; .agi*;");
r_core_agraph_print(core, -1, input + 1);
case 'x': {// "agx" cross refs
RGraph *graph = r_core_anal_codexrefs (core, core->offset);
if (!graph) {
eprintf ("Couldn't create graph");
break;
}
r_core_graph_print (core, graph, -1, true, input + 1);
r_graph_free (graph);
break;
}
case 'i': { // "agi" import graph
RGraph *graph = r_core_anal_importxrefs (core);
if (!graph) {
eprintf ("Couldn't create graph");
break;
}
r_core_graph_print (core, graph, -1, true, input + 1);
r_graph_free (graph);
break;
}
case 'c': // "agc"
switch (input[1]) {
case 'v':
@ -8775,7 +8961,7 @@ static void cmd_anal_graph(RCore *core, const char *input) {
case 'w':
case ' ': {
core->graph->is_callgraph = true;
r_core_cmdf (core, "ag-; .agc* @ %"PFMT64u"; agg%s;", core->offset, input + 1);
r_core_cmdf (core, "ag-; .agc* @ %" PFMT64u "; agg%s;", core->offset, input + 1);
core->graph->is_callgraph = false;
break;
}
@ -8877,25 +9063,21 @@ static void cmd_anal_graph(RCore *core, const char *input) {
case 'k':
case 'v':
case 'g': {
ut64 addr = input[2] ? r_num_math (core->num, input + 2) : core->offset;
ut64 addr = input[2]? r_num_math (core->num, input + 2): core->offset;
r_core_cmdf (core, "ag-; .agd* @ %"PFMT64u"; agg%s;", addr, input + 1);
break;
}
case 'd': {
ut64 addr = input[2] ? r_num_math (core->num, input + 2) : core->offset;
ut64 addr = input[2]? r_num_math (core->num, input + 2): core->offset;
r_core_gdiff_fcn (core, addr, core->offset);
r_core_anal_graph (core, addr, diff_opt);
break;
}
case 'w': {
char *cmdargs = r_str_newf ("agdd 0x%"PFMT64x, core->offset);
char *cmd = graph_cmd (core, cmdargs, input + 2);
if (cmd && *cmd) {
r_core_cmd0(core, cmd);
}
free(cmd);
free(cmdargs);
break;
convert_dotcmd_to_image (core, cmdargs, input + 2);
free (cmdargs);
break;
}
}
break;
@ -8908,15 +9090,7 @@ static void cmd_anal_graph(RCore *core, const char *input) {
r_core_cmd0 (core, "=H /graph/");
} else {
char *cmdargs = r_str_newf ("agfd @ 0x%"PFMT64x, core->offset);
char *cmd = graph_cmd (core, cmdargs, input + 1);
if (cmd && *cmd) {
if (input[1]) {
r_cons_printf ("Saving to file %s ...\n", input + 1);
r_cons_flush ();
}
r_core_cmd0 (core, cmd);
}
free (cmd);
convert_dotcmd_to_image (core, cmdargs, input + 1);
free (cmdargs);
}
break;
@ -10235,15 +10409,8 @@ static void cmd_anal_classes(RCore *core, const char *input) {
eprintf ("Couldn't create graph");
break;
}
RAGraph *agraph = create_agraph_from_graph (graph);
if (!agraph) {
r_graph_free (graph);
eprintf ("Couldn't create graph");
break;
}
r_agraph_print (agraph);
r_core_graph_print (core, graph, -1, false, input + 1);
r_graph_free (graph);
r_agraph_free (agraph);
} break;
default: // "ac?"
r_core_cmd_help (core, help_msg_ac);

View File

@ -578,16 +578,16 @@ R_API void r_core_anal_esil(RCore *core, const char *str, const char *addr);
R_API void r_core_anal_fcn_merge (RCore *core, ut64 addr, ut64 addr2);
R_API const char *r_core_anal_optype_colorfor(RCore *core, ut64 addr, bool verbose);
R_API ut64 r_core_anal_address (RCore *core, ut64 addr);
R_API void r_core_anal_undefine (RCore *core, ut64 off);
R_API void r_core_anal_hint_print (RAnal* a, ut64 addr, int mode);
R_API void r_core_anal_hint_list (RAnal *a, int mode);
R_API void r_core_anal_undefine(RCore *core, ut64 off);
R_API void r_core_anal_hint_print(RAnal* a, ut64 addr, int mode);
R_API void r_core_anal_hint_list(RAnal *a, int mode);
R_API int r_core_anal_search(RCore *core, ut64 from, ut64 to, ut64 ref, int mode);
R_API int r_core_anal_search_xrefs(RCore *core, ut64 from, ut64 to, int rad);
R_API int r_core_anal_data (RCore *core, ut64 addr, int count, int depth, int wordsize);
R_API int r_core_anal_data(RCore *core, ut64 addr, int count, int depth, int wordsize);
R_API void r_core_anal_datarefs(RCore *core, ut64 addr);
R_API void r_core_anal_coderefs(RCore *core, ut64 addr);
R_API void r_core_anal_codexrefs(RCore *core, ut64 addr);
R_API void r_core_anal_importxrefs(RCore *core);
R_API RGraph/*<RGraphNodeInfo>*/ *r_core_anal_codexrefs(RCore *core, ut64 addr);
R_API RGraph/*<RGraphNodeInfo>*/ *r_core_anal_importxrefs(RCore *core);
R_API void r_core_anal_callgraph(RCore *core, ut64 addr, int fmt);
R_API int r_core_anal_refs(RCore *core, const char *input);
R_API void r_core_agraph_print(RCore *core, int use_utf, const char *input);

View File

@ -7,15 +7,6 @@
extern "C" {
#endif
/**
* @brief Generic graph node info
*/
typedef struct r_anal_graph_node_info_t {
char *title;
char *body;
ut64 offset;
} RGraphNodeInfo;
typedef struct r_graph_node_t {
unsigned int idx;
void *data;
@ -58,6 +49,7 @@ R_API RGraphNode *r_graph_get_node(const RGraph *g, unsigned int idx);
R_API RListIter *r_graph_node_iter(const RGraph *g, unsigned int idx);
R_API void r_graph_reset(RGraph *g);
R_API RGraphNode *r_graph_add_node(RGraph *g, void *data);
R_API RGraphNode *r_graph_add_nodef(RGraph *g, void *data, RListFree user_free);
// XXX 'n' is destroyed after calling this function.
R_API void r_graph_del_node(RGraph *g, RGraphNode *n);
R_API void r_graph_add_edge(RGraph *g, RGraphNode *from, RGraphNode *to);
@ -73,8 +65,6 @@ R_API bool r_graph_adjacent(const RGraph *g, const RGraphNode *from, const RGrap
R_API void r_graph_dfs_node(RGraph *g, RGraphNode *n, RGraphVisitor *vis);
R_API void r_graph_dfs_node_reverse(RGraph *g, RGraphNode *n, RGraphVisitor *vis);
R_API void r_graph_dfs(RGraph *g, RGraphVisitor *vis);
R_API void r_graph_free_node_info(void *ptr);
R_API RGraphNodeInfo *r_graph_create_node_info(char *title, char *body, ut64 offset);
#ifdef __cplusplus
}

View File

@ -0,0 +1,53 @@
#ifndef R2_GRAPH_DRAWABLE_H
#define R2_GRAPH_DRAWABLE_H
#include <r_types.h>
#include <r_util/r_graph.h>
#include <r_config.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Generic drawable graph node.
*
* Provides minimal information to draw something without output format specific details.
*/
typedef struct r_anal_graph_node_info_t {
char *title;
char *body;
/**
* @brief Optional offset for the object corresponding to node.
*
* Interactive output modes can use it to provide actions like seeking to
* this position or modify the object.
*/
ut64 offset;
} RGraphNodeInfo;
R_API void r_graph_free_node_info(void *ptr);
R_API RGraphNodeInfo *r_graph_create_node_info(const char *title, const char *body, ut64 offset);
R_API RGraphNode *r_graph_add_node_info(RGraph *graph, const char *title, const char *body, ut64 offset);
/**
* @brief Convert graph to Graphviz dot format.
*
* @param graph Graph with RGraphNodeInfo used as node user data
* @param node_properties List node styling attributes. Can be set to NULL.
* @param edge_properties List edge styling attributes. Can be set to NULL.
*/
R_API char *r_graph_drawable_to_dot(RGraph /*RGraphNodeInfo*/ *graph, const char *node_properties, const char *edge_properties);
/**
* @brief Convert graph to JSON.
*
* @param[in] graph Graph to convert
* @param[out] pj Json output structure. Can be used to include the resulting JSON value inside bigger JSON.
* @param[in] use_offset Set this to true if graph uses \ref RGraphNodeInfo::offset offset field.
*/
R_API void r_graph_drawable_to_json(RGraph /*RGraphNodeInfo*/ *graph, PJ *pj, bool use_offset);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -404,6 +404,7 @@ r_util_files = [
'include/r_util/r_event.h',
'include/r_util/r_file.h',
'include/r_util/r_graph.h',
'include/r_util/r_graph_drawable.h',
'include/r_util/r_hex.h',
'include/r_util/r_idpool.h',
'include/r_util/r_itv.h',

View File

@ -22,7 +22,7 @@ OBJS+=utf8.o utf16.o utf32.o strbuf.o lib.o name.o spaces.o signal.o syscmd.o
OBJS+=udiff.o bdiff.o stack.o queue.o tree.o idpool.o assert.o
OBJS+=punycode.o pkcs7.o x509.o asn1.o astr.o json_parser.o json_indent.o skiplist.o
OBJS+=pj.o rbtree.o intervaltree.o qrcode.o vector.o str_constpool.o str_trim.o
OBJS+=ascii_table.o protobuf.o
OBJS+=ascii_table.o protobuf.o graph_drawable.o
OBJS+=annotated_code.o
ifeq (${HAVE_LIB_GMP},1)

View File

@ -162,6 +162,14 @@ R_API RGraphNode *r_graph_add_node(RGraph *t, void *data) {
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) {
@ -190,7 +198,7 @@ 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) {
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);
@ -209,8 +217,8 @@ R_API RGraphNode *r_graph_node_split_forward(RGraph *g, RGraphNode *split_me, vo
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 (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);
@ -219,7 +227,6 @@ R_API RGraphNode *r_graph_node_split_forward(RGraph *g, RGraphNode *split_me, vo
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;
@ -302,20 +309,3 @@ R_API void r_graph_dfs(RGraph *g, RGraphVisitor *vis) {
free (color);
}
}
R_API void r_graph_free_node_info(void *ptr) {
RGraphNodeInfo *info = ptr;
free (info->body);
free (info->title);
free (info);
}
R_API RGraphNodeInfo *r_graph_create_node_info(char *title, char *body, ut64 offset) {
RGraphNodeInfo *data = R_NEW0 (RGraphNodeInfo);
if (data) {
data->title = title;
data->body = body;
data->offset = offset;
}
return data;
}

104
libr/util/graph_drawable.c Normal file
View File

@ -0,0 +1,104 @@
#include <r_core.h>
#include <r_util/r_graph_drawable.h>
R_API void r_graph_free_node_info(void *ptr) {
if (!ptr) {
return;
}
RGraphNodeInfo *info = ptr;
free (info->body);
free (info->title);
free (info);
}
R_API RGraphNodeInfo *r_graph_create_node_info(const char *title, const char *body, ut64 offset) {
RGraphNodeInfo *data = R_NEW0 (RGraphNodeInfo);
if (data) {
data->title = R_STR_DUP (title);
data->body = R_STR_DUP (body);
data->offset = offset;
}
return data;
}
R_API RGraphNode *r_graph_add_node_info(RGraph *graph, const char *title, const char *body, ut64 offset) {
r_return_val_if_fail (graph, NULL);
RGraphNodeInfo *data = r_graph_create_node_info (title, body, offset);
if (!data) {
return NULL;
}
RGraphNode *node = r_graph_add_nodef (graph, data, r_graph_free_node_info);
if (!node) {
r_graph_free_node_info (data);
}
return node;
}
R_API char *r_graph_drawable_to_dot(RGraph /*RGraphNodeInfo*/ *graph, const char *node_properties, const char *edge_properties) {
RList *nodes = graph->nodes;
RListIter *it, *itt;
RGraphNode *node = NULL, *target = NULL;
RStrBuf buf;
r_strbuf_init (&buf);
r_strbuf_appendf (&buf,
"digraph code {\nrankdir=LR;\noutputorder=edgesfirst\ngraph [bgcolor=azure];\n"
"edge [arrowhead=normal, color=\"#3030c0\" style=bold weight=2 %s];\n"
"node [fillcolor=white, style=filled shape=box "
"fontsize=\"8\" %s];\n",
edge_properties? edge_properties: "",
node_properties? node_properties: "");
r_list_foreach (nodes, it, node) {
RGraphNodeInfo *print_node = (RGraphNodeInfo *)node->data;
const char *body = print_node->body;
if (!body || !*body) {
r_strbuf_appendf (&buf, "%d [URL=\"%s\", color=\"lightgray\", label=\"%s\"]\n",
node->idx, print_node->title, print_node->title);
} else {
r_strbuf_appendf (&buf, "%d [URL=\"%s\", color=\"lightgray\", label=\"%s\\n%s\"]\n",
node->idx, print_node->title, print_node->title, body);
}
r_list_foreach (node->out_nodes, itt, target) {
r_strbuf_appendf (&buf, "%d -> %d\n", node->idx, target->idx);
}
}
r_strbuf_append (&buf, "}\n");
return r_strbuf_drain_nofree (&buf);
}
R_API void r_graph_drawable_to_json(RGraph /*RGraphNodeInfo*/ *graph, PJ *pj, bool use_offset) {
RList *nodes = graph->nodes, *neighbours = NULL;
RListIter *it, *itt;
RGraphNode *node = NULL, *neighbour = NULL;
if (!pj) {
return;
}
pj_o (pj);
pj_k (pj, "nodes");
pj_a (pj);
r_list_foreach (nodes, it, node) {
RGraphNodeInfo *print_node = (RGraphNodeInfo *)node->data;
pj_o (pj);
pj_ki (pj, "id", node->idx);
if (print_node->title) {
pj_ks (pj, "title", print_node->title);
}
if (print_node->body) {
pj_ks (pj, "body", print_node->body);
}
if (use_offset) {
pj_kn (pj, "offset", print_node->offset);
}
pj_k (pj, "out_nodes");
pj_a (pj);
neighbours = node->out_nodes;
r_list_foreach (neighbours, itt, neighbour) {
pj_i (pj, neighbour->idx);
}
pj_end (pj);
pj_end (pj);
}
pj_end (pj);
pj_end (pj);
}

View File

@ -22,6 +22,7 @@ r_util_sources = [
'file.c',
'flist.c',
'graph.c',
'graph_drawable.c',
'hex.c',
'idpool.c',
'json_parser.c',

337
test/db/cmd/cmd_ag Normal file
View File

@ -0,0 +1,337 @@
NAME=agi
FILE=bins/elf/hello_world
CMDS=<<EOF
aa 2> /dev/null
agi*
EOF
EXPECT=<<EOF
agn "sym.imp.free"
agn "0x0000083f"
agn "_ITM_deregisterTMCloneTable"
agn "sym.imp.strcpy"
agn "0x00000814"
agn "sym.imp.puts"
agn "0x00000833"
agn "sym.imp.strlen"
agn "0x000007cf"
agn "0x000007de"
agn "__libc_start_main"
agn "__gmon_start__"
agn "sym.imp.malloc"
agn "0x000007f6"
agn "sym.imp.strcat"
agn "0x00000827"
agn "_ITM_registerTMCloneTable"
agn "section..plt.got"
agn "0x0000077e"
age "0x0000083f" "sym.imp.free"
age "0x00000814" "sym.imp.strcpy"
age "0x00000833" "sym.imp.puts"
age "0x000007cf" "sym.imp.strlen"
age "0x000007de" "sym.imp.strlen"
age "0x000007f6" "sym.imp.malloc"
age "0x00000827" "sym.imp.strcat"
age "0x0000077e" "section..plt.got"
EOF
RUN
NAME=agx
FILE=bins/elf/hello_world
CMDS=<<EOF
aa 2> /dev/null
agx* @ main
EOF
EXPECT=<<EOF
agn "main"
agn "0x000006bd"
age "0x000006bd" "main"
EOF
RUN
NAME=formats
FILE=bins/elf/hello_world
CMDS=<<EOF
aa 2> /dev/null
agx @ main
agid
agig
agij
agik
agit
EOF
EXPECT=<<EOF
.--------------------.
| 0x000006bd |
`--------------------'
v
|
|
.--------------------.
| main |
`--------------------'
digraph code {
rankdir=LR;
outputorder=edgesfirst
graph [bgcolor=azure];
edge [arrowhead=normal, color="#3030c0" style=bold weight=2 ];
node [fillcolor=white, style=filled shape=box fontsize="8" fontname="Courier"];
0 [URL="sym.imp.free", color="lightgray", label="sym.imp.free"]
1 [URL="0x0000083f", color="lightgray", label="0x0000083f"]
1 -> 0
2 [URL="_ITM_deregisterTMCloneTable", color="lightgray", label="_ITM_deregisterTMCloneTable"]
3 [URL="sym.imp.strcpy", color="lightgray", label="sym.imp.strcpy"]
4 [URL="0x00000814", color="lightgray", label="0x00000814"]
4 -> 3
5 [URL="sym.imp.puts", color="lightgray", label="sym.imp.puts"]
6 [URL="0x00000833", color="lightgray", label="0x00000833"]
6 -> 5
7 [URL="sym.imp.strlen", color="lightgray", label="sym.imp.strlen"]
8 [URL="0x000007cf", color="lightgray", label="0x000007cf"]
8 -> 7
9 [URL="0x000007de", color="lightgray", label="0x000007de"]
9 -> 7
10 [URL="__libc_start_main", color="lightgray", label="__libc_start_main"]
11 [URL="__gmon_start__", color="lightgray", label="__gmon_start__"]
12 [URL="sym.imp.malloc", color="lightgray", label="sym.imp.malloc"]
13 [URL="0x000007f6", color="lightgray", label="0x000007f6"]
13 -> 12
14 [URL="sym.imp.strcat", color="lightgray", label="sym.imp.strcat"]
15 [URL="0x00000827", color="lightgray", label="0x00000827"]
15 -> 14
16 [URL="_ITM_registerTMCloneTable", color="lightgray", label="_ITM_registerTMCloneTable"]
17 [URL="section..plt.got", color="lightgray", label="section..plt.got"]
18 [URL="0x0000077e", color="lightgray", label="0x0000077e"]
18 -> 17
}
graph
[
hierarchic 1
label ""
directed 1
node [
id 0
label "sym.imp.free"
]
node [
id 1
label "0x0000083f"
]
node [
id 2
label "_ITM_deregisterTMCloneTable"
]
node [
id 3
label "sym.imp.strcpy"
]
node [
id 4
label "0x00000814"
]
node [
id 5
label "sym.imp.puts"
]
node [
id 6
label "0x00000833"
]
node [
id 7
label "sym.imp.strlen"
]
node [
id 8
label "0x000007cf"
]
node [
id 9
label "0x000007de"
]
node [
id 10
label "__libc_start_main"
]
node [
id 11
label "__gmon_start__"
]
node [
id 12
label "sym.imp.malloc"
]
node [
id 13
label "0x000007f6"
]
node [
id 14
label "sym.imp.strcat"
]
node [
id 15
label "0x00000827"
]
node [
id 16
label "_ITM_registerTMCloneTable"
]
node [
id 17
label "section..plt.got"
]
node [
id 18
label "0x0000077e"
]
edge [
source 1
target 0
]
edge [
source 4
target 3
]
edge [
source 6
target 5
]
edge [
source 8
target 7
]
edge [
source 9
target 7
]
edge [
source 13
target 12
]
edge [
source 15
target 14
]
edge [
source 18
target 17
]
]
{"nodes":[{"id":0,"title":"sym.imp.free","offset":1584,"out_nodes":[]},{"id":1,"title":"0x0000083f","offset":2111,"out_nodes":[0]},{"id":2,"title":"_ITM_deregisterTMCloneTable","offset":0,"out_nodes":[]},{"id":3,"title":"sym.imp.strcpy","offset":1600,"out_nodes":[]},{"id":4,"title":"0x00000814","offset":2068,"out_nodes":[3]},{"id":5,"title":"sym.imp.puts","offset":1616,"out_nodes":[]},{"id":6,"title":"0x00000833","offset":2099,"out_nodes":[5]},{"id":7,"title":"sym.imp.strlen","offset":1632,"out_nodes":[]},{"id":8,"title":"0x000007cf","offset":1999,"out_nodes":[7]},{"id":9,"title":"0x000007de","offset":2014,"out_nodes":[7]},{"id":10,"title":"__libc_start_main","offset":0,"out_nodes":[]},{"id":11,"title":"__gmon_start__","offset":0,"out_nodes":[]},{"id":12,"title":"sym.imp.malloc","offset":1648,"out_nodes":[]},{"id":13,"title":"0x000007f6","offset":2038,"out_nodes":[12]},{"id":14,"title":"sym.imp.strcat","offset":1664,"out_nodes":[]},{"id":15,"title":"0x00000827","offset":2087,"out_nodes":[14]},{"id":16,"title":"_ITM_registerTMCloneTable","offset":0,"out_nodes":[]},{"id":17,"title":"section..plt.got","offset":1680,"out_nodes":[]},{"id":18,"title":"0x0000077e","offset":1918,"out_nodes":[17]}]}
agraph.color_box=G1swbQ==
agraph.delta_x=0x8a
agraph.delta_y=0x1
agraph.h=0xf
agraph.is_callgraph=false
agraph.nodes=sym.imp.free,0x0000083f,_ITM_deregisterTMCloneTable,sym.imp.strcpy,0x00000814,sym.imp.puts,0x00000833,sym.imp.strlen,0x000007cf,0x000007de,__libc_start_main,__gmon_start__,sym.imp.malloc,0x000007f6,sym.imp.strcat,0x00000827,_ITM_registerTMCloneTable,section..plt.got,0x0000077e
agraph.nodes.0x0000077e.body=base64:
agraph.nodes.0x0000077e.h=0x3
agraph.nodes.0x0000077e.neighbours=section..plt.got
agraph.nodes.0x0000077e.w=0x16
agraph.nodes.0x0000077e.x=0x130
agraph.nodes.0x0000077e.y=0x8a
agraph.nodes.0x000007cf.body=base64:
agraph.nodes.0x000007cf.h=0x3
agraph.nodes.0x000007cf.neighbours=sym.imp.strlen
agraph.nodes.0x000007cf.w=0x16
agraph.nodes.0x000007cf.x=0x72
agraph.nodes.0x000007cf.y=0x8a
agraph.nodes.0x000007de.body=base64:
agraph.nodes.0x000007de.h=0x3
agraph.nodes.0x000007de.neighbours=sym.imp.strlen
agraph.nodes.0x000007de.w=0x16
agraph.nodes.0x000007de.x=0x8c
agraph.nodes.0x000007de.y=0x8a
agraph.nodes.0x000007f6.body=base64:
agraph.nodes.0x000007f6.h=0x3
agraph.nodes.0x000007f6.neighbours=sym.imp.malloc
agraph.nodes.0x000007f6.w=0x16
agraph.nodes.0x000007f6.x=0xda
agraph.nodes.0x000007f6.y=0x8a
agraph.nodes.0x00000814.body=base64:
agraph.nodes.0x00000814.h=0x3
agraph.nodes.0x00000814.neighbours=sym.imp.strcpy
agraph.nodes.0x00000814.w=0x16
agraph.nodes.0x00000814.x=0x3e
agraph.nodes.0x00000814.y=0x8a
agraph.nodes.0x00000827.body=base64:
agraph.nodes.0x00000827.h=0x3
agraph.nodes.0x00000827.neighbours=sym.imp.strcat
agraph.nodes.0x00000827.w=0x16
agraph.nodes.0x00000827.x=0xf4
agraph.nodes.0x00000827.y=0x8a
agraph.nodes.0x00000833.body=base64:
agraph.nodes.0x00000833.h=0x3
agraph.nodes.0x00000833.neighbours=sym.imp.puts
agraph.nodes.0x00000833.w=0x16
agraph.nodes.0x00000833.x=0x58
agraph.nodes.0x00000833.y=0x8a
agraph.nodes.0x0000083f.body=base64:
agraph.nodes.0x0000083f.h=0x3
agraph.nodes.0x0000083f.neighbours=sym.imp.free
agraph.nodes.0x0000083f.w=0x16
agraph.nodes.0x0000083f.x=0
agraph.nodes.0x0000083f.y=0x8a
agraph.nodes._ITM_deregisterTMCloneTable.body=base64:
agraph.nodes._ITM_deregisterTMCloneTable.h=0x3
agraph.nodes._ITM_deregisterTMCloneTable.w=0x21
agraph.nodes._ITM_deregisterTMCloneTable.x=0x1a
agraph.nodes._ITM_deregisterTMCloneTable.y=0x8a
agraph.nodes._ITM_registerTMCloneTable.body=base64:
agraph.nodes._ITM_registerTMCloneTable.h=0x3
agraph.nodes._ITM_registerTMCloneTable.w=0x1f
agraph.nodes._ITM_registerTMCloneTable.x=0x10e
agraph.nodes._ITM_registerTMCloneTable.y=0x8a
agraph.nodes.__gmon_start__.body=base64:
agraph.nodes.__gmon_start__.h=0x3
agraph.nodes.__gmon_start__.w=0x16
agraph.nodes.__gmon_start__.x=0xc0
agraph.nodes.__gmon_start__.y=0x8a
agraph.nodes.__libc_start_main.body=base64:
agraph.nodes.__libc_start_main.h=0x3
agraph.nodes.__libc_start_main.w=0x17
agraph.nodes.__libc_start_main.x=0xa6
agraph.nodes.__libc_start_main.y=0x8a
agraph.nodes.section..plt.got.body=base64:
agraph.nodes.section..plt.got.h=0x3
agraph.nodes.section..plt.got.w=0x16
agraph.nodes.section..plt.got.x=0x130
agraph.nodes.section..plt.got.y=0x92
agraph.nodes.sym.imp.free.body=base64:
agraph.nodes.sym.imp.free.h=0x3
agraph.nodes.sym.imp.free.w=0x16
agraph.nodes.sym.imp.free.x=0
agraph.nodes.sym.imp.free.y=0x92
agraph.nodes.sym.imp.malloc.body=base64:
agraph.nodes.sym.imp.malloc.h=0x3
agraph.nodes.sym.imp.malloc.w=0x16
agraph.nodes.sym.imp.malloc.x=0xda
agraph.nodes.sym.imp.malloc.y=0x92
agraph.nodes.sym.imp.puts.body=base64:
agraph.nodes.sym.imp.puts.h=0x3
agraph.nodes.sym.imp.puts.w=0x16
agraph.nodes.sym.imp.puts.x=0x58
agraph.nodes.sym.imp.puts.y=0x92
agraph.nodes.sym.imp.strcat.body=base64:
agraph.nodes.sym.imp.strcat.h=0x3
agraph.nodes.sym.imp.strcat.w=0x16
agraph.nodes.sym.imp.strcat.x=0xf4
agraph.nodes.sym.imp.strcat.y=0x92
agraph.nodes.sym.imp.strcpy.body=base64:
agraph.nodes.sym.imp.strcpy.h=0x3
agraph.nodes.sym.imp.strcpy.w=0x16
agraph.nodes.sym.imp.strcpy.x=0x3e
agraph.nodes.sym.imp.strcpy.y=0x92
agraph.nodes.sym.imp.strlen.body=base64:
agraph.nodes.sym.imp.strlen.h=0x3
agraph.nodes.sym.imp.strlen.w=0x16
agraph.nodes.sym.imp.strlen.x=0x7f
agraph.nodes.sym.imp.strlen.y=0x92
agraph.w=0x146
() () () () () () () () () () () ()
() () () () () () ()
EOF
RUN

View File

@ -1,6 +1,7 @@
#include <r_core.h>
#include <r_anal.h>
#include <r_util.h>
#include <r_util/r_graph_drawable.h>
#include "minunit.h"
bool test_inherit_graph_creation() {