From Eric Youngdale <eric@andante.jic.com>:
authorIan Lance Taylor <ian@airs.com>
Mon, 10 Mar 1997 04:43:42 +0000 (04:43 +0000)
committerIan Lance Taylor <ian@airs.com>
Mon, 10 Mar 1997 04:43:42 +0000 (04:43 +0000)
* elf-bfd.h (elf_symbol_type): Add version field.
* elfcode.h (elf_slurp_symbol_table): Set version field.
* elflink.h (elf_link_add_object_symbols): When creating an
  indirect symbol for a default version symbol, set DEF_DYNAMIC if
  appropriate.  Set up an indirection from the nondefault version of
the symbol as well.
(NAME(bfd_elf,size_dynamic_sections)): Call
elf_link_assign_sym_version before checking whether there are any
versions.  Always record the version name as a dynamic symbol.
Initialize counters.
(elf_link_assign_sym_version): After finding a version, see if a
symbol should be forced to local scope.  Create a new version
definition if appropriate.
(elf_link_output_extsym): Correct indirect symbol handling.
* elf.c (bfd_elf_print_symbol): Print version information.
(bfd_section_from_shdr): Turn version sections into BFD sections.
(elf_fake_sections): Only copy cverdefs and cverrefs into sh_info
if sh_info is not already set.
(_bfd_elf_copy_private_section_data): Copy sh_info for version
sections.
* elflink.c (_bfd_elf_link_record_dynamic_symbol): Tell
_bfd_stringtab_add to copy the name into permanent memory if
appropriate.

bfd/ChangeLog
bfd/elf-bfd.h
bfd/elf.c
bfd/elflink.h

index 0e4e5cd9508b324141b745f77d0548aaffafbe61..295938b14c8929edc9d2102d027fd964c69255c0 100644 (file)
@@ -1,3 +1,30 @@
+Sun Mar  9 23:08:49 1997  Ian Lance Taylor  <ian@cygnus.com>
+
+       From Eric Youngdale <eric@andante.jic.com>:
+       * elf-bfd.h (elf_symbol_type): Add version field.
+       * elfcode.h (elf_slurp_symbol_table): Set version field.
+       * elflink.h (elf_link_add_object_symbols): When creating an
+       indirect symbol for a default version symbol, set DEF_DYNAMIC if
+       appropriate.  Set up an indirection from the nondefault version of
+       the symbol as well.
+       (NAME(bfd_elf,size_dynamic_sections)): Call
+       elf_link_assign_sym_version before checking whether there are any
+       versions.  Always record the version name as a dynamic symbol.
+       Initialize counters.
+       (elf_link_assign_sym_version): After finding a version, see if a
+       symbol should be forced to local scope.  Create a new version
+       definition if appropriate.
+       (elf_link_output_extsym): Correct indirect symbol handling.
+       * elf.c (bfd_elf_print_symbol): Print version information.
+       (bfd_section_from_shdr): Turn version sections into BFD sections.
+       (elf_fake_sections): Only copy cverdefs and cverrefs into sh_info
+       if sh_info is not already set.
+       (_bfd_elf_copy_private_section_data): Copy sh_info for version
+       sections.
+       * elflink.c (_bfd_elf_link_record_dynamic_symbol): Tell
+       _bfd_stringtab_add to copy the name into permanent memory if
+       appropriate.
+
 Fri Mar  7 11:55:31 1997  H.J. Lu  <hjl@gnu.ai.mit.edu>
 
        * elf64-alpha.c (alpha_elf_dynamic_symbol_p): Fully parenthesize.
index 22532598b14f20459939eeb90af711d67bc9f475..455bc5c2ef88eb519e376caf4af737ddc28cb17d 100644 (file)
@@ -1,5 +1,5 @@
 /* BFD back-end data structures for ELF files.
-   Copyright (C) 1992, 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
+   Copyright (C) 1992, 93, 94, 95, 96, 1997 Free Software Foundation, Inc.
    Written by Cygnus Support.
 
 This file is part of BFD, the Binary File Descriptor library.
@@ -62,6 +62,12 @@ typedef struct
       PTR any;
     }
   tc_data;
+
+  /* Version information.  This is from an Elf_Internal_Versym
+     structure in a SHT_GNU_versym section.  It is zero if there is no
+     version information.  */
+  unsigned short version;
+
 } elf_symbol_type;
 \f
 /* ELF linker hash table entries.  */
@@ -107,6 +113,19 @@ struct elf_link_hash_entry
      from the beginning of the section.  */
   struct elf_linker_section_pointers *linker_section_pointer;
 
+  /* Version information.  */
+  union
+  {
+    /* This field is used for a symbol which is not defined in a
+       regular object.  It points to the version information read in
+       from the dynamic object.  */
+    Elf_Internal_Verdef *verdef;
+    /* This field is used for a symbol which is defined in a regular
+       object.  It is set up in size_dynamic_sections.  It points to
+       the version information we should write out for this symbol.  */
+    struct bfd_elf_version_tree *vertree;
+  } verinfo;
+
   /* Symbol type (STT_NOTYPE, STT_OBJECT, etc.).  */
   char type;
 
@@ -114,7 +133,7 @@ struct elf_link_hash_entry
   unsigned char other;
 
   /* Some flags; legal values follow.  */
-  unsigned char elf_link_hash_flags;
+  unsigned short elf_link_hash_flags;
   /* Symbol is referenced by a non-shared object.  */
 #define ELF_LINK_HASH_REF_REGULAR 01
   /* Symbol is defined by a non-shared object.  */
@@ -131,8 +150,8 @@ struct elf_link_hash_entry
 #define ELF_LINK_HASH_NEEDS_PLT 0100
   /* Symbol appears in a non-ELF input file.  */
 #define ELF_LINK_NON_ELF 0200
-  /* Note: If you add more flags, you must change the type of
-     elf_link_hash_flags.  */
+  /* Symbol should be marked as hidden in the version information.  */
+#define ELF_LINK_HIDDEN 0400
 };
 
 /* ELF linker hash table.  */
@@ -572,8 +591,12 @@ struct elf_obj_tdata
   Elf_Internal_Shdr strtab_hdr;
   Elf_Internal_Shdr dynsymtab_hdr;
   Elf_Internal_Shdr dynstrtab_hdr;
+  Elf_Internal_Shdr dynversym_hdr;
+  Elf_Internal_Shdr dynverref_hdr;
+  Elf_Internal_Shdr dynverdef_hdr;
   unsigned int symtab_section, shstrtab_section;
   unsigned int strtab_section, dynsymtab_section;
+  unsigned int dynversym_section, dynverdef_section, dynverref_section;
   file_ptr next_file_pos;
   void *prstatus;                      /* The raw /proc prstatus structure */
   void *prpsinfo;                      /* The raw /proc prpsinfo structure */
@@ -626,9 +649,26 @@ struct elf_obj_tdata
      find_nearest_line.  */
   struct mips_elf_find_line *find_line_info;
 
+  /* An array of stub sections indexed by symbol number, used by the
+     MIPS ELF linker.  FIXME: We should figure out some way to only
+     include this field for a MIPS ELF target.  */
+  asection **local_stubs;
+
   /* Used to determine if the e_flags field has been initialized */
   boolean flags_init;
 
+  /* Number of symbol version definitions we are about to emit.  */
+  int cverdefs;
+
+  /* Number of symbol version references we are about to emit.  */
+  int cverrefs;
+
+  /* Symbol version definitions in external objects.  */
+  Elf_Internal_Verdef *verdef;
+
+  /* Symbol version references to external objects.  */
+  Elf_Internal_Verneed *verref;
+
   /* Linker sections that we are interested in.  */
   struct elf_linker_section *linker_section[ (int)LINKER_SECTION_MAX ];
 };
@@ -639,6 +679,9 @@ struct elf_obj_tdata
 #define elf_shstrtab(bfd)      (elf_tdata(bfd) -> strtab_ptr)
 #define elf_onesymtab(bfd)     (elf_tdata(bfd) -> symtab_section)
 #define elf_dynsymtab(bfd)     (elf_tdata(bfd) -> dynsymtab_section)
+#define elf_dynversym(bfd)     (elf_tdata(bfd) -> dynversym_section)
+#define elf_dynverdef(bfd)     (elf_tdata(bfd) -> dynverdef_section)
+#define elf_dynverref(bfd)     (elf_tdata(bfd) -> dynverref_section)
 #define elf_num_locals(bfd)    (elf_tdata(bfd) -> num_locals)
 #define elf_num_globals(bfd)   (elf_tdata(bfd) -> num_globals)
 #define elf_section_syms(bfd)  (elf_tdata(bfd) -> section_syms)
@@ -654,6 +697,27 @@ struct elf_obj_tdata
 #define elf_flags_init(bfd)    (elf_tdata(bfd) -> flags_init)
 #define elf_linker_section(bfd,n) (elf_tdata(bfd) -> linker_section[(int)n])
 \f
+extern void _bfd_elf_swap_verdef_in
+  PARAMS ((bfd *, const Elf_External_Verdef *, Elf_Internal_Verdef *));
+extern void _bfd_elf_swap_verdef_out
+  PARAMS ((bfd *, const Elf_Internal_Verdef *, Elf_External_Verdef *));
+extern void _bfd_elf_swap_verdaux_in
+  PARAMS ((bfd *, const Elf_External_Verdaux *, Elf_Internal_Verdaux *));
+extern void _bfd_elf_swap_verdaux_out
+  PARAMS ((bfd *, const Elf_Internal_Verdaux *, Elf_External_Verdaux *));
+extern void _bfd_elf_swap_verneed_in
+  PARAMS ((bfd *, const Elf_External_Verneed *, Elf_Internal_Verneed *));
+extern void _bfd_elf_swap_verneed_out
+  PARAMS ((bfd *, const Elf_Internal_Verneed *, Elf_External_Verneed *));
+extern void _bfd_elf_swap_vernaux_in
+  PARAMS ((bfd *, const Elf_External_Vernaux *, Elf_Internal_Vernaux *));
+extern void _bfd_elf_swap_vernaux_out
+  PARAMS ((bfd *, const Elf_Internal_Vernaux *, Elf_External_Vernaux *));
+extern void _bfd_elf_swap_versym_in
+  PARAMS ((bfd *, const Elf_External_Versym *, Elf_Internal_Versym *));
+extern void _bfd_elf_swap_versym_out
+  PARAMS ((bfd *, const Elf_Internal_Versym *, Elf_External_Versym *));
+
 extern int _bfd_elf_section_from_bfd_section PARAMS ((bfd *, asection *));
 extern char *bfd_elf_string_from_elf_section
   PARAMS ((bfd *, unsigned, unsigned));
@@ -690,6 +754,7 @@ extern boolean _bfd_elf_link_hash_table_init
           struct bfd_hash_entry *(*) (struct bfd_hash_entry *,
                                       struct bfd_hash_table *,
                                       const char *)));
+extern boolean _bfd_elf_slurp_version_tables PARAMS ((bfd *));
 
 extern boolean _bfd_elf_copy_private_symbol_data
   PARAMS ((bfd *, asymbol *, bfd *, asymbol *));
@@ -712,6 +777,7 @@ extern long _bfd_elf_canonicalize_dynamic_reloc PARAMS ((bfd *, arelent **,
 extern asymbol *_bfd_elf_make_empty_symbol PARAMS ((bfd *));
 extern void _bfd_elf_get_symbol_info PARAMS ((bfd *, asymbol *,
                                               symbol_info *));
+extern boolean _bfd_elf_is_local_label_name PARAMS ((bfd *, const char *));
 extern alent *_bfd_elf_get_lineno PARAMS ((bfd *, asymbol *));
 extern boolean _bfd_elf_set_arch_mach PARAMS ((bfd *, enum bfd_architecture,
                                                unsigned long));
index cdf1a31004a1f54f432ec6bbb38fd3f119b0727a..602de954309382387d938359e6cd1ebd69bfdef8 100644 (file)
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -701,9 +701,10 @@ _bfd_elf_print_private_bfd_data (abfd, farg)
 }
 
 /* Display ELF-specific fields of a symbol.  */
+
 void
-bfd_elf_print_symbol (ignore_abfd, filep, symbol, how)
-     bfd *ignore_abfd;
+bfd_elf_print_symbol (abfd, filep, symbol, how)
+     bfd *abfd;
      PTR filep;
      asymbol *symbol;
      bfd_print_symbol_type how;
@@ -733,11 +734,64 @@ bfd_elf_print_symbol (ignore_abfd, filep, symbol, how)
                     (bfd_is_com_section (symbol->section)
                      ? ((elf_symbol_type *) symbol)->internal_elf_sym.st_value
                      : ((elf_symbol_type *) symbol)->internal_elf_sym.st_size));
+
+       /* If we have version information, print it.  */
+       if (elf_tdata (abfd)->dynversym_section != 0
+           && (elf_tdata (abfd)->dynverdef_section != 0
+               || elf_tdata (abfd)->dynverref_section != 0))
+         {
+           unsigned int vernum;
+           const char *version_string;
+
+           vernum = ((elf_symbol_type *) symbol)->version & VERSYM_VERSION;
+
+           if (vernum == 0)
+             version_string = "";
+           else if (vernum == 1)
+             version_string = "Base";
+           else if (vernum < elf_tdata (abfd)->cverdefs)
+             version_string =
+               elf_tdata (abfd)->verdef[vernum - 1].vd_nodename;
+           else
+             {
+               Elf_Internal_Verneed *t;
+
+               version_string = "";
+               for (t = elf_tdata (abfd)->verref;
+                    t != NULL;
+                    t = t->vn_nextref)
+                 {
+                   Elf_Internal_Vernaux *a;
+
+                   for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr)
+                     {
+                       if (a->vna_other == vernum)
+                         {
+                           version_string = a->vna_nodename;
+                           break;
+                         }
+                     }
+                 }
+             }
+
+           if ((((elf_symbol_type *) symbol)->version & VERSYM_HIDDEN) == 0)
+             fprintf (file, " %-12s", version_string);
+           else
+             {
+               int i;
+
+               fprintf (file, " (%s)", version_string);
+               for (i = strlen (version_string) - 10; i > 0; --i)
+                 putc (' ', file);
+             }
+         }
+
        /* If the st_other field is not zero, print it.  */
        if (((elf_symbol_type *) symbol)->internal_elf_sym.st_other != 0)
          fprintf (file, " 0x%02x",
                   ((unsigned int)
                    ((elf_symbol_type *) symbol)->internal_elf_sym.st_other));
+
        fprintf (file, " %s", symbol->name);
       }
       break;
@@ -1098,16 +1152,19 @@ bfd_section_from_shdr (abfd, shindex)
     case SHT_GNU_verdef:
       elf_dynverdef (abfd) = shindex;
       elf_tdata (abfd)->dynverdef_hdr = *hdr;
+      return _bfd_elf_make_section_from_shdr (abfd, hdr, name);
       break;
 
     case SHT_GNU_versym:
       elf_dynversym (abfd) = shindex;
       elf_tdata (abfd)->dynversym_hdr = *hdr;
+      return _bfd_elf_make_section_from_shdr (abfd, hdr, name);
       break;
 
     case SHT_GNU_verneed:
       elf_dynverref (abfd) = shindex;
       elf_tdata (abfd)->dynverref_hdr = *hdr;
+      return _bfd_elf_make_section_from_shdr (abfd, hdr, name);
       break;
 
     case SHT_SHLIB:
@@ -1338,13 +1395,27 @@ elf_fake_sections (abfd, asect, failedptrarg)
     {
       this_hdr->sh_type = SHT_GNU_verdef;
       this_hdr->sh_entsize = 0;
-      this_hdr->sh_info = elf_tdata (abfd)->cverdefs;
+      /* objcopy or strip will copy over sh_info, but may not set
+         cverdefs.  The linker will set cverdefs, but sh_info will be
+         zero.  */
+      if (this_hdr->sh_info == 0)
+       this_hdr->sh_info = elf_tdata (abfd)->cverdefs;
+      else
+       BFD_ASSERT (elf_tdata (abfd)->cverdefs == 0
+                   || this_hdr->sh_info == elf_tdata (abfd)->cverdefs);
     }
   else if (strcmp (asect->name, ".gnu.version_r") == 0)
     {
       this_hdr->sh_type = SHT_GNU_verneed;
       this_hdr->sh_entsize = 0;
-      this_hdr->sh_info = elf_tdata (abfd)->cverrefs;
+      /* objcopy or strip will copy over sh_info, but may not set
+         cverrefs.  The linker will set cverrefs, but sh_info will be
+         zero.  */
+      if (this_hdr->sh_info == 0)
+       this_hdr->sh_info = elf_tdata (abfd)->cverrefs;
+      else
+       BFD_ASSERT (elf_tdata (abfd)->cverrefs == 0
+                   || this_hdr->sh_info == elf_tdata (abfd)->cverrefs);
     }
   else if ((asect->flags & SEC_ALLOC) != 0
           && (asect->flags & SEC_LOAD) != 0)
@@ -3187,7 +3258,9 @@ _bfd_elf_copy_private_section_data (ibfd, isec, obfd, osec)
   ohdr->sh_entsize = ihdr->sh_entsize;
 
   if (ihdr->sh_type == SHT_SYMTAB
-      || ihdr->sh_type == SHT_DYNSYM)
+      || ihdr->sh_type == SHT_DYNSYM
+      || ihdr->sh_type == SHT_GNU_verneed
+      || ihdr->sh_type == SHT_GNU_verdef)
     ohdr->sh_info = ihdr->sh_info;
 
   return true;
@@ -3831,17 +3904,34 @@ _bfd_elf_get_symbol_info (ignore_abfd, symbol, ret)
   bfd_symbol_info (symbol, ret);
 }
 
-/* Return whether a symbol name implies a local symbol.  In ELF, local
-   symbols generally start with ``.L''.  Most targets use this
-   function for the is_local_label_name entry point, but some override
-   it.  */
+/* Return whether a symbol name implies a local symbol.  Most targets
+   use this function for the is_local_label_name entry point, but some
+   override it.  */
 
 boolean
 _bfd_elf_is_local_label_name (abfd, name)
      bfd *abfd;
      const char *name;
 {
-  return name[0] == '.' && name[1] == 'L';
+  /* Normal local symbols start with ``.L''.  */
+  if (name[0] == '.' && name[1] == 'L')
+    return true;
+
+  /* At least some SVR4 compilers (e.g., UnixWare 2.1 cc) generate
+     DWARF debugging symbols starting with ``..''.  */
+  if (name[0] == '.' && name[1] == '.')
+    return true;
+
+  /* gcc will sometimes generate symbols beginning with ``_.L_'' when
+     emitting DWARF debugging output.  I suspect this is actually a
+     small bug in gcc (it calls ASM_OUTPUT_LABEL when it should call
+     ASM_GENERATE_INTERNAL_LABEL, and this causes the leading
+     underscore to be emitted on some ELF targets).  For ease of use,
+     we treat such symbols as local.  */
+  if (name[0] == '_' && name[1] == '.' && name[2] == 'L' && name[3] == '_')
+    return true;
+
+  return false;
 }
 
 alent *
index 940bd2a92c7f0af4de5614e46e3203dadd41eb49..5c1ac67dffefce95adf38ced447f7955007e8f8b 100644 (file)
@@ -1075,7 +1075,41 @@ elf_link_add_object_symbols (abfd, info)
                    goto error_return;
 
                  if (hi->root.type == bfd_link_hash_indirect)
-                   hi->elf_link_hash_flags &= ~ ELF_LINK_NON_ELF;
+                   {
+                     hi->elf_link_hash_flags &= ~ ELF_LINK_NON_ELF;
+                     if (dynamic)
+                       hi->elf_link_hash_flags |= ELF_LINK_HASH_DEF_DYNAMIC;
+                     /* We don't set DEF_REGULAR because we don't the
+                         symbol to get exported even if we are
+                         exporting all defined symbols.  FIXME: What a
+                         hack. */
+                     /* FIXME: Do we need to copy any flags from H to
+                         HI?  */
+                   }
+
+                 /* We also need to define an indirection from the
+                     nondefault version of the symbol.  */
+
+                 shortname = bfd_hash_allocate (&info->hash->table,
+                                                strlen (name));
+                 if (shortname == NULL)
+                   goto error_return;
+                 strncpy (shortname, name, p - name);
+                 strcpy (shortname + (p - name), p + 1);
+
+                 hi = NULL;
+                 if (! (_bfd_generic_link_add_one_symbol
+                        (info, abfd, shortname, BSF_INDIRECT,
+                         bfd_ind_section_ptr, (bfd_vma) 0, name, false,
+                         collect, (struct bfd_link_hash_entry **) &hi)))
+                   goto error_return;
+
+                 if (hi->root.type == bfd_link_hash_indirect)
+                   {
+                     hi->elf_link_hash_flags &= ~ ELF_LINK_NON_ELF;
+                     if (dynamic)
+                       hi->elf_link_hash_flags |= ELF_LINK_HASH_DEF_DYNAMIC;
+                   }
                }
            }
 
@@ -1823,30 +1857,35 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
       size_t i;
       size_t bucketcount = 0;
       Elf_Internal_Sym isym;
+      struct elf_assign_sym_version_info sinfo;
 
       /* Set up the version definition section.  */
       s = bfd_get_section_by_name (dynobj, ".gnu.version_d");
       BFD_ASSERT (s != NULL);
+
+      /* Attach all the symbols to their version information.  This
+         may cause some symbols to be unexported.  */
+      sinfo.output_bfd = output_bfd;
+      sinfo.info = info;
+      sinfo.verdefs = verdefs;
+      sinfo.export_dynamic = export_dynamic;
+      sinfo.removed_dynamic = false;
+      sinfo.failed = false;
+
+      elf_link_hash_traverse (elf_hash_table (info),
+                             elf_link_assign_sym_version,
+                             (PTR) &sinfo);
+      if (sinfo.failed)
+       return false;
+
+      /* We may have created additional version definitions if we are
+         just linking a regular application.  */
+      verdefs = sinfo.verdefs;
+
       if (verdefs == NULL)
        {
-         struct elf_assign_sym_version_info sinfo;
          asection **spp;
 
-         /* No version script was used.  In this case, we just check
-             that there were no version overrides for any symbols.  */
-         sinfo.output_bfd = output_bfd;
-         sinfo.info = info;
-         sinfo.verdefs = verdefs;
-         sinfo.removed_dynamic = false;
-         sinfo.export_dynamic = export_dynamic;
-         sinfo.failed = false;
-
-         elf_link_hash_traverse (elf_hash_table (info),
-                                 elf_link_assign_sym_version,
-                                 (PTR) &sinfo);
-         if (sinfo.failed)
-           return false;
-
          /* Don't include this section in the output file.  */
          for (spp = &output_bfd->sections;
               *spp != s->output_section;
@@ -1857,7 +1896,6 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
        }
       else
        {
-         struct elf_assign_sym_version_info sinfo;
          unsigned int cdefs;
          bfd_size_type size;
          struct bfd_elf_version_tree *t;
@@ -1865,20 +1903,6 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
          Elf_Internal_Verdef def;
          Elf_Internal_Verdaux defaux;
 
-         /* Attach all of the symbols to their version information.
-             This may cause some symbols to be unexported.  */
-         sinfo.output_bfd = output_bfd;
-         sinfo.info = info;
-         sinfo.verdefs = verdefs;
-         sinfo.export_dynamic = export_dynamic;
-         sinfo.removed_dynamic = false;
-         sinfo.failed = false;
-         elf_link_hash_traverse (elf_hash_table (info),
-                                 elf_link_assign_sym_version,
-                                 (PTR) &sinfo);
-         if (sinfo.failed)
-           return false;
-
          if (sinfo.removed_dynamic)
            {
              /* Some dynamic symbols were changed to be local
@@ -1979,11 +2003,8 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
              h->type = STT_OBJECT;
              h->verinfo.vertree = t;
 
-             if (info->shared)
-               {
-                 if (! _bfd_elf_link_record_dynamic_symbol (info, h))
-                   return false;
-               }
+             if (! _bfd_elf_link_record_dynamic_symbol (info, h))
+               return false;
 
              def.vd_version = VER_DEF_CURRENT;
              def.vd_flags = 0;
@@ -2075,6 +2096,8 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
            bfd_byte *p;
 
            /* Build the version definition section.  */
+           size = 0;
+           crefs = 0;
            for (t = elf_tdata (output_bfd)->verref;
                 t != NULL;
                 t = t->vn_nextref)
@@ -2549,14 +2572,94 @@ elf_link_assign_sym_version (h, data)
            {
              h->verinfo.vertree = t;
              t->used = true;
+
+             /* See if there is anything to force this symbol to
+                 local scope.  */
+             if (t->locals != NULL)
+               {
+                 int len;
+                 char *alc;
+                 struct bfd_elf_version_expr *d;
+
+                 len = p - h->root.root.string;
+                 alc = bfd_alloc (sinfo->output_bfd, len);
+                 if (alc == NULL)
+                   return false;
+                 strncpy (alc, h->root.root.string, len - 1);
+                 alc[len - 1] = '\0';
+                 if (alc[len - 2] == ELF_VER_CHR)
+                   alc[len - 2] = '\0';
+
+                 for (d = t->locals; d != NULL; d = d->next)
+                   {
+                     if ((d->match[0] == '*' && d->match[1] == '\0')
+                         || fnmatch (d->match, alc, 0) == 0)
+                       {
+                         if (h->dynindx != -1
+                             && info->shared
+                             && ! sinfo->export_dynamic
+                             && (h->elf_link_hash_flags
+                                 & ELF_LINK_HASH_NEEDS_PLT) == 0)
+                           {
+                             sinfo->removed_dynamic = true;
+                             h->dynindx = -1;
+                             /* FIXME: The name of the symbol has
+                                already been recorded in the dynamic
+                                string table section.  */
+                           }
+
+                         break;
+                       }
+                   }
+
+                 bfd_release (sinfo->output_bfd, alc);
+               }
+
              break;
            }
        }
 
-      if (t == NULL)
+      /* If we are building an application, we need to create a
+         version node for this version.  */
+      if (t == NULL && ! info->shared)
+       {
+         struct bfd_elf_version_tree **pp;
+         int version_index;
+
+         /* If we aren't going to export this symbol, we don't need
+             to worry about it. */
+         if (h->dynindx == -1)
+           return true;
+
+         t = ((struct bfd_elf_version_tree *)
+              bfd_alloc (sinfo->output_bfd, sizeof *t));
+         if (t == NULL)
+           {
+             sinfo->failed = true;
+             return false;
+           }
+
+         t->next = NULL;
+         t->name = p;
+         t->globals = NULL;
+         t->locals = NULL;
+         t->deps = NULL;
+         t->name_indx = (unsigned int) -1;
+         t->used = true;
+
+         version_index = 1;
+         for (pp = &sinfo->verdefs; *pp != NULL; pp = &(*pp)->next)
+           ++version_index;
+         t->vernum = version_index;
+
+         *pp = t;
+
+         h->verinfo.vertree = t;
+       }
+      else if (t == NULL)
        {
-         /* We could not find the version.  Return an error.
-            FIXME: Why?  */
+         /* We could not find the version for a symbol when
+             generating a shared archive.  Return an error.  */
          (*_bfd_error_handler)
            ("%s: invalid version %s", bfd_get_filename (sinfo->output_bfd),
             h->root.root.string);
@@ -3617,8 +3720,10 @@ elf_link_output_extsym (h, data)
          to the decorated version of the name.  For example, if the
          symbol foo@@GNU_1.2 is the default, which should be used when
          foo is used with no version, then we add an indirect symbol
-         foo which points to foo@@GNU_1.2.  */
-      if ((h->elf_link_hash_flags & ELF_LINK_NON_ELF) != 0)
+         foo which points to foo@@GNU_1.2.  We ignore these symbols,
+         since the indirected symbol is already in the hash table.  If
+         the indirect symbol is non-ELF, fall through and output it.  */
+      if ((h->elf_link_hash_flags & ELF_LINK_NON_ELF) == 0)
        return true;
 
       /* Fall through.  */