PR26656, power10 libstdc++.so segfault in __cxxabiv1::__cxa_throw
authorAlan Modra <amodra@gmail.com>
Tue, 22 Sep 2020 13:21:42 +0000 (22:51 +0930)
committerAlan Modra <amodra@gmail.com>
Wed, 23 Sep 2020 22:24:05 +0000 (07:54 +0930)
This adds missing support for a power10 version of the __tls_get_addr
call stub implementing DT_PPC64_OPT PPC64_OPT_TLS.  Without this,
power10 code using __tls_get_addr fails miserably at runtime unless
the --no-tls-get-addr-optimize option is given.

PR 26656
* elf64-ppc.c (plt_stub_size): Add "odd" param.  Use it with
size_power10_offset rather than calculating from start of stub.
Add size for notoc tls_get_addr_opt stub.
(plt_stub_pad): Add "odd" param, pass to plt_stub_size.
(build_tls_get_addr_head, build_tls_get_addr_tail): New functions.
(build_tls_get_addr_stub): Delete.
(ppc_build_one_stub): Use a temp for htab->params->stub_bfd.
Emit notoc tls_get_addr_opt stub.  Move eh_frame code to
suit.  Adjust code to use bfd_tls_get_addr_head/tail in place
of build_tls_get_addr_stub.
(ppc_size_one_stub): Size notoc tls_get_addr_opt stub.
Adjust plt_stub_size and plt_stub_pad calls.  Correct "odd"
when padding stub.  Size eh_frame for notoc stub too.
Correct lr_restore value.
(ppc64_elf_relocate_section): Don't skip over first insn of
notoc tls_get_addr_opt stub.

bfd/ChangeLog
bfd/elf64-ppc.c

index 39e5ead6d73be75e85f6cc14e60aa1d549880f48..f642403309bf1f93905cf674dfa823db574bc7c6 100644 (file)
@@ -1,3 +1,23 @@
+2020-09-24  Alan Modra  <amodra@gmail.com>
+
+       PR 26656
+       * elf64-ppc.c (plt_stub_size): Add "odd" param.  Use it with
+       size_power10_offset rather than calculating from start of stub.
+       Add size for notoc tls_get_addr_opt stub.
+       (plt_stub_pad): Add "odd" param, pass to plt_stub_size.
+       (build_tls_get_addr_head, build_tls_get_addr_tail): New functions.
+       (build_tls_get_addr_stub): Delete.
+       (ppc_build_one_stub): Use a temp for htab->params->stub_bfd.
+       Emit notoc tls_get_addr_opt stub.  Move eh_frame code to
+       suit.  Adjust code to use bfd_tls_get_addr_head/tail in place
+       of build_tls_get_addr_stub.
+       (ppc_size_one_stub): Size notoc tls_get_addr_opt stub.
+       Adjust plt_stub_size and plt_stub_pad calls.  Correct "odd"
+       when padding stub.  Size eh_frame for notoc stub too.
+       Correct lr_restore value.
+       (ppc64_elf_relocate_section): Don't skip over first insn of
+       notoc tls_get_addr_opt stub.
+
 2020-09-24  Alan Modra  <amodra@gmail.com>
 
        PR 26655
index 4d16dc3cd46b17c45d1725523add0bf4c0d92c1f..320717cb4539a28ce807c161b98d1f92f071a7c1 100644 (file)
@@ -10842,62 +10842,60 @@ eh_advance_size (unsigned int delta)
 static inline unsigned int
 plt_stub_size (struct ppc_link_hash_table *htab,
               struct ppc_stub_hash_entry *stub_entry,
-              bfd_vma off)
+              bfd_vma off,
+              unsigned int odd)
 {
   unsigned size;
 
   if (stub_entry->stub_type >= ppc_stub_plt_call_notoc)
     {
       if (htab->params->power10_stubs != 0)
-       {
-         bfd_vma start = (stub_entry->stub_offset
-                          + stub_entry->group->stub_sec->output_offset
-                          + stub_entry->group->stub_sec->output_section->vma);
-         if (stub_entry->stub_type > ppc_stub_plt_call_notoc)
-           start += 4;
-         size = 8 + size_power10_offset (off, start & 4);
-       }
+       size = 8 + size_power10_offset (off, odd);
       else
        size = 8 + size_offset (off - 8);
       if (stub_entry->stub_type > ppc_stub_plt_call_notoc)
        size += 4;
-      return size;
     }
-
-  size = 12;
-  if (ALWAYS_EMIT_R2SAVE
-      || stub_entry->stub_type == ppc_stub_plt_call_r2save)
-    size += 4;
-  if (PPC_HA (off) != 0)
-    size += 4;
-  if (htab->opd_abi)
+  else
     {
-      size += 4;
-      if (htab->params->plt_static_chain)
+      size = 12;
+      if (ALWAYS_EMIT_R2SAVE
+         || stub_entry->stub_type == ppc_stub_plt_call_r2save)
        size += 4;
-      if (htab->params->plt_thread_safe
-         && htab->elf.dynamic_sections_created
-         && stub_entry->h != NULL
-         && stub_entry->h->elf.dynindx != -1)
-       size += 8;
-      if (PPC_HA (off + 8 + 8 * htab->params->plt_static_chain) != PPC_HA (off))
+      if (PPC_HA (off) != 0)
        size += 4;
+      if (htab->opd_abi)
+       {
+         size += 4;
+         if (htab->params->plt_static_chain)
+           size += 4;
+         if (htab->params->plt_thread_safe
+             && htab->elf.dynamic_sections_created
+             && stub_entry->h != NULL
+             && stub_entry->h->elf.dynindx != -1)
+           size += 8;
+         if (PPC_HA (off + 8 + 8 * htab->params->plt_static_chain)
+             != PPC_HA (off))
+           size += 4;
+       }
     }
   if (stub_entry->h != NULL
       && is_tls_get_addr (&stub_entry->h->elf, htab)
       && htab->params->tls_get_addr_opt)
     {
-      if (htab->params->no_tls_get_addr_regsave)
+      if (!htab->params->no_tls_get_addr_regsave)
        {
-         size += 7 * 4;
-         if (stub_entry->stub_type == ppc_stub_plt_call_r2save)
-           size += 6 * 4;
+         size += 30 * 4;
+         if (stub_entry->stub_type == ppc_stub_plt_call_r2save
+             || stub_entry->stub_type == ppc_stub_plt_call_both)
+           size += 4;
        }
       else
        {
-         size += 30 * 4;
-         if (stub_entry->stub_type == ppc_stub_plt_call_r2save)
-           size += 4;
+         size += 7 * 4;
+         if (stub_entry->stub_type == ppc_stub_plt_call_r2save
+             || stub_entry->stub_type == ppc_stub_plt_call_both)
+           size += 6 * 4;
        }
     }
   return size;
@@ -10912,7 +10910,8 @@ plt_stub_size (struct ppc_link_hash_table *htab,
 static inline unsigned int
 plt_stub_pad (struct ppc_link_hash_table *htab,
              struct ppc_stub_hash_entry *stub_entry,
-             bfd_vma plt_off)
+             bfd_vma plt_off,
+             unsigned int odd)
 {
   int stub_align;
   unsigned stub_size;
@@ -10927,7 +10926,7 @@ plt_stub_pad (struct ppc_link_hash_table *htab,
     }
 
   stub_align = 1 << -htab->params->plt_stub_align;
-  stub_size = plt_stub_size (htab, stub_entry, plt_off);
+  stub_size = plt_stub_size (htab, stub_entry, plt_off, odd);
   if (((stub_off + stub_size - 1) & -stub_align) - (stub_off & -stub_align)
       > ((stub_size - 1) & -stub_align))
     return stub_align - (stub_off & (stub_align - 1));
@@ -11123,14 +11122,12 @@ build_plt_stub (struct ppc_link_hash_table *htab,
 #define MR_R3_R0       0x7c030378
 #define BCTRL          0x4e800421
 
-static inline bfd_byte *
-build_tls_get_addr_stub (struct ppc_link_hash_table *htab,
+static bfd_byte *
+build_tls_get_addr_head (struct ppc_link_hash_table *htab,
                         struct ppc_stub_hash_entry *stub_entry,
-                        bfd_byte *p, bfd_vma offset, Elf_Internal_Rela *r)
+                        bfd_byte *p)
 {
   bfd *obfd = htab->params->stub_bfd;
-  bfd_byte *loc = p;
-  unsigned int i;
 
   bfd_put_32 (obfd, LD_R0_0R3 + 0, p),         p += 4;
   bfd_put_32 (obfd, LD_R12_0R3 + 8, p),                p += 4;
@@ -11139,21 +11136,43 @@ build_tls_get_addr_stub (struct ppc_link_hash_table *htab,
   bfd_put_32 (obfd, ADD_R3_R12_R13, p),                p += 4;
   bfd_put_32 (obfd, BEQLR, p),                 p += 4;
   bfd_put_32 (obfd, MR_R3_R0, p),              p += 4;
-  if (htab->params->no_tls_get_addr_regsave)
-    {
-      if (r != NULL)
-       r[0].r_offset += 7 * 4;
-      if (stub_entry->stub_type != ppc_stub_plt_call_r2save)
-       return build_plt_stub (htab, stub_entry, p, offset, r);
 
+  if (!htab->params->no_tls_get_addr_regsave)
+    p = tls_get_addr_prologue (obfd, p, htab);
+  else if (stub_entry->stub_type == ppc_stub_plt_call_r2save
+          || stub_entry->stub_type == ppc_stub_plt_call_both)
+    {
       bfd_put_32 (obfd, MFLR_R0, p);
       p += 4;
       bfd_put_32 (obfd, STD_R0_0R1 + STK_LINKER (htab), p);
       p += 4;
+    }
+  return p;
+}
 
-      if (r != NULL)
-       r[0].r_offset += 2 * 4;
-      p = build_plt_stub (htab, stub_entry, p, offset, r);
+static bfd_byte *
+build_tls_get_addr_tail (struct ppc_link_hash_table *htab,
+                        struct ppc_stub_hash_entry *stub_entry,
+                        bfd_byte *p,
+                        bfd_byte *loc)
+{
+  bfd *obfd = htab->params->stub_bfd;
+
+  if (!htab->params->no_tls_get_addr_regsave)
+    {
+      bfd_put_32 (obfd, BCTRL, p - 4);
+
+      if (stub_entry->stub_type == ppc_stub_plt_call_r2save
+         || stub_entry->stub_type == ppc_stub_plt_call_both)
+       {
+         bfd_put_32 (obfd, LD_R2_0R1 + STK_TOC (htab), p);
+         p += 4;
+       }
+      p = tls_get_addr_epilogue (obfd, p, htab);
+    }
+  else if (stub_entry->stub_type == ppc_stub_plt_call_r2save
+          || stub_entry->stub_type == ppc_stub_plt_call_both)
+    {
       bfd_put_32 (obfd, BCTRL, p - 4);
 
       bfd_put_32 (obfd, LD_R2_0R1 + STK_TOC (htab), p);
@@ -11165,24 +11184,6 @@ build_tls_get_addr_stub (struct ppc_link_hash_table *htab,
       bfd_put_32 (obfd, BLR, p);
       p += 4;
     }
-  else
-    {
-      p = tls_get_addr_prologue (obfd, p, htab);
-
-      if (r != NULL)
-       r[0].r_offset += 18 * 4;
-
-      p = build_plt_stub (htab, stub_entry, p, offset, r);
-      bfd_put_32 (obfd, BCTRL, p - 4);
-
-      if (stub_entry->stub_type == ppc_stub_plt_call_r2save)
-       {
-         bfd_put_32 (obfd, LD_R2_0R1 + STK_TOC (htab), p);
-         p += 4;
-       }
-
-      p = tls_get_addr_epilogue (obfd, p, htab);
-    }
 
   if (htab->glink_eh_frame != NULL
       && htab->glink_eh_frame->size != 0)
@@ -11191,21 +11192,11 @@ build_tls_get_addr_stub (struct ppc_link_hash_table *htab,
 
       base = htab->glink_eh_frame->contents + stub_entry->group->eh_base + 17;
       eh = base + stub_entry->group->eh_size;
-      if (htab->params->no_tls_get_addr_regsave)
-       {
-         unsigned int lr_used, delta;
-         lr_used = stub_entry->stub_offset + (p - 20 - loc);
-         delta = lr_used - stub_entry->group->lr_restore;
-         stub_entry->group->lr_restore = lr_used + 16;
-         eh = eh_advance (htab->elf.dynobj, eh, delta);
-         *eh++ = DW_CFA_offset_extended_sf;
-         *eh++ = 65;
-         *eh++ = -(STK_LINKER (htab) / 8) & 0x7f;
-         *eh++ = DW_CFA_advance_loc + 4;
-       }
-      else
+
+      if (!htab->params->no_tls_get_addr_regsave)
        {
-         unsigned int cfa_updt, delta;
+         unsigned int cfa_updt, delta, i;
+
          /* After the bctrl, lr has been modified so we need to emit
             .eh_frame info saying the return address is on the stack.  In
             fact we must put the EH info at or before the call rather
@@ -11244,10 +11235,27 @@ build_tls_get_addr_stub (struct ppc_link_hash_table *htab,
          for (i = 4; i < 12; i++)
            *eh++ = DW_CFA_restore + i;
          *eh++ = DW_CFA_advance_loc + 2;
+         *eh++ = DW_CFA_restore_extended;
+         *eh++ = 65;
+         stub_entry->group->eh_size = eh - base;
+       }
+      else if (stub_entry->stub_type == ppc_stub_plt_call_r2save
+              || stub_entry->stub_type == ppc_stub_plt_call_both)
+       {
+         unsigned int lr_used, delta;
+
+         lr_used = stub_entry->stub_offset + (p - 20 - loc);
+         delta = lr_used - stub_entry->group->lr_restore;
+         stub_entry->group->lr_restore = lr_used + 16;
+         eh = eh_advance (htab->elf.dynobj, eh, delta);
+         *eh++ = DW_CFA_offset_extended_sf;
+         *eh++ = 65;
+         *eh++ = -(STK_LINKER (htab) / 8) & 0x7f;
+         *eh++ = DW_CFA_advance_loc + 4;
+         *eh++ = DW_CFA_restore_extended;
+         *eh++ = 65;
+         stub_entry->group->eh_size = eh - base;
        }
-      *eh++ = DW_CFA_restore_extended;
-      *eh++ = 65;
-      stub_entry->group->eh_size = eh - base;
     }
   return p;
 }
@@ -11381,6 +11389,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
   struct ppc_branch_hash_entry *br_entry;
   struct bfd_link_info *info;
   struct ppc_link_hash_table *htab;
+  bfd *obfd;
   bfd_byte *loc;
   bfd_byte *p, *relp;
   bfd_vma targ, off;
@@ -11388,6 +11397,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
   asection *plt;
   int num_rel;
   int odd;
+  bfd_boolean is_tga;
 
   /* Massage our args to the form they really have.  */
   stub_entry = (struct ppc_stub_hash_entry *) gen_entry;
@@ -11437,6 +11447,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       off = targ - off;
 
       p = loc;
+      obfd = htab->params->stub_bfd;
       if (stub_entry->stub_type == ppc_stub_long_branch_r2off)
        {
          bfd_vma r2off = get_r2off (info, stub_entry);
@@ -11446,23 +11457,21 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
              htab->stub_error = TRUE;
              return FALSE;
            }
-         bfd_put_32 (htab->params->stub_bfd, STD_R2_0R1 + STK_TOC (htab), p);
+         bfd_put_32 (obfd, STD_R2_0R1 + STK_TOC (htab), p);
          p += 4;
          if (PPC_HA (r2off) != 0)
            {
-             bfd_put_32 (htab->params->stub_bfd,
-                         ADDIS_R2_R2 | PPC_HA (r2off), p);
+             bfd_put_32 (obfd, ADDIS_R2_R2 | PPC_HA (r2off), p);
              p += 4;
            }
          if (PPC_LO (r2off) != 0)
            {
-             bfd_put_32 (htab->params->stub_bfd,
-                         ADDI_R2_R2 | PPC_LO (r2off), p);
+             bfd_put_32 (obfd, ADDI_R2_R2 | PPC_LO (r2off), p);
              p += 4;
            }
          off -= p - loc;
        }
-      bfd_put_32 (htab->params->stub_bfd, B_DOT | (off & 0x3fffffc), p);
+      bfd_put_32 (obfd, B_DOT | (off & 0x3fffffc), p);
       p += 4;
 
       if (off + (1 << 25) >= (bfd_vma) (1 << 26))
@@ -11588,19 +11597,17 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
        }
 
       p = loc;
+      obfd = htab->params->stub_bfd;
       if (stub_entry->stub_type != ppc_stub_plt_branch_r2off)
        {
          if (PPC_HA (off) != 0)
            {
-             bfd_put_32 (htab->params->stub_bfd,
-                         ADDIS_R12_R2 | PPC_HA (off), p);
+             bfd_put_32 (obfd, ADDIS_R12_R2 | PPC_HA (off), p);
              p += 4;
-             bfd_put_32 (htab->params->stub_bfd,
-                         LD_R12_0R12 | PPC_LO (off), p);
+             bfd_put_32 (obfd, LD_R12_0R12 | PPC_LO (off), p);
            }
          else
-           bfd_put_32 (htab->params->stub_bfd,
-                       LD_R12_0R2 | PPC_LO (off), p);
+           bfd_put_32 (obfd, LD_R12_0R2 | PPC_LO (off), p);
        }
       else
        {
@@ -11612,36 +11619,32 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
              return FALSE;
            }
 
-         bfd_put_32 (htab->params->stub_bfd, STD_R2_0R1 + STK_TOC (htab), p);
+         bfd_put_32 (obfd, STD_R2_0R1 + STK_TOC (htab), p);
          p += 4;
          if (PPC_HA (off) != 0)
            {
-             bfd_put_32 (htab->params->stub_bfd,
-                         ADDIS_R12_R2 | PPC_HA (off), p);
+             bfd_put_32 (obfd, ADDIS_R12_R2 | PPC_HA (off), p);
              p += 4;
-             bfd_put_32 (htab->params->stub_bfd,
-                         LD_R12_0R12 | PPC_LO (off), p);
+             bfd_put_32 (obfd, LD_R12_0R12 | PPC_LO (off), p);
            }
          else
-           bfd_put_32 (htab->params->stub_bfd, LD_R12_0R2 | PPC_LO (off), p);
+           bfd_put_32 (obfd, LD_R12_0R2 | PPC_LO (off), p);
 
          if (PPC_HA (r2off) != 0)
            {
              p += 4;
-             bfd_put_32 (htab->params->stub_bfd,
-                         ADDIS_R2_R2 | PPC_HA (r2off), p);
+             bfd_put_32 (obfd, ADDIS_R2_R2 | PPC_HA (r2off), p);
            }
          if (PPC_LO (r2off) != 0)
            {
              p += 4;
-             bfd_put_32 (htab->params->stub_bfd,
-                         ADDI_R2_R2 | PPC_LO (r2off), p);
+             bfd_put_32 (obfd, ADDI_R2_R2 | PPC_LO (r2off), p);
            }
        }
       p += 4;
-      bfd_put_32 (htab->params->stub_bfd, MTCTR_R12, p);
+      bfd_put_32 (obfd, MTCTR_R12, p);
       p += 4;
-      bfd_put_32 (htab->params->stub_bfd, BCTR, p);
+      bfd_put_32 (obfd, BCTR, p);
       p += 4;
       break;
 
@@ -11655,12 +11658,22 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       off = (stub_entry->stub_offset
             + stub_entry->group->stub_sec->output_offset
             + stub_entry->group->stub_sec->output_section->vma);
+      obfd = htab->params->stub_bfd;
+      is_tga = ((stub_entry->stub_type == ppc_stub_plt_call_notoc
+                || stub_entry->stub_type == ppc_stub_plt_call_both)
+               && is_tls_get_addr (&stub_entry->h->elf, htab)
+               && htab->params->tls_get_addr_opt);
+      if (is_tga)
+       {
+         p = build_tls_get_addr_head (htab, stub_entry, p);
+         off += p - loc;
+       }
       if (stub_entry->stub_type == ppc_stub_long_branch_both
          || stub_entry->stub_type == ppc_stub_plt_branch_both
          || stub_entry->stub_type == ppc_stub_plt_call_both)
        {
          off += 4;
-         bfd_put_32 (htab->params->stub_bfd, STD_R2_0R1 + STK_TOC (htab), p);
+         bfd_put_32 (obfd, STD_R2_0R1 + STK_TOC (htab), p);
          p += 4;
        }
       if (stub_entry->stub_type >= ppc_stub_plt_call_notoc)
@@ -11693,17 +11706,39 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       if (htab->params->power10_stubs != 0)
        {
          bfd_boolean load = stub_entry->stub_type >= ppc_stub_plt_call_notoc;
-         p = build_power10_offset (htab->params->stub_bfd, p, off, odd, load);
+         p = build_power10_offset (obfd, p, off, odd, load);
        }
       else
        {
+         if (htab->glink_eh_frame != NULL
+             && htab->glink_eh_frame->size != 0)
+           {
+             bfd_byte *base, *eh;
+             unsigned int lr_used, delta;
+
+             base = (htab->glink_eh_frame->contents
+                     + stub_entry->group->eh_base + 17);
+             eh = base + stub_entry->group->eh_size;
+             lr_used = stub_entry->stub_offset + (p - loc) + 8;
+             delta = lr_used - stub_entry->group->lr_restore;
+             stub_entry->group->lr_restore = lr_used + 8;
+             eh = eh_advance (htab->elf.dynobj, eh, delta);
+             *eh++ = DW_CFA_register;
+             *eh++ = 65;
+             *eh++ = 12;
+             *eh++ = DW_CFA_advance_loc + 2;
+             *eh++ = DW_CFA_restore_extended;
+             *eh++ = 65;
+             stub_entry->group->eh_size = eh - base;
+           }
+
          /* The notoc stubs calculate their target (either a PLT entry or
             the global entry point of a function) relative to the PC
             returned by the "bcl" two instructions past the start of the
             sequence emitted by build_offset.  The offset is therefore 8
             less than calculated from the start of the sequence.  */
          off -= 8;
-         p = build_offset (htab->params->stub_bfd, p, off,
+         p = build_offset (obfd, p, off,
                            stub_entry->stub_type >= ppc_stub_plt_call_notoc);
        }
 
@@ -11715,17 +11750,19 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
                  + stub_entry->group->stub_sec->output_offset
                  + stub_entry->group->stub_sec->output_section->vma
                  + (p - loc));
-         bfd_put_32 (htab->params->stub_bfd,
-                     B_DOT | ((targ - from) & 0x3fffffc), p);
+         bfd_put_32 (obfd, B_DOT | ((targ - from) & 0x3fffffc), p);
        }
       else
        {
-         bfd_put_32 (htab->params->stub_bfd, MTCTR_R12, p);
+         bfd_put_32 (obfd, MTCTR_R12, p);
          p += 4;
-         bfd_put_32 (htab->params->stub_bfd, BCTR, p);
+         bfd_put_32 (obfd, BCTR, p);
        }
       p += 4;
 
+      if (is_tga)
+       p = build_tls_get_addr_tail (htab, stub_entry, p, loc);
+
       if (info->emitrelocations)
        {
          bfd_vma roff = relp - stub_entry->group->stub_sec->contents;
@@ -11756,33 +11793,6 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
                return FALSE;
            }
        }
-
-      if (htab->params->power10_stubs == 0
-         && htab->glink_eh_frame != NULL
-         && htab->glink_eh_frame->size != 0)
-       {
-         bfd_byte *base, *eh;
-         unsigned int lr_used, delta;
-
-         base = (htab->glink_eh_frame->contents
-                 + stub_entry->group->eh_base + 17);
-         eh = base + stub_entry->group->eh_size;
-         lr_used = stub_entry->stub_offset + 8;
-         if (stub_entry->stub_type == ppc_stub_long_branch_both
-             || stub_entry->stub_type == ppc_stub_plt_branch_both
-             || stub_entry->stub_type == ppc_stub_plt_call_both)
-           lr_used += 4;
-         delta = lr_used - stub_entry->group->lr_restore;
-         stub_entry->group->lr_restore = lr_used + 8;
-         eh = eh_advance (htab->elf.dynobj, eh, delta);
-         *eh++ = DW_CFA_register;
-         *eh++ = 65;
-         *eh++ = 12;
-         *eh++ = DW_CFA_advance_loc + 2;
-         *eh++ = DW_CFA_restore_extended;
-         *eh++ = 65;
-         stub_entry->group->eh_size = eh - base;
-       }
       break;
 
     case ppc_stub_plt_call:
@@ -11851,12 +11861,20 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
            r[0].r_offset += 2;
          r[0].r_addend = targ;
        }
-      if (stub_entry->h != NULL
-         && is_tls_get_addr (&stub_entry->h->elf, htab)
-         && htab->params->tls_get_addr_opt)
-       p = build_tls_get_addr_stub (htab, stub_entry, loc, off, r);
-      else
-       p = build_plt_stub (htab, stub_entry, loc, off, r);
+      p = loc;
+      obfd = htab->params->stub_bfd;
+      is_tga = (stub_entry->h != NULL
+               && is_tls_get_addr (&stub_entry->h->elf, htab)
+               && htab->params->tls_get_addr_opt);
+      if (is_tga)
+       {
+         p = build_tls_get_addr_head (htab, stub_entry, p);
+         if (r != NULL)
+           r[0].r_offset += p - loc;
+       }
+      p = build_plt_stub (htab, stub_entry, p, off, r);
+      if (is_tga)
+       p = build_tls_get_addr_tail (htab, stub_entry, p, loc);
       break;
 
     case ppc_stub_save_res:
@@ -12152,11 +12170,18 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
 
     case ppc_stub_plt_call_notoc:
     case ppc_stub_plt_call_both:
-      off = (stub_entry->stub_offset
-            + stub_entry->group->stub_sec->output_offset
-            + stub_entry->group->stub_sec->output_section->vma);
+      lr_used = 0;
+      if (is_tls_get_addr (&stub_entry->h->elf, htab)
+         && htab->params->tls_get_addr_opt)
+       {
+         lr_used += 7 * 4;
+         if (!htab->params->no_tls_get_addr_regsave)
+           lr_used += 11 * 4;
+         else if (stub_entry->stub_type == ppc_stub_plt_call_both)
+           lr_used += 2 * 4;
+       }
       if (stub_entry->stub_type == ppc_stub_plt_call_both)
-       off += 4;
+       lr_used += 4;
       targ = stub_entry->plt_ent->plt.offset & ~1;
       if (targ >= (bfd_vma) -2)
        abort ();
@@ -12172,16 +12197,21 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
            plt = htab->pltlocal;
        }
       targ += plt->output_offset + plt->output_section->vma;
+      off = (stub_entry->stub_offset
+            + stub_entry->group->stub_sec->output_offset
+            + stub_entry->group->stub_sec->output_section->vma
+            + lr_used);
       odd = off & 4;
       off = targ - off;
 
       if (htab->params->plt_stub_align != 0)
        {
-         unsigned pad = plt_stub_pad (htab, stub_entry, off);
+         unsigned pad = plt_stub_pad (htab, stub_entry, off, odd);
 
          stub_entry->group->stub_sec->size += pad;
          stub_entry->stub_offset = stub_entry->group->stub_sec->size;
          off -= pad;
+         odd ^= pad & 4;
        }
 
       if (info->emitrelocations)
@@ -12195,15 +12225,13 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          stub_entry->group->stub_sec->flags |= SEC_RELOC;
        }
 
-      size = plt_stub_size (htab, stub_entry, off);
+      size = plt_stub_size (htab, stub_entry, off, odd);
 
       if (htab->params->power10_stubs == 0)
        {
          /* After the bcl, lr has been modified so we need to emit
             .eh_frame info saying the return address is in r12.  */
-         lr_used = stub_entry->stub_offset + 8;
-         if (stub_entry->stub_type == ppc_stub_plt_call_both)
-           lr_used += 4;
+         lr_used += stub_entry->stub_offset + 8;
          /* The eh_frame info will consist of a DW_CFA_advance_loc or
             variant, DW_CFA_register, 65, 12, DW_CFA_advance_loc+2,
             DW_CFA_restore_extended 65.  */
@@ -12211,6 +12239,29 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          stub_entry->group->eh_size += eh_advance_size (delta) + 6;
          stub_entry->group->lr_restore = lr_used + 8;
        }
+      if ((stub_entry->stub_type == ppc_stub_plt_call_notoc
+          || stub_entry->stub_type == ppc_stub_plt_call_both)
+         && is_tls_get_addr (&stub_entry->h->elf, htab)
+         && htab->params->tls_get_addr_opt)
+       {
+         if (!htab->params->no_tls_get_addr_regsave)
+           {
+             unsigned int cfa_updt = stub_entry->stub_offset + 18 * 4;
+             delta = cfa_updt - stub_entry->group->lr_restore;
+             stub_entry->group->eh_size += eh_advance_size (delta);
+             stub_entry->group->eh_size += htab->opd_abi ? 36 : 35;
+             stub_entry->group->lr_restore
+               = stub_entry->stub_offset + size - 4;
+           }
+         else if (stub_entry->stub_type == ppc_stub_plt_call_both)
+           {
+             lr_used = stub_entry->stub_offset + size - 20;
+             delta = lr_used - stub_entry->group->lr_restore;
+             stub_entry->group->eh_size += eh_advance_size (delta) + 6;
+             stub_entry->group->lr_restore
+               = stub_entry->stub_offset + size - 4;
+           }
+       }
       break;
 
     case ppc_stub_plt_call:
@@ -12236,7 +12287,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
 
       if (htab->params->plt_stub_align != 0)
        {
-         unsigned pad = plt_stub_pad (htab, stub_entry, off);
+         unsigned pad = plt_stub_pad (htab, stub_entry, off, 0);
 
          stub_entry->group->stub_sec->size += pad;
          stub_entry->stub_offset = stub_entry->group->stub_sec->size;
@@ -12253,14 +12304,22 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          stub_entry->group->stub_sec->flags |= SEC_RELOC;
        }
 
-      size = plt_stub_size (htab, stub_entry, off);
+      size = plt_stub_size (htab, stub_entry, off, 0);
 
       if (stub_entry->h != NULL
          && is_tls_get_addr (&stub_entry->h->elf, htab)
          && htab->params->tls_get_addr_opt
          && stub_entry->stub_type == ppc_stub_plt_call_r2save)
        {
-         if (htab->params->no_tls_get_addr_regsave)
+         if (!htab->params->no_tls_get_addr_regsave)
+           {
+             /* Adjustments to r1 need to be described.  */
+             unsigned int cfa_updt = stub_entry->stub_offset + 18 * 4;
+             delta = cfa_updt - stub_entry->group->lr_restore;
+             stub_entry->group->eh_size += eh_advance_size (delta);
+             stub_entry->group->eh_size += htab->opd_abi ? 36 : 35;
+           }
+         else
            {
              lr_used = stub_entry->stub_offset + size - 20;
              /* The eh_frame info will consist of a DW_CFA_advance_loc
@@ -12269,15 +12328,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
              delta = lr_used - stub_entry->group->lr_restore;
              stub_entry->group->eh_size += eh_advance_size (delta) + 6;
            }
-         else
-           {
-             /* Adjustments to r1 need to be described.  */
-             unsigned int cfa_updt = stub_entry->stub_offset + 18 * 4;
-             delta = cfa_updt - stub_entry->group->lr_restore;
-             stub_entry->group->eh_size += eh_advance_size (delta);
-             stub_entry->group->eh_size += htab->opd_abi ? 36 : 35;
-           }
-         stub_entry->group->lr_restore = size - 4;
+         stub_entry->group->lr_restore = stub_entry->stub_offset + size - 4;
        }
       break;
 
@@ -15890,22 +15941,25 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              addend = 0;
              reloc_dest = DEST_STUB;
 
-             if (((stub_entry->stub_type == ppc_stub_plt_call
-                   && ALWAYS_EMIT_R2SAVE)
-                  || stub_entry->stub_type == ppc_stub_plt_call_r2save
-                  || stub_entry->stub_type == ppc_stub_plt_call_both)
-                 && !(h != NULL
-                      && is_tls_get_addr (&h->elf, htab)
-                      && htab->params->tls_get_addr_opt)
-                 && rel + 1 < relend
-                 && rel[1].r_offset == rel->r_offset + 4
-                 && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOCSAVE)
-               relocation += 4;
-             else if ((stub_entry->stub_type == ppc_stub_long_branch_both
-                       || stub_entry->stub_type == ppc_stub_plt_branch_both
-                       || stub_entry->stub_type == ppc_stub_plt_call_both)
-                      && r_type == R_PPC64_REL24_NOTOC)
-               relocation += 4;
+             if ((((stub_entry->stub_type == ppc_stub_plt_call
+                    && ALWAYS_EMIT_R2SAVE)
+                   || stub_entry->stub_type == ppc_stub_plt_call_r2save
+                   || stub_entry->stub_type == ppc_stub_plt_call_both)
+                  && rel + 1 < relend
+                  && rel[1].r_offset == rel->r_offset + 4
+                  && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOCSAVE)
+                 || ((stub_entry->stub_type == ppc_stub_long_branch_both
+                      || stub_entry->stub_type == ppc_stub_plt_branch_both
+                      || stub_entry->stub_type == ppc_stub_plt_call_both)
+                     && r_type == R_PPC64_REL24_NOTOC))
+               {
+                 /* Skip over the r2 store at the start of the stub.  */
+                 if (!(stub_entry->stub_type >= ppc_stub_plt_call
+                       && htab->params->tls_get_addr_opt
+                       && h != NULL
+                       && is_tls_get_addr (&h->elf, htab)))
+                   relocation += 4;
+               }
 
              if (r_type == R_PPC64_REL24_NOTOC
                  && (stub_entry->stub_type == ppc_stub_plt_call_notoc