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