[OE-core] [PATCH 71/73] binutils: Fix gold linking errors due to unresolved R_ARM_MOVW_ABS_NC

Khem Raj raj.khem at gmail.com
Sun Jan 17 11:36:44 UTC 2016


This issue has been seen in multiple times e.g.

http://patchwork.openembedded.org/patch/103083/

https://www.mail-archive.com/openembedded-core@lists.openembedded.org/msg72513.html

Signed-off-by: Khem Raj <raj.khem at gmail.com>
---
 meta/recipes-devtools/binutils/binutils-2.25.1.inc |    1 +
 ...h-adds-IFUNC-support-for-arm-gold-backend.patch | 1004 ++++++++++++++++++++
 2 files changed, 1005 insertions(+)
 create mode 100644 meta/recipes-devtools/binutils/binutils/0016-This-patch-adds-IFUNC-support-for-arm-gold-backend.patch

diff --git a/meta/recipes-devtools/binutils/binutils-2.25.1.inc b/meta/recipes-devtools/binutils/binutils-2.25.1.inc
index bf29da1..dc23c4d 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 0000000..dcac308
--- /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 at 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 at 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
+
-- 
2.7.0




More information about the Openembedded-core mailing list