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/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) {
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) ||
73              SP.getDirectory().contains("\\") ||
74              SP.getFilename().contains("\\")
75          ? sys::path::Style::windows_backslash
76          : sys::path::Style::posix;
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());
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);
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;
94  for (auto C : sys::path::filename(FilePath, PathStyle))
95    Suffix.push_back(C == '.' ? '@' : C);
96
97  sys::path::remove_filename(FilePath, PathStyle);
98  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);
123  PointerType *VoidPtrTy = Type::getInt8PtrTy(Ctx);
124  return FunctionType::get(VoidTy, VoidPtrTy, false);
125}
126
127Function *createDefaultCheckFunction(Module &M, bool UseX86FastCall) {
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);
139
140  BasicBlock *EntryBB = BasicBlock::Create(Ctx, "", DefaultCheckFunc);
141  ReturnInst::Create(Ctx, EntryBB);
142  return DefaultCheckFunc;
143}
144} // namespace
145
146bool JMCInstrumenter::runOnModule(Module &M) {
147  bool Changed = false;
148  LLVMContext &Ctx = M.getContext();
149  Triple ModuleTriple(M.getTargetTriple());
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;
154  const char *const FlagSymbolSection = IsELF ? ".data.just.my.code" : ".msvcjmc";
155
156  GlobalValue *CheckFunction = nullptr;
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);
175        GV->setSection(FlagSymbolSection);
176        GV->setAlignment(Align(1));
177        GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
178        attachDebugInfo(*GV, *SP);
179        return GV;
180      });
181    }
182
183    if (!CheckFunction) {
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);
218      }
219    }
220    // FIXME: it would be nice to make CI scheduling boundary, although in
221    //        practice it does not matter much.
222    auto *CI = CallInst::Create(getCheckFunctionType(Ctx), CheckFunction,
223                                {Flag}, "", &*F.begin()->getFirstInsertionPt());
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  }
232  return Changed;
233}
234