@@ -5886,6 +5886,16 @@ ix86_set_indirect_branch_type (tree fndecl)
((cfun->machine->indirect_branch_type
== indirect_branch_thunk_extern)
? "thunk-extern" : "thunk"));
+
+ /* -mindirect-branch=thunk-extern, -fcf-protection=branch and
+ -fcheck-pointer-bounds are not compatible. */
+ if ((cfun->machine->indirect_branch_type
+ == indirect_branch_thunk_extern)
+ && flag_check_pointer_bounds
+ && (flag_cf_protection & CF_BRANCH) != 0)
+ error ("%<-mindirect-branch=thunk-extern%>, "
+ "%<-fcf-protection=branch%> and "
+ "%<-fcheck-pointer-bounds%> are not compatible");
}
if (cfun->machine->function_return_type == indirect_branch_unset)
@@ -10794,18 +10804,62 @@ static int indirect_thunks_bnd_used;
# define INDIRECT_LABEL "LIND"
#endif
+/* Indicate what prefix is needed for an indirect branch. */
+enum indirect_thunk_prefix
+{
+ indirect_thunk_prefix_none,
+ indirect_thunk_prefix_bnd,
+ indirect_thunk_prefix_nt
+};
+
+/* Return the prefix needed for an indirect branch INSN. */
+
+enum indirect_thunk_prefix
+indirect_thunk_need_prefix (rtx_insn *insn)
+{
+ enum indirect_thunk_prefix need_prefix;
+ if (ix86_bnd_prefixed_insn_p (insn))
+ need_prefix = indirect_thunk_prefix_bnd;
+ else if ((cfun->machine->indirect_branch_type
+ == indirect_branch_thunk_extern)
+ && ix86_notrack_prefixed_insn_p (insn))
+ {
+ /* NOTRACK prefix is only used with external thunk so that it
+ can be properly updated to support CET at run-time. */
+ need_prefix = indirect_thunk_prefix_nt;
+ }
+ else
+ need_prefix = indirect_thunk_prefix_none;
+ return need_prefix;
+}
+
/* Fills in the label name that should be used for the indirect thunk. */
static void
indirect_thunk_name (char name[32], unsigned int regno,
- bool need_bnd_p, bool ret_p)
+ enum indirect_thunk_prefix need_prefix,
+ bool ret_p)
{
if (regno != INVALID_REGNUM && ret_p)
gcc_unreachable ();
if (USE_HIDDEN_LINKONCE)
{
- const char *bnd = need_bnd_p ? "_bnd" : "";
+ const char *prefix;
+
+ if (need_prefix == indirect_thunk_prefix_bnd)
+ prefix = "_bnd";
+ else if (need_prefix == indirect_thunk_prefix_nt
+ && regno != INVALID_REGNUM)
+ {
+ /* NOTRACK prefix is only used with external thunk via
+ register so that NOTRACK prefix can be added to indirect
+ branch via register to support CET at run-time. */
+ prefix = "_nt";
+ }
+ else
+ prefix = "";
+
if (regno != INVALID_REGNUM)
{
const char *reg_prefix;
@@ -10814,19 +10868,19 @@ indirect_thunk_name (char name[32], unsigned int regno,
else
reg_prefix = "";
sprintf (name, "__x86_indirect_thunk%s_%s%s",
- bnd, reg_prefix, reg_names[regno]);
+ prefix, reg_prefix, reg_names[regno]);
}
else
{
const char *ret = ret_p ? "return" : "indirect";
- sprintf (name, "__x86_%s_thunk%s", ret, bnd);
+ sprintf (name, "__x86_%s_thunk%s", ret, prefix);
}
}
else
{
if (regno != INVALID_REGNUM)
{
- if (need_bnd_p)
+ if (need_prefix == indirect_thunk_prefix_bnd)
ASM_GENERATE_INTERNAL_LABEL (name, "LITBR", regno);
else
ASM_GENERATE_INTERNAL_LABEL (name, "LITR", regno);
@@ -10835,14 +10889,14 @@ indirect_thunk_name (char name[32], unsigned int regno,
{
if (ret_p)
{
- if (need_bnd_p)
+ if (need_prefix == indirect_thunk_prefix_bnd)
ASM_GENERATE_INTERNAL_LABEL (name, "LRTB", 0);
else
ASM_GENERATE_INTERNAL_LABEL (name, "LRT", 0);
}
else
{
- if (need_bnd_p)
+ if (need_prefix == indirect_thunk_prefix_bnd)
ASM_GENERATE_INTERNAL_LABEL (name, "LITB", 0);
else
ASM_GENERATE_INTERNAL_LABEL (name, "LIT", 0);
@@ -10878,7 +10932,8 @@ indirect_thunk_name (char name[32], unsigned int regno,
*/
static void
-output_indirect_thunk (bool need_bnd_p, unsigned int regno)
+output_indirect_thunk (enum indirect_thunk_prefix need_prefix,
+ unsigned int regno)
{
char indirectlabel1[32];
char indirectlabel2[32];
@@ -10889,7 +10944,7 @@ output_indirect_thunk (bool need_bnd_p, unsigned int regno)
indirectlabelno++);
/* Call */
- if (need_bnd_p)
+ if (need_prefix == indirect_thunk_prefix_bnd)
fputs ("\tbnd call\t", asm_out_file);
else
fputs ("\tcall\t", asm_out_file);
@@ -10926,7 +10981,7 @@ output_indirect_thunk (bool need_bnd_p, unsigned int regno)
output_asm_insn ("lea\t{%E1, %0|%0, %E1}", xops);
}
- if (need_bnd_p)
+ if (need_prefix == indirect_thunk_prefix_bnd)
fputs ("\tbnd ret\n", asm_out_file);
else
fputs ("\tret\n", asm_out_file);
@@ -10938,13 +10993,14 @@ output_indirect_thunk (bool need_bnd_p, unsigned int regno)
on the top of stack. */
static void
-output_indirect_thunk_function (bool need_bnd_p, unsigned int regno)
+output_indirect_thunk_function (enum indirect_thunk_prefix need_prefix,
+ unsigned int regno)
{
char name[32];
tree decl;
/* Create __x86_indirect_thunk/__x86_indirect_thunk_bnd. */
- indirect_thunk_name (name, regno, need_bnd_p, false);
+ indirect_thunk_name (name, regno, need_prefix, false);
decl = build_decl (BUILTINS_LOCATION, FUNCTION_DECL,
get_identifier (name),
build_function_type_list (void_type_node, NULL_TREE));
@@ -10992,7 +11048,7 @@ output_indirect_thunk_function (bool need_bnd_p, unsigned int regno)
/* Create alias for __x86.return_thunk/__x86.return_thunk_bnd. */
char alias[32];
- indirect_thunk_name (alias, regno, need_bnd_p, true);
+ indirect_thunk_name (alias, regno, need_prefix, true);
#if TARGET_MACHO
if (TARGET_MACHO)
{
@@ -11028,7 +11084,7 @@ output_indirect_thunk_function (bool need_bnd_p, unsigned int regno)
/* Make sure unwind info is emitted for the thunk if needed. */
final_start_function (emit_barrier (), asm_out_file, 1);
- output_indirect_thunk (need_bnd_p, regno);
+ output_indirect_thunk (need_prefix, regno);
final_end_function ();
init_insn_lengths ();
@@ -11064,18 +11120,22 @@ ix86_code_end (void)
unsigned int regno;
if (indirect_thunk_needed)
- output_indirect_thunk_function (false, INVALID_REGNUM);
+ output_indirect_thunk_function (indirect_thunk_prefix_none,
+ INVALID_REGNUM);
if (indirect_thunk_bnd_needed)
- output_indirect_thunk_function (true, INVALID_REGNUM);
+ output_indirect_thunk_function (indirect_thunk_prefix_bnd,
+ INVALID_REGNUM);
for (regno = FIRST_REX_INT_REG; regno <= LAST_REX_INT_REG; regno++)
{
unsigned int i = regno - FIRST_REX_INT_REG + LAST_INT_REG + 1;
if ((indirect_thunks_used & (1 << i)))
- output_indirect_thunk_function (false, regno);
+ output_indirect_thunk_function (indirect_thunk_prefix_none,
+ regno);
if ((indirect_thunks_bnd_used & (1 << i)))
- output_indirect_thunk_function (true, regno);
+ output_indirect_thunk_function (indirect_thunk_prefix_bnd,
+ regno);
}
for (regno = FIRST_INT_REG; regno <= LAST_INT_REG; regno++)
@@ -11084,10 +11144,12 @@ ix86_code_end (void)
tree decl;
if ((indirect_thunks_used & (1 << regno)))
- output_indirect_thunk_function (false, regno);
+ output_indirect_thunk_function (indirect_thunk_prefix_none,
+ regno);
if ((indirect_thunks_bnd_used & (1 << regno)))
- output_indirect_thunk_function (true, regno);
+ output_indirect_thunk_function (indirect_thunk_prefix_bnd,
+ regno);
if (!(pic_labels_used & (1 << regno)))
continue;
@@ -28664,7 +28726,8 @@ ix86_output_indirect_branch_via_reg (rtx call_op, bool sibcall_p)
{
char thunk_name_buf[32];
char *thunk_name;
- bool need_bnd_p = ix86_bnd_prefixed_insn_p (current_output_insn);
+ enum indirect_thunk_prefix need_prefix
+ = indirect_thunk_need_prefix (current_output_insn);
int regno = REGNO (call_op);
if (cfun->machine->indirect_branch_type
@@ -28675,12 +28738,12 @@ ix86_output_indirect_branch_via_reg (rtx call_op, bool sibcall_p)
int i = regno;
if (i >= FIRST_REX_INT_REG)
i -= (FIRST_REX_INT_REG - LAST_INT_REG - 1);
- if (need_bnd_p)
+ if (need_prefix == indirect_thunk_prefix_bnd)
indirect_thunks_bnd_used |= 1 << i;
else
indirect_thunks_used |= 1 << i;
}
- indirect_thunk_name (thunk_name_buf, regno, need_bnd_p, false);
+ indirect_thunk_name (thunk_name_buf, regno, need_prefix, false);
thunk_name = thunk_name_buf;
}
else
@@ -28690,19 +28753,19 @@ ix86_output_indirect_branch_via_reg (rtx call_op, bool sibcall_p)
{
if (thunk_name != NULL)
{
- if (need_bnd_p)
+ if (need_prefix == indirect_thunk_prefix_bnd)
fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
else
fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
}
else
- output_indirect_thunk (need_bnd_p, regno);
+ output_indirect_thunk (need_prefix, regno);
}
else
{
if (thunk_name != NULL)
{
- if (need_bnd_p)
+ if (need_prefix == indirect_thunk_prefix_bnd)
fprintf (asm_out_file, "\tbnd call\t%s\n", thunk_name);
else
fprintf (asm_out_file, "\tcall\t%s\n", thunk_name);
@@ -28720,7 +28783,7 @@ ix86_output_indirect_branch_via_reg (rtx call_op, bool sibcall_p)
indirectlabelno++);
/* Jump. */
- if (need_bnd_p)
+ if (need_prefix == indirect_thunk_prefix_bnd)
fputs ("\tbnd jmp\t", asm_out_file);
else
fputs ("\tjmp\t", asm_out_file);
@@ -28731,18 +28794,18 @@ ix86_output_indirect_branch_via_reg (rtx call_op, bool sibcall_p)
if (thunk_name != NULL)
{
- if (need_bnd_p)
+ if (need_prefix == indirect_thunk_prefix_bnd)
fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
else
fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
}
else
- output_indirect_thunk (need_bnd_p, regno);
+ output_indirect_thunk (need_prefix, regno);
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel2);
/* Call. */
- if (need_bnd_p)
+ if (need_prefix == indirect_thunk_prefix_bnd)
fputs ("\tbnd call\t", asm_out_file);
else
fputs ("\tcall\t", asm_out_file);
@@ -28776,7 +28839,8 @@ ix86_output_indirect_branch_via_push (rtx call_op, const char *xasm,
char thunk_name_buf[32];
char *thunk_name;
char push_buf[64];
- bool need_bnd_p = ix86_bnd_prefixed_insn_p (current_output_insn);
+ enum indirect_thunk_prefix need_prefix
+ = indirect_thunk_need_prefix (current_output_insn);
int regno = -1;
if (cfun->machine->indirect_branch_type
@@ -28784,12 +28848,12 @@ ix86_output_indirect_branch_via_push (rtx call_op, const char *xasm,
{
if (cfun->machine->indirect_branch_type == indirect_branch_thunk)
{
- if (need_bnd_p)
+ if (need_prefix == indirect_thunk_prefix_bnd)
indirect_thunk_bnd_needed = true;
else
indirect_thunk_needed = true;
}
- indirect_thunk_name (thunk_name_buf, regno, need_bnd_p, false);
+ indirect_thunk_name (thunk_name_buf, regno, need_prefix, false);
thunk_name = thunk_name_buf;
}
else
@@ -28803,13 +28867,13 @@ ix86_output_indirect_branch_via_push (rtx call_op, const char *xasm,
output_asm_insn (push_buf, &call_op);
if (thunk_name != NULL)
{
- if (need_bnd_p)
+ if (need_prefix == indirect_thunk_prefix_bnd)
fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
else
fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
}
else
- output_indirect_thunk (need_bnd_p, regno);
+ output_indirect_thunk (need_prefix, regno);
}
else
{
@@ -28824,7 +28888,7 @@ ix86_output_indirect_branch_via_push (rtx call_op, const char *xasm,
indirectlabelno++);
/* Jump. */
- if (need_bnd_p)
+ if (need_prefix == indirect_thunk_prefix_bnd)
fputs ("\tbnd jmp\t", asm_out_file);
else
fputs ("\tjmp\t", asm_out_file);
@@ -28870,18 +28934,18 @@ ix86_output_indirect_branch_via_push (rtx call_op, const char *xasm,
if (thunk_name != NULL)
{
- if (need_bnd_p)
+ if (need_prefix == indirect_thunk_prefix_bnd)
fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
else
fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name);
}
else
- output_indirect_thunk (need_bnd_p, regno);
+ output_indirect_thunk (need_prefix, regno);
ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel2);
/* Call. */
- if (need_bnd_p)
+ if (need_prefix == indirect_thunk_prefix_bnd)
fputs ("\tbnd call\t", asm_out_file);
else
fputs ("\tcall\t", asm_out_file);
@@ -28933,16 +28997,17 @@ ix86_output_function_return (bool long_p)
if (cfun->machine->function_return_type != indirect_branch_keep)
{
char thunk_name[32];
- bool need_bnd_p = ix86_bnd_prefixed_insn_p (current_output_insn);
+ enum indirect_thunk_prefix need_prefix
+ = indirect_thunk_need_prefix (current_output_insn);
if (cfun->machine->function_return_type
!= indirect_branch_thunk_inline)
{
bool need_thunk = (cfun->machine->function_return_type
== indirect_branch_thunk);
- indirect_thunk_name (thunk_name, INVALID_REGNUM, need_bnd_p,
+ indirect_thunk_name (thunk_name, INVALID_REGNUM, need_prefix,
true);
- if (need_bnd_p)
+ if (need_prefix == indirect_thunk_prefix_bnd)
{
indirect_thunk_bnd_needed |= need_thunk;
fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name);
@@ -28954,7 +29019,7 @@ ix86_output_function_return (bool long_p)
}
}
else
- output_indirect_thunk (need_bnd_p, INVALID_REGNUM);
+ output_indirect_thunk (need_prefix, INVALID_REGNUM);
return "";
}
@@ -26929,6 +26929,11 @@ Note that @option{-mcmodel=large} is incompatible with
@option{-mindirect-branch=thunk-extern} since the thunk function may
not be reachable in large code model.
+Note that @option{-mindirect-branch=thunk-extern} is incompatible with
+@option{-fcf-protection=branch} and @option{-fcheck-pointer-bounds}
+since the external thunk can not be modified to disable control-flow
+check.
+
@item -mfunction-return=@var{choice}
@opindex -mfunction-return
Convert function return with @var{choice}. The default is @samp{keep},
new file mode 100644
@@ -0,0 +1,7 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -fcf-protection -mcet -fcheck-pointer-bounds -mmpx" } */
+
+void
+bar (void)
+{ /* { dg-error "'-mindirect-branch=thunk-extern', '-fcf-protection=branch' and '-fcheck-pointer-bounds' are not compatible" } */
+}
new file mode 100644
@@ -0,0 +1,7 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fcf-protection -mcet -fcheck-pointer-bounds -mmpx" } */
+
+void
+bar (void)
+{
+}
new file mode 100644
@@ -0,0 +1,8 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mindirect-branch=keep -fcf-protection -mcet -fcheck-pointer-bounds -mmpx" } */
+
+__attribute__ ((indirect_branch("thunk-extern")))
+void
+bar (void)
+{ /* { dg-error "'-mindirect-branch=thunk-extern', '-fcf-protection=branch' and '-fcheck-pointer-bounds' are not compatible" } */
+}
new file mode 100644
@@ -0,0 +1,8 @@
+/* { dg-do compile { target { ! x32 } } } */
+/* { dg-options "-O2 -mindirect-branch=keep -fcf-protection -mcet -fcheck-pointer-bounds -mmpx" } */
+
+__attribute__ ((indirect_branch("thunk-inline")))
+void
+bar (void)
+{
+}
new file mode 100644
@@ -0,0 +1,14 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic -fplt -mindirect-branch=keep -fcf-protection -mcet" } */
+
+extern void (*bar) (void);
+
+__attribute__ ((indirect_branch("thunk-extern")))
+void
+foo (void)
+{
+ bar ();
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_indirect_thunk_nt" } } */
new file mode 100644
@@ -0,0 +1,14 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mindirect-branch-register -mfunction-return=keep -fno-pic -fplt -mindirect-branch=keep -fcf-protection -mcet" } */
+
+extern void (*bar) (void);
+
+__attribute__ ((indirect_branch("thunk-extern")))
+void
+foo (void)
+{
+ bar ();
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_indirect_thunk_nt" } } */
new file mode 100644
@@ -0,0 +1,13 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mindirect-branch-register -mfunction-return=keep -fno-pic -fplt -mindirect-branch=keep -fcf-protection -mcet" } */
+
+extern void (*bar) (void) __attribute__((nocf_check));
+
+__attribute__ ((indirect_branch("thunk-extern")))
+void
+foo (void)
+{
+ bar ();
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_nt_(r|e)" } } */
new file mode 100644
@@ -0,0 +1,12 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mindirect-branch-register -mfunction-return=keep -fno-pic -fplt -mindirect-branch=thunk-extern -fcf-protection -mcet" } */
+
+extern void (*bar) (void) __attribute__((nocf_check));
+
+void
+foo (void)
+{
+ bar ();
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk_nt_(r|e)" } } */
new file mode 100644
@@ -0,0 +1,13 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mno-indirect-branch-register -mfunction-return=keep -fno-pic -fplt -mindirect-branch=thunk-extern -fcf-protection -mcet" } */
+
+extern void (*bar) (void);
+
+void
+foo (void)
+{
+ bar ();
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_indirect_thunk_nt" } } */
new file mode 100644
@@ -0,0 +1,13 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -mindirect-branch-register -mfunction-return=keep -fno-pic -fplt -mindirect-branch=thunk-extern -fcf-protection -mcet" } */
+
+extern void (*bar) (void);
+
+void
+foo (void)
+{
+ bar ();
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_indirect_thunk" } } */
+/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_indirect_thunk_nt" } } */