bool is_tls_call
        = ((r_type == elfcpp::R_POWERPC_REL24
            || (size == 64 && r_type == elfcpp::R_PPC64_REL24_NOTOC)
+           || r_type == elfcpp::R_PPC64_REL24_P9NOTOC
            || r_type == elfcpp::R_PPC_PLTREL24
            || is_plt16_reloc<size>(r_type)
            || r_type == elfcpp::R_PPC64_PLT_PCREL34
 {
   return (r_type == elfcpp::R_POWERPC_REL24
          || (size == 64 && r_type == elfcpp::R_PPC64_REL24_NOTOC)
+         || r_type == elfcpp::R_PPC64_REL24_P9NOTOC
          || r_type == elfcpp::R_PPC_PLTREL24
          || r_type == elfcpp::R_PPC_LOCAL24PC
          || r_type == elfcpp::R_POWERPC_REL14
     return 1L << 15;
   if (r_type == elfcpp::R_POWERPC_REL24
       || (size == 64 && r_type == elfcpp::R_PPC64_REL24_NOTOC)
+      || r_type == elfcpp::R_PPC64_REL24_P9NOTOC
       || r_type == elfcpp::R_PPC_PLTREL24
       || r_type == elfcpp::R_PPC_LOCAL24PC)
     return 1L << 25;
        }
       unsigned int local_ent = 0;
       if (size == 64
-         && this->r_type_ != elfcpp::R_PPC64_REL24_NOTOC)
+         && this->r_type_ != elfcpp::R_PPC64_REL24_NOTOC
+         && this->r_type_ != elfcpp::R_PPC64_REL24_P9NOTOC)
        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
+             && (this->r_type_ == elfcpp::R_PPC64_REL24_NOTOC
+                 || this->r_type_ == elfcpp::R_PPC64_REL24_P9NOTOC)
              && (gsym != NULL
                  ? this->object_->ppc64_needs_toc(gsym)
                  : this->object_->ppc64_needs_toc(this->r_sym_))))
   struct Plt_stub_ent
   {
     Plt_stub_ent(unsigned int off, unsigned int indx)
-      : off_(off), indx_(indx), iter_(0), notoc_(0), toc_(0),
-       r2save_(0), localentry0_(0), tocoff_(0)
+      : off_(off), indx_(indx), tocoff_(0), p9off_(0), tsize_ (0), iter_(0),
+       toc_(0), notoc_(0), p9notoc_(0), r2save_(0), localentry0_(0)
     { }
 
     unsigned int off_;
     unsigned int indx_;
+    // off_ points at p10 notoc stub, tocoff_ is offset from there to
+    // toc stub, p9off_ is offset to p9notoc stub
+    unsigned int tocoff_ : 8;
+    unsigned int p9off_ : 8;
+    // The size of the toc stub, used to locate blr on tls_get_addr stub.
+    unsigned int tsize_ : 8;
+    // Stub revision management
     unsigned int iter_ : 1;
-    unsigned int notoc_ : 1;
+    // The three types of stubs.
     unsigned int toc_ : 1;
+    unsigned int notoc_ : 1;
+    unsigned int p9notoc_ : 1;
+    // Each with a possible variant saving r2 first
     unsigned int r2save_ : 1;
+    // Handy cached info from symbol
     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_(0), notoc_(notoc), toc_(0), save_res_(save_res),
-       other_(0), tocoff_(0)
+    Branch_stub_ent(unsigned int off)
+      : off_(off), tocoff_(0), p9off_(0), iter_(0), toc_(0), notoc_(0),
+       p9notoc_(0), save_res_(0), other_(0)
     { }
 
     unsigned int off_;
+    // off_ points at p10 notoc stub, tocoff_ is offset from there to
+    // toc stub, p9off_ is offset to p9notoc stub
+    unsigned int tocoff_ : 8;
+    unsigned int p9off_ : 8;
+    // Stub revision management
     unsigned int iter_ : 1;
-    unsigned int notoc_ : 1;
+    // Four types of stubs.
     unsigned int toc_ : 1;
+    unsigned int notoc_ : 1;
+    unsigned int p9notoc_ : 1;
     unsigned int save_res_ : 1;
+    // Handy cached info from symbol
     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;
          p.first->second.localentry0_ = 1;
          this->targ_->set_has_localentry0();
        }
-      if (r_type == elfcpp::R_PPC64_REL24_NOTOC)
+      if (r_type == elfcpp::R_PPC64_REL24_NOTOC
+         || r_type == elfcpp::R_PPC64_REL24_P9NOTOC)
        {
-         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;
+         if (this->targ_->power10_stubs()
+             && (!this->targ_->power10_stubs_auto()
+                 || r_type == elfcpp::R_PPC64_REL24_NOTOC))
+           {
+             if (!p.second && !p.first->second.notoc_)
+               this->need_resize_ = true;
+             p.first->second.notoc_ = 1;
+           }
+         else
+           {
+             if (!p.second && !p.first->second.p9notoc_)
+               this->need_resize_ = true;
+             p.first->second.p9notoc_ = 1;
+           }
        }
       else
        {
       this->plt_size_ += this->plt_call_size(p.first);
       if (this->targ_->is_tls_get_addr_opt(gsym))
        this->targ_->set_has_tls_get_addr_opt();
-      this->plt_size_ = this->plt_call_align(this->plt_size_);
     }
   return this->can_reach_stub(from, p.first->second.off_, r_type);
 }
          p.first->second.localentry0_ = 1;
          this->targ_->set_has_localentry0();
        }
-      if (r_type == elfcpp::R_PPC64_REL24_NOTOC)
+      if (r_type == elfcpp::R_PPC64_REL24_NOTOC
+         || r_type == elfcpp::R_PPC64_REL24_P9NOTOC)
        {
-         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;
+         if (this->targ_->power10_stubs()
+             && (!this->targ_->power10_stubs_auto()
+                 || r_type == elfcpp::R_PPC64_REL24_NOTOC))
+           {
+             if (!p.second && !p.first->second.notoc_)
+               this->need_resize_ = true;
+             p.first->second.notoc_ = 1;
+           }
+         else
+           {
+             if (!p.second && !p.first->second.p9notoc_)
+               this->need_resize_ = true;
+             p.first->second.p9notoc_ = 1;
+           }
        }
       else
        {
          p.first->second.off_ = this->plt_size_;
        }
       this->plt_size_ += this->plt_call_size(p.first);
-      this->plt_size_ = this->plt_call_align(this->plt_size_);
     }
   return this->can_reach_stub(from, p.first->second.off_, r_type);
 }
     bool save_res)
 {
   Branch_stub_key key(to);
-  bool notoc = (size == 64 && r_type == elfcpp::R_PPC64_REL24_NOTOC);
-  Branch_stub_ent ent(this->branch_size_, notoc, save_res);
+  Branch_stub_ent ent(this->branch_size_);
   std::pair<typename Branch_stub_entries::iterator, bool> p
     = this->long_branch_stubs_.insert(std::make_pair(key, ent));
-  if (notoc)
+  if (save_res)
     {
-      if (!p.second && !p.first->second.notoc_)
+      if (!p.second && !p.first->second.save_res_)
        this->need_resize_ = true;
-      p.first->second.notoc_ = true;
+      p.first->second.save_res_ = true;
+    }
+  else if (size == 64
+          && (r_type == elfcpp::R_PPC64_REL24_NOTOC
+              || r_type == elfcpp::R_PPC64_REL24_P9NOTOC))
+    {
+      if (this->targ_->power10_stubs()
+         && (!this->targ_->power10_stubs_auto()
+             || r_type == elfcpp::R_PPC64_REL24_NOTOC))
+       {
+         if (!p.second && !p.first->second.notoc_)
+           this->need_resize_ = true;
+         p.first->second.notoc_ = true;
+       }
+      else
+       {
+         if (!p.second && !p.first->second.p9notoc_)
+           this->need_resize_ = true;
+         p.first->second.p9notoc_ = true;
+       }
     }
   else
     {
     }
   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_))
     {
       if (this->resizing_)
     for (plt_iter cs = this->plt_call_stubs_.begin();
         cs != this->plt_call_stubs_.end();
         ++cs)
-      if ((this->targ_->is_tls_get_addr_opt(cs->first.sym_)
-          && cs->second.r2save_
-          && !cs->second.localentry0_)
-         || (cs->second.notoc_
-             && !this->targ_->power10_stubs()))
+      if (cs->second.p9notoc_
+         || (cs->second.toc_
+             && cs->second.r2save_
+             && !cs->second.localentry0_
+             && this->targ_->is_tls_get_addr_opt(cs->first.sym_)))
        calls.push_back(cs);
   if (calls.size() > 1)
     std::stable_sort(calls.begin(), calls.end(),
       if (this->targ_->is_tls_get_addr_opt(cs->first.sym_))
        {
          off += 7 * 4;
-         if (cs->second.r2save_
+         if (cs->second.toc_
+             && cs->second.r2save_
              && !cs->second.localentry0_)
            {
-             off += 2 * 4;
+             off += cs->second.tocoff_ + 2 * 4;
              eh_advance<big_endian>(fde, off - last_eh_loc);
              fde.resize(fde.size() + 6);
              unsigned char* p = &*fde.end() - 6;
              *p++ = elfcpp::DW_CFA_offset_extended_sf;
              *p++ = 65;
              *p++ = -(this->targ_->stk_linker() / 8) & 0x7f;
-             unsigned int delta = this->plt_call_size(cs) - 4 - 9 * 4;
+             unsigned int delta = cs->second.tsize_ - 9 * 4 - 4;
              *p++ = elfcpp::DW_CFA_advance_loc + delta / 4;
              *p++ = elfcpp::DW_CFA_restore_extended;
              *p++ = 65;
              last_eh_loc = off + delta;
-             continue;
+             off = cs->second.off_ + 7 * 4;
            }
        }
       // notoc stubs also should describe LR changes, to support
       // asynchronous exceptions.
-      off += (cs->second.r2save_ ? 4 : 0) + 8;
-      eh_advance<big_endian>(fde, off - last_eh_loc);
-      fde.resize(fde.size() + 6);
-      unsigned char* p = &*fde.end() - 6;
-      *p++ = elfcpp::DW_CFA_register;
-      *p++ = 65;
-      *p++ = 12;
-      *p++ = elfcpp::DW_CFA_advance_loc + 8 / 4;
-      *p++ = elfcpp::DW_CFA_restore_extended;
-      *p++ = 65;
-      last_eh_loc = off + 8;
+      if (cs->second.p9notoc_)
+       {
+         off += cs->second.p9off_;
+         off += (cs->second.r2save_ ? 4 : 0) + 8;
+         eh_advance<big_endian>(fde, off - last_eh_loc);
+         fde.resize(fde.size() + 6);
+         unsigned char* p = &*fde.end() - 6;
+         *p++ = elfcpp::DW_CFA_register;
+         *p++ = 65;
+         *p++ = 12;
+         *p++ = elfcpp::DW_CFA_advance_loc + 8 / 4;
+         *p++ = elfcpp::DW_CFA_restore_extended;
+         *p++ = 65;
+         last_eh_loc = off + 8;
+       }
     }
 
   for (unsigned int i = 0; i < branches.size(); i++)
          sprintf(name, "%08x.plt_call.%s%s%s", this->uniq_, obj, symname, add);
          Address value
            = this->stub_address() - this->address() + cs->second.off_;
-         unsigned int stub_size = this->plt_call_align(this->plt_call_size(cs));
+         unsigned int stub_size = this->plt_call_size(cs);
          this->targ_->define_local(symtab, name, this, value, stub_size);
        }
     }
 {
   if (size == 32)
     {
-      const Symbol* gsym = p->first.sym_;
-      return (4 * 4
-             + (this->targ_->is_tls_get_addr_opt(gsym) ? 8 * 4 : 0));
+      unsigned int bytes = 4 * 4;
+      if (this->targ_->is_tls_get_addr_opt(p->first.sym_))
+       bytes = 12 * 4;
+      return this->plt_call_align(bytes);
     }
 
   const Output_data_plt_powerpc<size, big_endian>* plt;
            bytes += 8 * 4;
          bytes = this->plt_call_align(bytes);
        }
-      unsigned int tail = 0;
       if (p->second.toc_)
        {
          p->second.tocoff_ = bytes;
            {
              bytes += 7 * 4;
              if (p->second.r2save_ && !p->second.localentry0_)
-               {
-                 bytes += 2 * 4;
-                 tail = 4 * 4;
-               }
+               bytes += 2 * 4 + 4 * 4;
            }
          if (p->second.r2save_)
            bytes += 4;
          uint64_t got_addr = this->targ_->toc_pointer();
          uint64_t off = plt_addr - got_addr;
          bytes += 3 * 4 + 4 * (ha(off) != 0);
+         p->second.tsize_ = bytes - p->second.tocoff_;
+         bytes = this->plt_call_align(bytes);
        }
-      return bytes + tail;
+      if (p->second.p9notoc_)
+       {
+         p->second.p9off_ = bytes;
+         if (this->targ_->is_tls_get_addr_opt(p->first.sym_))
+           bytes += 7 * 4;
+         uint64_t from = this->stub_address() + p->second.off_ + bytes + 2 * 4;
+         uint64_t off = plt_addr - from;
+         if (off + 0x8000 < 0x10000)
+           bytes += 7 * 4;
+         else if (off + 0x80008000ULL < 0x100000000ULL)
+           bytes += 8 * 4;
+         else
+           {
+             bytes += 8 * 4;
+             if (off + 0x800000000000ULL >= 0x1000000000000ULL
+                 && ((off >> 32) & 0xffff) != 0)
+               bytes += 4;
+             if (((off >> 32) & 0xffffffffULL) != 0)
+               bytes += 4;
+             if (hi(off) != 0)
+               bytes += 4;
+             if (l(off) != 0)
+               bytes += 4;
+           }
+         bytes = this->plt_call_align(bytes);
+       }
+      return bytes;
     }
   else
     {
            bytes += 7 * 4;
          else
            bytes += 8 * 4;
-         return bytes + tail;
+         return this->plt_call_align(bytes + tail);
        }
 
-      if (p->second.notoc_)
+      if (p->second.p9notoc_)
        {
          uint64_t from = this->stub_address() + p->second.off_ + bytes + 2 * 4;
          uint64_t off = plt_addr - from;
              if (l(off) != 0)
                bytes += 4;
            }
-         return bytes + tail;
+         return this->plt_call_align(bytes + tail);
        }
 
       uint64_t got_addr = this->targ_->toc_pointer();
                    + 8 * thread_safe
                    + 4 * (ha(off + 8 + 8 * static_chain) != ha(off)));
        }
-      return bytes + tail;
+      return this->plt_call_align(bytes + tail);
     }
 }
 
                      else
                        write_insn<big_endian>(p, bctr);
                    }
+                 if (cs->second.p9notoc_)
+                   {
+                     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_notoc_offset<big_endian>(p, delta, 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);
+                   }
                }
              else
                {
                      write_insn<big_endian>(p, bctr);
                    }
                }
+             if (bs->second.p9notoc_)
+               {
+                 unsigned char* startp = p;
+                 p = build_notoc_offset<big_endian>(p, delta, 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;
+               }
            }
          else
            {
                  write_insn<big_endian>(p, std_2_1 + this->targ_->stk_toc());
                  p += 4;
                }
-             if (cs->second.notoc_)
+             if (cs->second.p9notoc_)
                {
                  Address from = this->stub_address() + (p - oview) + 8;
                  Address off = plt_addr - from;
          p = oview + off;
          Address loc = this->stub_address() + off;
          Address delta = bs->first.dest_ - loc;
-         if (!bs->second.notoc_)
+         if (!bs->second.p9notoc_)
            delta += elfcpp::ppc64_decode_local_entry(bs->second.other_);
-         if (bs->second.notoc_)
+         if (bs->second.p9notoc_)
            {
              unsigned char* startp = p;
              p = build_notoc_offset<big_endian>(p, off, false);
       if (size == 32)
        break;
       // Fall through.
+    case elfcpp::R_PPC64_REL24_P9NOTOC:
     case elfcpp::R_POWERPC_REL24:
     case elfcpp::R_PPC_PLTREL24:
     case elfcpp::R_POWERPC_REL14:
       if (size == 32)
        break;
       // Fall through.
+    case elfcpp::R_PPC64_REL24_P9NOTOC:
     case elfcpp::R_POWERPC_ADDR24:
     case elfcpp::R_POWERPC_ADDR14:
     case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
       if (size == 32)
        break;
       // Fall through.
+    case elfcpp::R_PPC64_REL24_P9NOTOC:
     case elfcpp::R_POWERPC_REL24:
     case elfcpp::R_PPC_PLTREL24:
     case elfcpp::R_PPC_LOCAL24PC:
       if (size == 32)
        break;
       // Fall through.
+    case elfcpp::R_PPC64_REL24_P9NOTOC:
     case elfcpp::R_PPC_PLTREL24:
     case elfcpp::R_POWERPC_REL24:
       if (!is_ifunc)
                  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)
+                   {
+                     if (r_type == elfcpp::R_PPC64_REL24_NOTOC)
+                       {
+                         if (!ent->notoc_)
+                           value += ent->p9off_;
+                       }
+                     else if (r_type == elfcpp::R_PPC64_REL24_P9NOTOC)
+                       value += ent->p9off_;
+                     else
+                       value += ent->tocoff_;
+                   }
                  if (size == 64
                      && ent->r2save_
                      && !(gsym != NULL
                           && target->is_tls_get_addr_opt(gsym)))
                    {
-                     if (r_type == elfcpp::R_PPC64_REL24_NOTOC)
+                     if (r_type == elfcpp::R_PPC64_REL24_NOTOC
+                         || r_type == elfcpp::R_PPC64_REL24_P9NOTOC)
                        {
                          if (!(target->power10_stubs()
                                && target->power10_stubs_auto()))
                  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_REL24_P9NOTOC
                       || r_type2 == elfcpp::R_PPC64_PLTCALL_NOTOC)
                      && next_rela.get_r_offset() == rela.get_r_offset())
                    is_pcrel = true;
              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_REL24_P9NOTOC
                   || r_type2 == elfcpp::R_PPC64_PLTCALL_NOTOC)
                  && next_rela.get_r_offset() == rela.get_r_offset())
                is_pcrel = true;
       if (max_branch != 0
          && (value + local_ent - address + max_branch >= 2 * max_branch
              || (size == 64
-                 && r_type == elfcpp::R_PPC64_REL24_NOTOC
+                 && (r_type == elfcpp::R_PPC64_REL24_NOTOC
+                     || r_type == elfcpp::R_PPC64_REL24_NOTOC)
                  && (gsym != NULL
                      ? object->ppc64_needs_toc(gsym)
                      : object->ppc64_needs_toc(r_sym)))))
                      value = (stub_table->stub_address()
                               + stub_table->plt_size()
                               + ent->off_);
-                     if (size == 64
-                         && r_type != elfcpp::R_PPC64_REL24_NOTOC)
-                       value += ent->tocoff_;
+                     if (size == 64)
+                       {
+                         if (r_type == elfcpp::R_PPC64_REL24_NOTOC)
+                           {
+                             if (!ent->notoc_)
+                               value += ent->p9off_;
+                           }
+                         else if (r_type == elfcpp::R_PPC64_REL24_P9NOTOC)
+                           value += ent->p9off_;
+                         else
+                           value += ent->tocoff_;
+                       }
                    }
                  has_stub_value = true;
                }
       if (size == 32)
        break;
       // Fall through.
+    case elfcpp::R_PPC64_REL24_P9NOTOC:
     case elfcpp::R_PPC64_REL64:
     case elfcpp::R_POWERPC_REL32:
     case elfcpp::R_POWERPC_REL24:
       if (size == 32)
        break;
       // Fall through.
+    case elfcpp::R_PPC64_REL24_P9NOTOC:
     case elfcpp::R_POWERPC_ADDR24:
     case elfcpp::R_POWERPC_ADDR14:
     case elfcpp::R_POWERPC_ADDR14_BRTAKEN:
       if (size == 32)
        goto unsupp; // R_PPC_EMB_RELSDA
       // Fall through.
+    case elfcpp::R_PPC64_REL24_P9NOTOC:
     case elfcpp::R_POWERPC_ADDR24:
     case elfcpp::R_POWERPC_REL24:
     case elfcpp::R_PPC_PLTREL24: