blob: a1f0a50406643e57c57195a5e40f7c88ff56b15f [file] [log] [blame]
Yuanfang Chen379683f2022-02-10 15:10:48 -08001//===- 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 Chen379683f2022-02-10 15:10:48 -080010// - 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 Chena9ee7102022-03-07 12:42:09 -080013// - 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 Chen379683f2022-02-10 15:10:48 -080021//===----------------------------------------------------------------------===//
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
40using namespace llvm;
41
42#define DEBUG_TYPE "jmc-instrument"
43
44namespace {
45struct JMCInstrumenter : public ModulePass {
46 static char ID;
47 JMCInstrumenter() : ModulePass(ID) {
48 initializeJMCInstrumenterPass(*PassRegistry::getPassRegistry());
49 }
50 bool runOnModule(Module &M) override;
51};
52char JMCInstrumenter::ID = 0;
53} // namespace
54
55INITIALIZE_PASS(
56 JMCInstrumenter, DEBUG_TYPE,
57 "Instrument function entry with call to __CheckForDebuggerJustMyCode",
58 false, false)
59
60ModulePass *llvm::createJMCInstrumenterPass() { return new JMCInstrumenter(); }
61
62namespace {
63const char CheckFunctionName[] = "__CheckForDebuggerJustMyCode";
64
65std::string getFlagName(DISubprogram &SP, bool UseX86FastCall) {
Yuanfang Chen9bbf1662022-03-10 10:50:30 -080066 // 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 Hirata3e89ca92022-06-04 20:58:58 -070073 SP.getDirectory().contains("\\") ||
74 SP.getFilename().contains("\\")
Yuanfang Chen9bbf1662022-03-10 10:50:30 -080075 ? sys::path::Style::windows_backslash
76 : sys::path::Style::posix;
Yuanfang Chen379683f2022-02-10 15:10:48 -080077 // 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 Chen9bbf1662022-03-10 10:50:30 -080083 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 Chen379683f2022-02-10 15:10:48 -080086
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 Chen9bbf1662022-03-10 10:50:30 -080094 for (auto C : sys::path::filename(FilePath, PathStyle))
Yuanfang Chen379683f2022-02-10 15:10:48 -080095 Suffix.push_back(C == '.' ? '@' : C);
96
Yuanfang Chen9bbf1662022-03-10 10:50:30 -080097 sys::path::remove_filename(FilePath, PathStyle);
Yuanfang Chen379683f2022-02-10 15:10:48 -080098 return (UseX86FastCall ? "_" : "__") +
99 utohexstr(djbHash(FilePath), /*LowerCase=*/false,
100 /*Width=*/8) +
101 "_" + Suffix;
102}
103
104void 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
121FunctionType *getCheckFunctionType(LLVMContext &Ctx) {
122 Type *VoidTy = Type::getVoidTy(Ctx);
Bjorn Pettersson0d1869a2023-08-11 14:38:53 +0200123 PointerType *VoidPtrTy = PointerType::getUnqual(Ctx);
Yuanfang Chen379683f2022-02-10 15:10:48 -0800124 return FunctionType::get(VoidTy, VoidPtrTy, false);
125}
126
Yuanfang Chena9ee7102022-03-07 12:42:09 -0800127Function *createDefaultCheckFunction(Module &M, bool UseX86FastCall) {
Yuanfang Chen379683f2022-02-10 15:10:48 -0800128 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 Chena9ee7102022-03-07 12:42:09 -0800139
Yuanfang Chen379683f2022-02-10 15:10:48 -0800140 BasicBlock *EntryBB = BasicBlock::Create(Ctx, "", DefaultCheckFunc);
141 ReturnInst::Create(Ctx, EntryBB);
Yuanfang Chena9ee7102022-03-07 12:42:09 -0800142 return DefaultCheckFunc;
Yuanfang Chen379683f2022-02-10 15:10:48 -0800143}
144} // namespace
145
146bool JMCInstrumenter::runOnModule(Module &M) {
147 bool Changed = false;
148 LLVMContext &Ctx = M.getContext();
149 Triple ModuleTriple(M.getTargetTriple());
Yuanfang Chena9ee7102022-03-07 12:42:09 -0800150 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 Chen745b0fe2022-10-19 10:36:36 -0700154 const char *const FlagSymbolSection = IsELF ? ".data.just.my.code" : ".msvcjmc";
Yuanfang Chen379683f2022-02-10 15:10:48 -0800155
Yuanfang Chena9ee7102022-03-07 12:42:09 -0800156 GlobalValue *CheckFunction = nullptr;
Yuanfang Chen379683f2022-02-10 15:10:48 -0800157 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 Chena9ee7102022-03-07 12:42:09 -0800175 GV->setSection(FlagSymbolSection);
Yuanfang Chen379683f2022-02-10 15:10:48 -0800176 GV->setAlignment(Align(1));
177 GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
178 attachDebugInfo(*GV, *SP);
179 return GV;
180 });
181 }
182
183 if (!CheckFunction) {
Yuanfang Chena9ee7102022-03-07 12:42:09 -0800184 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 Chen379683f2022-02-10 15:10:48 -0800218 }
219 }
220 // FIXME: it would be nice to make CI scheduling boundary, although in
221 // practice it does not matter much.
Yuanfang Chena9ee7102022-03-07 12:42:09 -0800222 auto *CI = CallInst::Create(getCheckFunctionType(Ctx), CheckFunction,
223 {Flag}, "", &*F.begin()->getFirstInsertionPt());
Yuanfang Chen379683f2022-02-10 15:10:48 -0800224 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 Chena9ee7102022-03-07 12:42:09 -0800232 return Changed;
Yuanfang Chen379683f2022-02-10 15:10:48 -0800233}