diff options
author | Ian Lance Taylor <ian@airs.com> | 2010-01-12 06:41:36 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@airs.com> | 2010-01-12 06:41:36 +0000 |
commit | 98e090bd1ff5b948db254f3b9be8794d5ca93d88 (patch) | |
tree | e31a7c860d743e4fc26a005273004c9c1129b0fe /gold | |
parent | b4ba55a1812c4bde23d52111e54d2b72a1ec4af1 (diff) | |
download | gdb-98e090bd1ff5b948db254f3b9be8794d5ca93d88.zip gdb-98e090bd1ff5b948db254f3b9be8794d5ca93d88.tar.gz gdb-98e090bd1ff5b948db254f3b9be8794d5ca93d88.tar.bz2 |
* 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.
Diffstat (limited to 'gold')
-rw-r--r-- | gold/ChangeLog | 37 | ||||
-rw-r--r-- | gold/dynobj.cc | 12 | ||||
-rw-r--r-- | gold/script.cc | 450 | ||||
-rw-r--r-- | gold/script.h | 95 | ||||
-rw-r--r-- | gold/symtab.cc | 40 | ||||
-rwxr-xr-x | gold/testsuite/ver_matching_test.sh | 6 |
6 files changed, 467 insertions, 173 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog index 7036425..ea7dc35 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -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. diff --git a/gold/dynobj.cc b/gold/dynobj.cc index 86668fb..dc6b009 100644 --- a/gold/dynobj.cc +++ b/gold/dynobj.cc @@ -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()) diff --git a/gold/script.cc b/gold/script.cc index ce06052..839af3c 100644 --- a/gold/script.cc +++ b/gold/script.cc @@ -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()); } } } diff --git a/gold/script.h b/gold/script.h index 710afd0..f4c3f22 100644 --- a/gold/script.h +++ b/gold/script.h @@ -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_; }; diff --git a/gold/symtab.cc b/gold/symtab.cc index 92a91bc..c2a811f 100644 --- a/gold/symtab.cc +++ b/gold/symtab.cc @@ -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; + } } } diff --git a/gold/testsuite/ver_matching_test.sh b/gold/testsuite/ver_matching_test.sh index 7f425266..4880efa 100755 --- a/gold/testsuite/ver_matching_test.sh +++ b/gold/testsuite/ver_matching_test.sh @@ -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$" |