diff options
| -rw-r--r-- | meta/recipes-devtools/binutils/binutils-2.25.1.inc | 1 | ||||
| -rw-r--r-- | meta/recipes-devtools/binutils/binutils/0016-This-patch-adds-IFUNC-support-for-arm-gold-backend.patch | 1004 | 
2 files changed, 1005 insertions, 0 deletions
| diff --git a/meta/recipes-devtools/binutils/binutils-2.25.1.inc b/meta/recipes-devtools/binutils/binutils-2.25.1.inc index bf29da16f5..dc23c4d9d9 100644 --- a/meta/recipes-devtools/binutils/binutils-2.25.1.inc +++ b/meta/recipes-devtools/binutils/binutils-2.25.1.inc @@ -35,6 +35,7 @@ SRC_URI = "\       file://0013-Fix-an-internal-error-in-do_print_to_mapfile-seen-wi.patch \       file://0014-gold-arm-Skip-pic-check-for-R_ARM_REL32.patch \       file://0015-Fix-dynamic-list-so-that-symbols-not-in-the-list-are.patch \ +     file://0016-This-patch-adds-IFUNC-support-for-arm-gold-backend.patch \       file://binutils-octeon3.patch \       file://add-thunderx-support-for-gas.patch \       " diff --git a/meta/recipes-devtools/binutils/binutils/0016-This-patch-adds-IFUNC-support-for-arm-gold-backend.patch b/meta/recipes-devtools/binutils/binutils/0016-This-patch-adds-IFUNC-support-for-arm-gold-backend.patch new file mode 100644 index 0000000000..dcac308bcc --- /dev/null +++ b/meta/recipes-devtools/binutils/binutils/0016-This-patch-adds-IFUNC-support-for-arm-gold-backend.patch @@ -0,0 +1,1004 @@ +From b780c9e06cabe6d8e301aaf46f33f116f3224021 Mon Sep 17 00:00:00 2001 +From: Han Shen <shenhan@google.com> +Date: Thu, 29 Jan 2015 10:00:46 -0800 +Subject: [PATCH] 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 +--- +Upstream-Status: Backport +Signed-off-by: Khem Raj <raj.khem@gmail.com> + + elfcpp/arm.h    |   5 +- + gold/aarch64.cc |   2 +- + gold/arm.cc     | 593 +++++++++++++++++++++++++++++++++++++++++++++++++++----- + gold/output.h   |  11 ++ + 4 files changed, 561 insertions(+), 50 deletions(-) + +diff --git a/elfcpp/arm.h b/elfcpp/arm.h +index 8c6b6bf..1c13dc9 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/aarch64.cc b/gold/aarch64.cc +index afb9024..7fbbdbd 100644 +--- a/gold/aarch64.cc ++++ b/gold/aarch64.cc +@@ -1226,7 +1226,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 6c472bb..8719cc9 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 +@@ -11083,8 +11574,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; + 	} +@@ -12187,8 +12677,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; +@@ -12225,8 +12720,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 ba0cdaa..599c2b7 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. +  +--  +2.7.0 + | 
