diff options
Diffstat (limited to 'bfd')
-rw-r--r-- | bfd/ChangeLog | 30 | ||||
-rw-r--r-- | bfd/elf32-ppc.c | 111 | ||||
-rw-r--r-- | bfd/elf32-ppc.h | 2 | ||||
-rw-r--r-- | bfd/elf64-ppc.c | 155 | ||||
-rw-r--r-- | bfd/elf64-ppc.h | 2 |
5 files changed, 284 insertions, 16 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog index a4f99e7..9900119 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,33 @@ +2009-09-21 Alan Modra <amodra@bigpond.net.au> + + * elf32-ppc.c (TLS_GET_ADDR_GLINK_SIZE): Define. + (ADD_3_12_2, BEQLR, CMPWI_11_0, LWZ_11_3, LWZ_12_3): Define. + (MR_0_3, MR_3_0): Define. + (struct ppc_elf_link_hash_table): Add no_tls_get_addr_opt. + (ppc_elf_select_plt_layout): Save emit_stub_syms param earlier. + (ppc_elf_tls_setup): Add no_tls_get_addr_opt param and save to hash + table. Check for presense of __tls_get_addr_opt + (allocate_dynrelocs): Increase glink entry size for __tls_get_addr. + (ppc_elf_size_dynamic_sections): Add DT_PPC_TLS_OPT tag. + (write_glink_stub): Add param p. + (ppc_elf_relocate_section): Adjust write_glink_stub call. + (ppc_elf_finish_dynamic_symbol): Emit special glink call stub for + __tls_get_addr. + * elf32-ppc.h (ppc_elf_tls_setup): Update prototype. + * elf64-ppc.c (struct ppc_link_hash_table): Add no_tls_get_addr_opt. + (ppc64_elf_tls_setup): Add no_tls_get_addr_opt param and save to hash + table. Check for presense of __tls_get_addr_opt. + (ppc64_elf_size_dynamic_sections): Add DT_PPC64_TLS_OPT tag. + (LD_R11_0R3, LD_R12_0R3, MR_R0_R3, CMPDI_R11_0, ADD_R3_R12_R13, + BEQLR, MR_R3_R0, MFLR_R11, STD_R11_0R1, BCTRL, LD_R11_0R1, + LD_R2_0R1, MTLR_R11): Define. + (build_tls_get_addr_stub): New function. + (ppc_build_one_stub): Call it. + (ppc_size_one_stub): Add extra size for __tls_get_addr stub. + (ppc64_elf_relocate_section): Don't change nop to ld 2,40(1) for + __tls_get_addr plt call. + * elf64-ppc.h (ppc64_elf_tls_setup): Update prototype. + 2009-09-19 Richard Sandiford <rdsandiford@googlemail.com> * elf-bfd.h (eh_cie_fde): Add personality_offset and diff --git a/bfd/elf32-ppc.c b/bfd/elf32-ppc.c index bbe94db..95058a2 100644 --- a/bfd/elf32-ppc.c +++ b/bfd/elf32-ppc.c @@ -61,6 +61,7 @@ static bfd_reloc_status_type ppc_elf_unhandled_reloc /* For new-style .glink and .plt. */ #define GLINK_PLTRESOLVE 16*4 #define GLINK_ENTRY_SIZE 4*4 +#define TLS_GET_ADDR_GLINK_SIZE 12*4 /* VxWorks uses its own plt layout, filled in by the static linker. */ @@ -135,17 +136,24 @@ static const bfd_vma ppc_elf_vxworks_pic_plt0_entry #define ADDIS_12_12 0x3d8c0000 #define ADDI_11_11 0x396b0000 #define ADD_0_11_11 0x7c0b5a14 +#define ADD_3_12_2 0x7c6c1214 #define ADD_11_0_11 0x7d605a14 #define B 0x48000000 #define BCL_20_31 0x429f0005 #define BCTR 0x4e800420 +#define BEQLR 0x4d820020 +#define CMPWI_11_0 0x2c0b0000 #define LIS_11 0x3d600000 #define LIS_12 0x3d800000 #define LWZU_0_12 0x840c0000 #define LWZ_0_12 0x800c0000 +#define LWZ_11_3 0x81630000 #define LWZ_11_11 0x816b0000 #define LWZ_11_30 0x817e0000 +#define LWZ_12_3 0x81830000 #define LWZ_12_12 0x818c0000 +#define MR_0_3 0x7c601b78 +#define MR_3_0 0x7c030378 #define MFLR_0 0x7c0802a6 #define MFLR_12 0x7d8802a6 #define MTCTR_0 0x7c0903a6 @@ -2754,6 +2762,9 @@ struct ppc_elf_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; + /* True if the target system is VxWorks. */ unsigned int is_vxworks:1; @@ -4277,6 +4288,8 @@ ppc_elf_select_plt_layout (bfd *output_bfd ATTRIBUTE_UNUSED, htab = ppc_elf_hash_table (info); + htab->emit_stub_syms = emit_stub_syms; + if (htab->plt_type == PLT_UNSET) { if (plt_style == PLT_OLD) @@ -4310,8 +4323,6 @@ ppc_elf_select_plt_layout (bfd *output_bfd ATTRIBUTE_UNUSED, if (htab->plt_type == PLT_OLD && plt_style == PLT_NEW) info->callbacks->info (_("Using bss-plt due to %B"), htab->old_bfd); - htab->emit_stub_syms = emit_stub_syms; - BFD_ASSERT (htab->plt_type != PLT_VXWORKS); if (htab->plt_type == PLT_NEW) @@ -4539,11 +4550,62 @@ ppc_elf_gc_sweep_hook (bfd *abfd, generic ELF tls_setup function. */ asection * -ppc_elf_tls_setup (bfd *obfd, struct bfd_link_info *info) +ppc_elf_tls_setup (bfd *obfd, + struct bfd_link_info *info, + int no_tls_get_addr_opt) { struct ppc_elf_link_hash_table *htab; htab = ppc_elf_hash_table (info); + htab->tls_get_addr = elf_link_hash_lookup (&htab->elf, "__tls_get_addr", + FALSE, FALSE, TRUE); + if (!no_tls_get_addr_opt) + { + struct elf_link_hash_entry *opt, *tga; + opt = elf_link_hash_lookup (&htab->elf, "__tls_get_addr_opt", + FALSE, FALSE, TRUE); + if (opt != NULL + && (opt->root.type == bfd_link_hash_defined + || opt->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 = htab->tls_get_addr; + if (htab->elf.dynamic_sections_created + && tga != NULL + && (tga->type == STT_FUNC + || tga->needs_plt) + && !(SYMBOL_CALLS_LOCAL (info, tga) + || (ELF_ST_VISIBILITY (tga->other) != STV_DEFAULT + && tga->root.type == bfd_link_hash_undefweak))) + { + struct plt_entry *ent; + ent = find_plt_ent (&tga->plt.plist, NULL, 0); + if (ent != NULL + && ent->plt.refcount > 0) + { + tga->root.type = bfd_link_hash_indirect; + tga->root.u.i.link = &opt->root; + ppc_elf_copy_indirect_symbol (info, opt, tga); + if (opt->dynindx != -1) + { + /* Use __tls_get_addr_opt in dynamic relocations. */ + opt->dynindx = -1; + _bfd_elf_strtab_delref (elf_hash_table (info)->dynstr, + opt->dynstr_index); + if (!bfd_elf_link_record_dynamic_symbol (info, opt)) + return FALSE; + } + htab->tls_get_addr = opt; + } + } + } + else + no_tls_get_addr_opt = TRUE; + } + htab->no_tls_get_addr_opt = no_tls_get_addr_opt; if (htab->plt_type == PLT_NEW && htab->plt != NULL && htab->plt->output_section != NULL) @@ -4552,8 +4614,6 @@ ppc_elf_tls_setup (bfd *obfd, struct bfd_link_info *info) elf_section_flags (htab->plt->output_section) = SHF_ALLOC + SHF_WRITE; } - htab->tls_get_addr = elf_link_hash_lookup (&htab->elf, "__tls_get_addr", - FALSE, FALSE, TRUE); return _bfd_elf_tls_setup (obfd, info); } @@ -5144,6 +5204,9 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) { glink_offset = s->size; s->size += GLINK_ENTRY_SIZE; + if (h == htab->tls_get_addr + && !htab->no_tls_get_addr_opt) + s->size += TLS_GET_ADDR_GLINK_SIZE - GLINK_ENTRY_SIZE; } if (!doneone && !info->shared @@ -5820,6 +5883,11 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, { if (!add_dynamic_entry (DT_PPC_GOT, 0)) return FALSE; + if (!htab->no_tls_get_addr_opt + && htab->tls_get_addr != NULL + && htab->tls_get_addr->plt.plist != NULL + && !add_dynamic_entry (DT_PPC_TLSOPT, 0)) + return FALSE; } if (relocs) @@ -6474,18 +6542,16 @@ elf_finish_pointer_linker_section (bfd *input_bfd, #define PPC_HA(v) PPC_HI ((v) + 0x8000) static void -write_glink_stub (struct plt_entry *ent, asection *plt_sec, +write_glink_stub (struct plt_entry *ent, asection *plt_sec, unsigned char *p, struct bfd_link_info *info) { struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info); bfd *output_bfd = info->output_bfd; bfd_vma plt; - unsigned char *p; plt = ((ent->plt.offset & ~1) + plt_sec->output_section->vma + plt_sec->output_offset); - p = (unsigned char *) htab->glink->contents + ent->glink_offset; if (info->shared) { @@ -7045,7 +7111,9 @@ ppc_elf_relocate_section (bfd *output_bfd, } if (h == NULL && (ent->glink_offset & 1) == 0) { - write_glink_stub (ent, htab->iplt, info); + unsigned char *p = ((unsigned char *) htab->glink->contents + + ent->glink_offset); + write_glink_stub (ent, htab->iplt, p, info); ent->glink_offset |= 1; } @@ -8274,12 +8342,35 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd, || !htab->elf.dynamic_sections_created || h->dynindx == -1) { + unsigned char *p; asection *splt = htab->plt; if (!htab->elf.dynamic_sections_created || h->dynindx == -1) splt = htab->iplt; - write_glink_stub (ent, splt, info); + p = (unsigned char *) htab->glink->contents + ent->glink_offset; + + if (h == htab->tls_get_addr && !htab->no_tls_get_addr_opt) + { + bfd_put_32 (output_bfd, LWZ_11_3, p); + p += 4; + bfd_put_32 (output_bfd, LWZ_12_3 + 4, p); + p += 4; + bfd_put_32 (output_bfd, MR_0_3, p); + p += 4; + bfd_put_32 (output_bfd, CMPWI_11_0, p); + p += 4; + bfd_put_32 (output_bfd, ADD_3_12_2, p); + p += 4; + bfd_put_32 (output_bfd, BEQLR, p); + p += 4; + bfd_put_32 (output_bfd, MR_3_0, p); + p += 4; + bfd_put_32 (output_bfd, NOP, p); + p += 4; + } + + write_glink_stub (ent, splt, p, info); if (!info->shared) /* We only need one non-PIC glink stub. */ diff --git a/bfd/elf32-ppc.h b/bfd/elf32-ppc.h index ef1f0ac..4becb30 100644 --- a/bfd/elf32-ppc.h +++ b/bfd/elf32-ppc.h @@ -28,6 +28,6 @@ enum ppc_elf_plt_type int ppc_elf_select_plt_layout (bfd *, struct bfd_link_info *, enum ppc_elf_plt_type, int); -asection *ppc_elf_tls_setup (bfd *, struct bfd_link_info *); +asection *ppc_elf_tls_setup (bfd *, struct bfd_link_info *, int); bfd_boolean ppc_elf_tls_optimize (bfd *, struct bfd_link_info *); void ppc_elf_set_sdata_syms (bfd *, struct bfd_link_info *); 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; } } diff --git a/bfd/elf64-ppc.h b/bfd/elf64-ppc.h index 687b4a1..e5f7140 100644 --- a/bfd/elf64-ppc.h +++ b/bfd/elf64-ppc.h @@ -24,7 +24,7 @@ void ppc64_elf_init_stub_bfd bfd_boolean ppc64_elf_edit_opd (bfd *, struct bfd_link_info *, bfd_boolean); asection *ppc64_elf_tls_setup - (bfd *, struct bfd_link_info *); +(bfd *, struct bfd_link_info *, int); bfd_boolean ppc64_elf_tls_optimize (bfd *, struct bfd_link_info *); bfd_boolean ppc64_elf_edit_toc |