aboutsummaryrefslogtreecommitdiff
path: root/bfd
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2017-05-26 10:02:29 +0930
committerAlan Modra <amodra@gmail.com>2017-06-01 22:47:32 +0930
commitf378ab099d535f5540f292fed07fcf4b1fabd314 (patch)
treefb89fdf868b869cfb0ab1be6236812c3f9718628 /bfd
parent19fb31c0060f646a9f84be1a84ed1bea04e7ed57 (diff)
downloadgdb-f378ab099d535f5540f292fed07fcf4b1fabd314.zip
gdb-f378ab099d535f5540f292fed07fcf4b1fabd314.tar.gz
gdb-f378ab099d535f5540f292fed07fcf4b1fabd314.tar.bz2
PPC64_OPT_LOCALENTRY
ELFv2 functions with localentry:0 are those with a single entry point, ie. global entry == local entry, and that have no requirement on r2 or r12, and guarantee r2 is unchanged on return. Such an external function can be called via the PLT without saving r2 or restoring it on return, avoiding a common load-hit-store for small functions. The optimization is attractive. The TOC pointer load-hit-store is a major reason why calls to small functions that need no register saves, or with shrink-wrap, no register saves on a fast path, are slow on powerpc64le. To be safe, this optimization needs ld.so support to check that the run-time matches link-time function implementation. If a function in a shared library with st_other localentry non-zero is called without saving and restoring r2, r2 will be trashed on return, leading to segfaults. For that reason the optimization does not happen for weak functions since a weak definition is a fairly solid hint that the function will likely be overridden. I'm also not enabling the optimization by default unless glibc-2.26 is detected, which should have the ld.so checks implemented. bfd/ * elf64-ppc.c (struct ppc_link_hash_table): Add has_plt_localentry0. (ppc64_elf_merge_symbol_attribute): Merge localentry bits from dynamic objects. (is_elfv2_localentry0): New function. (ppc64_elf_tls_setup): Default params->plt_localentry0. (plt_stub_size): Adjust size for tls_get_addr_opt stub. (build_tls_get_addr_stub): Use a simpler stub when r2 is not saved. (ppc64_elf_size_stubs): Leave stub_type as ppc_stub_plt_call for optimized localentry:0 stubs. (ppc64_elf_build_stubs): Save r2 in ELFv2 __glink_PLTresolve. (ppc64_elf_relocate_section): Leave nop unchanged for optimized localentry:0 stubs. (ppc64_elf_finish_dynamic_sections): Set PPC64_OPT_LOCALENTRY in DT_PPC64_OPT. * elf64-ppc.h (struct ppc64_elf_params): Add plt_localentry0. include/ * elf/ppc64.h (PPC64_OPT_LOCALENTRY): Define. ld/ * emultempl/ppc64elf.em (params): Init plt_localentry0 field. (enum ppc64_opt): New, replacing OPTION_* defines. Add OPTION_PLT_LOCALENTRY, and OPTION_NO_PLT_LOCALENTRY. (PARSE_AND_LIST_*): Support --plt-localentry and --no-plt-localentry. * testsuite/ld-powerpc/elfv2so.d: Update. * testsuite/ld-powerpc/powerpc.exp (TLS opt 5): Use --no-plt-localentry. * testsuite/ld-powerpc/tlsopt5.d: Update.
Diffstat (limited to 'bfd')
-rw-r--r--bfd/ChangeLog18
-rw-r--r--bfd/elf64-ppc.c77
-rw-r--r--bfd/elf64-ppc.h3
3 files changed, 85 insertions, 13 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index 11a474e..cb4afa9 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,3 +1,21 @@
+2017-06-01 Alan Modra <amodra@gmail.com>
+
+ * elf64-ppc.c (struct ppc_link_hash_table): Add has_plt_localentry0.
+ (ppc64_elf_merge_symbol_attribute): Merge localentry bits from
+ dynamic objects.
+ (is_elfv2_localentry0): New function.
+ (ppc64_elf_tls_setup): Default params->plt_localentry0.
+ (plt_stub_size): Adjust size for tls_get_addr_opt stub.
+ (build_tls_get_addr_stub): Use a simpler stub when r2 is not saved.
+ (ppc64_elf_size_stubs): Leave stub_type as ppc_stub_plt_call for
+ optimized localentry:0 stubs.
+ (ppc64_elf_build_stubs): Save r2 in ELFv2 __glink_PLTresolve.
+ (ppc64_elf_relocate_section): Leave nop unchanged for optimized
+ localentry:0 stubs.
+ (ppc64_elf_finish_dynamic_sections): Set PPC64_OPT_LOCALENTRY in
+ DT_PPC64_OPT.
+ * elf64-ppc.h (struct ppc64_elf_params): Add plt_localentry0.
+
2017-05-30 Casey Smith <clegg89@gmail.com>
PR ld/21523
diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index 948de12..42abb96 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -4118,6 +4118,9 @@ struct ppc_link_hash_table
unsigned int local_ifunc_resolver:1;
unsigned int maybe_local_ifunc_resolver:1;
+ /* Whether plt calls for ELFv2 localentry:0 funcs have been optimized. */
+ unsigned int has_plt_localentry0:1;
+
/* Incremented every time we size stubs. */
unsigned int stub_iteration;
@@ -5005,7 +5008,7 @@ ppc64_elf_merge_symbol_attribute (struct elf_link_hash_entry *h,
bfd_boolean definition,
bfd_boolean dynamic)
{
- if (definition && !dynamic)
+ if (definition && (!dynamic || !h->def_regular))
h->other = ((isym->st_other & ~ELF_ST_VISIBILITY (-1))
| ELF_ST_VISIBILITY (h->other));
}
@@ -6318,6 +6321,21 @@ ppc64_elf_maybe_function_sym (const asymbol *sym, asection *sec,
return size;
}
+/* Return true if symbol is a strong function defined in an ELFv2
+ object with st_other localentry bits of zero, ie. its local entry
+ point coincides with its global entry point. */
+
+static bfd_boolean
+is_elfv2_localentry0 (struct elf_link_hash_entry *h)
+{
+ return (h != NULL
+ && h->type == STT_FUNC
+ && h->root.type == bfd_link_hash_defined
+ && (STO_PPC64_LOCAL_MASK & h->other) == 0
+ && is_ppc64_elf (h->root.u.def.section->owner)
+ && abiversion (h->root.u.def.section->owner) >= 2);
+}
+
/* Return true if symbol is defined in a regular object file. */
static bfd_boolean
@@ -8328,6 +8346,11 @@ ppc64_elf_tls_setup (struct bfd_link_info *info)
else if (!htab->do_multi_toc)
htab->params->no_multi_toc = 1;
+ if (htab->params->plt_localentry0 < 0)
+ htab->params->plt_localentry0
+ = elf_link_hash_lookup (&htab->elf, "GLIBC_2.26",
+ FALSE, FALSE, FALSE) != NULL;
+
htab->tls_get_addr = ((struct ppc_link_hash_entry *)
elf_link_hash_lookup (&htab->elf, ".__tls_get_addr",
FALSE, FALSE, TRUE));
@@ -10548,7 +10571,12 @@ plt_stub_size (struct ppc_link_hash_table *htab,
&& (stub_entry->h == htab->tls_get_addr_fd
|| stub_entry->h == htab->tls_get_addr)
&& htab->params->tls_get_addr_opt)
- size += 13 * 4;
+ {
+ size += 7 * 4;
+ if (ALWAYS_EMIT_R2SAVE
+ || stub_entry->stub_type == ppc_stub_plt_call_r2save)
+ size += 6 * 4;
+ }
return size;
}
@@ -10775,11 +10803,17 @@ build_tls_get_addr_stub (struct ppc_link_hash_table *htab,
bfd_put_32 (obfd, ADD_R3_R12_R13, p), p += 4;
bfd_put_32 (obfd, BEQLR, p), p += 4;
bfd_put_32 (obfd, MR_R3_R0, p), p += 4;
+ if (r != NULL)
+ r[0].r_offset += 7 * 4;
+ if (!ALWAYS_EMIT_R2SAVE
+ && stub_entry->stub_type != ppc_stub_plt_call_r2save)
+ return build_plt_stub (htab, stub_entry, p, offset, r);
+
bfd_put_32 (obfd, MFLR_R11, p), p += 4;
bfd_put_32 (obfd, STD_R11_0R1 + STK_LINKER (htab), p), p += 4;
if (r != NULL)
- r[0].r_offset += 9 * 4;
+ r[0].r_offset += 2 * 4;
p = build_plt_stub (htab, stub_entry, p, offset, r);
bfd_put_32 (obfd, BCTRL, p - 4);
@@ -12598,17 +12632,23 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
continue;
}
- if (stub_type == ppc_stub_plt_call
- && irela + 1 < irelaend
- && irela[1].r_offset == irela->r_offset + 4
- && ELF64_R_TYPE (irela[1].r_info) == R_PPC64_TOCSAVE)
+ if (stub_type == ppc_stub_plt_call)
{
- if (!tocsave_find (htab, INSERT,
- &local_syms, irela + 1, input_bfd))
- goto error_ret_free_internal;
+ if (irela + 1 < irelaend
+ && irela[1].r_offset == irela->r_offset + 4
+ && ELF64_R_TYPE (irela[1].r_info) == R_PPC64_TOCSAVE)
+ {
+ if (!tocsave_find (htab, INSERT,
+ &local_syms, irela + 1, input_bfd))
+ goto error_ret_free_internal;
+ }
+ else if (!htab->opd_abi
+ && htab->params->plt_localentry0 != 0
+ && is_elfv2_localentry0 (&hash->elf))
+ htab->has_plt_localentry0 = 1;
+ else
+ stub_type = ppc_stub_plt_call_r2save;
}
- else if (stub_type == ppc_stub_plt_call)
- stub_type = ppc_stub_plt_call_r2save;
/* Support for grouping stub sections. */
id_sec = htab->sec_info[section->id].u.group->link_sec;
@@ -13160,6 +13200,8 @@ ppc64_elf_build_stubs (struct bfd_link_info *info,
p += 4;
bfd_put_32 (htab->glink->owner, MFLR_R11, p);
p += 4;
+ bfd_put_32 (htab->glink->owner, STD_R2_0R1 + 24, p);
+ p += 4;
bfd_put_32 (htab->glink->owner, LD_R2_0R11 | (-16 & 0xfffc), p);
p += 4;
bfd_put_32 (htab->glink->owner, MTLR_R0, p);
@@ -14170,7 +14212,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
{
bfd_boolean can_plt_call = FALSE;
- /* All of these stubs will modify r2, so there must be a
+ /* All of these stubs may modify r2, so there must be a
branch and link followed by a nop. The nop is
replaced by an insn to restore r2. */
if (rel->r_offset + 8 <= input_section->size)
@@ -14195,6 +14237,13 @@ ppc64_elf_relocate_section (bfd *output_bfd,
{
/* Special stub used, leave nop alone. */
}
+ else if (stub_entry->stub_type == ppc_stub_plt_call
+ && !htab->opd_abi
+ && htab->params->plt_localentry0 != 0
+ && is_elfv2_localentry0 (&h->elf))
+ {
+ /* The function doesn't use or change r2. */
+ }
else
bfd_put_32 (input_bfd,
LD_R2_0R1 + STK_TOC (htab),
@@ -15636,6 +15685,8 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
case DT_PPC64_OPT:
if (htab->do_multi_toc && htab->multi_toc_needed)
dyn.d_un.d_val |= PPC64_OPT_MULTI_TOC;
+ if (htab->has_plt_localentry0)
+ dyn.d_un.d_val |= PPC64_OPT_LOCALENTRY;
break;
case DT_PPC64_OPDSZ:
diff --git a/bfd/elf64-ppc.h b/bfd/elf64-ppc.h
index 4609679..ca20fe4 100644
--- a/bfd/elf64-ppc.h
+++ b/bfd/elf64-ppc.h
@@ -48,6 +48,9 @@ struct ppc64_elf_params
/* Set if individual PLT call stubs should be aligned. */
int plt_stub_align;
+ /* Set if PLT call stubs for localentry:0 functions should omit r2 save. */
+ int plt_localentry0;
+
/* Whether to canonicalize .opd so that there are no overlapping
.opd entries. */
int non_overlapping_opd;