]>
Commit | Line | Data |
---|---|---|
d5e254e1 | 1 | /* Pointer Bounds Checker IPA passes. |
5624e564 | 2 | Copyright (C) 2014-2015 Free Software Foundation, Inc. |
d5e254e1 IE |
3 | Contributed by Ilya Enkovich (ilya.enkovich@intel.com) |
4 | ||
5 | This file is part of GCC. | |
6 | ||
7 | GCC is free software; you can redistribute it and/or modify it under | |
8 | the terms of the GNU General Public License as published by the Free | |
9 | Software Foundation; either version 3, or (at your option) any later | |
10 | version. | |
11 | ||
12 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
13 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 | for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with GCC; see the file COPYING3. If not see | |
19 | <http://www.gnu.org/licenses/>. */ | |
20 | ||
21 | #include "config.h" | |
22 | #include "system.h" | |
23 | #include "coretypes.h" | |
c7131fb2 | 24 | #include "backend.h" |
957060b5 | 25 | #include "hard-reg-set.h" |
d5e254e1 | 26 | #include "tree.h" |
c7131fb2 | 27 | #include "gimple.h" |
d5e254e1 IE |
28 | #include "tree-pass.h" |
29 | #include "stringpool.h" | |
d5e254e1 | 30 | #include "lto-streamer.h" |
957060b5 AM |
31 | #include "alias.h" |
32 | #include "fold-const.h" | |
33 | #include "stor-layout.h" | |
34 | #include "calls.h" | |
d5e254e1 IE |
35 | #include "cgraph.h" |
36 | #include "tree-chkp.h" | |
06201ad5 | 37 | #include "tree-inline.h" |
d5e254e1 | 38 | #include "ipa-chkp.h" |
d5e254e1 IE |
39 | |
40 | /* Pointer Bounds Checker has two IPA passes to support code instrumentation. | |
41 | ||
42 | In instrumented code each pointer is provided with bounds. For input | |
43 | pointer parameters it means we also have bounds passed. For calls it | |
44 | means we have additional bounds arguments for pointer arguments. | |
45 | ||
46 | To have all IPA optimizations working correctly we have to express | |
47 | dataflow between passed and received bounds explicitly via additional | |
48 | entries in function declaration arguments list and in function type. | |
49 | Since we may have both instrumented and not instrumented code at the | |
50 | same time, we cannot replace all original functions with their | |
51 | instrumented variants. Therefore we create clones (versions) instead. | |
52 | ||
53 | Instrumentation clones creation is a separate IPA pass which is a part | |
54 | of early local passes. Clones are created after SSA is built (because | |
55 | instrumentation pass works on SSA) and before any transformations | |
56 | which may change pointer flow and therefore lead to incorrect code | |
57 | instrumentation (possibly causing false bounds check failures). | |
58 | ||
59 | Instrumentation clones have pointer bounds arguments added right after | |
60 | pointer arguments. Clones have assembler name of the original | |
61 | function with suffix added. New assembler name is in transparent | |
62 | alias chain with the original name. Thus we expect all calls to the | |
63 | original and instrumented functions look similar in assembler. | |
64 | ||
65 | During instrumentation versioning pass we create instrumented versions | |
66 | of all function with body and also for all their aliases and thunks. | |
67 | Clones for functions with no body are created on demand (usually | |
68 | during call instrumentation). | |
69 | ||
70 | Original and instrumented function nodes are connected with IPA | |
71 | reference IPA_REF_CHKP. It is mostly done to have reachability | |
72 | analysis working correctly. We may have no references to the | |
73 | instrumented function in the code but it still should be counted | |
74 | as reachable if the original function is reachable. | |
75 | ||
76 | When original function bodies are not needed anymore we release | |
77 | them and transform functions into a special kind of thunks. Each | |
78 | thunk has a call edge to the instrumented version. These thunks | |
79 | help to keep externally visible instrumented functions visible | |
80 | when linker resolution files are used. Linker has no info about | |
81 | connection between original and instrumented function and | |
82 | therefore we may wrongly decide (due to difference in assembler | |
83 | names) that instrumented function version is local and can be | |
84 | removed. */ | |
85 | ||
86 | #define CHKP_BOUNDS_OF_SYMBOL_PREFIX "__chkp_bounds_of_" | |
226d62d2 IE |
87 | #define CHKP_WRAPPER_SYMBOL_PREFIX "__mpx_wrapper_" |
88 | ||
89 | /* Return 1 calls to FNDECL should be replaced with | |
90 | a call to wrapper function. */ | |
26625161 | 91 | bool |
226d62d2 IE |
92 | chkp_wrap_function (tree fndecl) |
93 | { | |
94 | if (!flag_chkp_use_wrappers) | |
95 | return false; | |
96 | ||
97 | if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL) | |
98 | { | |
99 | switch (DECL_FUNCTION_CODE (fndecl)) | |
100 | { | |
101 | case BUILT_IN_STRLEN: | |
102 | case BUILT_IN_STRCPY: | |
103 | case BUILT_IN_STRNCPY: | |
104 | case BUILT_IN_STPCPY: | |
105 | case BUILT_IN_STPNCPY: | |
106 | case BUILT_IN_STRCAT: | |
107 | case BUILT_IN_STRNCAT: | |
108 | case BUILT_IN_MEMCPY: | |
109 | case BUILT_IN_MEMPCPY: | |
110 | case BUILT_IN_MEMSET: | |
111 | case BUILT_IN_MEMMOVE: | |
112 | case BUILT_IN_BZERO: | |
113 | case BUILT_IN_MALLOC: | |
114 | case BUILT_IN_CALLOC: | |
115 | case BUILT_IN_REALLOC: | |
116 | return 1; | |
117 | ||
118 | default: | |
119 | return 0; | |
120 | } | |
121 | } | |
122 | ||
123 | return false; | |
124 | } | |
d5e254e1 | 125 | |
26625161 IE |
126 | static const char * |
127 | chkp_wrap_function_name (tree fndecl) | |
128 | { | |
129 | gcc_assert (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL); | |
130 | ||
131 | switch (DECL_FUNCTION_CODE (fndecl)) | |
132 | { | |
133 | case BUILT_IN_STRLEN: | |
134 | return CHKP_WRAPPER_SYMBOL_PREFIX "strlen"; | |
135 | case BUILT_IN_STRCPY: | |
136 | return CHKP_WRAPPER_SYMBOL_PREFIX "strcpy"; | |
137 | case BUILT_IN_STRNCPY: | |
138 | return CHKP_WRAPPER_SYMBOL_PREFIX "strncpy"; | |
139 | case BUILT_IN_STPCPY: | |
140 | return CHKP_WRAPPER_SYMBOL_PREFIX "stpcpy"; | |
141 | case BUILT_IN_STPNCPY: | |
142 | return CHKP_WRAPPER_SYMBOL_PREFIX "stpncpy"; | |
143 | case BUILT_IN_STRCAT: | |
144 | return CHKP_WRAPPER_SYMBOL_PREFIX "strcat"; | |
145 | case BUILT_IN_STRNCAT: | |
146 | return CHKP_WRAPPER_SYMBOL_PREFIX "strncat"; | |
147 | case BUILT_IN_MEMCPY: | |
148 | return CHKP_WRAPPER_SYMBOL_PREFIX "memcpy"; | |
149 | case BUILT_IN_MEMPCPY: | |
150 | return CHKP_WRAPPER_SYMBOL_PREFIX "mempcpy"; | |
151 | case BUILT_IN_MEMSET: | |
152 | return CHKP_WRAPPER_SYMBOL_PREFIX "memset"; | |
153 | case BUILT_IN_MEMMOVE: | |
154 | return CHKP_WRAPPER_SYMBOL_PREFIX "memmove"; | |
155 | case BUILT_IN_BZERO: | |
156 | return CHKP_WRAPPER_SYMBOL_PREFIX "bzero"; | |
157 | case BUILT_IN_MALLOC: | |
158 | return CHKP_WRAPPER_SYMBOL_PREFIX "malloc"; | |
159 | case BUILT_IN_CALLOC: | |
160 | return CHKP_WRAPPER_SYMBOL_PREFIX "calloc"; | |
161 | case BUILT_IN_REALLOC: | |
162 | return CHKP_WRAPPER_SYMBOL_PREFIX "realloc"; | |
163 | ||
164 | default: | |
165 | gcc_unreachable (); | |
166 | } | |
167 | ||
168 | return ""; | |
169 | } | |
170 | ||
d5e254e1 IE |
171 | /* Build a clone of FNDECL with a modified name. */ |
172 | ||
173 | static tree | |
174 | chkp_build_instrumented_fndecl (tree fndecl) | |
175 | { | |
176 | tree new_decl = copy_node (fndecl); | |
177 | tree new_name; | |
178 | std::string s; | |
179 | ||
180 | /* called_as_built_in checks DECL_NAME to identify calls to | |
181 | builtins. We want instrumented calls to builtins to be | |
182 | recognized by called_as_built_in. Therefore use original | |
183 | DECL_NAME for cloning with no prefixes. */ | |
184 | s = IDENTIFIER_POINTER (DECL_NAME (fndecl)); | |
185 | s += ".chkp"; | |
186 | DECL_NAME (new_decl) = get_identifier (s.c_str ()); | |
187 | ||
188 | /* References to the original and to the instrumented version | |
189 | should look the same in the output assembly. And we cannot | |
190 | use the same assembler name for the instrumented version | |
191 | because it conflicts with decl merging algorithms in LTO. | |
192 | Achieve the result by using transparent alias name for the | |
193 | instrumented version. */ | |
226d62d2 IE |
194 | if (chkp_wrap_function(fndecl)) |
195 | { | |
26625161 IE |
196 | new_name = get_identifier (chkp_wrap_function_name (fndecl)); |
197 | DECL_VISIBILITY (new_decl) = VISIBILITY_DEFAULT; | |
226d62d2 IE |
198 | } |
199 | else | |
200 | { | |
201 | s = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (fndecl)); | |
202 | s += ".chkp"; | |
203 | new_name = get_identifier (s.c_str ()); | |
204 | IDENTIFIER_TRANSPARENT_ALIAS (new_name) = 1; | |
205 | TREE_CHAIN (new_name) = DECL_ASSEMBLER_NAME (fndecl); | |
206 | } | |
d5e254e1 IE |
207 | SET_DECL_ASSEMBLER_NAME (new_decl, new_name); |
208 | ||
209 | /* For functions with body versioning will make a copy of arguments. | |
210 | For functions with no body we need to do it here. */ | |
211 | if (!gimple_has_body_p (fndecl)) | |
212 | DECL_ARGUMENTS (new_decl) = copy_list (DECL_ARGUMENTS (fndecl)); | |
213 | ||
214 | /* We are going to modify attributes list and therefore should | |
215 | make own copy. */ | |
216 | DECL_ATTRIBUTES (new_decl) = copy_list (DECL_ATTRIBUTES (fndecl)); | |
217 | ||
edcf72f3 IE |
218 | /* Change builtin function code. */ |
219 | if (DECL_BUILT_IN (new_decl)) | |
220 | { | |
221 | gcc_assert (DECL_BUILT_IN_CLASS (new_decl) == BUILT_IN_NORMAL); | |
222 | gcc_assert (DECL_FUNCTION_CODE (new_decl) < BEGIN_CHKP_BUILTINS); | |
223 | DECL_FUNCTION_CODE (new_decl) | |
224 | = (enum built_in_function)(DECL_FUNCTION_CODE (new_decl) | |
225 | + BEGIN_CHKP_BUILTINS + 1); | |
226 | } | |
227 | ||
d5e254e1 IE |
228 | return new_decl; |
229 | } | |
230 | ||
231 | ||
232 | /* Fix operands of attribute from ATTRS list named ATTR_NAME. | |
233 | Integer operands are replaced with values according to | |
234 | INDEXES map having LEN elements. For operands out of len | |
235 | we just add DELTA. */ | |
236 | ||
237 | static void | |
238 | chkp_map_attr_arg_indexes (tree attrs, const char *attr_name, | |
239 | unsigned *indexes, int len, int delta) | |
240 | { | |
241 | tree attr = lookup_attribute (attr_name, attrs); | |
242 | tree op; | |
243 | ||
244 | if (!attr) | |
245 | return; | |
246 | ||
247 | TREE_VALUE (attr) = copy_list (TREE_VALUE (attr)); | |
248 | for (op = TREE_VALUE (attr); op; op = TREE_CHAIN (op)) | |
249 | { | |
250 | int idx; | |
251 | ||
252 | if (TREE_CODE (TREE_VALUE (op)) != INTEGER_CST) | |
253 | continue; | |
254 | ||
255 | idx = TREE_INT_CST_LOW (TREE_VALUE (op)); | |
256 | ||
257 | /* If idx exceeds indexes length then we just | |
258 | keep it at the same distance from the last | |
259 | known arg. */ | |
260 | if (idx > len) | |
261 | idx += delta; | |
262 | else | |
263 | idx = indexes[idx - 1] + 1; | |
264 | TREE_VALUE (op) = build_int_cst (TREE_TYPE (TREE_VALUE (op)), idx); | |
265 | } | |
266 | } | |
267 | ||
268 | /* Make a copy of function type ORIG_TYPE adding pointer | |
269 | bounds as additional arguments. */ | |
270 | ||
271 | tree | |
272 | chkp_copy_function_type_adding_bounds (tree orig_type) | |
273 | { | |
274 | tree type; | |
b0845a1e | 275 | tree arg_type, attrs; |
d5e254e1 IE |
276 | unsigned len = list_length (TYPE_ARG_TYPES (orig_type)); |
277 | unsigned *indexes = XALLOCAVEC (unsigned, len); | |
278 | unsigned idx = 0, new_idx = 0; | |
279 | ||
280 | for (arg_type = TYPE_ARG_TYPES (orig_type); | |
281 | arg_type; | |
282 | arg_type = TREE_CHAIN (arg_type)) | |
283 | if (TREE_VALUE (arg_type) == void_type_node) | |
284 | continue; | |
285 | else if (BOUNDED_TYPE_P (TREE_VALUE (arg_type)) | |
286 | || pass_by_reference (NULL, TYPE_MODE (TREE_VALUE (arg_type)), | |
287 | TREE_VALUE (arg_type), true) | |
288 | || chkp_type_has_pointer (TREE_VALUE (arg_type))) | |
289 | break; | |
290 | ||
291 | /* We may use original type if there are no bounds passed. */ | |
292 | if (!arg_type) | |
293 | return orig_type; | |
294 | ||
551dc300 | 295 | type = build_distinct_type_copy (orig_type); |
d5e254e1 IE |
296 | TYPE_ARG_TYPES (type) = copy_list (TYPE_ARG_TYPES (type)); |
297 | ||
298 | for (arg_type = TYPE_ARG_TYPES (type); | |
299 | arg_type; | |
300 | arg_type = TREE_CHAIN (arg_type)) | |
301 | { | |
302 | indexes[idx++] = new_idx++; | |
303 | ||
304 | /* pass_by_reference returns 1 for void type, | |
305 | so check for it first. */ | |
306 | if (TREE_VALUE (arg_type) == void_type_node) | |
307 | continue; | |
308 | else if (BOUNDED_TYPE_P (TREE_VALUE (arg_type)) | |
309 | || pass_by_reference (NULL, TYPE_MODE (TREE_VALUE (arg_type)), | |
310 | TREE_VALUE (arg_type), true)) | |
311 | { | |
312 | tree new_type = build_tree_list (NULL_TREE, | |
313 | pointer_bounds_type_node); | |
314 | TREE_CHAIN (new_type) = TREE_CHAIN (arg_type); | |
315 | TREE_CHAIN (arg_type) = new_type; | |
316 | ||
317 | arg_type = TREE_CHAIN (arg_type); | |
318 | new_idx++; | |
319 | } | |
320 | else if (chkp_type_has_pointer (TREE_VALUE (arg_type))) | |
321 | { | |
322 | bitmap slots = BITMAP_ALLOC (NULL); | |
323 | bitmap_iterator bi; | |
324 | unsigned bnd_no; | |
325 | ||
326 | chkp_find_bound_slots (TREE_VALUE (arg_type), slots); | |
327 | ||
328 | EXECUTE_IF_SET_IN_BITMAP (slots, 0, bnd_no, bi) | |
329 | { | |
330 | tree new_type = build_tree_list (NULL_TREE, | |
331 | pointer_bounds_type_node); | |
332 | TREE_CHAIN (new_type) = TREE_CHAIN (arg_type); | |
333 | TREE_CHAIN (arg_type) = new_type; | |
334 | ||
335 | arg_type = TREE_CHAIN (arg_type); | |
336 | new_idx++; | |
337 | } | |
338 | BITMAP_FREE (slots); | |
339 | } | |
340 | } | |
341 | ||
342 | /* If function type has attribute with arg indexes then | |
343 | we have to copy it fixing attribute ops. Map for | |
344 | fixing is in indexes array. */ | |
345 | attrs = TYPE_ATTRIBUTES (type); | |
346 | if (lookup_attribute ("nonnull", attrs) | |
347 | || lookup_attribute ("format", attrs) | |
348 | || lookup_attribute ("format_arg", attrs)) | |
349 | { | |
350 | int delta = new_idx - len; | |
351 | attrs = copy_list (TYPE_ATTRIBUTES (type)); | |
352 | chkp_map_attr_arg_indexes (attrs, "nonnull", indexes, len, delta); | |
353 | chkp_map_attr_arg_indexes (attrs, "format", indexes, len, delta); | |
354 | chkp_map_attr_arg_indexes (attrs, "format_arg", indexes, len, delta); | |
355 | TYPE_ATTRIBUTES (type) = attrs; | |
356 | } | |
357 | ||
d5e254e1 IE |
358 | return type; |
359 | } | |
360 | ||
361 | /* For given function FNDECL add bounds arguments to arguments | |
362 | list. */ | |
363 | ||
364 | static void | |
365 | chkp_add_bounds_params_to_function (tree fndecl) | |
366 | { | |
367 | tree arg; | |
368 | ||
369 | for (arg = DECL_ARGUMENTS (fndecl); arg; arg = DECL_CHAIN (arg)) | |
370 | if (BOUNDED_P (arg)) | |
371 | { | |
372 | std::string new_name = CHKP_BOUNDS_OF_SYMBOL_PREFIX; | |
373 | tree new_arg; | |
374 | ||
375 | if (DECL_NAME (arg)) | |
376 | new_name += IDENTIFIER_POINTER (DECL_NAME (arg)); | |
377 | else | |
378 | { | |
379 | char uid[25]; | |
380 | snprintf (uid, 25, "D.%u", DECL_UID (arg)); | |
381 | new_name += uid; | |
382 | } | |
383 | ||
384 | new_arg = build_decl (DECL_SOURCE_LOCATION (arg), PARM_DECL, | |
385 | get_identifier (new_name.c_str ()), | |
386 | pointer_bounds_type_node); | |
387 | DECL_ARG_TYPE (new_arg) = pointer_bounds_type_node; | |
388 | DECL_CONTEXT (new_arg) = DECL_CONTEXT (arg); | |
389 | DECL_ARTIFICIAL (new_arg) = 1; | |
390 | DECL_CHAIN (new_arg) = DECL_CHAIN (arg); | |
391 | DECL_CHAIN (arg) = new_arg; | |
392 | ||
393 | arg = DECL_CHAIN (arg); | |
394 | ||
395 | } | |
396 | else if (chkp_type_has_pointer (TREE_TYPE (arg))) | |
397 | { | |
398 | tree orig_arg = arg; | |
399 | bitmap slots = BITMAP_ALLOC (NULL); | |
400 | bitmap_iterator bi; | |
401 | unsigned bnd_no; | |
402 | ||
403 | chkp_find_bound_slots (TREE_TYPE (arg), slots); | |
404 | ||
405 | EXECUTE_IF_SET_IN_BITMAP (slots, 0, bnd_no, bi) | |
406 | { | |
407 | std::string new_name = CHKP_BOUNDS_OF_SYMBOL_PREFIX; | |
408 | tree new_arg; | |
409 | char offs[25]; | |
410 | ||
411 | if (DECL_NAME (orig_arg)) | |
412 | new_name += IDENTIFIER_POINTER (DECL_NAME (orig_arg)); | |
413 | else | |
414 | { | |
415 | snprintf (offs, 25, "D.%u", DECL_UID (arg)); | |
416 | new_name += offs; | |
417 | } | |
418 | snprintf (offs, 25, "__%u", bnd_no * POINTER_SIZE / BITS_PER_UNIT); | |
419 | ||
420 | new_arg = build_decl (DECL_SOURCE_LOCATION (orig_arg), | |
421 | PARM_DECL, | |
422 | get_identifier (new_name.c_str ()), | |
423 | pointer_bounds_type_node); | |
424 | DECL_ARG_TYPE (new_arg) = pointer_bounds_type_node; | |
425 | DECL_CONTEXT (new_arg) = DECL_CONTEXT (orig_arg); | |
426 | DECL_ARTIFICIAL (new_arg) = 1; | |
427 | DECL_CHAIN (new_arg) = DECL_CHAIN (arg); | |
428 | DECL_CHAIN (arg) = new_arg; | |
429 | ||
430 | arg = DECL_CHAIN (arg); | |
431 | } | |
432 | BITMAP_FREE (slots); | |
433 | } | |
434 | ||
435 | TREE_TYPE (fndecl) = | |
436 | chkp_copy_function_type_adding_bounds (TREE_TYPE (fndecl)); | |
437 | } | |
438 | ||
edcf72f3 IE |
439 | /* Return an instrumentation clone for builtin function |
440 | FNDECL. Create one if needed. */ | |
441 | ||
442 | tree | |
443 | chkp_maybe_clone_builtin_fndecl (tree fndecl) | |
444 | { | |
445 | tree clone; | |
446 | enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); | |
447 | ||
448 | gcc_assert (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL | |
449 | && fcode < BEGIN_CHKP_BUILTINS); | |
450 | ||
451 | fcode = (enum built_in_function) (fcode + BEGIN_CHKP_BUILTINS + 1); | |
452 | clone = builtin_decl_explicit (fcode); | |
453 | if (clone) | |
454 | return clone; | |
455 | ||
456 | clone = chkp_build_instrumented_fndecl (fndecl); | |
457 | chkp_add_bounds_params_to_function (clone); | |
458 | ||
459 | gcc_assert (DECL_FUNCTION_CODE (clone) == fcode); | |
460 | ||
461 | set_builtin_decl (fcode, clone, false); | |
462 | ||
463 | return clone; | |
464 | } | |
465 | ||
06201ad5 IE |
466 | /* Return 1 if function FNDECL should be instrumented. */ |
467 | ||
468 | bool | |
469 | chkp_instrumentable_p (tree fndecl) | |
470 | { | |
471 | struct function *fn = DECL_STRUCT_FUNCTION (fndecl); | |
472 | return (!lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (fndecl)) | |
473 | && (!flag_chkp_instrument_marked_only | |
474 | || lookup_attribute ("bnd_instrument", DECL_ATTRIBUTES (fndecl))) | |
475 | && (!fn || !copy_forbidden (fn, fndecl))); | |
476 | } | |
477 | ||
d5e254e1 IE |
478 | /* Return clone created for instrumentation of NODE or NULL. */ |
479 | ||
480 | cgraph_node * | |
481 | chkp_maybe_create_clone (tree fndecl) | |
482 | { | |
483 | cgraph_node *node = cgraph_node::get_create (fndecl); | |
484 | cgraph_node *clone = node->instrumented_version; | |
485 | ||
486 | gcc_assert (!node->instrumentation_clone); | |
487 | ||
edcf72f3 IE |
488 | if (DECL_BUILT_IN (fndecl) |
489 | && (DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL | |
490 | || DECL_FUNCTION_CODE (fndecl) >= BEGIN_CHKP_BUILTINS)) | |
491 | return NULL; | |
492 | ||
493 | clone = node->instrumented_version; | |
494 | ||
495 | /* Some instrumented builtin function calls may be optimized and | |
496 | cgraph nodes may be removed as unreachable. Later optimizations | |
497 | may generate new calls to removed functions and in this case | |
498 | we have to recreate cgraph node. FUNCTION_DECL for instrumented | |
499 | builtin still exists and should be reused in such case. */ | |
500 | if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL | |
501 | && fndecl == builtin_decl_explicit (DECL_FUNCTION_CODE (fndecl)) | |
502 | && !clone) | |
503 | { | |
504 | enum built_in_function fncode = DECL_FUNCTION_CODE (fndecl); | |
505 | tree new_decl; | |
506 | ||
507 | fncode = (enum built_in_function) (fncode + BEGIN_CHKP_BUILTINS + 1); | |
508 | new_decl = builtin_decl_explicit (fncode); | |
509 | ||
510 | /* We've actually already created an instrumented clone once. | |
511 | Restore it. */ | |
512 | if (new_decl) | |
513 | { | |
514 | clone = cgraph_node::get (new_decl); | |
515 | ||
516 | if (!clone) | |
517 | { | |
518 | gcc_assert (!gimple_has_body_p (fndecl)); | |
519 | clone = cgraph_node::get_create (new_decl); | |
520 | clone->externally_visible = node->externally_visible; | |
521 | clone->local = node->local; | |
522 | clone->address_taken = node->address_taken; | |
523 | clone->thunk = node->thunk; | |
524 | clone->alias = node->alias; | |
525 | clone->weakref = node->weakref; | |
526 | clone->cpp_implicit_alias = node->cpp_implicit_alias; | |
527 | clone->orig_decl = fndecl; | |
528 | clone->instrumentation_clone = true; | |
529 | } | |
530 | ||
531 | clone->instrumented_version = node; | |
532 | node->instrumented_version = clone; | |
533 | } | |
534 | } | |
535 | ||
d5e254e1 IE |
536 | if (!clone) |
537 | { | |
538 | tree new_decl = chkp_build_instrumented_fndecl (fndecl); | |
539 | struct cgraph_edge *e; | |
540 | struct ipa_ref *ref; | |
541 | int i; | |
542 | ||
543 | clone = node->create_version_clone (new_decl, vNULL, NULL); | |
544 | clone->externally_visible = node->externally_visible; | |
545 | clone->local = node->local; | |
546 | clone->address_taken = node->address_taken; | |
547 | clone->thunk = node->thunk; | |
548 | clone->alias = node->alias; | |
549 | clone->weakref = node->weakref; | |
550 | clone->cpp_implicit_alias = node->cpp_implicit_alias; | |
551 | clone->instrumented_version = node; | |
552 | clone->orig_decl = fndecl; | |
553 | clone->instrumentation_clone = true; | |
554 | node->instrumented_version = clone; | |
555 | ||
556 | if (gimple_has_body_p (fndecl)) | |
557 | { | |
847ffe17 IE |
558 | gcc_assert (chkp_instrumentable_p (fndecl)); |
559 | tree_function_versioning (fndecl, new_decl, NULL, false, | |
560 | NULL, false, NULL, NULL); | |
561 | clone->lowered = true; | |
d5e254e1 IE |
562 | } |
563 | ||
564 | /* New params are inserted after versioning because it | |
565 | actually copies args list from the original decl. */ | |
566 | chkp_add_bounds_params_to_function (new_decl); | |
567 | ||
edcf72f3 IE |
568 | /* Remember builtin fndecl. */ |
569 | if (DECL_BUILT_IN_CLASS (clone->decl) == BUILT_IN_NORMAL | |
570 | && fndecl == builtin_decl_explicit (DECL_FUNCTION_CODE (fndecl))) | |
571 | { | |
572 | gcc_assert (!builtin_decl_explicit (DECL_FUNCTION_CODE (clone->decl))); | |
573 | set_builtin_decl (DECL_FUNCTION_CODE (clone->decl), | |
574 | clone->decl, false); | |
575 | } | |
576 | ||
d5e254e1 IE |
577 | /* Clones have the same comdat group as originals. */ |
578 | if (node->same_comdat_group | |
b7e85ee1 IE |
579 | || (DECL_ONE_ONLY (node->decl) |
580 | && !DECL_EXTERNAL (node->decl))) | |
d5e254e1 IE |
581 | clone->add_to_same_comdat_group (node); |
582 | ||
583 | if (gimple_has_body_p (fndecl)) | |
584 | symtab->call_cgraph_insertion_hooks (clone); | |
585 | ||
586 | /* Clone all aliases. */ | |
31de7606 | 587 | for (i = 0; node->iterate_direct_aliases (i, ref); i++) |
67321dcf | 588 | chkp_maybe_create_clone (ref->referring->decl); |
d5e254e1 IE |
589 | |
590 | /* Clone all thunks. */ | |
591 | for (e = node->callers; e; e = e->next_caller) | |
06201ad5 | 592 | if (e->caller->thunk.thunk_p |
5cdd35db IE |
593 | && !e->caller->thunk.add_pointer_bounds_args |
594 | && !e->caller->instrumentation_clone) | |
d5e254e1 IE |
595 | { |
596 | struct cgraph_node *thunk | |
597 | = chkp_maybe_create_clone (e->caller->decl); | |
598 | /* Redirect thunk clone edge to the node clone. */ | |
599 | thunk->callees->redirect_callee (clone); | |
600 | } | |
601 | ||
602 | /* For aliases and thunks we should make sure target is cloned | |
603 | to have proper references and edges. */ | |
604 | if (node->thunk.thunk_p) | |
605 | chkp_maybe_create_clone (node->callees->callee->decl); | |
606 | else if (node->alias) | |
607 | { | |
608 | struct cgraph_node *target; | |
609 | ||
610 | ref = node->ref_list.first_reference (); | |
611 | if (ref) | |
67321dcf IE |
612 | { |
613 | target = chkp_maybe_create_clone (ref->referred->decl); | |
614 | clone->create_reference (target, IPA_REF_ALIAS); | |
615 | } | |
d5e254e1 IE |
616 | |
617 | if (node->alias_target) | |
618 | { | |
619 | if (TREE_CODE (node->alias_target) == FUNCTION_DECL) | |
620 | { | |
621 | target = chkp_maybe_create_clone (node->alias_target); | |
622 | clone->alias_target = target->decl; | |
623 | } | |
624 | else | |
625 | clone->alias_target = node->alias_target; | |
626 | } | |
627 | } | |
628 | ||
629 | /* Add IPA reference. It's main role is to keep instrumented | |
630 | version reachable while original node is reachable. */ | |
631 | ref = node->create_reference (clone, IPA_REF_CHKP, NULL); | |
632 | } | |
633 | ||
634 | return clone; | |
635 | } | |
636 | ||
637 | /* Create clone for all functions to be instrumented. */ | |
638 | ||
639 | static unsigned int | |
640 | chkp_versioning (void) | |
641 | { | |
642 | struct cgraph_node *node; | |
06201ad5 | 643 | const char *reason; |
d5e254e1 IE |
644 | |
645 | bitmap_obstack_initialize (NULL); | |
646 | ||
647 | FOR_EACH_DEFINED_FUNCTION (node) | |
648 | { | |
649 | if (!node->instrumentation_clone | |
650 | && !node->instrumented_version | |
651 | && !node->alias | |
652 | && !node->thunk.thunk_p | |
edcf72f3 IE |
653 | && (!DECL_BUILT_IN (node->decl) |
654 | || (DECL_BUILT_IN_CLASS (node->decl) == BUILT_IN_NORMAL | |
655 | && DECL_FUNCTION_CODE (node->decl) < BEGIN_CHKP_BUILTINS))) | |
06201ad5 IE |
656 | { |
657 | if (chkp_instrumentable_p (node->decl)) | |
658 | chkp_maybe_create_clone (node->decl); | |
659 | else if ((reason = copy_forbidden (DECL_STRUCT_FUNCTION (node->decl), | |
660 | node->decl))) | |
661 | { | |
662 | if (warning_at (DECL_SOURCE_LOCATION (node->decl), OPT_Wchkp, | |
663 | "function cannot be instrumented")) | |
664 | inform (DECL_SOURCE_LOCATION (node->decl), reason, node->decl); | |
665 | } | |
666 | } | |
d5e254e1 IE |
667 | } |
668 | ||
669 | /* Mark all aliases and thunks of functions with no instrumented | |
670 | version as legacy function. */ | |
671 | FOR_EACH_DEFINED_FUNCTION (node) | |
672 | { | |
673 | if (!node->instrumentation_clone | |
674 | && !node->instrumented_version | |
675 | && (node->alias || node->thunk.thunk_p) | |
676 | && !lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (node->decl))) | |
677 | DECL_ATTRIBUTES (node->decl) | |
678 | = tree_cons (get_identifier ("bnd_legacy"), NULL, | |
679 | DECL_ATTRIBUTES (node->decl)); | |
680 | } | |
681 | ||
682 | bitmap_obstack_release (NULL); | |
683 | ||
684 | return 0; | |
685 | } | |
686 | ||
687 | /* In this pass we remove bodies of functions having | |
688 | instrumented version. Functions with removed bodies | |
689 | become a special kind of thunks to provide a connection | |
690 | between calls to the original version and instrumented | |
691 | function. */ | |
692 | ||
693 | static unsigned int | |
c11d86b4 | 694 | chkp_produce_thunks (bool early) |
d5e254e1 IE |
695 | { |
696 | struct cgraph_node *node; | |
697 | ||
698 | FOR_EACH_DEFINED_FUNCTION (node) | |
699 | { | |
700 | if (!node->instrumentation_clone | |
701 | && node->instrumented_version | |
702 | && gimple_has_body_p (node->decl) | |
c11d86b4 IE |
703 | && gimple_has_body_p (node->instrumented_version->decl) |
704 | && (!lookup_attribute ("always_inline", DECL_ATTRIBUTES (node->decl)) | |
705 | || !early)) | |
d5e254e1 IE |
706 | { |
707 | node->release_body (); | |
708 | node->remove_callees (); | |
709 | node->remove_all_references (); | |
710 | ||
711 | node->thunk.thunk_p = true; | |
712 | node->thunk.add_pointer_bounds_args = true; | |
713 | node->create_edge (node->instrumented_version, NULL, | |
714 | 0, CGRAPH_FREQ_BASE); | |
715 | node->create_reference (node->instrumented_version, | |
716 | IPA_REF_CHKP, NULL); | |
71671f5d IE |
717 | /* Thunk shouldn't be a cdtor. */ |
718 | DECL_STATIC_CONSTRUCTOR (node->decl) = 0; | |
719 | DECL_STATIC_DESTRUCTOR (node->decl) = 0; | |
d5e254e1 IE |
720 | } |
721 | } | |
722 | ||
723 | /* Mark instrumentation clones created for aliases and thunks | |
724 | as insttrumented so they could be removed as unreachable | |
725 | now. */ | |
c11d86b4 | 726 | if (!early) |
d5e254e1 | 727 | { |
c11d86b4 IE |
728 | FOR_EACH_DEFINED_FUNCTION (node) |
729 | { | |
730 | if (node->instrumentation_clone | |
731 | && (node->alias || node->thunk.thunk_p) | |
732 | && !chkp_function_instrumented_p (node->decl)) | |
733 | chkp_function_mark_instrumented (node->decl); | |
734 | } | |
d5e254e1 IE |
735 | } |
736 | ||
17e0fc92 | 737 | return TODO_remove_functions; |
d5e254e1 IE |
738 | } |
739 | ||
740 | const pass_data pass_data_ipa_chkp_versioning = | |
741 | { | |
742 | SIMPLE_IPA_PASS, /* type */ | |
743 | "chkp_versioning", /* name */ | |
744 | OPTGROUP_NONE, /* optinfo_flags */ | |
745 | TV_NONE, /* tv_id */ | |
746 | 0, /* properties_required */ | |
747 | 0, /* properties_provided */ | |
748 | 0, /* properties_destroyed */ | |
749 | 0, /* todo_flags_start */ | |
750 | 0 /* todo_flags_finish */ | |
751 | }; | |
752 | ||
c11d86b4 IE |
753 | const pass_data pass_data_ipa_chkp_early_produce_thunks = |
754 | { | |
755 | SIMPLE_IPA_PASS, /* type */ | |
756 | "chkp_ecleanup", /* name */ | |
757 | OPTGROUP_NONE, /* optinfo_flags */ | |
758 | TV_NONE, /* tv_id */ | |
759 | 0, /* properties_required */ | |
760 | 0, /* properties_provided */ | |
761 | 0, /* properties_destroyed */ | |
762 | 0, /* todo_flags_start */ | |
763 | 0 /* todo_flags_finish */ | |
764 | }; | |
765 | ||
d5e254e1 IE |
766 | const pass_data pass_data_ipa_chkp_produce_thunks = |
767 | { | |
768 | SIMPLE_IPA_PASS, /* type */ | |
769 | "chkp_cleanup", /* name */ | |
770 | OPTGROUP_NONE, /* optinfo_flags */ | |
771 | TV_NONE, /* tv_id */ | |
772 | 0, /* properties_required */ | |
773 | 0, /* properties_provided */ | |
774 | 0, /* properties_destroyed */ | |
775 | 0, /* todo_flags_start */ | |
776 | 0 /* todo_flags_finish */ | |
777 | }; | |
778 | ||
779 | class pass_ipa_chkp_versioning : public simple_ipa_opt_pass | |
780 | { | |
781 | public: | |
782 | pass_ipa_chkp_versioning (gcc::context *ctxt) | |
783 | : simple_ipa_opt_pass (pass_data_ipa_chkp_versioning, ctxt) | |
784 | {} | |
785 | ||
786 | /* opt_pass methods: */ | |
787 | virtual opt_pass * clone () | |
788 | { | |
789 | return new pass_ipa_chkp_versioning (m_ctxt); | |
790 | } | |
791 | ||
792 | virtual bool gate (function *) | |
793 | { | |
794 | return flag_check_pointer_bounds; | |
795 | } | |
796 | ||
797 | virtual unsigned int execute (function *) | |
798 | { | |
799 | return chkp_versioning (); | |
800 | } | |
801 | ||
802 | }; // class pass_ipa_chkp_versioning | |
803 | ||
c11d86b4 IE |
804 | class pass_ipa_chkp_early_produce_thunks : public simple_ipa_opt_pass |
805 | { | |
806 | public: | |
807 | pass_ipa_chkp_early_produce_thunks (gcc::context *ctxt) | |
808 | : simple_ipa_opt_pass (pass_data_ipa_chkp_early_produce_thunks, ctxt) | |
809 | {} | |
810 | ||
811 | /* opt_pass methods: */ | |
812 | virtual opt_pass * clone () | |
813 | { | |
814 | return new pass_ipa_chkp_early_produce_thunks (m_ctxt); | |
815 | } | |
816 | ||
817 | virtual bool gate (function *) | |
818 | { | |
819 | return flag_check_pointer_bounds; | |
820 | } | |
821 | ||
822 | virtual unsigned int execute (function *) | |
823 | { | |
824 | return chkp_produce_thunks (true); | |
825 | } | |
826 | ||
827 | }; // class pass_chkp_produce_thunks | |
828 | ||
d5e254e1 IE |
829 | class pass_ipa_chkp_produce_thunks : public simple_ipa_opt_pass |
830 | { | |
831 | public: | |
832 | pass_ipa_chkp_produce_thunks (gcc::context *ctxt) | |
833 | : simple_ipa_opt_pass (pass_data_ipa_chkp_produce_thunks, ctxt) | |
834 | {} | |
835 | ||
836 | /* opt_pass methods: */ | |
837 | virtual opt_pass * clone () | |
838 | { | |
839 | return new pass_ipa_chkp_produce_thunks (m_ctxt); | |
840 | } | |
841 | ||
842 | virtual bool gate (function *) | |
843 | { | |
844 | return flag_check_pointer_bounds; | |
845 | } | |
846 | ||
847 | virtual unsigned int execute (function *) | |
848 | { | |
c11d86b4 | 849 | return chkp_produce_thunks (false); |
d5e254e1 IE |
850 | } |
851 | ||
852 | }; // class pass_chkp_produce_thunks | |
853 | ||
854 | simple_ipa_opt_pass * | |
855 | make_pass_ipa_chkp_versioning (gcc::context *ctxt) | |
856 | { | |
857 | return new pass_ipa_chkp_versioning (ctxt); | |
858 | } | |
859 | ||
c11d86b4 IE |
860 | simple_ipa_opt_pass * |
861 | make_pass_ipa_chkp_early_produce_thunks (gcc::context *ctxt) | |
862 | { | |
863 | return new pass_ipa_chkp_early_produce_thunks (ctxt); | |
864 | } | |
865 | ||
d5e254e1 IE |
866 | simple_ipa_opt_pass * |
867 | make_pass_ipa_chkp_produce_thunks (gcc::context *ctxt) | |
868 | { | |
869 | return new pass_ipa_chkp_produce_thunks (ctxt); | |
870 | } |