diff options
author | Alan Modra <amodra@gmail.com> | 2020-07-17 16:47:28 +0930 |
---|---|---|
committer | Alan Modra <amodra@gmail.com> | 2020-07-19 12:27:47 +0930 |
commit | e10a07b32dc1faed25b5bbcbbc47d68e2ff537b9 (patch) | |
tree | 1eeb63b4cdb332eb53f3e8f4ae91773bda69a008 /bfd | |
parent | 6095ca52575d8e49003a3685453bc5bd0d929f1e (diff) | |
download | binutils-e10a07b32dc1faed25b5bbcbbc47d68e2ff537b9.zip binutils-e10a07b32dc1faed25b5bbcbbc47d68e2ff537b9.tar.gz binutils-e10a07b32dc1faed25b5bbcbbc47d68e2ff537b9.tar.bz2 |
Power10 stub selection
This patch better supports mixing of power10 and non-power10 code,
as might be seen in a cpu-optimized library using ifuncs to select
functions optimized for a given cpu. Using -Wl,--no-power10-stubs
isn't that good in this situation since non-power10 notoc stubs are
slower and larger than the power10 variants, which you'd like to use
on power10 code paths.
With this change, power10 pc-relative code that makes calls marked
@notoc uses power10 stubs if stubs are necessary, and other calls use
non-power10 instructions in stubs. This will mean that if gcc is
generating code for -mcpu=power10 but with pc-rel disabled then you'll
get the older stubs even on power10 (unless you force with
-Wl,--power10-stubs). That shouldn't be too big a problem: stubs that
use r2 are reasonable. It's just the ones that set up addressing
using "mflr 12; bcl 20,31,.+4; mflr 11; mtlr 12" that should be
avoided if possible.
bfd/
* elf64-ppc.c (struct ppc_link_hash_table): Add has_power10_relocs.
(select_alt_stub): New function.
(ppc_get_stub_entry): Use it here.
(ppc64_elf_check_relocs): Set had_power10_relocs rather than
power10_stubs.
(ppc64_elf_size_stubs): Clear power10_stubs here instead. Don't
merge notoc stubs with other varieties when power10_stubs is "auto".
Instead dup the stub hash table entry.
(plt_stub_size, ppc_build_one_stub, ppc_size_one_stub): Adjust
tests of power10_stubs.
ld/
* emultempl/ppc64elf.em (power10-stubs): Accept optional "auto" arg.
* ld.texi (power10-stubs): Update.
* testsuite/ld-powerpc/callstub-1.d: Force --power10-stubs.
* testsuite/ld-powerpc/callstub-2.d: Relax branch offset comparison.
* testsuite/ld-powerpc/callstub-4.d: New test.
* testsuite/ld-powerpc/notoc.d: Force --no-power10-stubs.
* testsuite/ld-powerpc/notoc3.d,
* testsuite/ld-powerpc/notoc3.s,
* testsuite/ld-powerpc/notoc3.wf: New test.
* testsuite/ld-powerpc/powerpc.exp: Run new tests. Pass
--no-power10-stubs for notoc link.
Diffstat (limited to 'bfd')
-rw-r--r-- | bfd/ChangeLog | 13 | ||||
-rw-r--r-- | bfd/elf64-ppc.c | 101 |
2 files changed, 102 insertions, 12 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 6532c53..43326b5 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,16 @@ +2020-07-19 Alan Modra <amodra@gmail.com> + + * elf64-ppc.c (struct ppc_link_hash_table): Add has_power10_relocs. + (select_alt_stub): New function. + (ppc_get_stub_entry): Use it here. + (ppc64_elf_check_relocs): Set had_power10_relocs rather than + power10_stubs. + (ppc64_elf_size_stubs): Clear power10_stubs here instead. Don't + merge notoc stubs with other varieties when power10_stubs is "auto". + Instead dup the stub hash table entry. + (plt_stub_size, ppc_build_one_stub, ppc_size_one_stub): Adjust + tests of power10_stubs. + 2020-07-15 Alan Modra <amodra@gmail.com> PR 26239 diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index e54f561..8d622fb 100644 --- a/bfd/elf64-ppc.c +++ b/bfd/elf64-ppc.c @@ -3245,6 +3245,9 @@ struct ppc_link_hash_table /* Whether calls are made via the PLT from NOTOC functions. */ unsigned int notoc_plt:1; + /* Whether any code linked seems to be Power10. */ + unsigned int has_power10_relocs:1; + /* Incremented every time we size stubs. */ unsigned int stub_iteration; @@ -3679,6 +3682,37 @@ ppc_stub_name (const asection *input_section, return stub_name; } +/* If mixing power10 with non-power10 code and --power10-stubs is not + specified (or is auto) then calls using @notoc relocations that + need a stub will utilize power10 instructions in the stub, and + calls without @notoc relocations will not use power10 instructions. + The two classes of stubs are stored in separate stub_hash_table + entries having the same key string. The two entries will always be + adjacent on entry->root.next chain, even if hash table resizing + occurs. This function selects the correct entry to use. */ + +static struct ppc_stub_hash_entry * +select_alt_stub (struct ppc_stub_hash_entry *entry, bfd_boolean notoc) +{ + bfd_boolean have_notoc; + + have_notoc = (entry->stub_type == ppc_stub_plt_call_notoc + || entry->stub_type == ppc_stub_plt_branch_notoc + || entry->stub_type == ppc_stub_long_branch_notoc); + + if (have_notoc != notoc) + { + const char *stub_name = entry->root.string; + + entry = (struct ppc_stub_hash_entry *) entry->root.next; + if (entry != NULL + && entry->root.string != stub_name) + entry = NULL; + } + + return entry; +} + /* Look up an entry in the stub hash. Stub entries are cached because creating the stub name takes a bit of time. */ @@ -3723,6 +3757,13 @@ ppc_get_stub_entry (const asection *input_section, free (stub_name); } + if (stub_entry != NULL && htab->params->power10_stubs == -1) + { + bfd_boolean notoc = ELF64_R_TYPE (rel->r_info) == R_PPC64_REL24_NOTOC; + + stub_entry = select_alt_stub (stub_entry, notoc); + } + return stub_entry; } @@ -4599,8 +4640,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, case R_PPC64_PLT_PCREL34: case R_PPC64_PLT_PCREL34_NOTOC: case R_PPC64_PCREL28: - if (htab->params->power10_stubs < 0) - htab->params->power10_stubs = 1; + htab->has_power10_relocs = 1; break; default: break; @@ -10761,7 +10801,7 @@ plt_stub_size (struct ppc_link_hash_table *htab, if (stub_entry->stub_type >= ppc_stub_plt_call_notoc) { - if (htab->params->power10_stubs > 0) + if (htab->params->power10_stubs != 0) { bfd_vma start = (stub_entry->stub_offset + stub_entry->group->stub_sec->output_offset @@ -11602,7 +11642,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) relp = p; num_rel = 0; - if (htab->params->power10_stubs > 0) + if (htab->params->power10_stubs != 0) { bfd_boolean load = stub_entry->stub_type >= ppc_stub_plt_call_notoc; p = build_power10_offset (htab->params->stub_bfd, p, off, odd, load); @@ -11641,7 +11681,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) if (info->emitrelocations) { bfd_vma roff = relp - stub_entry->group->stub_sec->contents; - if (htab->params->power10_stubs > 0) + if (htab->params->power10_stubs != 0) num_rel += num_relocs_for_power10_offset (off, odd); else { @@ -11651,7 +11691,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) r = get_relocs (stub_entry->group->stub_sec, num_rel); if (r == NULL) return FALSE; - if (htab->params->power10_stubs > 0) + if (htab->params->power10_stubs != 0) r = emit_relocs_for_power10_offset (info, r, roff, targ, off, odd); else r = emit_relocs_for_offset (info, r, roff, targ, off); @@ -11669,7 +11709,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) } } - if (htab->params->power10_stubs <= 0 + if (htab->params->power10_stubs == 0 && htab->glink_eh_frame != NULL && htab->glink_eh_frame->size != 0) { @@ -12017,7 +12057,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) if (info->emitrelocations) { unsigned int num_rel; - if (htab->params->power10_stubs > 0) + if (htab->params->power10_stubs != 0) num_rel = num_relocs_for_power10_offset (off, odd); else num_rel = num_relocs_for_offset (off - 8); @@ -12025,7 +12065,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) stub_entry->group->stub_sec->flags |= SEC_RELOC; } - if (htab->params->power10_stubs > 0) + if (htab->params->power10_stubs != 0) extra = size_power10_offset (off, odd); else extra = size_offset (off - 8); @@ -12036,7 +12076,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) calculated. */ off -= extra; - if (htab->params->power10_stubs <= 0) + if (htab->params->power10_stubs == 0) { /* After the bcl, lr has been modified so we need to emit .eh_frame info saying the return address is in r12. */ @@ -12099,7 +12139,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) if (info->emitrelocations) { unsigned int num_rel; - if (htab->params->power10_stubs > 0) + if (htab->params->power10_stubs != 0) num_rel = num_relocs_for_power10_offset (off, odd); else num_rel = num_relocs_for_offset (off - 8); @@ -12109,7 +12149,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) size = plt_stub_size (htab, stub_entry, off); - if (htab->params->power10_stubs <= 0) + if (htab->params->power10_stubs == 0) { /* After the bcl, lr has been modified so we need to emit .eh_frame info saying the return address is in r12. */ @@ -13034,6 +13074,9 @@ ppc64_elf_size_stubs (struct bfd_link_info *info) if (htab == NULL) return FALSE; + if (htab->params->power10_stubs == -1 && !htab->has_power10_relocs) + htab->params->power10_stubs = 0; + if (htab->params->plt_thread_safe == -1 && !bfd_link_executable (info)) htab->params->plt_thread_safe = 1; if (!htab->opd_abi) @@ -13409,6 +13452,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info) if (stub_entry != NULL) { enum ppc_stub_type old_type; + /* A stub has already been created, but it may not be the required type. We shouldn't be transitioning from plt_call to long_branch @@ -13416,6 +13460,39 @@ ppc64_elf_size_stubs (struct bfd_link_info *info) upgrading from plt_call to plt_call_r2save or from long_branch to long_branch_r2off. */ free (stub_name); + if (htab->params->power10_stubs == -1) + { + /* For --power10-stubs=auto, don't merge _notoc + and other varieties of stubs. (The _both + variety won't be created.) */ + bfd_boolean notoc = r_type == R_PPC64_REL24_NOTOC; + struct ppc_stub_hash_entry *alt_stub + = select_alt_stub (stub_entry, notoc); + + if (alt_stub == NULL) + { + alt_stub = (struct ppc_stub_hash_entry *) + stub_hash_newfunc (NULL, + &htab->stub_hash_table, + stub_entry->root.string); + if (alt_stub == NULL) + { + /* xgettext:c-format */ + _bfd_error_handler + (_("%pB: cannot create stub entry %s"), + section->owner, stub_entry->root.string); + goto error_ret_free_internal; + } + *alt_stub = *stub_entry; + stub_entry->root.next = &alt_stub->root; + if (notoc) + /* Sort notoc stubs first, for no good + reason. */ + alt_stub = stub_entry; + alt_stub->stub_type = stub_type; + } + stub_entry = alt_stub; + } old_type = stub_entry->stub_type; switch (old_type) { |