aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2022-01-28 09:46:13 +1030
committerAlan Modra <amodra@gmail.com>2022-02-05 17:41:08 +1030
commitd3ec1c514429b8c695974e785e47af8f71c388a2 (patch)
tree1f0626931250f5666d7971a07130122a0cf2f8fc
parentUpdate PowerPC64 symtocbase test (diff)
downloadbinutils-gdb-d3ec1c514429b8c695974e785e47af8f71c388a2.tar.gz
binutils-gdb-d3ec1c514429b8c695974e785e47af8f71c388a2.tar.bz2
binutils-gdb-d3ec1c514429b8c695974e785e47af8f71c388a2.zip
PR28827, assertion building LLVM 9 on powerpc64le-linux-gnu
The assertion is this one in ppc_build_one_stub BFD_ASSERT (stub_entry->stub_offset >= stub_entry->group->stub_sec->size); It is checking that a stub doesn't overwrite the tail of a previous stub, so not something trivial. Normally, stub sizing iterates until no stubs are added, detected by no change in stub section size. Iteration also continues if no stubs are added but one or more stubs increases in size, which also can be detected by a change in stub section size. But there is a pathological case where stub section sizing decreases one iteration then increases the next. To handle that situation, stub sizing also stops at more than STUB_SHRINK_ITER (20) iterations when calculated stub section size is smaller. The previous larger size is kept for the actual layout (so that building the stubs, which behaves like another iteration of stub sizing, will see the stub section sizes shrink). The problem with that stopping condition is that it assumes that stub sizing is only affected by addresses external to the stub sections, which isn't always true. This patch fixes that by also keeping larger individual stub_offset addresses past STUB_SHRINK_ITER. It also catches a further pathological case where one stub shrinks and another expands in such a way that no stub section size change is seen. PR 28827 * elf64-ppc.c (struct ppc_link_hash_table): Add stub_changed. (STUB_SHRINK_ITER): Move earlier in file. (ppc_size_one_stub): Detect any change in stub_offset. Keep larger one if past STUB_SHRINK_ITER. (ppc64_elf_size_stubs): Iterate on stub_changed too. (cherry picked from commit 0441f94fba61998b4bd18487aacf70a672df099c) Re: PR28827, assertion building LLVM 9 on powerpc64le-linux-gnu The previous patch wasn't quite correct. The size and padding depends on offset used in the current iteration, and if we're fudging the offset past STUB_SHRINK_ITER then we'd better use that offset. We can't have plt_stub_pad using stub_sec->size as the offset. PR 28827 * elf64-ppc.c (plt_stub_pad): Add stub_off param. (ppc_size_one_stub): Set up stub_offset to value used in this iteration before sizing the stub. Adjust plt_stub_pad calls. (cherry picked from commit 2405fc4016feadea33cb747d5654514f62b74ff4) Re: PR28827, assertion building LLVM 9 on powerpc64le-linux-gnu In trying to find a testcase for PR28827, I managed to hit a linker error in bfd_set_section_contents with a .branch_lt input section being too large for the output .branch_lt. bfd/ PR 28827 * elf64-ppc.c (ppc64_elf_size_stubs): Set section size to maxsize past STUB_SHRINK_ITER before laying out. Remove now unnecessary conditional setting of maxsize at start of loop. ld/ * testsuite/ld-powerpc/pr28827-2.d, * testsuite/ld-powerpc/pr28827-2.lnk, * testsuite/ld-powerpc/pr28827-2.s: New test. * testsuite/ld-powerpc/powerpc.exp: Run it. (cherry picked from commit 9ff8aa7d418bc508dbd429576b93e30ed9dc5891)
-rw-r--r--bfd/elf64-ppc.c96
-rw-r--r--ld/testsuite/ld-powerpc/powerpc.exp1
-rw-r--r--ld/testsuite/ld-powerpc/pr28827-2.d48
-rw-r--r--ld/testsuite/ld-powerpc/pr28827-2.lnk9
-rw-r--r--ld/testsuite/ld-powerpc/pr28827-2.s15
5 files changed, 131 insertions, 38 deletions
diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index 05d2d9f91d3..a944703db10 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -3296,6 +3296,9 @@ struct ppc_link_hash_table
/* Set if inline plt calls should be converted to direct calls. */
unsigned int can_convert_all_inline_plt:1;
+ /* Set if a stub_offset changed. */
+ unsigned int stub_changed:1;
+
/* Set on error. */
unsigned int stub_error:1;
@@ -3313,6 +3316,13 @@ struct ppc_link_hash_table
/* Incremented every time we size stubs. */
unsigned int stub_iteration;
+
+/* After 20 iterations of stub sizing we no longer allow stubs to
+ shrink. This is to break out of a pathological case where adding
+ stubs or increasing their size on one iteration decreases section
+ gaps (perhaps due to alignment), which then results in smaller
+ stubs on the next iteration. */
+#define STUB_SHRINK_ITER 20
};
/* Rename some of the generic section flags to better document how they
@@ -11164,12 +11174,12 @@ plt_stub_size (struct ppc_link_hash_table *htab,
static inline unsigned int
plt_stub_pad (struct ppc_link_hash_table *htab,
struct ppc_stub_hash_entry *stub_entry,
+ bfd_vma stub_off,
bfd_vma plt_off,
unsigned int odd)
{
int stub_align;
unsigned stub_size;
- bfd_vma stub_off = stub_entry->group->stub_sec->size;
if (htab->params->plt_stub_align >= 0)
{
@@ -12164,6 +12174,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
asection *plt;
bfd_vma targ, off, r2off;
unsigned int size, extra, lr_used, delta, odd;
+ bfd_vma stub_offset;
/* Massage our args to the form they really have. */
stub_entry = (struct ppc_stub_hash_entry *) gen_entry;
@@ -12193,7 +12204,10 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
stub_entry->target_section);
/* Make a note of the offset within the stubs for this entry. */
- stub_entry->stub_offset = stub_entry->group->stub_sec->size;
+ stub_offset = stub_entry->group->stub_sec->size;
+ if (htab->stub_iteration > STUB_SHRINK_ITER
+ && stub_entry->stub_offset > stub_offset)
+ stub_offset = stub_entry->stub_offset;
if (stub_entry->h != NULL
&& stub_entry->h->save_res
@@ -12223,7 +12237,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
+ stub_entry->target_section->output_offset
+ stub_entry->target_section->output_section->vma);
targ += PPC64_LOCAL_ENTRY_OFFSET (stub_entry->other);
- off = (stub_entry->stub_offset
+ off = (stub_offset
+ stub_entry->group->stub_sec->output_offset
+ stub_entry->group->stub_sec->output_section->vma);
@@ -12322,7 +12336,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
}
else if (stub_entry->type.main == ppc_stub_long_branch)
{
- off = (stub_entry->stub_offset
+ off = (stub_offset
+ stub_entry->group->stub_sec->output_offset
+ stub_entry->group->stub_sec->output_section->vma);
size = 0;
@@ -12361,7 +12375,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
{
/* After the bcl, lr has been modified so we need to emit
.eh_frame info saying the return address is in r12. */
- lr_used = stub_entry->stub_offset + 8;
+ lr_used = stub_offset + 8;
if (stub_entry->type.r2save)
lr_used += 4;
/* The eh_frame info will consist of a DW_CFA_advance_loc or
@@ -12410,7 +12424,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
plt = htab->pltlocal;
}
targ += plt->output_offset + plt->output_section->vma;
- off = (stub_entry->stub_offset
+ off = (stub_offset
+ stub_entry->group->stub_sec->output_offset
+ stub_entry->group->stub_sec->output_section->vma
+ lr_used);
@@ -12419,10 +12433,9 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
if (htab->params->plt_stub_align != 0)
{
- unsigned pad = plt_stub_pad (htab, stub_entry, off, odd);
+ unsigned pad = plt_stub_pad (htab, stub_entry, stub_offset, off, odd);
- stub_entry->group->stub_sec->size += pad;
- stub_entry->stub_offset = stub_entry->group->stub_sec->size;
+ stub_offset += pad;
off -= pad;
odd ^= pad & 4;
}
@@ -12444,7 +12457,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
{
/* After the bcl, lr has been modified so we need to emit
.eh_frame info saying the return address is in r12. */
- lr_used += stub_entry->stub_offset + 8;
+ lr_used += stub_offset + 8;
/* The eh_frame info will consist of a DW_CFA_advance_loc or
variant, DW_CFA_register, 65, 12, DW_CFA_advance_loc+2,
DW_CFA_restore_extended 65. */
@@ -12458,20 +12471,18 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
{
if (!htab->params->no_tls_get_addr_regsave)
{
- unsigned int cfa_updt = stub_entry->stub_offset + 18 * 4;
+ unsigned int cfa_updt = stub_offset + 18 * 4;
delta = cfa_updt - stub_entry->group->lr_restore;
stub_entry->group->eh_size += eh_advance_size (delta);
stub_entry->group->eh_size += htab->opd_abi ? 36 : 35;
- stub_entry->group->lr_restore
- = stub_entry->stub_offset + size - 4;
+ stub_entry->group->lr_restore = stub_offset + size - 4;
}
else if (stub_entry->type.r2save)
{
- lr_used = stub_entry->stub_offset + size - 20;
+ lr_used = stub_offset + size - 20;
delta = lr_used - stub_entry->group->lr_restore;
stub_entry->group->eh_size += eh_advance_size (delta) + 6;
- stub_entry->group->lr_restore
- = stub_entry->stub_offset + size - 4;
+ stub_entry->group->lr_restore = stub_offset + size - 4;
}
}
}
@@ -12496,10 +12507,9 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
if (htab->params->plt_stub_align != 0)
{
- unsigned pad = plt_stub_pad (htab, stub_entry, off, 0);
+ unsigned pad = plt_stub_pad (htab, stub_entry, stub_offset, off, 0);
- stub_entry->group->stub_sec->size += pad;
- stub_entry->stub_offset = stub_entry->group->stub_sec->size;
+ stub_offset += pad;
}
if (info->emitrelocations)
@@ -12523,21 +12533,21 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
if (!htab->params->no_tls_get_addr_regsave)
{
/* Adjustments to r1 need to be described. */
- unsigned int cfa_updt = stub_entry->stub_offset + 18 * 4;
+ unsigned int cfa_updt = stub_offset + 18 * 4;
delta = cfa_updt - stub_entry->group->lr_restore;
stub_entry->group->eh_size += eh_advance_size (delta);
stub_entry->group->eh_size += htab->opd_abi ? 36 : 35;
}
else
{
- lr_used = stub_entry->stub_offset + size - 20;
+ lr_used = stub_offset + size - 20;
/* The eh_frame info will consist of a DW_CFA_advance_loc
or variant, DW_CFA_offset_externed_sf, 65, -stackoff,
DW_CFA_advance_loc+4, DW_CFA_restore_extended, 65. */
delta = lr_used - stub_entry->group->lr_restore;
stub_entry->group->eh_size += eh_advance_size (delta) + 6;
}
- stub_entry->group->lr_restore = stub_entry->stub_offset + size - 4;
+ stub_entry->group->lr_restore = stub_offset + size - 4;
}
}
else
@@ -12546,7 +12556,10 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
return false;
}
- stub_entry->group->stub_sec->size += size;
+ if (stub_entry->stub_offset != stub_offset)
+ htab->stub_changed = true;
+ stub_entry->stub_offset = stub_offset;
+ stub_entry->group->stub_sec->size = stub_offset + size;
return true;
}
@@ -13644,12 +13657,8 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
_bfd_elf_link_hash_hide_symbol (info, &htab->tga_desc_fd->elf, true);
}
-#define STUB_SHRINK_ITER 20
/* Loop until no stubs added. After iteration 20 of this loop we may
- exit on a stub section shrinking. This is to break out of a
- pathological case where adding stubs on one iteration decreases
- section gaps (perhaps due to alignment), which then requires
- fewer or smaller stubs on the next iteration. */
+ exit on a stub section shrinking. */
while (1)
{
@@ -14084,10 +14093,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
{
asection *stub_sec = group->stub_sec;
- if (htab->stub_iteration <= STUB_SHRINK_ITER
- || stub_sec->rawsize < stub_sec->size)
- /* Past STUB_SHRINK_ITER, rawsize is the max size seen. */
- stub_sec->rawsize = stub_sec->size;
+ stub_sec->rawsize = stub_sec->size;
stub_sec->size = 0;
stub_sec->reloc_count = 0;
stub_sec->flags &= ~SEC_RELOC;
@@ -14102,9 +14108,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
htab->tga_group->stub_sec->size = 24 * 4;
}
- if (htab->stub_iteration <= STUB_SHRINK_ITER
- || htab->brlt->rawsize < htab->brlt->size)
- htab->brlt->rawsize = htab->brlt->size;
+ htab->brlt->rawsize = htab->brlt->size;
htab->brlt->size = 0;
htab->brlt->reloc_count = 0;
htab->brlt->flags &= ~SEC_RELOC;
@@ -14113,12 +14117,11 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
if (htab->elf.srelrdyn != NULL)
{
- if (htab->stub_iteration <= STUB_SHRINK_ITER
- || htab->elf.srelrdyn->rawsize < htab->elf.srelrdyn->size)
- htab->elf.srelrdyn->rawsize = htab->elf.srelrdyn->size;
+ htab->elf.srelrdyn->rawsize = htab->elf.srelrdyn->size;
htab->elf.srelrdyn->size = 0;
}
+ htab->stub_changed = false;
bfd_hash_traverse (&htab->stub_hash_table, ppc_size_one_stub, info);
for (group = htab->group; group != NULL; group = group->next)
@@ -14215,6 +14218,8 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
break;
if (group == NULL
+ && (!htab->stub_changed
+ || htab->stub_iteration > STUB_SHRINK_ITER)
&& (htab->brlt->rawsize == htab->brlt->size
|| (htab->stub_iteration > STUB_SHRINK_ITER
&& htab->brlt->rawsize > htab->brlt->size))
@@ -14228,6 +14233,21 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
|| htab->stub_iteration > 1))
break;
+ if (htab->stub_iteration > STUB_SHRINK_ITER)
+ {
+ for (group = htab->group; group != NULL; group = group->next)
+ if (group->stub_sec != NULL
+ && group->stub_sec->size < group->stub_sec->rawsize)
+ group->stub_sec->size = group->stub_sec->rawsize;
+
+ if (htab->brlt->size < htab->brlt->rawsize)
+ htab->brlt->size = htab->brlt->rawsize;
+
+ if (htab->elf.srelrdyn != NULL
+ && htab->elf.srelrdyn->size < htab->elf.srelrdyn->rawsize)
+ htab->elf.srelrdyn->size = htab->elf.srelrdyn->rawsize;
+ }
+
/* Ask the linker to do its stuff. */
(*htab->params->layout_sections_again) ();
}
diff --git a/ld/testsuite/ld-powerpc/powerpc.exp b/ld/testsuite/ld-powerpc/powerpc.exp
index 3eb707f42ea..3d738f5f93c 100644
--- a/ld/testsuite/ld-powerpc/powerpc.exp
+++ b/ld/testsuite/ld-powerpc/powerpc.exp
@@ -445,6 +445,7 @@ if [ supports_ppc64 ] then {
run_dump_test "tlsie"
run_dump_test "non-contiguous-powerpc64"
run_dump_test "tprel"
+ run_dump_test "pr28827-2"
}
run_dump_test "localgot"
diff --git a/ld/testsuite/ld-powerpc/pr28827-2.d b/ld/testsuite/ld-powerpc/pr28827-2.d
new file mode 100644
index 00000000000..8da819d822a
--- /dev/null
+++ b/ld/testsuite/ld-powerpc/pr28827-2.d
@@ -0,0 +1,48 @@
+#as: -a64
+#ld: -melf64ppc --plt-align=0 -T pr28827-2.lnk
+#objdump: -dr
+
+.*: file format .*
+
+Disassembly of section \.text:
+
+.*:
+.*: (49 ff ff f0|f0 ff ff 49) b .* <far1>
+ \.\.\.
+
+.* <.*\.plt_branch\..*>:
+.*: (e9 82 80 28|28 80 82 e9) ld r12,-32728\(r2\)
+.*: (7d 89 03 a6|a6 03 89 7d) mtctr r12
+.*: (4e 80 04 20|20 04 80 4e) bctr
+
+.* <_start>:
+.*: (49 ff ff d8|d8 ff ff 49) b .* <far1>
+.*: (4b ff ff f0|f0 ff ff 4b) b .*
+
+Disassembly of section \.far1:
+
+.*:
+.*: (4a 00 00 38|38 00 00 4a) b .* <_start>
+
+.* <.*\.long_branch\..*>:
+.*: (49 ff ff f4|f4 ff ff 49) b .* <far2>
+ \.\.\.
+
+.* <far1>:
+.*: (41 82 ff f4|f4 ff 82 41) beq .*
+.*: (4a 00 00 24|24 00 00 4a) b .* <_start>
+
+Disassembly of section \.far2:
+
+.*:
+.*: (e9 82 80 20|20 80 82 e9) ld r12,-32736\(r2\)
+.*: (7d 89 03 a6|a6 03 89 7d) mtctr r12
+.*: (4e 80 04 20|20 04 80 4e) bctr
+
+.*:
+.*: (4a 00 00 24|24 00 00 4a) b .* <far1>
+ \.\.\.
+
+.* <far2>:
+.*: (40 82 ff f4|f4 ff 82 40) bne .*
+.*: (4b ff ff e4|e4 ff ff 4b) b .*
diff --git a/ld/testsuite/ld-powerpc/pr28827-2.lnk b/ld/testsuite/ld-powerpc/pr28827-2.lnk
new file mode 100644
index 00000000000..61a8ca269f3
--- /dev/null
+++ b/ld/testsuite/ld-powerpc/pr28827-2.lnk
@@ -0,0 +1,9 @@
+SECTIONS
+{
+ . = SIZEOF_HEADERS;
+ .text : { *(.text) }
+ . = . + 0x2000000 + 32 - 4 * SIZEOF (.text);
+ .far1 : { *(.far1) }
+ . = . + 0x2000000 + 32 - 4 * SIZEOF (.far1);
+ .far2 : { *(.far2) }
+}
diff --git a/ld/testsuite/ld-powerpc/pr28827-2.s b/ld/testsuite/ld-powerpc/pr28827-2.s
new file mode 100644
index 00000000000..a628d6d09f9
--- /dev/null
+++ b/ld/testsuite/ld-powerpc/pr28827-2.s
@@ -0,0 +1,15 @@
+ .globl _start
+ .text
+_start:
+ b far1
+ b far2
+
+ .section .far1,"ax",@progbits
+far1:
+ beq far2
+ b _start
+
+ .section .far2,"ax",@progbits
+far2:
+ bne far1
+ b _start