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