Return unique_xmalloc_ptr for generate_c_for_variable_locations
[deliverable/binutils-gdb.git] / bfd / elf64-ppc.c
index 5cd3b8dde3e58800f3b06dc1f6fead96adf6d317..c9ac8e9aace7db5dfd2a61dc503bf2e97bb66e11 100644 (file)
@@ -164,6 +164,8 @@ static bfd_vma opd_entry_value
 #define BCTR           0x4e800420      /* bctr                      */
 
 #define ADDI_R11_R11   0x396b0000      /* addi %r11,%r11,off@l  */
+#define ADDI_R12_R11   0x398b0000      /* addi %r12,%r11,off@l  */
+#define ADDI_R12_R12   0x398c0000      /* addi %r12,%r12,off@l  */
 #define ADDIS_R2_R2    0x3c420000      /* addis %r2,%r2,off@ha  */
 #define ADDI_R2_R2     0x38420000      /* addi  %r2,%r2,off@l   */
 
@@ -184,10 +186,17 @@ static bfd_vma opd_entry_value
 #define ADD_R2_R2_R12  0x7c426214      /* add   %r2,%r2,%r12    */
 
 #define LIS_R2         0x3c400000      /* lis %r2,xxx@ha         */
+#define LIS_R12                0x3d800000      /* lis %r12,xxx@ha        */
 #define ADDIS_R2_R12   0x3c4c0000      /* addis %r2,%r12,xxx@ha  */
 #define ADDIS_R12_R2   0x3d820000      /* addis %r12,%r2,xxx@ha  */
+#define ADDIS_R12_R11  0x3d8b0000      /* addis %r12,%r11,xxx@ha */
 #define ADDIS_R12_R12  0x3d8c0000      /* addis %r12,%r12,xxx@ha */
+#define ORIS_R12_R12_0 0x658c0000      /* oris  %r12,%r12,xxx@hi */
+#define ORI_R12_R12_0  0x618c0000      /* ori   %r12,%r12,xxx@l  */
 #define LD_R12_0R12    0xe98c0000      /* ld    %r12,xxx@l(%r12) */
+#define SLDI_R12_R12_32        0x799c07c6      /* sldi  %r12,%r12,32     */
+#define LDX_R12_R11_R12 0x7d8b602a     /* ldx   %r12,%r11,%r12   */
+#define ADD_R12_R11_R12 0x7d8b6214     /* add   %r12,%r11,%r12   */
 
 /* __glink_PLTresolve stub instructions.  We enter with the index in R0.  */
 #define GLINK_PLTRESOLVE_SIZE(htab)                    \
@@ -446,6 +455,21 @@ static reloc_howto_type ppc64_elf_howto_raw[] =
         0x03fffffc,            /* dst_mask */
         TRUE),                 /* pcrel_offset */
 
+  /* A variant of R_PPC64_REL24, used when r2 is not the toc pointer.  */
+  HOWTO (R_PPC64_REL24_NOTOC,  /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        26,                    /* bitsize */
+        TRUE,                  /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        ppc64_elf_branch_reloc, /* special_function */
+        "R_PPC64_REL24_NOTOC", /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0x03fffffc,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
   /* A relative 16 bit branch; the lower two bits must be zero.  */
   HOWTO (R_PPC64_REL14,                /* type */
         0,                     /* rightshift */
@@ -2308,6 +2332,8 @@ ppc64_elf_reloc_type_lookup (bfd *abfd,
       break;
     case BFD_RELOC_PPC_B26:                    r = R_PPC64_REL24;
       break;
+    case BFD_RELOC_PPC64_REL24_NOTOC:          r = R_PPC64_REL24_NOTOC;
+      break;
     case BFD_RELOC_PPC_B16:                    r = R_PPC64_REL14;
       break;
     case BFD_RELOC_PPC_B16_BRTAKEN:            r = R_PPC64_REL14_BRTAKEN;
@@ -3954,7 +3980,7 @@ must_be_dyn_reloc (struct bfd_link_info *info,
    Used to call a function in a shared library.  If it so happens that
    the plt entry referenced crosses a 64k boundary, then an extra
    "addi %r11,%r11,xxx@toc@l" will be inserted before the "mtctr".
-   .   std     %r2,40(%r1)
+   ppc_stub_plt_call_r2save starts with "std %r2,40(%r1)".
    .   addis   %r11,%r2,xxx@toc@ha
    .   ld      %r12,xxx+0@toc@l(%r11)
    .   mtctr   %r12
@@ -3979,18 +4005,98 @@ must_be_dyn_reloc (struct bfd_link_info *info,
    .   mtctr   %r12
    .   bctr
 
-   In cases where the "addis" instruction would add zero, the "addis" is
-   omitted and following instructions modified slightly in some cases.
-*/
+   All of the above stubs are shown as their ELFv1 variants.  ELFv2
+   variants exist too, simpler for plt calls since a new toc pointer
+   and static chain are not loaded by the stub.  In addition, ELFv2
+   has some more complex stubs to handle calls marked with NOTOC
+   relocs from functions where r2 is not a valid toc pointer.  These
+   come in two flavours, the ones shown below, and _both variants that
+   start with "std %r2,24(%r1)" to save r2 in the unlikely event that
+   one call is from a function where r2 is used as the toc pointer but
+   needs a toc adjusting stub for small-model multi-toc, and another
+   call is from a function where r2 is not valid.
+   ppc_stub_long_branch_notoc:
+   .   mflr    %r12
+   .   bcl     20,31,1f
+   .  1:
+   .   mflr    %r11
+   .   mtlr    %r12
+   .   lis     %r12,xxx-1b@highest
+   .   ori     %r12,xxx-1b@higher
+   .   sldi    %r12,%r12,32
+   .   oris    %r12,%r12,xxx-1b@hi
+   .   ori     %r12,%r12,xxx-1b@l
+   .   add     %r12,%r11,%r12
+   .   b       dest
+
+   ppc_stub_plt_branch_notoc:
+   .   mflr    %r12
+   .   bcl     20,31,1f
+   .  1:
+   .   mflr    %r11
+   .   mtlr    %r12
+   .   lis     %r12,xxx-1b@highest
+   .   ori     %r12,xxx-1b@higher
+   .   sldi    %r12,%r12,32
+   .   oris    %r12,%r12,xxx-1b@hi
+   .   ori     %r12,%r12,xxx-1b@l
+   .   add     %r12,%r11,%r12
+   .   mtctr   %r12
+   .   bctr
+
+   ppc_stub_plt_call_notoc:
+   .   mflr    %r12
+   .   bcl     20,31,1f
+   .  1:
+   .   mflr    %r11
+   .   mtlr    %r12
+   .   lis     %r12,xxx-1b@highest
+   .   ori     %r12,xxx-1b@higher
+   .   sldi    %r12,%r12,32
+   .   oris    %r12,%r12,xxx-1b@hi
+   .   ori     %r12,%r12,xxx-1b@l
+   .   ldx     %r12,%r11,%r12
+   .   mtctr   %r12
+   .   bctr
+
+   In cases where the high instructions would add zero, they are
+   omitted and following instructions modified in some cases.
+
+   For a given stub group (a set of sections all using the same toc
+   pointer value) there will be just one stub type used for any
+   particular function symbol.  For example, if printf is called from
+   code with the tocsave optimization (ie. r2 saved in function
+   prologue) and therefore calls use a ppc_stub_plt_call linkage stub,
+   and from other code without the tocsave optimization requiring a
+   ppc_stub_plt_call_r2save linkage stub, a single stub of the latter
+   type will be created.  Calls with the tocsave optimization will
+   enter this stub after the instruction saving r2.  A similar
+   situation exists when calls are marked with R_PPC64_REL24_NOTOC
+   relocations.  These require a ppc_stub_plt_call_notoc linkage stub
+   to call an external function like printf.  If other calls to printf
+   require a ppc_stub_plt_call linkage stub then a single
+   ppc_stub_plt_call_notoc linkage stub will be used for both types of
+   call.  If other calls to printf require a ppc_stub_plt_call_r2save
+   linkage stub then a single ppc_stub_plt_call_both linkage stub will
+   be created and calls not requiring r2 to be saved will enter the
+   stub after the r2 save instruction.  There is an analogous
+   hierarchy of long branch and plt branch stubs for local call
+   linkage.  */
 
 enum ppc_stub_type {
   ppc_stub_none,
   ppc_stub_long_branch,
   ppc_stub_long_branch_r2off,
+  ppc_stub_long_branch_notoc,
+  ppc_stub_long_branch_both, /* r2off and notoc variants both needed.  */
   ppc_stub_plt_branch,
   ppc_stub_plt_branch_r2off,
+  ppc_stub_plt_branch_notoc,
+  ppc_stub_plt_branch_both,
   ppc_stub_plt_call,
   ppc_stub_plt_call_r2save,
+  ppc_stub_plt_call_notoc,
+  ppc_stub_plt_call_both,
   ppc_stub_global_entry,
   ppc_stub_save_res
 };
@@ -4007,9 +4113,15 @@ struct map_stub
   /* Whether to emit a copy of register save/restore functions in this
      group.  */
   int needs_save_res;
-  /* The offset of the __tls_get_addr_opt plt stub bctrl in this group,
-     or -1u if no such stub with bctrl exists.  */
-  unsigned int tls_get_addr_opt_bctrl;
+  /* Current offset within stubs after the insn restoring lr in a
+     _notoc or _both stub using bcl for pc-relative addressing, or
+     after the insn restoring lr in a __tls_get_addr_opt plt stub.  */
+  unsigned int lr_restore;
+  /* Accumulated size of EH info emitted to describe return address
+     if stubs modify lr.  Does not include 17 byte FDE header.  */
+  unsigned int eh_size;
+  /* Offset in glink_eh_frame to the start of EH info for this group.  */
+  unsigned int eh_base;
 };
 
 struct ppc_stub_hash_entry {
@@ -5478,6 +5590,7 @@ static bfd_boolean
 is_branch_reloc (enum elf_ppc64_reloc_type r_type)
 {
   return (r_type == R_PPC64_REL24
+         || r_type == R_PPC64_REL24_NOTOC
          || r_type == R_PPC64_REL14
          || r_type == R_PPC64_REL14_BRTAKEN
          || r_type == R_PPC64_REL14_BRNTAKEN
@@ -5848,6 +5961,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
          /* Fall through.  */
 
        case R_PPC64_REL24:
+       case R_PPC64_REL24_NOTOC:
        rel24:
          plt_list = ifunc;
          if (h != NULL)
@@ -10757,6 +10871,152 @@ ppc_type_of_stub (asection *input_sec,
   return ppc_stub_none;
 }
 
+/* Builds a 64-bit offset in r12 then adds it to r11 (LOAD false) or
+   loads r12 from r11+r12 (LOAD true).
+   .   lis     %r12,xxx-1b@highest
+   .   ori     %r12,xxx-1b@higher
+   .   sldi    %r12,%r12,32
+   .   oris    %r12,%r12,xxx-1b@hi
+   .   ori     %r12,%r12,xxx-1b@l
+   .   add     %r12,%r11,%r12  */
+
+static bfd_byte *
+build_offset (bfd *abfd, bfd_byte *p, bfd_vma off, bfd_boolean load)
+{
+  if (off + 0x8000 < 0x10000)
+    {
+      if (load)
+       bfd_put_32 (abfd, LD_R12_0R11 + PPC_LO (off), p);
+      else
+       bfd_put_32 (abfd, ADDI_R12_R11 + PPC_LO (off), p);
+      p += 4;
+    }
+  else if (off + 0x80008000ULL < 0x100000000ULL)
+    {
+      bfd_put_32 (abfd, ADDIS_R12_R11 + PPC_HA (off), p);
+      p += 4;
+      if (load)
+       bfd_put_32 (abfd, LD_R12_0R12 + PPC_LO (off), p);
+      else
+       bfd_put_32 (abfd, ADDI_R12_R12 + PPC_LO (off), p);
+      p += 4;
+    }
+  else
+    {
+      if (off + 0x800000000000ULL < 0x1000000000000ULL)
+       {
+         bfd_put_32 (abfd, LI_R12_0 + ((off >> 32) & 0xffff), p);
+         p += 4;
+       }
+      else
+       {
+         bfd_put_32 (abfd, LIS_R12 + ((off >> 48) & 0xffff), p);
+         p += 4;
+         if (((off >> 32) & 0xffff) != 0)
+           {
+             bfd_put_32 (abfd, ORI_R12_R12_0 + ((off >> 32) & 0xffff), p);
+             p += 4;
+           }
+       }
+      if (((off >> 32) & 0xffffffffULL) != 0)
+       {
+         bfd_put_32 (abfd, SLDI_R12_R12_32, p);
+         p += 4;
+       }
+      if (PPC_HI (off) != 0)
+       {
+         bfd_put_32 (abfd, ORIS_R12_R12_0 + PPC_HI (off), p);
+         p += 4;
+       }
+      if (PPC_LO (off) != 0)
+       {
+         bfd_put_32 (abfd, ORI_R12_R12_0 + PPC_LO (off), p);
+         p += 4;
+       }
+      if (load)
+       bfd_put_32 (abfd, LDX_R12_R11_R12, p);
+      else
+       bfd_put_32 (abfd, ADD_R12_R11_R12, p);
+      p += 4;
+    }
+  return p;
+}
+
+static unsigned int
+size_offset (bfd_vma off)
+{
+  unsigned int size;
+  if (off + 0x8000 < 0x10000)
+    size = 4;
+  else if (off + 0x80008000ULL < 0x100000000ULL)
+    size = 8;
+  else
+    {
+      if (off + 0x800000000000ULL < 0x1000000000000ULL)
+       size = 4;
+      else
+       {
+         size = 4;
+         if (((off >> 32) & 0xffff) != 0)
+           size += 4;
+       }
+      if (((off >> 32) & 0xffffffffULL) != 0)
+       size += 4;
+      if (PPC_HI (off) != 0)
+       size += 4;
+      if (PPC_LO (off) != 0)
+       size += 4;
+      size += 4;
+    }
+  return size;
+}
+
+/* Emit .eh_frame opcode to advance pc by DELTA.  */
+
+static bfd_byte *
+eh_advance (bfd *abfd, bfd_byte *eh, unsigned int delta)
+{
+  delta /= 4;
+  if (delta < 64)
+    *eh++ = DW_CFA_advance_loc + delta;
+  else if (delta < 256)
+    {
+      *eh++ = DW_CFA_advance_loc1;
+      *eh++ = delta;
+    }
+  else if (delta < 65536)
+    {
+      *eh++ = DW_CFA_advance_loc2;
+      bfd_put_16 (abfd, delta, eh);
+      eh += 2;
+    }
+  else
+    {
+      *eh++ = DW_CFA_advance_loc4;
+      bfd_put_32 (abfd, delta, eh);
+      eh += 4;
+    }
+  return eh;
+}
+
+/* Size of required .eh_frame opcode to advance pc by DELTA.  */
+
+static unsigned int
+eh_advance_size (unsigned int delta)
+{
+  if (delta < 64 * 4)
+    /* DW_CFA_advance_loc+[1..63].  */
+    return 1;
+  if (delta < 256 * 4)
+    /* DW_CFA_advance_loc1, byte.  */
+    return 2;
+  if (delta < 65536 * 4)
+    /* DW_CFA_advance_loc2, 2 bytes.  */
+    return 3;
+  /* DW_CFA_advance_loc4, 4 bytes.  */
+  return 5;
+}
+
 /* With power7 weakly ordered memory model, it is possible for ld.so
    to update a plt entry in one thread and have another thread see a
    stale zero toc entry.  To avoid this we need some sort of acquire
@@ -10784,8 +11044,17 @@ plt_stub_size (struct ppc_link_hash_table *htab,
               struct ppc_stub_hash_entry *stub_entry,
               bfd_vma off)
 {
-  unsigned size = 12;
+  unsigned size;
+
+  if (stub_entry->stub_type >= ppc_stub_plt_call_notoc)
+    {
+      size = 24 + size_offset (off);
+      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;
@@ -10810,8 +11079,7 @@ plt_stub_size (struct ppc_link_hash_table *htab,
       && htab->params->tls_get_addr_opt)
     {
       size += 7 * 4;
-      if (ALWAYS_EMIT_R2SAVE
-         || stub_entry->stub_type == ppc_stub_plt_call_r2save)
+      if (stub_entry->stub_type == ppc_stub_plt_call_r2save)
        size += 6 * 4;
     }
   return size;
@@ -10829,7 +11097,7 @@ plt_stub_pad (struct ppc_link_hash_table *htab,
              bfd_vma plt_off)
 {
   int stub_align;
-  unsigned stub_size = plt_stub_size (htab, stub_entry, plt_off);
+  unsigned stub_size;
   bfd_vma stub_off = stub_entry->group->stub_sec->size;
 
   if (htab->params->plt_stub_align >= 0)
@@ -10841,6 +11109,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);
   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));
@@ -11045,6 +11314,7 @@ build_tls_get_addr_stub (struct ppc_link_hash_table *htab,
                         bfd_byte *p, bfd_vma offset, Elf_Internal_Rela *r)
 {
   bfd *obfd = htab->params->stub_bfd;
+  bfd_byte *loc = p;
 
   bfd_put_32 (obfd, LD_R11_0R3 + 0, p),                p += 4;
   bfd_put_32 (obfd, LD_R12_0R3 + 8, p),                p += 4;
@@ -11055,8 +11325,7 @@ build_tls_get_addr_stub (struct ppc_link_hash_table *htab,
   bfd_put_32 (obfd, MR_R3_R0, p),              p += 4;
   if (r != NULL)
     r[0].r_offset += 7 * 4;
-  if (!ALWAYS_EMIT_R2SAVE
-      && stub_entry->stub_type != ppc_stub_plt_call_r2save)
+  if (stub_entry->stub_type != ppc_stub_plt_call_r2save)
     return build_plt_stub (htab, stub_entry, p, offset, r);
 
   bfd_put_32 (obfd, MFLR_R11, p),              p += 4;
@@ -11072,6 +11341,26 @@ build_tls_get_addr_stub (struct ppc_link_hash_table *htab,
   bfd_put_32 (obfd, MTLR_R11, p),              p += 4;
   bfd_put_32 (obfd, BLR, p),                   p += 4;
 
+  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 - 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;
+    }
   return p;
 }
 
@@ -11148,7 +11437,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
   struct ppc_link_hash_table *htab;
   bfd_byte *loc;
   bfd_byte *p;
-  bfd_vma dest, off;
+  bfd_vma targ, off;
   Elf_Internal_Rela *r;
   asection *plt;
 
@@ -11160,8 +11449,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
   if (htab == NULL)
     return FALSE;
 
-  /* Make a note of the offset within the stubs for this entry.  */
-  stub_entry->stub_offset = stub_entry->group->stub_sec->size;
+  BFD_ASSERT (stub_entry->stub_offset >= stub_entry->group->stub_sec->size);
   loc = stub_entry->group->stub_sec->contents + stub_entry->stub_offset;
 
   htab->stub_count[stub_entry->stub_type - 1] += 1;
@@ -11170,16 +11458,16 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
     case ppc_stub_long_branch:
     case ppc_stub_long_branch_r2off:
       /* Branches are relative.  This is where we are going to.  */
-      dest = (stub_entry->target_value
+      targ = (stub_entry->target_value
              + stub_entry->target_section->output_offset
              + stub_entry->target_section->output_section->vma);
-      dest += PPC64_LOCAL_ENTRY_OFFSET (stub_entry->other);
-      off = dest;
+      targ += PPC64_LOCAL_ENTRY_OFFSET (stub_entry->other);
 
       /* And this is where we are coming from.  */
-      off -= (stub_entry->stub_offset
-             + stub_entry->group->stub_sec->output_offset
-             + stub_entry->group->stub_sec->output_section->vma);
+      off = (stub_entry->stub_offset
+            + stub_entry->group->stub_sec->output_offset
+            + stub_entry->group->stub_sec->output_section->vma);
+      off = targ - off;
 
       p = loc;
       if (stub_entry->stub_type == ppc_stub_long_branch_r2off)
@@ -11226,7 +11514,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
            return FALSE;
          r->r_offset = p - 4 - stub_entry->group->stub_sec->contents;
          r->r_info = ELF64_R_INFO (0, R_PPC64_REL24);
-         r->r_addend = dest;
+         r->r_addend = targ;
          if (stub_entry->h != NULL)
            {
              struct elf_link_hash_entry **hashes;
@@ -11278,13 +11566,13 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          return FALSE;
        }
 
-      dest = (stub_entry->target_value
+      targ = (stub_entry->target_value
              + stub_entry->target_section->output_offset
              + stub_entry->target_section->output_section->vma);
       if (stub_entry->stub_type != ppc_stub_plt_branch_r2off)
-       dest += PPC64_LOCAL_ENTRY_OFFSET (stub_entry->other);
+       targ += PPC64_LOCAL_ENTRY_OFFSET (stub_entry->other);
 
-      bfd_put_64 (htab->brlt->owner, dest,
+      bfd_put_64 (htab->brlt->owner, targ,
                  htab->brlt->contents + br_entry->offset);
 
       if (br_entry->iter == htab->stub_iteration)
@@ -11301,7 +11589,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
                               + htab->brlt->output_offset
                               + htab->brlt->output_section->vma);
              rela.r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE);
-             rela.r_addend = dest;
+             rela.r_addend = targ;
 
              rl = htab->relbrlt->contents;
              rl += (htab->relbrlt->reloc_count++
@@ -11321,17 +11609,17 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
                             + htab->brlt->output_offset
                             + htab->brlt->output_section->vma);
              r->r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE);
-             r->r_addend = dest;
+             r->r_addend = targ;
            }
        }
 
-      dest = (br_entry->offset
+      targ = (br_entry->offset
              + htab->brlt->output_offset
              + htab->brlt->output_section->vma);
 
-      off = (dest
-            - elf_gp (info->output_bfd)
-            - htab->sec_info[stub_entry->group->link_sec->id].toc_off);
+      off = (elf_gp (info->output_bfd)
+            + htab->sec_info[stub_entry->group->link_sec->id].toc_off);
+      off = targ - off;
 
       if (off + 0x80008000 > 0xffffffff || (off & 7) != 0)
        {
@@ -11354,7 +11642,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          if (stub_entry->stub_type == ppc_stub_plt_branch_r2off)
            r[0].r_offset += 4;
          r[0].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_DS);
-         r[0].r_addend = dest;
+         r[0].r_addend = targ;
          if (PPC_HA (off) != 0)
            {
              r[0].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_HA);
@@ -11422,6 +11710,104 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       p += 4;
       break;
 
+    case ppc_stub_long_branch_notoc:
+    case ppc_stub_long_branch_both:
+    case ppc_stub_plt_branch_notoc:
+    case ppc_stub_plt_branch_both:
+    case ppc_stub_plt_call_notoc:
+    case ppc_stub_plt_call_both:
+      p = loc;
+      off = (8 + 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_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);
+         p += 4;
+       }
+      if (stub_entry->stub_type >= ppc_stub_plt_call_notoc)
+       {
+         targ = stub_entry->plt_ent->plt.offset & ~1;
+         if (targ >= (bfd_vma) -2)
+           abort ();
+
+         plt = htab->elf.splt;
+         if (!htab->elf.dynamic_sections_created
+             || stub_entry->h == NULL
+             || stub_entry->h->elf.dynindx == -1)
+           {
+             if (stub_entry->symtype == STT_GNU_IFUNC)
+               plt = htab->elf.iplt;
+             else
+               plt = htab->pltlocal;
+           }
+         targ += plt->output_offset + plt->output_section->vma;
+       }
+      else
+       targ = (stub_entry->target_value
+               + stub_entry->target_section->output_offset
+               + stub_entry->target_section->output_section->vma);
+      off = targ - off;
+      bfd_put_32 (htab->params->stub_bfd, MFLR_R12, p);
+      p += 4;
+      bfd_put_32 (htab->params->stub_bfd, BCL_20_31, p);
+      p += 4;
+      bfd_put_32 (htab->params->stub_bfd, MFLR_R11, p);
+      p += 4;
+      bfd_put_32 (htab->params->stub_bfd, MTLR_R12, p);
+      p += 4;
+      p = build_offset (htab->params->stub_bfd, p, off,
+                       stub_entry->stub_type >= ppc_stub_plt_call_notoc);
+      if (stub_entry->stub_type == ppc_stub_long_branch_notoc)
+       {
+         off += 8;
+         bfd_put_32 (htab->params->stub_bfd,
+                     B_DOT | ((off - (p - loc)) & 0x3fffffc), p);
+       }
+      else if (stub_entry->stub_type == ppc_stub_long_branch_both)
+       {
+         off += 12;
+         bfd_put_32 (htab->params->stub_bfd,
+                     B_DOT | ((off - (p - loc)) & 0x3fffffc), p);
+       }
+      else
+       {
+         bfd_put_32 (htab->params->stub_bfd, MTCTR_R12, p);
+         p += 4;
+         bfd_put_32 (htab->params->stub_bfd, BCTR, p);
+       }
+      p += 4;
+
+      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 + 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:
     case ppc_stub_plt_call_r2save:
       if (stub_entry->h != NULL
@@ -11439,8 +11825,8 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
        }
 
       /* Now build the stub.  */
-      dest = stub_entry->plt_ent->plt.offset & ~1;
-      if (dest >= (bfd_vma) -2)
+      targ = stub_entry->plt_ent->plt.offset & ~1;
+      if (targ >= (bfd_vma) -2)
        abort ();
 
       plt = htab->elf.splt;
@@ -11453,12 +11839,11 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          else
            plt = htab->pltlocal;
        }
+      targ += plt->output_offset + plt->output_section->vma;
 
-      dest += plt->output_offset + plt->output_section->vma;
-
-      off = (dest
-            - elf_gp (info->output_bfd)
-            - htab->sec_info[stub_entry->group->link_sec->id].toc_off);
+      off = (elf_gp (info->output_bfd)
+            + htab->sec_info[stub_entry->group->link_sec->id].toc_off);
+      off = targ - off;
 
       if (off + 0x80008000 > 0xffffffff || (off & 7) != 0)
        {
@@ -11473,15 +11858,6 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          return FALSE;
        }
 
-      if (htab->params->plt_stub_align != 0)
-       {
-         unsigned pad = plt_stub_pad (htab, stub_entry, off);
-
-         stub_entry->group->stub_sec->size += pad;
-         stub_entry->stub_offset = stub_entry->group->stub_sec->size;
-         loc += pad;
-       }
-
       r = NULL;
       if (info->emitrelocations)
        {
@@ -11496,7 +11872,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          r[0].r_offset = loc - stub_entry->group->stub_sec->contents;
          if (bfd_big_endian (info->output_bfd))
            r[0].r_offset += 2;
-         r[0].r_addend = dest;
+         r[0].r_addend = targ;
        }
       if (stub_entry->h != NULL
          && (stub_entry->h == htab->tls_get_addr_fd
@@ -11515,7 +11891,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       return FALSE;
     }
 
-  stub_entry->group->stub_sec->size += p - loc;
+  stub_entry->group->stub_sec->size = stub_entry->stub_offset + (p - loc);
 
   if (htab->params->emit_stub_syms)
     {
@@ -11523,9 +11899,15 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       size_t len1, len2;
       char *name;
       const char *const stub_str[] = { "long_branch",
-                                      "long_branch_r2off",
+                                      "long_branch",
+                                      "long_branch",
+                                      "long_branch",
+                                      "plt_branch",
+                                      "plt_branch",
                                       "plt_branch",
-                                      "plt_branch_r2off",
+                                      "plt_branch",
+                                      "plt_call",
+                                      "plt_call",
                                       "plt_call",
                                       "plt_call" };
 
@@ -11567,7 +11949,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
   struct ppc_stub_hash_entry *stub_entry;
   struct bfd_link_info *info;
   struct ppc_link_hash_table *htab;
-  bfd_vma off;
+  bfd_vma targ, off;
   int size;
 
   /* Massage our args to the form they really have.  */
@@ -11578,6 +11960,9 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
   if (htab == NULL)
     return FALSE;
 
+  /* Make a note of the offset within the stubs for this entry.  */
+  stub_entry->stub_offset = stub_entry->group->stub_sec->size;
+
   if (stub_entry->h != NULL
       && stub_entry->h->save_res
       && stub_entry->h->elf.root.type == bfd_link_hash_defined
@@ -11590,12 +11975,12 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       return TRUE;
     }
 
-  if (stub_entry->stub_type == ppc_stub_plt_call
-      || stub_entry->stub_type == ppc_stub_plt_call_r2save)
+  if (stub_entry->stub_type >= ppc_stub_plt_call
+      && stub_entry->stub_type <= ppc_stub_plt_call_both)
     {
       asection *plt;
-      off = stub_entry->plt_ent->plt.offset & ~(bfd_vma) 1;
-      if (off >= (bfd_vma) -2)
+      targ = stub_entry->plt_ent->plt.offset & ~(bfd_vma) 1;
+      if (targ >= (bfd_vma) -2)
        abort ();
       plt = htab->elf.splt;
       if (!htab->elf.dynamic_sections_created
@@ -11607,32 +11992,83 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          else
            plt = htab->pltlocal;
        }
-      off += (plt->output_offset
-             + plt->output_section->vma
-             - elf_gp (info->output_bfd)
-             - htab->sec_info[stub_entry->group->link_sec->id].toc_off);
+      targ += plt->output_offset + plt->output_section->vma;
 
+      if (stub_entry->stub_type >= ppc_stub_plt_call_notoc)
+       {
+         off = (8 + 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)
+           off += 4;
+       }
+      else
+       off = (elf_gp (info->output_bfd)
+              + htab->sec_info[stub_entry->group->link_sec->id].toc_off);
+
+      if (htab->params->plt_stub_align != 0)
+       {
+         unsigned pad = plt_stub_pad (htab, stub_entry, targ - off);
+
+         stub_entry->group->stub_sec->size += pad;
+         stub_entry->stub_offset = stub_entry->group->stub_sec->size;
+         if (stub_entry->stub_type >= ppc_stub_plt_call_notoc)
+           off += pad;
+       }
+
+      off = targ - off;
       size = plt_stub_size (htab, stub_entry, off);
-      if (stub_entry->h != NULL
-         && (stub_entry->h == htab->tls_get_addr_fd
-             || stub_entry->h == htab->tls_get_addr)
-         && htab->params->tls_get_addr_opt
-         && (ALWAYS_EMIT_R2SAVE
-             || stub_entry->stub_type == ppc_stub_plt_call_r2save))
-       stub_entry->group->tls_get_addr_opt_bctrl
-         = stub_entry->group->stub_sec->size + size - 5 * 4;
-
-      if (htab->params->plt_stub_align)
-       size += plt_stub_pad (htab, stub_entry, off);
-      if (info->emitrelocations)
+
+      if (stub_entry->stub_type >= ppc_stub_plt_call_notoc)
        {
-         stub_entry->group->stub_sec->reloc_count
-           += ((PPC_HA (off) != 0)
-               + (htab->opd_abi
-                  ? 2 + (htab->params->plt_static_chain
-                         && PPC_HA (off + 16) == PPC_HA (off))
-                  : 1));
-         stub_entry->group->stub_sec->flags |= SEC_RELOC;
+         /* After the bcl, lr has been modified so we need to emit
+            .eh_frame info saying the return address is in r12.  */
+         unsigned int lr_used = stub_entry->stub_offset + 8;
+         unsigned int delta;
+         if (stub_entry->stub_type > ppc_stub_plt_call_notoc)
+           lr_used += 4;
+         /* 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.  */
+         delta = lr_used - stub_entry->group->lr_restore;
+         stub_entry->group->eh_size += eh_advance_size (delta) + 6;
+         stub_entry->group->lr_restore = lr_used + 8;
+       }
+      else
+       {
+         if (stub_entry->h != NULL
+             && (stub_entry->h == htab->tls_get_addr_fd
+                 || stub_entry->h == htab->tls_get_addr)
+             && htab->params->tls_get_addr_opt
+             && stub_entry->stub_type == ppc_stub_plt_call_r2save)
+           {
+             /* 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 put the EH info specifying
+                that the return address is on the stack *at* the
+                call rather than after it, because the EH info for a
+                call needs to be specified by that point.
+                See libgcc/unwind-dw2.c execute_cfa_program.  */
+             unsigned int lr_used = stub_entry->stub_offset + size - 20;
+             unsigned int delta;
+             /* The eh_frame info will consist of a DW_CFA_advance_loc
+                or variant, DW_CFA_offset_externed_sf, 65, -stackoff,
+                DW_CFA_advance_loc+4, DW_CFA_restore_extended, 65.  */
+             delta = lr_used - stub_entry->group->lr_restore;
+             stub_entry->group->eh_size += eh_advance_size (delta) + 6;
+             stub_entry->group->lr_restore = size - 4;
+           }
+
+         if (info->emitrelocations)
+           {
+             stub_entry->group->stub_sec->reloc_count
+               += ((PPC_HA (off) != 0)
+                   + (htab->opd_abi
+                      ? 2 + (htab->params->plt_static_chain
+                             && PPC_HA (off + 16) == PPC_HA (off))
+                      : 1));
+             stub_entry->group->stub_sec->flags |= SEC_RELOC;
+           }
        }
     }
   else
@@ -11642,12 +12078,12 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       bfd_vma r2off = 0;
       bfd_vma local_off = 0;
 
-      off = (stub_entry->target_value
-            + stub_entry->target_section->output_offset
-            + stub_entry->target_section->output_section->vma);
-      off -= (stub_entry->group->stub_sec->size
-             + stub_entry->group->stub_sec->output_offset
-             + stub_entry->group->stub_sec->output_section->vma);
+      targ = (stub_entry->target_value
+             + stub_entry->target_section->output_offset
+             + stub_entry->target_section->output_section->vma);
+      off = (stub_entry->stub_offset
+            + stub_entry->group->stub_sec->output_offset
+            + stub_entry->group->stub_sec->output_section->vma);
 
       /* Reset the stub type from the plt variant in case we now
         can reach with a shorter stub.  */
@@ -11668,83 +12104,118 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
            size += 4;
          if (PPC_LO (r2off) != 0)
            size += 4;
-         off -= size - 4;
+         off += size - 4;
        }
-
-      local_off = PPC64_LOCAL_ENTRY_OFFSET (stub_entry->other);
-
-      /* If the branch offset is too big, use a ppc_stub_plt_branch.
-        Do the same for -R objects without function descriptors.  */
-      if (off + (1 << 25) >= (bfd_vma) (1 << 26) - local_off
-         || (stub_entry->stub_type == ppc_stub_long_branch_r2off
-             && r2off == 0
-             && htab->sec_info[stub_entry->target_section->id].toc_off == 0))
+      else if (stub_entry->stub_type >= ppc_stub_long_branch_notoc)
        {
-         struct ppc_branch_hash_entry *br_entry;
+         size = 20 + size_offset (targ - (off + 8));
+         if (stub_entry->stub_type > ppc_stub_long_branch_notoc)
+           size += 4;
+         off += size - 4;
+       }
+      off = targ - off;
 
-         br_entry = ppc_branch_hash_lookup (&htab->branch_hash_table,
-                                            stub_entry->root.string + 9,
-                                            TRUE, FALSE);
-         if (br_entry == NULL)
+      if (stub_entry->stub_type >= ppc_stub_long_branch_notoc)
+       {
+         /* After the bcl, lr has been modified so we need to emit
+            .eh_frame info saying the return address is in r12.  */
+         unsigned int lr_used = stub_entry->stub_offset + 8;
+         unsigned int delta;
+         if (stub_entry->stub_type > ppc_stub_long_branch_notoc)
+           lr_used += 4;
+         /* 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.  */
+         delta = lr_used - stub_entry->group->lr_restore;
+         stub_entry->group->eh_size += eh_advance_size (delta) + 6;
+         stub_entry->group->lr_restore = lr_used + 8;
+
+         if (off + (1 << 25) >= (bfd_vma) (1 << 26))
            {
-             _bfd_error_handler (_("can't build branch stub `%s'"),
-                                 stub_entry->root.string);
-             htab->stub_error = TRUE;
-             return FALSE;
+             stub_entry->stub_type += (ppc_stub_plt_branch_notoc
+                                       - ppc_stub_long_branch_notoc);
+             size += 4;
            }
-
-         if (br_entry->iter != htab->stub_iteration)
+       }
+      else
+       {
+         local_off = PPC64_LOCAL_ENTRY_OFFSET (stub_entry->other);
+
+         /* If the branch offset is too big, use a ppc_stub_plt_branch.
+            Do the same for -R objects without function descriptors.  */
+         if ((stub_entry->stub_type == ppc_stub_long_branch_r2off
+              && r2off == 0
+              && htab->sec_info[stub_entry->target_section->id].toc_off == 0)
+             || off + (1 << 25) >= (bfd_vma) (1 << 26) - local_off)
            {
-             br_entry->iter = htab->stub_iteration;
-             br_entry->offset = htab->brlt->size;
-             htab->brlt->size += 8;
+             struct ppc_branch_hash_entry *br_entry;
 
-             if (htab->relbrlt != NULL)
-               htab->relbrlt->size += sizeof (Elf64_External_Rela);
-             else if (info->emitrelocations)
+             br_entry = ppc_branch_hash_lookup (&htab->branch_hash_table,
+                                                stub_entry->root.string + 9,
+                                                TRUE, FALSE);
+             if (br_entry == NULL)
                {
-                 htab->brlt->reloc_count += 1;
-                 htab->brlt->flags |= SEC_RELOC;
+                 _bfd_error_handler (_("can't build branch stub `%s'"),
+                                     stub_entry->root.string);
+                 htab->stub_error = TRUE;
+                 return FALSE;
                }
-           }
 
-         stub_entry->stub_type += ppc_stub_plt_branch - ppc_stub_long_branch;
-         off = (br_entry->offset
-                + htab->brlt->output_offset
-                + htab->brlt->output_section->vma
-                - elf_gp (info->output_bfd)
-                - htab->sec_info[stub_entry->group->link_sec->id].toc_off);
+             if (br_entry->iter != htab->stub_iteration)
+               {
+                 br_entry->iter = htab->stub_iteration;
+                 br_entry->offset = htab->brlt->size;
+                 htab->brlt->size += 8;
 
-         if (info->emitrelocations)
-           {
-             stub_entry->group->stub_sec->reloc_count
-               += 1 + (PPC_HA (off) != 0);
-             stub_entry->group->stub_sec->flags |= SEC_RELOC;
-           }
+                 if (htab->relbrlt != NULL)
+                   htab->relbrlt->size += sizeof (Elf64_External_Rela);
+                 else if (info->emitrelocations)
+                   {
+                     htab->brlt->reloc_count += 1;
+                     htab->brlt->flags |= SEC_RELOC;
+                   }
+               }
 
-         if (stub_entry->stub_type != ppc_stub_plt_branch_r2off)
-           {
-             size = 12;
-             if (PPC_HA (off) != 0)
-               size = 16;
+             targ = (br_entry->offset
+                     + htab->brlt->output_offset
+                     + htab->brlt->output_section->vma);
+             off = (elf_gp (info->output_bfd)
+                    + htab->sec_info[stub_entry->group->link_sec->id].toc_off);
+             off = targ - off;
+
+             if (info->emitrelocations)
+               {
+                 stub_entry->group->stub_sec->reloc_count
+                   += 1 + (PPC_HA (off) != 0);
+                 stub_entry->group->stub_sec->flags |= SEC_RELOC;
+               }
+
+             stub_entry->stub_type
+               += ppc_stub_plt_branch - ppc_stub_long_branch;
+             if (stub_entry->stub_type != ppc_stub_plt_branch_r2off)
+               {
+                 size = 12;
+                 if (PPC_HA (off) != 0)
+                   size = 16;
+               }
+             else
+               {
+                 size = 16;
+                 if (PPC_HA (off) != 0)
+                   size += 4;
+
+                 if (PPC_HA (r2off) != 0)
+                   size += 4;
+                 if (PPC_LO (r2off) != 0)
+                   size += 4;
+               }
            }
-         else
+         else if (info->emitrelocations)
            {
-             size = 16;
-             if (PPC_HA (off) != 0)
-               size += 4;
-
-             if (PPC_HA (r2off) != 0)
-               size += 4;
-             if (PPC_LO (r2off) != 0)
-               size += 4;
+             stub_entry->group->stub_sec->reloc_count += 1;
+             stub_entry->group->stub_sec->flags |= SEC_RELOC;
            }
        }
-      else if (info->emitrelocations)
-       {
-         stub_entry->group->stub_sec->reloc_count += 1;
-         stub_entry->group->stub_sec->flags |= SEC_RELOC;
-       }
     }
 
   stub_entry->group->stub_sec->size += size;
@@ -12151,6 +12622,7 @@ toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec)
 
          r_type = ELF64_R_TYPE (rel->r_info);
          if (r_type != R_PPC64_REL24
+             && r_type != R_PPC64_REL24_NOTOC
              && r_type != R_PPC64_REL14
              && r_type != R_PPC64_REL14_BRTAKEN
              && r_type != R_PPC64_REL14_BRNTAKEN
@@ -12491,7 +12963,9 @@ group_sections (struct bfd_link_info *info,
          group->link_sec = curr;
          group->stub_sec = NULL;
          group->needs_save_res = 0;
-         group->tls_get_addr_opt_bctrl = -1u;
+         group->lr_restore = 0;
+         group->eh_size = 0;
+         group->eh_base = 0;
          group->next = htab->group;
          htab->group = group;
          do
@@ -12542,27 +13016,6 @@ static const unsigned char glink_eh_frame_cie[] =
   DW_CFA_def_cfa, 1, 0                 /* def_cfa: r1 offset 0.  */
 };
 
-static size_t
-stub_eh_frame_size (struct map_stub *group, size_t align)
-{
-  size_t this_size = 17;
-  if (group->tls_get_addr_opt_bctrl != -1u)
-    {
-      unsigned int to_bctrl = group->tls_get_addr_opt_bctrl / 4;
-      if (to_bctrl < 64)
-       this_size += 1;
-      else if (to_bctrl < 256)
-       this_size += 2;
-      else if (to_bctrl < 65536)
-       this_size += 3;
-      else
-       this_size += 5;
-      this_size += 6;
-    }
-  this_size = (this_size + align - 1) & -align;
-  return this_size;
-}
-
 /* Stripping output sections is normally done before dynamic section
    symbols have been allocated.  This function is called later, and
    handles cases like htab->brlt which is mapped to its own output
@@ -12747,6 +13200,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
 
                  /* Only look for stubs on branch instructions.  */
                  if (r_type != R_PPC64_REL24
+                     && r_type != R_PPC64_REL24_NOTOC
                      && r_type != R_PPC64_REL14
                      && r_type != R_PPC64_REL14_BRTAKEN
                      && r_type != R_PPC64_REL14_BRNTAKEN)
@@ -12854,7 +13308,19 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
                                                &plt_ent, destination,
                                                local_off);
 
-                 if (stub_type != ppc_stub_plt_call)
+                 if (r_type == R_PPC64_REL24_NOTOC)
+                   {
+                     if (stub_type == ppc_stub_plt_call)
+                       stub_type = ppc_stub_plt_call_notoc;
+                     else if (stub_type == ppc_stub_long_branch
+                              || (code_sec != NULL
+                                  && code_sec->output_section != NULL
+                                  && (((hash ? hash->elf.other : sym->st_other)
+                                       & STO_PPC64_LOCAL_MASK)
+                                      != 1 << STO_PPC64_LOCAL_BIT)))
+                       stub_type = ppc_stub_long_branch_notoc;
+                   }
+                 else if (stub_type != ppc_stub_plt_call)
                    {
                      /* Check whether we need a TOC adjusting stub.
                         Since the linker pastes together pieces from
@@ -12879,6 +13345,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
 
                  /* __tls_get_addr calls might be eliminated.  */
                  if (stub_type != ppc_stub_plt_call
+                     && stub_type != ppc_stub_plt_call_notoc
                      && hash != NULL
                      && (hash == htab->tls_get_addr
                          || hash == htab->tls_get_addr_fd)
@@ -12926,9 +13393,71 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
                                                     stub_name, FALSE, FALSE);
                  if (stub_entry != NULL)
                    {
-                     /* The proper stub has already been created.  */
+                     enum ppc_stub_type old_type;
+                     /* A stub has already been created, but it may
+                        not be the required type.  We shouldn't be
+                        transitioning from plt_call to long_branch
+                        stubs or vice versa, but we might be
+                        upgrading from plt_call to plt_call_r2save or
+                        from long_branch to long_branch_r2off.  */
                      free (stub_name);
-                     if (stub_type == ppc_stub_plt_call_r2save)
+                     old_type = stub_entry->stub_type;
+                     switch (old_type)
+                       {
+                       default:
+                         abort ();
+
+                       case ppc_stub_save_res:
+                         continue;
+
+                       case ppc_stub_plt_call:
+                       case ppc_stub_plt_call_r2save:
+                       case ppc_stub_plt_call_notoc:
+                       case ppc_stub_plt_call_both:
+                         if (stub_type == ppc_stub_plt_call)
+                           continue;
+                         else if (stub_type == ppc_stub_plt_call_r2save)
+                           {
+                             if (old_type == ppc_stub_plt_call_notoc)
+                               stub_type = ppc_stub_plt_call_both;
+                           }
+                         else if (stub_type == ppc_stub_plt_call_notoc)
+                           {
+                             if (old_type == ppc_stub_plt_call_r2save)
+                               stub_type = ppc_stub_plt_call_both;
+                           }
+                         else
+                           abort ();
+                         break;
+
+                       case ppc_stub_plt_branch:
+                       case ppc_stub_plt_branch_r2off:
+                       case ppc_stub_plt_branch_notoc:
+                       case ppc_stub_plt_branch_both:
+                         old_type += (ppc_stub_long_branch
+                                      - ppc_stub_plt_branch);
+                         /* Fall through.  */
+                       case ppc_stub_long_branch:
+                       case ppc_stub_long_branch_r2off:
+                       case ppc_stub_long_branch_notoc:
+                       case ppc_stub_long_branch_both:
+                         if (stub_type == ppc_stub_long_branch)
+                           continue;
+                         else if (stub_type == ppc_stub_long_branch_r2off)
+                           {
+                             if (old_type == ppc_stub_long_branch_notoc)
+                               stub_type = ppc_stub_long_branch_both;
+                           }
+                         else if (stub_type == ppc_stub_long_branch_notoc)
+                           {
+                             if (old_type == ppc_stub_long_branch_r2off)
+                               stub_type = ppc_stub_long_branch_both;
+                           }
+                         else
+                           abort ();
+                         break;
+                       }
+                     if (old_type < stub_type)
                        stub_entry->stub_type = stub_type;
                      continue;
                    }
@@ -12949,16 +13478,16 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
                    }
 
                  stub_entry->stub_type = stub_type;
-                 if (stub_type != ppc_stub_plt_call
-                     && stub_type != ppc_stub_plt_call_r2save)
+                 if (stub_type >= ppc_stub_plt_call
+                     && stub_type <= ppc_stub_plt_call_both)
                    {
-                     stub_entry->target_value = code_value;
-                     stub_entry->target_section = code_sec;
+                     stub_entry->target_value = sym_value;
+                     stub_entry->target_section = sym_sec;
                    }
                  else
                    {
-                     stub_entry->target_value = sym_value;
-                     stub_entry->target_section = sym_sec;
+                     stub_entry->target_value = code_value;
+                     stub_entry->target_section = code_sec;
                    }
                  stub_entry->h = hash;
                  stub_entry->plt_ent = plt_ent;
@@ -12988,18 +13517,22 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
       /* We may have added some stubs.  Find out the new size of the
         stub sections.  */
       for (group = htab->group; group != NULL; group = group->next)
-       if (group->stub_sec != NULL)
-         {
-           asection *stub_sec = group->stub_sec;
-
-           if (htab->stub_iteration <= STUB_SHRINK_ITER
-               || stub_sec->rawsize < stub_sec->size)
-             /* Past STUB_SHRINK_ITER, rawsize is the max size seen.  */
-             stub_sec->rawsize = stub_sec->size;
-           stub_sec->size = 0;
-           stub_sec->reloc_count = 0;
-           stub_sec->flags &= ~SEC_RELOC;
-         }
+       {
+         group->lr_restore = 0;
+         group->eh_size = 0;
+         if (group->stub_sec != NULL)
+           {
+             asection *stub_sec = group->stub_sec;
+
+             if (htab->stub_iteration <= STUB_SHRINK_ITER
+                 || stub_sec->rawsize < stub_sec->size)
+               /* Past STUB_SHRINK_ITER, rawsize is the max size seen.  */
+               stub_sec->rawsize = stub_sec->size;
+             stub_sec->size = 0;
+             stub_sec->reloc_count = 0;
+             stub_sec->flags &= ~SEC_RELOC;
+           }
+       }
 
       if (htab->stub_iteration <= STUB_SHRINK_ITER
          || htab->brlt->rawsize < htab->brlt->size)
@@ -13030,8 +13563,8 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
          size_t size = 0, align = 4;
 
          for (group = htab->group; group != NULL; group = group->next)
-           if (group->stub_sec != NULL)
-             size += stub_eh_frame_size (group, align);
+           if (group->eh_size != 0)
+             size += (group->eh_size + 17 + align - 1) & -align;
          if (htab->glink != NULL && htab->glink->size != 0)
            size += (24 + align - 1) & -align;
          if (size != 0)
@@ -13078,6 +13611,10 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
       size_t last_fde_len, size, align, pad;
       struct map_stub *group;
 
+      /* It is necessary to at least have a rough outline of the
+        linker generated CIEs and FDEs written before
+        bfd_elf_discard_info is run, in order for these FDEs to be
+        indexed in .eh_frame_hdr.  */
       p = bfd_zalloc (htab->glink_eh_frame->owner, htab->glink_eh_frame->size);
       if (p == NULL)
        return FALSE;
@@ -13092,10 +13629,11 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
       p += last_fde_len + 4;
 
       for (group = htab->group; group != NULL; group = group->next)
-       if (group->stub_sec != NULL)
+       if (group->eh_size != 0)
          {
+           group->eh_base = p - htab->glink_eh_frame->contents;
            last_fde = p;
-           last_fde_len = stub_eh_frame_size (group, align) - 4;
+           last_fde_len = ((group->eh_size + 17 + align - 1) & -align) - 4;
            /* FDE length.  */
            bfd_put_32 (htab->elf.dynobj, last_fde_len, p);
            p += 4;
@@ -13110,39 +13648,9 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
            p += 4;
            /* Augmentation.  */
            p += 1;
-           if (group->tls_get_addr_opt_bctrl != -1u)
-             {
-               unsigned int to_bctrl = group->tls_get_addr_opt_bctrl / 4;
-
-               /* This FDE needs more than just the default.
-                  Describe __tls_get_addr_opt stub LR.  */
-               if (to_bctrl < 64)
-                 *p++ = DW_CFA_advance_loc + to_bctrl;
-               else if (to_bctrl < 256)
-                 {
-                   *p++ = DW_CFA_advance_loc1;
-                   *p++ = to_bctrl;
-                 }
-               else if (to_bctrl < 65536)
-                 {
-                   *p++ = DW_CFA_advance_loc2;
-                   bfd_put_16 (htab->elf.dynobj, to_bctrl, p);
-                   p += 2;
-                 }
-               else
-                 {
-                   *p++ = DW_CFA_advance_loc4;
-                   bfd_put_32 (htab->elf.dynobj, to_bctrl, p);
-                   p += 4;
-                 }
-               *p++ = DW_CFA_offset_extended_sf;
-               *p++ = 65;
-               *p++ = -(STK_LINKER (htab) / 8) & 0x7f;
-               *p++ = DW_CFA_advance_loc + 4;
-               *p++ = DW_CFA_restore_extended;
-               *p++ = 65;
-             }
-           /* Pad.  */
+           /* Make sure we don't have all nops.  This is enough for
+              elf-eh-frame.c to detect the last non-nop opcode.  */
+           p[group->eh_size - 1] = DW_CFA_advance_loc + 1;
            p = last_fde + last_fde_len + 4;
          }
       if (htab->glink != NULL && htab->glink->size != 0)
@@ -13617,14 +14125,19 @@ ppc64_elf_build_stubs (struct bfd_link_info *info,
 
   /* Allocate memory to hold the linker stubs.  */
   for (group = htab->group; group != NULL; group = group->next)
-    if ((stub_sec = group->stub_sec) != NULL
-       && stub_sec->size != 0)
-      {
-       stub_sec->contents = bfd_zalloc (htab->params->stub_bfd, stub_sec->size);
-       if (stub_sec->contents == NULL)
-         return FALSE;
-       stub_sec->size = 0;
-      }
+    {
+      group->eh_size = 0;
+      group->lr_restore = 0;
+      if ((stub_sec = group->stub_sec) != NULL
+         && stub_sec->size != 0)
+       {
+         stub_sec->contents = bfd_zalloc (htab->params->stub_bfd,
+                                          stub_sec->size);
+         if (stub_sec->contents == NULL)
+           return FALSE;
+         stub_sec->size = 0;
+       }
+    }
 
   if (htab->glink != NULL && htab->glink->size != 0)
     {
@@ -13807,6 +14320,55 @@ ppc64_elf_build_stubs (struct bfd_link_info *info,
          }
       }
 
+  if (htab->glink_eh_frame != NULL
+      && htab->glink_eh_frame->size != 0)
+    {
+      bfd_vma val;
+      size_t align = 4;
+
+      p = htab->glink_eh_frame->contents;
+      p += (sizeof (glink_eh_frame_cie) + align - 1) & -align;
+
+      for (group = htab->group; group != NULL; group = group->next)
+       if (group->eh_size != 0)
+         {
+           /* Offset to stub section.  */
+           val = (group->stub_sec->output_section->vma
+                  + group->stub_sec->output_offset);
+           val -= (htab->glink_eh_frame->output_section->vma
+                   + htab->glink_eh_frame->output_offset
+                   + (p + 8 - htab->glink_eh_frame->contents));
+           if (val + 0x80000000 > 0xffffffff)
+             {
+               _bfd_error_handler
+                 (_("%s offset too large for .eh_frame sdata4 encoding"),
+                  group->stub_sec->name);
+               return FALSE;
+             }
+           bfd_put_32 (htab->elf.dynobj, val, p + 8);
+           p += (group->eh_size + 17 + 3) & -4;
+         }
+      if (htab->glink != NULL && htab->glink->size != 0)
+       {
+         /* Offset to .glink.  */
+         val = (htab->glink->output_section->vma
+                + htab->glink->output_offset
+                + 8);
+         val -= (htab->glink_eh_frame->output_section->vma
+                 + htab->glink_eh_frame->output_offset
+                 + (p + 8 - htab->glink_eh_frame->contents));
+         if (val + 0x80000000 > 0xffffffff)
+           {
+             _bfd_error_handler
+               (_("%s offset too large for .eh_frame sdata4 encoding"),
+                htab->glink->name);
+             return FALSE;
+           }
+         bfd_put_32 (htab->elf.dynobj, val, p + 8);
+         p += (24 + align - 1) & -align;
+       }
+    }
+
   for (group = htab->group; group != NULL; group = group->next)
     if ((stub_sec = group->stub_sec) != NULL)
       {
@@ -13838,19 +14400,31 @@ ppc64_elf_build_stubs (struct bfd_link_info *info,
                               "linker stubs in %u groups\n",
                               stub_sec_count),
                     stub_sec_count);
-      sprintf (*stats + len, _("  branch       %lu\n"
-                              "  toc adjust   %lu\n"
-                              "  long branch  %lu\n"
-                              "  long toc adj %lu\n"
-                              "  plt call     %lu\n"
-                              "  plt call toc %lu\n"
-                              "  global entry %lu"),
+      sprintf (*stats + len, _("  branch         %lu\n"
+                              "  branch toc adj %lu\n"
+                              "  branch notoc   %lu\n"
+                              "  branch both    %lu\n"
+                              "  long branch    %lu\n"
+                              "  long toc adj   %lu\n"
+                              "  long notoc     %lu\n"
+                              "  long both      %lu\n"
+                              "  plt call       %lu\n"
+                              "  plt call save  %lu\n"
+                              "  plt call notoc %lu\n"
+                              "  plt call both  %lu\n"
+                              "  global entry   %lu"),
               htab->stub_count[ppc_stub_long_branch - 1],
               htab->stub_count[ppc_stub_long_branch_r2off - 1],
+              htab->stub_count[ppc_stub_long_branch_notoc - 1],
+              htab->stub_count[ppc_stub_long_branch_both - 1],
               htab->stub_count[ppc_stub_plt_branch - 1],
               htab->stub_count[ppc_stub_plt_branch_r2off - 1],
+              htab->stub_count[ppc_stub_plt_branch_notoc - 1],
+              htab->stub_count[ppc_stub_plt_branch_both - 1],
               htab->stub_count[ppc_stub_plt_call - 1],
               htab->stub_count[ppc_stub_plt_call_r2save - 1],
+              htab->stub_count[ppc_stub_plt_call_notoc - 1],
+              htab->stub_count[ppc_stub_plt_call_both - 1],
               htab->stub_count[ppc_stub_global_entry - 1]);
     }
   return TRUE;
@@ -14682,6 +15256,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          /* Fall through.  */
 
        case R_PPC64_REL24:
+       case R_PPC64_REL24_NOTOC:
        case R_PPC64_PLTCALL:
          /* Calls to functions with a different TOC, such as calls to
             shared objects, need to alter the TOC pointer.  This is
@@ -14698,15 +15273,18 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                                           htab);
          if (r_type == R_PPC64_PLTCALL
              && stub_entry != NULL
-             && (stub_entry->stub_type == ppc_stub_plt_call
-                 || stub_entry->stub_type == ppc_stub_plt_call_r2save))
+             && stub_entry->stub_type >= ppc_stub_plt_call
+             && stub_entry->stub_type <= ppc_stub_plt_call_both)
            stub_entry = NULL;
 
          if (stub_entry != NULL
              && (stub_entry->stub_type == ppc_stub_plt_call
                  || stub_entry->stub_type == ppc_stub_plt_call_r2save
+                 || stub_entry->stub_type == ppc_stub_plt_call_both
                  || stub_entry->stub_type == ppc_stub_plt_branch_r2off
-                 || stub_entry->stub_type == ppc_stub_long_branch_r2off))
+                 || stub_entry->stub_type == ppc_stub_plt_branch_both
+                 || stub_entry->stub_type == ppc_stub_long_branch_r2off
+                 || stub_entry->stub_type == ppc_stub_long_branch_both))
            {
              bfd_boolean can_plt_call = FALSE;
 
@@ -14718,6 +15296,11 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                  /* The function doesn't use or change r2.  */
                  can_plt_call = TRUE;
                }
+             else if (r_type == R_PPC64_REL24_NOTOC)
+               {
+                 /* NOTOC calls don't need to restore r2.  */
+                 can_plt_call = TRUE;
+               }
 
              /* All of these stubs may modify r2, so there must be a
                 branch and link followed by a nop.  The nop is
@@ -14799,8 +15382,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
              if (!can_plt_call)
                {
-                 if (stub_entry->stub_type == ppc_stub_plt_call
-                     || stub_entry->stub_type == ppc_stub_plt_call_r2save)
+                 if (stub_entry->stub_type >= ppc_stub_plt_call
+                     && stub_entry->stub_type <= ppc_stub_plt_call_both)
                    info->callbacks->einfo
                      /* xgettext:c-format */
                      (_("%H: call to `%pT' lacks nop, can't restore toc; "
@@ -14818,8 +15401,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                }
 
              if (can_plt_call
-                 && (stub_entry->stub_type == ppc_stub_plt_call
-                     || stub_entry->stub_type == ppc_stub_plt_call_r2save))
+                 && stub_entry->stub_type >= ppc_stub_plt_call
+                 && stub_entry->stub_type <= ppc_stub_plt_call_both)
                unresolved_reloc = FALSE;
            }
 
@@ -14861,6 +15444,28 @@ ppc64_elf_relocate_section (bfd *output_bfd,
            /* Don't use the stub if this branch is in range.  */
            stub_entry = NULL;
 
+         if (stub_entry != NULL
+             && (stub_entry->stub_type == ppc_stub_long_branch_notoc
+                 || stub_entry->stub_type == ppc_stub_long_branch_both
+                 || stub_entry->stub_type == ppc_stub_plt_branch_notoc
+                 || stub_entry->stub_type == ppc_stub_plt_branch_both)
+             && (r_type != R_PPC64_REL24_NOTOC
+                 || ((fdh ? fdh->elf.other : sym->st_other)
+                     & STO_PPC64_LOCAL_MASK) == 1 << STO_PPC64_LOCAL_BIT)
+             && (relocation + addend - from + max_br_offset
+                 < 2 * max_br_offset))
+           stub_entry = NULL;
+
+         if (stub_entry != NULL
+             && (stub_entry->stub_type == ppc_stub_long_branch_r2off
+                 || stub_entry->stub_type == ppc_stub_long_branch_both
+                 || stub_entry->stub_type == ppc_stub_plt_branch_r2off
+                 || stub_entry->stub_type == ppc_stub_plt_branch_both)
+             && r_type == R_PPC64_REL24_NOTOC
+             && (relocation + addend - from + max_br_offset
+                 < 2 * max_br_offset))
+           stub_entry = NULL;
+
          if (stub_entry != NULL)
            {
              /* Munge up the value and addend so that we call the stub
@@ -14880,14 +15485,23 @@ ppc64_elf_relocate_section (bfd *output_bfd,
              addend = 0;
              reloc_dest = DEST_STUB;
 
-             if ((stub_entry->stub_type == ppc_stub_plt_call
-                  || stub_entry->stub_type == ppc_stub_plt_call_r2save)
-                 && (ALWAYS_EMIT_R2SAVE
-                     || stub_entry->stub_type == ppc_stub_plt_call_r2save)
+             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
+                      && (h == htab->tls_get_addr_fd
+                          || h == htab->tls_get_addr)
+                      && 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 (insn != 0)
@@ -14920,7 +15534,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          else if (h != NULL
                   && h->elf.root.type == bfd_link_hash_undefweak
                   && h->elf.dynindx == -1
-                  && r_type == R_PPC64_REL24
+                  && (r_type == R_PPC64_REL24
+                      || r_type == R_PPC64_REL24_NOTOC)
                   && relocation == 0
                   && addend == 0)
            {
@@ -15297,6 +15912,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_REL14_BRNTAKEN:
        case R_PPC64_REL14_BRTAKEN:
        case R_PPC64_REL24:
+       case R_PPC64_REL24_NOTOC:
          break;
 
        case R_PPC64_TPREL16:
@@ -16285,62 +16901,14 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
                                       NULL))
     return FALSE;
 
-  if (htab->glink_eh_frame != NULL
-      && htab->glink_eh_frame->size != 0)
-    {
-      bfd_vma val;
-      bfd_byte *p;
-      struct map_stub *group;
-      size_t align = 4;
-
-      p = htab->glink_eh_frame->contents;
-      p += (sizeof (glink_eh_frame_cie) + align - 1) & -align;
-
-      for (group = htab->group; group != NULL; group = group->next)
-       if (group->stub_sec != NULL)
-         {
-           /* Offset to stub section.  */
-           val = (group->stub_sec->output_section->vma
-                  + group->stub_sec->output_offset);
-           val -= (htab->glink_eh_frame->output_section->vma
-                   + htab->glink_eh_frame->output_offset
-                   + (p + 8 - htab->glink_eh_frame->contents));
-           if (val + 0x80000000 > 0xffffffff)
-             {
-               _bfd_error_handler
-                 (_("%s offset too large for .eh_frame sdata4 encoding"),
-                  group->stub_sec->name);
-               return FALSE;
-             }
-           bfd_put_32 (dynobj, val, p + 8);
-           p += stub_eh_frame_size (group, align);
-         }
-      if (htab->glink != NULL && htab->glink->size != 0)
-       {
-         /* Offset to .glink.  */
-         val = (htab->glink->output_section->vma
-                + htab->glink->output_offset
-                + 8);
-         val -= (htab->glink_eh_frame->output_section->vma
-                 + htab->glink_eh_frame->output_offset
-                 + (p + 8 - htab->glink_eh_frame->contents));
-         if (val + 0x80000000 > 0xffffffff)
-           {
-             _bfd_error_handler
-               (_("%s offset too large for .eh_frame sdata4 encoding"),
-                htab->glink->name);
-             return FALSE;
-           }
-         bfd_put_32 (dynobj, val, p + 8);
-         p += (24 + align - 1) & -align;
-       }
 
-      if (htab->glink_eh_frame->sec_info_type == SEC_INFO_TYPE_EH_FRAME
-         && !_bfd_elf_write_section_eh_frame (output_bfd, info,
-                                              htab->glink_eh_frame,
-                                              htab->glink_eh_frame->contents))
-       return FALSE;
-    }
+  if (htab->glink_eh_frame != NULL
+      && htab->glink_eh_frame->size != 0
+      && htab->glink_eh_frame->sec_info_type == SEC_INFO_TYPE_EH_FRAME
+      && !_bfd_elf_write_section_eh_frame (output_bfd, info,
+                                          htab->glink_eh_frame,
+                                          htab->glink_eh_frame->contents))
+    return FALSE;
 
   /* We need to handle writing out multiple GOT sections ourselves,
      since we didn't add them to DYNOBJ.  We know dynobj is the first
This page took 0.044804 seconds and 4 git commands to generate.