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