diff options
Diffstat (limited to 'gold/script.cc')
-rw-r--r-- | gold/script.cc | 1188 |
1 files changed, 1188 insertions, 0 deletions
diff --git a/gold/script.cc b/gold/script.cc new file mode 100644 index 0000000..b22611f --- /dev/null +++ b/gold/script.cc @@ -0,0 +1,1188 @@ +// script.cc -- handle linker scripts for gold. + +#include "gold.h" + +#include <string> +#include <vector> +#include <cassert> +#include <cstdio> +#include <cstdlib> + +#include "options.h" +#include "fileread.h" +#include "workqueue.h" +#include "readsyms.h" +#include "yyscript.h" +#include "script.h" +#include "script-c.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 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_(), opcode_(0), + lineno_(0), charpos_(0) + { } + + // A general token with no value. + Token(Classification classification, int lineno, int charpos) + : classification_(classification), value_(), opcode_(0), + lineno_(lineno), charpos_(charpos) + { assert(classification == TOKEN_INVALID || classification == TOKEN_EOF); } + + // A general token with a value. + Token(Classification classification, const std::string& value, + int lineno, int charpos) + : classification_(classification), value_(value), opcode_(0), + lineno_(lineno), charpos_(charpos) + { assert(classification != TOKEN_INVALID && classification != TOKEN_EOF); } + + // A token representing a string of characters. + Token(const std::string& s, int lineno, int charpos) + : classification_(TOKEN_STRING), value_(s), opcode_(0), + lineno_(lineno), charpos_(charpos) + { } + + // A token representing an operator. + Token(int opcode, int lineno, int charpos) + : classification_(TOKEN_OPERATOR), value_(), 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 std::string& + string_value() const + { + assert(this->classification_ == TOKEN_STRING); + return this->value_; + } + + int + operator_value() const + { + assert(this->classification_ == TOKEN_OPERATOR); + return this->opcode_; + } + + int64_t + integer_value() const + { + assert(this->classification_ == TOKEN_INTEGER); + return strtoll(this->value_.c_str(), NULL, 0); + } + + private: + // The token classification. + Classification classification_; + // The token value, for TOKEN_STRING or TOKEN_INTEGER. + std::string value_; + // The token value, for TOKEN_OPERATOR. + int opcode_; + // The line number where this token started (one based). + int lineno_; + // The character position within the line where this token started + // (one based). + int charpos_; +}; + +// This class handles lexing a file into a sequence of tokens. We +// don't expect linker scripts to be large, so we just read them and +// tokenize them all at once. + +class Lex +{ + public: + Lex(Input_file* input_file) + : input_file_(input_file), tokens_() + { } + + // Tokenize the file. Return the final token, which will be either + // an invalid token or an EOF token. An invalid token indicates + // that tokenization failed. + Token + tokenize(); + + // A token sequence. + typedef std::vector<Token> Token_sequence; + + // Return the tokens. + const Token_sequence& + tokens() const + { return this->tokens_; } + + private: + Lex(const Lex&); + Lex& operator=(const Lex&); + + // Read the file into a string buffer. + void + read_file(std::string*); + + // Make a general token with no value at the current location. + Token + make_token(Token::Classification c, const char* p) const + { return Token(c, this->lineno_, p - this->linestart_ + 1); } + + // Make a general token with a value at the current location. + Token + make_token(Token::Classification c, const std::string& v, const char* p) + const + { return Token(c, v, this->lineno_, p - this->linestart_ + 1); } + + // Make an operator token at the current location. + Token + make_token(int opcode, const char* p) const + { return Token(opcode, this->lineno_, p - this->linestart_ + 1); } + + // Make an invalid token at the current location. + Token + make_invalid_token(const char* p) + { return this->make_token(Token::TOKEN_INVALID, p); } + + // Make an EOF token at the current location. + Token + make_eof_token(const char* p) + { return this->make_token(Token::TOKEN_EOF, p); } + + // Return whether C can be the first character in a name. C2 is the + // next character, since we sometimes need that. + static inline bool + can_start_name(char c, char c2); + + // Return whether C can appear in a name which has already started. + static inline bool + can_continue_name(char c); + + // Return whether C, C2, C3 can start a hex number. + static inline bool + can_start_hex(char c, char c2, char c3); + + // Return whether C can appear in a hex number. + static inline bool + can_continue_hex(char c); + + // Return whether C can start a non-hex number. + static inline bool + can_start_number(char c); + + // Return whether C can appear in a non-hex number. + static inline bool + can_continue_number(char c) + { return Lex::can_start_number(c); } + + // 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, bool (*can_continue_fn)(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 file we are reading. + Input_file* input_file_; + // The token sequence we create. + Token_sequence tokens_; + // The current line number. + int lineno_; + // The start of the current line in the buffer. + 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(std::string* contents) +{ + contents->clear(); + off_t off = 0; + off_t got; + unsigned char buf[BUFSIZ]; + do + { + this->input_file_->file().read(off, sizeof buf, buf, &got); + contents->append(reinterpret_cast<char*>(&buf[0]), got); + } + while (got == sizeof buf); +} + +// 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, "~0" is a +// symbol 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 '$': case '/': case '\\': + return true; + + case '~': + return 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. + +inline bool +Lex::can_continue_name(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 '/': case '\\': + case '~': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case '=': case '+': case '-': case ':': case '[': case ']': + case ',': case '?': case '*': + return true; + + default: + return false; + } +} + +// For a number we accept 0x followed by hex digits, or any sequence +// of digits. The old linker accepts leading '$' for hex, and +// trailing HXBOD. Those are for MRI compatibility and we don't +// accept them. The old linker also accepts trailing MK for mega or +// kilo. Those are mentioned in the documentation, and we 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 Lex::can_continue_hex(c3); + return false; +} + +// Return whether C can appear in a hex number. + +inline bool +Lex::can_continue_hex(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 true; + + default: + return false; + } +} + +// 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, + bool (*can_continue_fn)(char), + const char* start, + const char* match, + const char **pp) +{ + while ((*can_continue_fn)(*match)) + ++match; + *pp = match; + return this->make_token(classification, + std::string(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_STRING, + std::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; + + 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 (Lex::can_start_name(p[0], p[1])) + return this->gather_token(Token::TOKEN_STRING, + Lex::can_continue_name, + p, p + 2, 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 (Lex::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); + } +} + +// Tokenize the file. Return the final token. + +Token +Lex::tokenize() +{ + std::string contents; + this->read_file(&contents); + + const char* p = contents.c_str(); + + this->lineno_ = 1; + this->linestart_ = p; + + while (true) + { + Token t(this->get_token(&p)); + + // Don't let an early null byte fool us into thinking that we've + // reached the end of the file. + if (t.is_eof() + && static_cast<size_t>(p - contents.c_str()) < contents.length()) + t = this->make_invalid_token(p); + + if (t.is_invalid() || t.is_eof()) + return t; + + this->tokens_.push_back(t); + } +} + +// A trivial task which waits for THIS_BLOCKER to be clear and then +// clears NEXT_BLOCKER. THIS_BLOCKER may be NULL. + +class Script_unblock : public Task +{ + public: + Script_unblock(Task_token* this_blocker, Task_token* next_blocker) + : this_blocker_(this_blocker), next_blocker_(next_blocker) + { } + + ~Script_unblock() + { + if (this->this_blocker_ != NULL) + delete this->this_blocker_; + } + + Is_runnable_type + is_runnable(Workqueue*) + { + if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked()) + return IS_BLOCKED; + return IS_RUNNABLE; + } + + Task_locker* + locks(Workqueue* workqueue) + { + return new Task_locker_block(*this->next_blocker_, workqueue); + } + + void + run(Workqueue*) + { } + + private: + Task_token* this_blocker_; + Task_token* next_blocker_; +}; + +// 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 in the +// main thread. + +class Parser_closure +{ + public: + Parser_closure(const char* filename, + const Position_dependent_options& posdep_options, + bool in_group, + const Lex::Token_sequence* tokens) + : filename_(filename), posdep_options_(posdep_options), + in_group_(in_group), tokens_(tokens), + next_token_index_(0), inputs_(NULL) + { } + + // 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_; } + + // Return whether this script is being run in a group. + bool + in_group() const + { return this->in_group_; } + + // Whether we are at the end of the token list. + bool + at_eof() const + { return this->next_token_index_ >= this->tokens_->size(); } + + // Return the next token. + const Token* + next_token() + { + const Token* ret = &(*this->tokens_)[this->next_token_index_]; + ++this->next_token_index_; + return ret; + } + + // 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(); } + + private: + // The name of the file we are reading. + const char* filename_; + // The position dependent options. + Position_dependent_options posdep_options_; + // Whether we are currently in a --start-group/--end-group. + bool in_group_; + + // The tokens to be returned by the lexer. + const Lex::Token_sequence* tokens_; + // The index of the next token to return. + unsigned int next_token_index_; + // New input files found to add to the link. + Input_arguments* inputs_; +}; + +// FILE was found as an argument on the command line. Try to read it +// as a script. We've already read BYTES of data into P, but we +// ignore that. Return true if the file was handled. + +bool +read_input_script(Workqueue* workqueue, const General_options& options, + Symbol_table* symtab, Layout* layout, + const Dirsearch& dirsearch, Input_objects* input_objects, + Input_group* input_group, + const Input_argument* input_argument, + Input_file* input_file, const unsigned char*, off_t, + Task_token* this_blocker, Task_token* next_blocker) +{ + Lex lex(input_file); + if (lex.tokenize().is_invalid()) + return false; + + Parser_closure closure(input_file->filename().c_str(), + input_argument->file().options(), + input_group != NULL, + &lex.tokens()); + + if (yyparse(&closure) != 0) + return false; + + // THIS_BLOCKER must be clear before we may add anything to the + // symbol table. We are responsible for unblocking NEXT_BLOCKER + // when we are done. We are responsible for deleting THIS_BLOCKER + // when it is unblocked. + + if (!closure.saw_inputs()) + { + // The script did not add any files to read. Note that we are + // not permitted to call NEXT_BLOCKER->unblock() here even if + // THIS_BLOCKER is NULL, as we are not in the main thread. + workqueue->queue(new Script_unblock(this_blocker, next_blocker)); + return true; + } + + 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(); + nb->add_blocker(); + } + workqueue->queue(new Read_symbols(options, input_objects, symtab, + layout, dirsearch, &*p, + input_group, this_blocker, nb)); + this_blocker = nb; + } + + return true; +} + +// Manage mapping from keywords to the codes expected by the bison +// parser. + +class Keyword_to_parsecode +{ + public: + // The structure which maps keywords to parsecodes. + struct Keyword_parsecode + { + // Keyword. + const char* keyword; + // Corresponding parsecode. + int parsecode; + }; + + // Return the parsecode corresponding KEYWORD, or 0 if it is not a + // keyword. + static int + keyword_to_parsecode(const char* keyword); + + private: + // The array of all keywords. + static const Keyword_parsecode keyword_parsecodes_[]; + + // The number of keywords. + static const int keyword_count; +}; + +// Mapping from keyword string to keyword parsecode. This array must +// be kept in sorted order. Parsecodes are looked up using bsearch. +// This array must correspond to the list of parsecodes in yyscript.y. + +const Keyword_to_parsecode::Keyword_parsecode +Keyword_to_parsecode::keyword_parsecodes_[] = +{ + { "ABSOLUTE", ABSOLUTE }, + { "ADDR", ADDR }, + { "ALIGN", ALIGN_K }, + { "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 }, + { "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_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 }, +}; + +const int Keyword_to_parsecode::keyword_count = + (sizeof(Keyword_to_parsecode::keyword_parsecodes_) + / sizeof(Keyword_to_parsecode::keyword_parsecodes_[0])); + +// Comparison function passed to bsearch. + +extern "C" +{ + +static int +ktt_compare(const void* keyv, const void* kttv) +{ + const char* key = static_cast<const char*>(keyv); + const Keyword_to_parsecode::Keyword_parsecode* ktt = + static_cast<const Keyword_to_parsecode::Keyword_parsecode*>(kttv); + return strcmp(key, ktt->keyword); +} + +} // End extern "C". + +int +Keyword_to_parsecode::keyword_to_parsecode(const char* keyword) +{ + void* kttv = bsearch(keyword, + Keyword_to_parsecode::keyword_parsecodes_, + Keyword_to_parsecode::keyword_count, + sizeof(Keyword_to_parsecode::keyword_parsecodes_[0]), + ktt_compare); + if (kttv == NULL) + return 0; + Keyword_parsecode* ktt = static_cast<Keyword_parsecode*>(kttv); + return ktt->parsecode; +} + +} // End namespace gold. + +// The remaining functions are extern "C", so it's clearer to not put +// them in namespace gold. + +using namespace gold; + +// This function is called by the bison parser to return the next +// token. + +extern "C" int +yylex(YYSTYPE* lvalp, void* closurev) +{ + Parser_closure* closure = static_cast<Parser_closure*>(closurev); + + if (closure->at_eof()) + return 0; + + const Token* token = closure->next_token(); + + switch (token->classification()) + { + default: + case Token::TOKEN_INVALID: + case Token::TOKEN_EOF: + abort(); + + case Token::TOKEN_STRING: + { + const char* str = token->string_value().c_str(); + int parsecode = Keyword_to_parsecode::keyword_to_parsecode(str); + if (parsecode != 0) + return parsecode; + lvalp->string = str; + return STRING; + } + + case Token::TOKEN_OPERATOR: + return token->operator_value(); + + case Token::TOKEN_INTEGER: + lvalp->integer = token->integer_value(); + return INTEGER; + } +} + +// This function is called by the bison parser to report an error. + +extern "C" void +yyerror(void* closurev, const char* message) +{ + Parser_closure* closure = static_cast<Parser_closure*>(closurev); + + fprintf(stderr, _("%s: %s: %s\n"), + program_name, closure->filename(), message); + gold_exit(false); +} + +// Called by the bison parser to add a file to the link. + +extern "C" void +script_add_file(void* closurev, const char* name) +{ + Parser_closure* closure = static_cast<Parser_closure*>(closurev); + Input_file_argument file(name, false, closure->position_dependent_options()); + closure->inputs()->add_file(file); +} + +// Called by the bison parser to start a group. If we are already in +// a group, that means that this script was invoked within a +// --start-group --end-group sequence on the command line, or that +// this script was found in a GROUP of another script. In that case, +// we simply continue the existing group, rather than starting a new +// one. It is possible to construct a case in which this will do +// something other than what would happen if we did a recursive group, +// but it's hard to imagine why the different behaviour would be +// useful for a real program. Avoiding recursive groups is simpler +// and more efficient. + +extern "C" void +script_start_group(void* closurev) +{ + Parser_closure* closure = static_cast<Parser_closure*>(closurev); + if (!closure->in_group()) + closure->inputs()->start_group(); +} + +// Called by the bison parser at the end of a group. + +extern "C" void +script_end_group(void* closurev) +{ + Parser_closure* closure = static_cast<Parser_closure*>(closurev); + if (!closure->in_group()) + closure->inputs()->end_group(); +} + +// Called by the bison parser to start an AS_NEEDED list. + +extern "C" void +script_start_as_needed(void* closurev) +{ + Parser_closure* closure = static_cast<Parser_closure*>(closurev); + closure->position_dependent_options().set_as_needed(); +} + +// Called by the bison parser at the end of an AS_NEEDED list. + +extern "C" void +script_end_as_needed(void* closurev) +{ + Parser_closure* closure = static_cast<Parser_closure*>(closurev); + closure->position_dependent_options().clear_as_needed(); +} |