[binutils-gdb] PR21503, Gold doesn't create linker stub symbols on ppc64

Alan Modra amodra@sourceware.org
Tue May 23 12:32:00 GMT 2017


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

commit 590b87ffa386ea403e2cb61525c6aafef77097a2
Author: Alan Modra <amodra@gmail.com>
Date:   Mon May 22 21:31:34 2017 +0930

    PR21503, Gold doesn't create linker stub symbols on ppc64
    
    	PR 21503
    	* options.h: Add --emit-stub-syms option.
    	* powerpc.cc (object_id): New.
    	(Powerpc_relobj): Add uniq_ and accessor.  Sort variables for
    	better packing.
    	(Powerpc_dynobj): Sort variables for better packing.
    	(Target_powerpc::define_local): New function.
    	(Target_powerpc::group_sections): Pass stub table size to
    	Stub_table constructor.
    	(Target_powerpc::do_relax): Define stub and glink symbols.
    	(Stub_table): Add uniq_ variable, and id param to constructor.
    	(Stub_table::Plt_stub_ent): Add indx_ variable.
    	(Stub_table::Branch_stub_entries): Move typedef earlier.
    	(Stub_table::branch_stub_size): Replace "to" parameter with a
    	Branch_stub_entries iterator.
    	(Stub_table::add_long_branch_entry): Adjust to suit.
    	(Stub_table::add_plt_call_entry): Set indx_.
    	(Stub_table::define_stub_syms): New function.

Diff:
---
 gold/ChangeLog  |  21 +++++++
 gold/options.h  |   4 ++
 gold/powerpc.cc | 192 ++++++++++++++++++++++++++++++++++++++++++++++++--------
 3 files changed, 190 insertions(+), 27 deletions(-)

diff --git a/gold/ChangeLog b/gold/ChangeLog
index 42bd6c3..1f7d01e 100644
--- a/gold/ChangeLog
+++ b/gold/ChangeLog
@@ -1,3 +1,24 @@
+2017-05-23  Alan Modra  <amodra@gmail.com>
+
+	PR 21503
+	* options.h: Add --emit-stub-syms option.
+	* powerpc.cc (object_id): New.
+	(Powerpc_relobj): Add uniq_ and accessor.  Sort variables for
+	better packing.
+	(Powerpc_dynobj): Sort variables for better packing.
+	(Target_powerpc::define_local): New function.
+	(Target_powerpc::group_sections): Pass stub table size to
+	Stub_table constructor.
+	(Target_powerpc::do_relax): Define stub and glink symbols.
+	(Stub_table): Add uniq_ variable, and id param to constructor.
+	(Stub_table::Plt_stub_ent): Add indx_ variable.
+	(Stub_table::Branch_stub_entries): Move typedef earlier.
+	(Stub_table::branch_stub_size): Replace "to" parameter with a
+	Branch_stub_entries iterator.
+	(Stub_table::add_long_branch_entry): Adjust to suit.
+	(Stub_table::add_plt_call_entry): Set indx_.
+	(Stub_table::define_stub_syms): New function.
+
 2017-05-15  Eric Christopher <echristo@gmail.com>
 
         * layout.cc (Layout::segment_precedes): Add a case for testing
diff --git a/gold/options.h b/gold/options.h
index a8b1d46..202d4b0 100644
--- a/gold/options.h
+++ b/gold/options.h
@@ -814,6 +814,10 @@ class General_options
 
   // e
 
+  DEFINE_bool(emit_stub_syms, options::TWO_DASHES, '\0', true,
+	      N_("(PowerPC only) Label linker stubs with a symbol"),
+	      N_("(PowerPC only) Do not label linker stubs with a symbol"));
+
   DEFINE_string(entry, options::TWO_DASHES, 'e', NULL,
 		N_("Set program start address"), N_("ADDRESS"));
 
diff --git a/gold/powerpc.cc b/gold/powerpc.cc
index 1477a10..1f2bc9e 100644
--- a/gold/powerpc.cc
+++ b/gold/powerpc.cc
@@ -81,6 +81,9 @@ struct Stub_table_owner
 inline bool
 is_branch_reloc(unsigned int r_type);
 
+// Counter incremented on every Powerpc_relobj constructed.
+static uint32_t object_id = 0;
+
 template<int size, bool big_endian>
 class Powerpc_relobj : public Sized_relobj_file<size, big_endian>
 {
@@ -92,10 +95,10 @@ public:
   Powerpc_relobj(const std::string& name, Input_file* input_file, off_t offset,
 		 const typename elfcpp::Ehdr<size, big_endian>& ehdr)
     : Sized_relobj_file<size, big_endian>(name, input_file, offset, ehdr),
-      special_(0), relatoc_(0), toc_(0), no_toc_opt_(),
-      has_small_toc_reloc_(false), opd_valid_(false), opd_ent_(),
-      access_from_map_(), has14_(), stub_table_index_(),
-      e_flags_(ehdr.get_e_flags()), st_other_()
+      uniq_(object_id++), special_(0), relatoc_(0), toc_(0),
+      has_small_toc_reloc_(false), opd_valid_(false),
+      e_flags_(ehdr.get_e_flags()), no_toc_opt_(), opd_ent_(),
+      access_from_map_(), has14_(), stub_table_index_(), st_other_()
   {
     this->set_abiversion(0);
   }
@@ -357,6 +360,10 @@ public:
     this->stub_table_index_.clear();
   }
 
+  uint32_t
+  uniq() const
+  { return this->uniq_; }
+
   int
   abiversion() const
   { return this->e_flags_ & elfcpp::EF_PPC64_ABI; }
@@ -396,6 +403,9 @@ private:
   opd_ent_ndx(size_t off) const
   { return off >> 4;}
 
+  // Per object unique identifier
+  uint32_t uniq_;
+
   // For 32-bit the .got2 section shdnx, for 64-bit the .opd section shndx.
   unsigned int special_;
 
@@ -403,10 +413,6 @@ private:
   unsigned int relatoc_;
   unsigned int toc_;
 
-  // For 64-bit, an array with one entry per 64-bit word in the .toc
-  // section, set if accesses using that word cannot be optimised.
-  std::vector<bool> no_toc_opt_;
-
   // For 64-bit, whether this object uses small model relocs to access
   // the toc.
   bool has_small_toc_reloc_;
@@ -418,6 +424,13 @@ private:
   // access_from_map_.
   bool opd_valid_;
 
+  // Header e_flags
+  elfcpp::Elf_Word e_flags_;
+
+  // For 64-bit, an array with one entry per 64-bit word in the .toc
+  // section, set if accesses using that word cannot be optimised.
+  std::vector<bool> no_toc_opt_;
+
   // The first 8-byte word of an OPD entry gives the address of the
   // entry point of the function.  Relocatable object files have a
   // relocation on this word.  The following vector records the
@@ -435,9 +448,6 @@ private:
   // The stub table to use for a given input section.
   std::vector<unsigned int> stub_table_index_;
 
-  // Header e_flags
-  elfcpp::Elf_Word e_flags_;
-
   // ELF st_other field for local symbols.
   std::vector<unsigned char> st_other_;
 };
@@ -451,7 +461,7 @@ public:
   Powerpc_dynobj(const std::string& name, Input_file* input_file, off_t offset,
 		 const typename elfcpp::Ehdr<size, big_endian>& ehdr)
     : Sized_dynobj<size, big_endian>(name, input_file, offset, ehdr),
-      opd_shndx_(0), opd_ent_(), e_flags_(ehdr.get_e_flags())
+      opd_shndx_(0), e_flags_(ehdr.get_e_flags()), opd_ent_()
   {
     this->set_abiversion(0);
   }
@@ -548,14 +558,14 @@ private:
   unsigned int opd_shndx_;
   Address opd_address_;
 
+  // Header e_flags
+  elfcpp::Elf_Word e_flags_;
+
   // The first 8-byte word of an OPD entry gives the address of the
   // entry point of the function.  Records the section and offset
   // corresponding to the address.  Note that in dynamic objects,
   // offset is *not* relative to the section.
   std::vector<Opd_ent> opd_ent_;
-
-  // Header e_flags
-  elfcpp::Elf_Word e_flags_;
 };
 
 // Powerpc_copy_relocs class.  Needed to peek at dynamic relocs the
@@ -935,6 +945,23 @@ class Target_powerpc : public Sized_target<size, big_endian>
       }
   }
 
+  // Wrapper used after relax to define a local symbol in output data,
+  // from the end if value < 0.
+  void
+  define_local(Symbol_table* symtab, const char* name,
+	       Output_data* od, Address value, unsigned int symsize)
+  {
+    Symbol* sym
+      = symtab->define_in_output_data(name, NULL, Symbol_table::PREDEFINED,
+				      od, value, symsize, elfcpp::STT_NOTYPE,
+				      elfcpp::STB_LOCAL, elfcpp::STV_HIDDEN, 0,
+				      static_cast<Signed_address>(value) < 0,
+				      false);
+    // We are creating this symbol late, so need to fix up things
+    // done early in Layout::finalize.
+    sym->set_dynsym_index(-1U);
+  }
+
   bool
   plt_thread_safe() const
   { return this->plt_thread_safe_; }
@@ -2836,7 +2863,8 @@ Target_powerpc<size, big_endian>::group_sections(Layout* layout,
       if ((*t)->owner->is_input_section())
 	stub_table = new Stub_table<size, big_endian>(this,
 						      (*t)->output_section,
-						      (*t)->owner);
+						      (*t)->owner,
+						      this->stub_tables_.size());
       else if ((*t)->owner->is_relaxed_input_section())
 	stub_table = static_cast<Stub_table<size, big_endian>*>(
 			(*t)->owner->relaxed_input_section());
@@ -3232,6 +3260,36 @@ Target_powerpc<size, big_endian>::do_relax(int pass,
 	}
       this->brlt_section_->finalize_brlt_sizes();
     }
+
+  if (!again
+      && (parameters->options().user_set_emit_stub_syms()
+	  ? parameters->options().emit_stub_syms()
+	  : (size == 64
+	     || parameters->options().output_is_position_independent()
+	     || parameters->options().emit_relocs())))
+    {
+      for (typename Stub_tables::iterator p = this->stub_tables_.begin();
+	   p != this->stub_tables_.end();
+	   ++p)
+	(*p)->define_stub_syms(symtab);
+
+      if (this->glink_ != NULL)
+	{
+	  int stub_size = this->glink_->pltresolve_size;
+	  Address value = -stub_size;
+	  if (size == 64)
+	    {
+	      value = 8;
+	      stub_size -= 8;
+	    }
+	  this->define_local(symtab, "__glink_PLTresolve",
+			     this->glink_, value, stub_size);
+
+	  if (size != 64)
+	    this->define_local(symtab, "__glink", this->glink_, 0, 0);
+	}
+    }
+
   return again;
 }
 
@@ -3857,7 +3915,8 @@ class Stub_table : public Output_relaxed_input_section
 
   Stub_table(Target_powerpc<size, big_endian>* targ,
 	     Output_section* output_section,
-	     const Output_section::Input_section* owner)
+	     const Output_section::Input_section* owner,
+	     uint32_t id)
     : Output_relaxed_input_section(owner->relobj(), owner->shndx(),
 				   owner->relobj()
 				   ->section_addralign(owner->shndx())),
@@ -3865,7 +3924,7 @@ class Stub_table : public Output_relaxed_input_section
       orig_data_size_(owner->current_data_size()),
       plt_size_(0), last_plt_size_(0),
       branch_size_(0), last_branch_size_(0), min_size_threshold_(0),
-      eh_frame_added_(false), need_save_res_(false)
+      eh_frame_added_(false), need_save_res_(false), uniq_(id)
   {
     this->set_output_section(output_section);
 
@@ -3986,9 +4045,13 @@ class Stub_table : public Output_relaxed_input_section
   plt_size() const
   { return this->plt_size_; }
 
-  void set_min_size_threshold(Address min_size)
+  void
+  set_min_size_threshold(Address min_size)
   { this->min_size_threshold_ = min_size; }
 
+  void
+  define_stub_syms(Symbol_table*);
+
   bool
   size_update()
   {
@@ -4058,6 +4121,10 @@ class Stub_table : public Output_relaxed_input_section
   class Plt_stub_ent_hash;
   typedef Unordered_map<Plt_stub_ent, unsigned int,
 			Plt_stub_ent_hash> Plt_stub_entries;
+  class Branch_stub_ent;
+  class Branch_stub_ent_hash;
+  typedef Unordered_map<Branch_stub_ent, unsigned int,
+			Branch_stub_ent_hash> Branch_stub_entries;
 
   // Alignment of stub section.
   unsigned int
@@ -4126,11 +4193,10 @@ class Stub_table : public Output_relaxed_input_section
 
   // Return long branch stub size.
   unsigned int
-  branch_stub_size(Address to)
+  branch_stub_size(typename Branch_stub_entries::const_iterator p)
   {
-    Address loc
-      = this->stub_address() + this->last_plt_size_ + this->branch_size_;
-    if (to - loc + (1 << 25) < 2 << 25)
+    Address loc = this->stub_address() + this->last_plt_size_ + p->second;
+    if (p->first.dest_ - loc + (1 << 25) < 2 << 25)
       return 4;
     if (size == 64 || !parameters->options().output_is_position_independent())
       return 16;
@@ -4196,6 +4262,7 @@ class Stub_table : public Output_relaxed_input_section
     const Sized_relobj_file<size, big_endian>* object_;
     typename elfcpp::Elf_types<size>::Elf_Addr addend_;
     unsigned int locsym_;
+    unsigned int indx_;
   };
 
   class Plt_stub_ent_hash
@@ -4246,8 +4313,6 @@ class Stub_table : public Output_relaxed_input_section
   // Map sym/object/addend to stub offset.
   Plt_stub_entries plt_call_stubs_;
   // Map destination address to stub offset.
-  typedef Unordered_map<Branch_stub_ent, unsigned int,
-			Branch_stub_ent_hash> Branch_stub_entries;
   Branch_stub_entries long_branch_stubs_;
   // size of input section
   section_size_type orig_data_size_;
@@ -4265,6 +4330,8 @@ class Stub_table : public Output_relaxed_input_section
   // Set if this stub group needs a copy of out-of-line register
   // save/restore functions.
   bool need_save_res_;
+  // Per stub table unique identifier.
+  uint32_t uniq_;
 };
 
 // Add a plt call stub, if we do not already have one for this
@@ -4281,6 +4348,7 @@ Stub_table<size, big_endian>::add_plt_call_entry(
 {
   Plt_stub_ent ent(object, gsym, r_type, addend);
   unsigned int off = this->plt_size_;
+  ent.indx_ = this->plt_call_stubs_.size();
   std::pair<typename Plt_stub_entries::iterator, bool> p
     = this->plt_call_stubs_.insert(std::make_pair(ent, off));
   if (p.second)
@@ -4299,6 +4367,7 @@ Stub_table<size, big_endian>::add_plt_call_entry(
 {
   Plt_stub_ent ent(object, locsym_index, r_type, addend);
   unsigned int off = this->plt_size_;
+  ent.indx_ = this->plt_call_stubs_.size();
   std::pair<typename Plt_stub_entries::iterator, bool> p
     = this->plt_call_stubs_.insert(std::make_pair(ent, off));
   if (p.second)
@@ -4368,13 +4437,15 @@ Stub_table<size, big_endian>::add_long_branch_entry(
 {
   Branch_stub_ent ent(object, to, save_res);
   Address off = this->branch_size_;
-  if (this->long_branch_stubs_.insert(std::make_pair(ent, off)).second)
+  std::pair<typename Branch_stub_entries::iterator, bool> p
+    = this->long_branch_stubs_.insert(std::make_pair(ent, off));
+  if (p.second)
     {
       if (save_res)
 	this->need_save_res_ = true;
       else
 	{
-	  unsigned int stub_size = this->branch_stub_size(to);
+	  unsigned int stub_size = this->branch_stub_size(p.first);
 	  this->branch_size_ = off + stub_size;
 	  if (size == 64 && stub_size != 4)
 	    this->targ_->add_branch_lookup_table(to);
@@ -4555,6 +4626,73 @@ Output_data_glink<size, big_endian>::set_final_data_size()
   this->set_data_size(total);
 }
 
+// Define symbols on stubs, identifying the stub.
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::define_stub_syms(Symbol_table* symtab)
+{
+  if (!this->plt_call_stubs_.empty())
+    {
+      // The key for the plt call stub hash table includes addresses,
+      // therefore traversal order depends on those addresses, which
+      // can change between runs if gold is a PIE.  Unfortunately the
+      // output .symtab ordering depends on the order in which symbols
+      // are added to the linker symtab.  We want reproducible output
+      // so must sort the call stub symbols.
+      typedef typename Plt_stub_entries::const_iterator plt_iter;
+      std::vector<plt_iter> sorted;
+      sorted.resize(this->plt_call_stubs_.size());
+
+      for (plt_iter cs = this->plt_call_stubs_.begin();
+	   cs != this->plt_call_stubs_.end();
+	   ++cs)
+	sorted[cs->first.indx_] = cs;
+
+      for (unsigned int i = 0; i < this->plt_call_stubs_.size(); ++i)
+	{
+	  plt_iter cs = sorted[i];
+	  char add[10];
+	  add[0] = 0;
+	  if (cs->first.addend_ != 0)
+	    sprintf(add, "+%x", static_cast<uint32_t>(cs->first.addend_));
+	  char localname[18];
+	  const char *symname;
+	  if (cs->first.sym_ == NULL)
+	    {
+	      const Powerpc_relobj<size, big_endian>* ppcobj = static_cast
+		<const Powerpc_relobj<size, big_endian>*>(cs->first.object_);
+	      sprintf(localname, "%x:%x", ppcobj->uniq(), cs->first.locsym_);
+	      symname = localname;
+	    }
+	  else
+	    symname = cs->first.sym_->name();
+	  char* name = new char[8 + 10 + strlen(symname) + strlen(add) + 1];
+	  sprintf(name, "%08x.plt_call.%s%s", this->uniq_, symname, add);
+	  Address value = this->stub_address() - this->address() + cs->second;
+	  unsigned int stub_size = this->plt_call_size(cs);
+	  this->targ_->define_local(symtab, name, this, value, stub_size);
+	}
+    }
+
+  typedef typename Branch_stub_entries::const_iterator branch_iter;
+  for (branch_iter bs = this->long_branch_stubs_.begin();
+       bs != this->long_branch_stubs_.end();
+       ++bs)
+    {
+      if (bs->first.save_res_)
+	continue;
+
+      char* name = new char[8 + 13 + 16 + 1];
+      sprintf(name, "%08x.long_branch.%llx", this->uniq_,
+	      static_cast<unsigned long long>(bs->first.dest_));
+      Address value = (this->stub_address() - this->address()
+		       + this->plt_size_ + bs->second);
+      unsigned int stub_size = this->branch_stub_size(bs);
+      this->targ_->define_local(symtab, name, this, value, stub_size);
+    }
+}
+
 // Write out plt and long branch stub code.
 
 template<int size, bool big_endian>



More information about the Binutils-cvs mailing list