1 /* Pointer Bounds Checker IPA passes.
2 Copyright (C) 2014 Free Software Foundation, Inc.
3 Contributed by Ilya Enkovich (ilya.enkovich@intel.com)
5 This file is part of GCC.
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
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
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/>. */
23 #include "coretypes.h"
24 #include "tree-core.h"
25 #include "stor-layout.h"
27 #include "tree-pass.h"
28 #include "stringpool.h"
30 #include "gimple-expr.h"
32 #include "hard-reg-set.h"
35 #include "tree-ssa-alias.h"
37 #include "basic-block.h"
40 #include "lto-streamer.h"
42 #include "tree-chkp.h"
45 /* Pointer Bounds Checker has two IPA passes to support code instrumentation.
47 In instrumented code each pointer is provided with bounds. For input
48 pointer parameters it means we also have bounds passed. For calls it
49 means we have additional bounds arguments for pointer arguments.
51 To have all IPA optimizations working correctly we have to express
52 dataflow between passed and received bounds explicitly via additional
53 entries in function declaration arguments list and in function type.
54 Since we may have both instrumented and not instrumented code at the
55 same time, we cannot replace all original functions with their
56 instrumented variants. Therefore we create clones (versions) instead.
58 Instrumentation clones creation is a separate IPA pass which is a part
59 of early local passes. Clones are created after SSA is built (because
60 instrumentation pass works on SSA) and before any transformations
61 which may change pointer flow and therefore lead to incorrect code
62 instrumentation (possibly causing false bounds check failures).
64 Instrumentation clones have pointer bounds arguments added right after
65 pointer arguments. Clones have assembler name of the original
66 function with suffix added. New assembler name is in transparent
67 alias chain with the original name. Thus we expect all calls to the
68 original and instrumented functions look similar in assembler.
70 During instrumentation versioning pass we create instrumented versions
71 of all function with body and also for all their aliases and thunks.
72 Clones for functions with no body are created on demand (usually
73 during call instrumentation).
75 Original and instrumented function nodes are connected with IPA
76 reference IPA_REF_CHKP. It is mostly done to have reachability
77 analysis working correctly. We may have no references to the
78 instrumented function in the code but it still should be counted
79 as reachable if the original function is reachable.
81 When original function bodies are not needed anymore we release
82 them and transform functions into a special kind of thunks. Each
83 thunk has a call edge to the instrumented version. These thunks
84 help to keep externally visible instrumented functions visible
85 when linker resolution files are used. Linker has no info about
86 connection between original and instrumented function and
87 therefore we may wrongly decide (due to difference in assembler
88 names) that instrumented function version is local and can be
91 #define CHKP_BOUNDS_OF_SYMBOL_PREFIX "__chkp_bounds_of_"
93 /* Build a clone of FNDECL with a modified name. */
96 chkp_build_instrumented_fndecl (tree fndecl
)
98 tree new_decl
= copy_node (fndecl
);
102 /* called_as_built_in checks DECL_NAME to identify calls to
103 builtins. We want instrumented calls to builtins to be
104 recognized by called_as_built_in. Therefore use original
105 DECL_NAME for cloning with no prefixes. */
106 s
= IDENTIFIER_POINTER (DECL_NAME (fndecl
));
108 DECL_NAME (new_decl
) = get_identifier (s
.c_str ());
110 /* References to the original and to the instrumented version
111 should look the same in the output assembly. And we cannot
112 use the same assembler name for the instrumented version
113 because it conflicts with decl merging algorithms in LTO.
114 Achieve the result by using transparent alias name for the
115 instrumented version. */
116 s
= IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (fndecl
));
118 new_name
= get_identifier (s
.c_str ());
119 IDENTIFIER_TRANSPARENT_ALIAS (new_name
) = 1;
120 TREE_CHAIN (new_name
) = DECL_ASSEMBLER_NAME (fndecl
);
121 SET_DECL_ASSEMBLER_NAME (new_decl
, new_name
);
123 /* For functions with body versioning will make a copy of arguments.
124 For functions with no body we need to do it here. */
125 if (!gimple_has_body_p (fndecl
))
126 DECL_ARGUMENTS (new_decl
) = copy_list (DECL_ARGUMENTS (fndecl
));
128 /* We are going to modify attributes list and therefore should
130 DECL_ATTRIBUTES (new_decl
) = copy_list (DECL_ATTRIBUTES (fndecl
));
136 /* Fix operands of attribute from ATTRS list named ATTR_NAME.
137 Integer operands are replaced with values according to
138 INDEXES map having LEN elements. For operands out of len
139 we just add DELTA. */
142 chkp_map_attr_arg_indexes (tree attrs
, const char *attr_name
,
143 unsigned *indexes
, int len
, int delta
)
145 tree attr
= lookup_attribute (attr_name
, attrs
);
151 TREE_VALUE (attr
) = copy_list (TREE_VALUE (attr
));
152 for (op
= TREE_VALUE (attr
); op
; op
= TREE_CHAIN (op
))
156 if (TREE_CODE (TREE_VALUE (op
)) != INTEGER_CST
)
159 idx
= TREE_INT_CST_LOW (TREE_VALUE (op
));
161 /* If idx exceeds indexes length then we just
162 keep it at the same distance from the last
167 idx
= indexes
[idx
- 1] + 1;
168 TREE_VALUE (op
) = build_int_cst (TREE_TYPE (TREE_VALUE (op
)), idx
);
172 /* Make a copy of function type ORIG_TYPE adding pointer
173 bounds as additional arguments. */
176 chkp_copy_function_type_adding_bounds (tree orig_type
)
179 tree arg_type
, attrs
, t
;
180 unsigned len
= list_length (TYPE_ARG_TYPES (orig_type
));
181 unsigned *indexes
= XALLOCAVEC (unsigned, len
);
182 unsigned idx
= 0, new_idx
= 0;
184 for (arg_type
= TYPE_ARG_TYPES (orig_type
);
186 arg_type
= TREE_CHAIN (arg_type
))
187 if (TREE_VALUE (arg_type
) == void_type_node
)
189 else if (BOUNDED_TYPE_P (TREE_VALUE (arg_type
))
190 || pass_by_reference (NULL
, TYPE_MODE (TREE_VALUE (arg_type
)),
191 TREE_VALUE (arg_type
), true)
192 || chkp_type_has_pointer (TREE_VALUE (arg_type
)))
195 /* We may use original type if there are no bounds passed. */
199 type
= copy_node (orig_type
);
200 TYPE_ARG_TYPES (type
) = copy_list (TYPE_ARG_TYPES (type
));
202 for (arg_type
= TYPE_ARG_TYPES (type
);
204 arg_type
= TREE_CHAIN (arg_type
))
206 indexes
[idx
++] = new_idx
++;
208 /* pass_by_reference returns 1 for void type,
209 so check for it first. */
210 if (TREE_VALUE (arg_type
) == void_type_node
)
212 else if (BOUNDED_TYPE_P (TREE_VALUE (arg_type
))
213 || pass_by_reference (NULL
, TYPE_MODE (TREE_VALUE (arg_type
)),
214 TREE_VALUE (arg_type
), true))
216 tree new_type
= build_tree_list (NULL_TREE
,
217 pointer_bounds_type_node
);
218 TREE_CHAIN (new_type
) = TREE_CHAIN (arg_type
);
219 TREE_CHAIN (arg_type
) = new_type
;
221 arg_type
= TREE_CHAIN (arg_type
);
224 else if (chkp_type_has_pointer (TREE_VALUE (arg_type
)))
226 bitmap slots
= BITMAP_ALLOC (NULL
);
230 chkp_find_bound_slots (TREE_VALUE (arg_type
), slots
);
232 EXECUTE_IF_SET_IN_BITMAP (slots
, 0, bnd_no
, bi
)
234 tree new_type
= build_tree_list (NULL_TREE
,
235 pointer_bounds_type_node
);
236 TREE_CHAIN (new_type
) = TREE_CHAIN (arg_type
);
237 TREE_CHAIN (arg_type
) = new_type
;
239 arg_type
= TREE_CHAIN (arg_type
);
246 /* If function type has attribute with arg indexes then
247 we have to copy it fixing attribute ops. Map for
248 fixing is in indexes array. */
249 attrs
= TYPE_ATTRIBUTES (type
);
250 if (lookup_attribute ("nonnull", attrs
)
251 || lookup_attribute ("format", attrs
)
252 || lookup_attribute ("format_arg", attrs
))
254 int delta
= new_idx
- len
;
255 attrs
= copy_list (TYPE_ATTRIBUTES (type
));
256 chkp_map_attr_arg_indexes (attrs
, "nonnull", indexes
, len
, delta
);
257 chkp_map_attr_arg_indexes (attrs
, "format", indexes
, len
, delta
);
258 chkp_map_attr_arg_indexes (attrs
, "format_arg", indexes
, len
, delta
);
259 TYPE_ATTRIBUTES (type
) = attrs
;
262 t
= TYPE_MAIN_VARIANT (orig_type
);
265 TYPE_MAIN_VARIANT (type
) = t
;
266 TYPE_NEXT_VARIANT (type
) = TYPE_NEXT_VARIANT (t
);
267 TYPE_NEXT_VARIANT (t
) = type
;
271 TYPE_MAIN_VARIANT (type
) = type
;
272 TYPE_NEXT_VARIANT (type
) = NULL
;
279 /* For given function FNDECL add bounds arguments to arguments
283 chkp_add_bounds_params_to_function (tree fndecl
)
287 for (arg
= DECL_ARGUMENTS (fndecl
); arg
; arg
= DECL_CHAIN (arg
))
290 std::string new_name
= CHKP_BOUNDS_OF_SYMBOL_PREFIX
;
294 new_name
+= IDENTIFIER_POINTER (DECL_NAME (arg
));
298 snprintf (uid
, 25, "D.%u", DECL_UID (arg
));
302 new_arg
= build_decl (DECL_SOURCE_LOCATION (arg
), PARM_DECL
,
303 get_identifier (new_name
.c_str ()),
304 pointer_bounds_type_node
);
305 DECL_ARG_TYPE (new_arg
) = pointer_bounds_type_node
;
306 DECL_CONTEXT (new_arg
) = DECL_CONTEXT (arg
);
307 DECL_ARTIFICIAL (new_arg
) = 1;
308 DECL_CHAIN (new_arg
) = DECL_CHAIN (arg
);
309 DECL_CHAIN (arg
) = new_arg
;
311 arg
= DECL_CHAIN (arg
);
314 else if (chkp_type_has_pointer (TREE_TYPE (arg
)))
317 bitmap slots
= BITMAP_ALLOC (NULL
);
321 chkp_find_bound_slots (TREE_TYPE (arg
), slots
);
323 EXECUTE_IF_SET_IN_BITMAP (slots
, 0, bnd_no
, bi
)
325 std::string new_name
= CHKP_BOUNDS_OF_SYMBOL_PREFIX
;
329 if (DECL_NAME (orig_arg
))
330 new_name
+= IDENTIFIER_POINTER (DECL_NAME (orig_arg
));
333 snprintf (offs
, 25, "D.%u", DECL_UID (arg
));
336 snprintf (offs
, 25, "__%u", bnd_no
* POINTER_SIZE
/ BITS_PER_UNIT
);
338 new_arg
= build_decl (DECL_SOURCE_LOCATION (orig_arg
),
340 get_identifier (new_name
.c_str ()),
341 pointer_bounds_type_node
);
342 DECL_ARG_TYPE (new_arg
) = pointer_bounds_type_node
;
343 DECL_CONTEXT (new_arg
) = DECL_CONTEXT (orig_arg
);
344 DECL_ARTIFICIAL (new_arg
) = 1;
345 DECL_CHAIN (new_arg
) = DECL_CHAIN (arg
);
346 DECL_CHAIN (arg
) = new_arg
;
348 arg
= DECL_CHAIN (arg
);
354 chkp_copy_function_type_adding_bounds (TREE_TYPE (fndecl
));
357 /* Return clone created for instrumentation of NODE or NULL. */
360 chkp_maybe_create_clone (tree fndecl
)
362 cgraph_node
*node
= cgraph_node::get_create (fndecl
);
363 cgraph_node
*clone
= node
->instrumented_version
;
365 gcc_assert (!node
->instrumentation_clone
);
369 tree new_decl
= chkp_build_instrumented_fndecl (fndecl
);
370 struct cgraph_edge
*e
;
374 clone
= node
->create_version_clone (new_decl
, vNULL
, NULL
);
375 clone
->externally_visible
= node
->externally_visible
;
376 clone
->local
= node
->local
;
377 clone
->address_taken
= node
->address_taken
;
378 clone
->thunk
= node
->thunk
;
379 clone
->alias
= node
->alias
;
380 clone
->weakref
= node
->weakref
;
381 clone
->cpp_implicit_alias
= node
->cpp_implicit_alias
;
382 clone
->instrumented_version
= node
;
383 clone
->orig_decl
= fndecl
;
384 clone
->instrumentation_clone
= true;
385 node
->instrumented_version
= clone
;
387 if (gimple_has_body_p (fndecl
))
389 /* If function will not be instrumented, then it's instrumented
390 version is a thunk for the original. */
391 if (lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (fndecl
))
392 || (flag_chkp_instrument_marked_only
393 && !lookup_attribute ("bnd_instrument", DECL_ATTRIBUTES (fndecl
))))
395 clone
->thunk
.thunk_p
= true;
396 clone
->thunk
.add_pointer_bounds_args
= true;
397 clone
->create_edge (node
, NULL
, 0, CGRAPH_FREQ_BASE
);
401 tree_function_versioning (fndecl
, new_decl
, NULL
, false,
402 NULL
, false, NULL
, NULL
);
403 clone
->lowered
= true;
407 /* New params are inserted after versioning because it
408 actually copies args list from the original decl. */
409 chkp_add_bounds_params_to_function (new_decl
);
411 /* Clones have the same comdat group as originals. */
412 if (node
->same_comdat_group
413 || DECL_ONE_ONLY (node
->decl
))
414 clone
->add_to_same_comdat_group (node
);
416 if (gimple_has_body_p (fndecl
))
417 symtab
->call_cgraph_insertion_hooks (clone
);
419 /* Clone all aliases. */
420 for (i
= 0; node
->iterate_referring (i
, ref
); i
++)
421 if (ref
->use
== IPA_REF_ALIAS
)
423 struct cgraph_node
*alias
= dyn_cast
<cgraph_node
*> (ref
->referring
);
424 struct cgraph_node
*chkp_alias
425 = chkp_maybe_create_clone (alias
->decl
);
426 chkp_alias
->create_reference (clone
, IPA_REF_ALIAS
, NULL
);
429 /* Clone all thunks. */
430 for (e
= node
->callers
; e
; e
= e
->next_caller
)
431 if (e
->caller
->thunk
.thunk_p
)
433 struct cgraph_node
*thunk
434 = chkp_maybe_create_clone (e
->caller
->decl
);
435 /* Redirect thunk clone edge to the node clone. */
436 thunk
->callees
->redirect_callee (clone
);
439 /* For aliases and thunks we should make sure target is cloned
440 to have proper references and edges. */
441 if (node
->thunk
.thunk_p
)
442 chkp_maybe_create_clone (node
->callees
->callee
->decl
);
443 else if (node
->alias
)
445 struct cgraph_node
*target
;
447 ref
= node
->ref_list
.first_reference ();
449 chkp_maybe_create_clone (ref
->referred
->decl
);
451 if (node
->alias_target
)
453 if (TREE_CODE (node
->alias_target
) == FUNCTION_DECL
)
455 target
= chkp_maybe_create_clone (node
->alias_target
);
456 clone
->alias_target
= target
->decl
;
459 clone
->alias_target
= node
->alias_target
;
463 /* Add IPA reference. It's main role is to keep instrumented
464 version reachable while original node is reachable. */
465 ref
= node
->create_reference (clone
, IPA_REF_CHKP
, NULL
);
471 /* Create clone for all functions to be instrumented. */
474 chkp_versioning (void)
476 struct cgraph_node
*node
;
478 bitmap_obstack_initialize (NULL
);
480 FOR_EACH_DEFINED_FUNCTION (node
)
482 if (!node
->instrumentation_clone
483 && !node
->instrumented_version
485 && !node
->thunk
.thunk_p
486 && !lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (node
->decl
))
487 && (!flag_chkp_instrument_marked_only
488 || lookup_attribute ("bnd_instrument",
489 DECL_ATTRIBUTES (node
->decl
)))
490 /* No builtins instrumentation for now. */
491 && DECL_BUILT_IN_CLASS (node
->decl
) == NOT_BUILT_IN
)
492 chkp_maybe_create_clone (node
->decl
);
495 /* Mark all aliases and thunks of functions with no instrumented
496 version as legacy function. */
497 FOR_EACH_DEFINED_FUNCTION (node
)
499 if (!node
->instrumentation_clone
500 && !node
->instrumented_version
501 && (node
->alias
|| node
->thunk
.thunk_p
)
502 && !lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (node
->decl
)))
503 DECL_ATTRIBUTES (node
->decl
)
504 = tree_cons (get_identifier ("bnd_legacy"), NULL
,
505 DECL_ATTRIBUTES (node
->decl
));
508 bitmap_obstack_release (NULL
);
513 /* In this pass we remove bodies of functions having
514 instrumented version. Functions with removed bodies
515 become a special kind of thunks to provide a connection
516 between calls to the original version and instrumented
520 chkp_produce_thunks (void)
522 struct cgraph_node
*node
;
524 FOR_EACH_DEFINED_FUNCTION (node
)
526 if (!node
->instrumentation_clone
527 && node
->instrumented_version
528 && gimple_has_body_p (node
->decl
)
529 && gimple_has_body_p (node
->instrumented_version
->decl
))
531 node
->release_body ();
532 node
->remove_callees ();
533 node
->remove_all_references ();
535 node
->thunk
.thunk_p
= true;
536 node
->thunk
.add_pointer_bounds_args
= true;
537 node
->create_edge (node
->instrumented_version
, NULL
,
538 0, CGRAPH_FREQ_BASE
);
539 node
->create_reference (node
->instrumented_version
,
544 /* Mark instrumentation clones created for aliases and thunks
545 as insttrumented so they could be removed as unreachable
547 FOR_EACH_DEFINED_FUNCTION (node
)
549 if (node
->instrumentation_clone
550 && (node
->alias
|| node
->thunk
.thunk_p
)
551 && !chkp_function_instrumented_p (node
->decl
))
552 chkp_function_mark_instrumented (node
->decl
);
555 symtab
->remove_unreachable_nodes (true, dump_file
);
560 const pass_data pass_data_ipa_chkp_versioning
=
562 SIMPLE_IPA_PASS
, /* type */
563 "chkp_versioning", /* name */
564 OPTGROUP_NONE
, /* optinfo_flags */
566 0, /* properties_required */
567 0, /* properties_provided */
568 0, /* properties_destroyed */
569 0, /* todo_flags_start */
570 0 /* todo_flags_finish */
573 const pass_data pass_data_ipa_chkp_produce_thunks
=
575 SIMPLE_IPA_PASS
, /* type */
576 "chkp_cleanup", /* name */
577 OPTGROUP_NONE
, /* optinfo_flags */
579 0, /* properties_required */
580 0, /* properties_provided */
581 0, /* properties_destroyed */
582 0, /* todo_flags_start */
583 0 /* todo_flags_finish */
586 class pass_ipa_chkp_versioning
: public simple_ipa_opt_pass
589 pass_ipa_chkp_versioning (gcc::context
*ctxt
)
590 : simple_ipa_opt_pass (pass_data_ipa_chkp_versioning
, ctxt
)
593 /* opt_pass methods: */
594 virtual opt_pass
* clone ()
596 return new pass_ipa_chkp_versioning (m_ctxt
);
599 virtual bool gate (function
*)
601 return flag_check_pointer_bounds
;
604 virtual unsigned int execute (function
*)
606 return chkp_versioning ();
609 }; // class pass_ipa_chkp_versioning
611 class pass_ipa_chkp_produce_thunks
: public simple_ipa_opt_pass
614 pass_ipa_chkp_produce_thunks (gcc::context
*ctxt
)
615 : simple_ipa_opt_pass (pass_data_ipa_chkp_produce_thunks
, ctxt
)
618 /* opt_pass methods: */
619 virtual opt_pass
* clone ()
621 return new pass_ipa_chkp_produce_thunks (m_ctxt
);
624 virtual bool gate (function
*)
626 return flag_check_pointer_bounds
;
629 virtual unsigned int execute (function
*)
631 return chkp_produce_thunks ();
634 }; // class pass_chkp_produce_thunks
636 simple_ipa_opt_pass
*
637 make_pass_ipa_chkp_versioning (gcc::context
*ctxt
)
639 return new pass_ipa_chkp_versioning (ctxt
);
642 simple_ipa_opt_pass
*
643 make_pass_ipa_chkp_produce_thunks (gcc::context
*ctxt
)
645 return new pass_ipa_chkp_produce_thunks (ctxt
);