PowerPC TPREL_HA/LO optimisation
[binutils-gdb.git] / gold / powerpc.cc
index 318c41744b5c52f73a4386246e1812c2851255c1..e35cbcf6c0b7935ab213e38f95e88bdace32883e 100644 (file)
@@ -650,6 +650,7 @@ class Target_powerpc : public Sized_target<size, big_endian>
       power10_stubs_(false), plt_thread_safe_(false), plt_localentry0_(false),
       plt_localentry0_init_(false), has_localentry0_(false),
       has_tls_get_addr_opt_(false),
+      tprel_opt_(parameters->options().tls_optimize()),
       relax_failed_(false), relax_fail_count_(0),
       stub_group_size_(0), savres_section_(0),
       tls_get_addr_(NULL), tls_get_addr_opt_(NULL),
@@ -1085,7 +1086,16 @@ class Target_powerpc : public Sized_target<size, big_endian>
   void
   set_power10_stubs()
   {
-    this->power10_stubs_ = true;
+    if (parameters->options().power10_stubs_enum()
+       != General_options::POWER10_STUBS_NO)
+      this->power10_stubs_ = true;
+  }
+
+  bool
+  power10_stubs_auto() const
+  {
+    return (parameters->options().power10_stubs_enum()
+           == General_options::POWER10_STUBS_AUTO);
   }
 
   bool
@@ -1136,6 +1146,14 @@ class Target_powerpc : public Sized_target<size, big_endian>
     return false;
   }
 
+  bool
+  tprel_opt() const
+  { return this->tprel_opt_; }
+
+  void
+  set_tprel_opt(bool val)
+  { this->tprel_opt_ = val; }
+
   // Remember any symbols seen with non-zero localentry, even those
   // not providing a definition
   bool
@@ -1693,6 +1711,7 @@ class Target_powerpc : public Sized_target<size, big_endian>
   bool plt_localentry0_init_;
   bool has_localentry0_;
   bool has_tls_get_addr_opt_;
+  bool tprel_opt_;
 
   bool relax_failed_;
   int relax_fail_count_;
@@ -4621,26 +4640,32 @@ class Stub_table : public Output_relaxed_input_section
   struct Plt_stub_ent
   {
     Plt_stub_ent(unsigned int off, unsigned int indx)
-      : off_(off), indx_(indx), iter_(0), notoc_(0), r2save_(0), localentry0_(0)
+      : off_(off), indx_(indx), iter_(0), notoc_(0), toc_(0),
+       r2save_(0), localentry0_(0), tocoff_(0)
     { }
 
     unsigned int off_;
-    unsigned int indx_ : 28;
+    unsigned int indx_;
     unsigned int iter_ : 1;
     unsigned int notoc_ : 1;
+    unsigned int toc_ : 1;
     unsigned int r2save_ : 1;
     unsigned int localentry0_ : 1;
+    unsigned int tocoff_ : 8;
   };
   struct Branch_stub_ent
   {
     Branch_stub_ent(unsigned int off, bool notoc, bool save_res)
-      : off_(off), iter_(false), notoc_(notoc), save_res_(save_res)
+      : off_(off), iter_(0), notoc_(notoc), toc_(0), save_res_(save_res),
+       tocoff_(0)
     { }
 
     unsigned int off_;
-    bool iter_;
-    bool notoc_;
-    bool save_res_;
+    unsigned int iter_ : 1;
+    unsigned int notoc_ : 1;
+    unsigned int toc_ : 1;
+    unsigned int save_res_ : 1;
+    unsigned int tocoff_ : 8;
   };
   typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
   static const Address invalid_address = static_cast<Address>(0) - 1;
@@ -4888,7 +4913,7 @@ class Stub_table : public Output_relaxed_input_section
 
   // Size of a given plt call stub.
   unsigned int
-  plt_call_size(typename Plt_stub_entries::const_iterator p) const;
+  plt_call_size(typename Plt_stub_entries::iterator p) const;
 
   unsigned int
   plt_call_align(unsigned int bytes) const
@@ -4899,16 +4924,14 @@ class Stub_table : public Output_relaxed_input_section
 
   // Return long branch stub size.
   unsigned int
-  branch_stub_size(typename Branch_stub_entries::const_iterator p,
+  branch_stub_size(typename Branch_stub_entries::iterator p,
                   bool* need_lt);
 
-  bool
-  build_tls_opt_head(unsigned char** pp,
-                    typename Plt_stub_entries::const_iterator cs);
+  void
+  build_tls_opt_head(unsigned char** pp,  bool save_lr);
 
-  bool
-  build_tls_opt_tail(unsigned char* p,
-                    typename Plt_stub_entries::const_iterator cs);
+  void
+  build_tls_opt_tail(unsigned char* p);
 
   void
   plt_error(const Plt_stub_key& p);
@@ -5073,15 +5096,22 @@ Stub_table<size, big_endian>::add_plt_call_entry(
       if (r_type == elfcpp::R_PPC64_REL24_NOTOC)
        {
          if (!p.second && !p.first->second.notoc_
-             && !this->targ_->power10_stubs())
+             && (!this->targ_->power10_stubs()
+                 || this->targ_->power10_stubs_auto()))
            this->need_resize_ = true;
          p.first->second.notoc_ = 1;
        }
-      else if (!tocsave && !p.first->second.localentry0_)
+      else
        {
-         if (!p.second && !p.first->second.r2save_)
+         if (!p.second && !p.first->second.toc_)
            this->need_resize_ = true;
-         p.first->second.r2save_ = 1;
+         p.first->second.toc_ = 1;
+         if (!tocsave && !p.first->second.localentry0_)
+           {
+             if (!p.second && !p.first->second.r2save_)
+               this->need_resize_ = true;
+             p.first->second.r2save_ = 1;
+           }
        }
     }
   if (p.second || (this->resizing_ && !p.first->second.iter_))
@@ -5124,15 +5154,22 @@ Stub_table<size, big_endian>::add_plt_call_entry(
       if (r_type == elfcpp::R_PPC64_REL24_NOTOC)
        {
          if (!p.second && !p.first->second.notoc_
-             && !this->targ_->power10_stubs())
+             && (!this->targ_->power10_stubs()
+                 || this->targ_->power10_stubs_auto()))
            this->need_resize_ = true;
          p.first->second.notoc_ = 1;
        }
-      else if (!tocsave && !p.first->second.localentry0_)
+      else
        {
-         if (!p.second && !p.first->second.r2save_)
+         if (!p.second && !p.first->second.toc_)
            this->need_resize_ = true;
-         p.first->second.r2save_ = 1;
+         p.first->second.toc_ = 1;
+         if (!tocsave && !p.first->second.localentry0_)
+           {
+             if (!p.second && !p.first->second.r2save_)
+               this->need_resize_ = true;
+             p.first->second.r2save_ = 1;
+           }
        }
     }
   if (p.second || (this->resizing_ && !p.first->second.iter_))
@@ -5221,11 +5258,18 @@ Stub_table<size, big_endian>::add_long_branch_entry(
   Branch_stub_ent ent(this->branch_size_, notoc, save_res);
   std::pair<typename Branch_stub_entries::iterator, bool> p
     = this->long_branch_stubs_.insert(std::make_pair(key, ent));
-  if (notoc && !p.first->second.notoc_)
+  if (notoc)
     {
-      this->need_resize_ = true;
+      if (!p.second && !p.first->second.notoc_)
+       this->need_resize_ = true;
       p.first->second.notoc_ = true;
     }
+  else
+    {
+      if (!p.second && !p.first->second.toc_)
+       this->need_resize_ = true;
+      p.first->second.toc_ = true;
+    }
   gold_assert(save_res == p.first->second.save_res_);
   if (p.second || (this->resizing_ && !p.first->second.iter_))
     {
@@ -5320,7 +5364,7 @@ Stub_table<size, big_endian>::add_eh_frame(Layout* layout)
   if (!this->targ_->has_glink())
     return;
 
-  typedef typename Plt_stub_entries::const_iterator plt_iter;
+  typedef typename Plt_stub_entries::iterator plt_iter;
   std::vector<plt_iter> calls;
   if (!this->plt_call_stubs_.empty())
     for (plt_iter cs = this->plt_call_stubs_.begin();
@@ -5622,7 +5666,7 @@ Stub_table<size, big_endian>::define_stub_syms(Symbol_table* symtab)
       // 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;
+      typedef typename Plt_stub_entries::iterator plt_iter;
       std::vector<plt_iter> sorted;
       sorted.resize(this->plt_call_stubs_.size());
 
@@ -5666,7 +5710,7 @@ Stub_table<size, big_endian>::define_stub_syms(Symbol_table* symtab)
        }
     }
 
-  typedef typename Branch_stub_entries::const_iterator branch_iter;
+  typedef typename Branch_stub_entries::iterator branch_iter;
   for (branch_iter bs = this->long_branch_stubs_.begin();
        bs != this->long_branch_stubs_.end();
        ++bs)
@@ -5688,88 +5732,72 @@ Stub_table<size, big_endian>::define_stub_syms(Symbol_table* symtab)
 // Emit the start of a __tls_get_addr_opt plt call stub.
 
 template<int size, bool big_endian>
-bool
-Stub_table<size, big_endian>::build_tls_opt_head(
-     unsigned char** pp,
-     typename Plt_stub_entries::const_iterator cs)
+void
+Stub_table<size, big_endian>::build_tls_opt_head(unsigned char** pp,
+                                                bool save_lr)
 {
-  if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+  unsigned char* p = *pp;
+  if (size == 64)
     {
-      unsigned char* p = *pp;
-      if (size == 64)
-       {
-         write_insn<big_endian>(p, ld_11_3 + 0);
-         p += 4;
-         write_insn<big_endian>(p, ld_12_3 + 8);
-         p += 4;
-         write_insn<big_endian>(p, mr_0_3);
-         p += 4;
-         write_insn<big_endian>(p, cmpdi_11_0);
-         p += 4;
-         write_insn<big_endian>(p, add_3_12_13);
-         p += 4;
-         write_insn<big_endian>(p, beqlr);
-         p += 4;
-         write_insn<big_endian>(p, mr_3_0);
-         p += 4;
-         if (cs->second.r2save_ && !cs->second.localentry0_)
-           {
-             write_insn<big_endian>(p, mflr_11);
-             p += 4;
-             write_insn<big_endian>(p, (std_11_1 + this->targ_->stk_linker()));
-             p += 4;
-           }
-       }
-      else
+      write_insn<big_endian>(p, ld_11_3 + 0);
+      p += 4;
+      write_insn<big_endian>(p, ld_12_3 + 8);
+      p += 4;
+      write_insn<big_endian>(p, mr_0_3);
+      p += 4;
+      write_insn<big_endian>(p, cmpdi_11_0);
+      p += 4;
+      write_insn<big_endian>(p, add_3_12_13);
+      p += 4;
+      write_insn<big_endian>(p, beqlr);
+      p += 4;
+      write_insn<big_endian>(p, mr_3_0);
+      p += 4;
+      if (save_lr)
        {
-         write_insn<big_endian>(p, lwz_11_3 + 0);
-         p += 4;
-         write_insn<big_endian>(p, lwz_12_3 + 4);
-         p += 4;
-         write_insn<big_endian>(p, mr_0_3);
-         p += 4;
-         write_insn<big_endian>(p, cmpwi_11_0);
-         p += 4;
-         write_insn<big_endian>(p, add_3_12_2);
-         p += 4;
-         write_insn<big_endian>(p, beqlr);
+         write_insn<big_endian>(p, mflr_11);
          p += 4;
-         write_insn<big_endian>(p, mr_3_0);
-         p += 4;
-         write_insn<big_endian>(p, nop);
+         write_insn<big_endian>(p, (std_11_1 + this->targ_->stk_linker()));
          p += 4;
        }
-      *pp = p;
-      return true;
     }
-  return false;
-}
-
-// Emit the tail of a __tls_get_addr_opt plt call stub.
-
-template<int size, bool big_endian>
-bool
-Stub_table<size, big_endian>::build_tls_opt_tail(
-     unsigned char* p,
-     typename Plt_stub_entries::const_iterator cs)
-{
-  if (size == 64
-      && cs->second.r2save_
-      && !cs->second.localentry0_
-      && this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+  else
     {
-      write_insn<big_endian>(p, bctrl);
+      write_insn<big_endian>(p, lwz_11_3 + 0);
       p += 4;
-      write_insn<big_endian>(p, ld_2_1 + this->targ_->stk_toc());
+      write_insn<big_endian>(p, lwz_12_3 + 4);
       p += 4;
-      write_insn<big_endian>(p, ld_11_1 + this->targ_->stk_linker());
+      write_insn<big_endian>(p, mr_0_3);
       p += 4;
-      write_insn<big_endian>(p, mtlr_11);
+      write_insn<big_endian>(p, cmpwi_11_0);
+      p += 4;
+      write_insn<big_endian>(p, add_3_12_2);
+      p += 4;
+      write_insn<big_endian>(p, beqlr);
+      p += 4;
+      write_insn<big_endian>(p, mr_3_0);
+      p += 4;
+      write_insn<big_endian>(p, nop);
       p += 4;
-      write_insn<big_endian>(p, blr);
-      return true;
     }
-  return false;
+  *pp = p;
+}
+
+// Emit the tail of a __tls_get_addr_opt plt call stub.
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::build_tls_opt_tail(unsigned char* p)
+{
+  write_insn<big_endian>(p, bctrl);
+  p += 4;
+  write_insn<big_endian>(p, ld_2_1 + this->targ_->stk_toc());
+  p += 4;
+  write_insn<big_endian>(p, ld_11_1 + this->targ_->stk_linker());
+  p += 4;
+  write_insn<big_endian>(p, mtlr_11);
+  p += 4;
+  write_insn<big_endian>(p, blr);
 }
 
 // Emit pc-relative plt call stub code.
@@ -5939,7 +5967,7 @@ build_notoc_offset(unsigned char* p, uint64_t off, bool load)
 template<int size, bool big_endian>
 unsigned int
 Stub_table<size, big_endian>::plt_call_size(
-    typename Plt_stub_entries::const_iterator p) const
+    typename Plt_stub_entries::iterator p) const
 {
   if (size == 32)
     {
@@ -5951,77 +5979,122 @@ Stub_table<size, big_endian>::plt_call_size(
   const Output_data_plt_powerpc<size, big_endian>* plt;
   uint64_t plt_addr = this->plt_off(p, &plt);
   plt_addr += plt->address();
-  unsigned int bytes = 0;
-  const Symbol* gsym = p->first.sym_;
-  if (this->targ_->is_tls_get_addr_opt(gsym))
+  if (this->targ_->power10_stubs()
+      && this->targ_->power10_stubs_auto())
     {
-      if (p->second.r2save_ && !p->second.localentry0_)
-       bytes = 13 * 4;
-      else
-       bytes = 7 * 4;
+      unsigned int bytes = 0;
+      if (p->second.notoc_)
+       {
+         if (this->targ_->is_tls_get_addr_opt(p->first.sym_))
+           bytes = 7 * 4;
+         uint64_t from = this->stub_address() + p->second.off_ + bytes;
+         uint64_t odd = from & 4;
+         uint64_t off = plt_addr - from;
+         if (off - odd + (1ULL << 33) < 1ULL << 34)
+           bytes += odd + 4 * 4;
+         else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+           bytes += 7 * 4;
+         else
+           bytes += 8 * 4;
+         bytes = this->plt_call_align(bytes);
+       }
+      unsigned int tail = 0;
+      if (p->second.toc_)
+       {
+         p->second.tocoff_ = bytes;
+         if (this->targ_->is_tls_get_addr_opt(p->first.sym_))
+           {
+             bytes += 7 * 4;
+             if (p->second.r2save_ && !p->second.localentry0_)
+               {
+                 bytes += 2 * 4;
+                 tail = 4 * 4;
+               }
+           }
+         if (p->second.r2save_)
+           bytes += 4;
+         uint64_t 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();
+         uint64_t off = plt_addr - got_addr;
+         bytes += 3 * 4 + 4 * (ha(off) != 0);
+       }
+      return bytes + tail;
     }
+  else
+    {
+      unsigned int bytes = 0;
+      unsigned int tail = 0;
+      if (this->targ_->is_tls_get_addr_opt(p->first.sym_))
+       {
+         bytes = 7 * 4;
+         if (p->second.r2save_ && !p->second.localentry0_)
+           {
+             bytes = 9 * 4;
+             tail = 4 * 4;
+           }
+       }
 
-  if (p->second.r2save_)
-    bytes += 4;
+      if (p->second.r2save_)
+       bytes += 4;
 
-  if (this->targ_->power10_stubs())
-    {
-      uint64_t from = this->stub_address() + p->second.off_ + bytes;
-      if (bytes > 8 * 4)
-       from -= 4 * 4;
-      uint64_t odd = from & 4;
-      uint64_t off = plt_addr - from;
-      if (off - odd + (1ULL << 33) < 1ULL << 34)
-       bytes += odd + 4 * 4;
-      else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
-       bytes += 7 * 4;
-      else
-       bytes += 8 * 4;
-      return bytes;
-    }
+      if (this->targ_->power10_stubs())
+       {
+         uint64_t from = this->stub_address() + p->second.off_ + bytes;
+         uint64_t odd = from & 4;
+         uint64_t off = plt_addr - from;
+         if (off - odd + (1ULL << 33) < 1ULL << 34)
+           bytes += odd + 4 * 4;
+         else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+           bytes += 7 * 4;
+         else
+           bytes += 8 * 4;
+         return bytes + tail;
+       }
 
-  if (p->second.notoc_)
-    {
-      uint64_t from = this->stub_address() + p->second.off_ + bytes + 2 * 4;
-      if (bytes > 32)
-       from -= 4 * 4;
-      uint64_t off = plt_addr - from;
-      if (off + 0x8000 < 0x10000)
-       bytes += 7 * 4;
-      else if (off + 0x80008000ULL < 0x100000000ULL)
-       bytes += 8 * 4;
-      else
+      if (p->second.notoc_)
        {
-         bytes += 8 * 4;
-         if (off + 0x800000000000ULL >= 0x1000000000000ULL
-             && ((off >> 32) & 0xffff) != 0)
-           bytes += 4;
-         if (((off >> 32) & 0xffffffffULL) != 0)
-           bytes += 4;
-         if (hi(off) != 0)
-           bytes += 4;
-         if (l(off) != 0)
-           bytes += 4;
+         uint64_t from = this->stub_address() + p->second.off_ + bytes + 2 * 4;
+         uint64_t off = plt_addr - from;
+         if (off + 0x8000 < 0x10000)
+           bytes += 7 * 4;
+         else if (off + 0x80008000ULL < 0x100000000ULL)
+           bytes += 8 * 4;
+         else
+           {
+             bytes += 8 * 4;
+             if (off + 0x800000000000ULL >= 0x1000000000000ULL
+                 && ((off >> 32) & 0xffff) != 0)
+               bytes += 4;
+             if (((off >> 32) & 0xffffffffULL) != 0)
+               bytes += 4;
+             if (hi(off) != 0)
+               bytes += 4;
+             if (l(off) != 0)
+               bytes += 4;
+           }
+         return bytes + tail;
        }
-      return bytes;
-    }
 
-  uint64_t 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();
-  uint64_t off = plt_addr - got_addr;
-  bytes += 3 * 4 + 4 * (ha(off) != 0);
-  if (this->targ_->abiversion() < 2)
-    {
-      bool static_chain = parameters->options().plt_static_chain();
-      bool thread_safe = this->targ_->plt_thread_safe();
-      bytes += (4
-               + 4 * static_chain
-               + 8 * thread_safe
-               + 4 * (ha(off + 8 + 8 * static_chain) != ha(off)));
+      uint64_t 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();
+      uint64_t off = plt_addr - got_addr;
+      bytes += 3 * 4 + 4 * (ha(off) != 0);
+      if (this->targ_->abiversion() < 2)
+       {
+         bool static_chain = parameters->options().plt_static_chain();
+         bool thread_safe = this->targ_->plt_thread_safe();
+         bytes += (4
+                   + 4 * static_chain
+                   + 8 * thread_safe
+                   + 4 * (ha(off + 8 + 8 * static_chain) != ha(off)));
+       }
+      return bytes + tail;
     }
-  return bytes;
 }
 
 // Return long branch stub size.
@@ -6029,7 +6102,7 @@ Stub_table<size, big_endian>::plt_call_size(
 template<int size, bool big_endian>
 unsigned int
 Stub_table<size, big_endian>::branch_stub_size(
-     typename Branch_stub_entries::const_iterator p,
+     typename Branch_stub_entries::iterator p,
      bool* need_lt)
 {
   Address loc = this->stub_address() + this->last_plt_size_ + p->second.off_;
@@ -6043,46 +6116,56 @@ Stub_table<size, big_endian>::branch_stub_size(
     }
 
   uint64_t off = p->first.dest_ - loc;
+  unsigned int bytes = 0;
   if (p->second.notoc_)
     {
       if (this->targ_->power10_stubs())
        {
          Address odd = loc & 4;
          if (off + (1 << 25) < 2 << 25)
-           return odd + 12;
-         if (off - odd + (1ULL << 33) < 1ULL << 34)
-           return odd + 16;
-         if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
-           return 28;
-         return 32;
-       }
-      off -= 8;
-      if (off + 0x8000 < 0x10000)
-       return 24;
-      if (off + 0x80008000ULL < 0x100000000ULL)
-       {
-         if (off + 24 + (1 << 25) < 2 << 25)
-           return 28;
-         return 32;
-       }
-      unsigned int bytes = 32;
-      if (off + 0x800000000000ULL >= 0x1000000000000ULL
-         && ((off >> 32) & 0xffff) != 0)
-       bytes += 4;
-      if (((off >> 32) & 0xffffffffULL) != 0)
-       bytes += 4;
-      if (hi(off) != 0)
-       bytes += 4;
-      if (l(off) != 0)
-       bytes += 4;
-      return bytes;
+           bytes = odd + 12;
+         else if (off - odd + (1ULL << 33) < 1ULL << 34)
+           bytes = odd + 16;
+         else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+           bytes = 28;
+         else
+           bytes = 32;
+         if (!(p->second.toc_ && this->targ_->power10_stubs_auto()))
+           return bytes;
+         p->second.tocoff_ = bytes;
+       }
+      else
+       {
+         off -= 8;
+         if (off + 0x8000 < 0x10000)
+           return 24;
+         if (off + 0x80008000ULL < 0x100000000ULL)
+           {
+             if (off + 24 + (1 << 25) < 2 << 25)
+               return 28;
+             return 32;
+           }
+
+         bytes = 32;
+         if (off + 0x800000000000ULL >= 0x1000000000000ULL
+             && ((off >> 32) & 0xffff) != 0)
+           bytes += 4;
+         if (((off >> 32) & 0xffffffffULL) != 0)
+           bytes += 4;
+         if (hi(off) != 0)
+           bytes += 4;
+         if (l(off) != 0)
+           bytes += 4;
+         return bytes;
+       }
     }
 
   if (off + (1 << 25) < 2 << 25)
-    return 4;
-  if (!this->targ_->power10_stubs())
+    return bytes + 4;
+  if (!this->targ_->power10_stubs()
+      || (p->second.toc_ && this->targ_->power10_stubs_auto()))
     *need_lt = true;
-  return 16;
+  return bytes + 16;
 }
 
 template<int size, bool big_endian>
@@ -6118,6 +6201,10 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
   if (size == 64
       && this->targ_->power10_stubs())
     {
+      const Output_data_got_powerpc<size, big_endian>* got
+       = this->targ_->got_section();
+      Address got_os_addr = got->output_section()->address();
+
       if (!this->plt_call_stubs_.empty())
        {
          // Write out plt call stubs.
@@ -6127,22 +6214,94 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
               ++cs)
            {
              p = oview + cs->second.off_;
-             this->build_tls_opt_head(&p, cs);
-             if (cs->second.r2save_)
-               {
-                 write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
-                 p += 4;
-               }
              const Output_data_plt_powerpc<size, big_endian>* plt;
              Address pltoff = this->plt_off(cs, &plt);
              Address plt_addr = pltoff + plt->address();
-             Address from = this->stub_address() + (p - oview);
-             Address delta = plt_addr - from;
-             p = build_power10_offset<big_endian>(p, delta, from & 4, true);
-             write_insn<big_endian>(p, mtctr_12);
-             p += 4;
-             if (!this->build_tls_opt_tail(p, cs))
-               write_insn<big_endian>(p, bctr);
+             if (this->targ_->power10_stubs_auto())
+               {
+                 if (cs->second.notoc_)
+                   {
+                     if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+                       this->build_tls_opt_head(&p, false);
+                     Address from = this->stub_address() + (p - oview);
+                     Address delta = plt_addr - from;
+                     p = build_power10_offset<big_endian>(p, delta, from & 4,
+                                                          true);
+                     write_insn<big_endian>(p, mtctr_12);
+                     p += 4;
+                     write_insn<big_endian>(p, bctr);
+                     p += 4;
+                     p = oview + this->plt_call_align(p - oview);
+                   }
+                 if (cs->second.toc_)
+                   {
+                     if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+                       {
+                         bool save_lr
+                           = cs->second.r2save_ && !cs->second.localentry0_;
+                         this->build_tls_opt_head(&p, save_lr);
+                       }
+                     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 off = plt_addr - got_addr;
+
+                     if (off + 0x80008000 > 0xffffffff || (off & 7) != 0)
+                       this->plt_error(cs->first);
+
+                     if (cs->second.r2save_)
+                       {
+                         write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
+                         p += 4;
+                       }
+                     if (ha(off) != 0)
+                       {
+                         write_insn<big_endian>(p, addis_12_2 + ha(off));
+                         p += 4;
+                         write_insn<big_endian>(p, ld_12_12 + l(off));
+                         p += 4;
+                       }
+                     else
+                       {
+                         write_insn<big_endian>(p, ld_12_2 + l(off));
+                         p += 4;
+                       }
+                     write_insn<big_endian>(p, mtctr_12);
+                     p += 4;
+                     if (cs->second.r2save_
+                         && !cs->second.localentry0_
+                         && this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+                       this->build_tls_opt_tail(p);
+                     else
+                       write_insn<big_endian>(p, bctr);
+                   }
+               }
+             else
+               {
+                 if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+                   {
+                     bool save_lr
+                       = cs->second.r2save_ && !cs->second.localentry0_;
+                     this->build_tls_opt_head(&p, save_lr);
+                   }
+                 if (cs->second.r2save_)
+                   {
+                     write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
+                     p += 4;
+                   }
+                 Address from = this->stub_address() + (p - oview);
+                 Address delta = plt_addr - from;
+                 p = build_power10_offset<big_endian>(p, delta, from & 4, true);
+                 write_insn<big_endian>(p, mtctr_12);
+                 p += 4;
+                 if (cs->second.r2save_
+                     && !cs->second.localentry0_
+                     && this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+                   this->build_tls_opt_tail(p);
+                 else
+                   write_insn<big_endian>(p, bctr);
+               }
            }
        }
 
@@ -6158,19 +6317,76 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
          p = oview + off;
          Address loc = this->stub_address() + off;
          Address delta = bs->first.dest_ - loc;
-         if (bs->second.notoc_ || delta + (1 << 25) >= 2 << 25)
+         if (this->targ_->power10_stubs_auto())
            {
-             unsigned char* startp = p;
-             p = build_power10_offset<big_endian>(p, delta, loc & 4, false);
-             delta -= p - startp;
+             if (bs->second.notoc_)
+               {
+                 unsigned char* startp = p;
+                 p = build_power10_offset<big_endian>(p, delta,
+                                                      loc & 4, false);
+                 delta -= p - startp;
+                 startp = p;
+                 if (delta + (1 << 25) < 2 << 25)
+                   write_insn<big_endian>(p, b | (delta & 0x3fffffc));
+                 else
+                   {
+                     write_insn<big_endian>(p, mtctr_12);
+                     p += 4;
+                     write_insn<big_endian>(p, bctr);
+                   }
+                 p += 4;
+                 delta -= p - startp;
+               }
+             if (bs->second.toc_)
+               {
+                 if (delta + (1 << 25) >= 2 << 25)
+                   {
+                     Address brlt_addr
+                       = this->targ_->find_branch_lookup_table(bs->first.dest_);
+                     gold_assert(brlt_addr != invalid_address);
+                     brlt_addr += this->targ_->brlt_section()->address();
+                     Address got_addr = got_os_addr + bs->first.toc_base_off_;
+                     Address brltoff = brlt_addr - got_addr;
+                     if (ha(brltoff) == 0)
+                       {
+                         write_insn<big_endian>(p, ld_12_2 + l(brltoff));
+                         p += 4;
+                       }
+                     else
+                       {
+                         write_insn<big_endian>(p, addis_12_2 + ha(brltoff));
+                         p += 4;
+                         write_insn<big_endian>(p, ld_12_12 + l(brltoff));
+                         p += 4;
+                       }
+                   }
+                 if (delta + (1 << 25) < 2 << 25)
+                   write_insn<big_endian>(p, b | (delta & 0x3fffffc));
+                 else
+                   {
+                     write_insn<big_endian>(p, mtctr_12);
+                     p += 4;
+                     write_insn<big_endian>(p, bctr);
+                   }
+               }
            }
-         if (delta + (1 << 25) < 2 << 25)
-           write_insn<big_endian>(p, b | (delta & 0x3fffffc));
          else
            {
-             write_insn<big_endian>(p, mtctr_12);
-             p += 4;
-             write_insn<big_endian>(p, bctr);
+             if (bs->second.notoc_ || delta + (1 << 25) >= 2 << 25)
+               {
+                 unsigned char* startp = p;
+                 p = build_power10_offset<big_endian>(p, delta,
+                                                      loc & 4, false);
+                 delta -= p - startp;
+               }
+             if (delta + (1 << 25) < 2 << 25)
+               write_insn<big_endian>(p, b | (delta & 0x3fffffc));
+             else
+               {
+                 write_insn<big_endian>(p, mtctr_12);
+                 p += 4;
+                 write_insn<big_endian>(p, bctr);
+               }
            }
        }
     }
@@ -6194,7 +6410,11 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
              Address plt_addr = pltoff + plt->address();
 
              p = oview + cs->second.off_;
-             this->build_tls_opt_head(&p, cs);
+             if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+               {
+                 bool save_lr = cs->second.r2save_ && !cs->second.localentry0_;
+                 this->build_tls_opt_head(&p, save_lr);
+               }
              if (cs->second.r2save_)
                {
                  write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
@@ -6231,7 +6451,11 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
                }
              write_insn<big_endian>(p, mtctr_12);
              p += 4;
-             if (!this->build_tls_opt_tail(p, cs))
+             if (cs->second.r2save_
+                 && !cs->second.localentry0_
+                 && this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+               this->build_tls_opt_tail(p);
+             else
                write_insn<big_endian>(p, bctr);
            }
        }
@@ -6282,8 +6506,12 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
                }
 
              p = oview + cs->second.off_;
-             if (this->build_tls_opt_head(&p, cs))
-               use_fake_dep = thread_safe;
+             if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+               {
+                 bool save_lr = cs->second.r2save_ && !cs->second.localentry0_;
+                 this->build_tls_opt_head(&p, save_lr);
+                 use_fake_dep = thread_safe;
+               }
              if (cs->second.r2save_)
                {
                  write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
@@ -6345,8 +6573,10 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
                  write_insn<big_endian>(p, ld_2_2 + l(off + 8));
                  p += 4;
                }
-             if (this->build_tls_opt_tail(p, cs))
-               ;
+             if (cs->second.r2save_
+                 && !cs->second.localentry0_
+                 && this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+               this->build_tls_opt_tail(p);
              else if (thread_safe && !use_fake_dep)
                {
                  write_insn<big_endian>(p, cmpldi_2_0);
@@ -6427,7 +6657,8 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
              plt_addr += plt->address();
 
              p = oview + cs->second.off_;
-             this->build_tls_opt_head(&p, cs);
+             if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+               this->build_tls_opt_head(&p, false);
              if (parameters->options().output_is_position_independent())
                {
                  Address got_addr;
@@ -7286,10 +7517,10 @@ Target_powerpc<size, big_endian>::Scan::get_reference_flags(
     case elfcpp::R_PPC64_TLSLD:
     case elfcpp::R_PPC64_TPREL34:
     case elfcpp::R_PPC64_DTPREL34:
-    case elfcpp::R_PPC64_GOT_TLSGD34:
-    case elfcpp::R_PPC64_GOT_TLSLD34:
-    case elfcpp::R_PPC64_GOT_TPREL34:
-    case elfcpp::R_PPC64_GOT_DTPREL34:
+    case elfcpp::R_PPC64_GOT_TLSGD_PCREL34:
+    case elfcpp::R_PPC64_GOT_TLSLD_PCREL34:
+    case elfcpp::R_PPC64_GOT_TPREL_PCREL34:
+    case elfcpp::R_PPC64_GOT_DTPREL_PCREL34:
       ref = Symbol::TLS_REF;
       break;
 
@@ -7883,7 +8114,7 @@ Target_powerpc<size, big_endian>::Scan::local(
       target->got_section(symtab, layout);
       break;
 
-    case elfcpp::R_PPC64_GOT_TLSGD34:
+    case elfcpp::R_PPC64_GOT_TLSGD_PCREL34:
     case elfcpp::R_POWERPC_GOT_TLSGD16:
     case elfcpp::R_POWERPC_GOT_TLSGD16_LO:
     case elfcpp::R_POWERPC_GOT_TLSGD16_HI:
@@ -7908,7 +8139,7 @@ Target_powerpc<size, big_endian>::Scan::local(
       }
       break;
 
-    case elfcpp::R_PPC64_GOT_TLSLD34:
+    case elfcpp::R_PPC64_GOT_TLSLD_PCREL34:
     case elfcpp::R_POWERPC_GOT_TLSLD16:
     case elfcpp::R_POWERPC_GOT_TLSLD16_LO:
     case elfcpp::R_POWERPC_GOT_TLSLD16_HI:
@@ -7932,7 +8163,7 @@ Target_powerpc<size, big_endian>::Scan::local(
       }
       break;
 
-    case elfcpp::R_PPC64_GOT_DTPREL34:
+    case elfcpp::R_PPC64_GOT_DTPREL_PCREL34:
     case elfcpp::R_POWERPC_GOT_DTPREL16:
     case elfcpp::R_POWERPC_GOT_DTPREL16_LO:
     case elfcpp::R_POWERPC_GOT_DTPREL16_HI:
@@ -7945,7 +8176,7 @@ Target_powerpc<size, big_endian>::Scan::local(
       }
       break;
 
-    case elfcpp::R_PPC64_GOT_TPREL34:
+    case elfcpp::R_PPC64_GOT_TPREL_PCREL34:
     case elfcpp::R_POWERPC_GOT_TPREL16:
     case elfcpp::R_POWERPC_GOT_TPREL16_LO:
     case elfcpp::R_POWERPC_GOT_TPREL16_HI:
@@ -8144,10 +8375,6 @@ Target_powerpc<size, big_endian>::Scan::local(
 
   switch (r_type)
     {
-    case elfcpp::R_POWERPC_TPREL16:
-    case elfcpp::R_POWERPC_TPREL16_LO:
-    case elfcpp::R_POWERPC_TPREL16_HI:
-    case elfcpp::R_POWERPC_TPREL16_HA:
     case elfcpp::R_PPC64_TPREL16_DS:
     case elfcpp::R_PPC64_TPREL16_LO_DS:
     case elfcpp::R_PPC64_TPREL16_HIGH:
@@ -8157,12 +8384,55 @@ Target_powerpc<size, big_endian>::Scan::local(
     case elfcpp::R_PPC64_TPREL16_HIGHEST:
     case elfcpp::R_PPC64_TPREL16_HIGHESTA:
     case elfcpp::R_PPC64_TPREL34:
+      if (size != 64)
+       break;
+      // Fall through.
+    case elfcpp::R_POWERPC_TPREL16:
+    case elfcpp::R_POWERPC_TPREL16_LO:
+    case elfcpp::R_POWERPC_TPREL16_HI:
+    case elfcpp::R_POWERPC_TPREL16_HA:
       layout->set_has_static_tls();
       break;
     default:
       break;
     }
 
+  switch (r_type)
+    {
+    case elfcpp::R_POWERPC_TPREL16_HA:
+      if (target->tprel_opt())
+       {
+         section_size_type slen;
+         const unsigned char* view = NULL;
+         view = ppc_object->section_contents(data_shndx, &slen, false);
+         section_size_type off
+           = convert_to_section_size_type(reloc.get_r_offset()) & -4;
+         if (off < slen)
+           {
+             uint32_t insn = elfcpp::Swap<32, big_endian>::readval(view + off);
+             if ((insn & ((0x3fu << 26) | 0x1f << 16))
+                 != ((15u << 26) | ((size == 32 ? 2 : 13) << 16)))
+               target->set_tprel_opt(false);
+           }
+       }
+      break;
+
+    case elfcpp::R_PPC64_TPREL16_HIGH:
+    case elfcpp::R_PPC64_TPREL16_HIGHA:
+    case elfcpp::R_PPC64_TPREL16_HIGHER:
+    case elfcpp::R_PPC64_TPREL16_HIGHERA:
+    case elfcpp::R_PPC64_TPREL16_HIGHEST:
+    case elfcpp::R_PPC64_TPREL16_HIGHESTA:
+      if (size != 64)
+       break;
+      // Fall through.
+    case elfcpp::R_POWERPC_TPREL16_HI:
+      target->set_tprel_opt(false);
+      break;
+    default:
+      break;
+    }
+
   switch (r_type)
     {
     case elfcpp::R_PPC64_D34:
@@ -8177,10 +8447,10 @@ Target_powerpc<size, big_endian>::Scan::local(
     case elfcpp::R_PPC64_PLT_PCREL34:
     case elfcpp::R_PPC64_PLT_PCREL34_NOTOC:
     case elfcpp::R_PPC64_GOT_PCREL34:
-    case elfcpp::R_PPC64_GOT_TLSGD34:
-    case elfcpp::R_PPC64_GOT_TLSLD34:
-    case elfcpp::R_PPC64_GOT_DTPREL34:
-    case elfcpp::R_PPC64_GOT_TPREL34:
+    case elfcpp::R_PPC64_GOT_TLSGD_PCREL34:
+    case elfcpp::R_PPC64_GOT_TLSLD_PCREL34:
+    case elfcpp::R_PPC64_GOT_DTPREL_PCREL34:
+    case elfcpp::R_PPC64_GOT_TPREL_PCREL34:
       target->set_power10_stubs();
       break;
     default:
@@ -8616,7 +8886,7 @@ Target_powerpc<size, big_endian>::Scan::global(
       target->got_section(symtab, layout);
       break;
 
-    case elfcpp::R_PPC64_GOT_TLSGD34:
+    case elfcpp::R_PPC64_GOT_TLSGD_PCREL34:
     case elfcpp::R_POWERPC_GOT_TLSGD16:
     case elfcpp::R_POWERPC_GOT_TLSGD16_LO:
     case elfcpp::R_POWERPC_GOT_TLSGD16_HI:
@@ -8665,7 +8935,7 @@ Target_powerpc<size, big_endian>::Scan::global(
       }
       break;
 
-    case elfcpp::R_PPC64_GOT_TLSLD34:
+    case elfcpp::R_PPC64_GOT_TLSLD_PCREL34:
     case elfcpp::R_POWERPC_GOT_TLSLD16:
     case elfcpp::R_POWERPC_GOT_TLSLD16_LO:
     case elfcpp::R_POWERPC_GOT_TLSLD16_HI:
@@ -8689,7 +8959,7 @@ Target_powerpc<size, big_endian>::Scan::global(
       }
       break;
 
-    case elfcpp::R_PPC64_GOT_DTPREL34:
+    case elfcpp::R_PPC64_GOT_DTPREL_PCREL34:
     case elfcpp::R_POWERPC_GOT_DTPREL16:
     case elfcpp::R_POWERPC_GOT_DTPREL16_LO:
     case elfcpp::R_POWERPC_GOT_DTPREL16_HI:
@@ -8709,7 +8979,7 @@ Target_powerpc<size, big_endian>::Scan::global(
       }
       break;
 
-    case elfcpp::R_PPC64_GOT_TPREL34:
+    case elfcpp::R_PPC64_GOT_TPREL_PCREL34:
     case elfcpp::R_POWERPC_GOT_TPREL16:
     case elfcpp::R_POWERPC_GOT_TPREL16_LO:
     case elfcpp::R_POWERPC_GOT_TPREL16_HI:
@@ -8902,10 +9172,6 @@ Target_powerpc<size, big_endian>::Scan::global(
 
   switch (r_type)
     {
-    case elfcpp::R_POWERPC_TPREL16:
-    case elfcpp::R_POWERPC_TPREL16_LO:
-    case elfcpp::R_POWERPC_TPREL16_HI:
-    case elfcpp::R_POWERPC_TPREL16_HA:
     case elfcpp::R_PPC64_TPREL16_DS:
     case elfcpp::R_PPC64_TPREL16_LO_DS:
     case elfcpp::R_PPC64_TPREL16_HIGH:
@@ -8915,12 +9181,55 @@ Target_powerpc<size, big_endian>::Scan::global(
     case elfcpp::R_PPC64_TPREL16_HIGHEST:
     case elfcpp::R_PPC64_TPREL16_HIGHESTA:
     case elfcpp::R_PPC64_TPREL34:
+      if (size != 64)
+       break;
+      // Fall through.
+    case elfcpp::R_POWERPC_TPREL16:
+    case elfcpp::R_POWERPC_TPREL16_LO:
+    case elfcpp::R_POWERPC_TPREL16_HI:
+    case elfcpp::R_POWERPC_TPREL16_HA:
       layout->set_has_static_tls();
       break;
     default:
       break;
     }
 
+  switch (r_type)
+    {
+    case elfcpp::R_POWERPC_TPREL16_HA:
+      if (target->tprel_opt())
+       {
+         section_size_type slen;
+         const unsigned char* view = NULL;
+         view = ppc_object->section_contents(data_shndx, &slen, false);
+         section_size_type off
+           = convert_to_section_size_type(reloc.get_r_offset()) & -4;
+         if (off < slen)
+           {
+             uint32_t insn = elfcpp::Swap<32, big_endian>::readval(view + off);
+             if ((insn & ((0x3fu << 26) | 0x1f << 16))
+                 != ((15u << 26) | ((size == 32 ? 2 : 13) << 16)))
+               target->set_tprel_opt(false);
+           }
+       }
+      break;
+
+    case elfcpp::R_PPC64_TPREL16_HIGH:
+    case elfcpp::R_PPC64_TPREL16_HIGHA:
+    case elfcpp::R_PPC64_TPREL16_HIGHER:
+    case elfcpp::R_PPC64_TPREL16_HIGHERA:
+    case elfcpp::R_PPC64_TPREL16_HIGHEST:
+    case elfcpp::R_PPC64_TPREL16_HIGHESTA:
+      if (size != 64)
+       break;
+      // Fall through.
+    case elfcpp::R_POWERPC_TPREL16_HI:
+      target->set_tprel_opt(false);
+      break;
+    default:
+      break;
+    }
+
   switch (r_type)
     {
     case elfcpp::R_PPC64_D34:
@@ -8935,10 +9244,10 @@ Target_powerpc<size, big_endian>::Scan::global(
     case elfcpp::R_PPC64_PLT_PCREL34:
     case elfcpp::R_PPC64_PLT_PCREL34_NOTOC:
     case elfcpp::R_PPC64_GOT_PCREL34:
-    case elfcpp::R_PPC64_GOT_TLSGD34:
-    case elfcpp::R_PPC64_GOT_TLSLD34:
-    case elfcpp::R_PPC64_GOT_DTPREL34:
-    case elfcpp::R_PPC64_GOT_TPREL34:
+    case elfcpp::R_PPC64_GOT_TLSGD_PCREL34:
+    case elfcpp::R_PPC64_GOT_TLSLD_PCREL34:
+    case elfcpp::R_PPC64_GOT_DTPREL_PCREL34:
+    case elfcpp::R_PPC64_GOT_TPREL_PCREL34:
       target->set_power10_stubs();
       break;
     default:
@@ -10087,10 +10396,17 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
                  const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
                  elfcpp::Shdr<size, big_endian> shdr(relinfo->reloc_shdr);
                  size_t reloc_count = shdr.get_sh_size() / reloc_size;
+                 if (size == 64
+                     && r_type != elfcpp::R_PPC64_REL24_NOTOC)
+                   value += ent->tocoff_;
                  if (size == 64
                      && ent->r2save_
                      && r_type == elfcpp::R_PPC64_REL24_NOTOC)
-                   value += 4;
+                   {
+                     if (!(target->power10_stubs()
+                           && target->power10_stubs_auto()))
+                       value += 4;
+                   }
                  else if (size == 64
                           && ent->r2save_
                           && relnum < reloc_count - 1)
@@ -10263,7 +10579,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
           || r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO
           || r_type == elfcpp::R_POWERPC_GOT_TLSGD16_HI
           || r_type == elfcpp::R_POWERPC_GOT_TLSGD16_HA
-          || r_type == elfcpp::R_PPC64_GOT_TLSGD34)
+          || r_type == elfcpp::R_PPC64_GOT_TLSGD_PCREL34)
     {
       // First instruction of a global dynamic sequence, arg setup insn.
       const bool final = gsym == NULL || gsym->final_value_is_known();
@@ -10285,14 +10601,14 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
              gold_assert(object->local_has_got_offset(r_sym, got_type));
              value = object->local_got_offset(r_sym, got_type);
            }
-         if (r_type == elfcpp::R_PPC64_GOT_TLSGD34)
+         if (r_type == elfcpp::R_PPC64_GOT_TLSGD_PCREL34)
            value += target->got_section()->address();
          else
            value -= target->got_section()->got_base_offset(object);
        }
       if (tls_type == tls::TLSOPT_TO_IE)
        {
-         if (r_type == elfcpp::R_PPC64_GOT_TLSGD34)
+         if (r_type == elfcpp::R_PPC64_GOT_TLSGD_PCREL34)
            {
              Insn* iview = reinterpret_cast<Insn*>(view);
              uint64_t pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
@@ -10303,7 +10619,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
              elfcpp::Swap<32, big_endian>::writeval(iview, pinsn >> 32);
              elfcpp::Swap<32, big_endian>::writeval(iview + 1,
                                                     pinsn & 0xffffffff);
-             r_type = elfcpp::R_PPC64_GOT_TPREL34;
+             r_type = elfcpp::R_PPC64_GOT_TPREL_PCREL34;
            }
          else
            {
@@ -10325,7 +10641,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
        }
       else if (tls_type == tls::TLSOPT_TO_LE)
        {
-         if (r_type == elfcpp::R_PPC64_GOT_TLSGD34)
+         if (r_type == elfcpp::R_PPC64_GOT_TLSGD_PCREL34)
            {
              Insn* iview = reinterpret_cast<Insn*>(view);
              uint64_t pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
@@ -10369,14 +10685,14 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
           || r_type == elfcpp::R_POWERPC_GOT_TLSLD16_LO
           || r_type == elfcpp::R_POWERPC_GOT_TLSLD16_HI
           || r_type == elfcpp::R_POWERPC_GOT_TLSLD16_HA
-          || r_type == elfcpp::R_PPC64_GOT_TLSLD34)
+          || r_type == elfcpp::R_PPC64_GOT_TLSLD_PCREL34)
     {
       // First instruction of a local dynamic sequence, arg setup insn.
       const tls::Tls_optimization tls_type = target->optimize_tls_ld();
       if (tls_type == tls::TLSOPT_NONE)
        {
          value = target->tlsld_got_offset();
-         if (r_type == elfcpp::R_PPC64_GOT_TLSLD34)
+         if (r_type == elfcpp::R_PPC64_GOT_TLSLD_PCREL34)
            value += target->got_section()->address();
          else
            value -= target->got_section()->got_base_offset(object);
@@ -10384,7 +10700,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       else
        {
          gold_assert(tls_type == tls::TLSOPT_TO_LE);
-         if (r_type == elfcpp::R_PPC64_GOT_TLSLD34)
+         if (r_type == elfcpp::R_PPC64_GOT_TLSLD_PCREL34)
            {
              Insn* iview = reinterpret_cast<Insn*>(view);
              uint64_t pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
@@ -10425,7 +10741,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
           || r_type == elfcpp::R_POWERPC_GOT_DTPREL16_LO
           || r_type == elfcpp::R_POWERPC_GOT_DTPREL16_HI
           || r_type == elfcpp::R_POWERPC_GOT_DTPREL16_HA
-          || r_type == elfcpp::R_PPC64_GOT_DTPREL34)
+          || r_type == elfcpp::R_PPC64_GOT_DTPREL_PCREL34)
     {
       // Accesses relative to a local dynamic sequence address,
       // no optimisation here.
@@ -10439,7 +10755,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
          gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_DTPREL));
          value = object->local_got_offset(r_sym, GOT_TYPE_DTPREL);
        }
-      if (r_type == elfcpp::R_PPC64_GOT_DTPREL34)
+      if (r_type == elfcpp::R_PPC64_GOT_DTPREL_PCREL34)
        value += target->got_section()->address();
       else
        value -= target->got_section()->got_base_offset(object);
@@ -10448,7 +10764,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
           || r_type == elfcpp::R_POWERPC_GOT_TPREL16_LO
           || r_type == elfcpp::R_POWERPC_GOT_TPREL16_HI
           || r_type == elfcpp::R_POWERPC_GOT_TPREL16_HA
-          || r_type == elfcpp::R_PPC64_GOT_TPREL34)
+          || r_type == elfcpp::R_PPC64_GOT_TPREL_PCREL34)
     {
       // First instruction of initial exec sequence.
       const bool final = gsym == NULL || gsym->final_value_is_known();
@@ -10465,7 +10781,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
              gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_TPREL));
              value = object->local_got_offset(r_sym, GOT_TYPE_TPREL);
            }
-         if (r_type == elfcpp::R_PPC64_GOT_TPREL34)
+         if (r_type == elfcpp::R_PPC64_GOT_TPREL_PCREL34)
            value += target->got_section()->address();
          else
            value -= target->got_section()->got_base_offset(object);
@@ -10473,7 +10789,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       else
        {
          gold_assert(tls_type == tls::TLSOPT_TO_LE);
-         if (r_type == elfcpp::R_PPC64_GOT_TPREL34)
+         if (r_type == elfcpp::R_PPC64_GOT_TPREL_PCREL34)
            {
              Insn* iview = reinterpret_cast<Insn*>(view);
              uint64_t pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
@@ -10707,8 +11023,14 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
                    value = (value - target->savres_section()->address()
                             + stub_table->branch_size());
                  else
-                   value = (stub_table->stub_address() + stub_table->plt_size()
-                            + ent->off_);
+                   {
+                     value = (stub_table->stub_address()
+                              + stub_table->plt_size()
+                              + ent->off_);
+                     if (size == 64
+                         && r_type != elfcpp::R_PPC64_REL24_NOTOC)
+                       value += ent->tocoff_;
+                   }
                  has_stub_value = true;
                }
            }
@@ -10745,10 +11067,10 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
     case elfcpp::R_PPC64_PLT_PCREL34:
     case elfcpp::R_PPC64_PLT_PCREL34_NOTOC:
     case elfcpp::R_PPC64_PCREL28:
-    case elfcpp::R_PPC64_GOT_TLSGD34:
-    case elfcpp::R_PPC64_GOT_TLSLD34:
-    case elfcpp::R_PPC64_GOT_TPREL34:
-    case elfcpp::R_PPC64_GOT_DTPREL34:
+    case elfcpp::R_PPC64_GOT_TLSGD_PCREL34:
+    case elfcpp::R_PPC64_GOT_TLSLD_PCREL34:
+    case elfcpp::R_PPC64_GOT_TPREL_PCREL34:
+    case elfcpp::R_PPC64_GOT_DTPREL_PCREL34:
     case elfcpp::R_PPC64_REL16_HIGHER34:
     case elfcpp::R_PPC64_REL16_HIGHERA34:
     case elfcpp::R_PPC64_REL16_HIGHEST34:
@@ -10890,10 +11212,9 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       break;
     }
 
-  if (size == 64
-      && (gsym
-         ? relative_value_is_known(gsym)
-         : relative_value_is_known(psymval)))
+  if (gsym
+      ? relative_value_is_known(gsym)
+      : relative_value_is_known(psymval))
     {
       Insn* iview;
       Insn* iview2;
@@ -10918,7 +11239,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
        case elfcpp::R_POWERPC_GOT_DTPREL16_HA:
        case elfcpp::R_POWERPC_GOT16_HA:
        case elfcpp::R_PPC64_TOC16_HA:
-         if (parameters->options().toc_optimize())
+         if (size == 64 && parameters->options().toc_optimize())
            {
              iview = reinterpret_cast<Insn*>(view - d_offset);
              insn = elfcpp::Swap<32, big_endian>::readval(iview);
@@ -10950,7 +11271,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
        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())
+         if (size == 64 && parameters->options().toc_optimize())
            {
              iview = reinterpret_cast<Insn*>(view - d_offset);
              insn = elfcpp::Swap<32, big_endian>::readval(iview);
@@ -10989,7 +11310,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
          break;
 
        case elfcpp::R_PPC64_GOT_PCREL34:
-         if (parameters->options().toc_optimize())
+         if (size == 64 && parameters->options().toc_optimize())
            {
              iview = reinterpret_cast<Insn*>(view);
              pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
@@ -11015,63 +11336,57 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
          break;
 
        case elfcpp::R_PPC64_PCREL34:
-         {
-           iview = reinterpret_cast<Insn*>(view);
-           pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
-           pinsn <<= 32;
-           pinsn |= elfcpp::Swap<32, big_endian>::readval(iview + 1);
-           if ((pinsn & ((-1ULL << 50) | (63ULL << 26)))
-               != ((1ULL << 58) | (2ULL << 56) | (1ULL << 52)
-                   | (14ULL << 26) /* paddi */))
-             break;
+         if (size == 64)
+           {
+             iview = reinterpret_cast<Insn*>(view);
+             pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
+             pinsn <<= 32;
+             pinsn |= elfcpp::Swap<32, big_endian>::readval(iview + 1);
+             if ((pinsn & ((-1ULL << 50) | (63ULL << 26)))
+                 != ((1ULL << 58) | (2ULL << 56) | (1ULL << 52)
+                     | (14ULL << 26) /* paddi */))
+               break;
 
-         pcrelopt:
-           const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
-           elfcpp::Shdr<size, big_endian> shdr(relinfo->reloc_shdr);
-           size_t reloc_count = shdr.get_sh_size() / reloc_size;
-           if (relnum >= reloc_count - 1)
-             break;
+           pcrelopt:
+             const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
+             elfcpp::Shdr<size, big_endian> shdr(relinfo->reloc_shdr);
+             size_t reloc_count = shdr.get_sh_size() / reloc_size;
+             if (relnum >= reloc_count - 1)
+               break;
 
-           Reltype next_rela(preloc + reloc_size);
-           if ((elfcpp::elf_r_type<size>(next_rela.get_r_info())
-                != elfcpp::R_PPC64_PCREL_OPT)
-               || next_rela.get_r_offset() != rela.get_r_offset())
-             break;
+             Reltype next_rela(preloc + reloc_size);
+             if ((elfcpp::elf_r_type<size>(next_rela.get_r_info())
+                  != elfcpp::R_PPC64_PCREL_OPT)
+                 || next_rela.get_r_offset() != rela.get_r_offset())
+               break;
 
-           Address off = next_rela.get_r_addend();
-           if (off == 0)
-             off = 8; // zero means next insn.
-           if (off + rela.get_r_offset() + 4 > view_size)
-             break;
+             Address off = next_rela.get_r_addend();
+             if (off == 0)
+               off = 8; // zero means next insn.
+             if (off + rela.get_r_offset() + 4 > view_size)
+               break;
 
-           iview2 = reinterpret_cast<Insn*>(view + off);
-           pinsn2 = elfcpp::Swap<32, big_endian>::readval(iview2);
-           pinsn2 <<= 32;
-           if ((pinsn2 & (63ULL << 58)) == 1ULL << 58)
-             break;
-           if (xlate_pcrel_opt(&pinsn, &pinsn2))
-             {
-               elfcpp::Swap<32, big_endian>::writeval(iview, pinsn >> 32);
-               elfcpp::Swap<32, big_endian>::writeval(iview + 1,
-                                                      pinsn & 0xffffffff);
-               elfcpp::Swap<32, big_endian>::writeval(iview2, pinsn2 >> 32);
-             }
-         }
+             iview2 = reinterpret_cast<Insn*>(view + off);
+             pinsn2 = elfcpp::Swap<32, big_endian>::readval(iview2);
+             pinsn2 <<= 32;
+             if ((pinsn2 & (63ULL << 58)) == 1ULL << 58)
+               break;
+             if (xlate_pcrel_opt(&pinsn, &pinsn2))
+               {
+                 elfcpp::Swap<32, big_endian>::writeval(iview, pinsn >> 32);
+                 elfcpp::Swap<32, big_endian>::writeval(iview + 1,
+                                                        pinsn & 0xffffffff);
+                 elfcpp::Swap<32, big_endian>::writeval(iview2, pinsn2 >> 32);
+               }
+           }
          break;
 
        case elfcpp::R_POWERPC_TPREL16_HA:
-         if (parameters->options().tls_optimize() && value + 0x8000 < 0x10000)
+         if (target->tprel_opt() && value + 0x8000 < 0x10000)
            {
              Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
-             Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
-             if ((insn & ((0x3f << 26) | 0x1f << 16))
-                 != ((15u << 26) | ((size == 32 ? 2 : 13) << 16)))
-               ;
-             else
-               {
-                 elfcpp::Swap<32, big_endian>::writeval(iview, nop);
-                 return true;
-               }
+             elfcpp::Swap<32, big_endian>::writeval(iview, nop);
+             return true;
            }
          break;
 
@@ -11081,7 +11396,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
            break;
          // Fall through.
        case elfcpp::R_POWERPC_TPREL16_LO:
-         if (parameters->options().tls_optimize() && value + 0x8000 < 0x10000)
+         if (target->tprel_opt() && value + 0x8000 < 0x10000)
            {
              Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
              Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
@@ -11092,29 +11407,12 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
          break;
 
        case elfcpp::R_PPC64_ENTRY:
-         value = (target->got_section()->output_section()->address()
-                  + object->toc_base_offset());
-         if (value + 0x80008000 <= 0xffffffff
-             && !parameters->options().output_is_position_independent())
-           {
-             Insn* iview = reinterpret_cast<Insn*>(view);
-             Insn insn1 = elfcpp::Swap<32, big_endian>::readval(iview);
-             Insn insn2 = elfcpp::Swap<32, big_endian>::readval(iview + 1);
-
-             if ((insn1 & ~0xfffc) == ld_2_12
-                 && insn2 == add_2_2_12)
-               {
-                 insn1 = lis_2 + ha(value);
-                 elfcpp::Swap<32, big_endian>::writeval(iview, insn1);
-                 insn2 = addi_2_2 + l(value);
-                 elfcpp::Swap<32, big_endian>::writeval(iview + 1, insn2);
-                 return true;
-               }
-           }
-         else
+         if (size == 64)
            {
-             value -= address;
-             if (value + 0x80008000 <= 0xffffffff)
+             value = (target->got_section()->output_section()->address()
+                      + object->toc_base_offset());
+             if (value + 0x80008000 <= 0xffffffff
+                 && !parameters->options().output_is_position_independent())
                {
                  Insn* iview = reinterpret_cast<Insn*>(view);
                  Insn insn1 = elfcpp::Swap<32, big_endian>::readval(iview);
@@ -11123,13 +11421,33 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
                  if ((insn1 & ~0xfffc) == ld_2_12
                      && insn2 == add_2_2_12)
                    {
-                     insn1 = addis_2_12 + ha(value);
+                     insn1 = lis_2 + ha(value);
                      elfcpp::Swap<32, big_endian>::writeval(iview, insn1);
                      insn2 = addi_2_2 + l(value);
                      elfcpp::Swap<32, big_endian>::writeval(iview + 1, insn2);
                      return true;
                    }
                }
+             else
+               {
+                 value -= address;
+                 if (value + 0x80008000 <= 0xffffffff)
+                   {
+                     Insn* iview = reinterpret_cast<Insn*>(view);
+                     Insn insn1 = elfcpp::Swap<32, big_endian>::readval(iview);
+                     Insn insn2 = elfcpp::Swap<32, big_endian>::readval(iview + 1);
+
+                     if ((insn1 & ~0xfffc) == ld_2_12
+                         && insn2 == add_2_2_12)
+                       {
+                         insn1 = addis_2_12 + ha(value);
+                         elfcpp::Swap<32, big_endian>::writeval(iview, insn1);
+                         insn2 = addi_2_2 + l(value);
+                         elfcpp::Swap<32, big_endian>::writeval(iview + 1, insn2);
+                         return true;
+                       }
+                   }
+               }
            }
          break;
 
@@ -11141,7 +11459,8 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
          //            lis 2,.TOC.@ha
          //            addi 2,2,.TOC.@l
          // if .TOC. is in range.  */
-         if (value + address - 4 + 0x80008000 <= 0xffffffff
+         if (size == 64
+             && value + address - 4 + 0x80008000 <= 0xffffffff
              && relnum + 1 > 1
              && preloc != NULL
              && target->abiversion() >= 2
@@ -11285,10 +11604,10 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
     case elfcpp::R_PPC64_PCREL28:
     case elfcpp::R_PPC64_TPREL34:
     case elfcpp::R_PPC64_DTPREL34:
-    case elfcpp::R_PPC64_GOT_TLSGD34:
-    case elfcpp::R_PPC64_GOT_TLSLD34:
-    case elfcpp::R_PPC64_GOT_TPREL34:
-    case elfcpp::R_PPC64_GOT_DTPREL34:
+    case elfcpp::R_PPC64_GOT_TLSGD_PCREL34:
+    case elfcpp::R_PPC64_GOT_TLSLD_PCREL34:
+    case elfcpp::R_PPC64_GOT_TPREL_PCREL34:
+    case elfcpp::R_PPC64_GOT_DTPREL_PCREL34:
       overflow = Reloc::CHECK_SIGNED;
       break;
     }
@@ -11587,10 +11906,10 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
     case elfcpp::R_PPC64_PLT_PCREL34_NOTOC:
     case elfcpp::R_PPC64_TPREL34:
     case elfcpp::R_PPC64_DTPREL34:
-    case elfcpp::R_PPC64_GOT_TLSGD34:
-    case elfcpp::R_PPC64_GOT_TLSLD34:
-    case elfcpp::R_PPC64_GOT_TPREL34:
-    case elfcpp::R_PPC64_GOT_DTPREL34:
+    case elfcpp::R_PPC64_GOT_TLSGD_PCREL34:
+    case elfcpp::R_PPC64_GOT_TLSLD_PCREL34:
+    case elfcpp::R_PPC64_GOT_TPREL_PCREL34:
+    case elfcpp::R_PPC64_GOT_DTPREL_PCREL34:
       if (size == 32)
        goto unsupp;
       status = Reloc::addr34(view, value, overflow);