#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "hash-set.h"
-#include "machmode.h"
-#include "vec.h"
-#include "double-int.h"
-#include "input.h"
#include "alias.h"
#include "symtab.h"
#include "options.h"
-#include "wide-int.h"
-#include "inchash.h"
#include "tree.h"
#include "fold-const.h"
#include "stor-layout.h"
#include "tm.h"
#include "hard-reg-set.h"
#include "function.h"
-#include "is-a.h"
+#include "calls.h"
#include "tree-ssa-alias.h"
#include "predict.h"
#include "basic-block.h"
#include "gimple.h"
-#include "ipa-ref.h"
#include "lto-streamer.h"
#include "cgraph.h"
#include "tree-chkp.h"
+#include "tree-inline.h"
#include "ipa-chkp.h"
/* Pointer Bounds Checker has two IPA passes to support code instrumentation.
removed. */
#define CHKP_BOUNDS_OF_SYMBOL_PREFIX "__chkp_bounds_of_"
+#define CHKP_WRAPPER_SYMBOL_PREFIX "__mpx_wrapper_"
+
+/* Return 1 calls to FNDECL should be replaced with
+ a call to wrapper function. */
+bool
+chkp_wrap_function (tree fndecl)
+{
+ if (!flag_chkp_use_wrappers)
+ return false;
+
+ if (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
+ {
+ 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_MALLOC:
+ case BUILT_IN_CALLOC:
+ case BUILT_IN_REALLOC:
+ return 1;
+
+ default:
+ return 0;
+ }
+ }
+
+ return false;
+}
+
+static const char *
+chkp_wrap_function_name (tree fndecl)
+{
+ gcc_assert (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL);
+
+ switch (DECL_FUNCTION_CODE (fndecl))
+ {
+ case BUILT_IN_STRLEN:
+ return CHKP_WRAPPER_SYMBOL_PREFIX "strlen";
+ case BUILT_IN_STRCPY:
+ return CHKP_WRAPPER_SYMBOL_PREFIX "strcpy";
+ case BUILT_IN_STRNCPY:
+ return CHKP_WRAPPER_SYMBOL_PREFIX "strncpy";
+ case BUILT_IN_STPCPY:
+ return CHKP_WRAPPER_SYMBOL_PREFIX "stpcpy";
+ case BUILT_IN_STPNCPY:
+ return CHKP_WRAPPER_SYMBOL_PREFIX "stpncpy";
+ case BUILT_IN_STRCAT:
+ return CHKP_WRAPPER_SYMBOL_PREFIX "strcat";
+ case BUILT_IN_STRNCAT:
+ return CHKP_WRAPPER_SYMBOL_PREFIX "strncat";
+ case BUILT_IN_MEMCPY:
+ return CHKP_WRAPPER_SYMBOL_PREFIX "memcpy";
+ case BUILT_IN_MEMPCPY:
+ return CHKP_WRAPPER_SYMBOL_PREFIX "mempcpy";
+ case BUILT_IN_MEMSET:
+ return CHKP_WRAPPER_SYMBOL_PREFIX "memset";
+ case BUILT_IN_MEMMOVE:
+ return CHKP_WRAPPER_SYMBOL_PREFIX "memmove";
+ case BUILT_IN_BZERO:
+ return CHKP_WRAPPER_SYMBOL_PREFIX "bzero";
+ case BUILT_IN_MALLOC:
+ return CHKP_WRAPPER_SYMBOL_PREFIX "malloc";
+ case BUILT_IN_CALLOC:
+ return CHKP_WRAPPER_SYMBOL_PREFIX "calloc";
+ case BUILT_IN_REALLOC:
+ return CHKP_WRAPPER_SYMBOL_PREFIX "realloc";
+
+ default:
+ gcc_unreachable ();
+ }
+
+ return "";
+}
/* Build a clone of FNDECL with a modified name. */
because it conflicts with decl merging algorithms in LTO.
Achieve the result by using transparent alias name for the
instrumented version. */
- s = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (fndecl));
- s += ".chkp";
- new_name = get_identifier (s.c_str ());
- IDENTIFIER_TRANSPARENT_ALIAS (new_name) = 1;
- TREE_CHAIN (new_name) = DECL_ASSEMBLER_NAME (fndecl);
+ if (chkp_wrap_function(fndecl))
+ {
+ new_name = get_identifier (chkp_wrap_function_name (fndecl));
+ DECL_VISIBILITY (new_decl) = VISIBILITY_DEFAULT;
+ }
+ else
+ {
+ s = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (fndecl));
+ s += ".chkp";
+ new_name = get_identifier (s.c_str ());
+ IDENTIFIER_TRANSPARENT_ALIAS (new_name) = 1;
+ TREE_CHAIN (new_name) = DECL_ASSEMBLER_NAME (fndecl);
+ }
SET_DECL_ASSEMBLER_NAME (new_decl, new_name);
/* For functions with body versioning will make a copy of arguments.
chkp_copy_function_type_adding_bounds (tree orig_type)
{
tree type;
- tree arg_type, attrs, t;
+ tree arg_type, attrs;
unsigned len = list_length (TYPE_ARG_TYPES (orig_type));
unsigned *indexes = XALLOCAVEC (unsigned, len);
unsigned idx = 0, new_idx = 0;
if (!arg_type)
return orig_type;
- type = copy_node (orig_type);
+ type = build_distinct_type_copy (orig_type);
TYPE_ARG_TYPES (type) = copy_list (TYPE_ARG_TYPES (type));
for (arg_type = TYPE_ARG_TYPES (type);
TYPE_ATTRIBUTES (type) = attrs;
}
- t = TYPE_MAIN_VARIANT (orig_type);
- if (orig_type != t)
- {
- TYPE_MAIN_VARIANT (type) = t;
- TYPE_NEXT_VARIANT (type) = TYPE_NEXT_VARIANT (t);
- TYPE_NEXT_VARIANT (t) = type;
- }
- else
- {
- TYPE_MAIN_VARIANT (type) = type;
- TYPE_NEXT_VARIANT (type) = NULL;
- }
-
-
return type;
}
return clone;
}
+/* Return 1 if function FNDECL should be instrumented. */
+
+bool
+chkp_instrumentable_p (tree fndecl)
+{
+ struct function *fn = DECL_STRUCT_FUNCTION (fndecl);
+ return (!lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (fndecl))
+ && (!flag_chkp_instrument_marked_only
+ || lookup_attribute ("bnd_instrument", DECL_ATTRIBUTES (fndecl)))
+ && (!fn || !copy_forbidden (fn, fndecl)));
+}
+
/* Return clone created for instrumentation of NODE or NULL. */
cgraph_node *
if (gimple_has_body_p (fndecl))
{
- /* If function will not be instrumented, then it's instrumented
- version is a thunk for the original. */
- if (lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (fndecl))
- || (flag_chkp_instrument_marked_only
- && !lookup_attribute ("bnd_instrument", DECL_ATTRIBUTES (fndecl))))
- {
- clone->thunk.thunk_p = true;
- clone->thunk.add_pointer_bounds_args = true;
- clone->create_edge (node, NULL, 0, CGRAPH_FREQ_BASE);
- }
- else
- {
- tree_function_versioning (fndecl, new_decl, NULL, false,
- NULL, false, NULL, NULL);
- clone->lowered = true;
- }
+ gcc_assert (chkp_instrumentable_p (fndecl));
+ tree_function_versioning (fndecl, new_decl, NULL, false,
+ NULL, false, NULL, NULL);
+ clone->lowered = true;
}
/* New params are inserted after versioning because it
/* Clones have the same comdat group as originals. */
if (node->same_comdat_group
- || DECL_ONE_ONLY (node->decl))
+ || (DECL_ONE_ONLY (node->decl)
+ && !DECL_EXTERNAL (node->decl)))
clone->add_to_same_comdat_group (node);
if (gimple_has_body_p (fndecl))
symtab->call_cgraph_insertion_hooks (clone);
/* Clone all aliases. */
- for (i = 0; node->iterate_referring (i, ref); i++)
- if (ref->use == IPA_REF_ALIAS)
- {
- struct cgraph_node *alias = dyn_cast <cgraph_node *> (ref->referring);
- struct cgraph_node *chkp_alias
- = chkp_maybe_create_clone (alias->decl);
- chkp_alias->create_reference (clone, IPA_REF_ALIAS, NULL);
- }
+ for (i = 0; node->iterate_direct_aliases (i, ref); i++)
+ chkp_maybe_create_clone (ref->referring->decl);
/* Clone all thunks. */
for (e = node->callers; e; e = e->next_caller)
- if (e->caller->thunk.thunk_p)
+ if (e->caller->thunk.thunk_p
+ && !e->caller->thunk.add_pointer_bounds_args
+ && !e->caller->instrumentation_clone)
{
struct cgraph_node *thunk
= chkp_maybe_create_clone (e->caller->decl);
ref = node->ref_list.first_reference ();
if (ref)
- chkp_maybe_create_clone (ref->referred->decl);
+ {
+ target = chkp_maybe_create_clone (ref->referred->decl);
+ clone->create_reference (target, IPA_REF_ALIAS);
+ }
if (node->alias_target)
{
chkp_versioning (void)
{
struct cgraph_node *node;
+ const char *reason;
bitmap_obstack_initialize (NULL);
&& !node->instrumented_version
&& !node->alias
&& !node->thunk.thunk_p
- && !lookup_attribute ("bnd_legacy", DECL_ATTRIBUTES (node->decl))
- && (!flag_chkp_instrument_marked_only
- || lookup_attribute ("bnd_instrument",
- DECL_ATTRIBUTES (node->decl)))
&& (!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);
+ {
+ if (chkp_instrumentable_p (node->decl))
+ chkp_maybe_create_clone (node->decl);
+ else if ((reason = copy_forbidden (DECL_STRUCT_FUNCTION (node->decl),
+ node->decl)))
+ {
+ if (warning_at (DECL_SOURCE_LOCATION (node->decl), OPT_Wchkp,
+ "function cannot be instrumented"))
+ inform (DECL_SOURCE_LOCATION (node->decl), reason, node->decl);
+ }
+ }
}
/* Mark all aliases and thunks of functions with no instrumented
function. */
static unsigned int
-chkp_produce_thunks (void)
+chkp_produce_thunks (bool early)
{
struct cgraph_node *node;
if (!node->instrumentation_clone
&& node->instrumented_version
&& gimple_has_body_p (node->decl)
- && gimple_has_body_p (node->instrumented_version->decl))
+ && gimple_has_body_p (node->instrumented_version->decl)
+ && (!lookup_attribute ("always_inline", DECL_ATTRIBUTES (node->decl))
+ || !early))
{
node->release_body ();
node->remove_callees ();
0, CGRAPH_FREQ_BASE);
node->create_reference (node->instrumented_version,
IPA_REF_CHKP, NULL);
+ /* Thunk shouldn't be a cdtor. */
+ DECL_STATIC_CONSTRUCTOR (node->decl) = 0;
+ DECL_STATIC_DESTRUCTOR (node->decl) = 0;
}
}
/* Mark instrumentation clones created for aliases and thunks
as insttrumented so they could be removed as unreachable
now. */
- FOR_EACH_DEFINED_FUNCTION (node)
+ if (!early)
{
- if (node->instrumentation_clone
- && (node->alias || node->thunk.thunk_p)
- && !chkp_function_instrumented_p (node->decl))
- chkp_function_mark_instrumented (node->decl);
+ FOR_EACH_DEFINED_FUNCTION (node)
+ {
+ if (node->instrumentation_clone
+ && (node->alias || node->thunk.thunk_p)
+ && !chkp_function_instrumented_p (node->decl))
+ chkp_function_mark_instrumented (node->decl);
+ }
}
return TODO_remove_functions;
0 /* todo_flags_finish */
};
+const pass_data pass_data_ipa_chkp_early_produce_thunks =
+{
+ SIMPLE_IPA_PASS, /* type */
+ "chkp_ecleanup", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ TV_NONE, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ 0 /* todo_flags_finish */
+};
+
const pass_data pass_data_ipa_chkp_produce_thunks =
{
SIMPLE_IPA_PASS, /* type */
}; // class pass_ipa_chkp_versioning
+class pass_ipa_chkp_early_produce_thunks : public simple_ipa_opt_pass
+{
+public:
+ pass_ipa_chkp_early_produce_thunks (gcc::context *ctxt)
+ : simple_ipa_opt_pass (pass_data_ipa_chkp_early_produce_thunks, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ virtual opt_pass * clone ()
+ {
+ return new pass_ipa_chkp_early_produce_thunks (m_ctxt);
+ }
+
+ virtual bool gate (function *)
+ {
+ return flag_check_pointer_bounds;
+ }
+
+ virtual unsigned int execute (function *)
+ {
+ return chkp_produce_thunks (true);
+ }
+
+}; // class pass_chkp_produce_thunks
+
class pass_ipa_chkp_produce_thunks : public simple_ipa_opt_pass
{
public:
virtual unsigned int execute (function *)
{
- return chkp_produce_thunks ();
+ return chkp_produce_thunks (false);
}
}; // class pass_chkp_produce_thunks
return new pass_ipa_chkp_versioning (ctxt);
}
+simple_ipa_opt_pass *
+make_pass_ipa_chkp_early_produce_thunks (gcc::context *ctxt)
+{
+ return new pass_ipa_chkp_early_produce_thunks (ctxt);
+}
+
simple_ipa_opt_pass *
make_pass_ipa_chkp_produce_thunks (gcc::context *ctxt)
{