2015-01-11 20:01:44 +01:00
|
|
|
/* Copyright radare2 2014-2015 - Author: pancake */
|
2014-04-29 03:53:48 +02:00
|
|
|
|
|
|
|
#include <r_core.h>
|
2015-02-17 01:28:51 +01:00
|
|
|
static const char *mousemodes[] = { "canvas-y", "canvas-x", "node-y", "node-x", NULL };
|
2015-03-05 02:07:53 +01:00
|
|
|
static int mousemode = 0;
|
2015-05-30 01:36:40 +02:00
|
|
|
|
|
|
|
#define BORDER 3
|
|
|
|
#define BORDER_WIDTH 4
|
|
|
|
#define BORDER_HEIGHT 3
|
2015-05-30 18:04:03 +02:00
|
|
|
#define MARGIN_TEXT_X 2
|
|
|
|
#define MARGIN_TEXT_Y 2
|
2015-05-30 01:36:40 +02:00
|
|
|
#define MAX_NODE_WIDTH 18
|
|
|
|
|
2014-10-23 03:29:05 +02:00
|
|
|
#define OS_SIZE 128
|
2015-06-03 00:18:33 +02:00
|
|
|
struct ostack {
|
2015-01-09 11:38:00 +01:00
|
|
|
int nodes[OS_SIZE];
|
|
|
|
int size;
|
2015-06-03 00:18:33 +02:00
|
|
|
};
|
2014-05-05 03:15:28 +02:00
|
|
|
|
2014-04-29 03:53:48 +02:00
|
|
|
typedef struct {
|
|
|
|
int x;
|
|
|
|
int y;
|
|
|
|
int w;
|
|
|
|
int h;
|
|
|
|
ut64 addr;
|
2014-05-05 03:15:28 +02:00
|
|
|
int depth;
|
2014-07-09 05:05:22 +02:00
|
|
|
char *text;
|
2014-04-29 03:53:48 +02:00
|
|
|
} Node;
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
int nth;
|
|
|
|
int from;
|
|
|
|
int to;
|
|
|
|
} Edge;
|
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
struct graph {
|
|
|
|
RCore *core;
|
|
|
|
RConsCanvas *can;
|
|
|
|
RAnalFunction *fcn;
|
|
|
|
Node *nodes;
|
|
|
|
Edge *edges;
|
|
|
|
int n_nodes;
|
|
|
|
int n_edges;
|
|
|
|
int is_callgraph;
|
|
|
|
int is_instep;
|
|
|
|
int is_simple_mode;
|
|
|
|
int is_small_nodes;
|
|
|
|
|
2015-06-03 11:38:31 +02:00
|
|
|
unsigned int curnode;
|
2015-06-03 00:18:33 +02:00
|
|
|
|
|
|
|
struct ostack ostack;
|
2015-06-03 15:51:08 +02:00
|
|
|
int need_reload_nodes;
|
|
|
|
int need_update_seek;
|
|
|
|
int update_seek_on;
|
|
|
|
int force_update_seek;
|
2015-06-03 00:18:33 +02:00
|
|
|
};
|
2014-04-29 03:53:48 +02:00
|
|
|
|
|
|
|
#if 0
|
|
|
|
static Node nodes[] = {
|
|
|
|
{25,4, 18, 6, 0x8048320, "push ebp\nmov esp, ebp\njz 0x8048332" },
|
|
|
|
{10,13, 18, 5, 0x8048332, "xor eax, eax\nint 0x80\n"},
|
|
|
|
{30,13, 18, 5, 0x8048324, "pop ebp\nret"},
|
|
|
|
{NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
static Edge edges[] = {
|
|
|
|
{ 0, 0, 1 },
|
|
|
|
{ 1, 0, 2 },
|
|
|
|
{ -1 }
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
#define G(x,y) r_cons_canvas_gotoxy (g->can, x, y)
|
|
|
|
#define W(x) r_cons_canvas_write (g->can, x)
|
|
|
|
#define B(x,y,w,h) r_cons_canvas_box(g->can, x,y,w,h,NULL)
|
|
|
|
#define B1(x,y,w,h) r_cons_canvas_box(g->can, x,y,w,h,Color_BLUE)
|
|
|
|
#define B2(x,y,w,h) r_cons_canvas_box(g->can, x,y,w,h,Color_MAGENTA)
|
|
|
|
#define L(x,y,x2,y2) r_cons_canvas_line(g->can, x,y,x2,y2,0)
|
|
|
|
#define L1(x,y,x2,y2) r_cons_canvas_line(g->can, x,y,x2,y2,1)
|
|
|
|
#define L2(x,y,x2,y2) r_cons_canvas_line(g->can, x,y,x2,y2,2)
|
|
|
|
#define F(x,y,x2,y2,c) r_cons_canvas_fill(g->can, x,y,x2,y2,c,0)
|
|
|
|
|
|
|
|
static void ostack_init(struct ostack *os) {
|
|
|
|
os->size = 0;
|
|
|
|
os->nodes[0] = 0;
|
2015-05-30 01:21:48 +02:00
|
|
|
}
|
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
static void ostack_push(struct ostack *os, int el) {
|
|
|
|
if (os->size < OS_SIZE - 1)
|
|
|
|
os->nodes[++os->size] = el;
|
2015-05-30 01:21:48 +02:00
|
|
|
}
|
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
static int ostack_pop(struct ostack *os) {
|
|
|
|
return os->size > 0 ? os->nodes[--os->size] : 0;
|
2015-05-30 01:21:48 +02:00
|
|
|
}
|
|
|
|
|
2015-06-03 14:47:34 +02:00
|
|
|
static void update_node_dimension(Node nodes[], int nodes_size, int is_small) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < nodes_size; ++i) {
|
|
|
|
Node *n = &nodes[i];
|
|
|
|
if (is_small) {
|
|
|
|
n->w = n->h = 0;
|
|
|
|
} else {
|
|
|
|
n->w = r_str_bounds (n->text, &n->h);
|
|
|
|
n->w += BORDER_WIDTH;
|
|
|
|
n->h += BORDER_HEIGHT;
|
|
|
|
n->w = R_MAX (MAX_NODE_WIDTH, n->w);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
static void small_Node_print(struct graph *g, Node *n, int cur) {
|
2014-04-29 03:53:48 +02:00
|
|
|
char title[128];
|
|
|
|
|
2015-05-30 12:37:42 +02:00
|
|
|
if (!G (n->x + 2, n->y - 1))
|
2015-05-30 12:16:31 +02:00
|
|
|
return;
|
2015-05-30 12:37:42 +02:00
|
|
|
if (cur) {
|
|
|
|
W("[_@@_]");
|
2015-06-03 00:18:33 +02:00
|
|
|
(void)G (-g->can->sx, -g->can->sy + 2);
|
2015-05-30 12:37:42 +02:00
|
|
|
snprintf (title, sizeof (title) - 1,
|
2015-01-09 11:38:00 +01:00
|
|
|
"0x%08"PFMT64x":", n->addr);
|
2015-05-30 12:37:42 +02:00
|
|
|
W (title);
|
2015-06-03 00:18:33 +02:00
|
|
|
(void)G (-g->can->sx, -g->can->sy + 3);
|
2015-05-30 12:37:42 +02:00
|
|
|
W (n->text);
|
|
|
|
} else {
|
|
|
|
W("[____]");
|
2015-01-09 11:38:00 +01:00
|
|
|
}
|
2015-05-30 12:37:42 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
static void normal_Node_print(struct graph *g, Node *n, int cur) {
|
2015-05-30 12:37:42 +02:00
|
|
|
char title[128];
|
|
|
|
char *text;
|
|
|
|
int delta_x = 0;
|
|
|
|
int delta_y = 0;
|
2015-05-31 23:46:32 +02:00
|
|
|
int x, y;
|
2015-01-09 11:38:00 +01:00
|
|
|
|
2015-01-11 01:48:20 +01:00
|
|
|
#if SHOW_OUT_OF_SCREEN_NODES
|
2015-06-03 00:18:33 +02:00
|
|
|
x = n->x + g->can->sx;
|
|
|
|
y = n->y + n->h + g->can->sy;
|
|
|
|
if (x < 0 || x > g->can->w)
|
2015-05-30 01:36:40 +02:00
|
|
|
return;
|
2015-06-03 00:18:33 +02:00
|
|
|
if (y < 0 || y > g->can->h)
|
2015-05-30 01:36:40 +02:00
|
|
|
return;
|
2015-01-11 01:48:20 +01:00
|
|
|
#endif
|
2015-06-03 00:18:33 +02:00
|
|
|
x = n->x + g->can->sx;
|
|
|
|
y = n->y + g->can->sy;
|
2015-05-30 18:04:03 +02:00
|
|
|
if (x < -MARGIN_TEXT_X)
|
|
|
|
delta_x = -x - MARGIN_TEXT_X;
|
|
|
|
if (x + n->w < -MARGIN_TEXT_X)
|
2015-05-30 01:36:40 +02:00
|
|
|
return;
|
|
|
|
if (y < -1)
|
2015-05-30 18:04:03 +02:00
|
|
|
delta_y = -y - MARGIN_TEXT_Y;
|
2015-05-30 01:36:40 +02:00
|
|
|
|
2014-04-29 03:53:48 +02:00
|
|
|
if (cur) {
|
2014-05-18 03:41:39 +02:00
|
|
|
//F (n->x,n->y, n->w, n->h, '.');
|
2014-04-29 03:53:48 +02:00
|
|
|
snprintf (title, sizeof (title)-1,
|
2015-05-30 12:37:42 +02:00
|
|
|
"-[ 0x%08"PFMT64x" ]-", n->addr);
|
2014-04-29 03:53:48 +02:00
|
|
|
} else {
|
|
|
|
snprintf (title, sizeof (title)-1,
|
2015-05-30 12:37:42 +02:00
|
|
|
" 0x%08"PFMT64x" ", n->addr);
|
2014-04-29 03:53:48 +02:00
|
|
|
}
|
2015-05-30 18:16:03 +02:00
|
|
|
if (delta_x < strlen(title) && G(n->x + MARGIN_TEXT_X + delta_x, n->y + 1))
|
|
|
|
W(title + delta_x);
|
2015-05-30 12:37:42 +02:00
|
|
|
|
2015-05-31 23:11:03 +02:00
|
|
|
if (G(n->x + MARGIN_TEXT_X + delta_x, n->y + MARGIN_TEXT_Y + delta_y)) {
|
2015-05-31 22:27:47 +02:00
|
|
|
text = r_str_crop (n->text, delta_x, delta_y, n->w - BORDER_WIDTH, n->h);
|
|
|
|
if (text) {
|
|
|
|
W (text);
|
|
|
|
free (text);
|
|
|
|
} else {
|
|
|
|
W (n->text);
|
|
|
|
}
|
2015-01-11 02:28:59 +01:00
|
|
|
}
|
2015-05-30 12:37:42 +02:00
|
|
|
|
2015-02-15 21:48:46 +01:00
|
|
|
// TODO: check if node is traced or not and hsow proper color
|
|
|
|
// This info must be stored inside Node* from RCore*
|
|
|
|
if (cur) {
|
|
|
|
B1 (n->x, n->y, n->w, n->h);
|
|
|
|
} else {
|
|
|
|
B (n->x, n->y, n->w, n->h);
|
|
|
|
}
|
2014-04-29 03:53:48 +02:00
|
|
|
}
|
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
static Node *get_current_node(struct graph *g) {
|
|
|
|
return &g->nodes[g->curnode];
|
|
|
|
}
|
2015-05-30 12:37:42 +02:00
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
static int count_exit_edges(struct graph *g, int n) {
|
|
|
|
int i, count = 0;
|
2015-06-03 12:24:42 +02:00
|
|
|
for (i = 0; i < g->n_edges; i++) {
|
2015-06-03 00:18:33 +02:00
|
|
|
if (g->edges[i].from == n) {
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int find_edge_node(struct graph *g, int cur, int nth) {
|
|
|
|
if (g->edges) {
|
|
|
|
int i;
|
2015-06-03 12:24:42 +02:00
|
|
|
for (i = 0; i < g->n_edges; i++) {
|
2015-06-03 14:20:03 +02:00
|
|
|
if (g->edges[i].from == cur && g->edges[i].nth == nth)
|
|
|
|
return g->edges[i].to;
|
2015-06-03 00:18:33 +02:00
|
|
|
}
|
2014-04-29 03:53:48 +02:00
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
static int find_node_idx(struct graph *g, ut64 addr) {
|
|
|
|
if (g->nodes) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < g->n_nodes; i++) {
|
|
|
|
if (g->nodes[i].addr == addr)
|
|
|
|
return i;
|
|
|
|
}
|
2014-04-29 03:53:48 +02:00
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-06-03 15:25:05 +02:00
|
|
|
static void set_layout_bb_depth(struct graph *g, int nth, int depth) {
|
2015-06-03 00:18:33 +02:00
|
|
|
int j, f, old_d;
|
|
|
|
if (nth >= g->n_nodes)
|
2014-05-05 03:15:28 +02:00
|
|
|
return;
|
2015-06-03 00:18:33 +02:00
|
|
|
|
|
|
|
old_d = g->nodes[nth].depth;
|
|
|
|
g->nodes[nth].depth = depth;
|
|
|
|
if (old_d != -1)
|
2014-05-05 03:15:28 +02:00
|
|
|
return;
|
2015-06-03 00:18:33 +02:00
|
|
|
|
|
|
|
j = find_edge_node (g, nth, 0);
|
|
|
|
if (j != -1)
|
2015-06-03 15:25:05 +02:00
|
|
|
set_layout_bb_depth (g, j, depth + 1);
|
2015-06-03 00:18:33 +02:00
|
|
|
f = find_edge_node (g, nth, 1);
|
|
|
|
if (f != -1)
|
2015-06-03 15:25:05 +02:00
|
|
|
set_layout_bb_depth (g, f, depth + 1);
|
2014-05-05 03:15:28 +02:00
|
|
|
// TODO: support more than two destination points (switch tables?)
|
|
|
|
}
|
|
|
|
|
2015-06-03 14:59:53 +02:00
|
|
|
static void set_layout_bb(struct graph *g) {
|
2014-05-05 03:15:28 +02:00
|
|
|
int i, j, rh, nx;
|
|
|
|
int *rowheight = NULL;
|
|
|
|
int maxdepth = 0;
|
|
|
|
const int h_spacing = 12;
|
|
|
|
const int v_spacing = 4;
|
|
|
|
|
2015-06-03 15:25:05 +02:00
|
|
|
set_layout_bb_depth (g, 0, 0);
|
2014-05-06 01:17:02 +04:00
|
|
|
|
2014-05-05 03:15:28 +02:00
|
|
|
// identify max depth
|
2015-06-03 00:18:33 +02:00
|
|
|
for (i = 0; i < g->n_nodes; i++) {
|
|
|
|
if (g->nodes[i].depth > maxdepth)
|
|
|
|
maxdepth = g->nodes[i].depth;
|
2014-05-05 03:15:28 +02:00
|
|
|
}
|
|
|
|
// identify row height
|
2015-06-03 00:18:33 +02:00
|
|
|
rowheight = malloc (sizeof (int) * maxdepth);
|
|
|
|
for (i = 0; i < maxdepth; i++) {
|
2014-05-05 03:15:28 +02:00
|
|
|
rh = 0;
|
2015-06-03 00:18:33 +02:00
|
|
|
for (j = 0; j < g->n_nodes; j++) {
|
|
|
|
if (g->nodes[j].depth == i)
|
|
|
|
if (g->nodes[j].h > rh)
|
|
|
|
rh = g->nodes[j].h;
|
2014-05-05 03:15:28 +02:00
|
|
|
}
|
|
|
|
rowheight[i] = rh;
|
|
|
|
}
|
|
|
|
|
|
|
|
// vertical align // depe
|
2015-06-03 00:18:33 +02:00
|
|
|
for (i = 0; i < g->n_nodes; i++) {
|
|
|
|
g->nodes[i].y = 1;
|
2015-06-03 12:24:42 +02:00
|
|
|
for (j = 0; j < g->nodes[i].depth; j++)
|
2015-06-03 00:18:33 +02:00
|
|
|
g->nodes[i].y += rowheight[j] + v_spacing;
|
2014-05-05 03:15:28 +02:00
|
|
|
}
|
|
|
|
// horitzontal align
|
2015-06-03 00:18:33 +02:00
|
|
|
for (i = 0; i < maxdepth; i++) {
|
|
|
|
nx = (i % 2) * 10;
|
|
|
|
for (j = 0; j < g->n_nodes; j++) {
|
|
|
|
if (g->nodes[j].depth == i) {
|
|
|
|
g->nodes[j].x = nx;
|
|
|
|
nx += g->nodes[j].w + h_spacing;
|
2014-05-05 03:15:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free (rowheight);
|
|
|
|
}
|
|
|
|
|
2015-06-03 14:59:53 +02:00
|
|
|
static void set_layout_callgraph(struct graph *g) {
|
|
|
|
int y = 5, x = 20;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < g->n_nodes; i++) {
|
|
|
|
// wrap to width 'w'
|
|
|
|
if (i > 0) {
|
|
|
|
if (g->nodes[i].x < g->nodes[i-1].x) {
|
|
|
|
y += 10;
|
|
|
|
x = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g->nodes[i].x = x;
|
|
|
|
g->nodes[i].y = i? y: 2;
|
|
|
|
x += 30;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-03 15:25:05 +02:00
|
|
|
static int get_bbnodes(struct graph *g) {
|
2014-04-29 03:53:48 +02:00
|
|
|
RAnalBlock *bb;
|
2015-06-03 00:18:33 +02:00
|
|
|
RListIter *iter;
|
|
|
|
Node *nodes;
|
|
|
|
int i;
|
|
|
|
|
2015-06-03 12:24:42 +02:00
|
|
|
nodes = calloc(r_list_length (g->fcn->bbs), sizeof(Node));
|
2015-06-03 00:18:33 +02:00
|
|
|
if (!nodes)
|
|
|
|
return R_FALSE;
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
r_list_foreach (g->fcn->bbs, iter, bb) {
|
|
|
|
if (bb->addr == UT64_MAX)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (g->is_simple_mode) {
|
|
|
|
nodes[i].text = r_core_cmd_strf (g->core,
|
|
|
|
"pI %d @ 0x%08"PFMT64x, bb->size, bb->addr);
|
|
|
|
}else {
|
|
|
|
nodes[i].text = r_core_cmd_strf (g->core,
|
|
|
|
"pDi %d @ 0x%08"PFMT64x, bb->size, bb->addr);
|
2014-07-09 05:05:22 +02:00
|
|
|
}
|
2015-06-03 00:18:33 +02:00
|
|
|
nodes[i].addr = bb->addr;
|
|
|
|
nodes[i].depth = -1;
|
|
|
|
nodes[i].x = 0;
|
|
|
|
nodes[i].y = 0;
|
|
|
|
nodes[i].w = 0;
|
|
|
|
nodes[i].h = 0;
|
|
|
|
i++;
|
2014-07-09 05:05:22 +02:00
|
|
|
}
|
2015-06-03 00:18:33 +02:00
|
|
|
|
|
|
|
if (g->nodes)
|
|
|
|
free(g->nodes);
|
|
|
|
g->nodes = nodes;
|
|
|
|
g->n_nodes = i;
|
|
|
|
return R_TRUE;
|
2014-07-09 05:05:22 +02:00
|
|
|
}
|
2014-04-29 03:53:48 +02:00
|
|
|
|
2015-06-03 15:25:05 +02:00
|
|
|
static int get_cgnodes(struct graph *g) {
|
2014-09-23 00:40:35 +02:00
|
|
|
int i = 0;
|
|
|
|
#if FCN_OLD
|
|
|
|
int j;
|
2014-07-09 05:05:22 +02:00
|
|
|
char *code;
|
|
|
|
RAnalRef *ref;
|
|
|
|
RListIter *iter;
|
2014-09-23 00:40:35 +02:00
|
|
|
Node *nodes;
|
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
int fcn_refs_length = r_list_length (g->fcn->refs);
|
|
|
|
nodes = calloc (fcn_refs_length + 2, sizeof(Node));
|
2014-07-09 05:05:22 +02:00
|
|
|
if (!nodes)
|
2014-04-29 03:53:48 +02:00
|
|
|
return R_FALSE;
|
2015-06-03 00:18:33 +02:00
|
|
|
|
2014-07-09 05:05:22 +02:00
|
|
|
nodes[i].text = strdup ("");
|
2015-06-03 00:18:33 +02:00
|
|
|
nodes[i].addr = g->fcn->addr;
|
2014-07-09 05:05:22 +02:00
|
|
|
nodes[i].depth = -1;
|
|
|
|
nodes[i].x = 10;
|
|
|
|
nodes[i].y = 3;
|
|
|
|
nodes[i].w = 0;
|
|
|
|
nodes[i].h = 0;
|
|
|
|
i++;
|
2015-06-03 12:44:40 +02:00
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
r_list_foreach (g->fcn->refs, iter, ref) {
|
2015-06-03 12:44:40 +02:00
|
|
|
/* XXX: something is broken, why there are duplicated
|
|
|
|
* nodes here?! goto check fcn->refs!! */
|
2014-07-09 05:05:22 +02:00
|
|
|
/* avoid dups wtf */
|
2015-06-03 12:24:42 +02:00
|
|
|
for (j = 0; j < i; j++) {
|
2014-07-09 05:05:22 +02:00
|
|
|
if (ref->addr == nodes[j].addr)
|
2015-06-03 00:18:33 +02:00
|
|
|
continue;
|
2014-07-09 05:05:22 +02:00
|
|
|
}
|
2015-06-03 00:18:33 +02:00
|
|
|
RFlagItem *fi = r_flag_get_at (g->core->flags, ref->addr);
|
2014-07-09 05:05:22 +02:00
|
|
|
if (fi) {
|
|
|
|
nodes[i].text = strdup (fi->name);
|
|
|
|
nodes[i].text = r_str_concat (nodes[i].text, ":\n");
|
|
|
|
} else {
|
|
|
|
nodes[i].text = strdup ("");
|
|
|
|
}
|
2015-06-03 00:18:33 +02:00
|
|
|
code = r_core_cmd_strf (g->core,
|
2014-07-09 05:05:22 +02:00
|
|
|
"pi 4 @ 0x%08"PFMT64x, ref->addr);
|
|
|
|
nodes[i].text = r_str_concat (nodes[i].text, code);
|
|
|
|
free (code);
|
|
|
|
nodes[i].text = r_str_concat (nodes[i].text, "...\n");
|
|
|
|
nodes[i].addr = ref->addr;
|
|
|
|
nodes[i].depth = -1;
|
|
|
|
nodes[i].x = 10;
|
|
|
|
nodes[i].y = 10;
|
|
|
|
nodes[i].w = 0;
|
|
|
|
nodes[i].h = 0;
|
|
|
|
i++;
|
2014-04-29 03:53:48 +02:00
|
|
|
}
|
2015-06-03 00:18:33 +02:00
|
|
|
|
|
|
|
if (g->nodes)
|
|
|
|
free(g->nodes);
|
|
|
|
g->nodes = nodes;
|
|
|
|
g->n_nodes = i;
|
2014-09-23 00:40:35 +02:00
|
|
|
#else
|
|
|
|
eprintf ("Must be sdbized\n");
|
|
|
|
#endif
|
2015-06-03 00:18:33 +02:00
|
|
|
|
|
|
|
return R_TRUE;
|
2014-07-09 05:05:22 +02:00
|
|
|
}
|
2014-04-29 03:53:48 +02:00
|
|
|
|
2015-06-03 15:25:05 +02:00
|
|
|
static int get_bbedges(struct graph *g) {
|
2015-06-03 00:18:33 +02:00
|
|
|
Edge *edges = NULL;
|
|
|
|
RListIter *iter;
|
|
|
|
RAnalBlock *bb;
|
2015-06-03 12:44:40 +02:00
|
|
|
int i, n_edges;
|
|
|
|
|
|
|
|
n_edges = 0;
|
|
|
|
r_list_foreach (g->fcn->bbs, iter, bb) {
|
|
|
|
if (bb->jump != UT64_MAX)
|
|
|
|
n_edges++;
|
|
|
|
if (bb->fail != UT64_MAX)
|
|
|
|
n_edges++;
|
|
|
|
}
|
|
|
|
|
|
|
|
edges = calloc(n_edges, sizeof(Edge));
|
|
|
|
if (!edges && n_edges != 0)
|
|
|
|
return R_FALSE;
|
2015-06-03 00:18:33 +02:00
|
|
|
|
2015-06-03 12:44:40 +02:00
|
|
|
i = 0;
|
2015-06-03 00:18:33 +02:00
|
|
|
r_list_foreach (g->fcn->bbs, iter, bb) {
|
|
|
|
// add edge from bb->addr to bb->jump / bb->fail
|
|
|
|
if (bb->jump != UT64_MAX) {
|
|
|
|
edges[i].nth = 0;
|
|
|
|
edges[i].from = find_node_idx (g, bb->addr);
|
|
|
|
edges[i].to = find_node_idx (g, bb->jump);
|
|
|
|
i++;
|
2015-06-03 12:44:40 +02:00
|
|
|
}
|
|
|
|
if (bb->fail != UT64_MAX) {
|
|
|
|
edges[i].nth = 1;
|
|
|
|
edges[i].from = find_node_idx (g, bb->addr);
|
|
|
|
edges[i].to = find_node_idx (g, bb->fail);
|
|
|
|
i++;
|
2015-06-03 00:18:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g->edges)
|
|
|
|
free(g->edges);
|
|
|
|
g->edges = edges;
|
2015-06-03 12:24:42 +02:00
|
|
|
g->n_edges = i;
|
2015-06-03 00:18:33 +02:00
|
|
|
return R_TRUE;
|
|
|
|
}
|
|
|
|
|
2015-06-03 15:25:05 +02:00
|
|
|
static int get_cgedges(struct graph *g) {
|
2014-07-09 05:05:22 +02:00
|
|
|
int i = 0;
|
2014-09-23 00:40:35 +02:00
|
|
|
#if FCN_OLD
|
2014-07-09 05:05:22 +02:00
|
|
|
Edge *edges = NULL;
|
|
|
|
RAnalRef *ref;
|
|
|
|
RListIter *iter;
|
2015-06-03 12:44:40 +02:00
|
|
|
int refs_length;
|
|
|
|
|
|
|
|
refs_length = r_list_length(g->fcn->refs);
|
|
|
|
edges = calloc(refs_length, sizeof(Edge));
|
|
|
|
if (!edges && refs_length != 0)
|
|
|
|
return R_FALSE;
|
2015-06-03 00:18:33 +02:00
|
|
|
|
|
|
|
r_list_foreach (g->fcn->refs, iter, ref) {
|
2014-07-09 05:05:22 +02:00
|
|
|
edges[i].nth = 0;
|
2015-06-03 00:18:33 +02:00
|
|
|
edges[i].from = find_node_idx (g, g->fcn->addr);
|
|
|
|
edges[i].to = find_node_idx (g, ref->addr);
|
2014-07-09 05:05:22 +02:00
|
|
|
i++;
|
|
|
|
}
|
2015-06-03 00:18:33 +02:00
|
|
|
|
|
|
|
if (g->edges)
|
|
|
|
free(g->edges);
|
|
|
|
g->edges = edges;
|
|
|
|
g->n_edges = i;
|
2014-09-23 00:40:35 +02:00
|
|
|
#else
|
|
|
|
#warning cgEdges not sdbized for fcn refs
|
|
|
|
#endif
|
2015-06-03 00:18:33 +02:00
|
|
|
|
|
|
|
return R_TRUE;
|
2014-07-09 05:05:22 +02:00
|
|
|
}
|
|
|
|
|
2015-06-03 14:59:53 +02:00
|
|
|
static int reload_nodes(struct graph *g) {
|
|
|
|
int ret;
|
2015-06-03 00:18:33 +02:00
|
|
|
|
2015-06-03 11:38:31 +02:00
|
|
|
if (g->is_callgraph) {
|
2015-06-03 15:25:05 +02:00
|
|
|
ret = get_cgnodes(g);
|
2015-06-03 11:38:31 +02:00
|
|
|
if (!ret)
|
|
|
|
return R_FALSE;
|
2015-06-03 15:25:05 +02:00
|
|
|
ret = get_cgedges(g);
|
2015-06-03 11:38:31 +02:00
|
|
|
if (!ret)
|
|
|
|
return R_FALSE;
|
|
|
|
} else {
|
2015-06-03 15:25:05 +02:00
|
|
|
ret = get_bbnodes(g);
|
2015-06-03 11:38:31 +02:00
|
|
|
if (!ret)
|
|
|
|
return R_FALSE;
|
2015-06-03 00:18:33 +02:00
|
|
|
|
2015-06-03 15:25:05 +02:00
|
|
|
ret = get_bbedges(g);
|
2015-06-03 11:38:31 +02:00
|
|
|
if (!ret)
|
|
|
|
return R_FALSE;
|
2014-04-29 03:53:48 +02:00
|
|
|
}
|
2015-06-03 11:38:31 +02:00
|
|
|
|
2015-06-03 14:59:53 +02:00
|
|
|
update_node_dimension(g->nodes, g->n_nodes, g->is_small_nodes);
|
|
|
|
return R_TRUE;
|
|
|
|
}
|
|
|
|
|
2015-06-03 15:51:08 +02:00
|
|
|
static void update_seek(RConsCanvas *can, Node *n, int force) {
|
2015-06-03 15:25:05 +02:00
|
|
|
int x, y, w, h;
|
|
|
|
int doscroll = R_FALSE;
|
|
|
|
|
|
|
|
if (!n) return;
|
|
|
|
|
|
|
|
x = n->x + can->sx;
|
|
|
|
y = n->y + can->sy;
|
|
|
|
w = can->w;
|
|
|
|
h = can->h;
|
|
|
|
|
|
|
|
doscroll = force || y < 0 || y + 5 > h || x + 5 > w || x + n->w + 5 < 0;
|
|
|
|
|
|
|
|
if (doscroll) {
|
|
|
|
// top-left
|
|
|
|
can->sy = -n->y + BORDER;
|
|
|
|
can->sx = -n->x + BORDER;
|
|
|
|
// center
|
|
|
|
can->sy = -n->y + BORDER + (h / 8);
|
|
|
|
can->sx = -n->x + BORDER + (w / 4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-03 15:51:08 +02:00
|
|
|
static void graph_set_layout(struct graph *g) {
|
|
|
|
if (g->is_callgraph)
|
|
|
|
set_layout_callgraph(g);
|
|
|
|
else
|
|
|
|
set_layout_bb(g);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void graph_update_seek(struct graph *g, int node_index, int force) {
|
|
|
|
g->need_update_seek = R_TRUE;
|
|
|
|
g->update_seek_on = node_index;
|
|
|
|
g->force_update_seek = force;
|
|
|
|
}
|
|
|
|
|
2015-06-03 15:25:05 +02:00
|
|
|
static void graph_free(struct graph *g) {
|
|
|
|
if (g->nodes)
|
|
|
|
free(g->nodes);
|
|
|
|
if (g->edges)
|
|
|
|
free(g->edges);
|
|
|
|
free(g);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void graph_print_node(struct graph *g, Node *n) {
|
|
|
|
int cur = get_current_node(g) == n;
|
|
|
|
|
|
|
|
if (g->is_small_nodes)
|
|
|
|
small_Node_print(g, n, cur);
|
|
|
|
else
|
|
|
|
normal_Node_print(g, n, cur);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void graph_print_nodes(struct graph *g) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < g->n_nodes; ++i)
|
|
|
|
if (i != g->curnode)
|
|
|
|
graph_print_node(g, &g->nodes[i]);
|
|
|
|
|
|
|
|
/* draw current node now to make it appear on top */
|
|
|
|
graph_print_node (g, &g->nodes[g->curnode]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void graph_print_edge(struct graph *g, Node *a, Node *b, int nth) {
|
|
|
|
int x, y, x2, y2;
|
|
|
|
int xinc = 3 + 2 * (nth + 1);
|
|
|
|
x = a->x + xinc;
|
|
|
|
y = a->y + a->h;
|
|
|
|
x2 = b->x + xinc;
|
|
|
|
y2 = b->y;
|
|
|
|
if (a == b) {
|
|
|
|
x2 = a->x;
|
|
|
|
y2 = y - 3;
|
|
|
|
}
|
|
|
|
switch (nth) {
|
|
|
|
case 0: L1 (x, y, x2, y2); break;
|
|
|
|
case 1: L2 (x, y, x2, y2); break;
|
|
|
|
case -1: L (x, y, x2, y2); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void graph_print_edges(struct graph *g) {
|
|
|
|
int i;
|
|
|
|
if (g->edges) {
|
|
|
|
for (i = 0; i < g->n_edges; i++) {
|
|
|
|
if (g->edges[i].from == -1 || g->edges[i].to == -1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
Node *a = &g->nodes[g->edges[i].from];
|
|
|
|
Node *b = &g->nodes[g->edges[i].to];
|
|
|
|
int nth = g->edges[i].nth;
|
|
|
|
if (count_exit_edges(g, g->edges[i].from) == 1)
|
|
|
|
nth = -1; // blue line
|
|
|
|
|
|
|
|
graph_print_edge (g, a, b, nth);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void graph_toggle_small_nodes(struct graph *g) {
|
|
|
|
g->is_small_nodes = !g->is_small_nodes;
|
2015-06-03 15:51:08 +02:00
|
|
|
g->need_reload_nodes = R_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void graph_toggle_simple_mode(struct graph *g) {
|
|
|
|
g->is_simple_mode = !g->is_simple_mode;
|
|
|
|
g->need_reload_nodes = R_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void graph_toggle_callgraph(struct graph *g) {
|
|
|
|
g->is_callgraph = !g->is_callgraph;
|
|
|
|
g->need_reload_nodes = R_TRUE;
|
2015-06-03 15:25:05 +02:00
|
|
|
}
|
|
|
|
|
2015-06-03 14:59:53 +02:00
|
|
|
static int graph_reload_nodes(struct graph *g) {
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = reload_nodes(g);
|
|
|
|
if (!ret)
|
|
|
|
return R_FALSE;
|
|
|
|
graph_set_layout(g);
|
2015-06-03 00:18:33 +02:00
|
|
|
return R_TRUE;
|
2014-07-09 05:05:22 +02:00
|
|
|
}
|
2014-04-29 03:53:48 +02:00
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
static void follow_nth(struct graph *g, int nth) {
|
|
|
|
int cn = find_edge_node (g, g->curnode, nth);
|
|
|
|
if (cn != -1) {
|
|
|
|
g->curnode = cn;
|
|
|
|
ostack_push (&g->ostack, cn);
|
|
|
|
}
|
2015-05-30 01:21:48 +02:00
|
|
|
}
|
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
static void graph_follow_true(struct graph *g) {
|
|
|
|
follow_nth(g, 0);
|
2015-06-03 15:51:08 +02:00
|
|
|
graph_update_seek(g, g->curnode, R_FALSE);
|
2015-06-03 00:18:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void graph_follow_false(struct graph *g) {
|
|
|
|
follow_nth(g, 1);
|
2015-06-03 15:51:08 +02:00
|
|
|
graph_update_seek(g, g->curnode, R_FALSE);
|
2015-02-14 05:10:03 +01:00
|
|
|
}
|
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
static void graph_undo_node(struct graph *g) {
|
|
|
|
g->curnode = ostack_pop(&g->ostack);
|
2015-06-03 15:51:08 +02:00
|
|
|
graph_update_seek (g, g->curnode, R_FALSE);
|
2015-06-03 00:18:33 +02:00
|
|
|
}
|
|
|
|
|
2015-06-03 11:38:31 +02:00
|
|
|
static void graph_next_node(struct graph *g) {
|
|
|
|
g->curnode = (g->curnode + 1) % g->n_nodes;
|
2015-06-03 15:25:05 +02:00
|
|
|
ostack_push (&g->ostack, g->curnode);
|
2015-06-03 15:51:08 +02:00
|
|
|
graph_update_seek (g, g->curnode, R_FALSE);
|
2015-06-03 11:38:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void graph_prev_node(struct graph *g) {
|
|
|
|
if (g->curnode == 0)
|
|
|
|
g->curnode = g->n_nodes - 1;
|
|
|
|
else
|
|
|
|
g->curnode = g->curnode - 1;
|
2015-06-03 15:25:05 +02:00
|
|
|
ostack_push (&g->ostack, g->curnode);
|
2015-06-03 15:51:08 +02:00
|
|
|
graph_update_seek (g, g->curnode, R_FALSE);
|
2015-06-03 11:38:31 +02:00
|
|
|
}
|
|
|
|
|
2015-06-11 03:55:15 +02:00
|
|
|
static int graph_refresh(struct graph *g, int fs) {
|
2014-08-04 01:29:53 +02:00
|
|
|
char title[128];
|
2015-06-03 14:20:03 +02:00
|
|
|
int h, w = r_cons_get_size (&h);
|
2015-06-03 15:51:08 +02:00
|
|
|
int ret;
|
2015-06-03 12:24:42 +02:00
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
if (g->is_instep && g->core->io->debug) {
|
2015-05-30 01:36:40 +02:00
|
|
|
RAnalFunction *f;
|
2015-06-03 00:18:33 +02:00
|
|
|
r_core_cmd0 (g->core, "sr pc");
|
|
|
|
f = r_anal_get_fcn_in (g->core->anal, g->core->offset, 0);
|
|
|
|
if (f && f != g->fcn) {
|
|
|
|
g->fcn = f;
|
2015-06-03 15:51:08 +02:00
|
|
|
g->need_reload_nodes = R_TRUE;
|
2014-10-23 03:29:05 +02:00
|
|
|
}
|
|
|
|
}
|
2015-06-03 15:51:08 +02:00
|
|
|
|
|
|
|
/* look for any change in the state of the graph
|
|
|
|
* and update what's necessary */
|
|
|
|
if (g->need_reload_nodes) {
|
|
|
|
ret = graph_reload_nodes(g);
|
|
|
|
if (!ret)
|
|
|
|
return R_FALSE;
|
|
|
|
|
|
|
|
g->need_reload_nodes = R_FALSE;
|
|
|
|
}
|
|
|
|
if (g->need_update_seek) {
|
|
|
|
update_seek(g->can, &g->nodes[g->update_seek_on], g->force_update_seek);
|
|
|
|
g->need_update_seek = R_FALSE;
|
|
|
|
g->update_seek_on = 0;
|
|
|
|
g->force_update_seek = R_FALSE;
|
|
|
|
}
|
|
|
|
|
2015-06-11 03:55:15 +02:00
|
|
|
if (fs) {
|
|
|
|
r_cons_clear00 ();
|
|
|
|
}
|
2015-05-30 01:36:40 +02:00
|
|
|
|
2015-06-11 03:55:15 +02:00
|
|
|
r_cons_canvas_resize (g->can, w, 1024);
|
2015-06-03 00:18:33 +02:00
|
|
|
r_cons_canvas_clear (g->can);
|
2014-08-04 01:29:53 +02:00
|
|
|
|
2015-06-03 14:20:03 +02:00
|
|
|
graph_print_edges(g);
|
|
|
|
graph_print_nodes(g);
|
2014-08-04 01:29:53 +02:00
|
|
|
|
2015-06-11 03:55:15 +02:00
|
|
|
if (fs) {
|
|
|
|
(void)G (-g->can->sx, -g->can->sy);
|
|
|
|
snprintf (title, sizeof (title)-1,
|
|
|
|
"[0x%08"PFMT64x"]> %d VV @ %s (nodes %d edges %d) %s mouse:%s",
|
|
|
|
g->fcn->addr, g->ostack.size, g->fcn->name,
|
|
|
|
g->n_nodes, g->n_edges, g->is_callgraph?"CG":"BB",
|
|
|
|
mousemodes[mousemode]);
|
|
|
|
W (title);
|
|
|
|
}
|
2014-08-04 01:29:53 +02:00
|
|
|
|
2015-06-11 03:55:15 +02:00
|
|
|
if (fs) {
|
|
|
|
r_cons_canvas_print (g->can);
|
|
|
|
} else {
|
|
|
|
r_cons_canvas_print_region (g->can);
|
|
|
|
}
|
|
|
|
if (fs) {
|
|
|
|
const char *cmdv = r_config_get (g->core->config, "cmd.gprompt");
|
|
|
|
if (cmdv && *cmdv) {
|
|
|
|
r_cons_gotoxy (0, 1);
|
|
|
|
r_core_cmd0 (g->core, cmdv);
|
|
|
|
}
|
2014-10-23 03:29:05 +02:00
|
|
|
}
|
2015-06-04 01:30:33 +02:00
|
|
|
r_cons_flush_nonewline ();
|
2015-06-03 00:18:33 +02:00
|
|
|
return R_TRUE;
|
2014-08-04 01:29:53 +02:00
|
|
|
}
|
2014-09-26 15:40:17 +02:00
|
|
|
|
2015-06-03 15:51:08 +02:00
|
|
|
static void graph_init(struct graph *g) {
|
2015-06-03 00:18:33 +02:00
|
|
|
g->nodes = NULL;
|
|
|
|
g->edges = NULL;
|
|
|
|
|
|
|
|
g->is_callgraph = R_FALSE;
|
|
|
|
g->is_instep = R_FALSE;
|
|
|
|
g->is_simple_mode = R_TRUE;
|
|
|
|
g->is_small_nodes = R_FALSE;
|
2015-06-03 15:51:08 +02:00
|
|
|
g->need_reload_nodes = R_TRUE;
|
2015-06-03 00:18:33 +02:00
|
|
|
g->curnode = 0;
|
2015-06-03 15:51:08 +02:00
|
|
|
g->need_update_seek = R_TRUE;
|
|
|
|
g->update_seek_on = g->curnode;
|
|
|
|
g->force_update_seek = R_TRUE;
|
2015-06-03 00:18:33 +02:00
|
|
|
|
2015-06-03 15:51:08 +02:00
|
|
|
ostack_init(&g->ostack);
|
2014-10-23 03:29:05 +02:00
|
|
|
}
|
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
static struct graph *graph_new(RCore *core, RConsCanvas *can, RAnalFunction *fcn) {
|
|
|
|
struct graph *g;
|
2015-04-11 02:41:02 +03:00
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
g = (struct graph *)malloc(sizeof(struct graph));
|
|
|
|
if (!g)
|
2015-06-03 15:51:08 +02:00
|
|
|
return NULL;
|
2015-04-11 02:41:02 +03:00
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
g->core = core;
|
|
|
|
g->can = can;
|
|
|
|
g->fcn = fcn;
|
|
|
|
|
2015-06-03 15:51:08 +02:00
|
|
|
graph_init(g);
|
2015-06-03 00:18:33 +02:00
|
|
|
return g;
|
2014-10-23 02:13:25 +02:00
|
|
|
}
|
|
|
|
|
2015-06-11 03:55:15 +02:00
|
|
|
R_API int r_core_fcn_graph(RCore *core, RAnalFunction *_fcn) {
|
|
|
|
RAnalFunction *fcn;
|
|
|
|
RConsCanvas *can;
|
|
|
|
struct graph *g;
|
|
|
|
int h;
|
|
|
|
|
|
|
|
fcn = _fcn? _fcn: r_anal_get_fcn_in (core->anal, core->offset, 0);
|
|
|
|
if (!fcn) {
|
|
|
|
eprintf ("No function in current seek\n");
|
|
|
|
return R_FALSE;
|
|
|
|
}
|
|
|
|
(void)r_cons_get_size (&h);
|
|
|
|
can = r_cons_canvas_new (1,1); //w - 1, h - 1);
|
|
|
|
if (!can) {
|
|
|
|
eprintf ("Cannot create RCons.canvas context\n");
|
|
|
|
return R_FALSE;
|
|
|
|
}
|
|
|
|
can->linemode = 1;
|
|
|
|
can->color = r_config_get_i (core->config, "scr.color");
|
|
|
|
// disable colors in disasm because canvas doesnt supports ansi text yet
|
|
|
|
r_config_set_i (core->config, "scr.color", 0);
|
|
|
|
|
|
|
|
g = graph_new (core, can, fcn);
|
|
|
|
if (!g) {
|
|
|
|
goto err_graph_new;
|
|
|
|
}
|
|
|
|
|
|
|
|
(void)graph_refresh (g, 0);
|
|
|
|
r_cons_printf (Color_RESET);
|
|
|
|
graph_free (g);
|
|
|
|
err_graph_new:
|
|
|
|
r_config_set_i (core->config, "scr.color", can->color);
|
|
|
|
free (can);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-07-09 05:05:22 +02:00
|
|
|
R_API int r_core_visual_graph(RCore *core, RAnalFunction *_fcn) {
|
2015-06-03 00:18:33 +02:00
|
|
|
RAnalFunction *fcn;
|
|
|
|
RConsCanvas *can;
|
|
|
|
struct graph *g;
|
|
|
|
int ret;
|
2015-02-17 01:28:51 +01:00
|
|
|
int wheelspeed;
|
2015-06-03 00:18:33 +02:00
|
|
|
int okey, key, wheel;
|
|
|
|
int w, h;
|
|
|
|
int exit_graph = R_FALSE, is_error = R_FALSE;
|
|
|
|
|
2014-09-26 18:10:33 +02:00
|
|
|
fcn = _fcn? _fcn: r_anal_get_fcn_in (core->anal, core->offset, 0);
|
2014-07-09 05:05:22 +02:00
|
|
|
if (!fcn) {
|
|
|
|
eprintf ("No function in current seek\n");
|
|
|
|
return R_FALSE;
|
2014-04-29 03:53:48 +02:00
|
|
|
}
|
2014-07-09 05:05:22 +02:00
|
|
|
w = r_cons_get_size (&h);
|
2015-06-03 00:18:33 +02:00
|
|
|
can = r_cons_canvas_new (w - 1, h - 1);
|
2015-06-02 23:50:02 +02:00
|
|
|
if (!can) {
|
|
|
|
eprintf ("Cannot create RCons.canvas context\n");
|
|
|
|
return R_FALSE;
|
|
|
|
}
|
2015-02-15 21:34:12 +01:00
|
|
|
can->linemode = 1;
|
2015-02-14 04:50:29 +01:00
|
|
|
can->color = r_config_get_i (core->config, "scr.color");
|
|
|
|
// disable colors in disasm because canvas doesnt supports ansi text yet
|
|
|
|
r_config_set_i (core->config, "scr.color", 0);
|
2015-06-03 00:18:33 +02:00
|
|
|
|
2015-06-11 03:55:15 +02:00
|
|
|
g = graph_new (core, can, fcn);
|
2015-06-03 00:18:33 +02:00
|
|
|
if (!g) {
|
|
|
|
is_error = R_TRUE;
|
|
|
|
goto err_graph_new;
|
2014-08-03 14:43:44 +02:00
|
|
|
}
|
2014-05-05 03:15:28 +02:00
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
core->cons->event_data = g;
|
|
|
|
core->cons->event_resize = (RConsEvent)graph_refresh;
|
|
|
|
|
|
|
|
while (!exit_graph && !is_error) {
|
2015-05-30 01:36:40 +02:00
|
|
|
w = r_cons_get_size (&h);
|
2015-06-11 03:55:15 +02:00
|
|
|
ret = graph_refresh (g, 1);
|
2015-06-03 00:18:33 +02:00
|
|
|
if (!ret) {
|
|
|
|
is_error = R_TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-06-04 01:30:33 +02:00
|
|
|
r_cons_show_cursor(R_FALSE);
|
2015-05-30 01:36:40 +02:00
|
|
|
wheel = r_config_get_i (core->config, "scr.wheel");
|
|
|
|
if (wheel)
|
|
|
|
r_cons_enable_mouse (R_TRUE);
|
|
|
|
|
|
|
|
// r_core_graph_inputhandle()
|
|
|
|
okey = r_cons_readchar ();
|
|
|
|
key = r_cons_arrow_to_hjkl (okey);
|
|
|
|
if (r_cons_singleton()->mouse_event) {
|
|
|
|
wheelspeed = r_config_get_i (core->config, "scr.wheelspeed");
|
2014-07-09 05:05:22 +02:00
|
|
|
} else {
|
2015-05-30 01:36:40 +02:00
|
|
|
wheelspeed = 1;
|
2014-07-09 05:05:22 +02:00
|
|
|
}
|
2015-05-30 01:36:40 +02:00
|
|
|
|
|
|
|
switch (key) {
|
|
|
|
case '=':
|
|
|
|
case '|':
|
|
|
|
{ // TODO: edit
|
|
|
|
const char *buf = NULL;
|
|
|
|
const char *cmd = r_config_get (core->config, "cmd.gprompt");
|
|
|
|
r_line_set_prompt ("cmd.gprompt> ");
|
|
|
|
core->cons->line->contents = strdup (cmd);
|
|
|
|
buf = r_line_readline ();
|
|
|
|
core->cons->line->contents = NULL;
|
|
|
|
r_config_set (core->config, "cmd.gprompt", buf);
|
2015-02-17 01:28:51 +01:00
|
|
|
}
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'O':
|
2015-06-03 15:51:08 +02:00
|
|
|
graph_toggle_simple_mode(g);
|
2015-02-17 01:28:51 +01:00
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'V':
|
2015-06-03 15:51:08 +02:00
|
|
|
graph_toggle_callgraph(g);
|
2015-02-17 01:28:51 +01:00
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'z':
|
2015-06-03 00:18:33 +02:00
|
|
|
g->is_instep = R_TRUE;
|
2015-05-30 01:36:40 +02:00
|
|
|
if (r_config_get_i (core->config, "cfg.debug"))
|
|
|
|
r_core_cmd0 (core, "ds;.dr*");
|
2015-06-03 11:38:31 +02:00
|
|
|
else
|
|
|
|
r_core_cmd0 (core, "aes;.dr*");
|
2015-06-03 00:18:33 +02:00
|
|
|
|
|
|
|
ret = graph_reload_nodes(g);
|
|
|
|
if (!ret)
|
|
|
|
is_error = R_TRUE;
|
2015-02-17 01:28:51 +01:00
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'Z':
|
|
|
|
if (okey == 27) {
|
2015-06-03 11:38:31 +02:00
|
|
|
graph_prev_node(g);
|
2015-05-30 01:36:40 +02:00
|
|
|
} else {
|
|
|
|
// 'Z'
|
2015-06-03 00:18:33 +02:00
|
|
|
g->is_instep = R_TRUE;
|
2015-05-30 01:36:40 +02:00
|
|
|
if (r_config_get_i (core->config, "cfg.debug"))
|
|
|
|
r_core_cmd0 (core, "dso;.dr*");
|
2015-06-03 11:38:31 +02:00
|
|
|
else
|
|
|
|
r_core_cmd0 (core, "aeso;.dr*");
|
2015-06-03 00:18:33 +02:00
|
|
|
|
|
|
|
ret = graph_reload_nodes(g);
|
|
|
|
if (!ret)
|
|
|
|
is_error = R_TRUE;
|
2015-05-30 01:36:40 +02:00
|
|
|
}
|
2015-02-17 01:28:51 +01:00
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'x':
|
|
|
|
if (r_core_visual_xrefs_x (core))
|
2015-06-03 00:18:33 +02:00
|
|
|
exit_graph = R_TRUE;
|
2015-02-17 01:28:51 +01:00
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'X':
|
|
|
|
if (r_core_visual_xrefs_X (core))
|
2015-06-03 00:18:33 +02:00
|
|
|
exit_graph = R_TRUE;
|
2015-02-17 01:28:51 +01:00
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 9: // tab
|
2015-06-03 11:38:31 +02:00
|
|
|
graph_next_node(g);
|
2015-02-17 01:28:51 +01:00
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case '?':
|
|
|
|
r_cons_clear00 ();
|
|
|
|
r_cons_printf ("Visual Ascii Art graph keybindings:\n"
|
|
|
|
" . - center graph to the current node\n"
|
|
|
|
" C - toggle scr.color\n"
|
|
|
|
" hjkl - move node\n"
|
|
|
|
" asdw - scroll canvas\n"
|
|
|
|
" tab - select next node\n"
|
|
|
|
" TAB - select previous node\n"
|
|
|
|
" t/f - follow true/false edges\n"
|
|
|
|
" e - toggle edge-lines style (diagonal/square)\n"
|
|
|
|
" O - toggle disasm mode\n"
|
2015-06-09 01:45:39 +02:00
|
|
|
" p - toggle mini-graph\n"
|
2015-05-30 01:36:40 +02:00
|
|
|
" u - select previous node\n"
|
|
|
|
" V - toggle basicblock / call graphs\n"
|
|
|
|
" x/X - jump to xref/ref\n"
|
|
|
|
" z/Z - step / step over\n"
|
|
|
|
" R - relayout\n");
|
|
|
|
r_cons_flush ();
|
|
|
|
r_cons_any_key (NULL);
|
|
|
|
break;
|
|
|
|
case 'R':
|
2015-06-03 00:18:33 +02:00
|
|
|
case 'r':
|
|
|
|
graph_set_layout (g);
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'j':
|
2015-06-03 00:18:33 +02:00
|
|
|
if (r_cons_singleton()->mouse_event) {
|
|
|
|
switch (mousemode) {
|
|
|
|
case 0: // canvas-y
|
|
|
|
can->sy += wheelspeed;
|
|
|
|
break;
|
|
|
|
case 1: // canvas-x
|
|
|
|
can->sx += wheelspeed;
|
|
|
|
break;
|
|
|
|
case 2: // node-y
|
|
|
|
get_current_node(g)->y += wheelspeed;
|
|
|
|
break;
|
|
|
|
case 3: // node-x
|
|
|
|
get_current_node(g)->x += wheelspeed;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
get_current_node(g)->y++;
|
|
|
|
}
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'k':
|
2015-06-03 00:18:33 +02:00
|
|
|
if (r_cons_singleton()->mouse_event) {
|
|
|
|
switch (mousemode) {
|
|
|
|
case 0: // canvas-y
|
|
|
|
can->sy -= wheelspeed;
|
|
|
|
break;
|
|
|
|
case 1: // canvas-x
|
|
|
|
can->sx -= wheelspeed;
|
|
|
|
break;
|
|
|
|
case 2: // node-y
|
|
|
|
get_current_node(g)->y -= wheelspeed;
|
|
|
|
break;
|
|
|
|
case 3: // node-x
|
|
|
|
get_current_node(g)->x -= wheelspeed;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
get_current_node(g)->y--;
|
|
|
|
}
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'm':
|
2015-06-03 00:18:33 +02:00
|
|
|
mousemode++;
|
|
|
|
if (!mousemodes[mousemode])
|
|
|
|
mousemode = 0;
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'M':
|
2015-06-03 00:18:33 +02:00
|
|
|
mousemode--;
|
|
|
|
if (mousemode<0)
|
|
|
|
mousemode = 3;
|
|
|
|
break;
|
|
|
|
case 'h': get_current_node(g)->x--; break;
|
|
|
|
case 'l': get_current_node(g)->x++; break;
|
|
|
|
case 'J': get_current_node(g)->y += 5; break;
|
|
|
|
case 'K': get_current_node(g)->y -= 5; break;
|
|
|
|
case 'H': get_current_node(g)->x -= 5; break;
|
|
|
|
case 'L': get_current_node(g)->x += 5; break;
|
2015-05-30 01:36:40 +02:00
|
|
|
// scroll
|
|
|
|
case '0': can->sx = can->sy = 0; break;
|
|
|
|
case 'w': can->sy -= 1; break;
|
|
|
|
case 's': can->sy += 1; break;
|
|
|
|
case 'a': can->sx -= 1; break;
|
|
|
|
case 'd': can->sx += 1; break;
|
|
|
|
case 'W': can->sy -= 5; break;
|
|
|
|
case 'S': can->sy += 5; break;
|
|
|
|
case 'A': can->sx -= 5; break;
|
|
|
|
case 'D': can->sx += 5; break;
|
|
|
|
case 'e':
|
2015-06-03 00:18:33 +02:00
|
|
|
can->linemode = !!!can->linemode;
|
|
|
|
break;
|
2015-06-09 01:45:39 +02:00
|
|
|
case 'p':
|
2015-06-03 14:47:34 +02:00
|
|
|
graph_toggle_small_nodes(g);
|
2015-06-03 00:18:33 +02:00
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'u':
|
2015-06-03 00:18:33 +02:00
|
|
|
graph_undo_node(g);
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case '.':
|
2015-06-03 15:51:08 +02:00
|
|
|
graph_update_seek (g, g->curnode, R_TRUE);
|
2015-06-03 00:18:33 +02:00
|
|
|
g->is_instep = R_TRUE;
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 't':
|
2015-06-03 00:18:33 +02:00
|
|
|
graph_follow_true(g);
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'f':
|
2015-06-03 00:18:33 +02:00
|
|
|
graph_follow_false(g);
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case '/':
|
2015-06-03 00:18:33 +02:00
|
|
|
r_core_cmd0 (core, "?i highlight;e scr.highlight=`?y`");
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case ':':
|
2015-06-03 00:18:33 +02:00
|
|
|
core->vmode = R_FALSE;
|
|
|
|
r_core_visual_prompt_input (core);
|
|
|
|
core->vmode = R_TRUE;
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 'C':
|
2015-06-03 00:18:33 +02:00
|
|
|
can->color = !!!can->color;
|
|
|
|
//r_config_swap (core->config, "scr.color");
|
|
|
|
// refresh graph
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case -1: // EOF
|
|
|
|
case 'q':
|
2015-06-03 00:18:33 +02:00
|
|
|
exit_graph = R_TRUE;
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
case 27: // ESC
|
2015-06-03 00:18:33 +02:00
|
|
|
if (r_cons_readchar () == 91) {
|
|
|
|
if (r_cons_readchar () == 90) {
|
|
|
|
if (g->curnode < 1) {
|
|
|
|
int i;
|
2015-06-03 12:24:42 +02:00
|
|
|
for (i = 0; i < g->n_nodes; i++) ;
|
|
|
|
g->curnode = i - 1;
|
|
|
|
} else {
|
|
|
|
g->curnode--;
|
|
|
|
}
|
2015-05-30 01:36:40 +02:00
|
|
|
}
|
2015-06-03 00:18:33 +02:00
|
|
|
}
|
|
|
|
break;
|
2015-05-30 01:36:40 +02:00
|
|
|
default:
|
2015-06-03 00:18:33 +02:00
|
|
|
eprintf ("Key %d\n", key);
|
|
|
|
//sleep (1);
|
|
|
|
break;
|
2014-04-29 03:53:48 +02:00
|
|
|
}
|
|
|
|
}
|
2015-05-30 01:36:40 +02:00
|
|
|
|
2015-06-03 00:18:33 +02:00
|
|
|
graph_free(g);
|
|
|
|
err_graph_new:
|
2014-08-05 05:37:48 +02:00
|
|
|
free (can);
|
2015-06-11 03:55:15 +02:00
|
|
|
r_config_set_i (core->config, "scr.color", can->color);
|
2015-06-03 00:18:33 +02:00
|
|
|
return !is_error;
|
2014-04-29 03:53:48 +02:00
|
|
|
}
|