mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-24 18:44:20 +08:00
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.
1337 lines
35 KiB
C++
1337 lines
35 KiB
C++
// expression.cc -- expressions in 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 <string>
|
|
|
|
#include "elfcpp.h"
|
|
#include "parameters.h"
|
|
#include "symtab.h"
|
|
#include "layout.h"
|
|
#include "output.h"
|
|
#include "script.h"
|
|
#include "script-c.h"
|
|
|
|
namespace gold
|
|
{
|
|
|
|
// This file holds the code which handles linker expressions.
|
|
|
|
// The dot symbol, which linker scripts refer to simply as ".",
|
|
// requires special treatment. The dot symbol is set several times,
|
|
// section addresses will refer to it, output sections will change it,
|
|
// and it can be set based on the value of other symbols. We simplify
|
|
// the handling by prohibiting setting the dot symbol to the value of
|
|
// a non-absolute symbol.
|
|
|
|
// When evaluating the value of an expression, we pass in a pointer to
|
|
// this struct, so that the expression evaluation can find the
|
|
// information it needs.
|
|
|
|
struct Expression::Expression_eval_info
|
|
{
|
|
// The symbol table.
|
|
const Symbol_table* symtab;
|
|
// The layout--we use this to get section information.
|
|
const Layout* layout;
|
|
// Whether to check assertions.
|
|
bool check_assertions;
|
|
// Whether expressions can refer to the dot symbol. The dot symbol
|
|
// is only available within a SECTIONS clause.
|
|
bool is_dot_available;
|
|
// The current value of the dot symbol.
|
|
uint64_t dot_value;
|
|
// The section in which the dot symbol is defined; this is NULL if
|
|
// it is absolute.
|
|
Output_section* dot_section;
|
|
// Points to where the section of the result should be stored.
|
|
Output_section** result_section_pointer;
|
|
// Pointer to where the alignment of the result should be stored.
|
|
uint64_t* result_alignment_pointer;
|
|
// Pointer to where the type of the symbol on the RHS should be stored.
|
|
elfcpp::STT* type_pointer;
|
|
// Pointer to where the visibility of the symbol on the RHS should be stored.
|
|
elfcpp::STV* vis_pointer;
|
|
// Pointer to where the rest of the symbol's st_other field should be stored.
|
|
unsigned char* nonvis_pointer;
|
|
// Whether the value is valid. In Symbol_assignment::set_if_absolute, we
|
|
// may be trying to evaluate the address of a section whose address is not
|
|
// yet finalized, and we need to fail the evaluation gracefully.
|
|
bool *is_valid_pointer;
|
|
};
|
|
|
|
// Evaluate an expression.
|
|
|
|
uint64_t
|
|
Expression::eval(const Symbol_table* symtab, const Layout* layout,
|
|
bool check_assertions)
|
|
{
|
|
return this->eval_maybe_dot(symtab, layout, check_assertions, false, 0,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, false, NULL);
|
|
}
|
|
|
|
// Evaluate an expression which may refer to the dot symbol.
|
|
|
|
uint64_t
|
|
Expression::eval_with_dot(const Symbol_table* symtab, const Layout* layout,
|
|
bool check_assertions, uint64_t dot_value,
|
|
Output_section* dot_section,
|
|
Output_section** result_section_pointer,
|
|
uint64_t* result_alignment_pointer,
|
|
bool is_section_dot_assignment)
|
|
{
|
|
return this->eval_maybe_dot(symtab, layout, check_assertions, true,
|
|
dot_value, dot_section, result_section_pointer,
|
|
result_alignment_pointer, NULL, NULL, NULL,
|
|
is_section_dot_assignment, NULL);
|
|
}
|
|
|
|
// Evaluate an expression which may or may not refer to the dot
|
|
// symbol.
|
|
|
|
uint64_t
|
|
Expression::eval_maybe_dot(const Symbol_table* symtab, const Layout* layout,
|
|
bool check_assertions, bool is_dot_available,
|
|
uint64_t dot_value, Output_section* dot_section,
|
|
Output_section** result_section_pointer,
|
|
uint64_t* result_alignment_pointer,
|
|
elfcpp::STT* type_pointer,
|
|
elfcpp::STV* vis_pointer,
|
|
unsigned char* nonvis_pointer,
|
|
bool is_section_dot_assignment,
|
|
bool* is_valid_pointer)
|
|
{
|
|
Expression_eval_info eei;
|
|
eei.symtab = symtab;
|
|
eei.layout = layout;
|
|
eei.check_assertions = check_assertions;
|
|
eei.is_dot_available = is_dot_available;
|
|
eei.dot_value = dot_value;
|
|
eei.dot_section = dot_section;
|
|
|
|
// We assume the value is absolute, and only set this to a section
|
|
// if we find a section-relative reference.
|
|
if (result_section_pointer != NULL)
|
|
*result_section_pointer = NULL;
|
|
eei.result_section_pointer = result_section_pointer;
|
|
|
|
// For symbol=symbol assignments, we need to track the type, visibility,
|
|
// and remaining st_other bits.
|
|
eei.type_pointer = type_pointer;
|
|
eei.vis_pointer = vis_pointer;
|
|
eei.nonvis_pointer = nonvis_pointer;
|
|
|
|
eei.result_alignment_pointer = result_alignment_pointer;
|
|
|
|
// Assume the value is valid until we try to evaluate an expression
|
|
// that can't be evaluated yet.
|
|
bool is_valid = true;
|
|
eei.is_valid_pointer = &is_valid;
|
|
|
|
uint64_t val = this->value(&eei);
|
|
|
|
if (is_valid_pointer != NULL)
|
|
*is_valid_pointer = is_valid;
|
|
else
|
|
gold_assert(is_valid);
|
|
|
|
// If this is an assignment to dot within a section, and the value
|
|
// is absolute, treat it as a section-relative offset.
|
|
if (is_section_dot_assignment && *result_section_pointer == NULL)
|
|
{
|
|
gold_assert(dot_section != NULL);
|
|
val += dot_section->address();
|
|
*result_section_pointer = dot_section;
|
|
}
|
|
return val;
|
|
}
|
|
|
|
// A number.
|
|
|
|
class Integer_expression : public Expression
|
|
{
|
|
public:
|
|
Integer_expression(uint64_t val)
|
|
: val_(val)
|
|
{ }
|
|
|
|
uint64_t
|
|
value(const Expression_eval_info*)
|
|
{ return this->val_; }
|
|
|
|
void
|
|
print(FILE* f) const
|
|
{ fprintf(f, "0x%llx", static_cast<unsigned long long>(this->val_)); }
|
|
|
|
private:
|
|
uint64_t val_;
|
|
};
|
|
|
|
extern "C" Expression*
|
|
script_exp_integer(uint64_t val)
|
|
{
|
|
return new Integer_expression(val);
|
|
}
|
|
|
|
// An expression whose value is the value of a symbol.
|
|
|
|
class Symbol_expression : public Expression
|
|
{
|
|
public:
|
|
Symbol_expression(const char* name, size_t length)
|
|
: name_(name, length)
|
|
{ }
|
|
|
|
uint64_t
|
|
value(const Expression_eval_info*);
|
|
|
|
void
|
|
print(FILE* f) const
|
|
{ fprintf(f, "%s", this->name_.c_str()); }
|
|
|
|
private:
|
|
std::string name_;
|
|
};
|
|
|
|
uint64_t
|
|
Symbol_expression::value(const Expression_eval_info* eei)
|
|
{
|
|
Symbol* sym = eei->symtab->lookup(this->name_.c_str());
|
|
if (sym == NULL || !sym->is_defined())
|
|
{
|
|
gold_error(_("undefined symbol '%s' referenced in expression"),
|
|
this->name_.c_str());
|
|
return 0;
|
|
}
|
|
|
|
if (eei->result_section_pointer != NULL)
|
|
*eei->result_section_pointer = sym->output_section();
|
|
if (eei->type_pointer != NULL)
|
|
*eei->type_pointer = sym->type();
|
|
if (eei->vis_pointer != NULL)
|
|
*eei->vis_pointer = sym->visibility();
|
|
if (eei->nonvis_pointer != NULL)
|
|
*eei->nonvis_pointer = sym->nonvis();
|
|
|
|
if (parameters->target().get_size() == 32)
|
|
return eei->symtab->get_sized_symbol<32>(sym)->value();
|
|
else if (parameters->target().get_size() == 64)
|
|
return eei->symtab->get_sized_symbol<64>(sym)->value();
|
|
else
|
|
gold_unreachable();
|
|
}
|
|
|
|
// An expression whose value is the value of the special symbol ".".
|
|
// This is only valid within a SECTIONS clause.
|
|
|
|
class Dot_expression : public Expression
|
|
{
|
|
public:
|
|
Dot_expression()
|
|
{ }
|
|
|
|
uint64_t
|
|
value(const Expression_eval_info*);
|
|
|
|
void
|
|
print(FILE* f) const
|
|
{ fprintf(f, "."); }
|
|
};
|
|
|
|
uint64_t
|
|
Dot_expression::value(const Expression_eval_info* eei)
|
|
{
|
|
if (!eei->is_dot_available)
|
|
{
|
|
gold_error(_("invalid reference to dot symbol outside of "
|
|
"SECTIONS clause"));
|
|
return 0;
|
|
}
|
|
if (eei->result_section_pointer != NULL)
|
|
*eei->result_section_pointer = eei->dot_section;
|
|
return eei->dot_value;
|
|
}
|
|
|
|
// A string. This is either the name of a symbol, or ".".
|
|
|
|
extern "C" Expression*
|
|
script_exp_string(const char* name, size_t length)
|
|
{
|
|
if (length == 1 && name[0] == '.')
|
|
return new Dot_expression();
|
|
else
|
|
return new Symbol_expression(name, length);
|
|
}
|
|
|
|
// A unary expression.
|
|
|
|
class Unary_expression : public Expression
|
|
{
|
|
public:
|
|
Unary_expression(Expression* arg)
|
|
: arg_(arg)
|
|
{ }
|
|
|
|
~Unary_expression()
|
|
{ delete this->arg_; }
|
|
|
|
protected:
|
|
uint64_t
|
|
arg_value(const Expression_eval_info* eei,
|
|
Output_section** arg_section_pointer) const
|
|
{
|
|
return this->arg_->eval_maybe_dot(eei->symtab, eei->layout,
|
|
eei->check_assertions,
|
|
eei->is_dot_available,
|
|
eei->dot_value,
|
|
eei->dot_section,
|
|
arg_section_pointer,
|
|
eei->result_alignment_pointer,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
false,
|
|
eei->is_valid_pointer);
|
|
}
|
|
|
|
void
|
|
arg_print(FILE* f) const
|
|
{ this->arg_->print(f); }
|
|
|
|
private:
|
|
Expression* arg_;
|
|
};
|
|
|
|
// Handle unary operators. We use a preprocessor macro as a hack to
|
|
// capture the C operator.
|
|
|
|
#define UNARY_EXPRESSION(NAME, OPERATOR) \
|
|
class Unary_ ## NAME : public Unary_expression \
|
|
{ \
|
|
public: \
|
|
Unary_ ## NAME(Expression* arg) \
|
|
: Unary_expression(arg) \
|
|
{ } \
|
|
\
|
|
uint64_t \
|
|
value(const Expression_eval_info* eei) \
|
|
{ \
|
|
Output_section* arg_section; \
|
|
uint64_t ret = OPERATOR this->arg_value(eei, &arg_section); \
|
|
if (arg_section != NULL && parameters->options().relocatable()) \
|
|
gold_warning(_("unary " #NAME " applied to section " \
|
|
"relative value")); \
|
|
return ret; \
|
|
} \
|
|
\
|
|
void \
|
|
print(FILE* f) const \
|
|
{ \
|
|
fprintf(f, "(%s ", #OPERATOR); \
|
|
this->arg_print(f); \
|
|
fprintf(f, ")"); \
|
|
} \
|
|
}; \
|
|
\
|
|
extern "C" Expression* \
|
|
script_exp_unary_ ## NAME(Expression* arg) \
|
|
{ \
|
|
return new Unary_ ## NAME(arg); \
|
|
}
|
|
|
|
UNARY_EXPRESSION(minus, -)
|
|
UNARY_EXPRESSION(logical_not, !)
|
|
UNARY_EXPRESSION(bitwise_not, ~)
|
|
|
|
// A binary expression.
|
|
|
|
class Binary_expression : public Expression
|
|
{
|
|
public:
|
|
Binary_expression(Expression* left, Expression* right)
|
|
: left_(left), right_(right)
|
|
{ }
|
|
|
|
~Binary_expression()
|
|
{
|
|
delete this->left_;
|
|
delete this->right_;
|
|
}
|
|
|
|
protected:
|
|
uint64_t
|
|
left_value(const Expression_eval_info* eei,
|
|
Output_section** section_pointer,
|
|
uint64_t* alignment_pointer) const
|
|
{
|
|
return this->left_->eval_maybe_dot(eei->symtab, eei->layout,
|
|
eei->check_assertions,
|
|
eei->is_dot_available,
|
|
eei->dot_value,
|
|
eei->dot_section,
|
|
section_pointer,
|
|
alignment_pointer,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
false,
|
|
eei->is_valid_pointer);
|
|
}
|
|
|
|
uint64_t
|
|
right_value(const Expression_eval_info* eei,
|
|
Output_section** section_pointer,
|
|
uint64_t* alignment_pointer) const
|
|
{
|
|
return this->right_->eval_maybe_dot(eei->symtab, eei->layout,
|
|
eei->check_assertions,
|
|
eei->is_dot_available,
|
|
eei->dot_value,
|
|
eei->dot_section,
|
|
section_pointer,
|
|
alignment_pointer,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
false,
|
|
eei->is_valid_pointer);
|
|
}
|
|
|
|
void
|
|
left_print(FILE* f) const
|
|
{ this->left_->print(f); }
|
|
|
|
void
|
|
right_print(FILE* f) const
|
|
{ this->right_->print(f); }
|
|
|
|
// This is a call to function FUNCTION_NAME. Print it. This is for
|
|
// debugging.
|
|
void
|
|
print_function(FILE* f, const char* function_name) const
|
|
{
|
|
fprintf(f, "%s(", function_name);
|
|
this->left_print(f);
|
|
fprintf(f, ", ");
|
|
this->right_print(f);
|
|
fprintf(f, ")");
|
|
}
|
|
|
|
private:
|
|
Expression* left_;
|
|
Expression* right_;
|
|
};
|
|
|
|
// Handle binary operators. We use a preprocessor macro as a hack to
|
|
// capture the C operator. KEEP_LEFT means that if the left operand
|
|
// is section relative and the right operand is not, the result uses
|
|
// the same section as the left operand. KEEP_RIGHT is the same with
|
|
// left and right swapped. IS_DIV means that we need to give an error
|
|
// if the right operand is zero. WARN means that we should warn if
|
|
// used on section relative values in a relocatable link. We always
|
|
// warn if used on values in different sections in a relocatable link.
|
|
|
|
#define BINARY_EXPRESSION(NAME, OPERATOR, KEEP_LEFT, KEEP_RIGHT, IS_DIV, WARN) \
|
|
class Binary_ ## NAME : public Binary_expression \
|
|
{ \
|
|
public: \
|
|
Binary_ ## NAME(Expression* left, Expression* right) \
|
|
: Binary_expression(left, right) \
|
|
{ } \
|
|
\
|
|
uint64_t \
|
|
value(const Expression_eval_info* eei) \
|
|
{ \
|
|
Output_section* left_section; \
|
|
uint64_t left_alignment = 0; \
|
|
uint64_t left = this->left_value(eei, &left_section, \
|
|
&left_alignment); \
|
|
Output_section* right_section; \
|
|
uint64_t right_alignment = 0; \
|
|
uint64_t right = this->right_value(eei, &right_section, \
|
|
&right_alignment); \
|
|
if (KEEP_RIGHT && left_section == NULL && right_section != NULL) \
|
|
{ \
|
|
if (eei->result_section_pointer != NULL) \
|
|
*eei->result_section_pointer = right_section; \
|
|
if (eei->result_alignment_pointer != NULL \
|
|
&& right_alignment > *eei->result_alignment_pointer) \
|
|
*eei->result_alignment_pointer = right_alignment; \
|
|
} \
|
|
else if (KEEP_LEFT \
|
|
&& left_section != NULL \
|
|
&& right_section == NULL) \
|
|
{ \
|
|
if (eei->result_section_pointer != NULL) \
|
|
*eei->result_section_pointer = left_section; \
|
|
if (eei->result_alignment_pointer != NULL \
|
|
&& left_alignment > *eei->result_alignment_pointer) \
|
|
*eei->result_alignment_pointer = left_alignment; \
|
|
} \
|
|
else if ((WARN || left_section != right_section) \
|
|
&& (left_section != NULL || right_section != NULL) \
|
|
&& parameters->options().relocatable()) \
|
|
gold_warning(_("binary " #NAME " applied to section " \
|
|
"relative value")); \
|
|
if (IS_DIV && right == 0) \
|
|
{ \
|
|
gold_error(_(#NAME " by zero")); \
|
|
return 0; \
|
|
} \
|
|
return left OPERATOR right; \
|
|
} \
|
|
\
|
|
void \
|
|
print(FILE* f) const \
|
|
{ \
|
|
fprintf(f, "("); \
|
|
this->left_print(f); \
|
|
fprintf(f, " %s ", #OPERATOR); \
|
|
this->right_print(f); \
|
|
fprintf(f, ")"); \
|
|
} \
|
|
}; \
|
|
\
|
|
extern "C" Expression* \
|
|
script_exp_binary_ ## NAME(Expression* left, Expression* right) \
|
|
{ \
|
|
return new Binary_ ## NAME(left, right); \
|
|
}
|
|
|
|
BINARY_EXPRESSION(mult, *, false, false, false, true)
|
|
BINARY_EXPRESSION(div, /, false, false, true, true)
|
|
BINARY_EXPRESSION(mod, %, false, false, true, true)
|
|
BINARY_EXPRESSION(add, +, true, true, false, true)
|
|
BINARY_EXPRESSION(sub, -, true, false, false, false)
|
|
BINARY_EXPRESSION(lshift, <<, false, false, false, true)
|
|
BINARY_EXPRESSION(rshift, >>, false, false, false, true)
|
|
BINARY_EXPRESSION(eq, ==, false, false, false, false)
|
|
BINARY_EXPRESSION(ne, !=, false, false, false, false)
|
|
BINARY_EXPRESSION(le, <=, false, false, false, false)
|
|
BINARY_EXPRESSION(ge, >=, false, false, false, false)
|
|
BINARY_EXPRESSION(lt, <, false, false, false, false)
|
|
BINARY_EXPRESSION(gt, >, false, false, false, false)
|
|
BINARY_EXPRESSION(bitwise_and, &, true, true, false, true)
|
|
BINARY_EXPRESSION(bitwise_xor, ^, true, true, false, true)
|
|
BINARY_EXPRESSION(bitwise_or, |, true, true, false, true)
|
|
BINARY_EXPRESSION(logical_and, &&, false, false, false, true)
|
|
BINARY_EXPRESSION(logical_or, ||, false, false, false, true)
|
|
|
|
// A trinary expression.
|
|
|
|
class Trinary_expression : public Expression
|
|
{
|
|
public:
|
|
Trinary_expression(Expression* arg1, Expression* arg2, Expression* arg3)
|
|
: arg1_(arg1), arg2_(arg2), arg3_(arg3)
|
|
{ }
|
|
|
|
~Trinary_expression()
|
|
{
|
|
delete this->arg1_;
|
|
delete this->arg2_;
|
|
delete this->arg3_;
|
|
}
|
|
|
|
protected:
|
|
uint64_t
|
|
arg1_value(const Expression_eval_info* eei,
|
|
Output_section** section_pointer) const
|
|
{
|
|
return this->arg1_->eval_maybe_dot(eei->symtab, eei->layout,
|
|
eei->check_assertions,
|
|
eei->is_dot_available,
|
|
eei->dot_value,
|
|
eei->dot_section,
|
|
section_pointer,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
false,
|
|
eei->is_valid_pointer);
|
|
}
|
|
|
|
uint64_t
|
|
arg2_value(const Expression_eval_info* eei,
|
|
Output_section** section_pointer,
|
|
uint64_t* alignment_pointer) const
|
|
{
|
|
return this->arg1_->eval_maybe_dot(eei->symtab, eei->layout,
|
|
eei->check_assertions,
|
|
eei->is_dot_available,
|
|
eei->dot_value,
|
|
eei->dot_section,
|
|
section_pointer,
|
|
alignment_pointer,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
false,
|
|
eei->is_valid_pointer);
|
|
}
|
|
|
|
uint64_t
|
|
arg3_value(const Expression_eval_info* eei,
|
|
Output_section** section_pointer,
|
|
uint64_t* alignment_pointer) const
|
|
{
|
|
return this->arg1_->eval_maybe_dot(eei->symtab, eei->layout,
|
|
eei->check_assertions,
|
|
eei->is_dot_available,
|
|
eei->dot_value,
|
|
eei->dot_section,
|
|
section_pointer,
|
|
alignment_pointer,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
false,
|
|
eei->is_valid_pointer);
|
|
}
|
|
|
|
void
|
|
arg1_print(FILE* f) const
|
|
{ this->arg1_->print(f); }
|
|
|
|
void
|
|
arg2_print(FILE* f) const
|
|
{ this->arg2_->print(f); }
|
|
|
|
void
|
|
arg3_print(FILE* f) const
|
|
{ this->arg3_->print(f); }
|
|
|
|
private:
|
|
Expression* arg1_;
|
|
Expression* arg2_;
|
|
Expression* arg3_;
|
|
};
|
|
|
|
// The conditional operator.
|
|
|
|
class Trinary_cond : public Trinary_expression
|
|
{
|
|
public:
|
|
Trinary_cond(Expression* arg1, Expression* arg2, Expression* arg3)
|
|
: Trinary_expression(arg1, arg2, arg3)
|
|
{ }
|
|
|
|
uint64_t
|
|
value(const Expression_eval_info* eei)
|
|
{
|
|
Output_section* arg1_section;
|
|
uint64_t arg1 = this->arg1_value(eei, &arg1_section);
|
|
return (arg1
|
|
? this->arg2_value(eei, eei->result_section_pointer,
|
|
eei->result_alignment_pointer)
|
|
: this->arg3_value(eei, eei->result_section_pointer,
|
|
eei->result_alignment_pointer));
|
|
}
|
|
|
|
void
|
|
print(FILE* f) const
|
|
{
|
|
fprintf(f, "(");
|
|
this->arg1_print(f);
|
|
fprintf(f, " ? ");
|
|
this->arg2_print(f);
|
|
fprintf(f, " : ");
|
|
this->arg3_print(f);
|
|
fprintf(f, ")");
|
|
}
|
|
};
|
|
|
|
extern "C" Expression*
|
|
script_exp_trinary_cond(Expression* arg1, Expression* arg2, Expression* arg3)
|
|
{
|
|
return new Trinary_cond(arg1, arg2, arg3);
|
|
}
|
|
|
|
// Max function.
|
|
|
|
class Max_expression : public Binary_expression
|
|
{
|
|
public:
|
|
Max_expression(Expression* left, Expression* right)
|
|
: Binary_expression(left, right)
|
|
{ }
|
|
|
|
uint64_t
|
|
value(const Expression_eval_info* eei)
|
|
{
|
|
Output_section* left_section;
|
|
uint64_t left_alignment;
|
|
uint64_t left = this->left_value(eei, &left_section, &left_alignment);
|
|
Output_section* right_section;
|
|
uint64_t right_alignment;
|
|
uint64_t right = this->right_value(eei, &right_section, &right_alignment);
|
|
if (left_section == right_section)
|
|
{
|
|
if (eei->result_section_pointer != NULL)
|
|
*eei->result_section_pointer = left_section;
|
|
}
|
|
else if ((left_section != NULL || right_section != NULL)
|
|
&& parameters->options().relocatable())
|
|
gold_warning(_("max applied to section relative value"));
|
|
if (eei->result_alignment_pointer != NULL)
|
|
{
|
|
uint64_t ra = *eei->result_alignment_pointer;
|
|
if (left > right)
|
|
ra = std::max(ra, left_alignment);
|
|
else if (right > left)
|
|
ra = std::max(ra, right_alignment);
|
|
else
|
|
ra = std::max(ra, std::max(left_alignment, right_alignment));
|
|
*eei->result_alignment_pointer = ra;
|
|
}
|
|
return std::max(left, right);
|
|
}
|
|
|
|
void
|
|
print(FILE* f) const
|
|
{ this->print_function(f, "MAX"); }
|
|
};
|
|
|
|
extern "C" Expression*
|
|
script_exp_function_max(Expression* left, Expression* right)
|
|
{
|
|
return new Max_expression(left, right);
|
|
}
|
|
|
|
// Min function.
|
|
|
|
class Min_expression : public Binary_expression
|
|
{
|
|
public:
|
|
Min_expression(Expression* left, Expression* right)
|
|
: Binary_expression(left, right)
|
|
{ }
|
|
|
|
uint64_t
|
|
value(const Expression_eval_info* eei)
|
|
{
|
|
Output_section* left_section;
|
|
uint64_t left_alignment;
|
|
uint64_t left = this->left_value(eei, &left_section, &left_alignment);
|
|
Output_section* right_section;
|
|
uint64_t right_alignment;
|
|
uint64_t right = this->right_value(eei, &right_section, &right_alignment);
|
|
if (left_section == right_section)
|
|
{
|
|
if (eei->result_section_pointer != NULL)
|
|
*eei->result_section_pointer = left_section;
|
|
}
|
|
else if ((left_section != NULL || right_section != NULL)
|
|
&& parameters->options().relocatable())
|
|
gold_warning(_("min applied to section relative value"));
|
|
if (eei->result_alignment_pointer != NULL)
|
|
{
|
|
uint64_t ra = *eei->result_alignment_pointer;
|
|
if (left < right)
|
|
ra = std::max(ra, left_alignment);
|
|
else if (right < left)
|
|
ra = std::max(ra, right_alignment);
|
|
else
|
|
ra = std::max(ra, std::max(left_alignment, right_alignment));
|
|
*eei->result_alignment_pointer = ra;
|
|
}
|
|
return std::min(left, right);
|
|
}
|
|
|
|
void
|
|
print(FILE* f) const
|
|
{ this->print_function(f, "MIN"); }
|
|
};
|
|
|
|
extern "C" Expression*
|
|
script_exp_function_min(Expression* left, Expression* right)
|
|
{
|
|
return new Min_expression(left, right);
|
|
}
|
|
|
|
// Class Section_expression. This is a parent class used for
|
|
// functions which take the name of an output section.
|
|
|
|
class Section_expression : public Expression
|
|
{
|
|
public:
|
|
Section_expression(const char* section_name, size_t section_name_len)
|
|
: section_name_(section_name, section_name_len)
|
|
{ }
|
|
|
|
uint64_t
|
|
value(const Expression_eval_info*);
|
|
|
|
void
|
|
print(FILE* f) const
|
|
{ fprintf(f, "%s(%s)", this->function_name(), this->section_name_.c_str()); }
|
|
|
|
protected:
|
|
// The child class must implement this.
|
|
virtual uint64_t
|
|
value_from_output_section(const Expression_eval_info*,
|
|
Output_section*) = 0;
|
|
|
|
// The child class must implement this.
|
|
virtual uint64_t
|
|
value_from_script_output_section(uint64_t address, uint64_t load_address,
|
|
uint64_t addralign, uint64_t size) = 0;
|
|
|
|
// The child class must implement this.
|
|
virtual const char*
|
|
function_name() const = 0;
|
|
|
|
private:
|
|
std::string section_name_;
|
|
};
|
|
|
|
uint64_t
|
|
Section_expression::value(const Expression_eval_info* eei)
|
|
{
|
|
const char* section_name = this->section_name_.c_str();
|
|
Output_section* os = eei->layout->find_output_section(section_name);
|
|
if (os != NULL)
|
|
return this->value_from_output_section(eei, os);
|
|
|
|
uint64_t address;
|
|
uint64_t load_address;
|
|
uint64_t addralign;
|
|
uint64_t size;
|
|
const Script_options* ss = eei->layout->script_options();
|
|
if (ss->saw_sections_clause())
|
|
{
|
|
if (ss->script_sections()->get_output_section_info(section_name,
|
|
&address,
|
|
&load_address,
|
|
&addralign,
|
|
&size))
|
|
return this->value_from_script_output_section(address, load_address,
|
|
addralign, size);
|
|
}
|
|
|
|
gold_error("%s called on nonexistent output section '%s'",
|
|
this->function_name(), section_name);
|
|
return 0;
|
|
}
|
|
|
|
// ABSOLUTE function.
|
|
|
|
class Absolute_expression : public Unary_expression
|
|
{
|
|
public:
|
|
Absolute_expression(Expression* arg)
|
|
: Unary_expression(arg)
|
|
{ }
|
|
|
|
uint64_t
|
|
value(const Expression_eval_info* eei)
|
|
{
|
|
uint64_t ret = this->arg_value(eei, NULL);
|
|
// Force the value to be absolute.
|
|
if (eei->result_section_pointer != NULL)
|
|
*eei->result_section_pointer = NULL;
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
print(FILE* f) const
|
|
{
|
|
fprintf(f, "ABSOLUTE(");
|
|
this->arg_print(f);
|
|
fprintf(f, ")");
|
|
}
|
|
};
|
|
|
|
extern "C" Expression*
|
|
script_exp_function_absolute(Expression* arg)
|
|
{
|
|
return new Absolute_expression(arg);
|
|
}
|
|
|
|
// ALIGN function.
|
|
|
|
class Align_expression : public Binary_expression
|
|
{
|
|
public:
|
|
Align_expression(Expression* left, Expression* right)
|
|
: Binary_expression(left, right)
|
|
{ }
|
|
|
|
uint64_t
|
|
value(const Expression_eval_info* eei)
|
|
{
|
|
Output_section* align_section;
|
|
uint64_t align = this->right_value(eei, &align_section, NULL);
|
|
if (align_section != NULL
|
|
&& parameters->options().relocatable())
|
|
gold_warning(_("aligning to section relative value"));
|
|
|
|
if (eei->result_alignment_pointer != NULL
|
|
&& align > *eei->result_alignment_pointer)
|
|
{
|
|
uint64_t a = align;
|
|
while ((a & (a - 1)) != 0)
|
|
a &= a - 1;
|
|
*eei->result_alignment_pointer = a;
|
|
}
|
|
|
|
uint64_t value = this->left_value(eei, eei->result_section_pointer, NULL);
|
|
if (align <= 1)
|
|
return value;
|
|
return ((value + align - 1) / align) * align;
|
|
}
|
|
|
|
void
|
|
print(FILE* f) const
|
|
{ this->print_function(f, "ALIGN"); }
|
|
};
|
|
|
|
extern "C" Expression*
|
|
script_exp_function_align(Expression* left, Expression* right)
|
|
{
|
|
return new Align_expression(left, right);
|
|
}
|
|
|
|
// ASSERT function.
|
|
|
|
class Assert_expression : public Unary_expression
|
|
{
|
|
public:
|
|
Assert_expression(Expression* arg, const char* message, size_t length)
|
|
: Unary_expression(arg), message_(message, length)
|
|
{ }
|
|
|
|
uint64_t
|
|
value(const Expression_eval_info* eei)
|
|
{
|
|
uint64_t value = this->arg_value(eei, eei->result_section_pointer);
|
|
if (!value && eei->check_assertions)
|
|
gold_error("%s", this->message_.c_str());
|
|
return value;
|
|
}
|
|
|
|
void
|
|
print(FILE* f) const
|
|
{
|
|
fprintf(f, "ASSERT(");
|
|
this->arg_print(f);
|
|
fprintf(f, ", %s)", this->message_.c_str());
|
|
}
|
|
|
|
private:
|
|
std::string message_;
|
|
};
|
|
|
|
extern "C" Expression*
|
|
script_exp_function_assert(Expression* expr, const char* message,
|
|
size_t length)
|
|
{
|
|
return new Assert_expression(expr, message, length);
|
|
}
|
|
|
|
// ADDR function.
|
|
|
|
class Addr_expression : public Section_expression
|
|
{
|
|
public:
|
|
Addr_expression(const char* section_name, size_t section_name_len)
|
|
: Section_expression(section_name, section_name_len)
|
|
{ }
|
|
|
|
protected:
|
|
uint64_t
|
|
value_from_output_section(const Expression_eval_info* eei,
|
|
Output_section* os)
|
|
{
|
|
if (eei->result_section_pointer != NULL)
|
|
*eei->result_section_pointer = os;
|
|
if (os->is_address_valid())
|
|
return os->address();
|
|
*eei->is_valid_pointer = false;
|
|
return 0;
|
|
}
|
|
|
|
uint64_t
|
|
value_from_script_output_section(uint64_t address, uint64_t, uint64_t,
|
|
uint64_t)
|
|
{ return address; }
|
|
|
|
const char*
|
|
function_name() const
|
|
{ return "ADDR"; }
|
|
};
|
|
|
|
extern "C" Expression*
|
|
script_exp_function_addr(const char* section_name, size_t section_name_len)
|
|
{
|
|
return new Addr_expression(section_name, section_name_len);
|
|
}
|
|
|
|
// ALIGNOF.
|
|
|
|
class Alignof_expression : public Section_expression
|
|
{
|
|
public:
|
|
Alignof_expression(const char* section_name, size_t section_name_len)
|
|
: Section_expression(section_name, section_name_len)
|
|
{ }
|
|
|
|
protected:
|
|
uint64_t
|
|
value_from_output_section(const Expression_eval_info*,
|
|
Output_section* os)
|
|
{ return os->addralign(); }
|
|
|
|
uint64_t
|
|
value_from_script_output_section(uint64_t, uint64_t, uint64_t addralign,
|
|
uint64_t)
|
|
{ return addralign; }
|
|
|
|
const char*
|
|
function_name() const
|
|
{ return "ALIGNOF"; }
|
|
};
|
|
|
|
extern "C" Expression*
|
|
script_exp_function_alignof(const char* section_name, size_t section_name_len)
|
|
{
|
|
return new Alignof_expression(section_name, section_name_len);
|
|
}
|
|
|
|
// CONSTANT. It would be nice if we could simply evaluate this
|
|
// immediately and return an Integer_expression, but unfortunately we
|
|
// don't know the target.
|
|
|
|
class Constant_expression : public Expression
|
|
{
|
|
public:
|
|
Constant_expression(const char* name, size_t length);
|
|
|
|
uint64_t
|
|
value(const Expression_eval_info*);
|
|
|
|
void
|
|
print(FILE* f) const;
|
|
|
|
private:
|
|
enum Constant_function
|
|
{
|
|
CONSTANT_MAXPAGESIZE,
|
|
CONSTANT_COMMONPAGESIZE
|
|
};
|
|
|
|
Constant_function function_;
|
|
};
|
|
|
|
Constant_expression::Constant_expression(const char* name, size_t length)
|
|
{
|
|
if (length == 11 && strncmp(name, "MAXPAGESIZE", length) == 0)
|
|
this->function_ = CONSTANT_MAXPAGESIZE;
|
|
else if (length == 14 && strncmp(name, "COMMONPAGESIZE", length) == 0)
|
|
this->function_ = CONSTANT_COMMONPAGESIZE;
|
|
else
|
|
{
|
|
std::string s(name, length);
|
|
gold_error(_("unknown constant %s"), s.c_str());
|
|
this->function_ = CONSTANT_MAXPAGESIZE;
|
|
}
|
|
}
|
|
|
|
uint64_t
|
|
Constant_expression::value(const Expression_eval_info*)
|
|
{
|
|
switch (this->function_)
|
|
{
|
|
case CONSTANT_MAXPAGESIZE:
|
|
return parameters->target().abi_pagesize();
|
|
case CONSTANT_COMMONPAGESIZE:
|
|
return parameters->target().common_pagesize();
|
|
default:
|
|
gold_unreachable();
|
|
}
|
|
}
|
|
|
|
void
|
|
Constant_expression::print(FILE* f) const
|
|
{
|
|
const char* name;
|
|
switch (this->function_)
|
|
{
|
|
case CONSTANT_MAXPAGESIZE:
|
|
name = "MAXPAGESIZE";
|
|
break;
|
|
case CONSTANT_COMMONPAGESIZE:
|
|
name = "COMMONPAGESIZE";
|
|
break;
|
|
default:
|
|
gold_unreachable();
|
|
}
|
|
fprintf(f, "CONSTANT(%s)", name);
|
|
}
|
|
|
|
extern "C" Expression*
|
|
script_exp_function_constant(const char* name, size_t length)
|
|
{
|
|
return new Constant_expression(name, length);
|
|
}
|
|
|
|
// DATA_SEGMENT_ALIGN. FIXME: we don't implement this; we always fall
|
|
// back to the general case.
|
|
|
|
extern "C" Expression*
|
|
script_exp_function_data_segment_align(Expression* left, Expression*)
|
|
{
|
|
Expression* e1 = script_exp_function_align(script_exp_string(".", 1), left);
|
|
Expression* e2 = script_exp_binary_sub(left, script_exp_integer(1));
|
|
Expression* e3 = script_exp_binary_bitwise_and(script_exp_string(".", 1),
|
|
e2);
|
|
return script_exp_binary_add(e1, e3);
|
|
}
|
|
|
|
// DATA_SEGMENT_RELRO. FIXME: This is not implemented.
|
|
|
|
extern "C" Expression*
|
|
script_exp_function_data_segment_relro_end(Expression*, Expression* right)
|
|
{
|
|
return right;
|
|
}
|
|
|
|
// DATA_SEGMENT_END. FIXME: This is not implemented.
|
|
|
|
extern "C" Expression*
|
|
script_exp_function_data_segment_end(Expression* val)
|
|
{
|
|
return val;
|
|
}
|
|
|
|
// DEFINED function.
|
|
|
|
class Defined_expression : public Expression
|
|
{
|
|
public:
|
|
Defined_expression(const char* symbol_name, size_t symbol_name_len)
|
|
: symbol_name_(symbol_name, symbol_name_len)
|
|
{ }
|
|
|
|
uint64_t
|
|
value(const Expression_eval_info* eei)
|
|
{
|
|
Symbol* sym = eei->symtab->lookup(this->symbol_name_.c_str());
|
|
return sym != NULL && sym->is_defined();
|
|
}
|
|
|
|
void
|
|
print(FILE* f) const
|
|
{ fprintf(f, "DEFINED(%s)", this->symbol_name_.c_str()); }
|
|
|
|
private:
|
|
std::string symbol_name_;
|
|
};
|
|
|
|
extern "C" Expression*
|
|
script_exp_function_defined(const char* symbol_name, size_t symbol_name_len)
|
|
{
|
|
return new Defined_expression(symbol_name, symbol_name_len);
|
|
}
|
|
|
|
// LOADADDR function
|
|
|
|
class Loadaddr_expression : public Section_expression
|
|
{
|
|
public:
|
|
Loadaddr_expression(const char* section_name, size_t section_name_len)
|
|
: Section_expression(section_name, section_name_len)
|
|
{ }
|
|
|
|
protected:
|
|
uint64_t
|
|
value_from_output_section(const Expression_eval_info* eei,
|
|
Output_section* os)
|
|
{
|
|
if (os->has_load_address())
|
|
return os->load_address();
|
|
else
|
|
{
|
|
if (eei->result_section_pointer != NULL)
|
|
*eei->result_section_pointer = os;
|
|
return os->address();
|
|
}
|
|
}
|
|
|
|
uint64_t
|
|
value_from_script_output_section(uint64_t, uint64_t load_address, uint64_t,
|
|
uint64_t)
|
|
{ return load_address; }
|
|
|
|
const char*
|
|
function_name() const
|
|
{ return "LOADADDR"; }
|
|
};
|
|
|
|
extern "C" Expression*
|
|
script_exp_function_loadaddr(const char* section_name, size_t section_name_len)
|
|
{
|
|
return new Loadaddr_expression(section_name, section_name_len);
|
|
}
|
|
|
|
// SIZEOF function
|
|
|
|
class Sizeof_expression : public Section_expression
|
|
{
|
|
public:
|
|
Sizeof_expression(const char* section_name, size_t section_name_len)
|
|
: Section_expression(section_name, section_name_len)
|
|
{ }
|
|
|
|
protected:
|
|
uint64_t
|
|
value_from_output_section(const Expression_eval_info*,
|
|
Output_section* os)
|
|
{
|
|
// We can not use data_size here, as the size of the section may
|
|
// not have been finalized. Instead we get whatever the current
|
|
// size is. This will work correctly for backward references in
|
|
// linker scripts.
|
|
return os->current_data_size();
|
|
}
|
|
|
|
uint64_t
|
|
value_from_script_output_section(uint64_t, uint64_t, uint64_t,
|
|
uint64_t size)
|
|
{ return size; }
|
|
|
|
const char*
|
|
function_name() const
|
|
{ return "SIZEOF"; }
|
|
};
|
|
|
|
extern "C" Expression*
|
|
script_exp_function_sizeof(const char* section_name, size_t section_name_len)
|
|
{
|
|
return new Sizeof_expression(section_name, section_name_len);
|
|
}
|
|
|
|
// SIZEOF_HEADERS.
|
|
|
|
class Sizeof_headers_expression : public Expression
|
|
{
|
|
public:
|
|
Sizeof_headers_expression()
|
|
{ }
|
|
|
|
uint64_t
|
|
value(const Expression_eval_info*);
|
|
|
|
void
|
|
print(FILE* f) const
|
|
{ fprintf(f, "SIZEOF_HEADERS"); }
|
|
};
|
|
|
|
uint64_t
|
|
Sizeof_headers_expression::value(const Expression_eval_info* eei)
|
|
{
|
|
unsigned int ehdr_size;
|
|
unsigned int phdr_size;
|
|
if (parameters->target().get_size() == 32)
|
|
{
|
|
ehdr_size = elfcpp::Elf_sizes<32>::ehdr_size;
|
|
phdr_size = elfcpp::Elf_sizes<32>::phdr_size;
|
|
}
|
|
else if (parameters->target().get_size() == 64)
|
|
{
|
|
ehdr_size = elfcpp::Elf_sizes<64>::ehdr_size;
|
|
phdr_size = elfcpp::Elf_sizes<64>::phdr_size;
|
|
}
|
|
else
|
|
gold_unreachable();
|
|
|
|
return ehdr_size + phdr_size * eei->layout->expected_segment_count();
|
|
}
|
|
|
|
extern "C" Expression*
|
|
script_exp_function_sizeof_headers()
|
|
{
|
|
return new Sizeof_headers_expression();
|
|
}
|
|
|
|
// SEGMENT_START.
|
|
|
|
class Segment_start_expression : public Unary_expression
|
|
{
|
|
public:
|
|
Segment_start_expression(const char* segment_name, size_t segment_name_len,
|
|
Expression* default_value)
|
|
: Unary_expression(default_value),
|
|
segment_name_(segment_name, segment_name_len)
|
|
{ }
|
|
|
|
uint64_t
|
|
value(const Expression_eval_info*);
|
|
|
|
void
|
|
print(FILE* f) const
|
|
{
|
|
fprintf(f, "SEGMENT_START(\"%s\", ", this->segment_name_.c_str());
|
|
this->arg_print(f);
|
|
fprintf(f, ")");
|
|
}
|
|
|
|
private:
|
|
std::string segment_name_;
|
|
};
|
|
|
|
uint64_t
|
|
Segment_start_expression::value(const Expression_eval_info* eei)
|
|
{
|
|
// Check for command line overrides.
|
|
if (parameters->options().user_set_Ttext()
|
|
&& this->segment_name_ == ".text")
|
|
return parameters->options().Ttext();
|
|
else if (parameters->options().user_set_Tdata()
|
|
&& this->segment_name_ == ".data")
|
|
return parameters->options().Tdata();
|
|
else if (parameters->options().user_set_Tbss()
|
|
&& this->segment_name_ == ".bss")
|
|
return parameters->options().Tbss();
|
|
else
|
|
{
|
|
uint64_t ret = this->arg_value(eei, NULL);
|
|
// Force the value to be absolute.
|
|
if (eei->result_section_pointer != NULL)
|
|
*eei->result_section_pointer = NULL;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
extern "C" Expression*
|
|
script_exp_function_segment_start(const char* segment_name,
|
|
size_t segment_name_len,
|
|
Expression* default_value)
|
|
{
|
|
return new Segment_start_expression(segment_name, segment_name_len,
|
|
default_value);
|
|
}
|
|
|
|
} // End namespace gold.
|