aboutsummaryrefslogtreecommitdiff
path: root/bfd
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2020-01-22 12:24:56 +1030
committerAlan Modra <amodra@gmail.com>2020-01-22 17:14:08 +1030
commita804e4760a9f05aeb61071f8caad3579e6a197a2 (patch)
treec25f0e5d42c379e7d388df47f6f0fe698a0bdd3b /bfd
parent9e7028aa1e788d666bad91fb20159da6c95bbab1 (diff)
downloadfsf-binutils-gdb-a804e4760a9f05aeb61071f8caad3579e6a197a2.zip
fsf-binutils-gdb-a804e4760a9f05aeb61071f8caad3579e6a197a2.tar.gz
fsf-binutils-gdb-a804e4760a9f05aeb61071f8caad3579e6a197a2.tar.bz2
PowerPC64 tls_get_addr_desc static support
This provides a linker generated __tls_get_addr_desc wrapper function preserving registers around a __tls_get_addr call. The idea being to support __tls_get_addr_desc without requiring a glibc update. bfd/ * elf64-ppc.c (struct ppc_link_hash_table): Add tga_group. (ppc64_elf_archive_symbol_lookup): Extract __tls_get_addr_opt for __tls_get_addr_desc. (ppc64_elf_size_stubs): Add section for linker generated __tls_get_addr_desc wrapper function. Loop at least once if generating this function. (emit_tga_desc, emit_tga_desc_eh_frame): New functions. (ppc64_elf_build_stubs): Generate __tls_get_addr_desc. ld/ * testsuite/ld-powerpc/tlsdesc3.d, * testsuite/ld-powerpc/tlsdesc3.wf, * testsuite/ld-powerpc/tlsdesc4.d, * testsuite/ld-powerpc/tlsdesc4.s, * testsuite/ld-powerpc/tlsdesc4.wf: New tests. * testsuite/ld-powerpc/powerpc.exp: Run them.
Diffstat (limited to 'bfd')
-rw-r--r--bfd/ChangeLog11
-rw-r--r--bfd/elf64-ppc.c138
2 files changed, 148 insertions, 1 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index 1bd9b5f..f20b2c0 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,5 +1,16 @@
2020-01-22 Alan Modra <amodra@gmail.com>
+ * elf64-ppc.c (struct ppc_link_hash_table): Add tga_group.
+ (ppc64_elf_archive_symbol_lookup): Extract __tls_get_addr_opt for
+ __tls_get_addr_desc.
+ (ppc64_elf_size_stubs): Add section for linker generated
+ __tls_get_addr_desc wrapper function. Loop at least once if
+ generating this function.
+ (emit_tga_desc, emit_tga_desc_eh_frame): New functions.
+ (ppc64_elf_build_stubs): Generate __tls_get_addr_desc.
+
+2020-01-22 Alan Modra <amodra@gmail.com>
+
* elf64-ppc.h (struct ppc64_elf_params): Add no_tls_get_addr_regsave.
* elf64-ppc.c (struct ppc_link_hash_table): Add tga_desc and
tga_desc_fd.
diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index 6b11a11..b3f8f6b 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -3182,6 +3182,7 @@ struct ppc_link_hash_table
struct ppc_link_hash_entry *tls_get_addr_fd;
struct ppc_link_hash_entry *tga_desc;
struct ppc_link_hash_entry *tga_desc_fd;
+ struct map_stub *tga_group;
/* The size of reliplt used by got entry relocs. */
bfd_size_type got_reli_size;
@@ -4150,6 +4151,11 @@ ppc64_elf_archive_symbol_lookup (bfd *abfd,
memcpy (dot_name + 1, name, len + 1);
h = _bfd_elf_archive_symbol_lookup (abfd, info, dot_name);
bfd_release (abfd, dot_name);
+ if (h != NULL)
+ return h;
+
+ if (strcmp (name, "__tls_get_addr_opt") == 0)
+ h = _bfd_elf_archive_symbol_lookup (abfd, info, "__tls_get_addr_desc");
return h;
}
@@ -13088,6 +13094,40 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
if (!group_sections (info, stub_group_size, stubs_always_before_branch))
return FALSE;
+ htab->tga_group = NULL;
+ if (!htab->params->no_tls_get_addr_regsave
+ && htab->tga_desc_fd != NULL
+ && (htab->tga_desc_fd->elf.root.type == bfd_link_hash_undefined
+ || htab->tga_desc_fd->elf.root.type == bfd_link_hash_undefweak)
+ && htab->tls_get_addr_fd != NULL
+ && is_static_defined (&htab->tls_get_addr_fd->elf))
+ {
+ asection *sym_sec, *code_sec, *stub_sec;
+ bfd_vma sym_value;
+ struct _opd_sec_data *opd;
+
+ sym_sec = htab->tls_get_addr_fd->elf.root.u.def.section;
+ sym_value = defined_sym_val (&htab->tls_get_addr_fd->elf);
+ code_sec = sym_sec;
+ opd = get_opd_info (sym_sec);
+ if (opd != NULL)
+ opd_entry_value (sym_sec, sym_value, &code_sec, NULL, FALSE);
+ htab->tga_group = htab->sec_info[code_sec->id].u.group;
+ stub_sec = (*htab->params->add_stub_section) (".tga_desc.stub",
+ htab->tga_group->link_sec);
+ if (stub_sec == NULL)
+ return FALSE;
+ htab->tga_group->stub_sec = stub_sec;
+
+ htab->tga_desc_fd->elf.root.type = bfd_link_hash_defined;
+ htab->tga_desc_fd->elf.root.u.def.section = stub_sec;
+ htab->tga_desc_fd->elf.root.u.def.value = 0;
+ htab->tga_desc_fd->elf.type = STT_FUNC;
+ htab->tga_desc_fd->elf.def_regular = 1;
+ htab->tga_desc_fd->elf.non_elf = 0;
+ _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
@@ -13517,6 +13557,14 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
stub_sec->flags &= ~SEC_RELOC;
}
}
+ if (htab->tga_group != NULL)
+ {
+ /* See emit_tga_desc and emit_tga_desc_eh_frame. */
+ htab->tga_group->eh_size
+ = 1 + 2 + (htab->opd_abi != 0) + 3 + 8 * 2 + 3 + 8 + 3;
+ htab->tga_group->lr_restore = 23 * 4;
+ htab->tga_group->stub_sec->size = 24 * 4;
+ }
if (htab->stub_iteration <= STUB_SHRINK_ITER
|| htab->brlt->rawsize < htab->brlt->size)
@@ -13580,7 +13628,9 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
|| (htab->stub_iteration > STUB_SHRINK_ITER
&& htab->brlt->rawsize > htab->brlt->size))
&& (htab->glink_eh_frame == NULL
- || htab->glink_eh_frame->rawsize == htab->glink_eh_frame->size))
+ || htab->glink_eh_frame->rawsize == htab->glink_eh_frame->size)
+ && (htab->tga_group == NULL
+ || htab->stub_iteration > 1))
break;
/* Ask the linker to do its stuff. */
@@ -14086,6 +14136,74 @@ write_plt_relocs_for_local_syms (struct bfd_link_info *info)
return TRUE;
}
+/* Emit the static wrapper function preserving registers around a
+ __tls_get_addr_opt call. */
+
+static bfd_boolean
+emit_tga_desc (struct ppc_link_hash_table *htab)
+{
+ asection *stub_sec = htab->tga_group->stub_sec;
+ unsigned int cfa_updt = 11 * 4;
+ bfd_byte *p;
+ bfd_vma to, from, delta;
+
+ BFD_ASSERT (htab->tga_desc_fd->elf.root.type == bfd_link_hash_defined
+ && htab->tga_desc_fd->elf.root.u.def.section == stub_sec
+ && htab->tga_desc_fd->elf.root.u.def.value == 0);
+ to = defined_sym_val (&htab->tls_get_addr_fd->elf);
+ from = defined_sym_val (&htab->tga_desc_fd->elf) + cfa_updt;
+ delta = to - from;
+ if (delta + (1 << 25) >= 1 << 26)
+ {
+ _bfd_error_handler (_("__tls_get_addr call offset overflow"));
+ htab->stub_error = TRUE;
+ return FALSE;
+ }
+
+ p = stub_sec->contents;
+ p = tls_get_addr_prologue (htab->elf.dynobj, p, htab);
+ bfd_put_32 (stub_sec->owner, B_DOT | 1 | (delta & 0x3fffffc), p);
+ p += 4;
+ p = tls_get_addr_epilogue (htab->elf.dynobj, p, htab);
+ return stub_sec->size == (bfd_size_type) (p - stub_sec->contents);
+}
+
+/* Emit eh_frame describing the static wrapper function. */
+
+static bfd_byte *
+emit_tga_desc_eh_frame (struct ppc_link_hash_table *htab, bfd_byte *p)
+{
+ unsigned int cfa_updt = 11 * 4;
+ unsigned int i;
+
+ *p++ = DW_CFA_advance_loc + cfa_updt / 4;
+ *p++ = DW_CFA_def_cfa_offset;
+ if (htab->opd_abi)
+ {
+ *p++ = 128;
+ *p++ = 1;
+ }
+ else
+ *p++ = 96;
+ *p++ = DW_CFA_offset_extended_sf;
+ *p++ = 65;
+ *p++ = (-16 / 8) & 0x7f;
+ for (i = 4; i < 12; i++)
+ {
+ *p++ = DW_CFA_offset + i;
+ *p++ = (htab->opd_abi ? 13 : 12) - i;
+ }
+ *p++ = DW_CFA_advance_loc + 10;
+ *p++ = DW_CFA_def_cfa_offset;
+ *p++ = 0;
+ for (i = 4; i < 12; i++)
+ *p++ = DW_CFA_restore + i;
+ *p++ = DW_CFA_advance_loc + 2;
+ *p++ = DW_CFA_restore_extended;
+ *p++ = 65;
+ return p;
+}
+
/* Build all the stubs associated with the current output file.
The stubs are kept in a hash table attached to the main linker
hash table. This function is called via gldelf64ppc_finish. */
@@ -14245,6 +14363,24 @@ ppc64_elf_build_stubs (struct bfd_link_info *info,
}
}
+ if (htab->tga_group != NULL)
+ {
+ htab->tga_group->lr_restore = 23 * 4;
+ htab->tga_group->stub_sec->size = 24 * 4;
+ if (!emit_tga_desc (htab))
+ return FALSE;
+ if (htab->glink_eh_frame != NULL
+ && htab->glink_eh_frame->size != 0)
+ {
+ size_t align = 4;
+
+ p = htab->glink_eh_frame->contents;
+ p += (sizeof (glink_eh_frame_cie) + align - 1) & -align;
+ p += 17;
+ htab->tga_group->eh_size = emit_tga_desc_eh_frame (htab, p) - p;
+ }
+ }
+
/* Build .glink global entry stubs, and PLT relocs for globals. */
elf_link_hash_traverse (&htab->elf, build_global_entry_stubs_and_plt, info);