On Thu, May 28, 2015 at 10:37:53AM -0700, H.J. Lu wrote: > This patch extends -fno-plt to normal non-PIC calls on x86. -fno-plt > works in 64-bit mode with the existing binutils. For 32-bit, we need > the updated assembler and linker to support "call/jmp *foo@GOT" with > a new relocation different from R_386_GOT32 to indicate that this > relocation applies to indirect branches. A configure time check is > added to verify that 32-bit assembler generates a known relocation > which is different from R_386_GOT32. A new 32-bit relocaton is needed > since "call/jmp *foo@GOT" requires a different relocation from R_386_GOT32 > which is used together with a GOT register in "call/jmp *foo@GOT(%reg)". > > OK for master? > > Thanks. > > H.J. > --- > * configure.ac (HAVE_AS_INDIRECT_BRANCH_VIA_GOT): New. Defined > if 32-bit assembler generates a known relocation which is > different from R_386_GOT32. > * config.in: Regenerated. > * configure: Likewise. > * config/i386/i386.c (ix86_output_call_insn): Extend -fno-plt > to normal non-PIC branches.
Here is the updated patch to properly handle local functions with testcases. H.J. --- gcc/ * configure.ac (HAVE_AS_INDIRECT_BRANCH_VIA_GOT): New. Defined if 32-bit assembler generates a known relocation which is different from R_386_GOT32. * config.in: Regenerated. * configure: Likewise. * config/i386/i386.c (ix86_output_call_insn): Extend -fno-plt to normal non-PIC branches. * config/i386/i386.h (TARGET_ELF): New. gcc/testsuite/ * gcc.target/i386/pr66232-6.c: New tests. * gcc.target/i386/pr66232-7.c: Likewise. * gcc.target/i386/pr66232-8.c: Likewise. * gcc.target/i386/pr66232-9.c: Likewise. * gcc.target/i386/pr66232-10.c: Likewise. * gcc.target/i386/pr66232-11.c: Likewise. * gcc.target/i386/pr66232-12.c: Likewise. * gcc.target/i386/pr66232-13.c: Likewise. * lib/target-supports.exp (check_effective_target_branch_via_got): New. --- gcc/config.in | 14 +++++--- gcc/config/i386/i386.c | 40 +++++++++++++++++++++-- gcc/config/i386/i386.h | 2 ++ gcc/configure | 47 ++++++++++++++++++++++++++- gcc/configure.ac | 18 ++++++++++- gcc/testsuite/gcc.target/i386/pr66232-10.c | 13 ++++++++ gcc/testsuite/gcc.target/i386/pr66232-11.c | 14 ++++++++ gcc/testsuite/gcc.target/i386/pr66232-12.c | 13 ++++++++ gcc/testsuite/gcc.target/i386/pr66232-13.c | 13 ++++++++ gcc/testsuite/gcc.target/i386/pr66232-6.c | 13 ++++++++ gcc/testsuite/gcc.target/i386/pr66232-7.c | 14 ++++++++ gcc/testsuite/gcc.target/i386/pr66232-8.c | 13 ++++++++ gcc/testsuite/gcc.target/i386/pr66232-9.c | 13 ++++++++ gcc/testsuite/lib/target-supports.exp | 52 ++++++++++++++++++++++++++++++ 14 files changed, 271 insertions(+), 8 deletions(-) create mode 100644 gcc/testsuite/gcc.target/i386/pr66232-10.c create mode 100644 gcc/testsuite/gcc.target/i386/pr66232-11.c create mode 100644 gcc/testsuite/gcc.target/i386/pr66232-12.c create mode 100644 gcc/testsuite/gcc.target/i386/pr66232-13.c create mode 100644 gcc/testsuite/gcc.target/i386/pr66232-6.c create mode 100644 gcc/testsuite/gcc.target/i386/pr66232-7.c create mode 100644 gcc/testsuite/gcc.target/i386/pr66232-8.c create mode 100644 gcc/testsuite/gcc.target/i386/pr66232-9.c diff --git a/gcc/config.in b/gcc/config.in index daaf906..0ee5c38 100644 --- a/gcc/config.in +++ b/gcc/config.in @@ -363,6 +363,12 @@ #endif +/* Define true if the assembler supports 'call *foo@GOT'. */ +#ifndef USED_FOR_TARGET +#undef HAVE_AS_INDIRECT_BRANCH_VIA_GOT +#endif + + /* Define if your assembler supports the Sun syntax for cmov. */ #ifndef USED_FOR_TARGET #undef HAVE_AS_IX86_CMOV_SUN_SYNTAX @@ -686,8 +692,8 @@ #endif -/* Define to 1 if we found a declaration for 'basename', otherwise define to - 0. */ +/* Define to 1 if you have the declaration of `basename(const char*)', and to + 0 if you don't. */ #ifndef USED_FOR_TARGET #undef HAVE_DECL_BASENAME #endif @@ -963,8 +969,8 @@ #endif -/* Define to 1 if we found a declaration for 'strstr', otherwise define to 0. - */ +/* Define to 1 if you have the declaration of `strstr(const char*,const + char*)', and to 0 if you don't. */ #ifndef USED_FOR_TARGET #undef HAVE_DECL_STRSTR #endif diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index e77cd04..63ebc7f 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -25611,7 +25611,25 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op) if (SIBLING_CALL_P (insn)) { if (direct_p) - xasm = "%!jmp\t%P0"; + { + if (!SYMBOL_REF_LOCAL_P (call_op) + && !flag_plt + && !flag_pic + && TARGET_ELF) + { + /* Avoid PLT. */ + if (TARGET_64BIT) + xasm = "%!jmp\t*%p0@GOTPCREL(%%rip)"; + else +#ifdef HAVE_AS_INDIRECT_BRANCH_VIA_GOT + xasm = "%!jmp\t*%p0@GOT"; +#else + xasm = "%!jmp\t%P0"; +#endif + } + else + xasm = "%!jmp\t%P0"; + } /* SEH epilogue detection requires the indirect branch case to include REX.W. */ else if (TARGET_SEH) @@ -25654,7 +25672,25 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op) } if (direct_p) - xasm = "%!call\t%P0"; + { + if (!SYMBOL_REF_LOCAL_P (call_op) + && !flag_plt + && !flag_pic + && TARGET_ELF) + { + /* Avoid PLT. */ + if (TARGET_64BIT) + xasm = "%!call\t*%p0@GOTPCREL(%%rip)"; + else +#ifdef HAVE_AS_INDIRECT_BRANCH_VIA_GOT + xasm = "%!call\t*%p0@GOT"; +#else + xasm = "%!call\t%P0"; +#endif + } + else + xasm = "%!call\t%P0"; + } else xasm = "%!call\t%A0"; diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h index 34ef343..9b7f10f 100644 --- a/gcc/config/i386/i386.h +++ b/gcc/config/i386/i386.h @@ -584,6 +584,8 @@ extern tree x86_mfence; /* This is re-defined by cygming.h. */ #define TARGET_PECOFF 0 +#define TARGET_ELF (!TARGET_MACHO && !TARGET_SEH && !TARGET_PECOFF) + /* The default abi used by target. */ #define DEFAULT_ABI SYSV_ABI diff --git a/gcc/configure b/gcc/configure index a9a76d6..4419035 100755 --- a/gcc/configure +++ b/gcc/configure @@ -25361,7 +25361,7 @@ $as_echo "#define HAVE_AS_IX86_DIFF_SECT_DELTA 1" >>confdefs.h fi - # These two are used unconditionally by i386.[ch]; it is to be defined + # These three are used unconditionally by i386.[ch]; it is to be defined # to 1 if the feature is present, 0 otherwise. as_ix86_gotoff_in_data_opt= if test x$gas = xyes; then @@ -25407,6 +25407,51 @@ cat >>confdefs.h <<_ACEOF _ACEOF + as_ix86_indirect_branch_via_got_opt= + if test x$gas = xyes; then + as_ix86_indirect_branch_via_got_opt="--32" + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for call *foo@GOT" >&5 +$as_echo_n "checking assembler for call *foo@GOT... " >&6; } +if test "${gcc_cv_as_ix86_indirect_branch_via_got+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + gcc_cv_as_ix86_indirect_branch_via_got=no + if test $in_tree_gas = yes; then + if test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 26 \) \* 1000 + 0` + then gcc_cv_as_ix86_indirect_branch_via_got=yes +fi + elif test x$gcc_cv_as != x; then + $as_echo ' .text + call *foo@GOT' > conftest.s + if { ac_try='$gcc_cv_as $gcc_cv_as_flags $as_ix86_indirect_branch_via_got_opt -o conftest.o conftest.s >&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } + then + if test x$gcc_cv_readelf != x \ + && $gcc_cv_readelf -r conftest.o | grep R_386_ | grep -v R_386_GOT32 > /dev/null 2>&1; then + gcc_cv_as_ix86_indirect_branch_via_got=yes + fi + else + echo "configure: failed program was" >&5 + cat conftest.s >&5 + fi + rm -f conftest.o conftest.s + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_as_ix86_indirect_branch_via_got" >&5 +$as_echo "$gcc_cv_as_ix86_indirect_branch_via_got" >&6; } +if test $gcc_cv_as_ix86_indirect_branch_via_got = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_AS_INDIRECT_BRANCH_VIA_GOT 1 +_ACEOF + +fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for rep and lock prefix" >&5 $as_echo_n "checking assembler for rep and lock prefix... " >&6; } if test "${gcc_cv_as_ix86_rep_lock_prefix+set}" = set; then : diff --git a/gcc/configure.ac b/gcc/configure.ac index 83f9c09..543e650 100644 --- a/gcc/configure.ac +++ b/gcc/configure.ac @@ -3953,7 +3953,7 @@ foo: nop [AC_DEFINE(HAVE_AS_IX86_DIFF_SECT_DELTA, 1, [Define if your assembler supports the subtraction of symbols in different sections.])]) - # These two are used unconditionally by i386.[ch]; it is to be defined + # These three are used unconditionally by i386.[ch]; it is to be defined # to 1 if the feature is present, 0 otherwise. as_ix86_gotoff_in_data_opt= if test x$gas = xyes; then @@ -3971,6 +3971,22 @@ foo: nop [`if test $gcc_cv_as_ix86_gotoff_in_data = yes; then echo 1; else echo 0; fi`], [Define true if the assembler supports '.long foo@GOTOFF'.]) + as_ix86_indirect_branch_via_got_opt= + if test x$gas = xyes; then + as_ix86_indirect_branch_via_got_opt="--32" + fi + gcc_GAS_CHECK_FEATURE([call *foo@GOT], + gcc_cv_as_ix86_indirect_branch_via_got, [2,26,0], + [$as_ix86_indirect_branch_via_got_opt], +[ .text + call *foo@GOT], + [if test x$gcc_cv_readelf != x \ + && $gcc_cv_readelf -r conftest.o | grep R_386_ | grep -v R_386_GOT32 > /dev/null 2>&1; then + gcc_cv_as_ix86_indirect_branch_via_got=yes + fi], + [AC_DEFINE_UNQUOTED(HAVE_AS_INDIRECT_BRANCH_VIA_GOT, 1, + [Define true if the assembler supports 'call *foo@GOT'.])]) + gcc_GAS_CHECK_FEATURE([rep and lock prefix], gcc_cv_as_ix86_rep_lock_prefix,,, [rep movsl diff --git a/gcc/testsuite/gcc.target/i386/pr66232-10.c b/gcc/testsuite/gcc.target/i386/pr66232-10.c new file mode 100644 index 0000000..e95c789 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr66232-10.c @@ -0,0 +1,13 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fno-pic -fno-plt" } */ + +extern void bar (void) __attribute__((visibility("hidden"))); + +void +foo (void) +{ + bar (); +} + +/* { dg-final { scan-assembler-not "jmp\[ \t\]*.bar@GOTPCREL" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "jmp\[ \t\]*.bar@GOT" { target ia32 } } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr66232-11.c b/gcc/testsuite/gcc.target/i386/pr66232-11.c new file mode 100644 index 0000000..63a1809 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr66232-11.c @@ -0,0 +1,14 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fno-pic -fno-plt" } */ + +extern void bar (void) __attribute__((visibility("hidden"))); + +int +foo (void) +{ + bar (); + return 0; +} + +/* { dg-final { scan-assembler-not "call\[ \t\]*.bar@GOTPCREL" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "call\[ \t\]*.bar@GOT" { target ia32 } } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr66232-12.c b/gcc/testsuite/gcc.target/i386/pr66232-12.c new file mode 100644 index 0000000..89bb605 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr66232-12.c @@ -0,0 +1,13 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fno-pic -fno-plt" } */ + +extern int bar (void) __attribute__((visibility("hidden"))); + +int +foo (void) +{ + return bar (); +} + +/* { dg-final { scan-assembler-not "jmp\[ \t\]*.bar@GOTPCREL" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "jmp\[ \t\]*.bar@GOT" { target ia32 } } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr66232-13.c b/gcc/testsuite/gcc.target/i386/pr66232-13.c new file mode 100644 index 0000000..4aec477 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr66232-13.c @@ -0,0 +1,13 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fno-pic -fno-plt" } */ + +extern int bar (void) __attribute__((visibility("hidden"))); + +int +foo (void) +{ + return bar () + 1; +} + +/* { dg-final { scan-assembler-not "call\[ \t\]*.bar@GOTPCREL" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "call\[ \t\]*.bar@GOT" { target ia32 } } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr66232-6.c b/gcc/testsuite/gcc.target/i386/pr66232-6.c new file mode 100644 index 0000000..b465e59 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr66232-6.c @@ -0,0 +1,13 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fno-pic -fno-plt" } */ + +extern void bar (void); + +void +foo (void) +{ + bar (); +} + +/* { dg-final { scan-assembler "jmp\[ \t\]*.bar@GOTPCREL" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*.bar@GOT" { target { ia32 && branch_via_got } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr66232-7.c b/gcc/testsuite/gcc.target/i386/pr66232-7.c new file mode 100644 index 0000000..2908541 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr66232-7.c @@ -0,0 +1,14 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fno-pic -fno-plt" } */ + +extern void bar (void); + +int +foo (void) +{ + bar (); + return 0; +} + +/* { dg-final { scan-assembler "call\[ \t\]*.bar@GOTPCREL" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "call\[ \t\]*.bar@GOT" { target { ia32 && branch_via_got } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr66232-8.c b/gcc/testsuite/gcc.target/i386/pr66232-8.c new file mode 100644 index 0000000..c23d6bb --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr66232-8.c @@ -0,0 +1,13 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fno-pic -fno-plt" } */ + +extern int bar (void); + +int +foo (void) +{ + return bar (); +} + +/* { dg-final { scan-assembler "jmp\[ \t\]*.bar@GOTPCREL" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*.bar@GOT" { target { ia32 && branch_via_got } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr66232-9.c b/gcc/testsuite/gcc.target/i386/pr66232-9.c new file mode 100644 index 0000000..b0cd40b --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr66232-9.c @@ -0,0 +1,13 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fno-pic -fno-plt" } */ + +extern int bar (void); + +int +foo (void) +{ + return bar () + 1; +} + +/* { dg-final { scan-assembler "call\[ \t\]*.bar@GOTPCREL" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "call\[ \t\]*.bar@GOT" { target { ia32 && branch_via_got } } } } */ diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp index f0c209f..f80fd60 100644 --- a/gcc/testsuite/lib/target-supports.exp +++ b/gcc/testsuite/lib/target-supports.exp @@ -6320,6 +6320,58 @@ proc check_effective_target_pie_copyreloc { } { return $pie_copyreloc_available_saved } +# Return 1 if the x86 target supports "call *foo@GOT", 0 otherwise. +# Cache the result. + +proc check_effective_target_branch_via_got { } { + global branch_via_got_available_saved + global tool + global GCC_UNDER_TEST + + if { !([istarget x86_64-*-*] || [istarget i?86-*-*]) } { + return 0 + } + + # Need auto-host.h to check linker support. + if { ![file exists ../../auto-host.h ] } { + return 0 + } + + if [info exists branch_via_got_available_saved] { + verbose "check_effective_target_branch_via_got returning saved $branch_via_got_available_saved" 2 + } else { + # Set up and compile to see if linker supports PIE with copy + # reloc. Include the current process ID in the file names to + # prevent conflicts with invocations for multiple testsuites. + + set src pie[pid].c + set obj pie[pid].o + + set f [open $src "w"] + puts $f "#include \"../../auto-host.h\"" + puts $f "#ifndef HAVE_AS_INDIRECT_BRANCH_VIA_GOT" + puts $f "# error Assembler does not support call *foo@GOT." + puts $f "#endif" + close $f + + verbose "check_effective_target_branch_via_got compiling testfile $src" 2 + set lines [${tool}_target_compile $src $obj object ""] + + file delete $src + file delete $obj + + if [string match "" $lines] then { + verbose "check_effective_target_branch_via_got testfile compilation passed" 2 + set branch_via_got_available_saved 1 + } else { + verbose "check_effective_target_branch_via_got testfile compilation failed" 2 + set branch_via_got_available_saved 0 + } + } + + return $branch_via_got_available_saved +} + # Return 1 if the target uses comdat groups. proc check_effective_target_comdat_group {} { -- 1.9.3