================
@@ -0,0 +1,599 @@
+//===------ 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Provides support for the Windows "Secure Hot-Patching" feature.
+//
+// Windows contains technology, called "Secure Hot-Patching" (SHP), for 
securely
+// applying hot-patches to a running system. Hot-patches may be applied to the
+// kernel, kernel-mode components, device drivers, user-mode system services,
+// etc.
+//
+// SHP relies on integration between many tools, including compiler, linker,
+// hot-patch generation tools, and the Windows kernel. This file implements 
that
+// part of the workflow needed in compilers / code generators.
+//
+// SHP is not intended for productivity scenarios such as Edit-and-Continue or
+// interactive development. SHP is intended to minimize downtime during
+// installation of Windows OS patches.
+//
+// In order to work with SHP, LLVM must do all of the following:
+//
+// * On some architectures (X86, AMD64), the function prolog must begin with
+//   hot-patchable instructions. This is handled by the MSVC `/hotpatch` option
+//   and the equivalent `-fms-hotpatch` function. This is necessary because we
+//   generally cannot anticipate which functions will need to be patched in the
+//   future. This option ensures that a function can be hot-patched in the
+//   future, but does not actually generate any hot-patch for it.
+//
+// * For a selected set of functions that are being hot-patched (which are
+//   identified using command-line options), LLVM must generate the
+//   `S_HOTPATCHFUNC` CodeView record (symbol). This record indicates that a
+//   function was compiled with hot-patching enabled.
+//
+//   This implementation uses the `MarkedForWindowsHotPatching` attribute to
+//   annotate those functions that were marked for hot-patching by command-line
+//   parameters. The attribute may be specified by a language front-end by
+//   setting an attribute when a function is created in LLVM IR, or it may be
+//   set by passing LLVM arguments.
+//
+// * For those functions that are hot-patched, LLVM must rewrite references to
+//   global variables so that they are indirected through a `__ref_*` pointer
+//   variable.  For each global variable, that is accessed by a hot-patched
+//   function, e.g. `FOO`, a `__ref_FOO` global pointer variable is created and
+//   all references to the original `FOO` are rewritten as dereferences of the
+//   `__ref_FOO` pointer.
+//
+//   Some globals do not need `__ref_*` indirection. The pointer indirection
+//   behavior can be disabled for these globals by marking them with the
+//   `AllowDirectAccessInHotPatchFunction`.
+//
+// References
+//
+// * "Hotpatching on Windows":
+//   
https://techcommunity.microsoft.com/blog/windowsosplatform/hotpatching-on-windows/2959541
+//
+// * "Hotpatch for Windows client now available":
+//   
https://techcommunity.microsoft.com/blog/windows-itpro-blog/hotpatch-for-windows-client-now-available/4399808
+//
+// * "Get hotpatching for Windows Server":
+//   
https://www.microsoft.com/en-us/windows-server/blog/2025/04/24/tired-of-all-the-restarts-get-hotpatching-for-windows-server/
+//
+//===----------------------------------------------------------------------===//
+
+#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-secure-hot-patch"
+
+// A file containing list of mangled function names to mark for hot patching.
+static cl::opt<std::string> LLVMMSSecureHotPatchFunctionsFile(
+    "ms-secure-hotpatch-functions-file", cl::value_desc("filename"),
+    cl::desc("A file containing list of mangled function names to mark for "
+             "Windows Secure Hot-Patching"));
+
+// A list of mangled function names to mark for hot patching.
+static cl::list<std::string> LLVMMSSecureHotPatchFunctionsList(
+    "ms-secure-hotpatch-functions-list", cl::value_desc("list"),
+    cl::desc("A list of mangled function names to mark for Windows Secure "
+             "Hot-Patching"),
+    cl::CommaSeparated);
+
+namespace {
+
+struct GlobalVariableUse {
+  // GlobalVariable *GV;
+  Instruction *User;
+  unsigned Op;
+};
+
+class WindowsSecureHotPatching : public ModulePass {
+public:
+  static char ID;
+
+  WindowsSecureHotPatching() : ModulePass(ID) {
+    initializeWindowsSecureHotPatchingPass(*PassRegistry::getPassRegistry());
+  }
+
+  void getAnalysisUsage(AnalysisUsage &AU) const override {
+    AU.setPreservesCFG();
+  }
+
+  bool doInitialization(Module &) override;
+  bool runOnModule(Module &M) override { return false; }
+
+private:
+  bool
+  runOnFunction(Function &F,
+                SmallDenseMap<GlobalVariable *, GlobalVariable *> &RefMapping);
+};
+
+} // end anonymous namespace
+
+char WindowsSecureHotPatching::ID = 0;
+
+INITIALIZE_PASS(WindowsSecureHotPatching, "windows-secure-hot-patch",
+                "Mark functions for Windows hot patch support", false, false)
+ModulePass *llvm::createWindowsSecureHotPatchingPass() {
+  return new WindowsSecureHotPatching();
+}
+
+// Find functions marked with Attribute::MarkedForWindowsHotPatching and modify
+// their code (if necessary) to account for accesses to global variables.
+//
+// This runs during doInitialization() instead of runOnModule() because it 
needs
+// to run before CodeViewDebug::collectGlobalVariableInfo().
+bool WindowsSecureHotPatching::doInitialization(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 (!LLVMMSSecureHotPatchFunctionsFile.empty() ||
+      !LLVMMSSecureHotPatchFunctionsList.empty()) {
+    std::vector<std::string> HotPatchFunctionsList;
+
+    if (!LLVMMSSecureHotPatchFunctionsFile.empty()) {
+      auto BufOrErr = MemoryBuffer::getFile(LLVMMSSecureHotPatchFunctionsFile);
+      if (BufOrErr) {
+        const MemoryBuffer &FileBuffer = **BufOrErr;
+        for (line_iterator I(FileBuffer.getMemBufferRef(), true), E; I != E;
+             ++I)
+          HotPatchFunctionsList.push_back(std::string{*I});
+      } else {
+        M.getContext().diagnose(DiagnosticInfoGeneric{
+            Twine("failed to open hotpatch functions file "
+                  "(--ms-hotpatch-functions-file): ") +
+            LLVMMSSecureHotPatchFunctionsFile + Twine(" : ") +
+            BufOrErr.getError().message()});
+      }
+    }
+
+    if (!LLVMMSSecureHotPatchFunctionsList.empty())
+      for (const auto &FuncName : LLVMMSSecureHotPatchFunctionsList)
+        HotPatchFunctionsList.push_back(FuncName);
+
+    // Build a set for quick lookups. This points into HotPatchFunctionsList, 
so
+    // HotPatchFunctionsList must live longer than HotPatchFunctionsSet.
+    SmallSet<StringRef, 16> HotPatchFunctionsSet;
+    for (const auto &FuncName : HotPatchFunctionsList)
+      HotPatchFunctionsSet.insert(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;
+
+      if (HotPatchFunctionsSet.contains(F.getName()))
+        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;
+}
+
+static bool TypeContainsPointers(Type *ty) {
+  switch (ty->getTypeID()) {
+  case Type::PointerTyID:
+    return true;
+
+  case Type::ArrayTyID:
+    return TypeContainsPointers(ty->getArrayElementType());
+
+  case Type::StructTyID: {
+    unsigned NumElements = ty->getStructNumElements();
+    for (unsigned I = 0; I < NumElements; ++I) {
+      if (TypeContainsPointers(ty->getStructElementType(I))) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  default:
+    return false;
+  }
+}
+
+// Returns true if GV needs redirection through a __ref_* variable.
+static bool globalVariableNeedsRedirect(GlobalVariable *GV) {
+  // If a global variable is explictly marked as allowing access in hot-patched
+  // functions, then do not redirect it.
+  if (GV->hasAttribute(Attribute::AllowDirectAccessInHotPatchFunction)) {
+    return false;
+  }
+
+  // If the global variable is not a constant, then we want to redirect it.
+  if (!GV->isConstant()) {
+    if (GV->getName().starts_with("??_R")) {
+      // This is the name mangling prefix that MSVC uses for RTTI data.
+      // Clang is currently generating RTTI data that is marked non-constant.
+      // We override that and treat it like it is constant.
+      return false;
+    }
+
+    // In general, if a global variable is not a constant, then redirect it.
+    return true;
+  }
+
+  // If the type of GV cannot contain pointers, then it cannot point to
+  // other global variables. In this case, there is no need for redirects.
+  // For example, string literals do not contain pointers.
+  return TypeContainsPointers(GV->getValueType());
+}
+
+/*
+
+Rewriting references to global variables has some complexity.
+
+For ordinary instructions that reference GlobalVariables, we rewrite the
+operand of the instruction to a Load of the __ref_* variable.
+
+For constant expressions, we have to convert the constant expression (and
+transitively all constant expressions in its parent chain) to non-constant
+expressions, i.e. to a sequence of instructions.
+
+Pass 1:
+  * Enumerate all instructions in all basic blocks.
+
+  * If an instruction references a GlobalVariable (and it is not marked
+    as being ignored), then we create (if necessary) the __ref_* variable
+    for the GlobalVariable reference. However, we do not yet modify the
+    Instruction.
+
+  * If an instruction has an operand that is a ConstantExpr and the
+    ConstantExpression tree contains a reference to a GlobalVariable, then
+    we similarly create __ref_*. Similarly, we do not yet modify the 
Instruction
+    or the ConstantExpr tree.
+
+After Pass 1 completes, we will know whether we found any references to
+globals in this pass.  If the function does not use any globals (and most
+functions do not use any globals), then we return immediately.
+
+If a function does reference globals, then we iterate the list of globals
+used by this function and we generate Load instructions for each (unique)
+global.
+
+Next, we do another pass over all instructions:
+
+Pass 2:
+  * Re-visit the instructions that were found in Pass 1.
+
+  * If an instruction operand is a GlobalVariable, then look up the replacement
+    __ref_* global variable and the Value that came from the Load instruction
+    for it.  Replace the operand of the GlobalVariable with the Load Value.
+
+  * If an instruction operand is a ConstantExpr, then recursively examine the
+    operands of all instructions in the ConstantExpr tree.  If an operand is
+    a GlobalVariable, then replace the operand with the result of the load
+    *and* convert the ConstantExpr to a non-constant instruction.  This
+    instruction will need to be inserted into the BB of the instruction whose
+    operand is being modified, ideally immediately before the instruction
+    being modified.
+*/
+
+// Get or create a new global variable that points to the old one and whose
+// name begins with `__ref_`.
+//
+// In hot-patched images, the __ref_* variables point to global variables in
+// the original (unpatched) image. Hot-patched functions in the hot-patch
+// image use these __ref_* variables to access global variables. This ensures
+// that all code (both unpatched and patched) is using the same instances of
+// global variables.
+//
+// The Windows hot-patch infrastructure handles initializing these __ref_*
----------------
dpaoliello wrote:

Should this be "modifying" or "redirecting" instead of "initializing"? As in, 
then point to the GVs in the current image but Windows will change them to 
point to the base image if required.

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