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(path: SP.getDirectory(), style: sys::path::Style::windows_backslash) || |
80 | SP.getDirectory().contains(Other: "\\" ) || |
81 | SP.getFilename().contains(Other: "\\" ) |
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(path&: FilePath, style: PathStyle, a: SP.getFilename()); |
91 | sys::path::native(path&: FilePath, style: PathStyle); |
92 | sys::path::remove_dots(path&: FilePath, /*remove_dot_dot=*/true, style: 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(path: FilePath, style: PathStyle)) |
102 | Suffix.push_back(c: C == '.' ? '@' : C); |
103 | |
104 | sys::path::remove_filename(path&: FilePath, style: PathStyle); |
105 | return (UseX86FastCall ? "_" : "__" ) + |
106 | utohexstr(X: djbHash(Buffer: 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(Name: "unsigned char" , SizeInBits: 8, Encoding: dwarf::DW_ATE_unsigned_char, |
119 | Flags: llvm::DINode::FlagArtificial); |
120 | |
121 | auto *DGVE = DB.createGlobalVariableExpression( |
122 | Context: CU, Name: GV.getName(), /*LinkageName=*/StringRef(), File: SP.getFile(), |
123 | /*LineNo=*/0, Ty: DType, /*IsLocalToUnit=*/true, /*IsDefined=*/isDefined: true); |
124 | GV.addMetadata(KindID: LLVMContext::MD_dbg, MD&: *DGVE); |
125 | DB.finalize(); |
126 | } |
127 | |
128 | FunctionType *getCheckFunctionType(LLVMContext &Ctx) { |
129 | Type *VoidTy = Type::getVoidTy(C&: Ctx); |
130 | PointerType *VoidPtrTy = PointerType::getUnqual(C&: Ctx); |
131 | return FunctionType::get(Result: VoidTy, Params: VoidPtrTy, isVarArg: 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(Ty: getCheckFunctionType(Ctx), Linkage: GlobalValue::ExternalLinkage, |
141 | N: DefaultCheckFunctionName, M: &M); |
142 | DefaultCheckFunc->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); |
143 | DefaultCheckFunc->addParamAttr(ArgNo: 0, Kind: Attribute::NoUndef); |
144 | if (UseX86FastCall) |
145 | DefaultCheckFunc->addParamAttr(ArgNo: 0, Kind: Attribute::InReg); |
146 | |
147 | BasicBlock *EntryBB = BasicBlock::Create(Context&: Ctx, Name: "" , Parent: DefaultCheckFunc); |
148 | ReturnInst::Create(C&: Ctx, InsertAtEnd: 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&: *SP, UseX86FastCall); |
175 | IntegerType *FlagTy = Type::getInt8Ty(C&: Ctx); |
176 | Flag = M.getOrInsertGlobal(Name: FlagName, Ty: FlagTy, CreateGlobalCallback: [&] { |
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(Ty: FlagTy, V: 1), FlagName); |
182 | GV->setSection(FlagSymbolSection); |
183 | GV->setAlignment(Align(1)); |
184 | GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); |
185 | attachDebugInfo(GV&: *GV, SP&: *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 | Val: M.getOrInsertFunction(Name: CheckFunctionName, T: getCheckFunctionType(Ctx)) |
202 | .getCallee()); |
203 | CheckFunc->setUnnamedAddr(GlobalValue::UnnamedAddr::Global); |
204 | CheckFunc->addParamAttr(ArgNo: 0, Kind: Attribute::NoUndef); |
205 | if (UseX86FastCall) { |
206 | CheckFunc->setCallingConv(CallingConv::X86_FastCall); |
207 | CheckFunc->addParamAttr(ArgNo: 0, Kind: Attribute::InReg); |
208 | } |
209 | CheckFunction = CheckFunc; |
210 | |
211 | StringRef DefaultCheckFunctionName = DefaultCheckFunc->getName(); |
212 | appendToUsed(M, Values: {DefaultCheckFunc}); |
213 | Comdat *C = M.getOrInsertComdat(Name: 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(Context&: Ctx, Str: AltOption)}; |
223 | MDTuple *N = MDNode::get(Context&: Ctx, MDs: Ops); |
224 | M.getOrInsertNamedMetadata(Name: "llvm.linker.options" )->addOperand(M: 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(Ty: getCheckFunctionType(Ctx), Func: CheckFunction, |
230 | Args: {Flag}, NameStr: "" , InsertBefore: F.begin()->getFirstInsertionPt()); |
231 | CI->addParamAttr(ArgNo: 0, Kind: Attribute::NoUndef); |
232 | if (UseX86FastCall) { |
233 | CI->setCallingConv(CallingConv::X86_FastCall); |
234 | CI->addParamAttr(ArgNo: 0, Kind: Attribute::InReg); |
235 | } |
236 | |
237 | Changed = true; |
238 | } |
239 | return Changed; |
240 | } |
241 | |