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