| //===--- RuntimeDyldChecker.cpp - RuntimeDyld tester framework --*- C++ -*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/ExecutionEngine/RuntimeDyldChecker.h" | |
| #include "RuntimeDyldCheckerImpl.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/MC/MCAsmInfo.h" | |
| #include "llvm/MC/MCContext.h" | |
| #include "llvm/MC/MCDisassembler/MCDisassembler.h" | |
| #include "llvm/MC/MCInst.h" | |
| #include "llvm/MC/MCInstPrinter.h" | |
| #include "llvm/MC/MCInstrInfo.h" | |
| #include "llvm/MC/MCRegisterInfo.h" | |
| #include "llvm/MC/MCSubtargetInfo.h" | |
| #include "llvm/MC/MCTargetOptions.h" | |
| #include "llvm/MC/TargetRegistry.h" | |
| #include "llvm/Support/Endian.h" | |
| #include "llvm/Support/MemoryBuffer.h" | |
| #include <cctype> | |
| #include <memory> | |
| #include <utility> | |
| #define DEBUG_TYPE "rtdyld" | |
| using namespace llvm; | |
| namespace { | |
| struct TargetInfo { | |
| const Target *TheTarget; | |
| std::unique_ptr<MCSubtargetInfo> STI; | |
| std::unique_ptr<MCRegisterInfo> MRI; | |
| std::unique_ptr<MCAsmInfo> MAI; | |
| std::unique_ptr<MCContext> Ctx; | |
| std::unique_ptr<MCDisassembler> Disassembler; | |
| std::unique_ptr<MCInstrInfo> MII; | |
| std::unique_ptr<MCInstPrinter> InstPrinter; | |
| }; | |
| } // anonymous namespace | |
| namespace llvm { | |
| // Helper class that implements the language evaluated by RuntimeDyldChecker. | |
| class RuntimeDyldCheckerExprEval { | |
| public: | |
| RuntimeDyldCheckerExprEval(const RuntimeDyldCheckerImpl &Checker, | |
| raw_ostream &ErrStream) | |
| : Checker(Checker) {} | |
| bool evaluate(StringRef Expr) const { | |
| // Expect equality expression of the form 'LHS = RHS'. | |
| Expr = Expr.trim(); | |
| size_t EQIdx = Expr.find('='); | |
| ParseContext OutsideLoad(false); | |
| // Evaluate LHS. | |
| StringRef LHSExpr = Expr.substr(0, EQIdx).rtrim(); | |
| StringRef RemainingExpr; | |
| EvalResult LHSResult; | |
| std::tie(LHSResult, RemainingExpr) = | |
| evalComplexExpr(evalSimpleExpr(LHSExpr, OutsideLoad), OutsideLoad); | |
| 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, OutsideLoad), OutsideLoad); | |
| if (RHSResult.hasError()) | |
| return handleError(Expr, RHSResult); | |
| if (RemainingExpr != "") | |
| return handleError(Expr, unexpectedToken(RemainingExpr, RHSExpr, "")); | |
| if (LHSResult.getValue() != RHSResult.getValue()) { | |
| Checker.ErrStream << "Expression '" << Expr << "' is false: " | |
| << format("0x%" PRIx64, LHSResult.getValue()) | |
| << " != " << format("0x%" PRIx64, RHSResult.getValue()) | |
| << "\n"; | |
| return false; | |
| } | |
| return true; | |
| } | |
| private: | |
| // RuntimeDyldCheckerExprEval requires some context when parsing exprs. In | |
| // particular, it needs to know whether a symbol is being evaluated in the | |
| // context of a load, in which case we want the linker's local address for | |
| // the symbol, or outside of a load, in which case we want the symbol's | |
| // address in the remote target. | |
| struct ParseContext { | |
| bool IsInsideLoad; | |
| ParseContext(bool IsInsideLoad) : IsInsideLoad(IsInsideLoad) {} | |
| }; | |
| const RuntimeDyldCheckerImpl &Checker; | |
| enum class BinOpToken : unsigned { | |
| Invalid, | |
| Add, | |
| Sub, | |
| BitwiseAnd, | |
| BitwiseOr, | |
| ShiftLeft, | |
| ShiftRight | |
| }; | |
| class EvalResult { | |
| public: | |
| EvalResult() : Value(0) {} | |
| EvalResult(uint64_t Value) : Value(Value) {} | |
| EvalResult(std::string ErrorMsg) | |
| : Value(0), ErrorMsg(std::move(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.starts_with("<<") || Expr.starts_with(">>")) | |
| 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."); | |
| Checker.ErrStream << "Error evaluating expression '" << Expr | |
| << "': " << R.getErrorMsg() << "\n"; | |
| return false; | |
| } | |
| std::pair<BinOpToken, StringRef> parseBinOpToken(StringRef Expr) const { | |
| if (Expr.empty()) | |
| return std::make_pair(BinOpToken::Invalid, ""); | |
| // Handle the two 2-character tokens. | |
| if (Expr.starts_with("<<")) | |
| return std::make_pair(BinOpToken::ShiftLeft, Expr.substr(2).ltrim()); | |
| if (Expr.starts_with(">>")) | |
| 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<StringRef, StringRef> 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, returns a pair containing the value of the operand, plus | |
| // the expression remaining to be evaluated. | |
| std::pair<EvalResult, StringRef> evalDecodeOperand(StringRef Expr) const { | |
| if (!Expr.starts_with("(")) | |
| return std::make_pair(unexpectedToken(Expr, Expr, "expected '('"), ""); | |
| StringRef RemainingExpr = Expr.substr(1).ltrim(); | |
| StringRef Symbol; | |
| std::tie(Symbol, RemainingExpr) = parseSymbol(RemainingExpr); | |
| if (!Checker.isSymbolValid(Symbol)) | |
| return std::make_pair( | |
| EvalResult(("Cannot decode unknown symbol '" + Symbol + "'").str()), | |
| ""); | |
| // if there is an offset number expr | |
| int64_t Offset = 0; | |
| BinOpToken BinOp; | |
| std::tie(BinOp, RemainingExpr) = parseBinOpToken(RemainingExpr); | |
| switch (BinOp) { | |
| case BinOpToken::Add: { | |
| EvalResult Number; | |
| std::tie(Number, RemainingExpr) = evalNumberExpr(RemainingExpr); | |
| Offset = Number.getValue(); | |
| break; | |
| } | |
| case BinOpToken::Invalid: | |
| break; | |
| default: | |
| return std::make_pair( | |
| unexpectedToken(RemainingExpr, RemainingExpr, | |
| "expected '+' for offset or ',' if no offset"), | |
| ""); | |
| } | |
| if (!RemainingExpr.starts_with(",")) | |
| 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.starts_with(")")) | |
| return std::make_pair( | |
| unexpectedToken(RemainingExpr, RemainingExpr, "expected ')'"), ""); | |
| RemainingExpr = RemainingExpr.substr(1).ltrim(); | |
| MCInst Inst; | |
| uint64_t Size; | |
| if (!decodeInst(Symbol, Inst, Size, Offset)) | |
| return std::make_pair( | |
| EvalResult(("Couldn't decode instruction at '" + Symbol + "'").str()), | |
| ""); | |
| unsigned OpIdx = OpIdxExpr.getValue(); | |
| auto printInst = [this](StringRef Symbol, MCInst Inst, | |
| raw_string_ostream &ErrMsgStream) { | |
| auto TT = Checker.getTripleForSymbol(Checker.getTargetFlag(Symbol)); | |
| auto TI = getTargetInfo(TT, Checker.getCPU(), Checker.getFeatures()); | |
| if (auto E = TI.takeError()) { | |
| errs() << "Error obtaining instruction printer: " | |
| << toString(std::move(E)) << "\n"; | |
| return; | |
| } | |
| Inst.dump_pretty(ErrMsgStream, TI->InstPrinter.get()); | |
| return; | |
| }; | |
| if (OpIdx >= Inst.getNumOperands()) { | |
| std::string ErrMsg; | |
| raw_string_ostream ErrMsgStream(ErrMsg); | |
| ErrMsgStream << "Invalid operand index '" << format("%i", OpIdx) | |
| << "' for instruction '" << Symbol | |
| << "'. Instruction has only " | |
| << format("%i", Inst.getNumOperands()) | |
| << " operands.\nInstruction is:\n "; | |
| printInst(Symbol, Inst, ErrMsgStream); | |
| return {EvalResult(std::move(ErrMsg)), ""}; | |
| } | |
| const MCOperand &Op = Inst.getOperand(OpIdx); | |
| if (!Op.isImm()) { | |
| std::string ErrMsg; | |
| raw_string_ostream ErrMsgStream(ErrMsg); | |
| ErrMsgStream << "Operand '" << format("%i", OpIdx) << "' of instruction '" | |
| << Symbol << "' is not an immediate.\nInstruction is:\n "; | |
| printInst(Symbol, Inst, ErrMsgStream); | |
| return {EvalResult(std::move(ErrMsg)), ""}; | |
| } | |
| 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 of the | |
| // expression remaining to be evaluated. | |
| std::pair<EvalResult, StringRef> evalNextPC(StringRef Expr, | |
| ParseContext PCtx) const { | |
| if (!Expr.starts_with("(")) | |
| return std::make_pair(unexpectedToken(Expr, Expr, "expected '('"), ""); | |
| StringRef RemainingExpr = Expr.substr(1).ltrim(); | |
| StringRef Symbol; | |
| std::tie(Symbol, RemainingExpr) = parseSymbol(RemainingExpr); | |
| if (!Checker.isSymbolValid(Symbol)) | |
| return std::make_pair( | |
| EvalResult(("Cannot decode unknown symbol '" + Symbol + "'").str()), | |
| ""); | |
| if (!RemainingExpr.starts_with(")")) | |
| return std::make_pair( | |
| unexpectedToken(RemainingExpr, RemainingExpr, "expected ')'"), ""); | |
| RemainingExpr = RemainingExpr.substr(1).ltrim(); | |
| MCInst Inst; | |
| uint64_t InstSize; | |
| if (!decodeInst(Symbol, Inst, InstSize, 0)) | |
| return std::make_pair( | |
| EvalResult(("Couldn't decode instruction at '" + Symbol + "'").str()), | |
| ""); | |
| uint64_t SymbolAddr = PCtx.IsInsideLoad | |
| ? Checker.getSymbolLocalAddr(Symbol) | |
| : Checker.getSymbolRemoteAddr(Symbol); | |
| // ARM PC offset is 8 instead of 4, because it accounts for an additional | |
| // prefetch instruction that increments PC even though it is implicit. | |
| auto TT = Checker.getTripleForSymbol(Checker.getTargetFlag(Symbol)); | |
| uint64_t PCOffset = TT.getArch() == Triple::ArchType::arm ? 4 : 0; | |
| uint64_t NextPC = SymbolAddr + InstSize + PCOffset; | |
| return std::make_pair(EvalResult(NextPC), RemainingExpr); | |
| } | |
| // Evaluate a call to stub_addr/got_addr. | |
| // Look up and return the address of the stub for the given | |
| // (<file name>, <section name>, <symbol name>) tuple. | |
| // On success, returns a pair containing the stub address, plus the expression | |
| // remaining to be evaluated. | |
| std::pair<EvalResult, StringRef> | |
| evalStubOrGOTAddr(StringRef Expr, ParseContext PCtx, bool IsStubAddr) const { | |
| if (!Expr.starts_with("(")) | |
| return std::make_pair(unexpectedToken(Expr, Expr, "expected '('"), ""); | |
| StringRef RemainingExpr = Expr.substr(1).ltrim(); | |
| // Handle file-name specially, as it may contain characters that aren't | |
| // legal for symbols. | |
| StringRef StubContainerName; | |
| size_t ComaIdx = RemainingExpr.find(','); | |
| StubContainerName = RemainingExpr.substr(0, ComaIdx).rtrim(); | |
| RemainingExpr = RemainingExpr.substr(ComaIdx).ltrim(); | |
| if (!RemainingExpr.starts_with(",")) | |
| return std::make_pair( | |
| unexpectedToken(RemainingExpr, Expr, "expected ','"), ""); | |
| RemainingExpr = RemainingExpr.substr(1).ltrim(); | |
| StringRef Symbol; | |
| std::tie(Symbol, RemainingExpr) = parseSymbol(RemainingExpr); | |
| // Parse optional parameter to filter by stub kind | |
| StringRef KindNameFilter; | |
| if (RemainingExpr.starts_with(",")) { | |
| RemainingExpr = RemainingExpr.substr(1).ltrim(); | |
| size_t ClosingBracket = RemainingExpr.find(")"); | |
| KindNameFilter = RemainingExpr.substr(0, ClosingBracket); | |
| RemainingExpr = RemainingExpr.substr(ClosingBracket); | |
| } | |
| if (!RemainingExpr.starts_with(")")) | |
| return std::make_pair( | |
| unexpectedToken(RemainingExpr, Expr, "expected ')'"), ""); | |
| RemainingExpr = RemainingExpr.substr(1).ltrim(); | |
| uint64_t StubAddr; | |
| std::string ErrorMsg; | |
| std::tie(StubAddr, ErrorMsg) = | |
| Checker.getStubOrGOTAddrFor(StubContainerName, Symbol, KindNameFilter, | |
| PCtx.IsInsideLoad, IsStubAddr); | |
| if (ErrorMsg != "") | |
| return std::make_pair(EvalResult(ErrorMsg), ""); | |
| return std::make_pair(EvalResult(StubAddr), RemainingExpr); | |
| } | |
| std::pair<EvalResult, StringRef> evalSectionAddr(StringRef Expr, | |
| ParseContext PCtx) const { | |
| if (!Expr.starts_with("(")) | |
| return std::make_pair(unexpectedToken(Expr, Expr, "expected '('"), ""); | |
| StringRef RemainingExpr = Expr.substr(1).ltrim(); | |
| // Handle file-name specially, as it may contain characters that aren't | |
| // legal for symbols. | |
| StringRef FileName; | |
| size_t ComaIdx = RemainingExpr.find(','); | |
| FileName = RemainingExpr.substr(0, ComaIdx).rtrim(); | |
| RemainingExpr = RemainingExpr.substr(ComaIdx).ltrim(); | |
| if (!RemainingExpr.starts_with(",")) | |
| return std::make_pair( | |
| unexpectedToken(RemainingExpr, Expr, "expected ','"), ""); | |
| RemainingExpr = RemainingExpr.substr(1).ltrim(); | |
| StringRef SectionName; | |
| size_t CloseParensIdx = RemainingExpr.find(')'); | |
| SectionName = RemainingExpr.substr(0, CloseParensIdx).rtrim(); | |
| RemainingExpr = RemainingExpr.substr(CloseParensIdx).ltrim(); | |
| if (!RemainingExpr.starts_with(")")) | |
| return std::make_pair( | |
| unexpectedToken(RemainingExpr, Expr, "expected ')'"), ""); | |
| RemainingExpr = RemainingExpr.substr(1).ltrim(); | |
| uint64_t StubAddr; | |
| std::string ErrorMsg; | |
| std::tie(StubAddr, ErrorMsg) = Checker.getSectionAddr( | |
| FileName, SectionName, PCtx.IsInsideLoad); | |
| if (ErrorMsg != "") | |
| return std::make_pair(EvalResult(ErrorMsg), ""); | |
| return std::make_pair(EvalResult(StubAddr), RemainingExpr); | |
| } | |
| // Evaluate an identifier 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<EvalResult, StringRef> evalIdentifierExpr(StringRef Expr, | |
| ParseContext PCtx) 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, PCtx); | |
| else if (Symbol == "stub_addr") | |
| return evalStubOrGOTAddr(RemainingExpr, PCtx, true); | |
| else if (Symbol == "got_addr") | |
| return evalStubOrGOTAddr(RemainingExpr, PCtx, false); | |
| else if (Symbol == "section_addr") | |
| return evalSectionAddr(RemainingExpr, PCtx); | |
| if (!Checker.isSymbolValid(Symbol)) { | |
| std::string ErrMsg("No known address for symbol '"); | |
| ErrMsg += Symbol; | |
| ErrMsg += "'"; | |
| if (Symbol.starts_with("L")) | |
| ErrMsg += " (this appears to be an assembler local label - " | |
| " perhaps drop the 'L'?)"; | |
| return std::make_pair(EvalResult(ErrMsg), ""); | |
| } | |
| // The value for the symbol depends on the context we're evaluating in: | |
| // Inside a load this is the address in the linker's memory, outside a | |
| // load it's the address in the target processes memory. | |
| uint64_t Value = PCtx.IsInsideLoad ? Checker.getSymbolLocalAddr(Symbol) | |
| : Checker.getSymbolRemoteAddr(Symbol); | |
| // Looks like a plain symbol reference. | |
| return std::make_pair(EvalResult(Value), 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<StringRef, StringRef> parseNumberString(StringRef Expr) const { | |
| size_t FirstNonDigit = StringRef::npos; | |
| if (Expr.starts_with("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 (hexadecimal or decimal) and | |
| // return a pair containing the result, and the expression remaining to be | |
| // evaluated. | |
| std::pair<EvalResult, StringRef> 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 "(<expr>)" and return a pair | |
| // containing the result of evaluating <expr>, plus the expression | |
| // remaining to be parsed. | |
| std::pair<EvalResult, StringRef> evalParensExpr(StringRef Expr, | |
| ParseContext PCtx) const { | |
| assert(Expr.starts_with("(") && "Not a parenthesized expression"); | |
| EvalResult SubExprResult; | |
| StringRef RemainingExpr; | |
| std::tie(SubExprResult, RemainingExpr) = | |
| evalComplexExpr(evalSimpleExpr(Expr.substr(1).ltrim(), PCtx), PCtx); | |
| if (SubExprResult.hasError()) | |
| return std::make_pair(SubExprResult, ""); | |
| if (!RemainingExpr.starts_with(")")) | |
| 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: | |
| // *{<number>}<expr> | |
| // Return a pair containing the result, plus the expression remaining to be | |
| // parsed. | |
| std::pair<EvalResult, StringRef> evalLoadExpr(StringRef Expr) const { | |
| assert(Expr.starts_with("*") && "Not a load expression"); | |
| StringRef RemainingExpr = Expr.substr(1).ltrim(); | |
| // Parse read size. | |
| if (!RemainingExpr.starts_with("{")) | |
| 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.starts_with("}")) | |
| return std::make_pair(EvalResult("Missing '}' for dereference."), ""); | |
| RemainingExpr = RemainingExpr.substr(1).ltrim(); | |
| // Evaluate the expression representing the load address. | |
| ParseContext LoadCtx(true); | |
| EvalResult LoadAddrExprResult; | |
| std::tie(LoadAddrExprResult, RemainingExpr) = | |
| evalComplexExpr(evalSimpleExpr(RemainingExpr, LoadCtx), LoadCtx); | |
| if (LoadAddrExprResult.hasError()) | |
| return std::make_pair(LoadAddrExprResult, ""); | |
| uint64_t LoadAddr = LoadAddrExprResult.getValue(); | |
| // If there is no error but the content pointer is null then this is a | |
| // zero-fill symbol/section. | |
| if (LoadAddr == 0) | |
| return std::make_pair(0, RemainingExpr); | |
| return std::make_pair( | |
| EvalResult(Checker.readMemoryAtAddr(LoadAddr, 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<EvalResult, StringRef> evalSimpleExpr(StringRef Expr, | |
| ParseContext PCtx) 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, PCtx); | |
| else if (Expr[0] == '*') | |
| std::tie(SubExprResult, RemainingExpr) = evalLoadExpr(Expr); | |
| else if (isalpha(Expr[0]) || Expr[0] == '_') | |
| std::tie(SubExprResult, RemainingExpr) = evalIdentifierExpr(Expr, PCtx); | |
| else if (isdigit(Expr[0])) | |
| std::tie(SubExprResult, RemainingExpr) = evalNumberExpr(Expr); | |
| else | |
| return std::make_pair( | |
| unexpectedToken(Expr, Expr, | |
| "expected '(', '*', identifier, or number"), ""); | |
| if (SubExprResult.hasError()) | |
| return std::make_pair(SubExprResult, RemainingExpr); | |
| // Evaluate bit-slice if present. | |
| if (RemainingExpr.starts_with("[")) | |
| 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 "<expr>[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<EvalResult, StringRef> | |
| evalSliceExpr(const std::pair<EvalResult, StringRef> &Ctx) const { | |
| EvalResult SubExprResult; | |
| StringRef RemainingExpr; | |
| std::tie(SubExprResult, RemainingExpr) = Ctx; | |
| assert(RemainingExpr.starts_with("[") && "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.starts_with(":")) | |
| 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.starts_with("]")) | |
| 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<EvalResult, StringRef> | |
| evalComplexExpr(const std::pair<EvalResult, StringRef> &LHSAndRemaining, | |
| ParseContext PCtx) const { | |
| EvalResult LHSResult; | |
| StringRef RemainingExpr; | |
| std::tie(LHSResult, RemainingExpr) = LHSAndRemaining; | |
| // 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 expression. | |
| 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, PCtx); | |
| // 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), PCtx); | |
| } | |
| bool decodeInst(StringRef Symbol, MCInst &Inst, uint64_t &Size, | |
| int64_t Offset) const { | |
| auto TT = Checker.getTripleForSymbol(Checker.getTargetFlag(Symbol)); | |
| auto TI = getTargetInfo(TT, Checker.getCPU(), Checker.getFeatures()); | |
| if (auto E = TI.takeError()) { | |
| errs() << "Error obtaining disassembler: " << toString(std::move(E)) | |
| << "\n"; | |
| return false; | |
| } | |
| StringRef SymbolMem = Checker.getSymbolContent(Symbol); | |
| ArrayRef<uint8_t> SymbolBytes(SymbolMem.bytes_begin() + Offset, | |
| SymbolMem.size() - Offset); | |
| MCDisassembler::DecodeStatus S = | |
| TI->Disassembler->getInstruction(Inst, Size, SymbolBytes, 0, nulls()); | |
| return (S == MCDisassembler::Success); | |
| } | |
| Expected<TargetInfo> getTargetInfo(const Triple &TT, const StringRef &CPU, | |
| const SubtargetFeatures &TF) const { | |
| std::string ErrorStr; | |
| const Target *TheTarget = TargetRegistry::lookupTarget(TT, ErrorStr); | |
| if (!TheTarget) | |
| return make_error<StringError>("Error accessing target '" + TT.str() + | |
| "': " + ErrorStr, | |
| inconvertibleErrorCode()); | |
| std::unique_ptr<MCSubtargetInfo> STI( | |
| TheTarget->createMCSubtargetInfo(TT, CPU, TF.getString())); | |
| if (!STI) | |
| return make_error<StringError>("Unable to create subtarget for " + | |
| TT.str(), | |
| inconvertibleErrorCode()); | |
| std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TT)); | |
| if (!MRI) | |
| return make_error<StringError>("Unable to create target register info " | |
| "for " + | |
| TT.str(), | |
| inconvertibleErrorCode()); | |
| MCTargetOptions MCOptions; | |
| std::unique_ptr<MCAsmInfo> MAI( | |
| TheTarget->createMCAsmInfo(*MRI, TT, MCOptions)); | |
| if (!MAI) | |
| return make_error<StringError>("Unable to create target asm info " + | |
| TT.str(), | |
| inconvertibleErrorCode()); | |
| auto Ctx = std::make_unique<MCContext>(Triple(TT.str()), *MAI, *MRI, *STI); | |
| std::unique_ptr<MCDisassembler> Disassembler( | |
| TheTarget->createMCDisassembler(*STI, *Ctx)); | |
| if (!Disassembler) | |
| return make_error<StringError>("Unable to create disassembler for " + | |
| TT.str(), | |
| inconvertibleErrorCode()); | |
| std::unique_ptr<MCInstrInfo> MII(TheTarget->createMCInstrInfo()); | |
| if (!MII) | |
| return make_error<StringError>("Unable to create instruction info for" + | |
| TT.str(), | |
| inconvertibleErrorCode()); | |
| std::unique_ptr<MCInstPrinter> InstPrinter( | |
| TheTarget->createMCInstPrinter(TT, 0, *MAI, *MII, *MRI)); | |
| if (!InstPrinter) | |
| return make_error<StringError>( | |
| "Unable to create instruction printer for" + TT.str(), | |
| inconvertibleErrorCode()); | |
| return TargetInfo({TheTarget, std::move(STI), std::move(MRI), | |
| std::move(MAI), std::move(Ctx), std::move(Disassembler), | |
| std::move(MII), std::move(InstPrinter)}); | |
| } | |
| }; | |
| } // namespace llvm | |
| RuntimeDyldCheckerImpl::RuntimeDyldCheckerImpl( | |
| IsSymbolValidFunction IsSymbolValid, GetSymbolInfoFunction GetSymbolInfo, | |
| GetSectionInfoFunction GetSectionInfo, GetStubInfoFunction GetStubInfo, | |
| GetGOTInfoFunction GetGOTInfo, llvm::endianness Endianness, Triple TT, | |
| StringRef CPU, SubtargetFeatures TF, raw_ostream &ErrStream) | |
| : IsSymbolValid(std::move(IsSymbolValid)), | |
| GetSymbolInfo(std::move(GetSymbolInfo)), | |
| GetSectionInfo(std::move(GetSectionInfo)), | |
| GetStubInfo(std::move(GetStubInfo)), GetGOTInfo(std::move(GetGOTInfo)), | |
| Endianness(Endianness), TT(std::move(TT)), CPU(std::move(CPU)), | |
| TF(std::move(TF)), ErrStream(ErrStream) {} | |
| bool RuntimeDyldCheckerImpl::check(StringRef CheckExpr) const { | |
| CheckExpr = CheckExpr.trim(); | |
| LLVM_DEBUG(dbgs() << "RuntimeDyldChecker: Checking '" << CheckExpr | |
| << "'...\n"); | |
| RuntimeDyldCheckerExprEval P(*this, ErrStream); | |
| bool Result = P.evaluate(CheckExpr); | |
| (void)Result; | |
| LLVM_DEBUG(dbgs() << "RuntimeDyldChecker: '" << CheckExpr << "' " | |
| << (Result ? "passed" : "FAILED") << ".\n"); | |
| return Result; | |
| } | |
| bool RuntimeDyldCheckerImpl::checkAllRulesInBuffer(StringRef RulePrefix, | |
| MemoryBuffer *MemBuf) const { | |
| bool DidAllTestsPass = true; | |
| unsigned NumRules = 0; | |
| std::string CheckExpr; | |
| const char *LineStart = MemBuf->getBufferStart(); | |
| // Eat whitespace. | |
| while (LineStart != MemBuf->getBufferEnd() && 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.starts_with(RulePrefix)) | |
| CheckExpr += Line.substr(RulePrefix.size()).str(); | |
| // If there's a check expr string... | |
| if (!CheckExpr.empty()) { | |
| // ... and it's complete then run it, otherwise remove the trailer '\'. | |
| if (CheckExpr.back() != '\\') { | |
| DidAllTestsPass &= check(CheckExpr); | |
| CheckExpr.clear(); | |
| ++NumRules; | |
| } else | |
| CheckExpr.pop_back(); | |
| } | |
| // Eat whitespace. | |
| LineStart = LineEnd; | |
| while (LineStart != MemBuf->getBufferEnd() && isSpace(*LineStart)) | |
| ++LineStart; | |
| } | |
| return DidAllTestsPass && (NumRules != 0); | |
| } | |
| bool RuntimeDyldCheckerImpl::isSymbolValid(StringRef Symbol) const { | |
| return IsSymbolValid(Symbol); | |
| } | |
| uint64_t RuntimeDyldCheckerImpl::getSymbolLocalAddr(StringRef Symbol) const { | |
| auto SymInfo = GetSymbolInfo(Symbol); | |
| if (!SymInfo) { | |
| logAllUnhandledErrors(SymInfo.takeError(), errs(), "RTDyldChecker: "); | |
| return 0; | |
| } | |
| if (SymInfo->isZeroFill()) | |
| return 0; | |
| return static_cast<uint64_t>( | |
| reinterpret_cast<uintptr_t>(SymInfo->getContent().data())); | |
| } | |
| uint64_t RuntimeDyldCheckerImpl::getSymbolRemoteAddr(StringRef Symbol) const { | |
| auto SymInfo = GetSymbolInfo(Symbol); | |
| if (!SymInfo) { | |
| logAllUnhandledErrors(SymInfo.takeError(), errs(), "RTDyldChecker: "); | |
| return 0; | |
| } | |
| return SymInfo->getTargetAddress(); | |
| } | |
| uint64_t RuntimeDyldCheckerImpl::readMemoryAtAddr(uint64_t SrcAddr, | |
| unsigned Size) const { | |
| uintptr_t PtrSizedAddr = static_cast<uintptr_t>(SrcAddr); | |
| assert(PtrSizedAddr == SrcAddr && "Linker memory pointer out-of-range."); | |
| void *Ptr = reinterpret_cast<void*>(PtrSizedAddr); | |
| switch (Size) { | |
| case 1: | |
| return support::endian::read<uint8_t>(Ptr, Endianness); | |
| case 2: | |
| return support::endian::read<uint16_t>(Ptr, Endianness); | |
| case 4: | |
| return support::endian::read<uint32_t>(Ptr, Endianness); | |
| case 8: | |
| return support::endian::read<uint64_t>(Ptr, Endianness); | |
| } | |
| llvm_unreachable("Unsupported read size"); | |
| } | |
| StringRef RuntimeDyldCheckerImpl::getSymbolContent(StringRef Symbol) const { | |
| auto SymInfo = GetSymbolInfo(Symbol); | |
| if (!SymInfo) { | |
| logAllUnhandledErrors(SymInfo.takeError(), errs(), "RTDyldChecker: "); | |
| return StringRef(); | |
| } | |
| return {SymInfo->getContent().data(), SymInfo->getContent().size()}; | |
| } | |
| TargetFlagsType RuntimeDyldCheckerImpl::getTargetFlag(StringRef Symbol) const { | |
| auto SymInfo = GetSymbolInfo(Symbol); | |
| if (!SymInfo) { | |
| logAllUnhandledErrors(SymInfo.takeError(), errs(), "RTDyldChecker: "); | |
| return TargetFlagsType{}; | |
| } | |
| return SymInfo->getTargetFlags(); | |
| } | |
| Triple | |
| RuntimeDyldCheckerImpl::getTripleForSymbol(TargetFlagsType Flag) const { | |
| Triple TheTriple = TT; | |
| switch (TT.getArch()) { | |
| case Triple::ArchType::arm: | |
| if (~Flag & 0x1) | |
| return TT; | |
| TheTriple.setArchName((Twine("thumb") + TT.getArchName().substr(3)).str()); | |
| return TheTriple; | |
| case Triple::ArchType::thumb: | |
| if (Flag & 0x1) | |
| return TT; | |
| TheTriple.setArchName((Twine("arm") + TT.getArchName().substr(5)).str()); | |
| return TheTriple; | |
| default: | |
| return TT; | |
| } | |
| } | |
| std::pair<uint64_t, std::string> RuntimeDyldCheckerImpl::getSectionAddr( | |
| StringRef FileName, StringRef SectionName, bool IsInsideLoad) const { | |
| auto SecInfo = GetSectionInfo(FileName, SectionName); | |
| if (!SecInfo) { | |
| std::string ErrMsg; | |
| { | |
| raw_string_ostream ErrMsgStream(ErrMsg); | |
| logAllUnhandledErrors(SecInfo.takeError(), ErrMsgStream, | |
| "RTDyldChecker: "); | |
| } | |
| return std::make_pair(0, std::move(ErrMsg)); | |
| } | |
| // If this address is being looked up in "load" mode, return the content | |
| // pointer, otherwise return the target address. | |
| uint64_t Addr = 0; | |
| if (IsInsideLoad) { | |
| if (SecInfo->isZeroFill()) | |
| Addr = 0; | |
| else | |
| Addr = pointerToJITTargetAddress(SecInfo->getContent().data()); | |
| } else | |
| Addr = SecInfo->getTargetAddress(); | |
| return std::make_pair(Addr, ""); | |
| } | |
| std::pair<uint64_t, std::string> RuntimeDyldCheckerImpl::getStubOrGOTAddrFor( | |
| StringRef StubContainerName, StringRef SymbolName, StringRef StubKindFilter, | |
| bool IsInsideLoad, bool IsStubAddr) const { | |
| assert((StubKindFilter.empty() || IsStubAddr) && | |
| "Kind name filter only supported for stubs"); | |
| auto StubInfo = | |
| IsStubAddr ? GetStubInfo(StubContainerName, SymbolName, StubKindFilter) | |
| : GetGOTInfo(StubContainerName, SymbolName); | |
| if (!StubInfo) { | |
| std::string ErrMsg; | |
| { | |
| raw_string_ostream ErrMsgStream(ErrMsg); | |
| logAllUnhandledErrors(StubInfo.takeError(), ErrMsgStream, | |
| "RTDyldChecker: "); | |
| } | |
| return std::make_pair((uint64_t)0, std::move(ErrMsg)); | |
| } | |
| uint64_t Addr = 0; | |
| if (IsInsideLoad) { | |
| if (StubInfo->isZeroFill()) | |
| return std::make_pair((uint64_t)0, "Detected zero-filled stub/GOT entry"); | |
| Addr = pointerToJITTargetAddress(StubInfo->getContent().data()); | |
| } else | |
| Addr = StubInfo->getTargetAddress(); | |
| return std::make_pair(Addr, ""); | |
| } | |
| RuntimeDyldChecker::RuntimeDyldChecker( | |
| IsSymbolValidFunction IsSymbolValid, GetSymbolInfoFunction GetSymbolInfo, | |
| GetSectionInfoFunction GetSectionInfo, GetStubInfoFunction GetStubInfo, | |
| GetGOTInfoFunction GetGOTInfo, llvm::endianness Endianness, Triple TT, | |
| StringRef CPU, SubtargetFeatures TF, raw_ostream &ErrStream) | |
| : Impl(::std::make_unique<RuntimeDyldCheckerImpl>( | |
| std::move(IsSymbolValid), std::move(GetSymbolInfo), | |
| std::move(GetSectionInfo), std::move(GetStubInfo), | |
| std::move(GetGOTInfo), Endianness, std::move(TT), std::move(CPU), | |
| std::move(TF), ErrStream)) {} | |
| RuntimeDyldChecker::~RuntimeDyldChecker() = default; | |
| bool RuntimeDyldChecker::check(StringRef CheckExpr) const { | |
| return Impl->check(CheckExpr); | |
| } | |
| bool RuntimeDyldChecker::checkAllRulesInBuffer(StringRef RulePrefix, | |
| MemoryBuffer *MemBuf) const { | |
| return Impl->checkAllRulesInBuffer(RulePrefix, MemBuf); | |
| } | |
| std::pair<uint64_t, std::string> | |
| RuntimeDyldChecker::getSectionAddr(StringRef FileName, StringRef SectionName, | |
| bool LocalAddress) { | |
| return Impl->getSectionAddr(FileName, SectionName, LocalAddress); | |
| } |