diff options
Diffstat (limited to 'bfd/elf64-ppc.c')
-rw-r--r-- | bfd/elf64-ppc.c | 155 |
1 files changed, 151 insertions, 4 deletions
diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index 99bfec2..35757b8 100644 --- a/bfd/elf64-ppc.c +++ b/bfd/elf64-ppc.c @@ -3771,6 +3771,9 @@ struct ppc_link_hash_table /* Set if we should emit symbols for stubs. */ unsigned int emit_stub_syms:1; + /* Set if __tls_get_addr optimization should not be done. */ + unsigned int no_tls_get_addr_opt:1; + /* Support for multiple toc sections. */ unsigned int no_multi_toc:1; unsigned int multi_toc_needed:1; @@ -7218,7 +7221,9 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info, /* Set htab->tls_get_addr and call the generic ELF tls_setup function. */ asection * -ppc64_elf_tls_setup (bfd *obfd, struct bfd_link_info *info) +ppc64_elf_tls_setup (bfd *obfd, + struct bfd_link_info *info, + int no_tls_get_addr_opt) { struct ppc_link_hash_table *htab; @@ -7226,9 +7231,83 @@ ppc64_elf_tls_setup (bfd *obfd, struct bfd_link_info *info) htab->tls_get_addr = ((struct ppc_link_hash_entry *) elf_link_hash_lookup (&htab->elf, ".__tls_get_addr", FALSE, FALSE, TRUE)); + /* Move dynamic linking info to the function descriptor sym. */ + if (htab->tls_get_addr != NULL) + func_desc_adjust (&htab->tls_get_addr->elf, info); htab->tls_get_addr_fd = ((struct ppc_link_hash_entry *) elf_link_hash_lookup (&htab->elf, "__tls_get_addr", FALSE, FALSE, TRUE)); + if (!no_tls_get_addr_opt) + { + struct elf_link_hash_entry *opt, *opt_fd, *tga, *tga_fd; + + opt = elf_link_hash_lookup (&htab->elf, ".__tls_get_addr_opt", + FALSE, FALSE, TRUE); + if (opt != NULL) + func_desc_adjust (opt, info); + opt_fd = elf_link_hash_lookup (&htab->elf, "__tls_get_addr_opt", + FALSE, FALSE, TRUE); + if (opt_fd != NULL + && (opt_fd->root.type == bfd_link_hash_defined + || opt_fd->root.type == bfd_link_hash_defweak)) + { + /* If glibc supports an optimized __tls_get_addr call stub, + signalled by the presence of __tls_get_addr_opt, and we'll + be calling __tls_get_addr via a plt call stub, then + make __tls_get_addr point to __tls_get_addr_opt. */ + tga_fd = &htab->tls_get_addr_fd->elf; + if (htab->elf.dynamic_sections_created + && tga_fd != NULL + && (tga_fd->type == STT_FUNC + || tga_fd->needs_plt) + && !(SYMBOL_CALLS_LOCAL (info, tga_fd) + || (ELF_ST_VISIBILITY (tga_fd->other) != STV_DEFAULT + && tga_fd->root.type == bfd_link_hash_undefweak))) + { + struct plt_entry *ent; + + for (ent = tga_fd->plt.plist; ent != NULL; ent = ent->next) + if (ent->plt.refcount > 0) + break; + if (ent != NULL) + { + tga_fd->root.type = bfd_link_hash_indirect; + tga_fd->root.u.i.link = &opt_fd->root; + ppc64_elf_copy_indirect_symbol (info, opt_fd, tga_fd); + if (opt_fd->dynindx != -1) + { + /* Use __tls_get_addr_opt in dynamic relocations. */ + opt_fd->dynindx = -1; + _bfd_elf_strtab_delref (elf_hash_table (info)->dynstr, + opt_fd->dynstr_index); + if (!bfd_elf_link_record_dynamic_symbol (info, opt_fd)) + return FALSE; + } + htab->tls_get_addr_fd = (struct ppc_link_hash_entry *) opt_fd; + tga = &htab->tls_get_addr->elf; + if (opt != NULL && tga != NULL) + { + tga->root.type = bfd_link_hash_indirect; + tga->root.u.i.link = &opt->root; + ppc64_elf_copy_indirect_symbol (info, opt, tga); + _bfd_elf_link_hash_hide_symbol (info, opt, + tga->forced_local); + htab->tls_get_addr = (struct ppc_link_hash_entry *) opt; + } + htab->tls_get_addr_fd->oh = htab->tls_get_addr; + htab->tls_get_addr_fd->is_func_descriptor = 1; + if (htab->tls_get_addr != NULL) + { + htab->tls_get_addr->oh = htab->tls_get_addr_fd; + htab->tls_get_addr->is_func = 1; + } + } + } + } + else + no_tls_get_addr_opt = TRUE; + } + htab->no_tls_get_addr_opt = no_tls_get_addr_opt; return _bfd_elf_tls_setup (obfd, info); } @@ -8666,6 +8745,12 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, return FALSE; } + if (!htab->no_tls_get_addr_opt + && htab->tls_get_addr_fd != NULL + && htab->tls_get_addr_fd->elf.plt.plist != NULL + && !add_dynamic_entry (DT_PPC64_TLSOPT, 0)) + return FALSE; + if (relocs) { if (!add_dynamic_entry (DT_RELA, 0) @@ -8859,6 +8944,49 @@ build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r) return p; } +/* Build a special .plt call stub for __tls_get_addr. */ + +#define LD_R11_0R3 0xe9630000 +#define LD_R12_0R3 0xe9830000 +#define MR_R0_R3 0x7c601b78 +#define CMPDI_R11_0 0x2c2b0000 +#define ADD_R3_R12_R13 0x7c6c6a14 +#define BEQLR 0x4d820020 +#define MR_R3_R0 0x7c030378 +#define MFLR_R11 0x7d6802a6 +#define STD_R11_0R1 0xf9610000 +#define BCTRL 0x4e800421 +#define LD_R11_0R1 0xe9610000 +#define LD_R2_0R1 0xe8410000 +#define MTLR_R11 0x7d6803a6 + +static inline bfd_byte * +build_tls_get_addr_stub (bfd *obfd, bfd_byte *p, int offset, + Elf_Internal_Rela *r) +{ + bfd_put_32 (obfd, LD_R11_0R3 + 0, p), p += 4; + bfd_put_32 (obfd, LD_R12_0R3 + 8, p), p += 4; + bfd_put_32 (obfd, MR_R0_R3, p), p += 4; + bfd_put_32 (obfd, CMPDI_R11_0, p), p += 4; + 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; + bfd_put_32 (obfd, MFLR_R11, p), p += 4; + bfd_put_32 (obfd, STD_R11_0R1 + 32, p), p += 4; + + if (r != NULL) + r[0].r_offset += 9 * 4; + p = build_plt_stub (obfd, p, offset, r); + bfd_put_32 (obfd, BCTRL, p - 4); + + bfd_put_32 (obfd, LD_R11_0R1 + 32, p), p += 4; + bfd_put_32 (obfd, LD_R2_0R1 + 40, p), p += 4; + bfd_put_32 (obfd, MTLR_R11, p), p += 4; + bfd_put_32 (obfd, BLR, p), p += 4; + + return p; +} + static Elf_Internal_Rela * get_relocs (asection *sec, int count) { @@ -9227,7 +9355,13 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) r[0].r_offset += 2; r[0].r_addend = dest; } - p = build_plt_stub (htab->stub_bfd, loc, off, r); + if (stub_entry->h != NULL + && (stub_entry->h == htab->tls_get_addr_fd + || stub_entry->h == htab->tls_get_addr) + && !htab->no_tls_get_addr_opt) + p = build_tls_get_addr_stub (htab->stub_bfd, loc, off, r); + else + p = build_plt_stub (htab->stub_bfd, loc, off, r); size = p - loc; break; @@ -9316,6 +9450,11 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) size -= 4; if (PPC_HA (off + 16) != PPC_HA (off)) size += 4; + if (stub_entry->h != NULL + && (stub_entry->h == htab->tls_get_addr_fd + || stub_entry->h == htab->tls_get_addr) + && !htab->no_tls_get_addr_opt) + size += 13 * 4; if (info->emitrelocations) { stub_entry->stub_sec->reloc_count @@ -11255,8 +11394,16 @@ ppc64_elf_relocate_section (bfd *output_bfd, if (nop == NOP || nop == CROR_151515 || nop == CROR_313131) { - bfd_put_32 (input_bfd, LD_R2_40R1, - contents + rel->r_offset + 4); + if (h != NULL + && (h == htab->tls_get_addr_fd + || h == htab->tls_get_addr) + && !htab->no_tls_get_addr_opt) + { + /* Special stub used, leave nop alone. */ + } + else + bfd_put_32 (input_bfd, LD_R2_40R1, + contents + rel->r_offset + 4); can_plt_call = TRUE; } } |