This is the mail archive of the binutils-cvs@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]

[binutils-gdb] This patch adds IFUNC support for arm gold backend.


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=fa89cc82f5ca51f3135a9f2043b85b6a16c205eb

commit fa89cc82f5ca51f3135a9f2043b85b6a16c205eb
Author: Han Shen <shenhan@google.com>
Date:   Thu Jan 29 10:00:46 2015 -0800

    This patch adds IFUNC support for arm gold backend.
    
    This is a feature required in chromeos arm development work.
    
    Tested:
    1) Built passed all-gold on x86_64 machine
    2) Tested with basic gold aarch64 ifunc unittests -
       a) global ifunc, statically/non-statically linked
       b) local ifunc, statically/non-statically linked
       c) global/local, other shared library routine mixed,
       statically/non-statically linked
       d) arm/thumb mode ifunc
       e) linking chrome browser passed

Diff:
---
 elfcpp/ChangeLog |   4 +
 elfcpp/arm.h     |   5 +-
 gold/ChangeLog   |  48 +++++
 gold/aarch64.cc  |   2 +-
 gold/arm.cc      | 593 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
 gold/output.h    |  11 ++
 6 files changed, 613 insertions(+), 50 deletions(-)

diff --git a/elfcpp/ChangeLog b/elfcpp/ChangeLog
index 0687860..47e6328 100644
--- a/elfcpp/ChangeLog
+++ b/elfcpp/ChangeLog
@@ -1,3 +1,7 @@
+2015-01-22  Han Shen  <shenhan@google.com>
+
+	* arm.h (R_ARM_IRELATIVE): New dynamic relocation.
+
 2015-01-01  Alan Modra  <amodra@gmail.com>
 
 	Update year range in copyright notice of all files.
diff --git a/elfcpp/arm.h b/elfcpp/arm.h
index 2d30add..c9cb753 100644
--- a/elfcpp/arm.h
+++ b/elfcpp/arm.h
@@ -192,11 +192,12 @@ enum
   R_ARM_PRIVATE_14 = 126,
   R_ARM_PRIVATE_15 = 127,
   R_ARM_ME_TOO = 128,		// Obsolete
-  R_ARM_THM_TLS_DESCSEQ16 = 129,// Static	Thumb16	
+  R_ARM_THM_TLS_DESCSEQ16 = 129,// Static	Thumb16
   R_ARM_THM_TLS_DESCSEQ32 = 130,// Static	Thumb32
   // 131 - 139			Unallocated
   // 140 - 159			Dynamic		Reserved for future allocation
-  // 160 - 255			Unallocated
+  R_ARM_IRELATIVE = 160,	// Dynamic
+  // 161 - 255			Unallocated
 };
 
 // e_flags values used for ARM.  We only support flags defined in AAELF.
diff --git a/gold/ChangeLog b/gold/ChangeLog
index 8803028..601d11b 100644
--- a/gold/ChangeLog
+++ b/gold/ChangeLog
@@ -1,3 +1,51 @@
+2015-01-22  Han Shen  <shenhan@google.com>
+
+	* arm.cc (Target_arm::Target_arm): Add initialization for new members.
+	(Target_arm::do_plt_address_for_global): New method.
+	(Target_arm::do_plt_address_for_local): New method.
+	(Target_arm::rel_irelative_section): New method.
+	(Target_arm::make_data_plt): Add more parameters for plt ctor.
+	(Target_arm::do_make_data_plt): Add more parameters for plt ctor.
+	(Target_arm::Scan::symbol_needs_plt_entry): Add ifunc support.
+	(Target_arm::Scan::reloc_needs_plt_for_ifunc): Add ifunc support.
+	(Target_arm::Scan::check_non_pic): Add ifunc support.
+	(Target_arm::Scan::local): Add ifunc support.
+	(Target_arm::Scan::global): Add ifunc support.
+	(Target_arm::make_plt_section): New method.
+	(Target_arm::make_plt_entry): Change to call to make_plt_section.
+	(Target_arm::make_local_ifunc_plt_entry): New method.
+	(Target_arm::got_irelative_): New member.
+	(Target_arm::rel_irelative_): New member.
+	(Target_arm::got_section): Add creation for got_irelative_.
+	(Target_arm::rel_dyn_section): Add creation for rel_irelative_.
+	(Target_arm::Relocate::relocate): Properly set local ifunc address.
+	(Target_arm::do_dynsym_value): Properly set global ifunc address.
+	(Target_arm::scan_reloc_for_stub): Properly set global ifunc address.
+	(Output_data_plt_arm::IRelative_data): New type.
+	(Output_data_plt_arm::Output_data_plt_arm): Add more parameters.
+	(Output_data_plt_arm::add_entry): Add more parameters.
+	(Output_data_plt_arm::add_relocation): New method.
+	(Output_data_plt_arm::add_local_ifunc_entry): New method.
+	(Output_data_plt_arm::rel_irelative): New method.
+	(Output_data_plt_arm::entry_count): Modified.
+	(Output_data_plt_arm::address_for_global): New method.
+	(Output_data_plt_arm::address_for_local): New method.
+	(Output_data_plt_arm::set_final_data_size): Add irelative_count_.
+	(Output_data_plt_arm::insert_irelative_data): New method.
+	(Output_data_plt_arm::irelative_rel_): New member.
+	(Output_data_plt_arm::got_): New member.
+	(Output_data_plt_arm::got_irelative_): New member.
+	(Output_data_plt_arm::irelative_count_): New member.
+	(Output_data_plt_arm::IRelative_data_vec): New typedef.
+	(Output_data_plt_arm::irelative_data_vec_): New member.
+	(Output_data_plt_arm::do_write): Write out irelative entries.
+	(Output_data_plt_arm_standard::Output_data_plt_arm_standard): Add
+	more parameters to ctor.
+	(Output_data_plt_arm_nacl::Output_data_plt_arm_nacl): Add
+	more parameters to ctor.
+	* output.h (Output_data_reloc::add_local_relative): New method.
+	* aarch64.cc (Output_data_plt_aarch64): Fixed typo in comment.
+
 2015-01-29  Alan Modra  <amodra@gmail.com>
 
 	* powerpc.cc (Target_powerpc::Relocate::relocate): Correct GOT_TLSLD
diff --git a/gold/aarch64.cc b/gold/aarch64.cc
index 7f5ebb6..b16049e 100644
--- a/gold/aarch64.cc
+++ b/gold/aarch64.cc
@@ -3082,7 +3082,7 @@ class Output_data_plt_aarch64 : public Output_section_data
   // The number of PLT entries.
   unsigned int count_;
 
-  // Number of PLT entries with R_X86_64_IRELATIVE relocs.  These
+  // Number of PLT entries with R_AARCH64_IRELATIVE relocs.  These
   // follow the regular PLT entries.
   unsigned int irelative_count_;
 
diff --git a/gold/arm.cc b/gold/arm.cc
index 6312cc9..be2294c 100644
--- a/gold/arm.cc
+++ b/gold/arm.cc
@@ -2119,8 +2119,8 @@ class Target_arm : public Sized_target<32, big_endian>
 
   Target_arm(const Target::Target_info* info = &arm_info)
     : Sized_target<32, big_endian>(info),
-      got_(NULL), plt_(NULL), got_plt_(NULL), rel_dyn_(NULL),
-      copy_relocs_(elfcpp::R_ARM_COPY),
+      got_(NULL), plt_(NULL), got_plt_(NULL), got_irelative_(NULL),
+      rel_dyn_(NULL), rel_irelative_(NULL), copy_relocs_(elfcpp::R_ARM_COPY),
       got_mod_index_offset_(-1U), tls_base_symbol_defined_(false),
       stub_tables_(), stub_factory_(Stub_factory::get_instance()),
       should_force_pic_veneer_(false),
@@ -2258,6 +2258,18 @@ class Target_arm : public Sized_target<32, big_endian>
   uint64_t
   do_dynsym_value(const Symbol*) const;
 
+  // Return the plt address for globals. Since we have irelative plt entries,
+  // address calculation is not as straightforward as plt_address + plt_offset.
+  uint64_t
+  do_plt_address_for_global(const Symbol* gsym) const
+  { return this->plt_section()->address_for_global(gsym); }
+
+  // Return the plt address for locals. Since we have irelative plt entries,
+  // address calculation is not as straightforward as plt_address + plt_offset.
+  uint64_t
+  do_plt_address_for_local(const Relobj* relobj, unsigned int symndx) const
+  { return this->plt_section()->address_for_local(relobj, symndx); }
+
   // Relocate a section.
   void
   relocate_section(const Relocate_info<32, big_endian>*,
@@ -2357,6 +2369,10 @@ class Target_arm : public Sized_target<32, big_endian>
   unsigned int
   plt_entry_size() const;
 
+  // Get the section to use for IRELATIVE relocations, create it if necessary.
+  Reloc_section*
+  rel_irelative_section(Layout*);
+
   // Map platform-specific reloc types
   static unsigned int
   get_real_reloc_type(unsigned int r_type);
@@ -2448,8 +2464,11 @@ class Target_arm : public Sized_target<32, big_endian>
  protected:
   // Make the PLT-generator object.
   Output_data_plt_arm<big_endian>*
-  make_data_plt(Layout* layout, Output_data_space* got_plt)
-  { return this->do_make_data_plt(layout, got_plt); }
+  make_data_plt(Layout* layout,
+		Arm_output_data_got<big_endian>* got,
+		Output_data_space* got_plt,
+		Output_data_space* got_irelative)
+  { return this->do_make_data_plt(layout, got, got_plt, got_irelative); }
 
   // Make an ELF object.
   Object*
@@ -2530,9 +2549,14 @@ class Target_arm : public Sized_target<32, big_endian>
   do_define_standard_symbols(Symbol_table*, Layout*);
 
   virtual Output_data_plt_arm<big_endian>*
-  do_make_data_plt(Layout* layout, Output_data_space* got_plt)
+  do_make_data_plt(Layout* layout,
+		   Arm_output_data_got<big_endian>* got,
+		   Output_data_space* got_plt,
+		   Output_data_space* got_irelative)
   {
-    return new Output_data_plt_arm_standard<big_endian>(layout, got_plt);
+    gold_assert(got_plt != NULL && got_irelative != NULL);
+    return new Output_data_plt_arm_standard<big_endian>(
+	layout, got, got_plt, got_irelative);
   }
 
  private:
@@ -2602,6 +2626,9 @@ class Target_arm : public Sized_target<32, big_endian>
       if (sym->is_undefined() && !parameters->options().shared())
 	return false;
 
+      if (sym->type() == elfcpp::STT_GNU_IFUNC)
+	return true;
+
       return (!parameters->doing_static_link()
 	      && (sym->type() == elfcpp::STT_FUNC
 		  || sym->type() == elfcpp::STT_ARM_TFUNC)
@@ -2613,6 +2640,11 @@ class Target_arm : public Sized_target<32, big_endian>
     inline bool
     possible_function_pointer_reloc(unsigned int r_type);
 
+    // Whether a plt entry is needed for ifunc.
+    bool
+    reloc_needs_plt_for_ifunc(Sized_relobj_file<32, big_endian>*,
+			      unsigned int r_type);
+
     // Whether we have issued an error about a non-PIC compilation.
     bool issued_non_pic_error_;
   };
@@ -2718,10 +2750,20 @@ class Target_arm : public Sized_target<32, big_endian>
     return this->got_plt_;
   }
 
+  // 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<32, big_endian>* relobj,
+			     unsigned int local_sym_index);
+
   // Define the _TLS_MODULE_BASE_ symbol in the TLS segment.
   void
   define_tls_base_symbol(Symbol_table*, Layout*);
@@ -2903,8 +2945,12 @@ class Target_arm : public Sized_target<32, big_endian>
   Output_data_plt_arm<big_endian>* plt_;
   // The GOT PLT section.
   Output_data_space* got_plt_;
+  // The GOT section for IRELATIVE relocations.
+  Output_data_space* got_irelative_;
   // 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, big_endian> copy_relocs_;
   // Offset of the GOT entry for the TLS module index.
@@ -4244,6 +4290,15 @@ Target_arm<big_endian>::got_section(Symbol_table* symtab, Layout* layout)
 				    elfcpp::STB_LOCAL,
 				    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(4, "** GOT IRELATIVE PLT");
+      layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
+				      (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE),
+				      this->got_irelative_,
+				      got_order, is_got_relro);
+
     }
   return this->got_;
 }
@@ -4257,14 +4312,43 @@ Target_arm<big_endian>::rel_dyn_section(Layout* layout)
   if (this->rel_dyn_ == NULL)
     {
       gold_assert(layout != NULL);
+      // Create both relocation sections in the same place, so as to ensure
+      // their relative order in the output section.
       this->rel_dyn_ = new Reloc_section(parameters->options().combreloc());
+      this->rel_irelative_ = new Reloc_section(false);
       layout->add_output_section_data(".rel.dyn", elfcpp::SHT_REL,
 				      elfcpp::SHF_ALLOC, this->rel_dyn_,
 				      ORDER_DYNAMIC_RELOCS, false);
+      layout->add_output_section_data(".rel.dyn", elfcpp::SHT_REL,
+				      elfcpp::SHF_ALLOC, this->rel_irelative_,
+				      ORDER_DYNAMIC_RELOCS, false);
     }
   return this->rel_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.
+
+template<bool big_endian>
+typename Target_arm<big_endian>::Reloc_section*
+Target_arm<big_endian>::rel_irelative_section(Layout* layout)
+{
+  if (this->rel_irelative_ == NULL)
+    {
+      // Delegate the creation to rel_dyn_section so as to ensure their order in
+      // the output section.
+      this->rel_dyn_section(layout);
+      gold_assert(this->rel_irelative_ != NULL
+		  && (this->rel_dyn_->output_section()
+		      == this->rel_irelative_->output_section()));
+    }
+  return this->rel_irelative_;
+}
+
+
 // Insn_template methods.
 
 // Return byte size of an instruction template.
@@ -7221,24 +7305,80 @@ template<bool big_endian>
 class Output_data_plt_arm : public Output_section_data
 {
  public:
+  // Unlike aarch64, which records symbol value in "addend" field of relocations
+  // and could be done at the same time an IRelative reloc is created for the
+  // symbol, arm puts the symbol value into "GOT" table, which, however, is
+  // issued later in Output_data_plt_arm::do_write(). So we have a struct here
+  // to keep necessary symbol information for later use in do_write. We usually
+  // have only a very limited number of ifuncs, so the extra data required here
+  // is also limited.
+
+  struct IRelative_data
+  {
+    IRelative_data(Sized_symbol<32>* sized_symbol)
+      : symbol_is_global_(true)
+    {
+      u_.global = sized_symbol;
+    }
+
+    IRelative_data(Sized_relobj_file<32, big_endian>* relobj,
+		   unsigned int index)
+      : symbol_is_global_(false)
+    {
+      u_.local.relobj = relobj;
+      u_.local.index = index;
+    }
+
+    union
+    {
+      Sized_symbol<32>* global;
+
+      struct
+      {
+	Sized_relobj_file<32, big_endian>* relobj;
+	unsigned int index;
+      } local;
+    } u_;
+
+    bool symbol_is_global_;
+  };
+
   typedef Output_data_reloc<elfcpp::SHT_REL, true, 32, big_endian>
     Reloc_section;
 
-  Output_data_plt_arm(Layout*, uint64_t addralign, Output_data_space*);
+  Output_data_plt_arm(Layout* layout, uint64_t addralign,
+		      Arm_output_data_got<big_endian>* got,
+		      Output_data_space* got_plt,
+		      Output_data_space* got_irelative);
 
   // Add an entry to the PLT.
   void
-  add_entry(Symbol* gsym);
+  add_entry(Symbol_table* symtab, Layout* layout, Symbol* gsym);
+
+  // Add the relocation for a plt entry.
+  void
+  add_relocation(Symbol_table* symtab, Layout* layout,
+		 Symbol* gsym, unsigned int got_offset);
+
+  // Add an entry to the PLT for a local STT_GNU_IFUNC symbol.
+  unsigned int
+  add_local_ifunc_entry(Symbol_table* symtab, Layout*,
+			Sized_relobj_file<32, big_endian>* relobj,
+			unsigned int local_sym_index);
 
   // Return the .rel.plt section data.
   const Reloc_section*
   rel_plt() const
   { return this->rel_; }
 
+  // Return the PLT relocation container for IRELATIVE.
+  Reloc_section*
+  rel_irelative(Symbol_table*, Layout*);
+
   // 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.
   unsigned int
@@ -7250,6 +7390,14 @@ class Output_data_plt_arm : public Output_section_data
   get_plt_entry_size() const
   { return this->do_get_plt_entry_size(); }
 
+  // Return the PLT address for globals.
+  uint32_t
+  address_for_global(const Symbol*) const;
+
+  // Return the PLT address for locals.
+  uint32_t
+  address_for_local(const Relobj*, unsigned int symndx) const;
+
  protected:
   // Fill in the first PLT entry.
   void
@@ -7298,19 +7446,37 @@ class Output_data_plt_arm : public Output_section_data
   set_final_data_size()
   {
     this->set_data_size(this->first_plt_entry_offset()
-			+ this->count_ * this->get_plt_entry_size());
+			+ ((this->count_ + this->irelative_count_)
+			   * this->get_plt_entry_size()));
   }
 
   // Write out the PLT data.
   void
   do_write(Output_file*);
 
+  // Record irelative symbol data.
+  void insert_irelative_data(const IRelative_data& idata)
+  { irelative_data_vec_.push_back(idata); }
+
   // The reloc section.
   Reloc_section* rel_;
+  // The IRELATIVE relocs, if necessary.  These must follow the
+  // regular PLT relocations.
+  Reloc_section* irelative_rel_;
+  // The .got section.
+  Arm_output_data_got<big_endian>* 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_ARM_IRELATIVE relocs.  These
+  // follow the regular PLT entries.
+  unsigned int irelative_count_;
+  // Vector for irelative data.
+  typedef std::vector<IRelative_data> IRelative_data_vec;
+  IRelative_data_vec irelative_data_vec_;
 };
 
 // Create the PLT section.  The ordinary .got section is an argument,
@@ -7318,10 +7484,14 @@ class Output_data_plt_arm : public Output_section_data
 // section just for PLT entries.
 
 template<bool big_endian>
-Output_data_plt_arm<big_endian>::Output_data_plt_arm(Layout* layout,
-						     uint64_t addralign,
-						     Output_data_space* got_plt)
-  : Output_section_data(addralign), got_plt_(got_plt), count_(0)
+Output_data_plt_arm<big_endian>::Output_data_plt_arm(
+    Layout* layout, uint64_t addralign,
+    Arm_output_data_got<big_endian>* got,
+    Output_data_space* got_plt,
+    Output_data_space* got_irelative)
+  : Output_section_data(addralign), irelative_rel_(NULL),
+    got_(got), got_plt_(got_plt), got_irelative_(got_irelative),
+    count_(0), irelative_count_(0)
 {
   this->rel_ = new Reloc_section(false);
   layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL,
@@ -7340,40 +7510,210 @@ Output_data_plt_arm<big_endian>::do_adjust_output_section(Output_section* os)
 
 template<bool big_endian>
 void
-Output_data_plt_arm<big_endian>::add_entry(Symbol* gsym)
+Output_data_plt_arm<big_endian>::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_) * this->get_plt_entry_size()
-		       + this->first_plt_entry_offset());
+  unsigned int* entry_count;
+  Output_section_data_build* got;
+
+  // We have 2 different types of plt entry here, normal and ifunc.
+
+  // For normal plt, the offset begins with first_plt_entry_offset(20), and the
+  // 1st entry offset would be 20, the second 32, third 44 ... etc.
+
+  // For ifunc plt, the offset begins with 0. So the first offset would 0,
+  // second 12, third 24 ... etc.
+
+  // IFunc plt entries *always* come after *normal* plt entries.
+
+  // Notice, when computing the plt address of a certain symbol, "plt_address +
+  // plt_offset" is no longer correct. Use target->plt_address_for_global() or
+  // target->plt_address_for_local() instead.
+
+  int begin_offset = 0;
+  if (gsym->type() == elfcpp::STT_GNU_IFUNC
+      && gsym->can_use_relative_reloc(false))
+    {
+      entry_count = &this->irelative_count_;
+      got = this->got_irelative_;
+      // For irelative plt entries, offset is relative to the end of normal plt
+      // entries, so it starts from 0.
+      begin_offset = 0;
+      // Record symbol information.
+      this->insert_irelative_data(
+	  IRelative_data(symtab->get_sized_symbol<32>(gsym)));
+    }
+  else
+    {
+      entry_count = &this->count_;
+      got = this->got_plt_;
+      // Note that for normal plt entries, when setting the PLT offset we skip
+      // the initial reserved PLT entry.
+      begin_offset = this->first_plt_entry_offset();
+    }
+
+  gsym->set_plt_offset(begin_offset
+		       + (*entry_count) * this->get_plt_entry_size());
 
-  ++this->count_;
+  ++(*entry_count);
 
-  section_offset_type got_offset = this->got_plt_->current_data_size();
+  section_offset_type 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 + 4);
+  got->set_current_data_size(got_offset + 4);
 
   // Every PLT entry needs a reloc.
-  gsym->set_needs_dynsym_entry();
-  this->rel_->add_global(gsym, elfcpp::R_ARM_JUMP_SLOT, this->got_plt_,
-			 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
   // appear in the relocations.
 }
 
+// Add an entry to the PLT for a local STT_GNU_IFUNC symbol.  Return
+// the PLT offset.
+
+template<bool big_endian>
+unsigned int
+Output_data_plt_arm<big_endian>::add_local_ifunc_entry(
+    Symbol_table* symtab,
+    Layout* layout,
+    Sized_relobj_file<32, big_endian>* relobj,
+    unsigned int local_sym_index)
+{
+  this->insert_irelative_data(IRelative_data(relobj, local_sym_index));
+
+  // Notice, when computingthe plt entry address, "plt_address + plt_offset" is
+  // no longer correct. Use target->plt_address_for_local() instead.
+  unsigned int plt_offset = this->irelative_count_ * this->get_plt_entry_size();
+  ++this->irelative_count_;
+
+  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_irelative_->set_current_data_size(got_offset + 4);
+
+
+  // Every PLT entry needs a reloc.
+  Reloc_section* rel = this->rel_irelative(symtab, layout);
+  rel->add_symbolless_local_addend(relobj, local_sym_index,
+				   elfcpp::R_ARM_IRELATIVE,
+				   this->got_irelative_, got_offset);
+  return plt_offset;
+}
+
+
+// Add the relocation for a PLT entry.
+
+template<bool big_endian>
+void
+Output_data_plt_arm<big_endian>::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))
+    {
+      Reloc_section* rel = this->rel_irelative(symtab, layout);
+      rel->add_symbolless_global_addend(gsym, elfcpp::R_ARM_IRELATIVE,
+					this->got_irelative_, got_offset);
+    }
+  else
+    {
+      gsym->set_needs_dynsym_entry();
+      this->rel_->add_global(gsym, elfcpp::R_ARM_JUMP_SLOT, this->got_plt_,
+			     got_offset);
+    }
+}
+
+
+// Create the irelative relocation data.
+
+template<bool big_endian>
+typename Output_data_plt_arm<big_endian>::Reloc_section*
+Output_data_plt_arm<big_endian>::rel_irelative(Symbol_table* symtab,
+						Layout* layout)
+{
+  if (this->irelative_rel_ == NULL)
+    {
+      // Since irelative relocations goes into 'rel.dyn', we delegate the
+      // creation of irelative_rel_ to where rel_dyn section gets created.
+      Target_arm<big_endian>* arm_target =
+	  Target_arm<big_endian>::default_target();
+      this->irelative_rel_ = arm_target->rel_irelative_section(layout);
+
+      // Make sure we have a place for the TLSDESC relocations, in
+      // case we see any later on.
+      // this->rel_tlsdesc(layout);
+      if (parameters->doing_static_link())
+	{
+	  // A statically linked executable will only have a .rel.plt section to
+	  // hold R_ARM_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 for a global symbol.
+
+template<bool big_endian>
+uint32_t
+Output_data_plt_arm<big_endian>::address_for_global(const Symbol* gsym) const
+{
+  uint64_t begin_offset = 0;
+  if (gsym->type() == elfcpp::STT_GNU_IFUNC
+      && gsym->can_use_relative_reloc(false))
+    {
+      begin_offset = (this->first_plt_entry_offset() +
+		      this->count_ * this->get_plt_entry_size());
+    }
+  return this->address() + begin_offset + gsym->plt_offset();
+}
+
+
+// Return the PLT address for a local symbol.  These are always
+// IRELATIVE relocs.
+
+template<bool big_endian>
+uint32_t
+Output_data_plt_arm<big_endian>::address_for_local(
+    const Relobj* object,
+    unsigned int r_sym) const
+{
+  return (this->address()
+	  + this->first_plt_entry_offset()
+	  + this->count_ * this->get_plt_entry_size()
+	  + object->local_plt_offset(r_sym));
+}
+
+
 template<bool big_endian>
 class Output_data_plt_arm_standard : public Output_data_plt_arm<big_endian>
 {
  public:
-  Output_data_plt_arm_standard(Layout* layout, Output_data_space* got_plt)
-    : Output_data_plt_arm<big_endian>(layout, 4, got_plt)
+  Output_data_plt_arm_standard(Layout* layout,
+			       Arm_output_data_got<big_endian>* got,
+			       Output_data_space* got_plt,
+			       Output_data_space* got_irelative)
+    : Output_data_plt_arm<big_endian>(layout, 4, got, got_plt, got_irelative)
   { }
 
  protected:
@@ -7485,8 +7825,11 @@ Output_data_plt_arm<big_endian>::do_write(Output_file* of)
   unsigned char* const oview = of->get_output_view(offset, oview_size);
 
   const off_t got_file_offset = this->got_plt_->offset();
+  gold_assert(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);
   unsigned char* pov = oview;
@@ -7505,7 +7848,8 @@ Output_data_plt_arm<big_endian>::do_write(Output_file* of)
 
   unsigned int plt_offset = this->first_plt_entry_offset();
   unsigned int got_offset = 12;
-  const unsigned int count = this->count_;
+  const unsigned int count = this->count_ + this->irelative_count_;
+  gold_assert(this->irelative_count_ == this->irelative_data_vec_.size());
   for (unsigned int i = 0;
        i < count;
        ++i,
@@ -7518,8 +7862,33 @@ Output_data_plt_arm<big_endian>::do_write(Output_file* of)
       this->fill_plt_entry(pov, got_address, plt_address,
 			   got_offset, plt_offset);
 
-      // Set the entry in the GOT.
-      elfcpp::Swap<32, big_endian>::writeval(got_pov, plt_address);
+      Arm_address value;
+      if (i < this->count_)
+	{
+	  // For non-irelative got entries, the value is the beginning of plt.
+	  value = plt_address;
+	}
+      else
+	{
+	  // For irelative got entries, the value is the (global/local) symbol
+	  // address.
+	  const IRelative_data& idata =
+	      this->irelative_data_vec_[i - this->count_];
+	  if (idata.symbol_is_global_)
+	    {
+	      // Set the entry in the GOT for irelative symbols.  The content is
+	      // the address of the ifunc, not the address of plt start.
+	      const Sized_symbol<32>* sized_symbol = idata.u_.global;
+	      gold_assert(sized_symbol->type() == elfcpp::STT_GNU_IFUNC);
+	      value = sized_symbol->value();
+	    }
+	  else
+	    {
+	      value = idata.u_.local.relobj->local_symbol_value(
+		  idata.u_.local.index, 0);
+	    }
+	}
+      elfcpp::Swap<32, big_endian>::writeval(got_pov, value);
     }
 
   gold_assert(static_cast<section_size_type>(pov - oview) == oview_size);
@@ -7529,6 +7898,7 @@ Output_data_plt_arm<big_endian>::do_write(Output_file* of)
   of->write_output_view(got_file_offset, got_size, got_view);
 }
 
+
 // Create a PLT entry for a global symbol.
 
 template<bool big_endian>
@@ -7540,20 +7910,58 @@ Target_arm<big_endian>::make_plt_entry(Symbol_table* symtab, Layout* layout,
     return;
 
   if (this->plt_ == NULL)
+    this->make_plt_section(symtab, layout);
+
+  this->plt_->add_entry(symtab, layout, gsym);
+}
+
+
+// Create the PLT section.
+template<bool big_endian>
+void
+Target_arm<big_endian>::make_plt_section(
+  Symbol_table* symtab, Layout* layout)
+{
+  if (this->plt_ == NULL)
     {
-      // Create the GOT sections first.
+      // Create the GOT section first.
       this->got_section(symtab, layout);
 
-      this->plt_ = this->make_data_plt(layout, this->got_plt_);
+      // GOT for irelatives is create along with got.plt.
+      gold_assert(this->got_ != NULL
+		  && this->got_plt_ != NULL
+		  && this->got_irelative_ != NULL);
+      this->plt_ = this->make_data_plt(layout, this->got_, this->got_plt_,
+				       this->got_irelative_);
 
       layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
 				      (elfcpp::SHF_ALLOC
 				       | elfcpp::SHF_EXECINSTR),
 				      this->plt_, ORDER_PLT, false);
     }
-  this->plt_->add_entry(gsym);
 }
 
+
+// Make a PLT entry for a local STT_GNU_IFUNC symbol.
+
+template<bool big_endian>
+void
+Target_arm<big_endian>::make_local_ifunc_plt_entry(
+    Symbol_table* symtab, Layout* layout,
+    Sized_relobj_file<32, 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.
 
 template<bool big_endian>
@@ -7823,6 +8231,7 @@ Target_arm<big_endian>::Scan::check_non_pic(Relobj* object,
     case elfcpp::R_ARM_JUMP_SLOT:
     case elfcpp::R_ARM_ABS32:
     case elfcpp::R_ARM_ABS32_NOI:
+    case elfcpp::R_ARM_IRELATIVE:
     case elfcpp::R_ARM_PC24:
     // FIXME: The following 3 types are not supported by Android's dynamic
     // linker.
@@ -7853,6 +8262,27 @@ Target_arm<big_endian>::Scan::check_non_pic(Relobj* object,
     }
 }
 
+
+// Return whether we need to make a PLT entry for a relocation of the
+// given type against a STT_GNU_IFUNC symbol.
+
+template<bool big_endian>
+bool
+Target_arm<big_endian>::Scan::reloc_needs_plt_for_ifunc(
+    Sized_relobj_file<32, 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 false;
+    }
+  return flags != 0;
+}
+
+
 // Scan a relocation for a local symbol.
 // FIXME: This only handles a subset of relocation types used by Android
 // on ARM v5te devices.
@@ -7874,6 +8304,15 @@ Target_arm<big_endian>::Scan::local(Symbol_table* symtab,
     return;
 
   r_type = get_real_reloc_type(r_type);
+
+  // A local STT_GNU_IFUNC symbol may require a PLT entry.
+  bool is_ifunc = lsym.get_st_type() == elfcpp::STT_GNU_IFUNC;
+  if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type))
+    {
+      unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info());
+      target->make_local_ifunc_plt_entry(symtab, layout, object, r_sym);
+    }
+
   switch (r_type)
     {
     case elfcpp::R_ARM_NONE:
@@ -7898,7 +8337,7 @@ Target_arm<big_endian>::Scan::local(Symbol_table* symtab,
 	  // we need to add check_non_pic(object, r_type) here.
 	  rel_dyn->add_local_relative(object, r_sym, elfcpp::R_ARM_RELATIVE,
 				      output_section, data_shndx,
-				      reloc.get_r_offset());
+				      reloc.get_r_offset(), is_ifunc);
 	}
       break;
 
@@ -8265,6 +8704,11 @@ Target_arm<big_endian>::Scan::global(Symbol_table* symtab,
       && strcmp(gsym->name(), "_GLOBAL_OFFSET_TABLE_") == 0)
     target->got_section(symtab, layout);
 
+  // A STT_GNU_IFUNC symbol may require a PLT entry.
+  if (gsym->type() == elfcpp::STT_GNU_IFUNC
+      && this->reloc_needs_plt_for_ifunc(object, r_type))
+    target->make_plt_entry(symtab, layout, gsym);
+
   r_type = get_real_reloc_type(r_type);
   switch (r_type)
     {
@@ -8309,6 +8753,24 @@ Target_arm<big_endian>::Scan::global(Symbol_table* symtab,
 	      }
 	    else if ((r_type == elfcpp::R_ARM_ABS32
 		      || r_type == elfcpp::R_ARM_ABS32_NOI)
+		     && 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* rel_irelative =
+		    target->rel_irelative_section(layout);
+		unsigned int r_type = elfcpp::R_ARM_IRELATIVE;
+		rel_irelative->add_symbolless_global_addend(
+		    gsym, r_type, output_section, object,
+		    data_shndx, reloc.get_r_offset());
+	      }
+	    else if ((r_type == elfcpp::R_ARM_ABS32
+		      || r_type == elfcpp::R_ARM_ABS32_NOI)
 		     && gsym->can_use_relative_reloc(false))
 	      {
 		Reloc_section* rel_dyn = target->rel_dyn_section(layout);
@@ -8442,7 +8904,13 @@ Target_arm<big_endian>::Scan::global(Symbol_table* symtab,
 	Arm_output_data_got<big_endian>* 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
@@ -8452,12 +8920,29 @@ Target_arm<big_endian>::Scan::global(Symbol_table* symtab,
 		|| gsym->is_undefined()
 		|| gsym->is_preemptible()
 		|| (gsym->visibility() == elfcpp::STV_PROTECTED
-		    && parameters->options().shared()))
+		    && parameters->options().shared())
+		|| (gsym->type() == elfcpp::STT_GNU_IFUNC
+		    && parameters->options().output_is_position_independent()))
 	      got->add_global_with_rel(gsym, GOT_TYPE_STANDARD,
 				       rel_dyn, elfcpp::R_ARM_GLOB_DAT);
 	    else
 	      {
-		if (got->add_global(gsym, GOT_TYPE_STANDARD))
+		// For a STT_GNU_IFUNC symbol we want to write the PLT
+		// offset into the GOT, so that function pointer
+		// comparisons work correctly.
+		bool is_new;
+		if (gsym->type() != elfcpp::STT_GNU_IFUNC)
+		  is_new = got->add_global(gsym, GOT_TYPE_STANDARD);
+		else
+		  {
+		    is_new = got->add_global_plt(gsym, GOT_TYPE_STANDARD);
+		    // 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();
+		  }
+		if (is_new)
 		  rel_dyn->add_global_relative(
 		      gsym, elfcpp::R_ARM_RELATIVE, got,
 		      gsym->got_offset(GOT_TYPE_STANDARD));
@@ -8919,8 +9404,7 @@ Target_arm<big_endian>::Relocate::relocate(
 	  if (gsym->use_plt_offset(Scan::get_reference_flags(r_type)))
 	    {
 	      // This uses a PLT, change the symbol value.
-	      symval.set_output_value(target->plt_section()->address()
-				      + gsym->plt_offset());
+	      symval.set_output_value(target->plt_address_for_global(gsym));
 	      psymval = &symval;
 	    }
 	  else if (gsym->is_weak_undefined())
@@ -8958,6 +9442,13 @@ Target_arm<big_endian>::Relocate::relocate(
 	  elfcpp::Elf_types<32>::Elf_WXword r_info = rel.get_r_info();
 	  unsigned int r_sym = elfcpp::elf_r_sym<32>(r_info);
 	  thumb_bit = object->local_symbol_is_thumb_function(r_sym) ? 1 : 0;
+
+	  if (psymval->is_ifunc_symbol() && object->local_has_plt_offset(r_sym))
+	    {
+	      symval.set_output_value(
+		  target->plt_address_for_local(object, r_sym));
+	      psymval = &symval;
+	    }
 	}
     }
   else
@@ -9936,7 +10427,7 @@ uint64_t
 Target_arm<big_endian>::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);
 }
 
 // Map platform-specific relocs to real relocs
@@ -11091,8 +11582,7 @@ Target_arm<big_endian>::scan_reloc_for_stub(
       if (gsym->use_plt_offset(Scan::get_reference_flags(r_type)))
 	{
 	  // This uses a PLT, change the symbol value.
-	  symval.set_output_value(this->plt_section()->address()
-				  + gsym->plt_offset());
+	  symval.set_output_value(this->plt_address_for_global(gsym));
 	  psymval = &symval;
 	  target_is_thumb = false;
 	}
@@ -12195,8 +12685,13 @@ class Target_arm_nacl : public Target_arm<big_endian>
 
  protected:
   virtual Output_data_plt_arm<big_endian>*
-  do_make_data_plt(Layout* layout, Output_data_space* got_plt)
-  { return new Output_data_plt_arm_nacl<big_endian>(layout, got_plt); }
+  do_make_data_plt(
+		   Layout* layout,
+		   Arm_output_data_got<big_endian>* got,
+		   Output_data_space* got_plt,
+		   Output_data_space* got_irelative)
+  { return new Output_data_plt_arm_nacl<big_endian>(
+      layout, got, got_plt, got_irelative); }
 
  private:
   static const Target::Target_info arm_nacl_info;
@@ -12233,8 +12728,12 @@ template<bool big_endian>
 class Output_data_plt_arm_nacl : public Output_data_plt_arm<big_endian>
 {
  public:
-  Output_data_plt_arm_nacl(Layout* layout, Output_data_space* got_plt)
-    : Output_data_plt_arm<big_endian>(layout, 16, got_plt)
+  Output_data_plt_arm_nacl(
+      Layout* layout,
+      Arm_output_data_got<big_endian>* got,
+      Output_data_space* got_plt,
+      Output_data_space* got_irelative)
+    : Output_data_plt_arm<big_endian>(layout, 16, got, got_plt, got_irelative)
   { }
 
  protected:
diff --git a/gold/output.h b/gold/output.h
index 8ef8942..8e0c33a 100644
--- a/gold/output.h
+++ b/gold/output.h
@@ -1714,6 +1714,17 @@ class Output_data_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>
 				    address, true, true, false, false));
   }
 
+  void
+  add_local_relative(Sized_relobj<size, big_endian>* relobj,
+		     unsigned int local_sym_index, unsigned int type,
+		     Output_data* od, unsigned int shndx, Address address,
+		     bool use_plt_offset)
+  {
+    this->add(od, Output_reloc_type(relobj, local_sym_index, type, shndx,
+				    address, true, true, false,
+				    use_plt_offset));
+  }
+
   // Add a local relocation which does not use a symbol for the relocation,
   // but which gets its addend from a symbol.


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