Hi, This patch adds intrumentation of calls and returns into instrumentation pass.
Thanks, Ilya -- 2014-10-08 Ilya Enkovich <ilya.enkov...@intel.com> * tree-chkp.c (chkp_add_bounds_to_ret_stmt): New. (chkp_replace_address_check_builtin): New. (chkp_replace_extract_builtin): New. (chkp_find_bounds_for_elem): New. (chkp_add_bounds_to_call_stmt): New. (chkp_instrument_function): Instrument rets and calls. diff --git a/gcc/tree-chkp.c b/gcc/tree-chkp.c index bbb6459..6bbceb0 100644 --- a/gcc/tree-chkp.c +++ b/gcc/tree-chkp.c @@ -1047,6 +1047,29 @@ chkp_get_registered_bounds (tree 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) +{ + gimple ret = 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 @@ -1169,6 +1192,64 @@ chkp_check_mem_access (tree first, tree last, tree bounds, 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) @@ -1247,6 +1328,82 @@ chkp_can_be_shared (tree t) return false; } +/* 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, gimple_build_nop (), ""); + 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 = chkp_can_be_shared (elem) + ? elem + : 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 = chkp_can_be_shared (elem) + ? elem + : 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); + } + } +} + /* Fill HAVE_BOUND output bitmap with information about bounds requred for object of type TYPE. @@ -1306,6 +1463,223 @@ chkp_find_bound_slots (const_tree type) return res; } +/* 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) +{ + gimple call = 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; + gimple 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; + + /* Donothing for calls to legacy functions. */ + if (fndecl + && lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (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; + + /* 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; + + /* If function decl is available then use it for + formal arguments list. Otherwise use function type. */ + if (fndecl && DECL_ARGUMENTS (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); + } + 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) + { + tree new_decl = chkp_maybe_create_clone (fndecl)->decl; + gimple_call_set_fndecl (new_call, new_decl); + 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 LB and UB if such var exists in varpool. Return NULL otherwise. */ static tree @@ -3314,7 +3688,8 @@ chkp_replace_function_pointers (gimple_stmt_iterator *gsi) walk_gimple_stmt (gsi, NULL, chkp_replace_function_pointer, NULL); } -/* This function instruments all statements working with memory. */ +/* This function instruments all statements working with memory, + calls and rets. */ static void chkp_instrument_function (void) { @@ -3358,12 +3733,22 @@ chkp_instrument_function (void) case GIMPLE_RETURN: if (gimple_return_retval (s) != NULL_TREE) - chkp_process_stmt (&i, gimple_return_retval (s), - gimple_location (s), - integer_zero_node, - NULL_TREE, NULL_TREE, safe); + { + chkp_process_stmt (&i, gimple_return_retval (s), + gimple_location (s), + integer_zero_node, + NULL_TREE, NULL_TREE, safe); + + /* Additionall 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: ; }