aboutsummaryrefslogtreecommitdiff
path: root/gold/script.cc
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@google.com>2008-01-15 23:41:28 +0000
committerIan Lance Taylor <iant@google.com>2008-01-15 23:41:28 +0000
commit091244672e9cb571cb7272d491826f85de871ced (patch)
tree40be47b8e61a974e262595c7aa516f597970f285 /gold/script.cc
parent58da7b1b61de1ecfbcae3bf0050e8bb65d3ff547 (diff)
downloadgdb-091244672e9cb571cb7272d491826f85de871ced.zip
gdb-091244672e9cb571cb7272d491826f85de871ced.tar.gz
gdb-091244672e9cb571cb7272d491826f85de871ced.tar.bz2
From Andrew Chatham and Craig Silverstein: Add support for version
scripts.
Diffstat (limited to 'gold/script.cc')
-rw-r--r--gold/script.cc475
1 files changed, 420 insertions, 55 deletions
diff --git a/gold/script.cc b/gold/script.cc
index ae9cb86..16c0cc0 100644
--- a/gold/script.cc
+++ b/gold/script.cc
@@ -22,6 +22,7 @@
#include "gold.h"
+#include <fnmatch.h>
#include <string>
#include <vector>
#include <cstdio>
@@ -29,6 +30,7 @@
#include "filenames.h"
#include "elfcpp.h"
+#include "demangle.h"
#include "dirsearch.h"
#include "options.h"
#include "fileread.h"
@@ -245,26 +247,32 @@ class Lex
inline bool
can_start_name(char c, char c2);
- // Return whether C can appear in a name which has already started.
- inline bool
- can_continue_name(char c);
+ // If C can appear in a name which has already started, return a
+ // pointer to a character later in the token or just past
+ // it. Otherwise, return NULL.
+ inline const char*
+ can_continue_name(const char* c);
// Return whether C, C2, C3 can start a hex number.
inline bool
can_start_hex(char c, char c2, char c3);
- // Return whether C can appear in a hex number.
- inline bool
- can_continue_hex(char c);
+ // If C can appear in a hex number which has already started, return
+ // a pointer to a character later in the token or just past
+ // it. Otherwise, return NULL.
+ inline const char*
+ can_continue_hex(const char* c);
// Return whether C can start a non-hex number.
static inline bool
can_start_number(char c);
- // Return whether C can appear in a non-hex number.
- inline bool
- can_continue_number(char c)
- { return Lex::can_start_number(c); }
+ // If C can appear in a decimal number which has already started,
+ // return a pointer to a character later in the token or just past
+ // it. Otherwise, return NULL.
+ inline const char*
+ can_continue_number(const char* c)
+ { return Lex::can_start_number(*c) ? c + 1 : NULL; }
// If C1 C2 C3 form a valid three character operator, return the
// opcode. Otherwise return 0.
@@ -299,7 +307,7 @@ class Lex
// MATCH. Set *PP to the character following the token.
inline Token
gather_token(Token::Classification,
- bool (Lex::*can_continue_fn)(char),
+ const char* (Lex::*can_continue_fn)(const char*),
const char* start, const char* match, const char** pp);
// Build a token from a quoted string.
@@ -382,7 +390,10 @@ Lex::can_start_name(char c, char c2)
return this->mode_ == LINKER_SCRIPT;
case '~':
- return this->mode_ == LINKER_SCRIPT && can_continue_name(c2);
+ return this->mode_ == LINKER_SCRIPT && can_continue_name(&c2);
+
+ case '*': case '[':
+ return this->mode_ == VERSION_SCRIPT;
default:
return false;
@@ -395,10 +406,10 @@ Lex::can_start_name(char c, char c2)
// script language requires spaces around operators, unless we know
// that we are parsing an expression.
-inline bool
-Lex::can_continue_name(char c)
+inline const char*
+Lex::can_continue_name(const char* c)
{
- switch (c)
+ switch (*c)
{
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
@@ -413,16 +424,38 @@ Lex::can_continue_name(char c)
case '_': case '.': case '$':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
- return true;
+ return c + 1;
case '/': case '\\': case '~':
- case '=': case '+': case '-':
- case ':': case '[': case ']':
- case ',': case '?': case '*':
- return this->mode_ == LINKER_SCRIPT;
+ case '=': case '+':
+ case ',': case '?':
+ if (this->mode_ == LINKER_SCRIPT)
+ return c + 1;
+ return NULL;
+
+ case '[': case ']': case '*': case '-':
+ if (this->mode_ == LINKER_SCRIPT || this->mode_ == VERSION_SCRIPT)
+ return c + 1;
+ return NULL;
+
+ case '^':
+ if (this->mode_ == VERSION_SCRIPT)
+ return c + 1;
+ return NULL;
+
+ case ':':
+ if (this->mode_ == LINKER_SCRIPT)
+ return c + 1;
+ else if (this->mode_ == VERSION_SCRIPT && (c[1] == ':'))
+ {
+ // A name can have '::' in it, as that's a c++ namespace
+ // separator. But a single colon is not part of a name.
+ return c + 2;
+ }
+ return NULL;
default:
- return false;
+ return NULL;
}
}
@@ -439,25 +472,25 @@ inline bool
Lex::can_start_hex(char c1, char c2, char c3)
{
if (c1 == '0' && (c2 == 'x' || c2 == 'X'))
- return this->can_continue_hex(c3);
+ return this->can_continue_hex(&c3);
return false;
}
// Return whether C can appear in a hex number.
-inline bool
-Lex::can_continue_hex(char c)
+inline const char*
+Lex::can_continue_hex(const char* c)
{
- switch (c)
+ switch (*c)
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
- return true;
+ return c + 1;
default:
- return false;
+ return NULL;
}
}
@@ -652,13 +685,14 @@ Lex::skip_line_comment(const char** pp)
inline Token
Lex::gather_token(Token::Classification classification,
- bool (Lex::*can_continue_fn)(char),
+ const char* (Lex::*can_continue_fn)(const char*),
const char* start,
const char* match,
const char **pp)
{
- while ((this->*can_continue_fn)(*match))
- ++match;
+ const char* new_match = NULL;
+ while ((new_match = (this->*can_continue_fn)(match)))
+ match = new_match;
*pp = match;
return this->make_token(classification, start, match - start, start);
}
@@ -941,8 +975,13 @@ class Parser_closure
: filename_(filename), posdep_options_(posdep_options),
in_group_(in_group), is_in_sysroot_(is_in_sysroot),
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)
- { }
+ {
+ // We start out processing C symbols in the default lex mode.
+ language_stack_.push_back("");
+ lex_mode_stack_.push_back(lex->mode());
+ }
// Return the file name.
const char*
@@ -978,6 +1017,11 @@ class Parser_closure
script_options()
{ return this->script_options_; }
+ // Return the object in which version script information should be stored.
+ Version_script_info*
+ version_script()
+ { return this->version_script_info_; }
+
// Return the next token, and advance.
const Token*
next_token()
@@ -1005,6 +1049,11 @@ class Parser_closure
this->lex_mode_stack_.pop_back();
}
+ // Return the current lexer mode.
+ Lex::Mode
+ lex_mode() const
+ { return this->lex_mode_stack_.back(); }
+
// Return the line number of the last token.
int
lineno() const
@@ -1030,6 +1079,23 @@ class Parser_closure
saw_inputs() const
{ return this->inputs_ != NULL && !this->inputs_->empty(); }
+ // Return the current language being processed in a version script
+ // (eg, "C++"). The empty string represents unmangled C names.
+ const std::string&
+ get_current_language() const
+ { return this->language_stack_.back(); }
+
+ // Push a language onto the stack when entering an extern block.
+ void push_language(const std::string& lang)
+ { this->language_stack_.push_back(lang); }
+
+ // Pop a language off of the stack when exiting an extern block.
+ void pop_language()
+ {
+ gold_assert(!this->language_stack_.empty());
+ this->language_stack_.pop_back();
+ }
+
private:
// The name of the file we are reading.
const char* filename_;
@@ -1043,6 +1109,8 @@ class Parser_closure
Command_line* command_line_;
// Options which may be set from any linker script.
Script_options* script_options_;
+ // Information parsed from a version script.
+ Version_script_info* version_script_info_;
// The lexer.
Lex* lex_;
// The line number of the last token returned by next_token.
@@ -1051,6 +1119,9 @@ class Parser_closure
int charpos_;
// A stack of lexer modes.
std::vector<Lex::Mode> lex_mode_stack_;
+ // A stack of which extern/language block we're inside. Can be C++,
+ // java, or empty for C.
+ std::vector<std::string> language_stack_;
// New input files found to add to the link.
Input_arguments* inputs_;
};
@@ -1119,11 +1190,13 @@ read_input_script(Workqueue* workqueue, const General_options& options,
return true;
}
-// FILENAME was found as an argument to --script (-T).
-// Read it as a script, and execute its contents immediately.
+// Helper function for read_version_script() and
+// read_commandline_script(). Processes the given file in the mode
+// indicated by first_token and lex_mode.
-bool
-read_commandline_script(const char* filename, Command_line* cmdline)
+static bool
+read_script_file(const char* filename, Command_line* cmdline,
+ 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.
@@ -1143,7 +1216,8 @@ read_commandline_script(const char* filename, Command_line* cmdline)
std::string input_string;
Lex::read_file(&input_file, &input_string);
- Lex lex(input_string.c_str(), input_string.length(), PARSING_LINKER_SCRIPT);
+ Lex lex(input_string.c_str(), input_string.length(), first_token);
+ lex.set_mode(lex_mode);
Parser_closure closure(filename,
cmdline->position_dependent_options(),
@@ -1165,6 +1239,27 @@ read_commandline_script(const char* filename, Command_line* cmdline)
return true;
}
+// FILENAME was found as an argument to --script (-T).
+// Read it as a script, and execute its contents immediately.
+
+bool
+read_commandline_script(const char* filename, Command_line* cmdline)
+{
+ return read_script_file(filename, cmdline,
+ PARSING_LINKER_SCRIPT, Lex::LINKER_SCRIPT);
+}
+
+// FILE was found as an argument to --version-script. Read it as a
+// version script, and store its contents in
+// cmdline->script_options()->version_script_info().
+
+bool
+read_version_script(const char* filename, Command_line* cmdline)
+{
+ return read_script_file(filename, cmdline,
+ PARSING_VERSION_SCRIPT, Lex::VERSION_SCRIPT);
+}
+
// Implement the --defsym option on the command line. Return true if
// all is well.
@@ -1189,7 +1284,8 @@ Script_options::define_symbol(const char* definition)
}
// Manage mapping from keywords to the codes expected by the bison
-// parser.
+// parser. We construct one global object for each lex mode with
+// keywords.
class Keyword_to_parsecode
{
@@ -1203,25 +1299,27 @@ class Keyword_to_parsecode
int parsecode;
};
+ Keyword_to_parsecode(const Keyword_parsecode* keywords,
+ int keyword_count)
+ : keyword_parsecodes_(keywords), keyword_count_(keyword_count)
+ { }
+
// Return the parsecode corresponding KEYWORD, or 0 if it is not a
// keyword.
- static int
- keyword_to_parsecode(const char* keyword, size_t len);
+ int
+ keyword_to_parsecode(const char* keyword, size_t len) const;
private:
- // The array of all keywords.
- static const Keyword_parsecode keyword_parsecodes_[];
-
- // The number of keywords.
- static const int keyword_count;
+ const Keyword_parsecode* keyword_parsecodes_;
+ const int keyword_count_;
};
// Mapping from keyword string to keyword parsecode. This array must
// be kept in sorted order. Parsecodes are looked up using bsearch.
// This array must correspond to the list of parsecodes in yyscript.y.
-const Keyword_to_parsecode::Keyword_parsecode
-Keyword_to_parsecode::keyword_parsecodes_[] =
+static const Keyword_to_parsecode::Keyword_parsecode
+script_keyword_parsecodes[] =
{
{ "ABSOLUTE", ABSOLUTE },
{ "ADDR", ADDR },
@@ -1303,9 +1401,23 @@ Keyword_to_parsecode::keyword_parsecodes_[] =
{ "sizeof_headers", SIZEOF_HEADERS },
};
-const int Keyword_to_parsecode::keyword_count =
- (sizeof(Keyword_to_parsecode::keyword_parsecodes_)
- / sizeof(Keyword_to_parsecode::keyword_parsecodes_[0]));
+static const Keyword_to_parsecode
+script_keywords(&script_keyword_parsecodes[0],
+ (sizeof(script_keyword_parsecodes)
+ / sizeof(script_keyword_parsecodes[0])));
+
+static const Keyword_to_parsecode::Keyword_parsecode
+version_script_keyword_parsecodes[] =
+{
+ { "extern", EXTERN },
+ { "global", GLOBAL },
+ { "local", LOCAL },
+};
+
+static const Keyword_to_parsecode
+version_script_keywords(&version_script_keyword_parsecodes[0],
+ (sizeof(version_script_keyword_parsecodes)
+ / sizeof(version_script_keyword_parsecodes[0])));
// Comparison function passed to bsearch.
@@ -1335,16 +1447,17 @@ ktt_compare(const void* keyv, const void* kttv)
} // End extern "C".
int
-Keyword_to_parsecode::keyword_to_parsecode(const char* keyword, size_t len)
+Keyword_to_parsecode::keyword_to_parsecode(const char* keyword,
+ size_t len) const
{
Ktt_key key;
key.str = keyword;
key.len = len;
void* kttv = bsearch(&key,
- Keyword_to_parsecode::keyword_parsecodes_,
- Keyword_to_parsecode::keyword_count,
- sizeof(Keyword_to_parsecode::keyword_parsecodes_[0]),
- ktt_compare);
+ this->keyword_parsecodes_,
+ this->keyword_count_,
+ sizeof(this->keyword_parsecodes_[0]),
+ ktt_compare);
if (kttv == NULL)
return 0;
Keyword_parsecode* ktt = static_cast<Keyword_parsecode*>(kttv);
@@ -1383,7 +1496,18 @@ yylex(YYSTYPE* lvalp, void* closurev)
// This is either a keyword or a STRING.
size_t len;
const char* str = token->string_value(&len);
- int parsecode = Keyword_to_parsecode::keyword_to_parsecode(str, len);
+ int parsecode = 0;
+ switch (closure->lex_mode())
+ {
+ case Lex::LINKER_SCRIPT:
+ parsecode = script_keywords.keyword_to_parsecode(str, len);
+ break;
+ case Lex::VERSION_SCRIPT:
+ parsecode = version_script_keywords.keyword_to_parsecode(str, len);
+ break;
+ default:
+ break;
+ }
if (parsecode != 0)
return parsecode;
lvalp->string.value = str;
@@ -1561,6 +1685,16 @@ script_push_lex_into_expression_mode(void* closurev)
closure->push_lex_mode(Lex::EXPRESSION);
}
+/* Called by the bison parser to push the lexer into version
+ mode. */
+
+extern void
+script_push_lex_into_version_mode(void* closurev)
+{
+ Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+ closure->push_lex_mode(Lex::VERSION_SCRIPT);
+}
+
/* Called by the bison parser to pop the lexer mode. */
extern void
@@ -1569,3 +1703,234 @@ script_pop_lex_mode(void* closurev)
Parser_closure* closure = static_cast<Parser_closure*>(closurev);
closure->pop_lex_mode();
}
+
+// The following structs are used within the VersionInfo class as well
+// as in the bison helper functions. They store the information
+// parsed from the version script.
+
+// A single version expression.
+// For example, pattern="std::map*" and language="C++".
+// pattern and language should be from the stringpool
+struct Version_expression {
+ Version_expression(const std::string& pattern,
+ const std::string& language)
+ : pattern(pattern), language(language) {}
+
+ std::string pattern;
+ std::string language;
+};
+
+
+// A list of expressions.
+struct Version_expression_list {
+ std::vector<struct Version_expression> expressions;
+};
+
+
+// A list of which versions upon which another version depends.
+// Strings should be from the Stringpool.
+struct Version_dependency_list {
+ std::vector<std::string> dependencies;
+};
+
+
+// The total definition of a version. It includes the tag for the
+// version, its global and local expressions, and any dependencies.
+struct Version_tree {
+ Version_tree()
+ : tag(), global(NULL), local(NULL), dependencies(NULL) {}
+
+ std::string tag;
+ const struct Version_expression_list* global;
+ const struct Version_expression_list* local;
+ const struct Version_dependency_list* dependencies;
+};
+
+Version_script_info::~Version_script_info()
+{
+ for (size_t k = 0; k < dependency_lists_.size(); ++k)
+ delete dependency_lists_[k];
+ for (size_t k = 0; k < version_trees_.size(); ++k)
+ delete version_trees_[k];
+ for (size_t k = 0; k < expression_lists_.size(); ++k)
+ delete expression_lists_[k];
+}
+
+std::vector<std::string>
+Version_script_info::get_versions() const
+{
+ std::vector<std::string> ret;
+ for (size_t j = 0; j < version_trees_.size(); ++j)
+ ret.push_back(version_trees_[j]->tag);
+ return ret;
+}
+
+std::vector<std::string>
+Version_script_info::get_dependencies(const char* version) const
+{
+ std::vector<std::string> ret;
+ for (size_t j = 0; j < version_trees_.size(); ++j)
+ if (version_trees_[j]->tag == version)
+ {
+ const struct Version_dependency_list* deps =
+ version_trees_[j]->dependencies;
+ if (deps != NULL)
+ for (size_t k = 0; k < deps->dependencies.size(); ++k)
+ ret.push_back(deps->dependencies[k]);
+ return ret;
+ }
+ return ret;
+}
+
+const std::string&
+Version_script_info::get_symbol_version_helper(const char* symbol_name,
+ bool check_global) const
+{
+ for (size_t j = 0; j < version_trees_.size(); ++j)
+ {
+ // Is it a global symbol for this version?
+ const Version_expression_list* exp =
+ check_global ? version_trees_[j]->global : version_trees_[j]->local;
+ if (exp != NULL)
+ for (size_t k = 0; k < exp->expressions.size(); ++k)
+ {
+ const char* name_to_match = symbol_name;
+ char* demangled_name = NULL;
+ if (exp->expressions[k].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->expressions[k].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 = fnmatch(exp->expressions[k].pattern.c_str(),
+ name_to_match, FNM_NOESCAPE) == 0;
+ if (demangled_name != NULL)
+ free(demangled_name);
+ if (matched)
+ return version_trees_[j]->tag;
+ }
+ }
+ static const std::string empty = "";
+ return empty;
+}
+
+struct Version_dependency_list*
+Version_script_info::allocate_dependency_list()
+{
+ dependency_lists_.push_back(new Version_dependency_list);
+ return dependency_lists_.back();
+}
+
+struct Version_expression_list*
+Version_script_info::allocate_expression_list()
+{
+ expression_lists_.push_back(new Version_expression_list);
+ return expression_lists_.back();
+}
+
+struct Version_tree*
+Version_script_info::allocate_version_tree()
+{
+ version_trees_.push_back(new Version_tree);
+ return version_trees_.back();
+}
+
+// Register an entire version node. For example:
+//
+// GLIBC_2.1 {
+// global: foo;
+// } GLIBC_2.0;
+//
+// - tag is "GLIBC_2.1"
+// - tree contains the information "global: foo"
+// - deps contains "GLIBC_2.0"
+
+extern "C" void
+script_register_vers_node(void*,
+ const char* tag,
+ int taglen,
+ struct Version_tree *tree,
+ struct Version_dependency_list *deps)
+{
+ gold_assert(tree != NULL);
+ gold_assert(tag != NULL);
+ tree->dependencies = deps;
+ tree->tag = std::string(tag, taglen);
+}
+
+// Add a dependencies to the list of existing dependencies, if any,
+// and return the expanded 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)
+{
+ Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+ if (all_deps == NULL)
+ all_deps = closure->version_script()->allocate_dependency_list();
+ all_deps->dependencies.push_back(std::string(depend_to_add, deplen));
+ return all_deps;
+}
+
+// Add a pattern expression to an existing list of expressions, if any.
+// TODO: In the old linker, the last argument used to be a bool, but I
+// don't know what it meant.
+
+extern "C" struct Version_expression_list *
+script_new_vers_pattern(void* closurev,
+ struct Version_expression_list *expressions,
+ const char *pattern, int patlen)
+{
+ Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+ if (expressions == NULL)
+ expressions = closure->version_script()->allocate_expression_list();
+ expressions->expressions.push_back(
+ Version_expression(std::string(pattern, patlen),
+ closure->get_current_language()));
+ return expressions;
+}
+
+// Combine the global and local expressions into a a Version_tree.
+
+extern "C" struct Version_tree *
+script_new_vers_node(void* closurev,
+ 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();
+ tree->global = global;
+ tree->local = local;
+ return tree;
+}
+
+// Handle a transition in language, such as at the
+// start or end of 'extern "C++"'
+
+extern "C" void
+version_script_push_lang(void* closurev, const char* lang, int langlen)
+{
+ Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+ closure->push_language(std::string(lang, langlen));
+}
+
+extern "C" void
+version_script_pop_lang(void* closurev)
+{
+ Parser_closure* closure = static_cast<Parser_closure*>(closurev);
+ closure->pop_language();
+}