diff mbox series

i386: Add __x86_indirect_thunk_nt_reg for -fcf-protection -mcet

Message ID 20180202165435.GA52594@intel.com
State New
Headers show
Series i386: Add __x86_indirect_thunk_nt_reg for -fcf-protection -mcet | expand

Commit Message

H.J. Lu Feb. 2, 2018, 4:54 p.m. UTC
nocf_check attribute can be used with -fcf-protection -mcet to disable
control-flow check by adding NOTRACK prefix before indirect branch.
When -mindirect-branch=thunk-extern -mindirect-branch-register is added,
indirect branch via register, "notrack call/jmp reg", is converted to

    call/jmp __x86_indirect_thunk_nt_reg

When running on machines with CET enabled, __x86_indirect_thunk_nt_reg
can be updated to

    notrack jmp reg

at run-time to restore NOTRACK prefix in the original indirect branch.

Since we don't support -mindirect-branch=thunk-extern, CET and MPX at
the same time, -mindirect-branch=thunk-extern is disallowed with
-fcf-protection=branch and -fcheck-pointer-bounds.

Tested on i686 and x86-64.  OK for trunk?

Thanks.

H.J.
---
gcc/

	PR target/84176
	* config/i386/i386.c (ix86_set_indirect_branch_type): Issue an
	error when -mindirect-branch=thunk-extern, -fcf-protection=branch
	and -fcheck-pointer-bounds are used together.
	(indirect_thunk_prefix): New enum.
	(indirect_thunk_need_prefix): New function.
	(indirect_thunk_name): Replace need_bnd_p with need_prefix.  Use
	"_nt" instead of "_bnd" for NOTRACK prefix.
	(output_indirect_thunk): Replace need_bnd_p with need_prefix.
	(output_indirect_thunk_function): Likewise.
	(): Likewise.
	(ix86_code_end): Update output_indirect_thunk_function calls.
	(ix86_output_indirect_branch_via_reg): Replace
	ix86_bnd_prefixed_insn_p with indirect_thunk_need_prefix.
	(ix86_output_indirect_branch_via_push): Likewise.
	(ix86_output_function_return): Likewise.
	* doc/invoke.texi: Document -mindirect-branch=thunk-extern is
	incompatible with -fcf-protection=branch and
	-fcheck-pointer-bounds.

gcc/testsuite/

	PR target/84176
	* gcc.target/i386/indirect-thunk-11.c: New test.
	* gcc.target/i386/indirect-thunk-12.c: Likewise.
	* gcc.target/i386/indirect-thunk-attr-12.c: Likewise.
	* gcc.target/i386/indirect-thunk-attr-13.c: Likewise.
	* gcc.target/i386/indirect-thunk-attr-14.c: Likewise.
	* gcc.target/i386/indirect-thunk-attr-15.c: Likewise.
	* gcc.target/i386/indirect-thunk-attr-16.c: Likewise.
	* gcc.target/i386/indirect-thunk-extern-10.c: Likewise.
	* gcc.target/i386/indirect-thunk-extern-8.c: Likewise.
	* gcc.target/i386/indirect-thunk-extern-9.c: Likewise.
---
 gcc/config/i386/i386.c                             | 151 +++++++++++++++------
 gcc/doc/invoke.texi                                |   5 +
 gcc/testsuite/gcc.target/i386/indirect-thunk-11.c  |   7 +
 gcc/testsuite/gcc.target/i386/indirect-thunk-12.c  |   7 +
 .../gcc.target/i386/indirect-thunk-attr-12.c       |   8 ++
 .../gcc.target/i386/indirect-thunk-attr-13.c       |   8 ++
 .../gcc.target/i386/indirect-thunk-attr-14.c       |  14 ++
 .../gcc.target/i386/indirect-thunk-attr-15.c       |  14 ++
 .../gcc.target/i386/indirect-thunk-attr-16.c       |  13 ++
 .../gcc.target/i386/indirect-thunk-extern-10.c     |  12 ++
 .../gcc.target/i386/indirect-thunk-extern-8.c      |  13 ++
 .../gcc.target/i386/indirect-thunk-extern-9.c      |  13 ++
 12 files changed, 222 insertions(+), 43 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-11.c
 create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-12.c
 create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-12.c
 create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-13.c
 create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-14.c
 create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-15.c
 create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-attr-16.c
 create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-10.c
 create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-8.c
 create mode 100644 gcc/testsuite/gcc.target/i386/indirect-thunk-extern-9.c
diff mbox series

Patch

diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index b07f581cd65..4abf3d5a2e3 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -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 "";
     }
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index f3d93367640..9e21b8b4bf3 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -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},
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-11.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-11.c
new file mode 100644
index 00000000000..afa89ff227b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-11.c
@@ -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" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-12.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-12.c
new file mode 100644
index 00000000000..d456973ee6d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-12.c
@@ -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)
+{
+}
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-12.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-12.c
new file mode 100644
index 00000000000..24a1a08a3ca
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-12.c
@@ -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" } */
+}
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-13.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-13.c
new file mode 100644
index 00000000000..ff2fccae935
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-13.c
@@ -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)
+{
+}
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-14.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-14.c
new file mode 100644
index 00000000000..669e56d7726
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-14.c
@@ -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" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-15.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-15.c
new file mode 100644
index 00000000000..c033fb23879
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-15.c
@@ -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" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-16.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-16.c
new file mode 100644
index 00000000000..0244edf53b7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-attr-16.c
@@ -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)" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-10.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-10.c
new file mode 100644
index 00000000000..2a982ab69f8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-10.c
@@ -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)" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-8.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-8.c
new file mode 100644
index 00000000000..30d12cc0711
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-8.c
@@ -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" } } */
diff --git a/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-9.c b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-9.c
new file mode 100644
index 00000000000..d714b0155df
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/indirect-thunk-extern-9.c
@@ -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" } } */