241 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// JMCInstrumenter pass:10// - instrument each function with a call to __CheckForDebuggerJustMyCode. The11// sole argument should be defined in .msvcjmc. Each flag is 1 byte initilized12// to 1.13// - create the dummy COMDAT function __JustMyCode_Default to prevent linking14// error if __CheckForDebuggerJustMyCode is not available.15// - For MSVC:16// add "/alternatename:__CheckForDebuggerJustMyCode=__JustMyCode_Default" to17// "llvm.linker.options"18// For ELF:19// Rename __JustMyCode_Default to __CheckForDebuggerJustMyCode and mark it as20// 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 41using namespace llvm;42 43#define DEBUG_TYPE "jmc-instrumenter"44 45static bool runImpl(Module &M);46namespace {47struct 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};54char JMCInstrumenter::ID = 0;55} // namespace56 57PreservedAnalyses JMCInstrumenterPass::run(Module &M, ModuleAnalysisManager &) {58 bool Changed = runImpl(M);59 return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all();60}61 62INITIALIZE_PASS(63 JMCInstrumenter, DEBUG_TYPE,64 "Instrument function entry with call to __CheckForDebuggerJustMyCode",65 false, false)66 67ModulePass *llvm::createJMCInstrumenterPass() { return new JMCInstrumenter(); }68 69namespace {70const char CheckFunctionName[] = "__CheckForDebuggerJustMyCode";71 72std::string getFlagName(DISubprogram &SP, bool UseX86FastCall) {73 // absolute windows path: windows_backslash74 // relative windows backslash path: windows_backslash75 // relative windows slash path: posix76 // absolute posix path: posix77 // relative posix path: posix78 sys::path::Style PathStyle =79 has_root_name(SP.getDirectory(), sys::path::Style::windows_backslash) ||80 SP.getDirectory().contains("\\") ||81 SP.getFilename().contains("\\")82 ? sys::path::Style::windows_backslash83 : sys::path::Style::posix;84 // Best effort path normalization. This is to guarantee an unique flag symbol85 // is produced for the same directory. Some builds may want to use relative86 // paths, or paths with a specific prefix (see the -fdebug-compilation-dir87 // flag), so only hash paths in debuginfo. Don't expand them to absolute88 // paths.89 SmallString<256> FilePath(SP.getDirectory());90 sys::path::append(FilePath, PathStyle, SP.getFilename());91 sys::path::native(FilePath, PathStyle);92 sys::path::remove_dots(FilePath, /*remove_dot_dot=*/true, PathStyle);93 94 // The naming convention for the flag name is __<hash>_<file name> with '.' in95 // <file name> replaced with '@'. For example C:\file.any.c would have a flag96 // __D032E919_file@any@c. The naming convention match MSVC's format however97 // the match is not required to make JMC work. The hashing function used here98 // is different from MSVC's.99 100 std::string Suffix;101 for (auto C : sys::path::filename(FilePath, PathStyle))102 Suffix.push_back(C == '.' ? '@' : C);103 104 sys::path::remove_filename(FilePath, PathStyle);105 return (UseX86FastCall ? "_" : "__") +106 utohexstr(djbHash(FilePath), /*LowerCase=*/false,107 /*Width=*/8) +108 "_" + Suffix;109}110 111void 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("unsigned char", 8, dwarf::DW_ATE_unsigned_char,119 llvm::DINode::FlagArtificial);120 121 auto *DGVE = DB.createGlobalVariableExpression(122 CU, GV.getName(), /*LinkageName=*/StringRef(), SP.getFile(),123 /*LineNo=*/0, DType, /*IsLocalToUnit=*/true, /*IsDefined=*/true);124 GV.addMetadata(LLVMContext::MD_dbg, *DGVE);125 DB.finalize();126}127 128FunctionType *getCheckFunctionType(LLVMContext &Ctx) {129 Type *VoidTy = Type::getVoidTy(Ctx);130 PointerType *VoidPtrTy = PointerType::getUnqual(Ctx);131 return FunctionType::get(VoidTy, VoidPtrTy, false);132}133 134Function *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(getCheckFunctionType(Ctx), GlobalValue::ExternalLinkage,141 DefaultCheckFunctionName, &M);142 DefaultCheckFunc->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);143 DefaultCheckFunc->addParamAttr(0, Attribute::NoUndef);144 if (UseX86FastCall)145 DefaultCheckFunc->addParamAttr(0, Attribute::InReg);146 147 BasicBlock *EntryBB = BasicBlock::Create(Ctx, "", DefaultCheckFunc);148 ReturnInst::Create(Ctx, EntryBB);149 return DefaultCheckFunc;150}151} // namespace152 153bool 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, UseX86FastCall);175 IntegerType *FlagTy = Type::getInt8Ty(Ctx);176 Flag = M.getOrInsertGlobal(FlagName, FlagTy, [&] {177 // FIXME: Put the GV in comdat and have linkonce_odr linkage to save178 // .msvcjmc section space? maybe not worth it.179 GlobalVariable *GV = new GlobalVariable(180 M, FlagTy, /*isConstant=*/false, GlobalValue::InternalLinkage,181 ConstantInt::get(FlagTy, 1), FlagName);182 GV->setSection(FlagSymbolSection);183 GV->setAlignment(Align(1));184 GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);185 attachDebugInfo(*GV, *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 M.getOrInsertFunction(CheckFunctionName, getCheckFunctionType(Ctx))202 .getCallee());203 CheckFunc->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);204 CheckFunc->addParamAttr(0, Attribute::NoUndef);205 if (UseX86FastCall) {206 CheckFunc->setCallingConv(CallingConv::X86_FastCall);207 CheckFunc->addParamAttr(0, Attribute::InReg);208 }209 CheckFunction = CheckFunc;210 211 StringRef DefaultCheckFunctionName = DefaultCheckFunc->getName();212 appendToUsed(M, {DefaultCheckFunc});213 Comdat *C = M.getOrInsertComdat(DefaultCheckFunctionName);214 C->setSelectionKind(Comdat::Any);215 DefaultCheckFunc->setComdat(C);216 // Add a linker option /alternatename to set the default implementation217 // for the check function.218 // https://devblogs.microsoft.com/oldnewthing/20200731-00/?p=104024219 std::string AltOption = std::string("/alternatename:") +220 CheckFunctionName + "=" +221 DefaultCheckFunctionName.str();222 llvm::Metadata *Ops[] = {llvm::MDString::get(Ctx, AltOption)};223 MDTuple *N = MDNode::get(Ctx, Ops);224 M.getOrInsertNamedMetadata("llvm.linker.options")->addOperand(N);225 }226 }227 // FIXME: it would be nice to make CI scheduling boundary, although in228 // practice it does not matter much.229 auto *CI = CallInst::Create(getCheckFunctionType(Ctx), CheckFunction,230 {Flag}, "", F.begin()->getFirstInsertionPt());231 CI->addParamAttr(0, Attribute::NoUndef);232 if (UseX86FastCall) {233 CI->setCallingConv(CallingConv::X86_FastCall);234 CI->addParamAttr(0, Attribute::InReg);235 }236 237 Changed = true;238 }239 return Changed;240}241