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