]>
Commit | Line | Data |
---|---|---|
d5e254e1 | 1 | /* Pointer Bounds Checker IPA passes. |
818ab71a | 2 | Copyright (C) 2014-2016 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)) | |
210 | DECL_ARGUMENTS (new_decl) = copy_list (DECL_ARGUMENTS (fndecl)); | |
211 | ||
212 | /* We are going to modify attributes list and therefore should | |
213 | make own copy. */ | |
214 | DECL_ATTRIBUTES (new_decl) = copy_list (DECL_ATTRIBUTES (fndecl)); | |
215 | ||
edcf72f3 IE |
216 | /* Change builtin function code. */ |
217 | if (DECL_BUILT_IN (new_decl)) | |
218 | { | |
219 | gcc_assert (DECL_BUILT_IN_CLASS (new_decl) == BUILT_IN_NORMAL); | |
220 | gcc_assert (DECL_FUNCTION_CODE (new_decl) < BEGIN_CHKP_BUILTINS); | |
221 | DECL_FUNCTION_CODE (new_decl) | |
222 | = (enum built_in_function)(DECL_FUNCTION_CODE (new_decl) | |
223 | + BEGIN_CHKP_BUILTINS + 1); | |
224 | } | |
225 | ||
d5e254e1 IE |
226 | return new_decl; |
227 | } | |
228 | ||
229 | ||
230 | /* Fix operands of attribute from ATTRS list named ATTR_NAME. | |
231 | Integer operands are replaced with values according to | |
232 | INDEXES map having LEN elements. For operands out of len | |
233 | we just add DELTA. */ | |
234 | ||
235 | static void | |
236 | chkp_map_attr_arg_indexes (tree attrs, const char *attr_name, | |
237 | unsigned *indexes, int len, int delta) | |
238 | { | |
239 | tree attr = lookup_attribute (attr_name, attrs); | |
240 | tree op; | |
241 | ||
242 | if (!attr) | |
243 | return; | |
244 | ||
245 | TREE_VALUE (attr) = copy_list (TREE_VALUE (attr)); | |
246 | for (op = TREE_VALUE (attr); op; op = TREE_CHAIN (op)) | |
247 | { | |
248 | int idx; | |
249 | ||
250 | if (TREE_CODE (TREE_VALUE (op)) != INTEGER_CST) | |
251 | continue; | |
252 | ||
253 | idx = TREE_INT_CST_LOW (TREE_VALUE (op)); | |
254 | ||
255 | /* If idx exceeds indexes length then we just | |
256 | keep it at the same distance from the last | |
257 | known arg. */ | |
258 | if (idx > len) | |
259 | idx += delta; | |
260 | else | |
261 | idx = indexes[idx - 1] + 1; | |
262 | TREE_VALUE (op) = build_int_cst (TREE_TYPE (TREE_VALUE (op)), idx); | |
263 | } | |
264 | } | |
265 | ||
266 | /* Make a copy of function type ORIG_TYPE adding pointer | |
267 | bounds as additional arguments. */ | |
268 | ||
269 | tree | |
270 | chkp_copy_function_type_adding_bounds (tree orig_type) | |
271 | { | |
272 | tree type; | |
b0845a1e | 273 | tree arg_type, attrs; |
d5e254e1 IE |
274 | unsigned len = list_length (TYPE_ARG_TYPES (orig_type)); |
275 | unsigned *indexes = XALLOCAVEC (unsigned, len); | |
276 | unsigned idx = 0, new_idx = 0; | |
277 | ||
278 | for (arg_type = TYPE_ARG_TYPES (orig_type); | |
279 | arg_type; | |
280 | arg_type = TREE_CHAIN (arg_type)) | |
281 | if (TREE_VALUE (arg_type) == void_type_node) | |
282 | continue; | |
283 | else if (BOUNDED_TYPE_P (TREE_VALUE (arg_type)) | |
284 | || pass_by_reference (NULL, TYPE_MODE (TREE_VALUE (arg_type)), | |
285 | TREE_VALUE (arg_type), true) | |
286 | || chkp_type_has_pointer (TREE_VALUE (arg_type))) | |
287 | break; | |
288 | ||
289 | /* We may use original type if there are no bounds passed. */ | |
290 | if (!arg_type) | |
291 | return orig_type; | |
292 | ||
551dc300 | 293 | type = build_distinct_type_copy (orig_type); |
d5e254e1 IE |
294 | TYPE_ARG_TYPES (type) = copy_list (TYPE_ARG_TYPES (type)); |
295 | ||
296 | for (arg_type = TYPE_ARG_TYPES (type); | |
297 | arg_type; | |
298 | arg_type = TREE_CHAIN (arg_type)) | |
299 | { | |
300 | indexes[idx++] = new_idx++; | |
301 | ||
302 | /* pass_by_reference returns 1 for void type, | |
303 | so check for it first. */ | |
304 | if (TREE_VALUE (arg_type) == void_type_node) | |
305 | continue; | |
306 | else if (BOUNDED_TYPE_P (TREE_VALUE (arg_type)) | |
307 | || pass_by_reference (NULL, TYPE_MODE (TREE_VALUE (arg_type)), | |
308 | TREE_VALUE (arg_type), true)) | |
309 | { | |
310 | tree new_type = build_tree_list (NULL_TREE, | |
311 | pointer_bounds_type_node); | |
312 | TREE_CHAIN (new_type) = TREE_CHAIN (arg_type); | |
313 | TREE_CHAIN (arg_type) = new_type; | |
314 | ||
315 | arg_type = TREE_CHAIN (arg_type); | |
316 | new_idx++; | |
317 | } | |
318 | else if (chkp_type_has_pointer (TREE_VALUE (arg_type))) | |
319 | { | |
320 | bitmap slots = BITMAP_ALLOC (NULL); | |
321 | bitmap_iterator bi; | |
322 | unsigned bnd_no; | |
323 | ||
324 | chkp_find_bound_slots (TREE_VALUE (arg_type), slots); | |
325 | ||
326 | EXECUTE_IF_SET_IN_BITMAP (slots, 0, bnd_no, bi) | |
327 | { | |
328 | tree new_type = build_tree_list (NULL_TREE, | |
329 | pointer_bounds_type_node); | |
330 | TREE_CHAIN (new_type) = TREE_CHAIN (arg_type); | |
331 | TREE_CHAIN (arg_type) = new_type; | |
332 | ||
333 | arg_type = TREE_CHAIN (arg_type); | |
334 | new_idx++; | |
335 | } | |
336 | BITMAP_FREE (slots); | |
337 | } | |
338 | } | |
339 | ||
340 | /* If function type has attribute with arg indexes then | |
341 | we have to copy it fixing attribute ops. Map for | |
342 | fixing is in indexes array. */ | |
343 | attrs = TYPE_ATTRIBUTES (type); | |
344 | if (lookup_attribute ("nonnull", attrs) | |
345 | || lookup_attribute ("format", attrs) | |
346 | || lookup_attribute ("format_arg", attrs)) | |
347 | { | |
348 | int delta = new_idx - len; | |
349 | attrs = copy_list (TYPE_ATTRIBUTES (type)); | |
350 | chkp_map_attr_arg_indexes (attrs, "nonnull", indexes, len, delta); | |
351 | chkp_map_attr_arg_indexes (attrs, "format", indexes, len, delta); | |
352 | chkp_map_attr_arg_indexes (attrs, "format_arg", indexes, len, delta); | |
353 | TYPE_ATTRIBUTES (type) = attrs; | |
354 | } | |
355 | ||
d5e254e1 IE |
356 | return type; |
357 | } | |
358 | ||
359 | /* For given function FNDECL add bounds arguments to arguments | |
360 | list. */ | |
361 | ||
362 | static void | |
363 | chkp_add_bounds_params_to_function (tree fndecl) | |
364 | { | |
365 | tree arg; | |
366 | ||
367 | for (arg = DECL_ARGUMENTS (fndecl); arg; arg = DECL_CHAIN (arg)) | |
368 | if (BOUNDED_P (arg)) | |
369 | { | |
370 | std::string new_name = CHKP_BOUNDS_OF_SYMBOL_PREFIX; | |
371 | tree new_arg; | |
372 | ||
373 | if (DECL_NAME (arg)) | |
374 | new_name += IDENTIFIER_POINTER (DECL_NAME (arg)); | |
375 | else | |
376 | { | |
377 | char uid[25]; | |
378 | snprintf (uid, 25, "D.%u", DECL_UID (arg)); | |
379 | new_name += uid; | |
380 | } | |
381 | ||
382 | new_arg = build_decl (DECL_SOURCE_LOCATION (arg), PARM_DECL, | |
383 | get_identifier (new_name.c_str ()), | |
384 | pointer_bounds_type_node); | |
385 | DECL_ARG_TYPE (new_arg) = pointer_bounds_type_node; | |
386 | DECL_CONTEXT (new_arg) = DECL_CONTEXT (arg); | |
387 | DECL_ARTIFICIAL (new_arg) = 1; | |
388 | DECL_CHAIN (new_arg) = DECL_CHAIN (arg); | |
389 | DECL_CHAIN (arg) = new_arg; | |
390 | ||
391 | arg = DECL_CHAIN (arg); | |
392 | ||
393 | } | |
394 | else if (chkp_type_has_pointer (TREE_TYPE (arg))) | |
395 | { | |
396 | tree orig_arg = arg; | |
397 | bitmap slots = BITMAP_ALLOC (NULL); | |
398 | bitmap_iterator bi; | |
399 | unsigned bnd_no; | |
400 | ||
401 | chkp_find_bound_slots (TREE_TYPE (arg), slots); | |
402 | ||
403 | EXECUTE_IF_SET_IN_BITMAP (slots, 0, bnd_no, bi) | |
404 | { | |
405 | std::string new_name = CHKP_BOUNDS_OF_SYMBOL_PREFIX; | |
406 | tree new_arg; | |
407 | char offs[25]; | |
408 | ||
409 | if (DECL_NAME (orig_arg)) | |
410 | new_name += IDENTIFIER_POINTER (DECL_NAME (orig_arg)); | |
411 | else | |
412 | { | |
413 | snprintf (offs, 25, "D.%u", DECL_UID (arg)); | |
414 | new_name += offs; | |
415 | } | |
416 | snprintf (offs, 25, "__%u", bnd_no * POINTER_SIZE / BITS_PER_UNIT); | |
417 | ||
418 | new_arg = build_decl (DECL_SOURCE_LOCATION (orig_arg), | |
419 | PARM_DECL, | |
420 | get_identifier (new_name.c_str ()), | |
421 | pointer_bounds_type_node); | |
422 | DECL_ARG_TYPE (new_arg) = pointer_bounds_type_node; | |
423 | DECL_CONTEXT (new_arg) = DECL_CONTEXT (orig_arg); | |
424 | DECL_ARTIFICIAL (new_arg) = 1; | |
425 | DECL_CHAIN (new_arg) = DECL_CHAIN (arg); | |
426 | DECL_CHAIN (arg) = new_arg; | |
427 | ||
428 | arg = DECL_CHAIN (arg); | |
429 | } | |
430 | BITMAP_FREE (slots); | |
431 | } | |
432 | ||
433 | TREE_TYPE (fndecl) = | |
434 | chkp_copy_function_type_adding_bounds (TREE_TYPE (fndecl)); | |
435 | } | |
436 | ||
edcf72f3 IE |
437 | /* Return an instrumentation clone for builtin function |
438 | FNDECL. Create one if needed. */ | |
439 | ||
440 | tree | |
441 | chkp_maybe_clone_builtin_fndecl (tree fndecl) | |
442 | { | |
443 | tree clone; | |
444 | enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); | |
445 | ||
446 | gcc_assert (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL | |
447 | && fcode < BEGIN_CHKP_BUILTINS); | |
448 | ||
449 | fcode = (enum built_in_function) (fcode + BEGIN_CHKP_BUILTINS + 1); | |
450 | clone = builtin_decl_explicit (fcode); | |
451 | if (clone) | |
452 | return clone; | |
453 | ||
454 | clone = chkp_build_instrumented_fndecl (fndecl); | |
455 | chkp_add_bounds_params_to_function (clone); | |
456 | ||
457 | gcc_assert (DECL_FUNCTION_CODE (clone) == fcode); | |
458 | ||
459 | set_builtin_decl (fcode, clone, false); | |
460 | ||
461 | return clone; | |
462 | } | |
463 | ||
06201ad5 IE |
464 | /* Return 1 if function FNDECL should be instrumented. */ |
465 | ||
466 | bool | |
467 | chkp_instrumentable_p (tree fndecl) | |
468 | { | |
469 | struct function *fn = DECL_STRUCT_FUNCTION (fndecl); | |
470 | return (!lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (fndecl)) | |
471 | && (!flag_chkp_instrument_marked_only | |
472 | || lookup_attribute ("bnd_instrument", DECL_ATTRIBUTES (fndecl))) | |
aa43616c | 473 | && (!fn || !copy_forbidden (fn))); |
06201ad5 IE |
474 | } |
475 | ||
d5e254e1 IE |
476 | /* Return clone created for instrumentation of NODE or NULL. */ |
477 | ||
478 | cgraph_node * | |
479 | chkp_maybe_create_clone (tree fndecl) | |
480 | { | |
481 | cgraph_node *node = cgraph_node::get_create (fndecl); | |
482 | cgraph_node *clone = node->instrumented_version; | |
483 | ||
484 | gcc_assert (!node->instrumentation_clone); | |
485 | ||
edcf72f3 IE |
486 | if (DECL_BUILT_IN (fndecl) |
487 | && (DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL | |
488 | || DECL_FUNCTION_CODE (fndecl) >= BEGIN_CHKP_BUILTINS)) | |
489 | return NULL; | |
490 | ||
491 | clone = node->instrumented_version; | |
492 | ||
493 | /* Some instrumented builtin function calls may be optimized and | |
494 | cgraph nodes may be removed as unreachable. Later optimizations | |
495 | may generate new calls to removed functions and in this case | |
496 | we have to recreate cgraph node. FUNCTION_DECL for instrumented | |
497 | builtin still exists and should be reused in such case. */ | |
498 | if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL | |
499 | && fndecl == builtin_decl_explicit (DECL_FUNCTION_CODE (fndecl)) | |
500 | && !clone) | |
501 | { | |
502 | enum built_in_function fncode = DECL_FUNCTION_CODE (fndecl); | |
503 | tree new_decl; | |
504 | ||
505 | fncode = (enum built_in_function) (fncode + BEGIN_CHKP_BUILTINS + 1); | |
506 | new_decl = builtin_decl_explicit (fncode); | |
507 | ||
508 | /* We've actually already created an instrumented clone once. | |
509 | Restore it. */ | |
510 | if (new_decl) | |
511 | { | |
512 | clone = cgraph_node::get (new_decl); | |
513 | ||
514 | if (!clone) | |
515 | { | |
516 | gcc_assert (!gimple_has_body_p (fndecl)); | |
517 | clone = cgraph_node::get_create (new_decl); | |
518 | clone->externally_visible = node->externally_visible; | |
519 | clone->local = node->local; | |
520 | clone->address_taken = node->address_taken; | |
521 | clone->thunk = node->thunk; | |
522 | clone->alias = node->alias; | |
523 | clone->weakref = node->weakref; | |
524 | clone->cpp_implicit_alias = node->cpp_implicit_alias; | |
525 | clone->orig_decl = fndecl; | |
526 | clone->instrumentation_clone = true; | |
527 | } | |
528 | ||
529 | clone->instrumented_version = node; | |
530 | node->instrumented_version = clone; | |
531 | } | |
532 | } | |
533 | ||
d5e254e1 IE |
534 | if (!clone) |
535 | { | |
536 | tree new_decl = chkp_build_instrumented_fndecl (fndecl); | |
537 | struct cgraph_edge *e; | |
538 | struct ipa_ref *ref; | |
539 | int i; | |
540 | ||
541 | clone = node->create_version_clone (new_decl, vNULL, NULL); | |
542 | clone->externally_visible = node->externally_visible; | |
543 | clone->local = node->local; | |
544 | clone->address_taken = node->address_taken; | |
545 | clone->thunk = node->thunk; | |
546 | clone->alias = node->alias; | |
547 | clone->weakref = node->weakref; | |
548 | clone->cpp_implicit_alias = node->cpp_implicit_alias; | |
549 | clone->instrumented_version = node; | |
550 | clone->orig_decl = fndecl; | |
551 | clone->instrumentation_clone = true; | |
552 | node->instrumented_version = clone; | |
553 | ||
554 | if (gimple_has_body_p (fndecl)) | |
555 | { | |
847ffe17 IE |
556 | gcc_assert (chkp_instrumentable_p (fndecl)); |
557 | tree_function_versioning (fndecl, new_decl, NULL, false, | |
558 | NULL, false, NULL, NULL); | |
559 | clone->lowered = true; | |
d5e254e1 IE |
560 | } |
561 | ||
562 | /* New params are inserted after versioning because it | |
563 | actually copies args list from the original decl. */ | |
564 | chkp_add_bounds_params_to_function (new_decl); | |
565 | ||
edcf72f3 IE |
566 | /* Remember builtin fndecl. */ |
567 | if (DECL_BUILT_IN_CLASS (clone->decl) == BUILT_IN_NORMAL | |
568 | && fndecl == builtin_decl_explicit (DECL_FUNCTION_CODE (fndecl))) | |
569 | { | |
570 | gcc_assert (!builtin_decl_explicit (DECL_FUNCTION_CODE (clone->decl))); | |
571 | set_builtin_decl (DECL_FUNCTION_CODE (clone->decl), | |
572 | clone->decl, false); | |
573 | } | |
574 | ||
d5e254e1 IE |
575 | /* Clones have the same comdat group as originals. */ |
576 | if (node->same_comdat_group | |
b7e85ee1 IE |
577 | || (DECL_ONE_ONLY (node->decl) |
578 | && !DECL_EXTERNAL (node->decl))) | |
d5e254e1 IE |
579 | clone->add_to_same_comdat_group (node); |
580 | ||
581 | if (gimple_has_body_p (fndecl)) | |
582 | symtab->call_cgraph_insertion_hooks (clone); | |
583 | ||
584 | /* Clone all aliases. */ | |
31de7606 | 585 | for (i = 0; node->iterate_direct_aliases (i, ref); i++) |
67321dcf | 586 | chkp_maybe_create_clone (ref->referring->decl); |
d5e254e1 IE |
587 | |
588 | /* Clone all thunks. */ | |
589 | for (e = node->callers; e; e = e->next_caller) | |
06201ad5 | 590 | if (e->caller->thunk.thunk_p |
5cdd35db IE |
591 | && !e->caller->thunk.add_pointer_bounds_args |
592 | && !e->caller->instrumentation_clone) | |
d5e254e1 IE |
593 | { |
594 | struct cgraph_node *thunk | |
595 | = chkp_maybe_create_clone (e->caller->decl); | |
596 | /* Redirect thunk clone edge to the node clone. */ | |
597 | thunk->callees->redirect_callee (clone); | |
598 | } | |
599 | ||
600 | /* For aliases and thunks we should make sure target is cloned | |
601 | to have proper references and edges. */ | |
602 | if (node->thunk.thunk_p) | |
603 | chkp_maybe_create_clone (node->callees->callee->decl); | |
604 | else if (node->alias) | |
605 | { | |
606 | struct cgraph_node *target; | |
607 | ||
608 | ref = node->ref_list.first_reference (); | |
609 | if (ref) | |
67321dcf IE |
610 | { |
611 | target = chkp_maybe_create_clone (ref->referred->decl); | |
612 | clone->create_reference (target, IPA_REF_ALIAS); | |
613 | } | |
d5e254e1 IE |
614 | |
615 | if (node->alias_target) | |
616 | { | |
617 | if (TREE_CODE (node->alias_target) == FUNCTION_DECL) | |
618 | { | |
619 | target = chkp_maybe_create_clone (node->alias_target); | |
620 | clone->alias_target = target->decl; | |
621 | } | |
622 | else | |
623 | clone->alias_target = node->alias_target; | |
624 | } | |
625 | } | |
626 | ||
627 | /* Add IPA reference. It's main role is to keep instrumented | |
628 | version reachable while original node is reachable. */ | |
629 | ref = node->create_reference (clone, IPA_REF_CHKP, NULL); | |
630 | } | |
631 | ||
632 | return clone; | |
633 | } | |
634 | ||
635 | /* Create clone for all functions to be instrumented. */ | |
636 | ||
637 | static unsigned int | |
638 | chkp_versioning (void) | |
639 | { | |
640 | struct cgraph_node *node; | |
06201ad5 | 641 | const char *reason; |
d5e254e1 IE |
642 | |
643 | bitmap_obstack_initialize (NULL); | |
644 | ||
645 | FOR_EACH_DEFINED_FUNCTION (node) | |
646 | { | |
aa43616c | 647 | tree decl = node->decl; |
d5e254e1 IE |
648 | if (!node->instrumentation_clone |
649 | && !node->instrumented_version | |
650 | && !node->alias | |
651 | && !node->thunk.thunk_p | |
aa43616c RH |
652 | && (!DECL_BUILT_IN (decl) |
653 | || (DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL | |
654 | && DECL_FUNCTION_CODE (decl) < BEGIN_CHKP_BUILTINS))) | |
06201ad5 | 655 | { |
aa43616c RH |
656 | if (chkp_instrumentable_p (decl)) |
657 | chkp_maybe_create_clone (decl); | |
658 | else if ((reason = copy_forbidden (DECL_STRUCT_FUNCTION (decl)))) | |
06201ad5 | 659 | { |
aa43616c | 660 | if (warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wchkp, |
06201ad5 | 661 | "function cannot be instrumented")) |
aa43616c | 662 | inform (DECL_SOURCE_LOCATION (decl), reason, decl); |
06201ad5 IE |
663 | } |
664 | } | |
d5e254e1 IE |
665 | } |
666 | ||
667 | /* Mark all aliases and thunks of functions with no instrumented | |
668 | version as legacy function. */ | |
669 | FOR_EACH_DEFINED_FUNCTION (node) | |
670 | { | |
671 | if (!node->instrumentation_clone | |
672 | && !node->instrumented_version | |
673 | && (node->alias || node->thunk.thunk_p) | |
674 | && !lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (node->decl))) | |
675 | DECL_ATTRIBUTES (node->decl) | |
676 | = tree_cons (get_identifier ("bnd_legacy"), NULL, | |
677 | DECL_ATTRIBUTES (node->decl)); | |
678 | } | |
679 | ||
680 | bitmap_obstack_release (NULL); | |
681 | ||
682 | return 0; | |
683 | } | |
684 | ||
685 | /* In this pass we remove bodies of functions having | |
686 | instrumented version. Functions with removed bodies | |
687 | become a special kind of thunks to provide a connection | |
688 | between calls to the original version and instrumented | |
689 | function. */ | |
690 | ||
691 | static unsigned int | |
c11d86b4 | 692 | chkp_produce_thunks (bool early) |
d5e254e1 IE |
693 | { |
694 | struct cgraph_node *node; | |
695 | ||
696 | FOR_EACH_DEFINED_FUNCTION (node) | |
697 | { | |
698 | if (!node->instrumentation_clone | |
699 | && node->instrumented_version | |
700 | && gimple_has_body_p (node->decl) | |
c11d86b4 IE |
701 | && gimple_has_body_p (node->instrumented_version->decl) |
702 | && (!lookup_attribute ("always_inline", DECL_ATTRIBUTES (node->decl)) | |
703 | || !early)) | |
d5e254e1 IE |
704 | { |
705 | node->release_body (); | |
706 | node->remove_callees (); | |
707 | node->remove_all_references (); | |
708 | ||
709 | node->thunk.thunk_p = true; | |
710 | node->thunk.add_pointer_bounds_args = true; | |
711 | node->create_edge (node->instrumented_version, NULL, | |
712 | 0, CGRAPH_FREQ_BASE); | |
713 | node->create_reference (node->instrumented_version, | |
714 | IPA_REF_CHKP, NULL); | |
71671f5d IE |
715 | /* Thunk shouldn't be a cdtor. */ |
716 | DECL_STATIC_CONSTRUCTOR (node->decl) = 0; | |
717 | DECL_STATIC_DESTRUCTOR (node->decl) = 0; | |
d5e254e1 IE |
718 | } |
719 | } | |
720 | ||
721 | /* Mark instrumentation clones created for aliases and thunks | |
722 | as insttrumented so they could be removed as unreachable | |
723 | now. */ | |
c11d86b4 | 724 | if (!early) |
d5e254e1 | 725 | { |
c11d86b4 IE |
726 | FOR_EACH_DEFINED_FUNCTION (node) |
727 | { | |
728 | if (node->instrumentation_clone | |
729 | && (node->alias || node->thunk.thunk_p) | |
730 | && !chkp_function_instrumented_p (node->decl)) | |
731 | chkp_function_mark_instrumented (node->decl); | |
732 | } | |
d5e254e1 IE |
733 | } |
734 | ||
17e0fc92 | 735 | return TODO_remove_functions; |
d5e254e1 IE |
736 | } |
737 | ||
738 | const pass_data pass_data_ipa_chkp_versioning = | |
739 | { | |
740 | SIMPLE_IPA_PASS, /* type */ | |
741 | "chkp_versioning", /* name */ | |
742 | OPTGROUP_NONE, /* optinfo_flags */ | |
743 | TV_NONE, /* tv_id */ | |
744 | 0, /* properties_required */ | |
745 | 0, /* properties_provided */ | |
746 | 0, /* properties_destroyed */ | |
747 | 0, /* todo_flags_start */ | |
748 | 0 /* todo_flags_finish */ | |
749 | }; | |
750 | ||
c11d86b4 IE |
751 | const pass_data pass_data_ipa_chkp_early_produce_thunks = |
752 | { | |
753 | SIMPLE_IPA_PASS, /* type */ | |
754 | "chkp_ecleanup", /* name */ | |
755 | OPTGROUP_NONE, /* optinfo_flags */ | |
756 | TV_NONE, /* tv_id */ | |
757 | 0, /* properties_required */ | |
758 | 0, /* properties_provided */ | |
759 | 0, /* properties_destroyed */ | |
760 | 0, /* todo_flags_start */ | |
761 | 0 /* todo_flags_finish */ | |
762 | }; | |
763 | ||
d5e254e1 IE |
764 | const pass_data pass_data_ipa_chkp_produce_thunks = |
765 | { | |
766 | SIMPLE_IPA_PASS, /* type */ | |
767 | "chkp_cleanup", /* name */ | |
768 | OPTGROUP_NONE, /* optinfo_flags */ | |
769 | TV_NONE, /* tv_id */ | |
770 | 0, /* properties_required */ | |
771 | 0, /* properties_provided */ | |
772 | 0, /* properties_destroyed */ | |
773 | 0, /* todo_flags_start */ | |
774 | 0 /* todo_flags_finish */ | |
775 | }; | |
776 | ||
777 | class pass_ipa_chkp_versioning : public simple_ipa_opt_pass | |
778 | { | |
779 | public: | |
780 | pass_ipa_chkp_versioning (gcc::context *ctxt) | |
781 | : simple_ipa_opt_pass (pass_data_ipa_chkp_versioning, ctxt) | |
782 | {} | |
783 | ||
784 | /* opt_pass methods: */ | |
785 | virtual opt_pass * clone () | |
786 | { | |
787 | return new pass_ipa_chkp_versioning (m_ctxt); | |
788 | } | |
789 | ||
790 | virtual bool gate (function *) | |
791 | { | |
792 | return flag_check_pointer_bounds; | |
793 | } | |
794 | ||
795 | virtual unsigned int execute (function *) | |
796 | { | |
797 | return chkp_versioning (); | |
798 | } | |
799 | ||
800 | }; // class pass_ipa_chkp_versioning | |
801 | ||
c11d86b4 IE |
802 | class pass_ipa_chkp_early_produce_thunks : public simple_ipa_opt_pass |
803 | { | |
804 | public: | |
805 | pass_ipa_chkp_early_produce_thunks (gcc::context *ctxt) | |
806 | : simple_ipa_opt_pass (pass_data_ipa_chkp_early_produce_thunks, ctxt) | |
807 | {} | |
808 | ||
809 | /* opt_pass methods: */ | |
810 | virtual opt_pass * clone () | |
811 | { | |
812 | return new pass_ipa_chkp_early_produce_thunks (m_ctxt); | |
813 | } | |
814 | ||
815 | virtual bool gate (function *) | |
816 | { | |
817 | return flag_check_pointer_bounds; | |
818 | } | |
819 | ||
820 | virtual unsigned int execute (function *) | |
821 | { | |
822 | return chkp_produce_thunks (true); | |
823 | } | |
824 | ||
825 | }; // class pass_chkp_produce_thunks | |
826 | ||
d5e254e1 IE |
827 | class pass_ipa_chkp_produce_thunks : public simple_ipa_opt_pass |
828 | { | |
829 | public: | |
830 | pass_ipa_chkp_produce_thunks (gcc::context *ctxt) | |
831 | : simple_ipa_opt_pass (pass_data_ipa_chkp_produce_thunks, ctxt) | |
832 | {} | |
833 | ||
834 | /* opt_pass methods: */ | |
835 | virtual opt_pass * clone () | |
836 | { | |
837 | return new pass_ipa_chkp_produce_thunks (m_ctxt); | |
838 | } | |
839 | ||
840 | virtual bool gate (function *) | |
841 | { | |
842 | return flag_check_pointer_bounds; | |
843 | } | |
844 | ||
845 | virtual unsigned int execute (function *) | |
846 | { | |
c11d86b4 | 847 | return chkp_produce_thunks (false); |
d5e254e1 IE |
848 | } |
849 | ||
850 | }; // class pass_chkp_produce_thunks | |
851 | ||
852 | simple_ipa_opt_pass * | |
853 | make_pass_ipa_chkp_versioning (gcc::context *ctxt) | |
854 | { | |
855 | return new pass_ipa_chkp_versioning (ctxt); | |
856 | } | |
857 | ||
c11d86b4 IE |
858 | simple_ipa_opt_pass * |
859 | make_pass_ipa_chkp_early_produce_thunks (gcc::context *ctxt) | |
860 | { | |
861 | return new pass_ipa_chkp_early_produce_thunks (ctxt); | |
862 | } | |
863 | ||
d5e254e1 IE |
864 | simple_ipa_opt_pass * |
865 | make_pass_ipa_chkp_produce_thunks (gcc::context *ctxt) | |
866 | { | |
867 | return new pass_ipa_chkp_produce_thunks (ctxt); | |
868 | } |