diff options
author | Alan Modra <amodra@gmail.com> | 2013-03-28 13:36:32 +0000 |
---|---|---|
committer | Alan Modra <amodra@gmail.com> | 2013-03-28 13:36:32 +0000 |
commit | 19e08130eec4abee547e82c5485603423deea193 (patch) | |
tree | b2ea2b92811daf8bfd72336fae8fbaad98d499eb /bfd | |
parent | 7f7cc26500d04c09295d1229706c4fc35697fc8a (diff) | |
download | gdb-19e08130eec4abee547e82c5485603423deea193.zip gdb-19e08130eec4abee547e82c5485603423deea193.tar.gz gdb-19e08130eec4abee547e82c5485603423deea193.tar.bz2 |
* elf64-ppc.c (struct ppc_dyn_relocs): New.
(ppc64_elf_check_relocs): Separate dynrel counts for local syms
into ifunc and non-ifunc.
(dec_dynrel_count): Pass in sym rather than sym_sec. Handle
separate ifunc/non-ifunc dynrel counts.
(allocate_got): Always use reliplt for ifunc.
(allocate_dynrelocs): Likewise.
(ppc64_elf_size_dynamic_sections): Likewise.
(ppc64_elf_layout_multitoc): Likewise.
(ppc64_elf_relocate_section): Likewise.
Diffstat (limited to 'bfd')
-rw-r--r-- | bfd/ChangeLog | 13 | ||||
-rw-r--r-- | bfd/elf64-ppc.c | 264 |
2 files changed, 176 insertions, 101 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog index c70bcff..d115964 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,5 +1,18 @@ 2013-03-28 Alan Modra <amodra@gmail.com> + * elf64-ppc.c (struct ppc_dyn_relocs): New. + (ppc64_elf_check_relocs): Separate dynrel counts for local syms + into ifunc and non-ifunc. + (dec_dynrel_count): Pass in sym rather than sym_sec. Handle + separate ifunc/non-ifunc dynrel counts. + (allocate_got): Always use reliplt for ifunc. + (allocate_dynrelocs): Likewise. + (ppc64_elf_size_dynamic_sections): Likewise. + (ppc64_elf_layout_multitoc): Likewise. + (ppc64_elf_relocate_section): Likewise. + +2013-03-28 Alan Modra <amodra@gmail.com> + * elf32-ppc.c (struct ppc_dyn_relocs): New. (ppc_elf_check_relocs): Separate dynrel counts for local syms into ifunc and non-ifunc. diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index 9d7c79e..2826d83 100644 --- a/bfd/elf64-ppc.c +++ b/bfd/elf64-ppc.c @@ -3638,6 +3638,21 @@ struct ppc_branch_hash_entry { unsigned int iter; }; +/* Used to track dynamic relocations for local symbols. */ +struct ppc_dyn_relocs +{ + struct ppc_dyn_relocs *next; + + /* The input section of the reloc. */ + asection *sec; + + /* Total number of relocs copied for the input section. */ + unsigned int count : 31; + + /* Whether this entry is for STT_GNU_IFUNC symbols. */ + unsigned int ifunc : 1; +}; + struct ppc_link_hash_entry { struct elf_link_hash_entry elf; @@ -5448,9 +5463,6 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, || (!info->shared && ifunc != NULL)) { - struct elf_dyn_relocs *p; - struct elf_dyn_relocs **head; - /* We must copy these reloc types into the output file. Create a reloc section in dynobj and make room for this reloc. */ @@ -5467,13 +5479,34 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, relocations we need for this symbol. */ if (h != NULL) { + struct elf_dyn_relocs *p; + struct elf_dyn_relocs **head; + head = &((struct ppc_link_hash_entry *) h)->dyn_relocs; + p = *head; + if (p == NULL || p->sec != sec) + { + p = bfd_alloc (htab->elf.dynobj, sizeof *p); + if (p == NULL) + return FALSE; + p->next = *head; + *head = p; + p->sec = sec; + p->count = 0; + p->pc_count = 0; + } + p->count += 1; + if (!must_be_dyn_reloc (info, r_type)) + p->pc_count += 1; } else { /* Track dynamic relocs needed for local syms too. We really need local syms available to do this easily. Oh well. */ + struct ppc_dyn_relocs *p; + struct ppc_dyn_relocs **head; + bfd_boolean is_ifunc; asection *s; void *vpp; Elf_Internal_Sym *isym; @@ -5488,25 +5521,24 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, s = sec; vpp = &elf_section_data (s)->local_dynrel; - head = (struct elf_dyn_relocs **) vpp; - } - - p = *head; - if (p == NULL || p->sec != sec) - { - p = bfd_alloc (htab->elf.dynobj, sizeof *p); - if (p == NULL) - return FALSE; - p->next = *head; - *head = p; - p->sec = sec; - p->count = 0; - p->pc_count = 0; + head = (struct ppc_dyn_relocs **) vpp; + is_ifunc = ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC; + p = *head; + if (p != NULL && p->sec == sec && p->ifunc != is_ifunc) + p = p->next; + if (p == NULL || p->sec != sec || p->ifunc != is_ifunc) + { + p = bfd_alloc (htab->elf.dynobj, sizeof *p); + if (p == NULL) + return FALSE; + p->next = *head; + *head = p; + p->sec = sec; + p->ifunc = is_ifunc; + p->count = 0; + } + p->count += 1; } - - p->count += 1; - if (!must_be_dyn_reloc (info, r_type)) - p->pc_count += 1; } break; @@ -6951,7 +6983,7 @@ adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED) } /* Handles decrementing dynamic reloc counts for the reloc specified by - R_INFO in section SEC. If LOCAL_SYMS is NULL, then H and SYM_SEC + R_INFO in section SEC. If LOCAL_SYMS is NULL, then H and SYM have already been determined. */ static bfd_boolean @@ -6960,11 +6992,10 @@ dec_dynrel_count (bfd_vma r_info, struct bfd_link_info *info, Elf_Internal_Sym **local_syms, struct elf_link_hash_entry *h, - asection *sym_sec) + Elf_Internal_Sym *sym) { enum elf_ppc64_reloc_type r_type; - struct elf_dyn_relocs *p; - struct elf_dyn_relocs **pp; + asection *sym_sec = NULL; /* Can this reloc be dynamic? This switch, and later tests here should be kept in sync with the code in check_relocs. */ @@ -7019,7 +7050,6 @@ dec_dynrel_count (bfd_vma r_info, if (local_syms != NULL) { unsigned long r_symndx; - Elf_Internal_Sym *sym; bfd *ibfd = sec->owner; r_symndx = ELF64_R_SYM (r_info); @@ -7043,40 +7073,62 @@ dec_dynrel_count (bfd_vma r_info, return TRUE; if (h != NULL) - pp = &((struct ppc_link_hash_entry *) h)->dyn_relocs; - else { - if (sym_sec != NULL) - { - void *vpp = &elf_section_data (sym_sec)->local_dynrel; - pp = (struct elf_dyn_relocs **) vpp; - } - else + struct elf_dyn_relocs *p; + struct elf_dyn_relocs **pp; + pp = &((struct ppc_link_hash_entry *) h)->dyn_relocs; + + /* elf_gc_sweep may have already removed all dyn relocs associated + with local syms for a given section. Also, symbol flags are + changed by elf_gc_sweep_symbol, confusing the test above. Don't + report a dynreloc miscount. */ + if (*pp == NULL && info->gc_sections) + return TRUE; + + while ((p = *pp) != NULL) { - void *vpp = &elf_section_data (sec)->local_dynrel; - pp = (struct elf_dyn_relocs **) vpp; + if (p->sec == sec) + { + if (!must_be_dyn_reloc (info, r_type)) + p->pc_count -= 1; + p->count -= 1; + if (p->count == 0) + *pp = p->next; + return TRUE; + } + pp = &p->next; } } + else + { + struct ppc_dyn_relocs *p; + struct ppc_dyn_relocs **pp; + void *vpp; + bfd_boolean is_ifunc; - /* elf_gc_sweep may have already removed all dyn relocs associated - with local syms for a given section. Also, symbol flags are - changed by elf_gc_sweep_symbol, confusing the test above. Don't - report a dynreloc miscount. */ - if (*pp == NULL && info->gc_sections) - return TRUE; + if (local_syms == NULL) + sym_sec = bfd_section_from_elf_index (sec->owner, sym->st_shndx); + if (sym_sec == NULL) + sym_sec = sec; - while ((p = *pp) != NULL) - { - if (p->sec == sec) + vpp = &elf_section_data (sym_sec)->local_dynrel; + pp = (struct ppc_dyn_relocs **) vpp; + + if (*pp == NULL && info->gc_sections) + return TRUE; + + is_ifunc = ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC; + while ((p = *pp) != NULL) { - if (!must_be_dyn_reloc (info, r_type)) - p->pc_count -= 1; - p->count -= 1; - if (p->count == 0) - *pp = p->next; - return TRUE; + if (p->sec == sec && p->ifunc == is_ifunc) + { + p->count -= 1; + if (p->count == 0) + *pp = p->next; + return TRUE; + } + pp = &p->next; } - pp = &p->next; } info->callbacks->einfo (_("%P: dynreloc miscount for %B, section %A\n"), @@ -7409,7 +7461,7 @@ ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping) if (!NO_OPD_RELOCS && !info->relocatable && !dec_dynrel_count (rel->r_info, sec, info, - NULL, h, sym_sec)) + NULL, h, sym)) goto error_ret; } else @@ -8016,13 +8068,13 @@ ppc64_elf_tls_optimize (struct bfd_link_info *info) /* If we got rid of a DTPMOD/DTPREL reloc pair then we'll lose one or two dyn relocs. */ if (!dec_dynrel_count (rel->r_info, sec, info, - NULL, h, sym_sec)) + NULL, h, sym)) return FALSE; if (tls_set == (TLS_EXPLICIT | TLS_GD)) { if (!dec_dynrel_count ((rel + 1)->r_info, sec, info, - NULL, h, sym_sec)) + NULL, h, sym)) return FALSE; } } @@ -8814,19 +8866,18 @@ allocate_got (struct elf_link_hash_entry *h, got->size += entsize; dyn = htab->elf.dynamic_sections_created; - if ((info->shared - || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)) - && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT - || h->root.type != bfd_link_hash_undefweak)) + if (h->type == STT_GNU_IFUNC) { - asection *relgot = ppc64_elf_tdata (gent->owner)->relgot; - relgot->size += rentsize; + htab->reliplt->size += rentsize; + htab->got_reli_size += rentsize; } - else if (h->type == STT_GNU_IFUNC) + else if ((info->shared + || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)) + && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT + || h->root.type != bfd_link_hash_undefweak)) { - asection *relgot = htab->reliplt; + asection *relgot = ppc64_elf_tdata (gent->owner)->relgot; relgot->size += rentsize; - htab->got_reli_size += rentsize; } } @@ -9090,7 +9141,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) for (p = eh->dyn_relocs; p != NULL; p = p->next) { asection *sreloc = elf_section_data (p->sec)->sreloc; - if (!htab->elf.dynamic_sections_created) + if (eh->elf.type == STT_GNU_IFUNC) sreloc = htab->reliplt; sreloc->size += p->count * sizeof (Elf64_External_Rela); } @@ -9169,14 +9220,13 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, unsigned char *lgot_masks; bfd_size_type locsymcount; Elf_Internal_Shdr *symtab_hdr; - asection *srel; if (!is_ppc64_elf (ibfd)) continue; for (s = ibfd->sections; s != NULL; s = s->next) { - struct elf_dyn_relocs *p; + struct ppc_dyn_relocs *p; for (p = elf_section_data (s)->local_dynrel; p != NULL; p = p->next) { @@ -9190,8 +9240,8 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, } else if (p->count != 0) { - srel = elf_section_data (p->sec)->sreloc; - if (!htab->elf.dynamic_sections_created) + asection *srel = elf_section_data (p->sec)->sreloc; + if (p->ifunc) srel = htab->reliplt; srel->size += p->count * sizeof (Elf64_External_Rela); if ((p->sec->output_section->flags & SEC_READONLY) != 0) @@ -9211,7 +9261,6 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, end_local_plt = local_plt + locsymcount; lgot_masks = (unsigned char *) end_local_plt; s = ppc64_elf_tdata (ibfd)->got; - srel = ppc64_elf_tdata (ibfd)->relgot; for (; lgot_ents < end_lgot_ents; ++lgot_ents, ++lgot_masks) { struct got_entry **pent, *ent; @@ -9227,19 +9276,25 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, } else { - unsigned int num = 1; + unsigned int ent_size = 8; + unsigned int rel_size = sizeof (Elf64_External_Rela); + ent->got.offset = s->size; if ((ent->tls_type & *lgot_masks & TLS_GD) != 0) - num = 2; - s->size += num * 8; - if (info->shared) - srel->size += num * sizeof (Elf64_External_Rela); - else if ((*lgot_masks & PLT_IFUNC) != 0) { - htab->reliplt->size - += num * sizeof (Elf64_External_Rela); - htab->got_reli_size - += num * sizeof (Elf64_External_Rela); + ent_size *= 2; + rel_size *= 2; + } + s->size += ent_size; + if ((*lgot_masks & PLT_IFUNC) != 0) + { + htab->reliplt->size += rel_size; + htab->got_reli_size += rel_size; + } + else if (info->shared) + { + asection *srel = ppc64_elf_tdata (ibfd)->relgot; + srel->size += rel_size; } pent = &ent->next; } @@ -10738,7 +10793,7 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info) unsigned char *lgot_masks; bfd_size_type locsymcount; Elf_Internal_Shdr *symtab_hdr; - asection *s, *srel; + asection *s; if (!is_ppc64_elf (ibfd)) continue; @@ -10754,26 +10809,31 @@ ppc64_elf_layout_multitoc (struct bfd_link_info *info) end_local_plt = local_plt + locsymcount; lgot_masks = (unsigned char *) end_local_plt; s = ppc64_elf_tdata (ibfd)->got; - srel = ppc64_elf_tdata (ibfd)->relgot; for (; lgot_ents < end_lgot_ents; ++lgot_ents, ++lgot_masks) { struct got_entry *ent; for (ent = *lgot_ents; ent != NULL; ent = ent->next) { - unsigned int num = 1; + unsigned int ent_size = 8; + unsigned int rel_size = sizeof (Elf64_External_Rela); + ent->got.offset = s->size; if ((ent->tls_type & *lgot_masks & TLS_GD) != 0) - num = 2; - s->size += num * 8; - if (info->shared) - srel->size += num * sizeof (Elf64_External_Rela); - else if ((*lgot_masks & PLT_IFUNC) != 0) { - htab->reliplt->size - += num * sizeof (Elf64_External_Rela); - htab->got_reli_size - += num * sizeof (Elf64_External_Rela); + ent_size *= 2; + rel_size *= 2; + } + s->size += ent_size; + if ((*lgot_masks & PLT_IFUNC) != 0) + { + htab->reliplt->size += rel_size; + htab->got_reli_size += rel_size; + } + else if (info->shared) + { + asection *srel = ppc64_elf_tdata (ibfd)->relgot; + srel->size += rel_size; } } } @@ -13208,15 +13268,15 @@ ppc64_elf_relocate_section (bfd *output_bfd, ifunc = (h != NULL ? h->elf.type == STT_GNU_IFUNC : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC); - if ((info->shared || indx != 0) - && (h == NULL - || (tls_type == (TLS_TLS | TLS_LD) - && !h->elf.def_dynamic) - || ELF_ST_VISIBILITY (h->elf.other) == STV_DEFAULT - || h->elf.root.type != bfd_link_hash_undefweak)) - relgot = ppc64_elf_tdata (ent->owner)->relgot; - else if (ifunc) + if (ifunc) relgot = htab->reliplt; + else if ((info->shared || indx != 0) + && (h == NULL + || (tls_type == (TLS_TLS | TLS_LD) + && !h->elf.def_dynamic) + || ELF_ST_VISIBILITY (h->elf.other) == STV_DEFAULT + || h->elf.root.type != bfd_link_hash_undefweak)) + relgot = ppc64_elf_tdata (ent->owner)->relgot; if (relgot != NULL) { outrel.r_offset = (got->output_section->vma @@ -13630,7 +13690,9 @@ ppc64_elf_relocate_section (bfd *output_bfd, } sreloc = elf_section_data (input_section)->sreloc; - if (!htab->elf.dynamic_sections_created) + if (h != NULL + ? h->elf.type == STT_GNU_IFUNC + : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC) sreloc = htab->reliplt; if (sreloc == NULL) abort (); |