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]

gold patch committed: Version script ambiguities


This patch makes gold act much more like GNU ld when parsing version
scripts.  Committed to mainline.

Ian


2010-01-11  Ian Lance Taylor  <iant@google.com>

	* script.cc (class Lazy_demangler): Recreate--revert part of patch
	of 2009-12-30.
	(Version_script_info::Version_script_info): Initialize globs_,
	default_version_, default_is_global_, and exact_.  Don't
	initialize globals_ or locals_.
	(Version_script_info::build_lookup_tables): Build local symbols
	first.
	(Version_script_info::unquote): New function.
	(Version_script_info::add_exact_match): New function.
	(Version_script_info::build_expression_list_lookup): Remove lookup
	parameter.  Add is_global parameter.  Change all callers.  Handle
	wildcard pattern specially.  Unquote pattern.  Call
	add_exact_match.
	(Version_script_info::get_name_to_match): New function.
	(Version_script_info::get_symbol_version): New function.
	(Version_script_info::get_symbol_version_helper): Remove.
	(Version_script_info::check_unmatched_names): Call unquote.
	* script.h (class Version_script_info): Change get_symbol_version
	to be non-inline and add is_global parameter; change all callers.
	Rewrite symbol_is_local.  Update declarations.  Define struct
	Version_tree_match, Exact, Globs.  Don't define struct Lookup.
	Remove globals_ and locals_ members.  Add exact_, globs_,
	default_version_, is_global_.
	(Version_script_info::Glob): Remove pattern, add expression and
	is_global.  Update constructor.  Change all callers.
	* dynobj.cc (Versions::finalize): Mark the version symbol as the
	default version.
	(Versions::symbol_section_contents): If a symbol is undefined, or
	defined in a dynamic object, set the version index to
	VER_NDX_LOCAL.
	* symtab.cc (Symbol_table::add_from_relobj): Don't call
	symbol_is_local.
	(Symbol_table::add_from_pluginobj): Likewise.
	* testsuite/ver_matching_test.sh: blaza1 and blaza go into V2.


Index: dynobj.cc
===================================================================
RCS file: /cvs/src/src/gold/dynobj.cc,v
retrieving revision 1.55
diff -p -u -r1.55 dynobj.cc
--- dynobj.cc	8 Jan 2010 23:10:58 -0000	1.55
+++ dynobj.cc	12 Jan 2010 06:40:53 -0000
@@ -1560,6 +1560,7 @@ Versions::finalize(Symbol_table* symtab,
 						    false, false);
 	  vsym->set_needs_dynsym_entry();
           vsym->set_dynsym_index(dynsym_index);
+	  vsym->set_is_default();
 	  ++dynsym_index;
 	  syms->push_back(vsym);
 	  // The name is already in the dynamic pool.
@@ -1649,10 +1650,15 @@ Versions::symbol_section_contents(const 
     {
       unsigned int version_index;
       const char* version = (*p)->version();
-      if (version == NULL)
-	version_index = elfcpp::VER_NDX_GLOBAL;
-      else        
+      if (version != NULL)
 	version_index = this->version_index(symtab, dynpool, *p);
+      else
+	{
+	  if ((*p)->is_defined() && !(*p)->is_from_dynobj())
+	    version_index = elfcpp::VER_NDX_GLOBAL;
+	  else
+	    version_index = elfcpp::VER_NDX_LOCAL;
+	}
       // If the symbol was defined as foo@V1 instead of foo@@V1, add
       // the hidden bit.
       if ((*p)->version() != NULL && !(*p)->is_default())
Index: script.cc
===================================================================
RCS file: /cvs/src/src/gold/script.cc,v
retrieving revision 1.66
diff -p -u -r1.66 script.cc
--- script.cc	9 Jan 2010 05:49:57 -0000	1.66
+++ script.cc	12 Jan 2010 06:40:54 -0000
@@ -1839,17 +1839,59 @@ struct Version_tree
   const struct Version_dependency_list* dependencies;
 };
 
+// Helper class that calls cplus_demangle when needed and takes care of freeing
+// the result.
+
+class Lazy_demangler
+{
+ public:
+  Lazy_demangler(const char* symbol, int options)
+    : symbol_(symbol), options_(options), demangled_(NULL), did_demangle_(false)
+  { }
+
+  ~Lazy_demangler()
+  { free(this->demangled_); }
+
+  // Return the demangled name. The actual demangling happens on the first call,
+  // and the result is later cached.
+  inline char*
+  get();
+
+ private:
+  // The symbol to demangle.
+  const char *symbol_;
+  // Option flags to pass to cplus_demagle.
+  const int options_;
+  // The cached demangled value, or NULL if demangling didn't happen yet or
+  // failed.
+  char *demangled_;
+  // Whether we already called cplus_demangle
+  bool did_demangle_;
+};
+
+// Return the demangled name. The actual demangling happens on the first call,
+// and the result is later cached. Returns NULL if the symbol cannot be
+// demangled.
+
+inline char*
+Lazy_demangler::get()
+{
+  if (!this->did_demangle_)
+    {
+      this->demangled_ = cplus_demangle(this->symbol_, this->options_);
+      this->did_demangle_ = true;
+    }
+  return this->demangled_;
+}
+
 // Class Version_script_info.
 
 Version_script_info::Version_script_info()
-  : dependency_lists_(), expression_lists_(), version_trees_(),
-    is_finalized_(false)
+  : dependency_lists_(), expression_lists_(), version_trees_(), globs_(),
+    default_version_(NULL), default_is_global_(false), is_finalized_(false)
 {
   for (int i = 0; i < LANGUAGE_COUNT; ++i)
-    {
-      this->globals_[i] = NULL;
-      this->locals_[i] = NULL;
-    }
+    this->exact_[i] = NULL;
 }
 
 Version_script_info::~Version_script_info()
@@ -1915,6 +1957,84 @@ Version_script_info::get_dependencies(co
   return ret;
 }
 
+// A version script essentially maps a symbol name to a version tag
+// and an indication of whether symbol is global or local within that
+// version tag.  Each symbol maps to at most one version tag.
+// Unfortunately, in practice, version scripts are ambiguous, and list
+// symbols multiple times.  Thus, we have to document the matching
+// process.
+
+// This is a description of what the GNU linker does as of 2010-01-11.
+// It walks through the version tags in the order in which they appear
+// in the version script.  For each tag, it first walks through the
+// global patterns for that tag, then the local patterns.  When
+// looking at a single pattern, it first applies any language specific
+// demangling as specified for the pattern, and then matches the
+// resulting symbol name to the pattern.  If it finds an exact match
+// for a literal pattern (a pattern enclosed in quotes or with no
+// wildcard characters), then that is the match that it uses.  If
+// finds a match with a wildcard pattern, then it saves it and
+// continues searching.  Wildcard patterns that are exactly "*" are
+// saved separately.
+
+// If no exact match with a literal pattern is ever found, then if a
+// wildcard match with a global pattern was found it is used,
+// otherwise if a wildcard match with a local pattern was found it is
+// used.
+
+// This is the result:
+//   * If there is an exact match, then we use the first tag in the
+//     version script where it matches.
+//     + If the exact match in that tag is global, it is used.
+//     + Otherwise the exact match in that tag is local, and is used.
+//   * Otherwise, if there is any match with a global wildcard pattern:
+//     + If there is any match with a wildcard pattern which is not
+//       "*", then we use the tag in which the *last* such pattern
+//       appears.
+//     + Otherwise, we matched "*".  If there is no match with a local
+//       wildcard pattern which is not "*", then we use the *last*
+//       match with a global "*".  Otherwise, continue.
+//   * Otherwise, if there is any match with a local wildcard pattern:
+//     + If there is any match with a wildcard pattern which is not
+//       "*", then we use the tag in which the *last* such pattern
+//       appears.
+//     + Otherwise, we matched "*", and we use the tag in which the
+//       *last* such match occurred.
+
+// There is an additional wrinkle.  When the GNU linker finds a symbol
+// with a version defined in an object file due to a .symver
+// directive, it looks up that symbol name in that version tag.  If it
+// finds it, it matches the symbol name against the patterns for that
+// version.  If there is no match with a global pattern, but there is
+// a match with a local pattern, then the GNU linker marks the symbol
+// as local.
+
+// We want gold to be generally compatible, but we also want gold to
+// be fast.  These are the rules that gold implements:
+//   * If there is an exact match for the mangled name, we use it.
+//     + If there is more than one exact match, we give a warning, and
+//       we use the first tag in the script which matches.
+//     + If a symbol has an exact match as both global and local for
+//       the same version tag, we give an error.
+//   * Otherwise, we look for an extern C++ or an extern Java exact
+//     match.  If we find an exact match, we use it.
+//     + If there is more than one exact match, we give a warning, and
+//       we use the first tag in the script which matches.
+//     + If a symbol has an exact match as both global and local for
+//       the same version tag, we give an error.
+//   * Otherwise, we look through the wildcard patterns, ignoring "*"
+//     patterns.  We look through the version tags in reverse order.
+//     For each version tag, we look through the global patterns and
+//     then the local patterns.  We use the first match we find (i.e.,
+//     the last matching version tag in the file).
+//   * Otherwise, we use the "*" pattern if there is one.  We give an
+//     error if there are multiple "*" patterns.
+
+// At least for now, gold does not look up the version tag for a
+// symbol version found in an object file to see if it should be
+// forced local.  There are other ways to force a symbol to be local,
+// and I don't understand why this one is useful.
+
 // Build a set of fast lookup tables for a version script.
 
 void
@@ -1924,131 +2044,206 @@ Version_script_info::build_lookup_tables
   for (size_t j = 0; j < size; ++j)
     {
       const Version_tree* v = this->version_trees_[j];
-      this->build_expression_list_lookup(v->global, v, &this->globals_[0]);
-      this->build_expression_list_lookup(v->local, v, &this->locals_[0]);
+      this->build_expression_list_lookup(v->local, v, false);
+      this->build_expression_list_lookup(v->global, v, true);
+    }
+}
+
+// If a pattern has backlashes but no unquoted wildcard characters,
+// then we apply backslash unquoting and look for an exact match.
+// Otherwise we treat it as a wildcard pattern.  This function returns
+// true for a wildcard pattern.  Otherwise, it does backslash
+// unquoting on *PATTERN and returns false.  If this returns true,
+// *PATTERN may have been partially unquoted.
+
+bool
+Version_script_info::unquote(std::string* pattern) const
+{
+  bool saw_backslash = false;
+  size_t len = pattern->length();
+  size_t j = 0;
+  for (size_t i = 0; i < len; ++i)
+    {
+      if (saw_backslash)
+	saw_backslash = false;
+      else
+	{
+	  switch ((*pattern)[i])
+	    {
+	    case '?': case '[': case '*':
+	      return true;
+	    case '\\':
+	      saw_backslash = true;
+	      continue;
+	    default:
+	      break;
+	    }
+	}
+
+      if (i != j)
+	(*pattern)[j] = (*pattern)[i];
+      ++j;
+    }
+  return false;
+}
+
+// Add an exact match for MATCH to *PE.  The result of the match is
+// V/IS_GLOBAL.
+
+void
+Version_script_info::add_exact_match(const std::string& match,
+				     const Version_tree* v, bool is_global,
+				     const Version_expression* ve,
+				     Exact *pe)
+{
+  std::pair<Exact::iterator, bool> ins =
+    pe->insert(std::make_pair(match, Version_tree_match(v, is_global, ve)));
+  if (ins.second)
+    {
+      // This is the first time we have seen this match.
+      return;
+    }
+
+  Version_tree_match& vtm(ins.first->second);
+  if (vtm.real->tag != v->tag)
+    {
+      // This is an ambiguous match.  We still return the
+      // first version that we found in the script, but we
+      // record the new version to issue a warning if we
+      // wind up looking up this symbol.
+      if (vtm.ambiguous == NULL)
+	vtm.ambiguous = v;
+    }
+  else if (is_global != vtm.is_global)
+    {
+      // We have a match for both the global and local entries for a
+      // version tag.  That's got to be wrong.
+      gold_error(_("'%s' appears as both a global and a local symbol "
+		   "for version '%s' in script"),
+		 match.c_str(), v->tag.c_str());
     }
 }
 
 // Build fast lookup information for EXPLIST and store it in LOOKUP.
+// All matches go to V, and IS_GLOBAL is true if they are global
+// matches.
 
 void
 Version_script_info::build_expression_list_lookup(
     const Version_expression_list* explist,
     const Version_tree* v,
-    Lookup** lookup)
+    bool is_global)
 {
   if (explist == NULL)
     return;
   size_t size = explist->expressions.size();
-  for (size_t j = 0; j < size; ++j)
+  for (size_t i = 0; i < size; ++i)
     {
-      const Version_expression& exp(explist->expressions[j]);
-      Lookup **pp = &lookup[exp.language];
-      if (*pp == NULL)
-	*pp = new Lookup();
-      Lookup* p = *pp;
+      const Version_expression& exp(explist->expressions[i]);
 
-      if (!exp.exact_match && strpbrk(exp.pattern.c_str(), "?*[") != NULL)
-	p->globs.push_back(Glob(exp.pattern.c_str(), v));
-      else
+      if (exp.pattern.length() == 1 && exp.pattern[0] == '*')
 	{
-	  std::pair<Exact::iterator, bool> ins =
-	    p->exact.insert(std::make_pair(exp.pattern, v));
-	  if (!ins.second)
+	  if (this->default_version_ != NULL
+	      && this->default_version_->tag != v->tag)
+	    gold_error(_("wildcard match appears in both version '%s' "
+			 "and '%s' in script"),
+		       this->default_version_->tag.c_str(), v->tag.c_str());
+	  else if (this->default_version_ != NULL
+		   && this->default_is_global_ != is_global)
+	    gold_error(_("wildcard match appears as both global and local "
+			 "in version '%s' in script"),
+		       v->tag.c_str());
+	  this->default_version_ = v;
+	  this->default_is_global_ = is_global;
+	  continue;
+	}
+
+      std::string pattern = exp.pattern;
+      if (!exp.exact_match)
+	{
+	  if (this->unquote(&pattern))
 	    {
-	      const Version_tree* v1 = ins.first->second;
-	      if (v1 != NULL && v1->tag != v->tag)
-		{
-		  // This is an ambiguous match.  It's OK if it's just
-		  // documenting symbol versions, but not if we look
-		  // up this symbol.
-		  ins.first->second = NULL;
-		}
+	      this->globs_.push_back(Glob(&exp, v, is_global));
+	      continue;
 	    }
 	}
+
+      if (this->exact_[exp.language] == NULL)
+	this->exact_[exp.language] = new Exact();
+      this->add_exact_match(pattern, v, is_global, &exp,
+			    this->exact_[exp.language]);
     }
 }
 
-// Record that we have matched a name found in the version script.
+// Return the name to match given a name, a language code, and two
+// lazy demanglers.
 
-void
-Version_script_info::matched_symbol(const Version_tree* version_tree,
-				    const char* name) const
+const char*
+Version_script_info::get_name_to_match(const char* name,
+				       int language,
+				       Lazy_demangler* cpp_demangler,
+				       Lazy_demangler* java_demangler) const
 {
-  const struct Version_expression_list* global = version_tree->global;
-  for (size_t i = 0; i < global->expressions.size(); ++i)
+  switch (language)
     {
-      const Version_expression& expression(global->expressions[i]);
-      if (expression.pattern == name
-	  && (expression.exact_match
-	      || strpbrk(expression.pattern.c_str(), "?*[") == NULL))
-	{
-	  expression.was_matched_by_symbol = true;
-	  return;
-	}
+    case LANGUAGE_C:
+      return name;
+    case LANGUAGE_CXX:
+      return cpp_demangler->get();
+    case LANGUAGE_JAVA:
+      return java_demangler->get();
+    default:
+      gold_unreachable();
     }
-  gold_unreachable();
 }
 
-// Look up SYMBOL_NAME in the list of versions.  If CHECK_GLOBAL is
-// true look at the globally visible symbols, otherwise look at the
-// symbols listed as "local:".  Return true if the symbol is found,
-// false otherwise.  If the symbol is found, then if PVERSION is not
-// NULL, set *PVERSION to the version.
+// Look up SYMBOL_NAME in the list of versions.  Return true if the
+// symbol is found, false if not.  If the symbol is found, then if
+// PVERSION is not NULL, set *PVERSION to the version tag, and if
+// P_IS_GLOBAL is not NULL, set *P_IS_GLOBAL according to whether the
+// symbol is global or not.
 
 bool
-Version_script_info::get_symbol_version_helper(const char* symbol_name,
-                                               bool check_global,
-					       std::string* pversion) const
+Version_script_info::get_symbol_version(const char* symbol_name,
+					std::string* pversion,
+					bool* p_is_global) const
 {
+  Lazy_demangler cpp_demangled_name(symbol_name, DMGL_ANSI | DMGL_PARAMS);
+  Lazy_demangler java_demangled_name(symbol_name,
+				     DMGL_ANSI | DMGL_PARAMS | DMGL_JAVA);
+
   gold_assert(this->is_finalized_);
-  const Lookup* const * pp = (check_global
-			      ? &this->globals_[0]
-			      : &this->locals_[0]);
   for (int i = 0; i < LANGUAGE_COUNT; ++i)
     {
-      const Lookup* lookup = pp[i];
-      if (lookup == NULL)
+      Exact* exact = this->exact_[i];
+      if (exact == NULL)
 	continue;
 
-      const char* name_to_match;
-      char* allocated;
-      switch (i)
+      const char* name_to_match = this->get_name_to_match(symbol_name, i,
+							  &cpp_demangled_name,
+							  &java_demangled_name);
+      if (name_to_match == NULL)
 	{
-	case LANGUAGE_C:
-	  allocated = NULL;
-	  name_to_match = symbol_name;
-	  break;
-	case LANGUAGE_CXX:
-	  allocated = cplus_demangle(symbol_name, DMGL_ANSI | DMGL_PARAMS);
-	  if (allocated == NULL)
-	    continue;
-	  name_to_match = allocated;
-	  break;
-	case LANGUAGE_JAVA:
-	  allocated = cplus_demangle(symbol_name,
-				     DMGL_ANSI | DMGL_PARAMS | DMGL_JAVA);
-	  if (allocated == NULL)
-	    continue;
-	  name_to_match = allocated;
-	default:
-	  gold_unreachable();
+	  // If the name can not be demangled, the GNU linker goes
+	  // ahead and tries to match it anyhow.  That does not
+	  // make sense to me and I have not implemented it.
+	  continue;
 	}
 
-      Exact::const_iterator pe = lookup->exact.find(name_to_match);
-      if (pe != lookup->exact.end())
+      Exact::const_iterator pe = exact->find(name_to_match);
+      if (pe != exact->end())
 	{
+	  const Version_tree_match& vtm(pe->second);
+	  if (vtm.ambiguous != NULL)
+	    gold_warning(_("using '%s' as version for '%s' which is also "
+			   "named in version '%s' in script"),
+			 vtm.real->tag.c_str(), name_to_match,
+			 vtm.ambiguous->tag.c_str());
+
 	  if (pversion != NULL)
-	    {
-	      if (pe->second != NULL)
-		*pversion = pe->second->tag;
-	      else
-		{
-		  gold_error(_("'%s' has multiple versions in version script"),
-			     name_to_match);
-		  return false;
-		}
-	    }
+	    *pversion = vtm.real->tag;
+	  if (p_is_global != NULL)
+	    *p_is_global = vtm.is_global;
 
 	  // If we are using --no-undefined-version, and this is a
 	  // global symbol, we have to record that we have found this
@@ -2056,33 +2251,46 @@ Version_script_info::get_symbol_version_
 	  // this now, because otherwise we have no way to get from a
 	  // non-C language back to the demangled name that we
 	  // matched.
-	  if (check_global && !parameters->options().undefined_version())
-	    this->matched_symbol(pe->second, name_to_match);
-
-	  if (allocated != NULL)
-	    free (allocated);
+	  if (p_is_global != NULL && vtm.is_global)
+	    vtm.expression->was_matched_by_symbol = true;
 
 	  return true;
 	}
+    }
 
-      for (std::vector<Glob>::const_iterator pg = lookup->globs.begin();
-	   pg != lookup->globs.end();
-	   ++pg)
+  // Look through the glob patterns in reverse order.
+
+  for (Globs::const_reverse_iterator p = this->globs_.rbegin();
+       p != this->globs_.rend();
+       ++p)
+    {
+      int language = p->expression->language;
+      const char* name_to_match = this->get_name_to_match(symbol_name,
+							  language,
+							  &cpp_demangled_name,
+							  &java_demangled_name);
+      if (name_to_match == NULL)
+	continue;
+
+      if (fnmatch(p->expression->pattern.c_str(), name_to_match,
+		  FNM_NOESCAPE) == 0)
 	{
-	  // Check for * specially since it is fairly common.
-	  if ((pg->pattern[0] == '*' && pg->pattern[1] == '\0')
-	      || fnmatch(pg->pattern, name_to_match, FNM_NOESCAPE) == 0)
-	    {
-	      if (pversion != NULL)
-		*pversion = pg->version->tag;
-	      if (allocated != NULL)
-		free (allocated);
-	      return true;
-	    }
+	  if (pversion != NULL)
+	    *pversion = p->version->tag;
+	  if (p_is_global != NULL)
+	    *p_is_global = p->is_global;
+	  return true;
 	}
+    }
 
-      if (allocated != NULL)
-	free (allocated);
+  // Finally, there may be a wildcard.
+  if (this->default_version_ != NULL)
+    {
+      if (pversion != NULL)
+	*pversion = this->default_version_->tag;
+      if (p_is_global != NULL)
+	*p_is_global = this->default_is_global_;
+      return true;
     }
 
   return false;
@@ -2116,18 +2324,18 @@ Version_script_info::check_unmatched_nam
 	  if (expression.language != LANGUAGE_C)
 	    continue;
 
-	  // Ignore wildcard patterns.
-	  if (!expression.exact_match
-	      && strpbrk(expression.pattern.c_str(), "?*[") != NULL)
-	    continue;
-
-	  if (symtab->lookup(expression.pattern.c_str(),
-			     vt->tag.c_str()) == NULL)
+	  // Remove backslash quoting, and ignore wildcard patterns.
+	  std::string pattern = expression.pattern;
+	  if (!expression.exact_match)
 	    {
-	      gold_error(_("version script assignment of %s to symbol %s "
-			   "failed: symbol not defined"),
-			 vt->tag.c_str(), expression.pattern.c_str());
+	      if (this->unquote(&pattern))
+		continue;
 	    }
+
+	  if (symtab->lookup(pattern.c_str(), vt->tag.c_str()) == NULL)
+	    gold_error(_("version script assignment of %s to symbol %s "
+			 "failed: symbol not defined"),
+		       vt->tag.c_str(), pattern.c_str());
 	}
     }
 }
Index: script.h
===================================================================
RCS file: /cvs/src/src/gold/script.h,v
retrieving revision 1.31
diff -p -u -r1.31 script.h
--- script.h	6 Jan 2010 05:30:24 -0000	1.31
+++ script.h	12 Jan 2010 06:40:54 -0000
@@ -55,6 +55,8 @@ class Workqueue;
 struct Version_dependency_list;
 struct Version_expression_list;
 struct Version_tree;
+struct Version_expression;
+class Lazy_demangler;
 
 // This class represents an expression in a linker script.
 
@@ -160,16 +162,21 @@ class Version_script_info
   { return this->version_trees_.empty(); }
 
   // If there is a version associated with SYMBOL, return true, and
-  // set *VERSION to the version.  Otherwise, return false.
+  // set *VERSION to the version, and *IS_GLOBAL to whether the symbol
+  // should be global.  Otherwise, return false.
   bool
-  get_symbol_version(const char* symbol, std::string* version) const
-  { return this->get_symbol_version_helper(symbol, true, version); }
+  get_symbol_version(const char* symbol, std::string* version,
+		     bool* is_global) const;
 
   // Return whether this symbol matches the local: section of some
   // version.
   bool
   symbol_is_local(const char* symbol) const
-  { return this->get_symbol_version_helper(symbol, false, NULL); }
+  {
+    bool is_global;
+    return (this->get_symbol_version(symbol, NULL, &is_global)
+	    && !is_global);
+  }
 
   // Return the names of versions defined in the version script.
   std::vector<std::string>
@@ -214,43 +221,72 @@ class Version_script_info
 			    bool check_global,
 			    std::string* pversion) const;
 
-  void
-  matched_symbol(const Version_tree*, const char*) const;
+  // Fast lookup information for a given language.
+
+  // We map from exact match strings to Version_tree's.  Historically
+  // version scripts sometimes have the same symbol multiple times,
+  // which is ambiguous.  We warn about that case by storing the
+  // second Version_tree we see.
+  struct Version_tree_match
+  {
+    Version_tree_match(const Version_tree* r, bool ig,
+		       const Version_expression* e)
+      : real(r), is_global(ig), expression(e), ambiguous(NULL)
+    { }
+
+    // The Version_tree that we return.
+    const Version_tree* real;
+    // True if this is a global match for the REAL member, false if it
+    // is a local match.
+    bool is_global;
+    // Point back to the Version_expression for which we created this
+    // match.
+    const Version_expression* expression;
+    // If not NULL, another Version_tree that defines the symbol.
+    const Version_tree* ambiguous;
+  };
+
+  // Map from an exact match string to a Version_tree.
+
+  typedef Unordered_map<std::string, Version_tree_match> Exact;
 
   // Fast lookup information for a glob pattern.
   struct Glob
   {
     Glob()
-      : pattern(NULL), version(NULL)
+      : expression(NULL), version(NULL), is_global(false)
     { }
 
-    Glob(const char* p, const Version_tree* v)
-      : pattern(p), version(v)
+    Glob(const Version_expression* e, const Version_tree* v, bool ig)
+      : expression(e), version(v), is_global(ig)
     { }
 
-    // A pointer to the glob pattern.  The pattern itself lives in a
-    // Version_expression structure.
-    const char* pattern;
+    // A pointer to the version expression holding the pattern to
+    // match and the language to use for demangling the symbol before
+    // doing the match.
+    const Version_expression* expression;
     // The Version_tree we use if this pattern matches.
     const Version_tree* version;
+    // True if this is a global symbol.
+    bool is_global;
   };
 
-  // Fast lookup information for a given language.
+  typedef std::vector<Glob> Globs;
 
-  typedef Unordered_map<std::string, const Version_tree*> Exact;
+  bool
+  unquote(std::string*) const;
 
-  struct Lookup
-  {
-    // A hash table of all exact match strings mapping to a
-    // Version_tree.
-    Exact exact;
-    // A vector of glob patterns mapping to Version_trees.
-    std::vector<Glob> globs;
-  };
+  void
+  add_exact_match(const std::string&, const Version_tree*, bool is_global,
+		  const Version_expression*, Exact*);
 
   void
   build_expression_list_lookup(const Version_expression_list*,
-			       const Version_tree*, Lookup**);
+			       const Version_tree*, bool);
+
+  const char*
+  get_name_to_match(const char*, int,
+		    Lazy_demangler*, Lazy_demangler*) const;
 
   // All the version dependencies we allocate.
   std::vector<Version_dependency_list*> dependency_lists_;
@@ -258,10 +294,15 @@ class Version_script_info
   std::vector<Version_expression_list*> expression_lists_;
   // The list of versions.
   std::vector<Version_tree*> version_trees_;
-  // Lookup information for global symbols, by language.
-  Lookup* globals_[LANGUAGE_COUNT];
-  // Lookup information for local symbols, by language.
-  Lookup* locals_[LANGUAGE_COUNT];
+  // Exact matches for global symbols, by language.
+  Exact* exact_[LANGUAGE_COUNT];
+  // A vector of glob patterns mapping to Version_trees.
+  Globs globs_;
+  // The default version to use, if there is one.  This is from a
+  // pattern of "*".
+  const Version_tree* default_version_;
+  // True if the default version is global.
+  bool default_is_global_;
   // Whether this has been finalized.
   bool is_finalized_;
 };
Index: symtab.cc
===================================================================
RCS file: /cvs/src/src/gold/symtab.cc,v
retrieving revision 1.137
diff -p -u -r1.137 symtab.cc
--- symtab.cc	9 Jan 2010 00:13:48 -0000	1.137
+++ symtab.cc	12 Jan 2010 06:40:55 -0000
@@ -1113,11 +1113,13 @@ Symbol_table::add_from_relobj(
 	      // The symbol name did not have a version, but the
 	      // version script may assign a version anyway.
 	      std::string version;
-	      if (this->version_script_.get_symbol_version(name, &version))
+	      bool is_global;
+	      if (this->version_script_.get_symbol_version(name, &version,
+							   &is_global))
 		{
-		  // The version can be empty if the version script is
-		  // only used to force some symbols to be local.
-		  if (!version.empty())
+		  if (!is_global)
+		    is_forced_local = true;
+		  else if (!version.empty())
 		    {
 		      ver = this->namepool_.add_with_length(version.c_str(),
 							    version.length(),
@@ -1126,8 +1128,6 @@ Symbol_table::add_from_relobj(
 		      is_default_version = true;
 		    }
 		}
-	      else if (this->version_script_.symbol_is_local(name))
-		is_forced_local = true;
 	    }
 	}
 
@@ -1232,11 +1232,13 @@ Symbol_table::add_from_pluginobj(
           // The symbol name did not have a version, but the
           // version script may assign a version anyway.
           std::string version;
-          if (this->version_script_.get_symbol_version(name, &version))
+	  bool is_global;
+          if (this->version_script_.get_symbol_version(name, &version,
+						       &is_global))
             {
-              // The version can be empty if the version script is
-              // only used to force some symbols to be local.
-              if (!version.empty())
+	      if (!is_global)
+		is_forced_local = true;
+	      else if (!version.empty())
                 {
                   ver = this->namepool_.add_with_length(version.c_str(),
                                                         version.length(),
@@ -1245,8 +1247,6 @@ Symbol_table::add_from_pluginobj(
                   is_default_version = true;
                 }
             }
-          else if (this->version_script_.symbol_is_local(name))
-            is_forced_local = true;
         }
     }
 
@@ -1566,14 +1566,16 @@ Symbol_table::define_special_symbol(cons
   bool is_default_version = false;
   if (*pversion == NULL)
     {
-      if (this->version_script_.get_symbol_version(*pname, &v))
+      bool is_global;
+      if (this->version_script_.get_symbol_version(*pname, &v, &is_global))
 	{
-	  if (!v.empty())
-	    *pversion = v.c_str();
-
-	  // If we get the version from a version script, then we are
-	  // also the default version.
-	  is_default_version = true;
+	  if (is_global && !v.empty())
+	    {
+	      *pversion = v.c_str();
+	      // If we get the version from a version script, then we
+	      // are also the default version.
+	      is_default_version = true;
+	    }
 	}
     }
 
Index: testsuite/ver_matching_test.sh
===================================================================
RCS file: /cvs/src/src/gold/testsuite/ver_matching_test.sh,v
retrieving revision 1.3
diff -p -u -r1.3 ver_matching_test.sh
--- testsuite/ver_matching_test.sh	24 Jan 2008 00:15:00 -0000	1.3
+++ testsuite/ver_matching_test.sh	12 Jan 2010 06:40:55 -0000
@@ -2,7 +2,7 @@
 
 # ver_matching_test.sh -- a test case for version script matching
 
-# Copyright 2008 Free Software Foundation, Inc.
+# Copyright 2008, 2010 Free Software Foundation, Inc.
 # Written by Ian Lance Taylor <iant@google.com>.
 
 # This file is part of gold.
@@ -66,9 +66,9 @@ check ver_matching_test.stdout "V1  *myn
 check ver_matching_test.stdout "V1  *myns::bip()$"
 check ver_matching_test.stdout "V1  *myns::Stuff::Stuff()$"
 check ver_matching_test.stdout "Base  *Biz::Biz()$"
-check ver_matching_test.stdout "V1  *blaza1$"
+check ver_matching_test.stdout "V2  *blaza1$"
 check ver_matching_test.stdout "V2  *blaza2$"
-check ver_matching_test.stdout "V1  *blaza$"
+check ver_matching_test.stdout "V2  *blaza$"
 check ver_matching_test.stdout "Base  *bla$"
 check ver_matching_test.stdout "V2  *blaz$"
 check ver_matching_test.stdout "V2  *blazb$"

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