[clang] [llvm] Add support for Windows hot-patching (PR #138972)
Eli Friedman via llvm-commits
llvm-commits at lists.llvm.org
Wed May 7 16:29:36 PDT 2025
================
@@ -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()) {
----------------
efriedma-quic wrote:
The primary problem here is that optimization passes can manipulate internal function symbols. What do you do if you want to hotpatch a symbol, but it's disappeared, or has a different signature?
https://github.com/llvm/llvm-project/pull/138972
More information about the llvm-commits
mailing list