| // script.cc -- handle linker scripts for gold. | |
| // Copyright (C) 2006-2024 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; | |
| 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_; | |
| }; | |
| // Return the value of a TOKEN_INTEGER. | |
| uint64_t | |
| Token::integer_value() const | |
| { | |
| gold_assert(this->classification_ == TOKEN_INTEGER); | |
| size_t len = this->value_length_; | |
| uint64_t multiplier = 1; | |
| char last = this->value_[len - 1]; | |
| if (last == 'm' || last == 'M') | |
| { | |
| multiplier = 1024 * 1024; | |
| --len; | |
| } | |
| else if (last == 'k' || last == 'K') | |
| { | |
| multiplier = 1024; | |
| --len; | |
| } | |
| char *end; | |
| uint64_t ret = strtoull(this->value_, &end, 0); | |
| gold_assert(static_cast<size_t>(end - this->value_) == len); | |
| return ret * multiplier; | |
| } | |
| // This class handles lexing a file into a sequence of tokens. | |
| class Lex | |
| { | |
| 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. | |
| // 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)) != NULL) | |
| match = new_match; | |
| // A special case: integers may be followed by a single M or K, | |
| // case-insensitive. | |
| if (classification == Token::TOKEN_INTEGER | |
| && (*match == 'm' || *match == 'M' || *match == 'k' || *match == 'K')) | |
| ++match; | |
| *pp = match; | |
| return this->make_token(classification, start, match - start, start); | |
| } | |
| // 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) | |
| { | |
| // Skip whitespace quickly. | |
| while (*p == ' ' || *p == '\t' || *p == '\r') | |
| ++p; | |
| if (*p == '\n') | |
| { | |
| ++p; | |
| ++this->lineno_; | |
| this->linestart_ = p; | |
| continue; | |
| } | |
| char c0 = *p; | |
| if (c0 == '\0') | |
| { | |
| *pp = p; | |
| return this->make_eof_token(p); | |
| } | |
| char c1 = p[1]; | |
| // Skip C style comments. | |
| if (c0 == '/' && c1 == '*') | |
| { | |
| 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 (c0 == '#') | |
| { | |
| *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(c0, c1)) | |
| 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); | |
| } | |
| // Be careful not to lookahead past the end of the buffer. | |
| char c2 = (c1 == '\0' ? '\0' : p[2]); | |
| // Check for a number. | |
| if (this->can_start_hex(c0, c1, c2)) | |
| return this->gather_token(Token::TOKEN_INTEGER, | |
| &Lex::can_continue_hex, | |
| p, p + 3, pp); | |
| if (Lex::can_start_number(c0)) | |
| return this->gather_token(Token::TOKEN_INTEGER, | |
| &Lex::can_continue_number, | |
| p, p + 1, pp); | |
| // Check for operators. | |
| int opcode = Lex::three_char_operator(c0, c1, c2); | |
| if (opcode != 0) | |
| { | |
| *pp = p + 3; | |
| return this->make_token(opcode, p); | |
| } | |
| opcode = Lex::two_char_operator(c0, c1); | |
| if (opcode != 0) | |
| { | |
| *pp = p + 2; | |
| return this->make_token(opcode, p); | |
| } | |
| opcode = Lex::one_char_operator(c0); | |
| 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; | |
| elfcpp::STT type = elfcpp::STT_NOTYPE; | |
| elfcpp::STV vis = elfcpp::STV_DEFAULT; | |
| unsigned char nonvis = 0; | |
| uint64_t final_val = this->val_->eval_maybe_dot(symtab, layout, true, | |
| is_dot_available, | |
| dot_value, dot_section, | |
| §ion, NULL, &type, | |
| &vis, &nonvis, false, NULL); | |
| Sized_symbol<size>* ssym = symtab->get_sized_symbol<size>(this->sym_); | |
| ssym->set_value(final_val); | |
| ssym->set_type(type); | |
| ssym->set_visibility(vis); | |
| ssym->set_nonvis(nonvis); | |
| if (section != NULL) | |
| ssym->set_output_section(section); | |
| } | |
| // Set the symbol value if the expression yields an absolute value or | |
| // a value relative to DOT_SECTION. | |
| void | |
| Symbol_assignment::set_if_absolute(Symbol_table* symtab, const Layout* layout, | |
| bool is_dot_available, uint64_t dot_value, | |
| Output_section* dot_section) | |
| { | |
| if (this->sym_ == NULL) | |
| return; | |
| Output_section* val_section; | |
| bool is_valid; | |
| uint64_t val = this->val_->eval_maybe_dot(symtab, layout, false, | |
| is_dot_available, dot_value, | |
| dot_section, &val_section, NULL, | |
| NULL, NULL, NULL, false, &is_valid); | |
| if (!is_valid || (val_section != NULL && val_section != dot_section)) | |
| return; | |
| if (parameters->target().get_size() == 32) | |
| { | |
| #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(); | |
| if (val_section != NULL) | |
| this->sym_->set_output_section(val_section); | |
| } | |
| // 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; | |
| } | |
| // Populates the set with symbols defined in defsym LHS. | |
| void Script_options::find_defsym_defs(Unordered_set<std::string>& defsym_set) | |
| { | |
| for (Symbol_assignments::const_iterator p = this->symbol_assignments_.begin(); | |
| p != this->symbol_assignments_.end(); | |
| ++p) | |
| { | |
| defsym_set.insert((*p)->name()); | |
| } | |
| } | |
| void | |
| Script_options::set_defsym_uses_in_real_elf(Symbol_table* symtab) const | |
| { | |
| for (Symbol_assignments::const_iterator p = this->symbol_assignments_.begin(); | |
| p != this->symbol_assignments_.end(); | |
| ++p) | |
| { | |
| (*p)->value()->set_expr_sym_in_real_elf(symtab); | |
| } | |
| } | |
| // Add a symbol to be defined. | |
| 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, NULL); | |
| 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(), read_commandline_script() and | |
| // script_include_directive(). Processes the given file in the mode indicated | |
| // by first_token and lex_mode. | |
| static bool | |
| read_script_file(const char* filename, Command_line* cmdline, | |
| Script_options* script_options, | |
| int first_token, Lex::Mode lex_mode) | |
| { | |
| Dirsearch dirsearch; | |
| std::string name = filename; | |
| // If filename is a relative filename, search for it manually using "." + | |
| // cmdline->options()->library_path() -- not dirsearch. | |
| if (!IS_ABSOLUTE_PATH(filename)) | |
| { | |
| const General_options::Dir_list& search_path = | |
| cmdline->options().library_path(); | |
| name = Dirsearch::find_file_in_dir_list(name, search_path, "."); | |
| } | |
| // The file locking code wants to record a Task, but we haven't | |
| // started the workqueue yet. This is only for debugging purposes, | |
| // 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(name.c_str(), | |
| 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 }, | |
| { "HIDDEN", HIDDEN }, | |
| { "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_INIT_PRIORITY", SORT_BY_INIT_PRIORITY }, | |
| { "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_bfd_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(int first_token, void* closurev, | |
| const char* filename, size_t length) | |
| { | |
| Parser_closure* closure = static_cast<Parser_closure*>(closurev); | |
| std::string name(filename, length); | |
| Command_line* cmdline = closure->command_line(); | |
| read_script_file(name.c_str(), cmdline, &cmdline->script_options(), | |
| first_token, Lex::LINKER_SCRIPT); | |
| } | |
| // Functions for memory regions. | |
| extern "C" Expression* | |
| script_exp_function_origin(void* closurev, const char* name, size_t namelen) | |
| { | |
| Parser_closure* closure = static_cast<Parser_closure*>(closurev); | |
| Script_sections* ss = closure->script_options()->script_sections(); | |
| Expression* origin = ss->find_memory_region_origin(name, namelen); | |
| if (origin == NULL) | |
| { | |
| gold_error(_("undefined memory region '%s' referenced " | |
| "in ORIGIN expression"), | |
| name); | |
| // Create a dummy expression to prevent crashes later on. | |
| origin = script_exp_integer(0); | |
| } | |
| return origin; | |
| } | |
| extern "C" Expression* | |
| script_exp_function_length(void* closurev, const char* name, size_t namelen) | |
| { | |
| Parser_closure* closure = static_cast<Parser_closure*>(closurev); | |
| Script_sections* ss = closure->script_options()->script_sections(); | |
| Expression* length = ss->find_memory_region_length(name, namelen); | |
| if (length == NULL) | |
| { | |
| gold_error(_("undefined memory region '%s' referenced " | |
| "in LENGTH expression"), | |
| name); | |
| // Create a dummy expression to prevent crashes later on. | |
| length = script_exp_integer(0); | |
| } | |
| return length; | |
| } |