diff options
Diffstat (limited to 'bfd/elf32-ppc.c')
-rw-r--r-- | bfd/elf32-ppc.c | 255 |
1 files changed, 143 insertions, 112 deletions
diff --git a/bfd/elf32-ppc.c b/bfd/elf32-ppc.c index d1e2cfc..2fbf01b 100644 --- a/bfd/elf32-ppc.c +++ b/bfd/elf32-ppc.c @@ -3458,15 +3458,13 @@ ppc_elf_check_relocs (bfd *abfd, tls_type = 0; ifunc = NULL; + r_type = ELF32_R_TYPE (rel->r_info); if (!htab->is_vxworks) { if (h != NULL) { if (h->type == STT_GNU_IFUNC) - { - h->needs_plt = 1; - ifunc = &h->plt.plist; - } + ifunc = &h->plt.plist; } else { @@ -3475,46 +3473,47 @@ ppc_elf_check_relocs (bfd *abfd, if (isym == NULL) return FALSE; - if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC) + if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC + && (!info->shared + || is_branch_reloc (r_type))) { + bfd_vma addend; + ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx, PLT_IFUNC); if (ifunc == NULL) return FALSE; + + /* STT_GNU_IFUNC symbols must have a PLT entry; + In a non-pie executable even when there are + no plt calls. */ + addend = 0; + if (r_type == R_PPC_PLTREL24) + { + ppc_elf_tdata (abfd)->makes_plt_call = 1; + addend = rel->r_addend; + } + if (!update_plt_info (abfd, ifunc, + addend < 32768 ? NULL : got2, addend)) + return FALSE; } } } - r_type = ELF32_R_TYPE (rel->r_info); - if (!htab->is_vxworks && is_branch_reloc (r_type)) + if (!htab->is_vxworks + && is_branch_reloc (r_type) + && h != NULL + && h == tga) { - if (h != NULL && h == tga) - { - if (rel != relocs - && (ELF32_R_TYPE (rel[-1].r_info) == R_PPC_TLSGD - || ELF32_R_TYPE (rel[-1].r_info) == R_PPC_TLSLD)) - /* We have a new-style __tls_get_addr call with a marker - reloc. */ - ; - else - /* Mark this section as having an old-style call. */ - sec->has_tls_get_addr_call = 1; - } - - /* STT_GNU_IFUNC symbols must have a PLT entry. */ - if (ifunc != NULL) - { - bfd_vma addend = 0; - - if (r_type == R_PPC_PLTREL24) - { - ppc_elf_tdata (abfd)->makes_plt_call = 1; - addend = rel->r_addend; - } - if (!update_plt_info (abfd, ifunc, - addend < 32768 ? NULL : got2, addend)) - return FALSE; - } + if (rel != relocs + && (ELF32_R_TYPE (rel[-1].r_info) == R_PPC_TLSGD + || ELF32_R_TYPE (rel[-1].r_info) == R_PPC_TLSLD)) + /* We have a new-style __tls_get_addr call with a marker + reloc. */ + ; + else + /* Mark this section as having an old-style call. */ + sec->has_tls_get_addr_call = 1; } switch (r_type) @@ -3690,7 +3689,7 @@ ppc_elf_check_relocs (bfd *abfd, break; case R_PPC_PLTREL24: - if (h == NULL || ifunc != NULL) + if (h == NULL) break; /* Fall through */ case R_PPC_PLT32: @@ -3903,8 +3902,7 @@ ppc_elf_check_relocs (bfd *abfd, /* We may need a plt entry if the symbol turns out to be a function defined in a dynamic object. */ h->needs_plt = 1; - if (ifunc == NULL - && !update_plt_info (abfd, &h->plt.plist, NULL, 0)) + if (!update_plt_info (abfd, &h->plt.plist, NULL, 0)) return FALSE; break; } @@ -3941,9 +3939,7 @@ ppc_elf_check_relocs (bfd *abfd, && !info->shared && h != NULL && (h->root.type == bfd_link_hash_defweak - || !h->def_regular)) - || (!info->shared - && ifunc != NULL)) + || !h->def_regular))) { struct ppc_elf_dyn_relocs *p; struct ppc_elf_dyn_relocs **head; @@ -4415,25 +4411,19 @@ ppc_elf_gc_sweep_hook (bfd *abfd, } r_type = ELF32_R_TYPE (rel->r_info); - if (!htab->is_vxworks && is_branch_reloc (r_type)) + if (!htab->is_vxworks + && h == NULL + && local_got_refcounts != NULL + && (!info->shared + || is_branch_reloc (r_type))) { - struct plt_entry **ifunc = NULL; - if (h != NULL) - { - if (h->type == STT_GNU_IFUNC) - ifunc = &h->plt.plist; - } - else if (local_got_refcounts != NULL) - { - struct plt_entry **local_plt = (struct plt_entry **) - (local_got_refcounts + symtab_hdr->sh_info); - char *local_got_tls_masks = (char *) - (local_plt + symtab_hdr->sh_info); - if ((local_got_tls_masks[r_symndx] & PLT_IFUNC) != 0) - ifunc = local_plt + r_symndx; - } - if (ifunc != NULL) + struct plt_entry **local_plt = (struct plt_entry **) + (local_got_refcounts + symtab_hdr->sh_info); + char *local_got_tls_masks = (char *) + (local_plt + symtab_hdr->sh_info); + if ((local_got_tls_masks[r_symndx] & PLT_IFUNC) != 0) { + struct plt_entry **ifunc = local_plt + r_symndx; bfd_vma addend = r_type == R_PPC_PLTREL24 ? rel->r_addend : 0; struct plt_entry *ent = find_plt_ent (ifunc, got2, addend); if (ent->plt.refcount > 0) @@ -5166,8 +5156,9 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) /* If this symbol is not defined in a regular file, and we are not generating a shared - library, then set the symbol to this location - in the .plt. This is required to make + library, then set the symbol to this location + in the .plt. This is to avoid text + relocations, and is required to make function pointers compare as equal between the normal executable and the shared library. */ if (! info->shared @@ -5313,8 +5304,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) eh->elf.got.offset = (bfd_vma) -1; if (eh->dyn_relocs == NULL - || (!htab->elf.dynamic_sections_created - && h->type != STT_GNU_IFUNC)) + || !htab->elf.dynamic_sections_created) return TRUE; /* In the shared -Bsymbolic case, discard space allocated for @@ -5385,11 +5375,6 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) } } } - else if (h->type == STT_GNU_IFUNC) - { - if (!h->non_got_ref) - eh->dyn_relocs = NULL; - } else if (ELIMINATE_COPY_RELOCS) { /* For the non-shared case, discard space for relocs against @@ -5938,6 +5923,7 @@ ppc_elf_relax_section (bfd *abfd, bfd_vma max_branch_offset, val; bfd_byte *hit_addr; unsigned long t0; + struct elf_link_hash_entry *h; unsigned char sym_type; switch (r_type) @@ -5959,6 +5945,7 @@ 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. */ @@ -5992,7 +5979,6 @@ ppc_elf_relax_section (bfd *abfd, { /* Global symbol handling. */ unsigned long indx; - struct elf_link_hash_entry *h; indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info; h = elf_sym_hashes (abfd)[indx]; @@ -6003,26 +5989,6 @@ ppc_elf_relax_section (bfd *abfd, tsec = NULL; toff = 0; - if (r_type == R_PPC_PLTREL24 - && htab->plt != NULL) - { - struct plt_entry *ent = find_plt_ent (&h->plt.plist, - got2, irel->r_addend); - - if (ent != NULL) - { - if (htab->plt_type == PLT_NEW) - { - tsec = htab->glink; - toff = ent->glink_offset; - } - else - { - tsec = htab->plt; - toff = ent->plt.offset; - } - } - } if (tsec != NULL) ; else if (h->root.type == bfd_link_hash_defined @@ -6043,6 +6009,46 @@ ppc_elf_relax_section (bfd *abfd, sym_type = h->type; } + if (is_branch_reloc (r_type)) + { + struct plt_entry **plist = NULL; + + if (h != NULL) + plist = &h->plt.plist; + else if (sym_type == STT_GNU_IFUNC) + { + bfd_vma *local_got_offsets = elf_local_got_offsets (abfd); + struct plt_entry **local_plt = (struct plt_entry **) + (local_got_offsets + symtab_hdr->sh_info); + plist = local_plt + ELF32_R_SYM (irel->r_info); + } + if (plist != NULL) + { + bfd_vma addend = 0; + struct plt_entry *ent; + + if (r_type == R_PPC_PLTREL24) + addend = irel->r_addend; + ent = find_plt_ent (plist, got2, addend); + if (ent != NULL) + { + if (htab->plt_type == PLT_NEW + || h == NULL + || !htab->elf.dynamic_sections_created + || h->dynindx == -1) + { + tsec = htab->glink; + toff = ent->glink_offset; + } + else + { + tsec = htab->plt; + toff = ent->plt.offset; + } + } + } + } + /* If the branch and target are in the same section, you have no hope of adding stubs. We'll error out later should the branch overflow. */ @@ -6940,25 +6946,35 @@ ppc_elf_relocate_section (bfd *output_bfd, ifunc = NULL; if (!htab->is_vxworks) { + struct plt_entry *ent; + if (h != NULL) { if (h->type == STT_GNU_IFUNC) ifunc = &h->plt.plist; } - else if (local_got_offsets != NULL) + else if (local_got_offsets != NULL + && ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC) { - if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC) - { - struct plt_entry **local_plt = (struct plt_entry **) - (local_got_offsets + symtab_hdr->sh_info); + struct plt_entry **local_plt; - ifunc = local_plt + r_symndx; - } + local_plt = (struct plt_entry **) (local_got_offsets + + symtab_hdr->sh_info); + ifunc = local_plt + r_symndx; } - if (ifunc != NULL && is_branch_reloc (r_type)) - { - struct plt_entry *ent = find_plt_ent (ifunc, got2, rel->r_addend); + ent = NULL; + if (ifunc != NULL + && (!info->shared + || is_branch_reloc (r_type))) + { + addend = 0; + if (r_type == R_PPC_PLTREL24) + addend = rel->r_addend; + ent = find_plt_ent (ifunc, got2, addend); + } + if (ent != NULL) + { if (h == NULL && (ent->plt.offset & 1) == 0) { Elf_Internal_Rela rela; @@ -7385,9 +7401,7 @@ ppc_elf_relocate_section (bfd *output_bfd, && h != NULL && h->dynindx != -1 && !h->non_got_ref - && !h->def_regular) - || (!info->shared - && ifunc != NULL)) + && !h->def_regular)) { int skip; @@ -7526,18 +7540,19 @@ ppc_elf_relocate_section (bfd *output_bfd, case R_PPC_RELAX32PC_PLT: case R_PPC_RELAX32_PLT: - { - struct plt_entry *ent = find_plt_ent (&h->plt.plist, got2, addend); - - if (htab->plt_type == PLT_NEW) - relocation = (htab->glink->output_section->vma - + htab->glink->output_offset - + ent->glink_offset); - else - relocation = (htab->plt->output_section->vma - + htab->plt->output_offset - + ent->plt.offset); - } + if (h != NULL) + { + struct plt_entry *ent = find_plt_ent (&h->plt.plist, got2, + addend); + if (htab->plt_type == PLT_NEW) + relocation = (htab->glink->output_section->vma + + htab->glink->output_offset + + ent->glink_offset); + else + relocation = (htab->plt->output_section->vma + + htab->plt->output_offset + + ent->plt.offset); + } if (r_type == R_PPC_RELAX32_PLT) goto relax32; /* Fall thru */ @@ -8164,6 +8179,22 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd, sym->st_value = 0; } } + else if (h->type == STT_GNU_IFUNC + && !info->shared) + { + /* 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); + } doneone = TRUE; } |