gdb: add target_ops::supports_displaced_step
[deliverable/binutils-gdb.git] / gold / powerpc.cc
index e251a9341f482aab87d85928b21129490ca67783..318c41744b5c52f73a4386246e1812c2851255c1 100644 (file)
@@ -1,6 +1,6 @@
 // powerpc.cc -- powerpc target support for gold.
 
-// Copyright (C) 2008-2019 Free Software Foundation, Inc.
+// Copyright (C) 2008-2020 Free Software Foundation, Inc.
 // Written by David S. Miller <davem@davemloft.net>
 //        and David Edelsohn <edelsohn@gnu.org>
 
@@ -79,6 +79,7 @@ struct Stub_table_owner
   const Output_section::Input_section* owner;
 };
 
+template<int size>
 inline bool is_branch_reloc(unsigned int);
 
 template<int size>
@@ -248,6 +249,12 @@ public:
   make_toc_relative(Target_powerpc<size, big_endian>* target,
                    Address* value);
 
+  bool
+  make_got_relative(Target_powerpc<size, big_endian>* target,
+                   const Symbol_value<size>* psymval,
+                   Address addend,
+                   Address* value);
+
   // Perform the Sized_relobj_file method, then set up opd info from
   // .opd relocs.
   void
@@ -390,6 +397,14 @@ public:
   ppc64_local_entry_offset(unsigned int symndx) const
   { return elfcpp::ppc64_decode_local_entry(this->st_other_[symndx] >> 5); }
 
+  bool
+  ppc64_needs_toc(const Symbol* sym) const
+  { return sym->nonvis() > 1 << 3; }
+
+  bool
+  ppc64_needs_toc(unsigned int symndx) const
+  { return this->st_other_[symndx] > 1 << 5; }
+
   // The contents of the .gnu.attributes section if there is one.
   const Attributes_section_data*
   attributes_section_data() const
@@ -632,7 +647,7 @@ class Target_powerpc : public Sized_target<size, big_endian>
       glink_(NULL), rela_dyn_(NULL), copy_relocs_(),
       tlsld_got_offset_(-1U),
       stub_tables_(), branch_lookup_table_(), branch_info_(), tocsave_loc_(),
-      plt_thread_safe_(false), plt_localentry0_(false),
+      power10_stubs_(false), plt_thread_safe_(false), plt_localentry0_(false),
       plt_localentry0_init_(false), has_localentry0_(false),
       has_tls_get_addr_opt_(false),
       relax_failed_(false), relax_fail_count_(0),
@@ -1063,6 +1078,16 @@ class Target_powerpc : public Sized_target<size, big_endian>
     sym->set_dynsym_index(-1U);
   }
 
+  bool
+  power10_stubs() const
+  { return this->power10_stubs_; }
+
+  void
+  set_power10_stubs()
+  {
+    this->power10_stubs_ = true;
+  }
+
   bool
   plt_thread_safe() const
   { return this->plt_thread_safe_; }
@@ -1179,7 +1204,7 @@ class Target_powerpc : public Sized_target<size, big_endian>
 
   // Merge object attributes from input object with those in the output.
   void
-  merge_object_attributes(const char*, const Attributes_section_data*);
+  merge_object_attributes(const Object*, const Attributes_section_data*);
 
  private:
 
@@ -1237,14 +1262,20 @@ class Target_powerpc : public Sized_target<size, big_endian>
     maybe_skip_tls_get_addr_call(Target_powerpc<size, big_endian>* target,
                                 unsigned int r_type, const Symbol* gsym)
     {
-      bool is_tls_call = ((r_type == elfcpp::R_POWERPC_REL24
-                          || r_type == elfcpp::R_PPC_PLTREL24
-                          || is_plt16_reloc<size>(r_type)
-                          || r_type == elfcpp::R_POWERPC_PLTSEQ
-                          || r_type == elfcpp::R_POWERPC_PLTCALL)
-                         && gsym != NULL
-                         && (gsym == target->tls_get_addr()
-                             || gsym == target->tls_get_addr_opt()));
+      bool is_tls_call
+       = ((r_type == elfcpp::R_POWERPC_REL24
+           || (size == 64 && r_type == elfcpp::R_PPC64_REL24_NOTOC)
+           || r_type == elfcpp::R_PPC_PLTREL24
+           || is_plt16_reloc<size>(r_type)
+           || r_type == elfcpp::R_PPC64_PLT_PCREL34
+           || r_type == elfcpp::R_PPC64_PLT_PCREL34_NOTOC
+           || r_type == elfcpp::R_POWERPC_PLTSEQ
+           || r_type == elfcpp::R_POWERPC_PLTCALL
+           || r_type == elfcpp::R_PPC64_PLTSEQ_NOTOC
+           || r_type == elfcpp::R_PPC64_PLTCALL_NOTOC)
+          && gsym != NULL
+          && (gsym == target->tls_get_addr()
+              || gsym == target->tls_get_addr_opt()));
       Tls_get_addr last_tls = this->tls_get_addr_state_;
       this->tls_get_addr_state_ = NOT_EXPECTED;
       if (is_tls_call && last_tls != EXPECTED)
@@ -1262,7 +1293,7 @@ class Target_powerpc : public Sized_target<size, big_endian>
     // 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.
+    // usual R_POWERPC_REL24 or R_PPC_PLTREL24 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,
@@ -1328,7 +1359,7 @@ class Target_powerpc : public Sized_target<size, big_endian>
        }
       // For 32-bit and ELFv2, conservatively assume anything but calls to
       // function code might be taking the address of the function.
-      return !is_branch_reloc(r_type);
+      return !is_branch_reloc<size>(r_type);
     }
 
     inline bool
@@ -1349,7 +1380,7 @@ class Target_powerpc : public Sized_target<size, big_endian>
          if (ppcobj->abiversion() == 1)
            return false;
        }
-      return !is_branch_reloc(r_type);
+      return !is_branch_reloc<size>(r_type);
     }
 
     static bool
@@ -1656,6 +1687,7 @@ class Target_powerpc : public Sized_target<size, big_endian>
   Branches branch_info_;
   Tocsave_loc tocsave_loc_;
 
+  bool power10_stubs_;
   bool plt_thread_safe_;
   bool plt_localentry0_;
   bool plt_localentry0_init_;
@@ -1799,10 +1831,12 @@ Target::Target_info Target_powerpc<64, false>::powerpc_info =
   elfcpp::SHT_PROGBITS,        // unwind_section_type
 };
 
+template<int size>
 inline bool
 is_branch_reloc(unsigned int r_type)
 {
   return (r_type == elfcpp::R_POWERPC_REL24
+         || (size == 64 && r_type == elfcpp::R_PPC64_REL24_NOTOC)
          || r_type == elfcpp::R_PPC_PLTREL24
          || r_type == elfcpp::R_PPC_LOCAL24PC
          || r_type == elfcpp::R_POWERPC_REL14
@@ -1962,11 +1996,15 @@ private:
     typedef typename elfcpp::Swap<fieldsize, big_endian>::Valtype Valtype;
     Valtype* wv = reinterpret_cast<Valtype*>(view);
     Valtype val = elfcpp::Swap<fieldsize, big_endian>::readval(wv);
-    Valtype reloc = value >> right_shift;
+    if (overflow == CHECK_SIGNED)
+      value = static_cast<SignedAddress>(value) >> right_shift;
+    else
+      value = value >> right_shift;
+    Valtype reloc = value;
     val &= ~dst_mask;
     reloc &= dst_mask;
     elfcpp::Swap<fieldsize, big_endian>::writeval(wv, val | reloc);
-    return overflowed<valsize>(value >> right_shift, overflow);
+    return overflowed<valsize>(value, overflow);
   }
 
   // Do a simple RELA relocation, unaligned.
@@ -1989,11 +2027,15 @@ private:
     typedef typename elfcpp::Swap_unaligned<fieldsize, big_endian>::Valtype
       Valtype;
     Valtype val = elfcpp::Swap<fieldsize, big_endian>::readval(view);
-    Valtype reloc = value >> right_shift;
+    if (overflow == CHECK_SIGNED)
+      value = static_cast<SignedAddress>(value) >> right_shift;
+    else
+      value = value >> right_shift;
+    Valtype reloc = value;
     val &= ~dst_mask;
     reloc &= dst_mask;
     elfcpp::Swap_unaligned<fieldsize, big_endian>::writeval(view, val | reloc);
-    return overflowed<valsize>(value >> right_shift, overflow);
+    return overflowed<valsize>(value, overflow);
   }
 
 public:
@@ -2111,6 +2153,56 @@ public:
     elfcpp::Swap<32, big_endian>::writeval(wv, val);
     return overflowed<16>(value, overflow);
   }
+
+  // R_PPC64_D34
+  static inline Status
+  addr34(unsigned char *view, uint64_t value, Overflow_check overflow)
+  {
+    Status stat = This::template rela<32,18>(view, 16, 0x3ffff,
+                                            value, overflow);
+    This::rela<32,16>(view + 4, 0, 0xffff, value, CHECK_NONE);
+    return stat;
+  }
+
+  // R_PPC64_D34_HI30
+  static inline void
+  addr34_hi(unsigned char *view, uint64_t value)
+  { This::addr34(view, value >> 34, CHECK_NONE);}
+
+  // R_PPC64_D34_HA30
+  static inline void
+  addr34_ha(unsigned char *view, uint64_t value)
+  { This::addr34_hi(view, value + (1ULL << 33));}
+
+  // R_PPC64_D28
+  static inline Status
+  addr28(unsigned char *view, uint64_t value, Overflow_check overflow)
+  {
+    Status stat = This::template rela<32,12>(view, 16, 0xfff,
+                                            value, overflow);
+    This::rela<32,16>(view + 4, 0, 0xffff, value, CHECK_NONE);
+    return stat;
+  }
+
+  // R_PPC64_ADDR16_HIGHER34
+  static inline void
+  addr16_higher34(unsigned char* view, uint64_t value)
+  { This::addr16(view, value >> 34, CHECK_NONE); }
+
+  // R_PPC64_ADDR16_HIGHERA34
+  static inline void
+  addr16_highera34(unsigned char* view, uint64_t value)
+  { This::addr16_higher34(view, value + (1ULL << 33)); }
+
+  // R_PPC64_ADDR16_HIGHEST34
+  static inline void
+  addr16_highest34(unsigned char* view, uint64_t value)
+  { This::addr16(view, value >> 50, CHECK_NONE); }
+
+  // R_PPC64_ADDR16_HIGHESTA34
+  static inline void
+  addr16_highesta34(unsigned char* view, uint64_t value)
+  { This::addr16_highest34(view, value + (1ULL << 33)); }
 };
 
 // Set ABI version for input and output.
@@ -2309,6 +2401,25 @@ Powerpc_relobj<size, big_endian>::make_toc_relative(
   return true;
 }
 
+template<int size, bool big_endian>
+bool
+Powerpc_relobj<size, big_endian>::make_got_relative(
+    Target_powerpc<size, big_endian>* target,
+    const Symbol_value<size>* psymval,
+    Address addend,
+    Address* value)
+{
+  Address addr = psymval->value(this, addend);
+  Address got_base = (target->got_section()->output_section()->address()
+                     + this->toc_base_offset());
+  addr -= got_base;
+  if (addr + 0x80008000 > 0xffffffff)
+    return false;
+
+  *value = addr;
+  return true;
+}
+
 // Perform the Sized_relobj_file method, then set up opd info from
 // .opd relocs.
 
@@ -3268,6 +3379,7 @@ Target_powerpc<size, big_endian>::group_sections(Layout* layout,
     }
 }
 
+template<int size>
 static unsigned long
 max_branch_delta (unsigned int r_type)
 {
@@ -3276,6 +3388,7 @@ max_branch_delta (unsigned int r_type)
       || r_type == elfcpp::R_POWERPC_REL14_BRNTAKEN)
     return 1L << 15;
   if (r_type == elfcpp::R_POWERPC_REL24
+      || (size == 64 && r_type == elfcpp::R_PPC64_REL24_NOTOC)
       || r_type == elfcpp::R_PPC_PLTREL24
       || r_type == elfcpp::R_PPC_LOCAL24PC)
     return 1L << 25;
@@ -3344,7 +3457,7 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
          && gsym != NULL
          && target->abiversion() >= 2
          && !parameters->options().output_is_position_independent()
-         && !is_branch_reloc(this->r_type_))
+         && !is_branch_reloc<size>(this->r_type_))
        target->glink_section()->add_global_entry(gsym);
       else
        {
@@ -3352,7 +3465,7 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
              && !(size == 32
                   && gsym != NULL
                   && !parameters->options().output_is_position_independent()
-                  && !is_branch_reloc(this->r_type_)))
+                  && !is_branch_reloc<size>(this->r_type_)))
            stub_table = this->object_->stub_table(this->shndx_);
          if (stub_table == NULL)
            {
@@ -3380,7 +3493,7 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
     }
   else
     {
-      Address max_branch_offset = max_branch_delta(this->r_type_);
+      Address max_branch_offset = max_branch_delta<size>(this->r_type_);
       if (max_branch_offset == 0)
        return true;
       Address from = this->object_->get_output_section_offset(this->shndx_);
@@ -3448,7 +3561,12 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
            return true;
        }
       Address delta = to - from;
-      if (delta + max_branch_offset >= 2 * max_branch_offset)
+      if (delta + max_branch_offset >= 2 * max_branch_offset
+         || (size == 64
+             && this->r_type_ == elfcpp::R_PPC64_REL24_NOTOC
+             && (gsym != NULL
+                 ? this->object_->ppc64_needs_toc(gsym)
+                 : this->object_->ppc64_needs_toc(this->r_sym_))))
        {
          if (stub_table == NULL)
            {
@@ -3620,6 +3738,40 @@ Target_powerpc<size, big_endian>::do_relax(int pass,
            return true;
        }
     }
+  bool do_resize = false;
+  for (typename Stub_tables::iterator p = this->stub_tables_.begin();
+       p != this->stub_tables_.end();
+       ++p)
+    if ((*p)->need_resize())
+      {
+       do_resize = true;
+       break;
+      }
+  if (do_resize)
+    {
+      this->branch_lookup_table_.clear();
+      for (typename Stub_tables::iterator p = this->stub_tables_.begin();
+          p != this->stub_tables_.end();
+          ++p)
+       (*p)->set_resizing(true);
+      for (typename Branches::const_iterator b = this->branch_info_.begin();
+          b != this->branch_info_.end();
+          b++)
+       {
+         if (!b->make_stub(one_stub_table, ifunc_stub_table, symtab)
+             && !this->relax_failed_)
+           {
+             this->relax_failed_ = true;
+             this->relax_fail_count_++;
+             if (this->relax_fail_count_ < 3)
+               return true;
+           }
+       }
+      for (typename Stub_tables::iterator p = this->stub_tables_.begin();
+          p != this->stub_tables_.end();
+          ++p)
+       (*p)->set_resizing(false);
+    }
 
   // Did anything change size?
   unsigned int num_huge_branches = this->branch_lookup_table_.size();
@@ -3987,11 +4139,13 @@ static const uint32_t add_3_12_13       = 0x7c6c6a14;
 static const uint32_t add_11_0_11      = 0x7d605a14;
 static const uint32_t add_11_2_11      = 0x7d625a14;
 static const uint32_t add_11_11_2      = 0x7d6b1214;
+static const uint32_t add_12_11_12     = 0x7d8b6214;
 static const uint32_t addi_0_12                = 0x380c0000;
 static const uint32_t addi_2_2         = 0x38420000;
 static const uint32_t addi_3_3         = 0x38630000;
 static const uint32_t addi_11_11       = 0x396b0000;
 static const uint32_t addi_12_1                = 0x39810000;
+static const uint32_t addi_12_11       = 0x398b0000;
 static const uint32_t addi_12_12       = 0x398c0000;
 static const uint32_t addis_0_2                = 0x3c020000;
 static const uint32_t addis_0_13       = 0x3c0d0000;
@@ -4001,6 +4155,7 @@ static const uint32_t addis_11_11 = 0x3d6b0000;
 static const uint32_t addis_11_30      = 0x3d7e0000;
 static const uint32_t addis_12_1       = 0x3d810000;
 static const uint32_t addis_12_2       = 0x3d820000;
+static const uint32_t addis_12_11      = 0x3d8b0000;
 static const uint32_t addis_12_12      = 0x3d8c0000;
 static const uint32_t b                        = 0x48000000;
 static const uint32_t bcl_20_31                = 0x429f0005;
@@ -4029,8 +4184,10 @@ static const uint32_t ld_12_2            = 0xe9820000;
 static const uint32_t ld_12_3          = 0xe9830000;
 static const uint32_t ld_12_11         = 0xe98b0000;
 static const uint32_t ld_12_12         = 0xe98c0000;
+static const uint32_t ldx_12_11_12     = 0x7d8b602a;
 static const uint32_t lfd_0_1          = 0xc8010000;
 static const uint32_t li_0_0           = 0x38000000;
+static const uint32_t li_11_0          = 0x39600000;
 static const uint32_t li_12_0          = 0x39800000;
 static const uint32_t lis_0            = 0x3c000000;
 static const uint32_t lis_2            = 0x3c400000;
@@ -4057,6 +4214,11 @@ static const uint32_t mtlr_11            = 0x7d6803a6;
 static const uint32_t mtlr_12          = 0x7d8803a6;
 static const uint32_t nop              = 0x60000000;
 static const uint32_t ori_0_0_0                = 0x60000000;
+static const uint32_t ori_11_11_0      = 0x616b0000;
+static const uint32_t ori_12_12_0      = 0x618c0000;
+static const uint32_t oris_12_12_0     = 0x658c0000;
+static const uint32_t sldi_11_11_34    = 0x796b1746;
+static const uint32_t sldi_12_12_32    = 0x799c07c6;
 static const uint32_t srdi_0_0_2       = 0x7800f082;
 static const uint32_t std_0_1          = 0xf8010000;
 static const uint32_t std_0_12         = 0xf80c0000;
@@ -4069,6 +4231,10 @@ static const uint32_t sub_12_12_11       = 0x7d8b6050;
 static const uint32_t xor_2_12_12      = 0x7d826278;
 static const uint32_t xor_11_12_12     = 0x7d8b6278;
 
+static const uint64_t paddi_12_pc      = 0x0610000039800000ULL;
+static const uint64_t pld_12_pc                = 0x04100000e5800000ULL;
+static const uint64_t pnop             = 0x0700000000000000ULL;
+
 // Write out the PLT.
 
 template<int size, bool big_endian>
@@ -4349,6 +4515,18 @@ ha(uint32_t a)
   return hi(a + 0x8000);
 }
 
+static inline uint64_t
+d34(uint64_t v)
+{
+  return ((v & 0x3ffff0000ULL) << 16) | (v & 0xffff);
+}
+
+static inline uint64_t
+ha34(uint64_t v)
+{
+  return (v + (1ULL << 33)) >> 34;
+}
+
 template<int size>
 struct Eh_cie
 {
@@ -4443,14 +4621,27 @@ 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), r2save_(0), localentry0_(0)
+      : off_(off), indx_(indx), iter_(0), notoc_(0), r2save_(0), localentry0_(0)
     { }
 
     unsigned int off_;
-    unsigned int indx_ : 30;
+    unsigned int indx_ : 28;
+    unsigned int iter_ : 1;
+    unsigned int notoc_ : 1;
     unsigned int r2save_ : 1;
     unsigned int localentry0_ : 1;
   };
+  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)
+    { }
+
+    unsigned int off_;
+    bool iter_;
+    bool notoc_;
+    bool save_res_;
+  };
   typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
   static const Address invalid_address = static_cast<Address>(0) - 1;
 
@@ -4465,8 +4656,8 @@ class Stub_table : public Output_relaxed_input_section
       orig_data_size_(owner->current_data_size()),
       plt_size_(0), last_plt_size_(0),
       branch_size_(0), last_branch_size_(0), min_size_threshold_(0),
-      need_save_res_(false), uniq_(id), tls_get_addr_opt_bctrl_(-1u),
-      plt_fde_len_(0)
+      need_save_res_(false), need_resize_(false), resizing_(false),
+      uniq_(id)
   {
     this->set_output_section(output_section);
 
@@ -4517,14 +4708,14 @@ class Stub_table : public Output_relaxed_input_section
   add_long_branch_entry(const Powerpc_relobj<size, big_endian>*,
                        unsigned int, Address, Address, bool);
 
-  Address
+  const Branch_stub_ent*
   find_long_branch_entry(const Powerpc_relobj<size, big_endian>*,
                         Address) const;
 
   bool
   can_reach_stub(Address from, unsigned int off, unsigned int r_type)
   {
-    Address max_branch_offset = max_branch_delta(r_type);
+    Address max_branch_offset = max_branch_delta<size>(r_type);
     if (max_branch_offset == 0)
       return true;
     gold_assert(from != invalid_address);
@@ -4547,6 +4738,23 @@ class Stub_table : public Output_relaxed_input_section
       }
   }
 
+  bool
+  need_resize() const
+  { return need_resize_; }
+
+  void
+  set_resizing(bool val)
+  {
+    this->resizing_ = val;
+    if (val)
+      {
+       this->need_resize_ = false;
+       this->plt_size_ = 0;
+       this->branch_size_ = 0;
+       this->need_save_res_ = false;
+      }
+  }
+
   Address
   set_address_and_size(const Output_section* os, Address off)
   {
@@ -4589,6 +4797,10 @@ class Stub_table : public Output_relaxed_input_section
   plt_size() const
   { return this->plt_size_; }
 
+  section_size_type
+  branch_size() const
+  { return this->branch_size_; }
+
   void
   set_min_size_threshold(Address min_size)
   { this->min_size_threshold_ = min_size; }
@@ -4627,10 +4839,6 @@ class Stub_table : public Output_relaxed_input_section
     return false;
   }
 
-  // Generate a suitable FDE to describe code in this stub group.
-  void
-  init_plt_fde();
-
   // Add .eh_frame info for this stub section.
   void
   add_eh_frame(Layout* layout);
@@ -4648,10 +4856,10 @@ class Stub_table : public Output_relaxed_input_section
   class Plt_stub_key_hash;
   typedef Unordered_map<Plt_stub_key, Plt_stub_ent,
                        Plt_stub_key_hash> Plt_stub_entries;
-  class Branch_stub_ent;
-  class Branch_stub_ent_hash;
-  typedef Unordered_map<Branch_stub_ent, unsigned int,
-                       Branch_stub_ent_hash> Branch_stub_entries;
+  class Branch_stub_key;
+  class Branch_stub_key_hash;
+  typedef Unordered_map<Branch_stub_key, Branch_stub_ent,
+                       Branch_stub_key_hash> Branch_stub_entries;
 
   // Alignment of stub section.
   unsigned int
@@ -4680,38 +4888,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
-  {
-    if (size == 32)
-      {
-       const Symbol* gsym = p->first.sym_;
-       return (4 * 4
-               + (this->targ_->is_tls_get_addr_opt(gsym) ? 8 * 4 : 0));
-      }
-
-    const Output_data_plt_powerpc<size, big_endian>* plt;
-    Address plt_addr = this->plt_off(p, &plt);
-    plt_addr += plt->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;
-    unsigned int bytes = 4 * 4 + 4 * (ha(off) != 0);
-    const Symbol* gsym = p->first.sym_;
-    if (this->targ_->is_tls_get_addr_opt(gsym))
-      bytes += 13 * 4;
-    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;
-  }
+  plt_call_size(typename Plt_stub_entries::const_iterator p) const;
 
   unsigned int
   plt_call_align(unsigned int bytes) const
@@ -4722,16 +4899,19 @@ 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)
-  {
-    Address loc = this->stub_address() + this->last_plt_size_ + p->second;
-    if (p->first.dest_ - loc + (1 << 25) < 2 << 25)
-      return 4;
-    unsigned int bytes = 16;
-    if (size == 32 && parameters->options().output_is_position_independent())
-      bytes += 16;
-    return bytes;
-  }
+  branch_stub_size(typename Branch_stub_entries::const_iterator p,
+                  bool* need_lt);
+
+  bool
+  build_tls_opt_head(unsigned char** pp,
+                    typename Plt_stub_entries::const_iterator cs);
+
+  bool
+  build_tls_opt_tail(unsigned char* p,
+                    typename Plt_stub_entries::const_iterator cs);
+
+  void
+  plt_error(const Plt_stub_key& p);
 
   // Write out stubs.
   void
@@ -4809,18 +4989,17 @@ class Stub_table : public Output_relaxed_input_section
   };
 
   // Long branch stub keys.
-  class Branch_stub_ent
+  class Branch_stub_key
   {
   public:
-    Branch_stub_ent(const Powerpc_relobj<size, big_endian>* obj,
-                   Address to, bool save_res)
-      : dest_(to), toc_base_off_(0), save_res_(save_res)
+    Branch_stub_key(const Powerpc_relobj<size, big_endian>* obj, Address to)
+      : dest_(to), toc_base_off_(0)
     {
       if (size == 64)
        toc_base_off_ = obj->toc_base_offset();
     }
 
-    bool operator==(const Branch_stub_ent& that) const
+    bool operator==(const Branch_stub_key& that) const
     {
       return (this->dest_ == that.dest_
              && (size == 32
@@ -4829,14 +5008,13 @@ class Stub_table : public Output_relaxed_input_section
 
     Address dest_;
     unsigned int toc_base_off_;
-    bool save_res_;
   };
 
-  class Branch_stub_ent_hash
+  class Branch_stub_key_hash
   {
   public:
-    size_t operator()(const Branch_stub_ent& ent) const
-    { return ent.dest_ ^ ent.toc_base_off_; }
+    size_t operator()(const Branch_stub_key& key) const
+    { return key.dest_ ^ key.toc_base_off_; }
   };
 
   // In a sane world this would be a global.
@@ -4859,13 +5037,12 @@ class Stub_table : public Output_relaxed_input_section
   // Set if this stub group needs a copy of out-of-line register
   // save/restore functions.
   bool need_save_res_;
+  // Set when notoc_/r2save_ changes after sizing a stub
+  bool need_resize_;
+  // Set when resizing stubs
+  bool resizing_;
   // Per stub table unique identifier.
   uint32_t uniq_;
-  // The bctrl in the __tls_get_addr_opt stub, if present.
-  unsigned int tls_get_addr_opt_bctrl_;
-  // FDE unwind info for this stub group.
-  unsigned int plt_fde_len_;
-  unsigned char plt_fde_[20];
 };
 
 // Add a plt call stub, if we do not already have one for this
@@ -4885,27 +5062,41 @@ Stub_table<size, big_endian>::add_plt_call_entry(
   Plt_stub_ent ent(this->plt_size_, this->plt_call_stubs_.size());
   std::pair<typename Plt_stub_entries::iterator, bool> p
     = this->plt_call_stubs_.insert(std::make_pair(key, ent));
-  if (p.second)
+  if (size == 64)
     {
-      this->plt_size_ = ent.off_ + this->plt_call_size(p.first);
-      if (size == 64
+      if (p.second
          && this->targ_->is_elfv2_localentry0(gsym))
        {
          p.first->second.localentry0_ = 1;
          this->targ_->set_has_localentry0();
        }
-      if (this->targ_->is_tls_get_addr_opt(gsym))
+      if (r_type == elfcpp::R_PPC64_REL24_NOTOC)
+       {
+         if (!p.second && !p.first->second.notoc_
+             && !this->targ_->power10_stubs())
+           this->need_resize_ = true;
+         p.first->second.notoc_ = 1;
+       }
+      else 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_))
+    {
+      if (this->resizing_)
        {
-         this->targ_->set_has_tls_get_addr_opt();
-         this->tls_get_addr_opt_bctrl_ = this->plt_size_ - 5 * 4;
+         p.first->second.iter_ = 1;
+         p.first->second.off_ = this->plt_size_;
        }
+      this->plt_size_ += this->plt_call_size(p.first);
+      if (this->targ_->is_tls_get_addr_opt(gsym))
+       this->targ_->set_has_tls_get_addr_opt();
       this->plt_size_ = this->plt_call_align(this->plt_size_);
     }
-  if (size == 64
-      && !tocsave
-      && !p.first->second.localentry0_)
-    p.first->second.r2save_ = 1;
-  return this->can_reach_stub(from, ent.off_, r_type);
+  return this->can_reach_stub(from, p.first->second.off_, r_type);
 }
 
 template<int size, bool big_endian>
@@ -4922,22 +5113,39 @@ Stub_table<size, big_endian>::add_plt_call_entry(
   Plt_stub_ent ent(this->plt_size_, this->plt_call_stubs_.size());
   std::pair<typename Plt_stub_entries::iterator, bool> p
     = this->plt_call_stubs_.insert(std::make_pair(key, ent));
-  if (p.second)
+  if (size == 64)
     {
-      this->plt_size_ = ent.off_ + this->plt_call_size(p.first);
-      this->plt_size_ = this->plt_call_align(this->plt_size_);
-      if (size == 64
+      if (p.second
          && this->targ_->is_elfv2_localentry0(object, locsym_index))
        {
          p.first->second.localentry0_ = 1;
          this->targ_->set_has_localentry0();
        }
+      if (r_type == elfcpp::R_PPC64_REL24_NOTOC)
+       {
+         if (!p.second && !p.first->second.notoc_
+             && !this->targ_->power10_stubs())
+           this->need_resize_ = true;
+         p.first->second.notoc_ = 1;
+       }
+      else if (!tocsave && !p.first->second.localentry0_)
+       {
+         if (!p.second && !p.first->second.r2save_)
+           this->need_resize_ = true;
+         p.first->second.r2save_ = 1;
+       }
     }
-  if (size == 64
-      && !tocsave
-      && !p.first->second.localentry0_)
-    p.first->second.r2save_ = 1;
-  return this->can_reach_stub(from, ent.off_, r_type);
+  if (p.second || (this->resizing_ && !p.first->second.iter_))
+    {
+      if (this->resizing_)
+       {
+         p.first->second.iter_ = 1;
+         p.first->second.off_ = this->plt_size_;
+       }
+      this->plt_size_ += this->plt_call_size(p.first);
+      this->plt_size_ = this->plt_call_align(this->plt_size_);
+    }
+  return this->can_reach_stub(from, p.first->second.off_, r_type);
 }
 
 // Find a plt call stub.
@@ -5008,85 +5216,87 @@ Stub_table<size, big_endian>::add_long_branch_entry(
     Address to,
     bool save_res)
 {
-  Branch_stub_ent ent(object, to, save_res);
-  Address off = this->branch_size_;
+  Branch_stub_key key(object, to);
+  bool notoc = (size == 64 && r_type == elfcpp::R_PPC64_REL24_NOTOC);
+  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(ent, off));
-  if (p.second)
+    = this->long_branch_stubs_.insert(std::make_pair(key, ent));
+  if (notoc && !p.first->second.notoc_)
+    {
+      this->need_resize_ = true;
+      p.first->second.notoc_ = true;
+    }
+  gold_assert(save_res == p.first->second.save_res_);
+  if (p.second || (this->resizing_ && !p.first->second.iter_))
     {
+      if (this->resizing_)
+       {
+         p.first->second.iter_ = 1;
+         p.first->second.off_ = this->branch_size_;
+       }
       if (save_res)
        this->need_save_res_ = true;
       else
        {
-         unsigned int stub_size = this->branch_stub_size(p.first);
-         this->branch_size_ = off + stub_size;
-         if (size == 64 && stub_size != 4)
+         bool need_lt = false;
+         unsigned int stub_size = this->branch_stub_size(p.first, &need_lt);
+         this->branch_size_ += stub_size;
+         if (size == 64 && need_lt)
            this->targ_->add_branch_lookup_table(to);
        }
     }
-  return this->can_reach_stub(from, off, r_type);
+  return this->can_reach_stub(from, p.first->second.off_, r_type);
 }
 
 // Find long branch stub offset.
 
 template<int size, bool big_endian>
-typename Stub_table<size, big_endian>::Address
+const typename Stub_table<size, big_endian>::Branch_stub_ent*
 Stub_table<size, big_endian>::find_long_branch_entry(
     const Powerpc_relobj<size, big_endian>* object,
     Address to) const
 {
-  Branch_stub_ent ent(object, to, false);
+  Branch_stub_key key(object, to);
   typename Branch_stub_entries::const_iterator p
-    = this->long_branch_stubs_.find(ent);
+    = this->long_branch_stubs_.find(key);
   if (p == this->long_branch_stubs_.end())
-    return invalid_address;
-  if (p->first.save_res_)
-    return to - this->targ_->savres_section()->address() + this->branch_size_;
-  return p->second;
+    return NULL;
+  return &p->second;
 }
 
-// Generate a suitable FDE to describe code in this stub group.
-// The __tls_get_addr_opt call stub needs to describe where it saves
-// LR, to support exceptions that might be thrown from __tls_get_addr.
-
-template<int size, bool big_endian>
-void
-Stub_table<size, big_endian>::init_plt_fde()
+template<bool big_endian>
+static void
+eh_advance (std::vector<unsigned char>& fde, unsigned int delta)
 {
-  unsigned char* p = this->plt_fde_;
-  // offset pcrel sdata4, size udata4, and augmentation size byte.
-  memset (p, 0, 9);
-  p += 9;
-  if (this->tls_get_addr_opt_bctrl_ != -1u)
+  delta /= 4;
+  if (delta < 64)
+    fde.push_back(elfcpp::DW_CFA_advance_loc + delta);
+  else if (delta < 256)
     {
-      unsigned int to_bctrl = this->tls_get_addr_opt_bctrl_ / 4;
-      if (to_bctrl < 64)
-       *p++ = elfcpp::DW_CFA_advance_loc + to_bctrl;
-      else if (to_bctrl < 256)
-       {
-         *p++ = elfcpp::DW_CFA_advance_loc1;
-         *p++ = to_bctrl;
-       }
-      else if (to_bctrl < 65536)
-       {
-         *p++ = elfcpp::DW_CFA_advance_loc2;
-         elfcpp::Swap<16, big_endian>::writeval(p, to_bctrl);
-         p += 2;
-       }
-      else
-       {
-         *p++ = elfcpp::DW_CFA_advance_loc4;
-         elfcpp::Swap<32, big_endian>::writeval(p, to_bctrl);
-         p += 4;
-       }
-      *p++ = elfcpp::DW_CFA_offset_extended_sf;
-      *p++ = 65;
-      *p++ = -(this->targ_->stk_linker() / 8) & 0x7f;
-      *p++ = elfcpp::DW_CFA_advance_loc + 4;
-      *p++ = elfcpp::DW_CFA_restore_extended;
-      *p++ = 65;
+      fde.push_back(elfcpp::DW_CFA_advance_loc1);
+      fde.push_back(delta);
+    }
+  else if (delta < 65536)
+    {
+      fde.resize(fde.size() + 3);
+      unsigned char *p = &*fde.end() - 3;
+      *p++ = elfcpp::DW_CFA_advance_loc2;
+      elfcpp::Swap<16, big_endian>::writeval(p, delta);
+    }
+  else
+    {
+      fde.resize(fde.size() + 5);
+      unsigned char *p = &*fde.end() - 5;
+      *p++ = elfcpp::DW_CFA_advance_loc4;
+      elfcpp::Swap<32, big_endian>::writeval(p, delta);
     }
-  this->plt_fde_len_ = p - this->plt_fde_;
+}
+
+template<typename T>
+static bool
+stub_sort(T s1, T s2)
+{
+  return s1->second.off_ < s2->second.off_;
 }
 
 // Add .eh_frame info for this stub section.  Unlike other linker
@@ -5098,7 +5308,8 @@ template<int size, bool big_endian>
 void
 Stub_table<size, big_endian>::add_eh_frame(Layout* layout)
 {
-  if (!parameters->options().ld_generated_unwind_info())
+  if (size != 64
+      || !parameters->options().ld_generated_unwind_info())
     return;
 
   // Since we add stub .eh_frame info late, it must be placed
@@ -5109,28 +5320,117 @@ Stub_table<size, big_endian>::add_eh_frame(Layout* layout)
   if (!this->targ_->has_glink())
     return;
 
-  if (this->plt_size_ + this->branch_size_ + this->need_save_res_ == 0)
+  typedef typename Plt_stub_entries::const_iterator plt_iter;
+  std::vector<plt_iter> calls;
+  if (!this->plt_call_stubs_.empty())
+    for (plt_iter cs = this->plt_call_stubs_.begin();
+        cs != this->plt_call_stubs_.end();
+        ++cs)
+      if ((this->targ_->is_tls_get_addr_opt(cs->first.sym_)
+          && cs->second.r2save_
+          && !cs->second.localentry0_)
+         || (cs->second.notoc_
+             && !this->targ_->power10_stubs()))
+       calls.push_back(cs);
+  if (calls.size() > 1)
+    std::stable_sort(calls.begin(), calls.end(),
+                    stub_sort<plt_iter>);
+
+  typedef typename Branch_stub_entries::const_iterator branch_iter;
+  std::vector<branch_iter> branches;
+  if (!this->long_branch_stubs_.empty()
+      && !this->targ_->power10_stubs())
+    for (branch_iter bs = this->long_branch_stubs_.begin();
+        bs != this->long_branch_stubs_.end();
+        ++bs)
+      if (bs->second.notoc_)
+       branches.push_back(bs);
+  if (branches.size() > 1)
+    std::stable_sort(branches.begin(), branches.end(),
+                    stub_sort<branch_iter>);
+
+  if (calls.empty() && branches.empty())
     return;
 
-  this->init_plt_fde();
+  unsigned int last_eh_loc = 0;
+  // offset pcrel sdata4, size udata4, and augmentation size byte.
+  std::vector<unsigned char> fde(9, 0);
+
+  for (unsigned int i = 0; i < calls.size(); i++)
+    {
+      plt_iter cs = calls[i];
+      unsigned int off = cs->second.off_;
+      // The __tls_get_addr_opt call stub needs to describe where
+      // it saves LR, to support exceptions that might be thrown
+      // from __tls_get_addr, and to support asynchronous exceptions.
+      if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+       {
+         off += 7 * 4;
+         if (cs->second.r2save_
+             && !cs->second.localentry0_)
+           {
+             off += 2 * 4;
+             eh_advance<big_endian>(fde, off - last_eh_loc);
+             fde.resize(fde.size() + 6);
+             unsigned char* p = &*fde.end() - 6;
+             *p++ = elfcpp::DW_CFA_offset_extended_sf;
+             *p++ = 65;
+             *p++ = -(this->targ_->stk_linker() / 8) & 0x7f;
+             unsigned int delta = this->plt_call_size(cs) - 4 - 9 * 4;
+             *p++ = elfcpp::DW_CFA_advance_loc + delta / 4;
+             *p++ = elfcpp::DW_CFA_restore_extended;
+             *p++ = 65;
+             last_eh_loc = off + delta;
+             continue;
+           }
+       }
+      // notoc stubs also should describe LR changes, to support
+      // asynchronous exceptions.
+      off += (cs->second.r2save_ ? 4 : 0) + 8;
+      eh_advance<big_endian>(fde, off - last_eh_loc);
+      fde.resize(fde.size() + 6);
+      unsigned char* p = &*fde.end() - 6;
+      *p++ = elfcpp::DW_CFA_register;
+      *p++ = 65;
+      *p++ = 12;
+      *p++ = elfcpp::DW_CFA_advance_loc + 8 / 4;
+      *p++ = elfcpp::DW_CFA_restore_extended;
+      *p++ = 65;
+      last_eh_loc = off + 8;
+    }
+
+  for (unsigned int i = 0; i < branches.size(); i++)
+    {
+      branch_iter bs = branches[i];
+      unsigned int off = bs->second.off_ + 8;
+      eh_advance<big_endian>(fde, off - last_eh_loc);
+      fde.resize(fde.size() + 6);
+      unsigned char* p = &*fde.end() - 6;
+      *p++ = elfcpp::DW_CFA_register;
+      *p++ = 65;
+      *p++ = 12;
+      *p++ = elfcpp::DW_CFA_advance_loc + 8 / 4;
+      *p++ = elfcpp::DW_CFA_restore_extended;
+      *p++ = 65;
+      last_eh_loc = off + 8;
+    }
+
   layout->add_eh_frame_for_plt(this,
                               Eh_cie<size>::eh_frame_cie,
                               sizeof (Eh_cie<size>::eh_frame_cie),
-                              this->plt_fde_, this->plt_fde_len_);
+                              &*fde.begin(), fde.size());
 }
 
 template<int size, bool big_endian>
 void
 Stub_table<size, big_endian>::remove_eh_frame(Layout* layout)
 {
-  if (this->plt_fde_len_ != 0)
-    {
-      layout->remove_eh_frame_for_plt(this,
-                                     Eh_cie<size>::eh_frame_cie,
-                                     sizeof (Eh_cie<size>::eh_frame_cie),
-                                     this->plt_fde_, this->plt_fde_len_);
-      this->plt_fde_len_ = 0;
-    }
+  if (size == 64
+      && parameters->options().ld_generated_unwind_info()
+      && this->targ_->has_glink())
+    layout->remove_eh_frame_for_plt(this,
+                                   Eh_cie<size>::eh_frame_cie,
+                                   sizeof (Eh_cie<size>::eh_frame_cie));
 }
 
 // A class to handle .glink.
@@ -5371,72 +5671,596 @@ Stub_table<size, big_endian>::define_stub_syms(Symbol_table* symtab)
        bs != this->long_branch_stubs_.end();
        ++bs)
     {
-      if (bs->first.save_res_)
+      if (bs->second.save_res_)
        continue;
 
       char* name = new char[8 + 13 + 16 + 1];
       sprintf(name, "%08x.long_branch.%llx", this->uniq_,
              static_cast<unsigned long long>(bs->first.dest_));
       Address value = (this->stub_address() - this->address()
-                      + this->plt_size_ + bs->second);
-      unsigned int stub_size = this->branch_stub_size(bs);
+                      + this->plt_size_ + bs->second.off_);
+      bool need_lt = false;
+      unsigned int stub_size = this->branch_stub_size(bs, &need_lt);
       this->targ_->define_local(symtab, name, this, value, stub_size);
     }
 }
 
-// Write out plt and long branch stub code.
+// Emit the start of a __tls_get_addr_opt plt call stub.
 
 template<int size, bool big_endian>
-void
-Stub_table<size, big_endian>::do_write(Output_file* of)
+bool
+Stub_table<size, big_endian>::build_tls_opt_head(
+     unsigned char** pp,
+     typename Plt_stub_entries::const_iterator cs)
 {
-  if (this->plt_call_stubs_.empty()
-      && this->long_branch_stubs_.empty())
-    return;
-
-  const section_size_type start_off = this->offset();
-  const section_size_type off = this->stub_offset();
-  const section_size_type oview_size =
-    convert_to_section_size_type(this->data_size() - (off - start_off));
-  unsigned char* const oview = of->get_output_view(off, oview_size);
-  unsigned char* p;
-
-  if (size == 64)
+  if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
     {
-      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())
+      unsigned char* p = *pp;
+      if (size == 64)
        {
-         // Write out plt call stubs.
-         typename Plt_stub_entries::const_iterator cs;
-         for (cs = this->plt_call_stubs_.begin();
-              cs != this->plt_call_stubs_.end();
-              ++cs)
+         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_)
            {
-             const Output_data_plt_powerpc<size, big_endian>* plt;
-             Address pltoff = this->plt_off(cs, &plt);
-             Address plt_addr = pltoff + plt->address();
-             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)
-               gold_error(_("%s: linkage table error against `%s'"),
-                          cs->first.object_->name().c_str(),
-                          cs->first.sym_->demangled_name().c_str());
-
-             bool plt_load_toc = this->targ_->abiversion() < 2;
-             bool static_chain
-               = plt_load_toc && parameters->options().plt_static_chain();
-             bool thread_safe
-               = plt_load_toc && this->targ_->plt_thread_safe();
-             bool use_fake_dep = false;
-             Address cmp_branch_off = 0;
-             if (thread_safe)
-               {
+             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, 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);
+         p += 4;
+         write_insn<big_endian>(p, mr_3_0);
+         p += 4;
+         write_insn<big_endian>(p, nop);
+         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_))
+    {
+      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);
+      return true;
+    }
+  return false;
+}
+
+// Emit pc-relative plt call stub code.
+
+template<bool big_endian>
+static unsigned char*
+build_power10_offset(unsigned char* p, uint64_t off, uint64_t odd, bool load)
+{
+  uint64_t insn;
+  if (off - odd + (1ULL << 33) < 1ULL << 34)
+    {
+      off -= odd;
+      if (odd)
+       {
+         write_insn<big_endian>(p, nop);
+         p += 4;
+       }
+      if (load)
+       insn = pld_12_pc;
+      else
+       insn = paddi_12_pc;
+      insn |= d34(off);
+      write_insn<big_endian>(p, insn >> 32);
+      p += 4;
+      write_insn<big_endian>(p, insn & 0xffffffff);
+    }
+  else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+    {
+      off -= 8 - odd;
+      write_insn<big_endian>(p, li_11_0 | (ha34(off) & 0xffff));
+      p += 4;
+      if (!odd)
+       {
+         write_insn<big_endian>(p, sldi_11_11_34);
+         p += 4;
+       }
+      insn = paddi_12_pc | d34(off);
+      write_insn<big_endian>(p, insn >> 32);
+      p += 4;
+      write_insn<big_endian>(p, insn & 0xffffffff);
+      p += 4;
+      if (odd)
+       {
+         write_insn<big_endian>(p, sldi_11_11_34);
+         p += 4;
+       }
+      if (load)
+       write_insn<big_endian>(p, ldx_12_11_12);
+      else
+       write_insn<big_endian>(p, add_12_11_12);
+    }
+  else
+    {
+      off -= odd + 8;
+      write_insn<big_endian>(p, lis_11 | ((ha34(off) >> 16) & 0x3fff));
+      p += 4;
+      write_insn<big_endian>(p, ori_11_11_0 | (ha34(off) & 0xffff));
+      p += 4;
+      if (odd)
+       {
+         write_insn<big_endian>(p, sldi_11_11_34);
+         p += 4;
+       }
+      insn = paddi_12_pc | d34(off);
+      write_insn<big_endian>(p, insn >> 32);
+      p += 4;
+      write_insn<big_endian>(p, insn & 0xffffffff);
+      p += 4;
+      if (!odd)
+       {
+         write_insn<big_endian>(p, sldi_11_11_34);
+         p += 4;
+       }
+      if (load)
+       write_insn<big_endian>(p, ldx_12_11_12);
+      else
+       write_insn<big_endian>(p, add_12_11_12);
+    }
+  p += 4;
+  return p;
+}
+
+// Gets the address of a label (1:) in r11 and builds an offset in r12,
+// then adds it to r11 (LOAD false) or loads r12 from r11+r12 (LOAD true).
+//     mflr    %r12
+//     bcl     20,31,1f
+// 1:  mflr    %r11
+//     mtlr    %r12
+//     lis     %r12,xxx-1b@highest
+//     ori     %r12,%r12,xxx-1b@higher
+//     sldi    %r12,%r12,32
+//     oris    %r12,%r12,xxx-1b@high
+//     ori     %r12,%r12,xxx-1b@l
+//     add/ldx %r12,%r11,%r12
+
+template<bool big_endian>
+static unsigned char*
+build_notoc_offset(unsigned char* p, uint64_t off, bool load)
+{
+  write_insn<big_endian>(p, mflr_12);
+  p += 4;
+  write_insn<big_endian>(p, bcl_20_31);
+  p += 4;
+  write_insn<big_endian>(p, mflr_11);
+  p += 4;
+  write_insn<big_endian>(p, mtlr_12);
+  p += 4;
+  if (off + 0x8000 < 0x10000)
+    {
+      if (load)
+       write_insn<big_endian>(p, ld_12_11 + l(off));
+      else
+       write_insn<big_endian>(p, addi_12_11 + l(off));
+    }
+  else if (off + 0x80008000ULL < 0x100000000ULL)
+    {
+      write_insn<big_endian>(p, addis_12_11 + ha(off));
+      p += 4;
+      if (load)
+       write_insn<big_endian>(p, ld_12_12 + l(off));
+      else
+       write_insn<big_endian>(p, addi_12_12 + l(off));
+    }
+  else
+    {
+      if (off + 0x800000000000ULL < 0x1000000000000ULL)
+       {
+         write_insn<big_endian>(p, li_12_0 + ((off >> 32) & 0xffff));
+         p += 4;
+       }
+      else
+       {
+         write_insn<big_endian>(p, lis_12 + ((off >> 48) & 0xffff));
+         p += 4;
+         if (((off >> 32) & 0xffff) != 0)
+           {
+             write_insn<big_endian>(p, ori_12_12_0 + ((off >> 32) & 0xffff));
+             p += 4;
+           }
+       }
+      if (((off >> 32) & 0xffffffffULL) != 0)
+       {
+         write_insn<big_endian>(p, sldi_12_12_32);
+         p += 4;
+       }
+      if (hi(off) != 0)
+       {
+         write_insn<big_endian>(p, oris_12_12_0 + hi(off));
+         p += 4;
+       }
+      if (l(off) != 0)
+       {
+         write_insn<big_endian>(p, ori_12_12_0 + l(off));
+         p += 4;
+       }
+      if (load)
+       write_insn<big_endian>(p, ldx_12_11_12);
+      else
+       write_insn<big_endian>(p, add_12_11_12);
+    }
+  p += 4;
+  return p;
+}
+
+// Size of a given plt call stub.
+
+template<int size, bool big_endian>
+unsigned int
+Stub_table<size, big_endian>::plt_call_size(
+    typename Plt_stub_entries::const_iterator p) const
+{
+  if (size == 32)
+    {
+      const Symbol* gsym = p->first.sym_;
+      return (4 * 4
+             + (this->targ_->is_tls_get_addr_opt(gsym) ? 8 * 4 : 0));
+    }
+
+  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 (p->second.r2save_ && !p->second.localentry0_)
+       bytes = 13 * 4;
+      else
+       bytes = 7 * 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 (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
+       {
+         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;
+    }
+
+  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;
+}
+
+// Return long branch stub size.
+
+template<int size, bool big_endian>
+unsigned int
+Stub_table<size, big_endian>::branch_stub_size(
+     typename Branch_stub_entries::const_iterator p,
+     bool* need_lt)
+{
+  Address loc = this->stub_address() + this->last_plt_size_ + p->second.off_;
+  if (size == 32)
+    {
+      if (p->first.dest_ - loc + (1 << 25) < 2 << 25)
+       return 4;
+      if (parameters->options().output_is_position_independent())
+       return 32;
+      return 16;
+    }
+
+  uint64_t off = p->first.dest_ - loc;
+  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;
+    }
+
+  if (off + (1 << 25) < 2 << 25)
+    return 4;
+  if (!this->targ_->power10_stubs())
+    *need_lt = true;
+  return 16;
+}
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::plt_error(const Plt_stub_key& p)
+{
+  if (p.sym_)
+    gold_error(_("linkage table error against `%s'"),
+              p.sym_->demangled_name().c_str());
+  else
+    gold_error(_("linkage table error against `%s:[local %u]'"),
+              p.object_->name().c_str(),
+              p.locsym_);
+}
+
+// Write out plt and long branch stub code.
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::do_write(Output_file* of)
+{
+  if (this->plt_call_stubs_.empty()
+      && this->long_branch_stubs_.empty())
+    return;
+
+  const section_size_type start_off = this->offset();
+  const section_size_type off = this->stub_offset();
+  const section_size_type oview_size =
+    convert_to_section_size_type(this->data_size() - (off - start_off));
+  unsigned char* const oview = of->get_output_view(off, oview_size);
+  unsigned char* p;
+
+  if (size == 64
+      && this->targ_->power10_stubs())
+    {
+      if (!this->plt_call_stubs_.empty())
+       {
+         // Write out plt call stubs.
+         typename Plt_stub_entries::const_iterator cs;
+         for (cs = this->plt_call_stubs_.begin();
+              cs != this->plt_call_stubs_.end();
+              ++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);
+           }
+       }
+
+      // Write out long branch stubs.
+      typename Branch_stub_entries::const_iterator bs;
+      for (bs = this->long_branch_stubs_.begin();
+          bs != this->long_branch_stubs_.end();
+          ++bs)
+       {
+         if (bs->second.save_res_)
+           continue;
+         Address off = this->plt_size_ + bs->second.off_;
+         p = oview + off;
+         Address loc = this->stub_address() + off;
+         Address delta = bs->first.dest_ - loc;
+         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);
+           }
+       }
+    }
+  else if (size == 64)
+    {
+      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()
+         && this->targ_->abiversion() >= 2)
+       {
+         // Write out plt call stubs for ELFv2.
+         typename Plt_stub_entries::const_iterator cs;
+         for (cs = this->plt_call_stubs_.begin();
+              cs != this->plt_call_stubs_.end();
+              ++cs)
+           {
+             const Output_data_plt_powerpc<size, big_endian>* plt;
+             Address pltoff = this->plt_off(cs, &plt);
+             Address plt_addr = pltoff + plt->address();
+
+             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;
+               }
+             if (cs->second.notoc_)
+               {
+                 Address from = this->stub_address() + (p - oview) + 8;
+                 Address off = plt_addr - from;
+                 p = build_notoc_offset<big_endian>(p, off, true);
+               }
+             else
+               {
+                 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 (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 (!this->build_tls_opt_tail(p, cs))
+               write_insn<big_endian>(p, bctr);
+           }
+       }
+      else if (!this->plt_call_stubs_.empty())
+       {
+         // Write out plt call stubs for ELFv1.
+         typename Plt_stub_entries::const_iterator cs;
+         for (cs = this->plt_call_stubs_.begin();
+              cs != this->plt_call_stubs_.end();
+              ++cs)
+           {
+             const Output_data_plt_powerpc<size, big_endian>* plt;
+             Address pltoff = this->plt_off(cs, &plt);
+             Address plt_addr = pltoff + plt->address();
+             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
+                 || cs->second.notoc_)
+               this->plt_error(cs->first);
+
+             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());
@@ -5458,94 +6282,47 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
                }
 
              p = oview + cs->second.off_;
-             const Symbol* gsym = cs->first.sym_;
-             if (this->targ_->is_tls_get_addr_opt(gsym))
+             if (this->build_tls_opt_head(&p, cs))
+               use_fake_dep = thread_safe;
+             if (cs->second.r2save_)
                {
-                 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);
+                 write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
                  p += 4;
-                 write_insn<big_endian>(p, beqlr);
-                 p += 4;
-                 write_insn<big_endian>(p, mr_3_0);
-                 p += 4;
-                 if (!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;
-                   }
-                 use_fake_dep = thread_safe;
                }
              if (ha(off) != 0)
                {
-                 if (cs->second.r2save_)
-                   {
-                     write_insn<big_endian>(p,
-                                            std_2_1 + this->targ_->stk_toc());
-                     p += 4;
-                   }
-                 if (plt_load_toc)
+                 write_insn<big_endian>(p, addis_11_2 + ha(off));
+                 p += 4;
+                 write_insn<big_endian>(p, ld_12_11 + l(off));
+                 p += 4;
+                 if (ha(off + 8 + 8 * static_chain) != ha(off))
                    {
-                     write_insn<big_endian>(p, addis_11_2 + ha(off));
-                     p += 4;
-                     write_insn<big_endian>(p, ld_12_11 + l(off));
+                     write_insn<big_endian>(p, addi_11_11 + l(off));
                      p += 4;
+                     off = 0;
                    }
-                 else
+                 write_insn<big_endian>(p, mtctr_12);
+                 p += 4;
+                 if (use_fake_dep)
                    {
-                     write_insn<big_endian>(p, addis_12_2 + ha(off));
+                     write_insn<big_endian>(p, xor_2_12_12);
                      p += 4;
-                     write_insn<big_endian>(p, ld_12_12 + l(off));
+                     write_insn<big_endian>(p, add_11_11_2);
                      p += 4;
                    }
-                 if (plt_load_toc
-                     && ha(off + 8 + 8 * static_chain) != ha(off))
-                   {
-                     write_insn<big_endian>(p, addi_11_11 + l(off));
-                     p += 4;
-                     off = 0;
-                   }
-                 write_insn<big_endian>(p, mtctr_12);
+                 write_insn<big_endian>(p, ld_2_11 + l(off + 8));
                  p += 4;
-                 if (plt_load_toc)
+                 if (static_chain)
                    {
-                     if (use_fake_dep)
-                       {
-                         write_insn<big_endian>(p, xor_2_12_12);
-                         p += 4;
-                         write_insn<big_endian>(p, add_11_11_2);
-                         p += 4;
-                       }
-                     write_insn<big_endian>(p, ld_2_11 + l(off + 8));
+                     write_insn<big_endian>(p, ld_11_11 + l(off + 16));
                      p += 4;
-                     if (static_chain)
-                       {
-                         write_insn<big_endian>(p, ld_11_11 + l(off + 16));
-                         p += 4;
-                       }
                    }
                }
              else
                {
-                 if (cs->second.r2save_)
-                   {
-                     write_insn<big_endian>(p,
-                                            std_2_1 + this->targ_->stk_toc());
-                     p += 4;
-                   }
                  write_insn<big_endian>(p, ld_12_2 + l(off));
                  p += 4;
-                 if (plt_load_toc
-                     && ha(off + 8 + 8 * static_chain) != ha(off))
+                 if (ha(off + 8 + 8 * static_chain) != ha(off))
                    {
                      write_insn<big_endian>(p, addi_2_2 + l(off));
                      p += 4;
@@ -5553,37 +6330,23 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
                    }
                  write_insn<big_endian>(p, mtctr_12);
                  p += 4;
-                 if (plt_load_toc)
+                 if (use_fake_dep)
                    {
-                     if (use_fake_dep)
-                       {
-                         write_insn<big_endian>(p, xor_11_12_12);
-                         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));
+                     write_insn<big_endian>(p, xor_11_12_12);
+                     p += 4;
+                     write_insn<big_endian>(p, add_2_2_11);
                      p += 4;
                    }
-               }
-             if (!cs->second.localentry0_
-                 && this->targ_->is_tls_get_addr_opt(gsym))
-               {
-                 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);
+                 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;
-                 write_insn<big_endian>(p, blr);
                }
+             if (this->build_tls_opt_tail(p, cs))
+               ;
              else if (thread_safe && !use_fake_dep)
                {
                  write_insn<big_endian>(p, cmpldi_2_0);
@@ -5603,14 +6366,19 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
           bs != this->long_branch_stubs_.end();
           ++bs)
        {
-         if (bs->first.save_res_)
+         if (bs->second.save_res_)
            continue;
-         p = oview + this->plt_size_ + bs->second;
-         Address loc = this->stub_address() + this->plt_size_ + bs->second;
+         Address off = this->plt_size_ + bs->second.off_;
+         p = oview + off;
+         Address loc = this->stub_address() + off;
          Address delta = bs->first.dest_ - loc;
-         if (delta + (1 << 25) < 2 << 25)
-           write_insn<big_endian>(p, b | (delta & 0x3fffffc));
-         else
+         if (bs->second.notoc_)
+           {
+             unsigned char* startp = p;
+             p = build_notoc_offset<big_endian>(p, off, false);
+             delta -= p - startp;
+           }
+         else if (delta + (1 << 25) >= 2 << 25)
            {
              Address brlt_addr
                = this->targ_->find_branch_lookup_table(bs->first.dest_);
@@ -5620,19 +6388,28 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
              Address brltoff = brlt_addr - got_addr;
              if (ha(brltoff) == 0)
                {
-                 write_insn<big_endian>(p, ld_12_2 + l(brltoff)),      p += 4;
+                 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;
+                 write_insn<big_endian>(p, addis_12_2 + ha(brltoff));
+                 p += 4;
+                 write_insn<big_endian>(p, ld_12_12 + l(brltoff));
+                 p += 4;
                }
-             write_insn<big_endian>(p, mtctr_12),                      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);
            }
        }
     }
-  else
+  else // size == 32
     {
       if (!this->plt_call_stubs_.empty())
        {
@@ -5650,26 +6427,7 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
              plt_addr += plt->address();
 
              p = oview + cs->second.off_;
-             const Symbol* gsym = cs->first.sym_;
-             if (this->targ_->is_tls_get_addr_opt(gsym))
-               {
-                 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);
-                 p += 4;
-                 write_insn<big_endian>(p, mr_3_0);
-                 p += 4;
-                 write_insn<big_endian>(p, nop);
-                 p += 4;
-               }
+             this->build_tls_opt_head(&p, cs);
              if (parameters->options().output_is_position_independent())
                {
                  Address got_addr;
@@ -5724,10 +6482,11 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
           bs != this->long_branch_stubs_.end();
           ++bs)
        {
-         if (bs->first.save_res_)
+         if (bs->second.save_res_)
            continue;
-         p = oview + this->plt_size_ + bs->second;
-         Address loc = this->stub_address() + this->plt_size_ + bs->second;
+         Address off = this->plt_size_ + bs->second.off_;
+         p = oview + off;
+         Address loc = this->stub_address() + off;
          Address delta = bs->first.dest_ - loc;
          if (delta + (1 << 25) < 2 << 25)
            write_insn<big_endian>(p, b | (delta & 0x3fffffc));
@@ -5871,8 +6630,7 @@ Output_data_glink<size, big_endian>::do_write(Output_file* of)
          Address off = plt_addr - my_addr;
 
          if (off + 0x80008000 > 0xffffffff || (off & 3) != 0)
-           gold_error(_("%s: linkage table error against `%s'"),
-                      ge->first->object()->name().c_str(),
+           gold_error(_("linkage table error against `%s'"),
                       ge->first->demangled_name().c_str());
 
          write_insn<big_endian>(p, addis_12_12 + ha(off)),     p += 4;
@@ -6447,6 +7205,15 @@ Target_powerpc<size, big_endian>::Scan::get_reference_flags(
     case elfcpp::R_POWERPC_ADDR16_LO:
     case elfcpp::R_POWERPC_ADDR16_HI:
     case elfcpp::R_POWERPC_ADDR16_HA:
+    case elfcpp::R_PPC64_ADDR16_HIGHER34:
+    case elfcpp::R_PPC64_ADDR16_HIGHERA34:
+    case elfcpp::R_PPC64_ADDR16_HIGHEST34:
+    case elfcpp::R_PPC64_ADDR16_HIGHESTA34:
+    case elfcpp::R_PPC64_D34:
+    case elfcpp::R_PPC64_D34_LO:
+    case elfcpp::R_PPC64_D34_HI30:
+    case elfcpp::R_PPC64_D34_HA30:
+    case elfcpp::R_PPC64_D28:
       ref = Symbol::ABSOLUTE_REF;
       break;
 
@@ -6464,9 +7231,25 @@ Target_powerpc<size, big_endian>::Scan::get_reference_flags(
     case elfcpp::R_POWERPC_REL16_LO:
     case elfcpp::R_POWERPC_REL16_HI:
     case elfcpp::R_POWERPC_REL16_HA:
+    case elfcpp::R_PPC64_REL16_HIGH:
+    case elfcpp::R_PPC64_REL16_HIGHA:
+    case elfcpp::R_PPC64_REL16_HIGHER:
+    case elfcpp::R_PPC64_REL16_HIGHERA:
+    case elfcpp::R_PPC64_REL16_HIGHEST:
+    case elfcpp::R_PPC64_REL16_HIGHESTA:
+    case elfcpp::R_PPC64_PCREL34:
+    case elfcpp::R_PPC64_REL16_HIGHER34:
+    case elfcpp::R_PPC64_REL16_HIGHERA34:
+    case elfcpp::R_PPC64_REL16_HIGHEST34:
+    case elfcpp::R_PPC64_REL16_HIGHESTA34:
+    case elfcpp::R_PPC64_PCREL28:
       ref = Symbol::RELATIVE_REF;
       break;
 
+    case elfcpp::R_PPC64_REL24_NOTOC:
+      if (size == 32)
+       break;
+      // Fall through.
     case elfcpp::R_POWERPC_REL24:
     case elfcpp::R_PPC_PLTREL24:
     case elfcpp::R_POWERPC_REL14:
@@ -6481,6 +7264,7 @@ Target_powerpc<size, big_endian>::Scan::get_reference_flags(
     case elfcpp::R_POWERPC_GOT16_HA:
     case elfcpp::R_PPC64_GOT16_DS:
     case elfcpp::R_PPC64_GOT16_LO_DS:
+    case elfcpp::R_PPC64_GOT_PCREL34:
     case elfcpp::R_PPC64_TOC16:
     case elfcpp::R_PPC64_TOC16_LO:
     case elfcpp::R_PPC64_TOC16_HI:
@@ -6491,11 +7275,21 @@ Target_powerpc<size, big_endian>::Scan::get_reference_flags(
     case elfcpp::R_POWERPC_PLT16_HI:
     case elfcpp::R_POWERPC_PLT16_HA:
     case elfcpp::R_PPC64_PLT16_LO_DS:
+    case elfcpp::R_PPC64_PLT_PCREL34:
+    case elfcpp::R_PPC64_PLT_PCREL34_NOTOC:
       ref = Symbol::RELATIVE_REF;
       break;
 
     case elfcpp::R_POWERPC_GOT_TPREL16:
     case elfcpp::R_POWERPC_TLS:
+    case elfcpp::R_PPC64_TLSGD:
+    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:
       ref = Symbol::TLS_REF;
       break;
 
@@ -6561,7 +7355,6 @@ Target_powerpc<size, big_endian>::Scan::check_non_pic(Relobj* object,
     case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
     case elfcpp::R_POWERPC_ADDR14_BRNTAKEN:
     case elfcpp::R_POWERPC_REL32:
-    case elfcpp::R_POWERPC_REL24:
     case elfcpp::R_POWERPC_TPREL16:
     case elfcpp::R_POWERPC_TPREL16_LO:
     case elfcpp::R_POWERPC_TPREL16_HI:
@@ -6610,6 +7403,7 @@ Target_powerpc<size, big_endian>::Scan::check_non_pic(Relobj* object,
        {
          // These are the relocation types supported only on 32-bit.
          // ??? glibc ld.so doesn't need to support these.
+       case elfcpp::R_POWERPC_REL24:
        case elfcpp::R_POWERPC_DTPREL16:
        case elfcpp::R_POWERPC_DTPREL16_LO:
        case elfcpp::R_POWERPC_DTPREL16_HI:
@@ -6672,6 +7466,7 @@ Target_powerpc<size, big_endian>::Scan::reloc_needs_plt_for_ifunc(
     case elfcpp::R_POWERPC_GOT16_HA:
     case elfcpp::R_PPC64_GOT16_DS:
     case elfcpp::R_PPC64_GOT16_LO_DS:
+    case elfcpp::R_PPC64_GOT_PCREL34:
       return false;
 
     // PLT relocs are OK and need a PLT entry.
@@ -6681,10 +7476,18 @@ Target_powerpc<size, big_endian>::Scan::reloc_needs_plt_for_ifunc(
     case elfcpp::R_PPC64_PLT16_LO_DS:
     case elfcpp::R_POWERPC_PLTSEQ:
     case elfcpp::R_POWERPC_PLTCALL:
+    case elfcpp::R_PPC64_PLTSEQ_NOTOC:
+    case elfcpp::R_PPC64_PLTCALL_NOTOC:
+    case elfcpp::R_PPC64_PLT_PCREL34:
+    case elfcpp::R_PPC64_PLT_PCREL34_NOTOC:
       return true;
       break;
 
     // Function calls are good, and these do need a PLT entry.
+    case elfcpp::R_PPC64_REL24_NOTOC:
+      if (size == 32)
+       break;
+      // Fall through.
     case elfcpp::R_POWERPC_ADDR24:
     case elfcpp::R_POWERPC_ADDR14:
     case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
@@ -6820,6 +7623,26 @@ Target_powerpc<size, big_endian>::Scan::local(
     case elfcpp::R_PPC64_ENTRY:
     case elfcpp::R_POWERPC_PLTSEQ:
     case elfcpp::R_POWERPC_PLTCALL:
+    case elfcpp::R_PPC64_PLTSEQ_NOTOC:
+    case elfcpp::R_PPC64_PLTCALL_NOTOC:
+    case elfcpp::R_PPC64_PCREL_OPT:
+    case elfcpp::R_PPC64_ADDR16_HIGHER34:
+    case elfcpp::R_PPC64_ADDR16_HIGHERA34:
+    case elfcpp::R_PPC64_ADDR16_HIGHEST34:
+    case elfcpp::R_PPC64_ADDR16_HIGHESTA34:
+    case elfcpp::R_PPC64_REL16_HIGHER34:
+    case elfcpp::R_PPC64_REL16_HIGHERA34:
+    case elfcpp::R_PPC64_REL16_HIGHEST34:
+    case elfcpp::R_PPC64_REL16_HIGHESTA34:
+    case elfcpp::R_PPC64_D34:
+    case elfcpp::R_PPC64_D34_LO:
+    case elfcpp::R_PPC64_D34_HI30:
+    case elfcpp::R_PPC64_D34_HA30:
+    case elfcpp::R_PPC64_D28:
+    case elfcpp::R_PPC64_PCREL34:
+    case elfcpp::R_PPC64_PCREL28:
+    case elfcpp::R_PPC64_TPREL34:
+    case elfcpp::R_PPC64_DTPREL34:
       break;
 
     case elfcpp::R_PPC64_TOC:
@@ -6911,6 +7734,8 @@ Target_powerpc<size, big_endian>::Scan::local(
        }
       break;
 
+    case elfcpp::R_PPC64_PLT_PCREL34:
+    case elfcpp::R_PPC64_PLT_PCREL34_NOTOC:
     case elfcpp::R_POWERPC_PLT16_LO:
     case elfcpp::R_POWERPC_PLT16_HI:
     case elfcpp::R_POWERPC_PLT16_HA:
@@ -6922,6 +7747,10 @@ Target_powerpc<size, big_endian>::Scan::local(
        }
       break;
 
+    case elfcpp::R_PPC64_REL24_NOTOC:
+      if (size == 32)
+       break;
+      // Fall through.
     case elfcpp::R_POWERPC_REL24:
     case elfcpp::R_PPC_PLTREL24:
     case elfcpp::R_PPC_LOCAL24PC:
@@ -6964,6 +7793,12 @@ Target_powerpc<size, big_endian>::Scan::local(
     case elfcpp::R_POWERPC_REL16_HI:
     case elfcpp::R_POWERPC_REL16_HA:
     case elfcpp::R_POWERPC_REL16DX_HA:
+    case elfcpp::R_PPC64_REL16_HIGH:
+    case elfcpp::R_PPC64_REL16_HIGHA:
+    case elfcpp::R_PPC64_REL16_HIGHER:
+    case elfcpp::R_PPC64_REL16_HIGHERA:
+    case elfcpp::R_PPC64_REL16_HIGHEST:
+    case elfcpp::R_PPC64_REL16_HIGHESTA:
     case elfcpp::R_POWERPC_SECTOFF:
     case elfcpp::R_POWERPC_SECTOFF_LO:
     case elfcpp::R_POWERPC_SECTOFF_HI:
@@ -6999,6 +7834,7 @@ Target_powerpc<size, big_endian>::Scan::local(
     case elfcpp::R_PPC64_ADDR64_LOCAL:
       break;
 
+    case elfcpp::R_PPC64_GOT_PCREL34:
     case elfcpp::R_POWERPC_GOT16:
     case elfcpp::R_POWERPC_GOT16_LO:
     case elfcpp::R_POWERPC_GOT16_HI:
@@ -7047,6 +7883,7 @@ Target_powerpc<size, big_endian>::Scan::local(
       target->got_section(symtab, layout);
       break;
 
+    case elfcpp::R_PPC64_GOT_TLSGD34:
     case elfcpp::R_POWERPC_GOT_TLSGD16:
     case elfcpp::R_POWERPC_GOT_TLSGD16_LO:
     case elfcpp::R_POWERPC_GOT_TLSGD16_HI:
@@ -7071,6 +7908,7 @@ Target_powerpc<size, big_endian>::Scan::local(
       }
       break;
 
+    case elfcpp::R_PPC64_GOT_TLSLD34:
     case elfcpp::R_POWERPC_GOT_TLSLD16:
     case elfcpp::R_POWERPC_GOT_TLSLD16_LO:
     case elfcpp::R_POWERPC_GOT_TLSLD16_HI:
@@ -7094,6 +7932,7 @@ Target_powerpc<size, big_endian>::Scan::local(
       }
       break;
 
+    case elfcpp::R_PPC64_GOT_DTPREL34:
     case elfcpp::R_POWERPC_GOT_DTPREL16:
     case elfcpp::R_POWERPC_GOT_DTPREL16_LO:
     case elfcpp::R_POWERPC_GOT_DTPREL16_HI:
@@ -7106,6 +7945,7 @@ Target_powerpc<size, big_endian>::Scan::local(
       }
       break;
 
+    case elfcpp::R_PPC64_GOT_TPREL34:
     case elfcpp::R_POWERPC_GOT_TPREL16:
     case elfcpp::R_POWERPC_GOT_TPREL16_LO:
     case elfcpp::R_POWERPC_GOT_TPREL16_HI:
@@ -7297,6 +8137,52 @@ Target_powerpc<size, big_endian>::Scan::local(
     case elfcpp::R_PPC64_TOC16:
     case elfcpp::R_PPC64_TOC16_DS:
       ppc_object->set_has_small_toc_reloc();
+      break;
+    default:
+      break;
+    }
+
+  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:
+    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:
+    case elfcpp::R_PPC64_TPREL34:
+      layout->set_has_static_tls();
+      break;
+    default:
+      break;
+    }
+
+  switch (r_type)
+    {
+    case elfcpp::R_PPC64_D34:
+    case elfcpp::R_PPC64_D34_LO:
+    case elfcpp::R_PPC64_D34_HI30:
+    case elfcpp::R_PPC64_D34_HA30:
+    case elfcpp::R_PPC64_D28:
+    case elfcpp::R_PPC64_PCREL34:
+    case elfcpp::R_PPC64_PCREL28:
+    case elfcpp::R_PPC64_TPREL34:
+    case elfcpp::R_PPC64_DTPREL34:
+    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:
+      target->set_power10_stubs();
+      break;
     default:
       break;
     }
@@ -7382,6 +8268,26 @@ Target_powerpc<size, big_endian>::Scan::global(
     case elfcpp::R_PPC64_ENTRY:
     case elfcpp::R_POWERPC_PLTSEQ:
     case elfcpp::R_POWERPC_PLTCALL:
+    case elfcpp::R_PPC64_PLTSEQ_NOTOC:
+    case elfcpp::R_PPC64_PLTCALL_NOTOC:
+    case elfcpp::R_PPC64_PCREL_OPT:
+    case elfcpp::R_PPC64_ADDR16_HIGHER34:
+    case elfcpp::R_PPC64_ADDR16_HIGHERA34:
+    case elfcpp::R_PPC64_ADDR16_HIGHEST34:
+    case elfcpp::R_PPC64_ADDR16_HIGHESTA34:
+    case elfcpp::R_PPC64_REL16_HIGHER34:
+    case elfcpp::R_PPC64_REL16_HIGHERA34:
+    case elfcpp::R_PPC64_REL16_HIGHEST34:
+    case elfcpp::R_PPC64_REL16_HIGHESTA34:
+    case elfcpp::R_PPC64_D34:
+    case elfcpp::R_PPC64_D34_LO:
+    case elfcpp::R_PPC64_D34_HI30:
+    case elfcpp::R_PPC64_D34_HA30:
+    case elfcpp::R_PPC64_D28:
+    case elfcpp::R_PPC64_PCREL34:
+    case elfcpp::R_PPC64_PCREL28:
+    case elfcpp::R_PPC64_TPREL34:
+    case elfcpp::R_PPC64_DTPREL34:
       break;
 
     case elfcpp::R_PPC64_TOC:
@@ -7517,6 +8423,8 @@ Target_powerpc<size, big_endian>::Scan::global(
       }
       break;
 
+    case elfcpp::R_PPC64_PLT_PCREL34:
+    case elfcpp::R_PPC64_PLT_PCREL34_NOTOC:
     case elfcpp::R_POWERPC_PLT16_LO:
     case elfcpp::R_POWERPC_PLT16_HI:
     case elfcpp::R_POWERPC_PLT16_HA:
@@ -7525,6 +8433,10 @@ Target_powerpc<size, big_endian>::Scan::global(
        target->make_plt_entry(symtab, layout, gsym);
       break;
 
+    case elfcpp::R_PPC64_REL24_NOTOC:
+      if (size == 32)
+       break;
+      // Fall through.
     case elfcpp::R_PPC_PLTREL24:
     case elfcpp::R_POWERPC_REL24:
       if (!is_ifunc)
@@ -7604,6 +8516,12 @@ Target_powerpc<size, big_endian>::Scan::global(
     case elfcpp::R_POWERPC_REL16_HI:
     case elfcpp::R_POWERPC_REL16_HA:
     case elfcpp::R_POWERPC_REL16DX_HA:
+    case elfcpp::R_PPC64_REL16_HIGH:
+    case elfcpp::R_PPC64_REL16_HIGHA:
+    case elfcpp::R_PPC64_REL16_HIGHER:
+    case elfcpp::R_PPC64_REL16_HIGHERA:
+    case elfcpp::R_PPC64_REL16_HIGHEST:
+    case elfcpp::R_PPC64_REL16_HIGHESTA:
     case elfcpp::R_POWERPC_SECTOFF:
     case elfcpp::R_POWERPC_SECTOFF_LO:
     case elfcpp::R_POWERPC_SECTOFF_HI:
@@ -7639,6 +8557,7 @@ Target_powerpc<size, big_endian>::Scan::global(
     case elfcpp::R_PPC64_ADDR64_LOCAL:
       break;
 
+    case elfcpp::R_PPC64_GOT_PCREL34:
     case elfcpp::R_POWERPC_GOT16:
     case elfcpp::R_POWERPC_GOT16_LO:
     case elfcpp::R_POWERPC_GOT16_HI:
@@ -7697,6 +8616,7 @@ Target_powerpc<size, big_endian>::Scan::global(
       target->got_section(symtab, layout);
       break;
 
+    case elfcpp::R_PPC64_GOT_TLSGD34:
     case elfcpp::R_POWERPC_GOT_TLSGD16:
     case elfcpp::R_POWERPC_GOT_TLSGD16_LO:
     case elfcpp::R_POWERPC_GOT_TLSGD16_HI:
@@ -7745,6 +8665,7 @@ Target_powerpc<size, big_endian>::Scan::global(
       }
       break;
 
+    case elfcpp::R_PPC64_GOT_TLSLD34:
     case elfcpp::R_POWERPC_GOT_TLSLD16:
     case elfcpp::R_POWERPC_GOT_TLSLD16_LO:
     case elfcpp::R_POWERPC_GOT_TLSLD16_HI:
@@ -7768,6 +8689,7 @@ Target_powerpc<size, big_endian>::Scan::global(
       }
       break;
 
+    case elfcpp::R_PPC64_GOT_DTPREL34:
     case elfcpp::R_POWERPC_GOT_DTPREL16:
     case elfcpp::R_POWERPC_GOT_DTPREL16_LO:
     case elfcpp::R_POWERPC_GOT_DTPREL16_HI:
@@ -7787,6 +8709,7 @@ Target_powerpc<size, big_endian>::Scan::global(
       }
       break;
 
+    case elfcpp::R_PPC64_GOT_TPREL34:
     case elfcpp::R_POWERPC_GOT_TPREL16:
     case elfcpp::R_POWERPC_GOT_TPREL16_LO:
     case elfcpp::R_POWERPC_GOT_TPREL16_HI:
@@ -7972,6 +8895,52 @@ Target_powerpc<size, big_endian>::Scan::global(
     case elfcpp::R_PPC64_TOC16:
     case elfcpp::R_PPC64_TOC16_DS:
       ppc_object->set_has_small_toc_reloc();
+      break;
+    default:
+      break;
+    }
+
+  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:
+    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:
+    case elfcpp::R_PPC64_TPREL34:
+      layout->set_has_static_tls();
+      break;
+    default:
+      break;
+    }
+
+  switch (r_type)
+    {
+    case elfcpp::R_PPC64_D34:
+    case elfcpp::R_PPC64_D34_LO:
+    case elfcpp::R_PPC64_D34_HI30:
+    case elfcpp::R_PPC64_D34_HA30:
+    case elfcpp::R_PPC64_D28:
+    case elfcpp::R_PPC64_PCREL34:
+    case elfcpp::R_PPC64_PCREL28:
+    case elfcpp::R_PPC64_TPREL34:
+    case elfcpp::R_PPC64_DTPREL34:
+    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:
+      target->set_power10_stubs();
+      break;
     default:
       break;
     }
@@ -8087,7 +9056,7 @@ Target_powerpc<size, big_endian>::do_gc_mark_symbol(
     Symbol_table* symtab,
     Symbol* sym) const
 {
-  if (size == 64)
+  if (size == 64 && sym->object()->pluginobj() == NULL)
     {
       Powerpc_relobj<size, big_endian>* ppc_object
        = static_cast<Powerpc_relobj<size, big_endian>*>(sym->object());
@@ -8512,7 +9481,7 @@ Target_powerpc<size, big_endian>::do_finalize_sections(
       Powerpc_relobj<size, big_endian>* ppc_relobj
        = static_cast<Powerpc_relobj<size, big_endian>*>(*p);
       if (ppc_relobj->attributes_section_data())
-       this->merge_object_attributes(ppc_relobj->name().c_str(),
+       this->merge_object_attributes(ppc_relobj,
                                      ppc_relobj->attributes_section_data());
     }
   for (Input_objects::Dynobj_iterator p = input_objects->dynobj_begin();
@@ -8522,7 +9491,7 @@ Target_powerpc<size, big_endian>::do_finalize_sections(
       Powerpc_dynobj<size, big_endian>* ppc_dynobj
        = static_cast<Powerpc_dynobj<size, big_endian>*>(*p);
       if (ppc_dynobj->attributes_section_data())
-       this->merge_object_attributes(ppc_dynobj->name().c_str(),
+       this->merge_object_attributes(ppc_dynobj,
                                      ppc_dynobj->attributes_section_data());
     }
 
@@ -8545,7 +9514,7 @@ Target_powerpc<size, big_endian>::do_finalize_sections(
 template<int size, bool big_endian>
 void
 Target_powerpc<size, big_endian>::merge_object_attributes(
-    const char* name,
+    const Object* obj,
     const Attributes_section_data* pasd)
 {
   // Return if there is no attributes section data.
@@ -8561,12 +9530,14 @@ Target_powerpc<size, big_endian>::merge_object_attributes(
   Object_attribute* out_attr
     = this->attributes_section_data_->known_attributes(vendor);
 
+  const char* name = obj->name().c_str();
   const char* err;
   const char* first;
   const char* second;
   int tag = elfcpp::Tag_GNU_Power_ABI_FP;
   int in_fp = in_attr[tag].int_value() & 0xf;
   int out_fp = out_attr[tag].int_value() & 0xf;
+  bool warn_only = obj->is_dynamic();
   if (in_fp != out_fp)
     {
       err = NULL;
@@ -8574,10 +9545,13 @@ Target_powerpc<size, big_endian>::merge_object_attributes(
        ;
       else if ((out_fp & 3) == 0)
        {
-         out_fp |= in_fp & 3;
-         out_attr[tag].set_int_value(out_fp);
-         out_attr[tag].set_type(Object_attribute::ATTR_TYPE_FLAG_INT_VAL);
-         this->last_fp_ = name;
+         if (!warn_only)
+           {
+             out_fp |= in_fp & 3;
+             out_attr[tag].set_int_value(out_fp);
+             out_attr[tag].set_type(Object_attribute::ATTR_TYPE_FLAG_INT_VAL);
+             this->last_fp_ = name;
+           }
        }
       else if ((out_fp & 3) != 2 && (in_fp & 3) == 2)
        {
@@ -8610,10 +9584,13 @@ Target_powerpc<size, big_endian>::merge_object_attributes(
        ;
       else if ((out_fp & 0xc) == 0)
        {
-         out_fp |= in_fp & 0xc;
-         out_attr[tag].set_int_value(out_fp);
-         out_attr[tag].set_type(Object_attribute::ATTR_TYPE_FLAG_INT_VAL);
-         this->last_ld_ = name;
+         if (!warn_only)
+           {
+             out_fp |= in_fp & 0xc;
+             out_attr[tag].set_int_value(out_fp);
+             out_attr[tag].set_type(Object_attribute::ATTR_TYPE_FLAG_INT_VAL);
+             this->last_ld_ = name;
+           }
        }
       else if ((out_fp & 0xc) != 2 * 4 && (in_fp & 0xc) == 2 * 4)
        {
@@ -8643,10 +9620,16 @@ Target_powerpc<size, big_endian>::merge_object_attributes(
       if (err)
        {
          if (parameters->options().warn_mismatch())
-           gold_error(_(err), first, second);
+           {
+             if (warn_only)
+               gold_warning(_(err), first, second);
+             else
+               gold_error(_(err), first, second);
+           }
          // Arrange for this attribute to be deleted.  It's better to
          // say "don't know" about a file than to wrongly claim compliance.
-         out_attr[tag].set_type(0);
+         if (!warn_only)
+           out_attr[tag].set_type(0);
        }
     }
 
@@ -8831,6 +9814,145 @@ Target_powerpc<size, big_endian>::symval_for_branch(
   return true;
 }
 
+template<int size>
+static bool
+relative_value_is_known(const Sized_symbol<size>* gsym)
+{
+  if (gsym->type() == elfcpp::STT_GNU_IFUNC)
+    return false;
+
+  if (gsym->is_from_dynobj()
+      || gsym->is_undefined()
+      || gsym->is_preemptible())
+    return false;
+
+  if (gsym->is_absolute())
+    return !parameters->options().output_is_position_independent();
+
+  return true;
+}
+
+template<int size>
+static bool
+relative_value_is_known(const Symbol_value<size>* psymval)
+{
+  if (psymval->is_ifunc_symbol())
+    return false;
+
+  bool is_ordinary;
+  unsigned int shndx = psymval->input_shndx(&is_ordinary);
+
+  return is_ordinary && shndx != elfcpp::SHN_UNDEF;
+}
+
+// PCREL_OPT in one instance flags to the linker that a pair of insns:
+//   pld ra,symbol@got@pcrel
+//   load/store rt,0(ra)
+// or
+//   pla ra,symbol@pcrel
+//   load/store rt,0(ra)
+// may be translated to
+//   pload/pstore rt,symbol@pcrel
+//   nop.
+// This function returns true if the optimization is possible, placing
+// the prefix insn in *PINSN1 and a NOP in *PINSN2.
+//
+// On entry to this function, the linker has already determined that
+// the pld can be replaced with pla: *PINSN1 is that pla insn,
+// while *PINSN2 is the second instruction.
+
+inline bool
+xlate_pcrel_opt(uint64_t *pinsn1, uint64_t *pinsn2)
+{
+  uint32_t insn2 = *pinsn2 >> 32;
+  uint64_t i1new;
+
+  // Check that regs match.
+  if (((insn2 >> 16) & 31) != ((*pinsn1 >> 21) & 31))
+    return false;
+
+  switch ((insn2 >> 26) & 63)
+    {
+    default:
+      return false;
+
+    case 32: // lwz
+    case 34: // lbz
+    case 36: // stw
+    case 38: // stb
+    case 40: // lhz
+    case 42: // lha
+    case 44: // sth
+    case 48: // lfs
+    case 50: // lfd
+    case 52: // stfs
+    case 54: // stfd
+      // These are the PMLS cases, where we just need to tack a prefix
+      // on the insn.  Check that the D field is zero.
+      if ((insn2 & 0xffff) != 0)
+       return false;
+      i1new = ((1ULL << 58) | (2ULL << 56) | (1ULL << 52)
+              | (insn2 & ((63ULL << 26) | (31ULL << 21))));
+      break;
+
+    case 58: // lwa, ld
+      if ((insn2 & 0xfffd) != 0)
+       return false;
+      i1new = ((1ULL << 58) | (1ULL << 52)
+              | (insn2 & 2 ? 41ULL << 26 : 57ULL << 26)
+              | (insn2 & (31ULL << 21)));
+      break;
+
+    case 57: // lxsd, lxssp
+      if ((insn2 & 0xfffc) != 0 || (insn2 & 3) < 2)
+       return false;
+      i1new = ((1ULL << 58) | (1ULL << 52)
+              | ((40ULL | (insn2 & 3)) << 26)
+              | (insn2 & (31ULL << 21)));
+      break;
+
+    case 61: // stxsd, stxssp, lxv, stxv
+      if ((insn2 & 3) == 0)
+       return false;
+      else if ((insn2 & 3) >= 2)
+       {
+         if ((insn2 & 0xfffc) != 0)
+           return false;
+         i1new = ((1ULL << 58) | (1ULL << 52)
+                  | ((44ULL | (insn2 & 3)) << 26)
+                  | (insn2 & (31ULL << 21)));
+       }
+      else
+       {
+         if ((insn2 & 0xfff0) != 0)
+           return false;
+         i1new = ((1ULL << 58) | (1ULL << 52)
+                  | ((50ULL | (insn2 & 4) | ((insn2 & 8) >> 3)) << 26)
+                  | (insn2 & (31ULL << 21)));
+       }
+      break;
+
+    case 56: // lq
+      if ((insn2 & 0xffff) != 0)
+       return false;
+      i1new = ((1ULL << 58) | (1ULL << 52)
+              | (insn2 & ((63ULL << 26) | (31ULL << 21))));
+      break;
+
+    case 62: // std, stq
+      if ((insn2 & 0xfffd) != 0)
+       return false;
+      i1new = ((1ULL << 58) | (1ULL << 52)
+              | ((insn2 & 2) == 0 ? 61ULL << 26 : 60ULL << 26)
+              | (insn2 & (31ULL << 21)));
+      break;
+    }
+
+  *pinsn1 = i1new;
+  *pinsn2 = (uint64_t) nop << 32;
+  return true;
+}
+
 // Perform a relocation.
 
 template<int size, bool big_endian>
@@ -8871,7 +9993,8 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       break;
     case Track_tls::SKIP:
       if (is_plt16_reloc<size>(r_type)
-         || r_type == elfcpp::R_POWERPC_PLTSEQ)
+         || r_type == elfcpp::R_POWERPC_PLTSEQ
+         || r_type == elfcpp::R_PPC64_PLTSEQ_NOTOC)
        {
          Insn* iview = reinterpret_cast<Insn*>(view);
          elfcpp::Swap<32, big_endian>::writeval(iview, nop);
@@ -8881,6 +10004,13 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
          Insn* iview = reinterpret_cast<Insn*>(view);
          elfcpp::Swap<32, big_endian>::writeval(iview + 1, nop);
        }
+      else if (size == 64 && (r_type == elfcpp::R_PPC64_PLT_PCREL34
+                             || r_type == elfcpp::R_PPC64_PLT_PCREL34_NOTOC))
+       {
+         Insn* iview = reinterpret_cast<Insn*>(view);
+         elfcpp::Swap<32, big_endian>::writeval(iview, pnop >> 32);
+         elfcpp::Swap<32, big_endian>::writeval(iview + 1, pnop & 0xffffffff);
+       }
       return true;
     case Track_tls::NORMAL:
       break;
@@ -8901,8 +10031,12 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
        : object->local_has_plt_offset(r_sym));
   if (has_plt_offset
       && !is_plt16_reloc<size>(r_type)
+      && r_type != elfcpp::R_PPC64_PLT_PCREL34
+      && r_type != elfcpp::R_PPC64_PLT_PCREL34_NOTOC
       && r_type != elfcpp::R_POWERPC_PLTSEQ
       && r_type != elfcpp::R_POWERPC_PLTCALL
+      && r_type != elfcpp::R_PPC64_PLTSEQ_NOTOC
+      && r_type != elfcpp::R_PPC64_PLTCALL_NOTOC
       && (!psymval->is_ifunc_symbol()
          || Scan::reloc_needs_plt_for_ifunc(target, object, r_type, false)))
     {
@@ -8910,7 +10044,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
          && gsym != NULL
          && target->abiversion() >= 2
          && !parameters->options().output_is_position_independent()
-         && !is_branch_reloc(r_type))
+         && !is_branch_reloc<size>(r_type))
        {
          Address off = target->glink_section()->find_global_entry(gsym);
          if (off != invalid_address)
@@ -8928,7 +10062,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
              && !(size == 32
                   && gsym != NULL
                   && !parameters->options().output_is_position_independent()
-                  && !is_branch_reloc(r_type)))
+                  && !is_branch_reloc<size>(r_type)))
            stub_table = object->stub_table(relinfo->data_shndx);
          if (stub_table == NULL)
            {
@@ -8955,7 +10089,11 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
                  size_t reloc_count = shdr.get_sh_size() / reloc_size;
                  if (size == 64
                      && ent->r2save_
-                     && relnum + 1 < reloc_count)
+                     && r_type == elfcpp::R_PPC64_REL24_NOTOC)
+                   value += 4;
+                 else if (size == 64
+                          && ent->r2save_
+                          && relnum < reloc_count - 1)
                    {
                      Reltype next_rela(preloc + reloc_size);
                      if (elfcpp::elf_r_type<size>(next_rela.get_r_info())
@@ -8974,7 +10112,9 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       gold_assert(has_stub_value || !(os->flags() & elfcpp::SHF_ALLOC));
     }
 
-  if (has_plt_offset && is_plt16_reloc<size>(r_type))
+  if (has_plt_offset && (is_plt16_reloc<size>(r_type)
+                        || r_type == elfcpp::R_PPC64_PLT_PCREL34
+                        || r_type == elfcpp::R_PPC64_PLT_PCREL34_NOTOC))
     {
       const Output_data_plt_powerpc<size, big_endian>* plt;
       if (gsym)
@@ -8984,8 +10124,12 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       value += plt->address();
 
       if (size == 64)
-       value -= (target->got_section()->output_section()->address()
-                 + object->toc_base_offset());
+       {
+         if (r_type != elfcpp::R_PPC64_PLT_PCREL34
+             && r_type != elfcpp::R_PPC64_PLT_PCREL34_NOTOC)
+           value -= (target->got_section()->output_section()->address()
+                     + object->toc_base_offset());
+       }
       else if (parameters->options().output_is_position_independent())
        {
          if (rela.get_r_addend() >= 32768)
@@ -9002,18 +10146,29 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
     }
   else if (!has_plt_offset
           && (is_plt16_reloc<size>(r_type)
-              || r_type == elfcpp::R_POWERPC_PLTSEQ))
+              || r_type == elfcpp::R_POWERPC_PLTSEQ
+              || r_type == elfcpp::R_PPC64_PLTSEQ_NOTOC))
     {
       Insn* iview = reinterpret_cast<Insn*>(view);
       elfcpp::Swap<32, big_endian>::writeval(iview, nop);
       r_type = elfcpp::R_POWERPC_NONE;
     }
+  else if (!has_plt_offset
+          && (r_type == elfcpp::R_PPC64_PLT_PCREL34
+              || r_type == elfcpp::R_PPC64_PLT_PCREL34_NOTOC))
+    {
+      Insn* iview = reinterpret_cast<Insn*>(view);
+      elfcpp::Swap<32, big_endian>::writeval(iview, pnop >> 32);
+      elfcpp::Swap<32, big_endian>::writeval(iview + 1, pnop & 0xffffffff);
+      r_type = elfcpp::R_POWERPC_NONE;
+    }
   else if (r_type == elfcpp::R_POWERPC_GOT16
           || r_type == elfcpp::R_POWERPC_GOT16_LO
           || r_type == elfcpp::R_POWERPC_GOT16_HI
           || r_type == elfcpp::R_POWERPC_GOT16_HA
           || r_type == elfcpp::R_PPC64_GOT16_DS
-          || r_type == elfcpp::R_PPC64_GOT16_LO_DS)
+          || r_type == elfcpp::R_PPC64_GOT16_LO_DS
+          || r_type == elfcpp::R_PPC64_GOT_PCREL34)
     {
       if (gsym != NULL)
        {
@@ -9025,7 +10180,10 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
          gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_STANDARD));
          value = object->local_got_offset(r_sym, GOT_TYPE_STANDARD);
        }
-      value -= target->got_section()->got_base_offset(object);
+      if (r_type == elfcpp::R_PPC64_GOT_PCREL34)
+       value += target->got_section()->address();
+      else
+       value -= target->got_section()->got_base_offset(object);
     }
   else if (r_type == elfcpp::R_PPC64_TOC)
     {
@@ -9104,7 +10262,8 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
   else if (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
           || 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_POWERPC_GOT_TLSGD16_HA
+          || r_type == elfcpp::R_PPC64_GOT_TLSGD34)
     {
       // First instruction of a global dynamic sequence, arg setup insn.
       const bool final = gsym == NULL || gsym->final_value_is_known();
@@ -9126,67 +10285,121 @@ 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);
            }
-         value -= target->got_section()->got_base_offset(object);
+         if (r_type == elfcpp::R_PPC64_GOT_TLSGD34)
+           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_POWERPC_GOT_TLSGD16
-             || r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO)
+         if (r_type == elfcpp::R_PPC64_GOT_TLSGD34)
            {
-             Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
-             Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
-             insn &= (1 << 26) - (1 << 16); // extract rt,ra from addi
-             if (size == 32)
-               insn |= 32 << 26; // lwz
-             else
-               insn |= 58 << 26; // ld
-             elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+             Insn* iview = reinterpret_cast<Insn*>(view);
+             uint64_t pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
+             pinsn <<= 32;
+             pinsn |= elfcpp::Swap<32, big_endian>::readval(iview + 1);
+             // pla -> pld
+             pinsn += (-2ULL << 56) + (57ULL << 26) - (14ULL << 26);
+             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;
+           }
+         else
+           {
+             if (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
+                 || r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO)
+               {
+                 Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
+                 Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+                 insn &= (1 << 26) - (1 << 16); // extract rt,ra from addi
+                 if (size == 32)
+                   insn |= 32 << 26; // lwz
+                 else
+                   insn |= 58 << 26; // ld
+                 elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+               }
+             r_type += (elfcpp::R_POWERPC_GOT_TPREL16
+                        - elfcpp::R_POWERPC_GOT_TLSGD16);
            }
-         r_type += (elfcpp::R_POWERPC_GOT_TPREL16
-                    - elfcpp::R_POWERPC_GOT_TLSGD16);
        }
       else if (tls_type == tls::TLSOPT_TO_LE)
        {
-         if (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
-             || r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO)
+         if (r_type == elfcpp::R_PPC64_GOT_TLSGD34)
            {
-             Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
-             Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
-             insn &= (1 << 26) - (1 << 21); // extract rt
-             if (size == 32)
-               insn |= addis_0_2;
-             else
-               insn |= addis_0_13;
-             elfcpp::Swap<32, big_endian>::writeval(iview, insn);
-             r_type = elfcpp::R_POWERPC_TPREL16_HA;
+             Insn* iview = reinterpret_cast<Insn*>(view);
+             uint64_t pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
+             pinsn <<= 32;
+             pinsn |= elfcpp::Swap<32, big_endian>::readval(iview + 1);
+             // pla pcrel -> paddi r13
+             pinsn += (-1ULL << 52) + (13ULL << 16);
+             elfcpp::Swap<32, big_endian>::writeval(iview, pinsn >> 32);
+             elfcpp::Swap<32, big_endian>::writeval(iview + 1,
+                                                    pinsn & 0xffffffff);
+             r_type = elfcpp::R_PPC64_TPREL34;
              value = psymval->value(object, rela.get_r_addend());
            }
          else
            {
-             Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
-             Insn insn = nop;
-             elfcpp::Swap<32, big_endian>::writeval(iview, insn);
-             r_type = elfcpp::R_POWERPC_NONE;
+             if (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
+                 || r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO)
+               {
+                 Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
+                 Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+                 insn &= (1 << 26) - (1 << 21); // extract rt
+                 if (size == 32)
+                   insn |= addis_0_2;
+                 else
+                   insn |= addis_0_13;
+                 elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+                 r_type = elfcpp::R_POWERPC_TPREL16_HA;
+                 value = psymval->value(object, rela.get_r_addend());
+               }
+             else
+               {
+                 Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
+                 Insn insn = nop;
+                 elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+                 r_type = elfcpp::R_POWERPC_NONE;
+               }
            }
        }
     }
   else if (r_type == elfcpp::R_POWERPC_GOT_TLSLD16
           || 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_POWERPC_GOT_TLSLD16_HA
+          || r_type == elfcpp::R_PPC64_GOT_TLSLD34)
     {
       // 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();
-         value -= target->got_section()->got_base_offset(object);
+         if (r_type == elfcpp::R_PPC64_GOT_TLSLD34)
+           value += target->got_section()->address();
+         else
+           value -= target->got_section()->got_base_offset(object);
        }
       else
        {
          gold_assert(tls_type == tls::TLSOPT_TO_LE);
-         if (r_type == elfcpp::R_POWERPC_GOT_TLSLD16
-             || r_type == elfcpp::R_POWERPC_GOT_TLSLD16_LO)
+         if (r_type == elfcpp::R_PPC64_GOT_TLSLD34)
+           {
+             Insn* iview = reinterpret_cast<Insn*>(view);
+             uint64_t pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
+             pinsn <<= 32;
+             pinsn |= elfcpp::Swap<32, big_endian>::readval(iview + 1);
+             // pla pcrel -> paddi r13
+             pinsn += (-1ULL << 52) + (13ULL << 16);
+             elfcpp::Swap<32, big_endian>::writeval(iview, pinsn >> 32);
+             elfcpp::Swap<32, big_endian>::writeval(iview + 1,
+                                                    pinsn & 0xffffffff);
+             r_type = elfcpp::R_PPC64_TPREL34;
+             value = dtp_offset;
+           }
+         else if (r_type == elfcpp::R_POWERPC_GOT_TLSLD16
+                  || r_type == elfcpp::R_POWERPC_GOT_TLSLD16_LO)
            {
              Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
              Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
@@ -9211,7 +10424,8 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
   else if (r_type == elfcpp::R_POWERPC_GOT_DTPREL16
           || 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_POWERPC_GOT_DTPREL16_HA
+          || r_type == elfcpp::R_PPC64_GOT_DTPREL34)
     {
       // Accesses relative to a local dynamic sequence address,
       // no optimisation here.
@@ -9225,12 +10439,16 @@ 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);
        }
-      value -= target->got_section()->got_base_offset(object);
+      if (r_type == elfcpp::R_PPC64_GOT_DTPREL34)
+       value += target->got_section()->address();
+      else
+       value -= target->got_section()->got_base_offset(object);
     }
   else if (r_type == elfcpp::R_POWERPC_GOT_TPREL16
           || 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_POWERPC_GOT_TPREL16_HA
+          || r_type == elfcpp::R_PPC64_GOT_TPREL34)
     {
       // First instruction of initial exec sequence.
       const bool final = gsym == NULL || gsym->final_value_is_known();
@@ -9247,13 +10465,31 @@ 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);
            }
-         value -= target->got_section()->got_base_offset(object);
+         if (r_type == elfcpp::R_PPC64_GOT_TPREL34)
+           value += target->got_section()->address();
+         else
+           value -= target->got_section()->got_base_offset(object);
        }
       else
        {
          gold_assert(tls_type == tls::TLSOPT_TO_LE);
-         if (r_type == elfcpp::R_POWERPC_GOT_TPREL16
-             || r_type == elfcpp::R_POWERPC_GOT_TPREL16_LO)
+         if (r_type == elfcpp::R_PPC64_GOT_TPREL34)
+           {
+             Insn* iview = reinterpret_cast<Insn*>(view);
+             uint64_t pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
+             pinsn <<= 32;
+             pinsn |= elfcpp::Swap<32, big_endian>::readval(iview + 1);
+             // pld ra,sym@got@tprel@pcrel -> paddi ra,r13,sym@tprel
+             pinsn += ((2ULL << 56) + (-1ULL << 52)
+                       + (14ULL << 26) - (57ULL << 26) + (13ULL << 16));
+             elfcpp::Swap<32, big_endian>::writeval(iview, pinsn >> 32);
+             elfcpp::Swap<32, big_endian>::writeval(iview + 1,
+                                                    pinsn & 0xffffffff);
+             r_type = elfcpp::R_PPC64_TPREL34;
+             value = psymval->value(object, rela.get_r_addend());
+           }
+         else if (r_type == elfcpp::R_POWERPC_GOT_TPREL16
+                  || r_type == elfcpp::R_POWERPC_GOT_TPREL16_LO)
            {
              Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
              Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
@@ -9296,12 +10532,33 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
            }
          else
            {
+             bool is_pcrel = false;
+             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)
+               {
+                 Reltype next_rela(preloc + reloc_size);
+                 unsigned int r_type2
+                   = elfcpp::elf_r_type<size>(next_rela.get_r_info());
+                 if ((r_type2 == elfcpp::R_PPC64_REL24_NOTOC
+                      || r_type2 == elfcpp::R_PPC64_PLTCALL_NOTOC)
+                     && next_rela.get_r_offset() == rela.get_r_offset())
+                   is_pcrel = true;
+               }
              Insn* iview = reinterpret_cast<Insn*>(view);
-             Insn insn = addi_3_3;
-             elfcpp::Swap<32, big_endian>::writeval(iview, insn);
-             r_type = elfcpp::R_POWERPC_TPREL16_LO;
-             view += d_offset;
-             value = psymval->value(object, rela.get_r_addend());
+             if (is_pcrel)
+               {
+                 elfcpp::Swap<32, big_endian>::writeval(iview, nop);
+                 r_type = elfcpp::R_POWERPC_NONE;
+               }
+             else
+               {
+                 elfcpp::Swap<32, big_endian>::writeval(iview, addi_3_3);
+                 r_type = elfcpp::R_POWERPC_TPREL16_LO;
+                 view += d_offset;
+                 value = psymval->value(object, rela.get_r_addend());
+               }
            }
          this->skip_next_tls_get_addr_call();
        }
@@ -9315,13 +10572,34 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       const tls::Tls_optimization tls_type = target->optimize_tls_ld();
       if (tls_type == tls::TLSOPT_TO_LE)
        {
+         bool is_pcrel = false;
+         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)
+           {
+             Reltype next_rela(preloc + reloc_size);
+             unsigned int r_type2
+               = elfcpp::elf_r_type<size>(next_rela.get_r_info());
+             if ((r_type2 == elfcpp::R_PPC64_REL24_NOTOC
+                  || r_type2 == elfcpp::R_PPC64_PLTCALL_NOTOC)
+                 && next_rela.get_r_offset() == rela.get_r_offset())
+               is_pcrel = true;
+           }
          Insn* iview = reinterpret_cast<Insn*>(view);
-         Insn insn = addi_3_3;
-         elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+         if (is_pcrel)
+           {
+             elfcpp::Swap<32, big_endian>::writeval(iview, nop);
+             r_type = elfcpp::R_POWERPC_NONE;
+           }
+         else
+           {
+             elfcpp::Swap<32, big_endian>::writeval(iview, addi_3_3);
+             r_type = elfcpp::R_POWERPC_TPREL16_LO;
+             view += d_offset;
+             value = dtp_offset;
+           }
          this->skip_next_tls_get_addr_call();
-         r_type = elfcpp::R_POWERPC_TPREL16_LO;
-         view += d_offset;
-         value = dtp_offset;
        }
     }
   else if (r_type == elfcpp::R_POWERPC_TLS)
@@ -9331,20 +10609,46 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       const tls::Tls_optimization tls_type = target->optimize_tls_ie(final);
       if (tls_type == tls::TLSOPT_TO_LE)
        {
-         Insn* iview = reinterpret_cast<Insn*>(view);
+         Address roff = rela.get_r_offset() & 3;
+         Insn* iview = reinterpret_cast<Insn*>(view - roff);
          Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
          unsigned int reg = size == 32 ? 2 : 13;
          insn = at_tls_transform(insn, reg);
          gold_assert(insn != 0);
-         elfcpp::Swap<32, big_endian>::writeval(iview, insn);
-         r_type = elfcpp::R_POWERPC_TPREL16_LO;
-         view += d_offset;
-         value = psymval->value(object, rela.get_r_addend());
+         if (roff == 0)
+           {
+             elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+             r_type = elfcpp::R_POWERPC_TPREL16_LO;
+             view += d_offset;
+             value = psymval->value(object, rela.get_r_addend());
+           }
+         else if (roff == 1)
+           {
+             // For pcrel IE to LE we already have the full offset
+             // and thus don't need an addi here.  A nop or mr will do.
+             if ((insn & (0x3f << 26)) == 14 << 26)
+               {
+                 // Extract regs from addi rt,ra,si.
+                 unsigned int rt = (insn >> 21) & 0x1f;
+                 unsigned int ra = (insn >> 16) & 0x1f;
+                 if (rt == ra)
+                   insn = nop;
+                 else
+                   {
+                     // Build or ra,rs,rb with rb==rs, ie. mr ra,rs.
+                     insn = (rt << 16) | (ra << 21) | (ra << 11);
+                     insn |= (31u << 26) | (444u << 1);
+                   }
+               }
+             elfcpp::Swap<32, big_endian>::writeval(iview, insn);
+             r_type = elfcpp::R_POWERPC_NONE;
+           }
        }
     }
   else if (!has_stub_value)
     {
-      if (!has_plt_offset && r_type == elfcpp::R_POWERPC_PLTCALL)
+      if (!has_plt_offset && (r_type == elfcpp::R_POWERPC_PLTCALL
+                             || r_type == elfcpp::R_PPC64_PLTCALL_NOTOC))
        {
          // PLTCALL without plt entry => convert to direct call
          Insn* iview = reinterpret_cast<Insn*>(view);
@@ -9353,6 +10657,8 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
          elfcpp::Swap<32, big_endian>::writeval(iview, insn);
          if (size == 32)
            r_type = elfcpp::R_PPC_PLTREL24;
+         else if (r_type == elfcpp::R_PPC64_PLTCALL_NOTOC)
+           r_type = elfcpp::R_PPC64_REL24_NOTOC;
          else
            r_type = elfcpp::R_POWERPC_REL24;
        }
@@ -9364,7 +10670,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
                || r_type == elfcpp::R_POWERPC_PLT16_HA)))
        addend = rela.get_r_addend();
       value = psymval->value(object, addend);
-      if (size == 64 && is_branch_reloc(r_type))
+      if (size == 64 && is_branch_reloc<size>(r_type))
        {
          if (target->abiversion() >= 2)
            {
@@ -9380,19 +10686,29 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
                                        &value, &dest_shndx);
            }
        }
-      Address max_branch_offset = max_branch_delta(r_type);
+      Address max_branch_offset = max_branch_delta<size>(r_type);
       if (max_branch_offset != 0
-         && value - address + max_branch_offset >= 2 * max_branch_offset)
+         && (value - address + max_branch_offset >= 2 * max_branch_offset
+             || (size == 64
+                 && r_type == elfcpp::R_PPC64_REL24_NOTOC
+                 && (gsym != NULL
+                     ? object->ppc64_needs_toc(gsym)
+                     : object->ppc64_needs_toc(r_sym)))))
        {
          Stub_table<size, big_endian>* stub_table
            = object->stub_table(relinfo->data_shndx);
          if (stub_table != NULL)
            {
-             Address off = stub_table->find_long_branch_entry(object, value);
-             if (off != invalid_address)
+             const typename Stub_table<size, big_endian>::Branch_stub_ent* ent
+               = stub_table->find_long_branch_entry(object, value);
+             if (ent != NULL)
                {
-                 value = (stub_table->stub_address() + stub_table->plt_size()
-                          + off);
+                 if (ent->save_res_)
+                   value = (value - target->savres_section()->address()
+                            + stub_table->branch_size());
+                 else
+                   value = (stub_table->stub_address() + stub_table->plt_size()
+                            + ent->off_);
                  has_stub_value = true;
                }
            }
@@ -9401,6 +10717,10 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
 
   switch (r_type)
     {
+    case elfcpp::R_PPC64_REL24_NOTOC:
+      if (size == 32)
+       break;
+      // Fall through.
     case elfcpp::R_PPC64_REL64:
     case elfcpp::R_POWERPC_REL32:
     case elfcpp::R_POWERPC_REL24:
@@ -9411,9 +10731,28 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
     case elfcpp::R_POWERPC_REL16_HI:
     case elfcpp::R_POWERPC_REL16_HA:
     case elfcpp::R_POWERPC_REL16DX_HA:
+    case elfcpp::R_PPC64_REL16_HIGH:
+    case elfcpp::R_PPC64_REL16_HIGHA:
+    case elfcpp::R_PPC64_REL16_HIGHER:
+    case elfcpp::R_PPC64_REL16_HIGHERA:
+    case elfcpp::R_PPC64_REL16_HIGHEST:
+    case elfcpp::R_PPC64_REL16_HIGHESTA:
     case elfcpp::R_POWERPC_REL14:
     case elfcpp::R_POWERPC_REL14_BRTAKEN:
     case elfcpp::R_POWERPC_REL14_BRNTAKEN:
+    case elfcpp::R_PPC64_PCREL34:
+    case elfcpp::R_PPC64_GOT_PCREL34:
+    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_REL16_HIGHER34:
+    case elfcpp::R_PPC64_REL16_HIGHERA34:
+    case elfcpp::R_PPC64_REL16_HIGHEST34:
+    case elfcpp::R_PPC64_REL16_HIGHESTA34:
       value -= address;
       break;
 
@@ -9455,6 +10794,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
     case elfcpp::R_PPC64_TPREL16_HIGHERA:
     case elfcpp::R_PPC64_TPREL16_HIGHEST:
     case elfcpp::R_PPC64_TPREL16_HIGHESTA:
+    case elfcpp::R_PPC64_TPREL34:
       // tls symbol values are relative to tls_segment()->vaddr()
       value -= tp_offset;
       break;
@@ -9477,6 +10817,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
     case elfcpp::R_POWERPC_DTPREL:
     case elfcpp::R_PPC64_DTPREL16_HIGH:
     case elfcpp::R_PPC64_DTPREL16_HIGHA:
+    case elfcpp::R_PPC64_DTPREL34:
       // tls symbol values are relative to tls_segment()->vaddr()
       value -= dtp_offset;
       break;
@@ -9549,8 +10890,16 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       break;
     }
 
-  if (size == 64)
+  if (size == 64
+      && (gsym
+         ? relative_value_is_known(gsym)
+         : relative_value_is_known(psymval)))
     {
+      Insn* iview;
+      Insn* iview2;
+      Insn insn;
+      uint64_t pinsn, pinsn2;
+
       switch (r_type)
        {
        default:
@@ -9563,12 +10912,6 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
          // and
          //     addis ra,r2,0; addi rb,ra,x@toc@l;
          // to  nop;           addi rb,r2,x@toc;
-         // FIXME: the @got sequence shown above is not yet
-         // optimized.  Note that gcc as of 2017-01-07 doesn't use
-         // the ELF @got relocs except for TLS, instead using the
-         // PowerOpen variant of a compiler managed GOT (called TOC).
-         // The PowerOpen TOC sequence equivalent to the first
-         // example is optimized.
        case elfcpp::R_POWERPC_GOT_TLSLD16_HA:
        case elfcpp::R_POWERPC_GOT_TLSGD16_HA:
        case elfcpp::R_POWERPC_GOT_TPREL16_HA:
@@ -9577,10 +10920,14 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
        case elfcpp::R_PPC64_TOC16_HA:
          if (parameters->options().toc_optimize())
            {
-             Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
-             Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
-             if (r_type == elfcpp::R_PPC64_TOC16_HA
-                 && object->make_toc_relative(target, &value))
+             iview = reinterpret_cast<Insn*>(view - d_offset);
+             insn = elfcpp::Swap<32, big_endian>::readval(iview);
+             if ((r_type == elfcpp::R_PPC64_TOC16_HA
+                  && object->make_toc_relative(target, &value))
+                 || (r_type == elfcpp::R_POWERPC_GOT16_HA
+                     && object->make_got_relative(target, psymval,
+                                                  rela.get_r_addend(),
+                                                  &value)))
                {
                  gold_assert((insn & ((0x3f << 26) | 0x1f << 16))
                              == ((15u << 26) | (2 << 16)));
@@ -9605,11 +10952,15 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
        case elfcpp::R_PPC64_TOC16_LO_DS:
          if (parameters->options().toc_optimize())
            {
-             Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
-             Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
+             iview = reinterpret_cast<Insn*>(view - d_offset);
+             insn = elfcpp::Swap<32, big_endian>::readval(iview);
              bool changed = false;
-             if (r_type == elfcpp::R_PPC64_TOC16_LO_DS
-                 && object->make_toc_relative(target, &value))
+             if ((r_type == elfcpp::R_PPC64_TOC16_LO_DS
+                  && object->make_toc_relative(target, &value))
+                 || (r_type == elfcpp::R_PPC64_GOT16_LO_DS
+                     && object->make_got_relative(target, psymval,
+                                                  rela.get_r_addend(),
+                                                  &value)))
                {
                  gold_assert ((insn & (0x3f << 26)) == 58u << 26 /* ld */);
                  insn ^= (14u << 26) ^ (58u << 26);
@@ -9637,6 +10988,77 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
            }
          break;
 
+       case elfcpp::R_PPC64_GOT_PCREL34:
+         if (parameters->options().toc_optimize())
+           {
+             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) | (1ULL << 52) | (57ULL << 26) /* pld */))
+               break;
+
+             Address relval = psymval->value(object, rela.get_r_addend());
+             relval -= address;
+             if (relval + (1ULL << 33) < 1ULL << 34)
+               {
+                 value = relval;
+                 // Replace with paddi
+                 pinsn += (2ULL << 56) + (14ULL << 26) - (57ULL << 26);
+                 elfcpp::Swap<32, big_endian>::writeval(iview, pinsn >> 32);
+                 elfcpp::Swap<32, big_endian>::writeval(iview + 1,
+                                                        pinsn & 0xffffffff);
+                 goto pcrelopt;
+               }
+           }
+         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;
+
+         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;
+
+           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);
+             }
+         }
+         break;
+
        case elfcpp::R_POWERPC_TPREL16_HA:
          if (parameters->options().tls_optimize() && value + 0x8000 < 0x10000)
            {
@@ -9720,7 +11142,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
          //            addi 2,2,.TOC.@l
          // if .TOC. is in range.  */
          if (value + address - 4 + 0x80008000 <= 0xffffffff
-             && relnum != 0
+             && relnum + 1 > 1
              && preloc != NULL
              && target->abiversion() >= 2
              && !parameters->options().output_is_position_independent()
@@ -9834,6 +11256,10 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       overflow = Reloc::CHECK_LOW_INSN;
       break;
 
+    case elfcpp::R_PPC64_REL24_NOTOC:
+      if (size == 32)
+       break;
+      // Fall through.
     case elfcpp::R_POWERPC_ADDR24:
     case elfcpp::R_POWERPC_ADDR14:
     case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
@@ -9850,6 +11276,19 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
     case elfcpp::R_POWERPC_REL14:
     case elfcpp::R_POWERPC_REL14_BRTAKEN:
     case elfcpp::R_POWERPC_REL14_BRNTAKEN:
+    case elfcpp::R_PPC64_D34:
+    case elfcpp::R_PPC64_PCREL34:
+    case elfcpp::R_PPC64_GOT_PCREL34:
+    case elfcpp::R_PPC64_PLT_PCREL34:
+    case elfcpp::R_PPC64_PLT_PCREL34_NOTOC:
+    case elfcpp::R_PPC64_D28:
+    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:
       overflow = Reloc::CHECK_SIGNED;
       break;
     }
@@ -9887,6 +11326,9 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
     case elfcpp::R_POWERPC_GNU_VTENTRY:
     case elfcpp::R_POWERPC_PLTSEQ:
     case elfcpp::R_POWERPC_PLTCALL:
+    case elfcpp::R_PPC64_PLTSEQ_NOTOC:
+    case elfcpp::R_PPC64_PLTCALL_NOTOC:
+    case elfcpp::R_PPC64_PCREL_OPT:
       break;
 
     case elfcpp::R_PPC64_ADDR64:
@@ -9917,6 +11359,10 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       status = Reloc::addr32_u(view, value, overflow);
       break;
 
+    case elfcpp::R_PPC64_REL24_NOTOC:
+      if (size == 32)
+       goto unsupp; // R_PPC_EMB_RELSDA
+      // Fall through.
     case elfcpp::R_POWERPC_ADDR24:
     case elfcpp::R_POWERPC_REL24:
     case elfcpp::R_PPC_PLTREL24:
@@ -9973,6 +11419,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       // Fall through.
     case elfcpp::R_POWERPC_ADDR16_HI:
     case elfcpp::R_POWERPC_REL16_HI:
+    case elfcpp::R_PPC64_REL16_HIGH:
     case elfcpp::R_PPC64_TOC16_HI:
     case elfcpp::R_POWERPC_GOT16_HI:
     case elfcpp::R_POWERPC_PLT16_HI:
@@ -9995,6 +11442,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       // Fall through.
     case elfcpp::R_POWERPC_ADDR16_HA:
     case elfcpp::R_POWERPC_REL16_HA:
+    case elfcpp::R_PPC64_REL16_HIGHA:
     case elfcpp::R_PPC64_TOC16_HA:
     case elfcpp::R_POWERPC_GOT16_HA:
     case elfcpp::R_POWERPC_PLT16_HA:
@@ -10018,6 +11466,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
        goto unsupp;
       // Fall through.
     case elfcpp::R_PPC64_ADDR16_HIGHER:
+    case elfcpp::R_PPC64_REL16_HIGHER:
     case elfcpp::R_PPC64_TPREL16_HIGHER:
       Reloc::addr16_hi2(view, value);
       break;
@@ -10028,6 +11477,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
        goto unsupp;
       // Fall through.
     case elfcpp::R_PPC64_ADDR16_HIGHERA:
+    case elfcpp::R_PPC64_REL16_HIGHERA:
     case elfcpp::R_PPC64_TPREL16_HIGHERA:
       Reloc::addr16_ha2(view, value);
       break;
@@ -10038,6 +11488,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
        goto unsupp;
       // Fall through.
     case elfcpp::R_PPC64_ADDR16_HIGHEST:
+    case elfcpp::R_PPC64_REL16_HIGHEST:
     case elfcpp::R_PPC64_TPREL16_HIGHEST:
       Reloc::addr16_hi3(view, value);
       break;
@@ -10048,6 +11499,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
        goto unsupp;
       // Fall through.
     case elfcpp::R_PPC64_ADDR16_HIGHESTA:
+    case elfcpp::R_PPC64_REL16_HIGHESTA:
     case elfcpp::R_PPC64_TPREL16_HIGHESTA:
       Reloc::addr16_ha3(view, value);
       break;
@@ -10127,6 +11579,70 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       // R_PPC64_TLSGD, R_PPC64_TLSLD
       break;
 
+    case elfcpp::R_PPC64_D34:
+    case elfcpp::R_PPC64_D34_LO:
+    case elfcpp::R_PPC64_PCREL34:
+    case elfcpp::R_PPC64_GOT_PCREL34:
+    case elfcpp::R_PPC64_PLT_PCREL34:
+    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:
+      if (size == 32)
+       goto unsupp;
+      status = Reloc::addr34(view, value, overflow);
+      break;
+
+    case elfcpp::R_PPC64_D34_HI30:
+      if (size == 32)
+       goto unsupp;
+      Reloc::addr34_hi(view, value);
+      break;
+
+    case elfcpp::R_PPC64_D34_HA30:
+      if (size == 32)
+       goto unsupp;
+      Reloc::addr34_ha(view, value);
+      break;
+
+    case elfcpp::R_PPC64_D28:
+    case elfcpp::R_PPC64_PCREL28:
+      if (size == 32)
+       goto unsupp;
+      status = Reloc::addr28(view, value, overflow);
+      break;
+
+    case elfcpp::R_PPC64_ADDR16_HIGHER34:
+    case elfcpp::R_PPC64_REL16_HIGHER34:
+      if (size == 32)
+       goto unsupp;
+      Reloc::addr16_higher34(view, value);
+      break;
+
+    case elfcpp::R_PPC64_ADDR16_HIGHERA34:
+    case elfcpp::R_PPC64_REL16_HIGHERA34:
+      if (size == 32)
+       goto unsupp;
+      Reloc::addr16_highera34(view, value);
+      break;
+
+    case elfcpp::R_PPC64_ADDR16_HIGHEST34:
+    case elfcpp::R_PPC64_REL16_HIGHEST34:
+      if (size == 32)
+       goto unsupp;
+      Reloc::addr16_highest34(view, value);
+      break;
+
+    case elfcpp::R_PPC64_ADDR16_HIGHESTA34:
+    case elfcpp::R_PPC64_REL16_HIGHESTA34:
+      if (size == 32)
+       goto unsupp;
+      Reloc::addr16_highesta34(view, value);
+      break;
+
     case elfcpp::R_POWERPC_PLT32:
     case elfcpp::R_POWERPC_PLTREL32:
     case elfcpp::R_PPC_SDAREL16:
@@ -10139,7 +11655,6 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
     case elfcpp::R_PPC64_PLTGOT16_HA:
     case elfcpp::R_PPC64_PLTGOT16_DS:
     case elfcpp::R_PPC64_PLTGOT16_LO_DS:
-    case elfcpp::R_PPC_EMB_RELSDA:
     case elfcpp::R_PPC_TOC16:
     default:
     unsupp:
@@ -10172,7 +11687,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       && (has_stub_value
          || !(gsym != NULL
               && gsym->is_undefined()
-              && is_branch_reloc(r_type))))
+              && is_branch_reloc<size>(r_type))))
     {
       gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
                             _("relocation overflow"));
This page took 0.062946 seconds and 4 git commands to generate.