diff mbox series

i386; Add -mmanual-endbr and cf_check function attribute

Message ID 20180615125949.GA114203@intel.com
State New
Headers show
Series i386; Add -mmanual-endbr and cf_check function attribute | expand

Commit Message

H.J. Lu June 15, 2018, 12:59 p.m. UTC
Currently GCC inserts ENDBR instruction at entries of all non-static
functions, unless LTO compilation is used.  Marking all functions,
which are not called indirectly with nocf_check attribute, is not
ideal since 99% of functions in a program may be of this kind.

This patch adds -mmanual-endbr and cf_check function attribute.  They
can be used together with -fcf-protection such that ENDBR instruction
is inserted only at entries of functions with cf_check attribute.  It
can limit number of ENDBR instructions to reduce program size.

OK for trubk?

H.J.
-----
gcc/

	* config/i386/i386.c (rest_of_insert_endbranch): Insert ENDBR
	at the function entry only when -mmanual-endbr isn't used or
	there is cf_check function attribute.
	(ix86_attribute_table): Add cf_check.
	* config/i386/i386.opt: Add -mmanual-endbr.
	* doc/extend.texi: Document cf_check attribute.
	* doc/invoke.texi: Document -mmanual-endbr.

gcc/testsuite/

	* gcc.target/i386/cf_check-1.c: New test.
	* gcc.target/i386/cf_check-2.c: Likewise.
	* gcc.target/i386/cf_check-3.c: Likewise.
	* gcc.target/i386/cf_check-4.c: Likewise.
	* gcc.target/i386/cf_check-5.c: Likewise.
---
 gcc/config/i386/i386.c                     |  5 +++++
 gcc/config/i386/i386.opt                   |  5 +++++
 gcc/doc/extend.texi                        |  7 +++++++
 gcc/doc/invoke.texi                        |  9 ++++++++-
 gcc/testsuite/gcc.target/i386/cf_check-1.c | 11 +++++++++++
 gcc/testsuite/gcc.target/i386/cf_check-2.c | 11 +++++++++++
 gcc/testsuite/gcc.target/i386/cf_check-3.c | 11 +++++++++++
 gcc/testsuite/gcc.target/i386/cf_check-4.c | 10 ++++++++++
 gcc/testsuite/gcc.target/i386/cf_check-5.c |  9 +++++++++
 9 files changed, 77 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/gcc.target/i386/cf_check-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/cf_check-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/cf_check-3.c
 create mode 100644 gcc/testsuite/gcc.target/i386/cf_check-4.c
 create mode 100644 gcc/testsuite/gcc.target/i386/cf_check-5.c

Comments

Richard Biener June 18, 2018, 9:20 a.m. UTC | #1
On Fri, Jun 15, 2018 at 2:59 PM H.J. Lu <hongjiu.lu@intel.com> wrote:
>
> Currently GCC inserts ENDBR instruction at entries of all non-static
> functions, unless LTO compilation is used.  Marking all functions,
> which are not called indirectly with nocf_check attribute, is not
> ideal since 99% of functions in a program may be of this kind.
>
> This patch adds -mmanual-endbr and cf_check function attribute.  They
> can be used together with -fcf-protection such that ENDBR instruction
> is inserted only at entries of functions with cf_check attribute.  It
> can limit number of ENDBR instructions to reduce program size.
>
> OK for trubk?

I wonder if the linker could assist with ENDBR creation by
redirecting all non-direct call relocs to a linker-generated
stub with ENBR and a direct branch?

Richard.

> H.J.
> -----
> gcc/
>
>         * config/i386/i386.c (rest_of_insert_endbranch): Insert ENDBR
>         at the function entry only when -mmanual-endbr isn't used or
>         there is cf_check function attribute.
>         (ix86_attribute_table): Add cf_check.
>         * config/i386/i386.opt: Add -mmanual-endbr.
>         * doc/extend.texi: Document cf_check attribute.
>         * doc/invoke.texi: Document -mmanual-endbr.
>
> gcc/testsuite/
>
>         * gcc.target/i386/cf_check-1.c: New test.
>         * gcc.target/i386/cf_check-2.c: Likewise.
>         * gcc.target/i386/cf_check-3.c: Likewise.
>         * gcc.target/i386/cf_check-4.c: Likewise.
>         * gcc.target/i386/cf_check-5.c: Likewise.
> ---
>  gcc/config/i386/i386.c                     |  5 +++++
>  gcc/config/i386/i386.opt                   |  5 +++++
>  gcc/doc/extend.texi                        |  7 +++++++
>  gcc/doc/invoke.texi                        |  9 ++++++++-
>  gcc/testsuite/gcc.target/i386/cf_check-1.c | 11 +++++++++++
>  gcc/testsuite/gcc.target/i386/cf_check-2.c | 11 +++++++++++
>  gcc/testsuite/gcc.target/i386/cf_check-3.c | 11 +++++++++++
>  gcc/testsuite/gcc.target/i386/cf_check-4.c | 10 ++++++++++
>  gcc/testsuite/gcc.target/i386/cf_check-5.c |  9 +++++++++
>  9 files changed, 77 insertions(+), 1 deletion(-)
>  create mode 100644 gcc/testsuite/gcc.target/i386/cf_check-1.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/cf_check-2.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/cf_check-3.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/cf_check-4.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/cf_check-5.c
>
> diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
> index 95cfa05ce61..d356e0e7acd 100644
> --- a/gcc/config/i386/i386.c
> +++ b/gcc/config/i386/i386.c
> @@ -2604,6 +2604,9 @@ rest_of_insert_endbranch (void)
>
>    if (!lookup_attribute ("nocf_check",
>                          TYPE_ATTRIBUTES (TREE_TYPE (cfun->decl)))
> +      && (!flag_manual_endbr
> +         || lookup_attribute ("cf_check",
> +                              DECL_ATTRIBUTES (cfun->decl)))
>        && !cgraph_node::get (cfun->decl)->only_called_directly_p ())
>      {
>        cet_eb = gen_nop_endbr ();
> @@ -45896,6 +45899,8 @@ static const struct attribute_spec ix86_attribute_table[] =
>      ix86_handle_fndecl_attribute, NULL },
>    { "function_return", 1, 1, true, false, false, false,
>      ix86_handle_fndecl_attribute, NULL },
> +  { "cf_check", 0, 0, true, false, false, false,
> +    ix86_handle_fndecl_attribute, NULL },
>
>    /* End element.  */
>    { NULL, 0, 0, false, false, false, false, NULL, NULL }
> diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
> index a34d4acf1a2..aebc023420b 100644
> --- a/gcc/config/i386/i386.opt
> +++ b/gcc/config/i386/i386.opt
> @@ -1016,6 +1016,11 @@ Target Report Undocumented Var(flag_cet_switch) Init(0)
>  Turn on CET instrumentation for switch statements that use a jump table and
>  an indirect jump.
>
> +mmanual-endbr
> +Target Report Var(flag_manual_endbr) Init(0)
> +Insert ENDBR instruction at function entry only via cf_check attribute
> +for CET instrumentation.
> +
>  mforce-indirect-call
>  Target Report Var(flag_force_indirect_call) Init(0)
>  Make all function calls indirect.
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index e0a84b8b3c5..5fd4a1b22b0 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -5864,6 +5864,13 @@ foo (void)
>  @}
>  @end smallexample
>
> +@item cf_check
> +@cindex @code{cf_check} function attribute, x86
> +
> +The @code{cf_check} attribute on a function is used to inform the
> +compiler that ENDBR instruction should be placed at the function
> +entry when @option{-fcf-protection=branch} is enabled.
> +
>  @end table
>
>  On the x86, the inliner does not inline a
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 940b84697fa..7ec4267b7b1 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -1250,7 +1250,7 @@ See RS/6000 and PowerPC Options.
>  -msse4a  -m3dnow  -m3dnowa  -mpopcnt  -mabm  -mbmi  -mtbm  -mfma4  -mxop @gol
>  -mlzcnt  -mbmi2  -mfxsr  -mxsave  -mxsaveopt  -mrtm  -mlwp @gol
>  -mmwaitx  -mclzero  -mpku  -mthreads -mgfni  -mvaes  -mwaitpkg @gol
> --mshstk -mforce-indirect-call -mavx512vbmi2 @gol
> +-mshstk -mforce-indirect-call -mmanual-endbr -mavx512vbmi2 @gol
>  -mvpclmulqdq -mavx512bitalg -mmovdiri -mmovdir64b -mavx512vpopcntdq
>  -mcldemote @gol
>  -mms-bitfields  -mno-align-stringops  -minline-all-stringops @gol
> @@ -27476,6 +27476,13 @@ Force all calls to functions to be indirect. This is useful
>  when using Intel Processor Trace where it generates more precise timing
>  information for function calls.
>
> +@item -mmanual-endbr
> +@opindex mmanual-endbr
> +Insert ENDBR instruction at function entry only via the @code{cf_check}
> +function attribute. This is useful when used with the option
> +@option{-fcf-protection=branch} to control ENDBR insertion at the
> +function entry.
> +
>  @item -mcall-ms2sysv-xlogues
>  @opindex mcall-ms2sysv-xlogues
>  @opindex mno-call-ms2sysv-xlogues
> diff --git a/gcc/testsuite/gcc.target/i386/cf_check-1.c b/gcc/testsuite/gcc.target/i386/cf_check-1.c
> new file mode 100644
> index 00000000000..c433eab854a
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/cf_check-1.c
> @@ -0,0 +1,11 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fcf-protection -mmanual-endbr" } */
> +/* { dg-final { scan-assembler-not {\mendbr} } } */
> +
> +extern void bar (void) __attribute__((__cf_check__));
> +
> +void
> +foo (void)
> +{
> +  bar ();
> +}
> diff --git a/gcc/testsuite/gcc.target/i386/cf_check-2.c b/gcc/testsuite/gcc.target/i386/cf_check-2.c
> new file mode 100644
> index 00000000000..e2b9c4dbcb2
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/cf_check-2.c
> @@ -0,0 +1,11 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fcf-protection -mno-manual-endbr" } */
> +/* { dg-final { scan-assembler-times {\mendbr} 1 } } */
> +
> +extern void bar (void) __attribute__((__cf_check__));
> +
> +void
> +foo (void)
> +{
> +  bar ();
> +}
> diff --git a/gcc/testsuite/gcc.target/i386/cf_check-3.c b/gcc/testsuite/gcc.target/i386/cf_check-3.c
> new file mode 100644
> index 00000000000..d835cc3a21b
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/cf_check-3.c
> @@ -0,0 +1,11 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fcf-protection=none" } */
> +/* { dg-final { scan-assembler-not {\mendbr} } } */
> +
> +extern void bar (void) __attribute__((__cf_check__));
> +
> +void
> +foo (void)
> +{
> +  bar ();
> +}
> diff --git a/gcc/testsuite/gcc.target/i386/cf_check-4.c b/gcc/testsuite/gcc.target/i386/cf_check-4.c
> new file mode 100644
> index 00000000000..d6cb27cf20b
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/cf_check-4.c
> @@ -0,0 +1,10 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fcf-protection -mmanual-endbr" } */
> +/* { dg-final { scan-assembler-times {\mendbr} 1 } } */
> +
> +extern void foo (void) __attribute__((__cf_check__));
> +
> +void
> +foo (void)
> +{
> +}
> diff --git a/gcc/testsuite/gcc.target/i386/cf_check-5.c b/gcc/testsuite/gcc.target/i386/cf_check-5.c
> new file mode 100644
> index 00000000000..f2c0c5c2c09
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/cf_check-5.c
> @@ -0,0 +1,9 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fcf-protection -mmanual-endbr" } */
> +/* { dg-final { scan-assembler-times {\mendbr} 1 } } */
> +
> +__attribute__((__cf_check__))
> +void
> +foo (void)
> +{
> +}
> --
> 2.17.1
>
H.J. Lu Dec. 3, 2018, 1:45 p.m. UTC | #2
On Mon, Jun 18, 2018 at 2:20 AM Richard Biener
<richard.guenther@gmail.com> wrote:
>
> On Fri, Jun 15, 2018 at 2:59 PM H.J. Lu <hongjiu.lu@intel.com> wrote:
> >
> > Currently GCC inserts ENDBR instruction at entries of all non-static
> > functions, unless LTO compilation is used.  Marking all functions,
> > which are not called indirectly with nocf_check attribute, is not
> > ideal since 99% of functions in a program may be of this kind.
> >
> > This patch adds -mmanual-endbr and cf_check function attribute.  They
> > can be used together with -fcf-protection such that ENDBR instruction
> > is inserted only at entries of functions with cf_check attribute.  It
> > can limit number of ENDBR instructions to reduce program size.
> >
> > OK for trubk?
>
> I wonder if the linker could assist with ENDBR creation by
> redirecting all non-direct call relocs to a linker-generated
> stub with ENBR and a direct branch?
>

The goal of this patch is to add as few as ENDBR as possible
to reduce program size as much as possible.   Also there is no
relocation for indirect branch via register.
diff mbox series

Patch

diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 95cfa05ce61..d356e0e7acd 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -2604,6 +2604,9 @@  rest_of_insert_endbranch (void)
 
   if (!lookup_attribute ("nocf_check",
 			 TYPE_ATTRIBUTES (TREE_TYPE (cfun->decl)))
+      && (!flag_manual_endbr
+	  || lookup_attribute ("cf_check",
+			       DECL_ATTRIBUTES (cfun->decl)))
       && !cgraph_node::get (cfun->decl)->only_called_directly_p ())
     {
       cet_eb = gen_nop_endbr ();
@@ -45896,6 +45899,8 @@  static const struct attribute_spec ix86_attribute_table[] =
     ix86_handle_fndecl_attribute, NULL },
   { "function_return", 1, 1, true, false, false, false,
     ix86_handle_fndecl_attribute, NULL },
+  { "cf_check", 0, 0, true, false, false, false,
+    ix86_handle_fndecl_attribute, NULL },
 
   /* End element.  */
   { NULL, 0, 0, false, false, false, false, NULL, NULL }
diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
index a34d4acf1a2..aebc023420b 100644
--- a/gcc/config/i386/i386.opt
+++ b/gcc/config/i386/i386.opt
@@ -1016,6 +1016,11 @@  Target Report Undocumented Var(flag_cet_switch) Init(0)
 Turn on CET instrumentation for switch statements that use a jump table and
 an indirect jump.
 
+mmanual-endbr
+Target Report Var(flag_manual_endbr) Init(0)
+Insert ENDBR instruction at function entry only via cf_check attribute
+for CET instrumentation.
+
 mforce-indirect-call
 Target Report Var(flag_force_indirect_call) Init(0)
 Make all function calls indirect.
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index e0a84b8b3c5..5fd4a1b22b0 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -5864,6 +5864,13 @@  foo (void)
 @}
 @end smallexample
 
+@item cf_check
+@cindex @code{cf_check} function attribute, x86
+
+The @code{cf_check} attribute on a function is used to inform the
+compiler that ENDBR instruction should be placed at the function
+entry when @option{-fcf-protection=branch} is enabled.
+
 @end table
 
 On the x86, the inliner does not inline a
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 940b84697fa..7ec4267b7b1 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -1250,7 +1250,7 @@  See RS/6000 and PowerPC Options.
 -msse4a  -m3dnow  -m3dnowa  -mpopcnt  -mabm  -mbmi  -mtbm  -mfma4  -mxop @gol
 -mlzcnt  -mbmi2  -mfxsr  -mxsave  -mxsaveopt  -mrtm  -mlwp @gol
 -mmwaitx  -mclzero  -mpku  -mthreads -mgfni  -mvaes  -mwaitpkg @gol
--mshstk -mforce-indirect-call -mavx512vbmi2 @gol
+-mshstk -mforce-indirect-call -mmanual-endbr -mavx512vbmi2 @gol
 -mvpclmulqdq -mavx512bitalg -mmovdiri -mmovdir64b -mavx512vpopcntdq
 -mcldemote @gol
 -mms-bitfields  -mno-align-stringops  -minline-all-stringops @gol
@@ -27476,6 +27476,13 @@  Force all calls to functions to be indirect. This is useful
 when using Intel Processor Trace where it generates more precise timing
 information for function calls.
 
+@item -mmanual-endbr
+@opindex mmanual-endbr
+Insert ENDBR instruction at function entry only via the @code{cf_check}
+function attribute. This is useful when used with the option
+@option{-fcf-protection=branch} to control ENDBR insertion at the
+function entry.
+
 @item -mcall-ms2sysv-xlogues
 @opindex mcall-ms2sysv-xlogues
 @opindex mno-call-ms2sysv-xlogues
diff --git a/gcc/testsuite/gcc.target/i386/cf_check-1.c b/gcc/testsuite/gcc.target/i386/cf_check-1.c
new file mode 100644
index 00000000000..c433eab854a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/cf_check-1.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fcf-protection -mmanual-endbr" } */
+/* { dg-final { scan-assembler-not {\mendbr} } } */
+
+extern void bar (void) __attribute__((__cf_check__));
+
+void
+foo (void)
+{
+  bar ();
+}
diff --git a/gcc/testsuite/gcc.target/i386/cf_check-2.c b/gcc/testsuite/gcc.target/i386/cf_check-2.c
new file mode 100644
index 00000000000..e2b9c4dbcb2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/cf_check-2.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fcf-protection -mno-manual-endbr" } */
+/* { dg-final { scan-assembler-times {\mendbr} 1 } } */
+
+extern void bar (void) __attribute__((__cf_check__));
+
+void
+foo (void)
+{
+  bar ();
+}
diff --git a/gcc/testsuite/gcc.target/i386/cf_check-3.c b/gcc/testsuite/gcc.target/i386/cf_check-3.c
new file mode 100644
index 00000000000..d835cc3a21b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/cf_check-3.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fcf-protection=none" } */
+/* { dg-final { scan-assembler-not {\mendbr} } } */
+
+extern void bar (void) __attribute__((__cf_check__));
+
+void
+foo (void)
+{
+  bar ();
+}
diff --git a/gcc/testsuite/gcc.target/i386/cf_check-4.c b/gcc/testsuite/gcc.target/i386/cf_check-4.c
new file mode 100644
index 00000000000..d6cb27cf20b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/cf_check-4.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fcf-protection -mmanual-endbr" } */
+/* { dg-final { scan-assembler-times {\mendbr} 1 } } */
+
+extern void foo (void) __attribute__((__cf_check__));
+
+void
+foo (void)
+{
+}
diff --git a/gcc/testsuite/gcc.target/i386/cf_check-5.c b/gcc/testsuite/gcc.target/i386/cf_check-5.c
new file mode 100644
index 00000000000..f2c0c5c2c09
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/cf_check-5.c
@@ -0,0 +1,9 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fcf-protection -mmanual-endbr" } */
+/* { dg-final { scan-assembler-times {\mendbr} 1 } } */
+
+__attribute__((__cf_check__))
+void
+foo (void)
+{
+}