================
@@ -0,0 +1,251 @@
+//===------ WindowsHotPatch.cpp - Support for Windows hotpatching 
---------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Marks functions with the `marked_for_windows_hot_patching` attribute.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/CodeGen/Passes.h"
+#include "llvm/IR/Attributes.h"
+#include "llvm/IR/DIBuilder.h"
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/Module.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "windows-hot-patch"
+
+// A file containing list of mangled function names to mark for hot patching.
+static cl::opt<std::string> LLVMMSHotPatchFunctionsFile(
+    "ms-hotpatch-functions-file", cl::value_desc("filename"),
+    cl::desc("A file containing list of mangled function names to mark for hot 
"
+             "patching"));
+
+// A list of mangled function names to mark for hot patching.
+static cl::list<std::string> LLVMMSHotPatchFunctionsList(
+    "ms-hotpatch-functions-list", cl::value_desc("list"),
+    cl::desc("A list of mangled function names to mark for hot patching"),
+    cl::CommaSeparated);
+
+namespace {
+
+class WindowsHotPatch : public ModulePass {
+  struct GlobalVariableUse {
+    GlobalVariable *GV;
+    Instruction *User;
+    unsigned Op;
+  };
+
+public:
+  static char ID;
+
+  WindowsHotPatch() : ModulePass(ID) {
+    initializeWindowsHotPatchPass(*PassRegistry::getPassRegistry());
+  }
+
+  void getAnalysisUsage(AnalysisUsage &AU) const override {
+    AU.setPreservesCFG();
+  }
+
+  bool runOnModule(Module &M) override;
+
+private:
+  bool
+  runOnFunction(Function &F,
+                SmallDenseMap<GlobalVariable *, GlobalVariable *> &RefMapping);
+  void replaceGlobalVariableUses(
+      Function &F, SmallVectorImpl<GlobalVariableUse> &GVUses,
+      SmallDenseMap<GlobalVariable *, GlobalVariable *> &RefMapping,
+      DIBuilder &DebugInfo);
+};
+
+} // end anonymous namespace
+
+char WindowsHotPatch::ID = 0;
+
+INITIALIZE_PASS(WindowsHotPatch, "windows-hot-patch",
+                "Mark functions for Windows hot patch support", false, false)
+ModulePass *llvm::createWindowsHotPatch() { return new WindowsHotPatch(); }
+
+// Find functions marked with Attribute::MarkedForWindowsHotPatching and modify
+// their code (if necessary) to account for accesses to global variables.
+bool WindowsHotPatch::runOnModule(Module &M) {
+  // The front end may have already marked functions for hot-patching. However,
+  // we also allow marking functions by passing -ms-hotpatch-functions-file or
+  // -ms-hotpatch-functions-list directly to LLVM. This allows hot-patching to
+  // work with languages that have not yet updated their front-ends.
+  if (!LLVMMSHotPatchFunctionsFile.empty() ||
+      !LLVMMSHotPatchFunctionsList.empty()) {
+    std::vector<std::string> HotPatchFunctionsList;
+
+    if (!LLVMMSHotPatchFunctionsFile.empty()) {
+      auto BufOrErr = llvm::MemoryBuffer::getFile(LLVMMSHotPatchFunctionsFile);
+      if (BufOrErr) {
+        const llvm::MemoryBuffer &FileBuffer = **BufOrErr;
+        for (llvm::line_iterator I(FileBuffer.getMemBufferRef(), true), E;
+             I != E; ++I) {
+          HotPatchFunctionsList.push_back(std::string{*I});
+        }
+      } else {
+        M.getContext().diagnose(llvm::DiagnosticInfoGeneric{
+            llvm::Twine("failed to open hotpatch functions file "
+                        "(--ms-hotpatch-functions-file): ") +
+            LLVMMSHotPatchFunctionsFile + llvm::Twine(" : ") +
+            BufOrErr.getError().message()});
+      }
+    }
+
+    if (!LLVMMSHotPatchFunctionsList.empty()) {
+      for (const auto &FuncName : LLVMMSHotPatchFunctionsList) {
+        HotPatchFunctionsList.push_back(FuncName);
+      }
+    }
+
+    // Build a set for quick lookups. This points into HotPatchFunctionsList, 
so
+    // HotPatchFunctionsList must live longer than HotPatchFunctionsSet.
+    llvm::SmallSet<llvm::StringRef, 16> HotPatchFunctionsSet;
+    for (const auto &FuncName : HotPatchFunctionsList) {
+      HotPatchFunctionsSet.insert(llvm::StringRef{FuncName});
+    }
+
+    // Iterate through all of the functions and check whether they need to be
+    // marked for hotpatching using the list provided directly to LLVM.
+    for (auto &F : M.functions()) {
+      // Ignore declarations that are not definitions.
+      if (F.isDeclarationForLinker()) {
+        continue;
+      }
+
+      llvm::StringRef FuncName{};
+      if (auto *SP = F.getSubprogram(); SP != nullptr) {
+        FuncName = SP->getLinkageName();
+        if (FuncName.empty()) {
+          FuncName = F.getName();
+        }
+      } else {
+        FuncName = F.getName();
+      }
+      if (HotPatchFunctionsSet.contains(FuncName)) {
+        F.addFnAttr(Attribute::MarkedForWindowsHotPatching);
+      }
+    }
+  }
+
+  SmallDenseMap<GlobalVariable *, GlobalVariable *> RefMapping;
+  bool MadeChanges = false;
+  for (auto &F : M.functions()) {
+    if (F.hasFnAttribute(Attribute::MarkedForWindowsHotPatching)) {
+      if (runOnFunction(F, RefMapping)) {
+        MadeChanges = true;
+      }
+    }
+  }
+  return MadeChanges;
+}
+
+// Processes a function that is marked for hot-patching.
+//
+// If a function is marked for hot-patching, we generate an S_HOTPATCHFUNC
+// CodeView debug symbol. Tools that generate hot-patches look for
+// S_HOTPATCHFUNC in final PDBs so that they can find functions that have been
+// hot-patched and so that they can distinguish hot-patched functions from
+// non-hot-patched functions.
+//
+// Also, in functions that are hot-patched, we must indirect all access to
+// (mutable) global variables through a pointer. This pointer may point into 
the
+// unpatched ("base") binary or may point into the patched image, depending on
+// whether a hot-patch was loaded as a patch or as a base image.  These
+// indirections go through a new global variable, `named __ref_<Foo>` where
+// `<Foo>` is the original symbol name of the global variable.
+//
+// This function handles rewriting accesses to global variables, but the
+// generation of S_HOTPATCHFUNC occurs in
+// CodeViewDebug::emitHotPatchInformation().
+//
+// Returns true if any changes were made to the function.
+bool WindowsHotPatch::runOnFunction(
+    Function &F,
+    SmallDenseMap<GlobalVariable *, GlobalVariable *> &RefMapping) {
+  SmallVector<GlobalVariableUse, 32> GVUses;
+  for (auto &I : instructions(F)) {
+    for (auto &U : I.operands()) {
+      // Discover all uses of GlobalVariable, these will need to be replaced.
+      GlobalVariable *GV = dyn_cast<GlobalVariable>(&U);
+      if ((GV != nullptr) &&
+          !GV->hasAttribute(Attribute::AllowDirectAccessInHotPatchFunction)) {
+        unsigned OpNo = &U - I.op_begin();
+        GVUses.push_back({GV, &I, OpNo});
+      }
+    }
+  }
+
+  if (!GVUses.empty()) {
+    const llvm::DISubprogram *Subprogram = F.getSubprogram();
+    DIBuilder DebugInfo{*F.getParent(), true,
+                        Subprogram != nullptr ? Subprogram->getUnit()
+                                              : nullptr};
+    replaceGlobalVariableUses(F, GVUses, RefMapping, DebugInfo);
+    if (Subprogram != nullptr) {
+      DebugInfo.finalize();
+    }
+    return true;
+  } else {
+    return false;
+  }
+}
+
+void WindowsHotPatch::replaceGlobalVariableUses(
+    Function &F, SmallVectorImpl<GlobalVariableUse> &GVUses,
+    SmallDenseMap<GlobalVariable *, GlobalVariable *> &RefMapping,
+    DIBuilder &DebugInfo) {
+  for (auto &GVUse : GVUses) {
+    IRBuilder<> Builder(GVUse.User);
+
+    // Get or create a new global variable that points to the old one and who's
+    // name begins with `__ref_`.
+    GlobalVariable *&ReplaceWithRefGV =
+        RefMapping.try_emplace(GVUse.GV).first->second;
+    if (ReplaceWithRefGV == nullptr) {
+      Constant *AddrOfOldGV = ConstantExpr::getGetElementPtr(
+          Builder.getPtrTy(), GVUse.GV, ArrayRef<Value *>{});
----------------
efriedma-quic wrote:

The getGetElementPtr call here just returns GVUse.GV, I think?  We do constant 
folding on constant expressions.

https://github.com/llvm/llvm-project/pull/138972
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to