This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

PR7047, wildcards in version scripts


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]