111
|
1 /* Pointer Bounds Checker IPA passes.
|
|
2 Copyright (C) 2014-2017 Free Software Foundation, Inc.
|
|
3 Contributed by Ilya Enkovich (ilya.enkovich@intel.com)
|
|
4
|
|
5 This file is part of GCC.
|
|
6
|
|
7 GCC is free software; you can redistribute it and/or modify it under
|
|
8 the terms of the GNU General Public License as published by the Free
|
|
9 Software Foundation; either version 3, or (at your option) any later
|
|
10 version.
|
|
11
|
|
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
15 for more details.
|
|
16
|
|
17 You should have received a copy of the GNU General Public License
|
|
18 along with GCC; see the file COPYING3. If not see
|
|
19 <http://www.gnu.org/licenses/>. */
|
|
20
|
|
21 #include "config.h"
|
|
22 #define INCLUDE_STRING
|
|
23 #include "system.h"
|
|
24 #include "coretypes.h"
|
|
25 #include "backend.h"
|
|
26 #include "tree.h"
|
|
27 #include "gimple.h"
|
|
28 #include "tree-pass.h"
|
|
29 #include "stringpool.h"
|
|
30 #include "lto-streamer.h"
|
|
31 #include "stor-layout.h"
|
|
32 #include "calls.h"
|
|
33 #include "cgraph.h"
|
|
34 #include "tree-chkp.h"
|
|
35 #include "tree-inline.h"
|
|
36 #include "ipa-chkp.h"
|
|
37 #include "stringpool.h"
|
|
38 #include "attribs.h"
|
|
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_"
|
|
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. */
|
|
91 bool
|
|
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 }
|
|
125
|
|
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
|
|
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. */
|
|
194 if (chkp_wrap_function(fndecl))
|
|
195 {
|
|
196 new_name = get_identifier (chkp_wrap_function_name (fndecl));
|
|
197 DECL_VISIBILITY (new_decl) = VISIBILITY_DEFAULT;
|
|
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 }
|
|
207 SET_DECL_ASSEMBLER_NAME (new_decl, new_name);
|
|
208
|
|
209 /* For functions with body versioning will make a copy of arguments.
|
|
210 For functions with no body we need to do it here. */
|
|
211 if (!gimple_has_body_p (fndecl))
|
|
212 {
|
|
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 }
|
|
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
|
|
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
|
|
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;
|
|
281 tree arg_type, attrs;
|
|
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
|
|
301 type = build_distinct_type_copy (orig_type);
|
|
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
|
|
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
|
|
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
|
|
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)))
|
|
481 && (!fn || !copy_forbidden (fn)));
|
|
482 }
|
|
483
|
|
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
|
|
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
|
|
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 {
|
|
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;
|
|
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
|
|
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
|
|
583 /* Clones have the same comdat group as originals. */
|
|
584 if (node->same_comdat_group
|
|
585 || (DECL_ONE_ONLY (node->decl)
|
|
586 && !DECL_EXTERNAL (node->decl)))
|
|
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. */
|
|
593 for (i = 0; node->iterate_direct_aliases (i, ref); i++)
|
|
594 chkp_maybe_create_clone (ref->referring->decl);
|
|
595
|
|
596 /* Clone all thunks. */
|
|
597 for (e = node->callers; e; e = e->next_caller)
|
|
598 if (e->caller->thunk.thunk_p
|
|
599 && !e->caller->thunk.add_pointer_bounds_args
|
|
600 && !e->caller->instrumentation_clone)
|
|
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)
|
|
618 {
|
|
619 target = chkp_maybe_create_clone (ref->referred->decl);
|
|
620 clone->create_reference (target, IPA_REF_ALIAS);
|
|
621 }
|
|
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;
|
|
649 const char *reason;
|
|
650
|
|
651 bitmap_obstack_initialize (NULL);
|
|
652
|
|
653 FOR_EACH_DEFINED_FUNCTION (node)
|
|
654 {
|
|
655 tree decl = node->decl;
|
|
656 if (!node->instrumentation_clone
|
|
657 && !node->instrumented_version
|
|
658 && !node->alias
|
|
659 && !node->thunk.thunk_p
|
|
660 && (!DECL_BUILT_IN (decl)
|
|
661 || (DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL
|
|
662 && DECL_FUNCTION_CODE (decl) < BEGIN_CHKP_BUILTINS)))
|
|
663 {
|
|
664 if (chkp_instrumentable_p (decl))
|
|
665 chkp_maybe_create_clone (decl);
|
|
666 else if ((reason = copy_forbidden (DECL_STRUCT_FUNCTION (decl))))
|
|
667 {
|
|
668 if (warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wchkp,
|
|
669 "function cannot be instrumented"))
|
|
670 inform (DECL_SOURCE_LOCATION (decl), reason, decl);
|
|
671 }
|
|
672 }
|
|
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
|
|
700 chkp_produce_thunks (bool early)
|
|
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)
|
|
709 && gimple_has_body_p (node->instrumented_version->decl)
|
|
710 && (!lookup_attribute ("always_inline", DECL_ATTRIBUTES (node->decl))
|
|
711 || !early))
|
|
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,
|
|
720 node->count, CGRAPH_FREQ_BASE);
|
|
721 node->create_reference (node->instrumented_version,
|
|
722 IPA_REF_CHKP, NULL);
|
|
723 /* Thunk shouldn't be a cdtor. */
|
|
724 DECL_STATIC_CONSTRUCTOR (node->decl) = 0;
|
|
725 DECL_STATIC_DESTRUCTOR (node->decl) = 0;
|
|
726 }
|
|
727 }
|
|
728
|
|
729 /* Mark instrumentation clones created for aliases and thunks
|
|
730 as insttrumented so they could be removed as unreachable
|
|
731 now. */
|
|
732 if (!early)
|
|
733 {
|
|
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 }
|
|
741 }
|
|
742
|
|
743 return TODO_remove_functions;
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 {
|
|
855 return chkp_produce_thunks (false);
|
|
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
|
|
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
|
|
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 }
|