@@ -2845,6 +2845,18 @@ expand_call_stmt (gcall *stmt)
add_reg_note (last, REG_CALL_NOCF_CHECK, const0_rtx);
}
+ if (flag_sanitize & SANITIZE_KERNEL_CONTROL_FLOW_INTEGRITY)
+ {
+ rtx_insn *last = get_last_insn ();
+ rtx datum = gen_rtx_CONST_INT (SImode, stmt->cfi_typeid);
+ while (!CALL_P (last)
+ && last != before_call)
+ last = PREV_INSN (last);
+
+ if (last != before_call)
+ add_reg_note (last, REG_CALL_CFI_TYPEID, datum);
+ }
+
mark_transaction_restart_calls (stmt);
}
@@ -6923,10 +6935,16 @@ pass_expand::execute (function *fun)
if (crtl->tail_call_emit)
fixup_tail_calls ();
+ crtl->is_kcfi_enabled
+ = sanitize_flags_p (SANITIZE_KERNEL_CONTROL_FLOW_INTEGRITY,
+ current_function_decl);
+
HOST_WIDE_INT patch_area_size, patch_area_entry;
parse_and_check_patch_area (flag_patchable_function_entry, false,
&patch_area_size, &patch_area_entry);
+ HOST_WIDE_INT patch_area_entry_org = patch_area_entry;
+
tree patchable_function_entry_attr
= lookup_attribute ("patchable_function_entry",
DECL_ATTRIBUTES (cfun->decl));
@@ -6954,6 +6972,14 @@ pass_expand::execute (function *fun)
patch_area_entry = 0;
}
+ if (crtl->is_kcfi_enabled
+ && (patch_area_entry_org != patch_area_entry))
+ {
+ error_at (DECL_SOURCE_LOCATION (current_function_decl),
+ "%<-fsanitize=kcfi%> conflict with attribute "
+ "%<patchable_function_entry%>");
+ }
+
crtl->patch_area_size = patch_area_size;
crtl->patch_area_entry = patch_area_entry;
@@ -2222,6 +2222,37 @@ ipa_passes (void)
bitmap_obstack_release (NULL);
}
+/* Output a weak symbol value of a decl's typeid (hash) to the
+ assembly file, like:
+ .weak __kcfi_typeid_A
+ .set __kcfi_typeid_A, 0x00000ADA
+ typeid is platform-dependent, because the bits in typeid that conflicts
+ with the instruction set of the current platform needs to be ignored. */
+
+static void
+output_decl_kcfi_typeid_symbol (FILE *stream, tree fndecl)
+{
+ unsigned int hash = targetm.calc_func_cfi_typeid (TREE_TYPE (fndecl));
+ const char *name = IDENTIFIER_POINTER (DECL_NAME (fndecl));
+
+ fprintf (stream, ".weak __kcfi_typeid_%s\n", name);
+ fprintf (stream, ".set __kcfi_typeid_%s, %#010x\n", name, hash);
+}
+
+/* Calculate and output the symbols corresponding to the typeid of all
+ external declarations whose address is taken within the current
+ compilation unit. If such a function is defined in assembly code,
+ its typeid can be obtained according to this symbol. */
+
+static void
+output_decl_kcfi_typeid_symbols (void)
+{
+ struct cgraph_node *node;
+
+ FOR_EACH_FUNCTION (node)
+ if (!node->definition && node->address_taken)
+ output_decl_kcfi_typeid_symbol (asm_out_file, node->decl);
+}
/* Weakrefs may be associated to external decls and thus not output
at expansion time. Emit all necessary aliases. */
@@ -2339,6 +2370,9 @@ symbol_table::compile (void)
}
#endif
+ if (flag_sanitize & SANITIZE_KERNEL_CONTROL_FLOW_INTEGRITY)
+ output_decl_kcfi_typeid_symbols ();
+
state = EXPANSION;
/* Output first asm statements and anything ordered. The process
@@ -14273,6 +14273,7 @@ distribute_notes (rtx notes, rtx_insn *from_insn, rtx_insn *i3, rtx_insn *i2,
case REG_SETJMP:
case REG_TM:
case REG_CALL_DECL:
+ case REG_CALL_CFI_TYPEID:
case REG_UNTYPED_CALL:
case REG_CALL_NOCF_CHECK:
/* These notes must remain with the call. It should not be
@@ -1003,6 +1003,14 @@ Return a value, with the same meaning as the C99 macro
@code{FLT_EVAL_METHOD} that describes which excess precision should be
applied.
+@deftypefn {Target Hook} {unsigned int} TARGET_CALC_FUNC_CFI_TYPEID (const_tree @var{fntype})
+This target hook is used to calculate a platform-dependent typeid
+of a function.
+Although the length of typeid is always 4 bytes on all platforms, different
+platforms may ignore some bits to avoid encoding conflicts with it's
+instruction set, so a platform-dependent function is required.
+@end deftypefn
+
@deftypefn {Target Hook} machine_mode TARGET_PROMOTE_FUNCTION_MODE (const_tree @var{type}, machine_mode @var{mode}, int *@var{punsignedp}, const_tree @var{funtype}, int @var{for_return})
Like @code{PROMOTE_MODE}, but it is applied to outgoing function arguments or
function return values. The target hook should return the new mode
@@ -8721,6 +8729,20 @@ global; that is, available for reference from other files.
The default implementation uses the TARGET_ASM_GLOBALIZE_LABEL target hook.
@end deftypefn
+@deftypefn {Target Hook} void TARGET_ASM_OUTPUT_FUNC_KCFI_TYPEID (FILE *@var{stream}, tree @var{decl})
+This target hook is used to output a function's typeid before
+its assembly code.
+For different platforms, the output format of typeid may be different,
+so a platform-dependent function is required.
+@end deftypefn
+
+@deftypefn {Target Hook} void TARGET_ASM_OUTPUT_ICALL_KCFI_CHECK (rtx @var{reg}, unsigned int @var{value})
+This target hook is used to output the assembly codes to check the
+callee's typeid before an indirect call.
+For different platforms, the location of typeid may be different,
+so a platform-dependent function is required.
+@end deftypefn
+
@deftypefn {Target Hook} void TARGET_ASM_ASSEMBLE_UNDEFINED_DECL (FILE *@var{stream}, const char *@var{name}, const_tree @var{decl})
This target hook is a function to output to the stdio stream
@var{stream} some commands that will declare the name associated with
@@ -12608,3 +12630,8 @@ type.
This value is true if the target platform supports
@option{-fsanitize=shadow-call-stack}. The default value is false.
@end deftypevr
+
+@deftypevr {Target Hook} bool TARGET_HAVE_KCFI
+This value is true if the target platform supports
+@option{-fsanitize=kcfi}. The default value is false.
+@end deftypevr
@@ -933,6 +933,8 @@ Return a value, with the same meaning as the C99 macro
@code{FLT_EVAL_METHOD} that describes which excess precision should be
applied.
+@hook TARGET_CALC_FUNC_CFI_TYPEID
+
@hook TARGET_PROMOTE_FUNCTION_MODE
@defmac PARM_BOUNDARY
@@ -5568,6 +5570,10 @@ You may wish to use @code{ASM_OUTPUT_SIZE_DIRECTIVE} and/or
@hook TARGET_ASM_GLOBALIZE_DECL_NAME
+@hook TARGET_ASM_OUTPUT_FUNC_KCFI_TYPEID
+
+@hook TARGET_ASM_OUTPUT_ICALL_KCFI_CHECK
+
@hook TARGET_ASM_ASSEMBLE_UNDEFINED_DECL
@defmac ASM_WEAKEN_LABEL (@var{stream}, @var{name})
@@ -8183,3 +8189,5 @@ maintainer is familiar with.
@hook TARGET_GCOV_TYPE_SIZE
@hook TARGET_HAVE_SHADOW_CALL_STACK
+
+@hook TARGET_HAVE_KCFI
@@ -3924,6 +3924,7 @@ try_split (rtx pat, rtx_insn *trial, int last)
fixup_args_size_notes (NULL, insn_last, get_args_size (note));
break;
+ case REG_CALL_CFI_TYPEID:
case REG_CALL_DECL:
case REG_UNTYPED_CALL:
gcc_assert (call_insn != NULL_RTX);
@@ -307,6 +307,10 @@ struct GTY(()) rtl_data {
pass. */
bool bb_reorder_complete;
+ /* True if we should add kcfi indirect call check for the current
+ function. */
+ bool is_kcfi_enabled;
+
/* Like regs_ever_live, but 1 if a reg is set or clobbered from an
asm. Unlike regs_ever_live, elements of this array corresponding
to eliminable regs (like the frame pointer) are set if an asm
@@ -2823,6 +2823,29 @@ final_scan_insn_1 (rtx_insn *insn, FILE *file, int optimize_p ATTRIBUTE_UNUSED,
current_output_insn = debug_insn = insn;
+ rtx_call_insn *call_insn = dyn_cast <rtx_call_insn *> (insn);
+
+ /* Do not insert cfg checks for functions that disable kcfi. */
+ if ((call_insn != NULL) && crtl->is_kcfi_enabled)
+ {
+ rtx x = call_from_call_insn (call_insn);
+ x = XEXP (x, 0);
+ if (x && MEM_P (x))
+ {
+ x = XEXP (x, 0);
+ if (GET_CODE (x) == REG)
+ {
+ rtx note = find_reg_note (insn, REG_CALL_CFI_TYPEID,
+ NULL_RTX);
+ gcc_assert (note);
+
+ unsigned value = (unsigned) XWINT (XEXP (note, 0), 0);
+
+ targetm.asm_out.output_icall_kcfi_check (x, value);
+ }
+ }
+ }
+
/* Find the proper template for this insn. */
templ = get_insn_template (insn_code_number, insn);
@@ -2875,7 +2898,6 @@ final_scan_insn_1 (rtx_insn *insn, FILE *file, int optimize_p ATTRIBUTE_UNUSED,
&& targetm.asm_out.unwind_emit)
targetm.asm_out.unwind_emit (asm_out_file, insn);
- rtx_call_insn *call_insn = dyn_cast <rtx_call_insn *> (insn);
if (call_insn != NULL)
{
rtx x = call_from_call_insn (call_insn);
@@ -323,6 +323,8 @@ enum sanitize_code {
SANITIZE_KERNEL_HWADDRESS = 1ULL << 30,
/* Shadow Call Stack. */
SANITIZE_SHADOW_CALL_STACK = 1ULL << 31,
+ /* Control Flow Integrity for linux kernel. */
+ SANITIZE_KERNEL_CONTROL_FLOW_INTEGRITY = 1ULL << 32,
SANITIZE_MAX = 1ULL << 63,
SANITIZE_SHIFT = SANITIZE_SHIFT_BASE | SANITIZE_SHIFT_EXPONENT,
SANITIZE_UNDEFINED = SANITIZE_SHIFT | SANITIZE_DIVIDE | SANITIZE_UNREACHABLE
@@ -222,9 +222,16 @@ gimple_call_reset_alias_info (gcall *s)
components of a GIMPLE_CALL statement to function FN with NARGS
arguments. */
+void
+gimple_call_set_cfi_typeid (gcall *call_stmt, unsigned int cfi_typeid)
+{
+ call_stmt->cfi_typeid = cfi_typeid;
+}
+
static inline gcall *
gimple_build_call_1 (tree fn, unsigned nargs)
{
+ unsigned int cfi_typeid;
gcall *s
= as_a <gcall *> (gimple_build_with_ops (GIMPLE_CALL, ERROR_MARK,
nargs + 3));
@@ -233,6 +240,10 @@ gimple_build_call_1 (tree fn, unsigned nargs)
gimple_set_op (s, 1, fn);
gimple_call_set_fntype (s, TREE_TYPE (TREE_TYPE (fn)));
gimple_call_reset_alias_info (s);
+
+ cfi_typeid = targetm.calc_func_cfi_typeid (TREE_TYPE (TREE_TYPE (fn)));
+ gimple_call_set_cfi_typeid (s, cfi_typeid);
+
return s;
}
@@ -362,12 +362,15 @@ struct GTY((tag("GSS_CALL")))
struct pt_solution call_clobbered;
/* [ WORD 14 ] */
+ unsigned int cfi_typeid;
+
+ /* [ WORD 15 ] */
union GTY ((desc ("%1.subcode & GF_CALL_INTERNAL"))) {
tree GTY ((tag ("0"))) fntype;
enum internal_fn GTY ((tag ("GF_CALL_INTERNAL"))) internal_fn;
} u;
- /* [ WORD 15 ]
+ /* [ WORD 16 ]
Operand vector. NOTE! This must always be the last field
of this structure. In particular, this means that this
structure cannot be embedded inside another one. */
@@ -2059,6 +2059,7 @@ const struct sanitizer_opts_s sanitizer_opts[] =
SANITIZER_OPT (pointer-overflow, SANITIZE_POINTER_OVERFLOW, true),
SANITIZER_OPT (builtin, SANITIZE_BUILTIN, true),
SANITIZER_OPT (shadow-call-stack, SANITIZE_SHADOW_CALL_STACK, false),
+ SANITIZER_OPT (kcfi, SANITIZE_KERNEL_CONTROL_FLOW_INTEGRITY, false),
SANITIZER_OPT (all, ~0ULL, true),
#undef SANITIZER_OPT
{ NULL, 0U, 0UL, false }
@@ -2186,7 +2187,8 @@ parse_sanitizer_options (const char *p, location_t loc, int scode,
else
flags |= ~(SANITIZE_THREAD | SANITIZE_LEAK
| SANITIZE_UNREACHABLE | SANITIZE_RETURN
- | SANITIZE_SHADOW_CALL_STACK);
+ | SANITIZE_SHADOW_CALL_STACK
+ | SANITIZE_KERNEL_CONTROL_FLOW_INTEGRITY);
}
else if (value)
{
@@ -606,6 +606,9 @@ extern bool default_binds_local_p_2 (const_tree);
extern bool default_binds_local_p_3 (const_tree, bool, bool, bool, bool);
extern void default_globalize_label (FILE *, const char *);
extern void default_globalize_decl_name (FILE *, tree);
+extern void default_output_func_kcfi_typeid (FILE *, tree);
+extern void default_output_icall_kcfi_check (rtx reg, unsigned int value);
+extern unsigned int default_calc_func_cfi_typeid (const_tree);
extern void default_emit_unwind_label (FILE *, tree, int, int);
extern void default_emit_except_table_label (FILE *);
extern void default_generate_internal_label (char *, const char *,
@@ -247,3 +247,4 @@ REG_NOTE (CALL_NOCF_CHECK)
/* The values passed to callee, for debuginfo purposes. */
REG_NOTE (CALL_ARG_LOCATION)
+REG_NOTE (CALL_CFI_TYPEID)
@@ -136,6 +136,26 @@ global; that is, available for reference from other files.\n\
The default implementation uses the TARGET_ASM_GLOBALIZE_LABEL target hook.",
void, (FILE *stream, tree decl), default_globalize_decl_name)
+/* Output the uniform type identifier in front of a function
+ when kcfi is enabled. */
+DEFHOOK
+(output_func_kcfi_typeid,
+ "This target hook is used to output a function's typeid before\n\
+its assembly code.\n\
+For different platforms, the output format of typeid may be different,\n\
+so a platform-dependent function is required.",
+ void, (FILE *stream, tree decl), default_output_func_kcfi_typeid)
+
+/* Output the assembly codes to check an indirect call's cfi typeid
+ when kcfi is enabled. */
+DEFHOOK
+(output_icall_kcfi_check,
+ "This target hook is used to output the assembly codes to check the\n\
+callee's typeid before an indirect call.\n\
+For different platforms, the location of typeid may be different,\n\
+so a platform-dependent function is required.",
+ void, (rtx reg, unsigned int value), default_output_icall_kcfi_check)
+
/* Output code that will declare an external variable. */
DEFHOOK
(assemble_undefined_decl,
@@ -4522,6 +4542,16 @@ by a subtarget.",
unsigned HOST_WIDE_INT, (void),
NULL)
+/* Calculate the typeid of a function's type. */
+DEFHOOK
+(calc_func_cfi_typeid,
+ "This target hook is used to calculate a platform-dependent typeid\n\
+of a function.\n\
+Although the length of typeid is always 4 bytes on all platforms, different\n\
+platforms may ignore some bits to avoid encoding conflicts with it's\n\
+instruction set, so a platform-dependent function is required.",
+ unsigned int, (const_tree fntype), default_calc_func_cfi_typeid)
+
/* Functions relating to calls - argument passing, returns, etc. */
/* Members of struct call have no special macro prefix. */
HOOK_VECTOR (TARGET_CALLS, calls)
@@ -7111,6 +7141,14 @@ DEFHOOKPOD
@option{-fsanitize=shadow-call-stack}. The default value is false.",
bool, false)
+/* This value represents whether the control flow integrity is implemented
+ on the target platform. */
+DEFHOOKPOD
+(have_kcfi,
+ "This value is true if the target platform supports\n\
+@option{-fsanitize=kcfi}. The default value is false.",
+ bool, false)
+
/* Close the 'struct gcc_target' definition. */
HOOK_VECTOR_END (C90_EMPTY_HACK)
@@ -1665,6 +1665,10 @@ process_options (bool no_backend)
"requires %<-fno-exceptions%>");
}
+ if (flag_sanitize & SANITIZE_KERNEL_CONTROL_FLOW_INTEGRITY)
+ if (!targetm.have_kcfi)
+ sorry ("%<-fsanitize=kcfi%> not supported in current platform");
+
HOST_WIDE_INT patch_area_size, patch_area_start;
parse_and_check_patch_area (flag_patchable_function_entry, false,
&patch_area_size, &patch_area_start);
@@ -137,6 +137,8 @@ static uint64_t tree_code_counts[MAX_TREE_CODES];
uint64_t tree_node_counts[(int) all_kinds];
uint64_t tree_node_sizes[(int) all_kinds];
+static unsigned int unified_tree_type_hash_table[MAX_TREE_CODES];
+
/* Keep in sync with tree.h:enum tree_node_kind. */
static const char * const tree_node_kind_names[] = {
"decls",
@@ -252,6 +254,8 @@ static void print_type_hash_statistics (void);
static void print_debug_expr_statistics (void);
static void print_value_expr_statistics (void);
+static void append_unified_type_hash (const_tree type, inchash::hash &hstate);
+
tree global_trees[TI_MAX];
tree integer_types[itk_none];
@@ -694,6 +698,143 @@ initialize_tree_contains_struct (void)
gcc_assert (tree_contains_struct[NAMELIST_DECL][TS_DECL_COMMON]);
}
+static void
+initialize_unified_tree_type_hash_table (void)
+{
+ unified_tree_type_hash_table[OFFSET_TYPE] = 10;
+ unified_tree_type_hash_table[ENUMERAL_TYPE] = 20;
+ unified_tree_type_hash_table[BOOLEAN_TYPE] = 30;
+ unified_tree_type_hash_table[INTEGER_TYPE] = 40;
+ unified_tree_type_hash_table[REAL_TYPE] = 50;
+ unified_tree_type_hash_table[POINTER_TYPE] = 60;
+ unified_tree_type_hash_table[REFERENCE_TYPE] = 70;
+ unified_tree_type_hash_table[NULLPTR_TYPE] = 80;
+ unified_tree_type_hash_table[FIXED_POINT_TYPE] = 90;
+ unified_tree_type_hash_table[COMPLEX_TYPE] = 100;
+ unified_tree_type_hash_table[VECTOR_TYPE] = 110;
+ unified_tree_type_hash_table[ARRAY_TYPE] = 120;
+ unified_tree_type_hash_table[RECORD_TYPE] = 130;
+ unified_tree_type_hash_table[UNION_TYPE] = 140;
+ unified_tree_type_hash_table[QUAL_UNION_TYPE] = 150;
+ unified_tree_type_hash_table[VOID_TYPE] = 160;
+ unified_tree_type_hash_table[FUNCTION_TYPE] = 170;
+ unified_tree_type_hash_table[METHOD_TYPE] = 180;
+ unified_tree_type_hash_table[LANG_TYPE] = 190;
+ unified_tree_type_hash_table[OPAQUE_TYPE] = 200;
+}
+
+static void
+append_unified_type_name_hash (const_tree type, inchash::hash &hstate)
+{
+ tree n = TYPE_NAME (TYPE_MAIN_VARIANT (type));
+
+ if (!n)
+ return;
+
+ if (TREE_CODE (n) != IDENTIFIER_NODE)
+ n = DECL_NAME (n);
+
+ hstate.add ((const void *) IDENTIFIER_POINTER (n), IDENTIFIER_LENGTH (n));
+}
+
+static void
+append_unified_type_precision_hash (const_tree type, inchash::hash &hstate)
+{
+ unsigned HOST_WIDE_INT size = TYPE_PRECISION (type);
+
+ hstate.add_hwi (size);
+}
+
+/* Add the return and all parameter types of the function
+ to the hash calculation. */
+
+static void
+append_unified_function_ret_and_args_hash (const_tree fntype,
+ inchash::hash &hstate)
+{
+ const_tree arg_type;
+ function_args_iterator args_iter;
+
+ append_unified_type_hash (TREE_TYPE (fntype), hstate);
+
+ FOREACH_FUNCTION_ARGS (fntype, arg_type, args_iter)
+ {
+ if (TYPE_READONLY (arg_type) || TYPE_VOLATILE (arg_type))
+ {
+ int quals = TYPE_QUALS (arg_type)
+ & ~TYPE_QUAL_CONST & ~TYPE_QUAL_VOLATILE;
+
+ arg_type = build_qualified_type (CONST_CAST_TREE (arg_type), quals);
+ }
+ append_unified_type_hash (arg_type, hstate);
+ }
+}
+
+static void
+append_unified_type_hash (const_tree type, inchash::hash &hstate)
+{
+ enum tree_code type_code = TREE_CODE (type);
+ unsigned int u_hash = unified_tree_type_hash_table[type_code];
+
+ /* Make sure all type nodes have a unique initial hash. */
+ if (!u_hash)
+ gcc_unreachable ();
+
+ hstate.add_int (u_hash);
+
+ /* Extra information about the type involved in the hash calculation. */
+ switch (type_code)
+ {
+ case VOID_TYPE:
+ case BOOLEAN_TYPE:
+ break;
+
+ case INTEGER_TYPE:
+ append_unified_type_name_hash (type, hstate);
+ append_unified_type_precision_hash (type, hstate);
+ break;
+
+ case ENUMERAL_TYPE:
+ append_unified_type_name_hash (type, hstate);
+ append_unified_type_precision_hash (type, hstate);
+ break;
+
+ case REAL_TYPE:
+ append_unified_type_precision_hash (TYPE_MAIN_VARIANT (type), hstate);
+ break;
+
+ case POINTER_TYPE:
+ case REFERENCE_TYPE:
+ case ARRAY_TYPE:
+ append_unified_type_hash (TREE_TYPE (type), hstate);
+ break;
+
+ case UNION_TYPE:
+ case RECORD_TYPE:
+ append_unified_type_name_hash (type, hstate);
+ break;
+
+ case FUNCTION_TYPE:
+ append_unified_function_ret_and_args_hash (type, hstate);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* Calculate the hash of the type node that are invariant across
+ compilation units. */
+
+hashval_t
+unified_type_hash (const_tree type)
+{
+ inchash::hash hstate;
+
+ append_unified_type_hash (type, hstate);
+
+ return hstate.end ();
+}
/* Init tree.cc. */
@@ -723,6 +864,9 @@ init_ttree (void)
/* Initialize the tree_contains_struct array. */
initialize_tree_contains_struct ();
+
+ initialize_unified_tree_type_hash_table ();
+
lang_hooks.init_ts ();
}
@@ -4813,6 +4813,7 @@ extern tree build_variant_type_copy (tree CXX_MEM_STAT_INFO);
extern hashval_t type_hash_canon_hash (tree);
extern tree type_hash_canon (unsigned int, tree);
+extern hashval_t unified_type_hash (const_tree);
extern tree convert (tree, tree);
extern tree size_in_bytes_loc (location_t, const_tree);
@@ -1956,6 +1956,11 @@ assemble_start_function (tree decl, const char *fnname)
if (!DECL_IGNORED_P (decl))
(*debug_hooks->begin_function) (decl);
+ /* Regardless of whether the function can be called indirectly,
+ a typeid is always required before the function. */
+ if (flag_sanitize & SANITIZE_KERNEL_CONTROL_FLOW_INTEGRITY)
+ targetm.asm_out.output_func_kcfi_typeid (asm_out_file, decl);
+
/* Make function name accessible from other files, if appropriate. */
if (TREE_PUBLIC (decl))
@@ -7674,6 +7679,27 @@ default_globalize_decl_name (FILE * stream, tree decl)
targetm.asm_out.globalize_label (stream, name);
}
+/* Default function to output the function's kcfi typeid. */
+void
+default_output_func_kcfi_typeid (FILE * stream ATTRIBUTE_UNUSED,
+ tree decl ATTRIBUTE_UNUSED)
+{
+}
+
+/* Default function to output the kcfi check for an indirect call. */
+void
+default_output_icall_kcfi_check (rtx reg ATTRIBUTE_UNUSED,
+ unsigned int value ATTRIBUTE_UNUSED)
+{
+}
+
+/* Default function to calculate the typeid of a function type. */
+unsigned int
+default_calc_func_cfi_typeid (const_tree fntype ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
/* Default function to output a label for unwind information. The
default is to do nothing. A target that needs nonlocal labels for
unwind information must provide its own function to do this. */