aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf32-ppc.c
diff options
context:
space:
mode:
Diffstat (limited to 'bfd/elf32-ppc.c')
-rw-r--r--bfd/elf32-ppc.c255
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;
}