| 1 | //===------ WindowsHotPatch.cpp - Support for Windows hotpatching ---------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // Provides support for the Windows "Secure Hot-Patching" feature. |
| 10 | // |
| 11 | // Windows contains technology, called "Secure Hot-Patching" (SHP), for securely |
| 12 | // applying hot-patches to a running system. Hot-patches may be applied to the |
| 13 | // kernel, kernel-mode components, device drivers, user-mode system services, |
| 14 | // etc. |
| 15 | // |
| 16 | // SHP relies on integration between many tools, including compiler, linker, |
| 17 | // hot-patch generation tools, and the Windows kernel. This file implements that |
| 18 | // part of the workflow needed in compilers / code generators. |
| 19 | // |
| 20 | // SHP is not intended for productivity scenarios such as Edit-and-Continue or |
| 21 | // interactive development. SHP is intended to minimize downtime during |
| 22 | // installation of Windows OS patches. |
| 23 | // |
| 24 | // In order to work with SHP, LLVM must do all of the following: |
| 25 | // |
| 26 | // * On some architectures (X86, AMD64), the function prolog must begin with |
| 27 | // hot-patchable instructions. This is handled by the MSVC `/hotpatch` option |
| 28 | // and the equivalent `-fms-hotpatch` function. This is necessary because we |
| 29 | // generally cannot anticipate which functions will need to be patched in the |
| 30 | // future. This option ensures that a function can be hot-patched in the |
| 31 | // future, but does not actually generate any hot-patch for it. |
| 32 | // |
| 33 | // * For a selected set of functions that are being hot-patched (which are |
| 34 | // identified using command-line options), LLVM must generate the |
| 35 | // `S_HOTPATCHFUNC` CodeView record (symbol). This record indicates that a |
| 36 | // function was compiled with hot-patching enabled. |
| 37 | // |
| 38 | // This implementation uses the `MarkedForWindowsHotPatching` attribute to |
| 39 | // annotate those functions that were marked for hot-patching by command-line |
| 40 | // parameters. The attribute may be specified by a language front-end by |
| 41 | // setting an attribute when a function is created in LLVM IR, or it may be |
| 42 | // set by passing LLVM arguments. |
| 43 | // |
| 44 | // * For those functions that are hot-patched, LLVM must rewrite references to |
| 45 | // global variables so that they are indirected through a `__ref_*` pointer |
| 46 | // variable. For each global variable, that is accessed by a hot-patched |
| 47 | // function, e.g. `FOO`, a `__ref_FOO` global pointer variable is created and |
| 48 | // all references to the original `FOO` are rewritten as dereferences of the |
| 49 | // `__ref_FOO` pointer. |
| 50 | // |
| 51 | // Some globals do not need `__ref_*` indirection. The pointer indirection |
| 52 | // behavior can be disabled for these globals by marking them with the |
| 53 | // `AllowDirectAccessInHotPatchFunction`. |
| 54 | // |
| 55 | // Rewriting references to global variables has some complexity. |
| 56 | // |
| 57 | // For ordinary instructions that reference GlobalVariables, we rewrite the |
| 58 | // operand of the instruction to a Load of the __ref_* variable. |
| 59 | // |
| 60 | // For constant expressions, we have to convert the constant expression (and |
| 61 | // transitively all constant expressions in its parent chain) to non-constant |
| 62 | // expressions, i.e. to a sequence of instructions. |
| 63 | // |
| 64 | // Pass 1: |
| 65 | // * Enumerate all instructions in all basic blocks. |
| 66 | // |
| 67 | // * If an instruction references a GlobalVariable (and it is not marked |
| 68 | // as being ignored), then we create (if necessary) the __ref_* variable |
| 69 | // for the GlobalVariable reference. However, we do not yet modify the |
| 70 | // Instruction. |
| 71 | // |
| 72 | // * If an instruction has an operand that is a ConstantExpr and the |
| 73 | // ConstantExpression tree contains a reference to a GlobalVariable, then |
| 74 | // we similarly create __ref_*. Similarly, we do not yet modify the |
| 75 | // Instruction or the ConstantExpr tree. |
| 76 | // |
| 77 | // After Pass 1 completes, we will know whether we found any references to |
| 78 | // globals in this pass. If the function does not use any globals (and most |
| 79 | // functions do not use any globals), then we return immediately. |
| 80 | // |
| 81 | // If a function does reference globals, then we iterate the list of globals |
| 82 | // used by this function and we generate Load instructions for each (unique) |
| 83 | // global. |
| 84 | // |
| 85 | // Next, we do another pass over all instructions: |
| 86 | // |
| 87 | // Pass 2: |
| 88 | // * Re-visit the instructions that were found in Pass 1. |
| 89 | // |
| 90 | // * If an instruction operand is a GlobalVariable, then look up the |
| 91 | // replacement |
| 92 | // __ref_* global variable and the Value that came from the Load instruction |
| 93 | // for it. Replace the operand of the GlobalVariable with the Load Value. |
| 94 | // |
| 95 | // * If an instruction operand is a ConstantExpr, then recursively examine the |
| 96 | // operands of all instructions in the ConstantExpr tree. If an operand is |
| 97 | // a GlobalVariable, then replace the operand with the result of the load |
| 98 | // *and* convert the ConstantExpr to a non-constant instruction. This |
| 99 | // instruction will need to be inserted into the BB of the instruction whose |
| 100 | // operand is being modified, ideally immediately before the instruction |
| 101 | // being modified. |
| 102 | // |
| 103 | // Limitations |
| 104 | // |
| 105 | // This feature is not intended to work in every situation. There are many |
| 106 | // legitimate code changes (patches) for which it is not possible to generate |
| 107 | // a hot-patch. Developers who are writing hot-patches are expected to |
| 108 | // understand the limitations. |
| 109 | // |
| 110 | // Tools which generate hot-patch metadata may also check that certain |
| 111 | // variables are upheld, and some of these invariants may be global (may require |
| 112 | // whole-program knowledge, not available in any single compiland). However, |
| 113 | // such tools are not required to be perfect; they are also best-effort. |
| 114 | // |
| 115 | // For these reasons, the hot-patching support implemented in this file is |
| 116 | // "best effort". It does not recognize every possible code pattern that could |
| 117 | // be patched, nor does it generate diagnostics for certain code patterns that |
| 118 | // could result in a binary that does not work with hot-patching. For example, |
| 119 | // const GlobalVariables that point to other non-const GlobalVariables are not |
| 120 | // compatible with hot-patching because they cannot use __ref_*-based |
| 121 | // redirection. |
| 122 | // |
| 123 | // References |
| 124 | // |
| 125 | // * "Hotpatching on Windows": |
| 126 | // https://techcommunity.microsoft.com/blog/windowsosplatform/hotpatching-on-windows/2959541 |
| 127 | // |
| 128 | // * "Hotpatch for Windows client now available": |
| 129 | // https://techcommunity.microsoft.com/blog/windows-itpro-blog/hotpatch-for-windows-client-now-available/4399808 |
| 130 | // |
| 131 | // * "Get hotpatching for Windows Server": |
| 132 | // https://www.microsoft.com/en-us/windows-server/blog/2025/04/24/tired-of-all-the-restarts-get-hotpatching-for-windows-server/ |
| 133 | // |
| 134 | //===----------------------------------------------------------------------===// |
| 135 | |
| 136 | #include "llvm/ADT/SmallSet.h" |
| 137 | #include "llvm/CodeGen/Passes.h" |
| 138 | #include "llvm/IR/Attributes.h" |
| 139 | #include "llvm/IR/DIBuilder.h" |
| 140 | #include "llvm/IR/DiagnosticInfo.h" |
| 141 | #include "llvm/IR/Function.h" |
| 142 | #include "llvm/IR/IRBuilder.h" |
| 143 | #include "llvm/IR/InstIterator.h" |
| 144 | #include "llvm/IR/Module.h" |
| 145 | #include "llvm/InitializePasses.h" |
| 146 | #include "llvm/Pass.h" |
| 147 | #include "llvm/Support/CommandLine.h" |
| 148 | #include "llvm/Support/LineIterator.h" |
| 149 | #include "llvm/Support/MemoryBuffer.h" |
| 150 | |
| 151 | using namespace llvm; |
| 152 | |
| 153 | #define DEBUG_TYPE "windows-secure-hot-patch" |
| 154 | |
| 155 | // A file containing list of mangled function names to mark for hot patching. |
| 156 | static cl::opt<std::string> LLVMMSSecureHotPatchFunctionsFile( |
| 157 | "ms-secure-hotpatch-functions-file" , cl::value_desc("filename" ), |
| 158 | cl::desc("A file containing list of mangled function names to mark for " |
| 159 | "Windows Secure Hot-Patching" )); |
| 160 | |
| 161 | // A list of mangled function names to mark for hot patching. |
| 162 | static cl::list<std::string> LLVMMSSecureHotPatchFunctionsList( |
| 163 | "ms-secure-hotpatch-functions-list" , cl::value_desc("list" ), |
| 164 | cl::desc("A list of mangled function names to mark for Windows Secure " |
| 165 | "Hot-Patching" ), |
| 166 | cl::CommaSeparated); |
| 167 | |
| 168 | namespace { |
| 169 | |
| 170 | struct GlobalVariableUse { |
| 171 | // GlobalVariable *GV; |
| 172 | Instruction *User; |
| 173 | unsigned Op; |
| 174 | }; |
| 175 | |
| 176 | class WindowsSecureHotPatching : public ModulePass { |
| 177 | public: |
| 178 | static char ID; |
| 179 | |
| 180 | WindowsSecureHotPatching() : ModulePass(ID) { |
| 181 | initializeWindowsSecureHotPatchingPass(*PassRegistry::getPassRegistry()); |
| 182 | } |
| 183 | |
| 184 | void getAnalysisUsage(AnalysisUsage &AU) const override { |
| 185 | AU.setPreservesCFG(); |
| 186 | } |
| 187 | |
| 188 | bool doInitialization(Module &) override; |
| 189 | bool runOnModule(Module &M) override { return false; } |
| 190 | |
| 191 | private: |
| 192 | bool |
| 193 | runOnFunction(Function &F, |
| 194 | SmallDenseMap<GlobalVariable *, GlobalVariable *> &RefMapping); |
| 195 | }; |
| 196 | |
| 197 | } // end anonymous namespace |
| 198 | |
| 199 | char WindowsSecureHotPatching::ID = 0; |
| 200 | |
| 201 | INITIALIZE_PASS(WindowsSecureHotPatching, "windows-secure-hot-patch" , |
| 202 | "Mark functions for Windows hot patch support" , false, false) |
| 203 | ModulePass *llvm::createWindowsSecureHotPatchingPass() { |
| 204 | return new WindowsSecureHotPatching(); |
| 205 | } |
| 206 | |
| 207 | // Find functions marked with Attribute::MarkedForWindowsHotPatching and modify |
| 208 | // their code (if necessary) to account for accesses to global variables. |
| 209 | // |
| 210 | // This runs during doInitialization() instead of runOnModule() because it needs |
| 211 | // to run before CodeViewDebug::collectGlobalVariableInfo(). |
| 212 | bool WindowsSecureHotPatching::doInitialization(Module &M) { |
| 213 | // The front end may have already marked functions for hot-patching. However, |
| 214 | // we also allow marking functions by passing -ms-hotpatch-functions-file or |
| 215 | // -ms-hotpatch-functions-list directly to LLVM. This allows hot-patching to |
| 216 | // work with languages that have not yet updated their front-ends. |
| 217 | if (!LLVMMSSecureHotPatchFunctionsFile.empty() || |
| 218 | !LLVMMSSecureHotPatchFunctionsList.empty()) { |
| 219 | std::vector<std::string> HotPatchFunctionsList; |
| 220 | |
| 221 | if (!LLVMMSSecureHotPatchFunctionsFile.empty()) { |
| 222 | auto BufOrErr = MemoryBuffer::getFile(Filename: LLVMMSSecureHotPatchFunctionsFile); |
| 223 | if (BufOrErr) { |
| 224 | const MemoryBuffer &FileBuffer = **BufOrErr; |
| 225 | for (line_iterator I(FileBuffer.getMemBufferRef(), true), E; I != E; |
| 226 | ++I) |
| 227 | HotPatchFunctionsList.push_back(x: std::string{*I}); |
| 228 | } else { |
| 229 | M.getContext().diagnose(DI: DiagnosticInfoGeneric{ |
| 230 | Twine("failed to open hotpatch functions file " |
| 231 | "(--ms-hotpatch-functions-file): " ) + |
| 232 | LLVMMSSecureHotPatchFunctionsFile + Twine(" : " ) + |
| 233 | BufOrErr.getError().message()}); |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | if (!LLVMMSSecureHotPatchFunctionsList.empty()) |
| 238 | for (const auto &FuncName : LLVMMSSecureHotPatchFunctionsList) |
| 239 | HotPatchFunctionsList.push_back(x: FuncName); |
| 240 | |
| 241 | // Build a set for quick lookups. This points into HotPatchFunctionsList, so |
| 242 | // HotPatchFunctionsList must live longer than HotPatchFunctionsSet. |
| 243 | SmallSet<StringRef, 16> HotPatchFunctionsSet; |
| 244 | for (const auto &FuncName : HotPatchFunctionsList) |
| 245 | HotPatchFunctionsSet.insert(V: StringRef{FuncName}); |
| 246 | |
| 247 | // Iterate through all of the functions and check whether they need to be |
| 248 | // marked for hotpatching using the list provided directly to LLVM. |
| 249 | for (auto &F : M.functions()) { |
| 250 | // Ignore declarations that are not definitions. |
| 251 | if (F.isDeclarationForLinker()) |
| 252 | continue; |
| 253 | |
| 254 | if (HotPatchFunctionsSet.contains(V: F.getName())) |
| 255 | F.addFnAttr(Kind: "marked_for_windows_hot_patching" ); |
| 256 | } |
| 257 | } |
| 258 | |
| 259 | SmallDenseMap<GlobalVariable *, GlobalVariable *> RefMapping; |
| 260 | bool MadeChanges = false; |
| 261 | for (auto &F : M.functions()) { |
| 262 | if (F.hasFnAttribute(Kind: "marked_for_windows_hot_patching" )) { |
| 263 | if (runOnFunction(F, RefMapping)) |
| 264 | MadeChanges = true; |
| 265 | } |
| 266 | } |
| 267 | return MadeChanges; |
| 268 | } |
| 269 | |
| 270 | static bool TypeContainsPointers(Type *ty) { |
| 271 | switch (ty->getTypeID()) { |
| 272 | case Type::PointerTyID: |
| 273 | return true; |
| 274 | |
| 275 | case Type::ArrayTyID: |
| 276 | return TypeContainsPointers(ty: ty->getArrayElementType()); |
| 277 | |
| 278 | case Type::StructTyID: { |
| 279 | unsigned NumElements = ty->getStructNumElements(); |
| 280 | for (unsigned I = 0; I < NumElements; ++I) { |
| 281 | if (TypeContainsPointers(ty: ty->getStructElementType(N: I))) { |
| 282 | return true; |
| 283 | } |
| 284 | } |
| 285 | return false; |
| 286 | } |
| 287 | |
| 288 | default: |
| 289 | return false; |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | // Returns true if GV needs redirection through a __ref_* variable. |
| 294 | static bool globalVariableNeedsRedirect(GlobalVariable *GV) { |
| 295 | // If a global variable is explictly marked as allowing access in hot-patched |
| 296 | // functions, then do not redirect it. |
| 297 | if (GV->hasAttribute(Kind: "allow_direct_access_in_hot_patch_function" )) |
| 298 | return false; |
| 299 | |
| 300 | // If the global variable is not a constant, then we want to redirect it. |
| 301 | if (!GV->isConstant()) { |
| 302 | if (GV->getName().starts_with(Prefix: "??_R" )) { |
| 303 | // This is the name mangling prefix that MSVC uses for RTTI data. |
| 304 | // Clang is currently generating RTTI data that is marked non-constant. |
| 305 | // We override that and treat it like it is constant. |
| 306 | return false; |
| 307 | } |
| 308 | |
| 309 | // In general, if a global variable is not a constant, then redirect it. |
| 310 | return true; |
| 311 | } |
| 312 | |
| 313 | // If the type of GV cannot contain pointers, then it cannot point to |
| 314 | // other global variables. In this case, there is no need for redirects. |
| 315 | // For example, string literals do not contain pointers. |
| 316 | return TypeContainsPointers(ty: GV->getValueType()); |
| 317 | } |
| 318 | |
| 319 | // Get or create a new global variable that points to the old one and whose |
| 320 | // name begins with `__ref_`. |
| 321 | // |
| 322 | // In hot-patched images, the __ref_* variables point to global variables in |
| 323 | // the original (unpatched) image. Hot-patched functions in the hot-patch |
| 324 | // image use these __ref_* variables to access global variables. This ensures |
| 325 | // that all code (both unpatched and patched) is using the same instances of |
| 326 | // global variables. |
| 327 | // |
| 328 | // The Windows hot-patch infrastructure handles modifying these __ref_* |
| 329 | // variables. By default, they are initialized with pointers to the equivalent |
| 330 | // global variables, so when a hot-patch module is loaded *as* a base image |
| 331 | // (such as after a system reboot), hot-patch functions will access the |
| 332 | // instances of global variables that are compiled into the hot-patch image. |
| 333 | // This is the desired outcome, since in this situation (normal boot) the |
| 334 | // hot-patch image *is* the base image. |
| 335 | // |
| 336 | // When we create the GlobalVariable for the __ref_* variable, we must create |
| 337 | // it as a *non-constant* global variable. The __ref_* pointers will not change |
| 338 | // during the runtime of the program, so it is tempting to think that they |
| 339 | // should be constant. However, they still need to be updateable by the |
| 340 | // hot-patching infrastructure. Also, if the GlobalVariable is created as a |
| 341 | // constant, then the LLVM optimizer will assume that it can dereference the |
| 342 | // definition of the __ref_* variable at compile time, which defeats the |
| 343 | // purpose of the indirection (pointer). |
| 344 | // |
| 345 | // The RefMapping table spans the entire module, not just a single function. |
| 346 | static GlobalVariable *getOrCreateRefVariable( |
| 347 | Function &F, SmallDenseMap<GlobalVariable *, GlobalVariable *> &RefMapping, |
| 348 | GlobalVariable *GV) { |
| 349 | GlobalVariable *&ReplaceWithRefGV = RefMapping.try_emplace(Key: GV).first->second; |
| 350 | if (ReplaceWithRefGV != nullptr) { |
| 351 | // We have already created a __ref_* pointer for this GlobalVariable. |
| 352 | return ReplaceWithRefGV; |
| 353 | } |
| 354 | |
| 355 | Module *M = F.getParent(); |
| 356 | |
| 357 | const DISubprogram *Subprogram = F.getSubprogram(); |
| 358 | DICompileUnit *Unit = Subprogram != nullptr ? Subprogram->getUnit() : nullptr; |
| 359 | DIFile *File = Subprogram != nullptr ? Subprogram->getFile() : nullptr; |
| 360 | DIBuilder DebugInfo{*F.getParent(), true, Unit}; |
| 361 | |
| 362 | auto PtrTy = PointerType::get(C&: M->getContext(), AddressSpace: 0); |
| 363 | |
| 364 | Constant *AddrOfOldGV = |
| 365 | ConstantExpr::getGetElementPtr(Ty: PtrTy, C: GV, IdxList: ArrayRef<Value *>{}); |
| 366 | |
| 367 | GlobalVariable *RefGV = |
| 368 | new GlobalVariable(*M, PtrTy, false, GlobalValue::LinkOnceAnyLinkage, |
| 369 | AddrOfOldGV, Twine("__ref_" ).concat(Suffix: GV->getName()), |
| 370 | nullptr, GlobalVariable::NotThreadLocal); |
| 371 | |
| 372 | // Create debug info for the replacement global variable. |
| 373 | DataLayout Layout = M->getDataLayout(); |
| 374 | DIType *DebugType = DebugInfo.createPointerType( |
| 375 | PointeeTy: nullptr, SizeInBits: Layout.getTypeSizeInBits(Ty: GV->getValueType())); |
| 376 | DIGlobalVariableExpression *GVE = DebugInfo.createGlobalVariableExpression( |
| 377 | Context: Unit, Name: RefGV->getName(), LinkageName: StringRef{}, File, |
| 378 | /*LineNo*/ 0, Ty: DebugType, |
| 379 | /*IsLocalToUnit*/ false); |
| 380 | RefGV->addDebugInfo(GV: GVE); |
| 381 | |
| 382 | // Store the __ref_* in RefMapping so that future calls use the same RefGV. |
| 383 | ReplaceWithRefGV = RefGV; |
| 384 | |
| 385 | return RefGV; |
| 386 | } |
| 387 | |
| 388 | // Given a ConstantExpr, this searches for GlobalVariable references within |
| 389 | // the expression tree. If found, it will generate instructions and will |
| 390 | // return a non-null Value* that points to the new root instruction. |
| 391 | // |
| 392 | // If C does not contain any GlobalVariable references, this returns nullptr. |
| 393 | // |
| 394 | // If this function creates new instructions, then it will insert them |
| 395 | // before InsertionPoint. |
| 396 | static Value *rewriteGlobalVariablesInConstant( |
| 397 | Constant *C, SmallDenseMap<GlobalVariable *, Value *> &GVLoadMap, |
| 398 | IRBuilder<> &IRBuilderAtEntry) { |
| 399 | if (C->getValueID() == Value::GlobalVariableVal) { |
| 400 | GlobalVariable *GV = cast<GlobalVariable>(Val: C); |
| 401 | if (globalVariableNeedsRedirect(GV)) { |
| 402 | return GVLoadMap.at(Val: GV); |
| 403 | } else { |
| 404 | return nullptr; |
| 405 | } |
| 406 | } |
| 407 | |
| 408 | // Scan the operands of this expression. |
| 409 | |
| 410 | SmallVector<Value *, 8> ReplacedValues; |
| 411 | bool ReplacedAnyOperands = false; |
| 412 | |
| 413 | unsigned NumOperands = C->getNumOperands(); |
| 414 | for (unsigned OpIndex = 0; OpIndex < NumOperands; ++OpIndex) { |
| 415 | Value *OldValue = C->getOperand(i: OpIndex); |
| 416 | Value *ReplacedValue = nullptr; |
| 417 | if (Constant *OldConstant = dyn_cast<Constant>(Val: OldValue)) { |
| 418 | ReplacedValue = rewriteGlobalVariablesInConstant(C: OldConstant, GVLoadMap, |
| 419 | IRBuilderAtEntry); |
| 420 | } |
| 421 | // Do not use short-circuiting, here. We need to traverse the whole tree. |
| 422 | ReplacedAnyOperands |= ReplacedValue != nullptr; |
| 423 | ReplacedValues.push_back(Elt: ReplacedValue); |
| 424 | } |
| 425 | |
| 426 | // If none of our operands were replaced, then don't rewrite this expression. |
| 427 | if (!ReplacedAnyOperands) { |
| 428 | return nullptr; |
| 429 | } |
| 430 | |
| 431 | // We need to rewrite this expression. Convert this constant expression |
| 432 | // to an instruction, then replace any operands as needed. |
| 433 | Instruction *NewInst = cast<ConstantExpr>(Val: C)->getAsInstruction(); |
| 434 | for (unsigned OpIndex = 0; OpIndex < NumOperands; ++OpIndex) { |
| 435 | Value *ReplacedValue = ReplacedValues[OpIndex]; |
| 436 | if (ReplacedValue != nullptr) { |
| 437 | NewInst->setOperand(i: OpIndex, Val: ReplacedValue); |
| 438 | } |
| 439 | } |
| 440 | |
| 441 | // Insert the new instruction before the reference instruction. |
| 442 | IRBuilderAtEntry.Insert(I: NewInst); |
| 443 | |
| 444 | return NewInst; |
| 445 | } |
| 446 | |
| 447 | static bool searchConstantExprForGlobalVariables( |
| 448 | Value *V, SmallDenseMap<GlobalVariable *, Value *> &GVLoadMap, |
| 449 | SmallVector<GlobalVariableUse> &GVUses) { |
| 450 | |
| 451 | SmallVector<Value *, 8> ReplacedOperands; |
| 452 | |
| 453 | if (GlobalVariable *GV = dyn_cast<GlobalVariable>(Val: V)) { |
| 454 | if (globalVariableNeedsRedirect(GV)) { |
| 455 | GVLoadMap[GV] = nullptr; |
| 456 | return true; |
| 457 | } else { |
| 458 | return false; |
| 459 | } |
| 460 | } |
| 461 | |
| 462 | if (User *U = dyn_cast<User>(Val: V)) { |
| 463 | unsigned NumOperands = U->getNumOperands(); |
| 464 | bool FoundAny = false; |
| 465 | for (unsigned OpIndex = 0; OpIndex < NumOperands; ++OpIndex) { |
| 466 | Value *Op = U->getOperand(i: OpIndex); |
| 467 | // Do not use short-circuiting, here. We need to traverse the whole tree. |
| 468 | FoundAny |= searchConstantExprForGlobalVariables(V: Op, GVLoadMap, GVUses); |
| 469 | } |
| 470 | return FoundAny; |
| 471 | } else { |
| 472 | return false; |
| 473 | } |
| 474 | } |
| 475 | |
| 476 | // Processes a function that is marked for hot-patching. |
| 477 | // |
| 478 | // If a function is marked for hot-patching, we generate an S_HOTPATCHFUNC |
| 479 | // CodeView debug symbol. Tools that generate hot-patches look for |
| 480 | // S_HOTPATCHFUNC in final PDBs so that they can find functions that have been |
| 481 | // hot-patched and so that they can distinguish hot-patched functions from |
| 482 | // non-hot-patched functions. |
| 483 | // |
| 484 | // Also, in functions that are hot-patched, we must indirect all access to |
| 485 | // (mutable) global variables through a pointer. This pointer may point into the |
| 486 | // unpatched ("base") binary or may point into the patched image, depending on |
| 487 | // whether a hot-patch was loaded as a patch or as a base image. These |
| 488 | // indirections go through a new global variable, named `__ref_<Foo>` where |
| 489 | // `<Foo>` is the original symbol name of the global variable. |
| 490 | // |
| 491 | // This function handles rewriting accesses to global variables, but the |
| 492 | // generation of S_HOTPATCHFUNC occurs in |
| 493 | // CodeViewDebug::emitHotPatchInformation(). |
| 494 | // |
| 495 | // Returns true if any global variable references were found and rewritten. |
| 496 | bool WindowsSecureHotPatching::runOnFunction( |
| 497 | Function &F, |
| 498 | SmallDenseMap<GlobalVariable *, GlobalVariable *> &RefMapping) { |
| 499 | // Scan the function for references to global variables. If we find such a |
| 500 | // reference, create (if necessary) the __ref_* variable, then add an entry |
| 501 | // to the GVUses table. |
| 502 | // |
| 503 | // We ignore references to global variables if the variable is marked with |
| 504 | // AllowDirectAccessInHotPatchFunction. |
| 505 | |
| 506 | SmallDenseMap<GlobalVariable *, Value *> GVLoadMap; |
| 507 | SmallVector<GlobalVariableUse> GVUses; |
| 508 | |
| 509 | for (auto &I : instructions(F)) { |
| 510 | unsigned NumOperands = I.getNumOperands(); |
| 511 | for (unsigned OpIndex = 0; OpIndex < NumOperands; ++OpIndex) { |
| 512 | Value *V = I.getOperand(i: OpIndex); |
| 513 | |
| 514 | bool FoundAnyGVUses = false; |
| 515 | |
| 516 | switch (V->getValueID()) { |
| 517 | case Value::GlobalVariableVal: { |
| 518 | // Discover all uses of GlobalVariable, these will need to be replaced. |
| 519 | GlobalVariable *GV = cast<GlobalVariable>(Val: V); |
| 520 | if (globalVariableNeedsRedirect(GV)) { |
| 521 | GVLoadMap.insert(KV: std::make_pair(x&: GV, y: nullptr)); |
| 522 | FoundAnyGVUses = true; |
| 523 | } |
| 524 | break; |
| 525 | } |
| 526 | |
| 527 | case Value::ConstantExprVal: { |
| 528 | ConstantExpr *CE = cast<ConstantExpr>(Val: V); |
| 529 | if (searchConstantExprForGlobalVariables(V: CE, GVLoadMap, GVUses)) { |
| 530 | FoundAnyGVUses = true; |
| 531 | } |
| 532 | break; |
| 533 | } |
| 534 | |
| 535 | default: |
| 536 | break; |
| 537 | } |
| 538 | |
| 539 | if (FoundAnyGVUses) { |
| 540 | GVUses.push_back(Elt: GlobalVariableUse{.User: &I, .Op: OpIndex}); |
| 541 | } |
| 542 | } |
| 543 | } |
| 544 | |
| 545 | // If this function did not reference any global variables then we have no |
| 546 | // work to do. Most functions do not access global variables. |
| 547 | if (GVUses.empty()) { |
| 548 | return false; |
| 549 | } |
| 550 | |
| 551 | // We know that there is at least one instruction that needs to be rewritten. |
| 552 | // Generate a Load instruction for each unique GlobalVariable used by this |
| 553 | // function. The Load instructions are inserted at the beginning of the |
| 554 | // entry block. Since entry blocks cannot contain PHI instructions, there is |
| 555 | // no need to skip PHI instructions. |
| 556 | |
| 557 | // We use a single IRBuilder for inserting Load instructions as well as the |
| 558 | // constants that we convert to instructions. Because constants do not |
| 559 | // depend on any dynamic values (they're constant, after all!), it is safe |
| 560 | // to move them to the start of entry BB. |
| 561 | |
| 562 | auto &EntryBlock = F.getEntryBlock(); |
| 563 | IRBuilder<> IRBuilderAtEntry(&EntryBlock, EntryBlock.begin()); |
| 564 | |
| 565 | for (auto &[GV, LoadValue] : GVLoadMap) { |
| 566 | assert(LoadValue == nullptr); |
| 567 | GlobalVariable *RefGV = getOrCreateRefVariable(F, RefMapping, GV); |
| 568 | LoadValue = IRBuilderAtEntry.CreateLoad(Ty: RefGV->getValueType(), Ptr: RefGV); |
| 569 | } |
| 570 | |
| 571 | const DISubprogram *Subprogram = F.getSubprogram(); |
| 572 | DICompileUnit *Unit = Subprogram != nullptr ? Subprogram->getUnit() : nullptr; |
| 573 | DIBuilder DebugInfo{*F.getParent(), true, Unit}; |
| 574 | |
| 575 | // Go back to the instructions and rewrite their uses of GlobalVariable. |
| 576 | // Because a ConstantExpr can be a tree, it may reference more than one |
| 577 | // GlobalVariable. |
| 578 | |
| 579 | for (auto &GVUse : GVUses) { |
| 580 | Value *OldOperandValue = GVUse.User->getOperand(i: GVUse.Op); |
| 581 | Value *NewOperandValue; |
| 582 | |
| 583 | switch (OldOperandValue->getValueID()) { |
| 584 | case Value::GlobalVariableVal: { |
| 585 | // This is easy. Look up the replacement value and store the operand. |
| 586 | Value *OperandValue = GVUse.User->getOperand(i: GVUse.Op); |
| 587 | GlobalVariable *GV = cast<GlobalVariable>(Val: OperandValue); |
| 588 | NewOperandValue = GVLoadMap.at(Val: GV); |
| 589 | break; |
| 590 | } |
| 591 | |
| 592 | case Value::ConstantExprVal: { |
| 593 | // Walk the recursive tree of the ConstantExpr. If we find a |
| 594 | // GlobalVariable then replace it with the loaded value and rewrite |
| 595 | // the ConstantExpr to an Instruction and insert it before the |
| 596 | // current instruction. |
| 597 | Value *OperandValue = GVUse.User->getOperand(i: GVUse.Op); |
| 598 | ConstantExpr *CE = cast<ConstantExpr>(Val: OperandValue); |
| 599 | NewOperandValue = |
| 600 | rewriteGlobalVariablesInConstant(C: CE, GVLoadMap, IRBuilderAtEntry); |
| 601 | assert(NewOperandValue != nullptr); |
| 602 | break; |
| 603 | } |
| 604 | |
| 605 | default: |
| 606 | // We should only ever get here because a GVUse was created in the first |
| 607 | // pass, and this only happens for GlobalVariableVal and ConstantExprVal. |
| 608 | llvm_unreachable_internal( |
| 609 | msg: "unexpected Value in second pass of hot-patching" ); |
| 610 | break; |
| 611 | } |
| 612 | |
| 613 | GVUse.User->setOperand(i: GVUse.Op, Val: NewOperandValue); |
| 614 | } |
| 615 | |
| 616 | return true; |
| 617 | } |
| 618 | |