gas/hash.c: add new functions
[binutils-gdb.git] / bfd / elf64-ppc.c
index e28546deb5421ed5ba6a44d2b980576f3e1b983b..5cbf9acfcd1cb077805fa88ec59d29b57f544d21 100644 (file)
@@ -3239,25 +3239,17 @@ struct ppc_link_hash_table
   /* Whether func_desc_adjust needs to be run over symbols.  */
   unsigned int need_func_desc_adj:1;
 
-  /* Whether there exist local gnu indirect function resolvers,
-     referenced by dynamic relocations.  */
-  unsigned int local_ifunc_resolver:1;
-  unsigned int maybe_local_ifunc_resolver:1;
-
   /* Whether plt calls for ELFv2 localentry:0 funcs have been optimized.  */
   unsigned int has_plt_localentry0:1;
 
   /* Whether calls are made via the PLT from NOTOC functions.  */
   unsigned int notoc_plt:1;
 
-  /* Whether to use power10 instructions in linkage stubs.  */
-  unsigned int power10_stubs:1;
+  /* Whether any code linked seems to be Power10.  */
+  unsigned int has_power10_relocs:1;
 
   /* Incremented every time we size stubs.  */
   unsigned int stub_iteration;
-
-  /* Small local sym cache.  */
-  struct sym_cache sym_cache;
 };
 
 /* Rename some of the generic section flags to better document how they
@@ -3687,6 +3679,37 @@ ppc_stub_name (const asection *input_section,
   return stub_name;
 }
 
+/* If mixing power10 with non-power10 code and --power10-stubs is not
+   specified (or is auto) then calls using @notoc relocations that
+   need a stub will utilize power10 instructions in the stub, and
+   calls without @notoc relocations will not use power10 instructions.
+   The two classes of stubs are stored in separate stub_hash_table
+   entries having the same key string.  The two entries will always be
+   adjacent on entry->root.next chain, even if hash table resizing
+   occurs.  This function selects the correct entry to use.  */
+
+static struct ppc_stub_hash_entry *
+select_alt_stub (struct ppc_stub_hash_entry *entry, bfd_boolean notoc)
+{
+  bfd_boolean have_notoc;
+
+  have_notoc = (entry->stub_type == ppc_stub_plt_call_notoc
+               || entry->stub_type == ppc_stub_plt_branch_notoc
+               || entry->stub_type == ppc_stub_long_branch_notoc);
+
+  if (have_notoc != notoc)
+    {
+      const char *stub_name = entry->root.string;
+
+      entry = (struct ppc_stub_hash_entry *) entry->root.next;
+      if (entry != NULL
+         && entry->root.string != stub_name)
+       entry = NULL;
+    }
+
+  return entry;
+}
+
 /* Look up an entry in the stub hash.  Stub entries are cached because
    creating the stub name takes a bit of time.  */
 
@@ -3731,6 +3754,13 @@ ppc_get_stub_entry (const asection *input_section,
       free (stub_name);
     }
 
+  if (stub_entry != NULL && htab->params->power10_stubs == -1)
+    {
+      bfd_boolean notoc = ELF64_R_TYPE (rel->r_info) == R_PPC64_REL24_NOTOC;
+
+      stub_entry = select_alt_stub (stub_entry, notoc);
+    }
+
   return stub_entry;
 }
 
@@ -4350,7 +4380,8 @@ ppc64_elf_before_check_relocs (bfd *ibfd, struct bfd_link_info *info)
              Elf_Internal_Sym *isym;
              asection *s;
 
-             isym = bfd_sym_from_r_symndx (&htab->sym_cache, ibfd, r_symndx);
+             isym = bfd_sym_from_r_symndx (&htab->elf.sym_cache, ibfd,
+                                           r_symndx);
              if (isym == NULL)
                {
                  if (elf_section_data (opd)->relocs != relocs)
@@ -4607,7 +4638,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case R_PPC64_PLT_PCREL34:
        case R_PPC64_PLT_PCREL34_NOTOC:
        case R_PPC64_PCREL28:
-         htab->power10_stubs = 1;
+         htab->has_power10_relocs = 1;
          break;
        default:
          break;
@@ -4651,7 +4682,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
        }
       else
        {
-         Elf_Internal_Sym *isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+         Elf_Internal_Sym *isym = bfd_sym_from_r_symndx (&htab->elf.sym_cache,
                                                          abfd, r_symndx);
          if (isym == NULL)
            return FALSE;
@@ -4922,7 +4953,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
              {
                Elf_Internal_Sym *isym;
 
-               isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+               isym = bfd_sym_from_r_symndx (&htab->elf.sym_cache,
                                              abfd, r_symndx);
                if (isym == NULL)
                  return FALSE;
@@ -5198,7 +5229,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
                  void *vpp;
                  Elf_Internal_Sym *isym;
 
-                 isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+                 isym = bfd_sym_from_r_symndx (&htab->elf.sym_cache,
                                                abfd, r_symndx);
                  if (isym == NULL)
                    return FALSE;
@@ -10768,7 +10799,7 @@ plt_stub_size (struct ppc_link_hash_table *htab,
 
   if (stub_entry->stub_type >= ppc_stub_plt_call_notoc)
     {
-      if (htab->power10_stubs)
+      if (htab->params->power10_stubs != 0)
        {
          bfd_vma start = (stub_entry->stub_offset
                           + stub_entry->group->stub_sec->output_offset
@@ -11609,7 +11640,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
 
       relp = p;
       num_rel = 0;
-      if (htab->power10_stubs)
+      if (htab->params->power10_stubs != 0)
        {
          bfd_boolean load = stub_entry->stub_type >= ppc_stub_plt_call_notoc;
          p = build_power10_offset (htab->params->stub_bfd, p, off, odd, load);
@@ -11648,7 +11679,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       if (info->emitrelocations)
        {
          bfd_vma roff = relp - stub_entry->group->stub_sec->contents;
-         if (htab->power10_stubs)
+         if (htab->params->power10_stubs != 0)
            num_rel += num_relocs_for_power10_offset (off, odd);
          else
            {
@@ -11658,7 +11689,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          r = get_relocs (stub_entry->group->stub_sec, num_rel);
          if (r == NULL)
            return FALSE;
-         if (htab->power10_stubs)
+         if (htab->params->power10_stubs != 0)
            r = emit_relocs_for_power10_offset (info, r, roff, targ, off, odd);
          else
            r = emit_relocs_for_offset (info, r, roff, targ, off);
@@ -11676,7 +11707,7 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
            }
        }
 
-      if (!htab->power10_stubs
+      if (htab->params->power10_stubs == 0
          && htab->glink_eh_frame != NULL
          && htab->glink_eh_frame->size != 0)
        {
@@ -12024,7 +12055,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       if (info->emitrelocations)
        {
          unsigned int num_rel;
-         if (htab->power10_stubs)
+         if (htab->params->power10_stubs != 0)
            num_rel = num_relocs_for_power10_offset (off, odd);
          else
            num_rel = num_relocs_for_offset (off - 8);
@@ -12032,7 +12063,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
          stub_entry->group->stub_sec->flags |= SEC_RELOC;
        }
 
-      if (htab->power10_stubs)
+      if (htab->params->power10_stubs != 0)
        extra = size_power10_offset (off, odd);
       else
        extra = size_offset (off - 8);
@@ -12043,7 +12074,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
         calculated.  */
       off -= extra;
 
-      if (!htab->power10_stubs)
+      if (htab->params->power10_stubs == 0)
        {
          /* After the bcl, lr has been modified so we need to emit
             .eh_frame info saying the return address is in r12.  */
@@ -12106,7 +12137,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       if (info->emitrelocations)
        {
          unsigned int num_rel;
-         if (htab->power10_stubs)
+         if (htab->params->power10_stubs != 0)
            num_rel = num_relocs_for_power10_offset (off, odd);
          else
            num_rel = num_relocs_for_offset (off - 8);
@@ -12116,7 +12147,7 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
 
       size = plt_stub_size (htab, stub_entry, off);
 
-      if (!htab->power10_stubs)
+      if (htab->params->power10_stubs == 0)
        {
          /* After the bcl, lr has been modified so we need to emit
             .eh_frame info saying the return address is in r12.  */
@@ -13041,6 +13072,9 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
   if (htab == NULL)
     return FALSE;
 
+  if (htab->params->power10_stubs == -1 && !htab->has_power10_relocs)
+    htab->params->power10_stubs = 0;
+
   if (htab->params->plt_thread_safe == -1 && !bfd_link_executable (info))
     htab->params->plt_thread_safe = 1;
   if (!htab->opd_abi)
@@ -13416,6 +13450,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
                  if (stub_entry != NULL)
                    {
                      enum ppc_stub_type old_type;
+
                      /* A stub has already been created, but it may
                         not be the required type.  We shouldn't be
                         transitioning from plt_call to long_branch
@@ -13423,6 +13458,39 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
                         upgrading from plt_call to plt_call_r2save or
                         from long_branch to long_branch_r2off.  */
                      free (stub_name);
+                     if (htab->params->power10_stubs == -1)
+                       {
+                         /* For --power10-stubs=auto, don't merge _notoc
+                            and other varieties of stubs.  (The _both
+                            variety won't be created.)  */
+                         bfd_boolean notoc = r_type == R_PPC64_REL24_NOTOC;
+                         struct ppc_stub_hash_entry *alt_stub
+                           = select_alt_stub (stub_entry, notoc);
+
+                         if (alt_stub == NULL)
+                           {
+                             alt_stub = (struct ppc_stub_hash_entry *)
+                               stub_hash_newfunc (NULL,
+                                                  &htab->stub_hash_table,
+                                                  stub_entry->root.string);
+                             if (alt_stub == NULL)
+                               {
+                                 /* xgettext:c-format */
+                                 _bfd_error_handler
+                                   (_("%pB: cannot create stub entry %s"),
+                                    section->owner, stub_entry->root.string);
+                                 goto error_ret_free_internal;
+                               }
+                             *alt_stub = *stub_entry;
+                             stub_entry->root.next = &alt_stub->root;
+                             if (notoc)
+                               /* Sort notoc stubs first, for no good
+                                  reason.  */
+                               alt_stub = stub_entry;
+                             alt_stub->stub_type = stub_type;
+                           }
+                         stub_entry = alt_stub;
+                       }
                      old_type = stub_entry->stub_type;
                      switch (old_type)
                        {
@@ -13881,7 +13949,7 @@ build_global_entry_stubs_and_plt (struct elf_link_hash_entry *h, void *inf)
              {
                plt = htab->elf.iplt;
                relplt = htab->elf.irelplt;
-               htab->local_ifunc_resolver = 1;
+               htab->elf.ifunc_resolvers = TRUE;
                if (htab->opd_abi)
                  rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL);
                else
@@ -13935,7 +14003,7 @@ build_global_entry_stubs_and_plt (struct elf_link_hash_entry *h, void *inf)
                   + ((ent->plt.offset - PLT_INITIAL_ENTRY_SIZE (htab))
                      / PLT_ENTRY_SIZE (htab) * sizeof (Elf64_External_Rela)));
            if (h->type == STT_GNU_IFUNC && is_static_defined (h))
-             htab->maybe_local_ifunc_resolver = 1;
+             htab->elf.ifunc_resolvers = TRUE;
            bfd_elf64_swap_reloca_out (info->output_bfd, &rela, loc);
          }
       }
@@ -14077,7 +14145,7 @@ write_plt_relocs_for_local_syms (struct bfd_link_info *info)
 
              if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
                {
-                 htab->local_ifunc_resolver = 1;
+                 htab->elf.ifunc_resolvers = TRUE;
                  plt = htab->elf.iplt;
                  relplt = htab->elf.irelplt;
                }
@@ -15835,7 +15903,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          break;
 
        case R_PPC64_GOT16_DS:
-         if ((h ? h->elf.type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC)
+         if ((h ? h->elf.type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC
+             || !htab->do_toc_opt)
            break;
          from = TOCstart + htab->sec_info[input_section->id].toc_off;
          if (relocation + addend - from + 0x8000 < 0x10000
@@ -15854,7 +15923,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
 
        case R_PPC64_GOT16_LO_DS:
        case R_PPC64_GOT16_HA:
-         if ((h ? h->elf.type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC)
+         if ((h ? h->elf.type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC
+             || !htab->do_toc_opt)
            break;
          from = TOCstart + htab->sec_info[input_section->id].toc_off;
          if (relocation + addend - from + 0x80008000ULL < 0x100000000ULL
@@ -15877,34 +15947,38 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          break;
 
        case R_PPC64_GOT_PCREL34:
-         if ((h ? h->elf.type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC)
+         if ((h ? h->elf.type : ELF_ST_TYPE (sym->st_info)) == STT_GNU_IFUNC
+             || !htab->do_toc_opt)
            break;
          from = (rel->r_offset
                  + input_section->output_section->vma
                  + input_section->output_offset);
-         if (relocation - from + (1ULL << 33) < 1ULL << 34
-             && SYMBOL_REFERENCES_LOCAL (info, &h->elf))
-           {
-             offset = rel->r_offset;
-             pinsn = bfd_get_32 (input_bfd, contents + offset);
-             pinsn <<= 32;
-             pinsn |= bfd_get_32 (input_bfd, contents + offset + 4);
-             if ((pinsn & ((-1ULL << 50) | (63ULL << 26)))
-                  == ((1ULL << 58) | (1ULL << 52) | (57ULL << 26) /* pld */))
-               {
-                 /* Replace with paddi.  */
-                 pinsn += (2ULL << 56) + (14ULL << 26) - (57ULL << 26);
-                 r_type = R_PPC64_PCREL34;
-                 rel->r_info = ELF64_R_INFO (r_symndx, r_type);
-                 bfd_put_32 (input_bfd, pinsn >> 32, contents + offset);
-                 bfd_put_32 (input_bfd, pinsn, contents + offset + 4);
-                 goto pcrelopt;
-               }
-           }
-         break;
+         if (!(relocation - from + (1ULL << 33) < 1ULL << 34
+               && SYMBOL_REFERENCES_LOCAL (info, &h->elf)))
+           break;
+
+         offset = rel->r_offset;
+         pinsn = bfd_get_32 (input_bfd, contents + offset);
+         pinsn <<= 32;
+         pinsn |= bfd_get_32 (input_bfd, contents + offset + 4);
+         if ((pinsn & ((-1ULL << 50) | (63ULL << 26)))
+             != ((1ULL << 58) | (1ULL << 52) | (57ULL << 26) /* pld */))
+           break;
+
+         /* Replace with paddi.  */
+         pinsn += (2ULL << 56) + (14ULL << 26) - (57ULL << 26);
+         r_type = R_PPC64_PCREL34;
+         rel->r_info = ELF64_R_INFO (r_symndx, r_type);
+         bfd_put_32 (input_bfd, pinsn >> 32, contents + offset);
+         bfd_put_32 (input_bfd, pinsn, contents + offset + 4);
+         /* Fall through.  */
 
        case R_PPC64_PCREL34:
-         if (SYMBOL_REFERENCES_LOCAL (info, &h->elf))
+         if (!htab->params->no_pcrel_opt
+             && rel + 1 < relend
+             && rel[1].r_offset == rel->r_offset
+             && rel[1].r_info == ELF64_R_INFO (0, R_PPC64_PCREL_OPT)
+             && SYMBOL_REFERENCES_LOCAL (info, &h->elf))
            {
              offset = rel->r_offset;
              pinsn = bfd_get_32 (input_bfd, contents + offset);
@@ -15914,43 +15988,37 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                   == ((1ULL << 58) | (2ULL << 56) | (1ULL << 52)
                       | (14ULL << 26) /* paddi */))
                {
-               pcrelopt:
-                 if (rel + 1 < relend
-                     && rel[1].r_offset == offset
-                     && rel[1].r_info == ELF64_R_INFO (0, R_PPC64_PCREL_OPT))
+                 bfd_vma off2 = rel[1].r_addend;
+                 if (off2 == 0)
+                   /* zero means next insn.  */
+                   off2 = 8;
+                 off2 += offset;
+                 if (off2 + 4 <= input_section->size)
                    {
-                     bfd_vma off2 = rel[1].r_addend;
-                     if (off2 == 0)
-                       /* zero means next insn.  */
-                       off2 = 8;
-                     off2 += offset;
-                     if (off2 + 4 <= input_section->size)
+                     uint64_t pinsn2;
+                     bfd_signed_vma addend_off;
+                     pinsn2 = bfd_get_32 (input_bfd, contents + off2);
+                     pinsn2 <<= 32;
+                     if ((pinsn2 & (63ULL << 58)) == 1ULL << 58)
                        {
-                         uint64_t pinsn2;
-                         bfd_signed_vma addend_off;
-                         pinsn2 = bfd_get_32 (input_bfd, contents + off2);
-                         pinsn2 <<= 32;
+                         if (off2 + 8 > input_section->size)
+                           break;
+                         pinsn2 |= bfd_get_32 (input_bfd,
+                                               contents + off2 + 4);
+                       }
+                     if (xlate_pcrel_opt (&pinsn, &pinsn2, &addend_off))
+                       {
+                         addend += addend_off;
+                         rel->r_addend = addend;
+                         bfd_put_32 (input_bfd, pinsn >> 32,
+                                     contents + offset);
+                         bfd_put_32 (input_bfd, pinsn,
+                                     contents + offset + 4);
+                         bfd_put_32 (input_bfd, pinsn2 >> 32,
+                                     contents + off2);
                          if ((pinsn2 & (63ULL << 58)) == 1ULL << 58)
-                           {
-                             if (off2 + 8 > input_section->size)
-                               break;
-                             pinsn2 |= bfd_get_32 (input_bfd,
-                                                   contents + off2 + 4);
-                           }
-                         if (xlate_pcrel_opt (&pinsn, &pinsn2, &addend_off))
-                           {
-                             addend += addend_off;
-                             rel->r_addend = addend;
-                             bfd_put_32 (input_bfd, pinsn >> 32,
-                                         contents + offset);
-                             bfd_put_32 (input_bfd, pinsn,
-                                         contents + offset + 4);
-                             bfd_put_32 (input_bfd, pinsn2 >> 32,
-                                         contents + off2);
-                             if ((pinsn2 & (63ULL << 58)) == 1ULL << 58)
-                               bfd_put_32 (input_bfd, pinsn2,
-                                           contents + off2 + 4);
-                           }
+                           bfd_put_32 (input_bfd, pinsn2,
+                                       contents + off2 + 4);
                        }
                    }
                }
@@ -16103,10 +16171,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                if (ifunc)
                  {
                    relgot = htab->elf.irelplt;
-                   if (indx == 0)
-                     htab->local_ifunc_resolver = 1;
-                   else if (is_static_defined (&h->elf))
-                     htab->maybe_local_ifunc_resolver = 1;
+                   if (indx == 0 || is_static_defined (&h->elf))
+                     htab->elf.ifunc_resolvers = TRUE;
                  }
                else if (indx != 0
                         || (bfd_link_pic (info)
@@ -16635,10 +16701,8 @@ ppc64_elf_relocate_section (bfd *output_bfd,
                  : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
                {
                  sreloc = htab->elf.irelplt;
-                 if (indx == 0)
-                   htab->local_ifunc_resolver = 1;
-                 else if (is_static_defined (&h->elf))
-                   htab->maybe_local_ifunc_resolver = 1;
+                 if (indx == 0 || is_static_defined (&h->elf))
+                   htab->elf.ifunc_resolvers = TRUE;
                }
              if (sreloc == NULL)
                abort ();
@@ -17402,11 +17466,7 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd,
              break;
 
            case DT_TEXTREL:
-             if (htab->local_ifunc_resolver)
-               info->callbacks->einfo
-                 (_("%X%P: text relocations and GNU indirect "
-                    "functions will result in a segfault at runtime\n"));
-             else if (htab->maybe_local_ifunc_resolver)
+             if (htab->elf.ifunc_resolvers)
                info->callbacks->einfo
                  (_("%P: warning: text relocations and GNU indirect "
                     "functions may result in a segfault at runtime\n"));