xref: /linux/scripts/gcc-plugins/sancov_plugin.c (revision ea21f589de9a7d787f50da480d01457d8dcdd64a)
1  /*
2   * Copyright 2011-2016 by Emese Revfy <re.emese@gmail.com>
3   * Licensed under the GPL v2, or (at your option) v3
4   *
5   * Homepage:
6   * https://github.com/ephox-gcc-plugins/sancov
7   *
8   * This plugin inserts a __sanitizer_cov_trace_pc() call at the start of basic blocks.
9   * It supports all gcc versions with plugin support (from gcc-4.5 on).
10   * It is based on the commit "Add fuzzing coverage support" by Dmitry Vyukov <dvyukov@google.com>.
11   *
12   * You can read about it more here:
13   *  https://gcc.gnu.org/viewcvs/gcc?limit_changes=0&view=revision&revision=231296
14   *  https://lwn.net/Articles/674854/
15   *  https://github.com/google/syzkaller
16   *  https://lwn.net/Articles/677764/
17   *
18   * Usage:
19   * make run
20   */
21  
22  #include "gcc-common.h"
23  
24  __visible int plugin_is_GPL_compatible;
25  
26  tree sancov_fndecl;
27  
28  static struct plugin_info sancov_plugin_info = {
29  	.version	= "20160402",
30  	.help		= "sancov plugin\n",
31  };
32  
33  static unsigned int sancov_execute(void)
34  {
35  	basic_block bb;
36  
37  	/* Remove this line when this plugin and kcov will be in the kernel.
38  	if (!strcmp(DECL_NAME_POINTER(current_function_decl), DECL_NAME_POINTER(sancov_fndecl)))
39  		return 0;
40  	*/
41  
42  	FOR_EACH_BB_FN(bb, cfun) {
43  		const_gimple stmt;
44  		gcall *gcall;
45  		gimple_stmt_iterator gsi = gsi_after_labels(bb);
46  
47  		if (gsi_end_p(gsi))
48  			continue;
49  
50  		stmt = gsi_stmt(gsi);
51  		gcall = as_a_gcall(gimple_build_call(sancov_fndecl, 0));
52  		gimple_set_location(gcall, gimple_location(stmt));
53  		gsi_insert_before(&gsi, gcall, GSI_SAME_STMT);
54  	}
55  	return 0;
56  }
57  
58  #define PASS_NAME sancov
59  
60  #define NO_GATE
61  #define TODO_FLAGS_FINISH TODO_dump_func | TODO_verify_stmts | TODO_update_ssa_no_phi | TODO_verify_flow
62  
63  #include "gcc-generate-gimple-pass.h"
64  
65  static void sancov_start_unit(void __unused *gcc_data, void __unused *user_data)
66  {
67  	tree leaf_attr, nothrow_attr;
68  	tree BT_FN_VOID = build_function_type_list(void_type_node, NULL_TREE);
69  
70  	sancov_fndecl = build_fn_decl("__sanitizer_cov_trace_pc", BT_FN_VOID);
71  
72  	DECL_ASSEMBLER_NAME(sancov_fndecl);
73  	TREE_PUBLIC(sancov_fndecl) = 1;
74  	DECL_EXTERNAL(sancov_fndecl) = 1;
75  	DECL_ARTIFICIAL(sancov_fndecl) = 1;
76  	DECL_PRESERVE_P(sancov_fndecl) = 1;
77  	DECL_UNINLINABLE(sancov_fndecl) = 1;
78  	TREE_USED(sancov_fndecl) = 1;
79  
80  	nothrow_attr = tree_cons(get_identifier("nothrow"), NULL, NULL);
81  	decl_attributes(&sancov_fndecl, nothrow_attr, 0);
82  	gcc_assert(TREE_NOTHROW(sancov_fndecl));
83  #if BUILDING_GCC_VERSION > 4005
84  	leaf_attr = tree_cons(get_identifier("leaf"), NULL, NULL);
85  	decl_attributes(&sancov_fndecl, leaf_attr, 0);
86  #endif
87  }
88  
89  __visible int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version)
90  {
91  	int i;
92  	const char * const plugin_name = plugin_info->base_name;
93  	const int argc = plugin_info->argc;
94  	const struct plugin_argument * const argv = plugin_info->argv;
95  	bool enable = true;
96  
97  	static const struct ggc_root_tab gt_ggc_r_gt_sancov[] = {
98  		{
99  			.base = &sancov_fndecl,
100  			.nelt = 1,
101  			.stride = sizeof(sancov_fndecl),
102  			.cb = &gt_ggc_mx_tree_node,
103  			.pchw = &gt_pch_nx_tree_node
104  		},
105  		LAST_GGC_ROOT_TAB
106  	};
107  
108  	/* BBs can be split afterwards?? */
109  #if BUILDING_GCC_VERSION >= 4009
110  	PASS_INFO(sancov, "asan", 0, PASS_POS_INSERT_BEFORE);
111  #else
112  	PASS_INFO(sancov, "nrv", 1, PASS_POS_INSERT_BEFORE);
113  #endif
114  
115  	if (!plugin_default_version_check(version, &gcc_version)) {
116  		error(G_("incompatible gcc/plugin versions"));
117  		return 1;
118  	}
119  
120  	for (i = 0; i < argc; ++i) {
121  		if (!strcmp(argv[i].key, "no-sancov")) {
122  			enable = false;
123  			continue;
124  		}
125  		error(G_("unknown option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key);
126  	}
127  
128  	register_callback(plugin_name, PLUGIN_INFO, NULL, &sancov_plugin_info);
129  
130  	if (!enable)
131  		return 0;
132  
133  #if BUILDING_GCC_VERSION < 6000
134  	register_callback(plugin_name, PLUGIN_START_UNIT, &sancov_start_unit, NULL);
135  	register_callback(plugin_name, PLUGIN_REGISTER_GGC_ROOTS, NULL, (void *)&gt_ggc_r_gt_sancov);
136  	register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &sancov_pass_info);
137  #endif
138  
139  	return 0;
140  }
141