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
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
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.
@@ -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)
{
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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