diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 660838cde78..123166c9562 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,31 @@ +2014-11-17 Ilya Enkovich + + * tree-core.h (built_in_class): Add builtin codes to be used + by Pointer Bounds Checker for instrumented builtin functions. + * tree-streamer-in.c: Include ipa-chkp.h. + (streamer_get_builtin_tree): Created instrumented decl if + required. + * ipa-chkp.h (chkp_maybe_clone_builtin_fndecl): New. + * ipa-chkp.c (chkp_build_instrumented_fndecl): Support builtin + function decls. + (chkp_maybe_clone_builtin_fndecl): New. + (chkp_maybe_create_clone): Support builtin function decls. + (chkp_versioning): Clone builtin functions. + * tree-chkp.c (chkp_instrument_normal_builtin): New. + (chkp_add_bounds_to_call_stmt): Support builtin functions. + (chkp_replace_function_pointer): Likewise. + * builtins.c (expand_builtin_memcpy_args): New. + (expand_builtin_memcpy): Call expand_builtin_memcpy_args. + (expand_builtin_memcpy_with_bounds): New. + (expand_builtin_mempcpy_with_bounds): New. + (expand_builtin_mempcpy_args): Add orig_exp arg. Support + BUILT_IN_CHKP_MEMCPY_NOBND_NOCHK + (expand_builtin_memset_with_bounds): New. + (expand_builtin_memset_args): Support BUILT_IN_CHKP_MEMSET_NOBND_NOCHK. + (expand_builtin_with_bounds): New. + * builtins.h (expand_builtin_with_bounds): New. + * expr.c (expand_expr_real_1): Support instrumented builtin calls. + 2014-11-17 Dodji Seketeli * gimple.h (gimple_set_visited, gimple_visited_p) diff --git a/gcc/builtins.c b/gcc/builtins.c index 311c0e38279..7ec2d5f8e8c 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -132,15 +132,19 @@ static rtx expand_builtin_strcmp (tree, rtx); static rtx expand_builtin_strncmp (tree, rtx, machine_mode); static rtx builtin_memcpy_read_str (void *, HOST_WIDE_INT, machine_mode); static rtx expand_builtin_memcpy (tree, rtx); +static rtx expand_builtin_memcpy_with_bounds (tree, rtx); +static rtx expand_builtin_memcpy_args (tree, tree, tree, rtx, tree); static rtx expand_builtin_mempcpy (tree, rtx, machine_mode); +static rtx expand_builtin_mempcpy_with_bounds (tree, rtx, machine_mode); static rtx expand_builtin_mempcpy_args (tree, tree, tree, rtx, - machine_mode, int); + machine_mode, int, tree); static rtx expand_builtin_strcpy (tree, rtx); static rtx expand_builtin_strcpy_args (tree, tree, rtx); static rtx expand_builtin_stpcpy (tree, rtx, machine_mode); static rtx expand_builtin_strncpy (tree, rtx); static rtx builtin_memset_gen_str (void *, HOST_WIDE_INT, machine_mode); static rtx expand_builtin_memset (tree, rtx, machine_mode); +static rtx expand_builtin_memset_with_bounds (tree, rtx, machine_mode); static rtx expand_builtin_memset_args (tree, tree, tree, rtx, machine_mode, tree); static rtx expand_builtin_bzero (tree); static rtx expand_builtin_strlen (tree, rtx, machine_mode); @@ -3175,6 +3179,81 @@ determine_block_size (tree len, rtx len_rtx, GET_MODE_MASK (GET_MODE (len_rtx))); } +/* Helper function to do the actual work for expand_builtin_memcpy. */ + +static rtx +expand_builtin_memcpy_args (tree dest, tree src, tree len, rtx target, tree exp) +{ + const char *src_str; + unsigned int src_align = get_pointer_alignment (src); + unsigned int dest_align = get_pointer_alignment (dest); + rtx dest_mem, src_mem, dest_addr, len_rtx; + HOST_WIDE_INT expected_size = -1; + unsigned int expected_align = 0; + unsigned HOST_WIDE_INT min_size; + unsigned HOST_WIDE_INT max_size; + unsigned HOST_WIDE_INT probable_max_size; + + /* If DEST is not a pointer type, call the normal function. */ + if (dest_align == 0) + return NULL_RTX; + + /* If either SRC is not a pointer type, don't do this + operation in-line. */ + if (src_align == 0) + return NULL_RTX; + + if (currently_expanding_gimple_stmt) + stringop_block_profile (currently_expanding_gimple_stmt, + &expected_align, &expected_size); + + if (expected_align < dest_align) + expected_align = dest_align; + dest_mem = get_memory_rtx (dest, len); + set_mem_align (dest_mem, dest_align); + len_rtx = expand_normal (len); + determine_block_size (len, len_rtx, &min_size, &max_size, + &probable_max_size); + src_str = c_getstr (src); + + /* If SRC is a string constant and block move would be done + by pieces, we can avoid loading the string from memory + and only stored the computed constants. */ + if (src_str + && CONST_INT_P (len_rtx) + && (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= strlen (src_str) + 1 + && can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str, + CONST_CAST (char *, src_str), + dest_align, false)) + { + dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx), + builtin_memcpy_read_str, + CONST_CAST (char *, src_str), + dest_align, false, 0); + dest_mem = force_operand (XEXP (dest_mem, 0), target); + dest_mem = convert_memory_address (ptr_mode, dest_mem); + return dest_mem; + } + + src_mem = get_memory_rtx (src, len); + set_mem_align (src_mem, src_align); + + /* Copy word part most expediently. */ + dest_addr = emit_block_move_hints (dest_mem, src_mem, len_rtx, + CALL_EXPR_TAILCALL (exp) + ? BLOCK_OP_TAILCALL : BLOCK_OP_NORMAL, + expected_align, expected_size, + min_size, max_size, probable_max_size); + + if (dest_addr == 0) + { + dest_addr = force_operand (XEXP (dest_mem, 0), target); + dest_addr = convert_memory_address (ptr_mode, dest_addr); + } + + return dest_addr; +} + /* Expand a call EXP to the memcpy builtin. Return NULL_RTX if we failed, the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient (and in @@ -3191,73 +3270,38 @@ expand_builtin_memcpy (tree exp, rtx target) tree dest = CALL_EXPR_ARG (exp, 0); tree src = CALL_EXPR_ARG (exp, 1); tree len = CALL_EXPR_ARG (exp, 2); - const char *src_str; - unsigned int src_align = get_pointer_alignment (src); - unsigned int dest_align = get_pointer_alignment (dest); - rtx dest_mem, src_mem, dest_addr, len_rtx; - HOST_WIDE_INT expected_size = -1; - unsigned int expected_align = 0; - unsigned HOST_WIDE_INT min_size; - unsigned HOST_WIDE_INT max_size; - unsigned HOST_WIDE_INT probable_max_size; + return expand_builtin_memcpy_args (dest, src, len, target, exp); + } +} - /* If DEST is not a pointer type, call the normal function. */ - if (dest_align == 0) - return NULL_RTX; +/* Expand an instrumented call EXP to the memcpy builtin. + Return NULL_RTX if we failed, the caller should emit a normal call, + otherwise try to get the result in TARGET, if convenient (and in + mode MODE if that's convenient). */ - /* If either SRC is not a pointer type, don't do this - operation in-line. */ - if (src_align == 0) - return NULL_RTX; +static rtx +expand_builtin_memcpy_with_bounds (tree exp, rtx target) +{ + if (!validate_arglist (exp, + POINTER_TYPE, POINTER_BOUNDS_TYPE, + POINTER_TYPE, POINTER_BOUNDS_TYPE, + INTEGER_TYPE, VOID_TYPE)) + return NULL_RTX; + else + { + tree dest = CALL_EXPR_ARG (exp, 0); + tree src = CALL_EXPR_ARG (exp, 2); + tree len = CALL_EXPR_ARG (exp, 4); + rtx res = expand_builtin_memcpy_args (dest, src, len, target, exp); - if (currently_expanding_gimple_stmt) - stringop_block_profile (currently_expanding_gimple_stmt, - &expected_align, &expected_size); - - if (expected_align < dest_align) - expected_align = dest_align; - dest_mem = get_memory_rtx (dest, len); - set_mem_align (dest_mem, dest_align); - len_rtx = expand_normal (len); - determine_block_size (len, len_rtx, &min_size, &max_size, - &probable_max_size); - src_str = c_getstr (src); - - /* If SRC is a string constant and block move would be done - by pieces, we can avoid loading the string from memory - and only stored the computed constants. */ - if (src_str - && CONST_INT_P (len_rtx) - && (unsigned HOST_WIDE_INT) INTVAL (len_rtx) <= strlen (src_str) + 1 - && can_store_by_pieces (INTVAL (len_rtx), builtin_memcpy_read_str, - CONST_CAST (char *, src_str), - dest_align, false)) + /* Return src bounds with the result. */ + if (res) { - dest_mem = store_by_pieces (dest_mem, INTVAL (len_rtx), - builtin_memcpy_read_str, - CONST_CAST (char *, src_str), - dest_align, false, 0); - dest_mem = force_operand (XEXP (dest_mem, 0), target); - dest_mem = convert_memory_address (ptr_mode, dest_mem); - return dest_mem; + rtx bnd = force_reg (BNDmode, + expand_normal (CALL_EXPR_ARG (exp, 1))); + res = chkp_join_splitted_slot (res, bnd); } - - src_mem = get_memory_rtx (src, len); - set_mem_align (src_mem, src_align); - - /* Copy word part most expediently. */ - dest_addr = emit_block_move_hints (dest_mem, src_mem, len_rtx, - CALL_EXPR_TAILCALL (exp) - ? BLOCK_OP_TAILCALL : BLOCK_OP_NORMAL, - expected_align, expected_size, - min_size, max_size, probable_max_size); - - if (dest_addr == 0) - { - dest_addr = force_operand (XEXP (dest_mem, 0), target); - dest_addr = convert_memory_address (ptr_mode, dest_addr); - } - return dest_addr; + return res; } } @@ -3281,7 +3325,40 @@ expand_builtin_mempcpy (tree exp, rtx target, machine_mode mode) tree src = CALL_EXPR_ARG (exp, 1); tree len = CALL_EXPR_ARG (exp, 2); return expand_builtin_mempcpy_args (dest, src, len, - target, mode, /*endp=*/ 1); + target, mode, /*endp=*/ 1, + exp); + } +} + +/* Expand an instrumented call EXP to the mempcpy builtin. + Return NULL_RTX if we failed, the caller should emit a normal call, + otherwise try to get the result in TARGET, if convenient (and in + mode MODE if that's convenient). */ + +static rtx +expand_builtin_mempcpy_with_bounds (tree exp, rtx target, machine_mode mode) +{ + if (!validate_arglist (exp, + POINTER_TYPE, POINTER_BOUNDS_TYPE, + POINTER_TYPE, POINTER_BOUNDS_TYPE, + INTEGER_TYPE, VOID_TYPE)) + return NULL_RTX; + else + { + tree dest = CALL_EXPR_ARG (exp, 0); + tree src = CALL_EXPR_ARG (exp, 2); + tree len = CALL_EXPR_ARG (exp, 4); + rtx res = expand_builtin_mempcpy_args (dest, src, len, target, + mode, 1, exp); + + /* Return src bounds with the result. */ + if (res) + { + rtx bnd = force_reg (BNDmode, + expand_normal (CALL_EXPR_ARG (exp, 1))); + res = chkp_join_splitted_slot (res, bnd); + } + return res; } } @@ -3293,10 +3370,23 @@ expand_builtin_mempcpy (tree exp, rtx target, machine_mode mode) static rtx expand_builtin_mempcpy_args (tree dest, tree src, tree len, - rtx target, machine_mode mode, int endp) + rtx target, machine_mode mode, int endp, + tree orig_exp) { + tree fndecl = get_callee_fndecl (orig_exp); + /* If return value is ignored, transform mempcpy into memcpy. */ - if (target == const0_rtx && builtin_decl_implicit_p (BUILT_IN_MEMCPY)) + if (target == const0_rtx + && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_CHKP_MEMPCPY_NOBND_NOCHK_CHKP + && builtin_decl_implicit_p (BUILT_IN_CHKP_MEMCPY_NOBND_NOCHK_CHKP)) + { + tree fn = builtin_decl_implicit (BUILT_IN_CHKP_MEMCPY_NOBND_NOCHK_CHKP); + tree result = build_call_nofold_loc (UNKNOWN_LOCATION, fn, 3, + dest, src, len); + return expand_expr (result, target, mode, EXPAND_NORMAL); + } + else if (target == const0_rtx + && builtin_decl_implicit_p (BUILT_IN_MEMCPY)) { tree fn = builtin_decl_implicit (BUILT_IN_MEMCPY); tree result = build_call_nofold_loc (UNKNOWN_LOCATION, fn, 3, @@ -3481,7 +3571,8 @@ expand_builtin_stpcpy (tree exp, rtx target, machine_mode mode) lenp1 = size_binop_loc (loc, PLUS_EXPR, len, ssize_int (1)); ret = expand_builtin_mempcpy_args (dst, src, lenp1, - target, mode, /*endp=*/2); + target, mode, /*endp=*/2, + exp); if (ret) return ret; @@ -3647,6 +3738,36 @@ expand_builtin_memset (tree exp, rtx target, machine_mode mode) } } +/* Expand expression EXP, which is an instrumented call to the memset builtin. + Return NULL_RTX if we failed the caller should emit a normal call, otherwise + try to get the result in TARGET, if convenient (and in mode MODE if that's + convenient). */ + +static rtx +expand_builtin_memset_with_bounds (tree exp, rtx target, machine_mode mode) +{ + if (!validate_arglist (exp, + POINTER_TYPE, POINTER_BOUNDS_TYPE, + INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return NULL_RTX; + else + { + tree dest = CALL_EXPR_ARG (exp, 0); + tree val = CALL_EXPR_ARG (exp, 2); + tree len = CALL_EXPR_ARG (exp, 3); + rtx res = expand_builtin_memset_args (dest, val, len, target, mode, exp); + + /* Return src bounds with the result. */ + if (res) + { + rtx bnd = force_reg (BNDmode, + expand_normal (CALL_EXPR_ARG (exp, 1))); + res = chkp_join_splitted_slot (res, bnd); + } + return res; + } +} + /* Helper function to do the actual work for expand_builtin_memset. The arguments to the builtin_memset call DEST, VAL, and LEN are broken out so that this can also be called without constructing an actual CALL_EXPR. @@ -3775,7 +3896,8 @@ expand_builtin_memset_args (tree dest, tree val, tree len, do_libcall: fndecl = get_callee_fndecl (orig_exp); fcode = DECL_FUNCTION_CODE (fndecl); - if (fcode == BUILT_IN_MEMSET) + if (fcode == BUILT_IN_MEMSET + || fcode == BUILT_IN_CHKP_MEMSET_NOBND_NOCHK_CHKP) fn = build_call_nofold_loc (EXPR_LOCATION (orig_exp), fndecl, 3, dest, val, len); else if (fcode == BUILT_IN_BZERO) @@ -5848,6 +5970,8 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode, } } + /* expand_builtin_with_bounds is supposed to be used for + instrumented builtin calls. */ gcc_assert (!CALL_WITH_BOUNDS_P (exp)); switch (fcode) @@ -6908,6 +7032,53 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode, return expand_call (exp, target, ignore); } +/* Similar to expand_builtin but is used for instrumented calls. */ + +rtx +expand_builtin_with_bounds (tree exp, rtx target, + rtx subtarget ATTRIBUTE_UNUSED, + machine_mode mode, int ignore) +{ + tree fndecl = get_callee_fndecl (exp); + enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); + + gcc_assert (CALL_WITH_BOUNDS_P (exp)); + + if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_MD) + return targetm.expand_builtin (exp, target, subtarget, mode, ignore); + + gcc_assert (fcode > BEGIN_CHKP_BUILTINS + && fcode < END_CHKP_BUILTINS); + + switch (fcode) + { + case BUILT_IN_CHKP_MEMCPY_NOBND_NOCHK_CHKP: + target = expand_builtin_memcpy_with_bounds (exp, target); + if (target) + return target; + break; + + case BUILT_IN_CHKP_MEMPCPY_NOBND_NOCHK_CHKP: + target = expand_builtin_mempcpy_with_bounds (exp, target, mode); + if (target) + return target; + break; + + case BUILT_IN_CHKP_MEMSET_NOBND_NOCHK_CHKP: + target = expand_builtin_memset_with_bounds (exp, target, mode); + if (target) + return target; + break; + + default: + break; + } + + /* The switch statement above can drop through to cause the function + to be called normally. */ + return expand_call (exp, target, ignore); + } + /* Determine whether a tree node represents a call to a built-in function. If the tree T is a call to a built-in function with the right number of arguments of the appropriate types, return diff --git a/gcc/builtins.h b/gcc/builtins.h index 7960b0158db..44bc5dfb2e1 100644 --- a/gcc/builtins.h +++ b/gcc/builtins.h @@ -69,6 +69,7 @@ extern tree std_canonical_va_list_type (tree); extern void std_expand_builtin_va_start (tree, rtx); extern void expand_builtin_trap (void); extern rtx expand_builtin (tree, rtx, rtx, machine_mode, int); +extern rtx expand_builtin_with_bounds (tree, rtx, rtx, machine_mode, int); extern enum built_in_function builtin_mathfn_code (const_tree); extern tree fold_builtin_expect (location_t, tree, tree, tree); extern tree fold_fma (location_t, tree, tree, tree, tree); diff --git a/gcc/expr.c b/gcc/expr.c index 093f544a993..c7621b0e9c8 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -10462,7 +10462,11 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode tmode, if (fndecl && DECL_BUILT_IN (fndecl)) { gcc_assert (DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_FRONTEND); - return expand_builtin (exp, target, subtarget, tmode, ignore); + if (CALL_WITH_BOUNDS_P (exp)) + return expand_builtin_with_bounds (exp, target, subtarget, + tmode, ignore); + else + return expand_builtin (exp, target, subtarget, tmode, ignore); } } return expand_call (exp, target, ignore); diff --git a/gcc/ipa-chkp.c b/gcc/ipa-chkp.c index 19a989453b6..46b2139758a 100644 --- a/gcc/ipa-chkp.c +++ b/gcc/ipa-chkp.c @@ -129,6 +129,16 @@ chkp_build_instrumented_fndecl (tree fndecl) make own copy. */ DECL_ATTRIBUTES (new_decl) = copy_list (DECL_ATTRIBUTES (fndecl)); + /* Change builtin function code. */ + if (DECL_BUILT_IN (new_decl)) + { + gcc_assert (DECL_BUILT_IN_CLASS (new_decl) == BUILT_IN_NORMAL); + gcc_assert (DECL_FUNCTION_CODE (new_decl) < BEGIN_CHKP_BUILTINS); + DECL_FUNCTION_CODE (new_decl) + = (enum built_in_function)(DECL_FUNCTION_CODE (new_decl) + + BEGIN_CHKP_BUILTINS + 1); + } + return new_decl; } @@ -354,6 +364,33 @@ chkp_add_bounds_params_to_function (tree fndecl) chkp_copy_function_type_adding_bounds (TREE_TYPE (fndecl)); } +/* Return an instrumentation clone for builtin function + FNDECL. Create one if needed. */ + +tree +chkp_maybe_clone_builtin_fndecl (tree fndecl) +{ + tree clone; + enum built_in_function fcode = DECL_FUNCTION_CODE (fndecl); + + gcc_assert (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL + && fcode < BEGIN_CHKP_BUILTINS); + + fcode = (enum built_in_function) (fcode + BEGIN_CHKP_BUILTINS + 1); + clone = builtin_decl_explicit (fcode); + if (clone) + return clone; + + clone = chkp_build_instrumented_fndecl (fndecl); + chkp_add_bounds_params_to_function (clone); + + gcc_assert (DECL_FUNCTION_CODE (clone) == fcode); + + set_builtin_decl (fcode, clone, false); + + return clone; +} + /* Return clone created for instrumentation of NODE or NULL. */ cgraph_node * @@ -364,6 +401,54 @@ chkp_maybe_create_clone (tree fndecl) gcc_assert (!node->instrumentation_clone); + if (DECL_BUILT_IN (fndecl) + && (DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL + || DECL_FUNCTION_CODE (fndecl) >= BEGIN_CHKP_BUILTINS)) + return NULL; + + clone = node->instrumented_version; + + /* Some instrumented builtin function calls may be optimized and + cgraph nodes may be removed as unreachable. Later optimizations + may generate new calls to removed functions and in this case + we have to recreate cgraph node. FUNCTION_DECL for instrumented + builtin still exists and should be reused in such case. */ + if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL + && fndecl == builtin_decl_explicit (DECL_FUNCTION_CODE (fndecl)) + && !clone) + { + enum built_in_function fncode = DECL_FUNCTION_CODE (fndecl); + tree new_decl; + + fncode = (enum built_in_function) (fncode + BEGIN_CHKP_BUILTINS + 1); + new_decl = builtin_decl_explicit (fncode); + + /* We've actually already created an instrumented clone once. + Restore it. */ + if (new_decl) + { + clone = cgraph_node::get (new_decl); + + if (!clone) + { + gcc_assert (!gimple_has_body_p (fndecl)); + clone = cgraph_node::get_create (new_decl); + clone->externally_visible = node->externally_visible; + clone->local = node->local; + clone->address_taken = node->address_taken; + clone->thunk = node->thunk; + clone->alias = node->alias; + clone->weakref = node->weakref; + clone->cpp_implicit_alias = node->cpp_implicit_alias; + clone->orig_decl = fndecl; + clone->instrumentation_clone = true; + } + + clone->instrumented_version = node; + node->instrumented_version = clone; + } + } + if (!clone) { tree new_decl = chkp_build_instrumented_fndecl (fndecl); @@ -408,6 +493,15 @@ chkp_maybe_create_clone (tree fndecl) actually copies args list from the original decl. */ chkp_add_bounds_params_to_function (new_decl); + /* Remember builtin fndecl. */ + if (DECL_BUILT_IN_CLASS (clone->decl) == BUILT_IN_NORMAL + && fndecl == builtin_decl_explicit (DECL_FUNCTION_CODE (fndecl))) + { + gcc_assert (!builtin_decl_explicit (DECL_FUNCTION_CODE (clone->decl))); + set_builtin_decl (DECL_FUNCTION_CODE (clone->decl), + clone->decl, false); + } + /* Clones have the same comdat group as originals. */ if (node->same_comdat_group || DECL_ONE_ONLY (node->decl)) @@ -487,8 +581,9 @@ chkp_versioning (void) && (!flag_chkp_instrument_marked_only || lookup_attribute ("bnd_instrument", DECL_ATTRIBUTES (node->decl))) - /* No builtins instrumentation for now. */ - && DECL_BUILT_IN_CLASS (node->decl) == NOT_BUILT_IN) + && (!DECL_BUILT_IN (node->decl) + || (DECL_BUILT_IN_CLASS (node->decl) == BUILT_IN_NORMAL + && DECL_FUNCTION_CODE (node->decl) < BEGIN_CHKP_BUILTINS))) chkp_maybe_create_clone (node->decl); } diff --git a/gcc/ipa-chkp.h b/gcc/ipa-chkp.h index d4ad113893a..b2d03ad194a 100644 --- a/gcc/ipa-chkp.h +++ b/gcc/ipa-chkp.h @@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see #define GCC_IPA_CHKP_H extern tree chkp_copy_function_type_adding_bounds (tree orig_type); +extern tree chkp_maybe_clone_builtin_fndecl (tree fndecl); extern cgraph_node *chkp_maybe_create_clone (tree fndecl); #endif /* GCC_IPA_CHKP_H */ diff --git a/gcc/tree-chkp.c b/gcc/tree-chkp.c index df7d425fe66..0fb78ccf076 100644 --- a/gcc/tree-chkp.c +++ b/gcc/tree-chkp.c @@ -1586,6 +1586,50 @@ chkp_find_bound_slots (const_tree type, bitmap 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. */ @@ -1619,7 +1663,7 @@ chkp_add_bounds_to_call_stmt (gimple_stmt_iterator *gsi) && DECL_FUNCTION_CODE (fndecl) == BUILT_IN_OBJECT_SIZE) return; - /* Donothing for calls to legacy functions. */ + /* Do nothing for calls to legacy functions. */ if (fndecl && lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (fndecl))) return; @@ -1686,11 +1730,20 @@ chkp_add_bounds_to_call_stmt (gimple_stmt_iterator *gsi) if (!flag_chkp_instrument_calls) return; - /* Avoid instrumented builtin functions for now. Due to IPA - it also means we have to avoid instrumentation of indirect - calls. */ - if (fndecl && DECL_BUILT_IN_CLASS (fndecl) != NOT_BUILT_IN) - 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. */ @@ -1764,14 +1817,6 @@ chkp_add_bounds_to_call_stmt (gimple_stmt_iterator *gsi) } new_args.release (); - /* If we call built-in function and pass no bounds then - we do not need to change anything. */ - if (new_call == call - && fndecl - && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL - && fndecl == builtin_decl_explicit (DECL_FUNCTION_CODE (fndecl))) - return; - /* For direct calls fndecl is replaced with instrumented version. */ if (fndecl) { @@ -3905,15 +3950,21 @@ chkp_replace_function_pointer (tree *op, int *walk_subtrees, { if (TREE_CODE (*op) == FUNCTION_DECL && !lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (*op)) - /* Do not replace builtins for now. */ - && DECL_BUILT_IN_CLASS (*op) == NOT_BUILT_IN) + && (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) - chkp_maybe_create_clone (*op); + clone = chkp_maybe_create_clone (*op); - *op = node->instrumented_version->decl; + if (clone) + *op = clone->decl; *walk_subtrees = 0; } diff --git a/gcc/tree-core.h b/gcc/tree-core.h index 58bdffff6ad..fe3fbb4e389 100644 --- a/gcc/tree-core.h +++ b/gcc/tree-core.h @@ -168,6 +168,14 @@ enum built_in_class { enum built_in_function { #include "builtins.def" + BEGIN_CHKP_BUILTINS, + +#undef DEF_BUILTIN +#define DEF_BUILTIN(ENUM, N, C, T, LT, B, F, NA, AT, IM, COND) ENUM##_CHKP, +#include "builtins.def" + + END_CHKP_BUILTINS, + /* Complex division routines in libgcc. These are done via builtins because emit_library_call_value can't handle complex values. */ BUILT_IN_COMPLEX_MUL_MIN, diff --git a/gcc/tree-streamer-in.c b/gcc/tree-streamer-in.c index a11a46e168d..fba60483f5e 100644 --- a/gcc/tree-streamer-in.c +++ b/gcc/tree-streamer-in.c @@ -49,6 +49,7 @@ along with GCC; see the file COPYING3. If not see #include "streamer-hooks.h" #include "lto-streamer.h" #include "builtins.h" +#include "ipa-chkp.h" /* Read a STRING_CST from the string table in DATA_IN using input block IB. */ @@ -1113,6 +1114,14 @@ streamer_get_builtin_tree (struct lto_input_block *ib, struct data_in *data_in) if (fcode >= END_BUILTINS) fatal_error ("machine independent builtin code out of range"); result = builtin_decl_explicit (fcode); + if (!result + && fcode > BEGIN_CHKP_BUILTINS + && fcode < END_CHKP_BUILTINS) + { + fcode = (enum built_in_function) (fcode - BEGIN_CHKP_BUILTINS - 1); + result = builtin_decl_explicit (fcode); + result = chkp_maybe_clone_builtin_fndecl (result); + } gcc_assert (result); } else if (fclass == BUILT_IN_MD)