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