mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-27 12:03:41 +08:00
* 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.
This commit is contained in:
parent
b4ba55a181
commit
98e090bd1f
@ -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.
|
||||
|
@ -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())
|
||||
|
448
gold/script.cc
448
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;
|
||||
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_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;
|
||||
}
|
||||
}
|
||||
|
||||
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_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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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_;
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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$"
|
||||
|
Loading…
Reference in New Issue
Block a user