PR 20472, PowerPC64 ifunc confusion
authorAlan Modra <amodra@gmail.com>
Fri, 19 Aug 2016 01:36:41 +0000 (11:06 +0930)
committerAlan Modra <amodra@gmail.com>
Fri, 19 Aug 2016 01:36:41 +0000 (11:06 +0930)
This patch fixes quite a lot of confusion in allocate_dynrelocs over
ifuncs.  Function descriptors make ELFv1 quite different to ELFv2.

PR 20472
* elf64-ppc.c (ppc64_elf_before_check_relocs): Tweak abiversion test.
(readonly_dynrelocs): Comment fix.
(global_entry_stub): New function.
(ppc64_elf_adjust_dynamic_symbol): Tweak abiversion test.  Match
ELFv2 code deciding on dynamic relocs vs. global entry stubs to
that in size_global_entry_stubs, handling ifunc too.  Delete dead
weak sym code.
(allocate_dynrelocs): Ensure dyn_relocs field is cleared when no
dyn_relocs are needed.  Correct handling of ifunc dyn_relocs.
Tidy ELIMINATE_COPY_RELOCS code, only setting dynindx for
undefweak syms.  Expand and correct comments.
(size_global_entry_stubs): Ensure symbol is defined.
(ppc64_elf_relocate_section): Match condition under which
dyn_relocs are emitted to that in allocate_dynrelocs.

bfd/ChangeLog
bfd/elf64-ppc.c

index 7326ecd4a95c2d6376f2bc02e23e94f5ad295658..c579a024ca38258c84e0f9a7bce15829200ddc23 100644 (file)
@@ -1,3 +1,21 @@
+2016-08-19  Alan Modra  <amodra@gmail.com>
+
+       PR 20472
+       * elf64-ppc.c (ppc64_elf_before_check_relocs): Tweak abiversion test.
+       (readonly_dynrelocs): Comment fix.
+       (global_entry_stub): New function.
+       (ppc64_elf_adjust_dynamic_symbol): Tweak abiversion test.  Match
+       ELFv2 code deciding on dynamic relocs vs. global entry stubs to
+       that in size_global_entry_stubs, handling ifunc too.  Delete dead
+       weak sym code.
+       (allocate_dynrelocs): Ensure dyn_relocs field is cleared when no
+       dyn_relocs are needed.  Correct handling of ifunc dyn_relocs.
+       Tidy ELIMINATE_COPY_RELOCS code, only setting dynindx for
+       undefweak syms.  Expand and correct comments.
+       (size_global_entry_stubs): Ensure symbol is defined.
+       (ppc64_elf_relocate_section): Match condition under which
+       dyn_relocs are emitted to that in allocate_dynrelocs.
+
 2016-08-12  Alan Modra  <amodra@gmail.com>
 
        * elf-bfd.h (struct elf_link_hash_table): Add local_dynsymcount.
index 4f854c7a56c6720864ebcecdba62dd1dc0cfa8c0..bcf71709c70d9f2ff12c180fb21923f108169059 100644 (file)
@@ -5081,7 +5081,7 @@ ppc64_elf_before_check_relocs (bfd *ibfd, struct bfd_link_info *info)
     {
       if (abiversion (ibfd) == 0)
        set_abiversion (ibfd, 1);
-      else if (abiversion (ibfd) == 2)
+      else if (abiversion (ibfd) >= 2)
        {
          info->callbacks->einfo (_("%P: %B .opd not allowed in ABI"
                                    " version %d\n"),
@@ -7102,7 +7102,8 @@ ppc64_elf_func_desc_adjust (bfd *obfd ATTRIBUTE_UNUSED,
   return TRUE;
 }
 
-/* Return true if we have dynamic relocs that apply to read-only sections.  */
+/* Return true if we have dynamic relocs against H that apply to
+   read-only sections.  */
 
 static bfd_boolean
 readonly_dynrelocs (struct elf_link_hash_entry *h)
@@ -7121,6 +7122,27 @@ readonly_dynrelocs (struct elf_link_hash_entry *h)
   return FALSE;
 }
 
+
+/* Return true if a global entry stub will be created for H.  Valid
+   for ELFv2 before plt entries have been allocated.  */
+
+static bfd_boolean
+global_entry_stub (struct elf_link_hash_entry *h)
+{
+  struct plt_entry *pent;
+
+  if (!h->pointer_equality_needed
+      || h->def_regular)
+    return FALSE;
+
+  for (pent = h->plt.plist; pent != NULL; pent = pent->next)
+    if (pent->plt.refcount > 0
+       && pent->addend == 0)
+      return TRUE;
+
+  return FALSE;
+}
+
 /* Adjust a symbol defined by a dynamic object and referenced by a
    regular object.  The current definition is in some section of the
    dynamic object, but we're not including those sections.  We have to
@@ -7160,35 +7182,25 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
          h->needs_plt = 0;
          h->pointer_equality_needed = 0;
        }
-      else if (abiversion (info->output_bfd) == 2)
+      else if (abiversion (info->output_bfd) >= 2)
        {
          /* Taking a function's address in a read/write section
             doesn't require us to define the function symbol in the
             executable on a global entry stub.  A dynamic reloc can
-            be used instead.  */
-         if (h->pointer_equality_needed
-             && h->type != STT_GNU_IFUNC
+            be used instead.  The reason we prefer a few more dynamic
+            relocs is that calling via a global entry stub costs a
+            few more instructions, and pointer_equality_needed causes
+            extra work in ld.so when resolving these symbols.  */
+         if (global_entry_stub (h)
              && !readonly_dynrelocs (h))
            {
              h->pointer_equality_needed = 0;
+             /* After adjust_dynamic_symbol, non_got_ref set in
+                the non-pic case means that dyn_relocs for this
+                symbol should be discarded.  */
              h->non_got_ref = 0;
            }
 
-         /* After adjust_dynamic_symbol, non_got_ref set in the
-            non-shared case means that we have allocated space in
-            .dynbss for the symbol and thus dyn_relocs for this
-            symbol should be discarded.
-            If we get here we know we are making a PLT entry for this
-            symbol, and in an executable we'd normally resolve
-            relocations against this symbol to the PLT entry.  Allow
-            dynamic relocs if the reference is weak, and the dynamic
-            relocs will not cause text relocation.  */
-         else if (!h->ref_regular_nonweak
-                  && h->non_got_ref
-                  && h->type != STT_GNU_IFUNC
-                  && !readonly_dynrelocs (h))
-           h->non_got_ref = 0;
-
          /* If making a plt entry, then we don't need copy relocs.  */
          return TRUE;
        }
@@ -9538,7 +9550,6 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   struct ppc_link_hash_table *htab;
   asection *s;
   struct ppc_link_hash_entry *eh;
-  struct elf_dyn_relocs *p;
   struct got_entry **pgent, *gent;
 
   if (h->root.type == bfd_link_hash_indirect)
@@ -9618,10 +9629,14 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
        allocate_got (h, info, gent);
       }
 
-  if (eh->dyn_relocs != NULL
-      && (htab->elf.dynamic_sections_created
-         || h->type == STT_GNU_IFUNC))
+  if (!htab->elf.dynamic_sections_created
+      && h->type != STT_GNU_IFUNC)
+    eh->dyn_relocs = NULL;
+
+  if (eh->dyn_relocs != NULL)
     {
+      struct elf_dyn_relocs *p, **pp;
+
       /* In the shared -Bsymbolic case, discard space allocated for
         dynamic pc-relative relocs against symbols which turn out to
         be defined in regular objects.  For the normal shared case,
@@ -9639,8 +9654,6 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
             avoid writing weird assembly.  */
          if (SYMBOL_CALLS_LOCAL (info, h))
            {
-             struct elf_dyn_relocs **pp;
-
              for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
                {
                  p->count -= p->pc_count;
@@ -9672,36 +9685,47 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
        }
       else if (h->type == STT_GNU_IFUNC)
        {
-         if (!h->non_got_ref)
+         /* A plt entry is always created when making direct calls to
+            an ifunc, even when building a static executable, but
+            that doesn't cover all cases.  We may have only an ifunc
+            initialised function pointer for a given ifunc symbol.
+
+            For ELFv2, dynamic relocations are not required when
+            generating a global entry PLT stub.  */
+         if (abiversion (info->output_bfd) >= 2)
+           {
+             if (global_entry_stub (h))
+               eh->dyn_relocs = NULL;
+           }
+
+         /* For ELFv1 we have function descriptors.  Descriptors need
+            to be treated like PLT entries and thus have dynamic
+            relocations.  One exception is when the function
+            descriptor is copied into .dynbss (which should only
+            happen with ancient versions of gcc).  */
+         else if (h->needs_copy)
            eh->dyn_relocs = NULL;
        }
       else if (ELIMINATE_COPY_RELOCS)
        {
-         /* For the non-shared case, discard space for relocs against
+         /* For the non-pic case, discard space for relocs against
             symbols which turn out to need copy relocs or are not
             dynamic.  */
 
-         if (!h->non_got_ref
-             && !h->def_regular)
-           {
-             /* Make sure this symbol is output as a dynamic symbol.
-                Undefined weak syms won't yet be marked as dynamic.  */
-             if (h->dynindx == -1
-                 && !h->forced_local)
-               {
-                 if (! bfd_elf_link_record_dynamic_symbol (info, h))
-                   return FALSE;
-               }
-
-             /* If that succeeded, we know we'll be keeping all the
-                relocs.  */
-             if (h->dynindx != -1)
-               goto keep;
-           }
-
-         eh->dyn_relocs = NULL;
+         /* First make sure this symbol is output as a dynamic symbol.
+            Undefined weak syms won't yet be marked as dynamic.  */
+         if (h->root.type == bfd_link_hash_undefweak
+             && !h->non_got_ref
+             && !h->def_regular
+             && h->dynindx == -1
+             && !h->forced_local
+             && !bfd_elf_link_record_dynamic_symbol (info, h))
+           return FALSE;
 
-       keep: ;
+         if (h->non_got_ref
+             || h->def_regular
+             || h->dynindx == -1)
+           eh->dyn_relocs = NULL;
        }
 
       /* Finally, allocate space.  */
@@ -9818,6 +9842,7 @@ size_global_entry_stubs (struct elf_link_hash_entry *h, void *inf)
           need to define the symbol in the executable on a call stub.
           This is to avoid text relocations.  */
        s->size = (s->size + 15) & -16;
+       h->root.type = bfd_link_hash_defined;
        h->root.u.def.section = s;
        h->root.u.def.value = s->size;
        s->size += 16;
@@ -14670,22 +14695,25 @@ ppc64_elf_relocate_section (bfd *output_bfd,
          if (NO_OPD_RELOCS && is_opd)
            break;
 
-         if ((bfd_link_pic (info)
-              && (h == NULL
-                  || ELF_ST_VISIBILITY (h->elf.other) == STV_DEFAULT
-                  || h->elf.root.type != bfd_link_hash_undefweak)
-              && (must_be_dyn_reloc (info, r_type)
-                  || !SYMBOL_CALLS_LOCAL (info, &h->elf)))
-             || (ELIMINATE_COPY_RELOCS
-                 && !bfd_link_pic (info)
-                 && h != NULL
-                 && h->elf.dynindx != -1
-                 && !h->elf.non_got_ref
-                 && !h->elf.def_regular)
-             || (!bfd_link_pic (info)
-                 && (h != NULL
-                     ? h->elf.type == STT_GNU_IFUNC
-                     : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)))
+         if (bfd_link_pic (info)
+             ? ((h == NULL
+                 || ELF_ST_VISIBILITY (h->elf.other) == STV_DEFAULT
+                 || h->elf.root.type != bfd_link_hash_undefweak)
+                && (must_be_dyn_reloc (info, r_type)
+                    || !SYMBOL_CALLS_LOCAL (info, &h->elf)))
+             : (h == NULL
+                ? ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC
+                : (h->elf.type == STT_GNU_IFUNC
+                   ? (abiversion (output_bfd) >= 2
+                      ? !(h->elf.pointer_equality_needed
+                          && !h->elf.def_regular
+                          && h->elf.root.type == bfd_link_hash_defined
+                          && h->elf.root.u.def.section == htab->glink)
+                      : !h->elf.needs_copy)
+                   : (ELIMINATE_COPY_RELOCS
+                      && !(h->elf.non_got_ref
+                           || h->elf.def_regular
+                           || h->elf.dynindx == -1)))))
            {
              bfd_boolean skip, relocate;
              asection *sreloc;