// expression.cc -- expressions in linker scripts for gold // Copyright 2006, 2007, 2008 Free Software Foundation, Inc. // Written by Ian Lance Taylor . // This file is part of gold. // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, // MA 02110-1301, USA. #include "gold.h" #include #include "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 expressions can refer to the dot symbol. The dot symbol // is only available within a SECTIONS clause. bool is_dot_available; // Whether the dot symbol currently has a value. bool dot_has_value; // The current value of the dot symbol. uint64_t dot_value; // Points to the IS_ABSOLUTE variable, which is set to false if the // expression uses a value which is not absolute. bool* is_absolute; }; // Evaluate an expression. uint64_t Expression::eval(const Symbol_table* symtab, const Layout* layout) { bool dummy; return this->eval_maybe_dot(symtab, layout, false, false, 0, &dummy); } // Evaluate an expression which may refer to the dot symbol. uint64_t Expression::eval_with_dot(const Symbol_table* symtab, const Layout* layout, bool dot_has_value, uint64_t dot_value, bool* is_absolute) { return this->eval_maybe_dot(symtab, layout, true, dot_has_value, dot_value, is_absolute); } // 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 is_dot_available, bool dot_has_value, uint64_t dot_value, bool* is_absolute) { Expression_eval_info eei; eei.symtab = symtab; eei.layout = layout; eei.is_dot_available = is_dot_available; eei.dot_has_value = dot_has_value; eei.dot_value = dot_value; // We assume the value is absolute, and only set this to false if we // find a section relative reference. *is_absolute = true; eei.is_absolute = is_absolute; return this->value(&eei); } // 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(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 this symbol does not have an absolute value, then the whole // expression does not have an absolute value. This is not strictly // accurate: the subtraction of two symbols in the same section is // absolute. This is unlikely to matter in practice, as this value // is only used for error checking. if (!sym->value_is_absolute()) *eei->is_absolute = false; if (parameters->get_size() == 32) return eei->symtab->get_sized_symbol<32>(sym)->value(); else if (parameters->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; } else if (!eei->dot_has_value) { gold_error(_("invalid reference to dot symbol before " "it has been given a value")); return 0; } 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) const { return this->arg_->value(eei); } 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) \ { return OPERATOR this->arg_value(eei); } \ \ 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) const { return this->left_->value(eei); } uint64_t right_value(const Expression_eval_info* eei) const { return this->right_->value(eei); } 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. #define BINARY_EXPRESSION(NAME, OPERATOR) \ 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) \ { \ return (this->left_value(eei) \ OPERATOR this->right_value(eei)); \ } \ \ 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, *) BINARY_EXPRESSION(div, /) BINARY_EXPRESSION(mod, %) BINARY_EXPRESSION(add, +) BINARY_EXPRESSION(sub, -) BINARY_EXPRESSION(lshift, <<) BINARY_EXPRESSION(rshift, >>) BINARY_EXPRESSION(eq, ==) BINARY_EXPRESSION(ne, !=) BINARY_EXPRESSION(le, <=) BINARY_EXPRESSION(ge, >=) BINARY_EXPRESSION(lt, <) BINARY_EXPRESSION(gt, >) BINARY_EXPRESSION(bitwise_and, &) BINARY_EXPRESSION(bitwise_xor, ^) BINARY_EXPRESSION(bitwise_or, |) BINARY_EXPRESSION(logical_and, &&) BINARY_EXPRESSION(logical_or, ||) // 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) const { return this->arg1_->value(eei); } uint64_t arg2_value(const Expression_eval_info* eei) const { return this->arg2_->value(eei); } uint64_t arg3_value(const Expression_eval_info* eei) const { return this->arg3_->value(eei); } 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) { return (this->arg1_value(eei) ? this->arg2_value(eei) : this->arg3_value(eei)); } 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) { return std::max(this->left_value(eei), this->right_value(eei)); } 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) { return std::min(this->left_value(eei), this->right_value(eei)); } 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); } // 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) { uint64_t align = this->right_value(eei); uint64_t value = this->left_value(eei); 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); if (!value) 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 Expression { public: Addr_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, "ADDR(%s)", this->section_name_.c_str()); } private: std::string section_name_; }; uint64_t Addr_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) { gold_error("ADDR called on nonexistent output section '%s'", section_name); return 0; } // Note that the address of a section is an absolute address, and we // should not clear *EEI->IS_ABSOLUTE here. return os->address(); } 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); } // 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; } // 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->get_size() == 32) { ehdr_size = elfcpp::Elf_sizes<32>::ehdr_size; phdr_size = elfcpp::Elf_sizes<32>::phdr_size; } else if (parameters->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(); } // Functions. extern "C" Expression* script_exp_function_defined(const char*, size_t) { gold_fatal(_("DEFINED not implemented")); } extern "C" Expression* script_exp_function_alignof(const char*, size_t) { gold_fatal(_("ALIGNOF not implemented")); } extern "C" Expression* script_exp_function_sizeof(const char*, size_t) { gold_fatal(_("SIZEOF not implemented")); } extern "C" Expression* script_exp_function_loadaddr(const char*, size_t) { gold_fatal(_("LOADADDR not implemented")); } extern "C" Expression* script_exp_function_origin(const char*, size_t) { gold_fatal(_("ORIGIN not implemented")); } extern "C" Expression* script_exp_function_length(const char*, size_t) { gold_fatal(_("LENGTH not implemented")); } extern "C" Expression* script_exp_function_absolute(Expression*) { gold_fatal(_("ABSOLUTE not implemented")); } extern "C" Expression* script_exp_function_segment_start(const char*, size_t, Expression*) { gold_fatal(_("SEGMENT_START not implemented")); } } // End namespace gold.