//===- RealtimeSanitizer.cpp - RealtimeSanitizer instrumentation *- 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 // //===----------------------------------------------------------------------===// // // This file is a part of the RealtimeSanitizer, an LLVM transformation for // detecting and reporting realtime safety violations. // // See also: llvm-project/compiler-rt/lib/rtsan/ // //===----------------------------------------------------------------------===// #include "llvm/IR/Analysis.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/InstIterator.h" #include "llvm/IR/Module.h" #include "llvm/Transforms/Utils/ModuleUtils.h" #include "llvm/Demangle/Demangle.h" #include "llvm/Transforms/Instrumentation/RealtimeSanitizer.h" using namespace llvm; const char kRtsanModuleCtorName[] = "rtsan.module_ctor"; const char kRtsanInitName[] = "__rtsan_ensure_initialized"; static SmallVector getArgTypes(ArrayRef FunctionArgs) { SmallVector Types; for (Value *Arg : FunctionArgs) Types.push_back(Arg->getType()); return Types; } static void insertCallBeforeInstruction(Function &Fn, Instruction &Instruction, const char *FunctionName, ArrayRef FunctionArgs) { LLVMContext &Context = Fn.getContext(); FunctionType *FuncType = FunctionType::get(Type::getVoidTy(Context), getArgTypes(FunctionArgs), false); FunctionCallee Func = Fn.getParent()->getOrInsertFunction(FunctionName, FuncType); IRBuilder<> Builder{&Instruction}; Builder.CreateCall(Func, FunctionArgs); } static void insertCallAtFunctionEntryPoint(Function &Fn, const char *InsertFnName, ArrayRef FunctionArgs) { insertCallBeforeInstruction(Fn, Fn.front().front(), InsertFnName, FunctionArgs); } static void insertCallAtAllFunctionExitPoints(Function &Fn, const char *InsertFnName, ArrayRef FunctionArgs) { for (auto &I : instructions(Fn)) if (isa(&I)) insertCallBeforeInstruction(Fn, I, InsertFnName, FunctionArgs); } static PreservedAnalyses rtsanPreservedCFGAnalyses() { PreservedAnalyses PA; PA.preserveSet(); return PA; } static PreservedAnalyses runSanitizeRealtime(Function &Fn) { insertCallAtFunctionEntryPoint(Fn, "__rtsan_realtime_enter", {}); insertCallAtAllFunctionExitPoints(Fn, "__rtsan_realtime_exit", {}); return rtsanPreservedCFGAnalyses(); } static PreservedAnalyses runSanitizeRealtimeBlocking(Function &Fn) { IRBuilder<> Builder(&Fn.front().front()); Value *Name = Builder.CreateGlobalString(demangle(Fn.getName())); insertCallAtFunctionEntryPoint(Fn, "__rtsan_notify_blocking_call", {Name}); return rtsanPreservedCFGAnalyses(); } PreservedAnalyses RealtimeSanitizerPass::run(Module &M, ModuleAnalysisManager &MAM) { getOrCreateSanitizerCtorAndInitFunctions( M, kRtsanModuleCtorName, kRtsanInitName, /*InitArgTypes=*/{}, /*InitArgs=*/{}, // This callback is invoked when the functions are created the first // time. Hook them into the global ctors list in that case: [&](Function *Ctor, FunctionCallee) { appendToGlobalCtors(M, Ctor, 0); }); for (Function &F : M) { if (F.hasFnAttribute(Attribute::SanitizeRealtime)) runSanitizeRealtime(F); if (F.hasFnAttribute(Attribute::SanitizeRealtimeBlocking)) runSanitizeRealtimeBlocking(F); } return PreservedAnalyses::none(); }