This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
Re: PowerPC STT_GNU_IFUNC support
On Thu, Jul 23, 2009 at 08:46:12AM +0930, Alan Modra wrote:
> On Tue, Jul 14, 2009 at 08:31:59PM +0000, Joseph S. Myers wrote:
> > On Fri, 10 Jul 2009, Alan Modra wrote:
> >
> > > include/elf/
> > > * ppc.h (R_PPC_IRELATIVE): Add.
> > > (R_PPC_RELAX32, R_PPC_RELAX32PC,
> > > R_PPC_RELAX32_PLT, R_PPC_RELAX32PC_PLT): Renumber.
> > > * ppc64.h (R_PPC64_IRELATIVE): Add.
> >
> > Do you have specifications for the new relocations? They'll need to be
> > looked at by the Power.org ABI working group, whether for the present
> > revision or a future revision. (We're thinking of working on the 64-bit
> > ABI once the 32-bit ABI revision is finished, though the 64-bit ABI needs
> > a lot less work done to it.)
I haven't forgotten about the spec, but it's still unwritten.
> I probably should have written a spec before I started, and I may have
> realised that PowerPC needs two new relocations. On PLT entries we
> need a relocation that behaves like R_PPC_JMP_SLOT, while for GOT
> entries we need one that behaves like R_PPC_RELATIVE. For the new
> secureplt ABI, the PLT is really just another GOT. Unsurprisingly,
> R_PPC_JMP_SLOT behaves just like GOT relocs there. However, for the
> old ABI, R_PPC_JMP_SLOT does a whole lot of work, creating various
> branches and other data. It's rather too messy to expect one new
> relocation to handle both situations.
>
> PowerPC64 similarly needs two new relocations. In the PLT, one new
> reloc needs to copy a 24 byte OPD entry, while in the GOT the other
> new reloc just writes an 8 byte word.
This patch is 1 of 2 to fix the rather awful ifunc support I added for
powerpc. I was able to use just one reloc for ppc32 ifunc plt
entries, by always using secure-plt style entries. However, powerpc64
still needs another reloc to copy the three word function descriptors
from .opd to iplt. I also added the REL16 relocs to ppc64, since
we'll need them when writing indirect functions. (In assembly. It
isn't possible with current ABIs/compiler to do so in C.)
include/elf/
* ppc64.h: Add R_PPC64_JMP_IREL, R_PPC64_REL16, R_PPC64_REL16_LO,
R_PPC64_REL16_HI, R_PPC64_REL16_HA.
bfd/
* elf32-ppc.c (ppc_elf_check_relocs): Always set up sections
used by indirect function support. Count dynamic relocs for
ifunc syms.
(ppc_elf_adjust_dynamic_symbol): Tweak for ifunc.
(allocate_dynrelocs): Allocate all non-dynamic ifunc plt entries
in iplt and their relocs in reliplt. Don't make ifunc syms
dynamic. Allocate got entry relocs for non-dynamic ifunc in
reliplt. Handle other dynamic relocs for ifunc.
(ppc_elf_size_dynamic_sections): Alloc dyn relocs for static
ifunc in reliplt, likewise relocs for got against local ifunc.
Typo fix on reliplt size adjust.
(ppc_elf_relocate_section): Don't use plt scheme of allocating
relocs 1-1 with entries for iplt, instead just add using
reloc_count. Write got relocs and dyn relocs for ifunc to reliplt.
Error on invalid ifunc dyn relocs.
(ppc_elf_finish_dynamic_symbol): Adjust for non-dynamic ifunc plt
in iplt/reliplt.
* elf64-ppc.c (ppc64_elf_howto_raw): Add R_PPC64_JMP_IREL,
R_PPC64_REL16, R_PPC64_REL16_LO, R_PPC64_REL16_HI, R_PPC64_REL16_HA.
(ppc64_elf_reloc_type_lookup): Handle new relocs.
(ppc64_elf_check_relocs): Likewise. Count dyn relocs for ifunc.
(allocate_dynrelocs): As for elf32-ppc.c above.
(ppc64_elf_size_dynamic_sections): Likewise.
(ppc_build_one_stub): Put non-dynamic ifunc plt call stubs in iplt,
and their relocs in reliplt. Use R_PPC64_JMP_IREL.
(ppc_size_one_stub): Similarly.
(ppc64_elf_relocate_section): As for elf32-ppc.c above. Handle new
relocs too.
(ppc64_elf_finish_dynamic_symbol): As for elf32-ppc.c above.
Index: include/elf/ppc64.h
===================================================================
RCS file: /cvs/src/src/include/elf/ppc64.h,v
retrieving revision 1.6
diff -u -p -r1.6 ppc64.h
--- include/elf/ppc64.h 10 Jul 2009 12:19:55 -0000 1.6
+++ include/elf/ppc64.h 29 Jul 2009 09:40:18 -0000
@@ -140,8 +140,15 @@ START_RELOC_NUMBERS (elf_ppc64_reloc_typ
RELOC_NUMBER (R_PPC64_TLSLD, 108)
/* Support STT_GNU_IFUNC plt calls. */
+ RELOC_NUMBER (R_PPC64_JMP_IREL, 247)
RELOC_NUMBER (R_PPC64_IRELATIVE, 248)
+/* These are GNU extensions used in PIC code sequences. */
+ RELOC_NUMBER (R_PPC64_REL16, 249)
+ RELOC_NUMBER (R_PPC64_REL16_LO, 250)
+ RELOC_NUMBER (R_PPC64_REL16_HI, 251)
+ RELOC_NUMBER (R_PPC64_REL16_HA, 252)
+
/* These are GNU extensions to enable C++ vtable garbage collection. */
RELOC_NUMBER (R_PPC64_GNU_VTINHERIT, 253)
RELOC_NUMBER (R_PPC64_GNU_VTENTRY, 254)
Index: bfd/elf32-ppc.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-ppc.c,v
retrieving revision 1.260
diff -u -p -r1.260 elf32-ppc.c
--- bfd/elf32-ppc.c 10 Jul 2009 12:19:57 -0000 1.260
+++ bfd/elf32-ppc.c 29 Jul 2009 09:39:56 -0000
@@ -3408,6 +3408,13 @@ ppc_elf_check_relocs (bfd *abfd,
ppc_elf_howto_init ();
htab = ppc_elf_hash_table (info);
+ if (htab->glink == NULL)
+ {
+ if (htab->elf.dynobj == NULL)
+ htab->elf.dynobj = abfd;
+ if (!ppc_elf_create_glink (htab->elf.dynobj, info))
+ return FALSE;
+ }
tga = elf_link_hash_lookup (&htab->elf, "__tls_get_addr",
FALSE, FALSE, TRUE);
symtab_hdr = &elf_symtab_hdr (abfd);
@@ -3451,23 +3458,8 @@ ppc_elf_check_relocs (bfd *abfd,
tls_type = 0;
ifunc = NULL;
- r_type = ELF32_R_TYPE (rel->r_info);
- if (!htab->is_vxworks && is_branch_reloc (r_type))
+ if (!htab->is_vxworks)
{
- 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 (h != NULL)
{
if (h->type == STT_GNU_IFUNC)
@@ -3491,6 +3483,25 @@ ppc_elf_check_relocs (bfd *abfd,
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;
@@ -3503,14 +3514,6 @@ ppc_elf_check_relocs (bfd *abfd,
if (!update_plt_info (abfd, ifunc,
addend < 32768 ? NULL : got2, addend))
return FALSE;
-
- if (htab->glink == NULL)
- {
- if (htab->elf.dynobj == NULL)
- htab->elf.dynobj = abfd;
- if (!ppc_elf_create_glink (htab->elf.dynobj, info))
- return FALSE;
- }
}
}
@@ -3938,7 +3941,9 @@ ppc_elf_check_relocs (bfd *abfd,
&& !info->shared
&& h != NULL
&& (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 **head;
@@ -4859,8 +4864,10 @@ ppc_elf_adjust_dynamic_symbol (struct bf
}
else
{
- /* After adjust_dynamic_symbol, non_got_ref set means that
- dyn_relocs for this symbol should be discarded.
+ /* After adjust_dynamic_symbol, non_got_ref set in the
+ non-shared case means that we have allocated space in
+ .dynbss for the symbol and thus dyn_relocs for this
+ symbol should be discarded.
If we get here we know we are making a PLT entry for this
symbol, and in an executable we'd normally resolve
relocations against this symbol to the PLT entry. Allow
@@ -4868,6 +4875,7 @@ ppc_elf_adjust_dynamic_symbol (struct bf
relocs will not cause text relocation. */
if (!h->ref_regular_nonweak
&& h->non_got_ref
+ && h->type != STT_GNU_IFUNC
&& !htab->is_vxworks
&& !ppc_elf_hash_entry (h)->has_sda_refs
&& !readonly_dynrelocs (h))
@@ -5105,10 +5113,10 @@ allocate_dynrelocs (struct elf_link_hash
|| WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h))
{
asection *s = htab->plt;
- if (!dyn)
+ if (!dyn || h->dynindx == -1)
s = htab->iplt;
- if (htab->plt_type == PLT_NEW || !dyn)
+ if (htab->plt_type == PLT_NEW || !dyn || h->dynindx == -1)
{
if (!doneone)
{
@@ -5186,7 +5194,8 @@ allocate_dynrelocs (struct elf_link_hash
/* We also need to make an entry in the .rela.plt section. */
if (!doneone)
{
- if (!htab->elf.dynamic_sections_created)
+ if (!htab->elf.dynamic_sections_created
+ || h->dynindx == -1)
htab->reliplt->size += sizeof (Elf32_External_Rela);
else
{
@@ -5246,7 +5255,7 @@ allocate_dynrelocs (struct elf_link_hash
/* Make sure this symbol is output as a dynamic symbol. */
if (eh->elf.dynindx == -1
&& !eh->elf.forced_local
- && !eh->elf.def_regular
+ && eh->elf.type != STT_GNU_IFUNC
&& htab->elf.dynamic_sections_created)
{
if (!bfd_elf_link_record_dynamic_symbol (info, &eh->elf))
@@ -5279,19 +5288,24 @@ allocate_dynrelocs (struct elf_link_hash
eh->elf.got.offset = (bfd_vma) -1;
else
{
+ asection *rsec = NULL;
eh->elf.got.offset = allocate_got (htab, need);
dyn = htab->elf.dynamic_sections_created;
if ((info->shared
|| WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, &eh->elf))
&& (ELF_ST_VISIBILITY (eh->elf.other) == STV_DEFAULT
|| eh->elf.root.type != bfd_link_hash_undefweak))
+ rsec = htab->relgot;
+ else if (h->type == STT_GNU_IFUNC)
+ rsec = htab->reliplt;
+ if (rsec != NULL)
{
/* All the entries we allocated need relocs.
Except LD only needs one. */
if ((eh->tls_mask & TLS_LD) != 0
&& eh->elf.def_dynamic)
need -= 4;
- htab->relgot->size += need * (sizeof (Elf32_External_Rela) / 4);
+ rsec->size += need * (sizeof (Elf32_External_Rela) / 4);
}
}
}
@@ -5299,7 +5313,8 @@ allocate_dynrelocs (struct elf_link_hash
eh->elf.got.offset = (bfd_vma) -1;
if (eh->dyn_relocs == NULL
- || !htab->elf.dynamic_sections_created)
+ || (!htab->elf.dynamic_sections_created
+ && h->type != STT_GNU_IFUNC))
return TRUE;
/* In the shared -Bsymbolic case, discard space allocated for
@@ -5370,6 +5385,11 @@ 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
@@ -5382,8 +5402,7 @@ allocate_dynrelocs (struct elf_link_hash
/* Make sure this symbol is output as a dynamic symbol.
Undefined weak syms won't yet be marked as dynamic. */
if (h->dynindx == -1
- && !h->forced_local
- && !h->def_regular)
+ && !h->forced_local)
{
if (! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
@@ -5404,6 +5423,8 @@ allocate_dynrelocs (struct elf_link_hash
for (p = eh->dyn_relocs; p != NULL; p = p->next)
{
asection *sreloc = elf_section_data (p->sec)->sreloc;
+ if (!htab->elf.dynamic_sections_created)
+ sreloc = htab->reliplt;
sreloc->size += p->count * sizeof (Elf32_External_Rela);
}
@@ -5508,8 +5529,10 @@ ppc_elf_size_dynamic_sections (bfd *outp
}
else if (p->count != 0)
{
- elf_section_data (p->sec)->sreloc->size
- += p->count * sizeof (Elf32_External_Rela);
+ asection *sreloc = elf_section_data (p->sec)->sreloc;
+ if (!htab->elf.dynamic_sections_created)
+ sreloc = htab->reliplt;
+ sreloc->size += p->count * sizeof (Elf32_External_Rela);
if ((p->sec->output_section->flags
& (SEC_READONLY | SEC_ALLOC))
== (SEC_READONLY | SEC_ALLOC))
@@ -5553,6 +5576,9 @@ ppc_elf_size_dynamic_sections (bfd *outp
if (info->shared)
htab->relgot->size += (need
* (sizeof (Elf32_External_Rela) / 4));
+ else if ((*lgot_masks & PLT_IFUNC) != 0)
+ htab->reliplt->size += (need
+ * (sizeof (Elf32_External_Rela) / 4));
}
}
else
@@ -5590,7 +5616,7 @@ ppc_elf_size_dynamic_sections (bfd *outp
if (!doneone)
{
- htab->reliplt += sizeof (Elf32_External_Rela);
+ htab->reliplt->size += sizeof (Elf32_External_Rela);
doneone = TRUE;
}
}
@@ -6912,7 +6938,7 @@ ppc_elf_relocate_section (bfd *output_bf
}
ifunc = NULL;
- if (!htab->is_vxworks && is_branch_reloc (r_type))
+ if (!htab->is_vxworks)
{
if (h != NULL)
{
@@ -6929,7 +6955,7 @@ ppc_elf_relocate_section (bfd *output_bf
ifunc = local_plt + r_symndx;
}
}
- if (ifunc != NULL)
+ if (ifunc != NULL && is_branch_reloc (r_type))
{
struct plt_entry *ent = find_plt_ent (ifunc, got2, rel->r_addend);
@@ -6943,8 +6969,9 @@ ppc_elf_relocate_section (bfd *output_bf
+ ent->plt.offset);
rela.r_info = ELF32_R_INFO (0, R_PPC_IRELATIVE);
rela.r_addend = relocation;
- loc = (htab->reliplt->contents
- + ent->plt.offset * sizeof (Elf32_External_Rela) / 4);
+ loc = htab->reliplt->contents;
+ loc += (htab->reliplt->reloc_count++
+ * sizeof (Elf32_External_Rela));
bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
ent->plt.offset |= 1;
@@ -7081,6 +7108,7 @@ ppc_elf_relocate_section (bfd *output_bf
off &= ~1;
else
{
+ asection *rsec;
unsigned int tls_m = (tls_mask
& (TLS_LD | TLS_GD | TLS_DTPREL
| TLS_TPREL | TLS_TPRELGD));
@@ -7119,11 +7147,16 @@ ppc_elf_relocate_section (bfd *output_bf
}
/* Generate relocs for the dynamic linker. */
+ rsec = NULL;
if ((info->shared || indx != 0)
&& (offp == &htab->tlsld_got.offset
|| h == NULL
|| ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
|| h->root.type != bfd_link_hash_undefweak))
+ rsec = htab->relgot;
+ else if (ifunc != NULL)
+ rsec = htab->reliplt;
+ if (rsec != NULL)
{
outrel.r_offset = (htab->got->output_section->vma
+ htab->got->output_offset
@@ -7134,8 +7167,8 @@ ppc_elf_relocate_section (bfd *output_bf
outrel.r_info = ELF32_R_INFO (indx, R_PPC_DTPMOD32);
if (tls_ty == (TLS_TLS | TLS_GD))
{
- loc = htab->relgot->contents;
- loc += (htab->relgot->reloc_count++
+ loc = rsec->contents;
+ loc += (rsec->reloc_count++
* sizeof (Elf32_External_Rela));
bfd_elf32_swap_reloca_out (output_bfd,
&outrel, loc);
@@ -7148,18 +7181,20 @@ ppc_elf_relocate_section (bfd *output_bf
outrel.r_info = ELF32_R_INFO (indx, R_PPC_DTPREL32);
else if (tls_ty == (TLS_TLS | TLS_TPREL))
outrel.r_info = ELF32_R_INFO (indx, R_PPC_TPREL32);
- else if (indx == 0)
- outrel.r_info = ELF32_R_INFO (indx, R_PPC_RELATIVE);
- else
+ else if (indx != 0)
outrel.r_info = ELF32_R_INFO (indx, R_PPC_GLOB_DAT);
+ else if (ifunc != NULL)
+ outrel.r_info = ELF32_R_INFO (0, R_PPC_IRELATIVE);
+ else
+ outrel.r_info = ELF32_R_INFO (0, R_PPC_RELATIVE);
if (indx == 0 && tls_ty != (TLS_TLS | TLS_LD))
{
outrel.r_addend += relocation;
if (tls_ty & (TLS_GD | TLS_DTPREL | TLS_TPREL))
outrel.r_addend -= htab->elf.tls_sec->vma;
}
- loc = htab->relgot->contents;
- loc += (htab->relgot->reloc_count++
+ loc = rsec->contents;
+ loc += (rsec->reloc_count++
* sizeof (Elf32_External_Rela));
bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
}
@@ -7350,7 +7385,9 @@ ppc_elf_relocate_section (bfd *output_bf
&& h != NULL
&& h->dynindx != -1
&& !h->non_got_ref
- && !h->def_regular))
+ && !h->def_regular)
+ || (!info->shared
+ && ifunc != NULL))
{
int skip;
@@ -7366,8 +7403,9 @@ ppc_elf_relocate_section (bfd *output_bf
time. */
if (sreloc == NULL)
{
- sreloc = _bfd_elf_get_dynamic_reloc_section
- (input_bfd, input_section, /*rela?*/ TRUE);
+ sreloc = elf_section_data (input_section)->sreloc;
+ if (!htab->elf.dynamic_sections_created)
+ sreloc = htab->reliplt;
if (sreloc == NULL)
return FALSE;
}
@@ -7397,13 +7435,35 @@ ppc_elf_relocate_section (bfd *output_bf
{
outrel.r_addend = relocation + rel->r_addend;
- if (r_type == R_PPC_ADDR32)
- outrel.r_info = ELF32_R_INFO (0, R_PPC_RELATIVE);
- else
+ if (r_type != R_PPC_ADDR32)
{
long indx = 0;
- if (r_symndx == 0 || bfd_is_abs_section (sec))
+ if (ifunc != NULL)
+ {
+ /* If we get here when building a static
+ executable, then the libc startup function
+ responsible for applying indirect function
+ relocations is going to complain about
+ the reloc type.
+ If we get here when building a dynamic
+ executable, it will be because we have
+ a text relocation. The dynamic loader
+ will set the text segment writable and
+ non-executable to apply text relocations.
+ So we'll segfault when trying to run the
+ indirection function to resolve the reloc. */
+ (*_bfd_error_handler)
+ (_("%B(%A+0x%lx): relocation %s for indirect "
+ "function %s unsupported"),
+ input_bfd,
+ input_section,
+ (long) rel->r_offset,
+ howto->name,
+ sym_name);
+ ret = FALSE;
+ }
+ else if (r_symndx == 0 || bfd_is_abs_section (sec))
;
else if (sec == NULL || sec->owner == NULL)
{
@@ -7418,7 +7478,8 @@ ppc_elf_relocate_section (bfd *output_bf
against a section symbol. It would be
proper to subtract the symbol's value,
osec->vma, from the emitted reloc addend,
- but ld.so expects buggy relocs. */
+ but ld.so expects buggy relocs.
+ FIXME: Why not always use a zero index? */
osec = sec->output_section;
indx = elf_section_data (osec)->dynindx;
if (indx == 0)
@@ -7437,6 +7498,10 @@ ppc_elf_relocate_section (bfd *output_bf
outrel.r_info = ELF32_R_INFO (indx, r_type);
}
+ else if (ifunc != NULL)
+ outrel.r_info = ELF32_R_INFO (0, R_PPC_IRELATIVE);
+ else
+ outrel.r_info = ELF32_R_INFO (0, R_PPC_RELATIVE);
}
loc = sreloc->contents;
@@ -7891,7 +7956,8 @@ ppc_elf_finish_dynamic_symbol (bfd *outp
bfd_vma reloc_index;
if (htab->plt_type == PLT_NEW
- || !htab->elf.dynamic_sections_created)
+ || !htab->elf.dynamic_sections_created
+ || h->dynindx == -1)
reloc_index = ent->plt.offset / 4;
else
{
@@ -7905,7 +7971,8 @@ ppc_elf_finish_dynamic_symbol (bfd *outp
/* This symbol has an entry in the procedure linkage table.
Set it up. */
if (htab->plt_type == PLT_VXWORKS
- && htab->elf.dynamic_sections_created)
+ && htab->elf.dynamic_sections_created
+ && h->dynindx != -1)
{
bfd_vma got_offset;
const bfd_vma *plt_entry;
@@ -8028,14 +8095,16 @@ ppc_elf_finish_dynamic_symbol (bfd *outp
else
{
asection *splt = htab->plt;
- if (!htab->elf.dynamic_sections_created)
+ if (!htab->elf.dynamic_sections_created
+ || h->dynindx == -1)
splt = htab->iplt;
rela.r_offset = (splt->output_section->vma
+ splt->output_offset
+ ent->plt.offset);
if (htab->plt_type == PLT_OLD
- || !htab->elf.dynamic_sections_created)
+ || !htab->elf.dynamic_sections_created
+ || h->dynindx == -1)
{
/* We don't need to fill in the .plt. The ppc dynamic
linker will fill it in. */
@@ -8065,11 +8134,14 @@ ppc_elf_finish_dynamic_symbol (bfd *outp
else
rela.r_info = ELF32_R_INFO (h->dynindx, R_PPC_JMP_SLOT);
- if (!htab->elf.dynamic_sections_created)
- loc = htab->reliplt->contents;
+ if (!htab->elf.dynamic_sections_created
+ || h->dynindx == -1)
+ loc = (htab->reliplt->contents
+ + (htab->reliplt->reloc_count++
+ * sizeof (Elf32_External_Rela)));
else
- loc = htab->relplt->contents;
- loc += reloc_index * sizeof (Elf32_External_Rela);
+ loc = (htab->relplt->contents
+ + reloc_index * sizeof (Elf32_External_Rela));
bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
if (!h->def_regular)
@@ -8096,10 +8168,12 @@ ppc_elf_finish_dynamic_symbol (bfd *outp
}
if (htab->plt_type == PLT_NEW
- || !htab->elf.dynamic_sections_created)
+ || !htab->elf.dynamic_sections_created
+ || h->dynindx == -1)
{
asection *splt = htab->plt;
- if (!htab->elf.dynamic_sections_created)
+ if (!htab->elf.dynamic_sections_created
+ || h->dynindx == -1)
splt = htab->iplt;
write_glink_stub (ent, splt, info);
Index: bfd/elf64-ppc.c
===================================================================
RCS file: /cvs/src/src/bfd/elf64-ppc.c,v
retrieving revision 1.298
diff -u -p -r1.298 elf64-ppc.c
--- bfd/elf64-ppc.c 10 Jul 2009 12:19:57 -0000 1.298
+++ bfd/elf64-ppc.c 29 Jul 2009 09:40:00 -0000
@@ -1874,6 +1874,20 @@ static reloc_howto_type ppc64_elf_howto_
0xffff, /* dst_mask */
FALSE), /* pcrel_offset */
+ HOWTO (R_PPC64_JMP_IREL, /* type */
+ 0, /* rightshift */
+ 0, /* size (0=byte, 1=short, 2=long, 4=64 bits) */
+ 0, /* bitsize */
+ FALSE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ ppc64_elf_unhandled_reloc, /* special_function */
+ "R_PPC64_JMP_IREL", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0, /* dst_mask */
+ FALSE), /* pcrel_offset */
+
HOWTO (R_PPC64_IRELATIVE, /* type */
0, /* rightshift */
4, /* size (0=byte, 1=short, 2=long, 4=64 bits) */
@@ -1888,6 +1902,67 @@ static reloc_howto_type ppc64_elf_howto_
ONES (64), /* dst_mask */
FALSE), /* pcrel_offset */
+ /* A 16 bit relative relocation. */
+ HOWTO (R_PPC64_REL16, /* type */
+ 0, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_bitfield, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_PPC64_REL16", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffff, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* A 16 bit relative relocation without overflow. */
+ HOWTO (R_PPC64_REL16_LO, /* type */
+ 0, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont,/* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_PPC64_REL16_LO", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffff, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* The high order 16 bits of a relative address. */
+ HOWTO (R_PPC64_REL16_HI, /* type */
+ 16, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ bfd_elf_generic_reloc, /* special_function */
+ "R_PPC64_REL16_HI", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffff, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
+ /* The high order 16 bits of a relative address, plus 1 if the contents of
+ the low 16 bits, treated as a signed number, is negative. */
+ HOWTO (R_PPC64_REL16_HA, /* type */
+ 16, /* rightshift */
+ 1, /* size (0 = byte, 1 = short, 2 = long) */
+ 16, /* bitsize */
+ TRUE, /* pc_relative */
+ 0, /* bitpos */
+ complain_overflow_dont, /* complain_on_overflow */
+ ppc64_elf_ha_reloc, /* special_function */
+ "R_PPC64_REL16_HA", /* name */
+ FALSE, /* partial_inplace */
+ 0, /* src_mask */
+ 0xffff, /* dst_mask */
+ TRUE), /* pcrel_offset */
+
/* GNU extension to record C++ vtable hierarchy. */
HOWTO (R_PPC64_GNU_VTINHERIT, /* type */
0, /* rightshift */
@@ -2156,6 +2231,14 @@ ppc64_elf_reloc_type_lookup (bfd *abfd A
break;
case BFD_RELOC_PPC64_DTPREL16_HIGHESTA: r = R_PPC64_DTPREL16_HIGHESTA;
break;
+ case BFD_RELOC_16_PCREL: r = R_PPC64_REL16;
+ break;
+ case BFD_RELOC_LO16_PCREL: r = R_PPC64_REL16_LO;
+ break;
+ case BFD_RELOC_HI16_PCREL: r = R_PPC64_REL16_HI;
+ break;
+ case BFD_RELOC_HI16_S_PCREL: r = R_PPC64_REL16_HA;
+ break;
case BFD_RELOC_VTABLE_INHERIT: r = R_PPC64_GNU_VTINHERIT;
break;
case BFD_RELOC_VTABLE_ENTRY: r = R_PPC64_GNU_VTENTRY;
@@ -4763,6 +4846,29 @@ ppc64_elf_check_relocs (bfd *abfd, struc
tls_type = 0;
ifunc = NULL;
+ if (h != NULL)
+ {
+ if (h->type == STT_GNU_IFUNC)
+ {
+ h->needs_plt = 1;
+ ifunc = &h->plt.plist;
+ }
+ }
+ else
+ {
+ Elf_Internal_Sym *isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+ abfd, r_symndx);
+ if (isym == NULL)
+ return FALSE;
+
+ if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
+ {
+ ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx,
+ rel->r_addend, PLT_IFUNC);
+ if (ifunc == NULL)
+ return FALSE;
+ }
+ }
r_type = ELF64_R_TYPE (rel->r_info);
if (is_branch_reloc (r_type))
{
@@ -4780,29 +4886,6 @@ ppc64_elf_check_relocs (bfd *abfd, struc
}
/* STT_GNU_IFUNC symbols must have a PLT entry. */
- if (h != NULL)
- {
- if (h->type == STT_GNU_IFUNC)
- {
- h->needs_plt = 1;
- ifunc = &h->plt.plist;
- }
- }
- else
- {
- Elf_Internal_Sym *isym = bfd_sym_from_r_symndx (&htab->sym_cache,
- abfd, r_symndx);
- if (isym == NULL)
- return FALSE;
-
- if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
- {
- ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx,
- rel->r_addend, PLT_IFUNC);
- if (ifunc == NULL)
- return FALSE;
- }
- }
if (ifunc != NULL
&& !update_plt_info (abfd, ifunc, rel->r_addend))
return FALSE;
@@ -4944,6 +5027,12 @@ ppc64_elf_check_relocs (bfd *abfd, struc
break;
/* Nor do these. */
+ case R_PPC64_REL16:
+ case R_PPC64_REL16_LO:
+ case R_PPC64_REL16_HI:
+ case R_PPC64_REL16_HA:
+ break;
+
case R_PPC64_TOC16:
case R_PPC64_TOC16_LO:
case R_PPC64_TOC16_HI:
@@ -5194,7 +5283,9 @@ ppc64_elf_check_relocs (bfd *abfd, struc
&& !info->shared
&& h != NULL
&& (h->root.type == bfd_link_hash_defweak
- || !h->def_regular)))
+ || !h->def_regular))
+ || (!info->shared
+ && ifunc != NULL))
{
struct ppc_dyn_relocs *p;
struct ppc_dyn_relocs **head;
@@ -7990,7 +8081,8 @@ allocate_dynrelocs (struct elf_link_hash
for (pent = h->plt.plist; pent != NULL; pent = pent->next)
if (pent->plt.refcount > 0)
{
- if (!htab->elf.dynamic_sections_created)
+ if (!htab->elf.dynamic_sections_created
+ || h->dynindx == -1)
{
s = htab->iplt;
pent->plt.offset = s->size;
@@ -8069,12 +8161,14 @@ allocate_dynrelocs (struct elf_link_hash
if (gent->got.refcount > 0)
{
bfd_boolean dyn;
+ asection *rsec;
/* Make sure this symbol is output as a dynamic symbol.
Undefined weak syms won't yet be marked as dynamic,
nor will all TLS symbols. */
if (h->dynindx == -1
&& !h->forced_local
+ && h->type != STT_GNU_IFUNC
&& htab->elf.dynamic_sections_created)
{
if (! bfd_elf_link_record_dynamic_symbol (info, h))
@@ -8097,20 +8191,25 @@ allocate_dynrelocs (struct elf_link_hash
s->size
+= (gent->tls_type & eh->tls_mask & (TLS_GD | TLS_LD)) ? 16 : 8;
dyn = htab->elf.dynamic_sections_created;
+ rsec = NULL;
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))
- ppc64_elf_tdata (gent->owner)->relgot->size
- += (gent->tls_type & eh->tls_mask & TLS_GD
- ? 2 * sizeof (Elf64_External_Rela)
- : sizeof (Elf64_External_Rela));
+ rsec = ppc64_elf_tdata (gent->owner)->relgot;
+ else if (h->type == STT_GNU_IFUNC)
+ rsec = htab->reliplt;
+ if (rsec != NULL)
+ rsec->size += (gent->tls_type & eh->tls_mask & TLS_GD
+ ? 2 * sizeof (Elf64_External_Rela)
+ : sizeof (Elf64_External_Rela));
}
else
gent->got.offset = (bfd_vma) -1;
if (eh->dyn_relocs == NULL
- || !htab->elf.dynamic_sections_created)
+ || (!htab->elf.dynamic_sections_created
+ && h->type != STT_GNU_IFUNC))
return TRUE;
/* In the shared -Bsymbolic case, discard space allocated for
@@ -8160,6 +8259,11 @@ 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
@@ -8193,6 +8297,8 @@ allocate_dynrelocs (struct elf_link_hash
for (p = eh->dyn_relocs; p != NULL; p = p->next)
{
asection *sreloc = elf_section_data (p->sec)->sreloc;
+ if (!htab->elf.dynamic_sections_created)
+ sreloc = htab->reliplt;
sreloc->size += p->count * sizeof (Elf64_External_Rela);
}
@@ -8291,6 +8397,8 @@ ppc64_elf_size_dynamic_sections (bfd *ou
else if (p->count != 0)
{
srel = elf_section_data (p->sec)->sreloc;
+ if (!htab->elf.dynamic_sections_created)
+ srel = htab->reliplt;
srel->size += p->count * sizeof (Elf64_External_Rela);
if ((p->sec->output_section->flags & SEC_READONLY) != 0)
info->flags |= DF_TEXTREL;
@@ -8324,19 +8432,15 @@ ppc64_elf_size_dynamic_sections (bfd *ou
}
else
{
+ unsigned int num = 1;
ent->got.offset = s->size;
if ((ent->tls_type & *lgot_masks & TLS_GD) != 0)
- {
- s->size += 16;
- if (info->shared)
- srel->size += 2 * sizeof (Elf64_External_Rela);
- }
- else
- {
- s->size += 8;
- if (info->shared)
- srel->size += sizeof (Elf64_External_Rela);
- }
+ 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);
}
}
else
@@ -8356,7 +8460,7 @@ ppc64_elf_size_dynamic_sections (bfd *ou
ent->plt.offset = s->size;
s->size += PLT_ENTRY_SIZE;
- htab->reliplt += sizeof (Elf64_External_Rela);
+ htab->reliplt->size += sizeof (Elf64_External_Rela);
}
else
ent->plt.offset = (bfd_vma) -1;
@@ -9029,7 +9133,9 @@ ppc_build_one_stub (struct bfd_hash_entr
abort ();
plt = htab->plt;
- if (!htab->elf.dynamic_sections_created)
+ if (!htab->elf.dynamic_sections_created
+ || stub_entry->h == NULL
+ || stub_entry->h->elf.dynindx == -1)
plt = htab->iplt;
dest += plt->output_offset + plt->output_section->vma;
@@ -9041,20 +9147,15 @@ ppc_build_one_stub (struct bfd_hash_entr
bfd_byte *rl;
rela.r_offset = dest;
- rela.r_info = ELF64_R_INFO (0, R_PPC64_IRELATIVE);
+ rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL);
rela.r_addend = (stub_entry->target_value
+ stub_entry->target_section->output_offset
+ stub_entry->target_section->output_section->vma);
- if (!htab->elf.dynamic_sections_created)
- rl = (htab->reliplt->contents
- + (stub_entry->plt_ent->plt.offset
- / (PLT_ENTRY_SIZE / sizeof (Elf64_External_Rela))));
- else
- rl = (htab->relplt->contents
- + ((stub_entry->plt_ent->plt.offset - PLT_INITIAL_ENTRY_SIZE)
- / (PLT_ENTRY_SIZE / sizeof (Elf64_External_Rela))));
- bfd_elf32_swap_reloca_out (info->output_bfd, &rela, rl);
+ rl = (htab->reliplt->contents
+ + (htab->reliplt->reloc_count++
+ * sizeof (Elf64_External_Rela)));
+ bfd_elf64_swap_reloca_out (info->output_bfd, &rela, rl);
stub_entry->plt_ent->plt.offset |= 1;
}
@@ -9162,7 +9263,9 @@ ppc_size_one_stub (struct bfd_hash_entry
if (off >= (bfd_vma) -2)
abort ();
plt = htab->plt;
- if (!htab->elf.dynamic_sections_created)
+ if (!htab->elf.dynamic_sections_created
+ || stub_entry->h == NULL
+ || stub_entry->h->elf.dynindx == -1)
plt = htab->iplt;
off += (plt->output_offset
+ plt->output_section->vma
@@ -11093,7 +11196,9 @@ ppc64_elf_relocate_section (bfd *output_
&& sec->output_section != NULL
&& sec->id <= htab->top_id
&& (htab->stub_group[sec->id].toc_off
- != htab->stub_group[input_section->id].toc_off)))
+ != htab->stub_group[input_section->id].toc_off))
+ || (h == NULL
+ && ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC))
&& (stub_entry = ppc_get_stub_entry (input_section, sec, fdh,
rel, htab)) != NULL
&& (stub_entry->stub_type == ppc_stub_plt_call
@@ -11377,14 +11482,23 @@ ppc64_elf_relocate_section (bfd *output_
/* Generate relocs for the dynamic linker, except in
the case of TLSLD where we'll use one entry per
module. */
- asection *relgot = ppc64_elf_tdata (input_bfd)->relgot;
+ asection *relgot;
+ bfd_boolean ifunc;
*offp = off | 1;
+ relgot = NULL;
+ ifunc = (h != NULL
+ ? h->elf.type == STT_GNU_IFUNC
+ : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC);
if ((info->shared || indx != 0)
&& (offp == &ppc64_tlsld_got (input_bfd)->offset
|| h == NULL
|| ELF_ST_VISIBILITY (h->elf.other) == STV_DEFAULT
|| h->elf.root.type != bfd_link_hash_undefweak))
+ relgot = ppc64_elf_tdata (input_bfd)->relgot;
+ else if (ifunc)
+ relgot = htab->reliplt;
+ if (relgot != NULL)
{
outrel.r_offset = (got->output_section->vma
+ got->output_offset
@@ -11411,9 +11525,14 @@ ppc64_elf_relocate_section (bfd *output_
outrel.r_info = ELF64_R_INFO (indx, R_PPC64_DTPREL64);
else if (tls_type == (TLS_TLS | TLS_TPREL))
outrel.r_info = ELF64_R_INFO (indx, R_PPC64_TPREL64);
- else if (indx == 0)
+ else if (indx != 0)
+ outrel.r_info = ELF64_R_INFO (indx, R_PPC64_GLOB_DAT);
+ else
{
- outrel.r_info = ELF64_R_INFO (indx, R_PPC64_RELATIVE);
+ if (ifunc)
+ outrel.r_info = ELF64_R_INFO (0, R_PPC64_IRELATIVE);
+ else
+ outrel.r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE);
/* Write the .got section contents for the sake
of prelink. */
@@ -11421,8 +11540,6 @@ ppc64_elf_relocate_section (bfd *output_
bfd_put_64 (output_bfd, outrel.r_addend + relocation,
loc);
}
- else
- outrel.r_info = ELF64_R_INFO (indx, R_PPC64_GLOB_DAT);
if (indx == 0 && tls_type != (TLS_TLS | TLS_LD))
{
@@ -11541,6 +11658,12 @@ ppc64_elf_relocate_section (bfd *output_
addend -= sec->output_section->vma;
break;
+ case R_PPC64_REL16:
+ case R_PPC64_REL16_LO:
+ case R_PPC64_REL16_HI:
+ case R_PPC64_REL16_HA:
+ break;
+
case R_PPC64_REL14:
case R_PPC64_REL14_BRNTAKEN:
case R_PPC64_REL14_BRTAKEN:
@@ -11633,7 +11756,11 @@ ppc64_elf_relocate_section (bfd *output_
&& h != NULL
&& h->elf.dynindx != -1
&& !h->elf.non_got_ref
- && !h->elf.def_regular))
+ && !h->elf.def_regular)
+ || (!info->shared
+ && (h != NULL
+ ? h->elf.type == STT_GNU_IFUNC
+ : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)))
{
Elf_Internal_Rela outrel;
bfd_boolean skip, relocate;
@@ -11698,19 +11825,42 @@ ppc64_elf_relocate_section (bfd *output_
entry in this lib. */
unresolved_reloc = FALSE;
}
- outrel.r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE);
+ if (!is_opd
+ && r_type == R_PPC64_ADDR64
+ && (h != NULL
+ ? h->elf.type == STT_GNU_IFUNC
+ : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC))
+ outrel.r_info = ELF64_R_INFO (0, R_PPC64_IRELATIVE);
+ else
+ {
+ outrel.r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE);
- /* We need to relocate .opd contents for ld.so.
- Prelink also wants simple and consistent rules
- for relocs. This make all RELATIVE relocs have
- *r_offset equal to r_addend. */
- relocate = TRUE;
+ /* We need to relocate .opd contents for ld.so.
+ Prelink also wants simple and consistent rules
+ for relocs. This make all RELATIVE relocs have
+ *r_offset equal to r_addend. */
+ relocate = TRUE;
+ }
}
else
{
long indx = 0;
- if (r_symndx == 0 || bfd_is_abs_section (sec))
+ if (h != NULL
+ ? h->elf.type == STT_GNU_IFUNC
+ : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+ {
+ (*_bfd_error_handler)
+ (_("%B(%A+0x%lx): relocation %s for indirect "
+ "function %s unsupported"),
+ input_bfd,
+ input_section,
+ (long) rel->r_offset,
+ ppc64_elf_howto_table[r_type]->name,
+ sym_name);
+ ret = FALSE;
+ }
+ else if (r_symndx == 0 || bfd_is_abs_section (sec))
;
else if (sec == NULL || sec->owner == NULL)
{
@@ -11748,6 +11898,8 @@ ppc64_elf_relocate_section (bfd *output_
}
sreloc = elf_section_data (input_section)->sreloc;
+ if (!htab->elf.dynamic_sections_created)
+ sreloc = htab->reliplt;
if (sreloc == NULL)
abort ();
@@ -11789,6 +11941,7 @@ ppc64_elf_relocate_section (bfd *output_
case R_PPC64_COPY:
case R_PPC64_GLOB_DAT:
case R_PPC64_JMP_SLOT:
+ case R_PPC64_JMP_IREL:
case R_PPC64_RELATIVE:
/* We shouldn't ever see these dynamic relocs in relocatable
files. */
@@ -11821,6 +11974,7 @@ ppc64_elf_relocate_section (bfd *output_
break;
case R_PPC64_ADDR16_HA:
+ case R_PPC64_REL16_HA:
case R_PPC64_ADDR16_HIGHERA:
case R_PPC64_ADDR16_HIGHESTA:
case R_PPC64_TOC16_HA:
@@ -12037,14 +12191,6 @@ ppc64_elf_finish_dynamic_symbol (bfd *ou
{
/* This symbol has an entry in the procedure linkage
table. Set it up. */
- asection *plt = htab->plt;
- if (!htab->elf.dynamic_sections_created)
- plt = htab->iplt;
-
- rela.r_offset = (plt->output_section->vma
- + plt->output_offset
- + ent->plt.offset);
-
if (!htab->elf.dynamic_sections_created
|| h->dynindx == -1)
{
@@ -12052,26 +12198,29 @@ ppc64_elf_finish_dynamic_symbol (bfd *ou
&& h->def_regular
&& (h->root.type == bfd_link_hash_defined
|| h->root.type == bfd_link_hash_defweak));
- rela.r_info = ELF64_R_INFO (0, R_PPC64_IRELATIVE);
+ rela.r_offset = (htab->iplt->output_section->vma
+ + htab->iplt->output_offset
+ + ent->plt.offset);
+ rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL);
rela.r_addend = (h->root.u.def.value
+ h->root.u.def.section->output_offset
+ h->root.u.def.section->output_section->vma
+ ent->addend);
+ loc = (htab->reliplt->contents
+ + (htab->reliplt->reloc_count++
+ * sizeof (Elf64_External_Rela)));
}
else
{
+ rela.r_offset = (htab->plt->output_section->vma
+ + htab->plt->output_offset
+ + ent->plt.offset);
rela.r_info = ELF64_R_INFO (h->dynindx, R_PPC64_JMP_SLOT);
rela.r_addend = ent->addend;
+ loc = (htab->relplt->contents
+ + ((ent->plt.offset - PLT_INITIAL_ENTRY_SIZE)
+ / (PLT_ENTRY_SIZE / sizeof (Elf64_External_Rela))));
}
-
- if (!htab->elf.dynamic_sections_created)
- loc = (htab->reliplt->contents
- + (ent->plt.offset
- / (PLT_ENTRY_SIZE / sizeof (Elf64_External_Rela))));
- else
- loc = (htab->relplt->contents
- + ((ent->plt.offset - PLT_INITIAL_ENTRY_SIZE)
- / (PLT_ENTRY_SIZE / sizeof (Elf64_External_Rela))));
bfd_elf64_swap_reloca_out (output_bfd, &rela, loc);
}
--
Alan Modra
Australia Development Lab, IBM