xref: /llvm-project/llvm/lib/CodeGen/JMCInstrumenter.cpp (revision b9d83eff254668385fd3d9d5ddb5af762f378d7f)
1f9270214SYuanfang Chen //===- JMCInstrumenter.cpp - JMC Instrumentation --------------------------===//
2f9270214SYuanfang Chen //
3f9270214SYuanfang Chen // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4f9270214SYuanfang Chen // See https://llvm.org/LICENSE.txt for license information.
5f9270214SYuanfang Chen // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6f9270214SYuanfang Chen //
7f9270214SYuanfang Chen //===----------------------------------------------------------------------===//
8f9270214SYuanfang Chen //
9f9270214SYuanfang Chen // JMCInstrumenter pass:
10f9270214SYuanfang Chen // - instrument each function with a call to __CheckForDebuggerJustMyCode. The
11f9270214SYuanfang Chen //   sole argument should be defined in .msvcjmc. Each flag is 1 byte initilized
12f9270214SYuanfang Chen //   to 1.
13eddd94c2SYuanfang Chen // - create the dummy COMDAT function __JustMyCode_Default to prevent linking
14eddd94c2SYuanfang Chen //   error if __CheckForDebuggerJustMyCode is not available.
15eddd94c2SYuanfang Chen // - For MSVC:
16eddd94c2SYuanfang Chen //   add "/alternatename:__CheckForDebuggerJustMyCode=__JustMyCode_Default" to
17eddd94c2SYuanfang Chen //   "llvm.linker.options"
18eddd94c2SYuanfang Chen //   For ELF:
19eddd94c2SYuanfang Chen //   Rename __JustMyCode_Default to __CheckForDebuggerJustMyCode and mark it as
20eddd94c2SYuanfang Chen //   weak symbol.
21f9270214SYuanfang Chen //===----------------------------------------------------------------------===//
22f9270214SYuanfang Chen 
2362b21c6cSpaperchalice #include "llvm/CodeGen/JMCInstrumenter.h"
24f9270214SYuanfang Chen #include "llvm/ADT/SmallString.h"
25f9270214SYuanfang Chen #include "llvm/ADT/StringExtras.h"
26f9270214SYuanfang Chen #include "llvm/CodeGen/Passes.h"
27f9270214SYuanfang Chen #include "llvm/IR/DIBuilder.h"
28f9270214SYuanfang Chen #include "llvm/IR/DebugInfoMetadata.h"
29f9270214SYuanfang Chen #include "llvm/IR/DerivedTypes.h"
30f9270214SYuanfang Chen #include "llvm/IR/Function.h"
31f9270214SYuanfang Chen #include "llvm/IR/Instructions.h"
32f9270214SYuanfang Chen #include "llvm/IR/LLVMContext.h"
33f9270214SYuanfang Chen #include "llvm/IR/Module.h"
34f9270214SYuanfang Chen #include "llvm/IR/Type.h"
35f9270214SYuanfang Chen #include "llvm/InitializePasses.h"
36f9270214SYuanfang Chen #include "llvm/Pass.h"
37f9270214SYuanfang Chen #include "llvm/Support/DJB.h"
38f9270214SYuanfang Chen #include "llvm/Support/Path.h"
39f9270214SYuanfang Chen #include "llvm/Transforms/Utils/ModuleUtils.h"
40f9270214SYuanfang Chen 
41f9270214SYuanfang Chen using namespace llvm;
42f9270214SYuanfang Chen 
4362b21c6cSpaperchalice #define DEBUG_TYPE "jmc-instrumenter"
44f9270214SYuanfang Chen 
4562b21c6cSpaperchalice static bool runImpl(Module &M);
46f9270214SYuanfang Chen namespace {
47f9270214SYuanfang Chen struct JMCInstrumenter : public ModulePass {
48f9270214SYuanfang Chen   static char ID;
JMCInstrumenter__anona670eef10111::JMCInstrumenter49f9270214SYuanfang Chen   JMCInstrumenter() : ModulePass(ID) {
50f9270214SYuanfang Chen     initializeJMCInstrumenterPass(*PassRegistry::getPassRegistry());
51f9270214SYuanfang Chen   }
runOnModule__anona670eef10111::JMCInstrumenter5262b21c6cSpaperchalice   bool runOnModule(Module &M) override { return runImpl(M); }
53f9270214SYuanfang Chen };
54f9270214SYuanfang Chen char JMCInstrumenter::ID = 0;
55f9270214SYuanfang Chen } // namespace
56f9270214SYuanfang Chen 
run(Module & M,ModuleAnalysisManager &)5762b21c6cSpaperchalice PreservedAnalyses JMCInstrumenterPass::run(Module &M, ModuleAnalysisManager &) {
5862b21c6cSpaperchalice   bool Changed = runImpl(M);
5962b21c6cSpaperchalice   return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all();
6062b21c6cSpaperchalice }
6162b21c6cSpaperchalice 
62f9270214SYuanfang Chen INITIALIZE_PASS(
63f9270214SYuanfang Chen     JMCInstrumenter, DEBUG_TYPE,
64f9270214SYuanfang Chen     "Instrument function entry with call to __CheckForDebuggerJustMyCode",
65f9270214SYuanfang Chen     false, false)
66f9270214SYuanfang Chen 
createJMCInstrumenterPass()67f9270214SYuanfang Chen ModulePass *llvm::createJMCInstrumenterPass() { return new JMCInstrumenter(); }
68f9270214SYuanfang Chen 
69f9270214SYuanfang Chen namespace {
70f9270214SYuanfang Chen const char CheckFunctionName[] = "__CheckForDebuggerJustMyCode";
71f9270214SYuanfang Chen 
getFlagName(DISubprogram & SP,bool UseX86FastCall)72f9270214SYuanfang Chen std::string getFlagName(DISubprogram &SP, bool UseX86FastCall) {
73d538ad53SYuanfang Chen   // absolute windows path:           windows_backslash
74d538ad53SYuanfang Chen   // relative windows backslash path: windows_backslash
75d538ad53SYuanfang Chen   // relative windows slash path:     posix
76d538ad53SYuanfang Chen   // absolute posix path:             posix
77d538ad53SYuanfang Chen   // relative posix path:             posix
78d538ad53SYuanfang Chen   sys::path::Style PathStyle =
79d538ad53SYuanfang Chen       has_root_name(SP.getDirectory(), sys::path::Style::windows_backslash) ||
8032ce076dSKazu Hirata               SP.getDirectory().contains("\\") ||
8132ce076dSKazu Hirata               SP.getFilename().contains("\\")
82d538ad53SYuanfang Chen           ? sys::path::Style::windows_backslash
83d538ad53SYuanfang Chen           : sys::path::Style::posix;
84f9270214SYuanfang Chen   // Best effort path normalization. This is to guarantee an unique flag symbol
85f9270214SYuanfang Chen   // is produced for the same directory. Some builds may want to use relative
86f9270214SYuanfang Chen   // paths, or paths with a specific prefix (see the -fdebug-compilation-dir
87f9270214SYuanfang Chen   // flag), so only hash paths in debuginfo. Don't expand them to absolute
88f9270214SYuanfang Chen   // paths.
89f9270214SYuanfang Chen   SmallString<256> FilePath(SP.getDirectory());
90d538ad53SYuanfang Chen   sys::path::append(FilePath, PathStyle, SP.getFilename());
91d538ad53SYuanfang Chen   sys::path::native(FilePath, PathStyle);
92d538ad53SYuanfang Chen   sys::path::remove_dots(FilePath, /*remove_dot_dot=*/true, PathStyle);
93f9270214SYuanfang Chen 
94f9270214SYuanfang Chen   // The naming convention for the flag name is __<hash>_<file name> with '.' in
95f9270214SYuanfang Chen   // <file name> replaced with '@'. For example C:\file.any.c would have a flag
96f9270214SYuanfang Chen   // __D032E919_file@any@c. The naming convention match MSVC's format however
97f9270214SYuanfang Chen   // the match is not required to make JMC work. The hashing function used here
98f9270214SYuanfang Chen   // is different from MSVC's.
99f9270214SYuanfang Chen 
100f9270214SYuanfang Chen   std::string Suffix;
101d538ad53SYuanfang Chen   for (auto C : sys::path::filename(FilePath, PathStyle))
102f9270214SYuanfang Chen     Suffix.push_back(C == '.' ? '@' : C);
103f9270214SYuanfang Chen 
104d538ad53SYuanfang Chen   sys::path::remove_filename(FilePath, PathStyle);
105f9270214SYuanfang Chen   return (UseX86FastCall ? "_" : "__") +
106f9270214SYuanfang Chen          utohexstr(djbHash(FilePath), /*LowerCase=*/false,
107f9270214SYuanfang Chen                    /*Width=*/8) +
108f9270214SYuanfang Chen          "_" + Suffix;
109f9270214SYuanfang Chen }
110f9270214SYuanfang Chen 
attachDebugInfo(GlobalVariable & GV,DISubprogram & SP)111f9270214SYuanfang Chen void attachDebugInfo(GlobalVariable &GV, DISubprogram &SP) {
112f9270214SYuanfang Chen   Module &M = *GV.getParent();
113f9270214SYuanfang Chen   DICompileUnit *CU = SP.getUnit();
114f9270214SYuanfang Chen   assert(CU);
115f9270214SYuanfang Chen   DIBuilder DB(M, false, CU);
116f9270214SYuanfang Chen 
117f9270214SYuanfang Chen   auto *DType =
118f9270214SYuanfang Chen       DB.createBasicType("unsigned char", 8, dwarf::DW_ATE_unsigned_char,
119f9270214SYuanfang Chen                          llvm::DINode::FlagArtificial);
120f9270214SYuanfang Chen 
121f9270214SYuanfang Chen   auto *DGVE = DB.createGlobalVariableExpression(
122f9270214SYuanfang Chen       CU, GV.getName(), /*LinkageName=*/StringRef(), SP.getFile(),
123f9270214SYuanfang Chen       /*LineNo=*/0, DType, /*IsLocalToUnit=*/true, /*IsDefined=*/true);
124f9270214SYuanfang Chen   GV.addMetadata(LLVMContext::MD_dbg, *DGVE);
125f9270214SYuanfang Chen   DB.finalize();
126f9270214SYuanfang Chen }
127f9270214SYuanfang Chen 
getCheckFunctionType(LLVMContext & Ctx)128f9270214SYuanfang Chen FunctionType *getCheckFunctionType(LLVMContext &Ctx) {
129f9270214SYuanfang Chen   Type *VoidTy = Type::getVoidTy(Ctx);
130a7ee80faSBjorn Pettersson   PointerType *VoidPtrTy = PointerType::getUnqual(Ctx);
131f9270214SYuanfang Chen   return FunctionType::get(VoidTy, VoidPtrTy, false);
132f9270214SYuanfang Chen }
133f9270214SYuanfang Chen 
createDefaultCheckFunction(Module & M,bool UseX86FastCall)134eddd94c2SYuanfang Chen Function *createDefaultCheckFunction(Module &M, bool UseX86FastCall) {
135f9270214SYuanfang Chen   LLVMContext &Ctx = M.getContext();
136f9270214SYuanfang Chen   const char *DefaultCheckFunctionName =
137f9270214SYuanfang Chen       UseX86FastCall ? "_JustMyCode_Default" : "__JustMyCode_Default";
138f9270214SYuanfang Chen   // Create the function.
139f9270214SYuanfang Chen   Function *DefaultCheckFunc =
140f9270214SYuanfang Chen       Function::Create(getCheckFunctionType(Ctx), GlobalValue::ExternalLinkage,
141f9270214SYuanfang Chen                        DefaultCheckFunctionName, &M);
142f9270214SYuanfang Chen   DefaultCheckFunc->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
143f9270214SYuanfang Chen   DefaultCheckFunc->addParamAttr(0, Attribute::NoUndef);
144f9270214SYuanfang Chen   if (UseX86FastCall)
145f9270214SYuanfang Chen     DefaultCheckFunc->addParamAttr(0, Attribute::InReg);
146eddd94c2SYuanfang Chen 
147f9270214SYuanfang Chen   BasicBlock *EntryBB = BasicBlock::Create(Ctx, "", DefaultCheckFunc);
148f9270214SYuanfang Chen   ReturnInst::Create(Ctx, EntryBB);
149eddd94c2SYuanfang Chen   return DefaultCheckFunc;
150f9270214SYuanfang Chen }
151f9270214SYuanfang Chen } // namespace
152f9270214SYuanfang Chen 
runImpl(Module & M)15362b21c6cSpaperchalice bool runImpl(Module &M) {
154f9270214SYuanfang Chen   bool Changed = false;
155f9270214SYuanfang Chen   LLVMContext &Ctx = M.getContext();
156f9270214SYuanfang Chen   Triple ModuleTriple(M.getTargetTriple());
157eddd94c2SYuanfang Chen   bool IsMSVC = ModuleTriple.isKnownWindowsMSVCEnvironment();
158eddd94c2SYuanfang Chen   bool IsELF = ModuleTriple.isOSBinFormatELF();
159eddd94c2SYuanfang Chen   assert((IsELF || IsMSVC) && "Unsupported triple for JMC");
160eddd94c2SYuanfang Chen   bool UseX86FastCall = IsMSVC && ModuleTriple.getArch() == Triple::x86;
16124c6ea91SYuanfang Chen   const char *const FlagSymbolSection = IsELF ? ".data.just.my.code" : ".msvcjmc";
162f9270214SYuanfang Chen 
163eddd94c2SYuanfang Chen   GlobalValue *CheckFunction = nullptr;
164f9270214SYuanfang Chen   DenseMap<DISubprogram *, Constant *> SavedFlags(8);
165f9270214SYuanfang Chen   for (auto &F : M) {
166f9270214SYuanfang Chen     if (F.isDeclaration())
167f9270214SYuanfang Chen       continue;
168f9270214SYuanfang Chen     auto *SP = F.getSubprogram();
169f9270214SYuanfang Chen     if (!SP)
170f9270214SYuanfang Chen       continue;
171f9270214SYuanfang Chen 
172f9270214SYuanfang Chen     Constant *&Flag = SavedFlags[SP];
173f9270214SYuanfang Chen     if (!Flag) {
174f9270214SYuanfang Chen       std::string FlagName = getFlagName(*SP, UseX86FastCall);
175f9270214SYuanfang Chen       IntegerType *FlagTy = Type::getInt8Ty(Ctx);
176f9270214SYuanfang Chen       Flag = M.getOrInsertGlobal(FlagName, FlagTy, [&] {
177f9270214SYuanfang Chen         // FIXME: Put the GV in comdat and have linkonce_odr linkage to save
178f9270214SYuanfang Chen         //        .msvcjmc section space? maybe not worth it.
179f9270214SYuanfang Chen         GlobalVariable *GV = new GlobalVariable(
180f9270214SYuanfang Chen             M, FlagTy, /*isConstant=*/false, GlobalValue::InternalLinkage,
181f9270214SYuanfang Chen             ConstantInt::get(FlagTy, 1), FlagName);
182eddd94c2SYuanfang Chen         GV->setSection(FlagSymbolSection);
183f9270214SYuanfang Chen         GV->setAlignment(Align(1));
184f9270214SYuanfang Chen         GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
185f9270214SYuanfang Chen         attachDebugInfo(*GV, *SP);
186f9270214SYuanfang Chen         return GV;
187f9270214SYuanfang Chen       });
188f9270214SYuanfang Chen     }
189f9270214SYuanfang Chen 
190f9270214SYuanfang Chen     if (!CheckFunction) {
191eddd94c2SYuanfang Chen       Function *DefaultCheckFunc =
192eddd94c2SYuanfang Chen           createDefaultCheckFunction(M, UseX86FastCall);
193eddd94c2SYuanfang Chen       if (IsELF) {
194eddd94c2SYuanfang Chen         DefaultCheckFunc->setName(CheckFunctionName);
195eddd94c2SYuanfang Chen         DefaultCheckFunc->setLinkage(GlobalValue::WeakAnyLinkage);
196eddd94c2SYuanfang Chen         CheckFunction = DefaultCheckFunc;
197eddd94c2SYuanfang Chen       } else {
198f9270214SYuanfang Chen         assert(!M.getFunction(CheckFunctionName) &&
199f9270214SYuanfang Chen                "JMC instrument more than once?");
200eddd94c2SYuanfang Chen         auto *CheckFunc = cast<Function>(
201f9270214SYuanfang Chen             M.getOrInsertFunction(CheckFunctionName, getCheckFunctionType(Ctx))
202f9270214SYuanfang Chen                 .getCallee());
203eddd94c2SYuanfang Chen         CheckFunc->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
204eddd94c2SYuanfang Chen         CheckFunc->addParamAttr(0, Attribute::NoUndef);
205f9270214SYuanfang Chen         if (UseX86FastCall) {
206eddd94c2SYuanfang Chen           CheckFunc->setCallingConv(CallingConv::X86_FastCall);
207eddd94c2SYuanfang Chen           CheckFunc->addParamAttr(0, Attribute::InReg);
208eddd94c2SYuanfang Chen         }
209eddd94c2SYuanfang Chen         CheckFunction = CheckFunc;
210eddd94c2SYuanfang Chen 
211eddd94c2SYuanfang Chen         StringRef DefaultCheckFunctionName = DefaultCheckFunc->getName();
212eddd94c2SYuanfang Chen         appendToUsed(M, {DefaultCheckFunc});
213eddd94c2SYuanfang Chen         Comdat *C = M.getOrInsertComdat(DefaultCheckFunctionName);
214eddd94c2SYuanfang Chen         C->setSelectionKind(Comdat::Any);
215eddd94c2SYuanfang Chen         DefaultCheckFunc->setComdat(C);
216eddd94c2SYuanfang Chen         // Add a linker option /alternatename to set the default implementation
217eddd94c2SYuanfang Chen         // for the check function.
218eddd94c2SYuanfang Chen         // https://devblogs.microsoft.com/oldnewthing/20200731-00/?p=104024
219eddd94c2SYuanfang Chen         std::string AltOption = std::string("/alternatename:") +
220eddd94c2SYuanfang Chen                                 CheckFunctionName + "=" +
221eddd94c2SYuanfang Chen                                 DefaultCheckFunctionName.str();
222eddd94c2SYuanfang Chen         llvm::Metadata *Ops[] = {llvm::MDString::get(Ctx, AltOption)};
223eddd94c2SYuanfang Chen         MDTuple *N = MDNode::get(Ctx, Ops);
224eddd94c2SYuanfang Chen         M.getOrInsertNamedMetadata("llvm.linker.options")->addOperand(N);
225f9270214SYuanfang Chen       }
226f9270214SYuanfang Chen     }
227f9270214SYuanfang Chen     // FIXME: it would be nice to make CI scheduling boundary, although in
228f9270214SYuanfang Chen     //        practice it does not matter much.
229eddd94c2SYuanfang Chen     auto *CI = CallInst::Create(getCheckFunctionType(Ctx), CheckFunction,
230*b9d83effSJeremy Morse                                 {Flag}, "", F.begin()->getFirstInsertionPt());
231f9270214SYuanfang Chen     CI->addParamAttr(0, Attribute::NoUndef);
232f9270214SYuanfang Chen     if (UseX86FastCall) {
233f9270214SYuanfang Chen       CI->setCallingConv(CallingConv::X86_FastCall);
234f9270214SYuanfang Chen       CI->addParamAttr(0, Attribute::InReg);
235f9270214SYuanfang Chen     }
236f9270214SYuanfang Chen 
237f9270214SYuanfang Chen     Changed = true;
238f9270214SYuanfang Chen   }
239eddd94c2SYuanfang Chen   return Changed;
240f9270214SYuanfang Chen }
241