Replace hidden with versioned in elf_link_hash_entry

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.
This commit is contained in:
H.J. Lu 2015-08-10 07:57:40 -07:00
parent 75fb7498c2
commit 422f11824b
3 changed files with 107 additions and 47 deletions

View 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> 2015-08-07 H.J. Lu <hongjiu.lu@intel.com>
PR binutils/18785 PR binutils/18785
@ -27,7 +43,7 @@
(elf_link_output_extsym): Bind a symbol locally when linking (elf_link_output_extsym): Bind a symbol locally when linking
executable if it is locally defined, hidden versioned, not executable if it is locally defined, hidden versioned, not
referenced by shared library and not exported. Turn on 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. locally.
2015-08-05 Nick Clifton <nickc@redhat.com> 2015-08-05 Nick Clifton <nickc@redhat.com>

View File

@ -108,6 +108,15 @@ struct elf_link_virtual_table_entry
struct elf_link_hash_entry *parent; struct elf_link_hash_entry *parent;
}; };
/* ELF symbol version. */
enum elf_symbol_version
{
unknown = 0,
unversioned,
versioned,
versioned_hidden
};
/* ELF linker hash table entries. */ /* ELF linker hash table entries. */
struct elf_link_hash_entry struct elf_link_hash_entry
@ -178,8 +187,8 @@ struct elf_link_hash_entry
unsigned int needs_plt : 1; unsigned int needs_plt : 1;
/* Symbol appears in a non-ELF input file. */ /* Symbol appears in a non-ELF input file. */
unsigned int non_elf : 1; unsigned int non_elf : 1;
/* Symbol should be marked as hidden in the version information. */ /* Symbol version information. */
unsigned int hidden : 1; ENUM_BITFIELD (elf_symbol_version) versioned : 2;
/* Symbol was forced to local scope due to a version script file. */ /* Symbol was forced to local scope due to a version script file. */
unsigned int forced_local : 1; unsigned int forced_local : 1;
/* Symbol was forced to be dynamic due to a version script file. */ /* Symbol was forced to be dynamic due to a version script file. */

View File

@ -971,15 +971,28 @@ _bfd_elf_merge_symbol (bfd *abfd,
bed = get_elf_backend_data (abfd); bed = get_elf_backend_data (abfd);
/* NEW_VERSION is the symbol version of the new symbol. */ /* NEW_VERSION is the symbol version of the new symbol. */
new_version = strrchr (name, ELF_VER_CHR); if (h->versioned != unversioned)
if (new_version)
{ {
if (new_version > name && new_version[-1] != ELF_VER_CHR) /* Symbol version is unknown or versioned. */
h->hidden = 1; new_version = strrchr (name, ELF_VER_CHR);
new_version += 1; if (new_version)
if (new_version[0] == '\0') {
new_version = NULL; 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 /* For merging, we only care about real symbols. But we need to make
sure that indirect symbol dynamic flags are updated. */ 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 to the symbol with the same symbol version. NEW_HIDDEN is
true if the new symbol is only visibile to the symbol with true if the new symbol is only visibile to the symbol with
the same symbol version. */ the same symbol version. */
bfd_boolean old_hidden = h->hidden; bfd_boolean old_hidden = h->versioned == versioned_hidden;
bfd_boolean new_hidden = hi->hidden; bfd_boolean new_hidden = hi->versioned == versioned_hidden;
if (!old_hidden && !new_hidden) if (!old_hidden && !new_hidden)
/* The new symbol matches the existing symbol if both /* The new symbol matches the existing symbol if both
aren't hidden. */ aren't hidden. */
@ -1008,14 +1021,13 @@ _bfd_elf_merge_symbol (bfd *abfd,
{ {
/* OLD_VERSION is the symbol version of the existing /* OLD_VERSION is the symbol version of the existing
symbol. */ symbol. */
char *old_version = strrchr (h->root.root.string, char *old_version;
ELF_VER_CHR);
if (old_version) if (h->versioned >= versioned)
{ old_version = strrchr (h->root.root.string,
old_version += 1; ELF_VER_CHR) + 1;
if (old_version[0] == '\0') else
old_version = NULL; old_version = NULL;
}
/* The new symbol matches the existing symbol if they /* The new symbol matches the existing symbol if they
have the same symbol version. */ have the same symbol version. */
@ -1674,13 +1686,32 @@ _bfd_elf_add_default_symbol (bfd *abfd,
asection *tmp_sec; asection *tmp_sec;
bfd_boolean matched; 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 /* If this symbol has a version, and it is the default version, we
create an indirect symbol from the default name to the fully create an indirect symbol from the default name to the fully
decorated name. This will cause external references which do not decorated name. This will cause external references which do not
specify a version to be bound to this version of the symbol. */ specify a version to be bound to this version of the symbol. */
p = strchr (name, ELF_VER_CHR); p = strchr (name, ELF_VER_CHR);
if (p == NULL || p[1] != ELF_VER_CHR) if (h->versioned == unknown)
return TRUE; {
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); bed = get_elf_backend_data (abfd);
collect = bed->collect; 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; struct hash_codes_info *inf = (struct hash_codes_info *) data;
const char *name; const char *name;
char *p;
unsigned long ha; unsigned long ha;
char *alc = NULL; char *alc = NULL;
@ -5239,18 +5269,21 @@ elf_collect_hash_codes (struct elf_link_hash_entry *h, void *data)
return TRUE; return TRUE;
name = h->root.root.string; name = h->root.root.string;
p = strchr (name, ELF_VER_CHR); if (h->versioned >= versioned)
if (p != NULL)
{ {
alc = (char *) bfd_malloc (p - name + 1); char *p = strchr (name, ELF_VER_CHR);
if (alc == NULL) if (p != NULL)
{ {
inf->error = TRUE; alc = (char *) bfd_malloc (p - name + 1);
return FALSE; 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. */ /* 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; struct collect_gnu_hash_codes *s = (struct collect_gnu_hash_codes *) data;
const char *name; const char *name;
char *p;
unsigned long ha; unsigned long ha;
char *alc = NULL; char *alc = NULL;
@ -5311,18 +5343,21 @@ elf_collect_gnu_hash_codes (struct elf_link_hash_entry *h, void *data)
return TRUE; return TRUE;
name = h->root.root.string; name = h->root.root.string;
p = strchr (name, ELF_VER_CHR); if (h->versioned >= versioned)
if (p != NULL)
{ {
alc = (char *) bfd_malloc (p - name + 1); char *p = strchr (name, ELF_VER_CHR);
if (alc == NULL) if (p != NULL)
{ {
s->error = TRUE; alc = (char *) bfd_malloc (p - name + 1);
return FALSE; 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. */ /* 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 which just became indirect if DIR isn't a hidden versioned
symbol. */ symbol. */
if (!dir->hidden) if (dir->versioned != versioned_hidden)
{ {
dir->ref_dynamic |= ind->ref_dynamic; dir->ref_dynamic |= ind->ref_dynamic;
dir->ref_regular |= ind->ref_regular; dir->ref_regular |= ind->ref_regular;
@ -8963,7 +8998,7 @@ elf_link_output_extsym (struct bfd_hash_entry *bh, void *data)
&& !h->dynamic && !h->dynamic
&& !h->ref_dynamic && !h->ref_dynamic
&& h->def_regular && h->def_regular
&& h->hidden)); && h->versioned == versioned_hidden));
if (h->root.type == bfd_link_hash_warning) 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++; 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. */ defined locally. */
if (h->hidden && h->def_regular) if (h->versioned == versioned_hidden && h->def_regular)
iversym.vs_vers |= VERSYM_HIDDEN; iversym.vs_vers |= VERSYM_HIDDEN;
eversym = (Elf_External_Versym *) flinfo->symver_sec->contents; 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 || (h->dynamic
&& d != NULL && d != NULL
&& (*d->match) (&d->head, NULL, h->root.root.string))) && (*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, || !bfd_hide_sym_by_version (info->version_info,
h->root.root.string))))) h->root.root.string)))))
h->root.u.def.section->flags |= SEC_KEEP; h->root.u.def.section->flags |= SEC_KEEP;