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