From Eric Youngdale <eric@andante.jic.com>:
authorIan Lance Taylor <ian@airs.com>
Tue, 11 Mar 1997 06:46:46 +0000 (06:46 +0000)
committerIan Lance Taylor <ian@airs.com>
Tue, 11 Mar 1997 06:46:46 +0000 (06:46 +0000)
* elf-bfd.h (ELF_LINK_FORCED_LOCAL): Define.
* elf.c (bfd_elf_print_symbol): Correct errors in last change.
* elflink.h (elf_link_add_object_symbols): Handle cases in which a
versioned symbol appears in both a regular and a shared object.
(elf_link_assign_sym_version): Set ELF_LINK_FORCED_LOCAL when
appropriate.  Improve error message.
(struct elf_outext_info): Rename from elf_finfo_failed.  Change
all uses.  Add localsyms field.
(elf_bfd_final_link): When generating a shared library, call
elf_link_output_extsym to output all local symbols.
(elf_link_output_extsym): Handle symbols which were forced to
become local.

bfd/elflink.h

index 5c1ac67dffefce95adf38ced447f7955007e8f8b..28df37e108b75a78c7c6b9b0bbcef4cd043d4ce2 100644 (file)
@@ -1058,7 +1058,7 @@ elf_link_add_object_symbols (abfd, info)
              if (p != NULL && p[1] == ELF_VER_CHR)
                {
                  char *shortname;
-                 struct elf_link_hash_entry *hi;
+                 struct elf_link_hash_entry *hold;
 
                  shortname = bfd_hash_allocate (&info->hash->table,
                                                 p - name + 1);
@@ -1067,24 +1067,77 @@ elf_link_add_object_symbols (abfd, info)
                  strncpy (shortname, name, p - name);
                  shortname[p - name] = '\0';
 
-                 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;
+                 /* First look to see if we have an existing symbol
+                     with this name.  */
+                 hold = elf_link_hash_lookup (elf_hash_table (info),
+                                              shortname, false, false,
+                                              false);
+
+                 /* If we are looking at a normal object, and the
+                     symbol was seen in a shared object, clobber the
+                     definition in the shared object.  */
+                 if (hold != NULL
+                     && ! dynamic
+                     && (hold->root.type == bfd_link_hash_defined
+                         || hold->root.type == bfd_link_hash_defweak)
+                     && (hold->elf_link_hash_flags
+                         & ELF_LINK_HASH_DEF_DYNAMIC) != 0
+                     && ((hold->root.u.def.section->owner->flags & DYNAMIC)
+                         != 0))
+                   {
+                     /* Change the hash table entry to undefined, so
+                         that _bfd_generic_link_add_one_symbol will do
+                         the right thing.  */
+                     hold->root.type = bfd_link_hash_undefined;
+                     hold->root.u.undef.abfd =
+                       hold->root.u.def.section->owner;
+                     hold->verinfo.vertree = NULL;
+                     hold = NULL;
+                   }
 
-                 if (hi->root.type == bfd_link_hash_indirect)
+                 /* If we are looking at a shared object, and we have
+                     already seen this symbol defined elsewhere, then
+                     don't try to define it again. */
+                 if (hold != NULL
+                     && dynamic
+                     && (hold->root.type == bfd_link_hash_defined
+                         || hold->root.type == bfd_link_hash_defweak
+                         || hold->root.type == bfd_link_hash_indirect
+                         || (hold->root.type == bfd_link_hash_common
+                             && (bind == STB_WEAK
+                                 || ELF_ST_TYPE (sym.st_info) == STT_FUNC))))
+                   {
+                     /* Don't add an indirect symbol.  */
+                   }
+                 else
                    {
-                     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?  */
+                     struct elf_link_hash_entry *hi;
+
+                     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 there is a duplicate definition somewhere,
+                         then HI may not point to an indirect symbol.
+                         We will have reported an error to the user in
+                         that case.  */
+
+                     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;
+                         /* 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
@@ -1097,18 +1150,77 @@ elf_link_add_object_symbols (abfd, info)
                  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;
+                 /* First look to see if we have an existing symbol
+                     with this name.  */
+                 hold = elf_link_hash_lookup (elf_hash_table (info),
+                                              shortname, false, false,
+                                              false);
+
+                 /* If we are looking at a normal object, and the
+                     symbol was seen in a shared object, clobber the
+                     definition in the shared object.  */
+                 if (hold != NULL
+                     && ! dynamic
+                     && (hold->root.type == bfd_link_hash_defined
+                         || hold->root.type == bfd_link_hash_defweak)
+                     && (hold->elf_link_hash_flags
+                         & ELF_LINK_HASH_DEF_DYNAMIC) != 0
+                     && ((hold->root.u.def.section->owner->flags & DYNAMIC)
+                         != 0))
+                   {
+                     /* Change the hash table entry to undefined, so
+                         that _bfd_generic_link_add_one_symbol will do
+                         the right thing.  */
+                     hold->root.type = bfd_link_hash_undefined;
+                     hold->root.u.undef.abfd =
+                       hold->root.u.def.section->owner;
+                     hold->verinfo.vertree = NULL;
+                     hold = NULL;
+                   }
 
-                 if (hi->root.type == bfd_link_hash_indirect)
+                 /* If we are looking at a shared object, and we have
+                     already seen this symbol defined elsewhere, then
+                     don't try to define it again. */
+                 if (hold != NULL
+                     && dynamic
+                     && (hold->root.type == bfd_link_hash_defined
+                         || hold->root.type == bfd_link_hash_defweak
+                         || hold->root.type == bfd_link_hash_indirect
+                         || (hold->root.type == bfd_link_hash_common
+                             && (bind == STB_WEAK
+                                 || ELF_ST_TYPE (sym.st_info) == STT_FUNC))))
                    {
-                     hi->elf_link_hash_flags &= ~ ELF_LINK_NON_ELF;
-                     if (dynamic)
-                       hi->elf_link_hash_flags |= ELF_LINK_HASH_DEF_DYNAMIC;
+                     /* Don't add an indirect symbol.  */
+                   }
+                 else
+                   {
+                     struct elf_link_hash_entry *hi;
+
+                     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 there is a duplicate definition somewhere,
+                         then HI may not point to an indirect symbol.
+                         We will have reported an error to the user in
+                         that case.  */
+
+                     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;
+                         /* 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?  */
+                       }
                    }
                }
            }
@@ -2602,6 +2714,7 @@ elf_link_assign_sym_version (h, data)
                                  & ELF_LINK_HASH_NEEDS_PLT) == 0)
                            {
                              sinfo->removed_dynamic = true;
+                             h->elf_link_hash_flags |= ELF_LINK_FORCED_LOCAL;
                              h->dynindx = -1;
                              /* FIXME: The name of the symbol has
                                 already been recorded in the dynamic
@@ -2661,8 +2774,8 @@ elf_link_assign_sym_version (h, data)
          /* 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);
+           ("%s: undefined version name %s",
+            bfd_get_filename (sinfo->output_bfd), h->root.root.string);
          bfd_set_error (bfd_error_bad_value);
          sinfo->failed = true;
          return false;
@@ -2717,6 +2830,7 @@ elf_link_assign_sym_version (h, data)
                              & ELF_LINK_HASH_NEEDS_PLT) == 0)
                        {
                          sinfo->removed_dynamic = true;
+                         h->elf_link_hash_flags |= ELF_LINK_FORCED_LOCAL;
                          h->dynindx = -1;
                          /* FIXME: The name of the symbol has already
                             been recorded in the dynamic string table
@@ -2740,6 +2854,7 @@ elf_link_assign_sym_version (h, data)
              && (h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) == 0)
            {
              sinfo->removed_dynamic = true;
+             h->elf_link_hash_flags |= ELF_LINK_FORCED_LOCAL;
              h->dynindx = -1;
              /* FIXME: The name of the symbol has already been
                 recorded in the dynamic string table section.  */
@@ -2827,12 +2942,12 @@ static boolean elf_reloc_link_order
   PARAMS ((bfd *, struct bfd_link_info *, asection *,
           struct bfd_link_order *));
 
-/* This struct is used to pass information to routines called via
-   elf_link_hash_traverse which must return failure.  */
+/* This struct is used to pass information to elf_link_output_extsym.  */
 
-struct elf_finfo_failed
+struct elf_outext_info
 {
   boolean failed;
+  boolean localsyms;
   struct elf_final_link_info *finfo;
 };
 
@@ -2859,7 +2974,7 @@ elf_bfd_final_link (abfd, info)
   Elf_Internal_Shdr *symtab_hdr;
   Elf_Internal_Shdr *symstrtab_hdr;
   struct elf_backend_data *bed = get_elf_backend_data (abfd);
-  struct elf_finfo_failed eif;
+  struct elf_outext_info eoinfo;
 
   if (info->shared)
     abfd->flags |= DYNAMIC;
@@ -3196,6 +3311,23 @@ elf_bfd_final_link (abfd, info)
   /* That wrote out all the local symbols.  Finish up the symbol table
      with the global symbols.  */
 
+  if (info->strip != strip_all && info->shared)
+    {
+      /* Output any global symbols that got converted to local in a
+         version script.  We do this in a separate step since ELF
+         requires all local symbols to appear prior to any global
+         symbols.  FIXME: We should only do this if some global
+         symbols were, in fact, converted to become local.  FIXME:
+         Will this work correctly with the Irix 5 linker?  */
+      eoinfo.failed = false;
+      eoinfo.finfo = &finfo;
+      eoinfo.localsyms = true;
+      elf_link_hash_traverse (elf_hash_table (info), elf_link_output_extsym,
+                             (PTR) &eoinfo);
+      if (eoinfo.failed)
+       return false;
+    }
+
   /* The sh_info field records the index of the first non local
      symbol.  */
   symtab_hdr->sh_info = abfd->symcount;
@@ -3203,11 +3335,12 @@ elf_bfd_final_link (abfd, info)
     elf_section_data (finfo.dynsym_sec->output_section)->this_hdr.sh_info = 1;
 
   /* We get the global symbols from the hash table.  */
-  eif.failed = false;
-  eif.finfo = &finfo;
+  eoinfo.failed = false;
+  eoinfo.localsyms = false;
+  eoinfo.finfo = &finfo;
   elf_link_hash_traverse (elf_hash_table (info), elf_link_output_extsym,
-                         (PTR) &eif);
-  if (eif.failed)
+                         (PTR) &eoinfo);
+  if (eoinfo.failed)
     return false;
 
   /* Flush all symbols to the file.  */
@@ -3592,19 +3725,35 @@ elf_link_flush_output_syms (finfo)
 }
 
 /* Add an external symbol to the symbol table.  This is called from
-   the hash table traversal routine.  */
+   the hash table traversal routine.  When generating a shared object,
+   we go through the symbol table twice.  The first time we output
+   anything that might have been forced to local scope in a version
+   script.  The second time we output the symbols that are still
+   global symbols.  */
 
 static boolean
 elf_link_output_extsym (h, data)
      struct elf_link_hash_entry *h;
      PTR data;
 {
-  struct elf_finfo_failed *eif = (struct elf_finfo_failed *) data;
-  struct elf_final_link_info *finfo = eif->finfo;
+  struct elf_outext_info *eoinfo = (struct elf_outext_info *) data;
+  struct elf_final_link_info *finfo = eoinfo->finfo;
   boolean strip;
   Elf_Internal_Sym sym;
   asection *input_sec;
 
+  /* Decide whether to output this symbol in this pass.  */
+  if (eoinfo->localsyms)
+    {
+      if ((h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) == 0)
+       return true;
+    }
+  else
+    {
+      if ((h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0)
+       return true;
+    }
+
   /* If we are not creating a shared library, and this symbol is
      referenced by a shared library but is not defined anywhere, then
      warn that it is undefined.  If we do not do this, the runtime
@@ -3622,7 +3771,7 @@ elf_link_output_extsym (h, data)
             (finfo->info, h->root.root.string, h->root.u.undef.abfd,
              (asection *) NULL, 0)))
        {
-         eif->failed = true;
+         eoinfo->failed = true;
          return false;
        }
     }
@@ -3655,8 +3804,10 @@ elf_link_output_extsym (h, data)
   sym.st_value = 0;
   sym.st_size = h->size;
   sym.st_other = h->other;
-  if (h->root.type == bfd_link_hash_undefweak
-      || h->root.type == bfd_link_hash_defweak)
+  if ((h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0)
+    sym.st_info = ELF_ST_INFO (STB_LOCAL, h->type);
+  else if (h->root.type == bfd_link_hash_undefweak
+          || h->root.type == bfd_link_hash_defweak)
     sym.st_info = ELF_ST_INFO (STB_WEAK, h->type);
   else
     sym.st_info = ELF_ST_INFO (STB_GLOBAL, h->type);
@@ -3689,7 +3840,7 @@ elf_link_output_extsym (h, data)
                                                 input_sec->output_section);
            if (sym.st_shndx == (unsigned short) -1)
              {
-               eif->failed = true;
+               eoinfo->failed = true;
                return false;
              }
 
@@ -3761,7 +3912,7 @@ elf_link_output_extsym (h, data)
       if (! ((*bed->elf_backend_finish_dynamic_symbol)
             (finfo->output_bfd, finfo->info, h, &sym)))
        {
-         eif->failed = true;
+         eoinfo->failed = true;
          return false;
        }
 
@@ -3835,7 +3986,7 @@ elf_link_output_extsym (h, data)
 
   if (! elf_link_output_sym (finfo, h->root.root.string, &sym, input_sec))
     {
-      eif->failed = true;
+      eoinfo->failed = true;
       return false;
     }