* powerpc.cc (class Powerpc_relobj): Move some member functions.
[binutils-gdb.git] / gold / powerpc.cc
index 9c42c3ca6decfe8e557f35a9daed8ff9a81d8606..a978e7af18487a96430e306725945a1b099563b3 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <algorithm>
 #include "elfcpp.h"
+#include "dwarf.h"
 #include "parameters.h"
 #include "reloc.h"
 #include "powerpc.h"
@@ -71,7 +72,8 @@ 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), opd_valid_(false), opd_ent_(), access_from_map_()
+      special_(0), has_small_toc_reloc_(false), opd_valid_(false),
+      opd_ent_(), access_from_map_(), has14_(), stub_table_()
   { }
 
   ~Powerpc_relobj()
@@ -145,43 +147,6 @@ public:
     this->opd_ent_[ndx].discard = true;
   }
 
-  Access_from*
-  access_from_map()
-  { return &this->access_from_map_; }
-
-  // Add a reference from SRC_OBJ, SRC_INDX to this object's .opd
-  // section at DST_OFF.
-  void
-  add_reference(Object* src_obj,
-               unsigned int src_indx,
-               typename elfcpp::Elf_types<size>::Elf_Addr dst_off)
-  {
-    Section_id src_id(src_obj, src_indx);
-    this->access_from_map_[dst_off].insert(src_id);
-  }
-
-  // Add a reference to the code section specified by the .opd entry
-  // at DST_OFF
-  void
-  add_gc_mark(typename elfcpp::Elf_types<size>::Elf_Addr dst_off)
-  {
-    size_t ndx = this->opd_ent_ndx(dst_off);
-    if (ndx >= this->opd_ent_.size())
-      this->opd_ent_.resize(ndx + 1);
-    this->opd_ent_[ndx].gc_mark = true;
-  }
-
-  void
-  process_gc_mark(Symbol_table* symtab)
-  {
-    for (size_t i = 0; i < this->opd_ent_.size(); i++)
-      if (this->opd_ent_[i].gc_mark)
-       {
-         unsigned int shndx = this->opd_ent_[i].shndx;
-         symtab->gc()->worklist().push(Section_id(this, shndx));
-       }
-  }
-
   bool
   opd_valid() const
   { return this->opd_valid_; }
@@ -220,12 +185,57 @@ public:
     return true;
   }
 
+  Access_from*
+  access_from_map()
+  { return &this->access_from_map_; }
+
+  // Add a reference from SRC_OBJ, SRC_INDX to this object's .opd
+  // section at DST_OFF.
+  void
+  add_reference(Object* src_obj,
+               unsigned int src_indx,
+               typename elfcpp::Elf_types<size>::Elf_Addr dst_off)
+  {
+    Section_id src_id(src_obj, src_indx);
+    this->access_from_map_[dst_off].insert(src_id);
+  }
+
+  // Add a reference to the code section specified by the .opd entry
+  // at DST_OFF
+  void
+  add_gc_mark(typename elfcpp::Elf_types<size>::Elf_Addr dst_off)
+  {
+    size_t ndx = this->opd_ent_ndx(dst_off);
+    if (ndx >= this->opd_ent_.size())
+      this->opd_ent_.resize(ndx + 1);
+    this->opd_ent_[ndx].gc_mark = true;
+  }
+
+  void
+  process_gc_mark(Symbol_table* symtab)
+  {
+    for (size_t i = 0; i < this->opd_ent_.size(); i++)
+      if (this->opd_ent_[i].gc_mark)
+       {
+         unsigned int shndx = this->opd_ent_[i].shndx;
+         symtab->gc()->worklist().push(Section_id(this, shndx));
+       }
+  }
+
   // Return offset in output GOT section that this object will use
   // as a TOC pointer.  Won't be just a constant with multi-toc support.
   Address
   toc_base_offset() const
   { return 0x8000; }
 
+  void
+  set_has_small_toc_reloc()
+  { has_small_toc_reloc_ = true; }
+
+  bool
+  has_small_toc_reloc() const
+  { return has_small_toc_reloc_; }
+
   void
   set_has_14bit_branch(unsigned int shndx)
   {
@@ -280,6 +290,10 @@ private:
   // For 32-bit the .got2 section shdnx, for 64-bit the .opd section shndx.
   unsigned int special_;
 
+  // For 64-bit, whether this object uses small model relocs to access
+  // the toc.
+  bool has_small_toc_reloc_;
+
   // Set at the start of gc_process_relocs, when we know opd_ent_
   // vector is valid.  The flag could be made atomic and set in
   // do_read_relocs with memory_order_release and then tested with
@@ -323,7 +337,8 @@ class Target_powerpc : public Sized_target<size, big_endian>
       got_(NULL), plt_(NULL), iplt_(NULL), brlt_section_(NULL),
       glink_(NULL), rela_dyn_(NULL), copy_relocs_(elfcpp::R_POWERPC_COPY),
       dynbss_(NULL), tlsld_got_offset_(-1U),
-      stub_tables_(), branch_lookup_table_(), branch_info_()
+      stub_tables_(), branch_lookup_table_(), branch_info_(),
+      plt_thread_safe_(false)
   {
   }
 
@@ -380,6 +395,10 @@ class Target_powerpc : public Sized_target<size, big_endian>
   bool
   do_relax(int, const Input_objects*, Symbol_table*, Layout*, const Task*);
 
+  void
+  do_plt_fde_location(const Output_data*, unsigned char*,
+                     uint64_t*, off_t*) const;
+
   // Stash info about branches, for stub generation.
   void
   push_branch(Powerpc_relobj<size, big_endian>* ppc_object,
@@ -512,6 +531,9 @@ class Target_powerpc : public Sized_target<size, big_endian>
     return this->glink_;
   }
 
+  bool has_glink() const
+  { return this->glink_ != NULL; }
+
   // Get the GOT section.
   const Output_data_got_powerpc<size, big_endian>*
   got_section() const
@@ -603,16 +625,108 @@ class Target_powerpc : public Sized_target<size, big_endian>
       }
   }
 
+  bool
+  plt_thread_safe() const
+  { return this->plt_thread_safe_; }
+
  private:
 
+  class Track_tls
+  {
+  public:
+    enum Tls_get_addr
+    {
+      NOT_EXPECTED = 0,
+      EXPECTED = 1,
+      SKIP = 2,
+      NORMAL = 3
+    };
+
+    Track_tls()
+      : tls_get_addr_(NOT_EXPECTED),
+       relinfo_(NULL), relnum_(0), r_offset_(0)
+    { }
+
+    ~Track_tls()
+    {
+      if (this->tls_get_addr_ != NOT_EXPECTED)
+       this->missing();
+    }
+
+    void
+    missing(void)
+    {
+      if (this->relinfo_ != NULL)
+       gold_error_at_location(this->relinfo_, this->relnum_, this->r_offset_,
+                              _("missing expected __tls_get_addr call"));
+    }
+
+    void
+    expect_tls_get_addr_call(
+       const Relocate_info<size, big_endian>* relinfo,
+       size_t relnum,
+       Address r_offset)
+    {
+      this->tls_get_addr_ = EXPECTED;
+      this->relinfo_ = relinfo;
+      this->relnum_ = relnum;
+      this->r_offset_ = r_offset;
+    }
+
+    void
+    expect_tls_get_addr_call()
+    { this->tls_get_addr_ = EXPECTED; }
+
+    void
+    skip_next_tls_get_addr_call()
+    {this->tls_get_addr_ = SKIP; }
+
+    Tls_get_addr
+    maybe_skip_tls_get_addr_call(unsigned int r_type, const Symbol* gsym)
+    {
+      bool is_tls_call = ((r_type == elfcpp::R_POWERPC_REL24
+                          || r_type == elfcpp::R_PPC_PLTREL24)
+                         && gsym != NULL
+                         && strcmp(gsym->name(), "__tls_get_addr") == 0);
+      Tls_get_addr last_tls = this->tls_get_addr_;
+      this->tls_get_addr_ = NOT_EXPECTED;
+      if (is_tls_call && last_tls != EXPECTED)
+       return last_tls;
+      else if (!is_tls_call && last_tls != NOT_EXPECTED)
+       {
+         this->missing();
+         return EXPECTED;
+       }
+      return NORMAL;
+    }
+
+  private:
+    // What we're up to regarding calls to __tls_get_addr.
+    // On powerpc, the branch and link insn making a call to
+    // __tls_get_addr is marked with a relocation, R_PPC64_TLSGD,
+    // R_PPC64_TLSLD, R_PPC_TLSGD or R_PPC_TLSLD, in addition to the
+    // usual R_POWERPC_REL24 or R_PPC_PLTREL25 relocation on a call.
+    // The marker relocation always comes first, and has the same
+    // symbol as the reloc on the insn setting up the __tls_get_addr
+    // argument.  This ties the arg setup insn with the call insn,
+    // allowing ld to safely optimize away the call.  We check that
+    // every call to __tls_get_addr has a marker relocation, and that
+    // every marker relocation is on a call to __tls_get_addr.
+    Tls_get_addr tls_get_addr_;
+    // Info about the last reloc for error message.
+    const Relocate_info<size, big_endian>* relinfo_;
+    size_t relnum_;
+    Address r_offset_;
+  };
+
   // The class which scans relocations.
-  class Scan
+  class Scan : protected Track_tls
   {
   public:
     typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
 
     Scan()
-      : issued_non_pic_error_(false)
+      : Track_tls(), issued_non_pic_error_(false)
     { }
 
     static inline int
@@ -682,38 +796,23 @@ class Target_powerpc : public Sized_target<size, big_endian>
   };
 
   Address
-  symval_for_branch(Address value, const Sized_symbol<size>* gsym,
+  symval_for_branch(const Symbol_table* symtab, Address value,
+                   const Sized_symbol<size>* gsym,
                    Powerpc_relobj<size, big_endian>* object,
                    unsigned int *dest_shndx);
 
   // The class which implements relocation.
-  class Relocate
+  class Relocate : protected Track_tls
   {
    public:
     // Use 'at' branch hints when true, 'y' when false.
     // FIXME maybe: set this with an option.
     static const bool is_isa_v2 = true;
 
-    enum skip_tls
-    {
-      CALL_NOT_EXPECTED = 0,
-      CALL_EXPECTED = 1,
-      CALL_SKIP = 2
-    };
-
     Relocate()
-      : call_tls_get_addr_(CALL_NOT_EXPECTED)
+      : Track_tls()
     { }
 
-    ~Relocate()
-    {
-      if (this->call_tls_get_addr_ != CALL_NOT_EXPECTED)
-       {
-         // FIXME: This needs to specify the location somehow.
-         gold_error(_("missing expected __tls_get_addr call"));
-       }
-    }
-
     // Do a relocation.  Return false if the caller should not issue
     // any warnings about this relocation.
     inline bool
@@ -725,10 +824,6 @@ class Target_powerpc : public Sized_target<size, big_endian>
             unsigned char*,
             typename elfcpp::Elf_types<size>::Elf_Addr,
             section_size_type);
-
-    // This is set if we should skip the next reloc, which should be a
-    // call to __tls_get_addr.
-    enum skip_tls call_tls_get_addr_;
   };
 
   class Relocate_comdat_behavior
@@ -944,6 +1039,8 @@ class Target_powerpc : public Sized_target<size, big_endian>
 
   typedef std::vector<Branch_info> Branches;
   Branches branch_info_;
+
+  bool plt_thread_safe_;
 };
 
 template<>
@@ -1184,13 +1281,13 @@ use_plt_offset(const Symbol* gsym, int flags)
   if (gsym->is_from_dynobj())
     return true;
 
-  // If we are generating a shared object, and gsym symbol is
+  // If we are generating a shared object, and this symbol is
   // undefined or preemptible, we need to use the PLT entry.
   if (parameters->options().shared()
       && (gsym->is_undefined() || gsym->is_preemptible()))
     return true;
 
-  // If gsym is a call to a weak undefined symbol, we need to use
+  // If this is a call to a weak undefined symbol, we need to use
   // the PLT entry; the symbol may be defined by a library loaded
   // at runtime.
   if ((flags & Symbol::FUNCTION_CALL) && gsym->is_weak_undefined())
@@ -1633,7 +1730,7 @@ public:
       symtab_(symtab), layout_(layout),
       header_ent_cnt_(size == 32 ? 3 : 1),
       header_index_(size == 32 ? 0x2000 : 0)
-  {}
+  { }
 
   class Got_entry;
 
@@ -2060,13 +2157,15 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
            return;
          to = symval.value(this->object_, 0);
        }
+      to += this->addend_;
       if (stub_table == NULL)
        stub_table = this->object_->stub_table(this->shndx_);
       gold_assert(stub_table != NULL);
       if (size == 64 && is_branch_reloc(this->r_type_))
        {
          unsigned int dest_shndx;
-         to = stub_table->targ()->symval_for_branch(to, gsym, this->object_,
+         to = stub_table->targ()->symval_for_branch(symtab, to, gsym,
+                                                    this->object_,
                                                     &dest_shndx);
        }
       Address delta = to - from;
@@ -2089,17 +2188,48 @@ Target_powerpc<size, big_endian>::do_relax(int pass,
 {
   unsigned int prev_brlt_size = 0;
   if (pass == 1)
-    this->group_sections(layout, task);
-  else
     {
-      prev_brlt_size = this->branch_lookup_table_.size();
-      this->branch_lookup_table_.clear();
-      for (typename Stub_tables::iterator p = this->stub_tables_.begin();
-          p != this->stub_tables_.end();
-          ++p)
+      bool thread_safe = parameters->options().plt_thread_safe();
+      if (size == 64 && !parameters->options().user_set_plt_thread_safe())
        {
-         (*p)->clear_long_branch_stubs();
+         static const char* const thread_starter[] =
+           {
+             "pthread_create",
+             /* libstdc++ */
+             "_ZNSt6thread15_M_start_threadESt10shared_ptrINS_10_Impl_baseEE",
+             /* librt */
+             "aio_init", "aio_read", "aio_write", "aio_fsync", "lio_listio",
+             "mq_notify", "create_timer",
+             /* libanl */
+             "getaddrinfo_a",
+             /* libgomp */
+             "GOMP_parallel_start",
+             "GOMP_parallel_loop_static_start",
+             "GOMP_parallel_loop_dynamic_start",
+             "GOMP_parallel_loop_guided_start",
+             "GOMP_parallel_loop_runtime_start",
+             "GOMP_parallel_sections_start", 
+           };
+
+         if (parameters->options().shared())
+           thread_safe = true;
+         else
+           {
+             for (unsigned int i = 0;
+                  i < sizeof(thread_starter) / sizeof(thread_starter[0]);
+                  i++)
+               {
+                 Symbol* sym = symtab->lookup(thread_starter[i], NULL);
+                 thread_safe = (sym != NULL
+                                && sym->in_reg()
+                                && sym->in_real_elf());
+                 if (thread_safe)
+                   break;
+               }
+           }
        }
+      this->plt_thread_safe_ = thread_safe;
+      this->group_sections(layout, task);
     }
 
   // We need address of stub tables valid for make_stub.
@@ -2115,6 +2245,20 @@ Target_powerpc<size, big_endian>::do_relax(int pass,
       (*p)->set_address_and_size(os, off);
     }
 
+  if (pass != 1)
+    {
+      // Clear plt call stubs, long branch stubs and branch lookup table.
+      prev_brlt_size = this->branch_lookup_table_.size();
+      this->branch_lookup_table_.clear();
+      for (typename Stub_tables::iterator p = this->stub_tables_.begin();
+          p != this->stub_tables_.end();
+          ++p)
+       {
+         (*p)->clear_stubs();
+       }
+    }
+
+  // Build all the stubs.
   Stub_table<size, big_endian>* ifunc_stub_table
     = this->stub_tables_.size() == 0 ? NULL : this->stub_tables_[0];
   Stub_table<size, big_endian>* one_stub_table
@@ -2126,6 +2270,7 @@ Target_powerpc<size, big_endian>::do_relax(int pass,
       b->make_stub(one_stub_table, ifunc_stub_table, symtab);
     }
 
+  // Did anything change size?
   unsigned int num_huge_branches = this->branch_lookup_table_.size();
   bool again = num_huge_branches != prev_brlt_size;
   if (size == 64 && num_huge_branches != 0)
@@ -2142,10 +2287,14 @@ Target_powerpc<size, big_endian>::do_relax(int pass,
       if ((*p)->size_update())
        {
          again = true;
+         (*p)->add_eh_frame(layout);
          os_need_update.insert((*p)->output_section());
        }
     }
 
+  // Set output section offsets for all input sections in an output
+  // section that just changed size.  Anything past the stubs will
+  // need updating.
   for (typename Output_sections::iterator p = os_need_update.begin();
        p != os_need_update.end();
        p++)
@@ -2194,6 +2343,54 @@ Target_powerpc<size, big_endian>::do_relax(int pass,
   return again;
 }
 
+template<int size, bool big_endian>
+void
+Target_powerpc<size, big_endian>::do_plt_fde_location(const Output_data* plt,
+                                                     unsigned char* oview,
+                                                     uint64_t* paddress,
+                                                     off_t* plen) const
+{
+  uint64_t address = plt->address();
+  off_t len = plt->data_size();
+
+  if (plt == this->glink_)
+    {
+      // See Output_data_glink::do_write() for glink contents.
+      if (size == 64)
+       {
+         // There is one word before __glink_PLTresolve
+         address += 8;
+         len -= 8;
+       }
+      else if (parameters->options().output_is_position_independent())
+       {
+         // There are two FDEs for a position independent glink.
+         // The first covers the branch table, the second
+         // __glink_PLTresolve at the end of glink.
+         off_t resolve_size = this->glink_->pltresolve_size;
+         if (oview[9] == 0)
+           len -= resolve_size;
+         else
+           {
+             address += len - resolve_size;
+             len = resolve_size;
+           }
+       }
+    }
+  else
+    {
+      // Must be a stub table.
+      const Stub_table<size, big_endian>* stub_table
+       = static_cast<const Stub_table<size, big_endian>*>(plt);
+      uint64_t stub_address = stub_table->stub_address();
+      len -= stub_address - address;
+      address = stub_address;
+    }
+
+  *paddress = address;
+  *plen = len;
+}
+
 // A class to handle the PLT data.
 
 template<int size, bool big_endian>
@@ -2341,10 +2538,12 @@ Output_data_plt_powerpc<size, big_endian>::add_local_ifunc_entry(
 }
 
 static const uint32_t add_0_11_11      = 0x7c0b5a14;
+static const uint32_t add_2_2_11       = 0x7c425a14;
 static const uint32_t add_3_3_2                = 0x7c631214;
 static const uint32_t add_3_3_13       = 0x7c636a14;
 static const uint32_t add_11_0_11      = 0x7d605a14;
 static const uint32_t add_12_2_11      = 0x7d825a14;
+static const uint32_t add_12_12_11     = 0x7d8c5a14;
 static const uint32_t addi_11_11       = 0x396b0000;
 static const uint32_t addi_12_12       = 0x398c0000;
 static const uint32_t addi_2_2         = 0x38420000;
@@ -2363,6 +2562,8 @@ static const uint32_t bcl_20_31           = 0x429f0005;
 static const uint32_t bctr             = 0x4e800420;
 static const uint32_t blr              = 0x4e800020;
 static const uint32_t blrl             = 0x4e800021;
+static const uint32_t bnectr_p4                = 0x4ce20420;
+static const uint32_t cmpldi_2_0       = 0x28220000;
 static const uint32_t cror_15_15_15    = 0x4def7b82;
 static const uint32_t cror_31_31_31    = 0x4ffffb82;
 static const uint32_t ld_0_1           = 0xe8010000;
@@ -2401,6 +2602,7 @@ static const uint32_t std_2_1             = 0xf8410000;
 static const uint32_t stfd_0_1         = 0xd8010000;
 static const uint32_t stvx_0_12_0      = 0x7c0c01ce;
 static const uint32_t sub_11_11_12     = 0x7d6c5850;
+static const uint32_t xor_11_11_11     = 0x7d6b5a78;
 
 // Write out the PLT.
 
@@ -2613,6 +2815,85 @@ Output_data_brlt_powerpc<size, big_endian>::do_write(Output_file* of)
     }
 }
 
+static inline uint32_t
+l(uint32_t a)
+{
+  return a & 0xffff;
+}
+
+static inline uint32_t
+hi(uint32_t a)
+{
+  return l(a >> 16);
+}
+
+static inline uint32_t
+ha(uint32_t a)
+{
+  return hi(a + 0x8000);
+}
+
+template<int size>
+struct Eh_cie
+{
+  static const unsigned char eh_frame_cie[12];
+};
+
+template<int size>
+const unsigned char Eh_cie<size>::eh_frame_cie[] =
+{
+  1,                                   // CIE version.
+  'z', 'R', 0,                         // Augmentation string.
+  4,                                   // Code alignment.
+  0x80 - size / 8 ,                    // Data alignment.
+  65,                                  // RA reg.
+  1,                                   // Augmentation size.
+  (elfcpp::DW_EH_PE_pcrel
+   | elfcpp::DW_EH_PE_sdata4),         // FDE encoding.
+  elfcpp::DW_CFA_def_cfa, 1, 0         // def_cfa: r1 offset 0.
+};
+
+// Describe __glink_PLTresolve use of LR, 64-bit version.
+static const unsigned char glink_eh_frame_fde_64[] =
+{
+  0, 0, 0, 0,                          // Replaced with offset to .glink.
+  0, 0, 0, 0,                          // Replaced with size of .glink.
+  0,                                   // Augmentation size.
+  elfcpp::DW_CFA_advance_loc + 1,
+  elfcpp::DW_CFA_register, 65, 12,
+  elfcpp::DW_CFA_advance_loc + 4,
+  elfcpp::DW_CFA_restore_extended, 65
+};
+
+// Describe __glink_PLTresolve use of LR, 32-bit version.
+static const unsigned char glink_eh_frame_fde_32[] =
+{
+  0, 0, 0, 0,                          // Replaced with offset to .glink.
+  0, 0, 0, 0,                          // Replaced with size of .glink.
+  0,                                   // Augmentation size.
+  elfcpp::DW_CFA_advance_loc + 2,
+  elfcpp::DW_CFA_register, 65, 0,
+  elfcpp::DW_CFA_advance_loc + 4,
+  elfcpp::DW_CFA_restore_extended, 65
+};
+
+static const unsigned char default_fde[] =
+{
+  0, 0, 0, 0,                          // Replaced with offset to stubs.
+  0, 0, 0, 0,                          // Replaced with size of stubs.
+  0,                                   // Augmentation size.
+  elfcpp::DW_CFA_nop,                  // Pad.
+  elfcpp::DW_CFA_nop,
+  elfcpp::DW_CFA_nop
+};
+
+template<bool big_endian>
+static inline void
+write_insn(unsigned char* p, uint32_t v)
+{
+  elfcpp::Swap<32, big_endian>::writeval(p, v);
+}
+
 // Stub_table holds information about plt and long branch stubs.
 // Stubs are built in an area following some input section determined
 // by group_sections().  This input section is converted to a relaxed
@@ -2628,7 +2909,8 @@ class Stub_table : public Output_relaxed_input_section
   Stub_table(Target_powerpc<size, big_endian>* targ)
     : Output_relaxed_input_section(NULL, 0, 0),
       targ_(targ), plt_call_stubs_(), long_branch_stubs_(),
-      orig_data_size_(0), plt_size_(0), branch_size_(0), prev_size_(0)
+      orig_data_size_(0), plt_size_(0), last_plt_size_(0),
+      branch_size_(0), last_branch_size_(0), eh_frame_added_(false)
   { }
 
   // Delayed Output_relaxed_input_section init.
@@ -2673,11 +2955,14 @@ class Stub_table : public Output_relaxed_input_section
   add_long_branch_entry(const Powerpc_relobj<size, big_endian>*, Address);
 
   Address
-  find_long_branch_entry(const Powerpc_relobj<size, big_endian>*, Address);
+  find_long_branch_entry(const Powerpc_relobj<size, big_endian>*,
+                        Address) const;
 
   void
-  clear_long_branch_stubs()
+  clear_stubs()
   {
+    this->plt_call_stubs_.clear();
+    this->plt_size_ = 0;
     this->long_branch_stubs_.clear();
     this->branch_size_ = 0;
   }
@@ -2700,14 +2985,14 @@ class Stub_table : public Output_relaxed_input_section
   }
 
   Address
-  stub_address()
+  stub_address() const
   {
     return align_address(this->address() + this->orig_data_size_,
                         this->stub_align());
   }
 
   Address
-  stub_offset()
+  stub_offset() const
   {
     return align_address(this->offset() + this->orig_data_size_,
                         this->stub_align());
@@ -2738,42 +3023,124 @@ class Stub_table : public Output_relaxed_input_section
        // a suitably aligned address.
        os->checkpoint_set_addralign(this->stub_align());
       }
-    if (this->prev_size_ != this->plt_size_ + this->branch_size_)
+    if (this->last_plt_size_ != this->plt_size_
+       || this->last_branch_size_ != this->branch_size_)
       {
-       this->prev_size_ = this->plt_size_ + this->branch_size_;
+       this->last_plt_size_ = this->plt_size_;
+       this->last_branch_size_ = this->branch_size_;
        return true;
       }
     return false;
   }
 
-  section_size_type
-  prev_size() const
-  { return this->prev_size_; }
-
+  // Add .eh_frame info for this stub section.  Unlike other linker
+  // generated .eh_frame this is added late in the link, because we
+  // only want the .eh_frame info if this particular stub section is
+  // non-empty.
   void
-  set_prev_size(section_size_type val)
-  { this->prev_size_ = val; }
+  add_eh_frame(Layout* layout)
+  {
+    if (!this->eh_frame_added_)
+      {
+       if (!parameters->options().ld_generated_unwind_info())
+         return;
+
+       // Since we add stub .eh_frame info late, it must be placed
+       // after all other linker generated .eh_frame info so that
+       // merge mapping need not be updated for input sections.
+       // There is no provision to use a different CIE to that used
+       // by .glink.
+       if (!this->targ_->has_glink())
+         return;
+
+       layout->add_eh_frame_for_plt(this,
+                                    Eh_cie<size>::eh_frame_cie,
+                                    sizeof (Eh_cie<size>::eh_frame_cie),
+                                    default_fde,
+                                    sizeof (default_fde));
+       this->eh_frame_added_ = true;
+      }
+  }
 
   Target_powerpc<size, big_endian>*
   targ() const
   { return targ_; }
 
  private:
+  class Plt_stub_ent;
+  class Plt_stub_ent_hash;
+  typedef Unordered_map<Plt_stub_ent, unsigned int,
+                       Plt_stub_ent_hash> Plt_stub_entries;
+
+  // Alignment of stub section.
   unsigned int
-  stub_align()
-  { return size == 32 ? 16 : 32; }
+  stub_align() const
+  {
+    if (size == 32)
+      return 16;
+    unsigned int min_align = 32;
+    unsigned int user_align = 1 << parameters->options().plt_align();
+    return std::max(user_align, min_align);
+  }
 
-  // We keep plt stubs aligned, so no fancy sizing.
+  // Return the plt offset for the given call stub.
+  Address
+  plt_off(typename Plt_stub_entries::const_iterator p, bool* is_iplt) const
+  {
+    const Symbol* gsym = p->first.sym_;
+    if (gsym != NULL)
+      {
+       *is_iplt = (gsym->type() == elfcpp::STT_GNU_IFUNC
+                   && gsym->can_use_relative_reloc(false));
+       return gsym->plt_offset();
+      }
+    else
+      {
+       *is_iplt = true;
+       const Sized_relobj_file<size, big_endian>* relobj = p->first.object_;
+       unsigned int local_sym_index = p->first.locsym_;
+       return relobj->local_plt_offset(local_sym_index);
+      }
+  }
+
+  // Size of a given plt call stub.
   unsigned int
-  plt_call_size() const
-  { return size == 32 ? 16 : 32; }
+  plt_call_size(typename Plt_stub_entries::const_iterator p) const
+  {
+    if (size == 32)
+      return 16;
+
+    bool is_iplt;
+    Address plt_addr = this->plt_off(p, &is_iplt);
+    if (is_iplt)
+      plt_addr += this->targ_->iplt_section()->address();
+    else
+      plt_addr += this->targ_->plt_section()->address();
+    Address got_addr = this->targ_->got_section()->output_section()->address();
+    const Powerpc_relobj<size, big_endian>* ppcobj = static_cast
+      <const Powerpc_relobj<size, big_endian>*>(p->first.object_);
+    got_addr += ppcobj->toc_base_offset();
+    Address off = plt_addr - got_addr;
+    bool static_chain = parameters->options().plt_static_chain();
+    bool thread_safe = this->targ_->plt_thread_safe();
+    unsigned int bytes = (4 * 5
+                         + 4 * static_chain
+                         + 8 * thread_safe
+                         + 4 * (ha(off) != 0)
+                         + 4 * (ha(off + 8 + 8 * static_chain) != ha(off)));
+    unsigned int align = 1 << parameters->options().plt_align();
+    if (align > 1)
+      bytes = (bytes + align - 1) & -align;
+    return bytes;
+  }
 
   // Return long branch stub size.
   unsigned int
   branch_stub_size(Address to)
   {
-    Address loc = this->stub_address() + this->plt_size_ + this->branch_size_;
-    if (loc - to + (1 << 25) < 2 << 25)
+    Address loc
+      = this->stub_address() + this->last_plt_size_ + this->branch_size_;
+    if (to - loc + (1 << 25) < 2 << 25)
       return 4;
     if (size == 64 || !parameters->options().output_is_position_independent())
       return 16;
@@ -2885,8 +3252,6 @@ class Stub_table : public Output_relaxed_input_section
   // In a sane world this would be a global.
   Target_powerpc<size, big_endian>* targ_;
   // Map sym/object/addend to stub offset.
-  typedef Unordered_map<Plt_stub_ent, unsigned int,
-                       Plt_stub_ent_hash> Plt_stub_entries;
   Plt_stub_entries plt_call_stubs_;
   // Map destination address to stub offset.
   typedef Unordered_map<Branch_stub_ent, unsigned int,
@@ -2895,7 +3260,9 @@ class Stub_table : public Output_relaxed_input_section
   // size of input section
   section_size_type orig_data_size_;
   // size of stubs
-  section_size_type plt_size_, branch_size_, prev_size_;
+  section_size_type plt_size_, last_plt_size_, branch_size_, last_branch_size_;
+  // Whether .eh_frame info has been created for this stub section.
+  bool eh_frame_added_;
 };
 
 // Make a new stub table, and record.
@@ -2943,8 +3310,10 @@ Stub_table<size, big_endian>::add_plt_call_entry(
 {
   Plt_stub_ent ent(object, gsym, r_type, addend);
   Address off = this->plt_size_;
-  if (this->plt_call_stubs_.insert(std::make_pair(ent, off)).second)
-    this->plt_size_ = off + this->plt_call_size();
+  std::pair<typename Plt_stub_entries::iterator, bool> p
+    = this->plt_call_stubs_.insert(std::make_pair(ent, off));
+  if (p.second)
+    this->plt_size_ = off + this->plt_call_size(p.first);
 }
 
 template<int size, bool big_endian>
@@ -2957,14 +3326,16 @@ Stub_table<size, big_endian>::add_plt_call_entry(
 {
   Plt_stub_ent ent(object, locsym_index, r_type, addend);
   Address off = this->plt_size_;
-  if (this->plt_call_stubs_.insert(std::make_pair(ent, off)).second)
-    this->plt_size_ = off + this->plt_call_size();
+  std::pair<typename Plt_stub_entries::iterator, bool> p
+    = this->plt_call_stubs_.insert(std::make_pair(ent, off));
+  if (p.second)
+    this->plt_size_ = off + this->plt_call_size(p.first);
 }
 
 // Find a plt call stub.
 
 template<int size, bool big_endian>
-typename elfcpp::Elf_types<size>::Elf_Addr
+typename Stub_table<size, big_endian>::Address
 Stub_table<size, big_endian>::find_plt_call_entry(
     const Sized_relobj_file<size, big_endian>* object,
     const Symbol* gsym,
@@ -2977,7 +3348,7 @@ Stub_table<size, big_endian>::find_plt_call_entry(
 }
 
 template<int size, bool big_endian>
-typename elfcpp::Elf_types<size>::Elf_Addr
+typename Stub_table<size, big_endian>::Address
 Stub_table<size, big_endian>::find_plt_call_entry(const Symbol* gsym) const
 {
   Plt_stub_ent ent(gsym);
@@ -2986,7 +3357,7 @@ Stub_table<size, big_endian>::find_plt_call_entry(const Symbol* gsym) const
 }
 
 template<int size, bool big_endian>
-typename elfcpp::Elf_types<size>::Elf_Addr
+typename Stub_table<size, big_endian>::Address
 Stub_table<size, big_endian>::find_plt_call_entry(
     const Sized_relobj_file<size, big_endian>* object,
     unsigned int locsym_index,
@@ -2999,7 +3370,7 @@ Stub_table<size, big_endian>::find_plt_call_entry(
 }
 
 template<int size, bool big_endian>
-typename elfcpp::Elf_types<size>::Elf_Addr
+typename Stub_table<size, big_endian>::Address
 Stub_table<size, big_endian>::find_plt_call_entry(
     const Sized_relobj_file<size, big_endian>* object,
     unsigned int locsym_index) const
@@ -3032,10 +3403,10 @@ Stub_table<size, big_endian>::add_long_branch_entry(
 // Find long branch stub.
 
 template<int size, bool big_endian>
-typename elfcpp::Elf_types<size>::Elf_Addr
+typename Stub_table<size, big_endian>::Address
 Stub_table<size, big_endian>::find_long_branch_entry(
     const Powerpc_relobj<size, big_endian>* object,
-    Address to)
+    Address to) const
 {
   Branch_stub_ent ent(object, to);
   typename Branch_stub_entries::const_iterator p
@@ -3055,6 +3426,37 @@ class Output_data_glink : public Output_section_data
     : Output_section_data(16), targ_(targ)
   { }
 
+  void
+  add_eh_frame(Layout* layout)
+  {
+    if (!parameters->options().ld_generated_unwind_info())
+      return;
+
+    if (size == 64)
+      layout->add_eh_frame_for_plt(this,
+                                  Eh_cie<64>::eh_frame_cie,
+                                  sizeof (Eh_cie<64>::eh_frame_cie),
+                                  glink_eh_frame_fde_64,
+                                  sizeof (glink_eh_frame_fde_64));
+    else
+      {
+       // 32-bit .glink can use the default since the CIE return
+       // address reg, LR, is valid.
+       layout->add_eh_frame_for_plt(this,
+                                    Eh_cie<32>::eh_frame_cie,
+                                    sizeof (Eh_cie<32>::eh_frame_cie),
+                                    default_fde,
+                                    sizeof (default_fde));
+       // Except where LR is used in a PIC __glink_PLTresolve.
+       if (parameters->options().output_is_position_independent())
+         layout->add_eh_frame_for_plt(this,
+                                      Eh_cie<32>::eh_frame_cie,
+                                      sizeof (Eh_cie<32>::eh_frame_cie),
+                                      glink_eh_frame_fde_32,
+                                      sizeof (glink_eh_frame_fde_32));
+      }
+  }
+
  protected:
   // Write to a map file.
   void
@@ -3104,31 +3506,6 @@ Output_data_glink<size, big_endian>::set_final_data_size()
   this->set_data_size(total);
 }
 
-static inline uint32_t
-l(uint32_t a)
-{
-  return a & 0xffff;
-}
-
-static inline uint32_t
-hi(uint32_t a)
-{
-  return l(a >> 16);
-}
-
-static inline uint32_t
-ha(uint32_t a)
-{
-  return hi(a + 0x8000);
-}
-
-template<bool big_endian>
-static inline void
-write_insn(unsigned char* p, uint32_t v)
-{
-  elfcpp::Swap<32, big_endian>::writeval(p, v);
-}
-
 // Write out plt and long branch stub code.
 
 template<int size, bool big_endian>
@@ -3146,9 +3523,6 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
   unsigned char* const oview = of->get_output_view(off, oview_size);
   unsigned char* p;
 
-  typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
-  static const Address invalid_address = static_cast<Address>(0) - 1;
-
   if (size == 64)
     {
       const Output_data_got_powerpc<size, big_endian>* got
@@ -3167,24 +3541,10 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
               cs != this->plt_call_stubs_.end();
               ++cs)
            {
-             Address plt_addr;
-             bool is_ifunc;
-             const Symbol* gsym = cs->first.sym_;
-             if (gsym != NULL)
-               {
-                 is_ifunc = (gsym->type() == elfcpp::STT_GNU_IFUNC
-                             && gsym->can_use_relative_reloc(false));
-                 plt_addr = gsym->plt_offset();
-               }
-             else
-               {
-                 is_ifunc = true;
-                 const Sized_relobj_file<size, big_endian>* relobj
-                   = cs->first.object_;
-                 unsigned int local_sym_index = cs->first.locsym_;
-                 plt_addr = relobj->local_plt_offset(local_sym_index);
-               }
-             if (is_ifunc)
+             bool is_iplt;
+             Address pltoff = this->plt_off(cs, &is_iplt);
+             Address plt_addr = pltoff;
+             if (is_iplt)
                {
                  if (iplt_base == invalid_address)
                    iplt_base = this->targ_->iplt_section()->address();
@@ -3195,43 +3555,86 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
              const Powerpc_relobj<size, big_endian>* ppcobj = static_cast
                <const Powerpc_relobj<size, big_endian>*>(cs->first.object_);
              Address got_addr = got_os_addr + ppcobj->toc_base_offset();
-             Address pltoff = plt_addr - got_addr;
+             Address off = plt_addr - got_addr;
 
-             if (pltoff + 0x80008000 > 0xffffffff || (pltoff & 7) != 0)
+             if (off + 0x80008000 > 0xffffffff || (off & 7) != 0)
                gold_error(_("%s: linkage table error against `%s'"),
                           cs->first.object_->name().c_str(),
                           cs->first.sym_->demangled_name().c_str());
 
+             bool static_chain = parameters->options().plt_static_chain();
+             bool thread_safe = this->targ_->plt_thread_safe();
+             bool use_fake_dep = false;
+             Address cmp_branch_off = 0;
+             if (thread_safe)
+               {
+                 unsigned int pltindex
+                   = ((pltoff - this->targ_->first_plt_entry_offset())
+                      / this->targ_->plt_entry_size());
+                 Address glinkoff
+                   = (this->targ_->glink_section()->pltresolve_size
+                      + pltindex * 8);
+                 if (pltindex > 32768)
+                   glinkoff += (pltindex - 32768) * 4;
+                 Address to
+                   = this->targ_->glink_section()->address() + glinkoff;
+                 Address from
+                   = (this->stub_address() + cs->second + 24
+                      + 4 * (ha(off) != 0)
+                      + 4 * (ha(off + 8 + 8 * static_chain) != ha(off))
+                      + 4 * static_chain);
+                 cmp_branch_off = to - from;
+                 use_fake_dep = cmp_branch_off + (1 << 25) >= (1 << 26);
+               }
+
              p = oview + cs->second;
-             if (ha(pltoff) != 0)
+             if (ha(off) != 0)
                {
-                 write_insn<big_endian>(p, addis_12_2 + ha(pltoff)),   p += 4;
                  write_insn<big_endian>(p, std_2_1 + 40),              p += 4;
-                 write_insn<big_endian>(p, ld_11_12 + l(pltoff)),      p += 4;
-                 if (ha(pltoff + 16) != ha(pltoff))
+                 write_insn<big_endian>(p, addis_12_2 + ha(off)),      p += 4;
+                 write_insn<big_endian>(p, ld_11_12 + l(off)),         p += 4;
+                 if (ha(off + 8 + 8 * static_chain) != ha(off))
                    {
-                     write_insn<big_endian>(p, addi_12_12 + l(pltoff)),p += 4;
-                     pltoff = 0;
+                     write_insn<big_endian>(p, addi_12_12 + l(off)),   p += 4;
+                     off = 0;
                    }
                  write_insn<big_endian>(p, mtctr_11),                  p += 4;
-                 write_insn<big_endian>(p, ld_2_12 + l(pltoff + 8)),   p += 4;
-                 write_insn<big_endian>(p, ld_11_12 + l(pltoff + 16)), p += 4;
-                 write_insn<big_endian>(p, bctr);
+                 if (use_fake_dep)
+                   {
+                     write_insn<big_endian>(p, xor_11_11_11),          p += 4;
+                     write_insn<big_endian>(p, add_12_12_11),          p += 4;
+                   }
+                 write_insn<big_endian>(p, ld_2_12 + l(off + 8)),      p += 4;
+                 if (static_chain)
+                   write_insn<big_endian>(p, ld_11_12 + l(off + 16)),  p += 4;
                }
              else
                {
                  write_insn<big_endian>(p, std_2_1 + 40),              p += 4;
-                 write_insn<big_endian>(p, ld_11_2 + l(pltoff)),       p += 4;
-                 if (ha(pltoff + 16) != ha(pltoff))
+                 write_insn<big_endian>(p, ld_11_2 + l(off)),          p += 4;
+                 if (ha(off + 8 + 8 * static_chain) != ha(off))
                    {
-                     write_insn<big_endian>(p, addi_2_2 + l(pltoff)),  p += 4;
-                     pltoff = 0;
+                     write_insn<big_endian>(p, addi_2_2 + l(off)),     p += 4;
+                     off = 0;
                    }
                  write_insn<big_endian>(p, mtctr_11),                  p += 4;
-                 write_insn<big_endian>(p, ld_11_2 + l(pltoff + 16)),  p += 4;
-                 write_insn<big_endian>(p, ld_2_2 + l(pltoff + 8)),    p += 4;
-                 write_insn<big_endian>(p, bctr);
+                 if (use_fake_dep)
+                   {
+                     write_insn<big_endian>(p, xor_11_11_11),          p += 4;
+                     write_insn<big_endian>(p, add_2_2_11),            p += 4;
+                   }
+                 if (static_chain)
+                   write_insn<big_endian>(p, ld_11_2 + l(off + 16)),   p += 4;
+                 write_insn<big_endian>(p, ld_2_2 + l(off + 8)),       p += 4;
+               }
+             if (thread_safe && !use_fake_dep)
+               {
+                 write_insn<big_endian>(p, cmpldi_2_0),                p += 4;
+                 write_insn<big_endian>(p, bnectr_p4),                 p += 4;
+                 write_insn<big_endian>(p, b | (cmp_branch_off & 0x3fffffc));
                }
+             else
+               write_insn<big_endian>(p, bctr);
            }
        }
 
@@ -3284,24 +3687,9 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
               cs != this->plt_call_stubs_.end();
               ++cs)
            {
-             Address plt_addr;
-             bool is_ifunc;
-             const Symbol* gsym = cs->first.sym_;
-             if (gsym != NULL)
-               {
-                 is_ifunc = (gsym->type() == elfcpp::STT_GNU_IFUNC
-                             && gsym->can_use_relative_reloc(false));
-                 plt_addr = gsym->plt_offset();
-               }
-             else
-               {
-                 is_ifunc = true;
-                 const Sized_relobj_file<size, big_endian>* relobj
-                   = cs->first.object_;
-                 unsigned int local_sym_index = cs->first.locsym_;
-                 plt_addr = relobj->local_plt_offset(local_sym_index);
-               }
-             if (is_ifunc)
+             bool is_iplt;
+             Address plt_addr = this->plt_off(cs, &is_iplt);
+             if (is_iplt)
                {
                  if (iplt_base == invalid_address)
                    iplt_base = this->targ_->iplt_section()->address();
@@ -3336,17 +3724,17 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
                      got_addr = g_o_t;
                    }
 
-                 Address pltoff = plt_addr - got_addr;
-                 if (ha(pltoff) == 0)
+                 Address off = plt_addr - got_addr;
+                 if (ha(off) == 0)
                    {
-                     write_insn<big_endian>(p +  0, lwz_11_30 + l(pltoff));
+                     write_insn<big_endian>(p +  0, lwz_11_30 + l(off));
                      write_insn<big_endian>(p +  4, mtctr_11);
                      write_insn<big_endian>(p +  8, bctr);
                    }
                  else
                    {
-                     write_insn<big_endian>(p +  0, addis_11_30 + ha(pltoff));
-                     write_insn<big_endian>(p +  4, lwz_11_11 + l(pltoff));
+                     write_insn<big_endian>(p +  0, addis_11_30 + ha(off));
+                     write_insn<big_endian>(p +  4, lwz_11_11 + l(off));
                      write_insn<big_endian>(p +  8, mtctr_11);
                      write_insn<big_endian>(p + 12, bctr);
                    }
@@ -3874,6 +4262,7 @@ Target_powerpc<size, big_endian>::make_glink_section(Layout* layout)
   if (this->glink_ == NULL)
     {
       this->glink_ = new Output_data_glink<size, big_endian>(this);
+      this->glink_->add_eh_frame(layout);
       layout->add_output_section_data(".text", elfcpp::SHT_PROGBITS,
                                      elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR,
                                      this->glink_, ORDER_TEXT, false);
@@ -4250,6 +4639,25 @@ Target_powerpc<size, big_endian>::Scan::local(
     const elfcpp::Sym<size, big_endian>& lsym,
     bool is_discarded)
 {
+  this->maybe_skip_tls_get_addr_call(r_type, NULL);
+
+  if ((size == 64 && r_type == elfcpp::R_PPC64_TLSGD)
+      || (size == 32 && r_type == elfcpp::R_PPC_TLSGD))
+    {
+      this->expect_tls_get_addr_call();
+      const tls::Tls_optimization tls_type = target->optimize_tls_gd(true);
+      if (tls_type != tls::TLSOPT_NONE)
+       this->skip_next_tls_get_addr_call();
+    }
+  else if ((size == 64 && r_type == elfcpp::R_PPC64_TLSLD)
+          || (size == 32 && r_type == elfcpp::R_PPC_TLSLD))
+    {
+      this->expect_tls_get_addr_call();
+      const tls::Tls_optimization tls_type = target->optimize_tls_ld();
+      if (tls_type != tls::TLSOPT_NONE)
+       this->skip_next_tls_get_addr_call();
+    }
+
   Powerpc_relobj<size, big_endian>* ppc_object
     = static_cast<Powerpc_relobj<size, big_endian>*>(object);
 
@@ -4554,6 +4962,21 @@ Target_powerpc<size, big_endian>::Scan::local(
       unsupported_reloc_local(object, r_type);
       break;
     }
+
+  switch (r_type)
+    {
+    case elfcpp::R_POWERPC_GOT_TLSLD16:
+    case elfcpp::R_POWERPC_GOT_TLSGD16:
+    case elfcpp::R_POWERPC_GOT_TPREL16:
+    case elfcpp::R_POWERPC_GOT_DTPREL16:
+    case elfcpp::R_POWERPC_GOT16:
+    case elfcpp::R_PPC64_GOT16_DS:
+    case elfcpp::R_PPC64_TOC16:
+    case elfcpp::R_PPC64_TOC16_DS:
+      ppc_object->set_has_small_toc_reloc();
+    default:
+      break;
+    }
 }
 
 // Report an unsupported relocation against a global symbol.
@@ -4584,6 +5007,27 @@ Target_powerpc<size, big_endian>::Scan::global(
     unsigned int r_type,
     Symbol* gsym)
 {
+  if (this->maybe_skip_tls_get_addr_call(r_type, gsym) == Track_tls::SKIP)
+    return;
+
+  if ((size == 64 && r_type == elfcpp::R_PPC64_TLSGD)
+      || (size == 32 && r_type == elfcpp::R_PPC_TLSGD))
+    {
+      this->expect_tls_get_addr_call();
+      const bool final = gsym->final_value_is_known();
+      const tls::Tls_optimization tls_type = target->optimize_tls_gd(final);
+      if (tls_type != tls::TLSOPT_NONE)
+       this->skip_next_tls_get_addr_call();
+    }
+  else if ((size == 64 && r_type == elfcpp::R_PPC64_TLSLD)
+          || (size == 32 && r_type == elfcpp::R_PPC_TLSLD))
+    {
+      this->expect_tls_get_addr_call();
+      const tls::Tls_optimization tls_type = target->optimize_tls_ld();
+      if (tls_type != tls::TLSOPT_NONE)
+       this->skip_next_tls_get_addr_call();
+    }
+
   Powerpc_relobj<size, big_endian>* ppc_object
     = static_cast<Powerpc_relobj<size, big_endian>*>(object);
 
@@ -4990,6 +5434,21 @@ Target_powerpc<size, big_endian>::Scan::global(
       unsupported_reloc_global(object, r_type, gsym);
       break;
     }
+
+  switch (r_type)
+    {
+    case elfcpp::R_POWERPC_GOT_TLSLD16:
+    case elfcpp::R_POWERPC_GOT_TLSGD16:
+    case elfcpp::R_POWERPC_GOT_TPREL16:
+    case elfcpp::R_POWERPC_GOT_DTPREL16:
+    case elfcpp::R_POWERPC_GOT16:
+    case elfcpp::R_PPC64_GOT16_DS:
+    case elfcpp::R_PPC64_TOC16:
+    case elfcpp::R_PPC64_TOC16_DS:
+      ppc_object->set_has_small_toc_reloc();
+    default:
+      break;
+    }
 }
 
 // Process relocations for gc.
@@ -5070,11 +5529,12 @@ Target_powerpc<size, big_endian>::do_gc_add_reference(
     unsigned int dst_shndx,
     Address dst_off) const
 {
+  if (size != 64 || dst_obj->is_dynamic())
+    return;
+
   Powerpc_relobj<size, big_endian>* ppc_object
     = static_cast<Powerpc_relobj<size, big_endian>*>(dst_obj);
-  if (size == 64
-      && !ppc_object->is_dynamic()
-      && dst_shndx == ppc_object->opd_shndx())
+  if (dst_shndx == ppc_object->opd_shndx())
     {
       if (ppc_object->opd_valid())
        {
@@ -5180,10 +5640,12 @@ class Global_symbol_visitor_opd
        || !sym->in_real_elf())
       return;
 
+    if (sym->object()->is_dynamic())
+      return;
+
     Powerpc_relobj<64, big_endian>* symobj
       = static_cast<Powerpc_relobj<64, big_endian>*>(sym->object());
-    if (symobj->is_dynamic()
-       || symobj->opd_shndx() == 0)
+    if (symobj->opd_shndx() == 0)
       return;
 
     bool is_ordinary;
@@ -5210,6 +5672,31 @@ Target_powerpc<size, big_endian>::define_save_restore_funcs(
     }
 }
 
+// Sort linker created .got section first (for the header), then input
+// sections belonging to files using small model code.
+
+template<bool big_endian>
+class Sort_toc_sections
+{
+ public:
+  bool
+  operator()(const Output_section::Input_section& is1,
+            const Output_section::Input_section& is2) const
+  {
+    if (!is1.is_input_section() && is2.is_input_section())
+      return true;
+    bool small1
+      = (is1.is_input_section()
+        && (static_cast<const Powerpc_relobj<64, big_endian>*>(is1.relobj())
+            ->has_small_toc_reloc()));
+    bool small2
+      = (is2.is_input_section()
+        && (static_cast<const Powerpc_relobj<64, big_endian>*>(is2.relobj())
+            ->has_small_toc_reloc()));
+    return small1 && !small2;
+  }
+};
+
 // Finalize the sections.
 
 template<int size, bool big_endian>
@@ -5263,6 +5750,15 @@ Target_powerpc<size, big_endian>::do_finalize_sections(
          // need to mess with the relaxation machinery checkpointing.
          this->got_section(symtab, layout);
          this->make_brlt_section(layout);
+
+         if (parameters->options().toc_sort())
+           {
+             Output_section* os = this->got_->output_section();
+             if (os != NULL && os->input_sections().size() > 1)
+               std::stable_sort(os->input_sections().begin(),
+                                os->input_sections().end(),
+                                Sort_toc_sections<big_endian>());
+           }
        }
     }
 
@@ -5304,11 +5800,39 @@ Target_powerpc<size, big_endian>::do_finalize_sections(
     this->copy_relocs_.emit(this->rela_dyn_section(layout));
 }
 
+// Return TRUE iff INSN is one we expect on a _LO variety toc/got
+// reloc.
+
+static bool
+ok_lo_toc_insn(uint32_t insn)
+{
+  return ((insn & (0x3f << 26)) == 14u << 26 /* addi */
+         || (insn & (0x3f << 26)) == 32u << 26 /* lwz */
+         || (insn & (0x3f << 26)) == 34u << 26 /* lbz */
+         || (insn & (0x3f << 26)) == 36u << 26 /* stw */
+         || (insn & (0x3f << 26)) == 38u << 26 /* stb */
+         || (insn & (0x3f << 26)) == 40u << 26 /* lhz */
+         || (insn & (0x3f << 26)) == 42u << 26 /* lha */
+         || (insn & (0x3f << 26)) == 44u << 26 /* sth */
+         || (insn & (0x3f << 26)) == 46u << 26 /* lmw */
+         || (insn & (0x3f << 26)) == 47u << 26 /* stmw */
+         || (insn & (0x3f << 26)) == 48u << 26 /* lfs */
+         || (insn & (0x3f << 26)) == 50u << 26 /* lfd */
+         || (insn & (0x3f << 26)) == 52u << 26 /* stfs */
+         || (insn & (0x3f << 26)) == 54u << 26 /* stfd */
+         || ((insn & (0x3f << 26)) == 58u << 26 /* lwa,ld,lmd */
+             && (insn & 3) != 1)
+         || ((insn & (0x3f << 26)) == 62u << 26 /* std, stmd */
+             && ((insn & 3) == 0 || (insn & 3) == 3))
+         || (insn & (0x3f << 26)) == 12u << 26 /* addic */);
+}
+
 // Return the value to use for a branch relocation.
 
 template<int size, bool big_endian>
-typename elfcpp::Elf_types<size>::Elf_Addr
+typename Target_powerpc<size, big_endian>::Address
 Target_powerpc<size, big_endian>::symval_for_branch(
+    const Symbol_table* symtab,
     Address value,
     const Sized_symbol<size>* gsym,
     Powerpc_relobj<size, big_endian>* object,
@@ -5336,6 +5860,13 @@ Target_powerpc<size, big_endian>::symval_for_branch(
     {
       Address sec_off;
       *dest_shndx = symobj->get_opd_ent(value - opd_addr, &sec_off);
+      if (symtab->is_section_folded(symobj, *dest_shndx))
+       {
+         Section_id folded
+           = symtab->icf()->get_folded_section(symobj, *dest_shndx);
+         symobj = static_cast<Powerpc_relobj<size, big_endian>*>(folded.first);
+         *dest_shndx = folded.second;
+       }
       Address sec_addr = symobj->get_output_section_offset(*dest_shndx);
       gold_assert(sec_addr != invalid_address);
       sec_addr += symobj->output_section(*dest_shndx)->address();
@@ -5361,23 +5892,20 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
     Address address,
     section_size_type view_size)
 {
-  bool is_tls_call = ((r_type == elfcpp::R_POWERPC_REL24
-                      || r_type == elfcpp::R_PPC_PLTREL24)
-                     && gsym != NULL
-                     && strcmp(gsym->name(), "__tls_get_addr") == 0);
-  enum skip_tls last_tls = this->call_tls_get_addr_;
-  this->call_tls_get_addr_ = CALL_NOT_EXPECTED;
-  if (is_tls_call)
+  switch (this->maybe_skip_tls_get_addr_call(r_type, gsym))
     {
-      if (last_tls == CALL_NOT_EXPECTED)
-       gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
-                              _("__tls_get_addr call lacks marker reloc"));
-      else if (last_tls == CALL_SKIP)
-       return false;
+    case Track_tls::NOT_EXPECTED:
+      gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
+                            _("__tls_get_addr call lacks marker reloc"));
+      break;
+    case Track_tls::EXPECTED:
+      // We have already complained.
+      break;
+    case Track_tls::SKIP:
+      return true;
+    case Track_tls::NORMAL:
+      break;
     }
-  else if (last_tls != CALL_NOT_EXPECTED)
-    gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
-                          _("missing expected __tls_get_addr call"));
 
   typedef Powerpc_relocate_functions<size, big_endian> Reloc;
   typedef typename elfcpp::Swap<32, big_endian>::Valtype Insn;
@@ -5481,8 +6009,8 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
                  Address addend = rela.get_r_addend();
                  unsigned int dest_shndx;
                  Address opdent = psymval->value(object, addend);
-                 code = target->symval_for_branch(opdent, gsym, object,
-                                                  &dest_shndx);
+                 code = target->symval_for_branch(relinfo->symtab, opdent,
+                                                  gsym, object, &dest_shndx);
                  bool is_ordinary;
                  if (dest_shndx == 0)
                    dest_shndx = gsym->shndx(&is_ordinary);
@@ -5674,7 +6202,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
     {
       // Second instruction of a global dynamic sequence,
       // the __tls_get_addr call
-      this->call_tls_get_addr_ = CALL_EXPECTED;
+      this->expect_tls_get_addr_call(relinfo, relnum, rela.get_r_offset());
       const bool final = gsym == NULL || gsym->final_value_is_known();
       const tls::Tls_optimization tls_type = target->optimize_tls_gd(final);
       if (tls_type != tls::TLSOPT_NONE)
@@ -5697,7 +6225,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
              view += 2 * big_endian;
              value = psymval->value(object, rela.get_r_addend());
            }
-         this->call_tls_get_addr_ = CALL_SKIP;
+         this->skip_next_tls_get_addr_call();
        }
     }
   else if ((size == 64 && r_type == elfcpp::R_PPC64_TLSLD)
@@ -5705,14 +6233,14 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
     {
       // Second instruction of a local dynamic sequence,
       // the __tls_get_addr call
-      this->call_tls_get_addr_ = CALL_EXPECTED;
+      this->expect_tls_get_addr_call(relinfo, relnum, rela.get_r_offset());
       const tls::Tls_optimization tls_type = target->optimize_tls_ld();
       if (tls_type == tls::TLSOPT_TO_LE)
        {
          Insn* iview = reinterpret_cast<Insn*>(view);
          Insn insn = addi_3_3;
          elfcpp::Swap<32, big_endian>::writeval(iview, insn);
-         this->call_tls_get_addr_ = CALL_SKIP;
+         this->skip_next_tls_get_addr_call();
          r_type = elfcpp::R_POWERPC_TPREL16_LO;
          view += 2 * big_endian;
          value = dtp_offset;
@@ -5744,7 +6272,8 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
        addend = rela.get_r_addend();
       value = psymval->value(object, addend);
       if (size == 64 && is_branch_reloc(r_type))
-       value = target->symval_for_branch(value, gsym, object, &dest_shndx);
+       value = target->symval_for_branch(relinfo->symtab, value,
+                                         gsym, object, &dest_shndx);
       unsigned int max_branch_offset = 0;
       if (r_type == elfcpp::R_POWERPC_REL24
          || r_type == elfcpp::R_PPC_PLTREL24
@@ -5884,6 +6413,75 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       break;
     }
 
+  if (size == 64)
+    {
+      // Multi-instruction sequences that access the TOC can be
+      // optimized, eg. addis ra,r2,0; addi rb,ra,x;
+      // to             nop;           addi rb,r2,x;
+      switch (r_type)
+       {
+       default:
+         break;
+
+       case elfcpp::R_POWERPC_GOT_TLSLD16_HA:
+       case elfcpp::R_POWERPC_GOT_TLSGD16_HA:
+       case elfcpp::R_POWERPC_GOT_TPREL16_HA:
+       case elfcpp::R_POWERPC_GOT_DTPREL16_HA:
+       case elfcpp::R_POWERPC_GOT16_HA:
+       case elfcpp::R_PPC64_TOC16_HA:
+         if (parameters->options().toc_optimize())
+           {
+             Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+             Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+             if ((insn & ((0x3f << 26) | 0x1f << 16))
+                 != ((15u << 26) | (2 << 16)) /* addis rt,2,imm */)
+               gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
+                                      _("toc optimization is not supported "
+                                        "for %#08x instruction"), insn);
+             else if (value + 0x8000 < 0x10000)
+               {
+                 elfcpp::Swap<32, big_endian>::writeval(iview, nop);
+                 return true;
+               }
+           }
+         break;
+
+       case elfcpp::R_POWERPC_GOT_TLSLD16_LO:
+       case elfcpp::R_POWERPC_GOT_TLSGD16_LO:
+       case elfcpp::R_POWERPC_GOT_TPREL16_LO:
+       case elfcpp::R_POWERPC_GOT_DTPREL16_LO:
+       case elfcpp::R_POWERPC_GOT16_LO:
+       case elfcpp::R_PPC64_GOT16_LO_DS:
+       case elfcpp::R_PPC64_TOC16_LO:
+       case elfcpp::R_PPC64_TOC16_LO_DS:
+         if (parameters->options().toc_optimize())
+           {
+             Insn* iview = reinterpret_cast<Insn*>(view - 2 * big_endian);
+             Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+             if (!ok_lo_toc_insn(insn))
+               gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
+                                      _("toc optimization is not supported "
+                                        "for %#08x instruction"), insn);
+             else if (value + 0x8000 < 0x10000)
+               {
+                 if ((insn & (0x3f << 26)) == 12u << 26 /* addic */)
+                   {
+                     // Transform addic to addi when we change reg.
+                     insn &= ~((0x3f << 26) | (0x1f << 16));
+                     insn |= (14u << 26) | (2 << 16);
+                   }
+                 else
+                   {
+                     insn &= ~(0x1f << 16);
+                     insn |= 2 << 16;
+                   }
+                 elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+               }
+           }
+         break;
+       }
+    }
+
   typename Reloc::Overflow_check overflow = Reloc::CHECK_NONE;
   switch (r_type)
     {
@@ -6710,7 +7308,8 @@ class Target_selector_powerpc : public Target_selector
 {
 public:
   Target_selector_powerpc()
-    : Target_selector(elfcpp::EM_NONE, size, big_endian,
+    : Target_selector(size == 64 ? elfcpp::EM_PPC64 : elfcpp::EM_PPC,
+                     size, big_endian,
                      (size == 64
                       ? (big_endian ? "elf64-powerpc" : "elf64-powerpcle")
                       : (big_endian ? "elf32-powerpc" : "elf32-powerpcle")),
@@ -6719,28 +7318,6 @@ public:
                       : (big_endian ? "elf32ppc" : "elf32lppc")))
   { }
 
-  virtual Target*
-  do_recognize(Input_file*, off_t, int machine, int, int)
-  {
-    switch (size)
-      {
-      case 64:
-       if (machine != elfcpp::EM_PPC64)
-         return NULL;
-       break;
-
-      case 32:
-       if (machine != elfcpp::EM_PPC)
-         return NULL;
-       break;
-
-      default:
-       return NULL;
-      }
-
-    return this->instantiate_target();
-  }
-
   virtual Target*
   do_instantiate_target()
   { return new Target_powerpc<size, big_endian>(); }
@@ -6751,4 +7328,14 @@ Target_selector_powerpc<32, false> target_selector_ppc32le;
 Target_selector_powerpc<64, true> target_selector_ppc64;
 Target_selector_powerpc<64, false> target_selector_ppc64le;
 
+// Instantiate these constants for -O0
+template<int size, bool big_endian>
+const int Output_data_glink<size, big_endian>::pltresolve_size;
+template<int size, bool big_endian>
+const typename Stub_table<size, big_endian>::Address
+  Stub_table<size, big_endian>::invalid_address;
+template<int size, bool big_endian>
+const typename Target_powerpc<size, big_endian>::Address
+  Target_powerpc<size, big_endian>::invalid_address;
+
 } // End anonymous namespace.