- // Is it a global symbol for this version?
- const Version_expression_list* explist =
- check_global ? version_trees_[j]->global : version_trees_[j]->local;
- if (explist != NULL)
- for (size_t k = 0; k < explist->expressions.size(); ++k)
- {
- const char* name_to_match = symbol_name;
- const struct Version_expression& exp = explist->expressions[k];
- char* demangled_name = NULL;
- if (exp.language == "C++")
- {
- demangled_name = cplus_demangle(symbol_name,
- DMGL_ANSI | DMGL_PARAMS);
- // This isn't a C++ symbol.
- if (demangled_name == NULL)
- continue;
- name_to_match = demangled_name;
- }
- else if (exp.language == "Java")
- {
- demangled_name = cplus_demangle(symbol_name,
- (DMGL_ANSI | DMGL_PARAMS
- | DMGL_JAVA));
- // This isn't a Java symbol.
- if (demangled_name == NULL)
- continue;
- name_to_match = demangled_name;
- }
- bool matched;
- if (exp.exact_match)
- matched = strcmp(exp.pattern.c_str(), name_to_match) == 0;
- else
- matched = fnmatch(exp.pattern.c_str(), name_to_match,
- FNM_NOESCAPE) == 0;
- if (demangled_name != NULL)
- free(demangled_name);
- if (matched)
- {
- if (pversion != NULL)
- *pversion = this->version_trees_[j]->tag;
- return true;
- }
- }
+ 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,
+ bool is_global)
+{
+ if (explist == NULL)
+ return;
+ size_t size = explist->expressions.size();
+ for (size_t i = 0; i < size; ++i)
+ {
+ 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_warning(_("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))
+ {
+ 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]);
+ }
+}
+
+// Return the name to match given a name, a language code, and two
+// lazy demanglers.
+
+const char*
+Version_script_info::get_name_to_match(const char* name,
+ int language,
+ Lazy_demangler* cpp_demangler,
+ Lazy_demangler* java_demangler) const
+{
+ switch (language)
+ {
+ case LANGUAGE_C:
+ return name;
+ case LANGUAGE_CXX:
+ return cpp_demangler->get();
+ case LANGUAGE_JAVA:
+ return java_demangler->get();
+ default:
+ gold_unreachable();
+ }
+}
+
+// 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(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_);
+ for (int i = 0; i < LANGUAGE_COUNT; ++i)
+ {
+ Exact* exact = this->exact_[i];
+ if (exact == NULL)
+ continue;
+
+ const char* name_to_match = this->get_name_to_match(symbol_name, i,
+ &cpp_demangled_name,
+ &java_demangled_name);
+ if (name_to_match == NULL)
+ {
+ // 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 = 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)
+ *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
+ // symbol, so that we don't warn about it. We have to do
+ // this now, because otherwise we have no way to get from a
+ // non-C language back to the demangled name that we
+ // matched.
+ 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;
+
+ if (fnmatch(p->expression->pattern.c_str(), name_to_match,
+ FNM_NOESCAPE) == 0)
+ {
+ if (pversion != NULL)
+ *pversion = p->version->tag;
+ if (p_is_global != NULL)
+ *p_is_global = p->is_global;
+ return true;
+ }
+ }
+
+ // 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;