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