URL
https://opencores.org/ocsvn/open8_urisc/open8_urisc/trunk
Subversion Repositories open8_urisc
[/] [open8_urisc/] [trunk/] [gnu/] [binutils/] [gold/] [script.cc] - Rev 148
Go to most recent revision | Compare with Previous | Blame | View Log
// script.cc -- handle linker scripts for gold. // Copyright 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. // Written by Ian Lance Taylor <iant@google.com>. // This file is part of gold. // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, // MA 02110-1301, USA. #include "gold.h" #include <cstdio> #include <cstdlib> #include <cstring> #include <fnmatch.h> #include <string> #include <vector> #include "filenames.h" #include "elfcpp.h" #include "demangle.h" #include "dirsearch.h" #include "options.h" #include "fileread.h" #include "workqueue.h" #include "readsyms.h" #include "parameters.h" #include "layout.h" #include "symtab.h" #include "target-select.h" #include "script.h" #include "script-c.h" #include "incremental.h" namespace gold { // A token read from a script file. We don't implement keywords here; // all keywords are simply represented as a string. class Token { public: // Token classification. enum Classification { // Token is invalid. TOKEN_INVALID, // Token indicates end of input. TOKEN_EOF, // Token is a string of characters. TOKEN_STRING, // Token is a quoted string of characters. TOKEN_QUOTED_STRING, // Token is an operator. TOKEN_OPERATOR, // Token is a number (an integer). TOKEN_INTEGER }; // We need an empty constructor so that we can put this STL objects. Token() : classification_(TOKEN_INVALID), value_(NULL), value_length_(0), opcode_(0), lineno_(0), charpos_(0) { } // A general token with no value. Token(Classification classification, int lineno, int charpos) : classification_(classification), value_(NULL), value_length_(0), opcode_(0), lineno_(lineno), charpos_(charpos) { gold_assert(classification == TOKEN_INVALID || classification == TOKEN_EOF); } // A general token with a value. Token(Classification classification, const char* value, size_t length, int lineno, int charpos) : classification_(classification), value_(value), value_length_(length), opcode_(0), lineno_(lineno), charpos_(charpos) { gold_assert(classification != TOKEN_INVALID && classification != TOKEN_EOF); } // A token representing an operator. Token(int opcode, int lineno, int charpos) : classification_(TOKEN_OPERATOR), value_(NULL), value_length_(0), opcode_(opcode), lineno_(lineno), charpos_(charpos) { } // Return whether the token is invalid. bool is_invalid() const { return this->classification_ == TOKEN_INVALID; } // Return whether this is an EOF token. bool is_eof() const { return this->classification_ == TOKEN_EOF; } // Return the token classification. Classification classification() const { return this->classification_; } // Return the line number at which the token starts. int lineno() const { return this->lineno_; } // Return the character position at this the token starts. int charpos() const { return this->charpos_; } // Get the value of a token. const char* string_value(size_t* length) const { gold_assert(this->classification_ == TOKEN_STRING || this->classification_ == TOKEN_QUOTED_STRING); *length = this->value_length_; return this->value_; } int operator_value() const { gold_assert(this->classification_ == TOKEN_OPERATOR); return this->opcode_; } 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); } private: // The token classification. Classification classification_; // The token value, for TOKEN_STRING or TOKEN_QUOTED_STRING or // TOKEN_INTEGER. const char* value_; // The length of the token value. size_t value_length_; // The token value, for TOKEN_OPERATOR. int opcode_; // The line number where this token started (one based). int lineno_; // The character position within the line where this token started // (one based). int charpos_; }; // This class handles lexing a file into a sequence of tokens. class Lex { public: // We unfortunately have to support different lexing modes, because // when reading different parts of a linker script we need to parse // things differently. enum Mode { // Reading an ordinary linker script. LINKER_SCRIPT, // Reading an expression in a linker script. EXPRESSION, // Reading a version script. VERSION_SCRIPT, // Reading a --dynamic-list file. DYNAMIC_LIST }; Lex(const char* input_string, size_t input_length, int parsing_token) : input_string_(input_string), input_length_(input_length), current_(input_string), mode_(LINKER_SCRIPT), first_token_(parsing_token), token_(), lineno_(1), linestart_(input_string) { } // Read a file into a string. static void read_file(Input_file*, std::string*); // Return the next token. const Token* next_token(); // Return the current lexing mode. Lex::Mode mode() const { return this->mode_; } // Set the lexing mode. void set_mode(Mode mode) { this->mode_ = mode; } private: Lex(const Lex&); Lex& operator=(const Lex&); // Make a general token with no value at the current location. Token make_token(Token::Classification c, const char* start) const { return Token(c, this->lineno_, start - this->linestart_ + 1); } // Make a general token with a value at the current location. Token make_token(Token::Classification c, const char* v, size_t len, const char* start) const { return Token(c, v, len, this->lineno_, start - this->linestart_ + 1); } // Make an operator token at the current location. Token make_token(int opcode, const char* start) const { return Token(opcode, this->lineno_, start - this->linestart_ + 1); } // Make an invalid token at the current location. Token make_invalid_token(const char* start) { return this->make_token(Token::TOKEN_INVALID, start); } // Make an EOF token at the current location. Token make_eof_token(const char* start) { return this->make_token(Token::TOKEN_EOF, start); } // Return whether C can be the first character in a name. C2 is the // next character, since we sometimes need that. inline bool can_start_name(char c, char c2); // 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); // 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); // 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. static inline int three_char_operator(char c1, char c2, char c3); // If C1 C2 form a valid two character operator, return the opcode. // Otherwise return 0. static inline int two_char_operator(char c1, char c2); // If C1 is a valid one character operator, return the opcode. // Otherwise return 0. static inline int one_char_operator(char c1); // Read the next token. Token get_token(const char**); // Skip a C style /* */ comment. Return false if the comment did // not end. bool skip_c_comment(const char**); // Skip a line # comment. Return false if there was no newline. bool skip_line_comment(const char**); // Build a token CLASSIFICATION from all characters that match // CAN_CONTINUE_FN. The token starts at START. Start matching from // MATCH. Set *PP to the character following the token. inline Token gather_token(Token::Classification, const char* (Lex::*can_continue_fn)(const char*), const char* start, const char* match, const char** pp); // Build a token from a quoted string. Token gather_quoted_string(const char** pp); // The string we are tokenizing. const char* input_string_; // The length of the string. size_t input_length_; // The current offset into the string. const char* current_; // The current lexing mode. Mode mode_; // The code to use for the first token. This is set to 0 after it // is used. int first_token_; // The current token. Token token_; // The current line number. int lineno_; // The start of the current line in the string. const char* linestart_; }; // Read the whole file into memory. We don't expect linker scripts to // be large, so we just use a std::string as a buffer. We ignore the // data we've already read, so that we read aligned buffers. void Lex::read_file(Input_file* input_file, std::string* contents) { off_t filesize = input_file->file().filesize(); contents->clear(); contents->reserve(filesize); off_t off = 0; unsigned char buf[BUFSIZ]; while (off < filesize) { off_t get = BUFSIZ; if (get > filesize - off) get = filesize - off; input_file->file().read(off, get, buf); contents->append(reinterpret_cast<char*>(&buf[0]), get); off += get; } } // Return whether C can be the start of a name, if the next character // is C2. A name can being with a letter, underscore, period, or // dollar sign. Because a name can be a file name, we also permit // forward slash, backslash, and tilde. Tilde is the tricky case // here; GNU ld also uses it as a bitwise not operator. It is only // recognized as the operator if it is not immediately followed by // some character which can appear in a symbol. That is, when we // don't know that we are looking at an expression, "~0" is a file // name, and "~ 0" is an expression using bitwise not. We are // compatible. inline bool Lex::can_start_name(char c, char c2) { 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': case 'M': case 'N': case 'O': case 'Q': case 'P': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'q': case 'p': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case '_': case '.': case '$': return true; case '/': case '\\': return this->mode_ == LINKER_SCRIPT; case '~': return this->mode_ == LINKER_SCRIPT && can_continue_name(&c2); case '*': case '[': return (this->mode_ == VERSION_SCRIPT || this->mode_ == DYNAMIC_LIST || (this->mode_ == LINKER_SCRIPT && can_continue_name(&c2))); default: return false; } } // Return whether C can continue a name which has already started. // Subsequent characters in a name are the same as the leading // characters, plus digits and "=+-:[],?*". So in general the linker // script language requires spaces around operators, unless we know // that we are parsing an expression. inline const char* Lex::can_continue_name(const char* 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': case 'M': case 'N': case 'O': case 'Q': case 'P': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'q': case 'p': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case '_': case '.': case '$': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return c + 1; // TODO(csilvers): why not allow ~ in names for version-scripts? case '/': case '\\': case '~': case '=': case '+': case ',': if (this->mode_ == LINKER_SCRIPT) return c + 1; return NULL; case '[': case ']': case '*': case '?': case '-': if (this->mode_ == LINKER_SCRIPT || this->mode_ == VERSION_SCRIPT || this->mode_ == DYNAMIC_LIST) return c + 1; return NULL; // TODO(csilvers): why allow this? ^ is meaningless in version scripts. case '^': if (this->mode_ == VERSION_SCRIPT || this->mode_ == DYNAMIC_LIST) return c + 1; return NULL; case ':': if (this->mode_ == LINKER_SCRIPT) return c + 1; else if ((this->mode_ == VERSION_SCRIPT || this->mode_ == DYNAMIC_LIST) && (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 NULL; } } // 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. // Return whether C1 C2 C3 can start a hex number. 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 false; } // Return whether C can appear in a hex number. inline const char* Lex::can_continue_hex(const char* 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 c + 1; default: return NULL; } } // Return whether C can start a non-hex number. inline bool Lex::can_start_number(char c) { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return true; default: return false; } } // If C1 C2 C3 form a valid three character operator, return the // opcode (defined in the yyscript.h file generated from yyscript.y). // Otherwise return 0. inline int Lex::three_char_operator(char c1, char c2, char c3) { switch (c1) { case '<': if (c2 == '<' && c3 == '=') return LSHIFTEQ; break; case '>': if (c2 == '>' && c3 == '=') return RSHIFTEQ; break; default: break; } return 0; } // If C1 C2 form a valid two character operator, return the opcode // (defined in the yyscript.h file generated from yyscript.y). // Otherwise return 0. inline int Lex::two_char_operator(char c1, char c2) { switch (c1) { case '=': if (c2 == '=') return EQ; break; case '!': if (c2 == '=') return NE; break; case '+': if (c2 == '=') return PLUSEQ; break; case '-': if (c2 == '=') return MINUSEQ; break; case '*': if (c2 == '=') return MULTEQ; break; case '/': if (c2 == '=') return DIVEQ; break; case '|': if (c2 == '=') return OREQ; if (c2 == '|') return OROR; break; case '&': if (c2 == '=') return ANDEQ; if (c2 == '&') return ANDAND; break; case '>': if (c2 == '=') return GE; if (c2 == '>') return RSHIFT; break; case '<': if (c2 == '=') return LE; if (c2 == '<') return LSHIFT; break; default: break; } return 0; } // If C1 is a valid operator, return the opcode. Otherwise return 0. inline int Lex::one_char_operator(char c1) { switch (c1) { case '+': case '-': case '*': case '/': case '%': case '!': case '&': case '|': case '^': case '~': case '<': case '>': case '=': case '?': case ',': case '(': case ')': case '{': case '}': case '[': case ']': case ':': case ';': return c1; default: return 0; } } // Skip a C style comment. *PP points to just after the "/*". Return // false if the comment did not end. bool Lex::skip_c_comment(const char** pp) { const char* p = *pp; while (p[0] != '*' || p[1] != '/') { if (*p == '\0') { *pp = p; return false; } if (*p == '\n') { ++this->lineno_; this->linestart_ = p + 1; } ++p; } *pp = p + 2; return true; } // Skip a line # comment. Return false if there was no newline. bool Lex::skip_line_comment(const char** pp) { const char* p = *pp; size_t skip = strcspn(p, "\n"); if (p[skip] == '\0') { *pp = p + skip; return false; } p += skip + 1; ++this->lineno_; this->linestart_ = p; *pp = p; return true; } // Build a token CLASSIFICATION from all characters that match // CAN_CONTINUE_FN. Update *PP. inline Token Lex::gather_token(Token::Classification classification, const char* (Lex::*can_continue_fn)(const char*), const char* start, const char* match, const char** pp) { 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); } // Build a token from a quoted string. Token Lex::gather_quoted_string(const char** pp) { const char* start = *pp; const char* p = start; ++p; size_t skip = strcspn(p, "\"\n"); if (p[skip] != '"') return this->make_invalid_token(start); *pp = p + skip + 1; return this->make_token(Token::TOKEN_QUOTED_STRING, p, skip, start); } // Return the next token at *PP. Update *PP. General guideline: we // require linker scripts to be simple ASCII. No unicode linker // scripts. In particular we can assume that any '\0' is the end of // the input. Token Lex::get_token(const char** pp) { const char* p = *pp; while (true) { if (*p == '\0') { *pp = p; return this->make_eof_token(p); } // Skip whitespace quickly. while (*p == ' ' || *p == '\t' || *p == '\r') ++p; if (*p == '\n') { ++p; ++this->lineno_; this->linestart_ = p; continue; } // Skip C style comments. if (p[0] == '/' && p[1] == '*') { int lineno = this->lineno_; int charpos = p - this->linestart_ + 1; *pp = p + 2; if (!this->skip_c_comment(pp)) return Token(Token::TOKEN_INVALID, lineno, charpos); p = *pp; continue; } // Skip line comments. if (*p == '#') { *pp = p + 1; if (!this->skip_line_comment(pp)) return this->make_eof_token(p); p = *pp; continue; } // Check for a name. if (this->can_start_name(p[0], p[1])) return this->gather_token(Token::TOKEN_STRING, &Lex::can_continue_name, p, p + 1, pp); // We accept any arbitrary name in double quotes, as long as it // does not cross a line boundary. if (*p == '"') { *pp = p; return this->gather_quoted_string(pp); } // Check for a number. if (this->can_start_hex(p[0], p[1], p[2])) return this->gather_token(Token::TOKEN_INTEGER, &Lex::can_continue_hex, p, p + 3, pp); if (Lex::can_start_number(p[0])) 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]); if (opcode != 0) { *pp = p + 3; return this->make_token(opcode, p); } opcode = Lex::two_char_operator(p[0], p[1]); if (opcode != 0) { *pp = p + 2; return this->make_token(opcode, p); } opcode = Lex::one_char_operator(p[0]); if (opcode != 0) { *pp = p + 1; return this->make_token(opcode, p); } return this->make_token(Token::TOKEN_INVALID, p); } } // Return the next token. const Token* Lex::next_token() { // The first token is special. if (this->first_token_ != 0) { this->token_ = Token(this->first_token_, 0, 0); this->first_token_ = 0; return &this->token_; } this->token_ = this->get_token(&this->current_); // Don't let an early null byte fool us into thinking that we've // reached the end of the file. if (this->token_.is_eof() && (static_cast<size_t>(this->current_ - this->input_string_) < this->input_length_)) this->token_ = this->make_invalid_token(this->current_); return &this->token_; } // class Symbol_assignment. // Add the symbol to the symbol table. This makes sure the symbol is // there and defined. The actual value is stored later. We can't // determine the actual value at this point, because we can't // necessarily evaluate the expression until all ordinary symbols have // been finalized. // The GNU linker lets symbol assignments in the linker script // silently override defined symbols in object files. We are // compatible. FIXME: Should we issue a warning? void Symbol_assignment::add_to_table(Symbol_table* symtab) { elfcpp::STV vis = this->hidden_ ? elfcpp::STV_HIDDEN : elfcpp::STV_DEFAULT; this->sym_ = symtab->define_as_constant(this->name_.c_str(), NULL, // version (this->is_defsym_ ? Symbol_table::DEFSYM : Symbol_table::SCRIPT), 0, // value 0, // size elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL, vis, 0, // nonvis this->provide_, true); // force_override } // Finalize a symbol value. void Symbol_assignment::finalize(Symbol_table* symtab, const Layout* layout) { this->finalize_maybe_dot(symtab, layout, false, 0, NULL); } // Finalize a symbol value which can refer to the dot symbol. void Symbol_assignment::finalize_with_dot(Symbol_table* symtab, const Layout* layout, uint64_t dot_value, Output_section* dot_section) { this->finalize_maybe_dot(symtab, layout, true, dot_value, dot_section); } // Finalize a symbol value, internal version. void Symbol_assignment::finalize_maybe_dot(Symbol_table* symtab, const Layout* layout, bool is_dot_available, uint64_t dot_value, Output_section* dot_section) { // If we were only supposed to provide this symbol, the sym_ field // will be NULL if the symbol was not referenced. if (this->sym_ == NULL) { gold_assert(this->provide_); return; } if (parameters->target().get_size() == 32) { #if defined(HAVE_TARGET_32_LITTLE) || defined(HAVE_TARGET_32_BIG) this->sized_finalize<32>(symtab, layout, is_dot_available, dot_value, dot_section); #else gold_unreachable(); #endif } else if (parameters->target().get_size() == 64) { #if defined(HAVE_TARGET_64_LITTLE) || defined(HAVE_TARGET_64_BIG) this->sized_finalize<64>(symtab, layout, is_dot_available, dot_value, dot_section); #else gold_unreachable(); #endif } else gold_unreachable(); } template<int size> void Symbol_assignment::sized_finalize(Symbol_table* symtab, const Layout* layout, bool is_dot_available, uint64_t dot_value, Output_section* dot_section) { Output_section* section; uint64_t final_val = this->val_->eval_maybe_dot(symtab, layout, true, is_dot_available, dot_value, dot_section, §ion, NULL); Sized_symbol<size>* ssym = symtab->get_sized_symbol<size>(this->sym_); ssym->set_value(final_val); if (section != NULL) ssym->set_output_section(section); } // Set the symbol value if the expression yields an absolute value. void Symbol_assignment::set_if_absolute(Symbol_table* symtab, const Layout* layout, bool is_dot_available, uint64_t dot_value) { if (this->sym_ == NULL) return; Output_section* val_section; uint64_t val = this->val_->eval_maybe_dot(symtab, layout, false, is_dot_available, dot_value, NULL, &val_section, NULL); if (val_section != NULL) return; if (parameters->target().get_size() == 32) { #if defined(HAVE_TARGET_32_LITTLE) || defined(HAVE_TARGET_32_BIG) Sized_symbol<32>* ssym = symtab->get_sized_symbol<32>(this->sym_); ssym->set_value(val); #else gold_unreachable(); #endif } else if (parameters->target().get_size() == 64) { #if defined(HAVE_TARGET_64_LITTLE) || defined(HAVE_TARGET_64_BIG) Sized_symbol<64>* ssym = symtab->get_sized_symbol<64>(this->sym_); ssym->set_value(val); #else gold_unreachable(); #endif } else gold_unreachable(); } // Print for debugging. void Symbol_assignment::print(FILE* f) const { if (this->provide_ && this->hidden_) fprintf(f, "PROVIDE_HIDDEN("); else if (this->provide_) fprintf(f, "PROVIDE("); else if (this->hidden_) gold_unreachable(); fprintf(f, "%s = ", this->name_.c_str()); this->val_->print(f); if (this->provide_ || this->hidden_) fprintf(f, ")"); fprintf(f, "\n"); } // Class Script_assertion. // Check the assertion. void Script_assertion::check(const Symbol_table* symtab, const Layout* layout) { if (!this->check_->eval(symtab, layout, true)) gold_error("%s", this->message_.c_str()); } // Print for debugging. void Script_assertion::print(FILE* f) const { fprintf(f, "ASSERT("); this->check_->print(f); fprintf(f, ", \"%s\")\n", this->message_.c_str()); } // Class Script_options. Script_options::Script_options() : 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; } // Add a symbol to be defined. void Script_options::add_symbol_assignment(const char* name, size_t length, bool is_defsym, Expression* value, bool provide, bool hidden) { if (length != 1 || name[0] != '.') { if (this->script_sections_.in_sections_clause()) { gold_assert(!is_defsym); this->script_sections_.add_symbol_assignment(name, length, value, provide, hidden); } else { Symbol_assignment* p = new Symbol_assignment(name, length, is_defsym, 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 { if (provide || hidden) gold_error(_("invalid use of PROVIDE for dot symbol")); // The GNU linker permits assignments to dot outside of SECTIONS // clauses and treats them as occurring inside, so we don't // check in_sections_clause here. this->script_sections_.add_dot_assignment(value); } } // 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 Script_options::add_assertion(Expression* check, const char* message, size_t messagelen) { if (this->script_sections_.in_sections_clause()) this->script_sections_.add_assertion(check, message, messagelen); else { Script_assertion* p = new Script_assertion(check, message, messagelen); this->assertions_.push_back(p); } } // Create sections required by any linker scripts. void Script_options::create_script_sections(Layout* layout) { if (this->saw_sections_clause()) this->script_sections_.create_sections(layout); } // Add any symbols we are defining to the symbol table. void Script_options::add_symbols_to_table(Symbol_table* symtab) { for (Symbol_assignments::iterator p = this->symbol_assignments_.begin(); p != this->symbol_assignments_.end(); ++p) (*p)->add_to_table(symtab); this->script_sections_.add_symbols_to_table(symtab); } // Finalize symbol values. Also check assertions. void Script_options::finalize_symbols(Symbol_table* symtab, const Layout* layout) { // We finalize the symbols defined in SECTIONS first, because they // are the ones which may have changed. This way if symbol outside // SECTIONS are defined in terms of symbols inside SECTIONS, they // will get the right value. this->script_sections_.finalize_symbols(symtab, layout); for (Symbol_assignments::iterator p = this->symbol_assignments_.begin(); p != this->symbol_assignments_.end(); ++p) (*p)->finalize(symtab, layout); for (Assertions::iterator p = this->assertions_.begin(); p != this->assertions_.end(); ++p) (*p)->check(symtab, layout); } // Set section addresses. We set all the symbols which have absolute // values. Then we let the SECTIONS clause do its thing. This // returns the segment which holds the file header and segment // headers, if any. Output_segment* Script_options::set_section_addresses(Symbol_table* symtab, Layout* layout) { for (Symbol_assignments::iterator p = this->symbol_assignments_.begin(); p != this->symbol_assignments_.end(); ++p) (*p)->set_if_absolute(symtab, layout, false, 0); return this->script_sections_.set_section_addresses(symtab, layout); } // This class holds data passed through the parser to the lexer and to // the parser support functions. This avoids global variables. We // can't use global variables because we need not be called by a // singleton thread. class Parser_closure { public: Parser_closure(const char* filename, const Position_dependent_options& posdep_options, bool parsing_defsym, bool in_group, bool is_in_sysroot, Command_line* command_line, Script_options* script_options, Lex* lex, 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), skip_on_incompatible_target_(skip_on_incompatible_target), 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), 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->lex_mode_stack_.push_back(lex->mode()); } // Return the file name. const char* filename() const { return this->filename_; } // Return the position dependent options. The caller may modify // this. Position_dependent_options& position_dependent_options() { return this->posdep_options_; } // Whether we are parsing a --defsym. bool parsing_defsym() const { return this->parsing_defsym_; } // Return whether this script is being run in a group. bool in_group() const { return this->in_group_; } // Return whether this script was found using a directory in the // sysroot. bool is_in_sysroot() const { return this->is_in_sysroot_; } // Whether to skip to the next file with the same name if we find an // incompatible target in an OUTPUT_FORMAT statement. bool skip_on_incompatible_target() const { return this->skip_on_incompatible_target_; } // Stop skipping to the next file on an incompatible target. This // is called when we make some unrevocable change to the data // structures. void clear_skip_on_incompatible_target() { this->skip_on_incompatible_target_ = false; } // Whether we found an incompatible target in an OUTPUT_FORMAT // statement. bool found_incompatible_target() const { return this->found_incompatible_target_; } // Note that we found an incompatible target. void set_found_incompatible_target() { this->found_incompatible_target_ = true; } // Returns the Command_line structure passed in at constructor time. // This value may be NULL. The caller may modify this, which modifies // the passed-in Command_line object (not a copy). Command_line* command_line() { return this->command_line_; } // Return the options which may be set by a script. Script_options* 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() { const Token* token = this->lex_->next_token(); this->lineno_ = token->lineno(); this->charpos_ = token->charpos(); return token; } // Set a new lexer mode, pushing the current one. void push_lex_mode(Lex::Mode mode) { this->lex_mode_stack_.push_back(this->lex_->mode()); this->lex_->set_mode(mode); } // Pop the lexer mode. void pop_lex_mode() { gold_assert(!this->lex_mode_stack_.empty()); this->lex_->set_mode(this->lex_mode_stack_.back()); 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 { return this->lineno_; } // Return the character position in the line of the last token. int charpos() const { return this->charpos_; } // Return the list of input files, creating it if necessary. This // is a space leak--we never free the INPUTS_ pointer. Input_arguments* inputs() { if (this->inputs_ == NULL) this->inputs_ = new Input_arguments(); return this->inputs_; } // Return whether we saw any input files. bool 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. Version_script_info::Language get_current_language() const { return this->language_stack_.back(); } // Push a language onto the stack when entering an extern block. void push_language(Version_script_info::Language 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(); } // 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_; // The position dependent options. Position_dependent_options posdep_options_; // True if we are parsing a --defsym. bool parsing_defsym_; // Whether we are currently in a --start-group/--end-group. bool in_group_; // Whether the script was found in a sysrooted directory. bool is_in_sysroot_; // If this is true, then if we find an OUTPUT_FORMAT with an // incompatible target, then we tell the parser to abort so that we // can search for the next file with the same name. bool skip_on_incompatible_target_; // True if we found an OUTPUT_FORMAT with an incompatible target. bool found_incompatible_target_; // May be NULL if the user chooses not to pass one in. 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. int lineno_; // The column number of the last token returned by next_token. 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<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 // as a script. Return true if the file was handled. bool read_input_script(Workqueue* workqueue, Symbol_table* symtab, Layout* layout, Dirsearch* dirsearch, int dirindex, Input_objects* input_objects, Mapfile* mapfile, Input_group* input_group, const Input_argument* input_argument, Input_file* input_file, Task_token* next_blocker, bool* used_next_blocker) { *used_next_blocker = false; std::string input_string; Lex::read_file(input_file, &input_string); 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, input_group != NULL, input_file->is_in_sysroot(), NULL, layout->script_options(), &lex, input_file->will_search_for(), script_info); bool old_saw_sections_clause = layout->script_options()->saw_sections_clause(); if (yyparse(&closure) != 0) { if (closure.found_incompatible_target()) { Read_symbols::incompatible_warning(input_argument, input_file); Read_symbols::requeue(workqueue, input_objects, symtab, layout, dirsearch, dirindex, mapfile, input_argument, input_group, next_blocker); return true; } return false; } if (!old_saw_sections_clause && layout->script_options()->saw_sections_clause() && layout->have_added_input_section()) gold_error(_("%s: SECTIONS seen after other input files; try -T/--script"), input_file->filename().c_str()); if (!closure.saw_inputs()) return true; Task_token* this_blocker = NULL; for (Input_arguments::const_iterator p = closure.inputs()->begin(); p != closure.inputs()->end(); ++p) { Task_token* nb; if (p + 1 == closure.inputs()->end()) nb = next_blocker; else { nb = new Task_token(true); nb->add_blocker(); } workqueue->queue_soon(new Read_symbols(input_objects, symtab, layout, dirsearch, 0, mapfile, &*p, input_group, NULL, this_blocker, nb)); this_blocker = nb; } *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. 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; // The file locking code wants to record a Task, but we haven't // started the workqueue yet. This is only for debugging purposes, // so we invent a fake value. const Task* task = reinterpret_cast<const Task*>(-1); // We don't want this file to be opened in binary mode. 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_FILE_TYPE_FILE, "", false, posdep); Input_file input_file(&input_argument); int dummy = 0; if (!input_file.open(dirsearch, task, &dummy)) return false; std::string input_string; Lex::read_file(&input_file, &input_string); Lex lex(input_string.c_str(), input_string.length(), first_token); lex.set_mode(lex_mode); Parser_closure closure(filename, cmdline->position_dependent_options(), first_token == Lex::DYNAMIC_LIST, false, input_file.is_in_sysroot(), cmdline, script_options, &lex, false, NULL); if (yyparse(&closure) != 0) { input_file.file().unlock(task); return false; } input_file.file().unlock(task); gold_assert(!closure.saw_inputs()); 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, &cmdline->script_options(), PARSING_LINKER_SCRIPT, Lex::LINKER_SCRIPT); } // FILENAME 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, &cmdline->script_options(), PARSING_VERSION_SCRIPT, Lex::VERSION_SCRIPT); } // FILENAME was found as an argument to --dynamic-list. Read it as a // list of symbols, and store its contents in DYNAMIC_LIST. bool read_dynamic_list(const char* filename, Command_line* cmdline, Script_options* dynamic_list) { return read_script_file(filename, cmdline, dynamic_list, PARSING_DYNAMIC_LIST, Lex::DYNAMIC_LIST); } // Implement the --defsym option on the command line. Return true if // all is well. bool Script_options::define_symbol(const char* definition) { Lex lex(definition, strlen(definition), PARSING_DEFSYM); lex.set_mode(Lex::EXPRESSION); // Dummy value. Position_dependent_options posdep_options; Parser_closure closure("command line", posdep_options, true, false, false, NULL, this, &lex, false, NULL); if (yyparse(&closure) != 0) return false; gold_assert(!closure.saw_inputs()); return true; } // Print the script to F for debugging. void Script_options::print(FILE* f) const { fprintf(f, "%s: Dumping linker script\n", program_name); if (!this->entry_.empty()) fprintf(f, "ENTRY(%s)\n", this->entry_.c_str()); for (Symbol_assignments::const_iterator p = this->symbol_assignments_.begin(); p != this->symbol_assignments_.end(); ++p) (*p)->print(f); for (Assertions::const_iterator p = this->assertions_.begin(); p != this->assertions_.end(); ++p) (*p)->print(f); this->script_sections_.print(f); this->version_script_info_.print(f); } // Manage mapping from keywords to the codes expected by the bison // parser. We construct one global object for each lex mode with // keywords. class Keyword_to_parsecode { public: // The structure which maps keywords to parsecodes. struct Keyword_parsecode { // Keyword. const char* keyword; // Corresponding 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. int keyword_to_parsecode(const char* keyword, size_t len) const; private: 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. static const Keyword_to_parsecode::Keyword_parsecode script_keyword_parsecodes[] = { { "ABSOLUTE", ABSOLUTE }, { "ADDR", ADDR }, { "ALIGN", ALIGN_K }, { "ALIGNOF", ALIGNOF }, { "ASSERT", ASSERT_K }, { "AS_NEEDED", AS_NEEDED }, { "AT", AT }, { "BIND", BIND }, { "BLOCK", BLOCK }, { "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 }, { "FILL", FILL }, { "FLOAT", FLOAT }, { "FORCE_COMMON_ALLOCATION", FORCE_COMMON_ALLOCATION }, { "GROUP", GROUP }, { "HLL", HLL }, { "INCLUDE", INCLUDE }, { "INFO", INFO }, { "INHIBIT_COMMON_ALLOCATION", INHIBIT_COMMON_ALLOCATION }, { "INPUT", INPUT }, { "KEEP", KEEP }, { "LENGTH", LENGTH }, { "LOADADDR", LOADADDR }, { "LONG", LONG }, { "MAP", MAP }, { "MAX", MAX_K }, { "MEMORY", MEMORY }, { "MIN", MIN_K }, { "NEXT", NEXT }, { "NOCROSSREFS", NOCROSSREFS }, { "NOFLOAT", NOFLOAT }, { "NOLOAD", NOLOAD }, { "ONLY_IF_RO", ONLY_IF_RO }, { "ONLY_IF_RW", ONLY_IF_RW }, { "OPTION", OPTION }, { "ORIGIN", ORIGIN }, { "OUTPUT", OUTPUT }, { "OUTPUT_ARCH", OUTPUT_ARCH }, { "OUTPUT_FORMAT", OUTPUT_FORMAT }, { "OVERLAY", OVERLAY }, { "PHDRS", PHDRS }, { "PROVIDE", PROVIDE }, { "PROVIDE_HIDDEN", PROVIDE_HIDDEN }, { "QUAD", QUAD }, { "SEARCH_DIR", SEARCH_DIR }, { "SECTIONS", SECTIONS }, { "SEGMENT_START", SEGMENT_START }, { "SHORT", SHORT }, { "SIZEOF", SIZEOF }, { "SIZEOF_HEADERS", SIZEOF_HEADERS }, { "SORT", SORT_BY_NAME }, { "SORT_BY_ALIGNMENT", SORT_BY_ALIGNMENT }, { "SORT_BY_NAME", SORT_BY_NAME }, { "SPECIAL", SPECIAL }, { "SQUAD", SQUAD }, { "STARTUP", STARTUP }, { "SUBALIGN", SUBALIGN }, { "SYSLIB", SYSLIB }, { "TARGET", TARGET_K }, { "TRUNCATE", TRUNCATE }, { "VERSION", VERSIONK }, { "global", GLOBAL }, { "l", LENGTH }, { "len", LENGTH }, { "local", LOCAL }, { "o", ORIGIN }, { "org", ORIGIN }, { "sizeof_headers", SIZEOF_HEADERS }, }; 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]))); static const Keyword_to_parsecode::Keyword_parsecode dynamic_list_keyword_parsecodes[] = { { "extern", EXTERN }, }; static const Keyword_to_parsecode dynamic_list_keywords(&dynamic_list_keyword_parsecodes[0], (sizeof(dynamic_list_keyword_parsecodes) / sizeof(dynamic_list_keyword_parsecodes[0]))); // Comparison function passed to bsearch. extern "C" { struct Ktt_key { const char* str; size_t len; }; static int ktt_compare(const void* keyv, const void* kttv) { const Ktt_key* key = static_cast<const Ktt_key*>(keyv); const Keyword_to_parsecode::Keyword_parsecode* ktt = static_cast<const Keyword_to_parsecode::Keyword_parsecode*>(kttv); int i = strncmp(key->str, ktt->keyword, key->len); if (i != 0) return i; if (ktt->keyword[key->len] != '\0') return -1; return 0; } } // End extern "C". int 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, 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); return ktt->parsecode; } // 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++". struct Version_expression { Version_expression(const std::string& a_pattern, Version_script_info::Language a_language, bool a_exact_match) : pattern(a_pattern), language(a_language), exact_match(a_exact_match), was_matched_by_symbol(false) { } std::string pattern; Version_script_info::Language language; // If false, we use glob() to match pattern. If true, we use strcmp(). bool exact_match; // True if --no-undefined-version is in effect and we found this // version in get_symbol_version. We use mutable because this // struct is generally not modifiable after it has been created. mutable bool was_matched_by_symbol; }; // 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; }; // 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_(), globs_(), default_version_(NULL), default_is_global_(false), is_finalized_(false) { for (int i = 0; i < LANGUAGE_COUNT; ++i) this->exact_[i] = NULL; } Version_script_info::~Version_script_info() { } // Forget all the known version script information. void Version_script_info::clear() { for (size_t k = 0; k < this->dependency_lists_.size(); ++k) delete this->dependency_lists_[k]; this->dependency_lists_.clear(); for (size_t k = 0; k < this->version_trees_.size(); ++k) delete this->version_trees_[k]; this->version_trees_.clear(); for (size_t k = 0; k < this->expression_lists_.size(); ++k) delete this->expression_lists_[k]; this->expression_lists_.clear(); } // Finalize the version script information. void Version_script_info::finalize() { if (!this->is_finalized_) { this->build_lookup_tables(); this->is_finalized_ = true; } } // Return all the versions. std::vector<std::string> Version_script_info::get_versions() const { std::vector<std::string> ret; for (size_t j = 0; j < this->version_trees_.size(); ++j) if (!this->version_trees_[j]->tag.empty()) ret.push_back(this->version_trees_[j]->tag); return ret; } // Return the dependencies of VERSION. std::vector<std::string> Version_script_info::get_dependencies(const char* version) const { std::vector<std::string> ret; for (size_t j = 0; j < this->version_trees_.size(); ++j) if (this->version_trees_[j]->tag == version) { const struct Version_dependency_list* deps = this->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; } // 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 Version_script_info::build_lookup_tables() { size_t size = this->version_trees_.size(); for (size_t j = 0; j < size; ++j) { const Version_tree* v = this->version_trees_[j]; 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, bool is_global) { if (explist == NULL) return; size_t size = explist->expressions.size(); for (size_t i = 0; i < size; ++i) { const Version_expression& exp(explist->expressions[i]); if (exp.pattern.length() == 1 && exp.pattern[0] == '*') { if (this->default_version_ != NULL && this->default_version_->tag != v->tag) gold_warning(_("wildcard match appears in both version '%s' " "and '%s' in script"), this->default_version_->tag.c_str(), v->tag.c_str()); else if (this->default_version_ != NULL && this->default_is_global_ != is_global) gold_error(_("wildcard match appears as both global and local " "in version '%s' in script"), v->tag.c_str()); this->default_version_ = v; this->default_is_global_ = is_global; continue; } std::string pattern = exp.pattern; if (!exp.exact_match) { if (this->unquote(&pattern)) { this->globs_.push_back(Glob(&exp, v, is_global)); continue; } } if (this->exact_[exp.language] == NULL) this->exact_[exp.language] = new Exact(); this->add_exact_match(pattern, v, is_global, &exp, this->exact_[exp.language]); } } // Return the name to match given a name, a language code, and two // lazy demanglers. const char* Version_script_info::get_name_to_match(const char* name, int language, Lazy_demangler* cpp_demangler, Lazy_demangler* java_demangler) const { switch (language) { case LANGUAGE_C: return name; case LANGUAGE_CXX: return cpp_demangler->get(); case LANGUAGE_JAVA: return java_demangler->get(); default: gold_unreachable(); } } // Look up SYMBOL_NAME in the list of versions. Return true if the // symbol is found, false if not. If the symbol is found, then if // PVERSION is not NULL, set *PVERSION to the version tag, and if // P_IS_GLOBAL is not NULL, set *P_IS_GLOBAL according to whether the // symbol is global or not. bool Version_script_info::get_symbol_version(const char* symbol_name, std::string* pversion, bool* p_is_global) const { Lazy_demangler cpp_demangled_name(symbol_name, DMGL_ANSI | DMGL_PARAMS); Lazy_demangler java_demangled_name(symbol_name, DMGL_ANSI | DMGL_PARAMS | DMGL_JAVA); gold_assert(this->is_finalized_); for (int i = 0; i < LANGUAGE_COUNT; ++i) { Exact* exact = this->exact_[i]; if (exact == NULL) continue; const char* name_to_match = this->get_name_to_match(symbol_name, i, &cpp_demangled_name, &java_demangled_name); if (name_to_match == NULL) { // If the name can not be demangled, the GNU linker goes // ahead and tries to match it anyhow. That does not // make sense to me and I have not implemented it. continue; } Exact::const_iterator pe = exact->find(name_to_match); if (pe != exact->end()) { const Version_tree_match& vtm(pe->second); if (vtm.ambiguous != NULL) gold_warning(_("using '%s' as version for '%s' which is also " "named in version '%s' in script"), vtm.real->tag.c_str(), name_to_match, vtm.ambiguous->tag.c_str()); if (pversion != NULL) *pversion = vtm.real->tag; if (p_is_global != NULL) *p_is_global = vtm.is_global; // If we are using --no-undefined-version, and this is a // global symbol, we have to record that we have found this // symbol, so that we don't warn about it. We have to do // this now, because otherwise we have no way to get from a // non-C language back to the demangled name that we // matched. if (p_is_global != NULL && vtm.is_global) vtm.expression->was_matched_by_symbol = true; return true; } } // Look through the glob patterns in reverse order. for (Globs::const_reverse_iterator p = this->globs_.rbegin(); p != this->globs_.rend(); ++p) { int language = p->expression->language; const char* name_to_match = this->get_name_to_match(symbol_name, language, &cpp_demangled_name, &java_demangled_name); if (name_to_match == NULL) continue; if (fnmatch(p->expression->pattern.c_str(), name_to_match, FNM_NOESCAPE) == 0) { if (pversion != NULL) *pversion = p->version->tag; if (p_is_global != NULL) *p_is_global = p->is_global; return true; } } // Finally, there may be a wildcard. if (this->default_version_ != NULL) { if (pversion != NULL) *pversion = this->default_version_->tag; if (p_is_global != NULL) *p_is_global = this->default_is_global_; return true; } return false; } // Give an error if any exact symbol names (not wildcards) appear in a // version script, but there is no such symbol. void Version_script_info::check_unmatched_names(const Symbol_table* symtab) const { for (size_t i = 0; i < this->version_trees_.size(); ++i) { const Version_tree* vt = this->version_trees_[i]; if (vt->global == NULL) continue; for (size_t j = 0; j < vt->global->expressions.size(); ++j) { const Version_expression& expression(vt->global->expressions[j]); // Ignore cases where we used the version because we saw a // symbol that we looked up. Note that // WAS_MATCHED_BY_SYMBOL will be true even if the symbol was // not a definition. That's OK as in that case we most // likely gave an undefined symbol error anyhow. if (expression.was_matched_by_symbol) continue; // Just ignore names which are in languages other than C. // We have no way to look them up in the symbol table. if (expression.language != LANGUAGE_C) continue; // Remove backslash quoting, and ignore wildcard patterns. std::string pattern = expression.pattern; if (!expression.exact_match) { 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()); } } } 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(); } // Print for debugging. void Version_script_info::print(FILE* f) const { if (this->empty()) return; fprintf(f, "VERSION {"); for (size_t i = 0; i < this->version_trees_.size(); ++i) { const Version_tree* vt = this->version_trees_[i]; if (vt->tag.empty()) fprintf(f, " {\n"); else fprintf(f, " %s {\n", vt->tag.c_str()); if (vt->global != NULL) { fprintf(f, " global :\n"); this->print_expression_list(f, vt->global); } if (vt->local != NULL) { fprintf(f, " local :\n"); this->print_expression_list(f, vt->local); } fprintf(f, " }"); if (vt->dependencies != NULL) { const Version_dependency_list* deps = vt->dependencies; for (size_t j = 0; j < deps->dependencies.size(); ++j) { if (j < deps->dependencies.size() - 1) fprintf(f, "\n"); fprintf(f, " %s", deps->dependencies[j].c_str()); } } fprintf(f, ";\n"); } fprintf(f, "}\n"); } void Version_script_info::print_expression_list( FILE* f, const Version_expression_list* vel) const { Version_script_info::Language current_language = LANGUAGE_C; for (size_t i = 0; i < vel->expressions.size(); ++i) { const Version_expression& ve(vel->expressions[i]); if (ve.language != current_language) { if (current_language != LANGUAGE_C) fprintf(f, " }\n"); switch (ve.language) { case LANGUAGE_C: break; case LANGUAGE_CXX: fprintf(f, " extern \"C++\" {\n"); break; case LANGUAGE_JAVA: fprintf(f, " extern \"Java\" {\n"); break; default: gold_unreachable(); } current_language = ve.language; } fprintf(f, " "); if (current_language != LANGUAGE_C) fprintf(f, " "); if (ve.exact_match) fprintf(f, "\""); fprintf(f, "%s", ve.pattern.c_str()); if (ve.exact_match) fprintf(f, "\""); fprintf(f, "\n"); } if (current_language != LANGUAGE_C) fprintf(f, " }\n"); } } // End namespace gold. // The remaining functions are extern "C", so it's clearer to not put // them in namespace gold. using namespace gold; // This function is called by the bison parser to return the next // token. extern "C" int yylex(YYSTYPE* lvalp, void* closurev) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); const Token* token = closure->next_token(); switch (token->classification()) { default: gold_unreachable(); case Token::TOKEN_INVALID: yyerror(closurev, "invalid character"); return 0; case Token::TOKEN_EOF: return 0; case Token::TOKEN_STRING: { // This is either a keyword or a STRING. size_t len; const char* str = token->string_value(&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; case Lex::DYNAMIC_LIST: parsecode = dynamic_list_keywords.keyword_to_parsecode(str, len); break; default: break; } if (parsecode != 0) return parsecode; lvalp->string.value = str; lvalp->string.length = len; return STRING; } case Token::TOKEN_QUOTED_STRING: lvalp->string.value = token->string_value(&lvalp->string.length); return QUOTED_STRING; case Token::TOKEN_OPERATOR: return token->operator_value(); case Token::TOKEN_INTEGER: lvalp->integer = token->integer_value(); return INTEGER; } } // This function is called by the bison parser to report an error. extern "C" void yyerror(void* closurev, const char* message) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); gold_error(_("%s:%d:%d: %s"), closure->filename(), closure->lineno(), closure->charpos(), message); } // Called by the bison parser to add an external symbol to the link. extern "C" void script_add_extern(void* closurev, const char* name, size_t length) { 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. extern "C" void script_add_file(void* closurev, const char* name, size_t length) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); // If this is an absolute path, and we found the script in the // sysroot, then we want to prepend the sysroot to the file name. // For example, this is how we handle a cross link to the x86_64 // libc.so, which refers to /lib/libc.so.6. std::string name_string(name, length); const char* extra_search_path = "."; std::string script_directory; if (IS_ABSOLUTE_PATH(name_string.c_str())) { if (closure->is_in_sysroot()) { const std::string& sysroot(parameters->options().sysroot()); gold_assert(!sysroot.empty()); name_string = sysroot + name_string; } } else { // In addition to checking the normal library search path, we // also want to check in the script-directory. const char* slash = strrchr(closure->filename(), '/'); if (slash != NULL) { script_directory.assign(closure->filename(), slash - closure->filename() + 1); extra_search_path = script_directory.c_str(); } } Input_file_argument file(name_string.c_str(), Input_file_argument::INPUT_FILE_TYPE_FILE, extra_search_path, 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 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 // a group, that means that this script was invoked within a // --start-group --end-group sequence on the command line, or that // this script was found in a GROUP of another script. In that case, // we simply continue the existing group, rather than starting a new // one. It is possible to construct a case in which this will do // something other than what would happen if we did a recursive group, // but it's hard to imagine why the different behaviour would be // useful for a real program. Avoiding recursive groups is simpler // and more efficient. extern "C" void script_start_group(void* closurev) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); if (!closure->in_group()) closure->inputs()->start_group(); } // Called by the bison parser at the end of a group. extern "C" void script_end_group(void* closurev) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); if (!closure->in_group()) closure->inputs()->end_group(); } // Called by the bison parser to start an AS_NEEDED list. extern "C" void script_start_as_needed(void* closurev) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); closure->position_dependent_options().set_as_needed(true); } // Called by the bison parser at the end of an AS_NEEDED list. extern "C" void script_end_as_needed(void* closurev) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); closure->position_dependent_options().set_as_needed(false); } // Called by the bison parser to set the entry symbol. extern "C" void script_set_entry(void* closurev, const char* entry, size_t length) { // We'll parse this exactly the same as --entry=ENTRY on the commandline // TODO(csilvers): FIXME -- call set_entry directly. std::string arg("--entry="); arg.append(entry, length); script_parse_option(closurev, arg.c_str(), arg.size()); } // Called by the bison parser to set whether to define common symbols. extern "C" void script_set_common_allocation(void* closurev, int set) { const char* arg = set != 0 ? "--define-common" : "--no-define-common"; 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 script_set_symbol(void* closurev, const char* name, size_t length, Expression* value, int providei, int hiddeni) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); const bool provide = providei != 0; const bool hidden = hiddeni != 0; closure->script_options()->add_symbol_assignment(name, length, closure->parsing_defsym(), value, provide, hidden); closure->clear_skip_on_incompatible_target(); } // Called by the bison parser to add an assertion. extern "C" void script_add_assertion(void* closurev, Expression* check, const char* message, size_t messagelen) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); closure->script_options()->add_assertion(check, message, messagelen); closure->clear_skip_on_incompatible_target(); } // Called by the bison parser to parse an OPTION. extern "C" void script_parse_option(void* closurev, const char* option, size_t length) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); // We treat the option as a single command-line option, even if // it has internal whitespace. if (closure->command_line() == NULL) { // There are some options that we could handle here--e.g., // -lLIBRARY. Should we bother? gold_warning(_("%s:%d:%d: ignoring command OPTION; OPTION is only valid" " for scripts specified via -T/--script"), closure->filename(), closure->lineno(), closure->charpos()); } else { bool past_a_double_dash_option = false; const char* mutable_option = strndup(option, length); gold_assert(mutable_option != NULL); closure->command_line()->process_one_option(1, &mutable_option, 0, &past_a_double_dash_option); // The General_options class will quite possibly store a pointer // into mutable_option, so we can't free it. In cases the class // does not store such a pointer, this is a memory leak. Alas. :( } closure->clear_skip_on_incompatible_target(); } // Called by the bison parser to handle OUTPUT_FORMAT. OUTPUT_FORMAT // takes either one or three arguments. In the three argument case, // the format depends on the endianness option, which we don't // currently support (FIXME). If we see an OUTPUT_FORMAT for the // wrong format, then we want to search for a new file. Returning 0 // here will cause the parser to immediately abort. extern "C" int script_check_output_format(void* closurev, const char* default_name, size_t default_length, const char*, size_t, const char*, size_t) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); std::string name(default_name, default_length); Target* target = select_target_by_name(name.c_str()); if (target == NULL || !parameters->is_compatible_target(target)) { if (closure->skip_on_incompatible_target()) { closure->set_found_incompatible_target(); return 0; } // FIXME: Should we warn about the unknown target? } return 1; } // Called by the bison parser to handle TARGET. extern "C" void script_set_target(void* closurev, const char* target, size_t len) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); std::string s(target, len); General_options::Object_format format_enum; format_enum = General_options::string_to_object_format(s.c_str()); closure->position_dependent_options().set_format_enum(format_enum); } // Called by the bison parser to handle SEARCH_DIR. This is handled // exactly like a -L option. extern "C" void script_add_search_dir(void* closurev, const char* option, size_t length) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); if (closure->command_line() == NULL) 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 if (!closure->command_line()->options().nostdlib()) { std::string s = "-L" + std::string(option, length); script_parse_option(closurev, s.c_str(), s.size()); } } /* Called by the bison parser to push the lexer into expression mode. */ extern "C" void script_push_lex_into_expression_mode(void* closurev) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); closure->push_lex_mode(Lex::EXPRESSION); } /* Called by the bison parser to push the lexer into version mode. */ extern "C" void script_push_lex_into_version_mode(void* closurev) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); if (closure->version_script()->is_finalized()) gold_error(_("%s:%d:%d: invalid use of VERSION in input file"), closure->filename(), closure->lineno(), closure->charpos()); closure->push_lex_mode(Lex::VERSION_SCRIPT); } /* Called by the bison parser to pop the lexer mode. */ extern "C" void script_pop_lex_mode(void* closurev) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); closure->pop_lex_mode(); } // 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); tree->dependencies = deps; if (tag != NULL) 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. 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) { 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(), static_cast<bool>(exact_match))); return expressions; } // 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) { a->expressions.insert(a->expressions.end(), b->expressions.begin(), b->expressions.end()); // We could delete b and remove it from expressions_lists_, but // that's a lot of work. This works just as well. b->expressions.clear(); return a; } // 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); std::string language(lang, langlen); Version_script_info::Language code; if (language.empty() || language == "C") code = Version_script_info::LANGUAGE_C; else if (language == "C++") code = Version_script_info::LANGUAGE_CXX; else if (language == "Java") code = Version_script_info::LANGUAGE_JAVA; else { char* buf = new char[langlen + 100]; snprintf(buf, langlen + 100, _("unrecognized version script language '%s'"), language.c_str()); yyerror(closurev, buf); delete[] buf; code = Version_script_info::LANGUAGE_C; } closure->push_language(code); } extern "C" void version_script_pop_lang(void* closurev) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); closure->pop_language(); } // Called by the bison parser to start a SECTIONS clause. extern "C" void script_start_sections(void* closurev) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); closure->script_options()->script_sections()->start_sections(); closure->clear_skip_on_incompatible_target(); } // Called by the bison parser to finish a SECTIONS clause. extern "C" void script_finish_sections(void* closurev) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); closure->script_options()->script_sections()->finish_sections(); } // Start processing entries for an output section. extern "C" void script_start_output_section(void* closurev, const char* name, size_t namelen, const struct Parser_output_section_header* header) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); closure->script_options()->script_sections()->start_output_section(name, namelen, header); } // Finish processing entries for an output section. extern "C" void script_finish_output_section(void* closurev, const struct Parser_output_section_trailer* trail) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); closure->script_options()->script_sections()->finish_output_section(trail); } // Add a data item (e.g., "WORD (0)") to the current output section. extern "C" void script_add_data(void* closurev, int data_token, Expression* val) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); int size; bool is_signed = true; switch (data_token) { case QUAD: size = 8; is_signed = false; break; case SQUAD: size = 8; break; case LONG: size = 4; break; case SHORT: size = 2; break; case BYTE: size = 1; break; default: gold_unreachable(); } closure->script_options()->script_sections()->add_data(size, is_signed, val); } // Add a clause setting the fill value to the current output section. extern "C" void script_add_fill(void* closurev, Expression* val) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); closure->script_options()->script_sections()->add_fill(val); } // Add a new input section specification to the current output // section. extern "C" void script_add_input_section(void* closurev, const struct Input_section_spec* spec, int keepi) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); bool keep = keepi != 0; closure->script_options()->script_sections()->add_input_section(spec, keep); } // When we see DATA_SEGMENT_ALIGN we record that following output // sections may be relro. extern "C" void script_data_segment_align(void* closurev) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); if (!closure->script_options()->saw_sections_clause()) gold_error(_("%s:%d:%d: DATA_SEGMENT_ALIGN not in SECTIONS clause"), closure->filename(), closure->lineno(), closure->charpos()); else closure->script_options()->script_sections()->data_segment_align(); } // When we see DATA_SEGMENT_RELRO_END we know that all output sections // since DATA_SEGMENT_ALIGN should be relro. extern "C" void script_data_segment_relro_end(void* closurev) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); if (!closure->script_options()->saw_sections_clause()) gold_error(_("%s:%d:%d: DATA_SEGMENT_ALIGN not in SECTIONS clause"), closure->filename(), closure->lineno(), closure->charpos()); else closure->script_options()->script_sections()->data_segment_relro_end(); } // Create a new list of string/sort pairs. extern "C" String_sort_list_ptr script_new_string_sort_list(const struct Wildcard_section* string_sort) { return new String_sort_list(1, *string_sort); } // Add an entry to a list of string/sort pairs. The way the parser // works permits us to simply modify the first parameter, rather than // copy the vector. extern "C" String_sort_list_ptr script_string_sort_list_add(String_sort_list_ptr pv, const struct Wildcard_section* string_sort) { if (pv == NULL) return script_new_string_sort_list(string_sort); else { pv->push_back(*string_sort); return pv; } } // Create a new list of strings. extern "C" String_list_ptr script_new_string_list(const char* str, size_t len) { return new String_list(1, std::string(str, len)); } // Add an element to a list of strings. The way the parser works // permits us to simply modify the first parameter, rather than copy // the vector. extern "C" String_list_ptr script_string_list_push_back(String_list_ptr pv, const char* str, size_t len) { if (pv == NULL) return script_new_string_list(str, len); else { pv->push_back(std::string(str, len)); return pv; } } // Concatenate two string lists. Either or both may be NULL. The way // the parser works permits us to modify the parameters, rather than // copy the vector. extern "C" String_list_ptr script_string_list_append(String_list_ptr pv1, String_list_ptr pv2) { if (pv1 == NULL) return pv2; if (pv2 == NULL) return pv1; pv1->insert(pv1->end(), pv2->begin(), pv2->end()); return pv1; } // Add a new program header. extern "C" void script_add_phdr(void* closurev, const char* name, size_t namelen, unsigned int type, const Phdr_info* info) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); bool includes_filehdr = info->includes_filehdr != 0; bool includes_phdrs = info->includes_phdrs != 0; bool is_flags_valid = info->is_flags_valid != 0; Script_sections* ss = closure->script_options()->script_sections(); ss->add_phdr(name, namelen, type, includes_filehdr, includes_phdrs, is_flags_valid, info->flags, info->load_address); closure->clear_skip_on_incompatible_target(); } // Convert a program header string to a type. #define PHDR_TYPE(NAME) { #NAME, sizeof(#NAME) - 1, elfcpp::NAME } static struct { const char* name; size_t namelen; unsigned int val; } phdr_type_names[] = { PHDR_TYPE(PT_NULL), PHDR_TYPE(PT_LOAD), PHDR_TYPE(PT_DYNAMIC), PHDR_TYPE(PT_INTERP), PHDR_TYPE(PT_NOTE), PHDR_TYPE(PT_SHLIB), PHDR_TYPE(PT_PHDR), PHDR_TYPE(PT_TLS), PHDR_TYPE(PT_GNU_EH_FRAME), PHDR_TYPE(PT_GNU_STACK), PHDR_TYPE(PT_GNU_RELRO) }; extern "C" unsigned int script_phdr_string_to_type(void* closurev, const char* name, size_t namelen) { for (unsigned int i = 0; i < sizeof(phdr_type_names) / sizeof(phdr_type_names[0]); ++i) if (namelen == phdr_type_names[i].namelen && strncmp(name, phdr_type_names[i].name, namelen) == 0) return phdr_type_names[i].val; yyerror(closurev, _("unknown PHDR type (try integer)")); return elfcpp::PT_NULL; } extern "C" void script_saw_segment_start_expression(void* closurev) { Parser_closure* closure = static_cast<Parser_closure*>(closurev); 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(void* closurev, const char*, size_t) { // FIXME: Implement ? yyerror (closurev, _("GOLD does not currently support INCLUDE directives")); } // 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; }
Go to most recent revision | Compare with Previous | Blame | View Log