i386 psABI has been updated to clarify that R_386_GOT32X and R_386_GOT32 relocations can be used to access GOT without base register when PIC is disabled:
https://groups.google.com/forum/#!topic/ia32-abi/awsRSvJOJfs 32-bit x86 assembler and linker from binutils 2.26.1 and 2.27 support call/jmp *_start@GOT cmpl $0, bar@GOT for both normal and IFUNC functions. We check if 32-bit x86 assembler and linker have the fix for: https://sourceware.org/bugzilla/show_bug.cgi?id=20244 before accessing external function via GOT slot for -fno-plt in both PIC and non-PIC modes. Tested on i686 and x86-64. OK for trunk? H.J. ---- PR target/66232 PR target/67400 * configure.ac (HAVE_LD_IX86_GOT32X_RELOC): New. Defined to 1 if 32-bit assembler and linker support "jmp *_start@GOT" and "movl $0, bar@GOT". Otherise, defined to 0. * config.in: Regenerated. * configure: Likewise. * config/i386/i386.c (ix86_force_load_from_GOT_p): Return true if HAVE_LD_IX86_GOT32X_RELOC is 1 in 32-bit mode. (ix86_legitimate_address_p): Allow UNSPEC_GOT for -fno-plt if ix86_force_load_from_GOT_p returns true. (ix86_print_operand_address_as): Also support UNSPEC_GOT if ix86_force_load_from_GOT_p returns true. (ix86_expand_move): Generate UNSPEC_GOT in 32-bit mode to load the external function address via the GOT slot. (ix86_nopic_noplt_attribute_p): Check HAVE_LD_IX86_GOT32X_RELOC == 0 before returning false in 32-bit mode. (ix86_output_call_insn): Generate "%!jmp/call\t*%p0@GOT" in 32-bit mode if ix86_nopic_noplt_attribute_p returns true. gcc/testsuite/ PR target/66232 PR target/67400 * gcc.target/i386/pr66232-14.c: New file. * gcc.target/i386/pr66232-15.c: Likewise. * gcc.target/i386/pr66232-16.c: Likewise. * gcc.target/i386/pr66232-17.c: Likewise. * gcc.target/i386/pr67400-1.c: Don't disable for ia32. Scan for ia32 and if R_386_GOT32X relocation is supported. * gcc.target/i386/pr67400-2.c: Likewise. * gcc.target/i386/pr67400-3.c: Likewise. * gcc.target/i386/pr67400-4.c: Likewise. * gcc.target/i386/pr67400-6.c: Likewise. * gcc.target/i386/pr67400-7.c: Likewise. * lib/target-supports.exp (check_effective_target_got32x_reloc): New. --- gcc/config.in | 9 +++++- gcc/config/i386/i386.c | 35 ++++++++++++++++---- gcc/configure | 50 +++++++++++++++++++++++++++++ gcc/configure.ac | 42 ++++++++++++++++++++++++ gcc/testsuite/gcc.target/i386/pr66232-14.c | 13 ++++++++ gcc/testsuite/gcc.target/i386/pr66232-15.c | 14 ++++++++ gcc/testsuite/gcc.target/i386/pr66232-16.c | 13 ++++++++ gcc/testsuite/gcc.target/i386/pr66232-17.c | 13 ++++++++ gcc/testsuite/gcc.target/i386/pr67400-1.c | 8 +++-- gcc/testsuite/gcc.target/i386/pr67400-2.c | 8 +++-- gcc/testsuite/gcc.target/i386/pr67400-3.c | 3 +- gcc/testsuite/gcc.target/i386/pr67400-4.c | 5 +-- gcc/testsuite/gcc.target/i386/pr67400-6.c | 8 +++-- gcc/testsuite/gcc.target/i386/pr67400-7.c | 6 ++-- gcc/testsuite/lib/target-supports.exp | 51 ++++++++++++++++++++++++++++++ 15 files changed, 256 insertions(+), 22 deletions(-) create mode 100644 gcc/testsuite/gcc.target/i386/pr66232-14.c create mode 100644 gcc/testsuite/gcc.target/i386/pr66232-15.c create mode 100644 gcc/testsuite/gcc.target/i386/pr66232-16.c create mode 100644 gcc/testsuite/gcc.target/i386/pr66232-17.c diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 9c7b015..a2dcf36 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -15125,7 +15125,8 @@ darwin_local_data_pic (rtx disp) bool ix86_force_load_from_GOT_p (rtx x) { - return (TARGET_64BIT && !TARGET_PECOFF && !TARGET_MACHO + return ((TARGET_64BIT || HAVE_LD_IX86_GOT32X_RELOC) + && !TARGET_PECOFF && !TARGET_MACHO && !flag_plt && !flag_pic && ix86_cmodel != CM_LARGE && GET_CODE (x) == SYMBOL_REF @@ -15606,6 +15607,14 @@ ix86_legitimate_address_p (machine_mode, rtx addr, bool strict) used. While ABI specify also 32bit relocations, we don't produce them at all and use IP relative instead. */ case UNSPEC_GOT: + gcc_assert (flag_pic + || ix86_force_load_from_GOT_p (XVECEXP (XEXP (disp, 0), 0, 0))); + if (!TARGET_64BIT) + goto is_legitimate_pic; + + /* 64bit address unspec. */ + return false; + case UNSPEC_GOTOFF: gcc_assert (flag_pic); if (!TARGET_64BIT) @@ -18194,7 +18203,8 @@ ix86_print_operand_address_as (FILE *file, rtx addr, /* Load the external function address via the GOT slot to avoid PLT. */ else if (GET_CODE (disp) == CONST && GET_CODE (XEXP (disp, 0)) == UNSPEC - && XINT (XEXP (disp, 0), 1) == UNSPEC_GOTPCREL + && (XINT (XEXP (disp, 0), 1) == UNSPEC_GOTPCREL + || XINT (XEXP (disp, 0), 1) == UNSPEC_GOT) && ix86_force_load_from_GOT_p (XVECEXP (XEXP (disp, 0), 0, 0))) output_pic_addr_const (file, disp, 0); else if (flag_pic) @@ -19449,7 +19459,9 @@ ix86_expand_move (machine_mode mode, rtx operands[]) { /* Load the external function address via GOT slot to avoid PLT. */ op1 = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op1), - UNSPEC_GOTPCREL); + (TARGET_64BIT + ? UNSPEC_GOTPCREL + : UNSPEC_GOT)); op1 = gen_rtx_CONST (Pmode, op1); op1 = gen_const_mem (Pmode, op1); set_mem_alias_set (op1, ix86_GOT_alias_set ()); @@ -28027,7 +28039,8 @@ static bool ix86_nopic_noplt_attribute_p (rtx call_op) { if (flag_pic || ix86_cmodel == CM_LARGE - || !TARGET_64BIT || TARGET_MACHO || TARGET_SEH || TARGET_PECOFF + || (!TARGET_64BIT && HAVE_LD_IX86_GOT32X_RELOC == 0) + || TARGET_MACHO || TARGET_SEH || TARGET_PECOFF || SYMBOL_REF_LOCAL_P (call_op)) return false; @@ -28055,7 +28068,12 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op) if (direct_p) { if (ix86_nopic_noplt_attribute_p (call_op)) - xasm = "%!jmp\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}"; + { + if (TARGET_64BIT) + xasm = "%!jmp\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}"; + else + xasm = "%!jmp\t{*%p0@GOT|[DWORD PTR %p0@GOT]}"; + } else xasm = "%!jmp\t%P0"; } @@ -28103,7 +28121,12 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op) if (direct_p) { if (ix86_nopic_noplt_attribute_p (call_op)) - xasm = "%!call\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}"; + { + if (TARGET_64BIT) + xasm = "%!call\t{*%p0@GOTPCREL(%%rip)|[QWORD PTR %p0@GOTPCREL[rip]]}"; + else + xasm = "%!call\t{*%p0@GOT|[DWORD PTR %p0@GOT]}"; + } else xasm = "%!call\t%P0"; } diff --git a/gcc/configure.ac b/gcc/configure.ac index fabd48e..2506957 100644 --- a/gcc/configure.ac +++ b/gcc/configure.ac @@ -4200,6 +4200,48 @@ value:' [`if test $gcc_cv_as_ix86_tlsldm = yes; then echo 1; else echo 0; fi`], [Define to 1 if your assembler and linker support @tlsldm.]) + AC_CACHE_CHECK([linker for R_386_GOT32X relocation], + [gcc_cv_ld_ix86_got32x_reloc], + [gcc_cv_ld_ix86_got32x_reloc=no + if test x$gcc_cv_as != x -a x$gcc_cv_ld != x \ + -a x$gcc_cv_objdump != x -a x$gcc_cv_readelf != x; then + # Enforce 32-bit output with gas and gld. + if test x$gas = xyes; then + as_ix86_got32x_opt="--32" + fi + if echo "$ld_ver" | grep GNU > /dev/null; then + if $gcc_cv_ld -V 2>/dev/null | grep elf_i386_sol2 > /dev/null; then + ld_ix86_got32x_opt="-melf_i386_sol2" + else + ld_ix86_got32x_opt="-melf_i386" + fi + fi + cat > conftest.s <<EOF + .data +bar: + .byte 1 + .text + .global _start +_start: + movl \$0, bar@GOT + jmp *_start@GOT +EOF + if $gcc_cv_as $as_ix86_got32x_opt -o conftest.o conftest.s > /dev/null 2>&1 \ + && $gcc_cv_readelf --relocs --wide conftest.o 2>&1 \ + | grep R_386_GOT32X > /dev/null 2>&1 \ + && $gcc_cv_ld $ld_ix86_got32x_opt -o conftest conftest.o > /dev/null 2>&1; then + if $gcc_cv_objdump -dw conftest 2>&1 \ + | grep 0xffffff > /dev/null 2>&1; then + gcc_cv_ld_ix86_got32x_reloc=no + else + gcc_cv_ld_ix86_got32x_reloc=yes + fi + fi + fi + rm -f conftest conftest.s conftest.o]) + AC_DEFINE_UNQUOTED(HAVE_LD_IX86_GOT32X_RELOC, + [`if test x"$gcc_cv_ld_ix86_got32x_reloc" = xyes; then echo 1; else echo 0; fi`], + [Define 0/1 if Define if your linker supports R_386_GOT32X relocation.]) ;; ia64*-*-*) diff --git a/gcc/testsuite/gcc.target/i386/pr66232-14.c b/gcc/testsuite/gcc.target/i386/pr66232-14.c new file mode 100644 index 0000000..804e5a5 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr66232-14.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 && got32x_reloc } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr66232-15.c b/gcc/testsuite/gcc.target/i386/pr66232-15.c new file mode 100644 index 0000000..3d2f6da --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr66232-15.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 && got32x_reloc } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr66232-16.c b/gcc/testsuite/gcc.target/i386/pr66232-16.c new file mode 100644 index 0000000..d67f1a5 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr66232-16.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 && got32x_reloc } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr66232-17.c b/gcc/testsuite/gcc.target/i386/pr66232-17.c new file mode 100644 index 0000000..bf6f375 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr66232-17.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 && got32x_reloc } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr67400-1.c b/gcc/testsuite/gcc.target/i386/pr67400-1.c index 18b3790..8af6650 100644 --- a/gcc/testsuite/gcc.target/i386/pr67400-1.c +++ b/gcc/testsuite/gcc.target/i386/pr67400-1.c @@ -1,4 +1,4 @@ -/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */ +/* { dg-do compile { target *-*-linux* } } */ /* { dg-options "-O2 -fno-pic -fno-plt" } */ extern void bar (void); @@ -9,5 +9,7 @@ foo (void) return &bar; } -/* { dg-final { scan-assembler "mov\(l|q\)\[ \t\]*bar@GOTPCREL" } } */ -/* { dg-final { scan-assembler-not "\(mov|lea\)\(l|q\)\[ \t\]*\(\\\$|\)bar," } } */ +/* { dg-final { scan-assembler "mov\(l|q\)\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*bar@GOT," { target { ia32 && got32x_reloc } } } } */ +/* { dg-final { scan-assembler-not "\(mov|lea\)\(l|q\)\[ \t\]*\(\\\$|\)bar," { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "\(mov|lea\)l\[ \t\]*\(\\\$|\)bar," { target { ia32 && got32x_reloc } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr67400-2.c b/gcc/testsuite/gcc.target/i386/pr67400-2.c index 8f61c3f..23dd4bf 100644 --- a/gcc/testsuite/gcc.target/i386/pr67400-2.c +++ b/gcc/testsuite/gcc.target/i386/pr67400-2.c @@ -1,4 +1,4 @@ -/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */ +/* { dg-do compile { target *-*-linux* } } */ /* { dg-options "-O2 -fno-pic -fno-plt" } */ extern void bar (void); @@ -10,5 +10,7 @@ foo (void) p = &bar; } -/* { dg-final { scan-assembler "mov\(l|q\)\[ \t\]*bar@GOTPCREL" } } */ -/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*\\\$bar," } } */ +/* { dg-final { scan-assembler "mov\(l|q\)\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*bar@GOT," { target { ia32 && got32x_reloc } } } } */ +/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*\\\$bar," { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*\\\$bar," { target { ia32 && got32x_reloc } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr67400-3.c b/gcc/testsuite/gcc.target/i386/pr67400-3.c index 40d3521..649c980 100644 --- a/gcc/testsuite/gcc.target/i386/pr67400-3.c +++ b/gcc/testsuite/gcc.target/i386/pr67400-3.c @@ -13,4 +13,5 @@ foo (void) } /* { dg-final { scan-assembler "mov\(l|q\)\[ \t\]*\\\$bar," } } */ -/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*bar@GOTPCREL" } } */ +/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "movl\[ \t\]*bar@GOT," { target { ia32 && got32x_reloc } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr67400-4.c b/gcc/testsuite/gcc.target/i386/pr67400-4.c index a329bbf..5f6883d 100644 --- a/gcc/testsuite/gcc.target/i386/pr67400-4.c +++ b/gcc/testsuite/gcc.target/i386/pr67400-4.c @@ -1,4 +1,4 @@ -/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */ +/* { dg-do compile { target *-*-linux* } } */ /* { dg-options "-O2 -fno-pic -fno-plt" } */ extern void bar (void) __attribute__ ((visibility ("hidden"))); @@ -10,4 +10,5 @@ foo (void) } /* { dg-final { scan-assembler "mov\(l|q\)\[ \t\]*\\\$bar," } } */ -/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*bar@GOTPCREL" } } */ +/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "movl\[ \t\]*bar@GOT," { target { ia32 && got32x_reloc } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr67400-6.c b/gcc/testsuite/gcc.target/i386/pr67400-6.c index bb766cd..652add4 100644 --- a/gcc/testsuite/gcc.target/i386/pr67400-6.c +++ b/gcc/testsuite/gcc.target/i386/pr67400-6.c @@ -1,4 +1,4 @@ -/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */ +/* { dg-do compile { target *-*-linux* } } */ /* { dg-options "-O2 -fno-pic -fno-plt" } */ extern int bar (void); @@ -9,5 +9,7 @@ check (void *p) return p != &bar; } -/* { dg-final { scan-assembler "cmp\(l|q\)\[ \t\]*.*bar@GOTPCREL" } } */ -/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*\\\$bar," } } */ +/* { dg-final { scan-assembler "cmp\(l|q\)\[ \t\]*.*bar@GOTPCREL" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*bar@GOT," { target { ia32 && got32x_reloc } } } } */ +/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*\\\$bar," { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*\\\$bar," { target { ia32 && got32x_reloc } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr67400-7.c b/gcc/testsuite/gcc.target/i386/pr67400-7.c index 32ae85f..900e87a 100644 --- a/gcc/testsuite/gcc.target/i386/pr67400-7.c +++ b/gcc/testsuite/gcc.target/i386/pr67400-7.c @@ -1,4 +1,4 @@ -/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */ +/* { dg-do compile { target *-*-linux* } } */ /* { dg-options "-O2 -fno-pic -fno-plt" } */ extern void bar (void); @@ -9,5 +9,5 @@ foo (void) return &bar+1; } -/* { dg-final { scan-assembler "mov\(l|q\)\[ \t\]*bar@GOTPCREL" } } */ -/* { dg-final { scan-assembler-not "\(mov|lea\)\(l|q\)\[ \t\]*\(\\\$|\)bar," } } */ +/* { dg-final { scan-assembler "mov\(l|q\)\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "\(mov|lea\)\(l|q\)\[ \t\]*\(\\\$|\)bar," { target { ! ia32 } } } } */ diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp index 1b1d03a..0b9b507 100644 --- a/gcc/testsuite/lib/target-supports.exp +++ b/gcc/testsuite/lib/target-supports.exp @@ -6975,6 +6975,57 @@ proc check_effective_target_pie_copyreloc { } { return $pie_copyreloc_available_saved } +# Return 1 if the x86 target supports R_386_GOT32X relocation, 0 +# otherwise. Cache the result. + +proc check_effective_target_got32x_reloc { } { + global got32x_reloc_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 got32x_reloc_available_saved] { + verbose "check_effective_target_got32x_reloc returning saved $got32x_reloc_available_saved" 2 + } else { + # Include the current process ID in the file names to prevent + # conflicts with invocations for multiple testsuites. + + set src got32x[pid].c + set obj got32x[pid].o + + set f [open $src "w"] + puts $f "#include \"../../auto-host.h\"" + puts $f "#if HAVE_LD_IX86_GOT32X_RELOC == 0" + puts $f "# error Assembler does not support R_386_GOT32X." + puts $f "#endif" + close $f + + verbose "check_effective_target_got32x_reloc 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_got32x_reloc testfile compilation passed" 2 + set got32x_reloc_available_saved 1 + } else { + verbose "check_effective_target_got32x_reloc testfile compilation failed" 2 + set got32x_reloc_available_saved 0 + } + } + + return $got32x_reloc_available_saved +} + # Return 1 if the target uses comdat groups. proc check_effective_target_comdat_group {} { -- 2.5.5