public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 2/2] Add sparc IFUNC support to Gold.
@ 2012-04-11 19:15 David Miller
  2012-04-11 22:26 ` David Miller
  2012-04-17  0:02 ` Ian Lance Taylor
  0 siblings, 2 replies; 8+ messages in thread
From: David Miller @ 2012-04-11 19:15 UTC (permalink / raw)
  To: binutils


This adds sparc IFUNC support to Gold.

Actually, this is the first point at which I've been able to run the
Gold testsuite on sparc at all.  glibc on all my sparc machines has
had IFUNC symbols for more than a year, and the HJ's hack to turn
IFUNC into FUNC only works for shared links, so even the basic static
test wouldn't link.

Not all the ifunc tests pass, 6 still fail, but I know how to fix
that.  The fix will involve adding proper GOTDATA_OP code
transformations, because otherwise the IFUNC functions run and try to
use GOT slots which haven't been resolved yet.  We'll also need to
link the ifunc tests using -fPIC and -fPIE because we can't do the GOT
relative relocation to local symbol optimization with -fpic on sparc.

Besides the weird relative relocation issues (described in patch #1)
the implementation mostly mirrors what i386 and x86_64 do.  One big
difference is that I can't emit the PLT relocs for IFUNC at
add_entry() time.  I have to emit them later, in do_finalize_sections()

This is because the PLT relocs have the plt offset in them, and that
doesn't stabilize until we've generated all of relocs.  This is because
of the segregation between IFUNC and non-iFUNC relocations.

This points out another problem on sparc, we have variable sized
PLT entries.  On 64-bit after 32768 entries, the PLT entries change
in size.

The sparc delayed IFUNC PLT relocation emitter handles this correctly,
since it works with the final PLT index to calculate the correct
offset.

But the ->address_for_global and ->address_for_local abstractions
needs to be adjusted to handle this.  We'll have to pass PLT indices
around instead of offsets to get it right.

But realistically all the normal cases are handled properly, very
few objects require more than 32768 PLT slots :-)

Another testcase that still fails is memory_test.sh because that test
assumes a minimum alignment of 4K, or something like that.  Whereas
sparc's minimum alignment is 8K.  Any suggestions on how to fix that
are welcome.

elfcpp/

	* sparc.h (R_SPARC_JMP_IREL): New relocation.

gold/

	* sparc.cc (class Target_sparc): Add rela_ifunc_.
	(Target_sparc::Target_sparc): Initialize new field.
	(Target_sparc::do_plt_section_for_global): New function.
	(Target_sparc::do_plt_section_for_local): New function.
	(Target_sparc::reloc_needs_plt_for_ifunc): New function.
	(Target_sparc::make_plt_section): New function, broken out of
	make_plt_entry.  Use ORDER_NON_RELRO_FIRST for ".plt".
	(Target_sparc::make_plt_entry): Call make_plt_section.
	(Target_sparc::make_local_ifunc_plt_entry): New function.
	(Target_sparc::rela_ifunc_section): New function.
	(Target_sparc::plt_section): Remove const.
	(Output_data_plt_sparc): Update declarations.  Define Global_ifunc
	and Local_ifunc types.  Add global_ifuncs_, local_ifuncs_, ifunc_rel_,
	and ifunc_count_ fields.
	(Output_data_plt_sparc::Output_data_plt_sparc): Initialize new fields.
	(Output_data_plt_sparc::add_entry): Handle IFUNC symbols.
	(Output_data_plt_sparc::add_local_ifunc_entry): New function.
	(Output_data_plt_sparc::rela_ifunc): New function.
	(Output_data_plt_sparc::emit_pending_ifunc_relocs): New function.
	(Output_data_plt_sparc::has_ifunc_section): New function.
	(Output_data_plt_sparc::entry_count): Include ifunc_count_.
	(Output_data_plt_sparc::address_for_global): New function.
	(Output_data_plt_sparc::address_for_local): New function.
	(Output_data_plt_sparc::plt_index_to_offset): New function.
	(Output_data_plt_sparc::set_final_data_size): Use plt_index_to_offset
	and entry_count.
	(Output_data_plt_sparc::do_write): Use first_plt_entry_offset and
	entry_count.
	(Target_sparc::Scan::get_reference_flags): Add R_SPARC_IRELATIVE and
	R_SPARC_JMP_IREL to switch.
	(Target_sparc::Scan::check_non_pic): Likewise.
	(Target_sparc::Scan::local): Handle IFUNC symbols.
	(Target_sparc::Scan::local): Likewise.
	(Target_sparc::Relocate::relocate): Likewise, use plt_address_for_global
	and plt_address_for_local.
	(Target_sparc::do_finalize_sections): Call emit_pending_ifunc_relocs.
	Define __rel_iplt_start and __rel_iplt_end if doing a static link.
---
 elfcpp/sparc.h |    1 +
 gold/sparc.cc  |  563 ++++++++++++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 487 insertions(+), 77 deletions(-)

diff --git a/elfcpp/sparc.h b/elfcpp/sparc.h
index 77c4668..6b561be 100644
--- a/elfcpp/sparc.h
+++ b/elfcpp/sparc.h
@@ -142,6 +142,7 @@ enum
   R_SPARC_SIZE64 = 87,        // size of symbol, 64-bit
   R_SPARC_WDISP10 = 88,       // PC relative 10 bit shifted
 
+  R_SPARC_JMP_IREL = 248,     // Create PLT slot to IFUNC function
   R_SPARC_IRELATIVE = 249,    // Adjust indirectly by program base
 
   // GNU vtable garbage collection extensions.
diff --git a/gold/sparc.cc b/gold/sparc.cc
index e1bdc8e..993fc55 100644
--- a/gold/sparc.cc
+++ b/gold/sparc.cc
@@ -58,7 +58,7 @@ class Target_sparc : public Sized_target<size, big_endian>
 
   Target_sparc()
     : Sized_target<size, big_endian>(&sparc_info),
-      got_(NULL), plt_(NULL), rela_dyn_(NULL),
+      got_(NULL), plt_(NULL), rela_dyn_(NULL), rela_ifunc_(NULL),
       copy_relocs_(elfcpp::R_SPARC_COPY), dynbss_(NULL),
       got_mod_index_offset_(-1U), tls_get_addr_sym_(NULL)
   {
@@ -154,6 +154,15 @@ class Target_sparc : public Sized_target<size, big_endian>
     return strcmp(sym->name(), "___tls_get_addr") == 0;
   }
 
+  // Return the PLT address to use for a global symbol.
+  uint64_t
+  do_plt_address_for_global(const Symbol* gsym) const
+  { return this->plt_section()->address_for_global(gsym); }
+
+  uint64_t
+  do_plt_address_for_local(const Relobj* relobj, unsigned int symndx) const
+  { return this->plt_section()->address_for_local(relobj, symndx); }
+
   // Return whether there is a GOT section.
   bool
   has_got_section() const
@@ -256,6 +265,10 @@ class Target_sparc : public Sized_target<size, big_endian>
     void
     check_non_pic(Relobj*, unsigned int r_type);
 
+    bool
+    reloc_needs_plt_for_ifunc(Sized_relobj_file<size, big_endian>*,
+			      unsigned int r_type);
+
     // Whether we have issued an error about a non-PIC compilation.
     bool issued_non_pic_error_;
   };
@@ -320,10 +333,20 @@ class Target_sparc : public Sized_target<size, big_endian>
   Output_data_got<size, big_endian>*
   got_section(Symbol_table*, Layout*);
 
+  // Create the PLT section.
+  void
+  make_plt_section(Symbol_table* symtab, Layout* layout);
+
   // Create a PLT entry for a global symbol.
   void
   make_plt_entry(Symbol_table*, Layout*, Symbol*);
 
+  // Create a PLT entry for a local STT_GNU_IFUNC symbol.
+  void
+  make_local_ifunc_plt_entry(Symbol_table*, Layout*,
+			     Sized_relobj_file<size, big_endian>* relobj,
+			     unsigned int local_sym_index);
+
   // Create a GOT entry for the TLS module index.
   unsigned int
   got_mod_index_entry(Symbol_table* symtab, Layout* layout,
@@ -341,7 +364,7 @@ class Target_sparc : public Sized_target<size, big_endian>
   }
 
   // Get the PLT section.
-  const Output_data_plt_sparc<size, big_endian>*
+  Output_data_plt_sparc<size, big_endian>*
   plt_section() const
   {
     gold_assert(this->plt_ != NULL);
@@ -352,6 +375,10 @@ class Target_sparc : public Sized_target<size, big_endian>
   Reloc_section*
   rela_dyn_section(Layout*);
 
+  // Get the section to use for IFUNC relocations.
+  Reloc_section*
+  rela_ifunc_section(Layout*);
+
   // Copy a relocation against a global symbol.
   void
   copy_reloc(Symbol_table* symtab, Layout* layout,
@@ -386,6 +413,8 @@ class Target_sparc : public Sized_target<size, big_endian>
   Output_data_plt_sparc<size, big_endian>* plt_;
   // The dynamic reloc section.
   Reloc_section* rela_dyn_;
+  // The section to use for IFUNC relocs.
+  Reloc_section* rela_ifunc_;
   // Relocs saved to avoid a COPY reloc.
   Copy_relocs<elfcpp::SHT_RELA, size, big_endian> copy_relocs_;
   // Space for variables copied with a COPY reloc.
@@ -1145,6 +1174,30 @@ Target_sparc<size, big_endian>::rela_dyn_section(Layout* layout)
   return this->rela_dyn_;
 }
 
+// Get the section to use for IFUNC relocs, creating it if
+// necessary.  These go in .rela.dyn, but only after all other dynamic
+// relocations.  They need to follow the other dynamic relocations so
+// that they can refer to global variables initialized by those
+// relocs.
+
+template<int size, bool big_endian>
+typename Target_sparc<size, big_endian>::Reloc_section*
+Target_sparc<size, big_endian>::rela_ifunc_section(Layout* layout)
+{
+  if (this->rela_ifunc_ == NULL)
+    {
+      // Make sure we have already created the dynamic reloc section.
+      this->rela_dyn_section(layout);
+      this->rela_ifunc_ = new Reloc_section(false);
+      layout->add_output_section_data(".rela.dyn", elfcpp::SHT_RELA,
+				      elfcpp::SHF_ALLOC, this->rela_ifunc_,
+				      ORDER_DYNAMIC_RELOCS, false);
+      gold_assert(this->rela_dyn_->output_section()
+		  == this->rela_ifunc_->output_section());
+    }
+  return this->rela_ifunc_;
+}
+
 // A class to handle the PLT data.
 
 template<int size, bool big_endian>
@@ -1157,7 +1210,13 @@ class Output_data_plt_sparc : public Output_section_data
   Output_data_plt_sparc(Layout*);
 
   // Add an entry to the PLT.
-  void add_entry(Symbol* gsym);
+  void add_entry(Symbol_table* symtab, Layout* layout, Symbol* gsym);
+
+  // Add an entry to the PLT for a local STT_GNU_IFUNC symbol.
+  unsigned int
+  add_local_ifunc_entry(Symbol_table*, Layout*,
+			Sized_relobj_file<size, big_endian>* relobj,
+			unsigned int local_sym_index);
 
   // Return the .rela.plt section data.
   const Reloc_section* rel_plt() const
@@ -1165,10 +1224,22 @@ class Output_data_plt_sparc : public Output_section_data
     return this->rel_;
   }
 
+  // Return where the IFUNC relocations should go.
+  Reloc_section*
+  rela_ifunc(Symbol_table*, Layout*);
+
+  void
+  emit_pending_ifunc_relocs();
+
+  // Return whether we created a section for IFUNC relocations.
+  bool
+  has_ifunc_section() const
+  { return this->ifunc_rel_ != NULL; }
+
   // Return the number of PLT entries.
   unsigned int
   entry_count() const
-  { return this->count_; }
+  { return this->count_ + this->ifunc_count_; }
 
   // Return the offset of the first non-reserved PLT entry.
   static unsigned int
@@ -1180,6 +1251,14 @@ class Output_data_plt_sparc : public Output_section_data
   get_plt_entry_size()
   { return base_plt_entry_size; }
 
+  // Return the PLT address to use for a global symbol.
+  uint64_t
+  address_for_global(const Symbol*);
+
+  // Return the PLT address to use for a local symbol.
+  uint64_t
+  address_for_local(const Relobj*, unsigned int symndx);
+
  protected:
   void do_adjust_output_section(Output_section* os);
 
@@ -1199,34 +1278,69 @@ class Output_data_plt_sparc : public Output_section_data
     (plt_entries_per_block
      * (plt_insn_chunk_size + plt_pointer_chunk_size));
 
-  // Set the final size.
-  void
-  set_final_data_size()
+  section_offset_type
+  plt_index_to_offset(unsigned int index)
   {
-    unsigned int full_count = this->count_ + 4;
-    unsigned int extra = (size == 32 ? 4 : 0);
+    section_offset_type offset;
 
-    if (size == 32 || full_count < 32768)
-      this->set_data_size((full_count * base_plt_entry_size) + extra);
+    if (size == 32 || index < 32768)
+      offset = index * base_plt_entry_size;
     else
       {
-	unsigned int ext_cnt = full_count - 32768;
+	unsigned int ext_index = index - 32768;
 
-	this->set_data_size((32768 * base_plt_entry_size)
-			    + (ext_cnt
-			       * (plt_insn_chunk_size
-				  + plt_pointer_chunk_size)));
+	offset = (32768 * base_plt_entry_size)
+	  + ((ext_index / plt_entries_per_block)
+	     * plt_block_size)
+	  + ((ext_index % plt_entries_per_block)
+	     * plt_insn_chunk_size);
       }
+    return offset;
+  }
+
+  // Set the final size.
+  void
+  set_final_data_size()
+  {
+    unsigned int full_count = this->entry_count() + 4;
+    unsigned int extra = (size == 32 ? 4 : 0);
+    section_offset_type sz = plt_index_to_offset(full_count) + extra;
+
+    return this->set_data_size(sz);
   }
 
   // Write out the PLT data.
   void
   do_write(Output_file*);
 
+  struct Global_ifunc
+  {
+    Reloc_section* rel;
+    Symbol* gsym;
+    unsigned int plt_index;
+  };
+
+  struct Local_ifunc
+  {
+    Reloc_section* rel;
+    Sized_relobj_file<size, big_endian>* object;
+    unsigned int local_sym_index;
+    unsigned int plt_index;
+  };
+
   // The reloc section.
   Reloc_section* rel_;
+  // The IFUNC relocations, if necessary.  These must follow the
+  // regular relocations.
+  Reloc_section* ifunc_rel_;
   // The number of PLT entries.
   unsigned int count_;
+  // The number of PLT entries for IFUNC symbols.
+  unsigned int ifunc_count_;
+  // Global STT_GNU_IFUNC symbols.
+  std::vector<Global_ifunc> global_ifuncs_;
+  // Local STT_GNU_IFUNC symbols.
+  std::vector<Local_ifunc> local_ifuncs_;
 };
 
 // Define the constants as required by C++ standard.
@@ -1253,7 +1367,8 @@ const unsigned int Output_data_plt_sparc<size, big_endian>::plt_block_size;
 
 template<int size, bool big_endian>
 Output_data_plt_sparc<size, big_endian>::Output_data_plt_sparc(Layout* layout)
-  : Output_section_data(size == 32 ? 4 : 8), count_(0)
+  : Output_section_data(size == 32 ? 4 : 8), ifunc_rel_(NULL),
+    count_(0), ifunc_count_(0), global_ifuncs_(), local_ifuncs_()
 {
   this->rel_ = new Reloc_section(false);
   layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA,
@@ -1272,38 +1387,171 @@ Output_data_plt_sparc<size, big_endian>::do_adjust_output_section(Output_section
 
 template<int size, bool big_endian>
 void
-Output_data_plt_sparc<size, big_endian>::add_entry(Symbol* gsym)
+Output_data_plt_sparc<size, big_endian>::add_entry(Symbol_table* symtab,
+						   Layout* layout,
+						   Symbol* gsym)
 {
   gold_assert(!gsym->has_plt_offset());
 
-  unsigned int index = this->count_ + 4;
   section_offset_type plt_offset;
+  unsigned int index;
 
-  if (size == 32 || index < 32768)
-    plt_offset = index * base_plt_entry_size;
+  if (gsym->type() == elfcpp::STT_GNU_IFUNC
+      && gsym->can_use_relative_reloc(false))
+    {
+      index = this->ifunc_count_;
+      plt_offset = plt_index_to_offset(index);
+      gsym->set_plt_offset(plt_offset);
+      ++this->ifunc_count_;
+      Reloc_section* rel = this->rela_ifunc(symtab, layout);
+
+      struct Global_ifunc gi;
+      gi.rel = rel;
+      gi.gsym = gsym;
+      gi.plt_index = index;
+      this->global_ifuncs_.push_back(gi);
+    }
   else
     {
-	unsigned int ext_index = index - 32768;
+      plt_offset = plt_index_to_offset(this->count_ + 4);
+      gsym->set_plt_offset(plt_offset);
+      ++this->count_;
+      gsym->set_needs_dynsym_entry();
+      this->rel_->add_global(gsym, elfcpp::R_SPARC_JMP_SLOT, this,
+			     plt_offset, 0);
+    }
 
-	plt_offset = (32768 * base_plt_entry_size)
-	  + ((ext_index / plt_entries_per_block)
-	     * plt_block_size)
-	  + ((ext_index % plt_entries_per_block)
-	     * plt_insn_chunk_size);
+  // Note that we don't need to save the symbol.  The contents of the
+  // PLT are independent of which symbols are used.  The symbols only
+  // appear in the relocations.
+}
+
+template<int size, bool big_endian>
+unsigned int
+Output_data_plt_sparc<size, big_endian>::add_local_ifunc_entry(
+    Symbol_table* symtab,
+    Layout* layout,
+    Sized_relobj_file<size, big_endian>* relobj,
+    unsigned int local_sym_index)
+{
+  unsigned int index = this->ifunc_count_;
+  section_offset_type plt_offset;
+
+  plt_offset = plt_index_to_offset(index);
+  ++this->ifunc_count_;
+
+  Reloc_section* rel = this->rela_ifunc(symtab, layout);
+
+  struct Local_ifunc li;
+  li.rel = rel;
+  li.object = relobj;
+  li.local_sym_index = local_sym_index;
+  li.plt_index = index;
+  this->local_ifuncs_.push_back(li);
+
+  return plt_offset;
+}
+
+// Emit any pending IFUNC plt relocations.
+
+template<int size, bool big_endian>
+void
+Output_data_plt_sparc<size, big_endian>::emit_pending_ifunc_relocs()
+{
+  // Emit any pending IFUNC relocs.
+  for (typename std::vector<Global_ifunc>::const_iterator p =
+	 this->global_ifuncs_.begin();
+       p != this->global_ifuncs_.end();
+       ++p)
+    {
+      section_offset_type plt_offset;
+      unsigned int index;
+
+      index = this->count_ + p->plt_index + 4;
+      plt_offset = this->plt_index_to_offset(index);
+      p->rel->add_symbolless_global_addend(p->gsym, elfcpp::R_SPARC_JMP_IREL,
+					   this, plt_offset, 0);
     }
 
-  gsym->set_plt_offset(plt_offset);
+  for (typename std::vector<Local_ifunc>::const_iterator p =
+	 this->local_ifuncs_.begin();
+       p != this->local_ifuncs_.end();
+       ++p)
+    {
+      section_offset_type plt_offset;
+      unsigned int index;
+
+      index = this->count_ + p->plt_index + 4;
+      plt_offset = this->plt_index_to_offset(index);
+      p->rel->add_symbolless_local_addend(p->object, p->local_sym_index,
+					  elfcpp::R_SPARC_JMP_IREL,
+					  this, plt_offset, 0);
+    }
+}
 
-  ++this->count_;
+// Return where the IFUNC relocations should go in the PLT.  These
+// follow the non-IFUNC relocations.
 
-  // Every PLT entry needs a reloc.
-  gsym->set_needs_dynsym_entry();
-  this->rel_->add_global(gsym, elfcpp::R_SPARC_JMP_SLOT, this,
-			 plt_offset, 0);
+template<int size, bool big_endian>
+typename Output_data_plt_sparc<size, big_endian>::Reloc_section*
+Output_data_plt_sparc<size, big_endian>::rela_ifunc(
+	Symbol_table* symtab,
+	Layout* layout)
+{
+  if (this->ifunc_rel_ == NULL)
+    {
+      this->ifunc_rel_ = new Reloc_section(false);
+      layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA,
+				      elfcpp::SHF_ALLOC, this->ifunc_rel_,
+				      ORDER_DYNAMIC_PLT_RELOCS, false);
+      gold_assert(this->ifunc_rel_->output_section()
+		  == this->rel_->output_section());
+
+      if (parameters->doing_static_link())
+	{
+	  // A statically linked executable will only have a .rel.plt
+	  // section to hold R_SPARC_IRELATIVE and R_SPARC_JMP_IREL
+	  // relocs for STT_GNU_IFUNC symbols.  The library will use
+	  // these symbols to locate the IRELATIVE and JMP_IREL relocs
+	  // at program startup time.
+	  symtab->define_in_output_data("__rela_iplt_start", NULL,
+					Symbol_table::PREDEFINED,
+					this->ifunc_rel_, 0, 0,
+					elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
+					elfcpp::STV_HIDDEN, 0, false, true);
+	  symtab->define_in_output_data("__rela_iplt_end", NULL,
+					Symbol_table::PREDEFINED,
+					this->ifunc_rel_, 0, 0,
+					elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
+					elfcpp::STV_HIDDEN, 0, true, true);
+	}
+    }
+  return this->ifunc_rel_;
+}
 
-  // Note that we don't need to save the symbol.  The contents of the
-  // PLT are independent of which symbols are used.  The symbols only
-  // appear in the relocations.
+// Return the PLT address to use for a global symbol.
+
+template<int size, bool big_endian>
+uint64_t
+Output_data_plt_sparc<size, big_endian>::address_for_global(const Symbol* gsym)
+{
+  uint64_t offset = 0;
+  if (gsym->type() == elfcpp::STT_GNU_IFUNC
+      && gsym->can_use_relative_reloc(false))
+    offset = plt_index_to_offset(this->count_ + 4);
+  return this->address() + offset;
+}
+
+// Return the PLT address to use for a local symbol.  These are always
+// IRELATIVE relocs.
+
+template<int size, bool big_endian>
+uint64_t
+Output_data_plt_sparc<size, big_endian>::address_for_local(
+	const Relobj*,
+	unsigned int)
+{
+  return this->address() + plt_index_to_offset(this->count_ + 4);
 }
 
 static const unsigned int sparc_nop = 0x01000000;
@@ -1331,10 +1579,10 @@ Output_data_plt_sparc<size, big_endian>::do_write(Output_file* of)
   unsigned char* pov = oview;
 
   memset(pov, 0, base_plt_entry_size * 4);
-  pov += base_plt_entry_size * 4;
+  pov += this->first_plt_entry_offset();
 
   unsigned int plt_offset = base_plt_entry_size * 4;
-  const unsigned int count = this->count_;
+  const unsigned int count = this->entry_count();
 
   if (size == 64)
     {
@@ -1454,6 +1702,38 @@ Output_data_plt_sparc<size, big_endian>::do_write(Output_file* of)
   of->write_output_view(offset, oview_size, oview);
 }
 
+// Create the PLT section.
+
+template<int size, bool big_endian>
+void
+Target_sparc<size, big_endian>::make_plt_section(Symbol_table* symtab,
+						 Layout* layout)
+{
+  // Create the GOT sections first.
+  this->got_section(symtab, layout);
+
+  // Ensure that .rela.dyn always appears before .rela.plt  This is
+  // necessary due to how, on Sparc and some other targets, .rela.dyn
+  // needs to include .rela.plt in it's range.
+  this->rela_dyn_section(layout);
+
+  this->plt_ = new Output_data_plt_sparc<size, big_endian>(layout);
+  layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
+				  (elfcpp::SHF_ALLOC
+				   | elfcpp::SHF_EXECINSTR
+				   | elfcpp::SHF_WRITE),
+				  this->plt_, ORDER_NON_RELRO_FIRST, false);
+
+  // Define _PROCEDURE_LINKAGE_TABLE_ at the start of the .plt section.
+  symtab->define_in_output_data("_PROCEDURE_LINKAGE_TABLE_", NULL,
+				Symbol_table::PREDEFINED,
+				this->plt_,
+				0, 0, elfcpp::STT_OBJECT,
+				elfcpp::STB_LOCAL,
+				elfcpp::STV_HIDDEN, 0,
+				false, false);
+}
+
 // Create a PLT entry for a global symbol.
 
 template<int size, bool big_endian>
@@ -1466,33 +1746,29 @@ Target_sparc<size, big_endian>::make_plt_entry(Symbol_table* symtab,
     return;
 
   if (this->plt_ == NULL)
-    {
-      // Create the GOT sections first.
-      this->got_section(symtab, layout);
+    this->make_plt_section(symtab, layout);
 
-      // Ensure that .rela.dyn always appears before .rela.plt  This is
-      // necessary due to how, on Sparc and some other targets, .rela.dyn
-      // needs to include .rela.plt in it's range.
-      this->rela_dyn_section(layout);
-
-      this->plt_ = new Output_data_plt_sparc<size, big_endian>(layout);
-      layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
-				      (elfcpp::SHF_ALLOC
-				       | elfcpp::SHF_EXECINSTR
-				       | elfcpp::SHF_WRITE),
-				      this->plt_, ORDER_PLT, false);
+  this->plt_->add_entry(symtab, layout, gsym);
+}
 
-      // Define _PROCEDURE_LINKAGE_TABLE_ at the start of the .plt section.
-      symtab->define_in_output_data("_PROCEDURE_LINKAGE_TABLE_", NULL,
-				    Symbol_table::PREDEFINED,
-				    this->plt_,
-				    0, 0, elfcpp::STT_OBJECT,
-				    elfcpp::STB_LOCAL,
-				    elfcpp::STV_HIDDEN, 0,
-				    false, false);
-    }
+// Make a PLT entry for a local STT_GNU_IFUNC symbol.
 
-  this->plt_->add_entry(gsym);
+template<int size, bool big_endian>
+void
+Target_sparc<size, big_endian>::make_local_ifunc_plt_entry(
+	Symbol_table* symtab,
+	Layout* layout,
+	Sized_relobj_file<size, big_endian>* relobj,
+	unsigned int local_sym_index)
+{
+  if (relobj->local_has_plt_offset(local_sym_index))
+    return;
+  if (this->plt_ == NULL)
+    this->make_plt_section(symtab, layout);
+  unsigned int plt_offset = this->plt_->add_local_ifunc_entry(symtab, layout,
+							      relobj,
+							      local_sym_index);
+  relobj->set_local_plt_offset(local_sym_index, plt_offset);
 }
 
 // Return the number of entries in the PLT.
@@ -1720,7 +1996,9 @@ Target_sparc<size, big_endian>::Scan::get_reference_flags(unsigned int r_type)
     case elfcpp::R_SPARC_COPY:
     case elfcpp::R_SPARC_GLOB_DAT:
     case elfcpp::R_SPARC_JMP_SLOT:
+    case elfcpp::R_SPARC_JMP_IREL:
     case elfcpp::R_SPARC_RELATIVE:
+    case elfcpp::R_SPARC_IRELATIVE:
     case elfcpp::R_SPARC_TLS_DTPMOD64:
     case elfcpp::R_SPARC_TLS_DTPMOD32:
     case elfcpp::R_SPARC_TLS_DTPOFF64:
@@ -1772,10 +2050,12 @@ Target_sparc<size, big_endian>::Scan::check_non_pic(Relobj* object, unsigned int
 	{
 	  // These are the relocation types supported by glibc for sparc 64-bit.
 	case elfcpp::R_SPARC_RELATIVE:
+	case elfcpp::R_SPARC_IRELATIVE:
 	case elfcpp::R_SPARC_COPY:
 	case elfcpp::R_SPARC_64:
 	case elfcpp::R_SPARC_GLOB_DAT:
 	case elfcpp::R_SPARC_JMP_SLOT:
+	case elfcpp::R_SPARC_JMP_IREL:
 	case elfcpp::R_SPARC_TLS_DTPMOD64:
 	case elfcpp::R_SPARC_TLS_DTPOFF64:
 	case elfcpp::R_SPARC_TLS_TPOFF64:
@@ -1812,10 +2092,12 @@ Target_sparc<size, big_endian>::Scan::check_non_pic(Relobj* object, unsigned int
 	{
 	  // These are the relocation types supported by glibc for sparc 32-bit.
 	case elfcpp::R_SPARC_RELATIVE:
+	case elfcpp::R_SPARC_IRELATIVE:
 	case elfcpp::R_SPARC_COPY:
 	case elfcpp::R_SPARC_GLOB_DAT:
 	case elfcpp::R_SPARC_32:
 	case elfcpp::R_SPARC_JMP_SLOT:
+	case elfcpp::R_SPARC_JMP_IREL:
 	case elfcpp::R_SPARC_TLS_DTPMOD32:
 	case elfcpp::R_SPARC_TLS_DTPOFF32:
 	case elfcpp::R_SPARC_TLS_TPOFF32:
@@ -1850,6 +2132,22 @@ Target_sparc<size, big_endian>::Scan::check_non_pic(Relobj* object, unsigned int
   return;
 }
 
+// Return whether we need to make a PLT entry for a relocation of the
+// given type against a STT_GNU_IFUNC symbol.
+
+template<int size, bool big_endian>
+bool
+Target_sparc<size, big_endian>::Scan::reloc_needs_plt_for_ifunc(
+     Sized_relobj_file<size, big_endian>* object,
+     unsigned int r_type)
+{
+  int flags = Scan::get_reference_flags(r_type);
+  if (flags & Symbol::TLS_REF)
+    gold_error(_("%s: unsupported TLS reloc %u for IFUNC symbol"),
+               object->name().c_str(), r_type);
+  return flags != 0;
+}
+
 // Scan a relocation for a local symbol.
 
 template<int size, bool big_endian>
@@ -1865,9 +2163,17 @@ Target_sparc<size, big_endian>::Scan::local(
 			unsigned int r_type,
 			const elfcpp::Sym<size, big_endian>& lsym)
 {
+  bool is_ifunc = lsym.get_st_type() == elfcpp::STT_GNU_IFUNC;
   unsigned int orig_r_type = r_type;
-
   r_type &= 0xff;
+
+  if (is_ifunc
+      && this->reloc_needs_plt_for_ifunc(object, r_type))
+    {
+      unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
+      target->make_local_ifunc_plt_entry(symtab, layout, object, r_sym);
+    }
+
   switch (r_type)
     {
     case elfcpp::R_SPARC_NONE:
@@ -1891,7 +2197,7 @@ Target_sparc<size, big_endian>::Scan::local(
           rela_dyn->add_local_relative(object, r_sym, elfcpp::R_SPARC_RELATIVE,
 				       output_section, data_shndx,
 				       reloc.get_r_offset(),
-				       reloc.get_r_addend(), false);
+				       reloc.get_r_addend(), is_ifunc);
         }
       break;
 
@@ -1978,13 +2284,11 @@ Target_sparc<size, big_endian>::Scan::local(
 	    if (!object->local_has_got_offset(r_sym, GOT_TYPE_STANDARD))
 	      {
 		Reloc_section* rela_dyn = target->rela_dyn_section(layout);
-		unsigned int off;
-
-		off = got->add_constant(0);
+		unsigned int off = got->add_constant(0);
 		object->set_local_got_offset(r_sym, GOT_TYPE_STANDARD, off);
 		rela_dyn->add_local_relative(object, r_sym,
 					     elfcpp::R_SPARC_RELATIVE,
-					     got, off, 0, false);
+					     got, off, 0, is_ifunc);
 	      }
 	  }
 	else
@@ -2126,7 +2430,9 @@ Target_sparc<size, big_endian>::Scan::local(
     case elfcpp::R_SPARC_COPY:
     case elfcpp::R_SPARC_GLOB_DAT:
     case elfcpp::R_SPARC_JMP_SLOT:
+    case elfcpp::R_SPARC_JMP_IREL:
     case elfcpp::R_SPARC_RELATIVE:
+    case elfcpp::R_SPARC_IRELATIVE:
     case elfcpp::R_SPARC_TLS_DTPMOD64:
     case elfcpp::R_SPARC_TLS_DTPMOD32:
     case elfcpp::R_SPARC_TLS_DTPOFF64:
@@ -2172,6 +2478,7 @@ Target_sparc<size, big_endian>::Scan::global(
 				Symbol* gsym)
 {
   unsigned int orig_r_type = r_type;
+  bool is_ifunc = gsym->type() == elfcpp::STT_GNU_IFUNC;
 
   // A reference to _GLOBAL_OFFSET_TABLE_ implies that we need a got
   // section.  We check here to avoid creating a dynamic reloc against
@@ -2181,6 +2488,12 @@ Target_sparc<size, big_endian>::Scan::global(
     target->got_section(symtab, layout);
 
   r_type &= 0xff;
+
+  // A STT_GNU_IFUNC symbol may require a PLT entry.
+  if (is_ifunc
+      && this->reloc_needs_plt_for_ifunc(object, r_type))
+    target->make_plt_entry(symtab, layout, gsym);
+
   switch (r_type)
     {
     case elfcpp::R_SPARC_NONE:
@@ -2333,7 +2646,7 @@ Target_sparc<size, big_endian>::Scan::global(
                 rela_dyn->add_global_relative(gsym, elfcpp::R_SPARC_RELATIVE,
 					      output_section, object,
 					      data_shndx, reloc.get_r_offset(),
-					      reloc.get_r_addend(), false);
+					      reloc.get_r_addend(), is_ifunc);
               }
             else
               {
@@ -2370,24 +2683,62 @@ Target_sparc<size, big_endian>::Scan::global(
 
 	got = target->got_section(symtab, layout);
         if (gsym->final_value_is_known())
-          got->add_global(gsym, GOT_TYPE_STANDARD);
+	  {
+	    // For a STT_GNU_IFUNC symbol we want the PLT address.
+	    if (gsym->type() == elfcpp::STT_GNU_IFUNC)
+	      got->add_global_plt(gsym, GOT_TYPE_STANDARD);
+	    else
+	      got->add_global(gsym, GOT_TYPE_STANDARD);
+	  }
         else
           {
             // If this symbol is not fully resolved, we need to add a
+	    bool is_ifunc = gsym->type() == elfcpp::STT_GNU_IFUNC;
+
+	    // Use a GLOB_DAT rather than a RELATIVE reloc if:
+	    //
+	    // 1) The symbol may be defined in some other module.
+	    //
+	    // 2) We are building a shared library and this is a
+	    // protected symbol; using GLOB_DAT means that the dynamic
+	    // linker can use the address of the PLT in the main
+	    // executable when appropriate so that function address
+	    // comparisons work.
             // dynamic relocation for it.
             Reloc_section* rela_dyn = target->rela_dyn_section(layout);
             if (gsym->is_from_dynobj()
                 || gsym->is_undefined()
-                || gsym->is_preemptible())
-              got->add_global_with_rel(gsym, GOT_TYPE_STANDARD, rela_dyn,
-				       elfcpp::R_SPARC_GLOB_DAT);
+                || gsym->is_preemptible()
+		|| (gsym->visibility() == elfcpp::STV_PROTECTED
+		    && parameters->options().shared()))
+	      {
+		unsigned int r_type = elfcpp::R_SPARC_GLOB_DAT;
+
+		// If this symbol is forced local, this relocation will
+		// not work properly.  That's because ld.so on sparc
+		// (and 32-bit powerpc) expects st_value in the r_addend
+		// of relocations for STB_LOCAL symbols.  Curiously the
+		// BFD linker does not promote global hidden symbols to be
+		// STB_LOCAL in the dynamic symbol table like Gold does.
+		gold_assert(!gsym->is_forced_local());
+		got->add_global_with_rel(gsym, GOT_TYPE_STANDARD, rela_dyn,
+					 r_type);
+	      }
             else if (!gsym->has_got_offset(GOT_TYPE_STANDARD))
               {
 		unsigned int off = got->add_constant(0);
 
 		gsym->set_got_offset(GOT_TYPE_STANDARD, off);
+		if (is_ifunc)
+		  {
+		    // Tell the dynamic linker to use the PLT address
+		    // when resolving relocations.
+		    if (gsym->is_from_dynobj()
+			&& !parameters->options().shared())
+		      gsym->set_needs_dynsym_value();
+		  }
 		rela_dyn->add_global_relative(gsym, elfcpp::R_SPARC_RELATIVE,
-					      got, off, 0, false);
+					      got, off, 0, is_ifunc);
 	      }
           }
       }
@@ -2520,7 +2871,9 @@ Target_sparc<size, big_endian>::Scan::global(
     case elfcpp::R_SPARC_COPY:
     case elfcpp::R_SPARC_GLOB_DAT:
     case elfcpp::R_SPARC_JMP_SLOT:
+    case elfcpp::R_SPARC_JMP_IREL:
     case elfcpp::R_SPARC_RELATIVE:
+    case elfcpp::R_SPARC_IRELATIVE:
     case elfcpp::R_SPARC_TLS_DTPMOD64:
     case elfcpp::R_SPARC_TLS_DTPMOD32:
     case elfcpp::R_SPARC_TLS_DTPOFF64:
@@ -2620,8 +2973,11 @@ void
 Target_sparc<size, big_endian>::do_finalize_sections(
     Layout* layout,
     const Input_objects*,
-    Symbol_table*)
+    Symbol_table* symtab)
 {
+  if (this->plt_)
+    this->plt_->emit_pending_ifunc_relocs();
+
   // Fill in some more dynamic tags.
   const Reloc_section* rel_plt = (this->plt_ == NULL
 				  ? NULL
@@ -2633,6 +2989,47 @@ Target_sparc<size, big_endian>::do_finalize_sections(
   // relocs.
   if (this->copy_relocs_.any_saved_relocs())
     this->copy_relocs_.emit(this->rela_dyn_section(layout));
+
+  if (parameters->doing_static_link()
+      && (this->plt_ == NULL || !this->plt_->has_ifunc_section()))
+    {
+      // If linking statically, make sure that the __rela_iplt symbols
+      // were defined if necessary, even if we didn't create a PLT.
+      static const Define_symbol_in_segment syms[] =
+	{
+	  {
+	    "__rela_iplt_start",	// name
+	    elfcpp::PT_LOAD,		// segment_type
+	    elfcpp::PF_W,		// segment_flags_set
+	    elfcpp::PF(0),		// segment_flags_clear
+	    0,				// value
+	    0,				// size
+	    elfcpp::STT_NOTYPE,		// type
+	    elfcpp::STB_GLOBAL,		// binding
+	    elfcpp::STV_HIDDEN,		// visibility
+	    0,				// nonvis
+	    Symbol::SEGMENT_START,	// offset_from_base
+	    true			// only_if_ref
+	  },
+	  {
+	    "__rela_iplt_end",		// name
+	    elfcpp::PT_LOAD,		// segment_type
+	    elfcpp::PF_W,		// segment_flags_set
+	    elfcpp::PF(0),		// segment_flags_clear
+	    0,				// value
+	    0,				// size
+	    elfcpp::STT_NOTYPE,		// type
+	    elfcpp::STB_GLOBAL,		// binding
+	    elfcpp::STV_HIDDEN,		// visibility
+	    0,				// nonvis
+	    Symbol::SEGMENT_START,	// offset_from_base
+	    true			// only_if_ref
+	  }
+	};
+
+      symtab->define_symbols(layout, 2, syms,
+			     layout->script_options()->saw_sections_clause());
+    }
 }
 
 // Perform a relocation.
@@ -2669,6 +3066,7 @@ Target_sparc<size, big_endian>::Relocate::relocate(
     view -= 4;
 
   typedef Sparc_relocate_functions<size, big_endian> Reloc;
+  const Sized_relobj_file<size, big_endian>* object = relinfo->object;
 
   // Pick the value to use for symbols defined in shared objects.
   Symbol_value<size> symval;
@@ -2677,14 +3075,23 @@ Target_sparc<size, big_endian>::Relocate::relocate(
     {
       elfcpp::Elf_Xword value;
 
-      value = target->plt_section()->address() + gsym->plt_offset();
+      value = target->plt_address_for_global(gsym) + gsym->plt_offset();
 
       symval.set_output_value(value);
 
       psymval = &symval;
     }
+  else if (gsym == NULL && psymval->is_ifunc_symbol())
+    {
+      unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
+      if (object->local_has_plt_offset(r_sym))
+	{
+	  symval.set_output_value(target->plt_address_for_local(object, r_sym)
+				  + object->local_plt_offset(r_sym));
+	  psymval = &symval;
+	}
+    }
 
-  const Sized_relobj_file<size, big_endian>* object = relinfo->object;
   const elfcpp::Elf_Xword addend = rela.get_r_addend();
 
   // Get the GOT offset if needed.  Unlike i386 and x86_64, our GOT
@@ -2995,7 +3402,9 @@ Target_sparc<size, big_endian>::Relocate::relocate(
     case elfcpp::R_SPARC_COPY:
     case elfcpp::R_SPARC_GLOB_DAT:
     case elfcpp::R_SPARC_JMP_SLOT:
+    case elfcpp::R_SPARC_JMP_IREL:
     case elfcpp::R_SPARC_RELATIVE:
+    case elfcpp::R_SPARC_IRELATIVE:
       // These are outstanding tls relocs, which are unexpected when
       // linking.
     case elfcpp::R_SPARC_TLS_DTPMOD64:
-- 
1.7.9.5

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] Add sparc IFUNC support to Gold.
  2012-04-11 19:15 [PATCH 2/2] Add sparc IFUNC support to Gold David Miller
@ 2012-04-11 22:26 ` David Miller
  2012-04-17  0:15   ` Ian Lance Taylor
  2012-04-17  0:02 ` Ian Lance Taylor
  1 sibling, 1 reply; 8+ messages in thread
From: David Miller @ 2012-04-11 22:26 UTC (permalink / raw)
  To: binutils

From: David Miller <davem@davemloft.net>
Date: Wed, 11 Apr 2012 12:15:44 -0400 (EDT)

> This adds sparc IFUNC support to Gold.

Here is an updated version of the patch which fixes the
handling of local IFUNC symbols in shared objects, uses
GLOB_DAT when appropriate for global IFUNC symbols, and
updates the file copyright years.

Along with the sparc GOTDATA and IFUNC test -fPIC/-fPIE patches I'm
about to post, all of the IFUNC tests pass on sparc.

--------------------
[PATCH] Add sparc IFUNC support to Gold.

elfcpp/

	* sparc.h (R_SPARC_JMP_IREL): New relocation.

gold/

	* sparc.cc (class Target_sparc): Add rela_ifunc_.
	(Target_sparc::Target_sparc): Initialize new field.
	(Target_sparc::do_plt_section_for_global): New function.
	(Target_sparc::do_plt_section_for_local): New function.
	(Target_sparc::reloc_needs_plt_for_ifunc): New function.
	(Target_sparc::make_plt_section): New function, broken out of
	make_plt_entry.  Use ORDER_NON_RELRO_FIRST for ".plt".
	(Target_sparc::make_plt_entry): Call make_plt_section.
	(Target_sparc::make_local_ifunc_plt_entry): New function.
	(Target_sparc::rela_ifunc_section): New function.
	(Target_sparc::plt_section): Remove const.
	(Output_data_plt_sparc): Update declarations.  Define Global_ifunc
	and Local_ifunc types.  Add global_ifuncs_, local_ifuncs_, ifunc_rel_,
	and ifunc_count_ fields.
	(Output_data_plt_sparc::Output_data_plt_sparc): Initialize new fields.
	(Output_data_plt_sparc::add_entry): Handle IFUNC symbols.
	(Output_data_plt_sparc::add_local_ifunc_entry): New function.
	(Output_data_plt_sparc::rela_ifunc): New function.
	(Output_data_plt_sparc::emit_pending_ifunc_relocs): New function.
	(Output_data_plt_sparc::has_ifunc_section): New function.
	(Output_data_plt_sparc::entry_count): Include ifunc_count_.
	(Output_data_plt_sparc::address_for_global): New function.
	(Output_data_plt_sparc::address_for_local): New function.
	(Output_data_plt_sparc::plt_index_to_offset): New function.
	(Output_data_plt_sparc::set_final_data_size): Use plt_index_to_offset
	and entry_count.
	(Output_data_plt_sparc::do_write): Use first_plt_entry_offset and
	entry_count.
	(Target_sparc::Scan::get_reference_flags): Add R_SPARC_IRELATIVE and
	R_SPARC_JMP_IREL to switch.
	(Target_sparc::Scan::check_non_pic): Likewise.
	(Target_sparc::Scan::local): Handle IFUNC symbols.
	(Target_sparc::Scan::local): Likewise.
	(Target_sparc::Relocate::relocate): Likewise, use plt_address_for_global
	and plt_address_for_local.
	(Target_sparc::do_finalize_sections): Call emit_pending_ifunc_relocs.
	Define __rel_iplt_start and __rel_iplt_end if doing a static link.
---
 elfcpp/sparc.h |    1 +
 gold/sparc.cc  |  594 ++++++++++++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 516 insertions(+), 79 deletions(-)

diff --git a/elfcpp/sparc.h b/elfcpp/sparc.h
index 77c4668..6b561be 100644
--- a/elfcpp/sparc.h
+++ b/elfcpp/sparc.h
@@ -142,6 +142,7 @@ enum
   R_SPARC_SIZE64 = 87,        // size of symbol, 64-bit
   R_SPARC_WDISP10 = 88,       // PC relative 10 bit shifted
 
+  R_SPARC_JMP_IREL = 248,     // Create PLT slot to IFUNC function
   R_SPARC_IRELATIVE = 249,    // Adjust indirectly by program base
 
   // GNU vtable garbage collection extensions.
diff --git a/gold/sparc.cc b/gold/sparc.cc
index e1bdc8e..d1e83eb 100644
--- a/gold/sparc.cc
+++ b/gold/sparc.cc
@@ -1,6 +1,6 @@
 // sparc.cc -- sparc target support for gold.
 
-// Copyright 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+// Copyright 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
 // Written by David S. Miller <davem@davemloft.net>.
 
 // This file is part of gold.
@@ -58,7 +58,7 @@ class Target_sparc : public Sized_target<size, big_endian>
 
   Target_sparc()
     : Sized_target<size, big_endian>(&sparc_info),
-      got_(NULL), plt_(NULL), rela_dyn_(NULL),
+      got_(NULL), plt_(NULL), rela_dyn_(NULL), rela_ifunc_(NULL),
       copy_relocs_(elfcpp::R_SPARC_COPY), dynbss_(NULL),
       got_mod_index_offset_(-1U), tls_get_addr_sym_(NULL)
   {
@@ -154,6 +154,15 @@ class Target_sparc : public Sized_target<size, big_endian>
     return strcmp(sym->name(), "___tls_get_addr") == 0;
   }
 
+  // Return the PLT address to use for a global symbol.
+  uint64_t
+  do_plt_address_for_global(const Symbol* gsym) const
+  { return this->plt_section()->address_for_global(gsym); }
+
+  uint64_t
+  do_plt_address_for_local(const Relobj* relobj, unsigned int symndx) const
+  { return this->plt_section()->address_for_local(relobj, symndx); }
+
   // Return whether there is a GOT section.
   bool
   has_got_section() const
@@ -256,6 +265,10 @@ class Target_sparc : public Sized_target<size, big_endian>
     void
     check_non_pic(Relobj*, unsigned int r_type);
 
+    bool
+    reloc_needs_plt_for_ifunc(Sized_relobj_file<size, big_endian>*,
+			      unsigned int r_type);
+
     // Whether we have issued an error about a non-PIC compilation.
     bool issued_non_pic_error_;
   };
@@ -320,10 +333,20 @@ class Target_sparc : public Sized_target<size, big_endian>
   Output_data_got<size, big_endian>*
   got_section(Symbol_table*, Layout*);
 
+  // Create the PLT section.
+  void
+  make_plt_section(Symbol_table* symtab, Layout* layout);
+
   // Create a PLT entry for a global symbol.
   void
   make_plt_entry(Symbol_table*, Layout*, Symbol*);
 
+  // Create a PLT entry for a local STT_GNU_IFUNC symbol.
+  void
+  make_local_ifunc_plt_entry(Symbol_table*, Layout*,
+			     Sized_relobj_file<size, big_endian>* relobj,
+			     unsigned int local_sym_index);
+
   // Create a GOT entry for the TLS module index.
   unsigned int
   got_mod_index_entry(Symbol_table* symtab, Layout* layout,
@@ -341,7 +364,7 @@ class Target_sparc : public Sized_target<size, big_endian>
   }
 
   // Get the PLT section.
-  const Output_data_plt_sparc<size, big_endian>*
+  Output_data_plt_sparc<size, big_endian>*
   plt_section() const
   {
     gold_assert(this->plt_ != NULL);
@@ -352,6 +375,10 @@ class Target_sparc : public Sized_target<size, big_endian>
   Reloc_section*
   rela_dyn_section(Layout*);
 
+  // Get the section to use for IFUNC relocations.
+  Reloc_section*
+  rela_ifunc_section(Layout*);
+
   // Copy a relocation against a global symbol.
   void
   copy_reloc(Symbol_table* symtab, Layout* layout,
@@ -386,6 +413,8 @@ class Target_sparc : public Sized_target<size, big_endian>
   Output_data_plt_sparc<size, big_endian>* plt_;
   // The dynamic reloc section.
   Reloc_section* rela_dyn_;
+  // The section to use for IFUNC relocs.
+  Reloc_section* rela_ifunc_;
   // Relocs saved to avoid a COPY reloc.
   Copy_relocs<elfcpp::SHT_RELA, size, big_endian> copy_relocs_;
   // Space for variables copied with a COPY reloc.
@@ -1145,6 +1174,30 @@ Target_sparc<size, big_endian>::rela_dyn_section(Layout* layout)
   return this->rela_dyn_;
 }
 
+// Get the section to use for IFUNC relocs, creating it if
+// necessary.  These go in .rela.dyn, but only after all other dynamic
+// relocations.  They need to follow the other dynamic relocations so
+// that they can refer to global variables initialized by those
+// relocs.
+
+template<int size, bool big_endian>
+typename Target_sparc<size, big_endian>::Reloc_section*
+Target_sparc<size, big_endian>::rela_ifunc_section(Layout* layout)
+{
+  if (this->rela_ifunc_ == NULL)
+    {
+      // Make sure we have already created the dynamic reloc section.
+      this->rela_dyn_section(layout);
+      this->rela_ifunc_ = new Reloc_section(false);
+      layout->add_output_section_data(".rela.dyn", elfcpp::SHT_RELA,
+				      elfcpp::SHF_ALLOC, this->rela_ifunc_,
+				      ORDER_DYNAMIC_RELOCS, false);
+      gold_assert(this->rela_dyn_->output_section()
+		  == this->rela_ifunc_->output_section());
+    }
+  return this->rela_ifunc_;
+}
+
 // A class to handle the PLT data.
 
 template<int size, bool big_endian>
@@ -1157,7 +1210,13 @@ class Output_data_plt_sparc : public Output_section_data
   Output_data_plt_sparc(Layout*);
 
   // Add an entry to the PLT.
-  void add_entry(Symbol* gsym);
+  void add_entry(Symbol_table* symtab, Layout* layout, Symbol* gsym);
+
+  // Add an entry to the PLT for a local STT_GNU_IFUNC symbol.
+  unsigned int
+  add_local_ifunc_entry(Symbol_table*, Layout*,
+			Sized_relobj_file<size, big_endian>* relobj,
+			unsigned int local_sym_index);
 
   // Return the .rela.plt section data.
   const Reloc_section* rel_plt() const
@@ -1165,10 +1224,22 @@ class Output_data_plt_sparc : public Output_section_data
     return this->rel_;
   }
 
+  // Return where the IFUNC relocations should go.
+  Reloc_section*
+  rela_ifunc(Symbol_table*, Layout*);
+
+  void
+  emit_pending_ifunc_relocs();
+
+  // Return whether we created a section for IFUNC relocations.
+  bool
+  has_ifunc_section() const
+  { return this->ifunc_rel_ != NULL; }
+
   // Return the number of PLT entries.
   unsigned int
   entry_count() const
-  { return this->count_; }
+  { return this->count_ + this->ifunc_count_; }
 
   // Return the offset of the first non-reserved PLT entry.
   static unsigned int
@@ -1180,6 +1251,14 @@ class Output_data_plt_sparc : public Output_section_data
   get_plt_entry_size()
   { return base_plt_entry_size; }
 
+  // Return the PLT address to use for a global symbol.
+  uint64_t
+  address_for_global(const Symbol*);
+
+  // Return the PLT address to use for a local symbol.
+  uint64_t
+  address_for_local(const Relobj*, unsigned int symndx);
+
  protected:
   void do_adjust_output_section(Output_section* os);
 
@@ -1199,34 +1278,69 @@ class Output_data_plt_sparc : public Output_section_data
     (plt_entries_per_block
      * (plt_insn_chunk_size + plt_pointer_chunk_size));
 
-  // Set the final size.
-  void
-  set_final_data_size()
+  section_offset_type
+  plt_index_to_offset(unsigned int index)
   {
-    unsigned int full_count = this->count_ + 4;
-    unsigned int extra = (size == 32 ? 4 : 0);
+    section_offset_type offset;
 
-    if (size == 32 || full_count < 32768)
-      this->set_data_size((full_count * base_plt_entry_size) + extra);
+    if (size == 32 || index < 32768)
+      offset = index * base_plt_entry_size;
     else
       {
-	unsigned int ext_cnt = full_count - 32768;
+	unsigned int ext_index = index - 32768;
 
-	this->set_data_size((32768 * base_plt_entry_size)
-			    + (ext_cnt
-			       * (plt_insn_chunk_size
-				  + plt_pointer_chunk_size)));
+	offset = (32768 * base_plt_entry_size)
+	  + ((ext_index / plt_entries_per_block)
+	     * plt_block_size)
+	  + ((ext_index % plt_entries_per_block)
+	     * plt_insn_chunk_size);
       }
+    return offset;
+  }
+
+  // Set the final size.
+  void
+  set_final_data_size()
+  {
+    unsigned int full_count = this->entry_count() + 4;
+    unsigned int extra = (size == 32 ? 4 : 0);
+    section_offset_type sz = plt_index_to_offset(full_count) + extra;
+
+    return this->set_data_size(sz);
   }
 
   // Write out the PLT data.
   void
   do_write(Output_file*);
 
+  struct Global_ifunc
+  {
+    Reloc_section* rel;
+    Symbol* gsym;
+    unsigned int plt_index;
+  };
+
+  struct Local_ifunc
+  {
+    Reloc_section* rel;
+    Sized_relobj_file<size, big_endian>* object;
+    unsigned int local_sym_index;
+    unsigned int plt_index;
+  };
+
   // The reloc section.
   Reloc_section* rel_;
+  // The IFUNC relocations, if necessary.  These must follow the
+  // regular relocations.
+  Reloc_section* ifunc_rel_;
   // The number of PLT entries.
   unsigned int count_;
+  // The number of PLT entries for IFUNC symbols.
+  unsigned int ifunc_count_;
+  // Global STT_GNU_IFUNC symbols.
+  std::vector<Global_ifunc> global_ifuncs_;
+  // Local STT_GNU_IFUNC symbols.
+  std::vector<Local_ifunc> local_ifuncs_;
 };
 
 // Define the constants as required by C++ standard.
@@ -1253,7 +1367,8 @@ const unsigned int Output_data_plt_sparc<size, big_endian>::plt_block_size;
 
 template<int size, bool big_endian>
 Output_data_plt_sparc<size, big_endian>::Output_data_plt_sparc(Layout* layout)
-  : Output_section_data(size == 32 ? 4 : 8), count_(0)
+  : Output_section_data(size == 32 ? 4 : 8), ifunc_rel_(NULL),
+    count_(0), ifunc_count_(0), global_ifuncs_(), local_ifuncs_()
 {
   this->rel_ = new Reloc_section(false);
   layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA,
@@ -1272,38 +1387,171 @@ Output_data_plt_sparc<size, big_endian>::do_adjust_output_section(Output_section
 
 template<int size, bool big_endian>
 void
-Output_data_plt_sparc<size, big_endian>::add_entry(Symbol* gsym)
+Output_data_plt_sparc<size, big_endian>::add_entry(Symbol_table* symtab,
+						   Layout* layout,
+						   Symbol* gsym)
 {
   gold_assert(!gsym->has_plt_offset());
 
-  unsigned int index = this->count_ + 4;
   section_offset_type plt_offset;
+  unsigned int index;
 
-  if (size == 32 || index < 32768)
-    plt_offset = index * base_plt_entry_size;
+  if (gsym->type() == elfcpp::STT_GNU_IFUNC
+      && gsym->can_use_relative_reloc(false))
+    {
+      index = this->ifunc_count_;
+      plt_offset = plt_index_to_offset(index);
+      gsym->set_plt_offset(plt_offset);
+      ++this->ifunc_count_;
+      Reloc_section* rel = this->rela_ifunc(symtab, layout);
+
+      struct Global_ifunc gi;
+      gi.rel = rel;
+      gi.gsym = gsym;
+      gi.plt_index = index;
+      this->global_ifuncs_.push_back(gi);
+    }
   else
     {
-	unsigned int ext_index = index - 32768;
+      plt_offset = plt_index_to_offset(this->count_ + 4);
+      gsym->set_plt_offset(plt_offset);
+      ++this->count_;
+      gsym->set_needs_dynsym_entry();
+      this->rel_->add_global(gsym, elfcpp::R_SPARC_JMP_SLOT, this,
+			     plt_offset, 0);
+    }
 
-	plt_offset = (32768 * base_plt_entry_size)
-	  + ((ext_index / plt_entries_per_block)
-	     * plt_block_size)
-	  + ((ext_index % plt_entries_per_block)
-	     * plt_insn_chunk_size);
+  // Note that we don't need to save the symbol.  The contents of the
+  // PLT are independent of which symbols are used.  The symbols only
+  // appear in the relocations.
+}
+
+template<int size, bool big_endian>
+unsigned int
+Output_data_plt_sparc<size, big_endian>::add_local_ifunc_entry(
+    Symbol_table* symtab,
+    Layout* layout,
+    Sized_relobj_file<size, big_endian>* relobj,
+    unsigned int local_sym_index)
+{
+  unsigned int index = this->ifunc_count_;
+  section_offset_type plt_offset;
+
+  plt_offset = plt_index_to_offset(index);
+  ++this->ifunc_count_;
+
+  Reloc_section* rel = this->rela_ifunc(symtab, layout);
+
+  struct Local_ifunc li;
+  li.rel = rel;
+  li.object = relobj;
+  li.local_sym_index = local_sym_index;
+  li.plt_index = index;
+  this->local_ifuncs_.push_back(li);
+
+  return plt_offset;
+}
+
+// Emit any pending IFUNC plt relocations.
+
+template<int size, bool big_endian>
+void
+Output_data_plt_sparc<size, big_endian>::emit_pending_ifunc_relocs()
+{
+  // Emit any pending IFUNC relocs.
+  for (typename std::vector<Global_ifunc>::const_iterator p =
+	 this->global_ifuncs_.begin();
+       p != this->global_ifuncs_.end();
+       ++p)
+    {
+      section_offset_type plt_offset;
+      unsigned int index;
+
+      index = this->count_ + p->plt_index + 4;
+      plt_offset = this->plt_index_to_offset(index);
+      p->rel->add_symbolless_global_addend(p->gsym, elfcpp::R_SPARC_JMP_IREL,
+					   this, plt_offset, 0);
+    }
+
+  for (typename std::vector<Local_ifunc>::const_iterator p =
+	 this->local_ifuncs_.begin();
+       p != this->local_ifuncs_.end();
+       ++p)
+    {
+      section_offset_type plt_offset;
+      unsigned int index;
+
+      index = this->count_ + p->plt_index + 4;
+      plt_offset = this->plt_index_to_offset(index);
+      p->rel->add_symbolless_local_addend(p->object, p->local_sym_index,
+					  elfcpp::R_SPARC_JMP_IREL,
+					  this, plt_offset, 0);
     }
+}
 
-  gsym->set_plt_offset(plt_offset);
+// Return where the IFUNC relocations should go in the PLT.  These
+// follow the non-IFUNC relocations.
 
-  ++this->count_;
+template<int size, bool big_endian>
+typename Output_data_plt_sparc<size, big_endian>::Reloc_section*
+Output_data_plt_sparc<size, big_endian>::rela_ifunc(
+	Symbol_table* symtab,
+	Layout* layout)
+{
+  if (this->ifunc_rel_ == NULL)
+    {
+      this->ifunc_rel_ = new Reloc_section(false);
+      layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA,
+				      elfcpp::SHF_ALLOC, this->ifunc_rel_,
+				      ORDER_DYNAMIC_PLT_RELOCS, false);
+      gold_assert(this->ifunc_rel_->output_section()
+		  == this->rel_->output_section());
+
+      if (parameters->doing_static_link())
+	{
+	  // A statically linked executable will only have a .rel.plt
+	  // section to hold R_SPARC_IRELATIVE and R_SPARC_JMP_IREL
+	  // relocs for STT_GNU_IFUNC symbols.  The library will use
+	  // these symbols to locate the IRELATIVE and JMP_IREL relocs
+	  // at program startup time.
+	  symtab->define_in_output_data("__rela_iplt_start", NULL,
+					Symbol_table::PREDEFINED,
+					this->ifunc_rel_, 0, 0,
+					elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
+					elfcpp::STV_HIDDEN, 0, false, true);
+	  symtab->define_in_output_data("__rela_iplt_end", NULL,
+					Symbol_table::PREDEFINED,
+					this->ifunc_rel_, 0, 0,
+					elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
+					elfcpp::STV_HIDDEN, 0, true, true);
+	}
+    }
+  return this->ifunc_rel_;
+}
 
-  // Every PLT entry needs a reloc.
-  gsym->set_needs_dynsym_entry();
-  this->rel_->add_global(gsym, elfcpp::R_SPARC_JMP_SLOT, this,
-			 plt_offset, 0);
+// Return the PLT address to use for a global symbol.
 
-  // Note that we don't need to save the symbol.  The contents of the
-  // PLT are independent of which symbols are used.  The symbols only
-  // appear in the relocations.
+template<int size, bool big_endian>
+uint64_t
+Output_data_plt_sparc<size, big_endian>::address_for_global(const Symbol* gsym)
+{
+  uint64_t offset = 0;
+  if (gsym->type() == elfcpp::STT_GNU_IFUNC
+      && gsym->can_use_relative_reloc(false))
+    offset = plt_index_to_offset(this->count_ + 4);
+  return this->address() + offset;
+}
+
+// Return the PLT address to use for a local symbol.  These are always
+// IRELATIVE relocs.
+
+template<int size, bool big_endian>
+uint64_t
+Output_data_plt_sparc<size, big_endian>::address_for_local(
+	const Relobj*,
+	unsigned int)
+{
+  return this->address() + plt_index_to_offset(this->count_ + 4);
 }
 
 static const unsigned int sparc_nop = 0x01000000;
@@ -1331,10 +1579,10 @@ Output_data_plt_sparc<size, big_endian>::do_write(Output_file* of)
   unsigned char* pov = oview;
 
   memset(pov, 0, base_plt_entry_size * 4);
-  pov += base_plt_entry_size * 4;
+  pov += this->first_plt_entry_offset();
 
   unsigned int plt_offset = base_plt_entry_size * 4;
-  const unsigned int count = this->count_;
+  const unsigned int count = this->entry_count();
 
   if (size == 64)
     {
@@ -1454,6 +1702,38 @@ Output_data_plt_sparc<size, big_endian>::do_write(Output_file* of)
   of->write_output_view(offset, oview_size, oview);
 }
 
+// Create the PLT section.
+
+template<int size, bool big_endian>
+void
+Target_sparc<size, big_endian>::make_plt_section(Symbol_table* symtab,
+						 Layout* layout)
+{
+  // Create the GOT sections first.
+  this->got_section(symtab, layout);
+
+  // Ensure that .rela.dyn always appears before .rela.plt  This is
+  // necessary due to how, on Sparc and some other targets, .rela.dyn
+  // needs to include .rela.plt in it's range.
+  this->rela_dyn_section(layout);
+
+  this->plt_ = new Output_data_plt_sparc<size, big_endian>(layout);
+  layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
+				  (elfcpp::SHF_ALLOC
+				   | elfcpp::SHF_EXECINSTR
+				   | elfcpp::SHF_WRITE),
+				  this->plt_, ORDER_NON_RELRO_FIRST, false);
+
+  // Define _PROCEDURE_LINKAGE_TABLE_ at the start of the .plt section.
+  symtab->define_in_output_data("_PROCEDURE_LINKAGE_TABLE_", NULL,
+				Symbol_table::PREDEFINED,
+				this->plt_,
+				0, 0, elfcpp::STT_OBJECT,
+				elfcpp::STB_LOCAL,
+				elfcpp::STV_HIDDEN, 0,
+				false, false);
+}
+
 // Create a PLT entry for a global symbol.
 
 template<int size, bool big_endian>
@@ -1466,33 +1746,29 @@ Target_sparc<size, big_endian>::make_plt_entry(Symbol_table* symtab,
     return;
 
   if (this->plt_ == NULL)
-    {
-      // Create the GOT sections first.
-      this->got_section(symtab, layout);
-
-      // Ensure that .rela.dyn always appears before .rela.plt  This is
-      // necessary due to how, on Sparc and some other targets, .rela.dyn
-      // needs to include .rela.plt in it's range.
-      this->rela_dyn_section(layout);
+    this->make_plt_section(symtab, layout);
 
-      this->plt_ = new Output_data_plt_sparc<size, big_endian>(layout);
-      layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
-				      (elfcpp::SHF_ALLOC
-				       | elfcpp::SHF_EXECINSTR
-				       | elfcpp::SHF_WRITE),
-				      this->plt_, ORDER_PLT, false);
+  this->plt_->add_entry(symtab, layout, gsym);
+}
 
-      // Define _PROCEDURE_LINKAGE_TABLE_ at the start of the .plt section.
-      symtab->define_in_output_data("_PROCEDURE_LINKAGE_TABLE_", NULL,
-				    Symbol_table::PREDEFINED,
-				    this->plt_,
-				    0, 0, elfcpp::STT_OBJECT,
-				    elfcpp::STB_LOCAL,
-				    elfcpp::STV_HIDDEN, 0,
-				    false, false);
-    }
+// Make a PLT entry for a local STT_GNU_IFUNC symbol.
 
-  this->plt_->add_entry(gsym);
+template<int size, bool big_endian>
+void
+Target_sparc<size, big_endian>::make_local_ifunc_plt_entry(
+	Symbol_table* symtab,
+	Layout* layout,
+	Sized_relobj_file<size, big_endian>* relobj,
+	unsigned int local_sym_index)
+{
+  if (relobj->local_has_plt_offset(local_sym_index))
+    return;
+  if (this->plt_ == NULL)
+    this->make_plt_section(symtab, layout);
+  unsigned int plt_offset = this->plt_->add_local_ifunc_entry(symtab, layout,
+							      relobj,
+							      local_sym_index);
+  relobj->set_local_plt_offset(local_sym_index, plt_offset);
 }
 
 // Return the number of entries in the PLT.
@@ -1720,7 +1996,9 @@ Target_sparc<size, big_endian>::Scan::get_reference_flags(unsigned int r_type)
     case elfcpp::R_SPARC_COPY:
     case elfcpp::R_SPARC_GLOB_DAT:
     case elfcpp::R_SPARC_JMP_SLOT:
+    case elfcpp::R_SPARC_JMP_IREL:
     case elfcpp::R_SPARC_RELATIVE:
+    case elfcpp::R_SPARC_IRELATIVE:
     case elfcpp::R_SPARC_TLS_DTPMOD64:
     case elfcpp::R_SPARC_TLS_DTPMOD32:
     case elfcpp::R_SPARC_TLS_DTPOFF64:
@@ -1772,10 +2050,12 @@ Target_sparc<size, big_endian>::Scan::check_non_pic(Relobj* object, unsigned int
 	{
 	  // These are the relocation types supported by glibc for sparc 64-bit.
 	case elfcpp::R_SPARC_RELATIVE:
+	case elfcpp::R_SPARC_IRELATIVE:
 	case elfcpp::R_SPARC_COPY:
 	case elfcpp::R_SPARC_64:
 	case elfcpp::R_SPARC_GLOB_DAT:
 	case elfcpp::R_SPARC_JMP_SLOT:
+	case elfcpp::R_SPARC_JMP_IREL:
 	case elfcpp::R_SPARC_TLS_DTPMOD64:
 	case elfcpp::R_SPARC_TLS_DTPOFF64:
 	case elfcpp::R_SPARC_TLS_TPOFF64:
@@ -1812,10 +2092,12 @@ Target_sparc<size, big_endian>::Scan::check_non_pic(Relobj* object, unsigned int
 	{
 	  // These are the relocation types supported by glibc for sparc 32-bit.
 	case elfcpp::R_SPARC_RELATIVE:
+	case elfcpp::R_SPARC_IRELATIVE:
 	case elfcpp::R_SPARC_COPY:
 	case elfcpp::R_SPARC_GLOB_DAT:
 	case elfcpp::R_SPARC_32:
 	case elfcpp::R_SPARC_JMP_SLOT:
+	case elfcpp::R_SPARC_JMP_IREL:
 	case elfcpp::R_SPARC_TLS_DTPMOD32:
 	case elfcpp::R_SPARC_TLS_DTPOFF32:
 	case elfcpp::R_SPARC_TLS_TPOFF32:
@@ -1850,6 +2132,22 @@ Target_sparc<size, big_endian>::Scan::check_non_pic(Relobj* object, unsigned int
   return;
 }
 
+// Return whether we need to make a PLT entry for a relocation of the
+// given type against a STT_GNU_IFUNC symbol.
+
+template<int size, bool big_endian>
+bool
+Target_sparc<size, big_endian>::Scan::reloc_needs_plt_for_ifunc(
+     Sized_relobj_file<size, big_endian>* object,
+     unsigned int r_type)
+{
+  int flags = Scan::get_reference_flags(r_type);
+  if (flags & Symbol::TLS_REF)
+    gold_error(_("%s: unsupported TLS reloc %u for IFUNC symbol"),
+               object->name().c_str(), r_type);
+  return flags != 0;
+}
+
 // Scan a relocation for a local symbol.
 
 template<int size, bool big_endian>
@@ -1865,9 +2163,17 @@ Target_sparc<size, big_endian>::Scan::local(
 			unsigned int r_type,
 			const elfcpp::Sym<size, big_endian>& lsym)
 {
+  bool is_ifunc = lsym.get_st_type() == elfcpp::STT_GNU_IFUNC;
   unsigned int orig_r_type = r_type;
-
   r_type &= 0xff;
+
+  if (is_ifunc
+      && this->reloc_needs_plt_for_ifunc(object, r_type))
+    {
+      unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
+      target->make_local_ifunc_plt_entry(symtab, layout, object, r_sym);
+    }
+
   switch (r_type)
     {
     case elfcpp::R_SPARC_NONE:
@@ -1891,7 +2197,7 @@ Target_sparc<size, big_endian>::Scan::local(
           rela_dyn->add_local_relative(object, r_sym, elfcpp::R_SPARC_RELATIVE,
 				       output_section, data_shndx,
 				       reloc.get_r_offset(),
-				       reloc.get_r_addend(), false);
+				       reloc.get_r_addend(), is_ifunc);
         }
       break;
 
@@ -1978,13 +2284,11 @@ Target_sparc<size, big_endian>::Scan::local(
 	    if (!object->local_has_got_offset(r_sym, GOT_TYPE_STANDARD))
 	      {
 		Reloc_section* rela_dyn = target->rela_dyn_section(layout);
-		unsigned int off;
-
-		off = got->add_constant(0);
+		unsigned int off = got->add_constant(0);
 		object->set_local_got_offset(r_sym, GOT_TYPE_STANDARD, off);
 		rela_dyn->add_local_relative(object, r_sym,
 					     elfcpp::R_SPARC_RELATIVE,
-					     got, off, 0, false);
+					     got, off, 0, is_ifunc);
 	      }
 	  }
 	else
@@ -2126,7 +2430,9 @@ Target_sparc<size, big_endian>::Scan::local(
     case elfcpp::R_SPARC_COPY:
     case elfcpp::R_SPARC_GLOB_DAT:
     case elfcpp::R_SPARC_JMP_SLOT:
+    case elfcpp::R_SPARC_JMP_IREL:
     case elfcpp::R_SPARC_RELATIVE:
+    case elfcpp::R_SPARC_IRELATIVE:
     case elfcpp::R_SPARC_TLS_DTPMOD64:
     case elfcpp::R_SPARC_TLS_DTPMOD32:
     case elfcpp::R_SPARC_TLS_DTPOFF64:
@@ -2172,6 +2478,7 @@ Target_sparc<size, big_endian>::Scan::global(
 				Symbol* gsym)
 {
   unsigned int orig_r_type = r_type;
+  bool is_ifunc = gsym->type() == elfcpp::STT_GNU_IFUNC;
 
   // A reference to _GLOBAL_OFFSET_TABLE_ implies that we need a got
   // section.  We check here to avoid creating a dynamic reloc against
@@ -2181,6 +2488,12 @@ Target_sparc<size, big_endian>::Scan::global(
     target->got_section(symtab, layout);
 
   r_type &= 0xff;
+
+  // A STT_GNU_IFUNC symbol may require a PLT entry.
+  if (is_ifunc
+      && this->reloc_needs_plt_for_ifunc(object, r_type))
+    target->make_plt_entry(symtab, layout, gsym);
+
   switch (r_type)
     {
     case elfcpp::R_SPARC_NONE:
@@ -2325,6 +2638,27 @@ Target_sparc<size, big_endian>::Scan::global(
 	        target->copy_reloc(symtab, layout, object,
 	                           data_shndx, output_section, gsym, reloc);
               }
+	    else if (((size == 64 && r_type == elfcpp::R_SPARC_64)
+		      || (size == 32 && r_type == elfcpp::R_SPARC_32))
+		     && gsym->type() == elfcpp::STT_GNU_IFUNC
+		     && gsym->can_use_relative_reloc(false)
+		     && !gsym->is_from_dynobj()
+		     && !gsym->is_undefined()
+		     && !gsym->is_preemptible())
+	      {
+		// Use an IRELATIVE reloc for a locally defined
+		// STT_GNU_IFUNC symbol.  This makes a function
+		// address in a PIE executable match the address in a
+		// shared library that it links against.
+		Reloc_section* rela_dyn =
+		  target->rela_ifunc_section(layout);
+		unsigned int r_type = elfcpp::R_SPARC_IRELATIVE;
+		rela_dyn->add_symbolless_global_addend(gsym, r_type,
+						       output_section, object,
+						       data_shndx,
+						       reloc.get_r_offset(),
+						       reloc.get_r_addend());
+	      }
             else if ((r_type == elfcpp::R_SPARC_32
 		      || r_type == elfcpp::R_SPARC_64)
                      && gsym->can_use_relative_reloc(false))
@@ -2333,7 +2667,7 @@ Target_sparc<size, big_endian>::Scan::global(
                 rela_dyn->add_global_relative(gsym, elfcpp::R_SPARC_RELATIVE,
 					      output_section, object,
 					      data_shndx, reloc.get_r_offset(),
-					      reloc.get_r_addend(), false);
+					      reloc.get_r_addend(), is_ifunc);
               }
             else
               {
@@ -2370,24 +2704,68 @@ Target_sparc<size, big_endian>::Scan::global(
 
 	got = target->got_section(symtab, layout);
         if (gsym->final_value_is_known())
-          got->add_global(gsym, GOT_TYPE_STANDARD);
+	  {
+	    // For a STT_GNU_IFUNC symbol we want the PLT address.
+	    if (gsym->type() == elfcpp::STT_GNU_IFUNC)
+	      got->add_global_plt(gsym, GOT_TYPE_STANDARD);
+	    else
+	      got->add_global(gsym, GOT_TYPE_STANDARD);
+	  }
         else
           {
             // If this symbol is not fully resolved, we need to add a
-            // dynamic relocation for it.
+            // GOT entry with a dynamic relocation.
+	    bool is_ifunc = gsym->type() == elfcpp::STT_GNU_IFUNC;
+
+	    // Use a GLOB_DAT rather than a RELATIVE reloc if:
+	    //
+	    // 1) The symbol may be defined in some other module.
+	    //
+	    // 2) We are building a shared library and this is a
+	    // protected symbol; using GLOB_DAT means that the dynamic
+	    // linker can use the address of the PLT in the main
+	    // executable when appropriate so that function address
+	    // comparisons work.
+	    //
+	    // 3) This is a STT_GNU_IFUNC symbol in position dependent
+	    // code, again so that function address comparisons work.
             Reloc_section* rela_dyn = target->rela_dyn_section(layout);
             if (gsym->is_from_dynobj()
                 || gsym->is_undefined()
-                || gsym->is_preemptible())
-              got->add_global_with_rel(gsym, GOT_TYPE_STANDARD, rela_dyn,
-				       elfcpp::R_SPARC_GLOB_DAT);
+                || gsym->is_preemptible()
+		|| (gsym->visibility() == elfcpp::STV_PROTECTED
+		    && parameters->options().shared())
+		|| (gsym->type() == elfcpp::STT_GNU_IFUNC
+		    && parameters->options().output_is_position_independent()
+		    && !gsym->is_forced_local()))
+	      {
+		unsigned int r_type = elfcpp::R_SPARC_GLOB_DAT;
+
+		// If this symbol is forced local, this relocation will
+		// not work properly.  That's because ld.so on sparc
+		// (and 32-bit powerpc) expects st_value in the r_addend
+		// of relocations for STB_LOCAL symbols.  Curiously the
+		// BFD linker does not promote global hidden symbols to be
+		// STB_LOCAL in the dynamic symbol table like Gold does.
+		gold_assert(!gsym->is_forced_local());
+		got->add_global_with_rel(gsym, GOT_TYPE_STANDARD, rela_dyn,
+					 r_type);
+	      }
             else if (!gsym->has_got_offset(GOT_TYPE_STANDARD))
               {
 		unsigned int off = got->add_constant(0);
 
 		gsym->set_got_offset(GOT_TYPE_STANDARD, off);
+		if (is_ifunc)
+		  {
+		    // Tell the dynamic linker to use the PLT address
+		    // when resolving relocations.
+		    if (gsym->is_from_dynobj()
+			&& !parameters->options().shared())
+		      gsym->set_needs_dynsym_value();
+		  }
 		rela_dyn->add_global_relative(gsym, elfcpp::R_SPARC_RELATIVE,
-					      got, off, 0, false);
+					      got, off, 0, is_ifunc);
 	      }
           }
       }
@@ -2520,7 +2898,9 @@ Target_sparc<size, big_endian>::Scan::global(
     case elfcpp::R_SPARC_COPY:
     case elfcpp::R_SPARC_GLOB_DAT:
     case elfcpp::R_SPARC_JMP_SLOT:
+    case elfcpp::R_SPARC_JMP_IREL:
     case elfcpp::R_SPARC_RELATIVE:
+    case elfcpp::R_SPARC_IRELATIVE:
     case elfcpp::R_SPARC_TLS_DTPMOD64:
     case elfcpp::R_SPARC_TLS_DTPMOD32:
     case elfcpp::R_SPARC_TLS_DTPOFF64:
@@ -2620,8 +3000,11 @@ void
 Target_sparc<size, big_endian>::do_finalize_sections(
     Layout* layout,
     const Input_objects*,
-    Symbol_table*)
+    Symbol_table* symtab)
 {
+  if (this->plt_)
+    this->plt_->emit_pending_ifunc_relocs();
+
   // Fill in some more dynamic tags.
   const Reloc_section* rel_plt = (this->plt_ == NULL
 				  ? NULL
@@ -2633,6 +3016,47 @@ Target_sparc<size, big_endian>::do_finalize_sections(
   // relocs.
   if (this->copy_relocs_.any_saved_relocs())
     this->copy_relocs_.emit(this->rela_dyn_section(layout));
+
+  if (parameters->doing_static_link()
+      && (this->plt_ == NULL || !this->plt_->has_ifunc_section()))
+    {
+      // If linking statically, make sure that the __rela_iplt symbols
+      // were defined if necessary, even if we didn't create a PLT.
+      static const Define_symbol_in_segment syms[] =
+	{
+	  {
+	    "__rela_iplt_start",	// name
+	    elfcpp::PT_LOAD,		// segment_type
+	    elfcpp::PF_W,		// segment_flags_set
+	    elfcpp::PF(0),		// segment_flags_clear
+	    0,				// value
+	    0,				// size
+	    elfcpp::STT_NOTYPE,		// type
+	    elfcpp::STB_GLOBAL,		// binding
+	    elfcpp::STV_HIDDEN,		// visibility
+	    0,				// nonvis
+	    Symbol::SEGMENT_START,	// offset_from_base
+	    true			// only_if_ref
+	  },
+	  {
+	    "__rela_iplt_end",		// name
+	    elfcpp::PT_LOAD,		// segment_type
+	    elfcpp::PF_W,		// segment_flags_set
+	    elfcpp::PF(0),		// segment_flags_clear
+	    0,				// value
+	    0,				// size
+	    elfcpp::STT_NOTYPE,		// type
+	    elfcpp::STB_GLOBAL,		// binding
+	    elfcpp::STV_HIDDEN,		// visibility
+	    0,				// nonvis
+	    Symbol::SEGMENT_START,	// offset_from_base
+	    true			// only_if_ref
+	  }
+	};
+
+      symtab->define_symbols(layout, 2, syms,
+			     layout->script_options()->saw_sections_clause());
+    }
 }
 
 // Perform a relocation.
@@ -2669,6 +3093,7 @@ Target_sparc<size, big_endian>::Relocate::relocate(
     view -= 4;
 
   typedef Sparc_relocate_functions<size, big_endian> Reloc;
+  const Sized_relobj_file<size, big_endian>* object = relinfo->object;
 
   // Pick the value to use for symbols defined in shared objects.
   Symbol_value<size> symval;
@@ -2677,14 +3102,23 @@ Target_sparc<size, big_endian>::Relocate::relocate(
     {
       elfcpp::Elf_Xword value;
 
-      value = target->plt_section()->address() + gsym->plt_offset();
+      value = target->plt_address_for_global(gsym) + gsym->plt_offset();
 
       symval.set_output_value(value);
 
       psymval = &symval;
     }
+  else if (gsym == NULL && psymval->is_ifunc_symbol())
+    {
+      unsigned int r_sym = elfcpp::elf_r_sym<size>(rela.get_r_info());
+      if (object->local_has_plt_offset(r_sym))
+	{
+	  symval.set_output_value(target->plt_address_for_local(object, r_sym)
+				  + object->local_plt_offset(r_sym));
+	  psymval = &symval;
+	}
+    }
 
-  const Sized_relobj_file<size, big_endian>* object = relinfo->object;
   const elfcpp::Elf_Xword addend = rela.get_r_addend();
 
   // Get the GOT offset if needed.  Unlike i386 and x86_64, our GOT
@@ -2995,7 +3429,9 @@ Target_sparc<size, big_endian>::Relocate::relocate(
     case elfcpp::R_SPARC_COPY:
     case elfcpp::R_SPARC_GLOB_DAT:
     case elfcpp::R_SPARC_JMP_SLOT:
+    case elfcpp::R_SPARC_JMP_IREL:
     case elfcpp::R_SPARC_RELATIVE:
+    case elfcpp::R_SPARC_IRELATIVE:
       // These are outstanding tls relocs, which are unexpected when
       // linking.
     case elfcpp::R_SPARC_TLS_DTPMOD64:
-- 
1.7.9.5

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] Add sparc IFUNC support to Gold.
  2012-04-11 19:15 [PATCH 2/2] Add sparc IFUNC support to Gold David Miller
  2012-04-11 22:26 ` David Miller
@ 2012-04-17  0:02 ` Ian Lance Taylor
  2012-04-17  8:29   ` David Miller
  1 sibling, 1 reply; 8+ messages in thread
From: Ian Lance Taylor @ 2012-04-17  0:02 UTC (permalink / raw)
  To: David Miller; +Cc: binutils

David Miller <davem@davemloft.net> writes:

> Another testcase that still fails is memory_test.sh because that test
> assumes a minimum alignment of 4K, or something like that.  Whereas
> sparc's minimum alignment is 8K.  Any suggestions on how to fix that
> are welcome.

I assume the problem is the final 0x1000 in lines like

  "  LOAD           0x001000 0x0*02000 0x0*02000 0x0*04 0x0*04 R   0x1000"

That is not an important part of the test and it could simply be
removed.

Ian

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] Add sparc IFUNC support to Gold.
  2012-04-11 22:26 ` David Miller
@ 2012-04-17  0:15   ` Ian Lance Taylor
  0 siblings, 0 replies; 8+ messages in thread
From: Ian Lance Taylor @ 2012-04-17  0:15 UTC (permalink / raw)
  To: David Miller; +Cc: binutils

David Miller <davem@davemloft.net> writes:

> elfcpp/
>
> 	* sparc.h (R_SPARC_JMP_IREL): New relocation.
>
> gold/
>
> 	* sparc.cc (class Target_sparc): Add rela_ifunc_.
> 	(Target_sparc::Target_sparc): Initialize new field.
> 	(Target_sparc::do_plt_section_for_global): New function.
> 	(Target_sparc::do_plt_section_for_local): New function.
> 	(Target_sparc::reloc_needs_plt_for_ifunc): New function.
> 	(Target_sparc::make_plt_section): New function, broken out of
> 	make_plt_entry.  Use ORDER_NON_RELRO_FIRST for ".plt".
> 	(Target_sparc::make_plt_entry): Call make_plt_section.
> 	(Target_sparc::make_local_ifunc_plt_entry): New function.
> 	(Target_sparc::rela_ifunc_section): New function.
> 	(Target_sparc::plt_section): Remove const.
> 	(Output_data_plt_sparc): Update declarations.  Define Global_ifunc
> 	and Local_ifunc types.  Add global_ifuncs_, local_ifuncs_, ifunc_rel_,
> 	and ifunc_count_ fields.
> 	(Output_data_plt_sparc::Output_data_plt_sparc): Initialize new fields.
> 	(Output_data_plt_sparc::add_entry): Handle IFUNC symbols.
> 	(Output_data_plt_sparc::add_local_ifunc_entry): New function.
> 	(Output_data_plt_sparc::rela_ifunc): New function.
> 	(Output_data_plt_sparc::emit_pending_ifunc_relocs): New function.
> 	(Output_data_plt_sparc::has_ifunc_section): New function.
> 	(Output_data_plt_sparc::entry_count): Include ifunc_count_.
> 	(Output_data_plt_sparc::address_for_global): New function.
> 	(Output_data_plt_sparc::address_for_local): New function.
> 	(Output_data_plt_sparc::plt_index_to_offset): New function.
> 	(Output_data_plt_sparc::set_final_data_size): Use plt_index_to_offset
> 	and entry_count.
> 	(Output_data_plt_sparc::do_write): Use first_plt_entry_offset and
> 	entry_count.
> 	(Target_sparc::Scan::get_reference_flags): Add R_SPARC_IRELATIVE and
> 	R_SPARC_JMP_IREL to switch.
> 	(Target_sparc::Scan::check_non_pic): Likewise.
> 	(Target_sparc::Scan::local): Handle IFUNC symbols.
> 	(Target_sparc::Scan::local): Likewise.
> 	(Target_sparc::Relocate::relocate): Likewise, use plt_address_for_global
> 	and plt_address_for_local.
> 	(Target_sparc::do_finalize_sections): Call emit_pending_ifunc_relocs.
> 	Define __rel_iplt_start and __rel_iplt_end if doing a static link.

This is OK.

Thanks.

Ian

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] Add sparc IFUNC support to Gold.
  2012-04-17  0:02 ` Ian Lance Taylor
@ 2012-04-17  8:29   ` David Miller
  2012-04-17 19:53     ` Cary Coutant
  0 siblings, 1 reply; 8+ messages in thread
From: David Miller @ 2012-04-17  8:29 UTC (permalink / raw)
  To: iant; +Cc: binutils

From: Ian Lance Taylor <iant@google.com>
Date: Mon, 16 Apr 2012 17:00:44 -0700

> David Miller <davem@davemloft.net> writes:
> 
>> Another testcase that still fails is memory_test.sh because that test
>> assumes a minimum alignment of 4K, or something like that.  Whereas
>> sparc's minimum alignment is 8K.  Any suggestions on how to fix that
>> are welcome.
> 
> I assume the problem is the final 0x1000 in lines like
> 
>   "  LOAD           0x001000 0x0*02000 0x0*02000 0x0*04 0x0*04 R   0x1000"
> 
> That is not an important part of the test and it could simply be
> removed.

No, there are more differences than just that last value.  Here
is the stdout file from the test:

There are 10 section headers, starting at offset 0x60a8:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .sec0             PROGBITS        00002000 002000 000004 00   A  0   0  1
  [ 2] .sec1             PROGBITS        00001000 002004 000004 00   A  0   0  1
  [ 3] .sec4             PROGBITS        00002008 002008 000004 00   A  0   0  1
  [ 4] .sec5             PROGBITS        0000200c 00200c 000004 00   A  0   0  0
  [ 5] .sec3             PROGBITS        00005000 005000 000004 00   A  0   0  1
  [ 6] .sec2             PROGBITS        00004000 00603c 000004 00   A  0   0  1
  [ 7] .symtab           SYMTAB          00000000 006040 000020 10      8   1  4
  [ 8] .strtab           STRTAB          00000000 006060 000006 00      0   0  1
  [ 9] .shstrtab         STRTAB          00000000 006066 00003f 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

Elf file type is EXEC (Executable file)
Entry point 0x0
There are 4 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x00000000 0x00000000 0x02004 0x02004 R   0x2000
  LOAD           0x002004 0x00001000 0x00002004 0x00004 0x00004 R   0x2000
  LOAD           0x002008 0x00002008 0x00002008 0x02ffc 0x02ffc R   0x2000
  LOAD           0x00603c 0x00004000 0x0000603c 0x00004 0x00004 R   0x2000

 Section to Segment mapping:
  Segment Sections...
   00     .sec0 
   01     .sec1 
   02     .sec4 .sec5 .sec3 
   03     .sec2 

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] Add sparc IFUNC support to Gold.
  2012-04-17  8:29   ` David Miller
@ 2012-04-17 19:53     ` Cary Coutant
  2012-04-18  6:34       ` David Miller
  2012-04-20  6:51       ` David Miller
  0 siblings, 2 replies; 8+ messages in thread
From: Cary Coutant @ 2012-04-17 19:53 UTC (permalink / raw)
  To: David Miller; +Cc: iant, binutils

> No, there are more differences than just that last value.  Here
> is the stdout file from the test:

You could try doubling the origin values in the MEMORY section of
memory_test.t, in order to prevent the headers from merging with .sec0
in the first load segment, and .sec3 from being placed in the third
load segment with .sec4 and .sec5. Then you may need to modify the
patterns to ignore the values in the Offset column. For reference, I
modified memory_test.t as follows:

3,6c3,6
<   region1        : ORIGIN = 0x1000, LENGTH = 0x1000 ,
<   region2 (r)    :    org = 0x2000, len    =    300
<   region3 (wx)   :      o = 0x4000, l      =      4
<   region4 (!r)   : o = 0x6000 + 60, len    = 0x30 * 0x6
---
>   region1        : ORIGIN = 0x2000, LENGTH = 0x1000 ,
>   region2 (r)    :    org = 0x4000, len    =    300
>   region3 (wx)   :      o = 0x8000, l      =      4
>   region4 (!r)   : o = 0xc000 + 60, len    = 0x30 * 0x6
19c19
<   .sec3 0x5000 : { *(*.sec3) }
---
>   .sec3 0xa000 : { *(*.sec3) }

And I get the following load segments on x86_64:

Program Headers:
  Type           Offset   VirtAddr           PhysAddr
FileSiz  MemSiz   Flg Align
  LOAD           0x000000 0x0000000000003000 0x0000000000003000
0x000190 0x000190 R   0x1000
  LOAD           0x001000 0x0000000000004000 0x0000000000004000
0x000004 0x000004 R   0x1000
  LOAD           0x001004 0x0000000000002000 0x0000000000004004
0x000004 0x000004 R   0x1000
  LOAD           0x001008 0x0000000000004008 0x0000000000004008
0x000008 0x000008 R   0x1000
  LOAD           0x002000 0x000000000000a000 0x000000000000a000
0x000004 0x000004 R   0x1000
  LOAD           0x00203c 0x0000000000008000 0x000000000000c03c
0x000004 0x000004 R   0x1000

On Sparc, your offset values may differ, but with luck you'll get the
same section-to-segment mapping, and the PhysAddr values will be the
same. If that's the case, it should be possible to modify the patterns
in memory_test.sh to pass on all platforms.

I also get this when linking with the modified memory_test.t:

   ld: warning: creating a segment to contain the file and program
headers outside of any MEMORY region

But I'm not really sure why I don't see that warning with the original
memory_test.t. I think it's OK with that warning, though.

-cary

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] Add sparc IFUNC support to Gold.
  2012-04-17 19:53     ` Cary Coutant
@ 2012-04-18  6:34       ` David Miller
  2012-04-20  6:51       ` David Miller
  1 sibling, 0 replies; 8+ messages in thread
From: David Miller @ 2012-04-18  6:34 UTC (permalink / raw)
  To: ccoutant; +Cc: iant, binutils

From: Cary Coutant <ccoutant@google.com>
Date: Tue, 17 Apr 2012 11:42:40 -0700

>> No, there are more differences than just that last value.  Here
>> is the stdout file from the test:
> 
> You could try doubling the origin values in the MEMORY section of
> memory_test.t, in order to prevent the headers from merging with .sec0
> in the first load segment, and .sec3 from being placed in the third
> load segment with .sec4 and .sec5. Then you may need to modify the
> patterns to ignore the values in the Offset column. For reference, I
> modified memory_test.t as follows:

Thanks for looking into this Cary, I'll try to move this forward
soon.

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] Add sparc IFUNC support to Gold.
  2012-04-17 19:53     ` Cary Coutant
  2012-04-18  6:34       ` David Miller
@ 2012-04-20  6:51       ` David Miller
  1 sibling, 0 replies; 8+ messages in thread
From: David Miller @ 2012-04-20  6:51 UTC (permalink / raw)
  To: ccoutant; +Cc: iant, binutils

From: Cary Coutant <ccoutant@google.com>
Date: Tue, 17 Apr 2012 11:42:40 -0700

> You could try doubling the origin values in the MEMORY section of
> memory_test.t, in order to prevent the headers from merging with .sec0
> in the first load segment, and .sec3 from being placed in the third
> load segment with .sec4 and .sec5. Then you may need to modify the
> patterns to ignore the values in the Offset column. For reference, I
> modified memory_test.t as follows:
> 
> 3,6c3,6
> <   region1        : ORIGIN = 0x1000, LENGTH = 0x1000 ,
> <   region2 (r)    :    org = 0x2000, len    =    300
> <   region3 (wx)   :      o = 0x4000, l      =      4
> <   region4 (!r)   : o = 0x6000 + 60, len    = 0x30 * 0x6
> ---
>>   region1        : ORIGIN = 0x2000, LENGTH = 0x1000 ,
>>   region2 (r)    :    org = 0x4000, len    =    300
>>   region3 (wx)   :      o = 0x8000, l      =      4
>>   region4 (!r)   : o = 0xc000 + 60, len    = 0x30 * 0x6
> 19c19
> <   .sec3 0x5000 : { *(*.sec3) }
> ---
>>   .sec3 0xa000 : { *(*.sec3) }
> 
> And I get the following load segments on x86_64:

Unlike you I always get only 4 LOAD segments on Sparc.  I think
the linker is combining adjacent segments for whatever reason.

I always also get a LOAD segment starting at virtual address zero no
matter what gets used for the ORIGIN value in the scripts.  For
example, with the change you suggested I get:

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x00000000 0x00000000 0x04004 0x04004 R   0x2000
  LOAD           0x004004 0x00002000 0x00004004 0x00004 0x00004 R   0x2000
  LOAD           0x004008 0x00004008 0x00004008 0x05ffc 0x05ffc R   0x2000
  LOAD           0x00c03c 0x00008000 0x0000c03c 0x00004 0x00004 R   0x2000

 Section to Segment mapping:
  Segment Sections...
   00     .sec0
   01     .sec1
   02     .sec4 .sec5 .sec3
   03     .sec2

I note two things about the BFD linker test this GOLD test is based
upon (rgn-at5), that test checks the section headres, not the program
headers.  The BFD linker test also, unlike the GOLD test, passes in
"-max-pages-size=0x1000".

I wonder about that first LOAD segment you get on x86-64, it's smaller
than a page and in the Section to Segment mapping it doesn't correspond
to any named Section.  Whereas on sparc all of the listed segments
correspond to one of the sections created by the test.

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2012-04-20  3:51 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-04-11 19:15 [PATCH 2/2] Add sparc IFUNC support to Gold David Miller
2012-04-11 22:26 ` David Miller
2012-04-17  0:15   ` Ian Lance Taylor
2012-04-17  0:02 ` Ian Lance Taylor
2012-04-17  8:29   ` David Miller
2012-04-17 19:53     ` Cary Coutant
2012-04-18  6:34       ` David Miller
2012-04-20  6:51       ` David Miller

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).