PR21503, Gold doesn't create linker stub symbols on ppc64
authorAlan Modra <amodra@gmail.com>
Mon, 22 May 2017 12:01:34 +0000 (21:31 +0930)
committerAlan Modra <amodra@gmail.com>
Tue, 23 May 2017 12:19:33 +0000 (21:49 +0930)
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.

gold/ChangeLog
gold/options.h
gold/powerpc.cc

index 42bd6c30c1fddffab2456273c546f83c73634501..1f7d01e50a74c5278c41fa57ba1d72a7beaa8de9 100644 (file)
@@ -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
index a8b1d46aa1093e742f385eb62ce76d9666bc1024..202d4b0e8d20249a0eef1d3f9a63f19ca41ed038 100644 (file)
@@ -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"));
 
index 1477a10d671d3f3593d4364e7616e1f01995bbb7..1f2bc9ede798de03a647c91cd8b10e6268c0a0b6 100644 (file)
@@ -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>