ld: Estimate output section layout before sizing dynamic sections

Message ID 20250201085330.289730-1-hjl.tools@gmail.com
State New
Headers
Series ld: Estimate output section layout before sizing dynamic sections |

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 Feb. 1, 2025, 8:53 a.m. UTC
  When sizing dynamic sections, elf_x86_64_scan_relocs converts GOTPCREL
relocations to R_X86_64_PC32, R_X86_64_32S or R_X86_64_32 for local
symbols.  But at that time, since the output section layout is unknown,
the local symbol values can't be determined.  Later linker issues an
error if the converted relocation overflows when resolving relocations
against these local symbols.  Update the linker so that an ELF target
can opt-in to estimate output section layout before sizing dynamic
sections.  The linker created sections are excluded when estimating
output section layout as sizing dynamic sections may change linker
created sections.  Update the x86-64 ELF linker to use the preliminary
section layout info to skip the GOTPCREL relocation conversion if the
converted relocation overflows.

bfd/

	PR ld/32591
	* elf-bfd.h (elf_backend_data): Add perform_prelim_section_layout
	and exclude_linker_created_sections.
	* elf64-x86-64.c (elf_x86_64_convert_load_reloc): Add an input
	section argument.  Don't convert relocation if the converted
	relocation will overflow.
	(elf_x86_64_scan_relocs): Pass input section to
	elf_x86_64_convert_load_reloc.
	(elf_backend_perform_prelim_section_layout): New.
	(elf_backend_exclude_linker_created_sections): Likewise.
	* elfxx-target.h (elf_backend_perform_prelim_section_layout):
	New, default to false.
	(elf_backend_exclude_linker_created_sections): Likewise.
	(elfNN_bed): Add elf_backend_perform_prelim_section_layout and
	elf_backend_exclude_linker_created_sections.

ld/

	PR ld/32591
	* ldelf.c (ldelf_before_allocation): Run
	one_lang_size_sections_pass to estimate the output section output
	section layout before sizing dynamic sections if needed.
	* ldlang.c (strip_excluded_output_sections): Pass false to
	one_lang_size_sections_pass.
	(lang_size_sections_1): Add a bool argument to indicate if
	linker created sections should be excluded.  Ignore linker
	created sections if asked.
	(one_lang_size_sections_pass): Add a bool argument to indicate if
	linker created sections should be and excluded and pass it to
	lang_size_sections_1.
	(lang_size_relro_segment): Pass false to
	one_lang_size_sections_pass.
	(lang_size_sections): Likewise.
	* ldlang.h (one_lang_size_sections_pass): Add a bool argument.
	* emultempl/aix.em (gld${EMULATION_NAME}_before_allocation):
	Pass false to one_lang_size_sections_pass.
	* emultempl/ppc32elf.em (prelim_size_sections): Pass false to
	one_lang_size_sections_pass.
	* emultempl/ppc64elf.em (prelim_size_sections): Likewise.
	* emultempl/spuelf.em (spu_before_allocation): Likewise.
	* testsuite/ld-x86-64/pr19609-2a.d: Don't fail.
	* testsuite/ld-x86-64/pr19609-2b.d: Likewise.
	* testsuite/ld-x86-64/pr19609-4a.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-1.s: New file.
	* testsuite/ld-x86-64/pr32591-1a-x32.d: Likewise.
	* testsuite/ld-x86-64/pr32591-1a.d: Likewise.
	* testsuite/ld-x86-64/pr32591-1a.t: Likewise.
	* testsuite/ld-x86-64/pr32591-1b-x32.d: Likewise.
	* testsuite/ld-x86-64/pr32591-1b.d: Likewise.
	* testsuite/ld-x86-64/pr32591-1b.t: Likewise.
	* testsuite/ld-x86-64/pr32591-1c-x32.d: Likewise.
	* testsuite/ld-x86-64/pr32591-1c.d: Likewise.
	* testsuite/ld-x86-64/pr32591-1c.t: Likewise.
	* testsuite/ld-x86-64/pr32591-1d-x32.d: Likewise.
	* testsuite/ld-x86-64/pr32591-1d.d: Likewise.
	* testsuite/ld-x86-64/pr32591-1d.t: Likewise.
	* testsuite/ld-x86-64/pr32591-2.s: Likewise.
	* testsuite/ld-x86-64/pr32591-2-x32.d: Likewise.
	* testsuite/ld-x86-64/pr32591-2.d: Likewise.
	* testsuite/ld-x86-64/pr32591-2.t: 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-bfd.h                           |  8 +++
 bfd/elf64-x86-64.c                      | 77 +++++++++++++++++++++++--
 bfd/elfxx-target.h                      | 10 +++-
 ld/emultempl/aix.em                     |  2 +-
 ld/emultempl/ppc32elf.em                |  2 +-
 ld/emultempl/ppc64elf.em                |  2 +-
 ld/emultempl/spuelf.em                  |  2 +-
 ld/ldelf.c                              | 16 +++++
 ld/ldlang.c                             | 33 +++++++----
 ld/ldlang.h                             |  2 +-
 ld/testsuite/ld-x86-64/pr19609-2a.d     | 11 +++-
 ld/testsuite/ld-x86-64/pr19609-2b.d     | 11 +++-
 ld/testsuite/ld-x86-64/pr19609-4a.d     | 11 +++-
 ld/testsuite/ld-x86-64/pr19609-5d.d     | 10 +++-
 ld/testsuite/ld-x86-64/pr19609-7a.d     | 13 ++++-
 ld/testsuite/ld-x86-64/pr19609-7c.d     | 13 ++++-
 ld/testsuite/ld-x86-64/pr32591-1.s      | 24 ++++++++
 ld/testsuite/ld-x86-64/pr32591-1a-x32.d | 22 +++++++
 ld/testsuite/ld-x86-64/pr32591-1a.d     | 22 +++++++
 ld/testsuite/ld-x86-64/pr32591-1a.t     |  7 +++
 ld/testsuite/ld-x86-64/pr32591-1b-x32.d | 27 +++++++++
 ld/testsuite/ld-x86-64/pr32591-1b.d     | 27 +++++++++
 ld/testsuite/ld-x86-64/pr32591-1b.t     |  7 +++
 ld/testsuite/ld-x86-64/pr32591-1c-x32.d | 22 +++++++
 ld/testsuite/ld-x86-64/pr32591-1c.d     | 22 +++++++
 ld/testsuite/ld-x86-64/pr32591-1c.t     |  6 ++
 ld/testsuite/ld-x86-64/pr32591-1d-x32.d | 22 +++++++
 ld/testsuite/ld-x86-64/pr32591-1d.d     | 22 +++++++
 ld/testsuite/ld-x86-64/pr32591-1d.t     |  7 +++
 ld/testsuite/ld-x86-64/pr32591-2-x32.d  | 12 ++++
 ld/testsuite/ld-x86-64/pr32591-2.d      | 12 ++++
 ld/testsuite/ld-x86-64/pr32591-2.s      | 13 +++++
 ld/testsuite/ld-x86-64/pr32591-2.t      |  4 ++
 ld/testsuite/ld-x86-64/x86-64.exp       | 10 ++++
 34 files changed, 480 insertions(+), 31 deletions(-)
 create mode 100644 ld/testsuite/ld-x86-64/pr32591-1.s
 create mode 100644 ld/testsuite/ld-x86-64/pr32591-1a-x32.d
 create mode 100644 ld/testsuite/ld-x86-64/pr32591-1a.d
 create mode 100644 ld/testsuite/ld-x86-64/pr32591-1a.t
 create mode 100644 ld/testsuite/ld-x86-64/pr32591-1b-x32.d
 create mode 100644 ld/testsuite/ld-x86-64/pr32591-1b.d
 create mode 100644 ld/testsuite/ld-x86-64/pr32591-1b.t
 create mode 100644 ld/testsuite/ld-x86-64/pr32591-1c-x32.d
 create mode 100644 ld/testsuite/ld-x86-64/pr32591-1c.d
 create mode 100644 ld/testsuite/ld-x86-64/pr32591-1c.t
 create mode 100644 ld/testsuite/ld-x86-64/pr32591-1d-x32.d
 create mode 100644 ld/testsuite/ld-x86-64/pr32591-1d.d
 create mode 100644 ld/testsuite/ld-x86-64/pr32591-1d.t
 create mode 100644 ld/testsuite/ld-x86-64/pr32591-2-x32.d
 create mode 100644 ld/testsuite/ld-x86-64/pr32591-2.d
 create mode 100644 ld/testsuite/ld-x86-64/pr32591-2.s
 create mode 100644 ld/testsuite/ld-x86-64/pr32591-2.t
  

Comments

Alan Modra Feb. 2, 2025, 10:29 p.m. UTC | #1
On Sat, Feb 01, 2025 at 04:53:30PM +0800, H.J. Lu wrote:
> When sizing dynamic sections, elf_x86_64_scan_relocs converts GOTPCREL
> relocations to R_X86_64_PC32, R_X86_64_32S or R_X86_64_32 for local
> symbols.  But at that time, since the output section layout is unknown,
> the local symbol values can't be determined.  Later linker issues an
> error if the converted relocation overflows when resolving relocations
> against these local symbols.  Update the linker so that an ELF target
> can opt-in to estimate output section layout before sizing dynamic
> sections.

I think that is the wrong way to go.  A single backend_data bit
controlling prelim sizing is likely not flexible enough.  It's far
better I think to implement an x86 before_allocation override hook
rather than trying to put the code in ldelf_before_allocation.

>  The linker created sections are excluded when estimating
> output section layout as sizing dynamic sections may change linker
> created sections.

This logic also ought to go in the x86 backend to handle whatever
peculiarities are necessary for x86 linker created sections.
  
H.J. Lu Feb. 2, 2025, 11:34 p.m. UTC | #2
On Mon, Feb 3, 2025 at 6:29 AM Alan Modra <amodra@gmail.com> wrote:
>
> On Sat, Feb 01, 2025 at 04:53:30PM +0800, H.J. Lu wrote:
> > When sizing dynamic sections, elf_x86_64_scan_relocs converts GOTPCREL
> > relocations to R_X86_64_PC32, R_X86_64_32S or R_X86_64_32 for local
> > symbols.  But at that time, since the output section layout is unknown,
> > the local symbol values can't be determined.  Later linker issues an
> > error if the converted relocation overflows when resolving relocations
> > against these local symbols.  Update the linker so that an ELF target
> > can opt-in to estimate output section layout before sizing dynamic
> > sections.
>
> I think that is the wrong way to go.  A single backend_data bit
> controlling prelim sizing is likely not flexible enough.  It's far
> better I think to implement an x86 before_allocation override hook
> rather than trying to put the code in ldelf_before_allocation.

This means that the x86_64 backend needs to duplicate the whole
function so that it can do prelim sizing just before sizing dynamic sections.
It can be easily out of sync when ldelf_before_allocation is changed.
Is there a way to avoid it?

> >  The linker created sections are excluded when estimating
> > output section layout as sizing dynamic sections may change linker
> > created sections.
>
> This logic also ought to go in the x86 backend to handle whatever
> peculiarities are necessary for x86 linker created sections.

I will drop this change.

> --
> Alan Modra
  
Alan Modra Feb. 3, 2025, 12:05 a.m. UTC | #3
On Mon, Feb 03, 2025 at 07:34:01AM +0800, H.J. Lu wrote:
> On Mon, Feb 3, 2025 at 6:29 AM Alan Modra <amodra@gmail.com> wrote:
> >
> > On Sat, Feb 01, 2025 at 04:53:30PM +0800, H.J. Lu wrote:
> > > When sizing dynamic sections, elf_x86_64_scan_relocs converts GOTPCREL
> > > relocations to R_X86_64_PC32, R_X86_64_32S or R_X86_64_32 for local
> > > symbols.  But at that time, since the output section layout is unknown,
> > > the local symbol values can't be determined.  Later linker issues an
> > > error if the converted relocation overflows when resolving relocations
> > > against these local symbols.  Update the linker so that an ELF target
> > > can opt-in to estimate output section layout before sizing dynamic
> > > sections.
> >
> > I think that is the wrong way to go.  A single backend_data bit
> > controlling prelim sizing is likely not flexible enough.  It's far
> > better I think to implement an x86 before_allocation override hook
> > rather than trying to put the code in ldelf_before_allocation.
> 
> This means that the x86_64 backend needs to duplicate the whole
> function so that it can do prelim sizing just before sizing dynamic sections.
> It can be easily out of sync when ldelf_before_allocation is changed.
> Is there a way to avoid it?

Why would you need to duplicate the whole function?  Just call it.
See emultemp/ppc64elf.em.

> > >  The linker created sections are excluded when estimating
> > > output section layout as sizing dynamic sections may change linker
> > > created sections.
> >
> > This logic also ought to go in the x86 backend to handle whatever
> > peculiarities are necessary for x86 linker created sections.
> 
> I will drop this change.
> 
> > --
> > Alan Modra
> 
> 
> 
> -- 
> H.J.
  
H.J. Lu Feb. 3, 2025, 12:14 a.m. UTC | #4
On Mon, Feb 3, 2025 at 8:05 AM Alan Modra <amodra@gmail.com> wrote:
>
> On Mon, Feb 03, 2025 at 07:34:01AM +0800, H.J. Lu wrote:
> > On Mon, Feb 3, 2025 at 6:29 AM Alan Modra <amodra@gmail.com> wrote:
> > >
> > > On Sat, Feb 01, 2025 at 04:53:30PM +0800, H.J. Lu wrote:
> > > > When sizing dynamic sections, elf_x86_64_scan_relocs converts GOTPCREL
> > > > relocations to R_X86_64_PC32, R_X86_64_32S or R_X86_64_32 for local
> > > > symbols.  But at that time, since the output section layout is unknown,
> > > > the local symbol values can't be determined.  Later linker issues an
> > > > error if the converted relocation overflows when resolving relocations
> > > > against these local symbols.  Update the linker so that an ELF target
> > > > can opt-in to estimate output section layout before sizing dynamic
> > > > sections.
> > >
> > > I think that is the wrong way to go.  A single backend_data bit
> > > controlling prelim sizing is likely not flexible enough.  It's far
> > > better I think to implement an x86 before_allocation override hook
> > > rather than trying to put the code in ldelf_before_allocation.
> >
> > This means that the x86_64 backend needs to duplicate the whole
> > function so that it can do prelim sizing just before sizing dynamic sections.
> > It can be easily out of sync when ldelf_before_allocation is changed.
> > Is there a way to avoid it?
>
> Why would you need to duplicate the whole function?  Just call it.
> See emultemp/ppc64elf.em.

It doesn't work for x86-64 since prelim sizing must be done just before
sizing dynamic sections.  How about this

diff --git a/ld/ldelf.c b/ld/ldelf.c
index dbc3d77d47b..c10b0f8923b 100644
--- a/ld/ldelf.c
+++ b/ld/ldelf.c
@@ -1753,7 +1753,8 @@ ldelf_append_to_separated_string (char **to, char *op_arg)

 void
 ldelf_before_allocation (char **audit, char **depaudit,
-         const char *default_interpreter_name)
+         const char *default_interpreter_name,
+         void (*before_sizing_dynamic_section) (void))
 {
   const char *rpath;
   asection *sinterp;
@@ -1840,6 +1841,9 @@ ldelf_before_allocation (char **audit, char **depaudit,
     }
       }

+  if (before_sizing_dynamic_section)
+    before_sizing_dynamic_section ();
+
   if (! (bfd_elf_size_dynamic_sections
    (link_info.output_bfd, command_line.soname, rpath,

> > > >  The linker created sections are excluded when estimating
> > > > output section layout as sizing dynamic sections may change linker
> > > > created sections.
> > >
> > > This logic also ought to go in the x86 backend to handle whatever
> > > peculiarities are necessary for x86 linker created sections.
> >
> > I will drop this change.
> >
> > > --
> > > Alan Modra
> >
> >
> >
> > --
> > H.J.
>
> --
> Alan Modra
  
Alan Modra Feb. 3, 2025, 2 a.m. UTC | #5
On Mon, Feb 03, 2025 at 08:14:59AM +0800, H.J. Lu wrote:
> On Mon, Feb 3, 2025 at 8:05 AM Alan Modra <amodra@gmail.com> wrote:
> >
> > On Mon, Feb 03, 2025 at 07:34:01AM +0800, H.J. Lu wrote:
> > > On Mon, Feb 3, 2025 at 6:29 AM Alan Modra <amodra@gmail.com> wrote:
> > > >
> > > > On Sat, Feb 01, 2025 at 04:53:30PM +0800, H.J. Lu wrote:
> > > > > When sizing dynamic sections, elf_x86_64_scan_relocs converts GOTPCREL
> > > > > relocations to R_X86_64_PC32, R_X86_64_32S or R_X86_64_32 for local
> > > > > symbols.  But at that time, since the output section layout is unknown,
> > > > > the local symbol values can't be determined.  Later linker issues an
> > > > > error if the converted relocation overflows when resolving relocations
> > > > > against these local symbols.  Update the linker so that an ELF target
> > > > > can opt-in to estimate output section layout before sizing dynamic
> > > > > sections.
> > > >
> > > > I think that is the wrong way to go.  A single backend_data bit
> > > > controlling prelim sizing is likely not flexible enough.  It's far
> > > > better I think to implement an x86 before_allocation override hook
> > > > rather than trying to put the code in ldelf_before_allocation.
> > >
> > > This means that the x86_64 backend needs to duplicate the whole
> > > function so that it can do prelim sizing just before sizing dynamic sections.
> > > It can be easily out of sync when ldelf_before_allocation is changed.
> > > Is there a way to avoid it?
> >
> > Why would you need to duplicate the whole function?  Just call it.
> > See emultemp/ppc64elf.em.
> 
> It doesn't work for x86-64 since prelim sizing must be done just before
> sizing dynamic sections.  How about this

Explain "doesn't work".  How do the very few things that happen in
ldelf_before_allocation before the place you want to add another hook,
affect prelim sizing?
  
H.J. Lu Feb. 3, 2025, 2:04 a.m. UTC | #6
On Mon, Feb 3, 2025, 10:00 AM Alan Modra <amodra@gmail.com> wrote:

> On Mon, Feb 03, 2025 at 08:14:59AM +0800, H.J. Lu wrote:
> > On Mon, Feb 3, 2025 at 8:05 AM Alan Modra <amodra@gmail.com> wrote:
> > >
> > > On Mon, Feb 03, 2025 at 07:34:01AM +0800, H.J. Lu wrote:
> > > > On Mon, Feb 3, 2025 at 6:29 AM Alan Modra <amodra@gmail.com> wrote:
> > > > >
> > > > > On Sat, Feb 01, 2025 at 04:53:30PM +0800, H.J. Lu wrote:
> > > > > > When sizing dynamic sections, elf_x86_64_scan_relocs converts
> GOTPCREL
> > > > > > relocations to R_X86_64_PC32, R_X86_64_32S or R_X86_64_32 for
> local
> > > > > > symbols.  But at that time, since the output section layout is
> unknown,
> > > > > > the local symbol values can't be determined.  Later linker
> issues an
> > > > > > error if the converted relocation overflows when resolving
> relocations
> > > > > > against these local symbols.  Update the linker so that an ELF
> target
> > > > > > can opt-in to estimate output section layout before sizing
> dynamic
> > > > > > sections.
> > > > >
> > > > > I think that is the wrong way to go.  A single backend_data bit
> > > > > controlling prelim sizing is likely not flexible enough.  It's far
> > > > > better I think to implement an x86 before_allocation override hook
> > > > > rather than trying to put the code in ldelf_before_allocation.
> > > >
> > > > This means that the x86_64 backend needs to duplicate the whole
> > > > function so that it can do prelim sizing just before sizing dynamic
> sections.
> > > > It can be easily out of sync when ldelf_before_allocation is changed.
> > > > Is there a way to avoid it?
> > >
> > > Why would you need to duplicate the whole function?  Just call it.
> > > See emultemp/ppc64elf.em.
> >
> > It doesn't work for x86-64 since prelim sizing must be done just before
> > sizing dynamic sections.  How about this
>
> Explain "doesn't work".  How do the very few things that happen in
> ldelf_before_allocation before the place you want to add another hook,
> affect prelim sizing?
>

I sent the v2 patch.


> --
> Alan Modra
>
>
  

Patch

diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index c89327fdc9c..dd4e565eaa9 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -1790,6 +1790,14 @@  struct elf_backend_data
      section contents must be replaced by _bfd_elf_mmap_section_contents
      and _bfd_elf_munmap_section_contents.  */
   unsigned use_mmap : 1;
+
+  /* True if `one_lang_size_sections_passa' must be called to perform
+     preliminary output section layout before sizing dynamic sections.  */
+  unsigned perform_prelim_section_layout : 1;
+
+  /* True if linker created sections should be excluded for preliminary
+     output section layout.  */
+  unsigned exclude_linker_created_sections : 1;
 };
 
 /* Information about reloc sections associated with a bfd_elf_section_data
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index 61334c3ab04..f7a618b8fdc 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -1770,6 +1770,7 @@  static unsigned int evex_move_r_to_b (unsigned int byte1)
 
 static bool
 elf_x86_64_convert_load_reloc (bfd *abfd,
+			       asection *input_section,
 			       bfd_byte *contents,
 			       unsigned int *r_type_p,
 			       Elf_Internal_Rela *irel,
@@ -1793,6 +1794,10 @@  elf_x86_64_convert_load_reloc (bfd *abfd,
   unsigned int r_symndx;
   bfd_vma roff = irel->r_offset;
   bfd_vma abs_relocation;
+  reloc_howto_type *howto;
+  bfd_reloc_status_type r;
+  Elf_Internal_Sym *isym;
+  bfd_vma relocation;
 
   switch (r_type)
     {
@@ -1901,8 +1906,8 @@  elf_x86_64_convert_load_reloc (bfd *abfd,
   /* Get the symbol referred to by the reloc.  */
   if (h == NULL)
     {
-      Elf_Internal_Sym *isym
-	= bfd_sym_from_r_symndx (&htab->elf.sym_cache, abfd, r_symndx);
+      isym = bfd_sym_from_r_symndx (&htab->elf.sym_cache, abfd,
+				    r_symndx);
 
       /* Skip relocation against undefined symbols.  */
       if (isym->st_shndx == SHN_UNDEF)
@@ -1932,6 +1937,9 @@  elf_x86_64_convert_load_reloc (bfd *abfd,
 	 R_X86_64_PC32.  */
       struct elf_x86_link_hash_entry *eh = elf_x86_hash_entry (h);
 
+      isym = NULL;
+      tsec = NULL;
+
       abs_symbol = ABS_SYMBOL_P (h);
       abs_relocation = h->root.u.def.value;
 
@@ -1991,6 +1999,8 @@  elf_x86_64_convert_load_reloc (bfd *abfd,
 	      /* Skip since R_X86_64_32/R_X86_64_32S may overflow.  */
 	      if (no_overflow)
 		return true;
+	      if (h->start_stop)
+		tsec = h->root.u.def.section;
 	      goto convert;
 	    }
 	  tsec = h->root.u.def.section;
@@ -2009,6 +2019,25 @@  elf_x86_64_convert_load_reloc (bfd *abfd,
     return true;
 
  convert:
+  /* Compute relocation value so that it can be used later to check for
+     overflow against the converted relocation.  */
+  if (h == NULL)
+    {
+      /* Make a copy of IREL so that _bfd_elf_rela_local_sym won't
+	 change IREL.  */
+      Elf_Internal_Rela rel = *irel;
+      relocation = _bfd_elf_rela_local_sym (link_info->output_bfd, isym,
+					    &tsec, &rel);
+      /* Use the updated r_addend.  */
+      raddend = rel.r_addend;
+    }
+  else if (tsec != NULL)
+    relocation = (h->root.u.def.value
+		  + tsec->output_section->vma
+		  + tsec->output_offset);
+  else
+    relocation = 0;
+
   if (opcode == 0xff)
     {
       /* We have "call/jmp *foo@GOTPCREL(%rip)".  */
@@ -2016,6 +2045,16 @@  elf_x86_64_convert_load_reloc (bfd *abfd,
       unsigned int disp;
       bfd_vma nop_offset;
 
+      r_type = R_X86_64_PC32;
+
+      /* Skip if the converted relocation will overflow.  */
+      howto = elf_x86_64_rtype_to_howto (abfd, r_type);
+      r = _bfd_final_link_relocate (howto, abfd, input_section,
+				    contents, irel->r_offset,
+				    relocation, raddend);
+      if (r == bfd_reloc_overflow)
+	return true;
+
       /* Convert R_X86_64_GOTPCRELX and R_X86_64_REX_GOTPCRELX to
 	 R_X86_64_PC32.  */
       modrm = bfd_get_8 (abfd, contents + roff - 1);
@@ -2060,7 +2099,6 @@  elf_x86_64_convert_load_reloc (bfd *abfd,
 	}
       bfd_put_8 (abfd, nop, contents + nop_offset);
       bfd_put_8 (abfd, modrm, contents + irel->r_offset - 1);
-      r_type = R_X86_64_PC32;
     }
   else if (r_type == R_X86_64_CODE_6_GOTPCRELX && opcode != 0x8b)
     {
@@ -2101,6 +2139,14 @@  elf_x86_64_convert_load_reloc (bfd *abfd,
 	 overflow when sign-extending imm32 to 64 bits.  */
       r_type = evex[1] & 0x80 ? R_X86_64_32S : R_X86_64_32;
 
+      /* Skip if the converted relocation will overflow.  */
+      howto = elf_x86_64_rtype_to_howto (abfd, r_type);
+      r = _bfd_final_link_relocate (howto, abfd, input_section,
+				    contents, irel->r_offset,
+				    relocation, 0);
+      if (r == bfd_reloc_overflow)
+	return true;
+
       if (abs_relocation) /* Bogus; should be abs_symbol.  */
 	{
 	  /* Check if R_X86_64_32S/R_X86_64_32 fits.  */
@@ -2186,6 +2232,15 @@  elf_x86_64_convert_load_reloc (bfd *abfd,
 	      opcode = 0x8d;
 	      r_type = R_X86_64_PC32;
 
+	      /* Skip if the converted relocation will overflow.  */
+	      howto = elf_x86_64_rtype_to_howto (abfd, r_type);
+	      r = _bfd_final_link_relocate (howto, abfd, input_section,
+					    contents, irel->r_offset,
+					    relocation,
+					    raddend);
+	      if (r == bfd_reloc_overflow)
+		return true;
+
 	      /* For MOVRS move a possible REX prefix as necessary.  */
 	      if (movrs == 5)
 		bfd_put_8 (abfd, rex, contents + roff - 3);
@@ -2245,6 +2300,14 @@  elf_x86_64_convert_load_reloc (bfd *abfd,
 	  r_type = rex_w ? R_X86_64_32S : R_X86_64_32;
 
 	rewrite_modrm_rex:
+	  /* Skip if the converted relocation will overflow.  */
+	  howto = elf_x86_64_rtype_to_howto (abfd, r_type);
+	  r = _bfd_final_link_relocate (howto, abfd, input_section,
+					contents, irel->r_offset,
+					relocation, 0);
+	  if (r == bfd_reloc_overflow)
+	    return true;
+
 	  if (abs_relocation)
 	    {
 	      /* Check if R_X86_64_32S/R_X86_64_32 fits.  */
@@ -2465,9 +2528,9 @@  elf_x86_64_scan_relocs (bfd *abfd, struct bfd_link_info *info,
 	  && (h == NULL || h->type != STT_GNU_IFUNC))
 	{
 	  Elf_Internal_Rela *irel = (Elf_Internal_Rela *) rel;
-	  if (!elf_x86_64_convert_load_reloc (abfd, contents, &r_type,
-					      irel, h, &converted_reloc,
-					      info))
+	  if (!elf_x86_64_convert_load_reloc (abfd, sec, contents,
+					      &r_type, irel, h,
+					      &converted_reloc, info))
 	    goto error_return;
 
 	  if (converted_reloc)
@@ -6148,6 +6211,8 @@  elf_x86_64_special_sections[]=
   _bfd_x86_elf_hide_symbol
 #define elf_backend_add_glibc_version_dependency \
   elf_x86_64_add_glibc_version_dependency
+#define elf_backend_perform_prelim_section_layout  true
+#define elf_backend_exclude_linker_created_sections true
 
 #undef	elf64_bed
 #define elf64_bed elf64_x86_64_bed
diff --git a/bfd/elfxx-target.h b/bfd/elfxx-target.h
index 625243cac94..8f373d7cc7a 100644
--- a/bfd/elfxx-target.h
+++ b/bfd/elfxx-target.h
@@ -148,6 +148,12 @@ 
 #ifndef elf_backend_use_mmap
 #define elf_backend_use_mmap false
 #endif
+#ifndef elf_backend_perform_prelim_section_layout
+#define elf_backend_perform_prelim_section_layout false
+#endif
+#ifndef elf_backend_exclude_linker_created_sections
+#define elf_backend_exclude_linker_created_sections false
+#endif
 
 #define bfd_elfNN_bfd_debug_info_start		_bfd_void_bfd
 #define bfd_elfNN_bfd_debug_info_end		_bfd_void_bfd
@@ -976,7 +982,9 @@  static const struct elf_backend_data elfNN_bed =
   elf_backend_always_renumber_dynsyms,
   elf_backend_linux_prpsinfo32_ugid16,
   elf_backend_linux_prpsinfo64_ugid16,
-  elf_backend_use_mmap
+  elf_backend_use_mmap,
+  elf_backend_perform_prelim_section_layout,
+  elf_backend_exclude_linker_created_sections
 };
 
 /* Forward declaration for use when initialising alternative_target field.  */
diff --git a/ld/emultempl/aix.em b/ld/emultempl/aix.em
index d69e13cfa91..f7cb8d87a50 100644
--- a/ld/emultempl/aix.em
+++ b/ld/emultempl/aix.em
@@ -968,7 +968,7 @@  gld${EMULATION_NAME}_before_allocation (void)
 	{
 	  expld.phase = lang_mark_phase_enum;
 	  expld.dataseg.phase = exp_seg_none;
-	  one_lang_size_sections_pass (NULL, false);
+	  one_lang_size_sections_pass (NULL, false, false);
 	  lang_reset_memory_regions ();
 	}
 
diff --git a/ld/emultempl/ppc32elf.em b/ld/emultempl/ppc32elf.em
index 529a5920920..239f4b2f728 100644
--- a/ld/emultempl/ppc32elf.em
+++ b/ld/emultempl/ppc32elf.em
@@ -125,7 +125,7 @@  prelim_size_sections (void)
     {
       expld.phase = lang_mark_phase_enum;
       expld.dataseg.phase = exp_seg_none;
-      one_lang_size_sections_pass (NULL, false);
+      one_lang_size_sections_pass (NULL, false, false);
       /* We must not cache anything from the preliminary sizing.  */
       lang_reset_memory_regions ();
     }
diff --git a/ld/emultempl/ppc64elf.em b/ld/emultempl/ppc64elf.em
index 1e6ae7af738..464aad42c05 100644
--- a/ld/emultempl/ppc64elf.em
+++ b/ld/emultempl/ppc64elf.em
@@ -272,7 +272,7 @@  prelim_size_sections (void)
     {
       expld.phase = lang_mark_phase_enum;
       expld.dataseg.phase = exp_seg_none;
-      one_lang_size_sections_pass (NULL, false);
+      one_lang_size_sections_pass (NULL, false, false);
       /* We must not cache anything from the preliminary sizing.  */
       lang_reset_memory_regions ();
     }
diff --git a/ld/emultempl/spuelf.em b/ld/emultempl/spuelf.em
index 76776ba2555..d7877326779 100644
--- a/ld/emultempl/spuelf.em
+++ b/ld/emultempl/spuelf.em
@@ -273,7 +273,7 @@  spu_before_allocation (void)
 	 rough layout so that overlays can be found.  */
       expld.phase = lang_mark_phase_enum;
       expld.dataseg.phase = exp_seg_none;
-      one_lang_size_sections_pass (NULL, true);
+      one_lang_size_sections_pass (NULL, true, false);
 
       /* Find overlays by inspecting section vmas.  */
       ret = spu_elf_find_overlays (&link_info);
diff --git a/ld/ldelf.c b/ld/ldelf.c
index dbc3d77d47b..85b77cda3ff 100644
--- a/ld/ldelf.c
+++ b/ld/ldelf.c
@@ -1762,10 +1762,12 @@  ldelf_before_allocation (char **audit, char **depaudit,
   unsigned char ehdr_start_save_type = 0;
   char ehdr_start_save_u[sizeof ehdr_start->u
 			 - sizeof ehdr_start->u.def.next] = "";
+  const struct elf_backend_data *bed = NULL;
 
   if (is_elf_hash_table (link_info.hash))
     {
       _bfd_elf_tls_setup (link_info.output_bfd, &link_info);
+      bed = get_elf_backend_data (link_info.output_bfd);
 
       /* Make __ehdr_start hidden if it has been referenced, to
 	 prevent the symbol from being dynamic.  */
@@ -1840,6 +1842,20 @@  ldelf_before_allocation (char **audit, char **depaudit,
 	  }
       }
 
+  if (bed != NULL
+      && bed->perform_prelim_section_layout
+      && !bfd_link_relocatable (&link_info)
+      && expld.phase != lang_mark_phase_enum)
+    {
+      /* Run one_lang_size_sections_pass to estimate the output section
+	 layout before sizing dynamic sections.  */
+      expld.dataseg.phase = exp_seg_none;
+      expld.phase = lang_mark_phase_enum;
+      one_lang_size_sections_pass (NULL, false,
+				   bed->exclude_linker_created_sections);
+      lang_reset_memory_regions ();
+    }
+
   if (! (bfd_elf_size_dynamic_sections
 	 (link_info.output_bfd, command_line.soname, rpath,
 	  command_line.filter_shlib, *audit, *depaudit,
diff --git a/ld/ldlang.c b/ld/ldlang.c
index cd94e201837..97278bc045e 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -4676,7 +4676,7 @@  strip_excluded_output_sections (void)
     {
       expld.phase = lang_mark_phase_enum;
       expld.dataseg.phase = exp_seg_none;
-      one_lang_size_sections_pass (NULL, false);
+      one_lang_size_sections_pass (NULL, false, false);
       lang_reset_memory_regions ();
     }
 
@@ -5917,7 +5917,8 @@  lang_size_sections_1
    fill_type *fill,
    bfd_vma dot,
    bool *relax,
-   bool check_regions)
+   bool check_regions,
+   bool exclude_linker_created_sections)
 {
   lang_statement_union_type *s;
   lang_statement_union_type *prev_s = NULL;
@@ -6093,7 +6094,8 @@  lang_size_sections_1
 	      }
 
 	    lang_size_sections_1 (&os->children.head, os,
-				  os->fill, newdot, relax, check_regions);
+				  os->fill, newdot, relax, check_regions,
+				  exclude_linker_created_sections);
 
 	    os->processed_vma = true;
 
@@ -6267,7 +6269,8 @@  lang_size_sections_1
 
 	case lang_constructors_statement_enum:
 	  dot = lang_size_sections_1 (&constructor_list.head, current_os,
-				      fill, dot, relax, check_regions);
+				      fill, dot, relax, check_regions,
+				      exclude_linker_created_sections);
 	  break;
 
 	case lang_data_statement_enum:
@@ -6329,7 +6332,8 @@  lang_size_sections_1
 	case lang_wild_statement_enum:
 	  dot = lang_size_sections_1 (&s->wild_statement.children.head,
 				      current_os, fill, dot, relax,
-				      check_regions);
+				      check_regions,
+				      exclude_linker_created_sections);
 	  break;
 
 	case lang_object_symbols_statement_enum:
@@ -6346,6 +6350,10 @@  lang_size_sections_1
 	    asection *i;
 
 	    i = s->input_section.section;
+	    if (exclude_linker_created_sections
+		&& (i->flags & SEC_LINKER_CREATED))
+	      break;
+
 	    if (relax)
 	      {
 		bool again;
@@ -6443,7 +6451,8 @@  lang_size_sections_1
 	case lang_group_statement_enum:
 	  dot = lang_size_sections_1 (&s->group_statement.children.head,
 				      current_os, fill, dot, relax,
-				      check_regions);
+				      check_regions,
+				      exclude_linker_created_sections);
 	  break;
 
 	case lang_insert_statement_enum:
@@ -6547,13 +6556,15 @@  ldlang_override_segment_assignment (struct bfd_link_info *info ATTRIBUTE_UNUSED,
 }
 
 void
-one_lang_size_sections_pass (bool *relax, bool check_regions)
+one_lang_size_sections_pass (bool *relax, bool check_regions,
+			     bool exclude_linker_created_sections)
 {
   lang_statement_iteration++;
   if (expld.phase != lang_mark_phase_enum)
     lang_sizing_iteration++;
   lang_size_sections_1 (&statement_list.head, abs_output_section,
-			0, 0, relax, check_regions);
+			0, 0, relax, check_regions,
+			exclude_linker_created_sections);
 }
 
 static bool
@@ -6631,7 +6642,7 @@  lang_size_relro_segment (bool *relax, bool check_regions)
       bfd_vma data_relro_end = lang_size_relro_segment_1 ();
 
       lang_reset_memory_regions ();
-      one_lang_size_sections_pass (relax, check_regions);
+      one_lang_size_sections_pass (relax, check_regions, false);
 
       /* Assignments to dot, or to output section address in a user
 	 script have increased padding over the original.  Revert.  */
@@ -6653,7 +6664,7 @@  lang_size_sections (bool *relax, bool check_regions)
   expld.phase = lang_allocating_phase_enum;
   expld.dataseg.phase = exp_seg_none;
 
-  one_lang_size_sections_pass (relax, check_regions);
+  one_lang_size_sections_pass (relax, check_regions, false);
 
   if (expld.dataseg.phase != exp_seg_end_seen)
     expld.dataseg.phase = exp_seg_done;
@@ -6666,7 +6677,7 @@  lang_size_sections (bool *relax, bool check_regions)
       if (do_reset)
 	{
 	  lang_reset_memory_regions ();
-	  one_lang_size_sections_pass (relax, check_regions);
+	  one_lang_size_sections_pass (relax, check_regions, false);
 	}
 
       if (link_info.relro && expld.dataseg.relro_end)
diff --git a/ld/ldlang.h b/ld/ldlang.h
index 8d905f04742..d5225ac5564 100644
--- a/ld/ldlang.h
+++ b/ld/ldlang.h
@@ -681,7 +681,7 @@  extern void dprint_statement
 extern void lang_size_sections
   (bool *, bool);
 extern void one_lang_size_sections_pass
-  (bool *, bool);
+  (bool *, bool, bool);
 extern void lang_add_insert
   (const char *, int);
 extern void lang_enter_group
diff --git a/ld/testsuite/ld-x86-64/pr19609-2a.d b/ld/testsuite/ld-x86-64/pr19609-2a.d
index 6d3db92afb7..401d64affef 100644
--- a/ld/testsuite/ld-x86-64/pr19609-2a.d
+++ b/ld/testsuite/ld-x86-64/pr19609-2a.d
@@ -1,4 +1,13 @@ 
 #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.*
+#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        # .*
+#pass
diff --git a/ld/testsuite/ld-x86-64/pr19609-2b.d b/ld/testsuite/ld-x86-64/pr19609-2b.d
index 4fee93d7034..7e6e8cd3c91 100644
--- a/ld/testsuite/ld-x86-64/pr19609-2b.d
+++ b/ld/testsuite/ld-x86-64/pr19609-2b.d
@@ -1,4 +1,13 @@ 
 #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.*
+#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        # .*
+#pass
diff --git a/ld/testsuite/ld-x86-64/pr19609-4a.d b/ld/testsuite/ld-x86-64/pr19609-4a.d
index eb37d0c8729..b6e41f5ee78 100644
--- a/ld/testsuite/ld-x86-64/pr19609-4a.d
+++ b/ld/testsuite/ld-x86-64/pr19609-4a.d
@@ -1,4 +1,13 @@ 
 #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.*
+#objdump: -dw
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+0+70000000 <_start>:
+[ 	]*[a-f0-9]+:	48 8b 05 ([0-9a-f]{2} ){4} *	mov    [-]?0x[a-f0-9]+\(%rip\),%rax        # [a-f0-9]+ <_start\+0x1000>
+[ 	]*[a-f0-9]+:	4c 8b 1d ([0-9a-f]{2} ){4} *	mov    [-]?0x[a-f0-9]+\(%rip\),%r11        # [a-f0-9]+ <_start\+0x1000>
diff --git a/ld/testsuite/ld-x86-64/pr19609-5d.d b/ld/testsuite/ld-x86-64/pr19609-5d.d
index 0ab28efff1e..0b011e0943e 100644
--- a/ld/testsuite/ld-x86-64/pr19609-5d.d
+++ b/ld/testsuite/ld-x86-64/pr19609-5d.d
@@ -1,4 +1,12 @@ 
 #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
+#objdump: -dw
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+[a-f0-9]+ <_start>:
+[ ]+[a-f0-9]+:	ff 15 ([0-9a-f]{2} ){4} *	call   \*-?0x[a-f0-9]+\(%rip\)        # [a-f0-9]+ <.*>
diff --git a/ld/testsuite/ld-x86-64/pr19609-7a.d b/ld/testsuite/ld-x86-64/pr19609-7a.d
index 34704a94c16..f00f0b88de2 100644
--- a/ld/testsuite/ld-x86-64/pr19609-7a.d
+++ b/ld/testsuite/ld-x86-64/pr19609-7a.d
@@ -1,4 +1,13 @@ 
 #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
+#ld: -melf_x86_64 -Ttext=0x80000000 -z max-page-size=0x1000 -z separate-code --no-rosegment
+#objdump: -dw
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+[a-f0-9]+ <_start>:
+[ ]*[a-f0-9]+:	ff 15 ([0-9a-f]{2} ){4} *	call   \*-?0x[a-f0-9]+\(%rip\)        # [a-f0-9]+ <_start\+0x1000>
+#pass
diff --git a/ld/testsuite/ld-x86-64/pr19609-7c.d b/ld/testsuite/ld-x86-64/pr19609-7c.d
index 09488e22a8e..c1ee443dd30 100644
--- a/ld/testsuite/ld-x86-64/pr19609-7c.d
+++ b/ld/testsuite/ld-x86-64/pr19609-7c.d
@@ -1,4 +1,13 @@ 
 #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
+#ld: -melf32_x86_64 -Ttext=0x80000000 -z max-page-size=0x1000 -z separate-code --no-rosegment
+#objdump: -dw
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+[a-f0-9]+ <_start>:
+[ ]*[a-f0-9]+:	ff 15 ([0-9a-f]{2} ){4} *	call   \*-?0x[a-f0-9]+\(%rip\)        # [a-f0-9]+ <_start\+0x1000>
+#pass
diff --git a/ld/testsuite/ld-x86-64/pr32591-1.s b/ld/testsuite/ld-x86-64/pr32591-1.s
new file mode 100644
index 00000000000..853645646d2
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr32591-1.s
@@ -0,0 +1,24 @@ 
+.section .text.foo,"ax"
+.globl _foo
+.type _foo, @function
+_foo:
+  movl __start_data@GOTPCREL(%rip), %eax
+  addq foo_1@GOTPCREL(%rip), %rax
+  addq foo@GOTPCREL(%rip), %rax
+
+.section .text,"ax"
+.globl _start
+.type _start, @function
+_start:
+  movl __stop_data@GOTPCREL(%rip), %eax
+  movq __stop_data@GOTPCREL(%rip), %rax
+  movq __stop_data@GOTPCREL(%rip), %rax
+  movl __stop_data@GOTPCREL(%rip), %eax
+
+.section foo,"aw",@progbits
+.space 1
+foo_1:
+.space 1
+
+.section data,"aw",@progbits
+.space 13
diff --git a/ld/testsuite/ld-x86-64/pr32591-1a-x32.d b/ld/testsuite/ld-x86-64/pr32591-1a-x32.d
new file mode 100644
index 00000000000..693437317ea
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr32591-1a-x32.d
@@ -0,0 +1,22 @@ 
+#source: pr32591-1.s
+#as: --x32 -mrelax-relocations=yes
+#ld: -melf32_x86_64 -T pr32591-1a.t -z max-page-size=0x1000 -z separate-code --no-rosegment
+#objdump: -dw
+
+.*: +file format .*
+
+
+Disassembly of section .text.foo:
+
+00100000 <_foo>:
+  100000:	c7 c0 00 00 20 80    	mov    \$0x80200000,%eax
+  100006:	48 03 05 f3 ff 1f 00 	add    0x1ffff3\(%rip\),%rax        # 300000 <_start\+0x100000>
+  10000d:	48 81 c0 ff ff ff 7f 	add    \$0x7fffffff,%rax
+
+Disassembly of section .text:
+
+00200000 <_start>:
+  200000:	c7 c0 0d 00 20 80    	mov    \$0x8020000d,%eax
+  200006:	40 c7 c0 0d 00 20 80 	rex mov \$0x8020000d,%eax
+  20000d:	40 c7 c0 0d 00 20 80 	rex mov \$0x8020000d,%eax
+  200014:	c7 c0 0d 00 20 80    	mov    \$0x8020000d,%eax
diff --git a/ld/testsuite/ld-x86-64/pr32591-1a.d b/ld/testsuite/ld-x86-64/pr32591-1a.d
new file mode 100644
index 00000000000..30637a75d08
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr32591-1a.d
@@ -0,0 +1,22 @@ 
+#source: pr32591-1.s
+#as: --64 -mrelax-relocations=yes
+#ld: -melf_x86_64 -T pr32591-1a.t -z max-page-size=0x1000 -z separate-code --no-rosegment
+#objdump: -dw
+
+.*: +file format .*
+
+
+Disassembly of section .text.foo:
+
+0000000000100000 <_foo>:
+  100000:	c7 c0 00 00 20 80    	mov    \$0x80200000,%eax
+  100006:	48 03 05 f3 ff 1f 00 	add    0x1ffff3\(%rip\),%rax        # 300000 <_start\+0x100000>
+  10000d:	48 81 c0 ff ff ff 7f 	add    \$0x7fffffff,%rax
+
+Disassembly of section .text:
+
+0000000000200000 <_start>:
+  200000:	c7 c0 0d 00 20 80    	mov    \$0x8020000d,%eax
+  200006:	48 8b 05 fb ff 0f 00 	mov    0xffffb\(%rip\),%rax        # 300008 <_start\+0x100008>
+  20000d:	48 8b 05 f4 ff 0f 00 	mov    0xffff4\(%rip\),%rax        # 300008 <_start\+0x100008>
+  200014:	c7 c0 0d 00 20 80    	mov    \$0x8020000d,%eax
diff --git a/ld/testsuite/ld-x86-64/pr32591-1a.t b/ld/testsuite/ld-x86-64/pr32591-1a.t
new file mode 100644
index 00000000000..efe8a070143
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr32591-1a.t
@@ -0,0 +1,7 @@ 
+SECTIONS {
+  .text.foo 0x100000 : { *(.text.foo) }
+  .text 0x200000 : { *(.text) }
+  .got 0x300000 : { *(.got) }
+  foo 0x7fffffff : { *(foo) }
+  data 0x80200000 : { *(data) }
+}
diff --git a/ld/testsuite/ld-x86-64/pr32591-1b-x32.d b/ld/testsuite/ld-x86-64/pr32591-1b-x32.d
new file mode 100644
index 00000000000..0b98e87e84a
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr32591-1b-x32.d
@@ -0,0 +1,27 @@ 
+#source: pr32591-1.s
+#as: --x32 -mrelax-relocations=yes
+#ld: -melf32_x86_64 -T pr32591-1b.t -z max-page-size=0x1000 -z separate-code --no-rosegment
+#objdump: -dw
+
+.*: +file format .*
+
+
+Disassembly of section .text.foo:
+
+00100000 <_foo>:
+  100000:	c7 c0 00 00 20 80    	mov    \$0x80200000,%eax
+  100006:	48 03 05 f3 ff 1f 00 	add    0x1ffff3\(%rip\),%rax        # 300000 <_start\+0x100000>
+  10000d:	48 81 c0 ff ff ff 7f 	add    \$0x7fffffff,%rax
+
+Disassembly of section .text:
+
+001ff000 <_start-0x1000>:
+  1ff000:	66 2e 0f 1f 84 00 00 00 00 00 	cs nopw 0x0\(%rax,%rax,1\)
+#...
+  1ffffa:	66 0f 1f 44 00 00    	nopw   0x0\(%rax,%rax,1\)
+
+00200000 <_start>:
+  200000:	c7 c0 0d 00 20 80    	mov    \$0x8020000d,%eax
+  200006:	40 c7 c0 0d 00 20 80 	rex mov \$0x8020000d,%eax
+  20000d:	40 c7 c0 0d 00 20 80 	rex mov \$0x8020000d,%eax
+  200014:	c7 c0 0d 00 20 80    	mov    \$0x8020000d,%eax
diff --git a/ld/testsuite/ld-x86-64/pr32591-1b.d b/ld/testsuite/ld-x86-64/pr32591-1b.d
new file mode 100644
index 00000000000..149f851a97e
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr32591-1b.d
@@ -0,0 +1,27 @@ 
+#source: pr32591-1.s
+#as: --64 -mrelax-relocations=yes
+#ld: -melf_x86_64 -T pr32591-1b.t -z max-page-size=0x1000 -z separate-code --no-rosegment
+#objdump: -dw
+
+.*: +file format .*
+
+
+Disassembly of section .text.foo:
+
+0000000000100000 <_foo>:
+  100000:	c7 c0 00 00 20 80    	mov    \$0x80200000,%eax
+  100006:	48 03 05 f3 ff 1f 00 	add    0x1ffff3\(%rip\),%rax        # 300000 <_start\+0x100000>
+  10000d:	48 81 c0 ff ff ff 7f 	add    \$0x7fffffff,%rax
+
+Disassembly of section .text:
+
+00000000001ff000 <_start-0x1000>:
+  1ff000:	66 2e 0f 1f 84 00 00 00 00 00 	cs nopw 0x0\(%rax,%rax,1\)
+#...
+  1ffffa:	66 0f 1f 44 00 00    	nopw   0x0\(%rax,%rax,1\)
+
+0000000000200000 <_start>:
+  200000:	c7 c0 0d 00 20 80    	mov    \$0x8020000d,%eax
+  200006:	48 8b 05 fb ff 0f 00 	mov    0xffffb\(%rip\),%rax        # 300008 <_start\+0x100008>
+  20000d:	48 8b 05 f4 ff 0f 00 	mov    0xffff4\(%rip\),%rax        # 300008 <_start\+0x100008>
+  200014:	c7 c0 0d 00 20 80    	mov    \$0x8020000d,%eax
diff --git a/ld/testsuite/ld-x86-64/pr32591-1b.t b/ld/testsuite/ld-x86-64/pr32591-1b.t
new file mode 100644
index 00000000000..1f7a0e12023
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr32591-1b.t
@@ -0,0 +1,7 @@ 
+SECTIONS {
+  .text.foo 0x100000 : { *(.text.foo) }
+  .text 0x1ff000 : { . = . + 0x1000 ; *(.text) }
+  .got 0x300000 : { *(.got) }
+  foo 0x7fffffff : { *(foo) }
+  data 0x80200000 : { *(data) }
+}
diff --git a/ld/testsuite/ld-x86-64/pr32591-1c-x32.d b/ld/testsuite/ld-x86-64/pr32591-1c-x32.d
new file mode 100644
index 00000000000..e1c7fc61816
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr32591-1c-x32.d
@@ -0,0 +1,22 @@ 
+#source: pr32591-1.s
+#as: --x32 -mrelax-relocations=yes
+#ld: -melf32_x86_64 -T pr32591-1c.t -z max-page-size=0x1000 -z separate-code --no-rosegment
+#objdump: -dw
+
+.*: +file format .*
+
+
+Disassembly of section .text.foo:
+
+00100000 <_foo>:
+  100000:	c7 c0 00 00 40 00    	mov    \$0x400000,%eax
+  100006:	48 81 c0 0e 00 40 00 	add    \$0x40000e,%rax
+  10000d:	48 81 c0 0d 00 40 00 	add    \$0x40000d,%rax
+
+Disassembly of section .text:
+
+00200000 <_start>:
+  200000:	c7 c0 0d 00 40 00    	mov    \$0x40000d,%eax
+  200006:	40 c7 c0 0d 00 40 00 	rex mov \$0x40000d,%eax
+  20000d:	40 c7 c0 0d 00 40 00 	rex mov \$0x40000d,%eax
+  200014:	c7 c0 0d 00 40 00    	mov    \$0x40000d,%eax
diff --git a/ld/testsuite/ld-x86-64/pr32591-1c.d b/ld/testsuite/ld-x86-64/pr32591-1c.d
new file mode 100644
index 00000000000..f7b390fe485
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr32591-1c.d
@@ -0,0 +1,22 @@ 
+#source: pr32591-1.s
+#as: --64 -mrelax-relocations=yes
+#ld: -melf_x86_64 -T pr32591-1c.t -z max-page-size=0x1000 -z separate-code --no-rosegment
+#objdump: -dw
+
+.*: +file format .*
+
+
+Disassembly of section .text.foo:
+
+0000000000100000 <_foo>:
+  100000:	c7 c0 00 00 40 00    	mov    \$0x400000,%eax
+  100006:	48 81 c0 0e 00 40 00 	add    \$0x40000e,%rax
+  10000d:	48 81 c0 0d 00 40 00 	add    \$0x40000d,%rax
+
+Disassembly of section .text:
+
+0000000000200000 <_start>:
+  200000:	c7 c0 0d 00 40 00    	mov    \$0x40000d,%eax
+  200006:	48 c7 c0 0d 00 40 00 	mov    \$0x40000d,%rax
+  20000d:	48 c7 c0 0d 00 40 00 	mov    \$0x40000d,%rax
+  200014:	c7 c0 0d 00 40 00    	mov    \$0x40000d,%eax
diff --git a/ld/testsuite/ld-x86-64/pr32591-1c.t b/ld/testsuite/ld-x86-64/pr32591-1c.t
new file mode 100644
index 00000000000..3bfb7f22cd2
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr32591-1c.t
@@ -0,0 +1,6 @@ 
+SECTIONS {
+  .text.foo 0x100000 : { *(.text.foo) }
+  .text 0x200000 : { *(.text) }
+  .got 0x300000 : { *(.got) }
+  data 0x400000 : { *(data) }
+}
diff --git a/ld/testsuite/ld-x86-64/pr32591-1d-x32.d b/ld/testsuite/ld-x86-64/pr32591-1d-x32.d
new file mode 100644
index 00000000000..c5bc0eef6fb
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr32591-1d-x32.d
@@ -0,0 +1,22 @@ 
+#source: pr32591-1.s
+#as: --x32 -mrelax-relocations=yes
+#ld: -melf32_x86_64 -T pr32591-1d.t -z max-page-size=0x1000 -z separate-code --no-rosegment
+#objdump: -dw
+
+.*: +file format .*
+
+
+Disassembly of section .text.foo:
+
+00002000 <_foo>:
+    2000:	c7 c0 00 10 00 80    	mov    \$0x80001000,%eax
+    2006:	48 03 05 f3 1f 00 00 	add    0x1ff3\(%rip\),%rax        # 4000 <_start\+0x1000>
+    200d:	48 81 c0 ff ff ff 7f 	add    \$0x7fffffff,%rax
+
+Disassembly of section .text:
+
+00003000 <_start>:
+    3000:	c7 c0 0d 10 00 80    	mov    \$0x8000100d,%eax
+    3006:	40 c7 c0 0d 10 00 80 	rex mov \$0x8000100d,%eax
+    300d:	40 c7 c0 0d 10 00 80 	rex mov \$0x8000100d,%eax
+    3014:	c7 c0 0d 10 00 80    	mov    \$0x8000100d,%eax
diff --git a/ld/testsuite/ld-x86-64/pr32591-1d.d b/ld/testsuite/ld-x86-64/pr32591-1d.d
new file mode 100644
index 00000000000..36b0853c9ad
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr32591-1d.d
@@ -0,0 +1,22 @@ 
+#source: pr32591-1.s
+#as: --64 -mrelax-relocations=yes
+#ld: -melf_x86_64 -T pr32591-1d.t -z max-page-size=0x1000 -z separate-code --no-rosegment
+#objdump: -dw
+
+.*: +file format .*
+
+
+Disassembly of section .text.foo:
+
+0000000000002000 <_foo>:
+    2000:	c7 c0 00 10 00 80    	mov    \$0x80001000,%eax
+    2006:	48 03 05 f3 1f 00 00 	add    0x1ff3\(%rip\),%rax        # 4000 <_start\+0x1000>
+    200d:	48 81 c0 ff ff ff 7f 	add    \$0x7fffffff,%rax
+
+Disassembly of section .text:
+
+0000000000003000 <_start>:
+    3000:	c7 c0 0d 10 00 80    	mov    \$0x8000100d,%eax
+    3006:	48 8b 05 fb 0f 00 00 	mov    0xffb\(%rip\),%rax        # 4008 <_start\+0x1008>
+    300d:	48 8b 05 f4 0f 00 00 	mov    0xff4\(%rip\),%rax        # 4008 <_start\+0x1008>
+    3014:	c7 c0 0d 10 00 80    	mov    \$0x8000100d,%eax
diff --git a/ld/testsuite/ld-x86-64/pr32591-1d.t b/ld/testsuite/ld-x86-64/pr32591-1d.t
new file mode 100644
index 00000000000..accfd804f86
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr32591-1d.t
@@ -0,0 +1,7 @@ 
+SECTIONS {
+  .text.foo 0x02000 : { *(.text.foo) }
+  .text 0x3000 : { *(.text) }
+  .got 0x4000 : { *(.got) }
+  foo 0x7fffffff : { *(foo) }
+  data 0x80001000 : { *(data) }
+}
diff --git a/ld/testsuite/ld-x86-64/pr32591-2-x32.d b/ld/testsuite/ld-x86-64/pr32591-2-x32.d
new file mode 100644
index 00000000000..765b3793b4d
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr32591-2-x32.d
@@ -0,0 +1,12 @@ 
+#source: pr32591-2.s
+#as: --x32 -mrelax-relocations=yes
+#ld: -melf32_x86_64 -T pr32591-2.t -z max-page-size=0x1000 -z separate-code --no-rosegment
+#objdump: -dw
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+ffff0000 <_start>:
+ffff0000:	40 c7 c0 08 00 ff ff 	rex mov \$0xffff0008,%eax
diff --git a/ld/testsuite/ld-x86-64/pr32591-2.d b/ld/testsuite/ld-x86-64/pr32591-2.d
new file mode 100644
index 00000000000..dccf546db81
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr32591-2.d
@@ -0,0 +1,12 @@ 
+#source: pr32591-2.s
+#as: --64 -mrelax-relocations=yes
+#ld: -melf_x86_64 -T pr32591-2.t -z max-page-size=0x1000 -z separate-code --no-rosegment
+#objdump: -dw
+
+.*: +file format .*
+
+
+Disassembly of section .text:
+
+00000000ffff0000 <_start>:
+    ffff0000:	48 8b 05 09 00 00 00 	mov    0x9\(%rip\),%rax        # ffff0010 <__stack_chk_guard\+0x8>
diff --git a/ld/testsuite/ld-x86-64/pr32591-2.s b/ld/testsuite/ld-x86-64/pr32591-2.s
new file mode 100644
index 00000000000..252d331248e
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr32591-2.s
@@ -0,0 +1,13 @@ 
+	.text
+	.p2align 4
+	.globl	_start
+	.type	_start, @function
+_start:
+	movq	__stack_chk_guard@GOTPCREL(%rip), %rax
+	.globl	__stack_chk_guard
+	.section	.rodata
+	.align 8
+	.type	__stack_chk_guard, @object
+__stack_chk_guard:
+	.dc.a -1
+	.section	.note.GNU-stack,"",@progbits
diff --git a/ld/testsuite/ld-x86-64/pr32591-2.t b/ld/testsuite/ld-x86-64/pr32591-2.t
new file mode 100644
index 00000000000..0b1109c38c7
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/pr32591-2.t
@@ -0,0 +1,4 @@ 
+SECTIONS {
+  .text  0xffff0000 : { *(.text*) }
+  .rodata : AT(ADDR(".rodata") - 0xffff0000) { *(.rodata*) }
+}
diff --git a/ld/testsuite/ld-x86-64/x86-64.exp b/ld/testsuite/ld-x86-64/x86-64.exp
index bde29fbd571..d03e9e346cc 100644
--- a/ld/testsuite/ld-x86-64/x86-64.exp
+++ b/ld/testsuite/ld-x86-64/x86-64.exp
@@ -544,6 +544,16 @@  run_dump_test "tlsdesc4"
 run_dump_test "tlsdesc5"
 run_dump_test "pr32191"
 run_dump_test "pr32191-x32"
+run_dump_test "pr32591-1a"
+run_dump_test "pr32591-1a-x32"
+run_dump_test "pr32591-1b"
+run_dump_test "pr32591-1b-x32"
+run_dump_test "pr32591-1c"
+run_dump_test "pr32591-1c-x32"
+run_dump_test "pr32591-1d"
+run_dump_test "pr32591-1d-x32"
+run_dump_test "pr32591-2"
+run_dump_test "pr32591-2-x32"
 
 if { ![skip_sframe_tests] } {
     run_dump_test "sframe-simple-1"