[LinkerScript] Implement linker script expression evaluation

The expression evaluation is needed when interpreting linker scripts, in order
to calculate the value for new symbols or to determine a new position to load
sections in memory. This commit extends Expression nodes from the linker script
AST with evaluation functions, and also contains a unit test.

http://reviews.llvm.org/D8156

llvm-svn: 231707
This commit is contained in:
Rafael Auler 2015-03-09 21:43:35 +00:00
parent d157e19562
commit ad11d4c196
6 changed files with 155 additions and 4 deletions

View File

@ -51,7 +51,9 @@ const std::error_category &LinkerScriptReaderCategory();
enum class LinkerScriptReaderError {
success = 0,
parse_error
parse_error,
unknown_symbol_in_expr,
unrecognized_function_in_expr
};
inline std::error_code make_error_code(LinkerScriptReaderError e) {

View File

@ -301,6 +301,10 @@ public:
_scripts.push_back(std::move(script));
}
const std::vector<std::unique_ptr<script::Parser>> &scripts() const {
return _scripts;
}
// --wrap option.
void addWrapForSymbol(StringRef sym) { _wrapCalls.insert(sym); }

View File

@ -18,6 +18,7 @@
#include "lld/Core/Error.h"
#include "lld/Core/LLVM.h"
#include "lld/Core/range.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/MemoryBuffer.h"
@ -360,11 +361,16 @@ private:
///
class Expression {
public:
// The symbol table does not need to own its string keys and the use of StringMap
// here is an overkill.
typedef llvm::StringMap<int64_t, llvm::BumpPtrAllocator> SymbolTableTy;
enum class Kind { Constant, Symbol, FunctionCall, Unary, BinOp,
TernaryConditional };
Kind getKind() const { return _kind; }
inline llvm::BumpPtrAllocator &getAllocator() const;
virtual void dump(raw_ostream &os) const = 0;
virtual ErrorOr<int64_t> evalExpr(SymbolTableTy &symbolTable) const = 0;
virtual ~Expression() {}
protected:
@ -388,6 +394,8 @@ public:
return c->getKind() == Kind::Constant;
}
ErrorOr<int64_t> evalExpr(SymbolTableTy &symbolTable) const override;
private:
uint64_t _num;
};
@ -402,6 +410,8 @@ public:
return c->getKind() == Kind::Symbol;
}
ErrorOr<int64_t> evalExpr(SymbolTableTy &symbolTable) const override;
private:
StringRef _name;
};
@ -424,6 +434,8 @@ public:
return c->getKind() == Kind::FunctionCall;
}
ErrorOr<int64_t> evalExpr(SymbolTableTy &symbolTable) const override;
private:
StringRef _name;
llvm::ArrayRef<const Expression *> _args;
@ -444,6 +456,8 @@ public:
return c->getKind() == Kind::Unary;
}
ErrorOr<int64_t> evalExpr(SymbolTableTy &symbolTable) const override;
private:
Operation _op;
const Expression *_child;
@ -477,6 +491,8 @@ public:
return c->getKind() == Kind::BinOp;
}
ErrorOr<int64_t> evalExpr(SymbolTableTy &symbolTable) const override;
private:
Operation _op;
const Expression *_lhs;
@ -510,6 +526,8 @@ public:
return c->getKind() == Kind::TernaryConditional;
}
ErrorOr<int64_t> evalExpr(SymbolTableTy &symbolTable) const override;
private:
const Expression *_conditional;
const Expression *_trueExpr;
@ -530,7 +548,7 @@ private:
class SymbolAssignment : public Command {
public:
enum AssignmentKind { Simple, Sum, Sub, Mul, Div, Shl, Shr, And, Or };
enum AssignmentVisibility { Normal, Hidden, Provide, ProvideHidden };
enum AssignmentVisibility { Default, Hidden, Provide, ProvideHidden };
SymbolAssignment(Parser &ctx, StringRef name, const Expression *expr,
AssignmentKind kind, AssignmentVisibility visibility)
@ -542,6 +560,12 @@ public:
}
void dump(raw_ostream &os) const override;
const Expression *expr() const { return _expression; }
StringRef symbol() const { return _symbol; }
AssignmentKind assignmentKind() const { return _assignmentKind; }
AssignmentVisibility assignmentVisibility() const {
return _assignmentVisibility;
}
private:
const Expression *_expression;
@ -749,6 +773,8 @@ public:
/// Represents all the contents of the SECTIONS {} construct.
class Sections : public Command {
public:
typedef llvm::ArrayRef<const Command *>::const_iterator const_iterator;
Sections(Parser &ctx,
const SmallVectorImpl<const Command *> &sectionsCommands)
: Command(ctx, Kind::Sections) {
@ -765,6 +791,8 @@ public:
}
void dump(raw_ostream &os) const override;
const_iterator begin() const { return _sectionsCommands.begin(); }
const_iterator end() const { return _sectionsCommands.end(); }
private:
llvm::ArrayRef<const Command *> _sectionsCommands;

View File

@ -86,6 +86,11 @@ public:
return "Success";
case LinkerScriptReaderError::parse_error:
return "Error parsing linker script";
case LinkerScriptReaderError::unknown_symbol_in_expr:
return "Unknown symbol found when evaluating linker script expression";
case LinkerScriptReaderError::unrecognized_function_in_expr:
return "Unrecognized function call when evaluating linker script "
"expression";
}
llvm_unreachable("An enumerator of LinkerScriptReaderError does not have a "
"message defined.");

View File

@ -532,9 +532,20 @@ void Lexer::skipWhitespace() {
// Constant functions
void Constant::dump(raw_ostream &os) const { os << _num; }
ErrorOr<int64_t> Constant::evalExpr(SymbolTableTy &symbolTable) const {
return _num;
}
// Symbol functions
void Symbol::dump(raw_ostream &os) const { os << _name; }
ErrorOr<int64_t> Symbol::evalExpr(SymbolTableTy &symbolTable) const {
auto it = symbolTable.find(_name);
if (it == symbolTable.end())
return LinkerScriptReaderError::unknown_symbol_in_expr;
return it->second;
}
// FunctionCall functions
void FunctionCall::dump(raw_ostream &os) const {
os << _name << "(";
@ -546,6 +557,10 @@ void FunctionCall::dump(raw_ostream &os) const {
os << ")";
}
ErrorOr<int64_t> FunctionCall::evalExpr(SymbolTableTy &symbolTable) const {
return LinkerScriptReaderError::unrecognized_function_in_expr;
}
// Unary functions
void Unary::dump(raw_ostream &os) const {
os << "(";
@ -557,6 +572,22 @@ void Unary::dump(raw_ostream &os) const {
os << ")";
}
ErrorOr<int64_t> Unary::evalExpr(SymbolTableTy &symbolTable) const {
auto child = _child->evalExpr(symbolTable);
if (child.getError())
return child.getError();
int64_t childRes = *child;
switch (_op) {
case Unary::Minus:
return -childRes;
case Unary::Not:
return ~childRes;
}
llvm_unreachable("");
}
// BinOp functions
void BinOp::dump(raw_ostream &os) const {
os << "(";
@ -611,6 +642,37 @@ void BinOp::dump(raw_ostream &os) const {
os << ")";
}
ErrorOr<int64_t> BinOp::evalExpr(SymbolTableTy &symbolTable) const {
auto lhs = _lhs->evalExpr(symbolTable);
if (lhs.getError())
return lhs.getError();
auto rhs = _rhs->evalExpr(symbolTable);
if (rhs.getError())
return rhs.getError();
int64_t lhsRes = *lhs;
int64_t rhsRes = *rhs;
switch(_op) {
case And: return lhsRes & rhsRes;
case CompareDifferent: return lhsRes != rhsRes;
case CompareEqual: return lhsRes == rhsRes;
case CompareGreater: return lhsRes > rhsRes;
case CompareGreaterEqual: return lhsRes >= rhsRes;
case CompareLess: return lhsRes < rhsRes;
case CompareLessEqual: return lhsRes <= rhsRes;
case Div: return lhsRes / rhsRes;
case Mul: return lhsRes * rhsRes;
case Or: return lhsRes | rhsRes;
case Shl: return lhsRes << rhsRes;
case Shr: return lhsRes >> rhsRes;
case Sub: return lhsRes - rhsRes;
case Sum: return lhsRes + rhsRes;
}
llvm_unreachable("");
}
// TernaryConditional functions
void TernaryConditional::dump(raw_ostream &os) const {
_conditional->dump(os);
@ -620,11 +682,21 @@ void TernaryConditional::dump(raw_ostream &os) const {
_falseExpr->dump(os);
}
ErrorOr<int64_t>
TernaryConditional::evalExpr(SymbolTableTy &symbolTable) const {
auto conditional = _conditional->evalExpr(symbolTable);
if (conditional.getError())
return conditional.getError();
if (*conditional)
return _trueExpr->evalExpr(symbolTable);
return _falseExpr->evalExpr(symbolTable);
}
// SymbolAssignment functions
void SymbolAssignment::dump(raw_ostream &os) const {
int numParen = 0;
if (_assignmentVisibility != Normal) {
if (_assignmentVisibility != Default) {
switch (_assignmentVisibility) {
case Hidden:
os << "HIDDEN(";
@ -1354,7 +1426,7 @@ const SymbolAssignment *Parser::parseSymbolAssignment() {
_tok._kind == Token::kw_provide ||
_tok._kind == Token::kw_provide_hidden) &&
"Expected identifier!");
SymbolAssignment::AssignmentVisibility visibility = SymbolAssignment::Normal;
SymbolAssignment::AssignmentVisibility visibility = SymbolAssignment::Default;
SymbolAssignment::AssignmentKind kind;
int numParen = 0;

View File

@ -241,3 +241,43 @@ TEST_F(LinkerScriptTest, IgnoreSearchDirNoStdLib) {
std::vector<StringRef> paths = _ctx->getSearchPaths();
EXPECT_EQ((size_t)0, paths.size());
}
TEST_F(LinkerScriptTest, ExprEval) {
parse("SECTIONS { symbol = 0x4000 + 0x40; \n"
". = (symbol >= 0x4040)? (0x5001 * 2 & 0xFFF0) << 1 : 0}");
EXPECT_EQ((size_t)1, _ctx->scripts().size());
script::LinkerScript *ls = _ctx->scripts()[0]->get();
EXPECT_EQ((size_t)1, ls->_commands.size());
auto *secs = dyn_cast<const script::Sections>(*ls->_commands.begin());
EXPECT_TRUE(secs != nullptr);
EXPECT_EQ(2, secs->end() - secs->begin());
auto command = secs->begin();
auto *sa1 = dyn_cast<const script::SymbolAssignment>(*command);
EXPECT_TRUE(sa1 != nullptr);
EXPECT_EQ(script::SymbolAssignment::Simple, sa1->assignmentKind());
EXPECT_EQ(script::SymbolAssignment::Default, sa1->assignmentVisibility());
++command;
auto *sa2 = dyn_cast<const script::SymbolAssignment>(*command);
EXPECT_TRUE(sa2 != nullptr);
EXPECT_EQ(script::SymbolAssignment::Simple, sa2->assignmentKind());
EXPECT_EQ(script::SymbolAssignment::Default, sa2->assignmentVisibility());
script::Expression::SymbolTableTy mySymbolTable;
auto ans = sa1->expr()->evalExpr(mySymbolTable);
EXPECT_FALSE(ans.getError());
int64_t result = *ans;
EXPECT_EQ(0x4040, result);
mySymbolTable[sa1->symbol()] = result;
auto ans2 = sa2->expr()->evalExpr(mySymbolTable);
EXPECT_FALSE(ans2.getError());
result = *ans2;
EXPECT_EQ(0x14000, result);
EXPECT_EQ(0, sa2->symbol().compare(StringRef(".")));
}