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


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

gold patch committed: Put IRELATIVE relocs after JUMP_SLOT relocs


PR 12372 points out a problem in gold: if an STT_GNU_IFUNC symbol is
defined with a function that calls other functions through the PLT, then
it is essential that the dynamic linker prepare the PLT entries before
it invokes the function for the STT_GNU_IFUNC symbol.  The GNU linker
does this by simply putting all IRELATIVE relocs after all JUMP_SLOT
relocs.  This patch implements the same approach in gold.  Committed to
mainline.

Ian


2011-07-08  Ian Lance Taylor  <iant@google.com>

	PR gold/12372
	* target.h (Target::plt_address_for_global): New function.
	(Target::plt_address_for_local): New function.
	(Target::plt_section_for_global): Remove.
	(Target::plt_section_for_local): Remove.
	(Target::do_plt_address_for_global): New virtual function.
	(Target::do_plt_address_for_local): New virtual function.
	(Target::do_plt_section_for_global): Remove.
	(Target::do_plt_section_for_local): Remove.
	(Target::register_global_plt_entry): Add Symbol_table and Layout
	parameters.
	* output.cc (Output_data_got::Got_entry::write): Use
	plt_address_for_global and plt_address_for_local.
	* layout.cc (Layout::add_target_dynamic_tags): Use size and
	address of output section.
	* i386.cc (class Output_data_plt_i386): Add irelative_rel_,
	got_irelative_, and irelative_count_ fields.  Update
	declarations.
	(Output_data_plt_i386::has_irelative_section): New function.
	(Output_data_plt_i386::entry_count): Add irelative_count_.
	(Output_data_plt_i386::set_final_data_size): Likewise.
	(class Target_i386): Add got_irelative_ and rel_irelative_
	fields.  Update declarations.
	(Target_i386::Target_i386): Initialize new fields.
	(Target_i386::do_plt_address_for_global): New function replacing
	do_plt_section_for_global.
	(Target_i386::do_plt_address_for_local): New function replacing
	do_plt_section_for_local.
	(Target_i386::got_section): Create got_irelative_.
	(Target_i386::rel_irelative_section): New function.
	(Output_data_plt_i386::Output_data_plt_i386): Initialize new
	fields.  Don't define __rel_iplt_{start,end}.
	(Output_data_plt_i386::add_entry): Add symtab and layout
	parameters.  Change all callers.  Use different PLT and GOT for
	IFUNC symbols.
	(Output_data_plt_i386::add_local_ifunc_entry): Add symtab and
	layout parameters.  Change all callers.  Use different PLT and
	GOT.
	(Output_data_plt_i386::rel_tls_desc): Fix formatting.
	(Output_data_plt_i386::rel_irelative): New function.
	(Output_data_plt_i386::address_for_global): New function.
	(Output_data_plt_i386::address_for_local): New function.
	(Output_data_plt_i386::do_write): Write out IRELATIVE area.  Use
	IRELATIVE GOT when changing IFUNC GOT entries.
	(Target_i386::Scan::global): Use IRELATIVE GOT for IRELATIVE
	reloc.
	(Target_i386::do_finalize_sections): Create the __rel_iplt symbols
	if we didn't create an IRELATIVE GOT.
	(Target_i386::Relocate::relocate): Use plt_address_for_global and
	plt_address_for_local.
	(Target_i386::do_dynsym_value): Use plt_address_for_global.
	* x86_64.cc (class Output_data_plt_x86_64): Add irelative_rel_,
	got_irelative_, and irelative_count_ fields.  Update
	declarations.
	(Output_data_plt_x86_64::Output_data_plt_x86_64) [both versions]:
	Initialize new fields.  Remove symtab parameter.  Change all
	callers.
	(Output_data_plt_x86_64::get_tlsdesc_plt_offset): Add
	irelative_count_.
	(Output_data_plt_x86_64::has_irelative_section): New function.
	(Output_data_plt_x86_64::entry_count): Add irelative_count_.
	(class Target_x86_64): Add got_irelative_ and rel_irelative_
	fields.  Update declarations.
	(Target_x86_64::Target_x86_64): Initialize new fields.
	(Target_x86_64::do_plt_address_for_global): New function replacing
	do_plt_section_for_global.
	(Target_x86_64::do_plt_address_for_local): New function replacing
	do_plt_section_for_local.
	(Target_x86_64::got_section): Create got_irelative_.
	(Target_x86_64::rela_irelative_section): New function.
	(Output_data_plt_x86_64::init): Remove symtab parameter.  Change
	all callers.  Don't create __rel_iplt_{start,end}.
	(Output_data_plt_x86_64::add_entry): Add symtab and layout
	parameters.  Change all callers.  Use different PLT and GOT for
	IFUNC symbols.
	(Output_data_plt_x86_64::add_local_ifunc_entry): Add symtab and
	layout parameters.  Change all callers.  Use different PLT and
	GOT.
	(Output_data_plt_x86_64::add_relocation): Add symtab and layout
	parameters.  Change all callers.  Use different PLT and GOT for
	IFUNC symbols.
	(Output_data_plt_x86_64::rela_tlsdesc): Fix formatting.
	(Output_data_plt_x86_64::rela_irelative): New function.
	(Output_data_plt_x86_64::address_for_global): New function.
	(Output_data_plt_x86_64::address_for_local): New function.
	(Output_data_plt_x86_64::set_final_data_size): Likewise.
	(Output_data_plt_x86_64::do_write): Write out IRELATIVE area.
	(Target_x86_64::init_got_plt_for_update): Create got_irelative_.
	(Target_x86_64::register_global_plt_entry): Add symtab and layout
	parameters.
	(Target_x86_64::Scan::global): Use IRELATIVE GOT for IRELATIVE
	reloc.
	(Target_x86_64::do_finalize_sections): Create the __rela_iplt
	symbols if we didn't create an IRELATIVE GOT.
	(Target_x86_64::Relocate::relocate): Use plt_address_for_global and
	plt_address_for_local.
	(Target_x86_64::do_dynsym_value): Use plt_address_for_global.
	* testsuite/ifuncvar1.c: New test file.
	* testsuite/ifuncvar2.c: New test file.
	* testsuite/ifuncvar3.c: New test file.
	* testsuite/Makefile.am (check_PROGRAMS): Add ifuncvar.
	(ifuncvar1_pic.o, ifuncvar2_pic.o, ifuncvar.so): New targets.
	(ifuncvar_SOURCES, ifuncvar_DEPENDENCIES): New variables.
	(ifuncvar_LDFLAGS, ifuncvar_LDADD): New variables.
	* testsuite/Makefile.in: Rebuild.


Index: i386.cc
===================================================================
RCS file: /cvs/src/src/gold/i386.cc,v
retrieving revision 1.135
diff -u -p -r1.135 i386.cc
--- i386.cc	6 Jul 2011 05:12:03 -0000	1.135
+++ i386.cc	8 Jul 2011 22:33:21 -0000
@@ -53,15 +53,16 @@ class Output_data_plt_i386 : public Outp
  public:
   typedef Output_data_reloc<elfcpp::SHT_REL, true, 32, false> Reloc_section;
 
-  Output_data_plt_i386(Symbol_table*, Layout*, Output_data_space*);
+  Output_data_plt_i386(Layout*, Output_data_space*, Output_data_space*);
 
   // Add an entry to the PLT.
   void
-  add_entry(Symbol* gsym);
+  add_entry(Symbol_table*, Layout*, Symbol* gsym);
 
   // Add an entry to the PLT for a local STT_GNU_IFUNC symbol.
   unsigned int
-  add_local_ifunc_entry(Sized_relobj_file<32, false>* relobj,
+  add_local_ifunc_entry(Symbol_table*, Layout*,
+			Sized_relobj_file<32, false>* relobj,
 			unsigned int local_sym_index);
 
   // Return the .rel.plt section data.
@@ -73,10 +74,19 @@ class Output_data_plt_i386 : public Outp
   Reloc_section*
   rel_tls_desc(Layout*);
 
+  // Return where the IRELATIVE relocations should go.
+  Reloc_section*
+  rel_irelative(Symbol_table*, Layout*);
+
+  // Return whether we created a section for IRELATIVE relocations.
+  bool
+  has_irelative_section() const
+  { return this->irelative_rel_ != NULL; }
+
   // Return the number of PLT entries.
   unsigned int
   entry_count() const
-  { return this->count_; }
+  { return this->count_ + this->irelative_count_; }
 
   // Return the offset of the first non-reserved PLT entry.
   static unsigned int
@@ -88,6 +98,14 @@ class Output_data_plt_i386 : public Outp
   get_plt_entry_size()
   { return 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);
@@ -122,7 +140,10 @@ class Output_data_plt_i386 : public Outp
   // Set the final size.
   void
   set_final_data_size()
-  { this->set_data_size((this->count_ + 1) * plt_entry_size); }
+  {
+    this->set_data_size((this->count_ + this->irelative_count_ + 1)
+			* plt_entry_size);
+  }
 
   // Write out the PLT data.
   void
@@ -150,10 +171,18 @@ class Output_data_plt_i386 : public Outp
   // The TLS_DESC relocations, if necessary.  These must follow the
   // regular PLT relocs.
   Reloc_section* tls_desc_rel_;
+  // The IRELATIVE relocations, if necessary.  These must follow the
+  // regular relocatoins and the TLS_DESC relocations.
+  Reloc_section* irelative_rel_;
   // The .got.plt section.
   Output_data_space* got_plt_;
+  // The part of the .got.plt section used for IRELATIVE relocs.
+  Output_data_space* got_irelative_;
   // The number of PLT entries.
   unsigned int count_;
+  // Number of PLT entries with R_386_IRELATIVE relocs.  These follow
+  // the regular PLT entries.
+  unsigned int irelative_count_;
   // Global STT_GNU_IFUNC symbols.
   std::vector<Global_ifunc> global_ifuncs_;
   // Local STT_GNU_IFUNC symbols.
@@ -172,9 +201,9 @@ class Target_i386 : public Sized_target<
 
   Target_i386()
     : Sized_target<32, false>(&i386_info),
-      got_(NULL), plt_(NULL), got_plt_(NULL), got_tlsdesc_(NULL),
-      global_offset_table_(NULL), rel_dyn_(NULL),
-      copy_relocs_(elfcpp::R_386_COPY), dynbss_(NULL),
+      got_(NULL), plt_(NULL), got_plt_(NULL), got_irelative_(NULL),
+      got_tlsdesc_(NULL), global_offset_table_(NULL), rel_dyn_(NULL),
+      rel_irelative_(NULL), copy_relocs_(elfcpp::R_386_COPY), dynbss_(NULL),
       got_mod_index_offset_(-1U), tls_base_symbol_defined_(false)
   { }
 
@@ -281,14 +310,14 @@ class Target_i386 : public Sized_target<
     return Target::do_is_local_label_name(name);
   }
 
-  // Return the PLT section.
-  Output_data*
-  do_plt_section_for_global(const Symbol*) const
-  { return this->plt_section(); }
-
-  Output_data*
-  do_plt_section_for_local(const Relobj*, unsigned int) const
-  { return this->plt_section(); }
+  // 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); }
 
   // We can tell whether we take the address of a function.
   inline bool
@@ -589,6 +618,10 @@ class Target_i386 : public Sized_target<
   Reloc_section*
   rel_tls_desc_section(Layout*) const;
 
+  // Get the section to use for IRELATIVE relocations.
+  Reloc_section*
+  rel_irelative_section(Layout*);
+
   // Add a potential copy relocation.
   void
   copy_reloc(Symbol_table* symtab, Layout* layout,
@@ -625,12 +658,16 @@ class Target_i386 : public Sized_target<
   Output_data_plt_i386* plt_;
   // The GOT PLT section.
   Output_data_space* got_plt_;
+  // The GOT section for IRELATIVE relocations.
+  Output_data_space* got_irelative_;
   // The GOT section for TLSDESC relocations.
   Output_data_got<32, false>* got_tlsdesc_;
   // The _GLOBAL_OFFSET_TABLE_ symbol.
   Symbol* global_offset_table_;
   // The dynamic reloc section.
   Reloc_section* rel_dyn_;
+  // The section to use for IRELATIVE relocs.
+  Reloc_section* rel_irelative_;
   // Relocs saved to avoid a COPY reloc.
   Copy_relocs<elfcpp::SHT_REL, 32, false> copy_relocs_;
   // Space for variables copied with a COPY reloc.
@@ -703,6 +740,15 @@ Target_i386::got_section(Symbol_table* s
 				      elfcpp::STV_HIDDEN, 0,
 				      false, false);
 
+      // If there are any IRELATIVE relocations, they get GOT entries
+      // in .got.plt after the jump slot relocations.
+      this->got_irelative_ = new Output_data_space(4, "** GOT IRELATIVE PLT");
+      layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS,
+				      (elfcpp::SHF_ALLOC
+				       | elfcpp::SHF_WRITE),
+				      this->got_irelative_,
+				      ORDER_NON_RELRO_FIRST, false);
+
       // If there are any TLSDESC relocations, they get GOT entries in
       // .got.plt after the jump slot entries.
       this->got_tlsdesc_ = new Output_data_got<32, false>();
@@ -732,39 +778,45 @@ Target_i386::rel_dyn_section(Layout* lay
   return this->rel_dyn_;
 }
 
+// Get the section to use for IRELATIVE relocs, creating it if
+// necessary.  These go in .rel.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.
+
+Target_i386::Reloc_section*
+Target_i386::rel_irelative_section(Layout* layout)
+{
+  if (this->rel_irelative_ == NULL)
+    {
+      // Make sure we have already create the dynamic reloc section.
+      this->rel_dyn_section(layout);
+      this->rel_irelative_ = new Reloc_section(false);
+      layout->add_output_section_data(".rel.dyn", elfcpp::SHT_REL,
+				      elfcpp::SHF_ALLOC, this->rel_irelative_,
+				      ORDER_DYNAMIC_RELOCS, false);
+      gold_assert(this->rel_dyn_->output_section()
+		  == this->rel_irelative_->output_section());
+    }
+  return this->rel_irelative_;
+}
+
 // Create the PLT section.  The ordinary .got section is an argument,
 // since we need to refer to the start.  We also create our own .got
 // section just for PLT entries.
 
-Output_data_plt_i386::Output_data_plt_i386(Symbol_table* symtab,
-					   Layout* layout,
-					   Output_data_space* got_plt)
-  : Output_section_data(16), tls_desc_rel_(NULL), got_plt_(got_plt), count_(0),
-    global_ifuncs_(), local_ifuncs_()
+Output_data_plt_i386::Output_data_plt_i386(Layout* layout,
+					   Output_data_space* got_plt,
+					   Output_data_space* got_irelative)
+  : Output_section_data(16), tls_desc_rel_(NULL), irelative_rel_(NULL),
+    got_plt_(got_plt), got_irelative_(got_irelative), count_(0),
+    irelative_count_(0), global_ifuncs_(), local_ifuncs_()
 {
   this->rel_ = new Reloc_section(false);
   layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL,
 				  elfcpp::SHF_ALLOC, this->rel_,
 				  ORDER_DYNAMIC_PLT_RELOCS, false);
 
-  if (parameters->doing_static_link())
-    {
-      // A statically linked executable will only have a .rel.plt
-      // section to hold R_386_IRELATIVE relocs for STT_GNU_IFUNC
-      // symbols.  The library will use these symbols to locate the
-      // IRELATIVE relocs at program startup time.
-      symtab->define_in_output_data("__rel_iplt_start", NULL,
-				    Symbol_table::PREDEFINED,
-				    this->rel_, 0, 0, elfcpp::STT_NOTYPE,
-				    elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN,
-				    0, false, true);
-      symtab->define_in_output_data("__rel_iplt_end", NULL,
-				    Symbol_table::PREDEFINED,
-				    this->rel_, 0, 0, elfcpp::STT_NOTYPE,
-				    elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN,
-				    0, true, true);
-    }
-
   // Add unwind information if requested.
   if (parameters->options().ld_generated_unwind_info())
     layout->add_eh_frame_for_plt(this, plt_eh_frame_cie, plt_eh_frame_cie_size,
@@ -782,29 +834,23 @@ Output_data_plt_i386::do_adjust_output_s
 // Add an entry to the PLT.
 
 void
-Output_data_plt_i386::add_entry(Symbol* gsym)
+Output_data_plt_i386::add_entry(Symbol_table* symtab, Layout* layout,
+				Symbol* gsym)
 {
   gold_assert(!gsym->has_plt_offset());
 
-  // Note that when setting the PLT offset we skip the initial
-  // reserved PLT entry.
-  gsym->set_plt_offset((this->count_ + 1) * plt_entry_size);
-
-  ++this->count_;
-
-  section_offset_type got_offset = this->got_plt_->current_data_size();
-
-  // Every PLT entry needs a GOT entry which points back to the PLT
-  // entry (this will be changed by the dynamic linker, normally
-  // lazily when the function is called).
-  this->got_plt_->set_current_data_size(got_offset + 4);
-
   // Every PLT entry needs a reloc.
   if (gsym->type() == elfcpp::STT_GNU_IFUNC
       && gsym->can_use_relative_reloc(false))
     {
-      this->rel_->add_symbolless_global_addend(gsym, elfcpp::R_386_IRELATIVE,
-					       this->got_plt_, got_offset);
+      gsym->set_plt_offset(this->irelative_count_ * plt_entry_size);
+      ++this->irelative_count_;
+      section_offset_type got_offset =
+	this->got_irelative_->current_data_size();
+      this->got_irelative_->set_current_data_size(got_offset + 4);
+      Reloc_section* rel = this->rel_irelative(symtab, layout);
+      rel->add_symbolless_global_addend(gsym, elfcpp::R_386_IRELATIVE,
+					this->got_irelative_, got_offset);
       struct Global_ifunc gi;
       gi.sym = gsym;
       gi.got_offset = got_offset;
@@ -812,6 +858,19 @@ Output_data_plt_i386::add_entry(Symbol* 
     }
   else
     {
+      // When setting the PLT offset we skip the initial reserved PLT
+      // entry.
+      gsym->set_plt_offset((this->count_ + 1) * plt_entry_size);
+
+      ++this->count_;
+
+      section_offset_type got_offset = this->got_plt_->current_data_size();
+
+      // Every PLT entry needs a GOT entry which points back to the
+      // PLT entry (this will be changed by the dynamic linker,
+      // normally lazily when the function is called).
+      this->got_plt_->set_current_data_size(got_offset + 4);
+
       gsym->set_needs_dynsym_entry();
       this->rel_->add_global(gsym, elfcpp::R_386_JUMP_SLOT, this->got_plt_,
 			     got_offset);
@@ -827,22 +886,25 @@ Output_data_plt_i386::add_entry(Symbol* 
 
 unsigned int
 Output_data_plt_i386::add_local_ifunc_entry(
+    Symbol_table* symtab,
+    Layout* layout,
     Sized_relobj_file<32, false>* relobj,
     unsigned int local_sym_index)
 {
-  unsigned int plt_offset = (this->count_ + 1) * plt_entry_size;
-  ++this->count_;
+  unsigned int plt_offset = this->irelative_count_ * plt_entry_size;
+  ++this->irelative_count_;
 
-  section_offset_type got_offset = this->got_plt_->current_data_size();
+  section_offset_type got_offset = this->got_irelative_->current_data_size();
 
   // Every PLT entry needs a GOT entry which points back to the PLT
   // entry.
-  this->got_plt_->set_current_data_size(got_offset + 4);
+  this->got_irelative_->set_current_data_size(got_offset + 4);
 
   // Every PLT entry needs a reloc.
-  this->rel_->add_symbolless_local_addend(relobj, local_sym_index,
-					  elfcpp::R_386_IRELATIVE,
-					  this->got_plt_, got_offset);
+  Reloc_section* rel = this->rel_irelative(symtab, layout);
+  rel->add_symbolless_local_addend(relobj, local_sym_index,
+				   elfcpp::R_386_IRELATIVE,
+				   this->got_irelative_, got_offset);
 
   struct Local_ifunc li;
   li.object = relobj;
@@ -865,12 +927,72 @@ Output_data_plt_i386::rel_tls_desc(Layou
       layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL,
 				      elfcpp::SHF_ALLOC, this->tls_desc_rel_,
 				      ORDER_DYNAMIC_PLT_RELOCS, false);
-      gold_assert(this->tls_desc_rel_->output_section() ==
-		  this->rel_->output_section());
+      gold_assert(this->tls_desc_rel_->output_section()
+		  == this->rel_->output_section());
     }
   return this->tls_desc_rel_;
 }
 
+// Return where the IRELATIVE relocations should go in the PLT.  These
+// follow the JUMP_SLOT and TLS_DESC relocations.
+
+Output_data_plt_i386::Reloc_section*
+Output_data_plt_i386::rel_irelative(Symbol_table* symtab, Layout* layout)
+{
+  if (this->irelative_rel_ == NULL)
+    {
+      // Make sure we have a place for the TLS_DESC relocations, in
+      // case we see any later on.
+      this->rel_tls_desc(layout);
+      this->irelative_rel_ = new Reloc_section(false);
+      layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL,
+				      elfcpp::SHF_ALLOC, this->irelative_rel_,
+				      ORDER_DYNAMIC_PLT_RELOCS, false);
+      gold_assert(this->irelative_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_386_IRELATIVE relocs for STT_GNU_IFUNC
+	  // symbols.  The library will use these symbols to locate
+	  // the IRELATIVE relocs at program startup time.
+	  symtab->define_in_output_data("__rel_iplt_start", NULL,
+					Symbol_table::PREDEFINED,
+					this->irelative_rel_, 0, 0,
+					elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
+					elfcpp::STV_HIDDEN, 0, false, true);
+	  symtab->define_in_output_data("__rel_iplt_end", NULL,
+					Symbol_table::PREDEFINED,
+					this->irelative_rel_, 0, 0,
+					elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
+					elfcpp::STV_HIDDEN, 0, true, true);
+	}
+    }
+  return this->irelative_rel_;
+}
+
+// Return the PLT address to use for a global symbol.
+
+uint64_t
+Output_data_plt_i386::address_for_global(const Symbol* gsym)
+{
+  uint64_t offset = 0;
+  if (gsym->type() == elfcpp::STT_GNU_IFUNC
+      && gsym->can_use_relative_reloc(false))
+    offset = (this->count_ + 1) * plt_entry_size;
+  return this->address() + offset;
+}
+
+// Return the PLT address to use for a local symbol.  These are always
+// IRELATIVE relocs.
+
+uint64_t
+Output_data_plt_i386::address_for_local(const Relobj*, unsigned int)
+{
+  return this->address() + (this->count_ + 1) * plt_entry_size;
+}
+
 // The first entry in the PLT for an executable.
 
 const unsigned char Output_data_plt_i386::exec_first_plt_entry[plt_entry_size] =
@@ -976,8 +1098,12 @@ Output_data_plt_i386::do_write(Output_fi
   unsigned char* const oview = of->get_output_view(offset, oview_size);
 
   const off_t got_file_offset = this->got_plt_->offset();
+  gold_assert(parameters->incremental_update()
+	      || (got_file_offset + this->got_plt_->data_size()
+		  == this->got_irelative_->offset()));
   const section_size_type got_size =
-    convert_to_section_size_type(this->got_plt_->data_size());
+    convert_to_section_size_type(this->got_plt_->data_size()
+				 + this->got_irelative_->data_size());
   unsigned char* const got_view = of->get_output_view(got_file_offset,
 						      got_size);
 
@@ -1006,7 +1132,7 @@ Output_data_plt_i386::do_write(Output_fi
   unsigned int plt_offset = plt_entry_size;
   unsigned int plt_rel_offset = 0;
   unsigned int got_offset = 12;
-  const unsigned int count = this->count_;
+  const unsigned int count = this->count_ + this->irelative_count_;
   for (unsigned int i = 0;
        i < count;
        ++i,
@@ -1043,6 +1169,7 @@ Output_data_plt_i386::do_write(Output_fi
   // the GOT to point to the actual symbol value, rather than point to
   // the PLT entry.  That will let the dynamic linker call the right
   // function when resolving IRELATIVE relocations.
+  unsigned char* got_irelative_view = got_view + this->got_plt_->data_size();
   for (std::vector<Global_ifunc>::const_iterator p =
 	 this->global_ifuncs_.begin();
        p != this->global_ifuncs_.end();
@@ -1050,7 +1177,7 @@ Output_data_plt_i386::do_write(Output_fi
     {
       const Sized_symbol<32>* ssym =
 	static_cast<const Sized_symbol<32>*>(p->sym);
-      elfcpp::Swap<32, false>::writeval(got_view + p->got_offset,
+      elfcpp::Swap<32, false>::writeval(got_irelative_view + p->got_offset,
 					ssym->value());
     }
 
@@ -1061,7 +1188,7 @@ Output_data_plt_i386::do_write(Output_fi
     {
       const Symbol_value<32>* psymval =
 	p->object->local_symbol(p->local_sym_index);
-      elfcpp::Swap<32, false>::writeval(got_view + p->got_offset,
+      elfcpp::Swap<32, false>::writeval(got_irelative_view + p->got_offset,
 					psymval->value(p->object, 0));
     }
 
@@ -1082,7 +1209,8 @@ Target_i386::make_plt_section(Symbol_tab
       // Create the GOT sections first.
       this->got_section(symtab, layout);
 
-      this->plt_ = new Output_data_plt_i386(symtab, layout, this->got_plt_);
+      this->plt_ = new Output_data_plt_i386(layout, this->got_plt_,
+					    this->got_irelative_);
       layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
 				      (elfcpp::SHF_ALLOC
 				       | elfcpp::SHF_EXECINSTR),
@@ -1103,7 +1231,7 @@ Target_i386::make_plt_entry(Symbol_table
     return;
   if (this->plt_ == NULL)
     this->make_plt_section(symtab, layout);
-  this->plt_->add_entry(gsym);
+  this->plt_->add_entry(symtab, layout, gsym);
 }
 
 // Make a PLT entry for a local STT_GNU_IFUNC symbol.
@@ -1117,7 +1245,8 @@ Target_i386::make_local_ifunc_plt_entry(
     return;
   if (this->plt_ == NULL)
     this->make_plt_section(symtab, layout);
-  unsigned int plt_offset = this->plt_->add_local_ifunc_entry(relobj,
+  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);
 }
@@ -1779,7 +1908,7 @@ Target_i386::Scan::global(Symbol_table* 
 		// 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* rel_dyn = target->rel_dyn_section(layout);
+		Reloc_section* rel_dyn = target->rel_irelative_section(layout);
 		rel_dyn->add_symbolless_global_addend(gsym,
 						      elfcpp::R_386_IRELATIVE,
 						      output_section,
@@ -2182,7 +2311,8 @@ Target_i386::do_finalize_sections(
       symtab->get_sized_symbol<32>(sym)->set_symsize(data_size);
     }
 
-  if (parameters->doing_static_link() && this->plt_ == NULL)
+  if (parameters->doing_static_link()
+      && (this->plt_ == NULL || !this->plt_->has_irelative_section()))
     {
       // If linking statically, make sure that the __rel_iplt symbols
       // were defined if necessary, even if we didn't create a PLT.
@@ -2312,7 +2442,7 @@ Target_i386::Relocate::relocate(const Re
   else if (gsym != NULL
 	   && gsym->use_plt_offset(Scan::get_reference_flags(r_type)))
     {
-      symval.set_output_value(target->plt_section()->address()
+      symval.set_output_value(target->plt_address_for_global(gsym)
 			      + gsym->plt_offset());
       psymval = &symval;
     }
@@ -2321,7 +2451,7 @@ Target_i386::Relocate::relocate(const Re
       unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info());
       if (object->local_has_plt_offset(r_sym))
 	{
-	  symval.set_output_value(target->plt_section()->address()
+	  symval.set_output_value(target->plt_address_for_local(object, r_sym)
 				  + object->local_plt_offset(r_sym));
 	  psymval = &symval;
 	}
@@ -3245,7 +3375,7 @@ uint64_t
 Target_i386::do_dynsym_value(const Symbol* gsym) const
 {
   gold_assert(gsym->is_from_dynobj() && gsym->has_plt_offset());
-  return this->plt_section()->address() + gsym->plt_offset();
+  return this->plt_address_for_global(gsym) + gsym->plt_offset();
 }
 
 // Return a string used to fill a code section with nops to take up
Index: incremental.cc
===================================================================
RCS file: /cvs/src/src/gold/incremental.cc,v
retrieving revision 1.43
diff -u -p -r1.43 incremental.cc
--- incremental.cc	6 Jul 2011 22:26:37 -0000	1.43
+++ incremental.cc	8 Jul 2011 22:33:21 -0000
@@ -1,6 +1,6 @@
 // inremental.cc -- incremental linking support for gold
 
-// Copyright 2009, 2010 Free Software Foundation, Inc.
+// Copyright 2009, 2010, 2011 Free Software Foundation, Inc.
 // Written by Mikolaj Zalewski <mikolajz@google.com>.
 
 // This file is part of gold.
@@ -674,7 +674,7 @@ Sized_incremental_binary<size, big_endia
 	  gold_debug(DEBUG_INCREMENTAL,
 		     "PLT entry %d: %s",
 		     i, sym->name());
-	  target->register_global_plt_entry(i, sym);
+	  target->register_global_plt_entry(symtab, layout, i, sym);
 	}
     }
 }
Index: layout.cc
===================================================================
RCS file: /cvs/src/src/gold/layout.cc,v
retrieving revision 1.212
diff -u -p -r1.212 layout.cc
--- layout.cc	6 Jul 2011 21:19:31 -0000	1.212
+++ layout.cc	8 Jul 2011 22:33:21 -0000
@@ -4027,7 +4027,8 @@ Layout::create_interp(const Target* targ
 // some targets have multiple reloc sections in PLT_REL.
 
 // If DYN_REL is not NULL, it is used for DT_REL/DT_RELA,
-// DT_RELSZ/DT_RELASZ, DT_RELENT/DT_RELAENT.
+// DT_RELSZ/DT_RELASZ, DT_RELENT/DT_RELAENT.  Again we use the output
+// section.
 
 // If ADD_DEBUG is true, we add a DT_DEBUG entry when generating an
 // executable.
@@ -4056,13 +4057,16 @@ Layout::add_target_dynamic_tags(bool use
   if (dyn_rel != NULL && dyn_rel->output_section() != NULL)
     {
       odyn->add_section_address(use_rel ? elfcpp::DT_REL : elfcpp::DT_RELA,
-				dyn_rel);
-      if (plt_rel != NULL && dynrel_includes_plt)
+				dyn_rel->output_section());
+      if (plt_rel != NULL
+	  && plt_rel->output_section() != NULL
+	  && dynrel_includes_plt)
 	odyn->add_section_size(use_rel ? elfcpp::DT_RELSZ : elfcpp::DT_RELASZ,
-			       dyn_rel, plt_rel);
+			       dyn_rel->output_section(),
+			       plt_rel->output_section());
       else
 	odyn->add_section_size(use_rel ? elfcpp::DT_RELSZ : elfcpp::DT_RELASZ,
-			       dyn_rel);
+			       dyn_rel->output_section());
       const int size = parameters->target().get_size();
       elfcpp::DT rel_tag;
       int rel_size;
Index: output.cc
===================================================================
RCS file: /cvs/src/src/gold/output.cc,v
retrieving revision 1.154
diff -u -p -r1.154 output.cc
--- output.cc	6 Jul 2011 21:19:32 -0000	1.154
+++ output.cc	8 Jul 2011 22:33:21 -0000
@@ -1347,7 +1347,7 @@ Output_data_got<size, big_endian>::Got_e
 	// RELATIVE relocation.
 	Symbol* gsym = this->u_.gsym;
 	if (this->use_plt_offset_ && gsym->has_plt_offset())
-	  val = (parameters->target().plt_section_for_global(gsym)->address()
+	  val = (parameters->target().plt_address_for_global(gsym)
 		 + gsym->plt_offset());
 	else
 	  {
@@ -1381,9 +1381,9 @@ Output_data_got<size, big_endian>::Got_e
 	  val = symval->value(this->u_.object, 0);
 	else
 	  {
-	    const Output_data* plt =
-	      parameters->target().plt_section_for_local(object, lsi);
-	    val = plt->address() + object->local_plt_offset(lsi);
+	    uint64_t plt_address =
+	      parameters->target().plt_address_for_local(object, lsi);
+	    val = plt_address + object->local_plt_offset(lsi);
 	  }
       }
       break;
Index: target.h
===================================================================
RCS file: /cvs/src/src/gold/target.h,v
retrieving revision 1.61
diff -u -p -r1.61 target.h
--- target.h	2 Jul 2011 00:03:25 -0000	1.61
+++ target.h	8 Jul 2011 22:33:21 -0000
@@ -246,17 +246,19 @@ class Target
   reloc_addend(void* arg, unsigned int type, uint64_t addend) const
   { return this->do_reloc_addend(arg, type, addend); }
 
-  // Return the PLT section to use for a global symbol.  This is used
-  // for STT_GNU_IFUNC symbols.
-  Output_data*
-  plt_section_for_global(const Symbol* sym) const
-  { return this->do_plt_section_for_global(sym); }
-
-  // Return the PLT section to use for a local symbol.  This is used
-  // for STT_GNU_IFUNC symbols.
-  Output_data*
-  plt_section_for_local(const Relobj* object, unsigned int symndx) const
-  { return this->do_plt_section_for_local(object, symndx); }
+  // Return the PLT address to use for a global symbol.  This is used
+  // for STT_GNU_IFUNC symbols.  The symbol's plt_offset is relative
+  // to this PLT address.
+  uint64_t
+  plt_address_for_global(const Symbol* sym) const
+  { return this->do_plt_address_for_global(sym); }
+
+  // Return the PLT address to use for a local symbol.  This is used
+  // for STT_GNU_IFUNC symbols.  The symbol's plt_offset is relative
+  // to this PLT address.
+  uint64_t
+  plt_address_for_local(const Relobj* object, unsigned int symndx) const
+  { return this->do_plt_address_for_local(object, symndx); }
 
   // Return whether this target can use relocation types to determine
   // if a function's address is taken.
@@ -502,12 +504,12 @@ class Target
 
   // Virtual functions that must be overridden by a target that uses
   // STT_GNU_IFUNC symbols.
-  virtual Output_data*
-  do_plt_section_for_global(const Symbol*) const
+  virtual uint64_t
+  do_plt_address_for_global(const Symbol*) const
   { gold_unreachable(); }
 
-  virtual Output_data*
-  do_plt_section_for_local(const Relobj*, unsigned int) const
+  virtual uint64_t
+  do_plt_address_for_local(const Relobj*, unsigned int) const
   { gold_unreachable(); }
 
   // Virtual function which may be overriden by the child class.
@@ -870,7 +872,8 @@ class Sized_target : public Target
   // A target needs to implement this to support incremental linking.
 
   virtual void
-  register_global_plt_entry(unsigned int /* plt_index */,
+  register_global_plt_entry(Symbol_table*, Layout*,
+			    unsigned int /* plt_index */,
 			    Symbol*)
   { gold_unreachable(); }
 
Index: x86_64.cc
===================================================================
RCS file: /cvs/src/src/gold/x86_64.cc,v
retrieving revision 1.133
diff -u -p -r1.133 x86_64.cc
--- x86_64.cc	6 Jul 2011 05:12:03 -0000	1.133
+++ x86_64.cc	8 Jul 2011 22:33:21 -0000
@@ -54,22 +54,24 @@ class Output_data_plt_x86_64 : public Ou
  public:
   typedef Output_data_reloc<elfcpp::SHT_RELA, true, 64, false> Reloc_section;
 
-  Output_data_plt_x86_64(Symbol_table* symtab, Layout* layout,
-			 Output_data_got<64, false>* got,
-			 Output_data_space* got_plt)
-    : Output_section_data(16), tlsdesc_rel_(NULL), got_(got), got_plt_(got_plt),
-      count_(0), tlsdesc_got_offset_(-1U), free_list_()
-  { this->init(symtab, layout); }
+  Output_data_plt_x86_64(Layout* layout, Output_data_got<64, false>* got,
+			 Output_data_space* got_plt,
+			 Output_data_space* got_irelative)
+    : Output_section_data(16), tlsdesc_rel_(NULL), irelative_rel_(NULL),
+      got_(got), got_plt_(got_plt), got_irelative_(got_irelative), count_(0),
+      irelative_count_(0), tlsdesc_got_offset_(-1U), free_list_()
+  { this->init(layout); }
 
-  Output_data_plt_x86_64(Symbol_table* symtab, Layout* layout,
-			 Output_data_got<64, false>* got,
+  Output_data_plt_x86_64(Layout* layout, Output_data_got<64, false>* got,
 			 Output_data_space* got_plt,
+			 Output_data_space* got_irelative,
 			 unsigned int plt_count)
     : Output_section_data((plt_count + 1) * plt_entry_size, 16, false),
-      tlsdesc_rel_(NULL), got_(got), got_plt_(got_plt),
-      count_(plt_count), tlsdesc_got_offset_(-1U), free_list_()
+      tlsdesc_rel_(NULL), irelative_rel_(NULL), got_(got), got_plt_(got_plt),
+      got_irelative_(got_irelative), count_(plt_count), irelative_count_(0),
+      tlsdesc_got_offset_(-1U), free_list_()
   {
-    this->init(symtab, layout);
+    this->init(layout);
 
     // Initialize the free list and reserve the first entry.
     this->free_list_.init((plt_count + 1) * plt_entry_size, false);
@@ -78,20 +80,22 @@ class Output_data_plt_x86_64 : public Ou
 
   // Initialize the PLT section.
   void
-  init(Symbol_table* symtab, Layout* layout);
+  init(Layout* layout);
 
   // Add an entry to the PLT.
   void
-  add_entry(Symbol* gsym);
+  add_entry(Symbol_table*, Layout*, Symbol* gsym);
 
   // Add an entry to the PLT for a local STT_GNU_IFUNC symbol.
   unsigned int
-  add_local_ifunc_entry(Sized_relobj_file<64, false>* relobj,
+  add_local_ifunc_entry(Symbol_table* symtab, Layout*,
+			Sized_relobj_file<64, false>* relobj,
 			unsigned int local_sym_index);
 
   // Add the relocation for a PLT entry.
   void
-  add_relocation(Symbol* gsym, unsigned int got_offset);
+  add_relocation(Symbol_table*, Layout*, Symbol* gsym,
+		 unsigned int got_offset);
 
   // Add the reserved TLSDESC_PLT entry to the PLT.
   void
@@ -111,7 +115,7 @@ class Output_data_plt_x86_64 : public Ou
   // Return the offset of the reserved TLSDESC_PLT entry.
   unsigned int
   get_tlsdesc_plt_offset() const
-  { return (this->count_ + 1) * plt_entry_size; }
+  { return (this->count_ + this->irelative_count_ + 1) * plt_entry_size; }
 
   // Return the .rela.plt section data.
   Reloc_section*
@@ -122,10 +126,20 @@ class Output_data_plt_x86_64 : public Ou
   Reloc_section*
   rela_tlsdesc(Layout*);
 
+  // Return where the IRELATIVE relocations should go in the PLT
+  // relocations.
+  Reloc_section*
+  rela_irelative(Symbol_table*, Layout*);
+
+  // Return whether we created a section for IRELATIVE relocations.
+  bool
+  has_irelative_section() const
+  { return this->irelative_rel_ != NULL; }
+
   // Return the number of PLT entries.
   unsigned int
   entry_count() const
-  { return this->count_; }
+  { return this->count_ + this->irelative_count_; }
 
   // Return the offset of the first non-reserved PLT entry.
   static unsigned int
@@ -145,6 +159,14 @@ class Output_data_plt_x86_64 : public Ou
 			    (plt_index + 2) * 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);
@@ -188,12 +210,20 @@ class Output_data_plt_x86_64 : public Ou
   // The TLSDESC relocs, if necessary.  These must follow the regular
   // PLT relocs.
   Reloc_section* tlsdesc_rel_;
+  // The IRELATIVE relocs, if necessary.  These must follow the
+  // regular PLT relocations and the TLSDESC relocations.
+  Reloc_section* irelative_rel_;
   // The .got section.
   Output_data_got<64, false>* got_;
   // The .got.plt section.
   Output_data_space* got_plt_;
+  // The part of the .got.plt section used for IRELATIVE relocs.
+  Output_data_space* got_irelative_;
   // The number of PLT entries.
   unsigned int count_;
+  // Number of PLT entries with R_X86_64_IRELATIVE relocs.  These
+  // follow the regular PLT entries.
+  unsigned int irelative_count_;
   // Offset of the reserved TLSDESC_GOT entry when needed.
   unsigned int tlsdesc_got_offset_;
   // List of available regions within the section, for incremental
@@ -217,10 +247,10 @@ class Target_x86_64 : public Sized_targe
 
   Target_x86_64()
     : Sized_target<64, false>(&x86_64_info),
-      got_(NULL), plt_(NULL), got_plt_(NULL), got_tlsdesc_(NULL),
-      global_offset_table_(NULL), rela_dyn_(NULL),
-      copy_relocs_(elfcpp::R_X86_64_COPY), dynbss_(NULL),
-      got_mod_index_offset_(-1U), tlsdesc_reloc_info_(),
+      got_(NULL), plt_(NULL), got_plt_(NULL), got_irelative_(NULL),
+      got_tlsdesc_(NULL), global_offset_table_(NULL), rela_dyn_(NULL),
+      rela_irelative_(NULL), copy_relocs_(elfcpp::R_X86_64_COPY),
+      dynbss_(NULL), got_mod_index_offset_(-1U), tlsdesc_reloc_info_(),
       tls_base_symbol_defined_(false)
   { }
 
@@ -332,13 +362,13 @@ class Target_x86_64 : public Sized_targe
   do_reloc_addend(void* arg, unsigned int r_type, uint64_t addend) const;
 
   // Return the PLT section.
-  Output_data*
-  do_plt_section_for_global(const Symbol*) const
-  { return this->plt_section(); }
-
-  Output_data*
-  do_plt_section_for_local(const Relobj*, unsigned int) const
-  { return this->plt_section(); }
+  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); }
 
   // This function should be defined in targets that can use relocation
   // types to determine (implemented in local_reloc_may_be_function_pointer
@@ -413,7 +443,8 @@ class Target_x86_64 : public Sized_targe
 
   // Register an existing PLT entry for a global symbol.
   void
-  register_global_plt_entry(unsigned int plt_index, Symbol* gsym);
+  register_global_plt_entry(Symbol_table*, Layout*, unsigned int plt_index,
+			    Symbol* gsym);
 
   // Force a COPY relocation for a given symbol.
   void
@@ -683,6 +714,10 @@ class Target_x86_64 : public Sized_targe
   Reloc_section*
   rela_tlsdesc_section(Layout*) const;
 
+  // Get the section to use for IRELATIVE relocations.
+  Reloc_section*
+  rela_irelative_section(Layout*);
+
   // Add a potential copy relocation.
   void
   copy_reloc(Symbol_table* symtab, Layout* layout,
@@ -733,12 +768,16 @@ class Target_x86_64 : public Sized_targe
   Output_data_plt_x86_64* plt_;
   // The GOT PLT section.
   Output_data_space* got_plt_;
+  // The GOT section for IRELATIVE relocations.
+  Output_data_space* got_irelative_;
   // The GOT section for TLSDESC relocations.
   Output_data_got<64, false>* got_tlsdesc_;
   // The _GLOBAL_OFFSET_TABLE_ symbol.
   Symbol* global_offset_table_;
   // The dynamic reloc section.
   Reloc_section* rela_dyn_;
+  // The section to use for IRELATIVE relocs.
+  Reloc_section* rela_irelative_;
   // Relocs saved to avoid a COPY reloc.
   Copy_relocs<elfcpp::SHT_RELA, 64, false> copy_relocs_;
   // Space for variables copied with a COPY reloc.
@@ -826,8 +865,17 @@ Target_x86_64::got_section(Symbol_table*
 				      elfcpp::STV_HIDDEN, 0,
 				      false, false);
 
+      // If there are any IRELATIVE relocations, they get GOT entries
+      // in .got.plt after the jump slot entries.
+      this->got_irelative_ = new Output_data_space(8, "** GOT IRELATIVE PLT");
+      layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS,
+				      (elfcpp::SHF_ALLOC
+				       | elfcpp::SHF_WRITE),
+				      this->got_irelative_,
+				      ORDER_NON_RELRO_FIRST, false);
+
       // If there are any TLSDESC relocations, they get GOT entries in
-      // .got.plt after the jump slot entries.
+      // .got.plt after the jump slot and IRELATIVE entries.
       this->got_tlsdesc_ = new Output_data_got<64, false>();
       layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS,
 				      (elfcpp::SHF_ALLOC
@@ -855,34 +903,39 @@ Target_x86_64::rela_dyn_section(Layout* 
   return this->rela_dyn_;
 }
 
+// Get the section to use for IRELATIVE 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.
+
+Target_x86_64::Reloc_section*
+Target_x86_64::rela_irelative_section(Layout* layout)
+{
+  if (this->rela_irelative_ == NULL)
+    {
+      // Make sure we have already created the dynamic reloc section.
+      this->rela_dyn_section(layout);
+      this->rela_irelative_ = new Reloc_section(false);
+      layout->add_output_section_data(".rela.dyn", elfcpp::SHT_RELA,
+				      elfcpp::SHF_ALLOC, this->rela_irelative_,
+				      ORDER_DYNAMIC_RELOCS, false);
+      gold_assert(this->rela_dyn_->output_section()
+		  == this->rela_irelative_->output_section());
+    }
+  return this->rela_irelative_;
+}
+
 // Initialize the PLT section.
 
 void
-Output_data_plt_x86_64::init(Symbol_table* symtab, Layout* layout)
+Output_data_plt_x86_64::init(Layout* layout)
 {
   this->rel_ = new Reloc_section(false);
   layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA,
 				  elfcpp::SHF_ALLOC, this->rel_,
 				  ORDER_DYNAMIC_PLT_RELOCS, false);
 
-  if (parameters->doing_static_link())
-    {
-      // A statically linked executable will only have a .rela.plt
-      // section to hold R_X86_64_IRELATIVE relocs for STT_GNU_IFUNC
-      // symbols.  The library will use these symbols to locate the
-      // IRELATIVE relocs at program startup time.
-      symtab->define_in_output_data("__rela_iplt_start", NULL,
-				    Symbol_table::PREDEFINED,
-				    this->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->rel_, 0, 0, elfcpp::STT_NOTYPE,
-				    elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN,
-				    0, true, true);
-    }
-
   // Add unwind information if requested.
   if (parameters->options().ld_generated_unwind_info())
     layout->add_eh_frame_for_plt(this, plt_eh_frame_cie, plt_eh_frame_cie_size,
@@ -898,7 +951,8 @@ Output_data_plt_x86_64::do_adjust_output
 // Add an entry to the PLT.
 
 void
-Output_data_plt_x86_64::add_entry(Symbol* gsym)
+Output_data_plt_x86_64::add_entry(Symbol_table* symtab, Layout* layout,
+				  Symbol* gsym)
 {
   gold_assert(!gsym->has_plt_offset());
 
@@ -906,25 +960,47 @@ Output_data_plt_x86_64::add_entry(Symbol
   off_t plt_offset;
   section_offset_type got_offset;
 
+  unsigned int* pcount;
+  unsigned int offset;
+  unsigned int reserved;
+  Output_data_space* got;
+  if (gsym->type() == elfcpp::STT_GNU_IFUNC
+      && gsym->can_use_relative_reloc(false))
+    {
+      pcount = &this->irelative_count_;
+      offset = 0;
+      reserved = 0;
+      got = this->got_irelative_;
+    }
+  else
+    {
+      pcount = &this->count_;
+      offset = 1;
+      reserved = 3;
+      got = this->got_plt_;
+    }
+
   if (!this->is_data_size_valid())
     {
-      // Note that when setting the PLT offset we skip the initial
-      // reserved PLT entry.
-      plt_index = this->count_ + 1;
+      // Note that when setting the PLT offset for a non-IRELATIVE
+      // entry we skip the initial reserved PLT entry.
+      plt_index = *pcount + offset;
       plt_offset = plt_index * plt_entry_size;
 
-      ++this->count_;
+      ++*pcount;
 
-      got_offset = (plt_index - 1 + 3) * 8;
-      gold_assert(got_offset == this->got_plt_->current_data_size());
+      got_offset = (plt_index - offset + reserved) * 8;
+      gold_assert(got_offset == got->current_data_size());
 
       // Every PLT entry needs a GOT entry which points back to the PLT
       // entry (this will be changed by the dynamic linker, normally
       // lazily when the function is called).
-      this->got_plt_->set_current_data_size(got_offset + 8);
+      got->set_current_data_size(got_offset + 8);
     }
   else
     {
+      // FIXME: This is probably not correct for IRELATIVE relocs.
+
       // For incremental updates, find an available slot.
       plt_offset = this->free_list_.allocate(plt_entry_size, plt_entry_size, 0);
       if (plt_offset == -1)
@@ -935,13 +1011,13 @@ Output_data_plt_x86_64::add_entry(Symbol
       // can be calculated from the PLT index, adjusting for the three
       // reserved entries at the beginning of the GOT.
       plt_index = plt_offset / plt_entry_size - 1;
-      got_offset = (plt_index - 1 + 3) * 8;
+      got_offset = (plt_index - offset + reserved) * 8;
     }
 
   gsym->set_plt_offset(plt_offset);
 
   // Every PLT entry needs a reloc.
-  this->add_relocation(gsym, got_offset);
+  this->add_relocation(symtab, layout, gsym, got_offset);
 
   // 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
@@ -953,22 +1029,25 @@ Output_data_plt_x86_64::add_entry(Symbol
 
 unsigned int
 Output_data_plt_x86_64::add_local_ifunc_entry(
+    Symbol_table* symtab,
+    Layout* layout,
     Sized_relobj_file<64, false>* relobj,
     unsigned int local_sym_index)
 {
-  unsigned int plt_offset = (this->count_ + 1) * plt_entry_size;
-  ++this->count_;
+  unsigned int plt_offset = this->irelative_count_ * plt_entry_size;
+  ++this->irelative_count_;
 
-  section_offset_type got_offset = this->got_plt_->current_data_size();
+  section_offset_type got_offset = this->got_irelative_->current_data_size();
 
   // Every PLT entry needs a GOT entry which points back to the PLT
   // entry.
-  this->got_plt_->set_current_data_size(got_offset + 8);
+  this->got_irelative_->set_current_data_size(got_offset + 8);
 
   // Every PLT entry needs a reloc.
-  this->rel_->add_symbolless_local_addend(relobj, local_sym_index,
-					  elfcpp::R_X86_64_IRELATIVE,
-					  this->got_plt_, got_offset, 0);
+  Reloc_section* rela = this->rela_irelative(symtab, layout);
+  rela->add_symbolless_local_addend(relobj, local_sym_index,
+				    elfcpp::R_X86_64_IRELATIVE,
+				    this->got_irelative_, got_offset, 0);
 
   return plt_offset;
 }
@@ -976,12 +1055,16 @@ Output_data_plt_x86_64::add_local_ifunc_
 // Add the relocation for a PLT entry.
 
 void
-Output_data_plt_x86_64::add_relocation(Symbol* gsym, unsigned int got_offset)
+Output_data_plt_x86_64::add_relocation(Symbol_table* symtab, Layout* layout,
+				       Symbol* gsym, unsigned int got_offset)
 {
   if (gsym->type() == elfcpp::STT_GNU_IFUNC
       && gsym->can_use_relative_reloc(false))
-    this->rel_->add_symbolless_global_addend(gsym, elfcpp::R_X86_64_IRELATIVE,
-					     this->got_plt_, got_offset, 0);
+    {
+      Reloc_section* rela = this->rela_irelative(symtab, layout);
+      rela->add_symbolless_global_addend(gsym, elfcpp::R_X86_64_IRELATIVE,
+					 this->got_irelative_, got_offset, 0);
+    }
   else
     {
       gsym->set_needs_dynsym_entry();
@@ -1002,17 +1085,78 @@ Output_data_plt_x86_64::rela_tlsdesc(Lay
       layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA,
 				      elfcpp::SHF_ALLOC, this->tlsdesc_rel_,
 				      ORDER_DYNAMIC_PLT_RELOCS, false);
-      gold_assert(this->tlsdesc_rel_->output_section() ==
-		  this->rel_->output_section());
+      gold_assert(this->tlsdesc_rel_->output_section()
+		  == this->rel_->output_section());
     }
   return this->tlsdesc_rel_;
 }
 
+// Return where the IRELATIVE relocations should go in the PLT.  These
+// follow the JUMP_SLOT and the TLSDESC relocations.
+
+Output_data_plt_x86_64::Reloc_section*
+Output_data_plt_x86_64::rela_irelative(Symbol_table* symtab, Layout* layout)
+{
+  if (this->irelative_rel_ == NULL)
+    {
+      // Make sure we have a place for the TLSDESC relocations, in
+      // case we see any later on.
+      this->rela_tlsdesc(layout);
+      this->irelative_rel_ = new Reloc_section(false);
+      layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA,
+				      elfcpp::SHF_ALLOC, this->irelative_rel_,
+				      ORDER_DYNAMIC_PLT_RELOCS, false);
+      gold_assert(this->irelative_rel_->output_section()
+		  == this->rel_->output_section());
+
+      if (parameters->doing_static_link())
+	{
+	  // A statically linked executable will only have a .rela.plt
+	  // section to hold R_X86_64_IRELATIVE relocs for
+	  // STT_GNU_IFUNC symbols.  The library will use these
+	  // symbols to locate the IRELATIVE relocs at program startup
+	  // time.
+	  symtab->define_in_output_data("__rela_iplt_start", NULL,
+					Symbol_table::PREDEFINED,
+					this->irelative_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->irelative_rel_, 0, 0,
+					elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL,
+					elfcpp::STV_HIDDEN, 0, true, true);
+	}
+    }
+  return this->irelative_rel_;
+}
+
+// Return the PLT address to use for a global symbol.
+
+uint64_t
+Output_data_plt_x86_64::address_for_global(const Symbol* gsym)
+{
+  uint64_t offset = 0;
+  if (gsym->type() == elfcpp::STT_GNU_IFUNC
+      && gsym->can_use_relative_reloc(false))
+    offset = (this->count_ + 1) * plt_entry_size;
+  return this->address() + offset;
+}
+
+// Return the PLT address to use for a local symbol.  These are always
+// IRELATIVE relocs.
+
+uint64_t
+Output_data_plt_x86_64::address_for_local(const Relobj*, unsigned int)
+{
+  return this->address() + (this->count_ + 1) * plt_entry_size;
+}
+
 // Set the final size.
 void
 Output_data_plt_x86_64::set_final_data_size()
 {
-  unsigned int count = this->count_;
+  unsigned int count = this->count_ + this->irelative_count_;
   if (this->has_tlsdesc_entry())
     ++count;
   this->set_data_size((count + 1) * plt_entry_size);
@@ -1117,8 +1261,12 @@ Output_data_plt_x86_64::do_write(Output_
   unsigned char* const oview = of->get_output_view(offset, oview_size);
 
   const off_t got_file_offset = this->got_plt_->offset();
+  gold_assert(parameters->incremental_update()
+	      || (got_file_offset + this->got_plt_->data_size()
+		  == this->got_irelative_->offset()));
   const section_size_type got_size =
-    convert_to_section_size_type(this->got_plt_->data_size());
+    convert_to_section_size_type(this->got_plt_->data_size()
+				 + this->got_irelative_->data_size());
   unsigned char* const got_view = of->get_output_view(got_file_offset,
 						      got_size);
 
@@ -1150,7 +1298,7 @@ Output_data_plt_x86_64::do_write(Output_
 
   unsigned int plt_offset = plt_entry_size;
   unsigned int got_offset = 24;
-  const unsigned int count = this->count_;
+  const unsigned int count = this->count_ + this->irelative_count_;
   for (unsigned int plt_index = 0;
        plt_index < count;
        ++plt_index,
@@ -1208,8 +1356,9 @@ Target_x86_64::make_plt_section(Symbol_t
       // Create the GOT sections first.
       this->got_section(symtab, layout);
 
-      this->plt_ = new Output_data_plt_x86_64(symtab, layout, this->got_,
-                                              this->got_plt_);
+      this->plt_ = new Output_data_plt_x86_64(layout, this->got_,
+					      this->got_plt_,
+					      this->got_irelative_);
       layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
 				      (elfcpp::SHF_ALLOC
 				       | elfcpp::SHF_EXECINSTR),
@@ -1241,7 +1390,7 @@ Target_x86_64::make_plt_entry(Symbol_tab
   if (this->plt_ == NULL)
     this->make_plt_section(symtab, layout);
 
-  this->plt_->add_entry(gsym);
+  this->plt_->add_entry(symtab, layout, gsym);
 }
 
 // Make a PLT entry for a local STT_GNU_IFUNC symbol.
@@ -1255,7 +1404,8 @@ Target_x86_64::make_local_ifunc_plt_entr
     return;
   if (this->plt_ == NULL)
     this->make_plt_section(symtab, layout);
-  unsigned int plt_offset = this->plt_->add_local_ifunc_entry(relobj,
+  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);
 }
@@ -1330,9 +1480,17 @@ Target_x86_64::init_got_plt_for_update(S
 				  this->got_tlsdesc_,
 				  ORDER_NON_RELRO_FIRST, false);
 
+  // If there are any IRELATIVE relocations, they get GOT entries in
+  // .got.plt after the jump slot and TLSDESC entries.
+  this->got_irelative_ = new Output_data_space(0, 8, "** GOT IRELATIVE PLT");
+  layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS,
+				  elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE,
+				  this->got_irelative_,
+				  ORDER_NON_RELRO_FIRST, false);
+
   // Create the PLT section.
-  this->plt_ = new Output_data_plt_x86_64(symtab, layout, this->got_,
-					  this->got_plt_, plt_count);
+  this->plt_ = new Output_data_plt_x86_64(layout, this->got_, this->got_plt_,
+					  this->got_irelative_, plt_count);
   layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
 				  elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR,
 				  this->plt_, ORDER_PLT, false);
@@ -1439,7 +1597,9 @@ Target_x86_64::reserve_global_got_entry(
 // Register an existing PLT entry for a global symbol.
 
 void
-Target_x86_64::register_global_plt_entry(unsigned int plt_index,
+Target_x86_64::register_global_plt_entry(Symbol_table* symtab,
+					 Layout* layout,
+					 unsigned int plt_index,
 					 Symbol* gsym)
 {
   gold_assert(this->plt_ != NULL);
@@ -1450,7 +1610,7 @@ Target_x86_64::register_global_plt_entry
   gsym->set_plt_offset((plt_index + 1) * this->plt_entry_size());
 
   unsigned int got_offset = (plt_index + 3) * 8;
-  this->plt_->add_relocation(gsym, got_offset);
+  this->plt_->add_relocation(symtab, layout, gsym, got_offset);
 }
 
 // Force a COPY relocation for a given symbol.
@@ -2196,7 +2356,8 @@ Target_x86_64::Scan::global(Symbol_table
 		// 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_dyn_section(layout);
+		Reloc_section* rela_dyn =
+		  target->rela_irelative_section(layout);
 		unsigned int r_type = elfcpp::R_X86_64_IRELATIVE;
 		rela_dyn->add_symbolless_global_addend(gsym, r_type,
 						       output_section, object,
@@ -2600,7 +2761,8 @@ Target_x86_64::do_finalize_sections(
       symtab->get_sized_symbol<64>(sym)->set_symsize(data_size);
     }
 
-  if (parameters->doing_static_link() && this->plt_ == NULL)
+  if (parameters->doing_static_link()
+      && (this->plt_ == NULL || !this->plt_->has_irelative_section()))
     {
       // If linking statically, make sure that the __rela_iplt symbols
       // were defined if necessary, even if we didn't create a PLT.
@@ -2680,7 +2842,7 @@ Target_x86_64::Relocate::relocate(const 
   if (gsym != NULL
       && gsym->use_plt_offset(Scan::get_reference_flags(r_type)))
     {
-      symval.set_output_value(target->plt_section()->address()
+      symval.set_output_value(target->plt_address_for_global(gsym)
 			      + gsym->plt_offset());
       psymval = &symval;
     }
@@ -2689,7 +2851,7 @@ Target_x86_64::Relocate::relocate(const 
       unsigned int r_sym = elfcpp::elf_r_sym<64>(rela.get_r_info());
       if (object->local_has_plt_offset(r_sym))
 	{
-	  symval.set_output_value(target->plt_section()->address()
+	  symval.set_output_value(target->plt_address_for_local(object, r_sym)
 				  + object->local_plt_offset(r_sym));
 	  psymval = &symval;
 	}
@@ -3599,7 +3761,7 @@ uint64_t
 Target_x86_64::do_dynsym_value(const Symbol* gsym) const
 {
   gold_assert(gsym->is_from_dynobj() && gsym->has_plt_offset());
-  return this->plt_section()->address() + gsym->plt_offset();
+  return this->plt_address_for_global(gsym) + gsym->plt_offset();
 }
 
 // Return a string used to fill a code section with nops to take up
Index: testsuite/Makefile.am
===================================================================
RCS file: /cvs/src/src/gold/testsuite/Makefile.am,v
retrieving revision 1.175
diff -u -p -r1.175 Makefile.am
--- testsuite/Makefile.am	7 Jul 2011 18:41:53 -0000	1.175
+++ testsuite/Makefile.am	8 Jul 2011 22:33:22 -0000
@@ -1833,6 +1833,18 @@ check_PROGRAMS += ifuncmain7pie
 ifuncmain7pie: ifuncmain7pie.o gcctestdir/ld
 	$(LINK) -Bgcctestdir/ -pie ifuncmain7pie.o
 
+check_PROGRAMS += ifuncvar
+ifuncvar1_pic.o: ifuncvar1.c
+	$(COMPILE) -c -fpic -o $@ $<
+ifuncvar2_pic.o: ifuncvar2.c	
+	$(COMPILE) -c -fpic -o $@ $<
+ifuncvar.so: ifuncvar1_pic.o ifuncvar2_pic.o gcctestdir/ld
+	$(LINK) -Bgcctestdir/ -shared ifuncvar1_pic.o ifuncvar2_pic.o
+ifuncvar_SOURCES = ifuncvar3.c
+ifuncvar_DEPENDENCIES = gcctestdir/ld ifuncvar.so
+ifuncvar_LDFLAGS = -Bgcctestdir/ -Wl,-R,.
+ifuncvar_LDADD = ifuncvar.so
+
 endif IFUNC
 
 # Test that strong reference to a weak symbol in a DSO remains strong.
Index: testsuite/ifuncvar1.c
===================================================================
RCS file: testsuite/ifuncvar1.c
diff -N testsuite/ifuncvar1.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/ifuncvar1.c	8 Jul 2011 22:33:22 -0000
@@ -0,0 +1,20 @@
+/* Test global variable initialized to hidden STT_GNU_IFUNC symbol.  */
+
+int didit;
+
+extern void doit (void);
+
+void
+doit (void)
+{
+  didit = 1;
+}
+
+void (*get_foo (void)) (void) __asm__ ("foo");
+__asm__ (".type foo, %gnu_indirect_function");
+__asm__ (".hidden foo");
+
+void (*get_foo (void)) (void)
+{
+  return &doit;
+}
Index: testsuite/ifuncvar2.c
===================================================================
RCS file: testsuite/ifuncvar2.c
diff -N testsuite/ifuncvar2.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/ifuncvar2.c	8 Jul 2011 22:33:22 -0000
@@ -0,0 +1,12 @@
+/* Test global variable initialized to hidden STT_GNU_IFUNC symbol.  */
+
+extern void foo (void);
+void (*f) (void) = &foo;
+
+extern void bar (void);
+
+void
+bar (void)
+{
+  f ();
+}
Index: testsuite/ifuncvar3.c
===================================================================
RCS file: testsuite/ifuncvar3.c
diff -N testsuite/ifuncvar3.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/ifuncvar3.c	8 Jul 2011 22:33:22 -0000
@@ -0,0 +1,14 @@
+/* Test global variable initialized to hidden STT_GNU_IFUNC symbol.  */
+
+#include <assert.h>
+
+extern void bar (void);
+extern int didit;
+
+int
+main (void)
+{
+  bar ();
+  assert (didit == 1);
+  return 0;
+}

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