PR ld/20828: Move symbol version processing ahead of GC symbol sweep
authorMaciej W. Rozycki <macro@imgtec.com>
Tue, 21 Feb 2017 01:46:42 +0000 (01:46 +0000)
committerMaciej W. Rozycki <macro@imgtec.com>
Wed, 22 Feb 2017 18:16:28 +0000 (18:16 +0000)
Complement commit b531344c34b0 ("PR ld/20828: Reorder the symbol sweep
stage of section GC") and commit 81ff47b3a546 ("PR ld/20828: Fix linker
script symbols wrongly forced local with section GC") and move symbol
version processing ahead of the symbol sweep stage of section GC, all in
`bfd_elf_size_dynamic_sections', so that version symbols created stay in
the global scope and are not output as local symbols to the dynamic
symbol table in the presence of corresponding symbol definitions pulled
from a DSO involved in a link.

Consolidate the whole of symbol version processing into a single block
from all parts scattered across the function and rearranging the local
variables used as necessary, however leaving the setting of dynamic
entries associated with the DT_VERDEF, DT_VERDEFNUM, DT_VERNEED and
DT_VERNEEDNUM tags and the SEC_EXCLUDE flag for unused `.gnu.version'
section in the original places.

With the rearrangement of code blocks `Elf_Internal_Verneed *t' would
shadow the previous definition of `struct bfd_elf_version_tree *t', so
rename the former variable to `vn'.

bfd/
PR ld/20828
* elflink.c (bfd_elf_size_dynamic_sections): Move symbol version
processing ahead of the call to `elf_gc_sweep_symbol'.

ld/
PR ld/20828
* testsuite/ld-elf/pr20828-d.sd: New test.
* testsuite/ld-elf/pr20828-e.sd: New test.
* testsuite/ld-elf/pr20828-v.od: New test.
* testsuite/ld-elf/pr20828-v.ver: New test version script.
* testsuite/ld-elf/pr20828-v.ld: New test linker script.
* testsuite/ld-elf/pr20828.ld: Add `.gnu.version' and
`.gnu.version_d'.
* testsuite/ld-elf/shared.exp: Run the new tests.

bfd/ChangeLog
bfd/elflink.c
ld/ChangeLog
ld/testsuite/ld-elf/pr20828-d.sd [new file with mode: 0644]
ld/testsuite/ld-elf/pr20828-e.sd [new file with mode: 0644]
ld/testsuite/ld-elf/pr20828-v.ld [new file with mode: 0644]
ld/testsuite/ld-elf/pr20828-v.od [new file with mode: 0644]
ld/testsuite/ld-elf/pr20828-v.s [new file with mode: 0644]
ld/testsuite/ld-elf/pr20828-v.ver [new file with mode: 0644]
ld/testsuite/ld-elf/pr20828.ld
ld/testsuite/ld-elf/shared.exp

index 8f8d375..24b27c8 100644 (file)
@@ -1,3 +1,9 @@
+2017-02-22  Maciej W. Rozycki  <macro@imgtec.com>
+
+       PR ld/20828
+       * elflink.c (bfd_elf_size_dynamic_sections): Move symbol version
+       processing ahead of the call to `elf_gc_sweep_symbol'.
+
 2017-02-22  Nick Clifton  <nickc@redhat.com>
 
        PR binutils/21193
index 455b2c3..027ce83 100644 (file)
@@ -5925,7 +5925,6 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
   size_t soname_indx;
   bfd *dynobj;
   const struct elf_backend_data *bed;
-  struct elf_info_failed asvinfo;
 
   *sinterpptr = NULL;
 
@@ -5934,173 +5933,17 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
   if (!is_elf_hash_table (info->hash))
     return TRUE;
 
-  bed = get_elf_backend_data (output_bfd);
-
-  if (info->gc_sections && bed->can_gc_sections)
-    {
-      struct elf_gc_sweep_symbol_info sweep_info;
-      unsigned long section_sym_count;
-
-      /* Remove the symbols that were in the swept sections from the
-        dynamic symbol table.  GCFIXME: Anyone know how to get them
-        out of the static symbol table as well?  */
-      sweep_info.info = info;
-      sweep_info.hide_symbol = bed->elf_backend_hide_symbol;
-      elf_link_hash_traverse (elf_hash_table (info), elf_gc_sweep_symbol,
-                             &sweep_info);
-
-      _bfd_elf_link_renumber_dynsyms (output_bfd, info, &section_sym_count);
-    }
-
-  /* Any syms created from now on start with -1 in
-     got.refcount/offset and plt.refcount/offset.  */
-  elf_hash_table (info)->init_got_refcount
-    = elf_hash_table (info)->init_got_offset;
-  elf_hash_table (info)->init_plt_refcount
-    = elf_hash_table (info)->init_plt_offset;
-
-  if (bfd_link_relocatable (info)
-      && !_bfd_elf_size_group_sections (info))
-    return FALSE;
-
-  /* The backend may have to create some sections regardless of whether
-     we're dynamic or not.  */
-  if (bed->elf_backend_always_size_sections
-      && ! (*bed->elf_backend_always_size_sections) (output_bfd, info))
-    return FALSE;
-
-  /* Determine any GNU_STACK segment requirements, after the backend
-     has had a chance to set a default segment size.  */
-  if (info->execstack)
-    elf_stack_flags (output_bfd) = PF_R | PF_W | PF_X;
-  else if (info->noexecstack)
-    elf_stack_flags (output_bfd) = PF_R | PF_W;
-  else
-    {
-      bfd *inputobj;
-      asection *notesec = NULL;
-      int exec = 0;
-
-      for (inputobj = info->input_bfds;
-          inputobj;
-          inputobj = inputobj->link.next)
-       {
-         asection *s;
-
-         if (inputobj->flags
-             & (DYNAMIC | EXEC_P | BFD_PLUGIN | BFD_LINKER_CREATED))
-           continue;
-         s = bfd_get_section_by_name (inputobj, ".note.GNU-stack");
-         if (s)
-           {
-             if (s->flags & SEC_CODE)
-               exec = PF_X;
-             notesec = s;
-           }
-         else if (bed->default_execstack)
-           exec = PF_X;
-       }
-      if (notesec || info->stacksize > 0)
-       elf_stack_flags (output_bfd) = PF_R | PF_W | exec;
-      if (notesec && exec && bfd_link_relocatable (info)
-         && notesec->output_section != bfd_abs_section_ptr)
-       notesec->output_section->flags |= SEC_CODE;
-    }
-
   dynobj = elf_hash_table (info)->dynobj;
 
   if (dynobj != NULL && elf_hash_table (info)->dynamic_sections_created)
     {
-      struct elf_info_failed eif;
-      struct elf_link_hash_entry *h;
-      asection *dynstr;
+      struct bfd_elf_version_tree *verdefs;
+      struct elf_info_failed asvinfo;
       struct bfd_elf_version_tree *t;
       struct bfd_elf_version_expr *d;
-      asection *s;
+      struct elf_info_failed eif;
       bfd_boolean all_defined;
-
-      *sinterpptr = bfd_get_linker_section (dynobj, ".interp");
-      BFD_ASSERT (*sinterpptr != NULL || !bfd_link_executable (info) || info->nointerp);
-
-      if (soname != NULL)
-       {
-         soname_indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
-                                            soname, TRUE);
-         if (soname_indx == (size_t) -1
-             || !_bfd_elf_add_dynamic_entry (info, DT_SONAME, soname_indx))
-           return FALSE;
-       }
-
-      if (info->symbolic)
-       {
-         if (!_bfd_elf_add_dynamic_entry (info, DT_SYMBOLIC, 0))
-           return FALSE;
-         info->flags |= DF_SYMBOLIC;
-       }
-
-      if (rpath != NULL)
-       {
-         size_t indx;
-         bfd_vma tag;
-
-         indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr, rpath,
-                                     TRUE);
-         if (indx == (size_t) -1)
-           return FALSE;
-
-         tag = info->new_dtags ? DT_RUNPATH : DT_RPATH;
-         if (!_bfd_elf_add_dynamic_entry (info, tag, indx))
-           return FALSE;
-       }
-
-      if (filter_shlib != NULL)
-       {
-         size_t indx;
-
-         indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
-                                     filter_shlib, TRUE);
-         if (indx == (size_t) -1
-             || !_bfd_elf_add_dynamic_entry (info, DT_FILTER, indx))
-           return FALSE;
-       }
-
-      if (auxiliary_filters != NULL)
-       {
-         const char * const *p;
-
-         for (p = auxiliary_filters; *p != NULL; p++)
-           {
-             size_t indx;
-
-             indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
-                                         *p, TRUE);
-             if (indx == (size_t) -1
-                 || !_bfd_elf_add_dynamic_entry (info, DT_AUXILIARY, indx))
-               return FALSE;
-           }
-       }
-
-      if (audit != NULL)
-       {
-         size_t indx;
-
-         indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr, audit,
-                                     TRUE);
-         if (indx == (size_t) -1
-             || !_bfd_elf_add_dynamic_entry (info, DT_AUDIT, indx))
-           return FALSE;
-       }
-
-      if (depaudit != NULL)
-       {
-         size_t indx;
-
-         indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr, depaudit,
-                                     TRUE);
-         if (indx == (size_t) -1
-             || !_bfd_elf_add_dynamic_entry (info, DT_DEPAUDIT, indx))
-           return FALSE;
-       }
+      asection *s;
 
       eif.info = info;
       eif.failed = FALSE;
@@ -6200,168 +6043,44 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
            }
        }
 
-      /* Find all symbols which were defined in a dynamic object and make
-        the backend pick a reasonable value for them.  */
-      elf_link_hash_traverse (elf_hash_table (info),
-                             _bfd_elf_adjust_dynamic_symbol,
-                             &eif);
-      if (eif.failed)
-       return FALSE;
+      /* Set up the version definition section.  */
+      s = bfd_get_linker_section (dynobj, ".gnu.version_d");
+      BFD_ASSERT (s != NULL);
 
-      /* Add some entries to the .dynamic section.  We fill in some of the
-        values later, in bfd_elf_final_link, but we must add the entries
-        now so that we know the final size of the .dynamic section.  */
+      /* We may have created additional version definitions if we are
+        just linking a regular application.  */
+      verdefs = info->version_info;
 
-      /* If there are initialization and/or finalization functions to
-        call then add the corresponding DT_INIT/DT_FINI entries.  */
-      h = (info->init_function
-          ? elf_link_hash_lookup (elf_hash_table (info),
-                                  info->init_function, FALSE,
-                                  FALSE, FALSE)
-          : NULL);
-      if (h != NULL
-         && (h->ref_regular
-             || h->def_regular))
-       {
-         if (!_bfd_elf_add_dynamic_entry (info, DT_INIT, 0))
-           return FALSE;
-       }
-      h = (info->fini_function
-          ? elf_link_hash_lookup (elf_hash_table (info),
-                                  info->fini_function, FALSE,
-                                  FALSE, FALSE)
-          : NULL);
-      if (h != NULL
-         && (h->ref_regular
-             || h->def_regular))
-       {
-         if (!_bfd_elf_add_dynamic_entry (info, DT_FINI, 0))
-           return FALSE;
-       }
+      /* Skip anonymous version tag.  */
+      if (verdefs != NULL && verdefs->vernum == 0)
+       verdefs = verdefs->next;
 
-      s = bfd_get_section_by_name (output_bfd, ".preinit_array");
-      if (s != NULL && s->linker_has_input)
+      if (verdefs == NULL && !info->create_default_symver)
+       s->flags |= SEC_EXCLUDE;
+      else
        {
-         /* DT_PREINIT_ARRAY is not allowed in shared library.  */
-         if (! bfd_link_executable (info))
-           {
-             bfd *sub;
-             asection *o;
+         unsigned int cdefs;
+         bfd_size_type size;
+         bfd_byte *p;
+         Elf_Internal_Verdef def;
+         Elf_Internal_Verdaux defaux;
+         struct bfd_link_hash_entry *bh;
+         struct elf_link_hash_entry *h;
+         const char *name;
 
-             for (sub = info->input_bfds; sub != NULL;
-                  sub = sub->link.next)
-               if (bfd_get_flavour (sub) == bfd_target_elf_flavour)
-                 for (o = sub->sections; o != NULL; o = o->next)
-                   if (elf_section_data (o)->this_hdr.sh_type
-                       == SHT_PREINIT_ARRAY)
-                     {
-                       _bfd_error_handler
-                         (_("%B: .preinit_array section is not allowed in DSO"),
-                          sub);
-                       break;
-                     }
+         cdefs = 0;
+         size = 0;
 
-             bfd_set_error (bfd_error_nonrepresentable_section);
-             return FALSE;
-           }
-
-         if (!_bfd_elf_add_dynamic_entry (info, DT_PREINIT_ARRAY, 0)
-             || !_bfd_elf_add_dynamic_entry (info, DT_PREINIT_ARRAYSZ, 0))
-           return FALSE;
-       }
-      s = bfd_get_section_by_name (output_bfd, ".init_array");
-      if (s != NULL && s->linker_has_input)
-       {
-         if (!_bfd_elf_add_dynamic_entry (info, DT_INIT_ARRAY, 0)
-             || !_bfd_elf_add_dynamic_entry (info, DT_INIT_ARRAYSZ, 0))
-           return FALSE;
-       }
-      s = bfd_get_section_by_name (output_bfd, ".fini_array");
-      if (s != NULL && s->linker_has_input)
-       {
-         if (!_bfd_elf_add_dynamic_entry (info, DT_FINI_ARRAY, 0)
-             || !_bfd_elf_add_dynamic_entry (info, DT_FINI_ARRAYSZ, 0))
-           return FALSE;
-       }
-
-      dynstr = bfd_get_linker_section (dynobj, ".dynstr");
-      /* If .dynstr is excluded from the link, we don't want any of
-        these tags.  Strictly, we should be checking each section
-        individually;  This quick check covers for the case where
-        someone does a /DISCARD/ : { *(*) }.  */
-      if (dynstr != NULL && dynstr->output_section != bfd_abs_section_ptr)
-       {
-         bfd_size_type strsize;
-
-         strsize = _bfd_elf_strtab_size (elf_hash_table (info)->dynstr);
-         if ((info->emit_hash
-              && !_bfd_elf_add_dynamic_entry (info, DT_HASH, 0))
-             || (info->emit_gnu_hash
-                 && !_bfd_elf_add_dynamic_entry (info, DT_GNU_HASH, 0))
-             || !_bfd_elf_add_dynamic_entry (info, DT_STRTAB, 0)
-             || !_bfd_elf_add_dynamic_entry (info, DT_SYMTAB, 0)
-             || !_bfd_elf_add_dynamic_entry (info, DT_STRSZ, strsize)
-             || !_bfd_elf_add_dynamic_entry (info, DT_SYMENT,
-                                             bed->s->sizeof_sym))
-           return FALSE;
-       }
-    }
-
-  if (! _bfd_elf_maybe_strip_eh_frame_hdr (info))
-    return FALSE;
-
-  /* The backend must work out the sizes of all the other dynamic
-     sections.  */
-  if (dynobj != NULL
-      && bed->elf_backend_size_dynamic_sections != NULL
-      && ! (*bed->elf_backend_size_dynamic_sections) (output_bfd, info))
-    return FALSE;
-
-  if (dynobj != NULL && elf_hash_table (info)->dynamic_sections_created)
-    {
-      unsigned long section_sym_count;
-      struct bfd_elf_version_tree *verdefs;
-      asection *s;
-
-      /* Set up the version definition section.  */
-      s = bfd_get_linker_section (dynobj, ".gnu.version_d");
-      BFD_ASSERT (s != NULL);
-
-      /* We may have created additional version definitions if we are
-        just linking a regular application.  */
-      verdefs = info->version_info;
-
-      /* Skip anonymous version tag.  */
-      if (verdefs != NULL && verdefs->vernum == 0)
-       verdefs = verdefs->next;
-
-      if (verdefs == NULL && !info->create_default_symver)
-       s->flags |= SEC_EXCLUDE;
-      else
-       {
-         unsigned int cdefs;
-         bfd_size_type size;
-         struct bfd_elf_version_tree *t;
-         bfd_byte *p;
-         Elf_Internal_Verdef def;
-         Elf_Internal_Verdaux defaux;
-         struct bfd_link_hash_entry *bh;
-         struct elf_link_hash_entry *h;
-         const char *name;
-
-         cdefs = 0;
-         size = 0;
-
-         /* Make space for the base version.  */
-         size += sizeof (Elf_External_Verdef);
-         size += sizeof (Elf_External_Verdaux);
-         ++cdefs;
-
-         /* Make space for the default version.  */
-         if (info->create_default_symver)
-           {
-             size += sizeof (Elf_External_Verdef);
-             ++cdefs;
+         /* Make space for the base version.  */
+         size += sizeof (Elf_External_Verdef);
+         size += sizeof (Elf_External_Verdaux);
+         ++cdefs;
+
+         /* Make space for the default version.  */
+         if (info->create_default_symver)
+           {
+             size += sizeof (Elf_External_Verdef);
+             ++cdefs;
            }
 
          for (t = verdefs; t != NULL; t = t->next)
@@ -6557,34 +6276,9 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
                }
            }
 
-         if (!_bfd_elf_add_dynamic_entry (info, DT_VERDEF, 0)
-             || !_bfd_elf_add_dynamic_entry (info, DT_VERDEFNUM, cdefs))
-           return FALSE;
-
          elf_tdata (output_bfd)->cverdefs = cdefs;
        }
 
-      if ((info->new_dtags && info->flags) || (info->flags & DF_STATIC_TLS))
-       {
-         if (!_bfd_elf_add_dynamic_entry (info, DT_FLAGS, info->flags))
-           return FALSE;
-       }
-      else if (info->flags & DF_BIND_NOW)
-       {
-         if (!_bfd_elf_add_dynamic_entry (info, DT_BIND_NOW, 0))
-           return FALSE;
-       }
-
-      if (info->flags_1)
-       {
-         if (bfd_link_executable (info))
-           info->flags_1 &= ~ (DF_1_INITFIRST
-                               | DF_1_NODELETE
-                               | DF_1_NOOPEN);
-         if (!_bfd_elf_add_dynamic_entry (info, DT_FLAGS_1, info->flags_1))
-           return FALSE;
-       }
-
       /* Work out the size of the version reference section.  */
 
       s = bfd_get_linker_section (dynobj, ".gnu.version_r");
@@ -6608,7 +6302,7 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
          s->flags |= SEC_EXCLUDE;
        else
          {
-           Elf_Internal_Verneed *t;
+           Elf_Internal_Verneed *vn;
            unsigned int size;
            unsigned int crefs;
            bfd_byte *p;
@@ -6616,15 +6310,15 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
            /* Build the version dependency section.  */
            size = 0;
            crefs = 0;
-           for (t = elf_tdata (output_bfd)->verref;
-                t != NULL;
-                t = t->vn_nextref)
+           for (vn = elf_tdata (output_bfd)->verref;
+                vn != NULL;
+                vn = vn->vn_nextref)
              {
                Elf_Internal_Vernaux *a;
 
                size += sizeof (Elf_External_Verneed);
                ++crefs;
-               for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr)
+               for (a = vn->vn_auxptr; a != NULL; a = a->vna_nextptr)
                  size += sizeof (Elf_External_Vernaux);
              }
 
@@ -6634,40 +6328,40 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
              return FALSE;
 
            p = s->contents;
-           for (t = elf_tdata (output_bfd)->verref;
-                t != NULL;
-                t = t->vn_nextref)
+           for (vn = elf_tdata (output_bfd)->verref;
+                vn != NULL;
+                vn = vn->vn_nextref)
              {
                unsigned int caux;
                Elf_Internal_Vernaux *a;
                size_t indx;
 
                caux = 0;
-               for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr)
+               for (a = vn->vn_auxptr; a != NULL; a = a->vna_nextptr)
                  ++caux;
 
-               t->vn_version = VER_NEED_CURRENT;
-               t->vn_cnt = caux;
+               vn->vn_version = VER_NEED_CURRENT;
+               vn->vn_cnt = caux;
                indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
-                                           elf_dt_name (t->vn_bfd) != NULL
-                                           ? elf_dt_name (t->vn_bfd)
-                                           : lbasename (t->vn_bfd->filename),
+                                           elf_dt_name (vn->vn_bfd) != NULL
+                                           ? elf_dt_name (vn->vn_bfd)
+                                           : lbasename (vn->vn_bfd->filename),
                                            FALSE);
                if (indx == (size_t) -1)
                  return FALSE;
-               t->vn_file = indx;
-               t->vn_aux = sizeof (Elf_External_Verneed);
-               if (t->vn_nextref == NULL)
-                 t->vn_next = 0;
+               vn->vn_file = indx;
+               vn->vn_aux = sizeof (Elf_External_Verneed);
+               if (vn->vn_nextref == NULL)
+                 vn->vn_next = 0;
                else
-                 t->vn_next = (sizeof (Elf_External_Verneed)
+                 vn->vn_next = (sizeof (Elf_External_Verneed)
                                + caux * sizeof (Elf_External_Vernaux));
 
-               _bfd_elf_swap_verneed_out (output_bfd, t,
+               _bfd_elf_swap_verneed_out (output_bfd, vn,
                                           (Elf_External_Verneed *) p);
                p += sizeof (Elf_External_Verneed);
 
-               for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr)
+               for (a = vn->vn_auxptr; a != NULL; a = a->vna_nextptr)
                  {
                    a->vna_hash = bfd_elf_hash (a->vna_nodename);
                    indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
@@ -6686,19 +6380,344 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
                  }
              }
 
-           if (!_bfd_elf_add_dynamic_entry (info, DT_VERNEED, 0)
-               || !_bfd_elf_add_dynamic_entry (info, DT_VERNEEDNUM, crefs))
-             return FALSE;
-
            elf_tdata (output_bfd)->cverrefs = crefs;
          }
       }
+    }
+
+  bed = get_elf_backend_data (output_bfd);
+
+  if (info->gc_sections && bed->can_gc_sections)
+    {
+      struct elf_gc_sweep_symbol_info sweep_info;
+      unsigned long section_sym_count;
+
+      /* Remove the symbols that were in the swept sections from the
+        dynamic symbol table.  GCFIXME: Anyone know how to get them
+        out of the static symbol table as well?  */
+      sweep_info.info = info;
+      sweep_info.hide_symbol = bed->elf_backend_hide_symbol;
+      elf_link_hash_traverse (elf_hash_table (info), elf_gc_sweep_symbol,
+                             &sweep_info);
+
+      _bfd_elf_link_renumber_dynsyms (output_bfd, info, &section_sym_count);
+    }
+
+  /* Any syms created from now on start with -1 in
+     got.refcount/offset and plt.refcount/offset.  */
+  elf_hash_table (info)->init_got_refcount
+    = elf_hash_table (info)->init_got_offset;
+  elf_hash_table (info)->init_plt_refcount
+    = elf_hash_table (info)->init_plt_offset;
+
+  if (bfd_link_relocatable (info)
+      && !_bfd_elf_size_group_sections (info))
+    return FALSE;
+
+  /* The backend may have to create some sections regardless of whether
+     we're dynamic or not.  */
+  if (bed->elf_backend_always_size_sections
+      && ! (*bed->elf_backend_always_size_sections) (output_bfd, info))
+    return FALSE;
+
+  /* Determine any GNU_STACK segment requirements, after the backend
+     has had a chance to set a default segment size.  */
+  if (info->execstack)
+    elf_stack_flags (output_bfd) = PF_R | PF_W | PF_X;
+  else if (info->noexecstack)
+    elf_stack_flags (output_bfd) = PF_R | PF_W;
+  else
+    {
+      bfd *inputobj;
+      asection *notesec = NULL;
+      int exec = 0;
+
+      for (inputobj = info->input_bfds;
+          inputobj;
+          inputobj = inputobj->link.next)
+       {
+         asection *s;
+
+         if (inputobj->flags
+             & (DYNAMIC | EXEC_P | BFD_PLUGIN | BFD_LINKER_CREATED))
+           continue;
+         s = bfd_get_section_by_name (inputobj, ".note.GNU-stack");
+         if (s)
+           {
+             if (s->flags & SEC_CODE)
+               exec = PF_X;
+             notesec = s;
+           }
+         else if (bed->default_execstack)
+           exec = PF_X;
+       }
+      if (notesec || info->stacksize > 0)
+       elf_stack_flags (output_bfd) = PF_R | PF_W | exec;
+      if (notesec && exec && bfd_link_relocatable (info)
+         && notesec->output_section != bfd_abs_section_ptr)
+       notesec->output_section->flags |= SEC_CODE;
+    }
+
+  if (dynobj != NULL && elf_hash_table (info)->dynamic_sections_created)
+    {
+      struct elf_info_failed eif;
+      struct elf_link_hash_entry *h;
+      asection *dynstr;
+      asection *s;
+
+      *sinterpptr = bfd_get_linker_section (dynobj, ".interp");
+      BFD_ASSERT (*sinterpptr != NULL || !bfd_link_executable (info) || info->nointerp);
+
+      if (soname != NULL)
+       {
+         soname_indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
+                                            soname, TRUE);
+         if (soname_indx == (size_t) -1
+             || !_bfd_elf_add_dynamic_entry (info, DT_SONAME, soname_indx))
+           return FALSE;
+       }
+
+      if (info->symbolic)
+       {
+         if (!_bfd_elf_add_dynamic_entry (info, DT_SYMBOLIC, 0))
+           return FALSE;
+         info->flags |= DF_SYMBOLIC;
+       }
+
+      if (rpath != NULL)
+       {
+         size_t indx;
+         bfd_vma tag;
+
+         indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr, rpath,
+                                     TRUE);
+         if (indx == (size_t) -1)
+           return FALSE;
+
+         tag = info->new_dtags ? DT_RUNPATH : DT_RPATH;
+         if (!_bfd_elf_add_dynamic_entry (info, tag, indx))
+           return FALSE;
+       }
+
+      if (filter_shlib != NULL)
+       {
+         size_t indx;
+
+         indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
+                                     filter_shlib, TRUE);
+         if (indx == (size_t) -1
+             || !_bfd_elf_add_dynamic_entry (info, DT_FILTER, indx))
+           return FALSE;
+       }
+
+      if (auxiliary_filters != NULL)
+       {
+         const char * const *p;
+
+         for (p = auxiliary_filters; *p != NULL; p++)
+           {
+             size_t indx;
+
+             indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
+                                         *p, TRUE);
+             if (indx == (size_t) -1
+                 || !_bfd_elf_add_dynamic_entry (info, DT_AUXILIARY, indx))
+               return FALSE;
+           }
+       }
+
+      if (audit != NULL)
+       {
+         size_t indx;
+
+         indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr, audit,
+                                     TRUE);
+         if (indx == (size_t) -1
+             || !_bfd_elf_add_dynamic_entry (info, DT_AUDIT, indx))
+           return FALSE;
+       }
+
+      if (depaudit != NULL)
+       {
+         size_t indx;
+
+         indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr, depaudit,
+                                     TRUE);
+         if (indx == (size_t) -1
+             || !_bfd_elf_add_dynamic_entry (info, DT_DEPAUDIT, indx))
+           return FALSE;
+       }
+
+      eif.info = info;
+      eif.failed = FALSE;
+
+      /* Find all symbols which were defined in a dynamic object and make
+        the backend pick a reasonable value for them.  */
+      elf_link_hash_traverse (elf_hash_table (info),
+                             _bfd_elf_adjust_dynamic_symbol,
+                             &eif);
+      if (eif.failed)
+       return FALSE;
+
+      /* Add some entries to the .dynamic section.  We fill in some of the
+        values later, in bfd_elf_final_link, but we must add the entries
+        now so that we know the final size of the .dynamic section.  */
+
+      /* If there are initialization and/or finalization functions to
+        call then add the corresponding DT_INIT/DT_FINI entries.  */
+      h = (info->init_function
+          ? elf_link_hash_lookup (elf_hash_table (info),
+                                  info->init_function, FALSE,
+                                  FALSE, FALSE)
+          : NULL);
+      if (h != NULL
+         && (h->ref_regular
+             || h->def_regular))
+       {
+         if (!_bfd_elf_add_dynamic_entry (info, DT_INIT, 0))
+           return FALSE;
+       }
+      h = (info->fini_function
+          ? elf_link_hash_lookup (elf_hash_table (info),
+                                  info->fini_function, FALSE,
+                                  FALSE, FALSE)
+          : NULL);
+      if (h != NULL
+         && (h->ref_regular
+             || h->def_regular))
+       {
+         if (!_bfd_elf_add_dynamic_entry (info, DT_FINI, 0))
+           return FALSE;
+       }
+
+      s = bfd_get_section_by_name (output_bfd, ".preinit_array");
+      if (s != NULL && s->linker_has_input)
+       {
+         /* DT_PREINIT_ARRAY is not allowed in shared library.  */
+         if (! bfd_link_executable (info))
+           {
+             bfd *sub;
+             asection *o;
+
+             for (sub = info->input_bfds; sub != NULL;
+                  sub = sub->link.next)
+               if (bfd_get_flavour (sub) == bfd_target_elf_flavour)
+                 for (o = sub->sections; o != NULL; o = o->next)
+                   if (elf_section_data (o)->this_hdr.sh_type
+                       == SHT_PREINIT_ARRAY)
+                     {
+                       _bfd_error_handler
+                         (_("%B: .preinit_array section is not allowed in DSO"),
+                          sub);
+                       break;
+                     }
+
+             bfd_set_error (bfd_error_nonrepresentable_section);
+             return FALSE;
+           }
+
+         if (!_bfd_elf_add_dynamic_entry (info, DT_PREINIT_ARRAY, 0)
+             || !_bfd_elf_add_dynamic_entry (info, DT_PREINIT_ARRAYSZ, 0))
+           return FALSE;
+       }
+      s = bfd_get_section_by_name (output_bfd, ".init_array");
+      if (s != NULL && s->linker_has_input)
+       {
+         if (!_bfd_elf_add_dynamic_entry (info, DT_INIT_ARRAY, 0)
+             || !_bfd_elf_add_dynamic_entry (info, DT_INIT_ARRAYSZ, 0))
+           return FALSE;
+       }
+      s = bfd_get_section_by_name (output_bfd, ".fini_array");
+      if (s != NULL && s->linker_has_input)
+       {
+         if (!_bfd_elf_add_dynamic_entry (info, DT_FINI_ARRAY, 0)
+             || !_bfd_elf_add_dynamic_entry (info, DT_FINI_ARRAYSZ, 0))
+           return FALSE;
+       }
+
+      dynstr = bfd_get_linker_section (dynobj, ".dynstr");
+      /* If .dynstr is excluded from the link, we don't want any of
+        these tags.  Strictly, we should be checking each section
+        individually;  This quick check covers for the case where
+        someone does a /DISCARD/ : { *(*) }.  */
+      if (dynstr != NULL && dynstr->output_section != bfd_abs_section_ptr)
+       {
+         bfd_size_type strsize;
+
+         strsize = _bfd_elf_strtab_size (elf_hash_table (info)->dynstr);
+         if ((info->emit_hash
+              && !_bfd_elf_add_dynamic_entry (info, DT_HASH, 0))
+             || (info->emit_gnu_hash
+                 && !_bfd_elf_add_dynamic_entry (info, DT_GNU_HASH, 0))
+             || !_bfd_elf_add_dynamic_entry (info, DT_STRTAB, 0)
+             || !_bfd_elf_add_dynamic_entry (info, DT_SYMTAB, 0)
+             || !_bfd_elf_add_dynamic_entry (info, DT_STRSZ, strsize)
+             || !_bfd_elf_add_dynamic_entry (info, DT_SYMENT,
+                                             bed->s->sizeof_sym))
+           return FALSE;
+       }
+    }
+
+  if (! _bfd_elf_maybe_strip_eh_frame_hdr (info))
+    return FALSE;
+
+  /* The backend must work out the sizes of all the other dynamic
+     sections.  */
+  if (dynobj != NULL
+      && bed->elf_backend_size_dynamic_sections != NULL
+      && ! (*bed->elf_backend_size_dynamic_sections) (output_bfd, info))
+    return FALSE;
+
+  if (dynobj != NULL && elf_hash_table (info)->dynamic_sections_created)
+    {
+      unsigned long section_sym_count;
+
+      if (elf_tdata (output_bfd)->cverdefs)
+       {
+         unsigned int crefs = elf_tdata (output_bfd)->cverdefs;
+
+         if (!_bfd_elf_add_dynamic_entry (info, DT_VERDEF, 0)
+             || !_bfd_elf_add_dynamic_entry (info, DT_VERDEFNUM, crefs))
+           return FALSE;
+       }
+
+      if ((info->new_dtags && info->flags) || (info->flags & DF_STATIC_TLS))
+       {
+         if (!_bfd_elf_add_dynamic_entry (info, DT_FLAGS, info->flags))
+           return FALSE;
+       }
+      else if (info->flags & DF_BIND_NOW)
+       {
+         if (!_bfd_elf_add_dynamic_entry (info, DT_BIND_NOW, 0))
+           return FALSE;
+       }
+
+      if (info->flags_1)
+       {
+         if (bfd_link_executable (info))
+           info->flags_1 &= ~ (DF_1_INITFIRST
+                               | DF_1_NODELETE
+                               | DF_1_NOOPEN);
+         if (!_bfd_elf_add_dynamic_entry (info, DT_FLAGS_1, info->flags_1))
+           return FALSE;
+       }
+
+      if (elf_tdata (output_bfd)->cverrefs)
+       {
+         unsigned int crefs = elf_tdata (output_bfd)->cverrefs;
+
+         if (!_bfd_elf_add_dynamic_entry (info, DT_VERNEED, 0)
+             || !_bfd_elf_add_dynamic_entry (info, DT_VERNEEDNUM, crefs))
+           return FALSE;
+       }
 
       if ((elf_tdata (output_bfd)->cverrefs == 0
           && elf_tdata (output_bfd)->cverdefs == 0)
          || _bfd_elf_link_renumber_dynsyms (output_bfd, info,
                                             &section_sym_count) == 0)
        {
+         asection *s;
+
          s = bfd_get_linker_section (dynobj, ".gnu.version");
          s->flags |= SEC_EXCLUDE;
        }
index c73a5c3..a3dd303 100644 (file)
@@ -1,3 +1,15 @@
+2017-02-22  Maciej W. Rozycki  <macro@imgtec.com>
+
+       PR ld/20828
+       * testsuite/ld-elf/pr20828-d.sd: New test.
+       * testsuite/ld-elf/pr20828-e.sd: New test.
+       * testsuite/ld-elf/pr20828-v.od: New test.
+       * testsuite/ld-elf/pr20828-v.ver: New test version script.
+       * testsuite/ld-elf/pr20828-v.ld: New test linker script.
+       * testsuite/ld-elf/pr20828.ld: Add `.gnu.version' and
+       `.gnu.version_d'.
+       * testsuite/ld-elf/shared.exp: Run the new tests.
+
 2017-02-21  Nick Clifton  <nickc@redhat.com>
 
        * testsuite/ld-ifunc/pr18808b.c (bar): Fix compile time warning
diff --git a/ld/testsuite/ld-elf/pr20828-d.sd b/ld/testsuite/ld-elf/pr20828-d.sd
new file mode 100644 (file)
index 0000000..a5777a4
--- /dev/null
@@ -0,0 +1,9 @@
+# Make sure `vdata' is global rather than local in the dynamic symbol table,
+# e.g.:
+#    Num:    Value  Size Type    Bind   Vis      Ndx Name
+#      1: 00000000     0 OBJECT  GLOBAL DEFAULT  ABS vdata
+# vs:
+#      1: 00000000     0 OBJECT  LOCAL  DEFAULT  ABS vdata
+#...
+ *[0-9]+: +[0-9a-f]+ +0 +OBJECT +GLOBAL +DEFAULT +ABS +vdata
+#pass
diff --git a/ld/testsuite/ld-elf/pr20828-e.sd b/ld/testsuite/ld-elf/pr20828-e.sd
new file mode 100644 (file)
index 0000000..893fb31
--- /dev/null
@@ -0,0 +1,9 @@
+# Make sure `edata' is global rather than local in the dynamic symbol table,
+# e.g.:
+#    Num:    Value  Size Type    Bind   Vis      Ndx Name
+#      1: 00000000     0 NOTYPE  GLOBAL DEFAULT    1 edata@@vdata
+# vs:
+#      1: 00000000     0 NOTYPE  LOCAL  DEFAULT    1 edata@@vdata
+#...
+ *[0-9]+: +[0-9a-f]+ +0 +NOTYPE +GLOBAL +DEFAULT +[0-9]+ +edata@@vdata
+#pass
diff --git a/ld/testsuite/ld-elf/pr20828-v.ld b/ld/testsuite/ld-elf/pr20828-v.ld
new file mode 100644 (file)
index 0000000..cdba796
--- /dev/null
@@ -0,0 +1,18 @@
+SECTIONS
+{
+  .hash : { *(.hash) }
+  .dynsym : { *(.dynsym) }
+  .dynstr : { *(.dynstr) }
+  .gnu.version : { *(.gnu.version) }
+  .gnu.version_d : { *(.gnu.version_d) }
+  .text : { *(.text) }
+  .dynamic : { *(.dynamic) }
+  .data : { *(.data) }
+  .symtab : { *(.symtab) }
+  .strtab : { *(.strtab) }
+  .shstrtab : { *(.shstrtab) }
+  .plt : { *(.plt) }
+  .got.plt : { *(.got.plt) }
+  .got : { *(.got) }
+  /DISCARD/ : { *(*) }
+}
diff --git a/ld/testsuite/ld-elf/pr20828-v.od b/ld/testsuite/ld-elf/pr20828-v.od
new file mode 100644 (file)
index 0000000..12953b9
--- /dev/null
@@ -0,0 +1,5 @@
+#...
+Version definitions:
+1 0x01 0x0b2e1e3([12]) pr20828-v-\1
+2 0x00 0x0cc85d2f ver_foo
+#pass
diff --git a/ld/testsuite/ld-elf/pr20828-v.s b/ld/testsuite/ld-elf/pr20828-v.s
new file mode 100644 (file)
index 0000000..363e09f
--- /dev/null
@@ -0,0 +1,13 @@
+       .text
+       .globl  new_foo
+       .type   new_foo, %function
+new_foo:
+       .size   new_foo, . - new_foo
+       .symver new_foo, foo@@ver_foo
+
+       .data
+       .globl  bar
+       .type   bar, %object
+bar:
+       .dc.a   foo
+       .size   bar, . - bar
diff --git a/ld/testsuite/ld-elf/pr20828-v.ver b/ld/testsuite/ld-elf/pr20828-v.ver
new file mode 100644 (file)
index 0000000..addc0f0
--- /dev/null
@@ -0,0 +1 @@
+vdata { global: edata; local: *; };
index 0166f52..034887d 100644 (file)
@@ -10,6 +10,8 @@ SECTIONS
   .hash : { *(.hash) }
   .dynsym : { *(.dynsym) }
   .dynstr : { *(.dynstr) }
+  .gnu.version : { *(.gnu.version) }
+  .gnu.version_d : { *(.gnu.version_d) }
   .shstrtab : { *(.shstrtab) }
   .symtab : { *(.symtab) }
   .strtab : { *(.strtab) }
index 3b8ab18..41fb2ba 100644 (file)
@@ -50,6 +50,8 @@ if [istarget "tic6x-*-*"] {
 # - the shared library symbols have been swept in section garbage collection.
 # Verify that the symbols are global rather than local and that a version
 # script adjusts them accordingly.
+# Also verify that a version definition supplied by an object rather than
+# a version script and forcibly exported is unaffected by section GC.
 if { [check_gc_sections_available] } {
     run_ld_link_tests [list \
        [list \
@@ -77,7 +79,41 @@ if { [check_gc_sections_available] } {
            {pr20828.s} \
            {{readelf --dyn-syms pr20828-b.sd} \
             {readelf --dyn-syms pr20828-c.sd}} \
-           "pr20828-2.so"]]
+           "pr20828-2.so"] \
+       [list \
+           "PR ld/20828 dynamic symbols with section GC\
+            (versioned shared library)" \
+           "$LFLAGS -shared --gc-sections -T pr20828.ld\
+            --version-script=pr20828-v.ver" \
+           "" "$AFLAGS_PIC" \
+           {pr20828.s} \
+           {{readelf --dyn-syms pr20828-c.sd} \
+            {readelf --dyn-syms pr20828-d.sd} \
+            {readelf --dyn-syms pr20828-e.sd}} \
+           "libpr20828-v.so"] \
+       [list \
+           "PR ld/20828 dynamic symbols with section GC (versioned)" \
+           "$LFLAGS -shared --gc-sections -T pr20828.ld\
+            --version-script=pr20828-v.ver" \
+           "tmpdir/libpr20828-v.so" \
+           "$AFLAGS_PIC" \
+           {pr20828.s} \
+           {{readelf --dyn-syms pr20828-c.sd} \
+            {readelf --dyn-syms pr20828-d.sd} \
+            {readelf --dyn-syms pr20828-e.sd}} \
+           "pr20828-v.so"] \
+       [list \
+           "PR ld/20828 forcibly exported symbol version without section GC" \
+           "$LFLAGS -e foo -E -T pr20828-v.ld" "" "" \
+           {pr20828-v.s} \
+           {{objdump -p pr20828-v.od}} \
+           "pr20828-v-1"] \
+       [list \
+           "PR ld/20828 forcibly exported symbol version with section GC" \
+           "$LFLAGS -e foo --gc-sections -E -T pr20828-v.ld" "" "" \
+           {pr20828-v.s} \
+           {{objdump -p pr20828-v.od}} \
+           "pr20828-v-2"]]
 }
 
 # Check to see if the C compiler works