Handle R_X86_64_64 properly for x32
[deliverable/binutils-gdb.git] / bfd / elf64-ppc.c
index 4560599ec844c934dbfe9fad16e427beae4aac5f..433fabf4432ae583a884e6c72de20ac7d738197c 100644 (file)
@@ -1,6 +1,6 @@
 /* PowerPC64-specific support for 64-bit ELF.
    Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
-   2009, 2010, 2011 Free Software Foundation, Inc.
+   2009, 2010, 2011, 2012 Free Software Foundation, Inc.
    Written by Linus Nordberg, Swox AB <info@swox.com>,
    based on elf32-ppc.c by Ian Lance Taylor.
    Largely rewritten by Alan Modra.
@@ -105,6 +105,7 @@ static bfd_vma opd_entry_value
 #define elf_backend_gc_sweep_hook            ppc64_elf_gc_sweep_hook
 #define elf_backend_adjust_dynamic_symbol     ppc64_elf_adjust_dynamic_symbol
 #define elf_backend_hide_symbol                      ppc64_elf_hide_symbol
+#define elf_backend_maybe_function_sym       ppc64_elf_maybe_function_sym
 #define elf_backend_always_size_sections      ppc64_elf_func_desc_adjust
 #define elf_backend_size_dynamic_sections     ppc64_elf_size_dynamic_sections
 #define elf_backend_init_index_section       _bfd_elf_init_2_index_sections
@@ -152,6 +153,13 @@ static bfd_vma opd_entry_value
 #define ADDIS_R2_R2    0x3c420000      /* addis %r2,%r2,off@ha  */
 #define ADDI_R2_R2     0x38420000      /* addi  %r2,%r2,off@l   */
 
+#define XOR_R11_R11_R11        0x7d6b5a78      /* xor   %r11,%r11,%r11  */
+#define ADD_R12_R12_R11        0x7d8c5a14      /* add   %r12,%r12,%r11  */
+#define ADD_R2_R2_R11  0x7c425a14      /* add   %r2,%r2,%r11    */
+#define CMPLDI_R2_0    0x28220000      /* cmpldi %r2,0          */
+#define BNECTR         0x4ca20420      /* bnectr+               */
+#define BNECTR_P4      0x4ce20420      /* bnectr+               */
+
 #define LD_R11_0R2     0xe9620000      /* ld    %r11,xxx+0(%r2) */
 #define LD_R2_0R2      0xe8420000      /* ld    %r2,xxx+0(%r2)  */
 
@@ -2356,8 +2364,8 @@ ppc64_elf_brtaken_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
   long insn;
   enum elf_ppc64_reloc_type r_type;
   bfd_size_type octets;
-  /* Disabled until we sort out how ld should choose 'y' vs 'at'.  */
-  bfd_boolean is_power4 = FALSE;
+  /* Assume 'at' branch hints.  */
+  bfd_boolean is_isa_v2 = TRUE;
 
   /* If this is a relocatable link (output_bfd test tells us), just
      call the generic function.  Any adjustment will be done at final
@@ -2374,7 +2382,7 @@ ppc64_elf_brtaken_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol,
       || r_type == R_PPC64_REL14_BRTAKEN)
     insn |= 0x01 << 21; /* 'y' or 't' bit, lowest bit of BO field.  */
 
-  if (is_power4)
+  if (is_isa_v2)
     {
       /* Set 'a' bit.  This is 0b00010 in BO field for branch
         on CR(BI) insns (BO == 001at or 011at), and 0b01000
@@ -2714,7 +2722,7 @@ ppc64_elf_write_core_note (bfd *abfd, char *buf, int *bufsiz, int note_type,
        va_list ap;
 
        va_start (ap, note_type);
-       memset (data, 0, 40);
+       memset (data, 0, sizeof (data));
        strncpy (data + 40, va_arg (ap, const char *), 16);
        strncpy (data + 56, va_arg (ap, const char *), 80);
        va_end (ap);
@@ -3584,7 +3592,8 @@ enum ppc_stub_type {
   ppc_stub_long_branch_r2off,
   ppc_stub_plt_branch,
   ppc_stub_plt_branch_r2off,
-  ppc_stub_plt_call
+  ppc_stub_plt_call,
+  ppc_stub_plt_call_r2save
 };
 
 struct ppc_stub_hash_entry {
@@ -3752,14 +3761,20 @@ struct ppc_link_hash_table
   bfd_size_type got_reli_size;
 
   /* Statistics.  */
-  unsigned long stub_count[ppc_stub_plt_call];
+  unsigned long stub_count[ppc_stub_plt_call_r2save];
 
   /* Number of stubs against global syms.  */
   unsigned long stub_globals;
 
+  /* Alignment of PLT call stubs.  */
+  unsigned int plt_stub_align:4;
+
   /* Set if PLT call stubs should load r11.  */
   unsigned int plt_static_chain:1;
 
+  /* Set if PLT call stubs need a read-read barrier.  */
+  unsigned int plt_thread_safe:1;
+
   /* Set if we should emit symbols for stubs.  */
   unsigned int emit_stub_syms:1;
 
@@ -5514,31 +5529,50 @@ opd_entry_value (asection *opd_sec,
   Elf_Internal_Rela *lo, *hi, *look;
   bfd_vma val;
 
-  /* No relocs implies we are linking a --just-symbols object.  */
+  /* No relocs implies we are linking a --just-symbols object, or looking
+     at a final linked executable with addr2line or somesuch.  */
   if (opd_sec->reloc_count == 0)
     {
-      char buf[8];
-
+      /* PR 13897: Cache the loaded section to speed up the search.  */
+      static asection * buf_sec = NULL;
+      static char       buf[8];
+      static bfd_vma    buf_val = 0;
+      static asection * buf_likely = NULL;
+      
+      if (buf_sec == opd_sec)
+       {
+         if (code_sec != NULL)
+           * code_sec = buf_likely;
+         if (code_off != NULL && buf_likely != NULL)
+           * code_off = buf_val - buf_likely->vma;
+         return buf_val;
+       }
+   
       if (!bfd_get_section_contents (opd_bfd, opd_sec, buf, offset, 8))
        return (bfd_vma) -1;
+      buf_sec = opd_sec;
 
-      val = bfd_get_64 (opd_bfd, buf);
+      buf_val = bfd_get_64 (opd_bfd, buf);
       if (code_sec != NULL)
        {
-         asection *sec, *likely = NULL;
+         asection *sec;
+
+         buf_likely = NULL;
          for (sec = opd_bfd->sections; sec != NULL; sec = sec->next)
-           if (sec->vma <= val
+           if (sec->vma <= buf_val
                && (sec->flags & SEC_LOAD) != 0
                && (sec->flags & SEC_ALLOC) != 0)
-             likely = sec;
-         if (likely != NULL)
+             buf_likely = sec;
+         if (buf_likely != NULL)
            {
-             *code_sec = likely;
+             *code_sec = buf_likely;
              if (code_off != NULL)
-               *code_off = val - likely->vma;
+               *code_off = buf_val - buf_likely->vma;
            }
        }
-      return val;
+      else
+       buf_likely = NULL;
+      return buf_val;
     }
 
   BFD_ASSERT (is_ppc64_elf (opd_bfd));
@@ -5569,15 +5603,18 @@ opd_entry_value (asection *opd_sec,
              unsigned long symndx = ELF64_R_SYM (look->r_info);
              asection *sec;
 
-             if (symndx < symtab_hdr->sh_info)
+             if (symndx < symtab_hdr->sh_info
+                 || elf_sym_hashes (opd_bfd) == NULL)
                {
                  Elf_Internal_Sym *sym;
 
                  sym = (Elf_Internal_Sym *) symtab_hdr->contents;
                  if (sym == NULL)
                    {
-                     sym = bfd_elf_get_elf_syms (opd_bfd, symtab_hdr,
-                                                 symtab_hdr->sh_info,
+                     size_t symcnt = symtab_hdr->sh_info;
+                     if (elf_sym_hashes (opd_bfd) == NULL)
+                       symcnt = symtab_hdr->sh_size / symtab_hdr->sh_entsize;
+                     sym = bfd_elf_get_elf_syms (opd_bfd, symtab_hdr, symcnt,
                                                  0, NULL, NULL, NULL);
                      if (sym == NULL)
                        break;
@@ -5617,6 +5654,22 @@ opd_entry_value (asection *opd_sec,
   return val;
 }
 
+/* Return TRUE iff the ELF symbol SYM might be a function.  Set *CODE_SEC
+   and *CODE_OFF to the function's entry point.  */
+
+static bfd_boolean
+ppc64_elf_maybe_function_sym (const asymbol *sym,
+                             asection **code_sec, bfd_vma *code_off)
+{
+  if (_bfd_elf_maybe_function_sym (sym, code_sec, code_off))
+    {
+      if (strcmp (sym->section->name, ".opd") == 0)
+       opd_entry_value (sym->section, sym->value, code_sec, code_off);
+      return TRUE;
+    }
+  return FALSE;
+}
+
 /* Return true if symbol is defined in a regular object file.  */
 
 static bfd_boolean
@@ -6530,13 +6583,6 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
   /* This is a reference to a symbol defined by a dynamic object which
      is not a function.  */
 
-  if (h->size == 0)
-    {
-      info->callbacks->einfo (_("%P: dynamic variable `%s' is zero size\n"),
-                             h->root.root.string);
-      return TRUE;
-    }
-
   /* We must allocate the symbol in our .dynbss section, which will
      become part of the .bss section of the executable.  There will be
      an entry for this symbol in the .dynsym section.  The dynamic
@@ -6551,7 +6597,7 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
      to copy the initial value out of the dynamic object and into the
      runtime process image.  We need to remember the offset into the
      .rela.bss section we are going to use.  */
-  if ((h->root.u.def.section->flags & SEC_ALLOC) != 0)
+  if ((h->root.u.def.section->flags & SEC_ALLOC) != 0 && h->size != 0)
     {
       htab->relbss->size += sizeof (Elf64_External_Rela);
       h->needs_copy = 1;
@@ -6852,7 +6898,7 @@ adjust_opd_syms (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED)
          if (dsec == NULL)
            {
              for (dsec = sym_sec->owner->sections; dsec; dsec = dsec->next)
-               if (elf_discarded_section (dsec))
+               if (discarded_section (dsec))
                  {
                    ppc64_elf_tdata (sym_sec->owner)->deleted_section = dsec;
                    break;
@@ -7033,7 +7079,7 @@ ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping)
       if (sec == NULL || sec->size == 0)
        continue;
 
-      if (sec->sec_info_type == ELF_INFO_TYPE_JUST_SYMS)
+      if (sec->sec_info_type == SEC_INFO_TYPE_JUST_SYMS)
        continue;
 
       if (sec->output_section == bfd_abs_section_ptr)
@@ -8077,8 +8123,8 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
       toc = bfd_get_section_by_name (ibfd, ".toc");
       if (toc == NULL
          || toc->size == 0
-         || toc->sec_info_type == ELF_INFO_TYPE_JUST_SYMS
-         || elf_discarded_section (toc))
+         || toc->sec_info_type == SEC_INFO_TYPE_JUST_SYMS
+         || discarded_section (toc))
        continue;
 
       toc_relocs = NULL;
@@ -8091,7 +8137,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
       for (sec = ibfd->sections; sec != NULL; sec = sec->next)
        {
          if (sec->reloc_count == 0
-             || !elf_discarded_section (sec)
+             || !discarded_section (sec)
              || get_opd_info (sec)
              || (sec->flags & SEC_ALLOC) == 0
              || (sec->flags & SEC_DEBUGGING) != 0)
@@ -8201,7 +8247,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                goto error_ret;
 
              if (sym_sec == NULL
-                 || elf_discarded_section (sym_sec))
+                 || discarded_section (sym_sec))
                continue;
 
              if (!SYMBOL_CALLS_LOCAL (info, h))
@@ -8281,7 +8327,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
          int repeat;
 
          if (sec->reloc_count == 0
-             || elf_discarded_section (sec)
+             || discarded_section (sec)
              || get_opd_info (sec)
              || (sec->flags & SEC_ALLOC) == 0
              || (sec->flags & SEC_DEBUGGING) != 0)
@@ -8503,7 +8549,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
          for (sec = ibfd->sections; sec != NULL; sec = sec->next)
            {
              if (sec->reloc_count == 0
-                 || elf_discarded_section (sec))
+                 || discarded_section (sec))
                continue;
 
              relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
@@ -9471,21 +9517,126 @@ ppc_type_of_stub (asection *input_sec,
   return ppc_stub_none;
 }
 
-/* Build a .plt call stub.  */
+/* 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
+   barrier in the call stub.  One solution is to make the load of the
+   toc word seem to appear to depend on the load of the function entry
+   word.  Another solution is to test for r2 being zero, and branch to
+   the appropriate glink entry if so.
+
+   .   fake dep barrier        compare
+   .   ld 11,xxx(2)            ld 11,xxx(2)
+   .   mtctr 11                mtctr 11
+   .   xor 11,11,11            ld 2,xxx+8(2)
+   .   add 2,2,11              cmpldi 2,0
+   .   ld 2,xxx+8(2)           bnectr+
+   .   bctr                    b <glink_entry>
+
+   The solution involving the compare turns out to be faster, so
+   that's what we use unless the branch won't reach.  */
+
+#define ALWAYS_USE_FAKE_DEP 0
+#define ALWAYS_EMIT_R2SAVE 0
 
-static inline bfd_byte *
-build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r,
-               bfd_boolean plt_static_chain)
-{
 #define PPC_LO(v) ((v) & 0xffff)
 #define PPC_HI(v) (((v) >> 16) & 0xffff)
 #define PPC_HA(v) PPC_HI ((v) + 0x8000)
 
+static inline unsigned int
+plt_stub_size (struct ppc_link_hash_table *htab,
+              struct ppc_stub_hash_entry *stub_entry,
+              bfd_vma off)
+{
+  unsigned size = PLT_CALL_STUB_SIZE;
+
+  if (!(ALWAYS_EMIT_R2SAVE
+       || stub_entry->stub_type == ppc_stub_plt_call_r2save))
+    size -= 4;
+  if (!htab->plt_static_chain)
+    size -= 4;
+  if (htab->plt_thread_safe)
+    size += 8;
+  if (PPC_HA (off) == 0)
+    size -= 4;
+  if (PPC_HA (off + 8 + 8 * htab->plt_static_chain) != PPC_HA (off))
+    size += 4;
+  if (stub_entry->h != NULL
+      && (stub_entry->h == htab->tls_get_addr_fd
+         || stub_entry->h == htab->tls_get_addr)
+      && !htab->no_tls_get_addr_opt)
+    size += 13 * 4;
+  return size;
+}
+
+/* If this stub would cross fewer 2**plt_stub_align boundaries if we align,
+   then return the padding needed to do so.  */
+static inline unsigned int
+plt_stub_pad (struct ppc_link_hash_table *htab,
+             struct ppc_stub_hash_entry *stub_entry,
+             bfd_vma plt_off)
+{
+  int stub_align = 1 << htab->plt_stub_align;
+  unsigned stub_size = plt_stub_size (htab, stub_entry, plt_off);
+  bfd_vma stub_off = stub_entry->stub_sec->size;
+
+  if (((stub_off + stub_size - 1) & -stub_align) - (stub_off & -stub_align)
+      > (stub_size & -stub_align))
+    return stub_align - (stub_off & (stub_align - 1));
+  return 0;
+}
+
+/* Build a .plt call stub.  */
+
+static inline bfd_byte *
+build_plt_stub (struct ppc_link_hash_table *htab,
+               struct ppc_stub_hash_entry *stub_entry,
+               bfd_byte *p, bfd_vma offset, Elf_Internal_Rela *r)
+{
+  bfd *obfd = htab->stub_bfd;
+  bfd_boolean plt_static_chain = htab->plt_static_chain;
+  bfd_boolean plt_thread_safe = htab->plt_thread_safe;
+  bfd_boolean use_fake_dep = plt_thread_safe;
+  bfd_vma cmp_branch_off = 0;
+
+  if (!ALWAYS_USE_FAKE_DEP
+      && plt_thread_safe
+      && !(stub_entry->h != NULL
+          && (stub_entry->h == htab->tls_get_addr_fd
+              || stub_entry->h == htab->tls_get_addr)
+          && !htab->no_tls_get_addr_opt))
+    {
+      bfd_vma pltoff = stub_entry->plt_ent->plt.offset & ~1;
+      bfd_vma pltindex = (pltoff - PLT_INITIAL_ENTRY_SIZE) / PLT_ENTRY_SIZE;
+      bfd_vma glinkoff = GLINK_CALL_STUB_SIZE + pltindex * 8;
+      bfd_vma to, from;
+
+      if (pltindex > 32767)
+       glinkoff += (pltindex - 32767) * 4;
+      to = (glinkoff
+           + htab->glink->output_offset
+           + htab->glink->output_section->vma);
+      from = (p - stub_entry->stub_sec->contents
+             + 4 * (ALWAYS_EMIT_R2SAVE
+                    || stub_entry->stub_type == ppc_stub_plt_call_r2save)
+             + 4 * (PPC_HA (offset) != 0)
+             + 4 * (PPC_HA (offset + 8 + 8 * plt_static_chain)
+                    != PPC_HA (offset))
+             + 4 * (plt_static_chain != 0)
+             + 20
+             + stub_entry->stub_sec->output_offset
+             + stub_entry->stub_sec->output_section->vma);
+      cmp_branch_off = to - from;
+      use_fake_dep = cmp_branch_off + (1 << 25) >= (1 << 26);
+    }
+
   if (PPC_HA (offset) != 0)
     {
       if (r != NULL)
        {
-         r[0].r_offset += 4;
+         if (ALWAYS_EMIT_R2SAVE
+             || stub_entry->stub_type == ppc_stub_plt_call_r2save)
+           r[0].r_offset += 4;
          r[0].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_HA);
          r[1].r_offset = r[0].r_offset + 4;
          r[1].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO_DS);
@@ -9498,7 +9649,7 @@ build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r,
            }
          else
            {
-             r[2].r_offset = r[1].r_offset + 8;
+             r[2].r_offset = r[1].r_offset + 8 + 8 * use_fake_dep;
              r[2].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_LO_DS);
              r[2].r_addend = r[0].r_addend + 8;
              if (plt_static_chain)
@@ -9509,7 +9660,9 @@ build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r,
                }
            }
        }
-      bfd_put_32 (obfd, STD_R2_40R1, p),                       p += 4;
+      if (ALWAYS_EMIT_R2SAVE
+         || stub_entry->stub_type == ppc_stub_plt_call_r2save)
+       bfd_put_32 (obfd, STD_R2_40R1, p),                      p += 4;
       bfd_put_32 (obfd, ADDIS_R12_R2 | PPC_HA (offset), p),    p += 4;
       bfd_put_32 (obfd, LD_R11_0R12 | PPC_LO (offset), p),     p += 4;
       if (PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset))
@@ -9518,16 +9671,22 @@ build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r,
          offset = 0;
        }
       bfd_put_32 (obfd, MTCTR_R11, p),                         p += 4;
+      if (use_fake_dep)
+       {
+         bfd_put_32 (obfd, XOR_R11_R11_R11, p),                p += 4;
+         bfd_put_32 (obfd, ADD_R12_R12_R11, p),                p += 4;
+       }
       bfd_put_32 (obfd, LD_R2_0R12 | PPC_LO (offset + 8), p),  p += 4;
       if (plt_static_chain)
        bfd_put_32 (obfd, LD_R11_0R12 | PPC_LO (offset + 16), p), p += 4;
-      bfd_put_32 (obfd, BCTR, p),                              p += 4;
     }
   else
     {
       if (r != NULL)
        {
-         r[0].r_offset += 4;
+         if (ALWAYS_EMIT_R2SAVE
+             || stub_entry->stub_type == ppc_stub_plt_call_r2save)
+           r[0].r_offset += 4;
          r[0].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_DS);
          if (PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset))
            {
@@ -9537,7 +9696,7 @@ build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r,
            }
          else
            {
-             r[1].r_offset = r[0].r_offset + 8;
+             r[1].r_offset = r[0].r_offset + 8 + 8 * use_fake_dep;
              r[1].r_info = ELF64_R_INFO (0, R_PPC64_TOC16_DS);
              r[1].r_addend = r[0].r_addend + 8 + 8 * plt_static_chain;
              if (plt_static_chain)
@@ -9548,7 +9707,9 @@ build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r,
                }
            }
        }
-      bfd_put_32 (obfd, STD_R2_40R1, p),                       p += 4;
+      if (ALWAYS_EMIT_R2SAVE
+         || stub_entry->stub_type == ppc_stub_plt_call_r2save)
+       bfd_put_32 (obfd, STD_R2_40R1, p),                      p += 4;
       bfd_put_32 (obfd, LD_R11_0R2 | PPC_LO (offset), p),      p += 4;
       if (PPC_HA (offset + 8 + 8 * plt_static_chain) != PPC_HA (offset))
        {
@@ -9556,11 +9717,23 @@ build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r,
          offset = 0;
        }
       bfd_put_32 (obfd, MTCTR_R11, p),                         p += 4;
+      if (use_fake_dep)
+       {
+         bfd_put_32 (obfd, XOR_R11_R11_R11, p),                p += 4;
+         bfd_put_32 (obfd, ADD_R2_R2_R11, p),                  p += 4;
+       }
       if (plt_static_chain)
        bfd_put_32 (obfd, LD_R11_0R2 | PPC_LO (offset + 16), p), p += 4;
       bfd_put_32 (obfd, LD_R2_0R2 | PPC_LO (offset + 8), p),   p += 4;
-      bfd_put_32 (obfd, BCTR, p),                              p += 4;
     }
+  if (plt_thread_safe && !use_fake_dep)
+    {
+      bfd_put_32 (obfd, CMPLDI_R2_0, p),                       p += 4;
+      bfd_put_32 (obfd, BNECTR_P4, p),                         p += 4;
+      bfd_put_32 (obfd, B_DOT + cmp_branch_off, p),            p += 4;
+    }
+  else
+    bfd_put_32 (obfd, BCTR, p),                                        p += 4;
   return p;
 }
 
@@ -9581,9 +9754,12 @@ build_plt_stub (bfd *obfd, bfd_byte *p, int offset, Elf_Internal_Rela *r,
 #define MTLR_R11       0x7d6803a6
 
 static inline bfd_byte *
-build_tls_get_addr_stub (bfd *obfd, bfd_byte *p, int offset,
-                        Elf_Internal_Rela *r, bfd_boolean plt_static_chain)
+build_tls_get_addr_stub (struct ppc_link_hash_table *htab,
+                        struct ppc_stub_hash_entry *stub_entry,
+                        bfd_byte *p, bfd_vma offset, Elf_Internal_Rela *r)
 {
+  bfd *obfd = htab->stub_bfd;
+
   bfd_put_32 (obfd, LD_R11_0R3 + 0, p),                p += 4;
   bfd_put_32 (obfd, LD_R12_0R3 + 8, p),                p += 4;
   bfd_put_32 (obfd, MR_R0_R3, p),              p += 4;
@@ -9596,7 +9772,7 @@ build_tls_get_addr_stub (bfd *obfd, bfd_byte *p, int offset,
 
   if (r != NULL)
     r[0].r_offset += 9 * 4;
-  p = build_plt_stub (obfd, p, offset, r, plt_static_chain);
+  p = build_plt_stub (htab, stub_entry, p, offset, r);
   bfd_put_32 (obfd, BCTRL, p - 4);
 
   bfd_put_32 (obfd, LD_R11_0R1 + 32, p),       p += 4;
@@ -9943,6 +10119,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       break;
 
     case ppc_stub_plt_call:
+    case ppc_stub_plt_call_r2save:
       if (stub_entry->h != NULL
          && stub_entry->h->is_func_descriptor
          && stub_entry->h->oh != NULL)
@@ -10009,6 +10186,15 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          return FALSE;
        }
 
+      if (htab->plt_stub_align != 0)
+       {
+         unsigned pad = plt_stub_pad (htab, stub_entry, off);
+
+         stub_entry->stub_sec->size += pad;
+         stub_entry->stub_offset = stub_entry->stub_sec->size;
+         loc += pad;
+       }
+
       r = NULL;
       if (info->emitrelocations)
        {
@@ -10028,11 +10214,9 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          && (stub_entry->h == htab->tls_get_addr_fd
              || stub_entry->h == htab->tls_get_addr)
          && !htab->no_tls_get_addr_opt)
-       p = build_tls_get_addr_stub (htab->stub_bfd, loc, off, r,
-                                    htab->plt_static_chain);
+       p = build_tls_get_addr_stub (htab, stub_entry, loc, off, r);
       else
-       p = build_plt_stub (htab->stub_bfd, loc, off, r,
-                           htab->plt_static_chain);
+       p = build_plt_stub (htab, stub_entry, loc, off, r);
       size = p - loc;
       break;
 
@@ -10052,6 +10236,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
                                       "long_branch_r2off",
                                       "plt_branch",
                                       "plt_branch_r2off",
+                                      "plt_call",
                                       "plt_call" };
 
       len1 = strlen (stub_str[stub_entry->stub_type - 1]);
@@ -10102,7 +10287,8 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
   if (htab == NULL)
     return FALSE;
 
-  if (stub_entry->stub_type == ppc_stub_plt_call)
+  if (stub_entry->stub_type == ppc_stub_plt_call
+      || stub_entry->stub_type == ppc_stub_plt_call_r2save)
     {
       asection *plt;
       off = stub_entry->plt_ent->plt.offset & ~(bfd_vma) 1;
@@ -10118,18 +10304,9 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
              - elf_gp (plt->output_section->owner)
              - htab->stub_group[stub_entry->id_sec->id].toc_off);
 
-      size = PLT_CALL_STUB_SIZE;
-      if (!htab->plt_static_chain)
-       size -= 4;
-      if (PPC_HA (off) == 0)
-       size -= 4;
-      if (PPC_HA (off + 8 + 8 * htab->plt_static_chain) != PPC_HA (off))
-       size += 4;
-      if (stub_entry->h != NULL
-         && (stub_entry->h == htab->tls_get_addr_fd
-             || stub_entry->h == htab->tls_get_addr)
-         && !htab->no_tls_get_addr_opt)
-       size += 13 * 4;
+      size = plt_stub_size (htab, stub_entry, off);
+      if (htab->plt_stub_align)
+       size += plt_stub_pad (htab, stub_entry, off);
       if (info->emitrelocations)
        {
          stub_entry->stub_sec->reloc_count
@@ -11098,7 +11275,8 @@ maybe_strip_output (struct bfd_link_info *info, asection *isec)
 
 bfd_boolean
 ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
-                     bfd_boolean plt_static_chain)
+                     bfd_boolean plt_static_chain, int plt_thread_safe,
+                     int plt_stub_align)
 {
   bfd_size_type stub_group_size;
   bfd_boolean stubs_always_before_branch;
@@ -11108,6 +11286,40 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
     return FALSE;
 
   htab->plt_static_chain = plt_static_chain;
+  htab->plt_stub_align = plt_stub_align;
+  if (plt_thread_safe == -1)
+    {
+      const char *const thread_starter[] =
+       {
+         "pthread_create",
+         /* libstdc++ */
+         "_ZNSt6thread15_M_start_threadESt10shared_ptrINS_10_Impl_baseEE",
+         /* librt */
+         "aio_init", "aio_read", "aio_write", "aio_fsync", "lio_listio",
+         "mq_notify", "create_timer",
+         /* libanl */
+         "getaddrinfo_a",
+         /* libgomp */
+         "GOMP_parallel_start",
+         "GOMP_parallel_loop_static_start",
+         "GOMP_parallel_loop_dynamic_start",
+         "GOMP_parallel_loop_guided_start",
+         "GOMP_parallel_loop_runtime_start",
+         "GOMP_parallel_sections_start", 
+       };
+      unsigned i;
+
+      for (i = 0; i < sizeof (thread_starter)/ sizeof (thread_starter[0]); i++)
+       {
+         struct elf_link_hash_entry *h;
+         h = elf_link_hash_lookup (&htab->elf, thread_starter[i],
+                                   FALSE, FALSE, TRUE);
+         plt_thread_safe = h != NULL && h->ref_regular;
+         if (plt_thread_safe)
+           break;
+       }
+    }
+  htab->plt_thread_safe = plt_thread_safe;
   stubs_always_before_branch = group_size < 0;
   if (group_size < 0)
     stub_group_size = -group_size;
@@ -11342,10 +11554,14 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
                  if (stub_type == ppc_stub_plt_call
                      && irela + 1 < irelaend
                      && irela[1].r_offset == irela->r_offset + 4
-                     && ELF64_R_TYPE (irela[1].r_info) == R_PPC64_TOCSAVE
-                     && !tocsave_find (htab, INSERT,
-                                       &local_syms, irela + 1, input_bfd))
-                   goto error_ret_free_internal;
+                     && ELF64_R_TYPE (irela[1].r_info) == R_PPC64_TOCSAVE)
+                   {
+                     if (!tocsave_find (htab, INSERT,
+                                        &local_syms, irela + 1, input_bfd))
+                       goto error_ret_free_internal;
+                   }
+                 else if (stub_type == ppc_stub_plt_call)
+                   stub_type = ppc_stub_plt_call_r2save;
 
                  /* Support for grouping stub sections.  */
                  id_sec = htab->stub_group[section->id].link_sec;
@@ -11361,6 +11577,8 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
                    {
                      /* The proper stub has already been created.  */
                      free (stub_name);
+                     if (stub_type == ppc_stub_plt_call_r2save)
+                       stub_entry->stub_type = stub_type;
                      continue;
                    }
 
@@ -11380,7 +11598,8 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
                    }
 
                  stub_entry->stub_type = stub_type;
-                 if (stub_type != ppc_stub_plt_call)
+                 if (stub_type != ppc_stub_plt_call
+                     && stub_type != ppc_stub_plt_call_r2save)
                    {
                      stub_entry->target_value = code_value;
                      stub_entry->target_section = code_sec;
@@ -11460,6 +11679,14 @@ ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size,
          htab->glink_eh_frame->size = size;
        }
 
+      if (htab->plt_stub_align != 0)
+       for (stub_sec = htab->stub_bfd->sections;
+            stub_sec != NULL;
+            stub_sec = stub_sec->next)
+         if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
+           stub_sec->size = ((stub_sec->size + (1 << htab->plt_stub_align) - 1)
+                             & (-1 << htab->plt_stub_align));
+
       for (stub_sec = htab->stub_bfd->sections;
           stub_sec != NULL;
           stub_sec = stub_sec->next)
@@ -11785,6 +12012,14 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms,
   if (htab->relbrlt != NULL)
     htab->relbrlt->reloc_count = 0;
 
+  if (htab->plt_stub_align != 0)
+    for (stub_sec = htab->stub_bfd->sections;
+        stub_sec != NULL;
+        stub_sec = stub_sec->next)
+      if ((stub_sec->flags & SEC_LINKER_CREATED) == 0)
+       stub_sec->size = ((stub_sec->size + (1 << htab->plt_stub_align) - 1)
+                         & (-1 << htab->plt_stub_align));
+
   for (stub_sec = htab->stub_bfd->sections;
        stub_sec != NULL;
        stub_sec = stub_sec->next)
@@ -11818,14 +12053,16 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms,
                         "  toc adjust   %lu\n"
                         "  long branch  %lu\n"
                         "  long toc adj %lu\n"
-                        "  plt call     %lu"),
+                        "  plt call     %lu\n"
+                        "  plt call toc %lu"),
               stub_sec_count,
               stub_sec_count == 1 ? "" : "s",
               htab->stub_count[ppc_stub_long_branch - 1],
               htab->stub_count[ppc_stub_long_branch_r2off - 1],
               htab->stub_count[ppc_stub_plt_branch - 1],
               htab->stub_count[ppc_stub_plt_branch_r2off - 1],
-              htab->stub_count[ppc_stub_plt_call - 1]);
+              htab->stub_count[ppc_stub_plt_call - 1],
+              htab->stub_count[ppc_stub_plt_call_r2save - 1]);
     }
   return TRUE;
 }
@@ -11925,8 +12162,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
   bfd_vma TOCstart;
   bfd_boolean ret = TRUE;
   bfd_boolean is_opd;
-  /* Disabled until we sort out how ld should choose 'y' vs 'at'.  */
-  bfd_boolean is_power4 = FALSE;
+  /* Assume 'at' branch hints.  */
+  bfd_boolean is_isa_v2 = TRUE;
   bfd_vma d_offset = (bfd_big_endian (output_bfd) ? 2 : 0);
 
   /* Initialize howto table if needed.  */
@@ -12036,10 +12273,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        }
       h = (struct ppc_link_hash_entry *) h_elf;
 
-      if (sec != NULL && elf_discarded_section (sec))
+      if (sec != NULL && discarded_section (sec))
        RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
-                                        rel, relend,
-                                        ppc64_elf_howto_table[r_type],
+                                        rel, 1, relend,
+                                        ppc64_elf_howto_table[r_type], 0,
                                         contents);
 
       if (info->relocatable)
@@ -12558,6 +12795,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          stub_entry = ppc_get_stub_entry (input_section, sec, fdh, rel, htab);
          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_branch_r2off
                  || stub_entry->stub_type == ppc_stub_long_branch_r2off))
            {
@@ -12586,7 +12824,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
              if (!can_plt_call)
                {
-                 if (stub_entry->stub_type == ppc_stub_plt_call)
+                 if (stub_entry->stub_type == ppc_stub_plt_call
+                     || stub_entry->stub_type == ppc_stub_plt_call_r2save)
                    {
                      /* If this is a plain branch rather than a branch
                         and link, don't require a nop.  However, don't
@@ -12633,7 +12872,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
+                     || stub_entry->stub_type == ppc_stub_plt_call_r2save))
                unresolved_reloc = FALSE;
            }
 
@@ -12679,7 +12919,10 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                            + stub_entry->stub_sec->output_section->vma);
              addend = 0;
 
-             if (stub_entry->stub_type == ppc_stub_plt_call
+             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)
                  && rel + 1 < relend
                  && rel[1].r_offset == rel->r_offset + 4
                  && ELF64_R_TYPE (rel[1].r_info) == R_PPC64_TOCSAVE)
@@ -12688,7 +12931,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
          if (insn != 0)
            {
-             if (is_power4)
+             if (is_isa_v2)
                {
                  /* Set 'a' bit.  This is 0b00010 in BO field for branch
                     on CR(BI) insns (BO == 001at or 011at), and 0b01000
@@ -13867,7 +14110,7 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
 
 
   if (htab->glink_eh_frame != NULL
-      && htab->glink_eh_frame->sec_info_type == ELF_INFO_TYPE_EH_FRAME
+      && 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))
This page took 0.059544 seconds and 4 git commands to generate.