aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/llvm/lib/CodeGen/JMCInstrumenter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/llvm/lib/CodeGen/JMCInstrumenter.cpp')
-rw-r--r--contrib/llvm-project/llvm/lib/CodeGen/JMCInstrumenter.cpp240
1 files changed, 240 insertions, 0 deletions
diff --git a/contrib/llvm-project/llvm/lib/CodeGen/JMCInstrumenter.cpp b/contrib/llvm-project/llvm/lib/CodeGen/JMCInstrumenter.cpp
new file mode 100644
index 000000000000..e2aaebedf5a4
--- /dev/null
+++ b/contrib/llvm-project/llvm/lib/CodeGen/JMCInstrumenter.cpp
@@ -0,0 +1,240 @@
+//===- 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/CodeGen/JMCInstrumenter.h"
+#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-instrumenter"
+
+static bool runImpl(Module &M);
+namespace {
+struct JMCInstrumenter : public ModulePass {
+ static char ID;
+ JMCInstrumenter() : ModulePass(ID) {
+ initializeJMCInstrumenterPass(*PassRegistry::getPassRegistry());
+ }
+ bool runOnModule(Module &M) override { return runImpl(M); }
+};
+char JMCInstrumenter::ID = 0;
+} // namespace
+
+PreservedAnalyses JMCInstrumenterPass::run(Module &M, ModuleAnalysisManager &) {
+ bool Changed = runImpl(M);
+ return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all();
+}
+
+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 = PointerType::getUnqual(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 runImpl(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;
+}