xref: /llvm-project/llvm/lib/CodeGen/JMCInstrumenter.cpp (revision f46fa4de4a95329b74ba0bb4ce9623b64ad84876)
1 //===- JMCInstrumenter.cpp - JMC Instrumentation --------------------------===//
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 // JMCInstrumenter pass:
10 // - add "/alternatename:__CheckForDebuggerJustMyCode=__JustMyCode_Default" to
11 //   "llvm.linker.options"
12 // - create the dummy COMDAT function __JustMyCode_Default
13 // - instrument each function with a call to __CheckForDebuggerJustMyCode. The
14 //   sole argument should be defined in .msvcjmc. Each flag is 1 byte initilized
15 //   to 1.
16 // - (TODO) currently targeting MSVC, adds ELF debuggers support
17 //
18 //===----------------------------------------------------------------------===//
19 
20 #include "llvm/ADT/SmallString.h"
21 #include "llvm/ADT/StringExtras.h"
22 #include "llvm/CodeGen/Passes.h"
23 #include "llvm/IR/DIBuilder.h"
24 #include "llvm/IR/DebugInfoMetadata.h"
25 #include "llvm/IR/DerivedTypes.h"
26 #include "llvm/IR/Function.h"
27 #include "llvm/IR/Instructions.h"
28 #include "llvm/IR/LLVMContext.h"
29 #include "llvm/IR/Module.h"
30 #include "llvm/IR/Type.h"
31 #include "llvm/InitializePasses.h"
32 #include "llvm/Pass.h"
33 #include "llvm/Support/DJB.h"
34 #include "llvm/Support/Path.h"
35 #include "llvm/Transforms/Utils/ModuleUtils.h"
36 
37 using namespace llvm;
38 
39 #define DEBUG_TYPE "jmc-instrument"
40 
41 namespace {
42 struct JMCInstrumenter : public ModulePass {
43   static char ID;
44   JMCInstrumenter() : ModulePass(ID) {
45     initializeJMCInstrumenterPass(*PassRegistry::getPassRegistry());
46   }
47   bool runOnModule(Module &M) override;
48 };
49 char JMCInstrumenter::ID = 0;
50 } // namespace
51 
52 INITIALIZE_PASS(
53     JMCInstrumenter, DEBUG_TYPE,
54     "Instrument function entry with call to __CheckForDebuggerJustMyCode",
55     false, false)
56 
57 ModulePass *llvm::createJMCInstrumenterPass() { return new JMCInstrumenter(); }
58 
59 namespace {
60 const char CheckFunctionName[] = "__CheckForDebuggerJustMyCode";
61 
62 std::string getFlagName(DISubprogram &SP, bool UseX86FastCall) {
63   // Best effort path normalization. This is to guarantee an unique flag symbol
64   // is produced for the same directory. Some builds may want to use relative
65   // paths, or paths with a specific prefix (see the -fdebug-compilation-dir
66   // flag), so only hash paths in debuginfo. Don't expand them to absolute
67   // paths.
68   SmallString<256> FilePath(SP.getDirectory());
69   sys::path::append(FilePath, SP.getFilename());
70   sys::path::native(FilePath);
71   sys::path::remove_dots(FilePath, /*remove_dot_dot=*/true);
72 
73   // The naming convention for the flag name is __<hash>_<file name> with '.' in
74   // <file name> replaced with '@'. For example C:\file.any.c would have a flag
75   // __D032E919_file@any@c. The naming convention match MSVC's format however
76   // the match is not required to make JMC work. The hashing function used here
77   // is different from MSVC's.
78 
79   std::string Suffix;
80   for (auto C : sys::path::filename(FilePath))
81     Suffix.push_back(C == '.' ? '@' : C);
82 
83   sys::path::remove_filename(FilePath);
84   return (UseX86FastCall ? "_" : "__") +
85          utohexstr(djbHash(FilePath), /*LowerCase=*/false,
86                    /*Width=*/8) +
87          "_" + Suffix;
88 }
89 
90 void attachDebugInfo(GlobalVariable &GV, DISubprogram &SP) {
91   Module &M = *GV.getParent();
92   DICompileUnit *CU = SP.getUnit();
93   assert(CU);
94   DIBuilder DB(M, false, CU);
95 
96   auto *DType =
97       DB.createBasicType("unsigned char", 8, dwarf::DW_ATE_unsigned_char,
98                          llvm::DINode::FlagArtificial);
99 
100   auto *DGVE = DB.createGlobalVariableExpression(
101       CU, GV.getName(), /*LinkageName=*/StringRef(), SP.getFile(),
102       /*LineNo=*/0, DType, /*IsLocalToUnit=*/true, /*IsDefined=*/true);
103   GV.addMetadata(LLVMContext::MD_dbg, *DGVE);
104   DB.finalize();
105 }
106 
107 FunctionType *getCheckFunctionType(LLVMContext &Ctx) {
108   Type *VoidTy = Type::getVoidTy(Ctx);
109   PointerType *VoidPtrTy = Type::getInt8PtrTy(Ctx);
110   return FunctionType::get(VoidTy, VoidPtrTy, false);
111 }
112 
113 void createDefaultCheckFunction(Module &M, bool UseX86FastCall) {
114   LLVMContext &Ctx = M.getContext();
115   const char *DefaultCheckFunctionName =
116       UseX86FastCall ? "_JustMyCode_Default" : "__JustMyCode_Default";
117   // Create the function.
118   Function *DefaultCheckFunc =
119       Function::Create(getCheckFunctionType(Ctx), GlobalValue::ExternalLinkage,
120                        DefaultCheckFunctionName, &M);
121   DefaultCheckFunc->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
122   DefaultCheckFunc->addParamAttr(0, Attribute::NoUndef);
123   if (UseX86FastCall)
124     DefaultCheckFunc->addParamAttr(0, Attribute::InReg);
125   appendToUsed(M, {DefaultCheckFunc});
126   Comdat *C = M.getOrInsertComdat(DefaultCheckFunctionName);
127   C->setSelectionKind(Comdat::Any);
128   DefaultCheckFunc->setComdat(C);
129   BasicBlock *EntryBB = BasicBlock::Create(Ctx, "", DefaultCheckFunc);
130   ReturnInst::Create(Ctx, EntryBB);
131 
132   // Add a linker option /alternatename to set the default implementation for
133   // the check function.
134   // https://devblogs.microsoft.com/oldnewthing/20200731-00/?p=104024
135   std::string AltOption = std::string("/alternatename:") + CheckFunctionName +
136                           "=" + DefaultCheckFunctionName;
137   llvm::Metadata *Ops[] = {llvm::MDString::get(Ctx, AltOption)};
138   MDTuple *N = MDNode::get(Ctx, Ops);
139   M.getOrInsertNamedMetadata("llvm.linker.options")->addOperand(N);
140 }
141 } // namespace
142 
143 bool JMCInstrumenter::runOnModule(Module &M) {
144   bool Changed = false;
145   LLVMContext &Ctx = M.getContext();
146   Triple ModuleTriple(M.getTargetTriple());
147   bool UseX86FastCall =
148       ModuleTriple.isOSWindows() && ModuleTriple.getArch() == Triple::x86;
149 
150   Function *CheckFunction = nullptr;
151   DenseMap<DISubprogram *, Constant *> SavedFlags(8);
152   for (auto &F : M) {
153     if (F.isDeclaration())
154       continue;
155     auto *SP = F.getSubprogram();
156     if (!SP)
157       continue;
158 
159     Constant *&Flag = SavedFlags[SP];
160     if (!Flag) {
161       std::string FlagName = getFlagName(*SP, UseX86FastCall);
162       IntegerType *FlagTy = Type::getInt8Ty(Ctx);
163       Flag = M.getOrInsertGlobal(FlagName, FlagTy, [&] {
164         // FIXME: Put the GV in comdat and have linkonce_odr linkage to save
165         //        .msvcjmc section space? maybe not worth it.
166         GlobalVariable *GV = new GlobalVariable(
167             M, FlagTy, /*isConstant=*/false, GlobalValue::InternalLinkage,
168             ConstantInt::get(FlagTy, 1), FlagName);
169         GV->setSection(".msvcjmc");
170         GV->setAlignment(Align(1));
171         GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
172         attachDebugInfo(*GV, *SP);
173         return GV;
174       });
175     }
176 
177     if (!CheckFunction) {
178       assert(!M.getFunction(CheckFunctionName) &&
179              "JMC instrument more than once?");
180       CheckFunction = cast<Function>(
181           M.getOrInsertFunction(CheckFunctionName, getCheckFunctionType(Ctx))
182               .getCallee());
183       CheckFunction->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
184       CheckFunction->addParamAttr(0, Attribute::NoUndef);
185       if (UseX86FastCall) {
186         CheckFunction->setCallingConv(CallingConv::X86_FastCall);
187         CheckFunction->addParamAttr(0, Attribute::InReg);
188       }
189     }
190     // FIXME: it would be nice to make CI scheduling boundary, although in
191     //        practice it does not matter much.
192     auto *CI = CallInst::Create(CheckFunction, {Flag}, "",
193                                 &*F.begin()->getFirstInsertionPt());
194     CI->addParamAttr(0, Attribute::NoUndef);
195     if (UseX86FastCall) {
196       CI->setCallingConv(CallingConv::X86_FastCall);
197       CI->addParamAttr(0, Attribute::InReg);
198     }
199 
200     Changed = true;
201   }
202   if (!Changed)
203     return false;
204 
205   createDefaultCheckFunction(M, UseX86FastCall);
206   return true;
207 }
208