-
Notifications
You must be signed in to change notification settings - Fork 2
/
dump_call_graph_plugin.c
210 lines (179 loc) · 4.92 KB
/
dump_call_graph_plugin.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/*
* Copyright 2011-2015 by Emese Revfy <re.emese@gmail.com>
* Licensed under the GPL v2
*
* Homepage:
* http://www.grsecurity.net/~ephox/dump_call_graph_plugin/
*
* Usage:
* $ make clean; make run &> result
* $ create_cfg/create_call_graph.py -l result -g test -o test.txt -n fn_1
*
* Example cfg (printk):
* http://www.grsecurity.net/~ephox/dump_call_graph_plugin/printk_1.png
*/
#include "gcc-common.h"
int plugin_is_GPL_compatible;
static struct plugin_info dump_call_graph_plugin_info = {
.version = "20150523",
.help = "dump cfg\n",
};
#if BUILDING_GCC_VERSION >= 5000
typedef struct hash_set<const_tree> tree_set;
static inline bool pointer_set_insert(tree_set *visited, const_tree node)
{
return visited->add(node);
}
static inline bool pointer_set_contains(tree_set *visited, const_tree node)
{
return visited->contains(node);
}
static inline tree_set* pointer_set_create(void)
{
return new hash_set<const_tree>;
}
static inline void pointer_set_destroy(tree_set *visited)
{
delete visited;
}
#else
typedef struct pointer_set_t tree_set;
#endif
static void print_function(const_tree caller, const_tree callee)
{
expanded_location xloc;
const char *caller_name, *callee_name;
gcc_assert(callee != NULL_TREE);
if (DECL_ABSTRACT_ORIGIN(callee) != NULL_TREE)
return;
callee_name = DECL_NAME_POINTER(callee);
gcc_assert(caller != NULL_TREE);
caller_name = DECL_NAME_POINTER(caller);
xloc = expand_location(DECL_SOURCE_LOCATION(caller));
fprintf(stderr, "DUMP_CFG:%s:%s:%s\n", caller_name, callee_name, xloc.file);
}
static void walk_functions(tree_set *visited, const struct cgraph_node *node)
{
struct cgraph_edge *e;
const_tree caller;
if (!node)
return;
caller = NODE_DECL(node);
if (pointer_set_insert(visited, caller))
return;
for (e = node->callees; e; e = e->next_callee) {
const struct cgraph_node *next_node;
tree callee = gimple_call_fndecl(e->call_stmt);
if (DECL_BUILT_IN(callee))
continue;
print_function(caller, callee);
next_node = cgraph_get_node(callee);
walk_functions(visited, next_node);
}
}
static unsigned int handle_functions(void)
{
struct cgraph_node *node;
tree_set *visited;
visited = pointer_set_create();
FOR_EACH_FUNCTION_WITH_GIMPLE_BODY(node) {
if (DECL_BUILT_IN(NODE_DECL(node)))
continue;
walk_functions(visited, node);
}
pointer_set_destroy(visited);
return 0;
}
#if BUILDING_GCC_VERSION >= 4009
static const struct pass_data dump_call_graph_plugin_pass_data = {
#else
static struct ipa_opt_pass_d dump_call_graph_plugin_pass = {
.pass = {
#endif
.type = SIMPLE_IPA_PASS,
.name = "dump_call_graph_plugin",
#if BUILDING_GCC_VERSION >= 4008
.optinfo_flags = OPTGROUP_NONE,
#endif
#if BUILDING_GCC_VERSION >= 5000
#elif BUILDING_GCC_VERSION >= 4009
.has_gate = false,
.has_execute = true,
#else
.gate = NULL,
.execute = handle_functions,
.sub = NULL,
.next = NULL,
.static_pass_number = 0,
#endif
.tv_id = TV_NONE,
.properties_required = 0,
.properties_provided = 0,
.properties_destroyed = 0,
.todo_flags_start = 0,
.todo_flags_finish = 0,
#if BUILDING_GCC_VERSION < 4009
},
.generate_summary = NULL,
.write_summary = NULL,
.read_summary = NULL,
#if BUILDING_GCC_VERSION >= 4006
.write_optimization_summary = NULL,
.read_optimization_summary = NULL,
#endif
.stmt_fixup = NULL,
.function_transform_todo_flags_start = 0,
.function_transform = NULL,
.variable_transform = NULL,
#endif
};
#if BUILDING_GCC_VERSION >= 4009
namespace {
class dump_call_graph_plugin_pass : public ipa_opt_pass_d {
public:
dump_call_graph_plugin_pass() : ipa_opt_pass_d(dump_call_graph_plugin_pass_data,
g,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
0,
NULL,
NULL) {}
#if BUILDING_GCC_VERSION >= 5000
virtual unsigned int execute(function *) { return handle_functions(); }
#else
unsigned int execute() { return handle_functions(); }
#endif
};
}
opt_pass *make_dump_call_graph_plugin_pass(void)
{
return new dump_call_graph_plugin_pass();
}
#else
struct opt_pass *make_dump_call_graph_plugin_pass(void)
{
return &dump_call_graph_plugin_pass.pass;
}
#endif
int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version)
{
const char * const plugin_name = plugin_info->base_name;
bool enable = true;
struct register_pass_info dump_call_graph_plugin_pass_info;
dump_call_graph_plugin_pass_info.pass = make_dump_call_graph_plugin_pass();
dump_call_graph_plugin_pass_info.reference_pass_name = "increase_alignment";
dump_call_graph_plugin_pass_info.ref_pass_instance_number = 1;
dump_call_graph_plugin_pass_info.pos_op = PASS_POS_INSERT_BEFORE;
if (!plugin_default_version_check(version, &gcc_version)) {
error(G_("incompatible gcc/plugin versions"));
return 1;
}
register_callback(plugin_name, PLUGIN_INFO, NULL, &dump_call_graph_plugin_info);
if (enable)
register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &dump_call_graph_plugin_pass_info);
return 0;
}