Implement IFUC support for chromeos arm gold linker.

We (chromeos toolchain team) are to switch default linker to gold
linker for arm, the origin gold does not support IFUNC for arm
backend, so what we firstly did was to implement this for arm gold.

Originally implemented for trunk, back-port it here. Since everything
is using bfd (except gtalk and chrome, which could not use any ifunc
symbols, otherwise, the binary would be corrupted) , the impact of
this patch is minimal.

The trunk patch here - https://sourceware.org/ml/binutils/2015-01/msg00258.html

BUG=chromium:457723
TEST=Build everything (without switch to gold by default), all perfv2,
bootperf, and basic gold arm ifunc unit tests, cbuildbot - chromiumos-sdk daisy-full
Change-Id: Icbd119208e78aeb15fcbab1db10127c1cb7db6cf
Reviewed-on: https://chromium-review.googlesource.com/248122
Reviewed-by: Luis Lozano <llozano@chromium.org>
Commit-Queue: Han Shen <shenhan@chromium.org>
Tested-by: Han Shen <shenhan@chromium.org>
diff --git a/elfcpp/arm.h b/elfcpp/arm.h
index ab0618a..142ecb7 100644
--- a/elfcpp/arm.h
+++ b/elfcpp/arm.h
@@ -192,11 +192,12 @@
   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/arm.cc b/gold/arm.cc
index 85cc76b..04ca5da 100644
--- a/gold/arm.cc
+++ b/gold/arm.cc
@@ -2119,8 +2119,8 @@
 
   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 @@
   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 @@
   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 @@
  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 @@
   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 @@
       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 @@
     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 @@
     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 @@
   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 @@
 				    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 @@
   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 @@
 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 @@
   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 @@
   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 @@
 // 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 @@
 
 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;
 
-  ++this->count_;
+  // We have 2 different types of plt entry here, normal and ifunc.
 
-  section_offset_type got_offset = this->got_plt_->current_data_size();
+  // 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());
+
+  ++(*entry_count);
+
+  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 @@
   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 @@
 
   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 @@
       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 @@
   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 @@
     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 @@
     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 @@
     }
 }
 
+
+// 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 @@
     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 @@
 	  // 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 @@
       && 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 @@
 	      }
 	    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 @@
 	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 @@
 		|| 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 @@
 	  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 @@
 	  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 @@
 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
@@ -11083,8 +11574,7 @@
       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;
 	}
@@ -12187,8 +12677,13 @@
 
  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;
@@ -12225,8 +12720,12 @@
 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 574d270..8985ee6 100644
--- a/gold/output.h
+++ b/gold/output.h
@@ -1715,6 +1715,17 @@
 				    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.