This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]