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:
parent
02edb130d7
commit
bdb88df38c
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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',
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -22,6 +22,7 @@ r_util_sources = [
|
|||
'file.c',
|
||||
'flist.c',
|
||||
'graph.c',
|
||||
'graph_drawable.c',
|
||||
'hex.c',
|
||||
'idpool.c',
|
||||
'json_parser.c',
|
||||
|
|
|
@ -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
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in New Issue