PowerPC STT_GNU_IFUNC support

Alan Modra amodra@bigpond.net.au
Mon Aug 3 10:22:00 GMT 2009


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



More information about the Binutils mailing list