1//===- RealtimeSanitizer.cpp - RealtimeSanitizer instrumentation *- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file is a part of the RealtimeSanitizer, an LLVM transformation for
10// detecting and reporting realtime safety violations.
11//
12// See also: llvm-project/compiler-rt/lib/rtsan/
13//
14//===----------------------------------------------------------------------===//
15
16#include "llvm/IR/Analysis.h"
17#include "llvm/IR/IRBuilder.h"
18#include "llvm/IR/InstIterator.h"
19#include "llvm/IR/Module.h"
20#include "llvm/Transforms/Utils/ModuleUtils.h"
21
22#include "llvm/Demangle/Demangle.h"
23#include "llvm/Transforms/Instrumentation/RealtimeSanitizer.h"
24
25using namespace llvm;
26
27const char kRtsanModuleCtorName[] = "rtsan.module_ctor";
28const char kRtsanInitName[] = "__rtsan_ensure_initialized";
29
30static SmallVector<Type *> getArgTypes(ArrayRef<Value *> FunctionArgs) {
31 SmallVector<Type *> Types;
32 for (Value *Arg : FunctionArgs)
33 Types.push_back(Elt: Arg->getType());
34 return Types;
35}
36
37static void insertCallBeforeInstruction(Function &Fn, Instruction &Instruction,
38 const char *FunctionName,
39 ArrayRef<Value *> FunctionArgs) {
40 LLVMContext &Context = Fn.getContext();
41 FunctionType *FuncType = FunctionType::get(Result: Type::getVoidTy(C&: Context),
42 Params: getArgTypes(FunctionArgs), isVarArg: false);
43 FunctionCallee Func =
44 Fn.getParent()->getOrInsertFunction(Name: FunctionName, T: FuncType);
45 IRBuilder<> Builder{&Instruction};
46 Builder.CreateCall(Callee: Func, Args: FunctionArgs);
47}
48
49static void insertCallAtFunctionEntryPoint(Function &Fn,
50 const char *InsertFnName,
51 ArrayRef<Value *> FunctionArgs) {
52 insertCallBeforeInstruction(Fn, Instruction&: Fn.front().front(), FunctionName: InsertFnName,
53 FunctionArgs);
54}
55
56static void insertCallAtAllFunctionExitPoints(Function &Fn,
57 const char *InsertFnName,
58 ArrayRef<Value *> FunctionArgs) {
59 for (auto &I : instructions(F&: Fn))
60 if (isa<ReturnInst>(Val: &I))
61 insertCallBeforeInstruction(Fn, Instruction&: I, FunctionName: InsertFnName, FunctionArgs);
62}
63
64static PreservedAnalyses rtsanPreservedCFGAnalyses() {
65 PreservedAnalyses PA;
66 PA.preserveSet<CFGAnalyses>();
67 return PA;
68}
69
70static PreservedAnalyses runSanitizeRealtime(Function &Fn) {
71 insertCallAtFunctionEntryPoint(Fn, InsertFnName: "__rtsan_realtime_enter", FunctionArgs: {});
72 insertCallAtAllFunctionExitPoints(Fn, InsertFnName: "__rtsan_realtime_exit", FunctionArgs: {});
73 return rtsanPreservedCFGAnalyses();
74}
75
76static PreservedAnalyses runSanitizeRealtimeBlocking(Function &Fn) {
77 IRBuilder<> Builder(&Fn.front().front());
78 Value *Name = Builder.CreateGlobalString(Str: demangle(MangledName: Fn.getName()));
79 insertCallAtFunctionEntryPoint(Fn, InsertFnName: "__rtsan_notify_blocking_call", FunctionArgs: {Name});
80 return rtsanPreservedCFGAnalyses();
81}
82
83PreservedAnalyses RealtimeSanitizerPass::run(Module &M,
84 ModuleAnalysisManager &MAM) {
85 getOrCreateSanitizerCtorAndInitFunctions(
86 M, CtorName: kRtsanModuleCtorName, InitName: kRtsanInitName, /*InitArgTypes=*/{},
87 /*InitArgs=*/{},
88 // This callback is invoked when the functions are created the first
89 // time. Hook them into the global ctors list in that case:
90 FunctionsCreatedCallback: [&](Function *Ctor, FunctionCallee) { appendToGlobalCtors(M, F: Ctor, Priority: 0); });
91
92 for (Function &F : M) {
93 if (F.hasFnAttribute(Kind: Attribute::SanitizeRealtime))
94 runSanitizeRealtime(Fn&: F);
95
96 if (F.hasFnAttribute(Kind: Attribute::SanitizeRealtimeBlocking))
97 runSanitizeRealtimeBlocking(Fn&: F);
98 }
99
100 return PreservedAnalyses::none();
101}
102