Add engv32.h to SIM_EXTRA_DEPS in sim/cris
[deliverable/binutils-gdb.git] / gold / powerpc.cc
index 494a1fd93bc439293731fe5cf95262ac01b3b8ab..6b12ed8869b928da5cba4cda85c9f679221dbc19 100644 (file)
@@ -1,6 +1,6 @@
 // powerpc.cc -- powerpc target support for gold.
 
-// Copyright (C) 2008-2019 Free Software Foundation, Inc.
+// Copyright (C) 2008-2021 Free Software Foundation, Inc.
 // Written by David S. Miller <davem@davemloft.net>
 //        and David Edelsohn <edelsohn@gnu.org>
 
@@ -101,6 +101,7 @@ public:
     : Sized_relobj_file<size, big_endian>(name, input_file, offset, ehdr),
       uniq_(object_id++), special_(0), relatoc_(0), toc_(0),
       has_small_toc_reloc_(false), opd_valid_(false),
+      no_tls_marker_(false), tls_marker_(false), tls_opt_error_(false),
       e_flags_(ehdr.get_e_flags()), no_toc_opt_(), opd_ent_(),
       access_from_map_(), has14_(), stub_table_index_(), st_other_(),
       attributes_section_data_(NULL)
@@ -161,6 +162,30 @@ public:
     return this->no_toc_opt_[off];
   }
 
+  void
+  set_no_tls_marker()
+  {
+    if (!this->no_tls_marker_ && this->tls_marker_)
+      this->tls_opt_error_ = true;
+    this->no_tls_marker_ = true;
+  }
+
+  bool
+  no_tls_marker() const
+  { return this->no_tls_marker_; }
+
+  void
+  set_tls_marker()
+  { this->tls_marker_ = true; }
+
+  bool
+  tls_marker() const
+  { return this->tls_marker_; }
+
+  bool
+  tls_opt_error() const
+  { return this->tls_opt_error_; }
+
   // The .got2 section shndx.
   unsigned int
   got2_shndx() const
@@ -249,6 +274,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
@@ -448,6 +479,19 @@ private:
   // access_from_map_.
   bool opd_valid_;
 
+  // Set when finding a __tls_get_addr call without marker relocs.
+  // Such a call disables GD and LD tls optimisations for the object file.
+  bool no_tls_marker_;
+
+  // Set when finding a __tls_get_addr call with marker relocs, or
+  // when finding a relocation that needs __tls_get_addr calls with
+  // marker relocs.
+  bool tls_marker_;
+
+  // Set when seeing a __tls_get_addr call without marker relocs after
+  // seeing some __tls_get_addr calls with marker relocs.
+  bool tls_opt_error_;
+
   // Header e_flags
   elfcpp::Elf_Word e_flags_;
 
@@ -641,9 +685,9 @@ 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_relocs_(false), plt_thread_safe_(false), plt_localentry0_(false),
       plt_localentry0_init_(false), has_localentry0_(false),
-      has_tls_get_addr_opt_(false),
+      has_tls_get_addr_opt_(false), no_tprel_opt_(false),
       relax_failed_(false), relax_fail_count_(0),
       stub_group_size_(0), savres_section_(0),
       tls_get_addr_(NULL), tls_get_addr_opt_(NULL),
@@ -750,10 +794,10 @@ class Target_powerpc : public Sized_target<size, big_endian>
   }
 
   // Accessor
-  const Tocsave_loc
+  const Tocsave_loc*
   tocsave_loc() const
   {
-    return this->tocsave_loc_;
+    return &this->tocsave_loc_;
   }
 
   void
@@ -1072,6 +1116,27 @@ class Target_powerpc : public Sized_target<size, big_endian>
     sym->set_dynsym_index(-1U);
   }
 
+  void
+  set_power10_relocs()
+  {
+      this->power10_relocs_ = true;
+  }
+
+  bool
+  power10_stubs() const
+  {
+    return (this->power10_relocs_
+           && (parameters->options().power10_stubs_enum()
+               != General_options::POWER10_STUBS_NO));
+  }
+
+  bool
+  power10_stubs_auto() const
+  {
+    return (parameters->options().power10_stubs_enum()
+           == General_options::POWER10_STUBS_AUTO);
+  }
+
   bool
   plt_thread_safe() const
   { return this->plt_thread_safe_; }
@@ -1080,6 +1145,10 @@ class Target_powerpc : public Sized_target<size, big_endian>
   plt_localentry0() const
   { return this->plt_localentry0_; }
 
+  bool
+  has_localentry0() const
+  { return this->has_localentry0_; }
+
   void
   set_has_localentry0()
   {
@@ -1120,6 +1189,14 @@ class Target_powerpc : public Sized_target<size, big_endian>
     return false;
   }
 
+  bool
+  tprel_opt() const
+  { return !this->no_tprel_opt_ && parameters->options().tls_optimize(); }
+
+  void
+  set_no_tprel_opt()
+  { this->no_tprel_opt_ = true; }
+
   // Remember any symbols seen with non-zero localentry, even those
   // not providing a definition
   bool
@@ -1188,7 +1265,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:
 
@@ -1251,6 +1328,8 @@ class Target_powerpc : public Sized_target<size, big_endian>
            || (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
@@ -1669,11 +1748,13 @@ class Target_powerpc : public Sized_target<size, big_endian>
   Branches branch_info_;
   Tocsave_loc tocsave_loc_;
 
+  bool power10_relocs_;
   bool plt_thread_safe_;
   bool plt_localentry0_;
   bool plt_localentry0_init_;
   bool has_localentry0_;
   bool has_tls_get_addr_opt_;
+  bool no_tprel_opt_;
 
   bool relax_failed_;
   int relax_fail_count_;
@@ -1840,6 +1921,19 @@ is_plt16_reloc(unsigned int r_type)
          || (size == 64 && r_type == elfcpp::R_PPC64_PLT16_LO_DS));
 }
 
+// GOT_TYPE_STANDARD (ie. not TLS) GOT relocs
+inline bool
+is_got_reloc(unsigned int r_type)
+{
+  return (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_GOT_PCREL34);
+}
+
 // If INSN is an opcode that may be used with an @tls operand, return
 // the transformed insn for TLS optimisation, otherwise return 0.  If
 // REG is non-zero only match an insn with RB or RA equal to REG.
@@ -1977,11 +2071,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.
@@ -2004,11 +2102,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:
@@ -2126,6 +2228,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.
@@ -2324,6 +2476,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.
 
@@ -2633,8 +2804,6 @@ Powerpc_relobj<size, big_endian>::do_relocate_sections(
            if (this->local_has_plt_offset(i))
              {
                Address value = this->local_symbol_value(i, 0);
-               if (size == 64)
-                 value += ppc64_local_entry_offset(i);
                size_t off = this->local_plt_offset(i);
                elfcpp::Swap<size, big_endian>::writeval(oview + off, value);
                modified = true;
@@ -3405,6 +3574,7 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
       from += (this->object_->output_section(this->shndx_)->address()
               + this->offset_);
       Address to;
+      unsigned int other = 0;
       if (gsym != NULL)
        {
          switch (gsym->source())
@@ -3433,7 +3603,7 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
          if (status != Symbol_table::CFVS_OK)
            return true;
          if (size == 64)
-           to += this->object_->ppc64_local_entry_offset(gsym);
+           other = gsym->nonvis() >> 3;
        }
       else
        {
@@ -3451,7 +3621,7 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
            return true;
          to = symval.value(this->object_, 0);
          if (size == 64)
-           to += this->object_->ppc64_local_entry_offset(this->r_sym_);
+           other = this->object_->st_other(this->r_sym_) >> 5;
        }
       if (!(size == 32 && this->r_type_ == elfcpp::R_PPC_PLTREL24))
        to += this->addend_;
@@ -3464,7 +3634,11 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
                                         &to, &dest_shndx))
            return true;
        }
-      Address delta = to - from;
+      unsigned int local_ent = 0;
+      if (size == 64
+         && this->r_type_ != elfcpp::R_PPC64_REL24_NOTOC)
+       local_ent = elfcpp::ppc64_decode_local_entry(other);
+      Address delta = to + local_ent - from;
       if (delta + max_branch_offset >= 2 * max_branch_offset
          || (size == 64
              && this->r_type_ == elfcpp::R_PPC64_REL24_NOTOC
@@ -3486,7 +3660,7 @@ Target_powerpc<size, big_endian>::Branch_info::make_stub(
                           && gsym->output_data() == target->savres_section());
          ok = stub_table->add_long_branch_entry(this->object_,
                                                 this->r_type_,
-                                                from, to, save_res);
+                                                from, to, other, save_res);
        }
     }
   if (!ok)
@@ -3810,7 +3984,6 @@ Target_powerpc<size, big_endian>::do_plt_fde_location(const Output_data* plt,
       // See Output_data_glink::do_write() for glink contents.
       if (len == 0)
        {
-         gold_assert(parameters->doing_static_link());
          // Static linking may need stubs, to support ifunc and long
          // branches.  We need to create an output section for
          // .eh_frame early in the link process, to have a place to
@@ -4075,6 +4248,7 @@ static const uint32_t cmpwi_11_0  = 0x2c0b0000;
 static const uint32_t cror_15_15_15    = 0x4def7b82;
 static const uint32_t cror_31_31_31    = 0x4ffffb82;
 static const uint32_t ld_0_1           = 0xe8010000;
+static const uint32_t ld_0_11          = 0xe80b0000;
 static const uint32_t ld_0_12          = 0xe80c0000;
 static const uint32_t ld_2_1           = 0xe8410000;
 static const uint32_t ld_2_2           = 0xe8420000;
@@ -4091,6 +4265,7 @@ 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;
@@ -4117,8 +4292,10 @@ 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;
@@ -4132,6 +4309,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>
@@ -4412,6 +4593,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
 {
@@ -4438,9 +4631,9 @@ static const unsigned char glink_eh_frame_fde_64v1[] =
   0, 0, 0, 0,                          // Replaced with offset to .glink.
   0, 0, 0, 0,                          // Replaced with size of .glink.
   0,                                   // Augmentation size.
-  elfcpp::DW_CFA_advance_loc + 1,
+  elfcpp::DW_CFA_advance_loc + 2,
   elfcpp::DW_CFA_register, 65, 12,
-  elfcpp::DW_CFA_advance_loc + 5,
+  elfcpp::DW_CFA_advance_loc + 4,
   elfcpp::DW_CFA_restore_extended, 65
 };
 
@@ -4450,9 +4643,20 @@ static const unsigned char glink_eh_frame_fde_64v2[] =
   0, 0, 0, 0,                          // Replaced with offset to .glink.
   0, 0, 0, 0,                          // Replaced with size of .glink.
   0,                                   // Augmentation size.
-  elfcpp::DW_CFA_advance_loc + 1,
+  elfcpp::DW_CFA_advance_loc + 2,
+  elfcpp::DW_CFA_register, 65, 0,
+  elfcpp::DW_CFA_advance_loc + 2,
+  elfcpp::DW_CFA_restore_extended, 65
+};
+
+static const unsigned char glink_eh_frame_fde_64v2_localentry0[] =
+{
+  0, 0, 0, 0,                          // Replaced with offset to .glink.
+  0, 0, 0, 0,                          // Replaced with size of .glink.
+  0,                                   // Augmentation size.
+  elfcpp::DW_CFA_advance_loc + 3,
   elfcpp::DW_CFA_register, 65, 0,
-  elfcpp::DW_CFA_advance_loc + 7,
+  elfcpp::DW_CFA_advance_loc + 2,
   elfcpp::DW_CFA_restore_extended, 65
 };
 
@@ -4506,26 +4710,33 @@ class Stub_table : public Output_relaxed_input_section
   struct Plt_stub_ent
   {
     Plt_stub_ent(unsigned int off, unsigned int indx)
-      : off_(off), indx_(indx), iter_(0), notoc_(0), r2save_(0), localentry0_(0)
+      : off_(off), indx_(indx), iter_(0), notoc_(0), toc_(0),
+       r2save_(0), localentry0_(0), tocoff_(0)
     { }
 
     unsigned int off_;
-    unsigned int indx_ : 28;
+    unsigned int indx_;
     unsigned int iter_ : 1;
     unsigned int notoc_ : 1;
+    unsigned int toc_ : 1;
     unsigned int r2save_ : 1;
     unsigned int localentry0_ : 1;
+    unsigned int tocoff_ : 8;
   };
   struct Branch_stub_ent
   {
     Branch_stub_ent(unsigned int off, bool notoc, bool save_res)
-      : off_(off), iter_(false), notoc_(notoc), save_res_(save_res)
+      : off_(off), iter_(0), notoc_(notoc), toc_(0), save_res_(save_res),
+       other_(0), tocoff_(0)
     { }
 
     unsigned int off_;
-    bool iter_;
-    bool notoc_;
-    bool save_res_;
+    unsigned int iter_ : 1;
+    unsigned int notoc_ : 1;
+    unsigned int toc_ : 1;
+    unsigned int save_res_ : 1;
+    unsigned int other_ : 3;
+    unsigned int tocoff_ : 8;
   };
   typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
   static const Address invalid_address = static_cast<Address>(0) - 1;
@@ -4591,7 +4802,7 @@ class Stub_table : public Output_relaxed_input_section
   // Add a long branch stub.
   bool
   add_long_branch_entry(const Powerpc_relobj<size, big_endian>*,
-                       unsigned int, Address, Address, bool);
+                       unsigned int, Address, Address, unsigned int, bool);
 
   const Branch_stub_ent*
   find_long_branch_entry(const Powerpc_relobj<size, big_endian>*,
@@ -4773,7 +4984,7 @@ class Stub_table : public Output_relaxed_input_section
 
   // Size of a given plt call stub.
   unsigned int
-  plt_call_size(typename Plt_stub_entries::const_iterator p) const;
+  plt_call_size(typename Plt_stub_entries::iterator p) const;
 
   unsigned int
   plt_call_align(unsigned int bytes) const
@@ -4784,16 +4995,14 @@ class Stub_table : public Output_relaxed_input_section
 
   // Return long branch stub size.
   unsigned int
-  branch_stub_size(typename Branch_stub_entries::const_iterator p,
+  branch_stub_size(typename Branch_stub_entries::iterator p,
                   bool* need_lt);
 
-  bool
-  build_tls_opt_head(unsigned char** pp,
-                    typename Plt_stub_entries::const_iterator cs);
+  void
+  build_tls_opt_head(unsigned char** pp,  bool save_lr);
 
-  bool
-  build_tls_opt_tail(unsigned char* p,
-                    typename Plt_stub_entries::const_iterator cs);
+  void
+  build_tls_opt_tail(unsigned char* p);
 
   void
   plt_error(const Plt_stub_key& p);
@@ -4957,15 +5166,23 @@ Stub_table<size, big_endian>::add_plt_call_entry(
        }
       if (r_type == elfcpp::R_PPC64_REL24_NOTOC)
        {
-         if (!p.second && !p.first->second.notoc_)
+         if (!p.second && !p.first->second.notoc_
+             && (!this->targ_->power10_stubs()
+                 || this->targ_->power10_stubs_auto()))
            this->need_resize_ = true;
          p.first->second.notoc_ = 1;
        }
-      else if (!tocsave && !p.first->second.localentry0_)
+      else
        {
-         if (!p.second && !p.first->second.r2save_)
+         if (!p.second && !p.first->second.toc_)
            this->need_resize_ = true;
-         p.first->second.r2save_ = 1;
+         p.first->second.toc_ = 1;
+         if (!tocsave && !p.first->second.localentry0_)
+           {
+             if (!p.second && !p.first->second.r2save_)
+               this->need_resize_ = true;
+             p.first->second.r2save_ = 1;
+           }
        }
     }
   if (p.second || (this->resizing_ && !p.first->second.iter_))
@@ -5007,15 +5224,23 @@ Stub_table<size, big_endian>::add_plt_call_entry(
        }
       if (r_type == elfcpp::R_PPC64_REL24_NOTOC)
        {
-         if (!p.second && !p.first->second.notoc_)
+         if (!p.second && !p.first->second.notoc_
+             && (!this->targ_->power10_stubs()
+                 || this->targ_->power10_stubs_auto()))
            this->need_resize_ = true;
          p.first->second.notoc_ = 1;
        }
-      else if (!tocsave && !p.first->second.localentry0_)
+      else
        {
-         if (!p.second && !p.first->second.r2save_)
+         if (!p.second && !p.first->second.toc_)
            this->need_resize_ = true;
-         p.first->second.r2save_ = 1;
+         p.first->second.toc_ = 1;
+         if (!tocsave && !p.first->second.localentry0_)
+           {
+             if (!p.second && !p.first->second.r2save_)
+               this->need_resize_ = true;
+             p.first->second.r2save_ = 1;
+           }
        }
     }
   if (p.second || (this->resizing_ && !p.first->second.iter_))
@@ -5097,6 +5322,7 @@ Stub_table<size, big_endian>::add_long_branch_entry(
     unsigned int r_type,
     Address from,
     Address to,
+    unsigned int other,
     bool save_res)
 {
   Branch_stub_key key(object, to);
@@ -5104,11 +5330,20 @@ Stub_table<size, big_endian>::add_long_branch_entry(
   Branch_stub_ent ent(this->branch_size_, notoc, save_res);
   std::pair<typename Branch_stub_entries::iterator, bool> p
     = this->long_branch_stubs_.insert(std::make_pair(key, ent));
-  if (notoc && !p.first->second.notoc_)
+  if (notoc)
     {
-      this->need_resize_ = true;
+      if (!p.second && !p.first->second.notoc_)
+       this->need_resize_ = true;
       p.first->second.notoc_ = true;
     }
+  else
+    {
+      if (!p.second && !p.first->second.toc_)
+       this->need_resize_ = true;
+      p.first->second.toc_ = true;
+    }
+  if (size == 64 && p.first->second.other_ == 0)
+    p.first->second.other_ = other;
   gold_assert(save_res == p.first->second.save_res_);
   if (p.second || (this->resizing_ && !p.first->second.iter_))
     {
@@ -5203,7 +5438,7 @@ Stub_table<size, big_endian>::add_eh_frame(Layout* layout)
   if (!this->targ_->has_glink())
     return;
 
-  typedef typename Plt_stub_entries::const_iterator plt_iter;
+  typedef typename Plt_stub_entries::iterator plt_iter;
   std::vector<plt_iter> calls;
   if (!this->plt_call_stubs_.empty())
     for (plt_iter cs = this->plt_call_stubs_.begin();
@@ -5212,7 +5447,8 @@ Stub_table<size, big_endian>::add_eh_frame(Layout* layout)
       if ((this->targ_->is_tls_get_addr_opt(cs->first.sym_)
           && cs->second.r2save_
           && !cs->second.localentry0_)
-         || cs->second.notoc_)
+         || (cs->second.notoc_
+             && !this->targ_->power10_stubs()))
        calls.push_back(cs);
   if (calls.size() > 1)
     std::stable_sort(calls.begin(), calls.end(),
@@ -5220,7 +5456,8 @@ Stub_table<size, big_endian>::add_eh_frame(Layout* layout)
 
   typedef typename Branch_stub_entries::const_iterator branch_iter;
   std::vector<branch_iter> branches;
-  if (!this->long_branch_stubs_.empty())
+  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)
@@ -5362,7 +5599,8 @@ class Output_data_glink : public Output_section_data
   {
     if (size == 64)
       return (8
-             + (this->targ_->abiversion() < 2 ? 11 * 4 : 14 * 4));
+             + (this->targ_->abiversion() < 2 ? 11 * 4
+                : this->targ_->has_localentry0() ? 14 * 4 : 13 * 4));
     return 16 * 4;
   }
 
@@ -5405,6 +5643,12 @@ Output_data_glink<size, big_endian>::add_eh_frame(Layout* layout)
                                     sizeof (Eh_cie<64>::eh_frame_cie),
                                     glink_eh_frame_fde_64v1,
                                     sizeof (glink_eh_frame_fde_64v1));
+      else if (this->targ_->has_localentry0())
+       layout->add_eh_frame_for_plt(this,
+                                    Eh_cie<64>::eh_frame_cie,
+                                    sizeof (Eh_cie<64>::eh_frame_cie),
+                                    glink_eh_frame_fde_64v2_localentry0,
+                                    sizeof (glink_eh_frame_fde_64v2));
       else
        layout->add_eh_frame_for_plt(this,
                                     Eh_cie<64>::eh_frame_cie,
@@ -5503,7 +5747,7 @@ Stub_table<size, big_endian>::define_stub_syms(Symbol_table* symtab)
       // output .symtab ordering depends on the order in which symbols
       // are added to the linker symtab.  We want reproducible output
       // so must sort the call stub symbols.
-      typedef typename Plt_stub_entries::const_iterator plt_iter;
+      typedef typename Plt_stub_entries::iterator plt_iter;
       std::vector<plt_iter> sorted;
       sorted.resize(this->plt_call_stubs_.size());
 
@@ -5547,7 +5791,7 @@ Stub_table<size, big_endian>::define_stub_syms(Symbol_table* symtab)
        }
     }
 
-  typedef typename Branch_stub_entries::const_iterator branch_iter;
+  typedef typename Branch_stub_entries::iterator branch_iter;
   for (branch_iter bs = this->long_branch_stubs_.begin();
        bs != this->long_branch_stubs_.end();
        ++bs)
@@ -5569,88 +5813,152 @@ Stub_table<size, big_endian>::define_stub_syms(Symbol_table* symtab)
 // Emit the start of a __tls_get_addr_opt plt call stub.
 
 template<int size, bool big_endian>
-bool
-Stub_table<size, big_endian>::build_tls_opt_head(
-     unsigned char** pp,
-     typename Plt_stub_entries::const_iterator cs)
+void
+Stub_table<size, big_endian>::build_tls_opt_head(unsigned char** pp,
+                                                bool save_lr)
 {
-  if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+  unsigned char* p = *pp;
+  if (size == 64)
     {
-      unsigned char* p = *pp;
-      if (size == 64)
-       {
-         write_insn<big_endian>(p, ld_11_3 + 0);
-         p += 4;
-         write_insn<big_endian>(p, ld_12_3 + 8);
-         p += 4;
-         write_insn<big_endian>(p, mr_0_3);
-         p += 4;
-         write_insn<big_endian>(p, cmpdi_11_0);
-         p += 4;
-         write_insn<big_endian>(p, add_3_12_13);
-         p += 4;
-         write_insn<big_endian>(p, beqlr);
-         p += 4;
-         write_insn<big_endian>(p, mr_3_0);
-         p += 4;
-         if (cs->second.r2save_ && !cs->second.localentry0_)
-           {
-             write_insn<big_endian>(p, mflr_11);
-             p += 4;
-             write_insn<big_endian>(p, (std_11_1 + this->targ_->stk_linker()));
-             p += 4;
-           }
-       }
-      else
+      write_insn<big_endian>(p, ld_11_3 + 0);
+      p += 4;
+      write_insn<big_endian>(p, ld_12_3 + 8);
+      p += 4;
+      write_insn<big_endian>(p, mr_0_3);
+      p += 4;
+      write_insn<big_endian>(p, cmpdi_11_0);
+      p += 4;
+      write_insn<big_endian>(p, add_3_12_13);
+      p += 4;
+      write_insn<big_endian>(p, beqlr);
+      p += 4;
+      write_insn<big_endian>(p, mr_3_0);
+      p += 4;
+      if (save_lr)
        {
-         write_insn<big_endian>(p, lwz_11_3 + 0);
-         p += 4;
-         write_insn<big_endian>(p, lwz_12_3 + 4);
+         write_insn<big_endian>(p, mflr_11);
          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);
+         write_insn<big_endian>(p, (std_11_1 + this->targ_->stk_linker()));
          p += 4;
        }
-      *pp = p;
-      return true;
     }
-  return false;
+  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;
 }
 
 // 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)
+void
+Stub_table<size, big_endian>::build_tls_opt_tail(unsigned char* p)
 {
-  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);
+}
+
+// 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)
     {
-      write_insn<big_endian>(p, bctrl);
+      off -= 8 - odd;
+      write_insn<big_endian>(p, li_11_0 | (ha34(off) & 0xffff));
       p += 4;
-      write_insn<big_endian>(p, ld_2_1 + this->targ_->stk_toc());
+      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, ld_11_1 + this->targ_->stk_linker());
+      write_insn<big_endian>(p, insn & 0xffffffff);
       p += 4;
-      write_insn<big_endian>(p, mtlr_11);
+      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, blr);
-      return true;
+      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);
     }
-  return false;
+  p += 4;
+  return p;
 }
 
 // Gets the address of a label (1:) in r11 and builds an offset in r12,
@@ -5740,7 +6048,7 @@ build_notoc_offset(unsigned char* p, uint64_t off, bool load)
 template<int size, bool big_endian>
 unsigned int
 Stub_table<size, big_endian>::plt_call_size(
-    typename Plt_stub_entries::const_iterator p) const
+    typename Plt_stub_entries::iterator p) const
 {
   if (size == 32)
     {
@@ -5752,69 +6060,130 @@ Stub_table<size, big_endian>::plt_call_size(
   const Output_data_plt_powerpc<size, big_endian>* plt;
   uint64_t plt_addr = this->plt_off(p, &plt);
   plt_addr += plt->address();
-  unsigned int bytes = 0;
-  const Symbol* gsym = p->first.sym_;
-  if (this->targ_->is_tls_get_addr_opt(gsym))
-    {
-      if (p->second.r2save_ && !p->second.localentry0_)
-       bytes = 13 * 4;
-      else
-       bytes = 7 * 4;
-    }
-
-  if (p->second.r2save_)
-    bytes += 4;
-
-  if (p->second.notoc_)
-    {
-      uint64_t from = this->stub_address() + p->second.off_ + bytes + 2 * 4;
-      if (bytes > 32)
-       from -= 4 * 4;
-      uint64_t off = plt_addr - from;
-      if (off + 0x8000 < 0x10000)
-       bytes += 7 * 4;
-      else if (off + 0x80008000ULL < 0x100000000ULL)
-       bytes += 8 * 4;
-      else
+  if (this->targ_->power10_stubs()
+      && this->targ_->power10_stubs_auto())
+    {
+      unsigned int bytes = 0;
+      if (p->second.notoc_)
+       {
+         if (this->targ_->is_tls_get_addr_opt(p->first.sym_))
+           bytes = 7 * 4;
+         uint64_t from = this->stub_address() + p->second.off_ + bytes;
+         uint64_t odd = from & 4;
+         uint64_t off = plt_addr - from;
+         if (off - odd + (1ULL << 33) < 1ULL << 34)
+           bytes += odd + 4 * 4;
+         else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+           bytes += 7 * 4;
+         else
+           bytes += 8 * 4;
+         bytes = this->plt_call_align(bytes);
+       }
+      unsigned int tail = 0;
+      if (p->second.toc_)
        {
-         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)
+         p->second.tocoff_ = bytes;
+         if (this->targ_->is_tls_get_addr_opt(p->first.sym_))
+           {
+             bytes += 7 * 4;
+             if (p->second.r2save_ && !p->second.localentry0_)
+               {
+                 bytes += 2 * 4;
+                 tail = 4 * 4;
+               }
+           }
+         if (p->second.r2save_)
            bytes += 4;
+         uint64_t got_addr
+           = this->targ_->got_section()->output_section()->address();
+         const Powerpc_relobj<size, big_endian>* ppcobj = static_cast
+           <const Powerpc_relobj<size, big_endian>*>(p->first.object_);
+         got_addr += ppcobj->toc_base_offset();
+         uint64_t off = plt_addr - got_addr;
+         bytes += 3 * 4 + 4 * (ha(off) != 0);
        }
-      return bytes;
+      return bytes + tail;
     }
-
-  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)
+  else
     {
-      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;
-}
+      unsigned int bytes = 0;
+      unsigned int tail = 0;
+      if (this->targ_->is_tls_get_addr_opt(p->first.sym_))
+       {
+         bytes = 7 * 4;
+         if (p->second.r2save_ && !p->second.localentry0_)
+           {
+             bytes = 9 * 4;
+             tail = 4 * 4;
+           }
+       }
 
-// Return long branch stub size.
+      if (p->second.r2save_)
+       bytes += 4;
+
+      if (this->targ_->power10_stubs())
+       {
+         uint64_t from = this->stub_address() + p->second.off_ + bytes;
+         uint64_t odd = from & 4;
+         uint64_t off = plt_addr - from;
+         if (off - odd + (1ULL << 33) < 1ULL << 34)
+           bytes += odd + 4 * 4;
+         else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+           bytes += 7 * 4;
+         else
+           bytes += 8 * 4;
+         return bytes + tail;
+       }
+
+      if (p->second.notoc_)
+       {
+         uint64_t from = this->stub_address() + p->second.off_ + bytes + 2 * 4;
+         uint64_t off = plt_addr - from;
+         if (off + 0x8000 < 0x10000)
+           bytes += 7 * 4;
+         else if (off + 0x80008000ULL < 0x100000000ULL)
+           bytes += 8 * 4;
+         else
+           {
+             bytes += 8 * 4;
+             if (off + 0x800000000000ULL >= 0x1000000000000ULL
+                 && ((off >> 32) & 0xffff) != 0)
+               bytes += 4;
+             if (((off >> 32) & 0xffffffffULL) != 0)
+               bytes += 4;
+             if (hi(off) != 0)
+               bytes += 4;
+             if (l(off) != 0)
+               bytes += 4;
+           }
+         return bytes + tail;
+       }
+
+      uint64_t got_addr = this->targ_->got_section()->output_section()->address();
+      const Powerpc_relobj<size, big_endian>* ppcobj = static_cast
+       <const Powerpc_relobj<size, big_endian>*>(p->first.object_);
+      got_addr += ppcobj->toc_base_offset();
+      uint64_t off = plt_addr - got_addr;
+      bytes += 3 * 4 + 4 * (ha(off) != 0);
+      if (this->targ_->abiversion() < 2)
+       {
+         bool static_chain = parameters->options().plt_static_chain();
+         bool thread_safe = this->targ_->plt_thread_safe();
+         bytes += (4
+                   + 4 * static_chain
+                   + 8 * thread_safe
+                   + 4 * (ha(off + 8 + 8 * static_chain) != ha(off)));
+       }
+      return bytes + tail;
+    }
+}
+
+// Return 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,
+     typename Branch_stub_entries::iterator p,
      bool* need_lt)
 {
   Address loc = this->stub_address() + this->last_plt_size_ + p->second.off_;
@@ -5828,34 +6197,57 @@ Stub_table<size, big_endian>::branch_stub_size(
     }
 
   uint64_t off = p->first.dest_ - loc;
+  unsigned int bytes = 0;
   if (p->second.notoc_)
     {
-      off -= 8;
-      if (off + 0x8000 < 0x10000)
-       return 24;
-      if (off + 0x80008000ULL < 0x100000000ULL)
+      if (this->targ_->power10_stubs())
        {
-         if (off + 24 + (1 << 25) < 2 << 25)
-           return 28;
-         return 32;
+         Address odd = loc & 4;
+         if (off + (1 << 25) < 2 << 25)
+           bytes = odd + 12;
+         else if (off - odd + (1ULL << 33) < 1ULL << 34)
+           bytes = odd + 16;
+         else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+           bytes = 28;
+         else
+           bytes = 32;
+         if (!(p->second.toc_ && this->targ_->power10_stubs_auto()))
+           return bytes;
+         p->second.tocoff_ = bytes;
+       }
+      else
+       {
+         off -= 8;
+         if (off + 0x8000 < 0x10000)
+           return 24;
+         if (off + 0x80008000ULL < 0x100000000ULL)
+           {
+             if (off + 24 + (1 << 25) < 2 << 25)
+               return 28;
+             return 32;
+           }
+
+         bytes = 32;
+         if (off + 0x800000000000ULL >= 0x1000000000000ULL
+             && ((off >> 32) & 0xffff) != 0)
+           bytes += 4;
+         if (((off >> 32) & 0xffffffffULL) != 0)
+           bytes += 4;
+         if (hi(off) != 0)
+           bytes += 4;
+         if (l(off) != 0)
+           bytes += 4;
+         return bytes;
        }
-      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;
     }
 
+  off += elfcpp::ppc64_decode_local_entry(p->second.other_);
   if (off + (1 << 25) < 2 << 25)
-    return 4;
-  *need_lt = true;
-  return 16;
+    return bytes + 4;
+  if (!this->targ_->power10_stubs()
+      || (p->second.toc_ && this->targ_->power10_stubs_auto()))
+    *need_lt = true;
+  return bytes + 16;
 }
 
 template<int size, bool big_endian>
@@ -5888,7 +6280,202 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
   unsigned char* const oview = of->get_output_view(off, oview_size);
   unsigned char* p;
 
-  if (size == 64)
+  if (size == 64
+      && this->targ_->power10_stubs())
+    {
+      const Output_data_got_powerpc<size, big_endian>* got
+       = this->targ_->got_section();
+      Address got_os_addr = got->output_section()->address();
+
+      if (!this->plt_call_stubs_.empty())
+       {
+         // Write out plt call stubs.
+         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_;
+             const Output_data_plt_powerpc<size, big_endian>* plt;
+             Address pltoff = this->plt_off(cs, &plt);
+             Address plt_addr = pltoff + plt->address();
+             if (this->targ_->power10_stubs_auto())
+               {
+                 if (cs->second.notoc_)
+                   {
+                     if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+                       this->build_tls_opt_head(&p, false);
+                     Address from = this->stub_address() + (p - oview);
+                     Address delta = plt_addr - from;
+                     p = build_power10_offset<big_endian>(p, delta, from & 4,
+                                                          true);
+                     write_insn<big_endian>(p, mtctr_12);
+                     p += 4;
+                     write_insn<big_endian>(p, bctr);
+                     p += 4;
+                     p = oview + this->plt_call_align(p - oview);
+                   }
+                 if (cs->second.toc_)
+                   {
+                     if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+                       {
+                         bool save_lr
+                           = cs->second.r2save_ && !cs->second.localentry0_;
+                         this->build_tls_opt_head(&p, save_lr);
+                       }
+                     const Powerpc_relobj<size, big_endian>* ppcobj
+                       = static_cast<const Powerpc_relobj<size, big_endian>*>(
+                           cs->first.object_);
+                     Address got_addr = got_os_addr + ppcobj->toc_base_offset();
+                     Address off = plt_addr - got_addr;
+
+                     if (off + 0x80008000 > 0xffffffff || (off & 7) != 0)
+                       this->plt_error(cs->first);
+
+                     if (cs->second.r2save_)
+                       {
+                         write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
+                         p += 4;
+                       }
+                     if (ha(off) != 0)
+                       {
+                         write_insn<big_endian>(p, addis_12_2 + ha(off));
+                         p += 4;
+                         write_insn<big_endian>(p, ld_12_12 + l(off));
+                         p += 4;
+                       }
+                     else
+                       {
+                         write_insn<big_endian>(p, ld_12_2 + l(off));
+                         p += 4;
+                       }
+                     write_insn<big_endian>(p, mtctr_12);
+                     p += 4;
+                     if (cs->second.r2save_
+                         && !cs->second.localentry0_
+                         && this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+                       this->build_tls_opt_tail(p);
+                     else
+                       write_insn<big_endian>(p, bctr);
+                   }
+               }
+             else
+               {
+                 if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+                   {
+                     bool save_lr
+                       = cs->second.r2save_ && !cs->second.localentry0_;
+                     this->build_tls_opt_head(&p, save_lr);
+                   }
+                 if (cs->second.r2save_)
+                   {
+                     write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
+                     p += 4;
+                   }
+                 Address from = this->stub_address() + (p - oview);
+                 Address delta = plt_addr - from;
+                 p = build_power10_offset<big_endian>(p, delta, from & 4, true);
+                 write_insn<big_endian>(p, mtctr_12);
+                 p += 4;
+                 if (cs->second.r2save_
+                     && !cs->second.localentry0_
+                     && this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+                   this->build_tls_opt_tail(p);
+                 else
+                   write_insn<big_endian>(p, bctr);
+               }
+           }
+       }
+
+      // 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 (this->targ_->power10_stubs_auto())
+           {
+             if (bs->second.notoc_)
+               {
+                 unsigned char* startp = p;
+                 p = build_power10_offset<big_endian>(p, delta,
+                                                      loc & 4, false);
+                 delta -= p - startp;
+                 startp = p;
+                 if (delta + (1 << 25) < 2 << 25)
+                   write_insn<big_endian>(p, b | (delta & 0x3fffffc));
+                 else
+                   {
+                     write_insn<big_endian>(p, mtctr_12);
+                     p += 4;
+                     write_insn<big_endian>(p, bctr);
+                   }
+                 p += 4;
+                 delta -= p - startp;
+               }
+             if (bs->second.toc_)
+               {
+                 delta += elfcpp::ppc64_decode_local_entry(bs->second.other_);
+                 if (delta + (1 << 25) >= 2 << 25)
+                   {
+                     Address brlt_addr
+                       = this->targ_->find_branch_lookup_table(bs->first.dest_);
+                     gold_assert(brlt_addr != invalid_address);
+                     brlt_addr += this->targ_->brlt_section()->address();
+                     Address got_addr = got_os_addr + bs->first.toc_base_off_;
+                     Address brltoff = brlt_addr - got_addr;
+                     if (ha(brltoff) == 0)
+                       {
+                         write_insn<big_endian>(p, ld_12_2 + l(brltoff));
+                         p += 4;
+                       }
+                     else
+                       {
+                         write_insn<big_endian>(p, addis_12_2 + ha(brltoff));
+                         p += 4;
+                         write_insn<big_endian>(p, ld_12_12 + l(brltoff));
+                         p += 4;
+                       }
+                   }
+                 if (delta + (1 << 25) < 2 << 25)
+                   write_insn<big_endian>(p, b | (delta & 0x3fffffc));
+                 else
+                   {
+                     write_insn<big_endian>(p, mtctr_12);
+                     p += 4;
+                     write_insn<big_endian>(p, bctr);
+                   }
+               }
+           }
+         else
+           {
+             if (!bs->second.notoc_)
+               delta += elfcpp::ppc64_decode_local_entry(bs->second.other_);
+             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();
@@ -5908,7 +6495,11 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
              Address plt_addr = pltoff + plt->address();
 
              p = oview + cs->second.off_;
-             this->build_tls_opt_head(&p, cs);
+             if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+               {
+                 bool save_lr = cs->second.r2save_ && !cs->second.localentry0_;
+                 this->build_tls_opt_head(&p, save_lr);
+               }
              if (cs->second.r2save_)
                {
                  write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
@@ -5945,7 +6536,11 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
                }
              write_insn<big_endian>(p, mtctr_12);
              p += 4;
-             if (!this->build_tls_opt_tail(p, cs))
+             if (cs->second.r2save_
+                 && !cs->second.localentry0_
+                 && this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+               this->build_tls_opt_tail(p);
+             else
                write_insn<big_endian>(p, bctr);
            }
        }
@@ -5996,8 +6591,12 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
                }
 
              p = oview + cs->second.off_;
-             if (this->build_tls_opt_head(&p, cs))
-               use_fake_dep = thread_safe;
+             if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+               {
+                 bool save_lr = cs->second.r2save_ && !cs->second.localentry0_;
+                 this->build_tls_opt_head(&p, save_lr);
+                 use_fake_dep = thread_safe;
+               }
              if (cs->second.r2save_)
                {
                  write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
@@ -6059,8 +6658,10 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
                  write_insn<big_endian>(p, ld_2_2 + l(off + 8));
                  p += 4;
                }
-             if (this->build_tls_opt_tail(p, cs))
-               ;
+             if (cs->second.r2save_
+                 && !cs->second.localentry0_
+                 && this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+               this->build_tls_opt_tail(p);
              else if (thread_safe && !use_fake_dep)
                {
                  write_insn<big_endian>(p, cmpldi_2_0);
@@ -6086,6 +6687,8 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
          p = oview + off;
          Address loc = this->stub_address() + off;
          Address delta = bs->first.dest_ - loc;
+         if (!bs->second.notoc_)
+           delta += elfcpp::ppc64_decode_local_entry(bs->second.other_);
          if (bs->second.notoc_)
            {
              unsigned char* startp = p;
@@ -6141,7 +6744,8 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
              plt_addr += plt->address();
 
              p = oview + cs->second.off_;
-             this->build_tls_opt_head(&p, cs);
+             if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
+               this->build_tls_opt_head(&p, false);
              if (parameters->options().output_is_position_independent())
                {
                  Address got_addr;
@@ -6281,15 +6885,25 @@ Output_data_glink<size, big_endian>::do_write(Output_file* of)
            }
          else
            {
+             if (this->targ_->has_localentry0())
+               {
+                 write_insn<big_endian>(p, std_2_1 + 24),      p += 4;
+               }
              write_insn<big_endian>(p, mflr_0),                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, std_2_1 + 24),          p += 4;
-             write_insn<big_endian>(p, ld_2_11 + l(-16)),      p += 4;
              write_insn<big_endian>(p, mtlr_0),                p += 4;
+             if (this->targ_->has_localentry0())
+               {
+                 write_insn<big_endian>(p, ld_0_11 + l(-20)),  p += 4;
+               }
+             else
+               {
+                 write_insn<big_endian>(p, ld_0_11 + l(-16)),  p += 4;
+               }
              write_insn<big_endian>(p, sub_12_12_11),          p += 4;
-             write_insn<big_endian>(p, add_11_2_11),           p += 4;
-             write_insn<big_endian>(p, addi_0_12 + l(-48)),    p += 4;
+             write_insn<big_endian>(p, add_11_0_11),           p += 4;
+             write_insn<big_endian>(p, addi_0_12 + l(-44)),    p += 4;
              write_insn<big_endian>(p, ld_12_11 + 0),          p += 4;
              write_insn<big_endian>(p, srdi_0_0_2),            p += 4;
              write_insn<big_endian>(p, mtctr_12),              p += 4;
@@ -6919,6 +7533,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;
 
@@ -6942,6 +7565,12 @@ Target_powerpc<size, big_endian>::Scan::get_reference_flags(
     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;
 
@@ -6963,6 +7592,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:
@@ -6973,11 +7603,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_TLSGD_PCREL34:
+    case elfcpp::R_PPC64_GOT_TLSLD_PCREL34:
+    case elfcpp::R_PPC64_GOT_TPREL_PCREL34:
+    case elfcpp::R_PPC64_GOT_DTPREL_PCREL34:
       ref = Symbol::TLS_REF;
       break;
 
@@ -7043,7 +7683,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:
@@ -7092,6 +7731,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:
@@ -7154,6 +7794,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.
@@ -7165,6 +7806,8 @@ Target_powerpc<size, big_endian>::Scan::reloc_needs_plt_for_ifunc(
     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;
 
@@ -7258,28 +7901,47 @@ Target_powerpc<size, big_endian>::Scan::local(
     const elfcpp::Sym<size, big_endian>& lsym,
     bool is_discarded)
 {
-  this->maybe_skip_tls_get_addr_call(target, r_type, NULL);
+  Powerpc_relobj<size, big_endian>* ppc_object
+    = static_cast<Powerpc_relobj<size, big_endian>*>(object);
+
+  switch (this->maybe_skip_tls_get_addr_call(target, r_type, NULL))
+    {
+    case Track_tls::NOT_EXPECTED:
+      ppc_object->set_no_tls_marker();
+      break;
+    default:
+      break;
+    }
 
   if ((size == 64 && r_type == elfcpp::R_PPC64_TLSGD)
       || (size == 32 && r_type == elfcpp::R_PPC_TLSGD))
     {
       this->expect_tls_get_addr_call();
-      const tls::Tls_optimization tls_type = target->optimize_tls_gd(true);
-      if (tls_type != tls::TLSOPT_NONE)
-       this->skip_next_tls_get_addr_call();
+      if (!ppc_object->no_tls_marker())
+       {
+         tls::Tls_optimization tls_type = target->optimize_tls_gd(true);
+         if (tls_type != tls::TLSOPT_NONE)
+           {
+             this->skip_next_tls_get_addr_call();
+             ppc_object->set_tls_marker();
+           }
+       }
     }
   else if ((size == 64 && r_type == elfcpp::R_PPC64_TLSLD)
           || (size == 32 && r_type == elfcpp::R_PPC_TLSLD))
     {
       this->expect_tls_get_addr_call();
-      const tls::Tls_optimization tls_type = target->optimize_tls_ld();
-      if (tls_type != tls::TLSOPT_NONE)
-       this->skip_next_tls_get_addr_call();
+      if (!ppc_object->no_tls_marker())
+       {
+         tls::Tls_optimization tls_type = target->optimize_tls_ld();
+         if (tls_type != tls::TLSOPT_NONE)
+           {
+             this->skip_next_tls_get_addr_call();
+             ppc_object->set_tls_marker();
+           }
+       }
     }
 
-  Powerpc_relobj<size, big_endian>* ppc_object
-    = static_cast<Powerpc_relobj<size, big_endian>*>(object);
-
   if (is_discarded)
     {
       if (size == 64
@@ -7310,6 +7972,24 @@ Target_powerpc<size, big_endian>::Scan::local(
     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:
@@ -7401,6 +8081,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:
@@ -7499,6 +8181,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:
@@ -7547,12 +8230,15 @@ Target_powerpc<size, big_endian>::Scan::local(
       target->got_section(symtab, layout);
       break;
 
+    case elfcpp::R_PPC64_GOT_TLSGD_PCREL34:
     case elfcpp::R_POWERPC_GOT_TLSGD16:
     case elfcpp::R_POWERPC_GOT_TLSGD16_LO:
     case elfcpp::R_POWERPC_GOT_TLSGD16_HI:
     case elfcpp::R_POWERPC_GOT_TLSGD16_HA:
       {
-       const tls::Tls_optimization tls_type = target->optimize_tls_gd(true);
+       tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+       if (!ppc_object->no_tls_marker())
+         tls_type = target->optimize_tls_gd(true);
        if (tls_type == tls::TLSOPT_NONE)
          {
            Output_data_got_powerpc<size, big_endian>* got
@@ -7565,18 +8251,22 @@ Target_powerpc<size, big_endian>::Scan::local(
        else if (tls_type == tls::TLSOPT_TO_LE)
          {
            // no GOT relocs needed for Local Exec.
+           ppc_object->set_tls_marker();
          }
        else
          gold_unreachable();
       }
       break;
 
+    case elfcpp::R_PPC64_GOT_TLSLD_PCREL34:
     case elfcpp::R_POWERPC_GOT_TLSLD16:
     case elfcpp::R_POWERPC_GOT_TLSLD16_LO:
     case elfcpp::R_POWERPC_GOT_TLSLD16_HI:
     case elfcpp::R_POWERPC_GOT_TLSLD16_HA:
       {
-       const tls::Tls_optimization tls_type = target->optimize_tls_ld();
+       tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+       if (!ppc_object->no_tls_marker())
+         tls_type = target->optimize_tls_ld();
        if (tls_type == tls::TLSOPT_NONE)
          target->tlsld_got_offset(symtab, layout, object);
        else if (tls_type == tls::TLSOPT_TO_LE)
@@ -7588,12 +8278,14 @@ Target_powerpc<size, big_endian>::Scan::local(
                gold_assert(os != NULL);
                os->set_needs_symtab_index();
              }
+           ppc_object->set_tls_marker();
          }
        else
          gold_unreachable();
       }
       break;
 
+    case elfcpp::R_PPC64_GOT_DTPREL_PCREL34:
     case elfcpp::R_POWERPC_GOT_DTPREL16:
     case elfcpp::R_POWERPC_GOT_DTPREL16_LO:
     case elfcpp::R_POWERPC_GOT_DTPREL16_HI:
@@ -7606,12 +8298,13 @@ Target_powerpc<size, big_endian>::Scan::local(
       }
       break;
 
+    case elfcpp::R_PPC64_GOT_TPREL_PCREL34:
     case elfcpp::R_POWERPC_GOT_TPREL16:
     case elfcpp::R_POWERPC_GOT_TPREL16_LO:
     case elfcpp::R_POWERPC_GOT_TPREL16_HI:
     case elfcpp::R_POWERPC_GOT_TPREL16_HA:
       {
-       const tls::Tls_optimization tls_type = target->optimize_tls_ie(true);
+       tls::Tls_optimization tls_type = target->optimize_tls_ie(true);
        if (tls_type == tls::TLSOPT_NONE)
          {
            unsigned int r_sym = elfcpp::elf_r_sym<size>(reloc.get_r_info());
@@ -7797,6 +8490,91 @@ 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_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:
+      if (size != 64)
+       break;
+      // Fall through.
+    case elfcpp::R_POWERPC_TPREL16:
+    case elfcpp::R_POWERPC_TPREL16_LO:
+    case elfcpp::R_POWERPC_TPREL16_HI:
+    case elfcpp::R_POWERPC_TPREL16_HA:
+      layout->set_has_static_tls();
+      break;
+    default:
+      break;
+    }
+
+  switch (r_type)
+    {
+    case elfcpp::R_POWERPC_TPREL16_HA:
+      if (target->tprel_opt())
+       {
+         section_size_type slen;
+         const unsigned char* view = NULL;
+         view = ppc_object->section_contents(data_shndx, &slen, false);
+         section_size_type off
+           = convert_to_section_size_type(reloc.get_r_offset()) & -4;
+         if (off < slen)
+           {
+             uint32_t insn = elfcpp::Swap<32, big_endian>::readval(view + off);
+             if ((insn & ((0x3fu << 26) | 0x1f << 16))
+                 != ((15u << 26) | ((size == 32 ? 2 : 13) << 16)))
+               target->set_no_tprel_opt();
+           }
+       }
+      break;
+
+    case elfcpp::R_PPC64_TPREL16_HIGH:
+    case elfcpp::R_PPC64_TPREL16_HIGHA:
+    case elfcpp::R_PPC64_TPREL16_HIGHER:
+    case elfcpp::R_PPC64_TPREL16_HIGHERA:
+    case elfcpp::R_PPC64_TPREL16_HIGHEST:
+    case elfcpp::R_PPC64_TPREL16_HIGHESTA:
+      if (size != 64)
+       break;
+      // Fall through.
+    case elfcpp::R_POWERPC_TPREL16_HI:
+      target->set_no_tprel_opt();
+      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_TLSGD_PCREL34:
+    case elfcpp::R_PPC64_GOT_TLSLD_PCREL34:
+    case elfcpp::R_PPC64_GOT_DTPREL_PCREL34:
+    case elfcpp::R_PPC64_GOT_TPREL_PCREL34:
+      target->set_power10_relocs();
+      break;
     default:
       break;
     }
@@ -7830,9 +8608,19 @@ Target_powerpc<size, big_endian>::Scan::global(
     unsigned int r_type,
     Symbol* gsym)
 {
-  if (this->maybe_skip_tls_get_addr_call(target, r_type, gsym)
-      == Track_tls::SKIP)
-    return;
+  Powerpc_relobj<size, big_endian>* ppc_object
+    = static_cast<Powerpc_relobj<size, big_endian>*>(object);
+
+  switch (this->maybe_skip_tls_get_addr_call(target, r_type, gsym))
+    {
+    case Track_tls::NOT_EXPECTED:
+      ppc_object->set_no_tls_marker();
+      break;
+    case Track_tls::SKIP:
+      return;
+    default:
+      break;
+    }
 
   if (target->replace_tls_get_addr(gsym))
     // Change a __tls_get_addr reference to __tls_get_addr_opt
@@ -7843,23 +8631,32 @@ Target_powerpc<size, big_endian>::Scan::global(
       || (size == 32 && r_type == elfcpp::R_PPC_TLSGD))
     {
       this->expect_tls_get_addr_call();
-      const bool final = gsym->final_value_is_known();
-      const tls::Tls_optimization tls_type = target->optimize_tls_gd(final);
-      if (tls_type != tls::TLSOPT_NONE)
-       this->skip_next_tls_get_addr_call();
+      if (!ppc_object->no_tls_marker())
+       {
+         bool final = gsym->final_value_is_known();
+         tls::Tls_optimization tls_type = target->optimize_tls_gd(final);
+         if (tls_type != tls::TLSOPT_NONE)
+           {
+             this->skip_next_tls_get_addr_call();
+             ppc_object->set_tls_marker();
+           }
+       }
     }
   else if ((size == 64 && r_type == elfcpp::R_PPC64_TLSLD)
           || (size == 32 && r_type == elfcpp::R_PPC_TLSLD))
     {
       this->expect_tls_get_addr_call();
-      const tls::Tls_optimization tls_type = target->optimize_tls_ld();
-      if (tls_type != tls::TLSOPT_NONE)
-       this->skip_next_tls_get_addr_call();
+      if (!ppc_object->no_tls_marker())
+       {
+         tls::Tls_optimization tls_type = target->optimize_tls_ld();
+         if (tls_type != tls::TLSOPT_NONE)
+           {
+             this->skip_next_tls_get_addr_call();
+             ppc_object->set_tls_marker();
+           }
+       }
     }
 
-  Powerpc_relobj<size, big_endian>* ppc_object
-    = static_cast<Powerpc_relobj<size, big_endian>*>(object);
-
   // A STT_GNU_IFUNC symbol may require a PLT entry.
   bool is_ifunc = gsym->type() == elfcpp::STT_GNU_IFUNC;
   bool pushed_ifunc = false;
@@ -7884,6 +8681,24 @@ Target_powerpc<size, big_endian>::Scan::global(
     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:
@@ -8019,6 +8834,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:
@@ -8151,6 +8968,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:
@@ -8209,13 +9027,18 @@ Target_powerpc<size, big_endian>::Scan::global(
       target->got_section(symtab, layout);
       break;
 
+    case elfcpp::R_PPC64_GOT_TLSGD_PCREL34:
     case elfcpp::R_POWERPC_GOT_TLSGD16:
     case elfcpp::R_POWERPC_GOT_TLSGD16_LO:
     case elfcpp::R_POWERPC_GOT_TLSGD16_HI:
     case elfcpp::R_POWERPC_GOT_TLSGD16_HA:
       {
-       const bool final = gsym->final_value_is_known();
-       const tls::Tls_optimization tls_type = target->optimize_tls_gd(final);
+       tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+       if (!ppc_object->no_tls_marker())
+         {
+           bool final = gsym->final_value_is_known();
+           tls_type = target->optimize_tls_gd(final);
+         }
        if (tls_type == tls::TLSOPT_NONE)
          {
            Output_data_got_powerpc<size, big_endian>* got
@@ -8247,22 +9070,27 @@ Target_powerpc<size, big_endian>::Scan::global(
                                                           got, off, 0);
                  }
              }
+           ppc_object->set_tls_marker();
          }
        else if (tls_type == tls::TLSOPT_TO_LE)
          {
            // no GOT relocs needed for Local Exec.
+           ppc_object->set_tls_marker();
          }
        else
          gold_unreachable();
       }
       break;
 
+    case elfcpp::R_PPC64_GOT_TLSLD_PCREL34:
     case elfcpp::R_POWERPC_GOT_TLSLD16:
     case elfcpp::R_POWERPC_GOT_TLSLD16_LO:
     case elfcpp::R_POWERPC_GOT_TLSLD16_HI:
     case elfcpp::R_POWERPC_GOT_TLSLD16_HA:
       {
-       const tls::Tls_optimization tls_type = target->optimize_tls_ld();
+       tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+       if (!ppc_object->no_tls_marker())
+         tls_type = target->optimize_tls_ld();
        if (tls_type == tls::TLSOPT_NONE)
          target->tlsld_got_offset(symtab, layout, object);
        else if (tls_type == tls::TLSOPT_TO_LE)
@@ -8274,12 +9102,14 @@ Target_powerpc<size, big_endian>::Scan::global(
                gold_assert(os != NULL);
                os->set_needs_symtab_index();
              }
+           ppc_object->set_tls_marker();
          }
        else
          gold_unreachable();
       }
       break;
 
+    case elfcpp::R_PPC64_GOT_DTPREL_PCREL34:
     case elfcpp::R_POWERPC_GOT_DTPREL16:
     case elfcpp::R_POWERPC_GOT_DTPREL16_LO:
     case elfcpp::R_POWERPC_GOT_DTPREL16_HI:
@@ -8299,13 +9129,14 @@ Target_powerpc<size, big_endian>::Scan::global(
       }
       break;
 
+    case elfcpp::R_PPC64_GOT_TPREL_PCREL34:
     case elfcpp::R_POWERPC_GOT_TPREL16:
     case elfcpp::R_POWERPC_GOT_TPREL16_LO:
     case elfcpp::R_POWERPC_GOT_TPREL16_HI:
     case elfcpp::R_POWERPC_GOT_TPREL16_HA:
       {
-       const bool final = gsym->final_value_is_known();
-       const tls::Tls_optimization tls_type = target->optimize_tls_ie(final);
+       bool final = gsym->final_value_is_known();
+       tls::Tls_optimization tls_type = target->optimize_tls_ie(final);
        if (tls_type == tls::TLSOPT_NONE)
          {
            if (!gsym->has_got_offset(GOT_TYPE_TPREL))
@@ -8484,6 +9315,91 @@ 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_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:
+      if (size != 64)
+       break;
+      // Fall through.
+    case elfcpp::R_POWERPC_TPREL16:
+    case elfcpp::R_POWERPC_TPREL16_LO:
+    case elfcpp::R_POWERPC_TPREL16_HI:
+    case elfcpp::R_POWERPC_TPREL16_HA:
+      layout->set_has_static_tls();
+      break;
+    default:
+      break;
+    }
+
+  switch (r_type)
+    {
+    case elfcpp::R_POWERPC_TPREL16_HA:
+      if (target->tprel_opt())
+       {
+         section_size_type slen;
+         const unsigned char* view = NULL;
+         view = ppc_object->section_contents(data_shndx, &slen, false);
+         section_size_type off
+           = convert_to_section_size_type(reloc.get_r_offset()) & -4;
+         if (off < slen)
+           {
+             uint32_t insn = elfcpp::Swap<32, big_endian>::readval(view + off);
+             if ((insn & ((0x3fu << 26) | 0x1f << 16))
+                 != ((15u << 26) | ((size == 32 ? 2 : 13) << 16)))
+               target->set_no_tprel_opt();
+           }
+       }
+      break;
+
+    case elfcpp::R_PPC64_TPREL16_HIGH:
+    case elfcpp::R_PPC64_TPREL16_HIGHA:
+    case elfcpp::R_PPC64_TPREL16_HIGHER:
+    case elfcpp::R_PPC64_TPREL16_HIGHERA:
+    case elfcpp::R_PPC64_TPREL16_HIGHEST:
+    case elfcpp::R_PPC64_TPREL16_HIGHESTA:
+      if (size != 64)
+       break;
+      // Fall through.
+    case elfcpp::R_POWERPC_TPREL16_HI:
+      target->set_no_tprel_opt();
+      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_TLSGD_PCREL34:
+    case elfcpp::R_PPC64_GOT_TLSLD_PCREL34:
+    case elfcpp::R_PPC64_GOT_DTPREL_PCREL34:
+    case elfcpp::R_PPC64_GOT_TPREL_PCREL34:
+      target->set_power10_relocs();
+      break;
     default:
       break;
     }
@@ -8599,7 +9515,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());
@@ -8824,6 +9740,13 @@ Target_powerpc<size, big_endian>::scan_relocs(
     needs_special_offset_handling,
     local_symbol_count,
     plocal_symbols);
+
+  if (this->plt_localentry0_ && this->power10_relocs_)
+    {
+      gold_warning(_("--plt-localentry is incompatible with "
+                    "power10 pc-relative code"));
+      this->plt_localentry0_ = false;
+    }
 }
 
 // Functor class for processing the global symbol table.
@@ -9024,7 +9947,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();
@@ -9034,7 +9957,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());
     }
 
@@ -9057,7 +9980,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.
@@ -9073,12 +9996,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;
@@ -9086,10 +10011,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)
        {
@@ -9122,10 +10050,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)
        {
@@ -9155,10 +10086,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);
        }
     }
 
@@ -9343,6 +10280,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>
@@ -9372,11 +10448,25 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
 
   const elfcpp::Rela<size, big_endian> rela(preloc);
   unsigned int r_type = elfcpp::elf_r_type<size>(rela.get_r_info());
+  Powerpc_relobj<size, big_endian>* const object
+    = static_cast<Powerpc_relobj<size, big_endian>*>(relinfo->object);
   switch (this->maybe_skip_tls_get_addr_call(target, r_type, gsym))
     {
     case Track_tls::NOT_EXPECTED:
-      gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
-                            _("__tls_get_addr call lacks marker reloc"));
+      if (!parameters->options().shared()
+         && parameters->options().tls_optimize())
+       {
+         // It is a hard error to see a __tls_get_addr call without
+         // marker relocs after seeing calls with marker relocs in the
+         // same object file, because dynamic relocation accounting
+         // will be wrong.
+         if (object->tls_opt_error())
+           gold_error_at_location(relinfo, relnum, rela.get_r_offset(),
+                                  _("__tls_get_addr call lacks marker reloc"));
+         else
+           gold_warning_at_location(relinfo, relnum, rela.get_r_offset(),
+                                    _("__tls_get_addr call lacks marker reloc"));
+       }
       break;
     case Track_tls::EXPECTED:
       // We have already complained.
@@ -9394,6 +10484,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;
@@ -9402,8 +10499,6 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
   // Offset from start of insn to d-field reloc.
   const int d_offset = big_endian ? 2 : 0;
 
-  Powerpc_relobj<size, big_endian>* const object
-    = static_cast<Powerpc_relobj<size, big_endian>*>(relinfo->object);
   Address value = 0;
   bool has_stub_value = false;
   bool localentry0 = false;
@@ -9413,7 +10508,10 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
        ? gsym->use_plt_offset(Scan::get_reference_flags(r_type, target))
        : object->local_has_plt_offset(r_sym));
   if (has_plt_offset
+      && !is_got_reloc(r_type)
       && !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
@@ -9468,19 +10566,29 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
                  const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
                  elfcpp::Shdr<size, big_endian> shdr(relinfo->reloc_shdr);
                  size_t reloc_count = shdr.get_sh_size() / reloc_size;
+                 if (size == 64
+                     && r_type != elfcpp::R_PPC64_REL24_NOTOC)
+                   value += ent->tocoff_;
                  if (size == 64
                      && ent->r2save_
-                     && r_type == elfcpp::R_PPC64_REL24_NOTOC)
-                   value += 4;
-                 else if (size == 64
-                          && ent->r2save_
-                          && relnum < reloc_count - 1)
+                     && !(gsym != NULL
+                          && target->is_tls_get_addr_opt(gsym)))
                    {
-                     Reltype next_rela(preloc + reloc_size);
-                     if (elfcpp::elf_r_type<size>(next_rela.get_r_info())
-                         == elfcpp::R_PPC64_TOCSAVE
-                         && next_rela.get_r_offset() == rela.get_r_offset() + 4)
-                       value += 4;
+                     if (r_type == elfcpp::R_PPC64_REL24_NOTOC)
+                       {
+                         if (!(target->power10_stubs()
+                               && target->power10_stubs_auto()))
+                           value += 4;
+                       }
+                     else if (relnum < reloc_count - 1)
+                       {
+                         Reltype next_rela(preloc + reloc_size);
+                         if (elfcpp::elf_r_type<size>(next_rela.get_r_info())
+                             == elfcpp::R_PPC64_TOCSAVE
+                             && (next_rela.get_r_offset()
+                                 == rela.get_r_offset() + 4))
+                           value += 4;
+                       }
                    }
                  localentry0 = ent->localentry0_;
                  has_stub_value = true;
@@ -9493,7 +10601,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)
@@ -9503,8 +10613,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)
@@ -9528,12 +10642,16 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       elfcpp::Swap<32, big_endian>::writeval(iview, nop);
       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)
+  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 (is_got_reloc(r_type))
     {
       if (gsym != NULL)
        {
@@ -9545,7 +10663,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)
     {
@@ -9624,11 +10745,16 @@ 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_TLSGD_PCREL34)
     {
       // First instruction of a global dynamic sequence, arg setup insn.
-      const bool final = gsym == NULL || gsym->final_value_is_known();
-      const tls::Tls_optimization tls_type = target->optimize_tls_gd(final);
+      tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+      if (!object->no_tls_marker())
+       {
+         bool final = gsym == NULL || gsym->final_value_is_known();
+         tls_type = target->optimize_tls_gd(final);
+       }
       enum Got_type got_type = GOT_TYPE_STANDARD;
       if (tls_type == tls::TLSOPT_NONE)
        got_type = GOT_TYPE_TLSGD;
@@ -9646,67 +10772,123 @@ 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_TLSGD_PCREL34)
+           value += target->got_section()->address();
+         else
+           value -= target->got_section()->got_base_offset(object);
        }
       if (tls_type == tls::TLSOPT_TO_IE)
        {
-         if (r_type == elfcpp::R_POWERPC_GOT_TLSGD16
-             || r_type == elfcpp::R_POWERPC_GOT_TLSGD16_LO)
+         if (r_type == elfcpp::R_PPC64_GOT_TLSGD_PCREL34)
            {
-             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_TPREL_PCREL34;
+           }
+         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_TLSGD_PCREL34)
            {
-             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_TLSLD_PCREL34)
     {
       // First instruction of a local dynamic sequence, arg setup insn.
-      const tls::Tls_optimization tls_type = target->optimize_tls_ld();
+      tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+      if (!object->no_tls_marker())
+       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_TLSLD_PCREL34)
+           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_TLSLD_PCREL34)
+           {
+             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);
@@ -9731,7 +10913,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_DTPREL_PCREL34)
     {
       // Accesses relative to a local dynamic sequence address,
       // no optimisation here.
@@ -9745,16 +10928,20 @@ 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_DTPREL_PCREL34)
+       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_TPREL_PCREL34)
     {
       // First instruction of initial exec sequence.
-      const bool final = gsym == NULL || gsym->final_value_is_known();
-      const tls::Tls_optimization tls_type = target->optimize_tls_ie(final);
+      bool final = gsym == NULL || gsym->final_value_is_known();
+      tls::Tls_optimization tls_type = target->optimize_tls_ie(final);
       if (tls_type == tls::TLSOPT_NONE)
        {
          if (gsym != NULL)
@@ -9767,13 +10954,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_TPREL_PCREL34)
+           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_TPREL_PCREL34)
+           {
+             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);
@@ -9801,8 +11006,12 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       // Second instruction of a global dynamic sequence,
       // the __tls_get_addr call
       this->expect_tls_get_addr_call(relinfo, relnum, rela.get_r_offset());
-      const bool final = gsym == NULL || gsym->final_value_is_known();
-      const tls::Tls_optimization tls_type = target->optimize_tls_gd(final);
+      tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+      if (!object->no_tls_marker())
+       {
+         bool final = gsym == NULL || gsym->final_value_is_known();
+         tls_type = target->optimize_tls_gd(final);
+       }
       if (tls_type != tls::TLSOPT_NONE)
        {
          if (tls_type == tls::TLSOPT_TO_IE)
@@ -9816,12 +11025,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();
        }
@@ -9832,34 +11062,82 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       // Second instruction of a local dynamic sequence,
       // the __tls_get_addr call
       this->expect_tls_get_addr_call(relinfo, relnum, rela.get_r_offset());
-      const tls::Tls_optimization tls_type = target->optimize_tls_ld();
+      tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+      if (!object->no_tls_marker())
+       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)
     {
       // Second instruction of an initial exec sequence
-      const bool final = gsym == NULL || gsym->final_value_is_known();
-      const tls::Tls_optimization tls_type = target->optimize_tls_ie(final);
+      bool final = gsym == NULL || gsym->final_value_is_known();
+      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)
@@ -9887,14 +11165,15 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
                || r_type == elfcpp::R_POWERPC_PLT16_HA)))
        addend = rela.get_r_addend();
       value = psymval->value(object, addend);
+      unsigned int local_ent = 0;
       if (size == 64 && is_branch_reloc<size>(r_type))
        {
          if (target->abiversion() >= 2)
            {
              if (gsym != NULL)
-               value += object->ppc64_local_entry_offset(gsym);
+               local_ent = object->ppc64_local_entry_offset(gsym);
              else
-               value += object->ppc64_local_entry_offset(r_sym);
+               local_ent = object->ppc64_local_entry_offset(r_sym);
            }
          else
            {
@@ -9903,9 +11182,9 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
                                        &value, &dest_shndx);
            }
        }
-      Address max_branch_offset = max_branch_delta<size>(r_type);
-      if (max_branch_offset != 0
-         && (value - address + max_branch_offset >= 2 * max_branch_offset
+      Address max_branch = max_branch_delta<size>(r_type);
+      if (max_branch != 0
+         && (value + local_ent - address + max_branch >= 2 * max_branch
              || (size == 64
                  && r_type == elfcpp::R_PPC64_REL24_NOTOC
                  && (gsym != NULL
@@ -9924,12 +11203,20 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
                    value = (value - target->savres_section()->address()
                             + stub_table->branch_size());
                  else
-                   value = (stub_table->stub_address() + stub_table->plt_size()
-                            + ent->off_);
+                   {
+                     value = (stub_table->stub_address()
+                              + stub_table->plt_size()
+                              + ent->off_);
+                     if (size == 64
+                         && r_type != elfcpp::R_PPC64_REL24_NOTOC)
+                       value += ent->tocoff_;
+                   }
                  has_stub_value = true;
                }
            }
        }
+      if (!has_stub_value)
+       value += local_ent;
     }
 
   switch (r_type)
@@ -9957,6 +11244,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_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_TLSGD_PCREL34:
+    case elfcpp::R_PPC64_GOT_TLSLD_PCREL34:
+    case elfcpp::R_PPC64_GOT_TPREL_PCREL34:
+    case elfcpp::R_PPC64_GOT_DTPREL_PCREL34:
+    case elfcpp::R_PPC64_REL16_HIGHER34:
+    case elfcpp::R_PPC64_REL16_HIGHERA34:
+    case elfcpp::R_PPC64_REL16_HIGHEST34:
+    case elfcpp::R_PPC64_REL16_HIGHESTA34:
       value -= address;
       break;
 
@@ -9998,6 +11298,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;
@@ -10020,6 +11321,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;
@@ -10092,8 +11394,15 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
       break;
     }
 
-  if (size == 64)
+  if (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:
@@ -10106,24 +11415,22 @@ 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:
        case elfcpp::R_POWERPC_GOT_DTPREL16_HA:
        case elfcpp::R_POWERPC_GOT16_HA:
        case elfcpp::R_PPC64_TOC16_HA:
-         if (parameters->options().toc_optimize())
+         if (size == 64 && parameters->options().toc_optimize())
            {
-             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)));
@@ -10146,13 +11453,17 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
        case elfcpp::R_PPC64_GOT16_LO_DS:
        case elfcpp::R_PPC64_TOC16_LO:
        case elfcpp::R_PPC64_TOC16_LO_DS:
-         if (parameters->options().toc_optimize())
+         if (size == 64 && parameters->options().toc_optimize())
            {
-             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);
@@ -10180,29 +11491,94 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
            }
          break;
 
-       case elfcpp::R_POWERPC_TPREL16_HA:
-         if (parameters->options().tls_optimize() && value + 0x8000 < 0x10000)
+       case elfcpp::R_PPC64_GOT_PCREL34:
+         if (size == 64 && parameters->options().toc_optimize())
            {
-             Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
-             Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
-             if ((insn & ((0x3f << 26) | 0x1f << 16))
-                 != ((15u << 26) | ((size == 32 ? 2 : 13) << 16)))
-               ;
-             else
+             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)
                {
-                 elfcpp::Swap<32, big_endian>::writeval(iview, nop);
-                 return true;
+                 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:
+         if (size == 64)
+           {
+             iview = reinterpret_cast<Insn*>(view);
+             pinsn = elfcpp::Swap<32, big_endian>::readval(iview);
+             pinsn <<= 32;
+             pinsn |= elfcpp::Swap<32, big_endian>::readval(iview + 1);
+             if ((pinsn & ((-1ULL << 50) | (63ULL << 26)))
+                 != ((1ULL << 58) | (2ULL << 56) | (1ULL << 52)
+                     | (14ULL << 26) /* paddi */))
+               break;
+
+           pcrelopt:
+             const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
+             elfcpp::Shdr<size, big_endian> shdr(relinfo->reloc_shdr);
+             size_t reloc_count = shdr.get_sh_size() / reloc_size;
+             if (relnum >= reloc_count - 1)
+               break;
+
+             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 (target->tprel_opt() && value + 0x8000 < 0x10000)
+           {
+             Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
+             elfcpp::Swap<32, big_endian>::writeval(iview, nop);
+             return true;
+           }
+         break;
+
        case elfcpp::R_PPC64_TPREL16_LO_DS:
          if (size == 32)
            // R_PPC_TLSGD, R_PPC_TLSLD
            break;
          // Fall through.
        case elfcpp::R_POWERPC_TPREL16_LO:
-         if (parameters->options().tls_optimize() && value + 0x8000 < 0x10000)
+         if (target->tprel_opt() && value + 0x8000 < 0x10000)
            {
              Insn* iview = reinterpret_cast<Insn*>(view - d_offset);
              Insn insn = elfcpp::Swap<32, big_endian>::readval(iview);
@@ -10213,29 +11589,12 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
          break;
 
        case elfcpp::R_PPC64_ENTRY:
-         value = (target->got_section()->output_section()->address()
-                  + object->toc_base_offset());
-         if (value + 0x80008000 <= 0xffffffff
-             && !parameters->options().output_is_position_independent())
-           {
-             Insn* iview = reinterpret_cast<Insn*>(view);
-             Insn insn1 = elfcpp::Swap<32, big_endian>::readval(iview);
-             Insn insn2 = elfcpp::Swap<32, big_endian>::readval(iview + 1);
-
-             if ((insn1 & ~0xfffc) == ld_2_12
-                 && insn2 == add_2_2_12)
-               {
-                 insn1 = lis_2 + ha(value);
-                 elfcpp::Swap<32, big_endian>::writeval(iview, insn1);
-                 insn2 = addi_2_2 + l(value);
-                 elfcpp::Swap<32, big_endian>::writeval(iview + 1, insn2);
-                 return true;
-               }
-           }
-         else
+         if (size == 64)
            {
-             value -= address;
-             if (value + 0x80008000 <= 0xffffffff)
+             value = (target->got_section()->output_section()->address()
+                      + object->toc_base_offset());
+             if (value + 0x80008000 <= 0xffffffff
+                 && !parameters->options().output_is_position_independent())
                {
                  Insn* iview = reinterpret_cast<Insn*>(view);
                  Insn insn1 = elfcpp::Swap<32, big_endian>::readval(iview);
@@ -10244,13 +11603,33 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
                  if ((insn1 & ~0xfffc) == ld_2_12
                      && insn2 == add_2_2_12)
                    {
-                     insn1 = addis_2_12 + ha(value);
+                     insn1 = lis_2 + ha(value);
                      elfcpp::Swap<32, big_endian>::writeval(iview, insn1);
                      insn2 = addi_2_2 + l(value);
                      elfcpp::Swap<32, big_endian>::writeval(iview + 1, insn2);
                      return true;
                    }
                }
+             else
+               {
+                 value -= address;
+                 if (value + 0x80008000 <= 0xffffffff)
+                   {
+                     Insn* iview = reinterpret_cast<Insn*>(view);
+                     Insn insn1 = elfcpp::Swap<32, big_endian>::readval(iview);
+                     Insn insn2 = elfcpp::Swap<32, big_endian>::readval(iview + 1);
+
+                     if ((insn1 & ~0xfffc) == ld_2_12
+                         && insn2 == add_2_2_12)
+                       {
+                         insn1 = addis_2_12 + ha(value);
+                         elfcpp::Swap<32, big_endian>::writeval(iview, insn1);
+                         insn2 = addi_2_2 + l(value);
+                         elfcpp::Swap<32, big_endian>::writeval(iview + 1, insn2);
+                         return true;
+                       }
+                   }
+               }
            }
          break;
 
@@ -10262,7 +11641,8 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
          //            lis 2,.TOC.@ha
          //            addi 2,2,.TOC.@l
          // if .TOC. is in range.  */
-         if (value + address - 4 + 0x80008000 <= 0xffffffff
+         if (size == 64
+             && value + address - 4 + 0x80008000 <= 0xffffffff
              && relnum + 1 > 1
              && preloc != NULL
              && target->abiversion() >= 2
@@ -10397,6 +11777,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_TLSGD_PCREL34:
+    case elfcpp::R_PPC64_GOT_TLSLD_PCREL34:
+    case elfcpp::R_PPC64_GOT_TPREL_PCREL34:
+    case elfcpp::R_PPC64_GOT_DTPREL_PCREL34:
       overflow = Reloc::CHECK_SIGNED;
       break;
     }
@@ -10436,6 +11829,7 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
     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:
@@ -10666,8 +12060,8 @@ Target_powerpc<size, big_endian>::Relocate::relocate(
          loc.object = relinfo->object;
          loc.shndx = relinfo->data_shndx;
          loc.offset = rela.get_r_offset();
-         Tocsave_loc::const_iterator p = target->tocsave_loc().find(loc);
-         if (p != target->tocsave_loc().end())
+         const Tocsave_loc *tocsave = target->tocsave_loc();
+         if (tocsave->find(loc) != tocsave->end())
            {
              // If we've generated plt calls using this tocsave, then
              // the nop needs to be changed to save r2.
@@ -10686,6 +12080,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_TLSGD_PCREL34:
+    case elfcpp::R_PPC64_GOT_TLSLD_PCREL34:
+    case elfcpp::R_PPC64_GOT_TPREL_PCREL34:
+    case elfcpp::R_PPC64_GOT_DTPREL_PCREL34:
+      if (size == 32)
+       goto unsupp;
+      status = Reloc::addr34(view, value, overflow);
+      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:
@@ -11096,8 +12554,13 @@ Target_powerpc<size, big_endian>::relocate_relocs(
            {
              // First instruction of a global dynamic sequence,
              // arg setup insn.
-             const bool final = gsym == NULL || gsym->final_value_is_known();
-             switch (this->optimize_tls_gd(final))
+             tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+             if (!object->no_tls_marker())
+               {
+                 bool final = gsym == NULL || gsym->final_value_is_known();
+                 tls_type = this->optimize_tls_gd(final);
+               }
+             switch (tls_type)
                {
                case tls::TLSOPT_TO_IE:
                  r_type += (elfcpp::R_POWERPC_GOT_TPREL16
@@ -11124,7 +12587,10 @@ Target_powerpc<size, big_endian>::relocate_relocs(
            {
              // First instruction of a local dynamic sequence,
              // arg setup insn.
-             if (this->optimize_tls_ld() == tls::TLSOPT_TO_LE)
+             tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+             if (!object->no_tls_marker())
+               tls_type = this->optimize_tls_ld();
+             if (tls_type == tls::TLSOPT_TO_LE)
                {
                  if (r_type == elfcpp::R_POWERPC_GOT_TLSLD16
                      || r_type == elfcpp::R_POWERPC_GOT_TLSLD16_LO)
@@ -11150,7 +12616,7 @@ Target_powerpc<size, big_endian>::relocate_relocs(
                   || r_type == elfcpp::R_POWERPC_GOT_TPREL16_HA)
            {
              // First instruction of initial exec sequence.
-             const bool final = gsym == NULL || gsym->final_value_is_known();
+             bool final = gsym == NULL || gsym->final_value_is_known();
              if (this->optimize_tls_ie(final) == tls::TLSOPT_TO_LE)
                {
                  if (r_type == elfcpp::R_POWERPC_GOT_TPREL16
@@ -11168,8 +12634,13 @@ Target_powerpc<size, big_endian>::relocate_relocs(
            {
              // Second instruction of a global dynamic sequence,
              // the __tls_get_addr call
-             const bool final = gsym == NULL || gsym->final_value_is_known();
-             switch (this->optimize_tls_gd(final))
+             tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+             if (!object->no_tls_marker())
+               {
+                 bool final = gsym == NULL || gsym->final_value_is_known();
+                 tls_type = this->optimize_tls_gd(final);
+               }
+             switch (tls_type)
                {
                case tls::TLSOPT_TO_IE:
                  r_type = elfcpp::R_POWERPC_NONE;
@@ -11189,7 +12660,10 @@ Target_powerpc<size, big_endian>::relocate_relocs(
            {
              // Second instruction of a local dynamic sequence,
              // the __tls_get_addr call
-             if (this->optimize_tls_ld() == tls::TLSOPT_TO_LE)
+             tls::Tls_optimization tls_type = tls::TLSOPT_NONE;
+             if (!object->no_tls_marker())
+               tls_type = this->optimize_tls_ld();
+             if (tls_type == tls::TLSOPT_TO_LE)
                {
                  const Output_section* os = relinfo->layout->tls_segment()
                    ->first_section();
@@ -11205,7 +12679,7 @@ Target_powerpc<size, big_endian>::relocate_relocs(
          else if (r_type == elfcpp::R_POWERPC_TLS)
            {
              // Second instruction of an initial exec sequence
-             const bool final = gsym == NULL || gsym->final_value_is_known();
+             bool final = gsym == NULL || gsym->final_value_is_known();
              if (this->optimize_tls_ie(final) == tls::TLSOPT_TO_LE)
                {
                  r_type = elfcpp::R_POWERPC_TPREL16_LO;
This page took 0.06278 seconds and 4 git commands to generate.