This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
Re: PowerPC STT_GNU_IFUNC support
- From: Alan Modra <amodra at bigpond dot net dot au>
- To: binutils at sourceware dot org
- Date: Mon, 3 Aug 2009 19:52:07 +0930
- Subject: Re: PowerPC STT_GNU_IFUNC support
- References: <20090710122043.GI3181@bubble.grove.modra.org> <Pine.LNX.4.64.0907142028530.11824@digraph.polyomino.org.uk> <20090722231612.GB13233@bubble.grove.modra.org> <20090729145356.GA14121@bubble.grove.modra.org> <20090729145834.GB14121@bubble.grove.modra.org> <20090729152223.GC14121@bubble.grove.modra.org>
This implements HJ's suggestion to define ifunc symbols in non-pie
executables on the plt call stub, exactly as we do for plt calls to
functions in dynamic libraries. With this patch, all the current
glibc ifunc tests pass on powerpc (with my both my glibc patches
applied of course).
* elf32-ppc.c (ppc_elf_check_relocs): Always add a plt ref count
for local ifunc symbols in non-pie executables, regardless of
reloc type. Don't specially create ifunc dyn relocs. Tidy ifunc
code so that it's obvious that we only do anything special for
local ifunc syms.
(ppc_elf_gc_sweep_hook): Adjust to suit check_relocs changes.
(allocate_dynrelocs): Correct comment for syms defined in plt.
Don't specially allocate ifunc dyn relocs.
(ppc_elf_relax_section): Relax branches to ifunc plt entries too.
(ppc_elf_relocate_section): Set "relocation" value for ifunc
syms in non-pie executables. No specially allocated dyn relocs
for ifunc to write. Allow for local sym on R_PPC_RELAX32_PLT.
(ppc_elf_finish_dynamic_symbol): Set value of ifunc symbols in
a non-pie executable.
Index: bfd/elf32-ppc.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-ppc.c,v
retrieving revision 1.261
diff -u -p -r1.261 elf32-ppc.c
--- bfd/elf32-ppc.c 29 Jul 2009 14:56:37 -0000 1.261
+++ bfd/elf32-ppc.c 3 Aug 2009 06:55:42 -0000
@@ -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 (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 (!htab->is_vxworks
+ && is_branch_reloc (r_type)
+ && 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;
}
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))
- {
- 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)
+ if (!htab->is_vxworks
+ && h == NULL
+ && local_got_refcounts != NULL
+ && (!info->shared
+ || is_branch_reloc (r_type)))
+ {
+ 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
/* 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
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
}
}
}
- 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_bf
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_bf
&& 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_bf
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 *outp
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;
}
--
Alan Modra
Australia Development Lab, IBM