| //===-- IRDynamicChecks.cpp -----------------------------------------------===// | |
| // | |
| // 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/IR/Constants.h" | |
| #include "llvm/IR/DataLayout.h" | |
| #include "llvm/IR/Function.h" | |
| #include "llvm/IR/Instructions.h" | |
| #include "llvm/IR/Module.h" | |
| #include "llvm/IR/Value.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include "IRDynamicChecks.h" | |
| #include "lldb/Expression/UtilityFunction.h" | |
| #include "lldb/Target/ExecutionContext.h" | |
| #include "lldb/Target/Language.h" | |
| #include "lldb/Target/Process.h" | |
| #include "lldb/Target/StackFrame.h" | |
| #include "lldb/Target/Target.h" | |
| #include "lldb/Utility/ConstString.h" | |
| #include "lldb/Utility/LLDBLog.h" | |
| #include "lldb/Utility/Log.h" | |
| #include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" | |
| using namespace llvm; | |
| using namespace lldb_private; | |
| static char ID; | |
| #define VALID_OBJC_OBJECT_CHECK_NAME "$__lldb_objc_object_check" | |
| ClangDynamicCheckerFunctions::ClangDynamicCheckerFunctions() | |
| : DynamicCheckerFunctions(DCF_Clang) {} | |
| ClangDynamicCheckerFunctions::~ClangDynamicCheckerFunctions() = default; | |
| llvm::Error | |
| ClangDynamicCheckerFunctions::Install(DiagnosticManager &diagnostic_manager, | |
| ExecutionContext &exe_ctx) { | |
| if (Process *process = exe_ctx.GetProcessPtr()) { | |
| ObjCLanguageRuntime *objc_language_runtime = | |
| ObjCLanguageRuntime::Get(*process); | |
| SourceLanguage lang = process->GetTarget().GetLanguage(); | |
| if (!lang) | |
| if (auto *frame = exe_ctx.GetFramePtr()) | |
| lang = frame->GetLanguage(); | |
| if (objc_language_runtime && | |
| Language::LanguageIsObjC(lang.AsLanguageType())) { | |
| Expected<std::unique_ptr<UtilityFunction>> checker_fn = | |
| objc_language_runtime->CreateObjectChecker(VALID_OBJC_OBJECT_CHECK_NAME, exe_ctx); | |
| if (!checker_fn) | |
| return checker_fn.takeError(); | |
| m_objc_object_check = std::move(*checker_fn); | |
| } | |
| } | |
| return Error::success(); | |
| } | |
| bool ClangDynamicCheckerFunctions::DoCheckersExplainStop(lldb::addr_t addr, | |
| Stream &message) { | |
| // FIXME: We have to get the checkers to know why they scotched the call in | |
| // more detail, | |
| // so we can print a better message here. | |
| if (m_objc_object_check && m_objc_object_check->ContainsAddress(addr)) { | |
| message.Printf("Attempted to dereference an invalid ObjC Object or send it " | |
| "an unrecognized selector"); | |
| return true; | |
| } | |
| return false; | |
| } | |
| static std::string PrintValue(llvm::Value *V, bool truncate = false) { | |
| std::string s; | |
| raw_string_ostream rso(s); | |
| V->print(rso); | |
| if (truncate) | |
| s.resize(s.length() - 1); | |
| return s; | |
| } | |
| /// \class Instrumenter IRDynamicChecks.cpp | |
| /// Finds and instruments individual LLVM IR instructions | |
| /// | |
| /// When instrumenting LLVM IR, it is frequently desirable to first search for | |
| /// instructions, and then later modify them. This way iterators remain | |
| /// intact, and multiple passes can look at the same code base without | |
| /// treading on each other's toes. | |
| /// | |
| /// The Instrumenter class implements this functionality. A client first | |
| /// calls Inspect on a function, which populates a list of instructions to be | |
| /// instrumented. Then, later, when all passes' Inspect functions have been | |
| /// called, the client calls Instrument, which adds the desired | |
| /// instrumentation. | |
| /// | |
| /// A subclass of Instrumenter must override InstrumentInstruction, which | |
| /// is responsible for adding whatever instrumentation is necessary. | |
| /// | |
| /// A subclass of Instrumenter may override: | |
| /// | |
| /// - InspectInstruction [default: does nothing] | |
| /// | |
| /// - InspectBasicBlock [default: iterates through the instructions in a | |
| /// basic block calling InspectInstruction] | |
| /// | |
| /// - InspectFunction [default: iterates through the basic blocks in a | |
| /// function calling InspectBasicBlock] | |
| class Instrumenter { | |
| public: | |
| /// Constructor | |
| /// | |
| /// \param[in] module | |
| /// The module being instrumented. | |
| Instrumenter(llvm::Module &module, | |
| std::shared_ptr<UtilityFunction> checker_function) | |
| : m_module(module), m_checker_function(checker_function) {} | |
| virtual ~Instrumenter() = default; | |
| /// Inspect a function to find instructions to instrument | |
| /// | |
| /// \param[in] function | |
| /// The function to inspect. | |
| /// | |
| /// \return | |
| /// True on success; false on error. | |
| bool Inspect(llvm::Function &function) { return InspectFunction(function); } | |
| /// Instrument all the instructions found by Inspect() | |
| /// | |
| /// \return | |
| /// True on success; false on error. | |
| bool Instrument() { | |
| for (InstIterator ii = m_to_instrument.begin(), | |
| last_ii = m_to_instrument.end(); | |
| ii != last_ii; ++ii) { | |
| if (!InstrumentInstruction(*ii)) | |
| return false; | |
| } | |
| return true; | |
| } | |
| protected: | |
| /// Add instrumentation to a single instruction | |
| /// | |
| /// \param[in] inst | |
| /// The instruction to be instrumented. | |
| /// | |
| /// \return | |
| /// True on success; false otherwise. | |
| virtual bool InstrumentInstruction(llvm::Instruction *inst) = 0; | |
| /// Register a single instruction to be instrumented | |
| /// | |
| /// \param[in] inst | |
| /// The instruction to be instrumented. | |
| void RegisterInstruction(llvm::Instruction &inst) { | |
| m_to_instrument.push_back(&inst); | |
| } | |
| /// Determine whether a single instruction is interesting to instrument, | |
| /// and, if so, call RegisterInstruction | |
| /// | |
| /// \param[in] i | |
| /// The instruction to be inspected. | |
| /// | |
| /// \return | |
| /// False if there was an error scanning; true otherwise. | |
| virtual bool InspectInstruction(llvm::Instruction &i) { return true; } | |
| /// Scan a basic block to see if any instructions are interesting | |
| /// | |
| /// \param[in] bb | |
| /// The basic block to be inspected. | |
| /// | |
| /// \return | |
| /// False if there was an error scanning; true otherwise. | |
| virtual bool InspectBasicBlock(llvm::BasicBlock &bb) { | |
| for (llvm::BasicBlock::iterator ii = bb.begin(), last_ii = bb.end(); | |
| ii != last_ii; ++ii) { | |
| if (!InspectInstruction(*ii)) | |
| return false; | |
| } | |
| return true; | |
| } | |
| /// Scan a function to see if any instructions are interesting | |
| /// | |
| /// \param[in] f | |
| /// The function to be inspected. | |
| /// | |
| /// \return | |
| /// False if there was an error scanning; true otherwise. | |
| virtual bool InspectFunction(llvm::Function &f) { | |
| for (llvm::Function::iterator bbi = f.begin(), last_bbi = f.end(); | |
| bbi != last_bbi; ++bbi) { | |
| if (!InspectBasicBlock(*bbi)) | |
| return false; | |
| } | |
| return true; | |
| } | |
| /// Build a function pointer for a function with signature void | |
| /// (*)(uint8_t*, uint8_t*) with a given address | |
| /// | |
| /// \param[in] start_address | |
| /// The address of the function. | |
| /// | |
| /// \return | |
| /// The function pointer, for use in a CallInst. | |
| llvm::FunctionCallee BuildObjectCheckerFunc(lldb::addr_t start_address) { | |
| llvm::Type *param_array[2]; | |
| param_array[0] = const_cast<llvm::PointerType *>(GetI8PtrTy()); | |
| param_array[1] = const_cast<llvm::PointerType *>(GetI8PtrTy()); | |
| ArrayRef<llvm::Type *> params(param_array, 2); | |
| FunctionType *fun_ty = FunctionType::get( | |
| llvm::Type::getVoidTy(m_module.getContext()), params, true); | |
| PointerType *fun_ptr_ty = PointerType::getUnqual(m_module.getContext()); | |
| Constant *fun_addr_int = | |
| ConstantInt::get(GetIntptrTy(), start_address, false); | |
| return {fun_ty, ConstantExpr::getIntToPtr(fun_addr_int, fun_ptr_ty)}; | |
| } | |
| PointerType *GetI8PtrTy() { | |
| if (!m_i8ptr_ty) | |
| m_i8ptr_ty = llvm::PointerType::getUnqual(m_module.getContext()); | |
| return m_i8ptr_ty; | |
| } | |
| IntegerType *GetIntptrTy() { | |
| if (!m_intptr_ty) { | |
| m_intptr_ty = llvm::Type::getIntNTy( | |
| m_module.getContext(), | |
| m_module.getDataLayout().getPointerSizeInBits()); | |
| } | |
| return m_intptr_ty; | |
| } | |
| typedef std::vector<llvm::Instruction *> InstVector; | |
| typedef InstVector::iterator InstIterator; | |
| InstVector m_to_instrument; ///< List of instructions the inspector found | |
| llvm::Module &m_module; ///< The module which is being instrumented | |
| std::shared_ptr<UtilityFunction> | |
| m_checker_function; ///< The dynamic checker function for the process | |
| private: | |
| PointerType *m_i8ptr_ty = nullptr; | |
| IntegerType *m_intptr_ty = nullptr; | |
| }; | |
| class ObjcObjectChecker : public Instrumenter { | |
| public: | |
| ObjcObjectChecker(llvm::Module &module, | |
| std::shared_ptr<UtilityFunction> checker_function) | |
| : Instrumenter(module, checker_function), | |
| m_objc_object_check_func(nullptr) {} | |
| ~ObjcObjectChecker() override = default; | |
| enum msgSend_type { | |
| eMsgSend = 0, | |
| eMsgSendSuper, | |
| eMsgSendSuper_stret, | |
| eMsgSend_fpret, | |
| eMsgSend_stret | |
| }; | |
| std::map<llvm::Instruction *, msgSend_type> msgSend_types; | |
| protected: | |
| bool InstrumentInstruction(llvm::Instruction *inst) override { | |
| CallInst *call_inst = dyn_cast<CallInst>(inst); | |
| if (!call_inst) | |
| return false; // call_inst really shouldn't be nullptr, because otherwise | |
| // InspectInstruction wouldn't have registered it | |
| if (!m_objc_object_check_func) | |
| m_objc_object_check_func = | |
| BuildObjectCheckerFunc(m_checker_function->StartAddress()); | |
| // id objc_msgSend(id theReceiver, SEL theSelector, ...) | |
| llvm::Value *target_object; | |
| llvm::Value *selector; | |
| switch (msgSend_types[inst]) { | |
| case eMsgSend: | |
| case eMsgSend_fpret: | |
| // On arm64, clang uses objc_msgSend for scalar and struct return | |
| // calls. The call instruction will record which was used. | |
| if (call_inst->hasStructRetAttr()) { | |
| target_object = call_inst->getArgOperand(1); | |
| selector = call_inst->getArgOperand(2); | |
| } else { | |
| target_object = call_inst->getArgOperand(0); | |
| selector = call_inst->getArgOperand(1); | |
| } | |
| break; | |
| case eMsgSend_stret: | |
| target_object = call_inst->getArgOperand(1); | |
| selector = call_inst->getArgOperand(2); | |
| break; | |
| case eMsgSendSuper: | |
| case eMsgSendSuper_stret: | |
| return true; | |
| } | |
| // These objects should always be valid according to Sean Calannan | |
| assert(target_object); | |
| assert(selector); | |
| // Insert an instruction to call the helper with the result | |
| llvm::Value *arg_array[2]; | |
| arg_array[0] = target_object; | |
| arg_array[1] = selector; | |
| ArrayRef<llvm::Value *> args(arg_array, 2); | |
| CallInst::Create(m_objc_object_check_func, args, "", inst->getIterator()); | |
| return true; | |
| } | |
| static llvm::Function *GetFunction(llvm::Value *value) { | |
| if (llvm::Function *function = llvm::dyn_cast<llvm::Function>(value)) { | |
| return function; | |
| } | |
| if (llvm::ConstantExpr *const_expr = | |
| llvm::dyn_cast<llvm::ConstantExpr>(value)) { | |
| switch (const_expr->getOpcode()) { | |
| default: | |
| return nullptr; | |
| case llvm::Instruction::BitCast: | |
| return GetFunction(const_expr->getOperand(0)); | |
| } | |
| } | |
| return nullptr; | |
| } | |
| static llvm::Function *GetCalledFunction(llvm::CallInst *inst) { | |
| return GetFunction(inst->getCalledOperand()); | |
| } | |
| bool InspectInstruction(llvm::Instruction &i) override { | |
| Log *log = GetLog(LLDBLog::Expressions); | |
| CallInst *call_inst = dyn_cast<CallInst>(&i); | |
| if (call_inst) { | |
| const llvm::Function *called_function = GetCalledFunction(call_inst); | |
| if (!called_function) | |
| return true; | |
| std::string name_str = called_function->getName().str(); | |
| const char *name_cstr = name_str.c_str(); | |
| LLDB_LOGF(log, "Found call to %s: %s\n", name_cstr, | |
| PrintValue(call_inst).c_str()); | |
| if (name_str.find("objc_msgSend") == std::string::npos) | |
| return true; | |
| if (!strcmp(name_cstr, "objc_msgSend")) { | |
| RegisterInstruction(i); | |
| msgSend_types[&i] = eMsgSend; | |
| return true; | |
| } | |
| if (!strcmp(name_cstr, "objc_msgSend_stret")) { | |
| RegisterInstruction(i); | |
| msgSend_types[&i] = eMsgSend_stret; | |
| return true; | |
| } | |
| if (!strcmp(name_cstr, "objc_msgSend_fpret")) { | |
| RegisterInstruction(i); | |
| msgSend_types[&i] = eMsgSend_fpret; | |
| return true; | |
| } | |
| if (!strcmp(name_cstr, "objc_msgSendSuper")) { | |
| RegisterInstruction(i); | |
| msgSend_types[&i] = eMsgSendSuper; | |
| return true; | |
| } | |
| if (!strcmp(name_cstr, "objc_msgSendSuper_stret")) { | |
| RegisterInstruction(i); | |
| msgSend_types[&i] = eMsgSendSuper_stret; | |
| return true; | |
| } | |
| LLDB_LOGF(log, | |
| "Function name '%s' contains 'objc_msgSend' but is not handled", | |
| name_str.c_str()); | |
| return true; | |
| } | |
| return true; | |
| } | |
| private: | |
| llvm::FunctionCallee m_objc_object_check_func; | |
| }; | |
| IRDynamicChecks::IRDynamicChecks( | |
| ClangDynamicCheckerFunctions &checker_functions, const char *func_name) | |
| : ModulePass(ID), m_func_name(func_name), | |
| m_checker_functions(checker_functions) {} | |
| IRDynamicChecks::~IRDynamicChecks() = default; | |
| bool IRDynamicChecks::runOnModule(llvm::Module &M) { | |
| Log *log = GetLog(LLDBLog::Expressions); | |
| llvm::Function *function = M.getFunction(StringRef(m_func_name)); | |
| if (!function) { | |
| LLDB_LOGF(log, "Couldn't find %s() in the module", m_func_name.c_str()); | |
| return false; | |
| } | |
| if (m_checker_functions.m_objc_object_check) { | |
| ObjcObjectChecker ooc(M, m_checker_functions.m_objc_object_check); | |
| if (!ooc.Inspect(*function)) | |
| return false; | |
| if (!ooc.Instrument()) | |
| return false; | |
| } | |
| if (log && log->GetVerbose()) { | |
| std::string s; | |
| raw_string_ostream oss(s); | |
| M.print(oss, nullptr); | |
| LLDB_LOGF(log, "Module after dynamic checks: \n%s", s.c_str()); | |
| } | |
| return true; | |
| } | |
| void IRDynamicChecks::assignPassManager(PMStack &PMS, PassManagerType T) {} | |
| PassManagerType IRDynamicChecks::getPotentialPassManagerType() const { | |
| return PMT_ModulePassManager; | |
| } |