ld: Add -z no-relax=SYMBOL

Message ID 20250127013330.599089-1-hjl.tools@gmail.com
State New
Headers
Series ld: Add -z no-relax=SYMBOL |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed

Commit Message

H.J. Lu Jan. 27, 2025, 1:33 a.m. UTC
  Add a "-z no-relax=SYMBOL" option to x86-64 linker to disable linker
optimization on GOTPCRELX relocations against SYMBOL.

bfd/

	PR ld/32591
	* elf-linker-x86.h (_bfd_elf_linker_x86_no_relax_symbol): New.
	* elf64-x86-64.c (elf_x86_64_convert_load_reloc): Avoid relocation
	overflow if a symbol is in no_relax_htab.
	(elf_x86_64_relocate_section): Don't warn GOTPCREL more than once
	for a symbol.  Replace --no-relax with -z no-relax=SYMBOL for
	GOTPCREL conversion overflow.
	* elfxx-x86.c: Include sysdep.h and libiberty.h.
	(_bfd_x86_elf_finish_dynamic_sections): Free no_relax_htab.
	(_bfd_elf_linker_x86_set_options): If --no-relax is used, free
	no_relax_htab.
	(_bfd_elf_linker_x86_no_relax_symbol): New.

include/

	PR ld/32591
	* bfdlink.h (bfd_link_info): Add no_relax_htab for the hash table
	for symbols without relaxation.

ld/

	PR ld/32591
	* NEWS: Mention "-z no-relax=SYMBOL".
	* ld.texi: Document "-z no-relax=SYMBOL".
	* emulparams/elf32_x86_64.sh: Source x86-64-no-relax.sh.
	* emulparams/elf_x86_64.sh: Likewise.
	* emulparams/x86-64-no-relax.sh: New file.
	* testsuite/ld-x86-64/pr19609-2a.d: Updated.
	* testsuite/ld-x86-64/pr19609-2b.d: Likewise.
	* testsuite/ld-x86-64/pr19609-4a.d: Likewise.
	* testsuite/ld-x86-64/pr19609-4c.d: Likewise.
	* testsuite/ld-x86-64/pr19609-5d.d: Likewise.
	* testsuite/ld-x86-64/pr19609-7a.d: Likewise.
	* testsuite/ld-x86-64/pr19609-7c.d: Likewise.
	* testsuite/ld-x86-64/pr32591-x32.d: New file.
	* testsuite/ld-x86-64/pr32591.d: Likewise.
	* testsuite/ld-x86-64/pr32591.s: Likewise.
	* testsuite/ld-x86-64/x86-64.exp: Run PR ld/32591 tests.

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
---
 bfd/elf-linker-x86.h                 |  2 ++
 bfd/elf64-x86-64.c                   | 27 +++++++++++++++++++++++++--
 bfd/elfxx-x86.c                      | 26 ++++++++++++++++++++++++++
 include/bfdlink.h                    |  3 +++
 ld/NEWS                              |  3 +++
 ld/emulparams/elf32_x86_64.sh        |  1 +
 ld/emulparams/elf_x86_64.sh          |  1 +
 ld/emulparams/x86-64-no-relax.sh     | 12 ++++++++++++
 ld/ld.texi                           |  5 +++++
 ld/testsuite/ld-x86-64/pr19609-2a.d  |  2 +-
 ld/testsuite/ld-x86-64/pr19609-2b.d  |  2 +-
 ld/testsuite/ld-x86-64/pr19609-4a.d  |  2 +-
 ld/testsuite/ld-x86-64/pr19609-4c.d  |  2 +-
 ld/testsuite/ld-x86-64/pr19609-5d.d  |  2 +-
 ld/testsuite/ld-x86-64/pr19609-7a.d  |  2 +-
 ld/testsuite/ld-x86-64/pr19609-7c.d  |  2 +-
 ld/testsuite/ld-x86-64/pr32591-x32.d | 13 +++++++++++++
 ld/testsuite/ld-x86-64/pr32591.d     | 13 +++++++++++++
 ld/testsuite/ld-x86-64/pr32591.s     | 13 +++++++++++++
 ld/testsuite/ld-x86-64/x86-64.exp    |  2 ++
 20 files changed, 126 insertions(+), 9 deletions(-)
 create mode 100644 ld/emulparams/x86-64-no-relax.sh
 create mode 100644 ld/testsuite/ld-x86-64/pr32591-x32.d
 create mode 100644 ld/testsuite/ld-x86-64/pr32591.d
 create mode 100644 ld/testsuite/ld-x86-64/pr32591.s
  

Comments

H.J. Lu Jan. 27, 2025, 4:05 a.m. UTC | #1
On Mon, Jan 27, 2025 at 9:33 AM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> Add a "-z no-relax=SYMBOL" option to x86-64 linker to disable linker
> optimization on GOTPCRELX relocations against SYMBOL.
>
> bfd/
>
>         PR ld/32591
>         * elf-linker-x86.h (_bfd_elf_linker_x86_no_relax_symbol): New.
>         * elf64-x86-64.c (elf_x86_64_convert_load_reloc): Avoid relocation
>         overflow if a symbol is in no_relax_htab.
>         (elf_x86_64_relocate_section): Don't warn GOTPCREL more than once
>         for a symbol.  Replace --no-relax with -z no-relax=SYMBOL for
>         GOTPCREL conversion overflow.
>         * elfxx-x86.c: Include sysdep.h and libiberty.h.
>         (_bfd_x86_elf_finish_dynamic_sections): Free no_relax_htab.
>         (_bfd_elf_linker_x86_set_options): If --no-relax is used, free
>         no_relax_htab.
>         (_bfd_elf_linker_x86_no_relax_symbol): New.
>
> include/
>
>         PR ld/32591
>         * bfdlink.h (bfd_link_info): Add no_relax_htab for the hash table
>         for symbols without relaxation.
>
> ld/
>
>         PR ld/32591
>         * NEWS: Mention "-z no-relax=SYMBOL".
>         * ld.texi: Document "-z no-relax=SYMBOL".
>         * emulparams/elf32_x86_64.sh: Source x86-64-no-relax.sh.
>         * emulparams/elf_x86_64.sh: Likewise.
>         * emulparams/x86-64-no-relax.sh: New file.
>         * testsuite/ld-x86-64/pr19609-2a.d: Updated.
>         * testsuite/ld-x86-64/pr19609-2b.d: Likewise.
>         * testsuite/ld-x86-64/pr19609-4a.d: Likewise.
>         * testsuite/ld-x86-64/pr19609-4c.d: Likewise.
>         * testsuite/ld-x86-64/pr19609-5d.d: Likewise.
>         * testsuite/ld-x86-64/pr19609-7a.d: Likewise.
>         * testsuite/ld-x86-64/pr19609-7c.d: Likewise.
>         * testsuite/ld-x86-64/pr32591-x32.d: New file.
>         * testsuite/ld-x86-64/pr32591.d: Likewise.
>         * testsuite/ld-x86-64/pr32591.s: Likewise.
>         * testsuite/ld-x86-64/x86-64.exp: Run PR ld/32591 tests.

I can make it generic if it is useful for other targets, like arm, longarch and
riscv.

> Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
> ---
>  bfd/elf-linker-x86.h                 |  2 ++
>  bfd/elf64-x86-64.c                   | 27 +++++++++++++++++++++++++--
>  bfd/elfxx-x86.c                      | 26 ++++++++++++++++++++++++++
>  include/bfdlink.h                    |  3 +++
>  ld/NEWS                              |  3 +++
>  ld/emulparams/elf32_x86_64.sh        |  1 +
>  ld/emulparams/elf_x86_64.sh          |  1 +
>  ld/emulparams/x86-64-no-relax.sh     | 12 ++++++++++++
>  ld/ld.texi                           |  5 +++++
>  ld/testsuite/ld-x86-64/pr19609-2a.d  |  2 +-
>  ld/testsuite/ld-x86-64/pr19609-2b.d  |  2 +-
>  ld/testsuite/ld-x86-64/pr19609-4a.d  |  2 +-
>  ld/testsuite/ld-x86-64/pr19609-4c.d  |  2 +-
>  ld/testsuite/ld-x86-64/pr19609-5d.d  |  2 +-
>  ld/testsuite/ld-x86-64/pr19609-7a.d  |  2 +-
>  ld/testsuite/ld-x86-64/pr19609-7c.d  |  2 +-
>  ld/testsuite/ld-x86-64/pr32591-x32.d | 13 +++++++++++++
>  ld/testsuite/ld-x86-64/pr32591.d     | 13 +++++++++++++
>  ld/testsuite/ld-x86-64/pr32591.s     | 13 +++++++++++++
>  ld/testsuite/ld-x86-64/x86-64.exp    |  2 ++
>  20 files changed, 126 insertions(+), 9 deletions(-)
>  create mode 100644 ld/emulparams/x86-64-no-relax.sh
>  create mode 100644 ld/testsuite/ld-x86-64/pr32591-x32.d
>  create mode 100644 ld/testsuite/ld-x86-64/pr32591.d
>  create mode 100644 ld/testsuite/ld-x86-64/pr32591.s
>
> diff --git a/bfd/elf-linker-x86.h b/bfd/elf-linker-x86.h
> index 2c98257038f..eef5cbb4352 100644
> --- a/bfd/elf-linker-x86.h
> +++ b/bfd/elf-linker-x86.h
> @@ -93,3 +93,5 @@ struct elf_linker_x86_params
>
>  extern void _bfd_elf_linker_x86_set_options
>    (struct bfd_link_info *, struct elf_linker_x86_params *);
> +extern void _bfd_elf_linker_x86_no_relax_symbol
> +  (struct bfd_link_info *, const char *);
> diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
> index d1d02bd7b32..78dfd7d4385 100644
> --- a/bfd/elf64-x86-64.c
> +++ b/bfd/elf64-x86-64.c
> @@ -1898,6 +1898,16 @@ elf_x86_64_convert_load_reloc (bfd *abfd,
>        if (isym->st_shndx == SHN_UNDEF)
>         return true;
>
> +      if (link_info->no_relax_htab != NULL)
> +       {
> +         const char *name = bfd_elf_sym_name (abfd,
> +                                              &elf_symtab_hdr (abfd),
> +                                              isym, NULL);
> +         if (htab_find (link_info->no_relax_htab, name)
> +             != HTAB_EMPTY_ENTRY)
> +           no_overflow = true;
> +       }
> +
>        local_ref = true;
>        if (isym->st_shndx == SHN_ABS)
>         {
> @@ -1922,6 +1932,11 @@ elf_x86_64_convert_load_reloc (bfd *abfd,
>          R_X86_64_PC32.  */
>        struct elf_x86_link_hash_entry *eh = elf_x86_hash_entry (h);
>
> +      if (link_info->no_relax_htab != NULL
> +         && (htab_find (link_info->no_relax_htab, h->root.root.string)
> +             != HTAB_EMPTY_ENTRY))
> +       no_overflow = true;
> +
>        abs_symbol = ABS_SYMBOL_P (h);
>        abs_relocation = h->root.u.def.value;
>
> @@ -4879,12 +4894,20 @@ elf_x86_64_relocate_section (bfd *output_bfd,
>             {
>               if (converted_reloc)
>                 {
> +                 if (info->no_relax_htab != NULL
> +                     && (htab_find (info->no_relax_htab, name)
> +                         != HTAB_EMPTY_ENTRY))
> +                   continue;
> +
> +                 /* Don't warn GOTPCREL more than once for a symbol.  */
> +                 _bfd_elf_linker_x86_no_relax_symbol (info, name);
> +
>                   info->callbacks->einfo
>                     ("%X%H:", input_bfd, input_section, rel->r_offset);
>                   info->callbacks->einfo
>                     (_(" failed to convert GOTPCREL relocation against "
> -                      "'%s'; relink with --no-relax\n"),
> -                    name);
> +                      "'%s'; relink with -z no-relax=%s\n"),
> +                    name, name);
>                   status = false;
>                   continue;
>                 }
> diff --git a/bfd/elfxx-x86.c b/bfd/elfxx-x86.c
> index 8e5a005fd36..a6d6894d887 100644
> --- a/bfd/elfxx-x86.c
> +++ b/bfd/elfxx-x86.c
> @@ -18,6 +18,8 @@
>     Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
>     MA 02110-1301, USA.  */
>
> +#include "sysdep.h"
> +#include "libiberty.h"
>  #include "elfxx-x86.h"
>  #include "elf-vxworks.h"
>  #include "objalloc.h"
> @@ -2756,6 +2758,12 @@ _bfd_x86_elf_finish_dynamic_sections (bfd *output_bfd,
>    bfd_byte *dyncon, *dynconend;
>    bfd_size_type sizeof_dyn;
>
> +  if (info->no_relax_htab != NULL)
> +    {
> +      htab_delete (info->no_relax_htab);
> +      info->no_relax_htab = NULL;
> +    }
> +
>    bed = get_elf_backend_data (output_bfd);
>    htab = elf_x86_hash_table (info, bed->target_id);
>    if (htab == NULL)
> @@ -4950,4 +4958,22 @@ _bfd_elf_linker_x86_set_options (struct bfd_link_info * info,
>      = elf_x86_hash_table (info, bed->target_id);
>    if (htab != NULL)
>      htab->params = params;
> +  /* If --no-relax is used, free no_relax_htab.  */
> +  if (info->disable_target_specific_optimizations > 1
> +      && info->no_relax_htab != NULL)
> +    {
> +      htab_delete (info->no_relax_htab);
> +      info->no_relax_htab = NULL;
> +    }
> +}
> +
> +void
> +_bfd_elf_linker_x86_no_relax_symbol (struct bfd_link_info *info,
> +                                    const char *name)
> +{
> +  if (info->no_relax_htab == NULL)
> +    info->no_relax_htab = htab_create_alloc (16, htab_hash_string,
> +                                            htab_eq_string, NULL,
> +                                            xcalloc, free);
> +  *htab_find_slot (info->no_relax_htab, name, INSERT) = (char *) name;
>  }
> diff --git a/include/bfdlink.h b/include/bfdlink.h
> index ae451075996..da60a271f69 100644
> --- a/include/bfdlink.h
> +++ b/include/bfdlink.h
> @@ -759,6 +759,9 @@ struct bfd_link_info
>    /* The version information.  */
>    struct bfd_elf_version_tree *version_info;
>
> +  /* The hash table for symbols without relaxation.  */
> +  void *no_relax_htab;
> +
>    /* Size of cache.  Backend can use it to keep strace cache size.   */
>    bfd_size_type cache_size;
>
> diff --git a/ld/NEWS b/ld/NEWS
> index 17fb20a6b9f..ca242e9b62d 100644
> --- a/ld/NEWS
> +++ b/ld/NEWS
> @@ -1,5 +1,8 @@
>  -*- text -*-
>
> +* Add a "-z no-relax=SYMBOL" option to x86-64 linker to disable linker
> +  optimization on GOTPCRELX relocations against SYMBOL.
> +
>  Changes in 2.44:
>
>  * Support for Nios II target has been removed, as this architecture has been
> diff --git a/ld/emulparams/elf32_x86_64.sh b/ld/emulparams/elf32_x86_64.sh
> index 6a92eec129d..3d9242d0d27 100644
> --- a/ld/emulparams/elf32_x86_64.sh
> +++ b/ld/emulparams/elf32_x86_64.sh
> @@ -8,6 +8,7 @@ source_sh ${srcdir}/emulparams/x86-report-relative.sh
>  source_sh ${srcdir}/emulparams/x86-64-level.sh
>  source_sh ${srcdir}/emulparams/x86-64-level-report.sh
>  source_sh ${srcdir}/emulparams/x86-64-plt.sh
> +source_sh ${srcdir}/emulparams/x86-64-no-relax.sh
>  source_sh ${srcdir}/emulparams/static.sh
>  source_sh ${srcdir}/emulparams/dt-relr.sh
>  SCRIPT_NAME=elf
> diff --git a/ld/emulparams/elf_x86_64.sh b/ld/emulparams/elf_x86_64.sh
> index 92449745c7a..dc0cbdd9da1 100644
> --- a/ld/emulparams/elf_x86_64.sh
> +++ b/ld/emulparams/elf_x86_64.sh
> @@ -9,6 +9,7 @@ source_sh ${srcdir}/emulparams/x86-64-level.sh
>  source_sh ${srcdir}/emulparams/x86-64-level-report.sh
>  source_sh ${srcdir}/emulparams/x86-64-lam.sh
>  source_sh ${srcdir}/emulparams/x86-64-plt.sh
> +source_sh ${srcdir}/emulparams/x86-64-no-relax.sh
>  source_sh ${srcdir}/emulparams/static.sh
>  source_sh ${srcdir}/emulparams/dt-relr.sh
>  SCRIPT_NAME=elf
> diff --git a/ld/emulparams/x86-64-no-relax.sh b/ld/emulparams/x86-64-no-relax.sh
> new file mode 100644
> index 00000000000..411a3c21833
> --- /dev/null
> +++ b/ld/emulparams/x86-64-no-relax.sh
> @@ -0,0 +1,12 @@
> +PARSE_AND_LIST_OPTIONS_X86_64_NO_RELAX='
> +  fprintf (file, _("\
> +  -z no-relax=SYMBOL          Do not use relaxation on SYMBOL\n"));
> +'
> +PARSE_AND_LIST_ARGS_CASE_Z_X86_64_NO_RELAX='
> +      else if (startswith (optarg, "no-relax="))
> +       _bfd_elf_linker_x86_no_relax_symbol (&link_info,
> +                                            optarg + 9);
> +'
> +
> +PARSE_AND_LIST_OPTIONS="$PARSE_AND_LIST_OPTIONS $PARSE_AND_LIST_OPTIONS_X86_64_NO_RELAX"
> +PARSE_AND_LIST_ARGS_CASE_Z="$PARSE_AND_LIST_ARGS_CASE_Z $PARSE_AND_LIST_ARGS_CASE_Z_X86_64_NO_RELAX"
> diff --git a/ld/ld.texi b/ld/ld.texi
> index f6384ad82dd..7e6648f93a2 100644
> --- a/ld/ld.texi
> +++ b/ld/ld.texi
> @@ -1569,6 +1569,11 @@ Supported for x86_64.
>  @item muldefs
>  Allow multiple definitions.
>
> +@item no-relax=@var{symbol}
> +Disable linker optimization on GOTPCRELX relocations against @var{symbol}.
> +This option may be used multiple times on the command line to disable
> +linker optimization against multiple symbols.  Supported for x86_64.
> +
>  @item nocopyreloc
>  Disable linker generated .dynbss variables used in place of variables
>  defined in shared libraries.  May result in dynamic text relocations.
> diff --git a/ld/testsuite/ld-x86-64/pr19609-2a.d b/ld/testsuite/ld-x86-64/pr19609-2a.d
> index 6d3db92afb7..de559775f15 100644
> --- a/ld/testsuite/ld-x86-64/pr19609-2a.d
> +++ b/ld/testsuite/ld-x86-64/pr19609-2a.d
> @@ -1,4 +1,4 @@
>  #source: pr19609-2.s
>  #as: --64 -mrelax-relocations=yes
>  #ld: -melf_x86_64 -Ttext=0x70000000 -Tdata=0xa0000000
> -#error: .*failed to convert GOTPCREL relocation against 'foo'; relink with --no-relax.*
> +#error: .*failed to convert GOTPCREL relocation against 'foo'; relink with -z no-relax=foo
> diff --git a/ld/testsuite/ld-x86-64/pr19609-2b.d b/ld/testsuite/ld-x86-64/pr19609-2b.d
> index 4fee93d7034..42870295d04 100644
> --- a/ld/testsuite/ld-x86-64/pr19609-2b.d
> +++ b/ld/testsuite/ld-x86-64/pr19609-2b.d
> @@ -1,4 +1,4 @@
>  #source: pr19609-2.s
>  #as: --x32 -mrelax-relocations=yes
>  #ld: -melf32_x86_64 -Ttext=0x70000000 -Tdata=0xa0000000
> -#error: .*failed to convert GOTPCREL relocation against 'foo'; relink with --no-relax.*
> +#error: .*failed to convert GOTPCREL relocation against 'foo'; relink with -z no-relax=foo
> diff --git a/ld/testsuite/ld-x86-64/pr19609-4a.d b/ld/testsuite/ld-x86-64/pr19609-4a.d
> index eb37d0c8729..931b2b7d109 100644
> --- a/ld/testsuite/ld-x86-64/pr19609-4a.d
> +++ b/ld/testsuite/ld-x86-64/pr19609-4a.d
> @@ -1,4 +1,4 @@
>  #source: pr19609-4.s
>  #as: --64 -mrelax-relocations=yes
>  #ld: -melf_x86_64 -Ttext=0x70000000 -Tdata=0xa0000000
> -#error: .*failed to convert GOTPCREL relocation against 'foo'; relink with --no-relax.*failed to convert GOTPCREL relocation against 'foo'; relink with --no-relax.*
> +#error: .*failed to convert GOTPCREL relocation against 'foo'; relink with -z no-relax=foo
> diff --git a/ld/testsuite/ld-x86-64/pr19609-4c.d b/ld/testsuite/ld-x86-64/pr19609-4c.d
> index eb37d0c8729..931b2b7d109 100644
> --- a/ld/testsuite/ld-x86-64/pr19609-4c.d
> +++ b/ld/testsuite/ld-x86-64/pr19609-4c.d
> @@ -1,4 +1,4 @@
>  #source: pr19609-4.s
>  #as: --64 -mrelax-relocations=yes
>  #ld: -melf_x86_64 -Ttext=0x70000000 -Tdata=0xa0000000
> -#error: .*failed to convert GOTPCREL relocation against 'foo'; relink with --no-relax.*failed to convert GOTPCREL relocation against 'foo'; relink with --no-relax.*
> +#error: .*failed to convert GOTPCREL relocation against 'foo'; relink with -z no-relax=foo
> diff --git a/ld/testsuite/ld-x86-64/pr19609-5d.d b/ld/testsuite/ld-x86-64/pr19609-5d.d
> index 0ab28efff1e..9fe54b130a3 100644
> --- a/ld/testsuite/ld-x86-64/pr19609-5d.d
> +++ b/ld/testsuite/ld-x86-64/pr19609-5d.d
> @@ -1,4 +1,4 @@
>  #source: pr19609-5.s
>  #as: --64 -mrelax-relocations=yes
>  #ld: -melf_x86_64 -Ttext=0x80000000
> -#error: .*failed to convert GOTPCREL relocation against 'bar'; relink with --no-relax
> +#error: .*failed to convert GOTPCREL relocation against 'bar'; relink with -z no-relax=bar
> diff --git a/ld/testsuite/ld-x86-64/pr19609-7a.d b/ld/testsuite/ld-x86-64/pr19609-7a.d
> index 34704a94c16..c1fe8b6f804 100644
> --- a/ld/testsuite/ld-x86-64/pr19609-7a.d
> +++ b/ld/testsuite/ld-x86-64/pr19609-7a.d
> @@ -1,4 +1,4 @@
>  #source: pr19609-7.s
>  #as: --64 -mrelax-relocations=yes
>  #ld: -melf_x86_64 -Ttext=0x80000000
> -#error: .*failed to convert GOTPCREL relocation against 'foobar'; relink with --no-relax
> +#error: .*failed to convert GOTPCREL relocation against 'foobar'; relink with -z no-relax=foobar
> diff --git a/ld/testsuite/ld-x86-64/pr19609-7c.d b/ld/testsuite/ld-x86-64/pr19609-7c.d
> index 09488e22a8e..add0d5cbabe 100644
> --- a/ld/testsuite/ld-x86-64/pr19609-7c.d
> +++ b/ld/testsuite/ld-x86-64/pr19609-7c.d
> @@ -1,4 +1,4 @@
>  #source: pr19609-7.s
>  #as: --x32 -mrelax-relocations=yes
>  #ld: -melf32_x86_64 -Ttext=0x80000000
> -#error: .*failed to convert GOTPCREL relocation against 'foobar'; relink with --no-relax
> +#error: .*failed to convert GOTPCREL relocation against 'foobar'; relink with -z no-relax=foobar
> diff --git a/ld/testsuite/ld-x86-64/pr32591-x32.d b/ld/testsuite/ld-x86-64/pr32591-x32.d
> new file mode 100644
> index 00000000000..ae438855935
> --- /dev/null
> +++ b/ld/testsuite/ld-x86-64/pr32591-x32.d
> @@ -0,0 +1,13 @@
> +#source: pr32591.s
> +#as: --x32 -mrelax-relocations=yes
> +#ld: -melf32_x86_64 -Ttext=0x70000000 -Tdata=0xa0000000 -z no-relax=foo -z no-relax=bar
> +#objdump: -dw
> +
> +.*: +file format .*
> +
> +
> +Disassembly of section .text:
> +
> +70000000 <_start>:
> +[      ]*[a-f0-9]+:    48 3b 05 ([0-9a-f]{2} ){4}      cmp    -?0x[a-f0-9]+\(%rip\),%rax        # .*
> +[      ]*[a-f0-9]+:    48 3b 05 ([0-9a-f]{2} ){4}      cmp    -?0x[a-f0-9]+\(%rip\),%rax        # .*
> diff --git a/ld/testsuite/ld-x86-64/pr32591.d b/ld/testsuite/ld-x86-64/pr32591.d
> new file mode 100644
> index 00000000000..eed53ab6565
> --- /dev/null
> +++ b/ld/testsuite/ld-x86-64/pr32591.d
> @@ -0,0 +1,13 @@
> +#as: --64 -mrelax-relocations=yes
> +#ld: -melf_x86_64 -Ttext=0x70000000 -Tdata=0xa0000000 -z no-relax=foo -z no-relax=bar
> +#objdump: -dw
> +
> +.*: +file format .*
> +
> +
> +Disassembly of section .text:
> +
> +0+70000000 <_start>:
> +[      ]*[a-f0-9]+:    48 3b 05 ([0-9a-f]{2} ){4}      cmp    -?0x[a-f0-9]+\(%rip\),%rax        # .*
> +[      ]*[a-f0-9]+:    48 3b 05 ([0-9a-f]{2} ){4}      cmp    -?0x[a-f0-9]+\(%rip\),%rax        # .*
> +#pass
> diff --git a/ld/testsuite/ld-x86-64/pr32591.s b/ld/testsuite/ld-x86-64/pr32591.s
> new file mode 100644
> index 00000000000..35ff57bcbf3
> --- /dev/null
> +++ b/ld/testsuite/ld-x86-64/pr32591.s
> @@ -0,0 +1,13 @@
> +       .data
> +foo:
> +       .quad   0
> +       .globl bar
> +bar:
> +       .quad   0
> +       .text
> +       .globl  _start
> +       .type   _start, @function
> +_start:
> +       cmpq    foo@GOTPCREL(%rip), %rax
> +       cmpq    bar@GOTPCREL(%rip), %rax
> +       .size   _start, .-_start
> diff --git a/ld/testsuite/ld-x86-64/x86-64.exp b/ld/testsuite/ld-x86-64/x86-64.exp
> index 48a468814c1..46f542f329f 100644
> --- a/ld/testsuite/ld-x86-64/x86-64.exp
> +++ b/ld/testsuite/ld-x86-64/x86-64.exp
> @@ -544,6 +544,8 @@ run_dump_test "tlsdesc4"
>  run_dump_test "tlsdesc5"
>  run_dump_test "pr32191"
>  run_dump_test "pr32191-x32"
> +run_dump_test "pr32591"
> +run_dump_test "pr32591-x32"
>
>  if { ![skip_sframe_tests] } {
>      run_dump_test "sframe-simple-1"
> --
> 2.48.1
>
  
Fangrui Song Jan. 27, 2025, 6:14 a.m. UTC | #2
On Sun, Jan 26, 2025 at 8:06 PM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> On Mon, Jan 27, 2025 at 9:33 AM H.J. Lu <hjl.tools@gmail.com> wrote:
> >
> > Add a "-z no-relax=SYMBOL" option to x86-64 linker to disable linker
> > optimization on GOTPCRELX relocations against SYMBOL.
> >
> > bfd/
> >
> >         PR ld/32591
> >         * elf-linker-x86.h (_bfd_elf_linker_x86_no_relax_symbol): New.
> >         * elf64-x86-64.c (elf_x86_64_convert_load_reloc): Avoid relocation
> >         overflow if a symbol is in no_relax_htab.
> >         (elf_x86_64_relocate_section): Don't warn GOTPCREL more than once
> >         for a symbol.  Replace --no-relax with -z no-relax=SYMBOL for
> >         GOTPCREL conversion overflow.
> >         * elfxx-x86.c: Include sysdep.h and libiberty.h.
> >         (_bfd_x86_elf_finish_dynamic_sections): Free no_relax_htab.
> >         (_bfd_elf_linker_x86_set_options): If --no-relax is used, free
> >         no_relax_htab.
> >         (_bfd_elf_linker_x86_no_relax_symbol): New.
> >
> > include/
> >
> >         PR ld/32591
> >         * bfdlink.h (bfd_link_info): Add no_relax_htab for the hash table
> >         for symbols without relaxation.
> >
> > ld/
> >
> >         PR ld/32591
> >         * NEWS: Mention "-z no-relax=SYMBOL".
> >         * ld.texi: Document "-z no-relax=SYMBOL".
> >         * emulparams/elf32_x86_64.sh: Source x86-64-no-relax.sh.
> >         * emulparams/elf_x86_64.sh: Likewise.
> >         * emulparams/x86-64-no-relax.sh: New file.
> >         * testsuite/ld-x86-64/pr19609-2a.d: Updated.
> >         * testsuite/ld-x86-64/pr19609-2b.d: Likewise.
> >         * testsuite/ld-x86-64/pr19609-4a.d: Likewise.
> >         * testsuite/ld-x86-64/pr19609-4c.d: Likewise.
> >         * testsuite/ld-x86-64/pr19609-5d.d: Likewise.
> >         * testsuite/ld-x86-64/pr19609-7a.d: Likewise.
> >         * testsuite/ld-x86-64/pr19609-7c.d: Likewise.
> >         * testsuite/ld-x86-64/pr32591-x32.d: New file.
> >         * testsuite/ld-x86-64/pr32591.d: Likewise.
> >         * testsuite/ld-x86-64/pr32591.s: Likewise.
> >         * testsuite/ld-x86-64/x86-64.exp: Run PR ld/32591 tests.
>
> I can make it generic if it is useful for other targets, like arm, longarch and
> riscv.
>

Interesting. I added a comment
https://sourceware.org/bugzilla/show_bug.cgi?id=32591#c4

I believe we should implement a more general and robust mechanism for
reverting GOT optimization decisions, rather than relying on the
ad-hoc -z no-relax= linker option.
With my experience on large x86-64 executables, -z no-relax= could be
cumbersome as there can be many symbols unreachable to their GOT
entries.

R_390_GOTENT for s390x is similar. The lld port handles it as well.

I think in RISC-V and LoongArch, --no-relax indicates linker
relaxation, which has sophisticated address dependent computation
doesn't need the -z no-relax= workaround.
(It seems that the term "linker optimization" is often used when the
number of bytes does not change (AArch64, PPC, x86-64, etc) while
"linker relaxation" is used when the number of bytes decreases
(RISC-V, LoongArch).)
  
Jan Beulich Jan. 27, 2025, 8:57 a.m. UTC | #3
On 27.01.2025 02:33, H.J. Lu wrote:
> Add a "-z no-relax=SYMBOL" option to x86-64 linker to disable linker
> optimization on GOTPCRELX relocations against SYMBOL.

If I'm not mistaken, this addresses only one of the two aspects mentioned
in the bug report: While it indeed makes -z no-relax more fine grained, it
at the same time still is all-or-nothing for a particular symbol. When, as
described there, the conversion to PC32 may be okay to make (as that's
merely dependent on executable size, not on its load address). Obviously
the situation can in principle also be the other way around: Conversion to
PC32 may not be possible, but conversion to 32/32S may be.

I continue to think along the lines of what Fangrui also said: We rather
need to be able to roll back (unless we can arrange for deferring the
decision until a point where we know whether the converted-to relocation
would overflow).

Jan
  

Patch

diff --git a/bfd/elf-linker-x86.h b/bfd/elf-linker-x86.h
index 2c98257038f..eef5cbb4352 100644
--- a/bfd/elf-linker-x86.h
+++ b/bfd/elf-linker-x86.h
@@ -93,3 +93,5 @@  struct elf_linker_x86_params
 
 extern void _bfd_elf_linker_x86_set_options
   (struct bfd_link_info *, struct elf_linker_x86_params *);
+extern void _bfd_elf_linker_x86_no_relax_symbol
+  (struct bfd_link_info *, const char *);
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index d1d02bd7b32..78dfd7d4385 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -1898,6 +1898,16 @@  elf_x86_64_convert_load_reloc (bfd *abfd,
       if (isym->st_shndx == SHN_UNDEF)
 	return true;
 
+      if (link_info->no_relax_htab != NULL)
+	{
+	  const char *name = bfd_elf_sym_name (abfd,
+					       &elf_symtab_hdr (abfd),
+					       isym, NULL);
+	  if (htab_find (link_info->no_relax_htab, name)
+	      != HTAB_EMPTY_ENTRY)
+	    no_overflow = true;
+	}
+
       local_ref = true;
       if (isym->st_shndx == SHN_ABS)
 	{
@@ -1922,6 +1932,11 @@  elf_x86_64_convert_load_reloc (bfd *abfd,
 	 R_X86_64_PC32.  */
       struct elf_x86_link_hash_entry *eh = elf_x86_hash_entry (h);
 
+      if (link_info->no_relax_htab != NULL
+	  && (htab_find (link_info->no_relax_htab, h->root.root.string)
+	      != HTAB_EMPTY_ENTRY))
+	no_overflow = true;
+
       abs_symbol = ABS_SYMBOL_P (h);
       abs_relocation = h->root.u.def.value;
 
@@ -4879,12 +4894,20 @@  elf_x86_64_relocate_section (bfd *output_bfd,
 	    {
 	      if (converted_reloc)
 		{
+		  if (info->no_relax_htab != NULL
+		      && (htab_find (info->no_relax_htab, name)
+			  != HTAB_EMPTY_ENTRY))
+		    continue;
+
+		  /* Don't warn GOTPCREL more than once for a symbol.  */
+		  _bfd_elf_linker_x86_no_relax_symbol (info, name);
+
 		  info->callbacks->einfo
 		    ("%X%H:", input_bfd, input_section, rel->r_offset);
 		  info->callbacks->einfo
 		    (_(" failed to convert GOTPCREL relocation against "
-		       "'%s'; relink with --no-relax\n"),
-		     name);
+		       "'%s'; relink with -z no-relax=%s\n"),
+		     name, name);
 		  status = false;
 		  continue;
 		}
diff --git a/bfd/elfxx-x86.c b/bfd/elfxx-x86.c
index 8e5a005fd36..a6d6894d887 100644
--- a/bfd/elfxx-x86.c
+++ b/bfd/elfxx-x86.c
@@ -18,6 +18,8 @@ 
    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
    MA 02110-1301, USA.  */
 
+#include "sysdep.h"
+#include "libiberty.h"
 #include "elfxx-x86.h"
 #include "elf-vxworks.h"
 #include "objalloc.h"
@@ -2756,6 +2758,12 @@  _bfd_x86_elf_finish_dynamic_sections (bfd *output_bfd,
   bfd_byte *dyncon, *dynconend;
   bfd_size_type sizeof_dyn;
 
+  if (info->no_relax_htab != NULL)
+    {
+      htab_delete (info->no_relax_htab);
+      info->no_relax_htab = NULL;
+    }
+
   bed = get_elf_backend_data (output_bfd);
   htab = elf_x86_hash_table (info, bed->target_id);
   if (htab == NULL)
@@ -4950,4 +4958,22 @@  _bfd_elf_linker_x86_set_options (struct bfd_link_info * info,
     = elf_x86_hash_table (info, bed->target_id);
   if (htab != NULL)
     htab->params = params;
+  /* If --no-relax is used, free no_relax_htab.  */
+  if (info->disable_target_specific_optimizations > 1
+      && info->no_relax_htab != NULL)
+    {
+      htab_delete (info->no_relax_htab);
+      info->no_relax_htab = NULL;
+    }
+}
+
+void
+_bfd_elf_linker_x86_no_relax_symbol (struct bfd_link_info *info,
+				     const char *name)
+{
+  if (info->no_relax_htab == NULL)
+    info->no_relax_htab = htab_create_alloc (16, htab_hash_string,
+					     htab_eq_string, NULL,
+					     xcalloc, free);
+  *htab_find_slot (info->no_relax_htab, name, INSERT) = (char *) name;
 }
diff --git a/include/bfdlink.h b/include/bfdlink.h
index ae451075996..da60a271f69 100644
--- a/include/bfdlink.h
+++ b/include/bfdlink.h
@@ -759,6 +759,9 @@  struct bfd_link_info
   /* The version information.  */
   struct bfd_elf_version_tree *version_info;
 
+  /* The hash table for symbols without relaxation.  */
+  void *no_relax_htab;
+
   /* Size of cache.  Backend can use it to keep strace cache size.   */
   bfd_size_type cache_size;
 
diff --git a/ld/NEWS b/ld/NEWS
index 17fb20a6b9f..ca242e9b62d 100644
--- a/ld/NEWS
+++ b/ld/NEWS
@@ -1,5 +1,8 @@ 
 -*- text -*-
 
+* Add a "-z no-relax=SYMBOL" option to x86-64 linker to disable linker
+  optimization on GOTPCRELX relocations against SYMBOL.
+
 Changes in 2.44:
 
 * Support for Nios II target has been removed, as this architecture has been
diff --git a/ld/emulparams/elf32_x86_64.sh b/ld/emulparams/elf32_x86_64.sh
index 6a92eec129d..3d9242d0d27 100644
--- a/ld/emulparams/elf32_x86_64.sh
+++ b/ld/emulparams/elf32_x86_64.sh
@@ -8,6 +8,7 @@  source_sh ${srcdir}/emulparams/x86-report-relative.sh
 source_sh ${srcdir}/emulparams/x86-64-level.sh
 source_sh ${srcdir}/emulparams/x86-64-level-report.sh
 source_sh ${srcdir}/emulparams/x86-64-plt.sh
+source_sh ${srcdir}/emulparams/x86-64-no-relax.sh
 source_sh ${srcdir}/emulparams/static.sh
 source_sh ${srcdir}/emulparams/dt-relr.sh
 SCRIPT_NAME=elf
diff --git a/ld/emulparams/elf_x86_64.sh b/ld/emulparams/elf_x86_64.sh
index 92449745c7a..dc0cbdd9da1 100644
--- a/ld/emulparams/elf_x86_64.sh
+++ b/ld/emulparams/elf_x86_64.sh
@@ -9,6 +9,7 @@  source_sh ${srcdir}/emulparams/x86-64-level.sh
 source_sh ${srcdir}/emulparams/x86-64-level-report.sh
 source_sh ${srcdir}/emulparams/x86-64-lam.sh
 source_sh ${srcdir}/emulparams/x86-64-plt.sh
+source_sh ${srcdir}/emulparams/x86-64-no-relax.sh
 source_sh ${srcdir}/emulparams/static.sh
 source_sh ${srcdir}/emulparams/dt-relr.sh
 SCRIPT_NAME=elf
diff --git a/ld/emulparams/x86-64-no-relax.sh b/ld/emulparams/x86-64-no-relax.sh
new file mode 100644
index 00000000000..411a3c21833
--- /dev/null
+++ b/ld/emulparams/x86-64-no-relax.sh
@@ -0,0 +1,12 @@ 
+PARSE_AND_LIST_OPTIONS_X86_64_NO_RELAX='
+  fprintf (file, _("\
+  -z no-relax=SYMBOL          Do not use relaxation on SYMBOL\n"));
+'
+PARSE_AND_LIST_ARGS_CASE_Z_X86_64_NO_RELAX='
+      else if (startswith (optarg, "no-relax="))
+	_bfd_elf_linker_x86_no_relax_symbol (&link_info,
+					     optarg + 9);
+'
+
+PARSE_AND_LIST_OPTIONS="$PARSE_AND_LIST_OPTIONS $PARSE_AND_LIST_OPTIONS_X86_64_NO_RELAX"
+PARSE_AND_LIST_ARGS_CASE_Z="$PARSE_AND_LIST_ARGS_CASE_Z $PARSE_AND_LIST_ARGS_CASE_Z_X86_64_NO_RELAX"
diff --git a/ld/ld.texi b/ld/ld.texi
index f6384ad82dd..7e6648f93a2 100644
--- a/ld/ld.texi
+++ b/ld/ld.texi
@@ -1569,6 +1569,11 @@  Supported for x86_64.
 @item muldefs
 Allow multiple definitions.
 
+@item no-relax=@var{symbol}
+Disable linker optimization on GOTPCRELX relocations against @var{symbol}.
+This option may be used multiple times on the command line to disable
+linker optimization against multiple symbols.  Supported for x86_64.
+
 @item nocopyreloc
 Disable linker generated .dynbss variables used in place of variables
 defined in shared libraries.  May result in dynamic text relocations.
diff --git a/ld/testsuite/ld-x86-64/pr19609-2a.d b/ld/testsuite/ld-x86-64/pr19609-2a.d
index 6d3db92afb7..de559775f15 100644
--- a/ld/testsuite/ld-x86-64/pr19609-2a.d
+++ b/ld/testsuite/ld-x86-64/pr19609-2a.d
@@ -1,4 +1,4 @@ 
 #source: pr19609-2.s
 #as: --64 -mrelax-relocations=yes
 #ld: -melf_x86_64 -Ttext=0x70000000 -Tdata=0xa0000000
-#error: .*failed to convert GOTPCREL relocation against 'foo'; relink with --no-relax.*
+#error: .*failed to convert GOTPCREL relocation against 'foo'; relink with -z no-relax=foo
diff --git a/ld/testsuite/ld-x86-64/pr19609-2b.d b/ld/testsuite/ld-x86-64/pr19609-2b.d
index 4fee93d7034..42870295d04 100644
--- a/ld/testsuite/ld-x86-64/pr19609-2b.d
+++ b/ld/testsuite/ld-x86-64/pr19609-2b.d
@@ -1,4 +1,4 @@ 
 #source: pr19609-2.s
 #as: --x32 -mrelax-relocations=yes
 #ld: -melf32_x86_64 -Ttext=0x70000000 -Tdata=0xa0000000
-#error: .*failed to convert GOTPCREL relocation against 'foo'; relink with --no-relax.*
+#error: .*failed to convert GOTPCREL relocation against 'foo'; relink with -z no-relax=foo
diff --git a/ld/testsuite/ld-x86-64/pr19609-4a.d b/ld/testsuite/ld-x86-64/pr19609-4a.d
index eb37d0c8729..931b2b7d109 100644
--- a/ld/testsuite/ld-x86-64/pr19609-4a.d
+++ b/ld/testsuite/ld-x86-64/pr19609-4a.d
@@ -1,4 +1,4 @@ 
 #source: pr19609-4.s
 #as: --64 -mrelax-relocations=yes
 #ld: -melf_x86_64 -Ttext=0x70000000 -Tdata=0xa0000000
-#error: .*failed to convert GOTPCREL relocation against 'foo'; relink with --no-relax.*failed to convert GOTPCREL relocation against 'foo'; relink with --no-relax.*
+#error: .*failed to convert GOTPCREL relocation against 'foo'; relink with -z no-relax=foo
diff --git a/ld/testsuite/ld-x86-64/pr19609-4c.d b/ld/testsuite/ld-x86-64/pr19609-4c.d
index eb37d0c8729..931b2b7d109 100644
--- a/ld/testsuite/ld-x86-64/pr19609-4c.d
+++ b/ld/testsuite/ld-x86-64/pr19609-4c.d
@@ -1,4 +1,4 @@ 
 #source: pr19609-4.s
 #as: --64 -mrelax-relocations=yes
 #ld: -melf_x86_64 -Ttext=0x70000000 -Tdata=0xa0000000
-#error: .*failed to convert GOTPCREL relocation against 'foo'; relink with --no-relax.*failed to convert GOTPCREL relocation against 'foo'; relink with --no-relax.*
+#error: .*failed to convert GOTPCREL relocation against 'foo'; relink with -z no-relax=foo
diff --git a/ld/testsuite/ld-x86-64/pr19609-5d.d b/ld/testsuite/ld-x86-64/pr19609-5d.d
index 0ab28efff1e..9fe54b130a3 100644
--- a/ld/testsuite/ld-x86-64/pr19609-5d.d
+++ b/ld/testsuite/ld-x86-64/pr19609-5d.d
@@ -1,4 +1,4 @@ 
 #source: pr19609-5.s
 #as: --64 -mrelax-relocations=yes
 #ld: -melf_x86_64 -Ttext=0x80000000
-#error: .*failed to convert GOTPCREL relocation against 'bar'; relink with --no-relax
+#error: .*failed to convert GOTPCREL relocation against 'bar'; relink with -z no-relax=bar
diff --git a/ld/testsuite/ld-x86-64/pr19609-7a.d b/ld/testsuite/ld-x86-64/pr19609-7a.d
index 34704a94c16..c1fe8b6f804 100644
--- a/ld/testsuite/ld-x86-64/pr19609-7a.d
+++ b/ld/testsuite/ld-x86-64/pr19609-7a.d
@@ -1,4 +1,4 @@ 
 #source: pr19609-7.s
 #as: --64 -mrelax-relocations=yes
 #ld: -melf_x86_64 -Ttext=0x80000000
-#error: .*failed to convert GOTPCREL relocation against 'foobar'; relink with --no-relax
+#error: .*failed to convert GOTPCREL relocation against 'foobar'; relink with -z no-relax=foobar
diff --git a/ld/testsuite/ld-x86-64/pr19609-7c.d b/ld/testsuite/ld-x86-64/pr19609-7c.d
index 09488e22a8e..add0d5cbabe 100644
--- a/ld/testsuite/ld-x86-64/pr19609-7c.d
+++ b/ld/testsuite/ld-x86-64/pr19609-7c.d
@@ -1,4 +1,4 @@ 
 #source: pr19609-7.s
 #as: --x32 -mrelax-relocations=yes
 #ld: -melf32_x86_64 -Ttext=0x80000000
-#error: .*failed to convert GOTPCREL relocation against 'foobar'; relink with --no-relax
+#error: .*failed to convert GOTPCREL relocation against 'foobar'; relink with -z no-relax=foobar
diff --git a/ld/testsuite/ld-x86-64/pr32591-x32.d b/ld/testsuite/ld-x86-64/pr32591-x32.d
new file mode 100644
index 00000000000..ae438855935
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr32591-x32.d
@@ -0,0 +1,13 @@ 
+#source: pr32591.s
+#as: --x32 -mrelax-relocations=yes
+#ld: -melf32_x86_64 -Ttext=0x70000000 -Tdata=0xa0000000 -z no-relax=foo -z no-relax=bar
+#objdump: -dw
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+70000000 <_start>:
+[ 	]*[a-f0-9]+:	48 3b 05 ([0-9a-f]{2} ){4}	cmp    -?0x[a-f0-9]+\(%rip\),%rax        # .*
+[ 	]*[a-f0-9]+:	48 3b 05 ([0-9a-f]{2} ){4}	cmp    -?0x[a-f0-9]+\(%rip\),%rax        # .*
diff --git a/ld/testsuite/ld-x86-64/pr32591.d b/ld/testsuite/ld-x86-64/pr32591.d
new file mode 100644
index 00000000000..eed53ab6565
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr32591.d
@@ -0,0 +1,13 @@ 
+#as: --64 -mrelax-relocations=yes
+#ld: -melf_x86_64 -Ttext=0x70000000 -Tdata=0xa0000000 -z no-relax=foo -z no-relax=bar
+#objdump: -dw
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+0+70000000 <_start>:
+[ 	]*[a-f0-9]+:	48 3b 05 ([0-9a-f]{2} ){4}	cmp    -?0x[a-f0-9]+\(%rip\),%rax        # .*
+[ 	]*[a-f0-9]+:	48 3b 05 ([0-9a-f]{2} ){4}	cmp    -?0x[a-f0-9]+\(%rip\),%rax        # .*
+#pass
diff --git a/ld/testsuite/ld-x86-64/pr32591.s b/ld/testsuite/ld-x86-64/pr32591.s
new file mode 100644
index 00000000000..35ff57bcbf3
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr32591.s
@@ -0,0 +1,13 @@ 
+	.data
+foo:
+	.quad	0
+	.globl bar
+bar:
+	.quad	0
+	.text
+	.globl  _start
+	.type	_start, @function
+_start:
+	cmpq	foo@GOTPCREL(%rip), %rax
+	cmpq	bar@GOTPCREL(%rip), %rax
+	.size	_start, .-_start
diff --git a/ld/testsuite/ld-x86-64/x86-64.exp b/ld/testsuite/ld-x86-64/x86-64.exp
index 48a468814c1..46f542f329f 100644
--- a/ld/testsuite/ld-x86-64/x86-64.exp
+++ b/ld/testsuite/ld-x86-64/x86-64.exp
@@ -544,6 +544,8 @@  run_dump_test "tlsdesc4"
 run_dump_test "tlsdesc5"
 run_dump_test "pr32191"
 run_dump_test "pr32191-x32"
+run_dump_test "pr32591"
+run_dump_test "pr32591-x32"
 
 if { ![skip_sframe_tests] } {
     run_dump_test "sframe-simple-1"