alpha: don't merge .got subsections once GPREL relocs exist

Message ID 20260926165132.1054779-1-mattst88@gmail.com
State New
Headers
Series alpha: don't merge .got subsections once GPREL relocs exist |

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

Matt Turner Sept. 26, 2026, 4:51 p.m. UTC
  Commit d1c109de72f stopped elf64_alpha_relax_section merging .got
subsections after the first relaxation pass, since the second pass
turns GOT loads into GPREL16 against the subsection's gp and a later
merge moves that gp.  It tested relax_pass == 0.

But ldelf_map_segments reruns lang_relax_sections, starting again from
pass 0, when it has to redo the layout.  Merging can then still happen
after GPREL16 relocs exist, giving

  relocation truncated to fit: GPREL16 against symbol `...'

Remember that pass 1 has been reached and don't merge after that.

	PR ld/5276
bfd/
	* elf64-alpha.c (struct alpha_elf_link_hash_table): Add
	no_got_merge.
	(elf64_alpha_relax_section): Set it once relax_pass is nonzero,
	and don't merge .got subsections after that.
ld/
	* testsuite/ld-alpha/got-merge-gprel-a.s: New.
	* testsuite/ld-alpha/got-merge-gprel-b.s: New.
	* testsuite/ld-alpha/got-merge-gprel.d: New.
---
 bfd/elf64-alpha.c                         | 14 ++++++--
 ld/testsuite/ld-alpha/got-merge-gprel-a.s | 20 +++++++++++
 ld/testsuite/ld-alpha/got-merge-gprel-b.s | 41 +++++++++++++++++++++++
 ld/testsuite/ld-alpha/got-merge-gprel.d   | 10 ++++++
 4 files changed, 82 insertions(+), 3 deletions(-)
 create mode 100644 ld/testsuite/ld-alpha/got-merge-gprel-a.s
 create mode 100644 ld/testsuite/ld-alpha/got-merge-gprel-b.s
 create mode 100644 ld/testsuite/ld-alpha/got-merge-gprel.d
  

Comments

Alan Modra Oct. 6, 2026, 11:44 p.m. UTC | #1
On Sat, Sep 26, 2026 at 12:51:32PM -0400, Matt Turner wrote:
> Commit d1c109de72f stopped elf64_alpha_relax_section merging .got
> subsections after the first relaxation pass, since the second pass
> turns GOT loads into GPREL16 against the subsection's gp and a later
> merge moves that gp.  It tested relax_pass == 0.
> 
> But ldelf_map_segments reruns lang_relax_sections, starting again from
> pass 0, when it has to redo the layout.  Merging can then still happen
> after GPREL16 relocs exist, giving
> 
>   relocation truncated to fit: GPREL16 against symbol `...'
> 
> Remember that pass 1 has been reached and don't merge after that.
> 
> 	PR ld/5276
> bfd/
> 	* elf64-alpha.c (struct alpha_elf_link_hash_table): Add
> 	no_got_merge.
> 	(elf64_alpha_relax_section): Set it once relax_pass is nonzero,
> 	and don't merge .got subsections after that.
> ld/
> 	* testsuite/ld-alpha/got-merge-gprel-a.s: New.
> 	* testsuite/ld-alpha/got-merge-gprel-b.s: New.
> 	* testsuite/ld-alpha/got-merge-gprel.d: New.

Applied, thanks.
  

Patch

diff --git ./bfd/elf64-alpha.c ./bfd/elf64-alpha.c
index d8f1a3bbf48..a4f4989abb6 100644
--- ./bfd/elf64-alpha.c
+++ ./bfd/elf64-alpha.c
@@ -202,6 +202,11 @@  struct alpha_elf_link_hash_table
      should be regenerated if this doesn't match.  */
   int relax_trip;
 
+  /* True once relaxation has reached its second pass.  ld may rerun
+     relaxation from pass 0, but .got subsections must not be merged
+     after GPREL relocs exist.  */
+  bool no_got_merge;
+
   /* The part of the size of .rela.iplt that is due to data references to
      an IFUNC, counted once the sections that survive into the output are
      known.  The rest comes from got entries and is recomputed whenever the
@@ -4051,6 +4056,9 @@  elf64_alpha_relax_section (bfd *abfd, asection *sec,
   BFD_ASSERT (is_alpha_elf (abfd));
   relax_pass = link_info->relax_pass;
 
+  if (relax_pass != 0)
+    htab->no_got_merge = true;
+
   /* Make sure our GOT and PLT tables are up-to-date.  */
   if (htab->relax_trip != link_info->relax_trip)
     {
@@ -4058,10 +4066,10 @@  elf64_alpha_relax_section (bfd *abfd, asection *sec,
 
       /* This should never fail after the initial round, since the only error
 	 is GOT overflow, and relaxation only shrinks the table overall.
-	 However, we may only merge got sections during the first pass.  If
-	 we merge sections after we've created GPREL relocs, the GP for the
+	 However, we may only merge got sections before GPREL relocs have
+	 been created.  If we merge sections afterwards, the GP for the
 	 merged section backs up which may put the relocs out of range.  */
-      if (!elf64_alpha_size_got_sections (link_info, relax_pass == 0))
+      if (!elf64_alpha_size_got_sections (link_info, !htab->no_got_merge))
 	abort ();
       if (elf_hash_table (link_info)->dynamic_sections_created)
 	{
diff --git ./ld/testsuite/ld-alpha/got-merge-gprel-a.s ./ld/testsuite/ld-alpha/got-merge-gprel-a.s
new file mode 100644
index 00000000000..d6620b59753
--- /dev/null
+++ ./ld/testsuite/ld-alpha/got-merge-gprel-a.s
@@ -0,0 +1,20 @@ 
+/* 5000 got entries (40000 bytes, the first .got subsection) against a
+   symbol too far from any gp for relaxation to remove them.  */
+
+	.text
+	.globl	afunc
+	.ent	afunc
+afunc:
+	ldgp	$29,0($27)
+	.prologue 1
+	.irpc	w,01234
+	.irpc	x,0123456789
+	.irpc	y,0123456789
+	.irpc	z,0123456789
+	ldq	$16,far+(1\w\x\y\z-10000)*8($29)	!literal
+	.endr
+	.endr
+	.endr
+	.endr
+	ret	$31,($26),1
+	.end	afunc
diff --git ./ld/testsuite/ld-alpha/got-merge-gprel-b.s ./ld/testsuite/ld-alpha/got-merge-gprel-b.s
new file mode 100644
index 00000000000..423e2ea0f87
--- /dev/null
+++ ./ld/testsuite/ld-alpha/got-merge-gprel-b.s
@@ -0,0 +1,41 @@ 
+/* 3000 more got entries against far, and 1000 against near in .sbss
+   just past the .got.  With got-merge-gprel-a.s this exceeds
+   MAX_GOT_SIZE, so this object gets its own .got subsection.  Once the
+   near loads are relaxed to GPREL16 the two would fit in one, but
+   merging then would move this object's gp down by 40000 bytes.  */
+
+	.text
+	.globl	_start
+	.ent	_start
+_start:
+	ldgp	$29,0($27)
+	.prologue 1
+	.irpc	w,567
+	.irpc	x,0123456789
+	.irpc	y,0123456789
+	.irpc	z,0123456789
+	ldq	$16,far+(1\w\x\y\z-10000)*8($29)	!literal
+	.endr
+	.endr
+	.endr
+	.endr
+	.irpc	x,0123456789
+	.irpc	y,0123456789
+	.irpc	z,0123456789
+	ldq	$16,near+(1\x\y\z-1000)*8($29)	!literal
+	.endr
+	.endr
+	.endr
+	ret	$31,($26),1
+	.end	_start
+
+	.section .sbss,"aw",@nobits
+	.globl	near
+near:
+	.skip	8000
+
+	.bss
+	.skip	0x200000
+	.globl	far
+far:
+	.skip	64000
diff --git ./ld/testsuite/ld-alpha/got-merge-gprel.d ./ld/testsuite/ld-alpha/got-merge-gprel.d
new file mode 100644
index 00000000000..206a15173d0
--- /dev/null
+++ ./ld/testsuite/ld-alpha/got-merge-gprel.d
@@ -0,0 +1,10 @@ 
+#source: got-merge-gprel-a.s
+#source: got-merge-gprel-b.s
+#ld: -relax -melf64alpha
+#readelf: -SW
+
+# The link must not fail with "relocation truncated to fit: GPREL16", and
+# the .got holds only the 8000 entries against far.
+#...
+ +\[ *[0-9]+\] \.got +PROGBITS +[0-9a-f]+ +[0-9a-f]+ 00fa00 .*
+#pass