* script.cc (class Lazy_demangler): Recreate--revert part of patch
authorIan Lance Taylor <ian@airs.com>
Tue, 12 Jan 2010 06:41:36 +0000 (06:41 +0000)
committerIan Lance Taylor <ian@airs.com>
Tue, 12 Jan 2010 06:41:36 +0000 (06:41 +0000)
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.

gold/ChangeLog
gold/dynobj.cc
gold/script.cc
gold/script.h
gold/symtab.cc
gold/testsuite/ver_matching_test.sh

index 7036425f0023a1136dc6ee3dfdeb637d91390dc2..ea7dc35cde24a2be93518135397e917db0c54c36 100644 (file)
@@ -1,3 +1,40 @@
+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.
+
 2010-01-11  Doug Kwan  <dougkwan@google.com>
 
        * Makefile.am (incremental_dump_DEPENDENCIES): Add libintl dependency.
index 86668fb0832e1a3f562290454a9bcbd24521b216..dc6b009f49034764c8616592a1552713ee6e3f8a 100644 (file)
@@ -1560,6 +1560,7 @@ Versions::finalize(Symbol_table* symtab, unsigned int dynsym_index,
                                                    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 Symbol_table* symtab,
     {
       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 ce0605206b1a2905a1d357555eb6214fd49f6d04..839af3c53105f88b58cc755cea6fd94b04c3ee65 100644 (file)
@@ -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(const char* version) const
   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;
-
-      if (!exp.exact_match && strpbrk(exp.pattern.c_str(), "?*[") != NULL)
-       p->globs.push_back(Glob(exp.pattern.c_str(), v));
-      else
+      const Version_expression& exp(explist->expressions[i]);
+
+      if (exp.pattern.length() == 1 && exp.pattern[0] == '*')
+       {
+         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)
        {
-         std::pair<Exact::iterator, bool> ins =
-           p->exact.insert(std::make_pair(exp.pattern, v));
-         if (!ins.second)
+         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_helper(const char* symbol_name,
          // 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;
        }
+    }
+
+  // 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;
 
-      for (std::vector<Glob>::const_iterator pg = lookup->globs.begin();
-          pg != lookup->globs.end();
-          ++pg)
+      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_names(const Symbol_table* symtab) const
          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 710afd0eea51adddd0c7d6f2c133ead08e784081..f4c3f22620be86b1025f4756d882fb0d550b91d9 100644 (file)
@@ -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 92a91bc1012ebcc2c99cde3f5bda605a2ef6d9a0..c2a811f19c0be84cb5b45b56ee6e176551a1dd9d 100644 (file)
@@ -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(const char** pname, const char** pversion,
   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 7f4252661007b6651f66ce54dce35a9460e64b1c..4880efab0c552a75f537f2d3ad51fef66b76ba7b 100755 (executable)
@@ -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  *myns::blah()$"
 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$"