brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.3 KiB · e2aaebe Raw
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