diff --git a/include/llvm/ExecutionEngine/ObjectBuffer.h b/include/llvm/ExecutionEngine/ObjectBuffer.h index 071a42b6b76..6221d3b335d 100644 --- a/include/llvm/ExecutionEngine/ObjectBuffer.h +++ b/include/llvm/ExecutionEngine/ObjectBuffer.h @@ -39,7 +39,8 @@ public: /// returns a pointer to an object that is owned by the caller. However, /// the caller does not take ownership of the underlying memory. MemoryBuffer *getMemBuffer() const { - return MemoryBuffer::getMemBuffer(Buffer->getBuffer(), "", false); + return MemoryBuffer::getMemBuffer(Buffer->getBuffer(), + Buffer->getBufferIdentifier(), false); } const char *getBufferStart() const { return Buffer->getBufferStart(); } diff --git a/include/llvm/ExecutionEngine/RuntimeDyld.h b/include/llvm/ExecutionEngine/RuntimeDyld.h index 30c0d49ade0..f123ffb803b 100644 --- a/include/llvm/ExecutionEngine/RuntimeDyld.h +++ b/include/llvm/ExecutionEngine/RuntimeDyld.h @@ -29,6 +29,8 @@ class RuntimeDyldImpl; class ObjectImage; class RuntimeDyld { + friend class RuntimeDyldChecker; + RuntimeDyld(const RuntimeDyld &) LLVM_DELETED_FUNCTION; void operator=(const RuntimeDyld &) LLVM_DELETED_FUNCTION; diff --git a/include/llvm/ExecutionEngine/RuntimeDyldChecker.h b/include/llvm/ExecutionEngine/RuntimeDyldChecker.h new file mode 100644 index 00000000000..38a4ea1ad52 --- /dev/null +++ b/include/llvm/ExecutionEngine/RuntimeDyldChecker.h @@ -0,0 +1,98 @@ +//===---- RuntimeDyldChecker.h - RuntimeDyld tester framework -----*- C++ -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_RUNTIMEDYLDCHECKER_H +#define LLVM_RUNTIMEDYLDCHECKER_H + +#include "RuntimeDyld.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include + +namespace llvm { + +class MCDisassembler; +class MCInstPrinter; + +/// \brief RuntimeDyld invariant checker for verifying that RuntimeDyld has +/// correctly applied relocations. +/// +/// The RuntimeDyldChecker class evaluates expressions against an attached +/// RuntimeDyld instance to verify that relocations have been applied +/// correctly. +/// +/// The expression language supports basic pointer arithmetic and bit-masking, +/// and has limited disassembler integration for accessing instruction +/// operands and the next PC (program counter) address for each instruction. +/// +/// The language syntax is: +/// +/// check = expr '=' expr +/// +/// expr = binary_expr +/// | sliceable_expr +/// +/// sliceable_expr = '*{' number '}' load_addr_expr [slice] +/// | '(' expr ')' [slice] +/// | ident_expr [slice] +/// | number [slice] +/// +/// slice = '[' high-bit-index ':' low-bit-index ']' +/// +/// load_addr_expr = symbol +/// | '(' symbol '+' number ')' +/// | '(' symbol '-' number ')' +/// +/// ident_expr = 'decode_operand' '(' symbol ',' operand-index ')' +/// | 'next_pc' '(' symbol ')' +/// | symbol +/// +/// binary_expr = expr '+' expr +/// | expr '-' expr +/// | expr '&' expr +/// | expr '|' expr +/// | expr '<<' expr +/// | expr '>>' expr +/// +class RuntimeDyldChecker { + friend class RuntimeDyldCheckerExprEval; +public: + RuntimeDyldChecker(RuntimeDyld &RTDyld, + MCDisassembler *Disassembler, + MCInstPrinter *InstPrinter, + llvm::raw_ostream &ErrStream) + : RTDyld(*RTDyld.Dyld), Disassembler(Disassembler), + InstPrinter(InstPrinter), ErrStream(ErrStream) {} + + /// \brief Check a single expression against the attached RuntimeDyld + /// instance. + bool check(StringRef CheckExpr) const; + + /// \brief Scan the given memory buffer for lines beginning with the string + /// in RulePrefix. The remainder of the line is passed to the check + /// method to be evaluated as an expression. + bool checkAllRulesInBuffer(StringRef RulePrefix, MemoryBuffer *MemBuf) const; + +private: + + bool checkSymbolIsValidForLoad(StringRef Symbol) const; + uint64_t getSymbolAddress(StringRef Symbol) const; + uint64_t readMemoryAtSymbol(StringRef Symbol, int64_t Offset, + unsigned Size) const; + StringRef getSubsectionStartingAt(StringRef Name) const; + + RuntimeDyldImpl &RTDyld; + MCDisassembler *Disassembler; + MCInstPrinter *InstPrinter; + llvm::raw_ostream &ErrStream; +}; + +} // end namespace llvm + +#endif // LLVM_RUNTIMEDYLDCHECKER_H diff --git a/lib/ExecutionEngine/RuntimeDyld/CMakeLists.txt b/lib/ExecutionEngine/RuntimeDyld/CMakeLists.txt index cbf7cf14d49..eb1a60b60d0 100644 --- a/lib/ExecutionEngine/RuntimeDyld/CMakeLists.txt +++ b/lib/ExecutionEngine/RuntimeDyld/CMakeLists.txt @@ -1,6 +1,7 @@ add_llvm_library(LLVMRuntimeDyld GDBRegistrar.cpp RuntimeDyld.cpp + RuntimeDyldChecker.cpp RuntimeDyldELF.cpp RuntimeDyldMachO.cpp ) diff --git a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldChecker.cpp b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldChecker.cpp new file mode 100644 index 00000000000..8ed3b1697cf --- /dev/null +++ b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldChecker.cpp @@ -0,0 +1,640 @@ +//===--- RuntimeDyldChecker.cpp - RuntimeDyld tester framework --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/RuntimeDyldChecker.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDisassembler.h" +#include "llvm/MC/MCInst.h" +#include "llvm/Support/StringRefMemoryObject.h" +#include "RuntimeDyldImpl.h" +#include + +#define DEBUG_TYPE "rtdyld" + +using namespace llvm; + +namespace llvm { + + // Helper class that implements the language evaluated by RuntimeDyldChecker. + class RuntimeDyldCheckerExprEval { + public: + + RuntimeDyldCheckerExprEval(const RuntimeDyldChecker &Checker, + llvm::raw_ostream &ErrStream) + : Checker(Checker), ErrStream(ErrStream) {} + + bool evaluate(StringRef Expr) const { + // Expect equality expression of the form 'LHS = RHS'. + Expr = Expr.trim(); + size_t EQIdx = Expr.find('='); + + // Evaluate LHS. + StringRef LHSExpr = Expr.substr(0, EQIdx).rtrim(); + StringRef RemainingExpr; + EvalResult LHSResult; + std::tie(LHSResult, RemainingExpr) = + evalComplexExpr(evalSimpleExpr(LHSExpr)); + if (LHSResult.hasError()) + return handleError(Expr, LHSResult); + if (RemainingExpr != "") + return handleError(Expr, unexpectedToken(RemainingExpr, LHSExpr, "")); + + // Evaluate RHS. + StringRef RHSExpr = Expr.substr(EQIdx + 1).ltrim(); + EvalResult RHSResult; + std::tie(RHSResult, RemainingExpr) = + evalComplexExpr(evalSimpleExpr(RHSExpr)); + if (RHSResult.hasError()) + return handleError(Expr, RHSResult); + if (RemainingExpr != "") + return handleError(Expr, unexpectedToken(RemainingExpr, RHSExpr, "")); + + if (LHSResult.getValue() != RHSResult.getValue()) { + ErrStream << "Expression '" << Expr << "' is false: " + << format("0x%lx", LHSResult.getValue()) << " != " + << format("0x%lx", RHSResult.getValue()) << "\n"; + return false; + } + return true; + } + + private: + const RuntimeDyldChecker &Checker; + llvm::raw_ostream &ErrStream; + + enum class BinOpToken : unsigned { Invalid, Add, Sub, BitwiseAnd, + BitwiseOr, ShiftLeft, ShiftRight }; + + class EvalResult { + public: + EvalResult() + : Value(0), ErrorMsg("") {} + EvalResult(uint64_t Value) + : Value(Value), ErrorMsg("") {} + EvalResult(std::string ErrorMsg) + : Value(0), ErrorMsg(ErrorMsg) {} + uint64_t getValue() const { return Value; } + bool hasError() const { return ErrorMsg != ""; } + const std::string& getErrorMsg() const { return ErrorMsg; } + private: + uint64_t Value; + std::string ErrorMsg; + }; + + StringRef getTokenForError(StringRef Expr) const { + if (Expr.empty()) + return ""; + + StringRef Token, Remaining; + if (isalpha(Expr[0])) + std::tie(Token, Remaining) = parseSymbol(Expr); + else if (isdigit(Expr[0])) + std::tie(Token, Remaining) = parseNumberString(Expr); + else { + unsigned TokLen = 1; + if (Expr.startswith("<<") || Expr.startswith(">>")) + TokLen = 2; + Token = Expr.substr(0, TokLen); + } + return Token; + } + + EvalResult unexpectedToken(StringRef TokenStart, + StringRef SubExpr, + StringRef ErrText) const { + std::string ErrorMsg("Encountered unexpected token '"); + ErrorMsg += getTokenForError(TokenStart); + if (SubExpr != "") { + ErrorMsg += "' while parsing subexpression '"; + ErrorMsg += SubExpr; + } + ErrorMsg += "'"; + if (ErrText != "") { + ErrorMsg += " "; + ErrorMsg += ErrText; + } + return EvalResult(std::move(ErrorMsg)); + } + + bool handleError(StringRef Expr, const EvalResult &R) const { + assert(R.hasError() && "Not an error result."); + ErrStream << "Error evaluating expression '" << Expr << "': " + << R.getErrorMsg() << "\n"; + return false; + } + + std::pair parseBinOpToken(StringRef Expr) const { + if (Expr.empty()) + return std::make_pair(BinOpToken::Invalid, ""); + + // Handle the two 2-character tokens. + if (Expr.startswith("<<")) + return std::make_pair(BinOpToken::ShiftLeft, + Expr.substr(2).ltrim()); + if (Expr.startswith(">>")) + return std::make_pair(BinOpToken::ShiftRight, + Expr.substr(2).ltrim()); + + // Handle one-character tokens. + BinOpToken Op; + switch (Expr[0]) { + default: return std::make_pair(BinOpToken::Invalid, Expr); + case '+': Op = BinOpToken::Add; break; + case '-': Op = BinOpToken::Sub; break; + case '&': Op = BinOpToken::BitwiseAnd; break; + case '|': Op = BinOpToken::BitwiseOr; break; + } + + return std::make_pair(Op, Expr.substr(1).ltrim()); + } + + EvalResult computeBinOpResult(BinOpToken Op, const EvalResult &LHSResult, + const EvalResult &RHSResult) const { + switch (Op) { + default: llvm_unreachable("Tried to evaluate unrecognized operation."); + case BinOpToken::Add: + return EvalResult(LHSResult.getValue() + RHSResult.getValue()); + case BinOpToken::Sub: + return EvalResult(LHSResult.getValue() - RHSResult.getValue()); + case BinOpToken::BitwiseAnd: + return EvalResult(LHSResult.getValue() & RHSResult.getValue()); + case BinOpToken::BitwiseOr: + return EvalResult(LHSResult.getValue() | RHSResult.getValue()); + case BinOpToken::ShiftLeft: + return EvalResult(LHSResult.getValue() << RHSResult.getValue()); + case BinOpToken::ShiftRight: + return EvalResult(LHSResult.getValue() >> RHSResult.getValue()); + } + } + + // Parse a symbol and return a (string, string) pair representing the symbol + // name and expression remaining to be parsed. + std::pair parseSymbol(StringRef Expr) const { + size_t FirstNonSymbol = + Expr.find_first_not_of("0123456789" + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + ":_"); + return std::make_pair(Expr.substr(0, FirstNonSymbol), + Expr.substr(FirstNonSymbol).ltrim()); + } + + // Evaluate a call to decode_operand. Decode the instruction operand at the + // given symbol and get the value of the requested operand. + // Returns an error if the instruction cannot be decoded, or the requested + // operand is not an immediate. + // On success, retuns a pair containing the value of the operand, plus + // the expression remaining to be evaluated. + std::pair evalDecodeOperand(StringRef Expr) const { + if (!Expr.startswith("(")) + return std::make_pair(unexpectedToken(Expr, Expr, "expected '('"), ""); + StringRef RemainingExpr = Expr.substr(1).ltrim(); + StringRef Symbol; + std::tie(Symbol, RemainingExpr) = parseSymbol(RemainingExpr); + + if (!Checker.checkSymbolIsValidForLoad(Symbol)) + return std::make_pair(EvalResult(("Cannot decode unknown symbol '" + + Symbol + "'").str()), + ""); + + if (!RemainingExpr.startswith(",")) + return std::make_pair(unexpectedToken(RemainingExpr, RemainingExpr, + "expected ','"), + ""); + RemainingExpr = RemainingExpr.substr(1).ltrim(); + + EvalResult OpIdxExpr; + std::tie(OpIdxExpr, RemainingExpr) = evalNumberExpr(RemainingExpr); + if (OpIdxExpr.hasError()) + return std::make_pair(OpIdxExpr, ""); + + if (!RemainingExpr.startswith(")")) + return std::make_pair(unexpectedToken(RemainingExpr, RemainingExpr, + "expected ')'"), + ""); + RemainingExpr = RemainingExpr.substr(1).ltrim(); + + MCInst Inst; + uint64_t Size; + if (!decodeInst(Symbol, Inst, Size)) + return std::make_pair(EvalResult(("Couldn't decode instruction at '" + + Symbol + "'").str()), + ""); + + unsigned OpIdx = OpIdxExpr.getValue(); + if (OpIdx >= Inst.getNumOperands()) + return std::make_pair(EvalResult(("Invalid operand index '" + + std::to_string(OpIdx) + + " for instruction '" + Symbol + + ". Instruction has only " + + std::to_string(Inst.getNumOperands()) + + " operands.").str()), + ""); + + const MCOperand &Op = Inst.getOperand(OpIdx); + if (!Op.isImm()) { + std::string InstrString; + raw_string_ostream InstrStringStream(InstrString); + Inst.dump_pretty(InstrStringStream, + Checker.Disassembler->getContext().getAsmInfo(), + Checker.InstPrinter); + return std::make_pair(EvalResult(("Operand '" + std::to_string(OpIdx) + + "' of instruction '" + Symbol + + "' is not an immediate.\n" + "Instruction is:\n " + + InstrStringStream.str()).str()), + ""); + } + + return std::make_pair(EvalResult(Op.getImm()), RemainingExpr); + } + + // Evaluate a call to next_pc. Decode the instruction at the given + // symbol and return the following program counter.. + // Returns an error if the instruction cannot be decoded. + // On success, returns a pair containing the next PC, plus the length of the + // expression remaining to be evaluated. + std::pair evalNextPC(StringRef Expr) const { + if (!Expr.startswith("(")) + return std::make_pair(unexpectedToken(Expr, Expr, "expected '('"), ""); + StringRef RemainingExpr = Expr.substr(1).ltrim(); + StringRef Symbol; + std::tie(Symbol, RemainingExpr) = parseSymbol(RemainingExpr); + + if (!Checker.checkSymbolIsValidForLoad(Symbol)) + return std::make_pair(EvalResult(("Cannot decode unknown symbol '" + + Symbol + "'").str()), + ""); + + if (!RemainingExpr.startswith(")")) + return std::make_pair(unexpectedToken(RemainingExpr, RemainingExpr, + "expected ')'"), + ""); + RemainingExpr = RemainingExpr.substr(1).ltrim(); + + MCInst Inst; + uint64_t Size; + if (!decodeInst(Symbol, Inst, Size)) + return std::make_pair(EvalResult(("Couldn't decode instruction at '" + + Symbol + "'").str()), + ""); + uint64_t NextPC = Checker.getSymbolAddress(Symbol) + Size; + + return std::make_pair(EvalResult(NextPC), RemainingExpr); + } + + // Evaluate an identiefer expr, which may be a symbol, or a call to + // one of the builtin functions: get_insn_opcode or get_insn_length. + // Return the result, plus the expression remaining to be parsed. + std::pair evalIdentifierExpr(StringRef Expr) const { + StringRef Symbol; + StringRef RemainingExpr; + std::tie(Symbol, RemainingExpr) = parseSymbol(Expr); + + // Check for builtin function calls. + if (Symbol == "decode_operand") + return evalDecodeOperand(RemainingExpr); + else if (Symbol == "next_pc") + return evalNextPC(RemainingExpr); + + // Looks like a plain symbol reference. + return std::make_pair(EvalResult(Checker.getSymbolAddress(Symbol)), + RemainingExpr); + } + + // Parse a number (hexadecimal or decimal) and return a (string, string) + // pair representing the number and the expression remaining to be parsed. + std::pair parseNumberString(StringRef Expr) const { + size_t FirstNonDigit = StringRef::npos; + if (Expr.startswith("0x")) { + FirstNonDigit = Expr.find_first_not_of("0123456789abcdefABCDEF", 2); + if (FirstNonDigit == StringRef::npos) + FirstNonDigit = Expr.size(); + } else { + FirstNonDigit = Expr.find_first_not_of("0123456789"); + if (FirstNonDigit == StringRef::npos) + FirstNonDigit = Expr.size(); + } + return std::make_pair(Expr.substr(0, FirstNonDigit), + Expr.substr(FirstNonDigit)); + } + + // Evaluate a constant numeric expression (hexidecimal or decimal) and + // return a pair containing the result, and the expression remaining to be + // evaluated. + std::pair evalNumberExpr(StringRef Expr) const { + StringRef ValueStr; + StringRef RemainingExpr; + std::tie(ValueStr, RemainingExpr) = parseNumberString(Expr); + + if (ValueStr.empty() || !isdigit(ValueStr[0])) + return std::make_pair(unexpectedToken(RemainingExpr, RemainingExpr, + "expected number"), + ""); + uint64_t Value; + ValueStr.getAsInteger(0, Value); + return std::make_pair(EvalResult(Value), RemainingExpr); + } + + // Evaluate an expression of the form "()" and return a pair + // containing the result of evaluating , plus the expression + // remaining to be parsed. + std::pair evalParensExpr(StringRef Expr) const { + assert(Expr.startswith("(") && "Not a parenthesized expression"); + EvalResult SubExprResult; + StringRef RemainingExpr; + std::tie(SubExprResult, RemainingExpr) = + evalComplexExpr(evalSimpleExpr(Expr.substr(1).ltrim())); + if (SubExprResult.hasError()) + return std::make_pair(SubExprResult, ""); + if (!RemainingExpr.startswith(")")) + return std::make_pair(unexpectedToken(RemainingExpr, Expr, + "expected ')'"), + ""); + RemainingExpr = RemainingExpr.substr(1).ltrim(); + return std::make_pair(SubExprResult, RemainingExpr); + } + + // Evaluate an expression in one of the following forms: + // *{} + // *{}( + ) + // *{}( - ) + // Return a pair containing the result, plus the expression remaining to be + // parsed. + std::pair evalLoadExpr(StringRef Expr) const { + assert(Expr.startswith("*") && "Not a load expression"); + StringRef RemainingExpr = Expr.substr(1).ltrim(); + // Parse read size. + if (!RemainingExpr.startswith("{")) + return std::make_pair(EvalResult("Expected '{' following '*'."), ""); + RemainingExpr = RemainingExpr.substr(1).ltrim(); + EvalResult ReadSizeExpr; + std::tie(ReadSizeExpr, RemainingExpr) = evalNumberExpr(RemainingExpr); + if (ReadSizeExpr.hasError()) + return std::make_pair(ReadSizeExpr, RemainingExpr); + uint64_t ReadSize = ReadSizeExpr.getValue(); + if (ReadSize < 1 || ReadSize > 8) + return std::make_pair(EvalResult("Invalid size for dereference."), ""); + if (!RemainingExpr.startswith("}")) + return std::make_pair(EvalResult("Missing '}' for dereference."), ""); + RemainingExpr = RemainingExpr.substr(1).ltrim(); + + // Check for '(symbol +/- constant)' form. + bool SymbolPlusConstant = false; + if (RemainingExpr.startswith("(")) { + SymbolPlusConstant = true; + RemainingExpr = RemainingExpr.substr(1).ltrim(); + } + + // Read symbol. + StringRef Symbol; + std::tie(Symbol, RemainingExpr) = parseSymbol(RemainingExpr); + + if (!Checker.checkSymbolIsValidForLoad(Symbol)) + return std::make_pair(EvalResult(("Cannot dereference unknown symbol '" + + Symbol + "'").str()), + ""); + + // Set up defaut offset. + int64_t Offset = 0; + + // Handle "+/- constant)" portion if necessary. + if (SymbolPlusConstant) { + char OpChar = RemainingExpr[0]; + if (OpChar != '+' && OpChar != '-') + return std::make_pair(EvalResult("Invalid operator in load address."), + ""); + RemainingExpr = RemainingExpr.substr(1).ltrim(); + + EvalResult OffsetExpr; + std::tie(OffsetExpr, RemainingExpr) = evalNumberExpr(RemainingExpr); + + Offset = (OpChar == '+') ? + OffsetExpr.getValue() : -1 * OffsetExpr.getValue(); + + if (!RemainingExpr.startswith(")")) + return std::make_pair(EvalResult("Missing ')' in load address."), + ""); + + RemainingExpr = RemainingExpr.substr(1).ltrim(); + } + + return std::make_pair( + EvalResult(Checker.readMemoryAtSymbol(Symbol, Offset, ReadSize)), + RemainingExpr); + } + + // Evaluate a "simple" expression. This is any expression that _isn't_ an + // un-parenthesized binary expression. + // + // "Simple" expressions can be optionally bit-sliced. See evalSlicedExpr. + // + // Returns a pair containing the result of the evaluation, plus the + // expression remaining to be parsed. + std::pair evalSimpleExpr(StringRef Expr) const { + EvalResult SubExprResult; + StringRef RemainingExpr; + + if (Expr.empty()) + return std::make_pair(EvalResult("Unexpected end of expression"), ""); + + if (Expr[0] == '(') + std::tie(SubExprResult, RemainingExpr) = evalParensExpr(Expr); + else if (Expr[0] == '*') + std::tie(SubExprResult, RemainingExpr) = evalLoadExpr(Expr); + else if (isalpha(Expr[0])) + std::tie(SubExprResult, RemainingExpr) = evalIdentifierExpr(Expr); + else if (isdigit(Expr[0])) + std::tie(SubExprResult, RemainingExpr) = evalNumberExpr(Expr); + + if (SubExprResult.hasError()) + return std::make_pair(SubExprResult, RemainingExpr); + + // Evaluate bit-slice if present. + if (RemainingExpr.startswith("[")) + std::tie(SubExprResult, RemainingExpr) = + evalSliceExpr(std::make_pair(SubExprResult, RemainingExpr)); + + return std::make_pair(SubExprResult, RemainingExpr); + } + + // Evaluate a bit-slice of an expression. + // A bit-slice has the form "[high:low]". The result of evaluating a + // slice is the bits between high and low (inclusive) in the original + // expression, right shifted so that the "low" bit is in position 0 in the + // result. + // Returns a pair containing the result of the slice operation, plus the + // expression remaining to be parsed. + std::pair evalSliceExpr( + std::pair Ctx) const{ + EvalResult SubExprResult; + StringRef RemainingExpr; + std::tie(SubExprResult, RemainingExpr) = Ctx; + + assert(RemainingExpr.startswith("[") && "Not a slice expr."); + RemainingExpr = RemainingExpr.substr(1).ltrim(); + + EvalResult HighBitExpr; + std::tie(HighBitExpr, RemainingExpr) = evalNumberExpr(RemainingExpr); + + if (HighBitExpr.hasError()) + return std::make_pair(HighBitExpr, RemainingExpr); + + if (!RemainingExpr.startswith(":")) + return std::make_pair(unexpectedToken(RemainingExpr, RemainingExpr, + "expected ':'"), + ""); + RemainingExpr = RemainingExpr.substr(1).ltrim(); + + EvalResult LowBitExpr; + std::tie(LowBitExpr, RemainingExpr) = evalNumberExpr(RemainingExpr); + + if (LowBitExpr.hasError()) + return std::make_pair(LowBitExpr, RemainingExpr); + + if (!RemainingExpr.startswith("]")) + return std::make_pair(unexpectedToken(RemainingExpr, RemainingExpr, + "expected ']'"), + ""); + RemainingExpr = RemainingExpr.substr(1).ltrim(); + + unsigned HighBit = HighBitExpr.getValue(); + unsigned LowBit = LowBitExpr.getValue(); + uint64_t Mask = ((uint64_t)1 << (HighBit - LowBit + 1)) - 1; + uint64_t SlicedValue = (SubExprResult.getValue() >> LowBit) & Mask; + return std::make_pair(EvalResult(SlicedValue), RemainingExpr); + } + + // Evaluate a "complex" expression. + // Takes an already evaluated subexpression and checks for the presence of a + // binary operator, computing the result of the binary operation if one is + // found. Used to make arithmetic expressions left-associative. + // Returns a pair containing the ultimate result of evaluating the + // expression, plus the expression remaining to be evaluated. + std::pair evalComplexExpr( + std::pair Ctx) const { + EvalResult LHSResult; + StringRef RemainingExpr; + std::tie(LHSResult, RemainingExpr) = Ctx; + + // If there was an error, or there's nothing left to evaluate, return the + // result. + if (LHSResult.hasError() || RemainingExpr == "") + return std::make_pair(LHSResult, RemainingExpr); + + // Otherwise check if this is a binary expressioan. + BinOpToken BinOp; + std::tie(BinOp, RemainingExpr) = parseBinOpToken(RemainingExpr); + + // If this isn't a recognized expression just return. + if (BinOp == BinOpToken::Invalid) + return std::make_pair(LHSResult, RemainingExpr); + + // This is a recognized bin-op. Evaluate the RHS, then evaluate the binop. + EvalResult RHSResult; + std::tie(RHSResult, RemainingExpr) = evalSimpleExpr(RemainingExpr); + + // If there was an error evaluating the RHS, return it. + if (RHSResult.hasError()) + return std::make_pair(RHSResult, RemainingExpr); + + // This is a binary expression - evaluate and try to continue as a + // complex expr. + EvalResult ThisResult(computeBinOpResult(BinOp, LHSResult, RHSResult)); + + return evalComplexExpr(std::make_pair(ThisResult, RemainingExpr)); + } + + bool decodeInst(StringRef Symbol, MCInst &Inst, uint64_t &Size) const { + MCDisassembler *Dis = Checker.Disassembler; + StringRef SectionMem = Checker.getSubsectionStartingAt(Symbol); + StringRefMemoryObject SectionBytes(SectionMem, 0); + + MCDisassembler::DecodeStatus S = + Dis->getInstruction(Inst, Size, SectionBytes, 0, nulls(), nulls()); + + return (S == MCDisassembler::Success); + } + + }; + +} + +bool RuntimeDyldChecker::check(StringRef CheckExpr) const { + CheckExpr = CheckExpr.trim(); + DEBUG(llvm::dbgs() << "RuntimeDyldChecker: Checking '" << CheckExpr + << "'...\n"); + RuntimeDyldCheckerExprEval P(*this, ErrStream); + bool Result = P.evaluate(CheckExpr); + (void)Result; + DEBUG(llvm::dbgs() << "RuntimeDyldChecker: '" << CheckExpr << "' " + << (Result ? "passed" : "FAILED") << ".\n"); + return Result; +} + +bool RuntimeDyldChecker::checkAllRulesInBuffer(StringRef RulePrefix, + MemoryBuffer* MemBuf) const { + bool DidAllTestsPass = true; + unsigned NumRules = 0; + + const char *LineStart = MemBuf->getBufferStart(); + + // Eat whitespace. + while (LineStart != MemBuf->getBufferEnd() && + std::isspace(*LineStart)) + ++LineStart; + + while (LineStart != MemBuf->getBufferEnd() && *LineStart != '\0') { + const char *LineEnd = LineStart; + while (LineEnd != MemBuf->getBufferEnd() && + *LineEnd != '\r' && *LineEnd != '\n') + ++LineEnd; + + StringRef Line(LineStart, LineEnd - LineStart); + if (Line.startswith(RulePrefix)) { + DidAllTestsPass &= check(Line.substr(RulePrefix.size())); + ++NumRules; + } + + // Eat whitespace. + LineStart = LineEnd; + while (LineStart != MemBuf->getBufferEnd() && + std::isspace(*LineStart)) + ++LineStart; + } + return DidAllTestsPass && (NumRules != 0); +} + +bool RuntimeDyldChecker::checkSymbolIsValidForLoad(StringRef Symbol) const { + return RTDyld.getSymbolAddress(Symbol) != nullptr; +} + +uint64_t RuntimeDyldChecker::getSymbolAddress(StringRef Symbol) const { + return RTDyld.getAnySymbolRemoteAddress(Symbol); +} + +uint64_t RuntimeDyldChecker::readMemoryAtSymbol(StringRef Symbol, + int64_t Offset, + unsigned Size) const { + uint8_t *Src = RTDyld.getSymbolAddress(Symbol); + uint64_t Result = 0; + memcpy(&Result, Src + Offset, Size); + return Result; +} + +StringRef RuntimeDyldChecker::getSubsectionStartingAt(StringRef Name) const { + RuntimeDyldImpl::SymbolTableMap::const_iterator pos = + RTDyld.GlobalSymbolTable.find(Name); + if (pos == RTDyld.GlobalSymbolTable.end()) + return StringRef(); + RuntimeDyldImpl::SymbolLoc Loc = pos->second; + uint8_t *SectionAddr = RTDyld.getSectionAddress(Loc.first); + return StringRef(reinterpret_cast(SectionAddr) + Loc.second, + RTDyld.Sections[Loc.first].Size - Loc.second); +} diff --git a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h index 11cc3b246ac..0336cbac908 100644 --- a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h +++ b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h @@ -20,6 +20,7 @@ #include "llvm/ADT/Triple.h" #include "llvm/ExecutionEngine/ObjectImage.h" #include "llvm/ExecutionEngine/RuntimeDyld.h" +#include "llvm/ExecutionEngine/RuntimeDyldChecker.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" @@ -158,6 +159,15 @@ public: }; class RuntimeDyldImpl { + friend class RuntimeDyldChecker; +private: + + uint64_t getAnySymbolRemoteAddress(StringRef Symbol) { + if (uint64_t InternalSymbolAddr = getSymbolLoadAddress(Symbol)) + return InternalSymbolAddr; + return MemMgr->getSymbolAddress(Symbol); + } + protected: // The MemoryManager to load objects into. RTDyldMemoryManager *MemMgr; @@ -339,7 +349,8 @@ protected: public: RuntimeDyldImpl(RTDyldMemoryManager *mm) - : MemMgr(mm), ProcessAllSections(false), HasError(false) {} + : MemMgr(mm), ProcessAllSections(false), HasError(false) { + } virtual ~RuntimeDyldImpl(); @@ -349,7 +360,7 @@ public: ObjectImage *loadObject(ObjectImage *InputObject); - void *getSymbolAddress(StringRef Name) { + uint8_t* getSymbolAddress(StringRef Name) { // FIXME: Just look up as a function for now. Overly simple of course. // Work in progress. SymbolTableMap::const_iterator pos = GlobalSymbolTable.find(Name); diff --git a/test/ExecutionEngine/RuntimeDyld/MachO_x86-64_PIC_relocations.s b/test/ExecutionEngine/RuntimeDyld/MachO_x86-64_PIC_relocations.s new file mode 100644 index 00000000000..6c27b62dbff --- /dev/null +++ b/test/ExecutionEngine/RuntimeDyld/MachO_x86-64_PIC_relocations.s @@ -0,0 +1,32 @@ +# RUN: llvm-mc -triple=x86_64-apple-macosx10.9 -relocation-model=pic -filetype=obj -o %s.o %s +# RUN: llvm-rtdyld -triple=x86_64-apple-macosx10.9 -verify -check=%s %s.o +# RUN: rm %s.o + + .section __TEXT,__text,regular,pure_instructions + .globl foo + .align 4, 0x90 +foo: + retq + + .globl main + .align 4, 0x90 +main: +# Test PC-rel branch. +# rtdyld-check: decode_operand(insn1, 0) = foo - next_pc(insn1) +insn1: + callq foo + +# Test PC-rel signed. +# rtdyld-check: decode_operand(insn2, 4) = x - next_pc(insn2) +insn2: + movl x(%rip), %eax + movl $0, %eax + retq + + .section __DATA,__data + .globl x + .align 2 +x: + .long 5 + +.subsections_via_symbols diff --git a/tools/llvm-rtdyld/CMakeLists.txt b/tools/llvm-rtdyld/CMakeLists.txt index 3ad127fd73c..feb213489d3 100644 --- a/tools/llvm-rtdyld/CMakeLists.txt +++ b/tools/llvm-rtdyld/CMakeLists.txt @@ -1,6 +1,8 @@ set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} DebugInfo ExecutionEngine + MC RuntimeDyld Support ) diff --git a/tools/llvm-rtdyld/llvm-rtdyld.cpp b/tools/llvm-rtdyld/llvm-rtdyld.cpp index f1364d53503..fcda9e7f8a8 100644 --- a/tools/llvm-rtdyld/llvm-rtdyld.cpp +++ b/tools/llvm-rtdyld/llvm-rtdyld.cpp @@ -16,6 +16,13 @@ #include "llvm/ExecutionEngine/ObjectBuffer.h" #include "llvm/ExecutionEngine/ObjectImage.h" #include "llvm/ExecutionEngine/RuntimeDyld.h" +#include "llvm/ExecutionEngine/RuntimeDyldChecker.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDisassembler.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCRegisterInfo.h" #include "llvm/Object/MachO.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/DynamicLibrary.h" @@ -23,9 +30,12 @@ #include "llvm/Support/Memory.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/PrettyStackTrace.h" -#include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" #include + using namespace llvm; using namespace llvm::object; @@ -35,7 +45,8 @@ InputFileList(cl::Positional, cl::ZeroOrMore, enum ActionType { AC_Execute, - AC_PrintLineInfo + AC_PrintLineInfo, + AC_Verify }; static cl::opt @@ -45,6 +56,8 @@ Action(cl::desc("Action to perform:"), "Load, link, and execute the inputs."), clEnumValN(AC_PrintLineInfo, "printline", "Load, link, and print line information for each function."), + clEnumValN(AC_Verify, "verify", + "Load, link and verify the resulting memory image."), clEnumValEnd)); static cl::opt @@ -57,6 +70,14 @@ Dylibs("dylib", cl::desc("Add library."), cl::ZeroOrMore); +static cl::opt +TripleName("triple", cl::desc("Target triple for disassembler")); + +static cl::list +CheckFiles("check", + cl::desc("File containing RuntimeDyld verifier checks."), + cl::ZeroOrMore); + /* *** */ // A trivial memory manager that doesn't do anything fancy, just uses the @@ -139,7 +160,6 @@ static void loadDylibs() { } } - /* *** */ static int printLineInfoForInput() { @@ -263,6 +283,95 @@ static int executeInput() { return Main(1, Argv); } +static int checkAllExpressions(RuntimeDyldChecker &Checker) { + for (const auto& CheckerFileName : CheckFiles) { + std::unique_ptr CheckerFileBuf; + if (std::error_code EC = + MemoryBuffer::getFileOrSTDIN(CheckerFileName, CheckerFileBuf)) + return Error("unable to read input '" + CheckerFileName + "': " + + EC.message()); + + if (!Checker.checkAllRulesInBuffer("# rtdyld-check:", CheckerFileBuf.get())) + return Error("some checks in '" + CheckerFileName + "' failed"); + } + return 0; +} + +static int linkAndVerify() { + + // Check for missing triple. + if (TripleName == "") { + llvm::errs() << "Error: -triple required when running in -verify mode.\n"; + return 1; + } + + // Look up the target and build the disassembler. + Triple TheTriple(Triple::normalize(TripleName)); + std::string ErrorStr; + const Target *TheTarget = + TargetRegistry::lookupTarget("", TheTriple, ErrorStr); + if (!TheTarget) { + llvm::errs() << "Error accessing target '" << TripleName << "': " + << ErrorStr << "\n"; + return 1; + } + TripleName = TheTriple.getTriple(); + + std::unique_ptr STI( + TheTarget->createMCSubtargetInfo(TripleName, "", "")); + assert(STI && "Unable to create subtarget info!"); + + std::unique_ptr MRI(TheTarget->createMCRegInfo(TripleName)); + assert(MRI && "Unable to create target register info!"); + + std::unique_ptr MAI(TheTarget->createMCAsmInfo(*MRI, TripleName)); + assert(MAI && "Unable to create target asm info!"); + + MCContext Ctx(MAI.get(), MRI.get(), nullptr); + + std::unique_ptr Disassembler( + TheTarget->createMCDisassembler(*STI, Ctx)); + assert(Disassembler && "Unable to create disassembler!"); + + std::unique_ptr MII(TheTarget->createMCInstrInfo()); + + std::unique_ptr InstPrinter( + TheTarget->createMCInstPrinter(0, *MAI, *MII, *MRI, *STI)); + + // Load any dylibs requested on the command line. + loadDylibs(); + + // Instantiate a dynamic linker. + TrivialMemoryManager MemMgr; + RuntimeDyld Dyld(&MemMgr); + + // If we don't have any input files, read from stdin. + if (!InputFileList.size()) + InputFileList.push_back("-"); + for(unsigned i = 0, e = InputFileList.size(); i != e; ++i) { + // Load the input memory buffer. + std::unique_ptr InputBuffer; + std::unique_ptr LoadedObject; + if (std::error_code ec = + MemoryBuffer::getFileOrSTDIN(InputFileList[i], InputBuffer)) + return Error("unable to read input: '" + ec.message() + "'"); + + // Load the object file + LoadedObject.reset( + Dyld.loadObject(new ObjectBuffer(InputBuffer.release()))); + if (!LoadedObject) { + return Error(Dyld.getErrorString()); + } + } + + // Resolve all the relocations we can. + Dyld.resolveRelocations(); + + RuntimeDyldChecker Checker(Dyld, Disassembler.get(), InstPrinter.get(), + llvm::dbgs()); + return checkAllExpressions(Checker); +} + int main(int argc, char **argv) { sys::PrintStackTraceOnErrorSignal(); PrettyStackTraceProgram X(argc, argv); @@ -270,6 +379,10 @@ int main(int argc, char **argv) { ProgramName = argv[0]; llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllDisassemblers(); + cl::ParseCommandLineOptions(argc, argv, "llvm MC-JIT tool\n"); switch (Action) { @@ -277,5 +390,7 @@ int main(int argc, char **argv) { return executeInput(); case AC_PrintLineInfo: return printLineInfoForInput(); + case AC_Verify: + return linkAndVerify(); } }