Replace hidden with versioned in elf_link_hash_entry
authorH.J. Lu <hjl.tools@gmail.com>
Mon, 10 Aug 2015 14:57:40 +0000 (07:57 -0700)
committerH.J. Lu <hjl.tools@gmail.com>
Mon, 10 Aug 2015 14:57:57 +0000 (07:57 -0700)
This patch replaces the "hidden" field with the "versioned" field in
elf_link_hash_entry so that we can avoid calling strchr and strrchr if
the symbol is unversioned.

* elf-bfd.h (elf_symbol_version): New enum.
(elf_link_hash_entry): Replace hidden with versioned.
* elflink.c (_bfd_elf_merge_symbol): Don't look for symbol
version if the symbol is unversioned.  Initialize versioned.
(_bfd_elf_add_default_symbol): Don't look for symbol version
if the symbol is unversioned or hidden.  Initialize versioned.
(elf_collect_hash_codes): Don't look for symbol version if the
symbol is unversioned.
(elf_collect_gnu_hash_codes): Likewise.
(bfd_elf_gc_mark_dynamic_ref_symbol): Likewise.
(_bfd_elf_link_hash_copy_indirect): Check versioned instead of
hidden.
(elf_link_output_extsym): Likewise.

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

index c319f80..97ed6ea 100644 (file)
@@ -1,3 +1,19 @@
+2015-08-10  H.J. Lu  <hongjiu.lu@intel.com>
+
+       * elf-bfd.h (elf_symbol_version): New enum.
+       (elf_link_hash_entry): Replace hidden with versioned.
+       * elflink.c (_bfd_elf_merge_symbol): Don't look for symbol
+       version if the symbol is unversioned.  Initialize versioned.
+       (_bfd_elf_add_default_symbol): Don't look for symbol version
+       if the symbol is unversioned or hidden.  Initialize versioned.
+       (elf_collect_hash_codes): Don't look for symbol version if the
+       symbol is unversioned.
+       (elf_collect_gnu_hash_codes): Likewise.
+       (bfd_elf_gc_mark_dynamic_ref_symbol): Likewise.
+       (_bfd_elf_link_hash_copy_indirect): Check versioned instead of
+       hidden.
+       (elf_link_output_extsym): Likewise.
+
 2015-08-07  H.J. Lu  <hongjiu.lu@intel.com>
 
        PR binutils/18785
@@ -27,7 +43,7 @@
        (elf_link_output_extsym): Bind a symbol locally when linking
        executable if it is locally defined, hidden versioned, not
        referenced by shared library and not exported.  Turn on
-       VERSYM_HIDDEN only if the hidden vesioned symbol is defined
+       VERSYM_HIDDEN only if the hidden versioned symbol is defined
        locally.
 
 2015-08-05  Nick Clifton  <nickc@redhat.com>
index e08b2d6..c92671a 100644 (file)
@@ -108,6 +108,15 @@ struct elf_link_virtual_table_entry
     struct elf_link_hash_entry *parent;
   };
 
+/* ELF symbol version.  */
+enum elf_symbol_version
+  {
+    unknown = 0,
+    unversioned,
+    versioned,
+    versioned_hidden
+  };
+
 /* ELF linker hash table entries.  */
 
 struct elf_link_hash_entry
@@ -178,8 +187,8 @@ struct elf_link_hash_entry
   unsigned int needs_plt : 1;
   /* Symbol appears in a non-ELF input file.  */
   unsigned int non_elf : 1;
-  /* Symbol should be marked as hidden in the version information.  */
-  unsigned int hidden : 1;
+  /* Symbol version information.  */
+  ENUM_BITFIELD (elf_symbol_version) versioned : 2;
   /* Symbol was forced to local scope due to a version script file.  */
   unsigned int forced_local : 1;
   /* Symbol was forced to be dynamic due to a version script file.  */
index 832b374..031cffe 100644 (file)
@@ -971,15 +971,28 @@ _bfd_elf_merge_symbol (bfd *abfd,
   bed = get_elf_backend_data (abfd);
 
   /* NEW_VERSION is the symbol version of the new symbol.  */
-  new_version = strrchr (name, ELF_VER_CHR);
-  if (new_version)
+  if (h->versioned != unversioned)
     {
-      if (new_version > name && new_version[-1] != ELF_VER_CHR)
-       h->hidden = 1;
-      new_version += 1;
-      if (new_version[0] == '\0')
-       new_version = NULL;
+      /* Symbol version is unknown or versioned.  */
+      new_version = strrchr (name, ELF_VER_CHR);
+      if (new_version)
+       {
+         if (h->versioned == unknown)
+           {
+             if (new_version > name && new_version[-1] != ELF_VER_CHR)
+               h->versioned = versioned_hidden;
+             else
+               h->versioned = versioned;
+           }
+         new_version += 1;
+         if (new_version[0] == '\0')
+           new_version = NULL;
+       }
+      else
+       h->versioned = unversioned;
     }
+  else
+    new_version = NULL;
 
   /* For merging, we only care about real symbols.  But we need to make
      sure that indirect symbol dynamic flags are updated.  */
@@ -998,8 +1011,8 @@ _bfd_elf_merge_symbol (bfd *abfd,
             to the symbol with the same symbol version.  NEW_HIDDEN is
             true if the new symbol is only visibile to the symbol with
             the same symbol version.  */
-         bfd_boolean old_hidden = h->hidden;
-         bfd_boolean new_hidden = hi->hidden;
+         bfd_boolean old_hidden = h->versioned == versioned_hidden;
+         bfd_boolean new_hidden = hi->versioned == versioned_hidden;
          if (!old_hidden && !new_hidden)
            /* The new symbol matches the existing symbol if both
               aren't hidden.  */
@@ -1008,14 +1021,13 @@ _bfd_elf_merge_symbol (bfd *abfd,
            {
              /* OLD_VERSION is the symbol version of the existing
                 symbol. */
-             char *old_version = strrchr (h->root.root.string,
-                                          ELF_VER_CHR);
-             if (old_version)
-               {
-                 old_version += 1;
-                 if (old_version[0] == '\0')
-                   old_version = NULL;
-               }
+             char *old_version;
+
+             if (h->versioned >= versioned)
+               old_version = strrchr (h->root.root.string,
+                                      ELF_VER_CHR) + 1;
+             else
+                old_version = NULL;
 
              /* The new symbol matches the existing symbol if they
                 have the same symbol version.  */
@@ -1674,13 +1686,32 @@ _bfd_elf_add_default_symbol (bfd *abfd,
   asection *tmp_sec;
   bfd_boolean matched;
 
+  if (h->versioned == unversioned || h->versioned == versioned_hidden)
+    return TRUE;
+
   /* If this symbol has a version, and it is the default version, we
      create an indirect symbol from the default name to the fully
      decorated name.  This will cause external references which do not
      specify a version to be bound to this version of the symbol.  */
   p = strchr (name, ELF_VER_CHR);
-  if (p == NULL || p[1] != ELF_VER_CHR)
-    return TRUE;
+  if (h->versioned == unknown)
+    {
+      if (p == NULL)
+       {
+         h->versioned = unversioned;
+         return TRUE;
+       }
+      else
+       {
+         if (p[1] != ELF_VER_CHR)
+           {
+             h->versioned = versioned_hidden;
+             return TRUE;
+           }
+         else
+           h->versioned = versioned;
+       }
+    }
 
   bed = get_elf_backend_data (abfd);
   collect = bed->collect;
@@ -5230,7 +5261,6 @@ elf_collect_hash_codes (struct elf_link_hash_entry *h, void *data)
 {
   struct hash_codes_info *inf = (struct hash_codes_info *) data;
   const char *name;
-  char *p;
   unsigned long ha;
   char *alc = NULL;
 
@@ -5239,18 +5269,21 @@ elf_collect_hash_codes (struct elf_link_hash_entry *h, void *data)
     return TRUE;
 
   name = h->root.root.string;
-  p = strchr (name, ELF_VER_CHR);
-  if (p != NULL)
+  if (h->versioned >= versioned)
     {
-      alc = (char *) bfd_malloc (p - name + 1);
-      if (alc == NULL)
+      char *p = strchr (name, ELF_VER_CHR);
+      if (p != NULL)
        {
-         inf->error = TRUE;
-         return FALSE;
+         alc = (char *) bfd_malloc (p - name + 1);
+         if (alc == NULL)
+           {
+             inf->error = TRUE;
+             return FALSE;
+           }
+         memcpy (alc, name, p - name);
+         alc[p - name] = '\0';
+         name = alc;
        }
-      memcpy (alc, name, p - name);
-      alc[p - name] = '\0';
-      name = alc;
     }
 
   /* Compute the hash value.  */
@@ -5298,7 +5331,6 @@ elf_collect_gnu_hash_codes (struct elf_link_hash_entry *h, void *data)
 {
   struct collect_gnu_hash_codes *s = (struct collect_gnu_hash_codes *) data;
   const char *name;
-  char *p;
   unsigned long ha;
   char *alc = NULL;
 
@@ -5311,18 +5343,21 @@ elf_collect_gnu_hash_codes (struct elf_link_hash_entry *h, void *data)
     return TRUE;
 
   name = h->root.root.string;
-  p = strchr (name, ELF_VER_CHR);
-  if (p != NULL)
+  if (h->versioned >= versioned)
     {
-      alc = (char *) bfd_malloc (p - name + 1);
-      if (alc == NULL)
+      char *p = strchr (name, ELF_VER_CHR);
+      if (p != NULL)
        {
-         s->error = TRUE;
-         return FALSE;
+         alc = (char *) bfd_malloc (p - name + 1);
+         if (alc == NULL)
+           {
+             s->error = TRUE;
+             return FALSE;
+           }
+         memcpy (alc, name, p - name);
+         alc[p - name] = '\0';
+         name = alc;
        }
-      memcpy (alc, name, p - name);
-      alc[p - name] = '\0';
-      name = alc;
     }
 
   /* Compute the hash value.  */
@@ -6859,7 +6894,7 @@ _bfd_elf_link_hash_copy_indirect (struct bfd_link_info *info,
      symbol which just became indirect if DIR isn't a hidden versioned
      symbol.  */
 
-  if (!dir->hidden)
+  if (dir->versioned != versioned_hidden)
     {
       dir->ref_dynamic |= ind->ref_dynamic;
       dir->ref_regular |= ind->ref_regular;
@@ -8963,7 +8998,7 @@ elf_link_output_extsym (struct bfd_hash_entry *bh, void *data)
                                && !h->dynamic
                                && !h->ref_dynamic
                                && h->def_regular
-                               && h->hidden));
+                               && h->versioned == versioned_hidden));
 
   if (h->root.type == bfd_link_hash_warning)
     {
@@ -9359,9 +9394,9 @@ elf_link_output_extsym (struct bfd_hash_entry *bh, void *data)
                iversym.vs_vers++;
            }
 
-         /* Turn on VERSYM_HIDDEN only if the hidden vesioned symbol is
+         /* Turn on VERSYM_HIDDEN only if the hidden versioned symbol is
             defined locally.  */
-         if (h->hidden && h->def_regular)
+         if (h->versioned == versioned_hidden && h->def_regular)
            iversym.vs_vers |= VERSYM_HIDDEN;
 
          eversym = (Elf_External_Versym *) flinfo->symver_sec->contents;
@@ -12541,7 +12576,7 @@ bfd_elf_gc_mark_dynamic_ref_symbol (struct elf_link_hash_entry *h, void *inf)
                  || (h->dynamic
                      && d != NULL
                      && (*d->match) (&d->head, NULL, h->root.root.string)))
-             && (strchr (h->root.root.string, ELF_VER_CHR) != NULL
+             && (h->versioned >= versioned
                  || !bfd_hide_sym_by_version (info->version_info,
                                               h->root.root.string)))))
     h->root.u.def.section->flags |= SEC_KEEP;