diff options
-rw-r--r-- | bfd/elf64-ppc.c | 96 | ||||
-rw-r--r-- | ld/testsuite/ld-powerpc/powerpc.exp | 1 | ||||
-rw-r--r-- | ld/testsuite/ld-powerpc/pr28827-2.d | 48 | ||||
-rw-r--r-- | ld/testsuite/ld-powerpc/pr28827-2.lnk | 9 | ||||
-rw-r--r-- | ld/testsuite/ld-powerpc/pr28827-2.s | 15 |
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 |