JMCInstrumenter.cpp 9.0 KB

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