// script.cc -- handle linker scripts for gold.
-// Copyright 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+// Copyright (C) 2006-2020 Free Software Foundation, Inc.
// Written by Ian Lance Taylor <iant@google.com>.
// This file is part of gold.
}
uint64_t
- integer_value() const
- {
- gold_assert(this->classification_ == TOKEN_INTEGER);
- // Null terminate.
- std::string s(this->value_, this->value_length_);
- return strtoull(s.c_str(), NULL, 0);
- }
+ integer_value() const;
private:
// The token classification.
int charpos_;
};
+// Return the value of a TOKEN_INTEGER.
+
+uint64_t
+Token::integer_value() const
+{
+ gold_assert(this->classification_ == TOKEN_INTEGER);
+
+ size_t len = this->value_length_;
+
+ uint64_t multiplier = 1;
+ char last = this->value_[len - 1];
+ if (last == 'm' || last == 'M')
+ {
+ multiplier = 1024 * 1024;
+ --len;
+ }
+ else if (last == 'k' || last == 'K')
+ {
+ multiplier = 1024;
+ --len;
+ }
+
+ char *end;
+ uint64_t ret = strtoull(this->value_, &end, 0);
+ gold_assert(static_cast<size_t>(end - this->value_) == len);
+
+ return ret * multiplier;
+}
+
// This class handles lexing a file into a sequence of tokens.
class Lex
// For a number we accept 0x followed by hex digits, or any sequence
// of digits. The old linker accepts leading '$' for hex, and
// trailing HXBOD. Those are for MRI compatibility and we don't
-// accept them. The old linker also accepts trailing MK for mega or
-// kilo. FIXME: Those are mentioned in the documentation, and we
-// should accept them.
+// accept them.
// Return whether C1 C2 C3 can start a hex number.
const char* (Lex::*can_continue_fn)(const char*),
const char* start,
const char* match,
- const char **pp)
+ const char** pp)
{
const char* new_match = NULL;
- while ((new_match = (this->*can_continue_fn)(match)))
+ while ((new_match = (this->*can_continue_fn)(match)) != NULL)
match = new_match;
+
+ // A special case: integers may be followed by a single M or K,
+ // case-insensitive.
+ if (classification == Token::TOKEN_INTEGER
+ && (*match == 'm' || *match == 'M' || *match == 'k' || *match == 'K'))
+ ++match;
+
*pp = match;
return this->make_token(classification, start, match - start, start);
}
while (true)
{
- if (*p == '\0')
- {
- *pp = p;
- return this->make_eof_token(p);
- }
-
// Skip whitespace quickly.
while (*p == ' ' || *p == '\t' || *p == '\r')
++p;
continue;
}
+ char c0 = *p;
+
+ if (c0 == '\0')
+ {
+ *pp = p;
+ return this->make_eof_token(p);
+ }
+
+ char c1 = p[1];
+
// Skip C style comments.
- if (p[0] == '/' && p[1] == '*')
+ if (c0 == '/' && c1 == '*')
{
int lineno = this->lineno_;
int charpos = p - this->linestart_ + 1;
}
// Skip line comments.
- if (*p == '#')
+ if (c0 == '#')
{
*pp = p + 1;
if (!this->skip_line_comment(pp))
}
// Check for a name.
- if (this->can_start_name(p[0], p[1]))
+ if (this->can_start_name(c0, c1))
return this->gather_token(Token::TOKEN_STRING,
&Lex::can_continue_name,
p, p + 1, pp);
return this->gather_quoted_string(pp);
}
+ // Be careful not to lookahead past the end of the buffer.
+ char c2 = (c1 == '\0' ? '\0' : p[2]);
+
// Check for a number.
- if (this->can_start_hex(p[0], p[1], p[2]))
+ if (this->can_start_hex(c0, c1, c2))
return this->gather_token(Token::TOKEN_INTEGER,
&Lex::can_continue_hex,
p, p + 3, pp);
- if (Lex::can_start_number(p[0]))
+ if (Lex::can_start_number(c0))
return this->gather_token(Token::TOKEN_INTEGER,
&Lex::can_continue_number,
p, p + 1, pp);
// Check for operators.
- int opcode = Lex::three_char_operator(p[0], p[1], p[2]);
+ int opcode = Lex::three_char_operator(c0, c1, c2);
if (opcode != 0)
{
*pp = p + 3;
return this->make_token(opcode, p);
}
- opcode = Lex::two_char_operator(p[0], p[1]);
+ opcode = Lex::two_char_operator(c0, c1);
if (opcode != 0)
{
*pp = p + 2;
return this->make_token(opcode, p);
}
- opcode = Lex::one_char_operator(p[0]);
+ opcode = Lex::one_char_operator(c0);
if (opcode != 0)
{
*pp = p + 1;
Output_section* dot_section)
{
Output_section* section;
+ elfcpp::STT type = elfcpp::STT_NOTYPE;
+ elfcpp::STV vis = elfcpp::STV_DEFAULT;
+ unsigned char nonvis = 0;
uint64_t final_val = this->val_->eval_maybe_dot(symtab, layout, true,
is_dot_available,
dot_value, dot_section,
- §ion);
+ §ion, NULL, &type,
+ &vis, &nonvis, false, NULL);
Sized_symbol<size>* ssym = symtab->get_sized_symbol<size>(this->sym_);
ssym->set_value(final_val);
+ ssym->set_type(type);
+ ssym->set_visibility(vis);
+ ssym->set_nonvis(nonvis);
if (section != NULL)
ssym->set_output_section(section);
}
-// Set the symbol value if the expression yields an absolute value.
+// Set the symbol value if the expression yields an absolute value or
+// a value relative to DOT_SECTION.
void
Symbol_assignment::set_if_absolute(Symbol_table* symtab, const Layout* layout,
- bool is_dot_available, uint64_t dot_value)
+ bool is_dot_available, uint64_t dot_value,
+ Output_section* dot_section)
{
if (this->sym_ == NULL)
return;
Output_section* val_section;
+ bool is_valid;
uint64_t val = this->val_->eval_maybe_dot(symtab, layout, false,
is_dot_available, dot_value,
- NULL, &val_section);
- if (val_section != NULL)
+ dot_section, &val_section, NULL,
+ NULL, NULL, NULL, false, &is_valid);
+ if (!is_valid || (val_section != NULL && val_section != dot_section))
return;
if (parameters->target().get_size() == 32)
}
else
gold_unreachable();
+ if (val_section != NULL)
+ this->sym_->set_output_section(val_section);
}
// Print for debugging.
// Class Script_options.
Script_options::Script_options()
- : entry_(), symbol_assignments_(), version_script_info_(),
- script_sections_()
+ : entry_(), symbol_assignments_(), symbol_definitions_(),
+ symbol_references_(), version_script_info_(), script_sections_()
+{
+}
+
+// Returns true if NAME is on the list of symbol assignments waiting
+// to be processed.
+
+bool
+Script_options::is_pending_assignment(const char* name)
+{
+ for (Symbol_assignments::iterator p = this->symbol_assignments_.begin();
+ p != this->symbol_assignments_.end();
+ ++p)
+ if ((*p)->name() == name)
+ return true;
+ return false;
+}
+
+// Populates the set with symbols defined in defsym LHS.
+
+void Script_options::find_defsym_defs(Unordered_set<std::string>& defsym_set)
{
+ for (Symbol_assignments::const_iterator p = this->symbol_assignments_.begin();
+ p != this->symbol_assignments_.end();
+ ++p)
+ {
+ defsym_set.insert((*p)->name());
+ }
+}
+
+void
+Script_options::set_defsym_uses_in_real_elf(Symbol_table* symtab) const
+{
+ for (Symbol_assignments::const_iterator p = this->symbol_assignments_.begin();
+ p != this->symbol_assignments_.end();
+ ++p)
+ {
+ (*p)->value()->set_expr_sym_in_real_elf(symtab);
+ }
}
// Add a symbol to be defined.
value, provide, hidden);
this->symbol_assignments_.push_back(p);
}
+
+ if (!provide)
+ {
+ std::string n(name, length);
+ this->symbol_definitions_.insert(n);
+ this->symbol_references_.erase(n);
+ }
}
else
{
}
}
+// Add a reference to a symbol.
+
+void
+Script_options::add_symbol_reference(const char* name, size_t length)
+{
+ if (length != 1 || name[0] != '.')
+ {
+ std::string n(name, length);
+ if (this->symbol_definitions_.find(n) == this->symbol_definitions_.end())
+ this->symbol_references_.insert(n);
+ }
+}
+
// Add an assertion.
void
for (Symbol_assignments::iterator p = this->symbol_assignments_.begin();
p != this->symbol_assignments_.end();
++p)
- (*p)->set_if_absolute(symtab, layout, false, 0);
+ (*p)->set_if_absolute(symtab, layout, false, 0, NULL);
return this->script_sections_.set_section_addresses(symtab, layout);
}
Command_line* command_line,
Script_options* script_options,
Lex* lex,
- bool skip_on_incompatible_target)
+ bool skip_on_incompatible_target,
+ Script_info* script_info)
: filename_(filename), posdep_options_(posdep_options),
parsing_defsym_(parsing_defsym), in_group_(in_group),
is_in_sysroot_(is_in_sysroot),
found_incompatible_target_(false),
command_line_(command_line), script_options_(script_options),
version_script_info_(script_options->version_script_info()),
- lex_(lex), lineno_(0), charpos_(0), lex_mode_stack_(), inputs_(NULL)
+ lex_(lex), lineno_(0), charpos_(0), lex_mode_stack_(), inputs_(NULL),
+ script_info_(script_info)
{
// We start out processing C symbols in the default lex mode.
this->language_stack_.push_back(Version_script_info::LANGUAGE_C);
this->language_stack_.pop_back();
}
+ // Return a pointer to the incremental info.
+ Script_info*
+ script_info()
+ { return this->script_info_; }
+
private:
// The name of the file we are reading.
const char* filename_;
std::vector<Version_script_info::Language> language_stack_;
// New input files found to add to the link.
Input_arguments* inputs_;
+ // Pointer to incremental linking info.
+ Script_info* script_info_;
};
// FILE was found as an argument on the command line. Try to read it
Lex lex(input_string.c_str(), input_string.length(), PARSING_LINKER_SCRIPT);
+ Script_info* script_info = NULL;
+ if (layout->incremental_inputs() != NULL)
+ {
+ const std::string& filename = input_file->filename();
+ Timespec mtime = input_file->file().get_mtime();
+ unsigned int arg_serial = input_argument->file().arg_serial();
+ script_info = new Script_info(filename);
+ layout->incremental_inputs()->report_script(script_info, arg_serial,
+ mtime);
+ }
+
Parser_closure closure(input_file->filename().c_str(),
input_argument->file().options(),
false,
NULL,
layout->script_options(),
&lex,
- input_file->will_search_for());
+ input_file->will_search_for(),
+ script_info);
bool old_saw_sections_clause =
layout->script_options()->saw_sections_clause();
}
workqueue->queue_soon(new Read_symbols(input_objects, symtab,
layout, dirsearch, 0, mapfile, &*p,
- input_group, this_blocker, nb));
+ input_group, NULL, this_blocker, nb));
this_blocker = nb;
}
- if (layout->incremental_inputs())
- {
- // Like new Read_symbols(...) above, we rely on close.inputs()
- // getting leaked by closure.
- Script_info* info = new Script_info(closure.inputs());
- layout->incremental_inputs()->report_script(
- input_argument,
- input_file->file().get_mtime(),
- info);
- }
*used_next_blocker = true;
return true;
}
-// Helper function for read_version_script() and
-// read_commandline_script(). Processes the given file in the mode
-// indicated by first_token and lex_mode.
+// Helper function for read_version_script(), read_commandline_script() and
+// script_include_directive(). Processes the given file in the mode indicated
+// by first_token and lex_mode.
static bool
read_script_file(const char* filename, Command_line* cmdline,
Script_options* script_options,
int first_token, Lex::Mode lex_mode)
{
- // TODO: if filename is a relative filename, search for it manually
- // using "." + cmdline->options()->search_path() -- not dirsearch.
Dirsearch dirsearch;
+ std::string name = filename;
+
+ // If filename is a relative filename, search for it manually using "." +
+ // cmdline->options()->library_path() -- not dirsearch.
+ if (!IS_ABSOLUTE_PATH(filename))
+ {
+ const General_options::Dir_list& search_path =
+ cmdline->options().library_path();
+ name = Dirsearch::find_file_in_dir_list(name, search_path, ".");
+ }
// The file locking code wants to record a Task, but we haven't
// started the workqueue yet. This is only for debugging purposes,
Position_dependent_options posdep = cmdline->position_dependent_options();
if (posdep.format_enum() == General_options::OBJECT_FORMAT_BINARY)
posdep.set_format_enum(General_options::OBJECT_FORMAT_ELF);
- Input_file_argument input_argument(filename,
+ Input_file_argument input_argument(name.c_str(),
Input_file_argument::INPUT_FILE_TYPE_FILE,
"", false, posdep);
Input_file input_file(&input_argument);
cmdline,
script_options,
&lex,
- false);
+ false,
+ NULL);
if (yyparse(&closure) != 0)
{
input_file.file().unlock(task);
Position_dependent_options posdep_options;
Parser_closure closure("command line", posdep_options, true,
- false, false, NULL, this, &lex, false);
+ false, false, NULL, this, &lex, false, NULL);
if (yyparse(&closure) != 0)
return false;
{ "BYTE", BYTE },
{ "CONSTANT", CONSTANT },
{ "CONSTRUCTORS", CONSTRUCTORS },
+ { "COPY", COPY },
{ "CREATE_OBJECT_SYMBOLS", CREATE_OBJECT_SYMBOLS },
{ "DATA_SEGMENT_ALIGN", DATA_SEGMENT_ALIGN },
{ "DATA_SEGMENT_END", DATA_SEGMENT_END },
{ "DATA_SEGMENT_RELRO_END", DATA_SEGMENT_RELRO_END },
{ "DEFINED", DEFINED },
+ { "DSECT", DSECT },
{ "ENTRY", ENTRY },
{ "EXCLUDE_FILE", EXCLUDE_FILE },
{ "EXTERN", EXTERN },
{ "FLOAT", FLOAT },
{ "FORCE_COMMON_ALLOCATION", FORCE_COMMON_ALLOCATION },
{ "GROUP", GROUP },
+ { "HIDDEN", HIDDEN },
{ "HLL", HLL },
{ "INCLUDE", INCLUDE },
+ { "INFO", INFO },
{ "INHIBIT_COMMON_ALLOCATION", INHIBIT_COMMON_ALLOCATION },
{ "INPUT", INPUT },
{ "KEEP", KEEP },
{ "NEXT", NEXT },
{ "NOCROSSREFS", NOCROSSREFS },
{ "NOFLOAT", NOFLOAT },
+ { "NOLOAD", NOLOAD },
{ "ONLY_IF_RO", ONLY_IF_RO },
{ "ONLY_IF_RW", ONLY_IF_RW },
{ "OPTION", OPTION },
{ "SIZEOF_HEADERS", SIZEOF_HEADERS },
{ "SORT", SORT_BY_NAME },
{ "SORT_BY_ALIGNMENT", SORT_BY_ALIGNMENT },
+ { "SORT_BY_INIT_PRIORITY", SORT_BY_INIT_PRIORITY },
{ "SORT_BY_NAME", SORT_BY_NAME },
{ "SPECIAL", SPECIAL },
{ "SQUAD", SQUAD },
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()
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
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] == '*')
{
- 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_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))
{
- 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
// 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;
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());
}
}
}
extern "C" void
script_add_extern(void* closurev, const char* name, size_t length)
{
- // We treat exactly like -u NAME. FIXME: If it seems useful, we
- // could handle this after the command line has been read, by adding
- // entries to the symbol table directly.
- std::string arg("--undefined=");
- arg.append(name, length);
- script_parse_option(closurev, arg.c_str(), arg.size());
+ Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+ closure->script_options()->add_symbol_reference(name, length);
}
// Called by the bison parser to add a file to the link.
{
// In addition to checking the normal library search path, we
// also want to check in the script-directory.
- const char *slash = strrchr(closure->filename(), '/');
+ const char* slash = strrchr(closure->filename(), '/');
if (slash != NULL)
{
script_directory.assign(closure->filename(),
Input_file_argument::INPUT_FILE_TYPE_FILE,
extra_search_path, false,
closure->position_dependent_options());
- closure->inputs()->add_file(file);
+ Input_argument& arg = closure->inputs()->add_file(file);
+ arg.set_script_info(closure->script_info());
+}
+
+// Called by the bison parser to add a library to the link.
+
+extern "C" void
+script_add_library(void* closurev, const char* name, size_t length)
+{
+ Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+ std::string name_string(name, length);
+
+ if (name_string[0] != 'l')
+ gold_error(_("library name must be prefixed with -l"));
+
+ Input_file_argument file(name_string.c_str() + 1,
+ Input_file_argument::INPUT_FILE_TYPE_LIBRARY,
+ "", false,
+ closure->position_dependent_options());
+ Input_argument& arg = closure->inputs()->add_file(file);
+ arg.set_script_info(closure->script_info());
}
// Called by the bison parser to start a group. If we are already in
script_parse_option(closurev, arg, strlen(arg));
}
+// Called by the bison parser to refer to a symbol.
+
+extern "C" Expression*
+script_symbol(void* closurev, const char* name, size_t length)
+{
+ Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+ if (length != 1 || name[0] != '.')
+ closure->script_options()->add_symbol_reference(name, length);
+ return script_exp_string(name, length);
+}
+
// Called by the bison parser to define a symbol.
extern "C" void
{
Parser_closure* closure = static_cast<Parser_closure*>(closurev);
std::string name(default_name, default_length);
- Target* target = select_target_by_name(name.c_str());
+ Target* target = select_target_by_bfd_name(name.c_str());
if (target == NULL || !parameters->is_compatible_target(target))
{
if (closure->skip_on_incompatible_target())
gold_warning(_("%s:%d:%d: ignoring SEARCH_DIR; SEARCH_DIR is only valid"
" for scripts specified via -T/--script"),
closure->filename(), closure->lineno(), closure->charpos());
- else
+ else if (!closure->command_line()->options().nostdlib())
{
std::string s = "-L" + std::string(option, length);
script_parse_option(closurev, s.c_str(), s.size());
script_register_vers_node(void*,
const char* tag,
int taglen,
- struct Version_tree *tree,
- struct Version_dependency_list *deps)
+ struct Version_tree* tree,
+ struct Version_dependency_list* deps)
{
gold_assert(tree != NULL);
tree->dependencies = deps;
// Add a dependencies to the list of existing dependencies, if any,
// and return the expanded list.
-extern "C" struct Version_dependency_list *
+extern "C" struct Version_dependency_list*
script_add_vers_depend(void* closurev,
- struct Version_dependency_list *all_deps,
- const char *depend_to_add, int deplen)
+ struct Version_dependency_list* all_deps,
+ const char* depend_to_add, int deplen)
{
Parser_closure* closure = static_cast<Parser_closure*>(closurev);
if (all_deps == NULL)
// Add a pattern expression to an existing list of expressions, if any.
-extern "C" struct Version_expression_list *
+extern "C" struct Version_expression_list*
script_new_vers_pattern(void* closurev,
- struct Version_expression_list *expressions,
- const char *pattern, int patlen, int exact_match)
+ struct Version_expression_list* expressions,
+ const char* pattern, int patlen, int exact_match)
{
Parser_closure* closure = static_cast<Parser_closure*>(closurev);
if (expressions == NULL)
// Attaches b to the end of a, and clears b. So a = a + b and b = {}.
extern "C" struct Version_expression_list*
-script_merge_expressions(struct Version_expression_list *a,
- struct Version_expression_list *b)
+script_merge_expressions(struct Version_expression_list* a,
+ struct Version_expression_list* b)
{
a->expressions.insert(a->expressions.end(),
b->expressions.begin(), b->expressions.end());
// Combine the global and local expressions into a a Version_tree.
-extern "C" struct Version_tree *
+extern "C" struct Version_tree*
script_new_vers_node(void* closurev,
- struct Version_expression_list *global,
- struct Version_expression_list *local)
+ struct Version_expression_list* global,
+ struct Version_expression_list* local)
{
Parser_closure* closure = static_cast<Parser_closure*>(closurev);
Version_tree* tree = closure->version_script()->allocate_version_tree();
Script_sections* ss = closure->script_options()->script_sections();
ss->set_saw_segment_start_expression(true);
}
+
+extern "C" void
+script_set_section_region(void* closurev, const char* name, size_t namelen,
+ int set_vma)
+{
+ Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+ if (!closure->script_options()->saw_sections_clause())
+ {
+ gold_error(_("%s:%d:%d: MEMORY region '%.*s' referred to outside of "
+ "SECTIONS clause"),
+ closure->filename(), closure->lineno(), closure->charpos(),
+ static_cast<int>(namelen), name);
+ return;
+ }
+
+ Script_sections* ss = closure->script_options()->script_sections();
+ Memory_region* mr = ss->find_memory_region(name, namelen);
+ if (mr == NULL)
+ {
+ gold_error(_("%s:%d:%d: MEMORY region '%.*s' not declared"),
+ closure->filename(), closure->lineno(), closure->charpos(),
+ static_cast<int>(namelen), name);
+ return;
+ }
+
+ ss->set_memory_region(mr, set_vma);
+}
+
+extern "C" void
+script_add_memory(void* closurev, const char* name, size_t namelen,
+ unsigned int attrs, Expression* origin, Expression* length)
+{
+ Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+ Script_sections* ss = closure->script_options()->script_sections();
+ ss->add_memory_region(name, namelen, attrs, origin, length);
+}
+
+extern "C" unsigned int
+script_parse_memory_attr(void* closurev, const char* attrs, size_t attrlen,
+ int invert)
+{
+ int attributes = 0;
+
+ while (attrlen--)
+ switch (*attrs++)
+ {
+ case 'R':
+ case 'r':
+ attributes |= MEM_READABLE; break;
+ case 'W':
+ case 'w':
+ attributes |= MEM_READABLE | MEM_WRITEABLE; break;
+ case 'X':
+ case 'x':
+ attributes |= MEM_EXECUTABLE; break;
+ case 'A':
+ case 'a':
+ attributes |= MEM_ALLOCATABLE; break;
+ case 'I':
+ case 'i':
+ case 'L':
+ case 'l':
+ attributes |= MEM_INITIALIZED; break;
+ default:
+ yyerror(closurev, _("unknown MEMORY attribute"));
+ }
+
+ if (invert)
+ attributes = (~ attributes) & MEM_ATTR_MASK;
+
+ return attributes;
+}
+
+extern "C" void
+script_include_directive(int first_token, void* closurev,
+ const char* filename, size_t length)
+{
+ Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+ std::string name(filename, length);
+ Command_line* cmdline = closure->command_line();
+ read_script_file(name.c_str(), cmdline, &cmdline->script_options(),
+ first_token, Lex::LINKER_SCRIPT);
+}
+
+// Functions for memory regions.
+
+extern "C" Expression*
+script_exp_function_origin(void* closurev, const char* name, size_t namelen)
+{
+ Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+ Script_sections* ss = closure->script_options()->script_sections();
+ Expression* origin = ss->find_memory_region_origin(name, namelen);
+
+ if (origin == NULL)
+ {
+ gold_error(_("undefined memory region '%s' referenced "
+ "in ORIGIN expression"),
+ name);
+ // Create a dummy expression to prevent crashes later on.
+ origin = script_exp_integer(0);
+ }
+
+ return origin;
+}
+
+extern "C" Expression*
+script_exp_function_length(void* closurev, const char* name, size_t namelen)
+{
+ Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+ Script_sections* ss = closure->script_options()->script_sections();
+ Expression* length = ss->find_memory_region_length(name, namelen);
+
+ if (length == NULL)
+ {
+ gold_error(_("undefined memory region '%s' referenced "
+ "in LENGTH expression"),
+ name);
+ // Create a dummy expression to prevent crashes later on.
+ length = script_exp_integer(0);
+ }
+
+ return length;
+}