diff mbox

i386: Access external function via GOT slot for -fno-plt

Message ID 20160623114505.GA17653@intel.com
State New
Headers show

Commit Message

H.J. Lu June 23, 2016, 11:45 a.m. UTC
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

Comments

Uros Bizjak June 23, 2016, 12:33 p.m. UTC | #1
On Thu, Jun 23, 2016 at 1:45 PM, H.J. Lu <hongjiu.lu@intel.com> wrote:
> 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;

The above should read like:

      case UNSPEC_GOT:
        if (!TARGET_64BIT
        && ix86_force_load_from_GOT_p (XVECEXP (XEXP (disp, 0), 0, 0)))
          goto is_legitimate_pic;
        /* FALLTHRU */
      case UNSPEC_GOTOFF:
        gcc_assert (flag_pic);
        if (!TARGET_64BIT)
          goto is_legitimate_pic;

Please also update the comment above.

>           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_64BIT || HAVE_LD_IX86_GOT32X_RELOC)

> +      || 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.])

THe configure script already determined how to enforce 32bit output
with gas and gld, please rename and reuse as_ix86_tls_ldm_opt and
ld_ix86_tls_ldm_opt from the above check. Then define conftest_s and
call gcc_GAS_CHECK_FEATURE, as is the case with R_386_TLS_LDM reloc
check.

Also, please name this variable in a similar way, HAVE_AS_IX86_GOT32X.

Please repost v2 patch.

Uros.

>      ;;
>
>    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
>
diff mbox

Patch

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 {} {