bfd/
[deliverable/binutils-gdb.git] / bfd / elf64-ppc.c
index 3259e2e719856f6fed14b849162af313cb227bcf..2cdb0a265a14bb0f44eb1ae4eee7b9d166daafc6 100644 (file)
@@ -61,6 +61,7 @@ static bfd_vma opd_entry_value
 #define TARGET_BIG_SYM         bfd_elf64_powerpc_vec
 #define TARGET_BIG_NAME                "elf64-powerpc"
 #define ELF_ARCH               bfd_arch_powerpc
+#define ELF_TARGET_ID          PPC64_ELF_DATA
 #define ELF_MACHINE_CODE       EM_PPC64
 #define ELF_MAXPAGESIZE                0x10000
 #define ELF_COMMONPAGESIZE     0x1000
@@ -2651,7 +2652,7 @@ ppc64_elf_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
   elf_tdata (abfd)->core_signal = bfd_get_16 (abfd, note->descdata + 12);
 
   /* pr_pid */
-  elf_tdata (abfd)->core_pid = bfd_get_32 (abfd, note->descdata + 32);
+  elf_tdata (abfd)->core_lwpid = bfd_get_32 (abfd, note->descdata + 32);
 
   /* pr_reg */
   offset = 112;
@@ -4844,7 +4845,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 {
   struct ppc_link_hash_table *htab;
   Elf_Internal_Shdr *symtab_hdr;
-  struct elf_link_hash_entry **sym_hashes, **sym_hashes_end;
+  struct elf_link_hash_entry **sym_hashes;
   const Elf_Internal_Rela *rel;
   const Elf_Internal_Rela *rel_end;
   asection *sreloc;
@@ -4874,12 +4875,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
   dottga = elf_link_hash_lookup (&htab->elf, ".__tls_get_addr",
                                 FALSE, FALSE, TRUE);
   symtab_hdr = &elf_symtab_hdr (abfd);
-
   sym_hashes = elf_sym_hashes (abfd);
-  sym_hashes_end = (sym_hashes
-                   + symtab_hdr->sh_size / sizeof (Elf64_External_Sym)
-                   - symtab_hdr->sh_info);
-
   sreloc = NULL;
   opd_sym_map = NULL;
   if (strcmp (sec->name, ".opd") == 0)
@@ -5566,6 +5562,17 @@ opd_entry_value (asection *opd_sec,
   return val;
 }
 
+/* Return true if symbol is defined in a regular object file.  */
+
+static bfd_boolean
+is_static_defined (struct elf_link_hash_entry *h)
+{
+  return ((h->root.type == bfd_link_hash_defined
+          || h->root.type == bfd_link_hash_defweak)
+         && h->root.u.def.section != NULL
+         && h->root.u.def.section->output_section != NULL);
+}
+
 /* If FDH is a function descriptor symbol, return the associated code
    entry symbol if it is defined.  Return NULL otherwise.  */
 
@@ -6683,6 +6690,7 @@ get_tls_mask (unsigned char **tls_maskp,
 
   if ((*tls_maskp != NULL && **tls_maskp != 0)
       || sec == NULL
+      || ppc64_elf_section_data (sec) == NULL
       || ppc64_elf_section_data (sec)->sec_type != sec_toc)
     return 1;
 
@@ -6704,10 +6712,7 @@ get_tls_mask (unsigned char **tls_maskp,
     *toc_addend = ppc64_elf_section_data (sec)->u.toc.add[off / 8];
   if (!get_sym_h (&h, &sym, &sec, tls_maskp, locsymsp, r_symndx, ibfd))
     return 0;
-  if ((h == NULL
-       || ((h->root.type == bfd_link_hash_defined
-           || h->root.type == bfd_link_hash_defweak)
-          && !h->def_dynamic))
+  if ((h == NULL || is_static_defined (h))
       && (next_r == -1 || next_r == -2))
     return 1 - next_r;
   return 1;
@@ -6918,12 +6923,14 @@ ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping)
       Elf_Internal_Rela *relstart, *rel, *relend;
       Elf_Internal_Shdr *symtab_hdr;
       Elf_Internal_Sym *local_syms;
-      struct elf_link_hash_entry **sym_hashes;
       bfd_vma offset;
       struct _opd_sec_data *opd;
       bfd_boolean need_edit, add_aux_fields;
       bfd_size_type cnt_16b = 0;
 
+      if (!is_ppc64_elf (ibfd))
+       continue;
+
       sec = bfd_get_section_by_name (ibfd, ".opd");
       if (sec == NULL || sec->size == 0)
        continue;
@@ -6940,7 +6947,6 @@ ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping)
 
       local_syms = NULL;
       symtab_hdr = &elf_symtab_hdr (ibfd);
-      sym_hashes = elf_sym_hashes (ibfd);
 
       /* Read the relocations.  */
       relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
@@ -7067,6 +7073,7 @@ ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping)
       if (need_edit || add_aux_fields)
        {
          Elf_Internal_Rela *write_rel;
+         Elf_Internal_Shdr *rel_hdr;
          bfd_byte *rptr, *wptr;
          bfd_byte *new_contents;
          bfd_boolean skip;
@@ -7246,9 +7253,8 @@ ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping)
 
          /* Fudge the header size too, as this is used later in
             elf_bfd_final_link if we are emitting relocs.  */
-         elf_section_data (sec)->rel_hdr.sh_size
-           = sec->reloc_count * elf_section_data (sec)->rel_hdr.sh_entsize;
-         BFD_ASSERT (elf_section_data (sec)->rel_hdr2 == NULL);
+         rel_hdr = _bfd_elf_single_rel_hdr (sec);
+         rel_hdr->sh_size = sec->reloc_count * rel_hdr->sh_entsize;
          some_edited = TRUE;
        }
       else if (elf_section_data (sec)->relocs != relstart)
@@ -7375,7 +7381,7 @@ ppc64_elf_tls_setup (struct bfd_link_info *info,
                      _bfd_elf_strtab_delref (elf_hash_table (info)->dynstr,
                                              opt_fd->dynstr_index);
                      if (!bfd_elf_link_record_dynamic_symbol (info, opt_fd))
-                       return FALSE;
+                       return NULL;
                    }
                  htab->tls_get_addr_fd = (struct ppc_link_hash_entry *) opt_fd;
                  tga = &htab->tls_get_addr->elf;
@@ -7835,11 +7841,14 @@ struct adjust_toc_info
   bfd_boolean global_toc_syms;
 };
 
+enum toc_skip_enum { ref_from_discarded = 1, can_optimize = 2 };
+
 static bfd_boolean
 adjust_toc_syms (struct elf_link_hash_entry *h, void *inf)
 {
   struct ppc_link_hash_entry *eh;
   struct adjust_toc_info *toc_inf = (struct adjust_toc_info *) inf;
+  unsigned long i;
 
   if (h->root.type == bfd_link_hash_indirect)
     return TRUE;
@@ -7857,16 +7866,22 @@ adjust_toc_syms (struct elf_link_hash_entry *h, void *inf)
 
   if (eh->elf.root.u.def.section == toc_inf->toc)
     {
-      unsigned long skip = toc_inf->skip[eh->elf.root.u.def.value >> 3];
-      if (skip != (unsigned long) -1)
-       eh->elf.root.u.def.value -= skip;
+      if (eh->elf.root.u.def.value > toc_inf->toc->rawsize)
+       i = toc_inf->toc->rawsize >> 3;
       else
+       i = eh->elf.root.u.def.value >> 3;
+
+      if ((toc_inf->skip[i] & (ref_from_discarded | can_optimize)) != 0)
        {
          (*_bfd_error_handler)
-           (_("%s defined in removed toc entry"), eh->elf.root.root.string);
-         eh->elf.root.u.def.section = &bfd_abs_section;
-         eh->elf.root.u.def.value = 0;
+           (_("%s defined on removed toc entry"), eh->elf.root.root.string);
+         do
+           ++i;
+         while ((toc_inf->skip[i] & (ref_from_discarded | can_optimize)) != 0);
+         eh->elf.root.u.def.value = (bfd_vma) i << 3;
        }
+
+      eh->elf.root.u.def.value -= toc_inf->skip[i];
       eh->adjust_done = 1;
     }
   else if (strcmp (eh->elf.root.u.def.section->name, ".toc") == 0)
@@ -7892,12 +7907,14 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
       asection *toc, *sec;
       Elf_Internal_Shdr *symtab_hdr;
       Elf_Internal_Sym *local_syms;
-      struct elf_link_hash_entry **sym_hashes;
       Elf_Internal_Rela *relstart, *rel;
       unsigned long *skip, *drop;
       unsigned char *used;
       unsigned char *keep, last, some_unused;
 
+      if (!is_ppc64_elf (ibfd))
+       continue;
+
       toc = bfd_get_section_by_name (ibfd, ".toc");
       if (toc == NULL
          || toc->size == 0
@@ -7907,7 +7924,6 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
 
       local_syms = NULL;
       symtab_hdr = &elf_symtab_hdr (ibfd);
-      sym_hashes = elf_sym_hashes (ibfd);
 
       /* Look at sections dropped from the final link.  */
       skip = NULL;
@@ -7975,18 +7991,100 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
 
              if (skip == NULL)
                {
-                 skip = bfd_zmalloc (sizeof (*skip) * (toc->size + 7) / 8);
+                 skip = bfd_zmalloc (sizeof (*skip) * (toc->size + 15) / 8);
                  if (skip == NULL)
                    goto error_ret;
                }
 
-             skip[val >> 3] = 1;
+             skip[val >> 3] = ref_from_discarded;
            }
 
          if (elf_section_data (sec)->relocs != relstart)
            free (relstart);
        }
 
+      /* For largetoc loads of address constants, we can convert
+        .  addis rx,2,addr@got@ha
+        .  ld ry,addr@got@l(rx)
+        to
+        .  addis rx,2,addr@toc@ha
+        .  addi ry,rx,addr@toc@l
+        when addr is within 2G of the toc pointer.  This then means
+        that the word storing "addr" in the toc is no longer needed.  */
+        
+      if (!ppc64_elf_tdata (ibfd)->has_small_toc_reloc
+         && toc->output_section->rawsize < (bfd_vma) 1 << 31
+         && toc->reloc_count != 0)
+       {
+         /* Read toc relocs.  */
+         relstart = _bfd_elf_link_read_relocs (ibfd, toc, NULL, NULL,
+                                               info->keep_memory);
+         if (relstart == NULL)
+           goto error_ret;
+
+         for (rel = relstart; rel < relstart + toc->reloc_count; ++rel)
+           {
+             enum elf_ppc64_reloc_type r_type;
+             unsigned long r_symndx;
+             asection *sym_sec;
+             struct elf_link_hash_entry *h;
+             Elf_Internal_Sym *sym;
+             bfd_vma val, addr;
+
+             r_type = ELF64_R_TYPE (rel->r_info);
+             if (r_type != R_PPC64_ADDR64)
+               continue;
+
+             r_symndx = ELF64_R_SYM (rel->r_info);
+             if (!get_sym_h (&h, &sym, &sym_sec, NULL, &local_syms,
+                             r_symndx, ibfd))
+               goto error_ret;
+
+             if (!SYMBOL_CALLS_LOCAL (info, h))
+               continue;
+
+             if (h != NULL)
+               {
+                 if (h->type == STT_GNU_IFUNC)
+                   continue;
+                 val = h->root.u.def.value;
+               }
+             else
+               {
+                 if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
+                   continue;
+                 val = sym->st_value;
+               }
+             val += rel->r_addend;
+             val += sym_sec->output_section->vma + sym_sec->output_offset;
+
+             /* We don't yet know the exact toc pointer value, but we
+                know it will be somewhere in the toc section.  Don't
+                optimize if the difference from any possible toc
+                pointer is outside [ff..f80008000, 7fff7fff].  */
+             addr = toc->output_section->vma + TOC_BASE_OFF;
+             if (val - addr + (bfd_vma) 0x80008000 >= (bfd_vma) 1 << 32)
+               continue;
+
+             addr = toc->output_section->vma + toc->output_section->rawsize;
+             if (val - addr + (bfd_vma) 0x80008000 >= (bfd_vma) 1 << 32)
+               continue;
+
+             if (skip == NULL)
+               {
+                 skip = bfd_zmalloc (sizeof (*skip) * (toc->size + 15) / 8);
+                 if (skip == NULL)
+                   goto error_ret;
+               }
+
+             skip[rel->r_offset >> 3]
+               |= can_optimize | ((rel - relstart) << 2);
+           }
+
+         if (elf_section_data (toc)->relocs != relstart)
+           free (relstart);
+       }
+
       if (skip == NULL)
        continue;
 
@@ -8025,7 +8123,8 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
              || (sec->flags & SEC_DEBUGGING) != 0)
            continue;
 
-         relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL, TRUE);
+         relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
+                                               info->keep_memory);
          if (relstart == NULL)
            goto error_ret;
 
@@ -8078,12 +8177,37 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                if (val >= toc->size)
                  continue;
 
+               if ((skip[val >> 3] & can_optimize) != 0)
+                 {
+                   bfd_vma off;
+                   unsigned char opc;
+
+                   switch (r_type)
+                     {
+                     case R_PPC64_TOC16_HA:
+                       break;
+
+                     case R_PPC64_TOC16_LO_DS:
+                       off = rel->r_offset + (bfd_big_endian (ibfd) ? -2 : 3);
+                       if (!bfd_get_section_contents (ibfd, sec, &opc, off, 1))
+                         return FALSE;
+                       if ((opc & (0x3f << 2)) == (58u << 2))
+                         break;
+                       /* Fall thru */
+
+                     default:
+                       /* Wrong sort of reloc, or not a ld.  We may
+                          as well clear ref_from_discarded too.  */
+                       skip[val >> 3] = 0;
+                     }
+                 }
+
                /* For the toc section, we only mark as used if
                   this entry itself isn't unused.  */
                if (sec == toc
                    && !used[val >> 3]
                    && (used[rel->r_offset >> 3]
-                       || !skip[rel->r_offset >> 3]))
+                       || !(skip[rel->r_offset >> 3] & ref_from_discarded)))
                  /* Do all the relocs again, to catch reference
                     chains.  */
                  repeat = 1;
@@ -8091,6 +8215,9 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                used[val >> 3] = 1;
              }
          while (repeat);
+
+         if (elf_section_data (sec)->relocs != relstart)
+           free (relstart);
        }
 
       /* Merge the used and skip arrays.  Assume that TOC
@@ -8102,13 +8229,15 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
        {
          if (*keep)
            {
-             *drop = 0;
+             *drop &= ~ref_from_discarded;
+             if ((*drop & can_optimize) != 0)
+               some_unused = 1;
              last = 0;
            }
          else if (*drop)
            {
              some_unused = 1;
-             last = 1;
+             last = ref_from_discarded;
            }
          else
            *drop = last;
@@ -8120,6 +8249,8 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
        {
          bfd_byte *contents, *src;
          unsigned long off;
+         Elf_Internal_Sym *sym;
+         bfd_boolean local_toc_syms = FALSE;
 
          /* Shuffle the toc contents, and at the same time convert the
             skip array from booleans into offsets.  */
@@ -8132,52 +8263,20 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
               src < contents + toc->size;
               src += 8, ++drop)
            {
-             if (*drop)
-               {
-                 *drop = (unsigned long) -1;
-                 off += 8;
-               }
+             if ((*drop & (can_optimize | ref_from_discarded)) != 0)
+               off += 8;
              else if (off != 0)
                {
                  *drop = off;
                  memcpy (src - off, src, 8);
                }
            }
+         *drop = off;
          toc->rawsize = toc->size;
          toc->size = src - contents - off;
 
-         if (toc->reloc_count != 0)
-           {
-             Elf_Internal_Rela *wrel;
-             bfd_size_type sz;
-
-             /* Read toc relocs.  */
-             relstart = _bfd_elf_link_read_relocs (ibfd, toc, NULL, NULL,
-                                                   TRUE);
-             if (relstart == NULL)
-               goto error_ret;
-
-             /* Remove unused toc relocs, and adjust those we keep.  */
-             wrel = relstart;
-             for (rel = relstart; rel < relstart + toc->reloc_count; ++rel)
-               if (skip[rel->r_offset >> 3] != (unsigned long) -1)
-                 {
-                   wrel->r_offset = rel->r_offset - skip[rel->r_offset >> 3];
-                   wrel->r_info = rel->r_info;
-                   wrel->r_addend = rel->r_addend;
-                   ++wrel;
-                 }
-               else if (!dec_dynrel_count (rel->r_info, toc, info,
-                                           &local_syms, NULL, NULL))
-                 goto error_ret;
-
-             toc->reloc_count = wrel - relstart;
-             sz = elf_section_data (toc)->rel_hdr.sh_entsize;
-             elf_section_data (toc)->rel_hdr.sh_size = toc->reloc_count * sz;
-             BFD_ASSERT (elf_section_data (toc)->rel_hdr2 == NULL);
-           }
-
-         /* Adjust addends for relocs against the toc section sym.  */
+         /* Adjust addends for relocs against the toc section sym,
+            and optimize any accesses we can.  */
          for (sec = ibfd->sections; sec != NULL; sec = sec->next)
            {
              if (sec->reloc_count == 0
@@ -8185,7 +8284,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                continue;
 
              relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
-                                                   TRUE);
+                                                   info->keep_memory);
              if (relstart == NULL)
                goto error_ret;
 
@@ -8195,7 +8294,7 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                  unsigned long r_symndx;
                  asection *sym_sec;
                  struct elf_link_hash_entry *h;
-                 Elf_Internal_Sym *sym;
+                 bfd_vma val;
 
                  r_type = ELF64_R_TYPE (rel->r_info);
                  switch (r_type)
@@ -8218,41 +8317,91 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
                                  r_symndx, ibfd))
                    goto error_ret;
 
-                 if (sym_sec != toc || h != NULL || sym->st_value != 0)
+                 if (sym_sec != toc)
+                   continue;
+
+                 if (h != NULL)
+                   val = h->root.u.def.value;
+                 else
+                   {
+                     val = sym->st_value;
+                     if (val != 0)
+                       local_toc_syms = TRUE;
+                   }
+
+                 val += rel->r_addend;
+
+                 if (val > toc->rawsize)
+                   val = toc->rawsize;
+                 else if ((skip[val >> 3] & ref_from_discarded) != 0)
                    continue;
+                 else if ((skip[val >> 3] & can_optimize) != 0)
+                   {
+                     Elf_Internal_Rela *tocrel
+                       = elf_section_data (toc)->relocs + (skip[val >> 3] >> 2);
+                     unsigned long tsym = ELF64_R_SYM (tocrel->r_info);
 
-                 rel->r_addend -= skip[rel->r_addend >> 3];
+                     switch (r_type)
+                       {
+                       case R_PPC64_TOC16_HA:
+                         rel->r_info = ELF64_R_INFO (tsym, R_PPC64_TOC16_HA);
+                         break;
+
+                       case R_PPC64_TOC16_LO_DS:
+                         rel->r_info = ELF64_R_INFO (tsym, R_PPC64_LO_DS_OPT);
+                         break;
+
+                       default:
+                         abort ();
+                       }
+                     rel->r_addend = tocrel->r_addend;
+                     elf_section_data (sec)->relocs = relstart;
+                     continue;
+                   }
+
+                 if (h != NULL || sym->st_value != 0)
+                   continue;
+
+                 rel->r_addend -= skip[val >> 3];
+                 elf_section_data (sec)->relocs = relstart;
                }
+
+             if (elf_section_data (sec)->relocs != relstart)
+               free (relstart);
            }
 
          /* We shouldn't have local or global symbols defined in the TOC,
             but handle them anyway.  */
-         if (local_syms != NULL)
-           {
-             Elf_Internal_Sym *sym;
+         for (sym = local_syms;
+              sym < local_syms + symtab_hdr->sh_info;
+              ++sym)
+           if (sym->st_value != 0
+               && bfd_section_from_elf_index (ibfd, sym->st_shndx) == toc)
+             {
+               unsigned long i;
+
+               if (sym->st_value > toc->rawsize)
+                 i = toc->rawsize >> 3;
+               else
+                 i = sym->st_value >> 3;
 
-             for (sym = local_syms;
-                  sym < local_syms + symtab_hdr->sh_info;
-                  ++sym)
-               if (sym->st_value != 0
-                   && bfd_section_from_elf_index (ibfd, sym->st_shndx) == toc)
+               if ((skip[i] & (ref_from_discarded | can_optimize)) != 0)
                  {
-                   if (skip[sym->st_value >> 3] != (unsigned long) -1)
-                     sym->st_value -= skip[sym->st_value >> 3];
-                   else
-                     {
-                       (*_bfd_error_handler)
-                         (_("%s defined in removed toc entry"),
-                          bfd_elf_sym_name (ibfd, symtab_hdr, sym,
-                                            NULL));
-                       sym->st_value = 0;
-                       sym->st_shndx = SHN_ABS;
-                     }
-                   symtab_hdr->contents = (unsigned char *) local_syms;
+                   if (local_toc_syms)
+                     (*_bfd_error_handler)
+                       (_("%s defined on removed toc entry"),
+                        bfd_elf_sym_name (ibfd, symtab_hdr, sym, NULL));
+                   do
+                     ++i;
+                   while ((skip[i] & (ref_from_discarded | can_optimize)));
+                   sym->st_value = (bfd_vma) i << 3;
                  }
-           }
 
-         /* Finally, adjust any global syms defined in the toc.  */
+               sym->st_value -= skip[i];
+               symtab_hdr->contents = (unsigned char *) local_syms;
+             }
+
+         /* Adjust any global syms defined in this toc input section.  */
          if (toc_inf.global_toc_syms)
            {
              toc_inf.toc = toc;
@@ -8261,6 +8410,39 @@ ppc64_elf_edit_toc (struct bfd_link_info *info)
              elf_link_hash_traverse (elf_hash_table (info), adjust_toc_syms,
                                      &toc_inf);
            }
+
+         if (toc->reloc_count != 0)
+           {
+             Elf_Internal_Shdr *rel_hdr;
+             Elf_Internal_Rela *wrel;
+             bfd_size_type sz;
+
+             /* Read toc relocs.  */
+             relstart = _bfd_elf_link_read_relocs (ibfd, toc, NULL, NULL,
+                                                   TRUE);
+             if (relstart == NULL)
+               goto error_ret;
+
+             /* Remove unused toc relocs, and adjust those we keep.  */
+             wrel = relstart;
+             for (rel = relstart; rel < relstart + toc->reloc_count; ++rel)
+               if ((skip[rel->r_offset >> 3]
+                    & (ref_from_discarded | can_optimize)) == 0)
+                 {
+                   wrel->r_offset = rel->r_offset - skip[rel->r_offset >> 3];
+                   wrel->r_info = rel->r_info;
+                   wrel->r_addend = rel->r_addend;
+                   ++wrel;
+                 }
+               else if (!dec_dynrel_count (rel->r_info, toc, info,
+                                           &local_syms, NULL, NULL))
+                 goto error_ret;
+
+             toc->reloc_count = wrel - relstart;
+             rel_hdr = _bfd_elf_single_rel_hdr (toc);
+             sz = rel_hdr->sh_entsize;
+             rel_hdr->sh_size = toc->reloc_count * sz;
+           }
        }
 
       if (local_syms != NULL
@@ -9007,12 +9189,8 @@ ppc_type_of_stub (asection *input_sec,
         either a defined function descriptor or a defined entry symbol
         in a regular object file, then it is pointless trying to make
         any other type of stub.  */
-      if (!((fdh->elf.root.type == bfd_link_hash_defined
-           || fdh->elf.root.type == bfd_link_hash_defweak)
-           && fdh->elf.root.u.def.section->output_section != NULL)
-         && !((h->elf.root.type == bfd_link_hash_defined
-               || h->elf.root.type == bfd_link_hash_defweak)
-              && h->elf.root.u.def.section->output_section != NULL))
+      if (!is_static_defined (&fdh->elf)
+         && !is_static_defined (&h->elf))
        return ppc_stub_none;
     }
   else if (elf_local_got_ents (input_sec->owner) != NULL)
@@ -9199,9 +9377,13 @@ get_relocs (asection *sec, int count)
       if (relocs == NULL)
        return NULL;
       elfsec_data->relocs = relocs;
-      elfsec_data->rel_hdr.sh_size = (sec->reloc_count
-                                     * sizeof (Elf64_External_Rela));
-      elfsec_data->rel_hdr.sh_entsize = sizeof (Elf64_External_Rela);
+      elfsec_data->rela.hdr = bfd_zalloc (sec->owner,
+                                         sizeof (Elf_Internal_Shdr));
+      if (elfsec_data->rela.hdr == NULL)
+       return NULL;
+      elfsec_data->rela.hdr->sh_size = (sec->reloc_count
+                                       * sizeof (Elf64_External_Rela));
+      elfsec_data->rela.hdr->sh_entsize = sizeof (Elf64_External_Rela);
       sec->reloc_count = 0;
     }
   relocs += sec->reloc_count;
@@ -10495,7 +10677,8 @@ group_sections (struct ppc_link_hash_table *htab,
 
          curr = tail;
          total = tail->size;
-         big_sec = total > (ppc64_elf_section_data (tail)->has_14bit_branch
+         big_sec = total > (ppc64_elf_section_data (tail) != NULL
+                            && ppc64_elf_section_data (tail)->has_14bit_branch
                             ? stub14_group_size : stub_group_size);
          if (big_sec && !suppress_size_errors)
            (*_bfd_error_handler) (_("%B section %A exceeds stub group size"),
@@ -10504,7 +10687,8 @@ group_sections (struct ppc_link_hash_table *htab,
 
          while ((prev = PREV_SEC (curr)) != NULL
                 && ((total += curr->output_offset - prev->output_offset)
-                    < (ppc64_elf_section_data (prev)->has_14bit_branch
+                    < (ppc64_elf_section_data (prev) != NULL
+                       && ppc64_elf_section_data (prev)->has_14bit_branch
                        ? stub14_group_size : stub_group_size))
                 && htab->stub_group[prev->id].toc_off == curr_toc)
            curr = prev;
@@ -10537,7 +10721,8 @@ group_sections (struct ppc_link_hash_table *htab,
              total = 0;
              while (prev != NULL
                     && ((total += tail->output_offset - prev->output_offset)
-                        < (ppc64_elf_section_data (prev)->has_14bit_branch
+                        < (ppc64_elf_section_data (prev) != NULL
+                           && ppc64_elf_section_data (prev)->has_14bit_branch
                            ? stub14_group_size : stub_group_size))
                     && htab->stub_group[prev->id].toc_off == curr_toc)
                {
@@ -11216,14 +11401,16 @@ ppc64_elf_action_discarded (asection *sec)
   return _bfd_elf_default_action_discarded (sec);
 }
 
-/* REL points to a low-part reloc on a bigtoc instruction sequence.
+/* REL points to a low-part reloc on a largetoc instruction sequence.
    Find the matching high-part reloc instruction and verify that it
-   is addis REG,r2,x.  If so, return a pointer to the high-part reloc.  */
+   is addis REG,x,imm.  If so, set *REG to x and return a pointer to
+   the high-part reloc.  */
 
 static const Elf_Internal_Rela *
 ha_reloc_match (const Elf_Internal_Rela *relocs,
                const Elf_Internal_Rela *rel,
-               unsigned int reg,
+               unsigned int *reg,
+               bfd_boolean match_addend,
                const bfd *input_bfd,
                const bfd_byte *contents)
 {
@@ -11255,14 +11442,17 @@ ha_reloc_match (const Elf_Internal_Rela *relocs,
 
   while (--rel >= relocs)
     if (rel->r_info == r_info_ha
-       && rel->r_addend == r_addend)
+       && (!match_addend
+           || rel->r_addend == r_addend))
       {
        const bfd_byte *p = contents + (rel->r_offset & ~3);
        unsigned int insn = bfd_get_32 (input_bfd, p);
-       if ((insn & ((0x3f << 26) | (0x1f << 16)))
-           == ((15u << 26) | (2 << 16)) /* addis rt,r2,x */
-           && (insn & (0x1f << 21)) == (reg << 21))
-         return rel;
+       if ((insn & (0x3f << 26)) == (15u << 26) /* addis rt,x,imm */
+           && (insn & (0x1f << 21)) == (*reg << 21))
+         {
+           *reg = (insn >> 16) & 0x1f;
+           return rel;
+         }
        break;
       }
   return NULL;
@@ -11315,7 +11505,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
   Elf_Internal_Rela outrel;
   bfd_byte *loc;
   struct got_entry **local_got_ents;
+  unsigned char *ha_opt;
   bfd_vma TOCstart;
+  bfd_boolean no_ha_opt;
   bfd_boolean ret = TRUE;
   bfd_boolean is_opd;
   /* Disabled until we sort out how ld should choose 'y' vs 'at'.  */
@@ -11341,6 +11533,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
   symtab_hdr = &elf_symtab_hdr (input_bfd);
   sym_hashes = elf_sym_hashes (input_bfd);
   is_opd = ppc64_elf_section_data (input_section)->sec_type == sec_opd;
+  ha_opt = NULL;
+  no_ha_opt = FALSE;
 
   rel = relocs;
   relend = relocs + input_section->reloc_count;
@@ -11479,7 +11673,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
       /* Check that tls relocs are used with tls syms, and non-tls
         relocs are used with non-tls syms.  */
-      if (r_symndx != 0
+      if (r_symndx != STN_UNDEF
          && r_type != R_PPC64_NONE
          && (h == NULL
              || h->elf.root.type == bfd_link_hash_defined
@@ -11525,6 +11719,16 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        default:
          break;
 
+       case R_PPC64_LO_DS_OPT:
+         insn = bfd_get_32 (output_bfd, contents + rel->r_offset - d_offset);
+         if ((insn & (0x3f << 26)) != 58u << 26)
+           abort ();
+         insn += (14u << 26) - (58u << 26);
+         bfd_put_32 (output_bfd, insn, contents + rel->r_offset - d_offset);
+         r_type = R_PPC64_TOC16_LO;
+         rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+         break;
+
        case R_PPC64_TOC16:
        case R_PPC64_TOC16_LO:
        case R_PPC64_TOC16_DS:
@@ -11650,8 +11854,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                          + R_PPC64_GOT_TPREL16_DS);
              else
                {
-                 bfd_put_32 (output_bfd, NOP, contents + rel->r_offset);
                  rel->r_offset -= d_offset;
+                 bfd_put_32 (output_bfd, NOP, contents + rel->r_offset);
                  r_type = R_PPC64_NONE;
                }
              rel->r_info = ELF64_R_INFO (r_symndx, r_type);
@@ -11718,9 +11922,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                        if (local_sections[r_symndx] == sec)
                          break;
                      if (r_symndx >= symtab_hdr->sh_info)
-                       r_symndx = 0;
+                       r_symndx = STN_UNDEF;
                      rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET;
-                     if (r_symndx != 0)
+                     if (r_symndx != STN_UNDEF)
                        rel->r_addend -= (local_syms[r_symndx].st_value
                                          + sec->output_offset
                                          + sec->output_section->vma);
@@ -11826,9 +12030,9 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                if (local_sections[r_symndx] == sec)
                  break;
              if (r_symndx >= symtab_hdr->sh_info)
-               r_symndx = 0;
+               r_symndx = STN_UNDEF;
              rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET;
-             if (r_symndx != 0)
+             if (r_symndx != STN_UNDEF)
                rel->r_addend -= (local_syms[r_symndx].st_value
                                  + sec->output_offset
                                  + sec->output_section->vma);
@@ -12182,7 +12386,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                    if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared,
                                                          &h->elf)
                        || (info->shared
-                           && SYMBOL_REFERENCES_LOCAL (info, &h->elf)))
+                           && SYMBOL_CALLS_LOCAL (info, &h->elf)))
                      /* This is actually a static link, or it is a
                         -Bsymbolic link and the symbol is defined
                         locally, or the symbol was forced to be local
@@ -12370,7 +12574,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_TOC:
          /* Relocation value is TOC base.  */
          relocation = TOCstart;
-         if (r_symndx == 0)
+         if (r_symndx == STN_UNDEF)
            relocation += htab->stub_group[input_section->id].toc_off;
          else if (unresolved_reloc)
            ;
@@ -12559,7 +12763,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
              if (skip)
                memset (&outrel, 0, sizeof outrel);
-             else if (!SYMBOL_REFERENCES_LOCAL (info, &h->elf)
+             else if (!SYMBOL_CALLS_LOCAL (info, &h->elf)
                       && !is_opd
                       && r_type != R_PPC64_TOC)
                outrel.r_info = ELF64_R_INFO (h->elf.dynindx, r_type);
@@ -12620,7 +12824,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                             sym_name);
                          ret = FALSE;
                        }
-                     else if (r_symndx == 0 || bfd_is_abs_section (sec))
+                     else if (r_symndx == STN_UNDEF || bfd_is_abs_section (sec))
                        ;
                      else if (sec == NULL || sec->owner == NULL)
                        {
@@ -12756,7 +12960,7 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        case R_PPC64_GOT_DTPREL16_HA:
        case R_PPC64_GOT16_HA:
        case R_PPC64_TOC16_HA:
-         /* For now we don't nop out the first instruction.  */
+         /* nop is done later.  */
          break;
 
        case R_PPC64_GOT_TLSLD16_LO:
@@ -12791,12 +12995,31 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                      && ((insn & 3) == 0 || (insn & 3) == 3)))
                {
                  unsigned int reg = (insn >> 16) & 0x1f;
-                 if (ha_reloc_match (relocs, rel, reg, input_bfd, contents))
+                 const Elf_Internal_Rela *ha;
+                 bfd_boolean match_addend;
+
+                 match_addend = (sym != NULL
+                                 && ELF_ST_TYPE (sym->st_info) == STT_SECTION);
+                 ha = ha_reloc_match (relocs, rel, &reg, match_addend,
+                                      input_bfd, contents);
+                 if (ha != NULL)
                    {
                      insn &= ~(0x1f << 16);
-                     insn |= 2 << 16;
+                     insn |= reg << 16;
                      bfd_put_32 (input_bfd, insn, p);
+                     if (ha_opt == NULL)
+                       {
+                         ha_opt = bfd_zmalloc (input_section->reloc_count);
+                         if (ha_opt == NULL)
+                           return FALSE;
+                       }
+                     ha_opt[ha - relocs] = 1;
                    }
+                 else
+                   /* If we don't find a matching high part insn,
+                      something is fishy.  Refuse to nop any high
+                      part insn in this section.  */
+                   no_ha_opt = TRUE;
                }
            }
          break;
@@ -12954,6 +13177,23 @@ ppc64_elf_relocate_section (bfd *output_bfd,
        }
     }
 
+  if (ha_opt != NULL)
+    {
+      if (!no_ha_opt)
+       {
+         unsigned char *opt = ha_opt;
+         rel = relocs;
+         relend = relocs + input_section->reloc_count;
+         for (; rel < relend; opt++, rel++)
+           if (*opt != 0)
+             {
+               bfd_byte *p = contents + (rel->r_offset & ~3);
+               bfd_put_32 (input_bfd, NOP, p);
+             }
+       }
+      free (ha_opt);
+    }
+
   /* If we're emitting relocations, then shortly after this function
      returns, reloc offsets and addends for this section will be
      adjusted.  Worse, reloc symbol indices will be for the output
@@ -13237,7 +13477,7 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
       && htab->brlt->reloc_count != 0
       && !_bfd_elf_link_output_relocs (output_bfd,
                                       htab->brlt,
-                                      &elf_section_data (htab->brlt)->rel_hdr,
+                                      elf_section_data (htab->brlt)->rela.hdr,
                                       elf_section_data (htab->brlt)->relocs,
                                       NULL))
     return FALSE;
@@ -13246,7 +13486,7 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
       && htab->glink->reloc_count != 0
       && !_bfd_elf_link_output_relocs (output_bfd,
                                       htab->glink,
-                                      &elf_section_data (htab->glink)->rel_hdr,
+                                      elf_section_data (htab->glink)->rela.hdr,
                                       elf_section_data (htab->glink)->relocs,
                                       NULL))
     return FALSE;
This page took 0.046078 seconds and 4 git commands to generate.