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