PowerPC PLT speculative execution barriers
authorAlan Modra <amodra@gmail.com>
Wed, 17 Jan 2018 03:49:08 +0000 (14:19 +1030)
committerAlan Modra <amodra@gmail.com>
Wed, 17 Jan 2018 08:22:57 +0000 (18:52 +1030)
Spectre variant 2 mitigation for PowerPC and PowerPC64.

bfd/
* elf32-ppc.c (GLINK_ENTRY_SIZE): Handle speculation barrier.
(CRSETEQ, BEQCTRM): Define.
(is_nonpic_glink_stub): Don't check bctr.
(ppc_elf_link_hash_table_create): Init new ppc_elf_params field.
(ppc_elf_relax_section): Size speculation barrier.
(output_bctr): New function.
(write_glink_stub): Use output_bctr.
(ppc_elf_relocate_section): Use output_bctr for long branch stub.
(ppc_elf_finish_dynamic_symbol): Likewise.
(ppc_elf_finish_dynamic_sections): Use output_bctr.
* elf32-ppc.h (struct ppc_elf_params): Add speculate_indirect_jumps.
* elf64-ppc.c (CRSETEQ, BEQCTRM, BEQCTRLM): Define.
(GLINK_PLTRESOLVE_SIZE): Size speculation barrier.
(size_global_entry_stubs): Handle speculation barrier sizing.
(plt_stub_size): Likewise.
(output_bctr): New function.
(build_plt_stub, build_tls_get_addr_stub): Output speculation
barrier.
(ppc_build_one_stub): Likewise for ppc_stub_plt_branch.
(ppc_size_one_stub): Size speculation barrier in ppc_stub_plt_branch.
(build_global_entry_stubs): Output speculation barrier.
(ppc64_elf_build_stubs): Likewise in __glink_PLTresolve stub.
* elf64-ppc.h (struct ppc64_elf_params): Add speculate_indirect_jumps.
gold/
* options.h (speculate_indirect_jumps): New option.
* powerpc.cc (beqctrm, beqctrlm, crseteq): New insn constants.
(output_bctr): New function.
(Stub_table::plt_call_size): Add space for speculation barrier.
(Stub_table::branch_stub_size): Likewise.
(Output_data_glink::pltresolve_size): Likewise.
(Stub_table::do_write): Output speculation barriers.
ld/
* emultempl/ppc32elf.em (params): Init new field.
(OPTION_SPECULATE_INDIRECT_JUMPS): Define.
(OPTION_NO_SPECULATE_INDIRECT_JUMPS): Define.
(PARSE_AND_LIST_LONGOPTS): Handle new options.
(PARSE_AND_LIST_ARGS_CASES): Likewise.
(PARSE_AND_LIST_OPTIONS): Likewise.
* emultempl/ppc64elf.em (params): Init new field.
(OPTION_SPECULATE_INDIRECT_JUMPS): Define.
(OPTION_NO_SPECULATE_INDIRECT_JUMPS): Define.
(PARSE_AND_LIST_LONGOPTS): Handle --speculate-indirect-jumps.
(PARSE_AND_LIST_OPTIONS): Likewise.
(PARSE_AND_LIST_ARGS_CASES): Likewise.
* ld.texinfo (--no-plt-thread-safe): Correct itemx.
(--speculate-indirect-jumps): Document.
* testsuite/ld-powerpc/elfv2exe.d,
* testsuite/ld-powerpc/elfv2so.d,
* testsuite/ld-powerpc/relbrlt.d,
* testsuite/ld-powerpc/powerpc.exp: Disable plt alignment and
speculation barriers on various tests.

16 files changed:
bfd/ChangeLog
bfd/elf32-ppc.c
bfd/elf32-ppc.h
bfd/elf64-ppc.c
bfd/elf64-ppc.h
gold/ChangeLog
gold/options.h
gold/powerpc.cc
ld/ChangeLog
ld/emultempl/ppc32elf.em
ld/emultempl/ppc64elf.em
ld/ld.texinfo
ld/testsuite/ld-powerpc/elfv2exe.d
ld/testsuite/ld-powerpc/elfv2so.d
ld/testsuite/ld-powerpc/powerpc.exp
ld/testsuite/ld-powerpc/relbrlt.d

index b922b73..cec6b16 100644 (file)
@@ -1,5 +1,31 @@
 2018-01-17  Alan Modra  <amodra@gmail.com>
 
+       * elf32-ppc.c (GLINK_ENTRY_SIZE): Handle speculation barrier.
+       (CRSETEQ, BEQCTRM): Define.
+       (is_nonpic_glink_stub): Don't check bctr.
+       (ppc_elf_link_hash_table_create): Init new ppc_elf_params field.
+       (ppc_elf_relax_section): Size speculation barrier.
+       (output_bctr): New function.
+       (write_glink_stub): Use output_bctr.
+       (ppc_elf_relocate_section): Use output_bctr for long branch stub.
+       (ppc_elf_finish_dynamic_symbol): Likewise.
+       (ppc_elf_finish_dynamic_sections): Use output_bctr.
+       * elf32-ppc.h (struct ppc_elf_params): Add speculate_indirect_jumps.
+       * elf64-ppc.c (CRSETEQ, BEQCTRM, BEQCTRLM): Define.
+       (GLINK_PLTRESOLVE_SIZE): Size speculation barrier.
+       (size_global_entry_stubs): Handle speculation barrier sizing.
+       (plt_stub_size): Likewise.
+       (output_bctr): New function.
+       (build_plt_stub, build_tls_get_addr_stub): Output speculation
+       barrier.
+       (ppc_build_one_stub): Likewise for ppc_stub_plt_branch.
+       (ppc_size_one_stub): Size speculation barrier in ppc_stub_plt_branch.
+       (build_global_entry_stubs): Output speculation barrier.
+       (ppc64_elf_build_stubs): Likewise in __glink_PLTresolve stub.
+       * elf64-ppc.h (struct ppc64_elf_params): Add speculate_indirect_jumps.
+
+2018-01-17  Alan Modra  <amodra@gmail.com>
+
        * elf32-ppc.c (GLINK_ENTRY_SIZE): Add parameters, handle
        __tls_get_addr_opt, and alignment sizing.
        (TLS_GET_ADDR_GLINK_SIZE): Delete.
index efdaf04..73701d4 100644 (file)
@@ -69,7 +69,7 @@ static bfd_reloc_status_type ppc_elf_unhandled_reloc
 /* For new-style .glink and .plt.  */
 #define GLINK_PLTRESOLVE 16*4
 #define GLINK_ENTRY_SIZE(htab, h)                                      \
-  ((4*4                                                                        \
+  (((!htab->params->speculate_indirect_jumps ? 6*4 : 4*4)                      \
     + (h != NULL                                                       \
        && h == htab->tls_get_addr                                      \
        && !htab->params->no_tls_get_addr_opt ? 8*4 : 0)                        \
@@ -155,6 +155,8 @@ static const bfd_vma ppc_elf_vxworks_pic_plt0_entry
 #define BA             0x48000002
 #define BCL_20_31      0x429f0005
 #define BCTR           0x4e800420
+#define CRSETEQ                0x4c421242
+#define BEQCTRM                0x4dc20420
 #define BEQLR          0x4d820020
 #define CMPWI_11_0     0x2c0b0000
 #define LIS_11         0x3d600000
@@ -2878,15 +2880,14 @@ ppc_elf_final_write_processing (bfd *abfd, bfd_boolean linker ATTRIBUTE_UNUSED)
 static bfd_boolean
 is_nonpic_glink_stub (bfd *abfd, asection *glink, bfd_vma off)
 {
-  bfd_byte buf[4 * 4];
+  bfd_byte buf[3 * 4];
 
   if (!bfd_get_section_contents (abfd, glink, buf, off, sizeof buf))
     return FALSE;
 
   return ((bfd_get_32 (abfd, buf + 0) & 0xffff0000) == LIS_11
          && (bfd_get_32 (abfd, buf + 4) & 0xffff0000) == LWZ_11_11
-         && bfd_get_32 (abfd, buf + 8) == MTCTR_11
-         && bfd_get_32 (abfd, buf + 12) == BCTR);
+         && bfd_get_32 (abfd, buf + 8) == MTCTR_11);
 }
 
 static bfd_boolean
@@ -3365,7 +3366,7 @@ ppc_elf_link_hash_table_create (bfd *abfd)
 {
   struct ppc_elf_link_hash_table *ret;
   static struct ppc_elf_params default_params
-    = { PLT_OLD, 0, 0, 1, 0, 0, 12, 0, 0, 0 };
+    = { PLT_OLD, 0, 1, 0, 1, 0, 0, 12, 0, 0, 0 };
 
   ret = bfd_zmalloc (sizeof (struct ppc_elf_link_hash_table));
   if (ret == NULL)
@@ -7167,6 +7168,8 @@ ppc_elf_relax_section (bfd *abfd,
                  size = 4 * ARRAY_SIZE (stub_entry);
                  insn_offset = 0;
                }
+             if (!htab->params->speculate_indirect_jumps)
+               size += 8;
              stub_rtype = R_PPC_RELAX;
              if (tsec == htab->elf.splt
                  || tsec == htab->glink)
@@ -7448,6 +7451,26 @@ elf_finish_pointer_linker_section (bfd *input_bfd,
 #define PPC_HI(v) (((v) >> 16) & 0xffff)
 #define PPC_HA(v) PPC_HI ((v) + 0x8000)
 
+static inline bfd_byte *
+output_bctr (struct ppc_elf_link_hash_table *htab, bfd *obfd, bfd_byte *p)
+{
+  if (!htab->params->speculate_indirect_jumps)
+    {
+      bfd_put_32 (obfd, CRSETEQ, p);
+      p += 4;
+      bfd_put_32 (obfd, BEQCTRM, p);
+      p += 4;
+      bfd_put_32 (obfd, B, p);
+      p += 4;
+    }
+  else
+    {
+      bfd_put_32 (obfd, BCTR, p);
+      p += 4;
+    }
+  return p;
+}
+
 static void
 write_glink_stub (struct elf_link_hash_entry *h, struct plt_entry *ent,
                  asection *plt_sec, unsigned char *p,
@@ -7515,8 +7538,7 @@ write_glink_stub (struct elf_link_hash_entry *h, struct plt_entry *ent,
   p += 4;
   bfd_put_32 (output_bfd, MTCTR_11, p);
   p += 4;
-  bfd_put_32 (output_bfd, BCTR, p);
-  p += 4;
+  p = output_bctr (htab, output_bfd, p);
   while (p < end)
     {
       bfd_put_32 (output_bfd, htab->params->ppc476_workaround ? BA : NOP, p);
@@ -8954,6 +8976,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
                stub = stub_entry;
                size = ARRAY_SIZE (stub_entry);
              }
+           --size;
 
            relocation += addend;
            if (bfd_link_relocatable (info))
@@ -8978,6 +9001,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
                bfd_put_32 (input_bfd, insn, contents + insn_offset);
                insn_offset += 4;
              }
+           output_bctr (htab, input_bfd, contents + insn_offset);
 
            /* Rewrite the reloc and convert one of the trailing nop
               relocs to describe this relocation.  */
@@ -10686,8 +10710,7 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
       p += 4;
       bfd_put_32 (output_bfd, ADD_11_0_11, p);
       p += 4;
-      bfd_put_32 (output_bfd, BCTR, p);
-      p += 4;
+      p = output_bctr (htab, output_bfd, p);
       while (p < endp)
        {
          bfd_put_32 (output_bfd,
index f56d027..8977efa 100644 (file)
@@ -35,6 +35,9 @@ struct ppc_elf_params
   /* Set if individual PLT call stubs should be aligned.  */
   int plt_stub_align;
 
+  /* Clear if PLT call stubs should use a speculative execution barrier.  */
+  int speculate_indirect_jumps;
+
   /* Whether to emit symbols for stubs.  */
   int emit_stub_syms;
 
index 10d3fe9..1b4404c 100644 (file)
@@ -161,6 +161,10 @@ static bfd_vma opd_entry_value
 #define LD_R11_0R11    0xe96b0000      /* ld    %r11,xxx+16@l(%r11) */
 #define BCTR           0x4e800420      /* bctr                      */
 
+#define CRSETEQ                0x4c421242      /* crset 4*%cr0+%eq      */
+#define BEQCTRM                0x4dc20420      /* beqctr-               */
+#define BEQCTRLM       0x4dc20421      /* beqctrl-              */
+
 #define ADDI_R11_R11   0x396b0000      /* addi %r11,%r11,off@l  */
 #define ADDIS_R2_R2    0x3c420000      /* addis %r2,%r2,off@ha  */
 #define ADDI_R2_R2     0x38420000      /* addi  %r2,%r2,off@l   */
@@ -189,7 +193,8 @@ static bfd_vma opd_entry_value
 
 /* __glink_PLTresolve stub instructions.  We enter with the index in R0.  */
 #define GLINK_PLTRESOLVE_SIZE(htab)                    \
-  (8u + (htab->opd_abi ? 11 * 4 : 14 * 4))
+  (8u + (htab->opd_abi ? 11 * 4 : 14 * 4)              \
+   + (!htab->params->speculate_indirect_jumps ? 2 * 4 : 0))
                                        /* 0:                           */
                                        /*  .quad plt0-1f               */
                                        /* __glink:                     */
@@ -9881,6 +9886,8 @@ size_global_entry_stubs (struct elf_link_hash_entry *h, void *inf)
        unsigned int align_power;
 
        stub_size = 16;
+       if (!htab->params->speculate_indirect_jumps)
+         stub_size += 8;
        stub_off = s->size;
        if (htab->params->plt_stub_align >= 0)
          align_power = htab->params->plt_stub_align;
@@ -10446,6 +10453,8 @@ plt_stub_size (struct ppc_link_hash_table *htab,
     size += 4;
   if (PPC_HA (off) != 0)
     size += 4;
+  if (!htab->params->speculate_indirect_jumps)
+    size += 8;
   if (htab->opd_abi)
     {
       size += 4;
@@ -10467,7 +10476,11 @@ plt_stub_size (struct ppc_link_hash_table *htab,
       size += 7 * 4;
       if (ALWAYS_EMIT_R2SAVE
          || stub_entry->stub_type == ppc_stub_plt_call_r2save)
-       size += 6 * 4;
+       {
+         size += 6 * 4;
+         if (!htab->params->speculate_indirect_jumps)
+           size -= 4;
+       }
     }
   return size;
 }
@@ -10502,6 +10515,26 @@ plt_stub_pad (struct ppc_link_hash_table *htab,
   return 0;
 }
 
+static inline bfd_byte *
+output_bctr (struct ppc_link_hash_table *htab, bfd *obfd, bfd_byte *p)
+{
+  if (!htab->params->speculate_indirect_jumps)
+    {
+      bfd_put_32 (obfd, CRSETEQ, p);
+      p += 4;
+      bfd_put_32 (obfd, BEQCTRM, p);
+      p += 4;
+      bfd_put_32 (obfd, B_DOT, p);
+      p += 4;
+    }
+  else
+    {
+      bfd_put_32 (obfd, BCTR, p);
+      p += 4;
+    }
+  return p;
+}
+
 /* Build a .plt call stub.  */
 
 static inline bfd_byte *
@@ -10522,6 +10555,7 @@ build_plt_stub (struct ppc_link_hash_table *htab,
   if (!ALWAYS_USE_FAKE_DEP
       && plt_load_toc
       && plt_thread_safe
+      && htab->params->speculate_indirect_jumps
       && !((stub_entry->h == htab->tls_get_addr_fd
            || stub_entry->h == htab->tls_get_addr)
           && htab->params->tls_get_addr_opt))
@@ -10676,7 +10710,7 @@ build_plt_stub (struct ppc_link_hash_table *htab,
       bfd_put_32 (obfd, B_DOT | (cmp_branch_off & 0x3fffffc), p), p += 4;
     }
   else
-    bfd_put_32 (obfd, BCTR, p),                                        p += 4;
+    p = output_bctr (htab, obfd, p);
   return p;
 }
 
@@ -10720,7 +10754,13 @@ build_tls_get_addr_stub (struct ppc_link_hash_table *htab,
   if (r != NULL)
     r[0].r_offset += 2 * 4;
   p = build_plt_stub (htab, stub_entry, p, offset, r);
-  bfd_put_32 (obfd, BCTRL, p - 4);
+  if (!htab->params->speculate_indirect_jumps)
+    {
+      p -= 4;
+      bfd_put_32 (obfd, BEQCTRLM, p - 4);
+    }
+  else
+    bfd_put_32 (obfd, BCTRL, p - 4);
 
   bfd_put_32 (obfd, LD_R2_0R1 + STK_TOC (htab), p),    p += 4;
   bfd_put_32 (obfd, LD_R11_0R1 + STK_LINKER (htab), p),        p += 4;
@@ -11073,8 +11113,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       p += 4;
       bfd_put_32 (htab->params->stub_bfd, MTCTR_R12, p);
       p += 4;
-      bfd_put_32 (htab->params->stub_bfd, BCTR, p);
-      p += 4;
+      p = output_bctr (htab, htab->params->stub_bfd, p);
       break;
 
     case ppc_stub_plt_call:
@@ -11407,6 +11446,8 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
              if (PPC_LO (r2off) != 0)
                size += 4;
            }
+         if (!htab->params->speculate_indirect_jumps)
+           size += 8;
        }
       else if (info->emitrelocations)
        {
@@ -13040,7 +13081,7 @@ build_global_entry_stubs (struct elf_link_hash_entry *h, void *inf)
        p += 4;
        bfd_put_32 (s->owner, MTCTR_R12, p);
        p += 4;
-       bfd_put_32 (s->owner, BCTR, p);
+       output_bctr (htab, s->owner, p);
        break;
       }
   return TRUE;
@@ -13169,8 +13210,7 @@ ppc64_elf_build_stubs (struct bfd_link_info *info,
          bfd_put_32 (htab->glink->owner, LD_R11_0R11 | 8, p);
          p += 4;
        }
-      bfd_put_32 (htab->glink->owner, BCTR, p);
-      p += 4;
+      p = output_bctr (htab, htab->glink->owner, p);
       BFD_ASSERT (p - htab->glink->contents == GLINK_PLTRESOLVE_SIZE (htab));
 
       /* Build the .glink lazy link call stubs.  */
index 8fa0140..b3d4d59 100644 (file)
@@ -51,6 +51,9 @@ struct ppc64_elf_params
   /* Set if PLT call stubs for localentry:0 functions should omit r2 save.  */
   int plt_localentry0;
 
+  /* Clear if PLT call stubs should use a speculative execution barrier.  */
+  int speculate_indirect_jumps;
+
   /* Whether to canonicalize .opd so that there are no overlapping
      .opd entries.  */
   int non_overlapping_opd;
index fff66e1..ec56a89 100644 (file)
@@ -1,5 +1,15 @@
 2018-01-17  Alan Modra  <amodra@gmail.com>
 
+       * options.h (speculate_indirect_jumps): New option.
+       * powerpc.cc (beqctrm, beqctrlm, crseteq): New insn constants.
+       (output_bctr): New function.
+       (Stub_table::plt_call_size): Add space for speculation barrier.
+       (Stub_table::branch_stub_size): Likewise.
+       (Output_data_glink::pltresolve_size): Likewise.
+       (Stub_table::do_write): Output speculation barriers.
+
+2018-01-17  Alan Modra  <amodra@gmail.com>
+
        * options.h (plt_align): Support for PowerPC32 too.
        * powerpc.cc (Stub_table::stub_align): Heed --plt-align for 32-bit.
        (Stub_table::plt_call_size, branch_stub_size): Tidy.
index b39d5ff..c80cd05 100644 (file)
@@ -1108,6 +1108,10 @@ class General_options
              N_("(PowerPC64 only) Optimize calls to ELFv2 localentry:0 functions"),
              N_("(PowerPC64 only) Don't optimize ELFv2 calls"));
 
+  DEFINE_bool(speculate_indirect_jumps, options::TWO_DASHES, '\0', true,
+             N_("(PowerPC only) PLT call stubs without speculation barrier"),
+             N_("(PowerPC only) PLT call stubs with speculation barrier"));
+
   DEFINE_bool(plt_static_chain, options::TWO_DASHES, '\0', false,
              N_("(PowerPC64 only) PLT call stubs should load r11"),
              N_("(PowerPC64 only) PLT call stubs should not load r11"));
index 9ed5b21..6b65792 100644 (file)
@@ -3781,6 +3781,8 @@ static const uint32_t b                   = 0x48000000;
 static const uint32_t bcl_20_31                = 0x429f0005;
 static const uint32_t bctr             = 0x4e800420;
 static const uint32_t bctrl            = 0x4e800421;
+static const uint32_t beqctrm          = 0x4dc20420;
+static const uint32_t beqctrlm         = 0x4dc20421;
 static const uint32_t beqlr            = 0x4d820020;
 static const uint32_t blr              = 0x4e800020;
 static const uint32_t bnectr_p4                = 0x4ce20420;
@@ -3790,6 +3792,7 @@ static const uint32_t cmpdi_11_0  = 0x2c2b0000;
 static const uint32_t cmpwi_11_0       = 0x2c0b0000;
 static const uint32_t cror_15_15_15    = 0x4def7b82;
 static const uint32_t cror_31_31_31    = 0x4ffffb82;
+static const uint32_t crseteq          = 0x4c421242;
 static const uint32_t ld_0_1           = 0xe8010000;
 static const uint32_t ld_0_12          = 0xe80c0000;
 static const uint32_t ld_2_1           = 0xe8410000;
@@ -4165,6 +4168,24 @@ write_insn(unsigned char* p, uint32_t v)
   elfcpp::Swap<32, big_endian>::writeval(p, v);
 }
 
+template<bool big_endian>
+static unsigned char*
+output_bctr(unsigned char* p)
+{
+  if (!parameters->options().speculate_indirect_jumps())
+    {
+      write_insn<big_endian>(p, crseteq);
+      p += 4;
+      write_insn<big_endian>(p, beqctrm);
+      p += 4;
+      write_insn<big_endian>(p, b);
+    }
+  else
+    write_insn<big_endian>(p, bctr);
+  p += 4;
+  return p;
+}
+
 // Stub_table holds information about plt and long branch stubs.
 // Stubs are built in an area following some input section determined
 // by group_sections().  This input section is converted to a relaxed
@@ -4426,6 +4447,7 @@ class Stub_table : public Output_relaxed_input_section
       {
        const Symbol* gsym = p->first.sym_;
        return (4 * 4
+               + (!parameters->options().speculate_indirect_jumps() ? 2 * 4 : 0)
                + (this->targ_->is_tls_get_addr_opt(gsym) ? 8 * 4 : 0));
       }
 
@@ -4441,6 +4463,8 @@ class Stub_table : public Output_relaxed_input_section
     got_addr += ppcobj->toc_base_offset();
     Address off = plt_addr - got_addr;
     unsigned int bytes = 4 * 4 + 4 * (ha(off) != 0);
+    if (!parameters->options().speculate_indirect_jumps())
+      bytes += 2 * 4;
     const Symbol* gsym = p->first.sym_;
     if (this->targ_->is_tls_get_addr_opt(gsym))
       bytes += 13 * 4;
@@ -4471,6 +4495,8 @@ class Stub_table : public Output_relaxed_input_section
     if (p->first.dest_ - loc + (1 << 25) < 2 << 25)
       return 4;
     unsigned int bytes = 16;
+    if (!parameters->options().speculate_indirect_jumps())
+      bytes += 8;
     if (size == 32 && parameters->options().output_is_position_independent())
       bytes += 16;
     return bytes;
@@ -4924,7 +4950,8 @@ class Output_data_glink : public Output_section_data
   {
     if (size == 64)
       return (8
-             + (this->targ_->abiversion() < 2 ? 11 * 4 : 14 * 4));
+             + (this->targ_->abiversion() < 2 ? 11 * 4 : 14 * 4)
+             + (!parameters->options().speculate_indirect_jumps() ? 2 * 4 : 0));
     return 16 * 4;
   }
 
@@ -5001,7 +5028,8 @@ Output_data_glink<size, big_endian>::add_global_entry(const Symbol* gsym)
   std::pair<typename Global_entry_stub_entries::iterator, bool> p
     = this->global_entry_stubs_.insert(std::make_pair(gsym, off));
   if (p.second)
-    this->ge_size_ = off + 16;
+    this->ge_size_
+      = off + 16 + (!parameters->options().speculate_indirect_jumps() ? 8 : 0);
 }
 
 template<int size, bool big_endian>
@@ -5190,7 +5218,10 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
                = plt_load_toc && this->targ_->plt_thread_safe();
              bool use_fake_dep = false;
              Address cmp_branch_off = 0;
-             if (thread_safe)
+             if (thread_safe
+                 && !parameters->options().speculate_indirect_jumps())
+               use_fake_dep = true;
+             else if (thread_safe)
                {
                  unsigned int pltindex
                    = ((pltoff - this->targ_->first_plt_entry_offset())
@@ -5238,7 +5269,7 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
                                                 + this->targ_->stk_linker()));
                      p += 4;
                    }
-                 use_fake_dep = thread_safe;
+                 use_fake_dep |= thread_safe;
                }
              if (ha(off) != 0)
                {
@@ -5329,7 +5360,14 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
              if (!cs->second.localentry0_
                  && this->targ_->is_tls_get_addr_opt(gsym))
                {
-                 write_insn<big_endian>(p, bctrl);
+                 if (!parameters->options().speculate_indirect_jumps())
+                   {
+                     write_insn<big_endian>(p, crseteq);
+                     p += 4;
+                     write_insn<big_endian>(p, beqctrlm);
+                   }
+                 else
+                   write_insn<big_endian>(p, bctrl);
                  p += 4;
                  write_insn<big_endian>(p, ld_2_1 + this->targ_->stk_toc());
                  p += 4;
@@ -5348,7 +5386,7 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
                  write_insn<big_endian>(p, b | (cmp_branch_off & 0x3fffffc));
                }
              else
-               write_insn<big_endian>(p, bctr);
+               output_bctr<big_endian>(p);
            }
        }
 
@@ -5383,7 +5421,7 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
                  write_insn<big_endian>(p, ld_12_12 + l(brltoff)),     p += 4;
                }
              write_insn<big_endian>(p, mtctr_12),                      p += 4;
-             write_insn<big_endian>(p, bctr);
+             output_bctr<big_endian>(p);
            }
        }
     }
@@ -5479,7 +5517,7 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
              p += 4;
              write_insn<big_endian>(p, mtctr_11);
              p += 4;
-             write_insn<big_endian>(p, bctr);
+             output_bctr<big_endian>(p);
            }
        }
 
@@ -5520,7 +5558,7 @@ Stub_table<size, big_endian>::do_write(Output_file* of)
          p += 4;
          write_insn<big_endian>(p, mtctr_12);
          p += 4;
-         write_insn<big_endian>(p, bctr);
+         output_bctr<big_endian>(p);
        }
     }
   if (this->need_save_res_)
@@ -5587,7 +5625,7 @@ Output_data_glink<size, big_endian>::do_write(Output_file* of)
              write_insn<big_endian>(p, mtctr_12),              p += 4;
              write_insn<big_endian>(p, ld_11_11 + 8),          p += 4;
            }
-         write_insn<big_endian>(p, bctr),                      p += 4;
+         p = output_bctr<big_endian>(p);
          gold_assert(p == oview + this->pltresolve_size());
 
          // Write lazy link call stubs.
@@ -5643,7 +5681,7 @@ Output_data_glink<size, big_endian>::do_write(Output_file* of)
          write_insn<big_endian>(p, addis_12_12 + ha(off)),     p += 4;
          write_insn<big_endian>(p, ld_12_12 + l(off)),         p += 4;
          write_insn<big_endian>(p, mtctr_12),                  p += 4;
-         write_insn<big_endian>(p, bctr);
+         output_bctr<big_endian>(p);
        }
     }
   else
@@ -5735,8 +5773,7 @@ Output_data_glink<size, big_endian>::do_write(Output_file* of)
          write_insn<big_endian>(p, add_11_0_11);
        }
       p += 4;
-      write_insn<big_endian>(p, bctr);
-      p += 4;
+      p = output_bctr<big_endian>(p);
       while (p < end_p)
        {
          write_insn<big_endian>(p, nop);
index f95032d..9ee555b 100644 (file)
@@ -1,6 +1,28 @@
 2018-01-17  Alan Modra  <amodra@gmail.com>
 
        * emultempl/ppc32elf.em (params): Init new field.
+       (OPTION_SPECULATE_INDIRECT_JUMPS): Define.
+       (OPTION_NO_SPECULATE_INDIRECT_JUMPS): Define.
+       (PARSE_AND_LIST_LONGOPTS): Handle new options.
+       (PARSE_AND_LIST_ARGS_CASES): Likewise.
+       (PARSE_AND_LIST_OPTIONS): Likewise.
+       * emultempl/ppc64elf.em (params): Init new field.
+       (OPTION_SPECULATE_INDIRECT_JUMPS): Define.
+       (OPTION_NO_SPECULATE_INDIRECT_JUMPS): Define.
+       (PARSE_AND_LIST_LONGOPTS): Handle --speculate-indirect-jumps.
+       (PARSE_AND_LIST_OPTIONS): Likewise.
+       (PARSE_AND_LIST_ARGS_CASES): Likewise.
+       * ld.texinfo (--no-plt-thread-safe): Correct itemx.
+       (--speculate-indirect-jumps): Document.
+       * testsuite/ld-powerpc/elfv2exe.d,
+       * testsuite/ld-powerpc/elfv2so.d,
+       * testsuite/ld-powerpc/relbrlt.d,
+       * testsuite/ld-powerpc/powerpc.exp: Disable plt alignment and
+       speculation barriers on various tests.
+
+2018-01-17  Alan Modra  <amodra@gmail.com>
+
+       * emultempl/ppc32elf.em (params): Init new field.
        (enum ppc32_opt): New enum to define OPTION_* values.  Add
        OPTION_PLT_ALIGN and OPTION_NO_PLT_ALIGN.
        (PARSE_AND_LIST_LONGOPTS): Handle new options.
index 70dd5a0..4e71a78 100644 (file)
@@ -38,7 +38,7 @@ static int notlsopt = 0;
 /* Choose the correct place for .got.  */
 static int old_got = 0;
 
-static struct ppc_elf_params params = { PLT_UNSET, 0, -1,
+static struct ppc_elf_params params = { PLT_UNSET, 0, 1, -1,
                                        0, 0, 0, 0, 0, 0, 0 };
 
 static void
@@ -246,6 +246,8 @@ enum ppc32_opt
   OPTION_NO_TLS_GET_ADDR_OPT,
   OPTION_NEW_PLT,
   OPTION_OLD_PLT,
+  OPTION_SPECULATE_INDIRECT_JUMPS,
+  OPTION_NO_SPECULATE_INDIRECT_JUMPS,
   OPTION_PLT_ALIGN,
   OPTION_NO_PLT_ALIGN,
   OPTION_OLD_GOT,
@@ -267,6 +269,8 @@ if test -z "$VXWORKS_BASE_EM_FILE" ; then
   PARSE_AND_LIST_LONGOPTS=${PARSE_AND_LIST_LONGOPTS}'
   { "secure-plt", no_argument, NULL, OPTION_NEW_PLT },
   { "bss-plt", no_argument, NULL, OPTION_OLD_PLT },
+  { "speculate-indirect-jumps", no_argument, NULL, OPTION_SPECULATE_INDIRECT_JUMPS },
+  { "no-speculate-indirect-jumps", no_argument, NULL, OPTION_NO_SPECULATE_INDIRECT_JUMPS },
   { "plt-align", no_argument, NULL, OPTION_PLT_ALIGN },
   { "no-plt-align", no_argument, NULL, OPTION_NO_PLT_ALIGN },
   { "sdata-got", no_argument, NULL, OPTION_OLD_GOT },'
@@ -300,6 +304,12 @@ if test -z "$VXWORKS_BASE_EM_FILE" ; then
   --bss-plt                   Force old-style BSS PLT.\n"
                   ));
   fprintf (file, _("\
+  --speculate-indirect-jumps  PLT call stubs without speculation barrier.\n"
+                  ));
+  fprintf (file, _("\
+  --no-speculate-indirect-jumps PLT call stubs with speculation barrier.\n"
+                  ));
+  fprintf (file, _("\
   --plt-align                 Align PLT call stubs to fit cache lines.\n"
                   ));
   fprintf (file, _("\
@@ -350,6 +360,14 @@ PARSE_AND_LIST_ARGS_CASES=${PARSE_AND_LIST_ARGS_CASES}'
       params.plt_style = PLT_OLD;
       break;
 
+    case OPTION_SPECULATE_INDIRECT_JUMPS:
+      params.speculate_indirect_jumps = 1;
+      break;
+
+    case OPTION_NO_SPECULATE_INDIRECT_JUMPS:
+      params.speculate_indirect_jumps = 0;
+      break;
+
     case OPTION_PLT_ALIGN:
       params.plt_stub_align = 5;
       break;
index 840bd44..c7c27b0 100644 (file)
@@ -38,7 +38,7 @@ static struct ppc64_elf_params params = { NULL,
                                          &ppc_layout_sections_again,
                                          1, -1, 0,
                                          ${DEFAULT_PLT_STATIC_CHAIN-0}, -1, 5,
-                                         -1, 0, -1, -1, 0};
+                                         -1, 1, 0, -1, -1, 0};
 
 /* Fake input file for stubs.  */
 static lang_input_statement_type *stub_file;
@@ -692,6 +692,8 @@ enum ppc64_opt
   OPTION_NO_PLT_STATIC_CHAIN,
   OPTION_PLT_THREAD_SAFE,
   OPTION_NO_PLT_THREAD_SAFE,
+  OPTION_SPECULATE_INDIRECT_JUMPS,
+  OPTION_NO_SPECULATE_INDIRECT_JUMPS,
   OPTION_PLT_ALIGN,
   OPTION_NO_PLT_ALIGN,
   OPTION_PLT_LOCALENTRY,
@@ -719,6 +721,8 @@ PARSE_AND_LIST_LONGOPTS=${PARSE_AND_LIST_LONGOPTS}'
   { "no-plt-static-chain", no_argument, NULL, OPTION_NO_PLT_STATIC_CHAIN },
   { "plt-thread-safe", no_argument, NULL, OPTION_PLT_THREAD_SAFE },
   { "no-plt-thread-safe", no_argument, NULL, OPTION_NO_PLT_THREAD_SAFE },
+  { "speculate-indirect-jumps", no_argument, NULL, OPTION_SPECULATE_INDIRECT_JUMPS },
+  { "no-speculate-indirect-jumps", no_argument, NULL, OPTION_NO_SPECULATE_INDIRECT_JUMPS },
   { "plt-align", optional_argument, NULL, OPTION_PLT_ALIGN },
   { "no-plt-align", no_argument, NULL, OPTION_NO_PLT_ALIGN },
   { "plt-localentry", optional_argument, NULL, OPTION_PLT_LOCALENTRY },
@@ -760,7 +764,13 @@ PARSE_AND_LIST_OPTIONS=${PARSE_AND_LIST_OPTIONS}'
   --plt-thread-safe           PLT call stubs with load-load barrier.\n"
                   ));
   fprintf (file, _("\
-  --no-plt-thread-safe        PLT call stubs without barrier.\n"
+  --no-plt-thread-safe        PLT call stubs without load-load barrier.\n"
+                  ));
+  fprintf (file, _("\
+  --speculate-indirect-jumps  PLT call stubs without speculation barrier.\n"
+                  ));
+  fprintf (file, _("\
+  --no-speculate-indirect-jumps PLT call stubs with speculation barrier.\n"
                   ));
   fprintf (file, _("\
   --plt-align [=<align>]      Align PLT call stubs to fit cache lines.\n"
@@ -850,6 +860,14 @@ PARSE_AND_LIST_ARGS_CASES=${PARSE_AND_LIST_ARGS_CASES}'
       params.plt_thread_safe = 0;
       break;
 
+    case OPTION_SPECULATE_INDIRECT_JUMPS:
+      params.speculate_indirect_jumps = 1;
+      break;
+
+    case OPTION_NO_SPECULATE_INDIRECT_JUMPS:
+      params.speculate_indirect_jumps = 0;
+      break;
+
     case OPTION_PLT_ALIGN:
       if (optarg != NULL)
        {
index 94d340c..c89915f 100644 (file)
@@ -7635,7 +7635,7 @@ chain since there is never any need to do so on a PLT call.
 @kindex --plt-thread-safe
 @kindex --no-plt-thread-safe
 @item --plt-thread-safe
-@itemx --no-thread-safe
+@itemx --no-plt-thread-safe
 With power7's weakly ordered memory model, it is possible when using
 lazy binding for ld.so to update a plt entry in one thread and have
 another thread see the individual plt entry words update in the wrong
@@ -7646,6 +7646,15 @@ looks for calls to commonly used functions that create threads, and if
 seen, adds the necessary barriers.  Use these options to change the
 default behaviour.
 
+@cindex PowerPC64 PLT call stub speculative execution barrier
+@kindex --speculate-indirect-jumps
+@kindex --no-speculate-indirect-jumps
+@item --speculate-indirect-jumps
+@itemx --no-speculate-indirect-jumps
+Use these options to control whether all indirect branch instructions
+emitted by @code{ld}, such as those in the PLT, have a speculative
+execution barrier to mitigate Spectre variant 2 attacks.
+
 @cindex PowerPC64 ELFv2 PLT localentry optimization
 @kindex --plt-localentry
 @kindex --no-plt-localentry
index 77bf6e2..fa5b622 100644 (file)
@@ -1,6 +1,6 @@
 #source: elfv2.s
 #as: -a64
-#ld: -melf64ppc --defsym f2=0x1234 --defsym f3=0x10008888 --defsym f4=0x1200000 --defsym _start=f1
+#ld: -melf64ppc --speculate-indirect-jumps --defsym f2=0x1234 --defsym f3=0x10008888 --defsym f4=0x1200000 --defsym _start=f1
 #objdump: -dr
 
 .*
index e7cd45c..1c048e1 100644 (file)
@@ -1,6 +1,6 @@
 #source: elfv2.s
 #as: -a64
-#ld: -melf64ppc -shared
+#ld: -melf64ppc -shared --speculate-indirect-jumps
 #objdump: -dr
 
 .*
index 217b443..d604787 100644 (file)
@@ -121,11 +121,11 @@ set ppcelftests {
      "tls32"}
     {"TLS32 helper shared library" "-shared -melf32ppc tmpdir/tlslib32.o" "" "" {}
      {} "libtlslib32.so"}
-    {"TLS32 dynamic exec" "-melf32ppc --no-ld-generated-unwind-info --hash-style=sysv tmpdir/tls32.o tmpdir/libtlslib32.so" "" "" {}
+    {"TLS32 dynamic exec" "-melf32ppc --no-plt-align --speculate-indirect-jumps --no-ld-generated-unwind-info --hash-style=sysv tmpdir/tls32.o tmpdir/libtlslib32.so" "" "" {}
      {{readelf -WSsrl tlsexe32.r} {objdump -dr tlsexe32.d}
       {objdump -sj.got tlsexe32.g} {objdump -sj.tdata tlsexe32.t}}
      "tlsexe32"}
-    {"TLS32 shared" "-shared -melf32ppc --no-ld-generated-unwind-info --hash-style=sysv tmpdir/tls32.o" "" "" {}
+    {"TLS32 shared" "-shared -melf32ppc --no-plt-align --speculate-indirect-jumps --no-ld-generated-unwind-info --hash-style=sysv tmpdir/tls32.o" "" "" {}
      {{readelf -WSsrl tlsso32.r} {objdump -dr tlsso32.d}
       {objdump -sj.got tlsso32.g} {objdump -sj.tdata tlsso32.t}}
      "tls32.so"}
@@ -147,7 +147,7 @@ set ppcelftests {
     {"TLS32 DLL" "-shared -melf32ppc --version-script tlsdll.ver" ""
      "-a32" {tlsdll_32.s}
      {} "tlsdll32.so"}
-    {"TLS32 opt 5" "-melf32ppc -shared --gc-sections --secure-plt tmpdir/tlsdll32.so" "" "-a32"  {tlsopt5_32.s}
+    {"TLS32 opt 5" "-melf32ppc -shared --gc-sections --secure-plt --no-plt-align --speculate-indirect-jumps tmpdir/tlsdll32.so" "" "-a32"  {tlsopt5_32.s}
      {{objdump -dr tlsopt5_32.d}}
      "tlsopt5_32"}
     {"Shared library with global symbol" "-shared -melf32ppc" "" "-a32" {sdalib.s}
@@ -174,15 +174,15 @@ set ppc64elftests {
      {} "libtlslib.so"}
     {"TLS helper old shared lib" "-shared -melf64ppc" "" "-a64" {oldtlslib.s}
      {} "liboldlib.so"}
-    {"TLS dynamic exec" "-melf64ppc --no-plt-align --no-ld-generated-unwind-info --hash-style=sysv tmpdir/tls.o tmpdir/libtlslib.so" "" "" {}
+    {"TLS dynamic exec" "-melf64ppc --no-plt-align --speculate-indirect-jumps --no-ld-generated-unwind-info --hash-style=sysv tmpdir/tls.o tmpdir/libtlslib.so" "" "" {}
      {{readelf -WSsrl tlsexe.r} {objdump -dr tlsexe.d}
       {objdump -sj.got tlsexe.g} {objdump -sj.tdata tlsexe.t}}
      "tlsexe"}
-    {"TLS dynamic old" "-melf64ppc --no-plt-align --no-ld-generated-unwind-info --hash-style=sysv tmpdir/tls.o tmpdir/liboldlib.so" "" "" {}
+    {"TLS dynamic old" "-melf64ppc --no-plt-align --speculate-indirect-jumps --no-ld-generated-unwind-info --hash-style=sysv tmpdir/tls.o tmpdir/liboldlib.so" "" "" {}
      {{readelf -WSsrl tlsexe.r} {objdump -dr tlsexe.d}
       {objdump -sj.got tlsexe.g} {objdump -sj.tdata tlsexe.t}}
      "tlsexeold"}
-    {"TLS shared" "-shared -melf64ppc --no-plt-align --no-ld-generated-unwind-info --hash-style=sysv tmpdir/tls.o" "" "" {}
+    {"TLS shared" "-shared -melf64ppc --no-plt-align --speculate-indirect-jumps --no-ld-generated-unwind-info --hash-style=sysv tmpdir/tls.o" "" "" {}
      {{readelf -WSsrl tlsso.r} {objdump -dr tlsso.d}
       {objdump -sj.got tlsso.g} {objdump -sj.tdata tlsso.t}}
      "tls.so"}
@@ -190,17 +190,17 @@ set ppc64elftests {
      {{objdump -dr tlstoc.d} {objdump -sj.got tlstoc.g}
       {objdump -sj.tdata tlstoc.t}}
      "tlstoc"}
-    {"TLSTOC dynamic exec" "-melf64ppc --no-plt-align --no-ld-generated-unwind-info --hash-style=sysv tmpdir/tlstoc.o tmpdir/libtlslib.so" ""
+    {"TLSTOC dynamic exec" "-melf64ppc --no-plt-align --speculate-indirect-jumps --no-ld-generated-unwind-info --hash-style=sysv tmpdir/tlstoc.o tmpdir/libtlslib.so" ""
      "" {}
      {{readelf -WSsrl tlsexetoc.r} {objdump -dr tlsexetoc.d}
       {objdump -sj.got tlsexetoc.g} {objdump -sj.tdata tlsexetoc.t}}
      "tlsexetoc"}
-    {"TLSTOC dynamic old" "-melf64ppc --no-plt-align --no-ld-generated-unwind-info --hash-style=sysv tmpdir/tlstoc.o tmpdir/liboldlib.so" ""
+    {"TLSTOC dynamic old" "-melf64ppc --no-plt-align --speculate-indirect-jumps --no-ld-generated-unwind-info --hash-style=sysv tmpdir/tlstoc.o tmpdir/liboldlib.so" ""
      "" {}
      {{readelf -WSsrl tlsexetoc.r} {objdump -dr tlsexetoc.d}
       {objdump -sj.got tlsexetoc.g} {objdump -sj.tdata tlsexetoc.t}}
      "tlsexetocold"}
-    {"TLSTOC shared" "-shared -melf64ppc --no-plt-align --no-ld-generated-unwind-info --hash-style=sysv tmpdir/tlstoc.o" "" "" {}
+    {"TLSTOC shared" "-shared -melf64ppc --no-plt-align --speculate-indirect-jumps --no-ld-generated-unwind-info --hash-style=sysv tmpdir/tlstoc.o" "" "" {}
      {{readelf -WSsrl tlstocso.r} {objdump -dr tlstocso.d}
       {objdump -sj.got tlstocso.g} {objdump -sj.tdata tlstocso.t}}
      "tlstoc.so"}
@@ -221,7 +221,7 @@ set ppc64elftests {
      "tlsopt4"}
     {"TLS DLL" "-shared -melf64ppc --version-script tlsdll.ver" "" "-a64" {tlsdll.s}
      {} "tlsdll.so"}
-    {"TLS opt 5" "-melf64ppc --no-plt-align -shared --gc-sections --no-plt-localentry tmpdir/tlsdll.so" "" "-a64"  {tlsopt5.s}
+    {"TLS opt 5" "-melf64ppc --no-plt-align --speculate-indirect-jumps -shared --gc-sections --no-plt-localentry tmpdir/tlsdll.so" "" "-a64"  {tlsopt5.s}
      {{objdump -dr tlsopt5.d} {readelf -wf tlsopt5.wf}}
      "tlsopt5"}
     {"sym@tocbase" "-shared -melf64ppc" "" "-a64" {symtocbase-1.s symtocbase-2.s}
index 6f3db7d..510ba84 100644 (file)
@@ -1,6 +1,6 @@
 #source: relbrlt.s
 #as: -a64
-#ld: -melf64ppc --no-plt-align --no-ld-generated-unwind-info --emit-relocs
+#ld: -melf64ppc --no-plt-align --speculate-indirect-jumps --no-ld-generated-unwind-info --emit-relocs
 #objdump: -Dr
 
 .*