* 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.
This commit is contained in:
Alan Modra 2009-08-03 10:23:18 +00:00
parent 0329406f62
commit de972ffadd
2 changed files with 160 additions and 112 deletions

View File

@ -1,3 +1,20 @@
2009-08-03 Alan Modra <amodra@bigpond.net.au>
* 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.
2009-08-02 H.J. Lu <hongjiu.lu@intel.com> 2009-08-02 H.J. Lu <hongjiu.lu@intel.com>
Jakub Jelinek <jakub@redhat.com> Jakub Jelinek <jakub@redhat.com>

View File

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