summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 05d2d9f91d..a944703db1 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 3eb707f42e..3d738f5f93 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 0000000000..8da819d822
--- /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 0000000000..61a8ca269f
--- /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 0000000000..a628d6d09f
--- /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