[GOLD] ifunc tests

Alan Modra amodra@gmail.com
Fri Mar 15 08:41:00 GMT 2013


On Thu, Mar 07, 2013 at 10:09:03AM +1030, Alan Modra wrote:
> *) I do have a reloc ordering problem in ifuncmod1.so that I intend to
> fix.  A foo() dynamic reloc there is before one setting up the GOT
> entry for global:
> 
> 00010c58  00000901 R_PPC_ADDR32           foo()      foo + 0
> 00010c44  00000f01 R_PPC_ADDR32           00010c7c   global + 0

This patch fixes the STT_GNU_IFUNC relocation ordering problem for
powerpc, and a number of other problems:

a) Gold generated unused plt call stubs for ppc32 PIEs using -fPIC
   code.  ppc32 PIC stubs depend on the calling function GOT pointer,
   a value passed in the addend (>= 32768) of R_PPC_PLTREL24 relocs,
   and multiple stubs came about due to calling push_branch with other
   reloc type addends (typically zero).  It isn't correct for PIEs
   to set the address of a function to a PLT call stub in the
   executable, as is done for ppc32 fixed address executables.

b) Entries in glink for lazy plt resolution were being created for
   .iplt.  These won't ever be used because .iplt relocations aren't
   part of .rela.plt.  .iplt is used just for local ifuncs on
   powerpc.

c) .iplt was initialised to point at these glink entries.  Well, there
   was an attempt, but the wrong value was written.

d) Gold created a huge .glink if .plt was created but empty.  An empty
   .plt can occur with ifunc testcases.

	* powerpc.cc (Target_powerpc::Scan::reloc_needs_plt_for_ifunc): Make
	static and public.  Add report_err param.  Return false for data refs.
	(Target_powerpc::rela_dyn_section): New overloaded function.
	(Target_powerpc::plt_, iplt_): Elucidate.
	(Output_data_plt_powerpc::entry_count): Handle current_data_size()==0.
	(Output_data_plt_powerpc::do_write): Don't write .iplt.
	(Output_data_plt_powerpc::plt_entry_count): Don't add .iplt entries.
	(Target_powerpc::Scan::local, global): Adjust reloc_needs_plt_for_ifunc
	calls.  Put ifunc dynamic relocs in irela_dyn_section.  Only
	push_branch and make_plt_entry for ifunc syms when
	reloc_needs_plt_for_ifunc.
	(Target_powerpc::Relocate::relocate): Don't use plt entry value
	for ifunc unless reloc_needs_plt_for_ifunc.

Index: gold/powerpc.cc
===================================================================
RCS file: /cvs/src/src/gold/powerpc.cc,v
retrieving revision 1.87
diff -u -p -r1.87 powerpc.cc
--- gold/powerpc.cc	12 Mar 2013 22:46:19 -0000	1.87
+++ gold/powerpc.cc	15 Mar 2013 05:06:27 -0000
@@ -899,6 +899,10 @@ class Target_powerpc : public Sized_targ
       return !is_branch_reloc(r_type);
     }
 
+    static bool
+    reloc_needs_plt_for_ifunc(Sized_relobj_file<size, big_endian>* object,
+			      unsigned int r_type, bool report_err);
+
   private:
     static void
     unsupported_reloc_local(Sized_relobj_file<size, big_endian>*,
@@ -915,10 +919,6 @@ class Target_powerpc : public Sized_targ
     void
     check_non_pic(Relobj*, unsigned int r_type);
 
-    bool
-    reloc_needs_plt_for_ifunc(Sized_relobj_file<size, big_endian>* object,
-			      unsigned int r_type);
-
     // Whether we have issued an error about a non-PIC compilation.
     bool issued_non_pic_error_;
   };
@@ -1068,6 +1068,10 @@ class Target_powerpc : public Sized_targ
   Reloc_section*
   rela_dyn_section(Layout*);
 
+  // Similarly, but for ifunc symbols get the one for ifunc.
+  Reloc_section*
+  rela_dyn_section(Symbol_table*, Layout*, bool for_ifunc);
+
   // Copy a relocation against a global symbol.
   void
   copy_reloc(Symbol_table* symtab, Layout* layout,
@@ -1144,9 +1148,30 @@ class Target_powerpc : public Sized_targ
 
   // The GOT section.
   Output_data_got_powerpc<size, big_endian>* got_;
-  // The PLT section.
+  // The PLT section.  This is a container for a table of addresses,
+  // and their relocations.  Each address in the PLT has a dynamic
+  // relocation (R_*_JMP_SLOT) and each address will have a
+  // corresponding entry in .glink for lazy resolution of the PLT.
+  // ppc32 initialises the PLT to point at the .glink entry, while
+  // ppc64 leaves this to ld.so.  To make a call via the PLT, the
+  // linker adds a stub that loads the PLT entry into ctr then
+  // branches to ctr.  There may be more than one stub for each PLT
+  // entry.  DT_JMPREL points at the first PLT dynamic relocation and
+  // DT_PLTRELSZ gives the total size of PLT dynamic relocations.
   Output_data_plt_powerpc<size, big_endian>* plt_;
-  // The IPLT section.
+  // The IPLT section.  Like plt_, this is a container for a table of
+  // addresses and their relocations, specifically for STT_GNU_IFUNC
+  // functions that resolve locally (STT_GNU_IFUNC functions that
+  // don't resolve locally go in PLT).  Unlike plt_, these have no
+  // entry in .glink for lazy resolution, and the relocation section
+  // does not have a 1-1 correspondence with IPLT addresses.  In fact,
+  // the relocation section may contain relocations against
+  // STT_GNU_IFUNC symbols at locations outside of IPLT.  The
+  // relocation section will appear at the end of other dynamic
+  // relocations, so that ld.so applies these relocations after other
+  // dynamic relocations.  In a static executable, the relocation
+  // section is emitted and marked with __rela_iplt_start and
+  // __rela_iplt_end symbols.
   Output_data_plt_powerpc<size, big_endian>* iplt_;
   // Section holding long branch destinations.
   Output_data_brlt_powerpc<size, big_endian>* brlt_section_;
@@ -2122,6 +2147,22 @@ Target_powerpc<size, big_endian>::rela_d
   return this->rela_dyn_;
 }
 
+// Similarly, but for ifunc symbols get the one for ifunc.
+
+template<int size, bool big_endian>
+typename Target_powerpc<size, big_endian>::Reloc_section*
+Target_powerpc<size, big_endian>::rela_dyn_section(Symbol_table* symtab,
+						   Layout* layout,
+						   bool for_ifunc)
+{
+  if (!for_ifunc)
+    return this->rela_dyn_section(layout);
+
+  if (this->iplt_ == NULL)
+    this->make_iplt_section(symtab, layout);
+  return this->iplt_->rel_plt();
+}
+
 class Stub_control
 {
  public:
@@ -2660,6 +2701,8 @@ class Output_data_plt_powerpc : public O
   unsigned int
   entry_count() const
   {
+    if (this->current_data_size() == 0)
+      return 0;
     return ((this->current_data_size() - this->initial_plt_entry_size_)
 	    / plt_entry_size);
   }
@@ -2838,7 +2881,7 @@ template<int size, bool big_endian>
 void
 Output_data_plt_powerpc<size, big_endian>::do_write(Output_file* of)
 {
-  if (size == 32)
+  if (size == 32 && this->name_[3] != 'I')
     {
       const section_size_type offset = this->offset();
       const section_size_type oview_size
@@ -2880,7 +2923,7 @@ Target_powerpc<size, big_endian>::make_p
 
       // Ensure that .rela.dyn always appears before .rela.plt  This is
       // necessary due to how, on PowerPC and some other targets, .rela.dyn
-      // needs to include .rela.plt in it's range.
+      // needs to include .rela.plt in its range.
       this->rela_dyn_section(layout);
 
       Reloc_section* plt_rel = new Reloc_section(false);
@@ -4543,10 +4586,7 @@ Target_powerpc<size, big_endian>::plt_en
 {
   if (this->plt_ == NULL)
     return 0;
-  unsigned int count = this->plt_->entry_count();
-  if (this->iplt_ != NULL)
-    count += this->iplt_->entry_count();
-  return count;
+  return this->plt_->entry_count();
 }
 
 // Return the offset of the first non-reserved PLT entry.
@@ -4790,7 +4830,8 @@ template<int size, bool big_endian>
 bool
 Target_powerpc<size, big_endian>::Scan::reloc_needs_plt_for_ifunc(
      Sized_relobj_file<size, big_endian>* object,
-     unsigned int r_type)
+     unsigned int r_type,
+     bool report_err)
 {
   // In non-pic code any reference will resolve to the plt call stub
   // for the ifunc symbol.
@@ -4799,29 +4840,29 @@ Target_powerpc<size, big_endian>::Scan::
 
   switch (r_type)
     {
-    // Word size refs from data sections are OK.
+    // Word size refs from data sections are OK, but don't need a PLT entry.
     case elfcpp::R_POWERPC_ADDR32:
     case elfcpp::R_POWERPC_UADDR32:
       if (size == 32)
-	return true;
+	return false;
       break;
 
     case elfcpp::R_PPC64_ADDR64:
     case elfcpp::R_PPC64_UADDR64:
       if (size == 64)
-	return true;
+	return false;
       break;
 
-    // GOT refs are good.
+    // GOT refs are good, but also don't need a PLT entry.
     case elfcpp::R_POWERPC_GOT16:
     case elfcpp::R_POWERPC_GOT16_LO:
     case elfcpp::R_POWERPC_GOT16_HI:
     case elfcpp::R_POWERPC_GOT16_HA:
     case elfcpp::R_PPC64_GOT16_DS:
     case elfcpp::R_PPC64_GOT16_LO_DS:
-      return true;
+      return false;
 
-    // So are function calls.
+    // Function calls are good, and these do need a PLT entry.
     case elfcpp::R_POWERPC_ADDR24:
     case elfcpp::R_POWERPC_ADDR14:
     case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
@@ -4846,7 +4887,8 @@ Target_powerpc<size, big_endian>::Scan::
   // writable and non-executable to apply text relocations.  So we'll
   // segfault when trying to run the indirection function to resolve
   // the reloc.
-  gold_error(_("%s: unsupported reloc %u for IFUNC symbol"),
+  if (report_err)
+    gold_error(_("%s: unsupported reloc %u for IFUNC symbol"),
 	       object->name().c_str(), r_type);
   return false;
 }
@@ -4900,7 +4942,7 @@ Target_powerpc<size, big_endian>::Scan::
 
   // A local STT_GNU_IFUNC symbol may require a PLT entry.
   bool is_ifunc = lsym.get_st_type() == elfcpp::STT_GNU_IFUNC;
-  if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type))
+  if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type, true))
     {
       unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
       target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
@@ -4966,18 +5008,14 @@ Target_powerpc<size, big_endian>::Scan::
       if (parameters->options().output_is_position_independent()
 	  || (size == 64 && is_ifunc))
 	{
-	  Reloc_section* rela_dyn = target->rela_dyn_section(layout);
-
+	  Reloc_section* rela_dyn = target->rela_dyn_section(symtab, layout,
+							     is_ifunc);
 	  if ((size == 32 && r_type == elfcpp::R_POWERPC_ADDR32)
 	      || (size == 64 && r_type == elfcpp::R_PPC64_ADDR64))
 	    {
 	      unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
-	      unsigned int dynrel = elfcpp::R_POWERPC_RELATIVE;
-	      if (is_ifunc)
-		{
-		  rela_dyn = target->iplt_section()->rel_plt();
-		  dynrel = elfcpp::R_POWERPC_IRELATIVE;
-		}
+	      unsigned int dynrel = (is_ifunc ? elfcpp::R_POWERPC_IRELATIVE
+				     : elfcpp::R_POWERPC_RELATIVE);
 	      rela_dyn->add_local_relative(object, r_sym, dynrel,
 					   output_section, data_shndx,
 					   reloc.get_r_offset(),
@@ -4997,17 +5035,13 @@ Target_powerpc<size, big_endian>::Scan::
     case elfcpp::R_POWERPC_REL24:
     case elfcpp::R_PPC_PLTREL24:
     case elfcpp::R_PPC_LOCAL24PC:
-      target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
-			  r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
-			  reloc.get_r_addend());
-      break;
-
     case elfcpp::R_POWERPC_REL14:
     case elfcpp::R_POWERPC_REL14_BRTAKEN:
     case elfcpp::R_POWERPC_REL14_BRNTAKEN:
-      target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
-			  r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
-			  reloc.get_r_addend());
+      if (!is_ifunc)
+	target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
+			    r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
+			    reloc.get_r_addend());
       break;
 
     case elfcpp::R_PPC64_REL64:
@@ -5073,13 +5107,10 @@ Target_powerpc<size, big_endian>::Scan::
 	    off = got->add_constant(0);
 	    object->set_local_got_offset(r_sym, GOT_TYPE_STANDARD, off);
 
-	    Reloc_section* rela_dyn = target->rela_dyn_section(layout);
-	    unsigned int dynrel = elfcpp::R_POWERPC_RELATIVE;
-	    if (is_ifunc)
-	      {
-		rela_dyn = target->iplt_section()->rel_plt();
-		dynrel = elfcpp::R_POWERPC_IRELATIVE;
-	      }
+	    Reloc_section* rela_dyn = target->rela_dyn_section(symtab, layout,
+							       is_ifunc);
+	    unsigned int dynrel = (is_ifunc ? elfcpp::R_POWERPC_IRELATIVE
+				   : elfcpp::R_POWERPC_RELATIVE);
 	    rela_dyn->add_local_relative(object, r_sym, dynrel,
 					 got, off, 0, false);
 	  }
@@ -5260,8 +5291,8 @@ Target_powerpc<size, big_endian>::Scan::
     = static_cast<Powerpc_relobj<size, big_endian>*>(object);
 
   // A STT_GNU_IFUNC symbol may require a PLT entry.
-  if (gsym->type() == elfcpp::STT_GNU_IFUNC
-      && this->reloc_needs_plt_for_ifunc(object, r_type))
+  bool is_ifunc = gsym->type() == elfcpp::STT_GNU_IFUNC;
+  if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type, true))
     {
       target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
 			  r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
@@ -5337,11 +5368,14 @@ Target_powerpc<size, big_endian>::Scan::
 	// Make a PLT entry if necessary.
 	if (gsym->needs_plt_entry())
 	  {
-	    target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
-				r_type,
-				elfcpp::elf_r_sym<size>(reloc.get_r_info()),
-				reloc.get_r_addend());
-	    target->make_plt_entry(symtab, layout, gsym);
+	    if (!is_ifunc)
+	      {
+		target->push_branch(ppc_object, data_shndx,
+				    reloc.get_r_offset(), r_type,
+				    elfcpp::elf_r_sym<size>(reloc.get_r_info()),
+				    reloc.get_r_addend());
+		target->make_plt_entry(symtab, layout, gsym);
+	      }
 	    // Since this is not a PC-relative relocation, we may be
 	    // taking the address of a function. In that case we need to
 	    // set the entry in the dynamic symbol table to the address of
@@ -5353,7 +5387,7 @@ Target_powerpc<size, big_endian>::Scan::
 	  }
 	// Make a dynamic relocation if necessary.
 	if (needs_dynamic_reloc<size>(gsym, Scan::get_reference_flags(r_type))
-	    || (size == 64 && gsym->type() == elfcpp::STT_GNU_IFUNC))
+	    || (size == 64 && is_ifunc))
 	  {
 	    if (gsym->may_need_copy_reloc())
 	      {
@@ -5370,20 +5404,18 @@ Target_powerpc<size, big_endian>::Scan::
 			 && (gsym->can_use_relative_reloc(false)
 			     || data_shndx == ppc_object->opd_shndx())))
 	      {
-		Reloc_section* rela_dyn = target->rela_dyn_section(layout);
-		unsigned int dynrel = elfcpp::R_POWERPC_RELATIVE;
-		if (gsym->type() == elfcpp::STT_GNU_IFUNC)
-		  {
-		    rela_dyn = target->iplt_section()->rel_plt();
-		    dynrel = elfcpp::R_POWERPC_IRELATIVE;
-		  }
+		Reloc_section* rela_dyn
+		  = target->rela_dyn_section(symtab, layout, is_ifunc);
+		unsigned int dynrel = (is_ifunc ? elfcpp::R_POWERPC_IRELATIVE
+				       : elfcpp::R_POWERPC_RELATIVE);
 		rela_dyn->add_symbolless_global_addend(
 		    gsym, dynrel, output_section, object, data_shndx,
 		    reloc.get_r_offset(), reloc.get_r_addend());
 	      }
 	    else
 	      {
-		Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+		Reloc_section* rela_dyn
+		  = target->rela_dyn_section(symtab, layout, is_ifunc);
 		check_non_pic(object, r_type);
 		rela_dyn->add_global(gsym, r_type, output_section,
 				     object, data_shndx,
@@ -5396,15 +5428,19 @@ Target_powerpc<size, big_endian>::Scan::
 
     case elfcpp::R_PPC_PLTREL24:
     case elfcpp::R_POWERPC_REL24:
-      target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
-			  r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
-			  reloc.get_r_addend());
-      if (gsym->needs_plt_entry()
-	  || (!gsym->final_value_is_known()
-	      && (gsym->is_undefined()
-		  || gsym->is_from_dynobj()
-		  || gsym->is_preemptible())))
-	target->make_plt_entry(symtab, layout, gsym);
+      if (!is_ifunc)
+	{
+	  target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
+			      r_type,
+			      elfcpp::elf_r_sym<size>(reloc.get_r_info()),
+			      reloc.get_r_addend());
+	  if (gsym->needs_plt_entry()
+	      || (!gsym->final_value_is_known()
+		  && (gsym->is_undefined()
+		      || gsym->is_from_dynobj()
+		      || gsym->is_preemptible())))
+	    target->make_plt_entry(symtab, layout, gsym);
+	}
       // Fall thru
 
     case elfcpp::R_PPC64_REL64:
@@ -5420,7 +5456,8 @@ Target_powerpc<size, big_endian>::Scan::
 	    }
 	  else
 	    {
-	      Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+	      Reloc_section* rela_dyn
+		= target->rela_dyn_section(symtab, layout, is_ifunc);
 	      check_non_pic(object, r_type);
 	      rela_dyn->add_global(gsym, r_type, output_section, object,
 				   data_shndx, reloc.get_r_offset(),
@@ -5432,9 +5469,10 @@ Target_powerpc<size, big_endian>::Scan::
     case elfcpp::R_POWERPC_REL14:
     case elfcpp::R_POWERPC_REL14_BRTAKEN:
     case elfcpp::R_POWERPC_REL14_BRNTAKEN:
-      target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
-			  r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
-			  reloc.get_r_addend());
+      if (!is_ifunc)
+	target->push_branch(ppc_object, data_shndx, reloc.get_r_offset(),
+			    r_type, elfcpp::elf_r_sym<size>(reloc.get_r_info()),
+			    reloc.get_r_addend());
       break;
 
     case elfcpp::R_POWERPC_REL16:
@@ -5484,7 +5522,7 @@ Target_powerpc<size, big_endian>::Scan::
 	got = target->got_section(symtab, layout);
 	if (gsym->final_value_is_known())
 	  {
-	    if (size == 32 && gsym->type() == elfcpp::STT_GNU_IFUNC)
+	    if (size == 32 && is_ifunc)
 	      got->add_global_plt(gsym, GOT_TYPE_STANDARD);
 	    else
 	      got->add_global(gsym, GOT_TYPE_STANDARD);
@@ -5496,18 +5534,16 @@ Target_powerpc<size, big_endian>::Scan::
 	    unsigned int off = got->add_constant(0);
 	    gsym->set_got_offset(GOT_TYPE_STANDARD, off);
 
-	    Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+	    Reloc_section* rela_dyn
+	      = target->rela_dyn_section(symtab, layout, is_ifunc);
+
 	    if (gsym->can_use_relative_reloc(false)
 		&& !(size == 32
 		     && gsym->visibility() == elfcpp::STV_PROTECTED
 		     && parameters->options().shared()))
 	      {
-		unsigned int dynrel = elfcpp::R_POWERPC_RELATIVE;
-		if (gsym->type() == elfcpp::STT_GNU_IFUNC)
-		  {
-		    rela_dyn = target->iplt_section()->rel_plt();
-		    dynrel = elfcpp::R_POWERPC_IRELATIVE;
-		  }
+		unsigned int dynrel = (is_ifunc ? elfcpp::R_POWERPC_IRELATIVE
+				       : elfcpp::R_POWERPC_RELATIVE);
 		rela_dyn->add_global_relative(gsym, dynrel, got, off, 0, false);
 	      }
 	    else
@@ -5540,8 +5576,8 @@ Target_powerpc<size, big_endian>::Scan::
 	  {
 	    Output_data_got_powerpc<size, big_endian>* got
 	      = target->got_section(symtab, layout);
-	    got->add_global_pair_with_rel(gsym, GOT_TYPE_TLSGD,
-					  target->rela_dyn_section(layout),
+	    Reloc_section* rela_dyn = target->rela_dyn_section(layout);
+	    got->add_global_pair_with_rel(gsym, GOT_TYPE_TLSGD, rela_dyn,
 					  elfcpp::R_POWERPC_DTPMOD,
 					  elfcpp::R_POWERPC_DTPREL);
 	  }
@@ -6178,9 +6214,11 @@ Target_powerpc<size, big_endian>::Reloca
   Address value = 0;
   bool has_plt_value = false;
   unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
-  if (gsym != NULL
-      ? use_plt_offset<size>(gsym, Scan::get_reference_flags(r_type))
-      : object->local_has_plt_offset(r_sym))
+  if ((gsym != NULL
+       ? use_plt_offset<size>(gsym, Scan::get_reference_flags(r_type))
+       : object->local_has_plt_offset(r_sym))
+      && (!psymval->is_ifunc_symbol()
+	  || Scan::reloc_needs_plt_for_ifunc(object, r_type, false)))
     {
       Stub_table<size, big_endian>* stub_table
 	= object->stub_table(relinfo->data_shndx);


-- 
Alan Modra
Australia Development Lab, IBM



More information about the Binutils mailing list