PR7047, wildcards in version scripts
Alan Modra
amodra@bigpond.net.au
Wed Nov 26 01:00:00 GMT 2008
I started looking at what needed to be done to fix PR7047, and spotted
quite a number of bugs. Sometimes it just doesn't pay to look..
First, ldlang.c:realsymbol didn't properly handle backslash quotes.
It simply stripped out all backslashes, so eg. "x\\*" would have been
seen as a non-glob matching 'x','*' instead of a glob 'x','\','*'.
Secondly, ldlang.c:lang_new_vers_pattern could set both "pattern" and
"symbol" in struct bfd_elf_version_expr. I wanted to use "pattern" as
a test whether we had a wildcard match, and soon found that the right
test was instead "!symbol". Not really a bug, but unnecessary to have
two pointers when logically one of them ought to be NULL.
Lastly, the logic in _bfd_elf_link_assign_sym_version used to hide an
unversioned symbol was quite broken, and the comments unhelpful.
This patch fixes all these problems, and allows wildcard patterns in
a version script to be overridden by any explict match. I don't try
to provide any ordering between two matching wildcards, except that
a match in a global specification has higher precedence than a local
spec. Otherwise, the last matching wildcard in the script wins.
include/
PR 7047
* bfdlink.h (struct bfd_elf_version_expr): Delete "symbol".
Add "literal".
bfd/
PR 7047
* configure.in: Bump version.
* configure: Regenerate.
* elflink.c (_bfd_elf_link_assign_sym_version): Continue matching
against version nodes when a global match is a wildcard. Similarly
continue matching on local wildcard matches, rather than only
continuing for "*". Have any global wildcard match override a
local wildcard match. Correct logic hiding unversioned symbol.
(bfd_elf_size_dynamic_sections): Update for changes to struct
bfd_elf_version_expr.
ld/
PR 7047
* emultempl/ppc64elf.em (gld${EMULATION_NAME}_new_vers_pattern): Update
for changes to struct bfd_elf_version_expr.
* ldlang.c (lang_vers_match, version_expr_head_hash): Likewise.
(version_expr_head_eq, lang_finalize_version_expr_head): Likewise.
(lang_register_vers_node): Likewise.
(lang_new_vers_pattern): Likewise. Ensure "literal" is set when
no glob chars found in "pattern".
(realsymbol): Correct backslash quote logic.
* ld.texinfo (VERSION): Warn about global wildcards.
Index: include/bfdlink.h
===================================================================
RCS file: /cvs/src/src/include/bfdlink.h,v
retrieving revision 1.75
diff -u -p -r1.75 bfdlink.h
--- include/bfdlink.h 17 Aug 2008 03:12:50 -0000 1.75
+++ include/bfdlink.h 25 Nov 2008 22:19:50 -0000
@@ -707,8 +707,8 @@ struct bfd_elf_version_expr
struct bfd_elf_version_expr *next;
/* Glob pattern. */
const char *pattern;
- /* NULL for a glob pattern, otherwise a straight symbol. */
- const char *symbol;
+ /* Set if pattern is not a glob. */
+ unsigned int literal : 1;
/* Defined by ".symver". */
unsigned int symver : 1;
/* Defined by version script. */
Index: bfd/configure.in
===================================================================
RCS file: /cvs/src/src/bfd/configure.in,v
retrieving revision 1.248
diff -u -p -r1.248 configure.in
--- bfd/configure.in 13 Nov 2008 23:09:47 -0000 1.248
+++ bfd/configure.in 25 Nov 2008 23:26:31 -0000
@@ -8,7 +8,7 @@ AC_CONFIG_SRCDIR([libbfd.c])
AC_CANONICAL_TARGET
AC_ISC_POSIX
-AM_INIT_AUTOMAKE(bfd, 2.19.50)
+AM_INIT_AUTOMAKE(bfd, 2.19.51)
dnl These must be called before LT_INIT, because it may want
dnl to call AC_CHECK_PROG.
Index: bfd/elflink.c
===================================================================
RCS file: /cvs/src/src/bfd/elflink.c,v
retrieving revision 1.316
diff -u -p -r1.316 elflink.c
--- bfd/elflink.c 21 Nov 2008 00:02:37 -0000 1.316
+++ bfd/elflink.c 25 Nov 2008 22:56:10 -0000
@@ -2010,41 +2010,36 @@ _bfd_elf_link_assign_sym_version (struct
if (h->verinfo.vertree == NULL && sinfo->verdefs != NULL)
{
struct bfd_elf_version_tree *t;
- struct bfd_elf_version_tree *local_ver;
+ struct bfd_elf_version_tree *local_ver, *global_ver, *exist_ver;
struct bfd_elf_version_expr *d;
/* See if can find what version this symbol is in. If the
symbol is supposed to be local, then don't actually register
it. */
local_ver = NULL;
+ global_ver = NULL;
+ exist_ver = NULL;
for (t = sinfo->verdefs; t != NULL; t = t->next)
{
if (t->globals.list != NULL)
{
- bfd_boolean matched;
-
- matched = FALSE;
d = NULL;
while ((d = (*t->match) (&t->globals, d,
h->root.root.string)) != NULL)
- if (d->symver)
- matched = TRUE;
- else
- {
- /* There is a version without definition. Make
- the symbol the default definition for this
- version. */
- h->verinfo.vertree = t;
- local_ver = NULL;
- d->script = 1;
+ {
+ global_ver = t;
+ local_ver = NULL;
+ if (d->symver)
+ exist_ver = t;
+ d->script = 1;
+ /* If the match is a wildcard pattern, keep looking for
+ a more explicit, perhaps even local, match. */
+ if (d->literal)
break;
- }
+ }
+
if (d != NULL)
break;
- else if (matched)
- /* There is no undefined version for this symbol. Hide the
- default one. */
- (*bed->elf_backend_hide_symbol) (info, h, TRUE);
}
if (t->locals.list != NULL)
@@ -2054,11 +2049,14 @@ _bfd_elf_link_assign_sym_version (struct
h->root.root.string)) != NULL)
{
local_ver = t;
- /* If the match is "*", keep looking for a more
- explicit, perhaps even global, match.
- XXX: Shouldn't this be !d->wildcard instead? */
- if (d->pattern[0] != '*' || d->pattern[1] != '\0')
- break;
+ /* If the match is a wildcard pattern, keep looking for
+ a more explicit, perhaps even global, match. */
+ if (d->literal)
+ {
+ /* An exact match overrides a global wildcard. */
+ global_ver = NULL;
+ break;
+ }
}
if (d != NULL)
@@ -2066,14 +2064,22 @@ _bfd_elf_link_assign_sym_version (struct
}
}
- if (local_ver != NULL)
+ if (global_ver != NULL)
+ {
+ h->verinfo.vertree = global_ver;
+ /* If we already have a versioned symbol that matches the
+ node for this symbol, then we don't want to create a
+ duplicate from the unversioned symbol. Instead hide the
+ unversioned symbol. */
+ if (exist_ver == global_ver)
+ (*bed->elf_backend_hide_symbol) (info, h, TRUE);
+ }
+ else if (local_ver != NULL)
{
h->verinfo.vertree = local_ver;
- if (h->dynindx != -1
- && ! info->export_dynamic)
- {
- (*bed->elf_backend_hide_symbol) (info, h, TRUE);
- }
+ if (!info->export_dynamic
+ || exist_ver == local_ver)
+ (*bed->elf_backend_hide_symbol) (info, h, TRUE);
}
}
@@ -5566,14 +5572,14 @@ bfd_elf_size_dynamic_sections (bfd *outp
/* Make all global versions with definition. */
for (t = verdefs; t != NULL; t = t->next)
for (d = t->globals.list; d != NULL; d = d->next)
- if (!d->symver && d->symbol)
+ if (!d->symver && d->literal)
{
const char *verstr, *name;
size_t namelen, verlen, newlen;
char *newname, *p;
struct elf_link_hash_entry *newh;
- name = d->symbol;
+ name = d->pattern;
namelen = strlen (name);
verstr = t->name;
verlen = strlen (verstr);
@@ -5631,7 +5637,7 @@ bfd_elf_size_dynamic_sections (bfd *outp
all_defined = TRUE;
for (t = verdefs; t != NULL; t = t->next)
for (d = t->globals.list; d != NULL; d = d->next)
- if (!d->symver && !d->script)
+ if (d->literal && !d->symver && !d->script)
{
(*_bfd_error_handler)
(_("%s: undefined version: %s"),
Index: ld/ldlang.c
===================================================================
RCS file: /cvs/src/src/ld/ldlang.c,v
retrieving revision 1.297
diff -u -p -r1.297 ldlang.c
--- ld/ldlang.c 24 Nov 2008 07:54:34 -0000 1.297
+++ ld/ldlang.c 25 Nov 2008 22:19:58 -0000
@@ -6989,7 +6989,7 @@ lang_vers_match (struct bfd_elf_version_
java_sym = sym;
}
- if (head->htab && (prev == NULL || prev->symbol))
+ if (head->htab && (prev == NULL || prev->literal))
{
struct bfd_elf_version_expr e;
@@ -6998,9 +6998,9 @@ lang_vers_match (struct bfd_elf_version_
case 0:
if (head->mask & BFD_ELF_VERSION_C_TYPE)
{
- e.symbol = sym;
+ e.pattern = sym;
expr = htab_find (head->htab, &e);
- while (expr && strcmp (expr->symbol, sym) == 0)
+ while (expr && strcmp (expr->pattern, sym) == 0)
if (expr->mask == BFD_ELF_VERSION_C_TYPE)
goto out_ret;
else
@@ -7010,9 +7010,9 @@ lang_vers_match (struct bfd_elf_version_
case BFD_ELF_VERSION_C_TYPE:
if (head->mask & BFD_ELF_VERSION_CXX_TYPE)
{
- e.symbol = cxx_sym;
+ e.pattern = cxx_sym;
expr = htab_find (head->htab, &e);
- while (expr && strcmp (expr->symbol, cxx_sym) == 0)
+ while (expr && strcmp (expr->pattern, cxx_sym) == 0)
if (expr->mask == BFD_ELF_VERSION_CXX_TYPE)
goto out_ret;
else
@@ -7022,9 +7022,9 @@ lang_vers_match (struct bfd_elf_version_
case BFD_ELF_VERSION_CXX_TYPE:
if (head->mask & BFD_ELF_VERSION_JAVA_TYPE)
{
- e.symbol = java_sym;
+ e.pattern = java_sym;
expr = htab_find (head->htab, &e);
- while (expr && strcmp (expr->symbol, java_sym) == 0)
+ while (expr && strcmp (expr->pattern, java_sym) == 0)
if (expr->mask == BFD_ELF_VERSION_JAVA_TYPE)
goto out_ret;
else
@@ -7037,7 +7037,7 @@ lang_vers_match (struct bfd_elf_version_
}
/* Finally, try the wildcards. */
- if (prev == NULL || prev->symbol)
+ if (prev == NULL || prev->literal)
expr = head->remaining;
else
expr = prev->next;
@@ -7070,7 +7070,7 @@ lang_vers_match (struct bfd_elf_version_
}
/* Return NULL if the PATTERN argument is a glob pattern, otherwise,
- return a string pointing to the symbol name. */
+ return a pointer to the symbol name with any backslash quotes removed. */
static const char *
realsymbol (const char *pattern)
@@ -7083,22 +7083,24 @@ realsymbol (const char *pattern)
{
/* It is a glob pattern only if there is no preceding
backslash. */
- if (! backslash && (*p == '?' || *p == '*' || *p == '['))
- {
- free (symbol);
- return NULL;
- }
-
if (backslash)
{
/* Remove the preceding backslash. */
*(s - 1) = *p;
+ backslash = FALSE;
changed = TRUE;
}
else
- *s++ = *p;
+ {
+ if (*p == '?' || *p == '*' || *p == '[')
+ {
+ free (symbol);
+ return NULL;
+ }
- backslash = *p == '\\';
+ *s++ = *p;
+ backslash = *p == '\\';
+ }
}
if (changed)
@@ -7127,10 +7129,15 @@ lang_new_vers_pattern (struct bfd_elf_ve
ret = xmalloc (sizeof *ret);
ret->next = orig;
- ret->pattern = literal_p ? NULL : new;
ret->symver = 0;
ret->script = 0;
- ret->symbol = literal_p ? new : realsymbol (new);
+ ret->literal = TRUE;
+ ret->pattern = literal_p ? new : realsymbol (new);
+ if (ret->pattern == NULL)
+ {
+ ret->pattern = new;
+ ret->literal = FALSE;
+ }
if (lang == NULL || strcasecmp (lang, "C") == 0)
ret->mask = BFD_ELF_VERSION_C_TYPE;
@@ -7174,7 +7181,7 @@ version_expr_head_hash (const void *p)
{
const struct bfd_elf_version_expr *e = p;
- return htab_hash_string (e->symbol);
+ return htab_hash_string (e->pattern);
}
static int
@@ -7183,7 +7190,7 @@ version_expr_head_eq (const void *p1, co
const struct bfd_elf_version_expr *e1 = p1;
const struct bfd_elf_version_expr *e2 = p2;
- return strcmp (e1->symbol, e2->symbol) == 0;
+ return strcmp (e1->pattern, e2->pattern) == 0;
}
static void
@@ -7195,7 +7202,7 @@ lang_finalize_version_expr_head (struct
for (e = head->list; e; e = e->next)
{
- if (e->symbol)
+ if (e->literal)
count++;
head->mask |= e->mask;
}
@@ -7209,7 +7216,7 @@ lang_finalize_version_expr_head (struct
for (e = head->list; e; e = next)
{
next = e->next;
- if (!e->symbol)
+ if (!e->literal)
{
*remaining_loc = e;
remaining_loc = &e->next;
@@ -7234,14 +7241,14 @@ lang_finalize_version_expr_head (struct
last = e1;
e1 = e1->next;
}
- while (e1 && strcmp (e1->symbol, e->symbol) == 0);
+ while (e1 && strcmp (e1->pattern, e->pattern) == 0);
if (last == NULL)
{
/* This is a duplicate. */
/* FIXME: Memory leak. Sometimes pattern is not
xmalloced alone, but in larger chunk of memory. */
- /* free (e->symbol); */
+ /* free (e->pattern); */
free (e);
}
else
@@ -7305,18 +7312,18 @@ lang_register_vers_node (const char *nam
{
struct bfd_elf_version_expr *e2;
- if (t->locals.htab && e1->symbol)
+ if (t->locals.htab && e1->literal)
{
e2 = htab_find (t->locals.htab, e1);
- while (e2 && strcmp (e1->symbol, e2->symbol) == 0)
+ while (e2 && strcmp (e1->pattern, e2->pattern) == 0)
{
if (e1->mask == e2->mask)
einfo (_("%X%P: duplicate expression `%s'"
- " in version information\n"), e1->symbol);
+ " in version information\n"), e1->pattern);
e2 = e2->next;
}
}
- else if (!e1->symbol)
+ else if (!e1->literal)
for (e2 = t->locals.remaining; e2 != NULL; e2 = e2->next)
if (strcmp (e1->pattern, e2->pattern) == 0
&& e1->mask == e2->mask)
@@ -7331,19 +7338,19 @@ lang_register_vers_node (const char *nam
{
struct bfd_elf_version_expr *e2;
- if (t->globals.htab && e1->symbol)
+ if (t->globals.htab && e1->literal)
{
e2 = htab_find (t->globals.htab, e1);
- while (e2 && strcmp (e1->symbol, e2->symbol) == 0)
+ while (e2 && strcmp (e1->pattern, e2->pattern) == 0)
{
if (e1->mask == e2->mask)
einfo (_("%X%P: duplicate expression `%s'"
" in version information\n"),
- e1->symbol);
+ e1->pattern);
e2 = e2->next;
}
}
- else if (!e1->symbol)
+ else if (!e1->literal)
for (e2 = t->globals.remaining; e2 != NULL; e2 = e2->next)
if (strcmp (e1->pattern, e2->pattern) == 0
&& e1->mask == e2->mask)
Index: ld/emultempl/ppc64elf.em
===================================================================
RCS file: /cvs/src/src/ld/emultempl/ppc64elf.em,v
retrieving revision 1.60
diff -u -p -r1.60 ppc64elf.em
--- ld/emultempl/ppc64elf.em 7 Jul 2008 00:46:51 -0000 1.60
+++ ld/emultempl/ppc64elf.em 25 Nov 2008 23:13:04 -0000
@@ -416,29 +416,18 @@ gld${EMULATION_NAME}_new_vers_pattern (s
char *dot_pat;
if (!dotsyms
- || (entry->pattern != NULL
- && (entry->pattern[0] == '*' || entry->pattern[0] == '.')))
+ || entry->pattern[0] == '.'
+ || (!entry->literal && entry->pattern[0] == '*'))
return entry;
dot_entry = xmalloc (sizeof *dot_entry);
*dot_entry = *entry;
dot_entry->next = entry;
- if (entry->pattern != NULL)
- {
- len = strlen (entry->pattern) + 2;
- dot_pat = xmalloc (len);
- dot_pat[0] = '.';
- memcpy (dot_pat + 1, entry->pattern, len - 1);
- dot_entry->pattern = dot_pat;
- }
- if (entry->symbol != NULL)
- {
- len = strlen (entry->symbol) + 2;
- dot_pat = xmalloc (len);
- dot_pat[0] = '.';
- memcpy (dot_pat + 1, entry->symbol, len - 1);
- dot_entry->symbol = dot_pat;
- }
+ len = strlen (entry->pattern) + 2;
+ dot_pat = xmalloc (len);
+ dot_pat[0] = '.';
+ memcpy (dot_pat + 1, entry->pattern, len - 1);
+ dot_entry->pattern = dot_pat;
return dot_entry;
}
Index: ld/ld.texinfo
===================================================================
RCS file: /cvs/src/src/ld/ld.texinfo,v
retrieving revision 1.222
diff -u -p -r1.222 ld.texinfo
--- ld/ld.texinfo 19 Nov 2008 16:22:48 -0000 1.222
+++ ld/ld.texinfo 26 Nov 2008 00:35:48 -0000
@@ -4561,7 +4561,11 @@ When the linker finds a symbol defined i
specifically bound to a version node, it will effectively bind it to an
unspecified base version of the library. You can bind all otherwise
unspecified symbols to a given version node by using @samp{global: *;}
-somewhere in the version script.
+somewhere in the version script. Note that it's slightly crazy to use
+wildcards in a global spec except on the last version node. Global
+wildcards elsewhere run the risk of accidentally adding symbols to the
+set exported for an old version. That's wrong since older versions
+ought to have a fixed set of symbols.
The names of the version nodes have no specific meaning other than what
they might suggest to the person reading them. The @samp{2.0} version
--
Alan Modra
Australia Development Lab, IBM
More information about the Binutils
mailing list