From 49c09209d06885dc8350042ce77e442bfbb5bf27 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Mon, 9 Apr 2018 09:21:59 +0930 Subject: [PATCH] Rearrange PLT reloc output on powerpc The current scheme where we output PLT relocs for global symbols in finish_dynamic_symbol, and PLT relocs for local symbols when outputting stubs does not work if PLT entries are to be used for inline PLT sequences against non-dynamic globals or local symbols. bfd/ * elf64-ppc.c (ppc_build_one_stub): Move output of PLT relocs for local symbols to.. (write_plt_relocs_for_local_syms): ..here. New function. (ppc64_elf_finish_dynamic_symbol): Move output of PLT relocs for global symbols to.. (build_global_entry_stubs_and_plt): ..here. Rename from build_global_entry_stubs. (ppc64_elf_build_stubs): Always call build_global_entry_stubs_and_plt. Call write_plt_relocs_for_local_syms. * elf32-ppc.c (get_sym_h): New function. (ppc_elf_relax_section): Use get_sym_h. (ppc_elf_relocate_section): Move output of PLT relocs and glink stubs for local symbols to.. (ppc_finish_symbols): ..here. New function. (ppc_elf_finish_dynamic_symbol): Move output of PLT relocs for global syms to.. (write_global_sym_plt): ..here. New function. * elf32-ppc.h (ppc_elf_modify_segment_map): Delete attribute. (ppc_finish_symbols): Declare. ld/ * ppc32elf.em (ppc_finish): Call ppc_finish_symbols. --- bfd/ChangeLog | 22 +++ bfd/elf32-ppc.c | 421 ++++++++++++++++++++++++++++++++--------------- bfd/elf32-ppc.h | 4 +- bfd/elf64-ppc.c | 284 ++++++++++++++++++++------------ ld/ChangeLog | 4 + ld/emultempl/ppc32elf.em | 2 + 6 files changed, 493 insertions(+), 244 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index cb65c7d..f6388c1 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,5 +1,27 @@ 2018-04-09 Alan Modra + * elf64-ppc.c (ppc_build_one_stub): Move output of PLT relocs + for local symbols to.. + (write_plt_relocs_for_local_syms): ..here. New function. + (ppc64_elf_finish_dynamic_symbol): Move output of PLT relocs for + global symbols to.. + (build_global_entry_stubs_and_plt): ..here. Rename from + build_global_entry_stubs. + (ppc64_elf_build_stubs): Always call build_global_entry_stubs_and_plt. + Call write_plt_relocs_for_local_syms. + * elf32-ppc.c (get_sym_h): New function. + (ppc_elf_relax_section): Use get_sym_h. + (ppc_elf_relocate_section): Move output of PLT relocs and glink + stubs for local symbols to.. + (ppc_finish_symbols): ..here. New function. + (ppc_elf_finish_dynamic_symbol): Move output of PLT relocs for + global syms to.. + (write_global_sym_plt): ..here. New function. + * elf32-ppc.h (ppc_elf_modify_segment_map): Delete attribute. + (ppc_finish_symbols): Declare. + +2018-04-09 Alan Modra + * elf32-ppc.c (ppc_elf_check_relocs): Handle PLT16 relocs. (ppc_elf_relocate_section): Likewise. * elf64-ppc.c (ppc64_elf_check_relocs): Handle PLT16_LO_DS. diff --git a/bfd/elf32-ppc.c b/bfd/elf32-ppc.c index 9736301..d24b095 100644 --- a/bfd/elf32-ppc.c +++ b/bfd/elf32-ppc.c @@ -5085,6 +5085,93 @@ ppc_elf_gc_mark_hook (asection *sec, return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym); } + +static bfd_boolean +get_sym_h (struct elf_link_hash_entry **hp, + Elf_Internal_Sym **symp, + asection **symsecp, + unsigned char **tls_maskp, + Elf_Internal_Sym **locsymsp, + unsigned long r_symndx, + bfd *ibfd) +{ + Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (ibfd); + + if (r_symndx >= symtab_hdr->sh_info) + { + struct elf_link_hash_entry **sym_hashes = elf_sym_hashes (ibfd); + struct elf_link_hash_entry *h; + + h = sym_hashes[r_symndx - symtab_hdr->sh_info]; + while (h->root.type == bfd_link_hash_indirect + || h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + if (hp != NULL) + *hp = h; + + if (symp != NULL) + *symp = NULL; + + if (symsecp != NULL) + { + asection *symsec = NULL; + if (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + symsec = h->root.u.def.section; + *symsecp = symsec; + } + + if (tls_maskp != NULL) + *tls_maskp = &ppc_elf_hash_entry (h)->tls_mask; + } + else + { + Elf_Internal_Sym *sym; + Elf_Internal_Sym *locsyms = *locsymsp; + + if (locsyms == NULL) + { + locsyms = (Elf_Internal_Sym *) symtab_hdr->contents; + if (locsyms == NULL) + locsyms = bfd_elf_get_elf_syms (ibfd, symtab_hdr, + symtab_hdr->sh_info, + 0, NULL, NULL, NULL); + if (locsyms == NULL) + return FALSE; + *locsymsp = locsyms; + } + sym = locsyms + r_symndx; + + if (hp != NULL) + *hp = NULL; + + if (symp != NULL) + *symp = sym; + + if (symsecp != NULL) + *symsecp = bfd_section_from_elf_index (ibfd, sym->st_shndx); + + if (tls_maskp != NULL) + { + bfd_signed_vma *local_got; + unsigned char *tls_mask; + + tls_mask = NULL; + local_got = elf_local_got_refcounts (ibfd); + if (local_got != NULL) + { + struct plt_entry **local_plt = (struct plt_entry **) + (local_got + symtab_hdr->sh_info); + unsigned char *lgot_masks = (unsigned char *) + (local_plt + symtab_hdr->sh_info); + tls_mask = &lgot_masks[r_symndx]; + } + *tls_maskp = tls_mask; + } + } + return TRUE; +} /* Set plt output section type, htab->tls_get_addr, and call the generic ELF tls_setup function. */ @@ -6892,6 +6979,7 @@ ppc_elf_relax_section (bfd *abfd, bfd_byte *hit_addr; unsigned long t0; struct elf_link_hash_entry *h; + Elf_Internal_Sym *isym; struct plt_entry **plist; unsigned char sym_type; @@ -6919,57 +7007,34 @@ ppc_elf_relax_section (bfd *abfd, } /* Get the value of the symbol referred to by the reloc. */ - h = NULL; - if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info) - { - /* A local symbol. */ - Elf_Internal_Sym *isym; + if (!get_sym_h (&h, &isym, &tsec, NULL, &isymbuf, + ELF32_R_SYM (irel->r_info), abfd)) + goto error_return; - /* Read this BFD's local symbols. */ - if (isymbuf == NULL) - { - isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; - if (isymbuf == NULL) - isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr, - symtab_hdr->sh_info, 0, - NULL, NULL, NULL); - if (isymbuf == 0) - goto error_return; - } - isym = isymbuf + ELF32_R_SYM (irel->r_info); - if (isym->st_shndx == SHN_UNDEF) + if (isym != NULL) + { + if (tsec != NULL) + ; + else if (isym->st_shndx == SHN_UNDEF) tsec = bfd_und_section_ptr; else if (isym->st_shndx == SHN_ABS) tsec = bfd_abs_section_ptr; else if (isym->st_shndx == SHN_COMMON) tsec = bfd_com_section_ptr; - else - tsec = bfd_section_from_elf_index (abfd, isym->st_shndx); toff = isym->st_value; sym_type = ELF_ST_TYPE (isym->st_info); } else { - /* Global symbol handling. */ - unsigned long indx; - - indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info; - h = elf_sym_hashes (abfd)[indx]; - - while (h->root.type == bfd_link_hash_indirect - || h->root.type == bfd_link_hash_warning) - h = (struct elf_link_hash_entry *) h->root.u.i.link; - - if (h->root.type == bfd_link_hash_defined - || h->root.type == bfd_link_hash_defweak) - { - tsec = h->root.u.def.section; - toff = h->root.u.def.value; - } + if (tsec != NULL) + toff = h->root.u.def.value; else if (h->root.type == bfd_link_hash_undefined || h->root.type == bfd_link_hash_undefweak) { + unsigned long indx; + + indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info; tsec = bfd_und_section_ptr; toff = bfd_link_relocatable (link_info) ? indx : 0; } @@ -8308,32 +8373,6 @@ ppc_elf_relocate_section (bfd *output_bfd, (_("%X%H: unsupported bss-plt -fPIC ifunc %s\n"), input_bfd, input_section, rel->r_offset, sym_name); } - if (h == NULL && (ent->plt.offset & 1) == 0) - { - Elf_Internal_Rela rela; - bfd_byte *loc; - - rela.r_offset = (htab->elf.iplt->output_section->vma - + htab->elf.iplt->output_offset - + ent->plt.offset); - rela.r_info = ELF32_R_INFO (0, R_PPC_IRELATIVE); - rela.r_addend = relocation; - loc = htab->elf.irelplt->contents; - loc += (htab->elf.irelplt->reloc_count++ - * sizeof (Elf32_External_Rela)); - bfd_elf32_swap_reloca_out (output_bfd, &rela, loc); - htab->local_ifunc_resolver = 1; - - ent->plt.offset |= 1; - } - if (h == NULL && (ent->glink_offset & 1) == 0) - { - unsigned char *p = ((unsigned char *) htab->glink->contents - + ent->glink_offset); - - write_glink_stub (NULL, ent, htab->elf.iplt, p, info); - ent->glink_offset |= 1; - } unresolved_reloc = FALSE; if (htab->plt_type == PLT_NEW @@ -10027,27 +10066,16 @@ ppc_elf_relocate_section (bfd *output_bfd, return ret; } -/* Finish up dynamic symbol handling. We set the contents of various - dynamic sections here. */ +/* Write out the PLT relocs and entries for H. */ static bfd_boolean -ppc_elf_finish_dynamic_symbol (bfd *output_bfd, - struct bfd_link_info *info, - struct elf_link_hash_entry *h, - Elf_Internal_Sym *sym) +write_global_sym_plt (struct elf_link_hash_entry *h, void *inf) { - struct ppc_elf_link_hash_table *htab; + struct bfd_link_info *info = (struct bfd_link_info *) inf; + struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info); struct plt_entry *ent; bfd_boolean doneone; -#ifdef DEBUG - fprintf (stderr, "ppc_elf_finish_dynamic_symbol called for %s", - h->root.root.string); -#endif - - htab = ppc_elf_hash_table (info); - BFD_ASSERT (htab->elf.dynobj != NULL); - doneone = FALSE; for (ent = h->plt.plist; ent != NULL; ent = ent->next) if (ent->plt.offset != (bfd_vma) -1) @@ -10090,10 +10118,10 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd, /* Fill in the .plt on VxWorks. */ if (bfd_link_pic (info)) { - bfd_put_32 (output_bfd, + bfd_put_32 (info->output_bfd, plt_entry[0] | PPC_HA (got_offset), htab->elf.splt->contents + ent->plt.offset + 0); - bfd_put_32 (output_bfd, + bfd_put_32 (info->output_bfd, plt_entry[1] | PPC_LO (got_offset), htab->elf.splt->contents + ent->plt.offset + 4); } @@ -10101,17 +10129,17 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd, { bfd_vma got_loc = got_offset + SYM_VAL (htab->elf.hgot); - bfd_put_32 (output_bfd, + bfd_put_32 (info->output_bfd, plt_entry[0] | PPC_HA (got_loc), htab->elf.splt->contents + ent->plt.offset + 0); - bfd_put_32 (output_bfd, + bfd_put_32 (info->output_bfd, plt_entry[1] | PPC_LO (got_loc), htab->elf.splt->contents + ent->plt.offset + 4); } - bfd_put_32 (output_bfd, plt_entry[2], + bfd_put_32 (info->output_bfd, plt_entry[2], htab->elf.splt->contents + ent->plt.offset + 8); - bfd_put_32 (output_bfd, plt_entry[3], + bfd_put_32 (info->output_bfd, plt_entry[3], htab->elf.splt->contents + ent->plt.offset + 12); /* This instruction is an immediate load. The value loaded is @@ -10120,7 +10148,7 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd, low-order 16 bits of the load instruction. */ /* NOTE: It appears that this is now an index rather than a prescaled offset. */ - bfd_put_32 (output_bfd, + bfd_put_32 (info->output_bfd, plt_entry[4] | reloc_index, htab->elf.splt->contents + ent->plt.offset + 16); /* This instruction is a PC-relative branch whose target is @@ -10129,21 +10157,22 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd, The address is encoded in bits 6-29, inclusive. The value stored is right-shifted by two bits, permitting a 26-bit offset. */ - bfd_put_32 (output_bfd, + bfd_put_32 (info->output_bfd, (plt_entry[5] | (-(ent->plt.offset + 20) & 0x03fffffc)), htab->elf.splt->contents + ent->plt.offset + 20); - bfd_put_32 (output_bfd, plt_entry[6], + bfd_put_32 (info->output_bfd, plt_entry[6], htab->elf.splt->contents + ent->plt.offset + 24); - bfd_put_32 (output_bfd, plt_entry[7], + bfd_put_32 (info->output_bfd, plt_entry[7], htab->elf.splt->contents + ent->plt.offset + 28); /* Fill in the GOT entry corresponding to this PLT slot with the address immediately after the "bctr" instruction in this PLT entry. */ - bfd_put_32 (output_bfd, (htab->elf.splt->output_section->vma - + htab->elf.splt->output_offset - + ent->plt.offset + 16), + bfd_put_32 (info->output_bfd, + (htab->elf.splt->output_section->vma + + htab->elf.splt->output_offset + + ent->plt.offset + 16), htab->elf.sgotplt->contents + got_offset); if (!bfd_link_pic (info)) @@ -10161,7 +10190,7 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd, rela.r_info = ELF32_R_INFO (htab->elf.hgot->indx, R_PPC_ADDR16_HA); rela.r_addend = got_offset; - bfd_elf32_swap_reloca_out (output_bfd, &rela, loc); + bfd_elf32_swap_reloca_out (info->output_bfd, &rela, loc); loc += sizeof (Elf32_External_Rela); /* Provide the @l relocation for the second instruction. */ @@ -10171,7 +10200,7 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd, rela.r_info = ELF32_R_INFO (htab->elf.hgot->indx, R_PPC_ADDR16_LO); rela.r_addend = got_offset; - bfd_elf32_swap_reloca_out (output_bfd, &rela, loc); + bfd_elf32_swap_reloca_out (info->output_bfd, &rela, loc); loc += sizeof (Elf32_External_Rela); /* Provide a relocation for the GOT entry corresponding to this @@ -10182,7 +10211,7 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd, rela.r_info = ELF32_R_INFO (htab->elf.hplt->indx, R_PPC_ADDR32); rela.r_addend = ent->plt.offset + 16; - bfd_elf32_swap_reloca_out (output_bfd, &rela, loc); + bfd_elf32_swap_reloca_out (info->output_bfd, &rela, loc); } /* VxWorks uses non-standard semantics for R_PPC_JMP_SLOT. @@ -10217,7 +10246,7 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd, bfd_vma val = (htab->glink_pltresolve + ent->plt.offset + htab->glink->output_section->vma + htab->glink->output_offset); - bfd_put_32 (output_bfd, val, + bfd_put_32 (info->output_bfd, val, splt->contents + ent->plt.offset); } } @@ -10252,44 +10281,7 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd, if (h->type == STT_GNU_IFUNC && is_static_defined (h)) htab->maybe_local_ifunc_resolver = 1; } - bfd_elf32_swap_reloca_out (output_bfd, &rela, loc); - - if (!h->def_regular) - { - /* Mark the symbol as undefined, rather than as - defined in the .plt section. Leave the value if - there were any relocations where pointer equality - matters (this is a clue for the dynamic linker, to - make function pointer comparisons work between an - application and shared library), otherwise set it - to zero. */ - sym->st_shndx = SHN_UNDEF; - if (!h->pointer_equality_needed) - sym->st_value = 0; - else if (!h->ref_regular_nonweak) - { - /* This breaks function pointer comparisons, but - that is better than breaking tests for a NULL - function pointer. */ - sym->st_value = 0; - } - } - else if (h->type == STT_GNU_IFUNC - && !bfd_link_pic (info)) - { - /* Set the value of ifunc symbols in a non-pie - executable to the glink entry. This is to avoid - text relocations. We can't do this for ifunc in - allocate_dynrelocs, as we do for normal dynamic - function symbols with plt entries, because we need - to keep the original value around for the ifunc - relocation. */ - sym->st_shndx = (_bfd_elf_section_from_bfd_section - (output_bfd, htab->glink->output_section)); - sym->st_value = (ent->glink_offset - + htab->glink->output_offset - + htab->glink->output_section->vma); - } + bfd_elf32_swap_reloca_out (info->output_bfd, &rela, loc); doneone = TRUE; } @@ -10298,14 +10290,14 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd, || h->dynindx == -1) { unsigned char *p; - asection *splt = htab->elf.splt; + asection *plt = htab->elf.splt; if (!htab->elf.dynamic_sections_created || h->dynindx == -1) - splt = htab->elf.iplt; + plt = htab->elf.iplt; p = (unsigned char *) htab->glink->contents + ent->glink_offset; - write_glink_stub (h, ent, splt, p, info); + write_glink_stub (h, ent, plt, p, info); if (!bfd_link_pic (info)) /* We only need one non-PIC glink stub. */ @@ -10314,6 +10306,165 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd, else break; } + return TRUE; +} + +/* Finish up PLT handling. */ + +bfd_boolean +ppc_finish_symbols (struct bfd_link_info *info) +{ + struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info); + bfd *ibfd; + + if (!htab) + return TRUE; + + elf_link_hash_traverse (&htab->elf, write_global_sym_plt, info); + + for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next) + { + bfd_vma *local_got, *end_local_got; + struct plt_entry **local_plt, **lplt, **end_local_plt; + Elf_Internal_Shdr *symtab_hdr; + bfd_size_type locsymcount; + Elf_Internal_Sym *local_syms = NULL; + struct plt_entry *ent; + + if (!is_ppc_elf (ibfd)) + continue; + + local_got = elf_local_got_offsets (ibfd); + if (!local_got) + continue; + + symtab_hdr = &elf_symtab_hdr (ibfd); + locsymcount = symtab_hdr->sh_info; + end_local_got = local_got + locsymcount; + local_plt = (struct plt_entry **) end_local_got; + end_local_plt = local_plt + locsymcount; + for (lplt = local_plt; lplt < end_local_plt; ++lplt) + for (ent = *lplt; ent != NULL; ent = ent->next) + { + if (ent->plt.offset != (bfd_vma) -1) + { + Elf_Internal_Sym *sym; + asection *sym_sec; + asection *plt, *relplt; + bfd_byte *loc; + bfd_vma val; + Elf_Internal_Rela rela; + + if (!get_sym_h (NULL, &sym, &sym_sec, NULL, &local_syms, + lplt - local_plt, ibfd)) + { + if (local_syms != NULL + && symtab_hdr->contents != (unsigned char *) local_syms) + free (local_syms); + return FALSE; + } + + val = sym->st_value; + if (sym_sec != NULL && sym_sec->output_section != NULL) + val += sym_sec->output_offset + sym_sec->output_section->vma; + + BFD_ASSERT (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC); + + htab->local_ifunc_resolver = 1; + plt = htab->elf.iplt; + relplt = htab->elf.irelplt; + + rela.r_offset = (ent->plt.offset + + plt->output_offset + + plt->output_section->vma); + rela.r_info = ELF32_R_INFO (0, R_PPC_IRELATIVE); + rela.r_addend = val; + loc = relplt->contents + (relplt->reloc_count++ + * sizeof (Elf32_External_Rela)); + bfd_elf32_swap_reloca_out (info->output_bfd, &rela, loc); + } + if ((ent->glink_offset & 1) == 0) + { + unsigned char *p = ((unsigned char *) htab->glink->contents + + ent->glink_offset); + + write_glink_stub (NULL, ent, htab->elf.iplt, p, info); + ent->glink_offset |= 1; + } + } + + if (local_syms != NULL + && symtab_hdr->contents != (unsigned char *) local_syms) + { + if (!info->keep_memory) + free (local_syms); + else + symtab_hdr->contents = (unsigned char *) local_syms; + } + } + return TRUE; +} + +/* Finish up dynamic symbol handling. We set the contents of various + dynamic sections here. */ + +static bfd_boolean +ppc_elf_finish_dynamic_symbol (bfd *output_bfd, + struct bfd_link_info *info, + struct elf_link_hash_entry *h, + Elf_Internal_Sym *sym) +{ + struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info); + struct plt_entry *ent; + +#ifdef DEBUG + fprintf (stderr, "ppc_elf_finish_dynamic_symbol called for %s", + h->root.root.string); +#endif + + if (!h->def_regular + || (h->type == STT_GNU_IFUNC && !bfd_link_pic (info))) + for (ent = h->plt.plist; ent != NULL; ent = ent->next) + if (ent->plt.offset != (bfd_vma) -1) + { + if (!h->def_regular) + { + /* Mark the symbol as undefined, rather than as + defined in the .plt section. Leave the value if + there were any relocations where pointer equality + matters (this is a clue for the dynamic linker, to + make function pointer comparisons work between an + application and shared library), otherwise set it + to zero. */ + sym->st_shndx = SHN_UNDEF; + if (!h->pointer_equality_needed) + sym->st_value = 0; + else if (!h->ref_regular_nonweak) + { + /* This breaks function pointer comparisons, but + that is better than breaking tests for a NULL + function pointer. */ + sym->st_value = 0; + } + } + else + { + /* Set the value of ifunc symbols in a non-pie + executable to the glink entry. This is to avoid + text relocations. We can't do this for ifunc in + allocate_dynrelocs, as we do for normal dynamic + function symbols with plt entries, because we need + to keep the original value around for the ifunc + relocation. */ + sym->st_shndx + = (_bfd_elf_section_from_bfd_section + (info->output_bfd, htab->glink->output_section)); + sym->st_value = (ent->glink_offset + + htab->glink->output_offset + + htab->glink->output_section->vma); + } + break; + } if (h->needs_copy) { diff --git a/bfd/elf32-ppc.h b/bfd/elf32-ppc.h index f56d027..265859b 100644 --- a/bfd/elf32-ppc.h +++ b/bfd/elf32-ppc.h @@ -63,6 +63,6 @@ int ppc_elf_select_plt_layout (bfd *, struct bfd_link_info *); asection *ppc_elf_tls_setup (bfd *, struct bfd_link_info *); bfd_boolean ppc_elf_tls_optimize (bfd *, struct bfd_link_info *); void ppc_elf_maybe_strip_sdata_syms (struct bfd_link_info *); -extern bfd_boolean ppc_elf_modify_segment_map (bfd *, - struct bfd_link_info * ATTRIBUTE_UNUSED); +extern bfd_boolean ppc_elf_modify_segment_map (bfd *, struct bfd_link_info *); extern bfd_boolean ppc_elf_section_processing (bfd *, Elf_Internal_Shdr *); +extern bfd_boolean ppc_finish_symbols (struct bfd_link_info *); diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index 835baec..8291db8 100644 --- a/bfd/elf64-ppc.c +++ b/bfd/elf64-ppc.c @@ -11158,29 +11158,6 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) dest += plt->output_offset + plt->output_section->vma; - if (stub_entry->h == NULL - && (stub_entry->plt_ent->plt.offset & 1) == 0) - { - Elf_Internal_Rela rela; - bfd_byte *rl; - - rela.r_offset = dest; - if (htab->opd_abi) - rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL); - else - rela.r_info = ELF64_R_INFO (0, R_PPC64_IRELATIVE); - rela.r_addend = (stub_entry->target_value - + stub_entry->target_section->output_offset - + stub_entry->target_section->output_section->vma); - - rl = (htab->elf.irelplt->contents - + (htab->elf.irelplt->reloc_count++ - * sizeof (Elf64_External_Rela))); - bfd_elf64_swap_reloca_out (info->output_bfd, &rela, rl); - stub_entry->plt_ent->plt.offset |= 1; - htab->local_ifunc_resolver = 1; - } - off = (dest - elf_gp (info->output_bfd) - htab->sec_info[stub_entry->group->link_sec->id].toc_off); @@ -13016,34 +12993,84 @@ ppc64_elf_set_toc (struct bfd_link_info *info, bfd *obfd) } /* Called via elf_link_hash_traverse from ppc64_elf_build_stubs to - write out any global entry stubs. */ + write out any global entry stubs, and PLT relocations. */ static bfd_boolean -build_global_entry_stubs (struct elf_link_hash_entry *h, void *inf) +build_global_entry_stubs_and_plt (struct elf_link_hash_entry *h, void *inf) { struct bfd_link_info *info; struct ppc_link_hash_table *htab; - struct plt_entry *pent; + struct plt_entry *ent; asection *s; if (h->root.type == bfd_link_hash_indirect) return TRUE; + info = inf; + htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; + + for (ent = h->plt.plist; ent != NULL; ent = ent->next) + if (ent->plt.offset != (bfd_vma) -1) + { + /* This symbol has an entry in the procedure linkage + table. Set it up. */ + Elf_Internal_Rela rela; + bfd_byte *loc; + + if (!htab->elf.dynamic_sections_created + || h->dynindx == -1) + { + if (!(h->def_regular + && (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak))) + continue; + rela.r_offset = (htab->elf.iplt->output_section->vma + + htab->elf.iplt->output_offset + + ent->plt.offset); + if (htab->opd_abi) + rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL); + else + rela.r_info = ELF64_R_INFO (0, R_PPC64_IRELATIVE); + rela.r_addend = (h->root.u.def.value + + h->root.u.def.section->output_offset + + h->root.u.def.section->output_section->vma + + ent->addend); + loc = (htab->elf.irelplt->contents + + (htab->elf.irelplt->reloc_count++ + * sizeof (Elf64_External_Rela))); + htab->local_ifunc_resolver = 1; + } + else + { + rela.r_offset = (htab->elf.splt->output_section->vma + + htab->elf.splt->output_offset + + ent->plt.offset); + rela.r_info = ELF64_R_INFO (h->dynindx, R_PPC64_JMP_SLOT); + rela.r_addend = ent->addend; + loc = (htab->elf.srelplt->contents + + ((ent->plt.offset - PLT_INITIAL_ENTRY_SIZE (htab)) + / PLT_ENTRY_SIZE (htab) * sizeof (Elf64_External_Rela))); + if (h->type == STT_GNU_IFUNC && is_static_defined (h)) + htab->maybe_local_ifunc_resolver = 1; + } + bfd_elf64_swap_reloca_out (info->output_bfd, &rela, loc); + } + if (!h->pointer_equality_needed) return TRUE; if (h->def_regular) return TRUE; - info = inf; - htab = ppc_hash_table (info); - if (htab == NULL) - return FALSE; - s = htab->global_entry; - for (pent = h->plt.plist; pent != NULL; pent = pent->next) - if (pent->plt.offset != (bfd_vma) -1 - && pent->addend == 0) + if (s == NULL || s->size == 0) + return TRUE; + + for (ent = h->plt.plist; ent != NULL; ent = ent->next) + if (ent->plt.offset != (bfd_vma) -1 + && ent->addend == 0) { bfd_byte *p; asection *plt; @@ -13054,7 +13081,7 @@ build_global_entry_stubs (struct elf_link_hash_entry *h, void *inf) if (!htab->elf.dynamic_sections_created || h->dynindx == -1) plt = htab->elf.iplt; - off = pent->plt.offset + plt->output_offset + plt->output_section->vma; + off = ent->plt.offset + plt->output_offset + plt->output_section->vma; off -= h->root.u.def.value + s->output_offset + s->output_section->vma; if (off + 0x80008000 > 0xffffffff || (off & 3) != 0) @@ -13108,6 +13135,91 @@ build_global_entry_stubs (struct elf_link_hash_entry *h, void *inf) return TRUE; } +/* Write PLT relocs for locals. */ + +static bfd_boolean +write_plt_relocs_for_local_syms (struct bfd_link_info *info) +{ + struct ppc_link_hash_table *htab = ppc_hash_table (info); + bfd *ibfd; + + for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next) + { + struct got_entry **lgot_ents, **end_lgot_ents; + struct plt_entry **local_plt, **lplt, **end_local_plt; + Elf_Internal_Shdr *symtab_hdr; + bfd_size_type locsymcount; + Elf_Internal_Sym *local_syms = NULL; + struct plt_entry *ent; + + if (!is_ppc64_elf (ibfd)) + continue; + + lgot_ents = elf_local_got_ents (ibfd); + if (!lgot_ents) + continue; + + symtab_hdr = &elf_symtab_hdr (ibfd); + locsymcount = symtab_hdr->sh_info; + end_lgot_ents = lgot_ents + locsymcount; + local_plt = (struct plt_entry **) end_lgot_ents; + end_local_plt = local_plt + locsymcount; + for (lplt = local_plt; lplt < end_local_plt; ++lplt) + for (ent = *lplt; ent != NULL; ent = ent->next) + if (ent->plt.offset != (bfd_vma) -1) + { + Elf_Internal_Sym *sym; + asection *sym_sec; + asection *plt, *relplt; + bfd_byte *loc; + bfd_vma val; + Elf_Internal_Rela rela; + + if (!get_sym_h (NULL, &sym, &sym_sec, NULL, &local_syms, + lplt - local_plt, ibfd)) + { + if (local_syms != NULL + && symtab_hdr->contents != (unsigned char *) local_syms) + free (local_syms); + return FALSE; + } + + val = sym->st_value + ent->addend; + val += PPC64_LOCAL_ENTRY_OFFSET (sym->st_other); + if (sym_sec != NULL && sym_sec->output_section != NULL) + val += sym_sec->output_offset + sym_sec->output_section->vma; + + BFD_ASSERT (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC); + + htab->local_ifunc_resolver = 1; + plt = htab->elf.iplt; + relplt = htab->elf.irelplt; + + rela.r_offset = (ent->plt.offset + + plt->output_offset + + plt->output_section->vma); + if (htab->opd_abi) + rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL); + else + rela.r_info = ELF64_R_INFO (0, R_PPC64_IRELATIVE); + rela.r_addend = val; + loc = relplt->contents + (relplt->reloc_count++ + * sizeof (Elf64_External_Rela)); + bfd_elf64_swap_reloca_out (info->output_bfd, &rela, loc); + } + + if (local_syms != NULL + && symtab_hdr->contents != (unsigned char *) local_syms) + { + if (!info->keep_memory) + free (local_syms); + else + symtab_hdr->contents = (unsigned char *) local_syms; + } + } + return TRUE; +} + /* 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. */ @@ -13262,9 +13374,11 @@ ppc64_elf_build_stubs (struct bfd_link_info *info, } } - /* Build .glink global entry stubs. */ - if (htab->global_entry != NULL && htab->global_entry->size != 0) - elf_link_hash_traverse (&htab->elf, build_global_entry_stubs, info); + /* Build .glink global entry stubs, and PLT relocs for globals. */ + elf_link_hash_traverse (&htab->elf, build_global_entry_stubs_and_plt, info); + + if (!write_plt_relocs_for_local_syms (info)) + return FALSE; if (htab->brlt != NULL && htab->brlt->size != 0) { @@ -15488,85 +15602,41 @@ ppc64_elf_finish_dynamic_symbol (bfd *output_bfd, { struct ppc_link_hash_table *htab; struct plt_entry *ent; - Elf_Internal_Rela rela; - bfd_byte *loc; htab = ppc_hash_table (info); if (htab == NULL) return FALSE; - for (ent = h->plt.plist; ent != NULL; ent = ent->next) - if (ent->plt.offset != (bfd_vma) -1) - { - /* This symbol has an entry in the procedure linkage - table. Set it up. */ - if (!htab->elf.dynamic_sections_created - || h->dynindx == -1) - { - BFD_ASSERT (h->type == STT_GNU_IFUNC - && h->def_regular - && (h->root.type == bfd_link_hash_defined - || h->root.type == bfd_link_hash_defweak)); - rela.r_offset = (htab->elf.iplt->output_section->vma - + htab->elf.iplt->output_offset - + ent->plt.offset); - if (htab->opd_abi) - rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL); - else - rela.r_info = ELF64_R_INFO (0, R_PPC64_IRELATIVE); - rela.r_addend = (h->root.u.def.value - + h->root.u.def.section->output_offset - + h->root.u.def.section->output_section->vma - + ent->addend); - loc = (htab->elf.irelplt->contents - + (htab->elf.irelplt->reloc_count++ - * sizeof (Elf64_External_Rela))); - htab->local_ifunc_resolver = 1; - } - else - { - rela.r_offset = (htab->elf.splt->output_section->vma - + htab->elf.splt->output_offset - + ent->plt.offset); - rela.r_info = ELF64_R_INFO (h->dynindx, R_PPC64_JMP_SLOT); - rela.r_addend = ent->addend; - loc = (htab->elf.srelplt->contents - + ((ent->plt.offset - PLT_INITIAL_ENTRY_SIZE (htab)) - / PLT_ENTRY_SIZE (htab) * sizeof (Elf64_External_Rela))); - if (h->type == STT_GNU_IFUNC && is_static_defined (h)) - htab->maybe_local_ifunc_resolver = 1; - } - bfd_elf64_swap_reloca_out (output_bfd, &rela, loc); - - if (!htab->opd_abi) - { - if (!h->def_regular) - { - /* Mark the symbol as undefined, rather than as - defined in glink. Leave the value if there were - any relocations where pointer equality matters - (this is a clue for the dynamic linker, to make - function pointer comparisons work between an - application and shared library), otherwise set it - to zero. */ - sym->st_shndx = SHN_UNDEF; - if (!h->pointer_equality_needed) - sym->st_value = 0; - else if (!h->ref_regular_nonweak) - { - /* This breaks function pointer comparisons, but - that is better than breaking tests for a NULL - function pointer. */ - sym->st_value = 0; - } - } - } - } + if (!htab->opd_abi && !h->def_regular) + for (ent = h->plt.plist; ent != NULL; ent = ent->next) + if (ent->plt.offset != (bfd_vma) -1) + { + /* Mark the symbol as undefined, rather than as + defined in glink. Leave the value if there were + any relocations where pointer equality matters + (this is a clue for the dynamic linker, to make + function pointer comparisons work between an + application and shared library), otherwise set it + to zero. */ + sym->st_shndx = SHN_UNDEF; + if (!h->pointer_equality_needed) + sym->st_value = 0; + else if (!h->ref_regular_nonweak) + { + /* This breaks function pointer comparisons, but + that is better than breaking tests for a NULL + function pointer. */ + sym->st_value = 0; + } + break; + } if (h->needs_copy) { /* This symbol needs a copy reloc. Set it up. */ + Elf_Internal_Rela rela; asection *srel; + bfd_byte *loc; if (h->dynindx == -1 || (h->root.type != bfd_link_hash_defined diff --git a/ld/ChangeLog b/ld/ChangeLog index b2fd135..4e697a3 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,7 @@ +2018-04-09 Alan Modra + + * ppc32elf.em (ppc_finish): Call ppc_finish_symbols. + 2018-04-05 H.J. Lu PR gas/22318 diff --git a/ld/emultempl/ppc32elf.em b/ld/emultempl/ppc32elf.em index 3c335b7..05a2894 100644 --- a/ld/emultempl/ppc32elf.em +++ b/ld/emultempl/ppc32elf.em @@ -212,6 +212,8 @@ ppc_finish (void) { if (params.ppc476_workaround) lang_for_each_statement (no_zero_padding); + if (!ppc_finish_symbols (&link_info)) + einfo (_("%X%P: ppc_finish_symbols problem %E\n")); finish_default (); } -- 2.7.4