From 54f1b3cf509cd889905287cb8ce6c5ae33911a21 Mon Sep 17 00:00:00 2001 From: Andrew Hsieh Date: Fri, 13 Jun 2014 12:38:00 -0700 Subject: Add upstream binutils-2.25 snapshot 4/4 2014 For MIPS -mmsa support Change-Id: I08c4f002fa7b33dec85ed75956e6ab551bb03c96 --- binutils-2.25/gold/script.cc | 3409 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3409 insertions(+) create mode 100644 binutils-2.25/gold/script.cc (limited to 'binutils-2.25/gold/script.cc') diff --git a/binutils-2.25/gold/script.cc b/binutils-2.25/gold/script.cc new file mode 100644 index 00000000..6a10c40e --- /dev/null +++ b/binutils-2.25/gold/script.cc @@ -0,0 +1,3409 @@ +// script.cc -- handle linker scripts for gold. + +// Copyright 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. +// Written by Ian Lance Taylor . + +// 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 +#include +#include +#include +#include +#include +#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(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(&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) + { + if (*p == '\0') + { + *pp = p; + return this->make_eof_token(p); + } + + // Skip whitespace quickly. + while (*p == ' ' || *p == '\t' || *p == '\r') + ++p; + + if (*p == '\n') + { + ++p; + ++this->lineno_; + this->linestart_ = p; + continue; + } + + // Skip C style comments. + if (p[0] == '/' && p[1] == '*') + { + int lineno = this->lineno_; + int charpos = p - this->linestart_ + 1; + + *pp = p + 2; + if (!this->skip_c_comment(pp)) + return Token(Token::TOKEN_INVALID, lineno, charpos); + p = *pp; + + continue; + } + + // Skip line comments. + if (*p == '#') + { + *pp = p + 1; + if (!this->skip_line_comment(pp)) + return this->make_eof_token(p); + p = *pp; + continue; + } + + // Check for a name. + if (this->can_start_name(p[0], p[1])) + return this->gather_token(Token::TOKEN_STRING, + &Lex::can_continue_name, + p, p + 1, pp); + + // We accept any arbitrary name in double quotes, as long as it + // does not cross a line boundary. + if (*p == '"') + { + *pp = p; + return this->gather_quoted_string(pp); + } + + // Check for a number. + + if (this->can_start_hex(p[0], p[1], p[2])) + return this->gather_token(Token::TOKEN_INTEGER, + &Lex::can_continue_hex, + p, p + 3, pp); + + if (Lex::can_start_number(p[0])) + return this->gather_token(Token::TOKEN_INTEGER, + &Lex::can_continue_number, + p, p + 1, pp); + + // Check for operators. + + int opcode = Lex::three_char_operator(p[0], p[1], p[2]); + if (opcode != 0) + { + *pp = p + 3; + return this->make_token(opcode, p); + } + + opcode = Lex::two_char_operator(p[0], p[1]); + if (opcode != 0) + { + *pp = p + 2; + return this->make_token(opcode, p); + } + + opcode = Lex::one_char_operator(p[0]); + if (opcode != 0) + { + *pp = p + 1; + return this->make_token(opcode, p); + } + + return this->make_token(Token::TOKEN_INVALID, p); + } +} + +// Return the next token. + +const Token* +Lex::next_token() +{ + // The first token is special. + if (this->first_token_ != 0) + { + this->token_ = Token(this->first_token_, 0, 0); + this->first_token_ = 0; + return &this->token_; + } + + this->token_ = this->get_token(&this->current_); + + // Don't let an early null byte fool us into thinking that we've + // reached the end of the file. + if (this->token_.is_eof() + && (static_cast(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 +void +Symbol_assignment::sized_finalize(Symbol_table* symtab, const Layout* layout, + bool is_dot_available, uint64_t dot_value, + Output_section* dot_section) +{ + Output_section* section; + uint64_t final_val = this->val_->eval_maybe_dot(symtab, layout, true, + is_dot_available, + dot_value, dot_section, + §ion, NULL, false); + Sized_symbol* ssym = symtab->get_sized_symbol(this->sym_); + ssym->set_value(final_val); + if (section != NULL) + ssym->set_output_section(section); +} + +// Set the symbol value if the expression yields an absolute value 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; + uint64_t val = this->val_->eval_maybe_dot(symtab, layout, false, + is_dot_available, dot_value, + dot_section, &val_section, NULL, + false); + if (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; +} + +// 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_stack_; + // A stack of which extern/language block we're inside. Can be C++, + // java, or empty for C. + std::vector 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(-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 }, + { "HLL", HLL }, + { "INCLUDE", INCLUDE }, + { "INFO", INFO }, + { "INHIBIT_COMMON_ALLOCATION", INHIBIT_COMMON_ALLOCATION }, + { "INPUT", INPUT }, + { "KEEP", KEEP }, + { "LENGTH", LENGTH }, + { "LOADADDR", LOADADDR }, + { "LONG", LONG }, + { "MAP", MAP }, + { "MAX", MAX_K }, + { "MEMORY", MEMORY }, + { "MIN", MIN_K }, + { "NEXT", NEXT }, + { "NOCROSSREFS", NOCROSSREFS }, + { "NOFLOAT", NOFLOAT }, + { "NOLOAD", NOLOAD }, + { "ONLY_IF_RO", ONLY_IF_RO }, + { "ONLY_IF_RW", ONLY_IF_RW }, + { "OPTION", OPTION }, + { "ORIGIN", ORIGIN }, + { "OUTPUT", OUTPUT }, + { "OUTPUT_ARCH", OUTPUT_ARCH }, + { "OUTPUT_FORMAT", OUTPUT_FORMAT }, + { "OVERLAY", OVERLAY }, + { "PHDRS", PHDRS }, + { "PROVIDE", PROVIDE }, + { "PROVIDE_HIDDEN", PROVIDE_HIDDEN }, + { "QUAD", QUAD }, + { "SEARCH_DIR", SEARCH_DIR }, + { "SECTIONS", SECTIONS }, + { "SEGMENT_START", SEGMENT_START }, + { "SHORT", SHORT }, + { "SIZEOF", SIZEOF }, + { "SIZEOF_HEADERS", SIZEOF_HEADERS }, + { "SORT", SORT_BY_NAME }, + { "SORT_BY_ALIGNMENT", SORT_BY_ALIGNMENT }, + { "SORT_BY_NAME", SORT_BY_NAME }, + { "SPECIAL", SPECIAL }, + { "SQUAD", SQUAD }, + { "STARTUP", STARTUP }, + { "SUBALIGN", SUBALIGN }, + { "SYSLIB", SYSLIB }, + { "TARGET", TARGET_K }, + { "TRUNCATE", TRUNCATE }, + { "VERSION", VERSIONK }, + { "global", GLOBAL }, + { "l", LENGTH }, + { "len", LENGTH }, + { "local", LOCAL }, + { "o", ORIGIN }, + { "org", ORIGIN }, + { "sizeof_headers", SIZEOF_HEADERS }, +}; + +static const Keyword_to_parsecode +script_keywords(&script_keyword_parsecodes[0], + (sizeof(script_keyword_parsecodes) + / sizeof(script_keyword_parsecodes[0]))); + +static const Keyword_to_parsecode::Keyword_parsecode +version_script_keyword_parsecodes[] = +{ + { "extern", EXTERN }, + { "global", GLOBAL }, + { "local", LOCAL }, +}; + +static const Keyword_to_parsecode +version_script_keywords(&version_script_keyword_parsecodes[0], + (sizeof(version_script_keyword_parsecodes) + / sizeof(version_script_keyword_parsecodes[0]))); + +static const Keyword_to_parsecode::Keyword_parsecode +dynamic_list_keyword_parsecodes[] = +{ + { "extern", EXTERN }, +}; + +static const Keyword_to_parsecode +dynamic_list_keywords(&dynamic_list_keyword_parsecodes[0], + (sizeof(dynamic_list_keyword_parsecodes) + / sizeof(dynamic_list_keyword_parsecodes[0]))); + + + +// Comparison function passed to bsearch. + +extern "C" +{ + +struct Ktt_key +{ + const char* str; + size_t len; +}; + +static int +ktt_compare(const void* keyv, const void* kttv) +{ + const Ktt_key* key = static_cast(keyv); + const Keyword_to_parsecode::Keyword_parsecode* ktt = + static_cast(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(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 expressions; +}; + +// A list of which versions upon which another version depends. +// Strings should be from the Stringpool. +struct Version_dependency_list +{ + std::vector 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 +Version_script_info::get_versions() const +{ + std::vector 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 +Version_script_info::get_dependencies(const char* version) const +{ + std::vector 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 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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(closurev); + Script_sections* ss = closure->script_options()->script_sections(); + ss->add_memory_region(name, namelen, attrs, origin, length); +} + +extern "C" unsigned int +script_parse_memory_attr(void* closurev, const char* attrs, size_t attrlen, + int invert) +{ + int attributes = 0; + + while (attrlen--) + switch (*attrs++) + { + case 'R': + case 'r': + attributes |= MEM_READABLE; break; + case 'W': + case 'w': + attributes |= MEM_READABLE | MEM_WRITEABLE; break; + case 'X': + case 'x': + attributes |= MEM_EXECUTABLE; break; + case 'A': + case 'a': + attributes |= MEM_ALLOCATABLE; break; + case 'I': + case 'i': + case 'L': + case 'l': + attributes |= MEM_INITIALIZED; break; + default: + yyerror(closurev, _("unknown MEMORY attribute")); + } + + if (invert) + attributes = (~ attributes) & MEM_ATTR_MASK; + + return attributes; +} + +extern "C" void +script_include_directive(void* closurev, const char* filename, size_t length) +{ + Parser_closure* closure = static_cast(closurev); + std::string name(filename, length); + Command_line* cmdline = closure->command_line(); + read_script_file(name.c_str(), cmdline, &cmdline->script_options(), + PARSING_LINKER_SCRIPT, 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(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(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; +} -- cgit v1.2.3