R_PPC64_REL24_NOTOC support
authorAlan Modra <amodra@gmail.com>
Fri, 3 Aug 2018 07:11:22 +0000 (16:41 +0930)
committerAlan Modra <amodra@gmail.com>
Sun, 5 Aug 2018 02:41:51 +0000 (12:11 +0930)
R_PPC64_REL24_NOTOC is used on calls like "bl foo@notoc" to tell the
linker that linkage stubs for PLT calls or long branches can't use r2
for pic addressing.  Instead, new stubs that generate pc-relative
addresses are used.  One complication is that pc-relative offsets to
the PLT may need to be 64-bit in large programs, in contrast to the
toc-relative addressing used by older PLT linkage stubs where a 32-bit
offset is sufficient until the PLT itself exceeds 2G in size.

.eh_frame info to cover the _notoc stubs is yet to be implemented.

bfd/
* elf64-ppc.c (ADDI_R12_R11, ADDI_R12_R12, LIS_R12),
(ADDIS_R12_R11, ORIS_R12_R12_0, ORI_R12_R12_0),
(SLDI_R12_R12_32, LDX_R12_R11_R12, ADD_R12_R11_R12): Define.
(ppc64_elf_howto_raw): Add R_PPC64_REL24_NOTOC entry.
(ppc64_elf_reloc_type_lookup): Support R_PPC64_REL24_NOTOC.
(ppc_stub_type): Add ppc_stub_long_branch_notoc,
ppc_stub_long_branch_both, ppc_stub_plt_branch_notoc,
ppc_stub_plt_branch_both, ppc_stub_plt_call_notoc, and
ppc_stub_plt_call_both.
(is_branch_reloc): Add R_PPC64_REL24_NOTOC.
(build_offset, size_offset): New functions.
(plt_stub_size): Support plt_call_notoc and plt_call_both.
(ppc_build_one_stub, ppc_size_one_stub): Support new stubs.
(toc_adjusting_stub_needed): Handle R_PPC64_REL24_NOTOC.
(ppc64_elf_size_stubs): Likewise, and new stubs.
(ppc64_elf_build_stubs, ppc64_elf_relocate_section): Likewise.
* reloc.c: Add BFD_RELOC_PPC64_REL24_NOTOC.
* bfd-in2.h: Regenerate.
* libbfd.h: Regenerate.
gas/
* config/tc-ppc.c (ppc_elf_suffix): Support @notoc.
(ppc_force_relocation, ppc_fix_adjustable): Handle REL24_NOTOC.
ld/
* testsuite/ld-powerpc/ext.d,
* testsuite/ld-powerpc/ext.s,
* testsuite/ld-powerpc/ext.lnk,
* testsuite/ld-powerpc/notoc.d,
* testsuite/ld-powerpc/notoc.s: New tests.
* testsuite/ld-powerpc/powerpc.exp: Run them.

14 files changed:
bfd/ChangeLog
bfd/bfd-in2.h
bfd/elf64-ppc.c
bfd/libbfd.h
bfd/reloc.c
gas/ChangeLog
gas/config/tc-ppc.c
ld/ChangeLog
ld/testsuite/ld-powerpc/ext.d [new file with mode: 0644]
ld/testsuite/ld-powerpc/ext.lnk [new file with mode: 0644]
ld/testsuite/ld-powerpc/ext.s [new file with mode: 0644]
ld/testsuite/ld-powerpc/notoc.d [new file with mode: 0644]
ld/testsuite/ld-powerpc/notoc.s [new file with mode: 0644]
ld/testsuite/ld-powerpc/powerpc.exp

index e23f7470e0aa21f83181bbfe34d22beaa3131ad4..95fe0eef0715f22637f0790715e890e3d0ff83e4 100644 (file)
@@ -1,3 +1,25 @@
+2018-08-05  Alan Modra  <amodra@gmail.com>
+
+       * elf64-ppc.c (ADDI_R12_R11, ADDI_R12_R12, LIS_R12),
+       (ADDIS_R12_R11, ORIS_R12_R12_0, ORI_R12_R12_0),
+       (SLDI_R12_R12_32, LDX_R12_R11_R12, ADD_R12_R11_R12): Define.
+       (ppc64_elf_howto_raw): Add R_PPC64_REL24_NOTOC entry.
+       (ppc64_elf_reloc_type_lookup): Support R_PPC64_REL24_NOTOC.
+       (ppc_stub_type): Add ppc_stub_long_branch_notoc,
+       ppc_stub_long_branch_both, ppc_stub_plt_branch_notoc,
+       ppc_stub_plt_branch_both, ppc_stub_plt_call_notoc, and
+       ppc_stub_plt_call_both.
+       (is_branch_reloc): Add R_PPC64_REL24_NOTOC.
+       (build_offset, size_offset): New functions.
+       (plt_stub_size): Support plt_call_notoc and plt_call_both.
+       (ppc_build_one_stub, ppc_size_one_stub): Support new stubs.
+       (toc_adjusting_stub_needed): Handle R_PPC64_REL24_NOTOC.
+       (ppc64_elf_size_stubs): Likewise, and new stubs.
+       (ppc64_elf_build_stubs, ppc64_elf_relocate_section): Likewise.
+       * reloc.c: Add BFD_RELOC_PPC64_REL24_NOTOC.
+       * bfd-in2.h: Regenerate.
+       * libbfd.h: Regenerate.
+
 2018-08-05  Alan Modra  <amodra@gmail.com>
 
        * elf64-ppc.c (ppc_build_one_stub): Lose "_r2off" in stub symbols.
index 7105850ff55fc10088a6e6ab8c84f98d933b1533..58afff24c9f61b8af1d36a86f31a78f3c7be1cb0 100644 (file)
@@ -3457,6 +3457,7 @@ instruction.  */
   BFD_RELOC_PPC64_ADDR16_HIGHA,
   BFD_RELOC_PPC64_ADDR64_LOCAL,
   BFD_RELOC_PPC64_ENTRY,
+  BFD_RELOC_PPC64_REL24_NOTOC,
 
 /* PowerPC and PowerPC64 thread-local storage relocations.  */
   BFD_RELOC_PPC_TLS,
index 634e537e47ec29c98d3ac41da2169e2501827596..008d352b242dd0e81e3109664e11b327c8e974bd 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
 };
@@ -5478,6 +5584,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 +5955,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 +10865,106 @@ 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;
+}
+
 /* 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 +10992,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;
@@ -11422,6 +11639,78 @@ 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;
+      break;
+
     case ppc_stub_plt_call:
     case ppc_stub_plt_call_r2save:
       if (stub_entry->h != NULL
@@ -11513,9 +11802,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",
+                                      "long_branch",
                                       "long_branch",
                                       "plt_branch",
                                       "plt_branch",
+                                      "plt_branch",
+                                      "plt_branch",
+                                      "plt_call",
+                                      "plt_call",
                                       "plt_call",
                                       "plt_call" };
 
@@ -11583,8 +11878,8 @@ 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;
       targ = stub_entry->plt_ent->plt.offset & ~(bfd_vma) 1;
@@ -11602,38 +11897,52 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
        }
       targ += plt->output_offset + plt->output_section->vma;
 
-      off = (elf_gp (info->output_bfd)
-            + htab->sec_info[stub_entry->group->link_sec->id].toc_off);
-      off = targ - off;
+      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, off);
+         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->stub_offset + size - 5 * 4;
-
-      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;
+         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->stub_offset + size - 5 * 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
@@ -11671,83 +11980,103 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
            size += 4;
          off += size - 4;
        }
+      else if (stub_entry->stub_type >= ppc_stub_long_branch_notoc)
+       {
+         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;
 
-      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))
+      if (stub_entry->stub_type >= ppc_stub_long_branch_notoc)
        {
-         struct ppc_branch_hash_entry *br_entry;
-
-         br_entry = ppc_branch_hash_lookup (&htab->branch_hash_table,
-                                            stub_entry->root.string + 9,
-                                            TRUE, FALSE);
-         if (br_entry == NULL)
+         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;
-         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 (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;
@@ -12154,6 +12483,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
@@ -12750,6 +13080,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)
@@ -12857,7 +13188,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
@@ -12882,6 +13225,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)
@@ -12929,9 +13273,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;
                    }
@@ -12952,16 +13358,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;
@@ -13841,19 +14247,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;
@@ -14685,6 +15103,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
@@ -14701,15 +15120,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;
 
@@ -14721,6 +15143,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
@@ -14802,8 +15229,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; "
@@ -14821,8 +15248,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;
            }
 
@@ -14864,6 +15291,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
@@ -14883,14 +15332,19 @@ 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)
                  && 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)
@@ -14923,7 +15377,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)
            {
@@ -15300,6 +15755,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:
index cd4b096ba88dbc17efb68e0b2a9cd25db94cd869..6c1e1ea9a597fcac1d33ceef2c4f6b887077b524 100644 (file)
@@ -1469,6 +1469,7 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_PPC64_ADDR16_HIGHA",
   "BFD_RELOC_PPC64_ADDR64_LOCAL",
   "BFD_RELOC_PPC64_ENTRY",
+  "BFD_RELOC_PPC64_REL24_NOTOC",
   "BFD_RELOC_PPC_TLS",
   "BFD_RELOC_PPC_TLSGD",
   "BFD_RELOC_PPC_TLSLD",
index c8ddb2f060095e101bf1fcca63b0736b60716df1..b63473e12aee566b99b3d58d97ef5898a7848352 100644 (file)
@@ -3011,6 +3011,8 @@ ENUMX
   BFD_RELOC_PPC64_ADDR64_LOCAL
 ENUMX
   BFD_RELOC_PPC64_ENTRY
+ENUMX
+  BFD_RELOC_PPC64_REL24_NOTOC
 ENUMDOC
   Power(rs6000) and PowerPC relocations.
 
index b704d8c8744bc529e050fdc0cc8e960095b852f0..94b496bce933b8a52f4de1828eacdc0c31f3324d 100644 (file)
@@ -1,3 +1,8 @@
+2018-08-05  Alan Modra  <amodra@gmail.com>
+
+       * config/tc-ppc.c (ppc_elf_suffix): Support @notoc.
+       (ppc_force_relocation, ppc_fix_adjustable): Handle REL24_NOTOC.
+
 2018-08-03  Dimitar Dimitrov  <dimitar@dinux.eu>
 
        * config/tc-pru.c (pru_regname_to_dw2regnum): Return the starting HW
index eaa866dbd291afe5f0cd076eaac64b5d2178e624..6135cb40f93d9b5f5f52477befdca623e958a4d1 100644 (file)
@@ -2085,6 +2085,7 @@ ppc_elf_suffix (char **str_p, expressionS *exp_p)
     MAP64 ("tprel@highera",    BFD_RELOC_PPC64_TPREL16_HIGHERA),
     MAP64 ("tprel@highest",    BFD_RELOC_PPC64_TPREL16_HIGHEST),
     MAP64 ("tprel@highesta",   BFD_RELOC_PPC64_TPREL16_HIGHESTA),
+    MAP64 ("notoc",            BFD_RELOC_PPC64_REL24_NOTOC),
     { (char *) 0, 0, 0, 0,     BFD_RELOC_NONE }
   };
 
@@ -6416,6 +6417,7 @@ ppc_force_relocation (fixS *fix)
     case BFD_RELOC_PPC_BA26:
     case BFD_RELOC_PPC_B16:
     case BFD_RELOC_PPC_BA16:
+    case BFD_RELOC_PPC64_REL24_NOTOC:
       /* All branch fixups targeting a localentry symbol must
          force a relocation.  */
       if (fix->fx_addsy)
@@ -6454,6 +6456,7 @@ ppc_fix_adjustable (fixS *fix)
     case BFD_RELOC_PPC_B16_BRNTAKEN:
     case BFD_RELOC_PPC_BA16_BRTAKEN:
     case BFD_RELOC_PPC_BA16_BRNTAKEN:
+    case BFD_RELOC_PPC64_REL24_NOTOC:
       if (fix->fx_addsy)
        {
          asymbol *bfdsym = symbol_get_bfdsym (fix->fx_addsy);
index b32879df0a385c8bc1104aabf31415b0f82f7723..62ba53007777120222aa03deb9c7911e5648a81d 100644 (file)
@@ -1,3 +1,12 @@
+2018-08-05  Alan Modra  <amodra@gmail.com>
+
+       * testsuite/ld-powerpc/ext.d,
+       * testsuite/ld-powerpc/ext.s,
+       * testsuite/ld-powerpc/ext.lnk,
+       * testsuite/ld-powerpc/notoc.d,
+       * testsuite/ld-powerpc/notoc.s: New tests.
+       * testsuite/ld-powerpc/powerpc.exp: Run them.
+
 2018-08-05  Alan Modra  <amodra@gmail.com>
 
        * testsuite/ld-powerpc/elfv2exe.d: Adjust for stub symbol change.
diff --git a/ld/testsuite/ld-powerpc/ext.d b/ld/testsuite/ld-powerpc/ext.d
new file mode 100644 (file)
index 0000000..61527f9
--- /dev/null
@@ -0,0 +1,17 @@
+#source: ext.s
+#as: -a64
+#objdump: -dr
+#target: powerpc64*-*-*
+# Just assembles an object for notoc.d
+
+.*
+
+Disassembly of section \.text:
+
+0+ <ext>:
+   0:  (00 00 4c 3c|3c 4c 00 00)       addis   r2,r12,0
+                       (0|2): R_PPC64_REL16_HA \.TOC\.(|\+0x2)
+   4:  (00 00 42 38|38 42 00 00)       addi    r2,r2,0
+                       (4|6): R_PPC64_REL16_LO \.TOC\.\+0x(4|6)
+   8:  (00 00 00 60|60 00 00 00)       nop
+   c:  (20 00 80 4e|4e 80 00 20)       blr
diff --git a/ld/testsuite/ld-powerpc/ext.lnk b/ld/testsuite/ld-powerpc/ext.lnk
new file mode 100644 (file)
index 0000000..62c4a23
--- /dev/null
@@ -0,0 +1,6 @@
+SECTIONS
+{
+  . = 0x8000000000000000;
+  .text.ext : { tmpdir/ext.o(.text) }
+}
+INSERT BEFORE .stab;
diff --git a/ld/testsuite/ld-powerpc/ext.s b/ld/testsuite/ld-powerpc/ext.s
new file mode 100644 (file)
index 0000000..dd51d8f
--- /dev/null
@@ -0,0 +1,9 @@
+ .text
+ .globl ext
+ext:
+0:
+ addis 2,12,.TOC.-0b@ha
+ addi 2,2,.TOC.-0b@l
+ .localentry ext,.-0b
+ nop
+ blr
diff --git a/ld/testsuite/ld-powerpc/notoc.d b/ld/testsuite/ld-powerpc/notoc.d
new file mode 100644 (file)
index 0000000..97e7274
--- /dev/null
@@ -0,0 +1,102 @@
+#source: notoc.s
+#as: -a64
+#ld: --no-plt-localentry -T ext.lnk
+#objdump: -dr
+#target: powerpc64*-*-*
+
+.*
+
+Disassembly of section \.text:
+
+.* <.*\.long_branch\.f1>:
+.*:    (18 00 41 f8|f8 41 00 18)       std     r2,24\(r1\)
+.*:    (7c 00 00 48|48 00 00 7c)       b       .* <f1>
+
+.* <.*\.long_branch\.g1>:
+.*:    (18 00 41 f8|f8 41 00 18)       std     r2,24\(r1\)
+.*:    (8c 00 00 48|48 00 00 8c)       b       .* <g1>
+
+.* <.*\.plt_branch\.ext>:
+.*:    (a6 02 88 7d|7d 88 02 a6)       mflr    r12
+.*:    (05 00 9f 42|42 9f 00 05)       bcl     .*
+.*:    (a6 02 68 7d|7d 68 02 a6)       mflr    r11
+.*:    (a6 03 88 7d|7d 88 03 a6)       mtlr    r12
+.*:    (ff 7f 80 3d|3d 80 7f ff)       lis     r12,32767
+.*:    (ff ff 8c 61|61 8c ff ff)       ori     r12,r12,65535
+.*:    (c6 07 9c 79|79 9c 07 c6)       rldicr  r28,r12,32,31
+.*:    (ff ef 8c 65|65 8c ef ff)       oris    r12,r12,61439
+.*:    (28 ff 8c 61|61 8c ff 28)       ori     r12,r12,65320
+.*:    (14 62 8b 7d|7d 8b 62 14)       add     r12,r11,r12
+.*:    (a6 03 89 7d|7d 89 03 a6)       mtctr   r12
+.*:    (20 04 80 4e|4e 80 04 20)       bctr
+
+.* <.*\.long_branch\.f2>:
+.*:    (a6 02 88 7d|7d 88 02 a6)       mflr    r12
+.*:    (05 00 9f 42|42 9f 00 05)       bcl     .*
+.*:    (a6 02 68 7d|7d 68 02 a6)       mflr    r11
+.*:    (a6 03 88 7d|7d 88 03 a6)       mtlr    r12
+.*:    (64 00 8b 39|39 8b 00 64)       addi    r12,r11,100
+.*:    (58 00 00 48|48 00 00 58)       b       .* <f2>
+
+.* <.*\.long_branch\.g2>:
+.*:    (a6 02 88 7d|7d 88 02 a6)       mflr    r12
+.*:    (05 00 9f 42|42 9f 00 05)       bcl     .*
+.*:    (a6 02 68 7d|7d 68 02 a6)       mflr    r11
+.*:    (a6 03 88 7d|7d 88 03 a6)       mtlr    r12
+.*:    (80 00 8b 39|39 8b 00 80)       addi    r12,r11,128
+.*:    (74 00 00 48|48 00 00 74)       b       .* <g2>
+       \.\.\.
+
+.* <f1>:
+.*:    (01 00 00 48|48 00 00 01)       bl      .* <f1>
+.*:    (bd ff ff 4b|4b ff ff bd)       bl      .* <.*\.long_branch\.f2>
+.*:    (11 00 00 48|48 00 00 11)       bl      .* <g1>
+.*:    (cd ff ff 4b|4b ff ff cd)       bl      .* <.*\.long_branch\.g2>
+.*:    (81 ff ff 4b|4b ff ff 81)       bl      .* <.*\.plt_branch\.ext>
+.*:    (20 00 80 4e|4e 80 00 20)       blr
+
+.* <g1>:
+.*:    (a9 ff ff 4b|4b ff ff a9)       bl      .* <.*\.long_branch\.f2>
+.*:    (e5 ff ff 4b|4b ff ff e5)       bl      .* <f1>
+.*:    (b9 ff ff 4b|4b ff ff b9)       bl      .* <.*\.long_branch\.g2>
+.*:    (f5 ff ff 4b|4b ff ff f5)       bl      .* <g1>
+.*:    (20 00 80 4e|4e 80 00 20)       blr
+
+.* <f2>:
+.*:    (02 10 40 3c|3c 40 10 02)       lis     r2,4098
+.*:    (00 90 42 38|38 42 90 00)       addi    r2,r2,-28672
+.*:    (4d ff ff 4b|4b ff ff 4d)       bl      .* <.*\.long_branch\.f1>
+.*:    (18 00 41 e8|e8 41 00 18)       ld      r2,24\(r1\)
+.*:    (f9 ff ff 4b|4b ff ff f9)       bl      .* <f2\+0x8>
+.*:    (00 00 00 60|60 00 00 00)       nop
+.*:    (45 ff ff 4b|4b ff ff 45)       bl      .* <.*\.long_branch\.g1>
+.*:    (18 00 41 e8|e8 41 00 18)       ld      r2,24\(r1\)
+.*:    (1d 00 00 48|48 00 00 1d)       bl      .* <g2\+0x8>
+.*:    (00 00 00 60|60 00 00 00)       nop
+.*:    (3d ff ff 4b|4b ff ff 3d)       bl      .* <.*\.plt_branch\.ext>
+.*:    (00 00 00 60|60 00 00 00)       nop
+.*:    (20 00 80 4e|4e 80 00 20)       blr
+
+.* <g2>:
+.*:    (02 10 40 3c|3c 40 10 02)       lis     r2,4098
+.*:    (00 90 42 38|38 42 90 00)       addi    r2,r2,-28672
+.*:    (cd ff ff 4b|4b ff ff cd)       bl      .* <f2\+0x8>
+.*:    (00 00 00 60|60 00 00 00)       nop
+.*:    (11 ff ff 4b|4b ff ff 11)       bl      .* <.*\.long_branch\.f1>
+.*:    (18 00 41 e8|e8 41 00 18)       ld      r2,24\(r1\)
+.*:    (f1 ff ff 4b|4b ff ff f1)       bl      .* <g2\+0x8>
+.*:    (00 00 00 60|60 00 00 00)       nop
+.*:    (09 ff ff 4b|4b ff ff 09)       bl      .* <.*\.long_branch\.g1>
+.*:    (18 00 41 e8|e8 41 00 18)       ld      r2,24\(r1\)
+.*:    (20 00 80 4e|4e 80 00 20)       blr
+
+.* <_start>:
+.*:    (00 00 00 48|48 00 00 00)       b       .* <_start>
+
+Disassembly of section \.text\.ext:
+
+8000000000000000 <ext>:
+8000000000000000:      (02 10 40 3c|3c 40 10 02)       lis     r2,4098
+8000000000000004:      (00 90 42 38|38 42 90 00)       addi    r2,r2,-28672
+8000000000000008:      (00 00 00 60|60 00 00 00)       nop
+800000000000000c:      (20 00 80 4e|4e 80 00 20)       blr
diff --git a/ld/testsuite/ld-powerpc/notoc.s b/ld/testsuite/ld-powerpc/notoc.s
new file mode 100644 (file)
index 0000000..8c620df
--- /dev/null
@@ -0,0 +1,56 @@
+ .text
+ .globl f1, f2, g1, g2, _start
+ .weak ext
+ .abiversion 2
+
+f1:
+ .localentry f1,1
+ bl f1@notoc
+ bl f2@notoc
+ bl g1@notoc
+ bl g2@notoc
+ bl ext@notoc
+ blr
+
+g1:
+ .localentry g1,1
+ bl f2@notoc
+ bl f1@notoc
+ bl g2@notoc
+ bl g1@notoc
+ blr
+
+f2:
+0:
+ addis 2,12,.TOC.-0b@ha
+ addi 2,2,.TOC.-0b@l
+ .localentry f2,.-0b
+ bl f1
+ nop
+ bl f2
+ nop
+ bl g1
+ nop
+ bl g2
+ nop
+ bl ext
+ nop
+ blr
+
+g2:
+0:
+ addis 2,12,.TOC.-0b@ha
+ addi 2,2,.TOC.-0b@l
+ .localentry g2,.-0b
+ bl f2
+ nop
+ bl f1
+ nop
+ bl g2
+ nop
+ bl g1
+ nop
+ blr
+
+_start:
+ b _start
index ac563eba14208cfd788d8e1e6bbc5e91d968b192..c4ab3decb87c13165274ff1b16d31fc14d52259c 100644 (file)
@@ -331,6 +331,8 @@ if [ supports_ppc64 ] then {
     run_dump_test "dotsym2"
     run_dump_test "dotsym3"
     run_dump_test "dotsym4"
+    run_dump_test "ext"
+    run_dump_test "notoc"
 }
 
 run_dump_test "tlsld32"