123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- //===- JMCInstrumenter.cpp - JMC Instrumentation --------------------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // JMCInstrumenter pass:
- // - instrument each function with a call to __CheckForDebuggerJustMyCode. The
- // sole argument should be defined in .msvcjmc. Each flag is 1 byte initilized
- // to 1.
- // - create the dummy COMDAT function __JustMyCode_Default to prevent linking
- // error if __CheckForDebuggerJustMyCode is not available.
- // - For MSVC:
- // add "/alternatename:__CheckForDebuggerJustMyCode=__JustMyCode_Default" to
- // "llvm.linker.options"
- // For ELF:
- // Rename __JustMyCode_Default to __CheckForDebuggerJustMyCode and mark it as
- // weak symbol.
- //===----------------------------------------------------------------------===//
- #include "llvm/ADT/SmallString.h"
- #include "llvm/ADT/StringExtras.h"
- #include "llvm/CodeGen/Passes.h"
- #include "llvm/IR/DIBuilder.h"
- #include "llvm/IR/DebugInfoMetadata.h"
- #include "llvm/IR/DerivedTypes.h"
- #include "llvm/IR/Function.h"
- #include "llvm/IR/Instructions.h"
- #include "llvm/IR/LLVMContext.h"
- #include "llvm/IR/Module.h"
- #include "llvm/IR/Type.h"
- #include "llvm/InitializePasses.h"
- #include "llvm/Pass.h"
- #include "llvm/Support/DJB.h"
- #include "llvm/Support/Path.h"
- #include "llvm/Transforms/Utils/ModuleUtils.h"
- using namespace llvm;
- #define DEBUG_TYPE "jmc-instrument"
- namespace {
- struct JMCInstrumenter : public ModulePass {
- static char ID;
- JMCInstrumenter() : ModulePass(ID) {
- initializeJMCInstrumenterPass(*PassRegistry::getPassRegistry());
- }
- bool runOnModule(Module &M) override;
- };
- char JMCInstrumenter::ID = 0;
- } // namespace
- INITIALIZE_PASS(
- JMCInstrumenter, DEBUG_TYPE,
- "Instrument function entry with call to __CheckForDebuggerJustMyCode",
- false, false)
- ModulePass *llvm::createJMCInstrumenterPass() { return new JMCInstrumenter(); }
- namespace {
- const char CheckFunctionName[] = "__CheckForDebuggerJustMyCode";
- std::string getFlagName(DISubprogram &SP, bool UseX86FastCall) {
- // absolute windows path: windows_backslash
- // relative windows backslash path: windows_backslash
- // relative windows slash path: posix
- // absolute posix path: posix
- // relative posix path: posix
- sys::path::Style PathStyle =
- has_root_name(SP.getDirectory(), sys::path::Style::windows_backslash) ||
- SP.getDirectory().contains("\\") ||
- SP.getFilename().contains("\\")
- ? sys::path::Style::windows_backslash
- : sys::path::Style::posix;
- // Best effort path normalization. This is to guarantee an unique flag symbol
- // is produced for the same directory. Some builds may want to use relative
- // paths, or paths with a specific prefix (see the -fdebug-compilation-dir
- // flag), so only hash paths in debuginfo. Don't expand them to absolute
- // paths.
- SmallString<256> FilePath(SP.getDirectory());
- sys::path::append(FilePath, PathStyle, SP.getFilename());
- sys::path::native(FilePath, PathStyle);
- sys::path::remove_dots(FilePath, /*remove_dot_dot=*/true, PathStyle);
- // The naming convention for the flag name is __<hash>_<file name> with '.' in
- // <file name> replaced with '@'. For example C:\file.any.c would have a flag
- // __D032E919_file@any@c. The naming convention match MSVC's format however
- // the match is not required to make JMC work. The hashing function used here
- // is different from MSVC's.
- std::string Suffix;
- for (auto C : sys::path::filename(FilePath, PathStyle))
- Suffix.push_back(C == '.' ? '@' : C);
- sys::path::remove_filename(FilePath, PathStyle);
- return (UseX86FastCall ? "_" : "__") +
- utohexstr(djbHash(FilePath), /*LowerCase=*/false,
- /*Width=*/8) +
- "_" + Suffix;
- }
- void attachDebugInfo(GlobalVariable &GV, DISubprogram &SP) {
- Module &M = *GV.getParent();
- DICompileUnit *CU = SP.getUnit();
- assert(CU);
- DIBuilder DB(M, false, CU);
- auto *DType =
- DB.createBasicType("unsigned char", 8, dwarf::DW_ATE_unsigned_char,
- llvm::DINode::FlagArtificial);
- auto *DGVE = DB.createGlobalVariableExpression(
- CU, GV.getName(), /*LinkageName=*/StringRef(), SP.getFile(),
- /*LineNo=*/0, DType, /*IsLocalToUnit=*/true, /*IsDefined=*/true);
- GV.addMetadata(LLVMContext::MD_dbg, *DGVE);
- DB.finalize();
- }
- FunctionType *getCheckFunctionType(LLVMContext &Ctx) {
- Type *VoidTy = Type::getVoidTy(Ctx);
- PointerType *VoidPtrTy = Type::getInt8PtrTy(Ctx);
- return FunctionType::get(VoidTy, VoidPtrTy, false);
- }
- Function *createDefaultCheckFunction(Module &M, bool UseX86FastCall) {
- LLVMContext &Ctx = M.getContext();
- const char *DefaultCheckFunctionName =
- UseX86FastCall ? "_JustMyCode_Default" : "__JustMyCode_Default";
- // Create the function.
- Function *DefaultCheckFunc =
- Function::Create(getCheckFunctionType(Ctx), GlobalValue::ExternalLinkage,
- DefaultCheckFunctionName, &M);
- DefaultCheckFunc->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
- DefaultCheckFunc->addParamAttr(0, Attribute::NoUndef);
- if (UseX86FastCall)
- DefaultCheckFunc->addParamAttr(0, Attribute::InReg);
- BasicBlock *EntryBB = BasicBlock::Create(Ctx, "", DefaultCheckFunc);
- ReturnInst::Create(Ctx, EntryBB);
- return DefaultCheckFunc;
- }
- } // namespace
- bool JMCInstrumenter::runOnModule(Module &M) {
- bool Changed = false;
- LLVMContext &Ctx = M.getContext();
- Triple ModuleTriple(M.getTargetTriple());
- bool IsMSVC = ModuleTriple.isKnownWindowsMSVCEnvironment();
- bool IsELF = ModuleTriple.isOSBinFormatELF();
- assert((IsELF || IsMSVC) && "Unsupported triple for JMC");
- bool UseX86FastCall = IsMSVC && ModuleTriple.getArch() == Triple::x86;
- const char *const FlagSymbolSection = IsELF ? ".data.just.my.code" : ".msvcjmc";
- GlobalValue *CheckFunction = nullptr;
- DenseMap<DISubprogram *, Constant *> SavedFlags(8);
- for (auto &F : M) {
- if (F.isDeclaration())
- continue;
- auto *SP = F.getSubprogram();
- if (!SP)
- continue;
- Constant *&Flag = SavedFlags[SP];
- if (!Flag) {
- std::string FlagName = getFlagName(*SP, UseX86FastCall);
- IntegerType *FlagTy = Type::getInt8Ty(Ctx);
- Flag = M.getOrInsertGlobal(FlagName, FlagTy, [&] {
- // FIXME: Put the GV in comdat and have linkonce_odr linkage to save
- // .msvcjmc section space? maybe not worth it.
- GlobalVariable *GV = new GlobalVariable(
- M, FlagTy, /*isConstant=*/false, GlobalValue::InternalLinkage,
- ConstantInt::get(FlagTy, 1), FlagName);
- GV->setSection(FlagSymbolSection);
- GV->setAlignment(Align(1));
- GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
- attachDebugInfo(*GV, *SP);
- return GV;
- });
- }
- if (!CheckFunction) {
- Function *DefaultCheckFunc =
- createDefaultCheckFunction(M, UseX86FastCall);
- if (IsELF) {
- DefaultCheckFunc->setName(CheckFunctionName);
- DefaultCheckFunc->setLinkage(GlobalValue::WeakAnyLinkage);
- CheckFunction = DefaultCheckFunc;
- } else {
- assert(!M.getFunction(CheckFunctionName) &&
- "JMC instrument more than once?");
- auto *CheckFunc = cast<Function>(
- M.getOrInsertFunction(CheckFunctionName, getCheckFunctionType(Ctx))
- .getCallee());
- CheckFunc->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
- CheckFunc->addParamAttr(0, Attribute::NoUndef);
- if (UseX86FastCall) {
- CheckFunc->setCallingConv(CallingConv::X86_FastCall);
- CheckFunc->addParamAttr(0, Attribute::InReg);
- }
- CheckFunction = CheckFunc;
- StringRef DefaultCheckFunctionName = DefaultCheckFunc->getName();
- appendToUsed(M, {DefaultCheckFunc});
- Comdat *C = M.getOrInsertComdat(DefaultCheckFunctionName);
- C->setSelectionKind(Comdat::Any);
- DefaultCheckFunc->setComdat(C);
- // Add a linker option /alternatename to set the default implementation
- // for the check function.
- // https://devblogs.microsoft.com/oldnewthing/20200731-00/?p=104024
- std::string AltOption = std::string("/alternatename:") +
- CheckFunctionName + "=" +
- DefaultCheckFunctionName.str();
- llvm::Metadata *Ops[] = {llvm::MDString::get(Ctx, AltOption)};
- MDTuple *N = MDNode::get(Ctx, Ops);
- M.getOrInsertNamedMetadata("llvm.linker.options")->addOperand(N);
- }
- }
- // FIXME: it would be nice to make CI scheduling boundary, although in
- // practice it does not matter much.
- auto *CI = CallInst::Create(getCheckFunctionType(Ctx), CheckFunction,
- {Flag}, "", &*F.begin()->getFirstInsertionPt());
- CI->addParamAttr(0, Attribute::NoUndef);
- if (UseX86FastCall) {
- CI->setCallingConv(CallingConv::X86_FastCall);
- CI->addParamAttr(0, Attribute::InReg);
- }
- Changed = true;
- }
- return Changed;
- }
|