summaryrefslogtreecommitdiff
path: root/gcc/tree-chkp.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/tree-chkp.c')
-rw-r--r--gcc/tree-chkp.c4678
1 files changed, 0 insertions, 4678 deletions
diff --git a/gcc/tree-chkp.c b/gcc/tree-chkp.c
deleted file mode 100644
index d10e6c40423..00000000000
--- a/gcc/tree-chkp.c
+++ /dev/null
@@ -1,4678 +0,0 @@
-/* Pointer Bounds Checker insrumentation pass.
- Copyright (C) 2014-2018 Free Software Foundation, Inc.
- Contributed by Ilya Enkovich (ilya.enkovich@intel.com)
-
-This file is part of GCC.
-
-GCC is free software; you can redistribute it and/or modify it under
-the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 3, or (at your option) any later
-version.
-
-GCC is distributed in the hope that it will be useful, but WITHOUT ANY
-WARRANTY; without even the implied warranty of MERCHANTABILITY or
-FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-for more details.
-
-You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING3. If not see
-<http://www.gnu.org/licenses/>. */
-
-#include "config.h"
-#include "system.h"
-#include "coretypes.h"
-#include "backend.h"
-#include "target.h"
-#include "rtl.h"
-#include "tree.h"
-#include "gimple.h"
-#include "cfghooks.h"
-#include "tree-pass.h"
-#include "ssa.h"
-#include "cgraph.h"
-#include "diagnostic.h"
-#include "fold-const.h"
-#include "stor-layout.h"
-#include "varasm.h"
-#include "tree-iterator.h"
-#include "tree-cfg.h"
-#include "langhooks.h"
-#include "tree-ssa-address.h"
-#include "tree-ssa-loop-niter.h"
-#include "gimple-pretty-print.h"
-#include "gimple-iterator.h"
-#include "gimplify.h"
-#include "gimplify-me.h"
-#include "print-tree.h"
-#include "calls.h"
-#include "expr.h"
-#include "tree-ssa-propagate.h"
-#include "tree-chkp.h"
-#include "gimple-walk.h"
-#include "tree-dfa.h"
-#include "ipa-chkp.h"
-#include "params.h"
-#include "stringpool.h"
-#include "attribs.h"
-
-/* Pointer Bounds Checker instruments code with memory checks to find
- out-of-bounds memory accesses. Checks are performed by computing
- bounds for each pointer and then comparing address of accessed
- memory before pointer dereferencing.
-
- 1. Function clones.
-
- See ipa-chkp.c.
-
- 2. Instrumentation.
-
- There are few things to instrument:
-
- a) Memory accesses - add checker calls to check address of accessed memory
- against bounds of dereferenced pointer. Obviously safe memory
- accesses like static variable access does not have to be instrumented
- with checks.
-
- Example:
-
- val_2 = *p_1;
-
- with 4 bytes access is transformed into:
-
- __builtin___chkp_bndcl (__bound_tmp.1_3, p_1);
- D.1_4 = p_1 + 3;
- __builtin___chkp_bndcu (__bound_tmp.1_3, D.1_4);
- val_2 = *p_1;
-
- where __bound_tmp.1_3 are bounds computed for pointer p_1,
- __builtin___chkp_bndcl is a lower bound check and
- __builtin___chkp_bndcu is an upper bound check.
-
- b) Pointer stores.
-
- When pointer is stored in memory we need to store its bounds. To
- achieve compatibility of instrumented code with regular codes
- we have to keep data layout and store bounds in special bound tables
- via special checker call. Implementation of bounds table may vary for
- different platforms. It has to associate pointer value and its
- location (it is required because we may have two equal pointers
- with different bounds stored in different places) with bounds.
- Another checker builtin allows to get bounds for specified pointer
- loaded from specified location.
-
- Example:
-
- buf1[i_1] = &buf2;
-
- is transformed into:
-
- buf1[i_1] = &buf2;
- D.1_2 = &buf1[i_1];
- __builtin___chkp_bndstx (D.1_2, &buf2, __bound_tmp.1_2);
-
- where __bound_tmp.1_2 are bounds of &buf2.
-
- c) Static initialization.
-
- The special case of pointer store is static pointer initialization.
- Bounds initialization is performed in a few steps:
- - register all static initializations in front-end using
- chkp_register_var_initializer
- - when file compilation finishes we create functions with special
- attribute 'chkp ctor' and put explicit initialization code
- (assignments) for all statically initialized pointers.
- - when checker constructor is compiled checker pass adds required
- bounds initialization for all statically initialized pointers
- - since we do not actually need excess pointers initialization
- in checker constructor we remove such assignments from them
-
- d) Calls.
-
- For each call in the code we add additional arguments to pass
- bounds for pointer arguments. We determine type of call arguments
- using arguments list from function declaration; if function
- declaration is not available we use function type; otherwise
- (e.g. for unnamed arguments) we use type of passed value. Function
- declaration/type is replaced with the instrumented one.
-
- Example:
-
- val_1 = foo (&buf1, &buf2, &buf1, 0);
-
- is translated into:
-
- val_1 = foo.chkp (&buf1, __bound_tmp.1_2, &buf2, __bound_tmp.1_3,
- &buf1, __bound_tmp.1_2, 0);
-
- e) Returns.
-
- If function returns a pointer value we have to return bounds also.
- A new operand was added for return statement to hold returned bounds.
-
- Example:
-
- return &_buf1;
-
- is transformed into
-
- return &_buf1, __bound_tmp.1_1;
-
- 3. Bounds computation.
-
- Compiler is fully responsible for computing bounds to be used for each
- memory access. The first step for bounds computation is to find the
- origin of pointer dereferenced for memory access. Basing on pointer
- origin we define a way to compute its bounds. There are just few
- possible cases:
-
- a) Pointer is returned by call.
-
- In this case we use corresponding checker builtin method to obtain returned
- bounds.
-
- Example:
-
- buf_1 = malloc (size_2);
- foo (buf_1);
-
- is translated into:
-
- buf_1 = malloc (size_2);
- __bound_tmp.1_3 = __builtin___chkp_bndret (buf_1);
- foo (buf_1, __bound_tmp.1_3);
-
- b) Pointer is an address of an object.
-
- In this case compiler tries to compute objects size and create corresponding
- bounds. If object has incomplete type then special checker builtin is used to
- obtain its size at runtime.
-
- Example:
-
- foo ()
- {
- <unnamed type> __bound_tmp.3;
- static int buf[100];
-
- <bb 3>:
- __bound_tmp.3_2 = __builtin___chkp_bndmk (&buf, 400);
-
- <bb 2>:
- return &buf, __bound_tmp.3_2;
- }
-
- Example:
-
- Address of an object 'extern int buf[]' with incomplete type is
- returned.
-
- foo ()
- {
- <unnamed type> __bound_tmp.4;
- long unsigned int __size_tmp.3;
-
- <bb 3>:
- __size_tmp.3_4 = __builtin_ia32_sizeof (buf);
- __bound_tmp.4_3 = __builtin_ia32_bndmk (&buf, __size_tmp.3_4);
-
- <bb 2>:
- return &buf, __bound_tmp.4_3;
- }
-
- c) Pointer is the result of object narrowing.
-
- It happens when we use pointer to an object to compute pointer to a part
- of an object. E.g. we take pointer to a field of a structure. In this
- case we perform bounds intersection using bounds of original object and
- bounds of object's part (which are computed basing on its type).
-
- There may be some debatable questions about when narrowing should occur
- and when it should not. To avoid false bound violations in correct
- programs we do not perform narrowing when address of an array element is
- obtained (it has address of the whole array) and when address of the first
- structure field is obtained (because it is guaranteed to be equal to
- address of the whole structure and it is legal to cast it back to structure).
-
- Default narrowing behavior may be changed using compiler flags.
-
- Example:
-
- In this example address of the second structure field is returned.
-
- foo (struct A * p, __bounds_type __bounds_of_p)
- {
- <unnamed type> __bound_tmp.3;
- int * _2;
- int * _5;
-
- <bb 2>:
- _5 = &p_1(D)->second_field;
- __bound_tmp.3_6 = __builtin___chkp_bndmk (_5, 4);
- __bound_tmp.3_8 = __builtin___chkp_intersect (__bound_tmp.3_6,
- __bounds_of_p_3(D));
- _2 = &p_1(D)->second_field;
- return _2, __bound_tmp.3_8;
- }
-
- Example:
-
- In this example address of the first field of array element is returned.
-
- foo (struct A * p, __bounds_type __bounds_of_p, int i)
- {
- long unsigned int _3;
- long unsigned int _4;
- struct A * _6;
- int * _7;
-
- <bb 2>:
- _3 = (long unsigned int) i_1(D);
- _4 = _3 * 8;
- _6 = p_5(D) + _4;
- _7 = &_6->first_field;
- return _7, __bounds_of_p_2(D);
- }
-
-
- d) Pointer is the result of pointer arithmetic or type cast.
-
- In this case bounds of the base pointer are used. In case of binary
- operation producing a pointer we are analyzing data flow further
- looking for operand's bounds. One operand is considered as a base
- if it has some valid bounds. If we fall into a case when none of
- operands (or both of them) has valid bounds, a default bounds value
- is used.
-
- Trying to find out bounds for binary operations we may fall into
- cyclic dependencies for pointers. To avoid infinite recursion all
- walked phi nodes instantly obtain corresponding bounds but created
- bounds are marked as incomplete. It helps us to stop DF walk during
- bounds search.
-
- When we reach pointer source, some args of incomplete bounds phi obtain
- valid bounds and those values are propagated further through phi nodes.
- If no valid bounds were found for phi node then we mark its result as
- invalid bounds. Process stops when all incomplete bounds become either
- valid or invalid and we are able to choose a pointer base.
-
- e) Pointer is loaded from the memory.
-
- In this case we just need to load bounds from the bounds table.
-
- Example:
-
- foo ()
- {
- <unnamed type> __bound_tmp.3;
- static int * buf;
- int * _2;
-
- <bb 2>:
- _2 = buf;
- __bound_tmp.3_4 = __builtin___chkp_bndldx (&buf, _2);
- return _2, __bound_tmp.3_4;
- }
-
-*/
-
-typedef void (*assign_handler)(tree, tree, void *);
-
-static tree chkp_get_zero_bounds ();
-static tree chkp_find_bounds (tree ptr, gimple_stmt_iterator *iter);
-static tree chkp_find_bounds_loaded (tree ptr, tree ptr_src,
- gimple_stmt_iterator *iter);
-static void chkp_parse_array_and_component_ref (tree node, tree *ptr,
- tree *elt, bool *safe,
- bool *bitfield,
- tree *bounds,
- gimple_stmt_iterator *iter,
- bool innermost_bounds);
-static void chkp_parse_bit_field_ref (tree node, location_t loc,
- tree *offset, tree *size);
-static tree
-chkp_make_addressed_object_bounds (tree obj, gimple_stmt_iterator *iter);
-
-#define chkp_bndldx_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDLDX))
-#define chkp_bndstx_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDSTX))
-#define chkp_checkl_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDCL))
-#define chkp_checku_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDCU))
-#define chkp_bndmk_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDMK))
-#define chkp_ret_bnd_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_BNDRET))
-#define chkp_intersect_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_INTERSECT))
-#define chkp_narrow_bounds_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_NARROW))
-#define chkp_sizeof_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_SIZEOF))
-#define chkp_extract_lower_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_EXTRACT_LOWER))
-#define chkp_extract_upper_fndecl \
- (targetm.builtin_chkp_function (BUILT_IN_CHKP_EXTRACT_UPPER))
-
-static GTY (()) tree chkp_uintptr_type;
-
-static GTY (()) tree chkp_zero_bounds_var;
-static GTY (()) tree chkp_none_bounds_var;
-
-static GTY (()) basic_block entry_block;
-static GTY (()) tree zero_bounds;
-static GTY (()) tree none_bounds;
-static GTY (()) tree incomplete_bounds;
-static GTY (()) tree tmp_var;
-static GTY (()) tree size_tmp_var;
-static GTY (()) bitmap chkp_abnormal_copies;
-
-struct hash_set<tree> *chkp_invalid_bounds;
-struct hash_set<tree> *chkp_completed_bounds_set;
-struct hash_map<tree, tree> *chkp_reg_bounds;
-struct hash_map<tree, tree> *chkp_bound_vars;
-struct hash_map<tree, tree> *chkp_reg_addr_bounds;
-struct hash_map<tree, tree> *chkp_incomplete_bounds_map;
-struct hash_map<tree, tree> *chkp_bounds_map;
-struct hash_map<tree, tree> *chkp_static_var_bounds;
-
-static bool in_chkp_pass;
-
-#define CHKP_BOUND_TMP_NAME "__bound_tmp"
-#define CHKP_SIZE_TMP_NAME "__size_tmp"
-#define CHKP_BOUNDS_OF_SYMBOL_PREFIX "__chkp_bounds_of_"
-#define CHKP_STRING_BOUNDS_PREFIX "__chkp_string_bounds_"
-#define CHKP_VAR_BOUNDS_PREFIX "__chkp_var_bounds_"
-#define CHKP_ZERO_BOUNDS_VAR_NAME "__chkp_zero_bounds"
-#define CHKP_NONE_BOUNDS_VAR_NAME "__chkp_none_bounds"
-
-/* Static checker constructors may become very large and their
- compilation with optimization may take too much time.
- Therefore we put a limit to number of statements in one
- constructor. Tests with 100 000 statically initialized
- pointers showed following compilation times on Sandy Bridge
- server (used -O2):
- limit 100 => ~18 sec.
- limit 300 => ~22 sec.
- limit 1000 => ~30 sec.
- limit 3000 => ~49 sec.
- limit 5000 => ~55 sec.
- limit 10000 => ~76 sec.
- limit 100000 => ~532 sec. */
-#define MAX_STMTS_IN_STATIC_CHKP_CTOR (PARAM_VALUE (PARAM_CHKP_MAX_CTOR_SIZE))
-
-struct chkp_ctor_stmt_list
-{
- tree stmts;
- int avail;
-};
-
-/* Return 1 if function FNDECL is instrumented by Pointer
- Bounds Checker. */
-bool
-chkp_function_instrumented_p (tree fndecl)
-{
- return fndecl
- && lookup_attribute ("chkp instrumented", DECL_ATTRIBUTES (fndecl));
-}
-
-/* Mark function FNDECL as instrumented. */
-void
-chkp_function_mark_instrumented (tree fndecl)
-{
- if (chkp_function_instrumented_p (fndecl))
- return;
-
- DECL_ATTRIBUTES (fndecl)
- = tree_cons (get_identifier ("chkp instrumented"), NULL,
- DECL_ATTRIBUTES (fndecl));
-}
-
-/* Return true when STMT is builtin call to instrumentation function
- corresponding to CODE. */
-
-bool
-chkp_gimple_call_builtin_p (gimple *call,
- enum built_in_function code)
-{
- tree fndecl;
- /* We are skipping the check for address-spaces, that's
- why we don't use gimple_call_builtin_p directly here. */
- if (is_gimple_call (call)
- && (fndecl = gimple_call_fndecl (call)) != NULL
- && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD
- && (fndecl = targetm.builtin_chkp_function (code))
- && (DECL_FUNCTION_CODE (gimple_call_fndecl (call))
- == DECL_FUNCTION_CODE (fndecl)))
- return true;
- return false;
-}
-
-/* Emit code to build zero bounds and return RTL holding
- the result. */
-rtx
-chkp_expand_zero_bounds ()
-{
- tree zero_bnd;
-
- if (flag_chkp_use_static_const_bounds)
- zero_bnd = chkp_get_zero_bounds_var ();
- else
- zero_bnd = chkp_build_make_bounds_call (integer_zero_node,
- integer_zero_node);
- return expand_normal (zero_bnd);
-}
-
-/* Emit code to store zero bounds for PTR located at MEM. */
-void
-chkp_expand_bounds_reset_for_mem (tree mem, tree ptr)
-{
- tree zero_bnd, bnd, addr, bndstx;
-
- if (flag_chkp_use_static_const_bounds)
- zero_bnd = chkp_get_zero_bounds_var ();
- else
- zero_bnd = chkp_build_make_bounds_call (integer_zero_node,
- integer_zero_node);
- bnd = make_tree (pointer_bounds_type_node,
- assign_temp (pointer_bounds_type_node, 0, 1));
- addr = build1 (ADDR_EXPR,
- build_pointer_type (TREE_TYPE (mem)), mem);
- bndstx = chkp_build_bndstx_call (addr, ptr, bnd);
-
- expand_assignment (bnd, zero_bnd, false);
- expand_normal (bndstx);
-}
-
-/* Build retbnd call for returned value RETVAL.
-
- If BNDVAL is not NULL then result is stored
- in it. Otherwise a temporary is created to
- hold returned value.
-
- GSI points to a position for a retbnd call
- and is set to created stmt.
-
- Cgraph edge is created for a new call if
- UPDATE_EDGE is 1.
-
- Obtained bounds are returned. */
-tree
-chkp_insert_retbnd_call (tree bndval, tree retval,
- gimple_stmt_iterator *gsi)
-{
- gimple *call;
-
- if (!bndval)
- bndval = create_tmp_reg (pointer_bounds_type_node, "retbnd");
-
- call = gimple_build_call (chkp_ret_bnd_fndecl, 1, retval);
- gimple_call_set_lhs (call, bndval);
- gsi_insert_after (gsi, call, GSI_CONTINUE_LINKING);
-
- return bndval;
-}
-
-/* Build a GIMPLE_CALL identical to CALL but skipping bounds
- arguments. */
-
-gcall *
-chkp_copy_call_skip_bounds (gcall *call)
-{
- bitmap bounds;
- unsigned i;
-
- bitmap_obstack_initialize (NULL);
- bounds = BITMAP_ALLOC (NULL);
-
- for (i = 0; i < gimple_call_num_args (call); i++)
- if (POINTER_BOUNDS_P (gimple_call_arg (call, i)))
- bitmap_set_bit (bounds, i);
-
- if (!bitmap_empty_p (bounds))
- call = gimple_call_copy_skip_args (call, bounds);
- gimple_call_set_with_bounds (call, false);
-
- BITMAP_FREE (bounds);
- bitmap_obstack_release (NULL);
-
- return call;
-}
-
-/* Redirect edge E to the correct node according to call_stmt.
- Return 1 if bounds removal from call_stmt should be done
- instead of redirection. */
-
-bool
-chkp_redirect_edge (cgraph_edge *e)
-{
- bool instrumented = false;
- tree decl = e->callee->decl;
-
- if (e->callee->instrumentation_clone
- || chkp_function_instrumented_p (decl))
- instrumented = true;
-
- if (instrumented
- && !gimple_call_with_bounds_p (e->call_stmt))
- e->redirect_callee (cgraph_node::get_create (e->callee->orig_decl));
- else if (!instrumented
- && gimple_call_with_bounds_p (e->call_stmt)
- && !chkp_gimple_call_builtin_p (e->call_stmt, BUILT_IN_CHKP_BNDCL)
- && !chkp_gimple_call_builtin_p (e->call_stmt, BUILT_IN_CHKP_BNDCU)
- && !chkp_gimple_call_builtin_p (e->call_stmt, BUILT_IN_CHKP_BNDSTX))
- {
- if (e->callee->instrumented_version)
- e->redirect_callee (e->callee->instrumented_version);
- else
- {
- tree args = TYPE_ARG_TYPES (TREE_TYPE (decl));
- /* Avoid bounds removal if all args will be removed. */
- if (!args || TREE_VALUE (args) != void_type_node)
- return true;
- else
- gimple_call_set_with_bounds (e->call_stmt, false);
- }
- }
-
- return false;
-}
-
-/* Mark statement S to not be instrumented. */
-static void
-chkp_mark_stmt (gimple *s)
-{
- gimple_set_plf (s, GF_PLF_1, true);
-}
-
-/* Mark statement S to be instrumented. */
-static void
-chkp_unmark_stmt (gimple *s)
-{
- gimple_set_plf (s, GF_PLF_1, false);
-}
-
-/* Return 1 if statement S should not be instrumented. */
-static bool
-chkp_marked_stmt_p (gimple *s)
-{
- return gimple_plf (s, GF_PLF_1);
-}
-
-/* Get var to be used for bound temps. */
-static tree
-chkp_get_tmp_var (void)
-{
- if (!tmp_var)
- tmp_var = create_tmp_reg (pointer_bounds_type_node, CHKP_BOUND_TMP_NAME);
-
- return tmp_var;
-}
-
-/* Get SSA_NAME to be used as temp. */
-static tree
-chkp_get_tmp_reg (gimple *stmt)
-{
- if (in_chkp_pass)
- return make_ssa_name (chkp_get_tmp_var (), stmt);
-
- return make_temp_ssa_name (pointer_bounds_type_node, stmt,
- CHKP_BOUND_TMP_NAME);
-}
-
-/* Get var to be used for size temps. */
-static tree
-chkp_get_size_tmp_var (void)
-{
- if (!size_tmp_var)
- size_tmp_var = create_tmp_reg (chkp_uintptr_type, CHKP_SIZE_TMP_NAME);
-
- return size_tmp_var;
-}
-
-/* Register bounds BND for address of OBJ. */
-static void
-chkp_register_addr_bounds (tree obj, tree bnd)
-{
- if (bnd == incomplete_bounds)
- return;
-
- chkp_reg_addr_bounds->put (obj, bnd);
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Regsitered bound ");
- print_generic_expr (dump_file, bnd);
- fprintf (dump_file, " for address of ");
- print_generic_expr (dump_file, obj);
- fprintf (dump_file, "\n");
- }
-}
-
-/* Return bounds registered for address of OBJ. */
-static tree
-chkp_get_registered_addr_bounds (tree obj)
-{
- tree *slot = chkp_reg_addr_bounds->get (obj);
- return slot ? *slot : NULL_TREE;
-}
-
-/* Mark BOUNDS as completed. */
-static void
-chkp_mark_completed_bounds (tree bounds)
-{
- chkp_completed_bounds_set->add (bounds);
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Marked bounds ");
- print_generic_expr (dump_file, bounds);
- fprintf (dump_file, " as completed\n");
- }
-}
-
-/* Return 1 if BOUNDS were marked as completed and 0 otherwise. */
-static bool
-chkp_completed_bounds (tree bounds)
-{
- return chkp_completed_bounds_set->contains (bounds);
-}
-
-/* Clear comleted bound marks. */
-static void
-chkp_erase_completed_bounds (void)
-{
- delete chkp_completed_bounds_set;
- chkp_completed_bounds_set = new hash_set<tree>;
-}
-
-/* This function is used to provide a base address for
- chkp_get_hard_register_fake_addr_expr. */
-static tree
-chkp_get_hard_register_var_fake_base_address ()
-{
- int prec = TYPE_PRECISION (ptr_type_node);
- return wide_int_to_tree (ptr_type_node, wi::min_value (prec, SIGNED));
-}
-
-/* If we check bounds for a hard register variable, we cannot
- use its address - it is illegal, so instead of that we use
- this fake value. */
-static tree
-chkp_get_hard_register_fake_addr_expr (tree obj)
-{
- tree addr = chkp_get_hard_register_var_fake_base_address ();
- tree outer = obj;
- while (TREE_CODE (outer) == COMPONENT_REF || TREE_CODE (outer) == ARRAY_REF)
- {
- if (TREE_CODE (outer) == COMPONENT_REF)
- {
- addr = fold_build_pointer_plus (addr,
- component_ref_field_offset (outer));
- outer = TREE_OPERAND (outer, 0);
- }
- else if (TREE_CODE (outer) == ARRAY_REF)
- {
- tree indx = fold_convert(size_type_node, TREE_OPERAND(outer, 1));
- tree offset = size_binop (MULT_EXPR,
- array_ref_element_size (outer), indx);
- addr = fold_build_pointer_plus (addr, offset);
- outer = TREE_OPERAND (outer, 0);
- }
- }
-
- return addr;
-}
-
-/* Mark BOUNDS associated with PTR as incomplete. */
-static void
-chkp_register_incomplete_bounds (tree bounds, tree ptr)
-{
- chkp_incomplete_bounds_map->put (bounds, ptr);
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Regsitered incomplete bounds ");
- print_generic_expr (dump_file, bounds);
- fprintf (dump_file, " for ");
- print_generic_expr (dump_file, ptr);
- fprintf (dump_file, "\n");
- }
-}
-
-/* Return 1 if BOUNDS are incomplete and 0 otherwise. */
-static bool
-chkp_incomplete_bounds (tree bounds)
-{
- if (bounds == incomplete_bounds)
- return true;
-
- if (chkp_completed_bounds (bounds))
- return false;
-
- return chkp_incomplete_bounds_map->get (bounds) != NULL;
-}
-
-/* Clear incomleted bound marks. */
-static void
-chkp_erase_incomplete_bounds (void)
-{
- delete chkp_incomplete_bounds_map;
- chkp_incomplete_bounds_map = new hash_map<tree, tree>;
-}
-
-/* Build and return bndmk call which creates bounds for structure
- pointed by PTR. Structure should have complete type. */
-tree
-chkp_make_bounds_for_struct_addr (tree ptr)
-{
- tree type = TREE_TYPE (ptr);
- tree size;
-
- gcc_assert (POINTER_TYPE_P (type));
-
- size = TYPE_SIZE (TREE_TYPE (type));
-
- gcc_assert (size);
-
- return build_call_nary (pointer_bounds_type_node,
- build_fold_addr_expr (chkp_bndmk_fndecl),
- 2, ptr, size);
-}
-
-/* Traversal function for chkp_may_finish_incomplete_bounds.
- Set RES to 0 if at least one argument of phi statement
- defining bounds (passed in KEY arg) is unknown.
- Traversal stops when first unknown phi argument is found. */
-bool
-chkp_may_complete_phi_bounds (tree const &bounds, tree *slot ATTRIBUTE_UNUSED,
- bool *res)
-{
- gimple *phi;
- unsigned i;
-
- gcc_assert (TREE_CODE (bounds) == SSA_NAME);
-
- phi = SSA_NAME_DEF_STMT (bounds);
-
- gcc_assert (phi && gimple_code (phi) == GIMPLE_PHI);
-
- for (i = 0; i < gimple_phi_num_args (phi); i++)
- {
- tree phi_arg = gimple_phi_arg_def (phi, i);
- if (!phi_arg)
- {
- *res = false;
- /* Do not need to traverse further. */
- return false;
- }
- }
-
- return true;
-}
-
-/* Return 1 if all phi nodes created for bounds have their
- arguments computed. */
-static bool
-chkp_may_finish_incomplete_bounds (void)
-{
- bool res = true;
-
- chkp_incomplete_bounds_map
- ->traverse<bool *, chkp_may_complete_phi_bounds> (&res);
-
- return res;
-}
-
-/* Helper function for chkp_finish_incomplete_bounds.
- Recompute args for bounds phi node. */
-bool
-chkp_recompute_phi_bounds (tree const &bounds, tree *slot,
- void *res ATTRIBUTE_UNUSED)
-{
- tree ptr = *slot;
- gphi *bounds_phi;
- gphi *ptr_phi;
- unsigned i;
-
- gcc_assert (TREE_CODE (bounds) == SSA_NAME);
- gcc_assert (TREE_CODE (ptr) == SSA_NAME);
-
- bounds_phi = as_a <gphi *> (SSA_NAME_DEF_STMT (bounds));
- ptr_phi = as_a <gphi *> (SSA_NAME_DEF_STMT (ptr));
-
- for (i = 0; i < gimple_phi_num_args (bounds_phi); i++)
- {
- tree ptr_arg = gimple_phi_arg_def (ptr_phi, i);
- tree bound_arg = chkp_find_bounds (ptr_arg, NULL);
-
- add_phi_arg (bounds_phi, bound_arg,
- gimple_phi_arg_edge (ptr_phi, i),
- UNKNOWN_LOCATION);
- }
-
- return true;
-}
-
-/* Mark BOUNDS as invalid. */
-static void
-chkp_mark_invalid_bounds (tree bounds)
-{
- chkp_invalid_bounds->add (bounds);
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Marked bounds ");
- print_generic_expr (dump_file, bounds);
- fprintf (dump_file, " as invalid\n");
- }
-}
-
-/* Return 1 if BOUNDS were marked as invalid and 0 otherwise. */
-static bool
-chkp_valid_bounds (tree bounds)
-{
- if (bounds == zero_bounds || bounds == none_bounds)
- return false;
-
- return !chkp_invalid_bounds->contains (bounds);
-}
-
-/* Helper function for chkp_finish_incomplete_bounds.
- Check all arguments of phi nodes trying to find
- valid completed bounds. If there is at least one
- such arg then bounds produced by phi node are marked
- as valid completed bounds and all phi args are
- recomputed. */
-bool
-chkp_find_valid_phi_bounds (tree const &bounds, tree *slot, bool *res)
-{
- gimple *phi;
- unsigned i;
-
- gcc_assert (TREE_CODE (bounds) == SSA_NAME);
-
- if (chkp_completed_bounds (bounds))
- return true;
-
- phi = SSA_NAME_DEF_STMT (bounds);
-
- gcc_assert (phi && gimple_code (phi) == GIMPLE_PHI);
-
- for (i = 0; i < gimple_phi_num_args (phi); i++)
- {
- tree phi_arg = gimple_phi_arg_def (phi, i);
-
- gcc_assert (phi_arg);
-
- if (chkp_valid_bounds (phi_arg) && !chkp_incomplete_bounds (phi_arg))
- {
- *res = true;
- chkp_mark_completed_bounds (bounds);
- chkp_recompute_phi_bounds (bounds, slot, NULL);
- return true;
- }
- }
-
- return true;
-}
-
-/* Helper function for chkp_finish_incomplete_bounds.
- Marks all incompleted bounds as invalid. */
-bool
-chkp_mark_invalid_bounds_walker (tree const &bounds,
- tree *slot ATTRIBUTE_UNUSED,
- void *res ATTRIBUTE_UNUSED)
-{
- if (!chkp_completed_bounds (bounds))
- {
- chkp_mark_invalid_bounds (bounds);
- chkp_mark_completed_bounds (bounds);
- }
- return true;
-}
-
-/* When all bound phi nodes have all their args computed
- we have enough info to find valid bounds. We iterate
- through all incompleted bounds searching for valid
- bounds. Found valid bounds are marked as completed
- and all remaining incompleted bounds are recomputed.
- Process continues until no new valid bounds may be
- found. All remained incompleted bounds are marked as
- invalid (i.e. have no valid source of bounds). */
-static void
-chkp_finish_incomplete_bounds (void)
-{
- bool found_valid = true;
-
- while (found_valid)
- {
- found_valid = false;
-
- chkp_incomplete_bounds_map->
- traverse<bool *, chkp_find_valid_phi_bounds> (&found_valid);
-
- if (found_valid)
- chkp_incomplete_bounds_map->
- traverse<void *, chkp_recompute_phi_bounds> (NULL);
- }
-
- chkp_incomplete_bounds_map->
- traverse<void *, chkp_mark_invalid_bounds_walker> (NULL);
- chkp_incomplete_bounds_map->
- traverse<void *, chkp_recompute_phi_bounds> (NULL);
-
- chkp_erase_completed_bounds ();
- chkp_erase_incomplete_bounds ();
-}
-
-/* Return 1 if type TYPE is a pointer type or a
- structure having a pointer type as one of its fields.
- Otherwise return 0. */
-bool
-chkp_type_has_pointer (const_tree type)
-{
- bool res = false;
-
- if (BOUNDED_TYPE_P (type))
- res = true;
- else if (RECORD_OR_UNION_TYPE_P (type))
- {
- tree field;
-
- for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
- if (TREE_CODE (field) == FIELD_DECL)
- res = res || chkp_type_has_pointer (TREE_TYPE (field));
- }
- else if (TREE_CODE (type) == ARRAY_TYPE)
- res = chkp_type_has_pointer (TREE_TYPE (type));
-
- return res;
-}
-
-unsigned
-chkp_type_bounds_count (const_tree type)
-{
- unsigned res = 0;
-
- if (!type)
- res = 0;
- else if (BOUNDED_TYPE_P (type))
- res = 1;
- else if (RECORD_OR_UNION_TYPE_P (type))
- {
- bitmap have_bound;
-
- bitmap_obstack_initialize (NULL);
- have_bound = BITMAP_ALLOC (NULL);
- chkp_find_bound_slots (type, have_bound);
- res = bitmap_count_bits (have_bound);
- BITMAP_FREE (have_bound);
- bitmap_obstack_release (NULL);
- }
-
- return res;
-}
-
-/* Get bounds associated with NODE via
- chkp_set_bounds call. */
-tree
-chkp_get_bounds (tree node)
-{
- tree *slot;
-
- if (!chkp_bounds_map)
- return NULL_TREE;
-
- slot = chkp_bounds_map->get (node);
- return slot ? *slot : NULL_TREE;
-}
-
-/* Associate bounds VAL with NODE. */
-void
-chkp_set_bounds (tree node, tree val)
-{
- if (!chkp_bounds_map)
- chkp_bounds_map = new hash_map<tree, tree>;
-
- chkp_bounds_map->put (node, val);
-}
-
-/* Check if statically initialized variable VAR require
- static bounds initialization. If VAR is added into
- bounds initlization list then 1 is returned. Otherwise
- return 0. */
-extern bool
-chkp_register_var_initializer (tree var)
-{
- if (!flag_check_pointer_bounds
- || DECL_INITIAL (var) == error_mark_node)
- return false;
-
- gcc_assert (VAR_P (var));
- gcc_assert (DECL_INITIAL (var));
-
- if (TREE_STATIC (var)
- && chkp_type_has_pointer (TREE_TYPE (var)))
- {
- varpool_node::get_create (var)->need_bounds_init = 1;
- return true;
- }
-
- return false;
-}
-
-/* Helper function for chkp_finish_file.
-
- Add new modification statement (RHS is assigned to LHS)
- into list of static initializer statementes (passed in ARG).
- If statements list becomes too big, emit checker constructor
- and start the new one. */
-static void
-chkp_add_modification_to_stmt_list (tree lhs,
- tree rhs,
- void *arg)
-{
- struct chkp_ctor_stmt_list *stmts = (struct chkp_ctor_stmt_list *)arg;
- tree modify;
-
- if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (rhs)))
- rhs = build1 (CONVERT_EXPR, TREE_TYPE (lhs), rhs);
-
- modify = build2 (MODIFY_EXPR, TREE_TYPE (lhs), lhs, rhs);
- append_to_statement_list (modify, &stmts->stmts);
-
- stmts->avail--;
-}
-
-/* Build and return ADDR_EXPR for specified object OBJ. */
-static tree
-chkp_build_addr_expr (tree obj)
-{
- /* We first check whether it is a "hard reg case". */
- tree base = get_base_address (obj);
- if (VAR_P (base) && DECL_HARD_REGISTER (base))
- return chkp_get_hard_register_fake_addr_expr (obj);
-
- /* If not - return regular ADDR_EXPR. */
- return TREE_CODE (obj) == TARGET_MEM_REF
- ? tree_mem_ref_addr (ptr_type_node, obj)
- : build_fold_addr_expr (obj);
-}
-
-/* Helper function for chkp_finish_file.
- Initialize bound variable BND_VAR with bounds of variable
- VAR to statements list STMTS. If statements list becomes
- too big, emit checker constructor and start the new one. */
-static void
-chkp_output_static_bounds (tree bnd_var, tree var,
- struct chkp_ctor_stmt_list *stmts)
-{
- tree lb, ub, size;
-
- if (TREE_CODE (var) == STRING_CST)
- {
- lb = build1 (CONVERT_EXPR, size_type_node, chkp_build_addr_expr (var));
- size = build_int_cst (size_type_node, TREE_STRING_LENGTH (var) - 1);
- }
- else if (DECL_SIZE (var)
- && !chkp_variable_size_type (TREE_TYPE (var)))
- {
- /* Compute bounds using statically known size. */
- lb = build1 (CONVERT_EXPR, size_type_node, chkp_build_addr_expr (var));
- size = size_binop (MINUS_EXPR, DECL_SIZE_UNIT (var), size_one_node);
- }
- else
- {
- /* Compute bounds using dynamic size. */
- tree call;
-
- lb = build1 (CONVERT_EXPR, size_type_node, chkp_build_addr_expr (var));
- call = build1 (ADDR_EXPR,
- build_pointer_type (TREE_TYPE (chkp_sizeof_fndecl)),
- chkp_sizeof_fndecl);
- size = build_call_nary (TREE_TYPE (TREE_TYPE (chkp_sizeof_fndecl)),
- call, 1, var);
-
- if (flag_chkp_zero_dynamic_size_as_infinite)
- {
- tree max_size, cond;
-
- max_size = build2 (MINUS_EXPR, size_type_node, size_zero_node, lb);
- cond = build2 (NE_EXPR, boolean_type_node, size, size_zero_node);
- size = build3 (COND_EXPR, size_type_node, cond, size, max_size);
- }
-
- size = size_binop (MINUS_EXPR, size, size_one_node);
- }
-
- ub = size_binop (PLUS_EXPR, lb, size);
- stmts->avail -= targetm.chkp_initialize_bounds (bnd_var, lb, ub,
- &stmts->stmts);
- if (stmts->avail <= 0)
- {
- cgraph_build_static_cdtor ('B', stmts->stmts,
- MAX_RESERVED_INIT_PRIORITY + 2);
- stmts->avail = MAX_STMTS_IN_STATIC_CHKP_CTOR;
- stmts->stmts = NULL;
- }
-}
-
-/* Return entry block to be used for checker initilization code.
- Create new block if required. */
-static basic_block
-chkp_get_entry_block (void)
-{
- if (!entry_block)
- entry_block
- = split_block_after_labels (ENTRY_BLOCK_PTR_FOR_FN (cfun))->dest;
-
- return entry_block;
-}
-
-/* Return a bounds var to be used for pointer var PTR_VAR. */
-static tree
-chkp_get_bounds_var (tree ptr_var)
-{
- tree bnd_var;
- tree *slot;
-
- slot = chkp_bound_vars->get (ptr_var);
- if (slot)
- bnd_var = *slot;
- else
- {
- bnd_var = create_tmp_reg (pointer_bounds_type_node,
- CHKP_BOUND_TMP_NAME);
- chkp_bound_vars->put (ptr_var, bnd_var);
- }
-
- return bnd_var;
-}
-
-/* If BND is an abnormal bounds copy, return a copied value.
- Otherwise return BND. */
-static tree
-chkp_get_orginal_bounds_for_abnormal_copy (tree bnd)
-{
- if (bitmap_bit_p (chkp_abnormal_copies, SSA_NAME_VERSION (bnd)))
- {
- gimple *bnd_def = SSA_NAME_DEF_STMT (bnd);
- gcc_checking_assert (gimple_code (bnd_def) == GIMPLE_ASSIGN);
- bnd = gimple_assign_rhs1 (bnd_def);
- }
-
- return bnd;
-}
-
-/* Register bounds BND for object PTR in global bounds table.
- A copy of bounds may be created for abnormal ssa names.
- Returns bounds to use for PTR. */
-static tree
-chkp_maybe_copy_and_register_bounds (tree ptr, tree bnd)
-{
- bool abnormal_ptr;
-
- if (!chkp_reg_bounds)
- return bnd;
-
- /* Do nothing if bounds are incomplete_bounds
- because it means bounds will be recomputed. */
- if (bnd == incomplete_bounds)
- return bnd;
-
- abnormal_ptr = (TREE_CODE (ptr) == SSA_NAME
- && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ptr)
- && gimple_code (SSA_NAME_DEF_STMT (ptr)) != GIMPLE_PHI);
-
- /* A single bounds value may be reused multiple times for
- different pointer values. It may cause coalescing issues
- for abnormal SSA names. To avoid it we create a bounds
- copy in case it is computed for abnormal SSA name.
-
- We also cannot reuse such created copies for other pointers */
- if (abnormal_ptr
- || bitmap_bit_p (chkp_abnormal_copies, SSA_NAME_VERSION (bnd)))
- {
- tree bnd_var = NULL_TREE;
-
- if (abnormal_ptr)
- {
- if (SSA_NAME_VAR (ptr))
- bnd_var = chkp_get_bounds_var (SSA_NAME_VAR (ptr));
- }
- else
- bnd_var = chkp_get_tmp_var ();
-
- /* For abnormal copies we may just find original
- bounds and use them. */
- if (!abnormal_ptr && !SSA_NAME_IS_DEFAULT_DEF (bnd))
- bnd = chkp_get_orginal_bounds_for_abnormal_copy (bnd);
- /* For undefined values we usually use none bounds
- value but in case of abnormal edge it may cause
- coalescing failures. Use default definition of
- bounds variable instead to avoid it. */
- else if (SSA_NAME_IS_DEFAULT_DEF (ptr)
- && TREE_CODE (SSA_NAME_VAR (ptr)) != PARM_DECL)
- {
- bnd = get_or_create_ssa_default_def (cfun, bnd_var);
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Using default def bounds ");
- print_generic_expr (dump_file, bnd);
- fprintf (dump_file, " for abnormal default def SSA name ");
- print_generic_expr (dump_file, ptr);
- fprintf (dump_file, "\n");
- }
- }
- else
- {
- tree copy;
- gimple *def = SSA_NAME_DEF_STMT (ptr);
- gimple *assign;
- gimple_stmt_iterator gsi;
-
- if (bnd_var)
- copy = make_ssa_name (bnd_var);
- else
- copy = make_temp_ssa_name (pointer_bounds_type_node,
- NULL,
- CHKP_BOUND_TMP_NAME);
- bnd = chkp_get_orginal_bounds_for_abnormal_copy (bnd);
- assign = gimple_build_assign (copy, bnd);
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Creating a copy of bounds ");
- print_generic_expr (dump_file, bnd);
- fprintf (dump_file, " for abnormal SSA name ");
- print_generic_expr (dump_file, ptr);
- fprintf (dump_file, "\n");
- }
-
- if (gimple_code (def) == GIMPLE_NOP)
- {
- gsi = gsi_last_bb (chkp_get_entry_block ());
- if (!gsi_end_p (gsi) && is_ctrl_stmt (gsi_stmt (gsi)))
- gsi_insert_before (&gsi, assign, GSI_CONTINUE_LINKING);
- else
- gsi_insert_after (&gsi, assign, GSI_CONTINUE_LINKING);
- }
- else
- {
- gimple *bnd_def = SSA_NAME_DEF_STMT (bnd);
- /* Sometimes (e.g. when we load a pointer from a
- memory) bounds are produced later than a pointer.
- We need to insert bounds copy appropriately. */
- if (gimple_code (bnd_def) != GIMPLE_NOP
- && stmt_dominates_stmt_p (def, bnd_def))
- gsi = gsi_for_stmt (bnd_def);
- else
- gsi = gsi_for_stmt (def);
- gsi_insert_after (&gsi, assign, GSI_CONTINUE_LINKING);
- }
-
- bnd = copy;
- }
-
- if (abnormal_ptr)
- bitmap_set_bit (chkp_abnormal_copies, SSA_NAME_VERSION (bnd));
- }
-
- chkp_reg_bounds->put (ptr, bnd);
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Regsitered bound ");
- print_generic_expr (dump_file, bnd);
- fprintf (dump_file, " for pointer ");
- print_generic_expr (dump_file, ptr);
- fprintf (dump_file, "\n");
- }
-
- return bnd;
-}
-
-/* Get bounds registered for object PTR in global bounds table. */
-static tree
-chkp_get_registered_bounds (tree ptr)
-{
- tree *slot;
-
- if (!chkp_reg_bounds)
- return NULL_TREE;
-
- slot = chkp_reg_bounds->get (ptr);
- return slot ? *slot : NULL_TREE;
-}
-
-/* Add bound retvals to return statement pointed by GSI. */
-
-static void
-chkp_add_bounds_to_ret_stmt (gimple_stmt_iterator *gsi)
-{
- greturn *ret = as_a <greturn *> (gsi_stmt (*gsi));
- tree retval = gimple_return_retval (ret);
- tree ret_decl = DECL_RESULT (cfun->decl);
- tree bounds;
-
- if (!retval)
- return;
-
- if (BOUNDED_P (ret_decl))
- {
- bounds = chkp_find_bounds (retval, gsi);
- bounds = chkp_maybe_copy_and_register_bounds (ret_decl, bounds);
- gimple_return_set_retbnd (ret, bounds);
- }
-
- update_stmt (ret);
-}
-
-/* Force OP to be suitable for using as an argument for call.
- New statements (if any) go to SEQ. */
-static tree
-chkp_force_gimple_call_op (tree op, gimple_seq *seq)
-{
- gimple_seq stmts;
- gimple_stmt_iterator si;
-
- op = force_gimple_operand (unshare_expr (op), &stmts, true, NULL_TREE);
-
- for (si = gsi_start (stmts); !gsi_end_p (si); gsi_next (&si))
- chkp_mark_stmt (gsi_stmt (si));
-
- gimple_seq_add_seq (seq, stmts);
-
- return op;
-}
-
-/* Generate lower bound check for memory access by ADDR.
- Check is inserted before the position pointed by ITER.
- DIRFLAG indicates whether memory access is load or store. */
-static void
-chkp_check_lower (tree addr, tree bounds,
- gimple_stmt_iterator iter,
- location_t location,
- tree dirflag)
-{
- gimple_seq seq;
- gimple *check;
- tree node;
-
- if (!chkp_function_instrumented_p (current_function_decl)
- && bounds == chkp_get_zero_bounds ())
- return;
-
- if (dirflag == integer_zero_node
- && !flag_chkp_check_read)
- return;
-
- if (dirflag == integer_one_node
- && !flag_chkp_check_write)
- return;
-
- seq = NULL;
-
- node = chkp_force_gimple_call_op (addr, &seq);
-
- check = gimple_build_call (chkp_checkl_fndecl, 2, node, bounds);
- chkp_mark_stmt (check);
- gimple_call_set_with_bounds (check, true);
- gimple_set_location (check, location);
- gimple_seq_add_stmt (&seq, check);
-
- gsi_insert_seq_before (&iter, seq, GSI_SAME_STMT);
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- gimple *before = gsi_stmt (iter);
- fprintf (dump_file, "Generated lower bound check for statement ");
- print_gimple_stmt (dump_file, before, 0, TDF_VOPS|TDF_MEMSYMS);
- fprintf (dump_file, " ");
- print_gimple_stmt (dump_file, check, 0, TDF_VOPS|TDF_MEMSYMS);
- }
-}
-
-/* Generate upper bound check for memory access by ADDR.
- Check is inserted before the position pointed by ITER.
- DIRFLAG indicates whether memory access is load or store. */
-static void
-chkp_check_upper (tree addr, tree bounds,
- gimple_stmt_iterator iter,
- location_t location,
- tree dirflag)
-{
- gimple_seq seq;
- gimple *check;
- tree node;
-
- if (!chkp_function_instrumented_p (current_function_decl)
- && bounds == chkp_get_zero_bounds ())
- return;
-
- if (dirflag == integer_zero_node
- && !flag_chkp_check_read)
- return;
-
- if (dirflag == integer_one_node
- && !flag_chkp_check_write)
- return;
-
- seq = NULL;
-
- node = chkp_force_gimple_call_op (addr, &seq);
-
- check = gimple_build_call (chkp_checku_fndecl, 2, node, bounds);
- chkp_mark_stmt (check);
- gimple_call_set_with_bounds (check, true);
- gimple_set_location (check, location);
- gimple_seq_add_stmt (&seq, check);
-
- gsi_insert_seq_before (&iter, seq, GSI_SAME_STMT);
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- gimple *before = gsi_stmt (iter);
- fprintf (dump_file, "Generated upper bound check for statement ");
- print_gimple_stmt (dump_file, before, 0, TDF_VOPS|TDF_MEMSYMS);
- fprintf (dump_file, " ");
- print_gimple_stmt (dump_file, check, 0, TDF_VOPS|TDF_MEMSYMS);
- }
-}
-
-/* Generate lower and upper bound checks for memory access
- to memory slot [FIRST, LAST] againsr BOUNDS. Checks
- are inserted before the position pointed by ITER.
- DIRFLAG indicates whether memory access is load or store. */
-void
-chkp_check_mem_access (tree first, tree last, tree bounds,
- gimple_stmt_iterator iter,
- location_t location,
- tree dirflag)
-{
- chkp_check_lower (first, bounds, iter, location, dirflag);
- chkp_check_upper (last, bounds, iter, location, dirflag);
-}
-
-/* Replace call to _bnd_chk_* pointed by GSI with
- bndcu and bndcl calls. DIRFLAG determines whether
- check is for read or write. */
-
-void
-chkp_replace_address_check_builtin (gimple_stmt_iterator *gsi,
- tree dirflag)
-{
- gimple_stmt_iterator call_iter = *gsi;
- gimple *call = gsi_stmt (*gsi);
- tree fndecl = gimple_call_fndecl (call);
- tree addr = gimple_call_arg (call, 0);
- tree bounds = chkp_find_bounds (addr, gsi);
-
- if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_LBOUNDS
- || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_BOUNDS)
- chkp_check_lower (addr, bounds, *gsi, gimple_location (call), dirflag);
-
- if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_UBOUNDS)
- chkp_check_upper (addr, bounds, *gsi, gimple_location (call), dirflag);
-
- if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_BOUNDS)
- {
- tree size = gimple_call_arg (call, 1);
- addr = fold_build_pointer_plus (addr, size);
- addr = fold_build_pointer_plus_hwi (addr, -1);
- chkp_check_upper (addr, bounds, *gsi, gimple_location (call), dirflag);
- }
-
- gsi_remove (&call_iter, true);
-}
-
-/* Replace call to _bnd_get_ptr_* pointed by GSI with
- corresponding bounds extract call. */
-
-void
-chkp_replace_extract_builtin (gimple_stmt_iterator *gsi)
-{
- gimple *call = gsi_stmt (*gsi);
- tree fndecl = gimple_call_fndecl (call);
- tree addr = gimple_call_arg (call, 0);
- tree bounds = chkp_find_bounds (addr, gsi);
- gimple *extract;
-
- if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_GET_PTR_LBOUND)
- fndecl = chkp_extract_lower_fndecl;
- else if (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_GET_PTR_UBOUND)
- fndecl = chkp_extract_upper_fndecl;
- else
- gcc_unreachable ();
-
- extract = gimple_build_call (fndecl, 1, bounds);
- gimple_call_set_lhs (extract, gimple_call_lhs (call));
- chkp_mark_stmt (extract);
-
- gsi_replace (gsi, extract, false);
-}
-
-/* Return COMPONENT_REF accessing FIELD in OBJ. */
-static tree
-chkp_build_component_ref (tree obj, tree field)
-{
- tree res;
-
- /* If object is TMR then we do not use component_ref but
- add offset instead. We need it to be able to get addr
- of the reasult later. */
- if (TREE_CODE (obj) == TARGET_MEM_REF)
- {
- tree offs = TMR_OFFSET (obj);
- offs = fold_binary_to_constant (PLUS_EXPR, TREE_TYPE (offs),
- offs, DECL_FIELD_OFFSET (field));
-
- gcc_assert (offs);
-
- res = copy_node (obj);
- TREE_TYPE (res) = TREE_TYPE (field);
- TMR_OFFSET (res) = offs;
- }
- else
- res = build3 (COMPONENT_REF, TREE_TYPE (field), obj, field, NULL_TREE);
-
- return res;
-}
-
-/* Return ARRAY_REF for array ARR and index IDX with
- specified element type ETYPE and element size ESIZE. */
-static tree
-chkp_build_array_ref (tree arr, tree etype, tree esize,
- unsigned HOST_WIDE_INT idx)
-{
- tree index = build_int_cst (size_type_node, idx);
- tree res;
-
- /* If object is TMR then we do not use array_ref but
- add offset instead. We need it to be able to get addr
- of the reasult later. */
- if (TREE_CODE (arr) == TARGET_MEM_REF)
- {
- tree offs = TMR_OFFSET (arr);
-
- esize = fold_binary_to_constant (MULT_EXPR, TREE_TYPE (esize),
- esize, index);
- gcc_assert(esize);
-
- offs = fold_binary_to_constant (PLUS_EXPR, TREE_TYPE (offs),
- offs, esize);
- gcc_assert (offs);
-
- res = copy_node (arr);
- TREE_TYPE (res) = etype;
- TMR_OFFSET (res) = offs;
- }
- else
- res = build4 (ARRAY_REF, etype, arr, index, NULL_TREE, NULL_TREE);
-
- return res;
-}
-
-/* Helper function for chkp_add_bounds_to_call_stmt.
- Fill ALL_BOUNDS output array with created bounds.
-
- OFFS is used for recursive calls and holds basic
- offset of TYPE in outer structure in bits.
-
- ITER points a position where bounds are searched.
-
- ALL_BOUNDS[i] is filled with elem bounds if there
- is a field in TYPE which has pointer type and offset
- equal to i * POINTER_SIZE in bits. */
-static void
-chkp_find_bounds_for_elem (tree elem, tree *all_bounds,
- HOST_WIDE_INT offs,
- gimple_stmt_iterator *iter)
-{
- tree type = TREE_TYPE (elem);
-
- if (BOUNDED_TYPE_P (type))
- {
- if (!all_bounds[offs / POINTER_SIZE])
- {
- tree temp = make_temp_ssa_name (type, NULL, "");
- gimple *assign = gimple_build_assign (temp, elem);
- gimple_stmt_iterator gsi;
-
- gsi_insert_before (iter, assign, GSI_SAME_STMT);
- gsi = gsi_for_stmt (assign);
-
- all_bounds[offs / POINTER_SIZE] = chkp_find_bounds (temp, &gsi);
- }
- }
- else if (RECORD_OR_UNION_TYPE_P (type))
- {
- tree field;
-
- for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
- if (TREE_CODE (field) == FIELD_DECL)
- {
- tree base = unshare_expr (elem);
- tree field_ref = chkp_build_component_ref (base, field);
- HOST_WIDE_INT field_offs
- = TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (field));
- if (DECL_FIELD_OFFSET (field))
- field_offs += TREE_INT_CST_LOW (DECL_FIELD_OFFSET (field)) * 8;
-
- chkp_find_bounds_for_elem (field_ref, all_bounds,
- offs + field_offs, iter);
- }
- }
- else if (TREE_CODE (type) == ARRAY_TYPE)
- {
- tree maxval = TYPE_MAX_VALUE (TYPE_DOMAIN (type));
- tree etype = TREE_TYPE (type);
- HOST_WIDE_INT esize = TREE_INT_CST_LOW (TYPE_SIZE (etype));
- unsigned HOST_WIDE_INT cur;
-
- if (!maxval || integer_minus_onep (maxval))
- return;
-
- for (cur = 0; cur <= TREE_INT_CST_LOW (maxval); cur++)
- {
- tree base = unshare_expr (elem);
- tree arr_elem = chkp_build_array_ref (base, etype,
- TYPE_SIZE (etype),
- cur);
- chkp_find_bounds_for_elem (arr_elem, all_bounds, offs + cur * esize,
- iter);
- }
- }
-}
-
-/* Maximum number of elements to check in an array. */
-
-#define CHKP_ARRAY_MAX_CHECK_STEPS 4096
-
-/* Fill HAVE_BOUND output bitmap with information about
- bounds requred for object of type TYPE.
-
- OFFS is used for recursive calls and holds basic
- offset of TYPE in outer structure in bits.
-
- HAVE_BOUND[i] is set to 1 if there is a field
- in TYPE which has pointer type and offset
- equal to i * POINTER_SIZE - OFFS in bits. */
-void
-chkp_find_bound_slots_1 (const_tree type, bitmap have_bound,
- HOST_WIDE_INT offs)
-{
- if (BOUNDED_TYPE_P (type))
- bitmap_set_bit (have_bound, offs / POINTER_SIZE);
- else if (RECORD_OR_UNION_TYPE_P (type))
- {
- tree field;
-
- for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
- if (TREE_CODE (field) == FIELD_DECL)
- {
- HOST_WIDE_INT field_offs = 0;
- if (DECL_FIELD_BIT_OFFSET (field))
- field_offs += TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (field));
- if (DECL_FIELD_OFFSET (field))
- field_offs += TREE_INT_CST_LOW (DECL_FIELD_OFFSET (field)) * 8;
- chkp_find_bound_slots_1 (TREE_TYPE (field), have_bound,
- offs + field_offs);
- }
- }
- else if (TREE_CODE (type) == ARRAY_TYPE && TYPE_DOMAIN (type))
- {
- /* The object type is an array of complete type, i.e., other
- than a flexible array. */
- tree maxval = TYPE_MAX_VALUE (TYPE_DOMAIN (type));
- tree etype = TREE_TYPE (type);
- HOST_WIDE_INT esize = TREE_INT_CST_LOW (TYPE_SIZE (etype));
- unsigned HOST_WIDE_INT cur;
-
- if (!maxval
- || TREE_CODE (maxval) != INTEGER_CST
- || integer_minus_onep (maxval))
- return;
-
- for (cur = 0;
- cur <= MIN (CHKP_ARRAY_MAX_CHECK_STEPS, TREE_INT_CST_LOW (maxval));
- cur++)
- chkp_find_bound_slots_1 (etype, have_bound, offs + cur * esize);
- }
-}
-
-/* Fill bitmap RES with information about bounds for
- type TYPE. See chkp_find_bound_slots_1 for more
- details. */
-void
-chkp_find_bound_slots (const_tree type, bitmap res)
-{
- bitmap_clear (res);
- chkp_find_bound_slots_1 (type, res, 0);
-}
-
-/* Return 1 if call to FNDECL should be instrumented
- and 0 otherwise. */
-
-static bool
-chkp_instrument_normal_builtin (tree fndecl)
-{
- switch (DECL_FUNCTION_CODE (fndecl))
- {
- case BUILT_IN_STRLEN:
- case BUILT_IN_STRCPY:
- case BUILT_IN_STRNCPY:
- case BUILT_IN_STPCPY:
- case BUILT_IN_STPNCPY:
- case BUILT_IN_STRCAT:
- case BUILT_IN_STRNCAT:
- case BUILT_IN_MEMCPY:
- case BUILT_IN_MEMPCPY:
- case BUILT_IN_MEMSET:
- case BUILT_IN_MEMMOVE:
- case BUILT_IN_BZERO:
- case BUILT_IN_STRCMP:
- case BUILT_IN_STRNCMP:
- case BUILT_IN_BCMP:
- case BUILT_IN_MEMCMP:
- case BUILT_IN_MEMCPY_CHK:
- case BUILT_IN_MEMPCPY_CHK:
- case BUILT_IN_MEMMOVE_CHK:
- case BUILT_IN_MEMSET_CHK:
- case BUILT_IN_STRCPY_CHK:
- case BUILT_IN_STRNCPY_CHK:
- case BUILT_IN_STPCPY_CHK:
- case BUILT_IN_STPNCPY_CHK:
- case BUILT_IN_STRCAT_CHK:
- case BUILT_IN_STRNCAT_CHK:
- case BUILT_IN_MALLOC:
- case BUILT_IN_CALLOC:
- case BUILT_IN_REALLOC:
- return 1;
-
- default:
- return 0;
- }
-}
-
-/* Add bound arguments to call statement pointed by GSI.
- Also performs a replacement of user checker builtins calls
- with internal ones. */
-
-static void
-chkp_add_bounds_to_call_stmt (gimple_stmt_iterator *gsi)
-{
- gcall *call = as_a <gcall *> (gsi_stmt (*gsi));
- unsigned arg_no = 0;
- tree fndecl = gimple_call_fndecl (call);
- tree fntype;
- tree first_formal_arg;
- tree arg;
- bool use_fntype = false;
- tree op;
- ssa_op_iter iter;
- gcall *new_call;
-
- /* Do nothing for internal functions. */
- if (gimple_call_internal_p (call))
- return;
-
- fntype = TREE_TYPE (TREE_TYPE (gimple_call_fn (call)));
-
- /* Do nothing if back-end builtin is called. */
- if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
- return;
-
- /* Do nothing for some middle-end builtins. */
- if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_OBJECT_SIZE)
- return;
-
- /* Do nothing for calls to not instrumentable functions. */
- if (fndecl && !chkp_instrumentable_p (fndecl))
- return;
-
- /* Ignore CHKP_INIT_PTR_BOUNDS, CHKP_NULL_PTR_BOUNDS
- and CHKP_COPY_PTR_BOUNDS. */
- if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_INIT_PTR_BOUNDS
- || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_NULL_PTR_BOUNDS
- || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_COPY_PTR_BOUNDS
- || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_SET_PTR_BOUNDS))
- return;
-
- /* Check user builtins are replaced with checks. */
- if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_LBOUNDS
- || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_UBOUNDS
- || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_CHECK_PTR_BOUNDS))
- {
- chkp_replace_address_check_builtin (gsi, integer_minus_one_node);
- return;
- }
-
- /* Check user builtins are replaced with bound extract. */
- if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && (DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_GET_PTR_LBOUND
- || DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_GET_PTR_UBOUND))
- {
- chkp_replace_extract_builtin (gsi);
- return;
- }
-
- /* BUILT_IN_CHKP_NARROW_PTR_BOUNDS call is replaced with
- target narrow bounds call. */
- if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_NARROW_PTR_BOUNDS)
- {
- tree arg = gimple_call_arg (call, 1);
- tree bounds = chkp_find_bounds (arg, gsi);
-
- gimple_call_set_fndecl (call, chkp_narrow_bounds_fndecl);
- gimple_call_set_arg (call, 1, bounds);
- update_stmt (call);
-
- return;
- }
-
- /* BUILT_IN_CHKP_STORE_PTR_BOUNDS call is replaced with
- bndstx call. */
- if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_STORE_PTR_BOUNDS)
- {
- tree addr = gimple_call_arg (call, 0);
- tree ptr = gimple_call_arg (call, 1);
- tree bounds = chkp_find_bounds (ptr, gsi);
- gimple_stmt_iterator iter = gsi_for_stmt (call);
-
- chkp_build_bndstx (addr, ptr, bounds, gsi);
- gsi_remove (&iter, true);
-
- return;
- }
-
- if (!flag_chkp_instrument_calls)
- return;
-
- /* We instrument only some subset of builtins. We also instrument
- builtin calls to be inlined. */
- if (fndecl
- && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && !chkp_instrument_normal_builtin (fndecl))
- {
- if (!lookup_attribute ("always_inline", DECL_ATTRIBUTES (fndecl)))
- return;
-
- struct cgraph_node *clone = chkp_maybe_create_clone (fndecl);
- if (!clone
- || !gimple_has_body_p (clone->decl))
- return;
- }
-
- /* If function decl is available then use it for
- formal arguments list. Otherwise use function type. */
- if (fndecl
- && DECL_ARGUMENTS (fndecl)
- && gimple_call_fntype (call) == TREE_TYPE (fndecl))
- first_formal_arg = DECL_ARGUMENTS (fndecl);
- else
- {
- first_formal_arg = TYPE_ARG_TYPES (fntype);
- use_fntype = true;
- }
-
- /* Fill vector of new call args. */
- vec<tree> new_args = vNULL;
- new_args.create (gimple_call_num_args (call));
- arg = first_formal_arg;
- for (arg_no = 0; arg_no < gimple_call_num_args (call); arg_no++)
- {
- tree call_arg = gimple_call_arg (call, arg_no);
- tree type;
-
- /* Get arg type using formal argument description
- or actual argument type. */
- if (arg)
- if (use_fntype)
- if (TREE_VALUE (arg) != void_type_node)
- {
- type = TREE_VALUE (arg);
- arg = TREE_CHAIN (arg);
- }
- else
- type = TREE_TYPE (call_arg);
- else
- {
- type = TREE_TYPE (arg);
- arg = TREE_CHAIN (arg);
- }
- else
- type = TREE_TYPE (call_arg);
-
- new_args.safe_push (call_arg);
-
- if (BOUNDED_TYPE_P (type)
- || pass_by_reference (NULL, TYPE_MODE (type), type, true))
- new_args.safe_push (chkp_find_bounds (call_arg, gsi));
- else if (chkp_type_has_pointer (type))
- {
- HOST_WIDE_INT max_bounds
- = TREE_INT_CST_LOW (TYPE_SIZE (type)) / POINTER_SIZE;
- tree *all_bounds = (tree *)xmalloc (sizeof (tree) * max_bounds);
- HOST_WIDE_INT bnd_no;
-
- memset (all_bounds, 0, sizeof (tree) * max_bounds);
-
- chkp_find_bounds_for_elem (call_arg, all_bounds, 0, gsi);
-
- for (bnd_no = 0; bnd_no < max_bounds; bnd_no++)
- if (all_bounds[bnd_no])
- new_args.safe_push (all_bounds[bnd_no]);
-
- free (all_bounds);
- }
- }
-
- if (new_args.length () == gimple_call_num_args (call))
- new_call = call;
- else
- {
- new_call = gimple_build_call_vec (gimple_op (call, 1), new_args);
- gimple_call_set_lhs (new_call, gimple_call_lhs (call));
- gimple_call_copy_flags (new_call, call);
- gimple_call_set_chain (new_call, gimple_call_chain (call));
- }
- new_args.release ();
-
- /* For direct calls fndecl is replaced with instrumented version. */
- if (fndecl)
- {
- tree new_decl = chkp_maybe_create_clone (fndecl)->decl;
- gimple_call_set_fndecl (new_call, new_decl);
- /* In case of a type cast we should modify used function
- type instead of using type of new fndecl. */
- if (gimple_call_fntype (call) != TREE_TYPE (fndecl))
- {
- tree type = gimple_call_fntype (call);
- type = chkp_copy_function_type_adding_bounds (type);
- gimple_call_set_fntype (new_call, type);
- }
- else
- gimple_call_set_fntype (new_call, TREE_TYPE (new_decl));
- }
- /* For indirect call we should fix function pointer type if
- pass some bounds. */
- else if (new_call != call)
- {
- tree type = gimple_call_fntype (call);
- type = chkp_copy_function_type_adding_bounds (type);
- gimple_call_set_fntype (new_call, type);
- }
-
- /* replace old call statement with the new one. */
- if (call != new_call)
- {
- FOR_EACH_SSA_TREE_OPERAND (op, call, iter, SSA_OP_ALL_DEFS)
- {
- SSA_NAME_DEF_STMT (op) = new_call;
- }
- gsi_replace (gsi, new_call, true);
- }
- else
- update_stmt (new_call);
-
- gimple_call_set_with_bounds (new_call, true);
-}
-
-/* Return constant static bounds var with specified bounds LB and UB.
- If such var does not exists then new var is created with specified NAME. */
-static tree
-chkp_make_static_const_bounds (HOST_WIDE_INT lb,
- HOST_WIDE_INT ub,
- const char *name)
-{
- tree id = get_identifier (name);
- tree var;
- varpool_node *node;
- symtab_node *snode;
-
- var = build_decl (UNKNOWN_LOCATION, VAR_DECL, id,
- pointer_bounds_type_node);
- TREE_STATIC (var) = 1;
- TREE_PUBLIC (var) = 1;
-
- /* With LTO we may have constant bounds already in varpool.
- Try to find it. */
- if ((snode = symtab_node::get_for_asmname (DECL_ASSEMBLER_NAME (var))))
- {
- /* We don't allow this symbol usage for non bounds. */
- if (snode->type != SYMTAB_VARIABLE
- || !POINTER_BOUNDS_P (snode->decl))
- sorry ("-fcheck-pointer-bounds requires %qs "
- "name for internal usage",
- IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (var)));
-
- return snode->decl;
- }
-
- TREE_USED (var) = 1;
- TREE_READONLY (var) = 1;
- TREE_ADDRESSABLE (var) = 0;
- DECL_ARTIFICIAL (var) = 1;
- DECL_READ_P (var) = 1;
- DECL_INITIAL (var) = targetm.chkp_make_bounds_constant (lb, ub);
- make_decl_one_only (var, DECL_ASSEMBLER_NAME (var));
- /* We may use this symbol during ctors generation in chkp_finish_file
- when all symbols are emitted. Force output to avoid undefined
- symbols in ctors. */
- node = varpool_node::get_create (var);
- node->force_output = 1;
-
- varpool_node::finalize_decl (var);
-
- return var;
-}
-
-/* Generate code to make bounds with specified lower bound LB and SIZE.
- if AFTER is 1 then code is inserted after position pointed by ITER
- otherwise code is inserted before position pointed by ITER.
- If ITER is NULL then code is added to entry block. */
-static tree
-chkp_make_bounds (tree lb, tree size, gimple_stmt_iterator *iter, bool after)
-{
- gimple_seq seq;
- gimple_stmt_iterator gsi;
- gimple *stmt;
- tree bounds;
-
- if (iter)
- gsi = *iter;
- else
- gsi = gsi_start_bb (chkp_get_entry_block ());
-
- seq = NULL;
-
- lb = chkp_force_gimple_call_op (lb, &seq);
- size = chkp_force_gimple_call_op (size, &seq);
-
- stmt = gimple_build_call (chkp_bndmk_fndecl, 2, lb, size);
- chkp_mark_stmt (stmt);
-
- bounds = chkp_get_tmp_reg (stmt);
- gimple_call_set_lhs (stmt, bounds);
-
- gimple_seq_add_stmt (&seq, stmt);
-
- if (iter && after)
- gsi_insert_seq_after (&gsi, seq, GSI_SAME_STMT);
- else
- gsi_insert_seq_before (&gsi, seq, GSI_SAME_STMT);
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Made bounds: ");
- print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS|TDF_MEMSYMS);
- if (iter)
- {
- fprintf (dump_file, " inserted before statement: ");
- print_gimple_stmt (dump_file, gsi_stmt (*iter), 0, TDF_VOPS|TDF_MEMSYMS);
- }
- else
- fprintf (dump_file, " at function entry\n");
- }
-
- /* update_stmt (stmt); */
-
- return bounds;
-}
-
-/* Return var holding zero bounds. */
-tree
-chkp_get_zero_bounds_var (void)
-{
- if (!chkp_zero_bounds_var)
- chkp_zero_bounds_var
- = chkp_make_static_const_bounds (0, -1,
- CHKP_ZERO_BOUNDS_VAR_NAME);
- return chkp_zero_bounds_var;
-}
-
-/* Return var holding none bounds. */
-tree
-chkp_get_none_bounds_var (void)
-{
- if (!chkp_none_bounds_var)
- chkp_none_bounds_var
- = chkp_make_static_const_bounds (-1, 0,
- CHKP_NONE_BOUNDS_VAR_NAME);
- return chkp_none_bounds_var;
-}
-
-/* Return SSA_NAME used to represent zero bounds. */
-static tree
-chkp_get_zero_bounds (void)
-{
- if (zero_bounds)
- return zero_bounds;
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "Creating zero bounds...");
-
- if ((flag_chkp_use_static_bounds && flag_chkp_use_static_const_bounds)
- || flag_chkp_use_static_const_bounds > 0)
- {
- gimple_stmt_iterator gsi = gsi_start_bb (chkp_get_entry_block ());
- gimple *stmt;
-
- zero_bounds = chkp_get_tmp_reg (NULL);
- stmt = gimple_build_assign (zero_bounds, chkp_get_zero_bounds_var ());
- gsi_insert_before (&gsi, stmt, GSI_SAME_STMT);
- }
- else
- zero_bounds = chkp_make_bounds (integer_zero_node,
- integer_zero_node,
- NULL,
- false);
-
- return zero_bounds;
-}
-
-/* Return SSA_NAME used to represent none bounds. */
-static tree
-chkp_get_none_bounds (void)
-{
- if (none_bounds)
- return none_bounds;
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "Creating none bounds...");
-
-
- if ((flag_chkp_use_static_bounds && flag_chkp_use_static_const_bounds)
- || flag_chkp_use_static_const_bounds > 0)
- {
- gimple_stmt_iterator gsi = gsi_start_bb (chkp_get_entry_block ());
- gimple *stmt;
-
- none_bounds = chkp_get_tmp_reg (NULL);
- stmt = gimple_build_assign (none_bounds, chkp_get_none_bounds_var ());
- gsi_insert_before (&gsi, stmt, GSI_SAME_STMT);
- }
- else
- none_bounds = chkp_make_bounds (integer_minus_one_node,
- build_int_cst (size_type_node, 2),
- NULL,
- false);
-
- return none_bounds;
-}
-
-/* Return bounds to be used as a result of operation which
- should not create poiunter (e.g. MULT_EXPR). */
-static tree
-chkp_get_invalid_op_bounds (void)
-{
- return chkp_get_zero_bounds ();
-}
-
-/* Return bounds to be used for loads of non-pointer values. */
-static tree
-chkp_get_nonpointer_load_bounds (void)
-{
- return chkp_get_zero_bounds ();
-}
-
-/* Return 1 if may use bndret call to get bounds for pointer
- returned by CALL. */
-static bool
-chkp_call_returns_bounds_p (gcall *call)
-{
- if (gimple_call_internal_p (call))
- {
- if (gimple_call_internal_fn (call) == IFN_VA_ARG)
- return true;
- return false;
- }
-
- if (gimple_call_builtin_p (call, BUILT_IN_CHKP_NARROW_PTR_BOUNDS)
- || chkp_gimple_call_builtin_p (call, BUILT_IN_CHKP_NARROW))
- return true;
-
- if (gimple_call_with_bounds_p (call))
- return true;
-
- tree fndecl = gimple_call_fndecl (call);
-
- if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD)
- return false;
-
- if (fndecl && !chkp_instrumentable_p (fndecl))
- return false;
-
- if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
- {
- if (chkp_instrument_normal_builtin (fndecl))
- return true;
-
- if (!lookup_attribute ("always_inline", DECL_ATTRIBUTES (fndecl)))
- return false;
-
- struct cgraph_node *clone = chkp_maybe_create_clone (fndecl);
- return (clone && gimple_has_body_p (clone->decl));
- }
-
- return true;
-}
-
-/* Build bounds returned by CALL. */
-static tree
-chkp_build_returned_bound (gcall *call)
-{
- gimple_stmt_iterator gsi;
- tree bounds;
- gimple *stmt;
- tree fndecl = gimple_call_fndecl (call);
- unsigned int retflags;
- tree lhs = gimple_call_lhs (call);
-
- /* To avoid fixing alloca expands in targets we handle
- it separately. */
- if (fndecl
- && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && ALLOCA_FUNCTION_CODE_P (DECL_FUNCTION_CODE (fndecl)))
- {
- tree size = gimple_call_arg (call, 0);
- gimple_stmt_iterator iter = gsi_for_stmt (call);
- bounds = chkp_make_bounds (lhs, size, &iter, true);
- }
- /* We know bounds returned by set_bounds builtin call. */
- else if (fndecl
- && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_SET_PTR_BOUNDS)
- {
- tree lb = gimple_call_arg (call, 0);
- tree size = gimple_call_arg (call, 1);
- gimple_stmt_iterator iter = gsi_for_stmt (call);
- bounds = chkp_make_bounds (lb, size, &iter, true);
- }
- /* Detect bounds initialization calls. */
- else if (fndecl
- && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_INIT_PTR_BOUNDS)
- bounds = chkp_get_zero_bounds ();
- /* Detect bounds nullification calls. */
- else if (fndecl
- && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_NULL_PTR_BOUNDS)
- bounds = chkp_get_none_bounds ();
- /* Detect bounds copy calls. */
- else if (fndecl
- && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_COPY_PTR_BOUNDS)
- {
- gimple_stmt_iterator iter = gsi_for_stmt (call);
- bounds = chkp_find_bounds (gimple_call_arg (call, 1), &iter);
- }
- /* Do not use retbnd when returned bounds are equal to some
- of passed bounds. */
- else if (((retflags = gimple_call_return_flags (call)) & ERF_RETURNS_ARG)
- && (retflags & ERF_RETURN_ARG_MASK) < gimple_call_num_args (call))
- {
- gimple_stmt_iterator iter = gsi_for_stmt (call);
- unsigned int retarg = retflags & ERF_RETURN_ARG_MASK, argno;
- if (gimple_call_with_bounds_p (call))
- {
- for (argno = 0; argno < gimple_call_num_args (call); argno++)
- if (!POINTER_BOUNDS_P (gimple_call_arg (call, argno)))
- {
- if (retarg)
- retarg--;
- else
- break;
- }
- }
- else
- argno = retarg;
-
- bounds = chkp_find_bounds (gimple_call_arg (call, argno), &iter);
- }
- else if (chkp_call_returns_bounds_p (call)
- && BOUNDED_P (lhs))
- {
- gcc_assert (TREE_CODE (lhs) == SSA_NAME);
-
- /* In general case build checker builtin call to
- obtain returned bounds. */
- stmt = gimple_build_call (chkp_ret_bnd_fndecl, 1,
- gimple_call_lhs (call));
- chkp_mark_stmt (stmt);
-
- gsi = gsi_for_stmt (call);
- gsi_insert_after (&gsi, stmt, GSI_SAME_STMT);
-
- bounds = chkp_get_tmp_reg (stmt);
- gimple_call_set_lhs (stmt, bounds);
-
- update_stmt (stmt);
- }
- else
- bounds = chkp_get_zero_bounds ();
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Built returned bounds (");
- print_generic_expr (dump_file, bounds);
- fprintf (dump_file, ") for call: ");
- print_gimple_stmt (dump_file, call, 0, TDF_VOPS | TDF_MEMSYMS);
- }
-
- bounds = chkp_maybe_copy_and_register_bounds (lhs, bounds);
-
- return bounds;
-}
-
-/* Return bounds used as returned by call
- which produced SSA name VAL. */
-gcall *
-chkp_retbnd_call_by_val (tree val)
-{
- if (TREE_CODE (val) != SSA_NAME)
- return NULL;
-
- gcc_assert (gimple_code (SSA_NAME_DEF_STMT (val)) == GIMPLE_CALL);
-
- imm_use_iterator use_iter;
- use_operand_p use_p;
- FOR_EACH_IMM_USE_FAST (use_p, use_iter, val)
- if (chkp_gimple_call_builtin_p (USE_STMT (use_p), BUILT_IN_CHKP_BNDRET))
- return as_a <gcall *> (USE_STMT (use_p));
-
- return NULL;
-}
-
-/* Check the next parameter for the given PARM is bounds
- and return it's default SSA_NAME (create if required). */
-static tree
-chkp_get_next_bounds_parm (tree parm)
-{
- tree bounds = TREE_CHAIN (parm);
- gcc_assert (POINTER_BOUNDS_P (bounds));
- bounds = ssa_default_def (cfun, bounds);
- if (!bounds)
- {
- bounds = make_ssa_name (TREE_CHAIN (parm), gimple_build_nop ());
- set_ssa_default_def (cfun, TREE_CHAIN (parm), bounds);
- }
- return bounds;
-}
-
-/* Return bounds to be used for input argument PARM. */
-static tree
-chkp_get_bound_for_parm (tree parm)
-{
- tree decl = SSA_NAME_VAR (parm);
- tree bounds;
-
- gcc_assert (TREE_CODE (decl) == PARM_DECL);
-
- bounds = chkp_get_registered_bounds (parm);
-
- if (!bounds)
- bounds = chkp_get_registered_bounds (decl);
-
- if (!bounds)
- {
- tree orig_decl = cgraph_node::get (cfun->decl)->orig_decl;
-
- /* For static chain param we return zero bounds
- because currently we do not check dereferences
- of this pointer. */
- if (cfun->static_chain_decl == decl)
- bounds = chkp_get_zero_bounds ();
- /* If non instrumented runtime is used then it may be useful
- to use zero bounds for input arguments of main
- function. */
- else if (flag_chkp_zero_input_bounds_for_main
- && id_equal (DECL_ASSEMBLER_NAME (orig_decl), "main"))
- bounds = chkp_get_zero_bounds ();
- else if (BOUNDED_P (parm))
- {
- bounds = chkp_get_next_bounds_parm (decl);
- bounds = chkp_maybe_copy_and_register_bounds (decl, bounds);
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Built arg bounds (");
- print_generic_expr (dump_file, bounds);
- fprintf (dump_file, ") for arg: ");
- print_node (dump_file, "", decl, 0);
- }
- }
- else
- bounds = chkp_get_zero_bounds ();
- }
-
- if (!chkp_get_registered_bounds (parm))
- bounds = chkp_maybe_copy_and_register_bounds (parm, bounds);
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Using bounds ");
- print_generic_expr (dump_file, bounds);
- fprintf (dump_file, " for parm ");
- print_generic_expr (dump_file, parm);
- fprintf (dump_file, " of type ");
- print_generic_expr (dump_file, TREE_TYPE (parm));
- fprintf (dump_file, ".\n");
- }
-
- return bounds;
-}
-
-/* Build and return CALL_EXPR for bndstx builtin with specified
- arguments. */
-tree
-chkp_build_bndldx_call (tree addr, tree ptr)
-{
- tree fn = build1 (ADDR_EXPR,
- build_pointer_type (TREE_TYPE (chkp_bndldx_fndecl)),
- chkp_bndldx_fndecl);
- tree call = build_call_nary (TREE_TYPE (TREE_TYPE (chkp_bndldx_fndecl)),
- fn, 2, addr, ptr);
- CALL_WITH_BOUNDS_P (call) = true;
- return call;
-}
-
-/* Insert code to load bounds for PTR located by ADDR.
- Code is inserted after position pointed by GSI.
- Loaded bounds are returned. */
-static tree
-chkp_build_bndldx (tree addr, tree ptr, gimple_stmt_iterator *gsi)
-{
- gimple_seq seq;
- gimple *stmt;
- tree bounds;
-
- seq = NULL;
-
- addr = chkp_force_gimple_call_op (addr, &seq);
- ptr = chkp_force_gimple_call_op (ptr, &seq);
-
- stmt = gimple_build_call (chkp_bndldx_fndecl, 2, addr, ptr);
- chkp_mark_stmt (stmt);
- bounds = chkp_get_tmp_reg (stmt);
- gimple_call_set_lhs (stmt, bounds);
-
- gimple_seq_add_stmt (&seq, stmt);
-
- gsi_insert_seq_after (gsi, seq, GSI_CONTINUE_LINKING);
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Generated bndldx for pointer ");
- print_generic_expr (dump_file, ptr);
- fprintf (dump_file, ": ");
- print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS | TDF_MEMSYMS);
- }
-
- return bounds;
-}
-
-/* Build and return CALL_EXPR for bndstx builtin with specified
- arguments. */
-tree
-chkp_build_bndstx_call (tree addr, tree ptr, tree bounds)
-{
- tree fn = build1 (ADDR_EXPR,
- build_pointer_type (TREE_TYPE (chkp_bndstx_fndecl)),
- chkp_bndstx_fndecl);
- tree call = build_call_nary (TREE_TYPE (TREE_TYPE (chkp_bndstx_fndecl)),
- fn, 3, ptr, bounds, addr);
- CALL_WITH_BOUNDS_P (call) = true;
- return call;
-}
-
-/* Insert code to store BOUNDS for PTR stored by ADDR.
- New statements are inserted after position pointed
- by GSI. */
-void
-chkp_build_bndstx (tree addr, tree ptr, tree bounds,
- gimple_stmt_iterator *gsi)
-{
- gimple_seq seq;
- gimple *stmt;
-
- seq = NULL;
-
- addr = chkp_force_gimple_call_op (addr, &seq);
- ptr = chkp_force_gimple_call_op (ptr, &seq);
-
- stmt = gimple_build_call (chkp_bndstx_fndecl, 3, ptr, bounds, addr);
- chkp_mark_stmt (stmt);
- gimple_call_set_with_bounds (stmt, true);
-
- gimple_seq_add_stmt (&seq, stmt);
-
- gsi_insert_seq_after (gsi, seq, GSI_CONTINUE_LINKING);
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Generated bndstx for pointer store ");
- print_gimple_stmt (dump_file, gsi_stmt (*gsi), 0, TDF_VOPS|TDF_MEMSYMS);
- print_gimple_stmt (dump_file, stmt, 2, TDF_VOPS|TDF_MEMSYMS);
- }
-}
-
-/* This function is called when call statement
- is inlined and therefore we can't use bndret
- for its LHS anymore. Function fixes bndret
- call using new RHS value if possible. */
-void
-chkp_fixup_inlined_call (tree lhs, tree rhs)
-{
- tree addr, bounds;
- gcall *retbnd, *bndldx;
-
- if (!BOUNDED_P (lhs))
- return;
-
- /* Search for retbnd call. */
- retbnd = chkp_retbnd_call_by_val (lhs);
- if (!retbnd)
- return;
-
- /* Currently only handle cases when call is replaced
- with a memory access. In this case bndret call
- may be replaced with bndldx call. Otherwise we
- have to search for bounds which may cause wrong
- result due to various optimizations applied. */
- switch (TREE_CODE (rhs))
- {
- case VAR_DECL:
- if (DECL_REGISTER (rhs))
- return;
- break;
-
- case MEM_REF:
- break;
-
- case ARRAY_REF:
- case COMPONENT_REF:
- addr = get_base_address (rhs);
- if (!DECL_P (addr)
- && TREE_CODE (addr) != MEM_REF)
- return;
- if (DECL_P (addr) && DECL_REGISTER (addr))
- return;
- break;
-
- default:
- return;
- }
-
- /* Create a new statements sequence with bndldx call. */
- gimple_stmt_iterator gsi = gsi_for_stmt (retbnd);
- addr = build_fold_addr_expr (rhs);
- chkp_build_bndldx (addr, lhs, &gsi);
- bndldx = as_a <gcall *> (gsi_stmt (gsi));
-
- /* Remove bndret call. */
- bounds = gimple_call_lhs (retbnd);
- gsi = gsi_for_stmt (retbnd);
- gsi_remove (&gsi, true);
-
- /* Link new bndldx call. */
- gimple_call_set_lhs (bndldx, bounds);
- update_stmt (bndldx);
-}
-
-/* Compute bounds for pointer NODE which was assigned in
- assignment statement ASSIGN. Return computed bounds. */
-static tree
-chkp_compute_bounds_for_assignment (tree node, gimple *assign)
-{
- enum tree_code rhs_code = gimple_assign_rhs_code (assign);
- tree rhs1 = gimple_assign_rhs1 (assign);
- tree bounds = NULL_TREE;
- gimple_stmt_iterator iter = gsi_for_stmt (assign);
- tree base = NULL;
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Computing bounds for assignment: ");
- print_gimple_stmt (dump_file, assign, 0, TDF_VOPS|TDF_MEMSYMS);
- }
-
- switch (rhs_code)
- {
- case MEM_REF:
- case TARGET_MEM_REF:
- case COMPONENT_REF:
- case ARRAY_REF:
- /* We need to load bounds from the bounds table. */
- bounds = chkp_find_bounds_loaded (node, rhs1, &iter);
- break;
-
- case VAR_DECL:
- case SSA_NAME:
- case ADDR_EXPR:
- case POINTER_PLUS_EXPR:
- case NOP_EXPR:
- case CONVERT_EXPR:
- case INTEGER_CST:
- /* Bounds are just propagated from RHS. */
- bounds = chkp_find_bounds (rhs1, &iter);
- base = rhs1;
- break;
-
- case VIEW_CONVERT_EXPR:
- /* Bounds are just propagated from RHS. */
- bounds = chkp_find_bounds (TREE_OPERAND (rhs1, 0), &iter);
- break;
-
- case PARM_DECL:
- if (BOUNDED_P (rhs1))
- {
- /* We need to load bounds from the bounds table. */
- bounds = chkp_build_bndldx (chkp_build_addr_expr (rhs1),
- node, &iter);
- TREE_ADDRESSABLE (rhs1) = 1;
- }
- else
- bounds = chkp_get_nonpointer_load_bounds ();
- break;
-
- case MINUS_EXPR:
- case PLUS_EXPR:
- case BIT_AND_EXPR:
- case BIT_IOR_EXPR:
- case BIT_XOR_EXPR:
- {
- tree rhs2 = gimple_assign_rhs2 (assign);
- tree bnd1 = chkp_find_bounds (rhs1, &iter);
- tree bnd2 = chkp_find_bounds (rhs2, &iter);
-
- /* First we try to check types of operands. If it
- does not help then look at bound values.
-
- If some bounds are incomplete and other are
- not proven to be valid (i.e. also incomplete
- or invalid because value is not pointer) then
- resulting value is incomplete and will be
- recomputed later in chkp_finish_incomplete_bounds. */
- if (BOUNDED_P (rhs1)
- && !BOUNDED_P (rhs2))
- bounds = bnd1;
- else if (BOUNDED_P (rhs2)
- && !BOUNDED_P (rhs1)
- && rhs_code != MINUS_EXPR)
- bounds = bnd2;
- else if (chkp_incomplete_bounds (bnd1))
- if (chkp_valid_bounds (bnd2) && rhs_code != MINUS_EXPR
- && !chkp_incomplete_bounds (bnd2))
- bounds = bnd2;
- else
- bounds = incomplete_bounds;
- else if (chkp_incomplete_bounds (bnd2))
- if (chkp_valid_bounds (bnd1)
- && !chkp_incomplete_bounds (bnd1))
- bounds = bnd1;
- else
- bounds = incomplete_bounds;
- else if (!chkp_valid_bounds (bnd1))
- if (chkp_valid_bounds (bnd2) && rhs_code != MINUS_EXPR)
- bounds = bnd2;
- else if (bnd2 == chkp_get_zero_bounds ())
- bounds = bnd2;
- else
- bounds = bnd1;
- else if (!chkp_valid_bounds (bnd2))
- bounds = bnd1;
- else
- /* Seems both operands may have valid bounds
- (e.g. pointer minus pointer). In such case
- use default invalid op bounds. */
- bounds = chkp_get_invalid_op_bounds ();
-
- base = (bounds == bnd1) ? rhs1 : (bounds == bnd2) ? rhs2 : NULL;
- }
- break;
-
- case BIT_NOT_EXPR:
- case NEGATE_EXPR:
- case LSHIFT_EXPR:
- case RSHIFT_EXPR:
- case LROTATE_EXPR:
- case RROTATE_EXPR:
- case EQ_EXPR:
- case NE_EXPR:
- case LT_EXPR:
- case LE_EXPR:
- case GT_EXPR:
- case GE_EXPR:
- case MULT_EXPR:
- case RDIV_EXPR:
- case TRUNC_DIV_EXPR:
- case FLOOR_DIV_EXPR:
- case CEIL_DIV_EXPR:
- case ROUND_DIV_EXPR:
- case TRUNC_MOD_EXPR:
- case FLOOR_MOD_EXPR:
- case CEIL_MOD_EXPR:
- case ROUND_MOD_EXPR:
- case EXACT_DIV_EXPR:
- case FIX_TRUNC_EXPR:
- case FLOAT_EXPR:
- case REALPART_EXPR:
- case IMAGPART_EXPR:
- case POINTER_DIFF_EXPR:
- /* No valid bounds may be produced by these exprs. */
- bounds = chkp_get_invalid_op_bounds ();
- break;
-
- case COND_EXPR:
- {
- tree val1 = gimple_assign_rhs2 (assign);
- tree val2 = gimple_assign_rhs3 (assign);
- tree bnd1 = chkp_find_bounds (val1, &iter);
- tree bnd2 = chkp_find_bounds (val2, &iter);
- gimple *stmt;
-
- if (chkp_incomplete_bounds (bnd1) || chkp_incomplete_bounds (bnd2))
- bounds = incomplete_bounds;
- else if (bnd1 == bnd2)
- bounds = bnd1;
- else
- {
- rhs1 = unshare_expr (rhs1);
-
- bounds = chkp_get_tmp_reg (assign);
- stmt = gimple_build_assign (bounds, COND_EXPR, rhs1, bnd1, bnd2);
- gsi_insert_after (&iter, stmt, GSI_SAME_STMT);
-
- if (!chkp_valid_bounds (bnd1) && !chkp_valid_bounds (bnd2))
- chkp_mark_invalid_bounds (bounds);
- }
- }
- break;
-
- case MAX_EXPR:
- case MIN_EXPR:
- {
- tree rhs2 = gimple_assign_rhs2 (assign);
- tree bnd1 = chkp_find_bounds (rhs1, &iter);
- tree bnd2 = chkp_find_bounds (rhs2, &iter);
-
- if (chkp_incomplete_bounds (bnd1) || chkp_incomplete_bounds (bnd2))
- bounds = incomplete_bounds;
- else if (bnd1 == bnd2)
- bounds = bnd1;
- else
- {
- gimple *stmt;
- tree cond = build2 (rhs_code == MAX_EXPR ? GT_EXPR : LT_EXPR,
- boolean_type_node, rhs1, rhs2);
- bounds = chkp_get_tmp_reg (assign);
- stmt = gimple_build_assign (bounds, COND_EXPR, cond, bnd1, bnd2);
-
- gsi_insert_after (&iter, stmt, GSI_SAME_STMT);
-
- if (!chkp_valid_bounds (bnd1) && !chkp_valid_bounds (bnd2))
- chkp_mark_invalid_bounds (bounds);
- }
- }
- break;
-
- default:
- bounds = chkp_get_zero_bounds ();
- warning (0, "pointer bounds were lost due to unexpected expression %s",
- get_tree_code_name (rhs_code));
- }
-
- gcc_assert (bounds);
-
- /* We may reuse bounds of other pointer we copy/modify. But it is not
- allowed for abnormal ssa names. If we produced a pointer using
- abnormal ssa name, we better make a bounds copy to avoid coalescing
- issues. */
- if (base
- && TREE_CODE (base) == SSA_NAME
- && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (base))
- {
- gimple *stmt = gimple_build_assign (chkp_get_tmp_reg (NULL), bounds);
- gsi_insert_after (&iter, stmt, GSI_SAME_STMT);
- bounds = gimple_assign_lhs (stmt);
- }
-
- if (node)
- bounds = chkp_maybe_copy_and_register_bounds (node, bounds);
-
- return bounds;
-}
-
-/* Compute bounds for ssa name NODE defined by DEF_STMT pointed by ITER.
-
- There are just few statement codes allowed: NOP (for default ssa names),
- ASSIGN, CALL, PHI, ASM.
-
- Return computed bounds. */
-static tree
-chkp_get_bounds_by_definition (tree node, gimple *def_stmt,
- gphi_iterator *iter)
-{
- tree var, bounds;
- enum gimple_code code = gimple_code (def_stmt);
- gphi *stmt;
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Searching for bounds for node: ");
- print_generic_expr (dump_file, node);
-
- fprintf (dump_file, " using its definition: ");
- print_gimple_stmt (dump_file, def_stmt, 0, TDF_VOPS | TDF_MEMSYMS);
- }
-
- switch (code)
- {
- case GIMPLE_NOP:
- var = SSA_NAME_VAR (node);
- switch (TREE_CODE (var))
- {
- case PARM_DECL:
- bounds = chkp_get_bound_for_parm (node);
- break;
-
- case VAR_DECL:
- /* For uninitialized pointers use none bounds. */
- bounds = chkp_get_none_bounds ();
- bounds = chkp_maybe_copy_and_register_bounds (node, bounds);
- break;
-
- case RESULT_DECL:
- {
- tree base_type;
-
- gcc_assert (TREE_CODE (TREE_TYPE (node)) == REFERENCE_TYPE);
-
- base_type = TREE_TYPE (TREE_TYPE (node));
-
- gcc_assert (TYPE_SIZE (base_type)
- && TREE_CODE (TYPE_SIZE (base_type)) == INTEGER_CST
- && tree_to_uhwi (TYPE_SIZE (base_type)) != 0);
-
- bounds = chkp_make_bounds (node, TYPE_SIZE_UNIT (base_type),
- NULL, false);
- bounds = chkp_maybe_copy_and_register_bounds (node, bounds);
- }
- break;
-
- default:
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Unexpected var with no definition\n");
- print_generic_expr (dump_file, var);
- }
- internal_error ("chkp_get_bounds_by_definition: Unexpected var of type %s",
- get_tree_code_name (TREE_CODE (var)));
- }
- break;
-
- case GIMPLE_ASSIGN:
- bounds = chkp_compute_bounds_for_assignment (node, def_stmt);
- break;
-
- case GIMPLE_CALL:
- bounds = chkp_build_returned_bound (as_a <gcall *> (def_stmt));
- break;
-
- case GIMPLE_PHI:
- if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (node))
- if (SSA_NAME_VAR (node))
- var = chkp_get_bounds_var (SSA_NAME_VAR (node));
- else
- var = make_temp_ssa_name (pointer_bounds_type_node,
- NULL,
- CHKP_BOUND_TMP_NAME);
- else
- var = chkp_get_tmp_var ();
- stmt = create_phi_node (var, gimple_bb (def_stmt));
- bounds = gimple_phi_result (stmt);
- *iter = gsi_for_phi (stmt);
-
- bounds = chkp_maybe_copy_and_register_bounds (node, bounds);
-
- /* Created bounds do not have all phi args computed and
- therefore we do not know if there is a valid source
- of bounds for that node. Therefore we mark bounds
- as incomplete and then recompute them when all phi
- args are computed. */
- chkp_register_incomplete_bounds (bounds, node);
- break;
-
- case GIMPLE_ASM:
- bounds = chkp_get_zero_bounds ();
- bounds = chkp_maybe_copy_and_register_bounds (node, bounds);
- break;
-
- default:
- internal_error ("chkp_get_bounds_by_definition: Unexpected GIMPLE code %s",
- gimple_code_name[code]);
- }
-
- return bounds;
-}
-
-/* Return CALL_EXPR for bndmk with specified LOWER_BOUND and SIZE. */
-tree
-chkp_build_make_bounds_call (tree lower_bound, tree size)
-{
- tree call = build1 (ADDR_EXPR,
- build_pointer_type (TREE_TYPE (chkp_bndmk_fndecl)),
- chkp_bndmk_fndecl);
- return build_call_nary (TREE_TYPE (TREE_TYPE (chkp_bndmk_fndecl)),
- call, 2, lower_bound, size);
-}
-
-/* Create static bounds var of specfified OBJ which is
- is either VAR_DECL or string constant. */
-static tree
-chkp_make_static_bounds (tree obj)
-{
- static int string_id = 1;
- static int var_id = 1;
- tree *slot;
- const char *var_name;
- char *bnd_var_name;
- tree bnd_var;
-
- /* First check if we already have required var. */
- if (chkp_static_var_bounds)
- {
- /* For vars we use assembler name as a key in
- chkp_static_var_bounds map. It allows to
- avoid duplicating bound vars for decls
- sharing assembler name. */
- if (VAR_P (obj))
- {
- tree name = DECL_ASSEMBLER_NAME (obj);
- slot = chkp_static_var_bounds->get (name);
- if (slot)
- return *slot;
- }
- else
- {
- slot = chkp_static_var_bounds->get (obj);
- if (slot)
- return *slot;
- }
- }
-
- /* Build decl for bounds var. */
- if (VAR_P (obj))
- {
- if (DECL_IGNORED_P (obj))
- {
- bnd_var_name = (char *) xmalloc (strlen (CHKP_VAR_BOUNDS_PREFIX) + 10);
- sprintf (bnd_var_name, "%s%d", CHKP_VAR_BOUNDS_PREFIX, var_id++);
- }
- else
- {
- var_name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (obj));
-
- /* For hidden symbols we want to skip first '*' char. */
- if (*var_name == '*')
- var_name++;
-
- bnd_var_name = (char *) xmalloc (strlen (var_name)
- + strlen (CHKP_BOUNDS_OF_SYMBOL_PREFIX) + 1);
- strcpy (bnd_var_name, CHKP_BOUNDS_OF_SYMBOL_PREFIX);
- strcat (bnd_var_name, var_name);
- }
-
- bnd_var = build_decl (UNKNOWN_LOCATION, VAR_DECL,
- get_identifier (bnd_var_name),
- pointer_bounds_type_node);
-
- /* Address of the obj will be used as lower bound. */
- TREE_ADDRESSABLE (obj) = 1;
- }
- else
- {
- bnd_var_name = (char *) xmalloc (strlen (CHKP_STRING_BOUNDS_PREFIX) + 10);
- sprintf (bnd_var_name, "%s%d", CHKP_STRING_BOUNDS_PREFIX, string_id++);
-
- bnd_var = build_decl (UNKNOWN_LOCATION, VAR_DECL,
- get_identifier (bnd_var_name),
- pointer_bounds_type_node);
- }
-
- free (bnd_var_name);
-
- TREE_PUBLIC (bnd_var) = 0;
- TREE_USED (bnd_var) = 1;
- TREE_READONLY (bnd_var) = 0;
- TREE_STATIC (bnd_var) = 1;
- TREE_ADDRESSABLE (bnd_var) = 0;
- DECL_ARTIFICIAL (bnd_var) = 1;
- DECL_COMMON (bnd_var) = 1;
- DECL_COMDAT (bnd_var) = 1;
- DECL_READ_P (bnd_var) = 1;
- DECL_INITIAL (bnd_var) = chkp_build_addr_expr (obj);
- /* Force output similar to constant bounds.
- See chkp_make_static_const_bounds. */
- varpool_node::get_create (bnd_var)->force_output = 1;
- /* Mark symbol as requiring bounds initialization. */
- varpool_node::get_create (bnd_var)->need_bounds_init = 1;
- varpool_node::finalize_decl (bnd_var);
-
- /* Add created var to the map to use it for other references
- to obj. */
- if (!chkp_static_var_bounds)
- chkp_static_var_bounds = new hash_map<tree, tree>;
-
- if (VAR_P (obj))
- {
- tree name = DECL_ASSEMBLER_NAME (obj);
- chkp_static_var_bounds->put (name, bnd_var);
- }
- else
- chkp_static_var_bounds->put (obj, bnd_var);
-
- return bnd_var;
-}
-
-/* When var has incomplete type we cannot get size to
- compute its bounds. In such cases we use checker
- builtin call which determines object size at runtime. */
-static tree
-chkp_generate_extern_var_bounds (tree var)
-{
- tree bounds, size_reloc, lb, size, max_size, cond;
- gimple_stmt_iterator gsi;
- gimple_seq seq = NULL;
- gimple *stmt;
-
- /* If instrumentation is not enabled for vars having
- incomplete type then just return zero bounds to avoid
- checks for this var. */
- if (!flag_chkp_incomplete_type)
- return chkp_get_zero_bounds ();
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Generating bounds for extern symbol '");
- print_generic_expr (dump_file, var);
- fprintf (dump_file, "'\n");
- }
-
- stmt = gimple_build_call (chkp_sizeof_fndecl, 1, var);
-
- size_reloc = create_tmp_reg (chkp_uintptr_type, CHKP_SIZE_TMP_NAME);
- gimple_call_set_lhs (stmt, size_reloc);
-
- gimple_seq_add_stmt (&seq, stmt);
-
- lb = chkp_build_addr_expr (var);
- size = make_ssa_name (chkp_get_size_tmp_var ());
-
- if (flag_chkp_zero_dynamic_size_as_infinite)
- {
- /* We should check that size relocation was resolved.
- If it was not then use maximum possible size for the var. */
- max_size = build2 (MINUS_EXPR, chkp_uintptr_type, integer_zero_node,
- fold_convert (chkp_uintptr_type, lb));
- max_size = chkp_force_gimple_call_op (max_size, &seq);
-
- cond = build2 (NE_EXPR, boolean_type_node,
- size_reloc, integer_zero_node);
- stmt = gimple_build_assign (size, COND_EXPR, cond, size_reloc, max_size);
- gimple_seq_add_stmt (&seq, stmt);
- }
- else
- {
- stmt = gimple_build_assign (size, size_reloc);
- gimple_seq_add_stmt (&seq, stmt);
- }
-
- gsi = gsi_start_bb (chkp_get_entry_block ());
- gsi_insert_seq_after (&gsi, seq, GSI_CONTINUE_LINKING);
-
- bounds = chkp_make_bounds (lb, size, &gsi, true);
-
- return bounds;
-}
-
-/* Return 1 if TYPE has fields with zero size or fields
- marked with chkp_variable_size attribute. */
-bool
-chkp_variable_size_type (tree type)
-{
- bool res = false;
- tree field;
-
- if (RECORD_OR_UNION_TYPE_P (type))
- for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
- {
- if (TREE_CODE (field) == FIELD_DECL)
- res = res
- || lookup_attribute ("bnd_variable_size", DECL_ATTRIBUTES (field))
- || chkp_variable_size_type (TREE_TYPE (field));
- }
- else
- res = !TYPE_SIZE (type)
- || TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST
- || tree_to_uhwi (TYPE_SIZE (type)) == 0;
-
- return res;
-}
-
-/* Compute and return bounds for address of DECL which is
- one of VAR_DECL, PARM_DECL, RESULT_DECL. */
-static tree
-chkp_get_bounds_for_decl_addr (tree decl)
-{
- tree bounds;
-
- gcc_assert (VAR_P (decl)
- || TREE_CODE (decl) == PARM_DECL
- || TREE_CODE (decl) == RESULT_DECL);
-
- bounds = chkp_get_registered_addr_bounds (decl);
-
- if (bounds)
- return bounds;
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Building bounds for address of decl ");
- print_generic_expr (dump_file, decl);
- fprintf (dump_file, "\n");
- }
-
- /* Use zero bounds if size is unknown and checks for
- unknown sizes are restricted. */
- if ((!DECL_SIZE (decl)
- || (chkp_variable_size_type (TREE_TYPE (decl))
- && (TREE_STATIC (decl)
- || DECL_EXTERNAL (decl)
- || TREE_PUBLIC (decl))))
- && !flag_chkp_incomplete_type)
- return chkp_get_zero_bounds ();
-
- if (VOID_TYPE_P (TREE_TYPE (decl)))
- return chkp_get_zero_bounds ();
-
- if (flag_chkp_use_static_bounds
- && VAR_P (decl)
- && (TREE_STATIC (decl)
- || DECL_EXTERNAL (decl)
- || TREE_PUBLIC (decl))
- && !DECL_THREAD_LOCAL_P (decl))
- {
- tree bnd_var = chkp_make_static_bounds (decl);
- gimple_stmt_iterator gsi = gsi_start_bb (chkp_get_entry_block ());
- gimple *stmt;
-
- bounds = chkp_get_tmp_reg (NULL);
- stmt = gimple_build_assign (bounds, bnd_var);
- gsi_insert_before (&gsi, stmt, GSI_SAME_STMT);
- }
- else if (!DECL_SIZE (decl)
- || (chkp_variable_size_type (TREE_TYPE (decl))
- && (TREE_STATIC (decl)
- || DECL_EXTERNAL (decl)
- || TREE_PUBLIC (decl))))
- {
- gcc_assert (VAR_P (decl));
- bounds = chkp_generate_extern_var_bounds (decl);
- }
- else
- {
- tree lb = chkp_build_addr_expr (decl);
- bounds = chkp_make_bounds (lb, DECL_SIZE_UNIT (decl), NULL, false);
- }
-
- return bounds;
-}
-
-/* Compute and return bounds for constant string. */
-static tree
-chkp_get_bounds_for_string_cst (tree cst)
-{
- tree bounds;
- tree lb;
- tree size;
-
- gcc_assert (TREE_CODE (cst) == STRING_CST);
-
- bounds = chkp_get_registered_bounds (cst);
-
- if (bounds)
- return bounds;
-
- if ((flag_chkp_use_static_bounds && flag_chkp_use_static_const_bounds)
- || flag_chkp_use_static_const_bounds > 0)
- {
- tree bnd_var = chkp_make_static_bounds (cst);
- gimple_stmt_iterator gsi = gsi_start_bb (chkp_get_entry_block ());
- gimple *stmt;
-
- bounds = chkp_get_tmp_reg (NULL);
- stmt = gimple_build_assign (bounds, bnd_var);
- gsi_insert_before (&gsi, stmt, GSI_SAME_STMT);
- }
- else
- {
- lb = chkp_build_addr_expr (cst);
- size = build_int_cst (chkp_uintptr_type, TREE_STRING_LENGTH (cst));
- bounds = chkp_make_bounds (lb, size, NULL, false);
- }
-
- bounds = chkp_maybe_copy_and_register_bounds (cst, bounds);
-
- return bounds;
-}
-
-/* Generate code to instersect bounds BOUNDS1 and BOUNDS2 and
- return the result. if ITER is not NULL then Code is inserted
- before position pointed by ITER. Otherwise code is added to
- entry block. */
-static tree
-chkp_intersect_bounds (tree bounds1, tree bounds2, gimple_stmt_iterator *iter)
-{
- if (!bounds1 || bounds1 == chkp_get_zero_bounds ())
- return bounds2 ? bounds2 : bounds1;
- else if (!bounds2 || bounds2 == chkp_get_zero_bounds ())
- return bounds1;
- else
- {
- gimple_seq seq;
- gimple *stmt;
- tree bounds;
-
- seq = NULL;
-
- stmt = gimple_build_call (chkp_intersect_fndecl, 2, bounds1, bounds2);
- chkp_mark_stmt (stmt);
-
- bounds = chkp_get_tmp_reg (stmt);
- gimple_call_set_lhs (stmt, bounds);
-
- gimple_seq_add_stmt (&seq, stmt);
-
- /* We are probably doing narrowing for constant expression.
- In such case iter may be undefined. */
- if (!iter)
- {
- gimple_stmt_iterator gsi = gsi_last_bb (chkp_get_entry_block ());
- iter = &gsi;
- gsi_insert_seq_after (iter, seq, GSI_SAME_STMT);
- }
- else
- gsi_insert_seq_before (iter, seq, GSI_SAME_STMT);
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "Bounds intersection: ");
- print_gimple_stmt (dump_file, stmt, 0, TDF_VOPS|TDF_MEMSYMS);
- fprintf (dump_file, " inserted before statement: ");
- print_gimple_stmt (dump_file, gsi_stmt (*iter), 0,
- TDF_VOPS|TDF_MEMSYMS);
- }
-
- return bounds;
- }
-}
-
-/* Return 1 if we are allowed to narrow bounds for addressed FIELD
- and 0 othersize. REF is reference to the field. */
-
-static bool
-chkp_may_narrow_to_field (tree ref, tree field)
-{
- return DECL_SIZE (field) && TREE_CODE (DECL_SIZE (field)) == INTEGER_CST
- && tree_to_uhwi (DECL_SIZE (field)) != 0
- && !(flag_chkp_flexible_struct_trailing_arrays
- && array_at_struct_end_p (ref))
- && (!DECL_FIELD_OFFSET (field)
- || TREE_CODE (DECL_FIELD_OFFSET (field)) == INTEGER_CST)
- && (!DECL_FIELD_BIT_OFFSET (field)
- || TREE_CODE (DECL_FIELD_BIT_OFFSET (field)) == INTEGER_CST)
- && !lookup_attribute ("bnd_variable_size", DECL_ATTRIBUTES (field))
- && !chkp_variable_size_type (TREE_TYPE (field));
-}
-
-/* Return 1 if bounds for FIELD should be narrowed to
- field's own size. REF is reference to the field. */
-
-static bool
-chkp_narrow_bounds_for_field (tree ref, tree field)
-{
- HOST_WIDE_INT offs;
- HOST_WIDE_INT bit_offs;
-
- if (!chkp_may_narrow_to_field (ref, field))
- return false;
-
- /* Access to compiler generated fields should not cause
- bounds narrowing. */
- if (DECL_ARTIFICIAL (field))
- return false;
-
- offs = tree_to_uhwi (DECL_FIELD_OFFSET (field));
- bit_offs = tree_to_uhwi (DECL_FIELD_BIT_OFFSET (field));
-
- return (flag_chkp_narrow_bounds
- && (flag_chkp_first_field_has_own_bounds
- || offs
- || bit_offs));
-}
-
-/* Perform narrowing for BOUNDS of an INNER reference. Shift boundary
- by OFFSET bytes and limit to SIZE bytes. Newly created statements are
- added to ITER. */
-
-static tree
-chkp_narrow_size_and_offset (tree bounds, tree inner, tree offset,
- tree size, gimple_stmt_iterator *iter)
-{
- tree addr = chkp_build_addr_expr (unshare_expr (inner));
- tree t = TREE_TYPE (addr);
-
- gimple *stmt = gimple_build_assign (NULL_TREE, addr);
- addr = make_temp_ssa_name (t, stmt, CHKP_BOUND_TMP_NAME);
- gimple_assign_set_lhs (stmt, addr);
- gsi_insert_seq_before (iter, stmt, GSI_SAME_STMT);
-
- stmt = gimple_build_assign (NULL_TREE, POINTER_PLUS_EXPR, addr, offset);
- tree shifted = make_temp_ssa_name (t, stmt, CHKP_BOUND_TMP_NAME);
- gimple_assign_set_lhs (stmt, shifted);
- gsi_insert_seq_before (iter, stmt, GSI_SAME_STMT);
-
- tree bounds2 = chkp_make_bounds (shifted, size, iter, false);
-
- return chkp_intersect_bounds (bounds, bounds2, iter);
-}
-
-/* Perform narrowing for BOUNDS using bounds computed for field
- access COMPONENT. ITER meaning is the same as for
- chkp_intersect_bounds. */
-
-static tree
-chkp_narrow_bounds_to_field (tree bounds, tree component,
- gimple_stmt_iterator *iter)
-{
- tree field = TREE_OPERAND (component, 1);
- tree size = DECL_SIZE_UNIT (field);
- tree field_ptr = chkp_build_addr_expr (component);
- tree field_bounds;
-
- field_bounds = chkp_make_bounds (field_ptr, size, iter, false);
-
- return chkp_intersect_bounds (field_bounds, bounds, iter);
-}
-
-/* Parse field or array access NODE.
-
- PTR ouput parameter holds a pointer to the outermost
- object.
-
- BITFIELD output parameter is set to 1 if bitfield is
- accessed and to 0 otherwise. If it is 1 then ELT holds
- outer component for accessed bit field.
-
- SAFE outer parameter is set to 1 if access is safe and
- checks are not required.
-
- BOUNDS outer parameter holds bounds to be used to check
- access (may be NULL).
-
- If INNERMOST_BOUNDS is 1 then try to narrow bounds to the
- innermost accessed component. */
-static void
-chkp_parse_array_and_component_ref (tree node, tree *ptr,
- tree *elt, bool *safe,
- bool *bitfield,
- tree *bounds,
- gimple_stmt_iterator *iter,
- bool innermost_bounds)
-{
- tree comp_to_narrow = NULL_TREE;
- tree last_comp = NULL_TREE;
- bool array_ref_found = false;
- tree *nodes;
- tree var;
- int len;
- int i;
-
- /* Compute tree height for expression. */
- var = node;
- len = 1;
- while (TREE_CODE (var) == COMPONENT_REF
- || TREE_CODE (var) == ARRAY_REF
- || TREE_CODE (var) == VIEW_CONVERT_EXPR
- || TREE_CODE (var) == BIT_FIELD_REF)
- {
- var = TREE_OPERAND (var, 0);
- len++;
- }
-
- gcc_assert (len > 1);
-
- /* It is more convenient for us to scan left-to-right,
- so walk tree again and put all node to nodes vector
- in reversed order. */
- nodes = XALLOCAVEC (tree, len);
- nodes[len - 1] = node;
- for (i = len - 2; i >= 0; i--)
- nodes[i] = TREE_OPERAND (nodes[i + 1], 0);
-
- if (bounds)
- *bounds = NULL;
- *safe = true;
- *bitfield = ((TREE_CODE (node) == COMPONENT_REF
- && DECL_BIT_FIELD_TYPE (TREE_OPERAND (node, 1)))
- || TREE_CODE (node) == BIT_FIELD_REF);
- /* To get bitfield address we will need outer element. */
- if (*bitfield)
- *elt = nodes[len - 2];
- else
- *elt = NULL_TREE;
-
- /* If we have indirection in expression then compute
- outermost structure bounds. Computed bounds may be
- narrowed later. */
- if (TREE_CODE (nodes[0]) == MEM_REF || INDIRECT_REF_P (nodes[0]))
- {
- *safe = false;
- *ptr = TREE_OPERAND (nodes[0], 0);
- if (bounds)
- *bounds = chkp_find_bounds (*ptr, iter);
- }
- else
- {
- gcc_assert (VAR_P (var)
- || TREE_CODE (var) == PARM_DECL
- || TREE_CODE (var) == RESULT_DECL
- || TREE_CODE (var) == STRING_CST
- || TREE_CODE (var) == SSA_NAME);
-
- *ptr = chkp_build_addr_expr (var);
-
- /* For hard register cases chkp_build_addr_expr returns INTEGER_CST
- and later on chkp_find_bounds will fail to find proper bounds.
- In order to avoid that, we find/create bounds right aways using
- the var itself. */
- if (VAR_P (var) && DECL_HARD_REGISTER (var))
- *bounds = chkp_make_addressed_object_bounds (var, iter);
- }
-
- /* In this loop we are trying to find a field access
- requiring narrowing. There are two simple rules
- for search:
- 1. Leftmost array_ref is chosen if any.
- 2. Rightmost suitable component_ref is chosen if innermost
- bounds are required and no array_ref exists. */
- for (i = 1; i < len; i++)
- {
- var = nodes[i];
-
- if (TREE_CODE (var) == ARRAY_REF)
- {
- *safe = false;
- array_ref_found = true;
- if (flag_chkp_narrow_bounds
- && !flag_chkp_narrow_to_innermost_arrray
- && (!last_comp
- || chkp_may_narrow_to_field (var,
- TREE_OPERAND (last_comp, 1))))
- {
- comp_to_narrow = last_comp;
- break;
- }
- }
- else if (TREE_CODE (var) == COMPONENT_REF)
- {
- tree field = TREE_OPERAND (var, 1);
-
- if (innermost_bounds
- && !array_ref_found
- && chkp_narrow_bounds_for_field (var, field))
- comp_to_narrow = var;
- last_comp = var;
-
- if (flag_chkp_narrow_bounds
- && flag_chkp_narrow_to_innermost_arrray
- && TREE_CODE (TREE_TYPE (field)) == ARRAY_TYPE)
- {
- if (bounds)
- *bounds = chkp_narrow_bounds_to_field (*bounds, var, iter);
- comp_to_narrow = NULL;
- }
- }
- else if (TREE_CODE (var) == BIT_FIELD_REF)
- {
- if (flag_chkp_narrow_bounds && bounds)
- {
- tree offset, size;
- chkp_parse_bit_field_ref (var, UNKNOWN_LOCATION, &offset, &size);
- *bounds
- = chkp_narrow_size_and_offset (*bounds, TREE_OPERAND (var, 0),
- offset, size, iter);
- }
- }
- else if (TREE_CODE (var) == VIEW_CONVERT_EXPR)
- /* Nothing to do for it. */
- ;
- else
- gcc_unreachable ();
- }
-
- if (comp_to_narrow && DECL_SIZE (TREE_OPERAND (comp_to_narrow, 1)) && bounds)
- *bounds = chkp_narrow_bounds_to_field (*bounds, comp_to_narrow, iter);
-
- if (innermost_bounds && bounds && !*bounds)
- *bounds = chkp_find_bounds (*ptr, iter);
-}
-
-/* Parse BIT_FIELD_REF to a NODE for a given location LOC. Return OFFSET
- and SIZE in bytes. */
-
-static
-void chkp_parse_bit_field_ref (tree node, location_t loc, tree *offset,
- tree *size)
-{
- tree bpu = fold_convert (size_type_node, bitsize_int (BITS_PER_UNIT));
- tree offs = fold_convert (size_type_node, TREE_OPERAND (node, 2));
- tree rem = size_binop_loc (loc, TRUNC_MOD_EXPR, offs, bpu);
- offs = size_binop_loc (loc, TRUNC_DIV_EXPR, offs, bpu);
-
- tree s = fold_convert (size_type_node, TREE_OPERAND (node, 1));
- s = size_binop_loc (loc, PLUS_EXPR, s, rem);
- s = size_binop_loc (loc, CEIL_DIV_EXPR, s, bpu);
- s = fold_convert (size_type_node, s);
-
- *offset = offs;
- *size = s;
-}
-
-/* Compute and return bounds for address of OBJ. */
-static tree
-chkp_make_addressed_object_bounds (tree obj, gimple_stmt_iterator *iter)
-{
- tree bounds = chkp_get_registered_addr_bounds (obj);
-
- if (bounds)
- return bounds;
-
- switch (TREE_CODE (obj))
- {
- case VAR_DECL:
- case PARM_DECL:
- case RESULT_DECL:
- bounds = chkp_get_bounds_for_decl_addr (obj);
- break;
-
- case STRING_CST:
- bounds = chkp_get_bounds_for_string_cst (obj);
- break;
-
- case ARRAY_REF:
- case COMPONENT_REF:
- case BIT_FIELD_REF:
- {
- tree elt;
- tree ptr;
- bool safe;
- bool bitfield;
-
- chkp_parse_array_and_component_ref (obj, &ptr, &elt, &safe,
- &bitfield, &bounds, iter, true);
-
- gcc_assert (bounds);
- }
- break;
-
- case FUNCTION_DECL:
- case LABEL_DECL:
- bounds = chkp_get_zero_bounds ();
- break;
-
- case MEM_REF:
- bounds = chkp_find_bounds (TREE_OPERAND (obj, 0), iter);
- break;
-
- case REALPART_EXPR:
- case IMAGPART_EXPR:
- bounds = chkp_make_addressed_object_bounds (TREE_OPERAND (obj, 0), iter);
- break;
-
- default:
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "chkp_make_addressed_object_bounds: "
- "unexpected object of type %s\n",
- get_tree_code_name (TREE_CODE (obj)));
- print_node (dump_file, "", obj, 0);
- }
- internal_error ("chkp_make_addressed_object_bounds: "
- "Unexpected tree code %s",
- get_tree_code_name (TREE_CODE (obj)));
- }
-
- chkp_register_addr_bounds (obj, bounds);
-
- return bounds;
-}
-
-/* Compute bounds for pointer PTR loaded from PTR_SRC. Generate statements
- to compute bounds if required. Computed bounds should be available at
- position pointed by ITER.
-
- If PTR_SRC is NULL_TREE then pointer definition is identified.
-
- If PTR_SRC is not NULL_TREE then ITER points to statements which loads
- PTR. If PTR is a any memory reference then ITER points to a statement
- after which bndldx will be inserterd. In both cases ITER will be updated
- to point to the inserted bndldx statement. */
-
-static tree
-chkp_find_bounds_1 (tree ptr, tree ptr_src, gimple_stmt_iterator *iter)
-{
- tree addr = NULL_TREE;
- tree bounds = NULL_TREE;
-
- if (!ptr_src)
- ptr_src = ptr;
-
- bounds = chkp_get_registered_bounds (ptr_src);
-
- if (bounds)
- return bounds;
-
- switch (TREE_CODE (ptr_src))
- {
- case MEM_REF:
- case VAR_DECL:
- if (BOUNDED_P (ptr_src))
- if (VAR_P (ptr) && DECL_REGISTER (ptr))
- bounds = chkp_get_zero_bounds ();
- else
- {
- addr = chkp_build_addr_expr (ptr_src);
- bounds = chkp_build_bndldx (addr, ptr, iter);
- }
- else
- bounds = chkp_get_nonpointer_load_bounds ();
- break;
-
- case ARRAY_REF:
- case COMPONENT_REF:
- addr = get_base_address (ptr_src);
- if (VAR_P (addr) && DECL_HARD_REGISTER (addr))
- {
- bounds = chkp_get_zero_bounds ();
- break;
- }
- if (DECL_P (addr)
- || TREE_CODE (addr) == MEM_REF
- || TREE_CODE (addr) == TARGET_MEM_REF)
- {
- if (BOUNDED_P (ptr_src))
- if (VAR_P (ptr) && DECL_REGISTER (ptr))
- bounds = chkp_get_zero_bounds ();
- else
- {
- addr = chkp_build_addr_expr (ptr_src);
- bounds = chkp_build_bndldx (addr, ptr, iter);
- }
- else
- bounds = chkp_get_nonpointer_load_bounds ();
- }
- else
- {
- gcc_assert (TREE_CODE (addr) == SSA_NAME);
- bounds = chkp_find_bounds (addr, iter);
- }
- break;
-
- case PARM_DECL:
- /* Handled above but failed. */
- bounds = chkp_get_invalid_op_bounds ();
- break;
-
- case TARGET_MEM_REF:
- addr = chkp_build_addr_expr (ptr_src);
- bounds = chkp_build_bndldx (addr, ptr, iter);
- break;
-
- case SSA_NAME:
- bounds = chkp_get_registered_bounds (ptr_src);
- if (!bounds)
- {
- gimple *def_stmt = SSA_NAME_DEF_STMT (ptr_src);
- gphi_iterator phi_iter;
-
- bounds = chkp_get_bounds_by_definition (ptr_src, def_stmt, &phi_iter);
-
- gcc_assert (bounds);
-
- if (gphi *def_phi = dyn_cast <gphi *> (def_stmt))
- {
- unsigned i;
-
- for (i = 0; i < gimple_phi_num_args (def_phi); i++)
- {
- tree arg = gimple_phi_arg_def (def_phi, i);
- tree arg_bnd;
- gphi *phi_bnd;
-
- arg_bnd = chkp_find_bounds (arg, NULL);
-
- /* chkp_get_bounds_by_definition created new phi
- statement and phi_iter points to it.
-
- Previous call to chkp_find_bounds could create
- new basic block and therefore change phi statement
- phi_iter points to. */
- phi_bnd = phi_iter.phi ();
-
- add_phi_arg (phi_bnd, arg_bnd,
- gimple_phi_arg_edge (def_phi, i),
- UNKNOWN_LOCATION);
- }
-
- /* If all bound phi nodes have their arg computed
- then we may finish its computation. See
- chkp_finish_incomplete_bounds for more details. */
- if (chkp_may_finish_incomplete_bounds ())
- chkp_finish_incomplete_bounds ();
- }
-
- gcc_assert (bounds == chkp_get_registered_bounds (ptr_src)
- || chkp_incomplete_bounds (bounds));
- }
- break;
-
- case ADDR_EXPR:
- case WITH_SIZE_EXPR:
- bounds = chkp_make_addressed_object_bounds (TREE_OPERAND (ptr_src, 0), iter);
- break;
-
- case INTEGER_CST:
- case COMPLEX_CST:
- case VECTOR_CST:
- if (integer_zerop (ptr_src))
- bounds = chkp_get_none_bounds ();
- else
- bounds = chkp_get_invalid_op_bounds ();
- break;
-
- default:
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "chkp_find_bounds: unexpected ptr of type %s\n",
- get_tree_code_name (TREE_CODE (ptr_src)));
- print_node (dump_file, "", ptr_src, 0);
- }
- internal_error ("chkp_find_bounds: Unexpected tree code %s",
- get_tree_code_name (TREE_CODE (ptr_src)));
- }
-
- if (!bounds)
- {
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (stderr, "chkp_find_bounds: cannot find bounds for pointer\n");
- print_node (dump_file, "", ptr_src, 0);
- }
- internal_error ("chkp_find_bounds: Cannot find bounds for pointer");
- }
-
- return bounds;
-}
-
-/* Normal case for bounds search without forced narrowing. */
-static tree
-chkp_find_bounds (tree ptr, gimple_stmt_iterator *iter)
-{
- return chkp_find_bounds_1 (ptr, NULL_TREE, iter);
-}
-
-/* Search bounds for pointer PTR loaded from PTR_SRC
- by statement *ITER points to. */
-static tree
-chkp_find_bounds_loaded (tree ptr, tree ptr_src, gimple_stmt_iterator *iter)
-{
- return chkp_find_bounds_1 (ptr, ptr_src, iter);
-}
-
-/* Helper function which checks type of RHS and finds all pointers in
- it. For each found pointer we build it's accesses in LHS and RHS
- objects and then call HANDLER for them. Function is used to copy
- or initilize bounds for copied object. */
-static void
-chkp_walk_pointer_assignments (tree lhs, tree rhs, void *arg,
- assign_handler handler)
-{
- tree type = TREE_TYPE (lhs);
-
- /* We have nothing to do with clobbers. */
- if (TREE_CLOBBER_P (rhs))
- return;
-
- if (BOUNDED_TYPE_P (type))
- handler (lhs, rhs, arg);
- else if (RECORD_OR_UNION_TYPE_P (type))
- {
- tree field;
-
- if (TREE_CODE (rhs) == CONSTRUCTOR)
- {
- unsigned HOST_WIDE_INT cnt;
- tree val;
-
- FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (rhs), cnt, field, val)
- {
- if (field && chkp_type_has_pointer (TREE_TYPE (field)))
- {
- tree lhs_field = chkp_build_component_ref (lhs, field);
- chkp_walk_pointer_assignments (lhs_field, val, arg, handler);
- }
- }
- }
- else
- for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
- if (TREE_CODE (field) == FIELD_DECL
- && chkp_type_has_pointer (TREE_TYPE (field)))
- {
- tree rhs_field = chkp_build_component_ref (rhs, field);
- tree lhs_field = chkp_build_component_ref (lhs, field);
- chkp_walk_pointer_assignments (lhs_field, rhs_field, arg, handler);
- }
- }
- else if (TREE_CODE (type) == ARRAY_TYPE)
- {
- unsigned HOST_WIDE_INT cur = 0;
- tree maxval = TYPE_MAX_VALUE (TYPE_DOMAIN (type));
- tree etype = TREE_TYPE (type);
- tree esize = TYPE_SIZE (etype);
-
- if (TREE_CODE (rhs) == CONSTRUCTOR)
- {
- unsigned HOST_WIDE_INT cnt;
- tree purp, val, lhs_elem;
-
- FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (rhs), cnt, purp, val)
- {
- if (purp && TREE_CODE (purp) == RANGE_EXPR)
- {
- tree lo_index = TREE_OPERAND (purp, 0);
- tree hi_index = TREE_OPERAND (purp, 1);
-
- for (cur = (unsigned)tree_to_uhwi (lo_index);
- cur <= (unsigned)tree_to_uhwi (hi_index);
- cur++)
- {
- lhs_elem = chkp_build_array_ref (lhs, etype, esize, cur);
- chkp_walk_pointer_assignments (lhs_elem, val, arg, handler);
- }
- }
- else
- {
- if (purp)
- {
- gcc_assert (TREE_CODE (purp) == INTEGER_CST);
- cur = tree_to_uhwi (purp);
- }
-
- lhs_elem = chkp_build_array_ref (lhs, etype, esize, cur++);
-
- chkp_walk_pointer_assignments (lhs_elem, val, arg, handler);
- }
- }
- }
- /* Copy array only when size is known. */
- else if (maxval && !integer_minus_onep (maxval))
- for (cur = 0; cur <= TREE_INT_CST_LOW (maxval); cur++)
- {
- tree lhs_elem = chkp_build_array_ref (lhs, etype, esize, cur);
- tree rhs_elem = chkp_build_array_ref (rhs, etype, esize, cur);
- chkp_walk_pointer_assignments (lhs_elem, rhs_elem, arg, handler);
- }
- }
- else
- internal_error("chkp_walk_pointer_assignments: unexpected RHS type: %s",
- get_tree_code_name (TREE_CODE (type)));
-}
-
-/* Add code to copy bounds for assignment of RHS to LHS.
- ARG is an iterator pointing ne code position. */
-static void
-chkp_copy_bounds_for_elem (tree lhs, tree rhs, void *arg)
-{
- gimple_stmt_iterator *iter = (gimple_stmt_iterator *)arg;
- tree bounds = chkp_find_bounds (rhs, iter);
- tree addr = chkp_build_addr_expr(lhs);
-
- chkp_build_bndstx (addr, rhs, bounds, iter);
-}
-
-/* Emit static bound initilizers and size vars. */
-void
-chkp_finish_file (void)
-{
- struct varpool_node *node;
- struct chkp_ctor_stmt_list stmts;
-
- if (seen_error ())
- return;
-
- /* Iterate through varpool and generate bounds initialization
- constructors for all statically initialized pointers. */
- stmts.avail = MAX_STMTS_IN_STATIC_CHKP_CTOR;
- stmts.stmts = NULL;
- FOR_EACH_VARIABLE (node)
- /* Check that var is actually emitted and we need and may initialize
- its bounds. */
- if (node->need_bounds_init
- && !POINTER_BOUNDS_P (node->decl)
- && DECL_RTL (node->decl)
- && MEM_P (DECL_RTL (node->decl))
- && TREE_ASM_WRITTEN (node->decl))
- {
- chkp_walk_pointer_assignments (node->decl,
- DECL_INITIAL (node->decl),
- &stmts,
- chkp_add_modification_to_stmt_list);
-
- if (stmts.avail <= 0)
- {
- cgraph_build_static_cdtor ('P', stmts.stmts,
- MAX_RESERVED_INIT_PRIORITY + 3);
- stmts.avail = MAX_STMTS_IN_STATIC_CHKP_CTOR;
- stmts.stmts = NULL;
- }
- }
-
- if (stmts.stmts)
- cgraph_build_static_cdtor ('P', stmts.stmts,
- MAX_RESERVED_INIT_PRIORITY + 3);
-
- /* Iterate through varpool and generate bounds initialization
- constructors for all static bounds vars. */
- stmts.avail = MAX_STMTS_IN_STATIC_CHKP_CTOR;
- stmts.stmts = NULL;
- FOR_EACH_VARIABLE (node)
- if (node->need_bounds_init
- && POINTER_BOUNDS_P (node->decl)
- && TREE_ASM_WRITTEN (node->decl))
- {
- tree bnd = node->decl;
- tree var;
-
- gcc_assert (DECL_INITIAL (bnd)
- && TREE_CODE (DECL_INITIAL (bnd)) == ADDR_EXPR);
-
- var = TREE_OPERAND (DECL_INITIAL (bnd), 0);
- chkp_output_static_bounds (bnd, var, &stmts);
- }
-
- if (stmts.stmts)
- cgraph_build_static_cdtor ('B', stmts.stmts,
- MAX_RESERVED_INIT_PRIORITY + 2);
-
- delete chkp_static_var_bounds;
- delete chkp_bounds_map;
-}
-
-/* An instrumentation function which is called for each statement
- having memory access we want to instrument. It inserts check
- code and bounds copy code.
-
- ITER points to statement to instrument.
-
- NODE holds memory access in statement to check.
-
- LOC holds the location information for statement.
-
- DIRFLAGS determines whether access is read or write.
-
- ACCESS_OFFS should be added to address used in NODE
- before check.
-
- ACCESS_SIZE holds size of checked access.
-
- SAFE indicates if NODE access is safe and should not be
- checked. */
-static void
-chkp_process_stmt (gimple_stmt_iterator *iter, tree node,
- location_t loc, tree dirflag,
- tree access_offs, tree access_size,
- bool safe)
-{
- tree node_type = TREE_TYPE (node);
- tree size = access_size ? access_size : TYPE_SIZE_UNIT (node_type);
- tree addr_first = NULL_TREE; /* address of the first accessed byte */
- tree addr_last = NULL_TREE; /* address of the last accessed byte */
- tree ptr = NULL_TREE; /* a pointer used for dereference */
- tree bounds = NULL_TREE;
- bool reg_store = false;
-
- /* We do not need instrumentation for clobbers. */
- if (dirflag == integer_one_node
- && gimple_code (gsi_stmt (*iter)) == GIMPLE_ASSIGN
- && TREE_CLOBBER_P (gimple_assign_rhs1 (gsi_stmt (*iter))))
- return;
-
- switch (TREE_CODE (node))
- {
- case ARRAY_REF:
- case COMPONENT_REF:
- {
- bool bitfield;
- tree elt;
-
- if (safe)
- {
- /* We are not going to generate any checks, so do not
- generate bounds as well. */
- addr_first = chkp_build_addr_expr (node);
- break;
- }
-
- chkp_parse_array_and_component_ref (node, &ptr, &elt, &safe,
- &bitfield, &bounds, iter, false);
-
- /* Break if there is no dereference and operation is safe. */
-
- if (bitfield)
- {
- tree field = TREE_OPERAND (node, 1);
-
- if (TREE_CODE (DECL_SIZE_UNIT (field)) == INTEGER_CST)
- size = DECL_SIZE_UNIT (field);
-
- if (elt)
- elt = chkp_build_addr_expr (elt);
- addr_first = fold_convert_loc (loc, ptr_type_node, elt ? elt : ptr);
- addr_first = fold_build_pointer_plus_loc (loc,
- addr_first,
- byte_position (field));
- }
- else
- addr_first = chkp_build_addr_expr (node);
- }
- break;
-
- case INDIRECT_REF:
- ptr = TREE_OPERAND (node, 0);
- addr_first = ptr;
- break;
-
- case MEM_REF:
- ptr = TREE_OPERAND (node, 0);
- addr_first = chkp_build_addr_expr (node);
- break;
-
- case TARGET_MEM_REF:
- ptr = TMR_BASE (node);
- addr_first = chkp_build_addr_expr (node);
- break;
-
- case ARRAY_RANGE_REF:
- printf("ARRAY_RANGE_REF\n");
- debug_gimple_stmt(gsi_stmt(*iter));
- debug_tree(node);
- gcc_unreachable ();
- break;
-
- case BIT_FIELD_REF:
- {
- tree offset, size;
-
- gcc_assert (!access_offs);
- gcc_assert (!access_size);
-
- chkp_parse_bit_field_ref (node, loc, &offset, &size);
-
- chkp_process_stmt (iter, TREE_OPERAND (node, 0), loc,
- dirflag, offset, size, safe);
- return;
- }
- break;
-
- case VAR_DECL:
- case RESULT_DECL:
- case PARM_DECL:
- if (dirflag != integer_one_node
- || DECL_REGISTER (node))
- return;
-
- safe = true;
- addr_first = chkp_build_addr_expr (node);
- break;
-
- default:
- return;
- }
-
- /* If addr_last was not computed then use (addr_first + size - 1)
- expression to compute it. */
- if (!addr_last)
- {
- addr_last = fold_build_pointer_plus_loc (loc, addr_first, size);
- addr_last = fold_build_pointer_plus_hwi_loc (loc, addr_last, -1);
- }
-
- /* Shift both first_addr and last_addr by access_offs if specified. */
- if (access_offs)
- {
- addr_first = fold_build_pointer_plus_loc (loc, addr_first, access_offs);
- addr_last = fold_build_pointer_plus_loc (loc, addr_last, access_offs);
- }
-
- if (dirflag == integer_one_node)
- {
- tree base = get_base_address (node);
- if (VAR_P (base) && DECL_HARD_REGISTER (base))
- reg_store = true;
- }
-
- /* Generate bndcl/bndcu checks if memory access is not safe. */
- if (!safe)
- {
- gimple_stmt_iterator stmt_iter = *iter;
-
- if (!bounds)
- bounds = chkp_find_bounds (ptr, iter);
-
- chkp_check_mem_access (addr_first, addr_last, bounds,
- stmt_iter, loc, dirflag);
- }
-
- /* We need to store bounds in case pointer is stored. */
- if (dirflag == integer_one_node
- && !reg_store
- && chkp_type_has_pointer (node_type)
- && flag_chkp_store_bounds)
- {
- gimple *stmt = gsi_stmt (*iter);
- tree rhs1 = gimple_assign_rhs1 (stmt);
- enum tree_code rhs_code = gimple_assign_rhs_code (stmt);
-
- if (get_gimple_rhs_class (rhs_code) == GIMPLE_SINGLE_RHS)
- chkp_walk_pointer_assignments (node, rhs1, iter,
- chkp_copy_bounds_for_elem);
- else
- {
- bounds = chkp_compute_bounds_for_assignment (NULL_TREE, stmt);
- chkp_build_bndstx (addr_first, rhs1, bounds, iter);
- }
- }
-}
-
-/* Add code to copy bounds for all pointers copied
- in ASSIGN created during inline of EDGE. */
-void
-chkp_copy_bounds_for_assign (gimple *assign, struct cgraph_edge *edge)
-{
- tree lhs = gimple_assign_lhs (assign);
- tree rhs = gimple_assign_rhs1 (assign);
- gimple_stmt_iterator iter = gsi_for_stmt (assign);
-
- if (!flag_chkp_store_bounds)
- return;
-
- chkp_walk_pointer_assignments (lhs, rhs, &iter, chkp_copy_bounds_for_elem);
-
- /* We should create edges for all created calls to bndldx and bndstx. */
- while (gsi_stmt (iter) != assign)
- {
- gimple *stmt = gsi_stmt (iter);
- if (gimple_code (stmt) == GIMPLE_CALL)
- {
- tree fndecl = gimple_call_fndecl (stmt);
- struct cgraph_node *callee = cgraph_node::get_create (fndecl);
-
- gcc_assert (chkp_gimple_call_builtin_p (stmt, BUILT_IN_CHKP_BNDSTX)
- || chkp_gimple_call_builtin_p (stmt, BUILT_IN_CHKP_BNDLDX)
- || chkp_gimple_call_builtin_p (stmt, BUILT_IN_CHKP_BNDRET));
-
- edge->caller->create_edge (callee, as_a <gcall *> (stmt), edge->count);
- }
- gsi_prev (&iter);
- }
-}
-
-/* Some code transformation made during instrumentation pass
- may put code into inconsistent state. Here we find and fix
- such flaws. */
-void
-chkp_fix_cfg ()
-{
- basic_block bb;
- gimple_stmt_iterator i;
-
- /* We could insert some code right after stmt which ends bb.
- We wanted to put this code on fallthru edge but did not
- add new edges from the beginning because it may cause new
- phi node creation which may be incorrect due to incomplete
- bound phi nodes. */
- FOR_ALL_BB_FN (bb, cfun)
- for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
- {
- gimple *stmt = gsi_stmt (i);
- gimple_stmt_iterator next = i;
-
- gsi_next (&next);
-
- if (stmt_ends_bb_p (stmt)
- && !gsi_end_p (next))
- {
- edge fall = find_fallthru_edge (bb->succs);
- basic_block dest = NULL;
- int flags = 0;
-
- gcc_assert (fall);
-
- /* We cannot split abnormal edge. Therefore we
- store its params, make it regular and then
- rebuild abnormal edge after split. */
- if (fall->flags & EDGE_ABNORMAL)
- {
- flags = fall->flags & ~EDGE_FALLTHRU;
- dest = fall->dest;
-
- fall->flags &= ~EDGE_COMPLEX;
- }
-
- while (!gsi_end_p (next))
- {
- gimple *next_stmt = gsi_stmt (next);
- gsi_remove (&next, false);
- gsi_insert_on_edge (fall, next_stmt);
- }
-
- gsi_commit_edge_inserts ();
-
- /* Re-create abnormal edge. */
- if (dest)
- make_edge (bb, dest, flags);
- }
- }
-}
-
-/* Walker callback for chkp_replace_function_pointers. Replaces
- function pointer in the specified operand with pointer to the
- instrumented function version. */
-static tree
-chkp_replace_function_pointer (tree *op, int *walk_subtrees,
- void *data ATTRIBUTE_UNUSED)
-{
- if (TREE_CODE (*op) == FUNCTION_DECL
- && chkp_instrumentable_p (*op)
- && (DECL_BUILT_IN_CLASS (*op) == NOT_BUILT_IN
- /* For builtins we replace pointers only for selected
- function and functions having definitions. */
- || (DECL_BUILT_IN_CLASS (*op) == BUILT_IN_NORMAL
- && (chkp_instrument_normal_builtin (*op)
- || gimple_has_body_p (*op)))))
- {
- struct cgraph_node *node = cgraph_node::get_create (*op);
- struct cgraph_node *clone = NULL;
-
- if (!node->instrumentation_clone)
- clone = chkp_maybe_create_clone (*op);
-
- if (clone)
- *op = clone->decl;
- *walk_subtrees = 0;
- }
-
- return NULL;
-}
-
-/* This function searches for function pointers in statement
- pointed by GSI and replaces them with pointers to instrumented
- function versions. */
-static void
-chkp_replace_function_pointers (gimple_stmt_iterator *gsi)
-{
- gimple *stmt = gsi_stmt (*gsi);
- /* For calls we want to walk call args only. */
- if (gimple_code (stmt) == GIMPLE_CALL)
- {
- unsigned i;
- for (i = 0; i < gimple_call_num_args (stmt); i++)
- walk_tree (gimple_call_arg_ptr (stmt, i),
- chkp_replace_function_pointer, NULL, NULL);
- }
- else
- walk_gimple_stmt (gsi, NULL, chkp_replace_function_pointer, NULL);
-}
-
-/* This function instruments all statements working with memory,
- calls and rets.
-
- It also removes excess statements from static initializers. */
-static void
-chkp_instrument_function (void)
-{
- basic_block bb, next;
- gimple_stmt_iterator i;
- enum gimple_rhs_class grhs_class;
- bool safe = lookup_attribute ("chkp ctor", DECL_ATTRIBUTES (cfun->decl));
-
- bb = ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb;
- do
- {
- next = bb->next_bb;
- for (i = gsi_start_bb (bb); !gsi_end_p (i); )
- {
- gimple *s = gsi_stmt (i);
-
- /* Skip statement marked to not be instrumented. */
- if (chkp_marked_stmt_p (s))
- {
- gsi_next (&i);
- continue;
- }
-
- chkp_replace_function_pointers (&i);
-
- switch (gimple_code (s))
- {
- case GIMPLE_ASSIGN:
- chkp_process_stmt (&i, gimple_assign_lhs (s),
- gimple_location (s), integer_one_node,
- NULL_TREE, NULL_TREE, safe);
- chkp_process_stmt (&i, gimple_assign_rhs1 (s),
- gimple_location (s), integer_zero_node,
- NULL_TREE, NULL_TREE, safe);
- grhs_class = get_gimple_rhs_class (gimple_assign_rhs_code (s));
- if (grhs_class == GIMPLE_BINARY_RHS)
- chkp_process_stmt (&i, gimple_assign_rhs2 (s),
- gimple_location (s), integer_zero_node,
- NULL_TREE, NULL_TREE, safe);
- break;
-
- case GIMPLE_RETURN:
- {
- greturn *r = as_a <greturn *> (s);
- if (gimple_return_retval (r) != NULL_TREE)
- {
- chkp_process_stmt (&i, gimple_return_retval (r),
- gimple_location (r),
- integer_zero_node,
- NULL_TREE, NULL_TREE, safe);
-
- /* Additionally we need to add bounds
- to return statement. */
- chkp_add_bounds_to_ret_stmt (&i);
- }
- }
- break;
-
- case GIMPLE_CALL:
- chkp_add_bounds_to_call_stmt (&i);
- break;
-
- default:
- ;
- }
-
- gsi_next (&i);
-
- /* We do not need any actual pointer stores in checker
- static initializer. */
- if (lookup_attribute ("chkp ctor", DECL_ATTRIBUTES (cfun->decl))
- && gimple_code (s) == GIMPLE_ASSIGN
- && gimple_store_p (s))
- {
- gimple_stmt_iterator del_iter = gsi_for_stmt (s);
- gsi_remove (&del_iter, true);
- unlink_stmt_vdef (s);
- release_defs(s);
- }
- }
- bb = next;
- }
- while (bb);
-
- /* Some input params may have bounds and be address taken. In this case
- we should store incoming bounds into bounds table. */
- tree arg;
- if (flag_chkp_store_bounds)
- for (arg = DECL_ARGUMENTS (cfun->decl); arg; arg = DECL_CHAIN (arg))
- if (TREE_ADDRESSABLE (arg))
- {
- if (BOUNDED_P (arg))
- {
- tree bounds = chkp_get_next_bounds_parm (arg);
- tree def_ptr = ssa_default_def (cfun, arg);
- gimple_stmt_iterator iter
- = gsi_start_bb (chkp_get_entry_block ());
- chkp_build_bndstx (chkp_build_addr_expr (arg),
- def_ptr ? def_ptr : arg,
- bounds, &iter);
-
- /* Skip bounds arg. */
- arg = TREE_CHAIN (arg);
- }
- else if (chkp_type_has_pointer (TREE_TYPE (arg)))
- {
- tree orig_arg = arg;
- bitmap slots = BITMAP_ALLOC (NULL);
- gimple_stmt_iterator iter
- = gsi_start_bb (chkp_get_entry_block ());
- bitmap_iterator bi;
- unsigned bnd_no;
-
- chkp_find_bound_slots (TREE_TYPE (arg), slots);
-
- EXECUTE_IF_SET_IN_BITMAP (slots, 0, bnd_no, bi)
- {
- tree bounds = chkp_get_next_bounds_parm (arg);
- HOST_WIDE_INT offs = bnd_no * POINTER_SIZE / BITS_PER_UNIT;
- tree addr = chkp_build_addr_expr (orig_arg);
- tree ptr = build2 (MEM_REF, ptr_type_node, addr,
- build_int_cst (ptr_type_node, offs));
- chkp_build_bndstx (chkp_build_addr_expr (ptr), ptr,
- bounds, &iter);
-
- arg = DECL_CHAIN (arg);
- }
- BITMAP_FREE (slots);
- }
- }
-}
-
-/* Find init/null/copy_ptr_bounds calls and replace them
- with assignments. It should allow better code
- optimization. */
-
-static void
-chkp_remove_useless_builtins ()
-{
- basic_block bb;
- gimple_stmt_iterator gsi;
-
- FOR_EACH_BB_FN (bb, cfun)
- {
- for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
- {
- gimple *stmt = gsi_stmt (gsi);
- tree fndecl;
- enum built_in_function fcode;
-
- /* Find builtins returning first arg and replace
- them with assignments. */
- if (gimple_code (stmt) == GIMPLE_CALL
- && (fndecl = gimple_call_fndecl (stmt))
- && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL
- && (fcode = DECL_FUNCTION_CODE (fndecl))
- && (fcode == BUILT_IN_CHKP_INIT_PTR_BOUNDS
- || fcode == BUILT_IN_CHKP_NULL_PTR_BOUNDS
- || fcode == BUILT_IN_CHKP_COPY_PTR_BOUNDS
- || fcode == BUILT_IN_CHKP_SET_PTR_BOUNDS))
- {
- tree res = gimple_call_arg (stmt, 0);
- update_call_from_tree (&gsi, res);
- stmt = gsi_stmt (gsi);
- update_stmt (stmt);
- }
- }
- }
-}
-
-/* Initialize pass. */
-static void
-chkp_init (void)
-{
- basic_block bb;
- gimple_stmt_iterator i;
-
- in_chkp_pass = true;
-
- for (bb = ENTRY_BLOCK_PTR_FOR_FN (cfun)->next_bb; bb; bb = bb->next_bb)
- for (i = gsi_start_bb (bb); !gsi_end_p (i); gsi_next (&i))
- chkp_unmark_stmt (gsi_stmt (i));
-
- chkp_invalid_bounds = new hash_set<tree>;
- chkp_completed_bounds_set = new hash_set<tree>;
- delete chkp_reg_bounds;
- chkp_reg_bounds = new hash_map<tree, tree>;
- delete chkp_bound_vars;
- chkp_bound_vars = new hash_map<tree, tree>;
- chkp_reg_addr_bounds = new hash_map<tree, tree>;
- chkp_incomplete_bounds_map = new hash_map<tree, tree>;
- delete chkp_bounds_map;
- chkp_bounds_map = new hash_map<tree, tree>;
- chkp_abnormal_copies = BITMAP_GGC_ALLOC ();
-
- entry_block = NULL;
- zero_bounds = NULL_TREE;
- none_bounds = NULL_TREE;
- incomplete_bounds = integer_zero_node;
- tmp_var = NULL_TREE;
- size_tmp_var = NULL_TREE;
-
- chkp_uintptr_type = lang_hooks.types.type_for_mode (ptr_mode, true);
-
- /* We create these constant bounds once for each object file.
- These symbols go to comdat section and result in single copy
- of each one in the final binary. */
- chkp_get_zero_bounds_var ();
- chkp_get_none_bounds_var ();
-
- calculate_dominance_info (CDI_DOMINATORS);
- calculate_dominance_info (CDI_POST_DOMINATORS);
-
- bitmap_obstack_initialize (NULL);
-}
-
-/* Finalize instrumentation pass. */
-static void
-chkp_fini (void)
-{
- in_chkp_pass = false;
-
- delete chkp_invalid_bounds;
- delete chkp_completed_bounds_set;
- delete chkp_reg_addr_bounds;
- delete chkp_incomplete_bounds_map;
-
- free_dominance_info (CDI_DOMINATORS);
- free_dominance_info (CDI_POST_DOMINATORS);
-
- bitmap_obstack_release (NULL);
-
- entry_block = NULL;
- zero_bounds = NULL_TREE;
- none_bounds = NULL_TREE;
-}
-
-/* Main instrumentation pass function. */
-static unsigned int
-chkp_execute (void)
-{
- chkp_init ();
-
- chkp_instrument_function ();
-
- chkp_remove_useless_builtins ();
-
- chkp_function_mark_instrumented (cfun->decl);
-
- chkp_fix_cfg ();
-
- chkp_fini ();
-
- return 0;
-}
-
-/* Instrumentation pass gate. */
-static bool
-chkp_gate (void)
-{
- cgraph_node *node = cgraph_node::get (cfun->decl);
- return ((node != NULL
- && node->instrumentation_clone)
- || lookup_attribute ("chkp ctor", DECL_ATTRIBUTES (cfun->decl)));
-}
-
-namespace {
-
-const pass_data pass_data_chkp =
-{
- GIMPLE_PASS, /* type */
- "chkp", /* name */
- OPTGROUP_NONE, /* optinfo_flags */
- TV_NONE, /* tv_id */
- PROP_ssa | PROP_cfg, /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- TODO_verify_il
- | TODO_update_ssa /* todo_flags_finish */
-};
-
-class pass_chkp : public gimple_opt_pass
-{
-public:
- pass_chkp (gcc::context *ctxt)
- : gimple_opt_pass (pass_data_chkp, ctxt)
- {}
-
- /* opt_pass methods: */
- virtual opt_pass * clone ()
- {
- return new pass_chkp (m_ctxt);
- }
-
- virtual bool gate (function *)
- {
- return chkp_gate ();
- }
-
- virtual unsigned int execute (function *)
- {
- return chkp_execute ();
- }
-
-}; // class pass_chkp
-
-} // anon namespace
-
-gimple_opt_pass *
-make_pass_chkp (gcc::context *ctxt)
-{
- return new pass_chkp (ctxt);
-}
-
-#include "gt-tree-chkp.h"