mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-07 08:34:59 +00:00
[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:
parent
d157e19562
commit
ad11d4c196
@ -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) {
|
||||
|
@ -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); }
|
||||
|
||||
|
@ -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 *> §ionsCommands)
|
||||
: 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;
|
||||
|
@ -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.");
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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(".")));
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user