brintos

brintos / llvm-project-archived public Read only

0
0
Text · 25.0 KiB · dab1416 Raw
629 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// Provides support for the Windows "Secure Hot-Patching" feature.10//11// Windows contains technology, called "Secure Hot-Patching" (SHP), for securely12// applying hot-patches to a running system. Hot-patches may be applied to the13// 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 that18// part of the workflow needed in compilers / code generators.19//20// SHP is not intended for productivity scenarios such as Edit-and-Continue or21// interactive development. SHP is intended to minimize downtime during22// 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 with27//   hot-patchable instructions. This is handled by the MSVC `/hotpatch` option28//   and the equivalent `-fms-hotpatch` function. This is necessary because we29//   generally cannot anticipate which functions will need to be patched in the30//   future. This option ensures that a function can be hot-patched in the31//   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 are34//   identified using command-line options), LLVM must generate the35//   `S_HOTPATCHFUNC` CodeView record (symbol). This record indicates that a36//   function was compiled with hot-patching enabled.37//38//   This implementation uses the `MarkedForWindowsHotPatching` attribute to39//   annotate those functions that were marked for hot-patching by command-line40//   parameters. The attribute may be specified by a language front-end by41//   setting an attribute when a function is created in LLVM IR, or it may be42//   set by passing LLVM arguments.43//44// * For those functions that are hot-patched, LLVM must rewrite references to45//   global variables so that they are indirected through a `__ref_*` pointer46//   variable.  For each global variable, that is accessed by a hot-patched47//   function, e.g. `FOO`, a `__ref_FOO` global pointer variable is created and48//   all references to the original `FOO` are rewritten as dereferences of the49//   `__ref_FOO` pointer.50//51//   Some globals do not need `__ref_*` indirection. The pointer indirection52//   behavior can be disabled for these globals by marking them with the53//   `AllowDirectAccessInHotPatchFunction`.54//55// Rewriting references to global variables has some complexity.56//57// For ordinary instructions that reference GlobalVariables, we rewrite the58// operand of the instruction to a Load of the __ref_* variable.59//60// For constant expressions, we have to convert the constant expression (and61// transitively all constant expressions in its parent chain) to non-constant62// 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 marked68//     as being ignored), then we create (if necessary) the __ref_* variable69//     for the GlobalVariable reference. However, we do not yet modify the70//     Instruction.71//72//   * If an instruction has an operand that is a ConstantExpr and the73//     ConstantExpression tree contains a reference to a GlobalVariable, then74//     we similarly create __ref_*. Similarly, we do not yet modify the75//     Instruction or the ConstantExpr tree.76//77// After Pass 1 completes, we will know whether we found any references to78// globals in this pass.  If the function does not use any globals (and most79// functions do not use any globals), then we return immediately.80//81// If a function does reference globals, then we iterate the list of globals82// 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 the91//   replacement92//     __ref_* global variable and the Value that came from the Load instruction93//     for it.  Replace the operand of the GlobalVariable with the Load Value.94//95//   * If an instruction operand is a ConstantExpr, then recursively examine the96//     operands of all instructions in the ConstantExpr tree.  If an operand is97//     a GlobalVariable, then replace the operand with the result of the load98//     *and* convert the ConstantExpr to a non-constant instruction.  This99//     instruction will need to be inserted into the BB of the instruction whose100//     operand is being modified, ideally immediately before the instruction101//     being modified.102//103// Limitations104//105// This feature is not intended to work in every situation. There are many106// legitimate code changes (patches) for which it is not possible to generate107// a hot-patch. Developers who are writing hot-patches are expected to108// understand the limitations.109//110// Tools which generate hot-patch metadata may also check that certain111// variables are upheld, and some of these invariants may be global (may require112// 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 is116// "best effort". It does not recognize every possible code pattern that could117// be patched, nor does it generate diagnostics for certain code patterns that118// 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 not120// compatible with hot-patching because they cannot use __ref_*-based121// redirection.122//123// References124//125// * "Hotpatching on Windows":126//   https://techcommunity.microsoft.com/blog/windowsosplatform/hotpatching-on-windows/2959541127//128// * "Hotpatch for Windows client now available":129//   https://techcommunity.microsoft.com/blog/windows-itpro-blog/hotpatch-for-windows-client-now-available/4399808130//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 151using 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.156static 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.162static 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 168namespace {169 170struct GlobalVariableUse {171  // GlobalVariable *GV;172  Instruction *User;173  unsigned Op;174};175 176class WindowsSecureHotPatching : public ModulePass {177public: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 191private:192  bool193  runOnFunction(Function &F,194                SmallDenseMap<GlobalVariable *, GlobalVariable *> &RefMapping);195};196 197} // end anonymous namespace198 199char WindowsSecureHotPatching::ID = 0;200 201INITIALIZE_PASS(WindowsSecureHotPatching, "windows-secure-hot-patch",202                "Mark functions for Windows hot patch support", false, false)203ModulePass *llvm::createWindowsSecureHotPatchingPass() {204  return new WindowsSecureHotPatching();205}206 207// Find functions marked with Attribute::MarkedForWindowsHotPatching and modify208// their code (if necessary) to account for accesses to global variables.209//210// This runs during doInitialization() instead of runOnModule() because it needs211// to run before CodeViewDebug::collectGlobalVariableInfo().212bool 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 or215  // -ms-hotpatch-functions-list directly to LLVM. This allows hot-patching to216  // 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(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(std::string{*I});228      } else {229        M.getContext().diagnose(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(FuncName);240 241    // Build a set for quick lookups. This points into HotPatchFunctionsList, so242    // HotPatchFunctionsList must live longer than HotPatchFunctionsSet.243    SmallSet<StringRef, 16> HotPatchFunctionsSet;244    for (const auto &FuncName : HotPatchFunctionsList)245      HotPatchFunctionsSet.insert(StringRef{FuncName});246 247    // Iterate through all of the functions and check whether they need to be248    // 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(F.getName()))255        F.addFnAttr("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("marked_for_windows_hot_patching")) {263      if (runOnFunction(F, RefMapping))264        MadeChanges = true;265    }266  }267  return MadeChanges;268}269 270static bool TypeContainsPointers(Type *ty) {271  switch (ty->getTypeID()) {272  case Type::PointerTyID:273    return true;274 275  case Type::ArrayTyID:276    return TypeContainsPointers(ty->getArrayElementType());277 278  case Type::StructTyID: {279    unsigned NumElements = ty->getStructNumElements();280    for (unsigned I = 0; I < NumElements; ++I) {281      if (TypeContainsPointers(ty->getStructElementType(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.294static bool globalVariableNeedsRedirect(GlobalVariable *GV) {295  // If a global variable is explictly marked as allowing access in hot-patched296  // functions, then do not redirect it.297  if (GV->hasAttribute("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("??_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 to314  // other global variables. In this case, there is no need for redirects.315  // For example, string literals do not contain pointers.316  return TypeContainsPointers(GV->getValueType());317}318 319// Get or create a new global variable that points to the old one and whose320// name begins with `__ref_`.321//322// In hot-patched images, the __ref_* variables point to global variables in323// the original (unpatched) image. Hot-patched functions in the hot-patch324// image use these __ref_* variables to access global variables. This ensures325// that all code (both unpatched and patched) is using the same instances of326// global variables.327//328// The Windows hot-patch infrastructure handles modifying these __ref_*329// variables. By default, they are initialized with pointers to the equivalent330// global variables, so when a hot-patch module is loaded *as* a base image331// (such as after a system reboot), hot-patch functions will access the332// instances of global variables that are compiled into the hot-patch image.333// This is the desired outcome, since in this situation (normal boot) the334// hot-patch image *is* the base image.335//336// When we create the GlobalVariable for the __ref_* variable, we must create337// it as a *non-constant* global variable. The __ref_* pointers will not change338// during the runtime of the program, so it is tempting to think that they339// should be constant. However, they still need to be updateable by the340// hot-patching infrastructure. Also, if the GlobalVariable is created as a341// constant, then the LLVM optimizer will assume that it can dereference the342// definition of the __ref_* variable at compile time, which defeats the343// purpose of the indirection (pointer).344//345// The RefMapping table spans the entire module, not just a single function.346static GlobalVariable *getOrCreateRefVariable(347    Function &F, SmallDenseMap<GlobalVariable *, GlobalVariable *> &RefMapping,348    GlobalVariable *GV) {349  GlobalVariable *&ReplaceWithRefGV = RefMapping.try_emplace(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(M->getContext(), 0);363 364  Constant *AddrOfOldGV =365      ConstantExpr::getGetElementPtr(PtrTy, GV, ArrayRef<Value *>{});366 367  GlobalVariable *RefGV =368      new GlobalVariable(*M, PtrTy, false, GlobalValue::LinkOnceAnyLinkage,369                         AddrOfOldGV, Twine("__ref_").concat(GV->getName()),370                         nullptr, GlobalVariable::NotThreadLocal);371 372  // RefGV is created with isConstant = false, but we want to place RefGV into373  // .rdata, not .data.  It is important that the GlobalVariable be mutable374  // from the compiler's point of view, so that the optimizer does not remove375  // the global variable entirely and replace all references to it with its376  // initial value.377  //378  // When the Windows hot-patch loader applies a hot-patch, it maps the379  // pages of .rdata as read/write so that it can set each __ref_* variable380  // to point to the original variable in the base image. Afterward, pages in381  // .rdata are remapped as read-only. This protects the __ref_* variables from382  // being overwritten during execution.383  RefGV->setSection(".rdata");384 385  // Create debug info for the replacement global variable.386  DataLayout Layout = M->getDataLayout();387  DIType *DebugType = DebugInfo.createPointerType(388      nullptr, Layout.getTypeSizeInBits(GV->getValueType()));389  DIGlobalVariableExpression *GVE = DebugInfo.createGlobalVariableExpression(390      Unit, RefGV->getName(), StringRef{}, File,391      /*LineNo*/ 0, DebugType,392      /*IsLocalToUnit*/ false);393  RefGV->addDebugInfo(GVE);394 395  // Store the __ref_* in RefMapping so that future calls use the same RefGV.396  ReplaceWithRefGV = RefGV;397 398  return RefGV;399}400 401// Given a ConstantExpr, this searches for GlobalVariable references within402// the expression tree.  If found, it will generate instructions and will403// return a non-null Value* that points to the new root instruction.404//405// If C does not contain any GlobalVariable references, this returns nullptr.406//407// If this function creates new instructions, then it will insert them408// before InsertionPoint.409static Value *rewriteGlobalVariablesInConstant(410    Constant *C, SmallDenseMap<GlobalVariable *, Value *> &GVLoadMap,411    IRBuilder<> &IRBuilderAtEntry) {412  if (C->getValueID() == Value::GlobalVariableVal) {413    GlobalVariable *GV = cast<GlobalVariable>(C);414    if (globalVariableNeedsRedirect(GV)) {415      return GVLoadMap.at(GV);416    } else {417      return nullptr;418    }419  }420 421  // Scan the operands of this expression.422 423  SmallVector<Value *, 8> ReplacedValues;424  bool ReplacedAnyOperands = false;425 426  unsigned NumOperands = C->getNumOperands();427  for (unsigned OpIndex = 0; OpIndex < NumOperands; ++OpIndex) {428    Value *OldValue = C->getOperand(OpIndex);429    Value *ReplacedValue = nullptr;430    if (Constant *OldConstant = dyn_cast<Constant>(OldValue)) {431      ReplacedValue = rewriteGlobalVariablesInConstant(OldConstant, GVLoadMap,432                                                       IRBuilderAtEntry);433    }434    // Do not use short-circuiting, here. We need to traverse the whole tree.435    ReplacedAnyOperands |= ReplacedValue != nullptr;436    ReplacedValues.push_back(ReplacedValue);437  }438 439  // If none of our operands were replaced, then don't rewrite this expression.440  if (!ReplacedAnyOperands) {441    return nullptr;442  }443 444  // We need to rewrite this expression. Convert this constant expression445  // to an instruction, then replace any operands as needed.446  Instruction *NewInst = cast<ConstantExpr>(C)->getAsInstruction();447  for (unsigned OpIndex = 0; OpIndex < NumOperands; ++OpIndex) {448    Value *ReplacedValue = ReplacedValues[OpIndex];449    if (ReplacedValue != nullptr) {450      NewInst->setOperand(OpIndex, ReplacedValue);451    }452  }453 454  // Insert the new instruction before the reference instruction.455  IRBuilderAtEntry.Insert(NewInst);456 457  return NewInst;458}459 460static bool searchConstantExprForGlobalVariables(461    Value *V, SmallDenseMap<GlobalVariable *, Value *> &GVLoadMap,462    SmallVector<GlobalVariableUse> &GVUses) {463 464  if (GlobalVariable *GV = dyn_cast<GlobalVariable>(V)) {465    if (globalVariableNeedsRedirect(GV)) {466      GVLoadMap[GV] = nullptr;467      return true;468    } else {469      return false;470    }471  }472 473  if (User *U = dyn_cast<User>(V)) {474    unsigned NumOperands = U->getNumOperands();475    bool FoundAny = false;476    for (unsigned OpIndex = 0; OpIndex < NumOperands; ++OpIndex) {477      Value *Op = U->getOperand(OpIndex);478      // Do not use short-circuiting, here. We need to traverse the whole tree.479      FoundAny |= searchConstantExprForGlobalVariables(Op, GVLoadMap, GVUses);480    }481    return FoundAny;482  } else {483    return false;484  }485}486 487// Processes a function that is marked for hot-patching.488//489// If a function is marked for hot-patching, we generate an S_HOTPATCHFUNC490// CodeView debug symbol. Tools that generate hot-patches look for491// S_HOTPATCHFUNC in final PDBs so that they can find functions that have been492// hot-patched and so that they can distinguish hot-patched functions from493// non-hot-patched functions.494//495// Also, in functions that are hot-patched, we must indirect all access to496// (mutable) global variables through a pointer. This pointer may point into the497// unpatched ("base") binary or may point into the patched image, depending on498// whether a hot-patch was loaded as a patch or as a base image.  These499// indirections go through a new global variable, named `__ref_<Foo>` where500// `<Foo>` is the original symbol name of the global variable.501//502// This function handles rewriting accesses to global variables, but the503// generation of S_HOTPATCHFUNC occurs in504// CodeViewDebug::emitHotPatchInformation().505//506// Returns true if any global variable references were found and rewritten.507bool WindowsSecureHotPatching::runOnFunction(508    Function &F,509    SmallDenseMap<GlobalVariable *, GlobalVariable *> &RefMapping) {510  // Scan the function for references to global variables. If we find such a511  // reference, create (if necessary) the __ref_* variable, then add an entry512  // to the GVUses table.513  //514  // We ignore references to global variables if the variable is marked with515  // AllowDirectAccessInHotPatchFunction.516 517  SmallDenseMap<GlobalVariable *, Value *> GVLoadMap;518  SmallVector<GlobalVariableUse> GVUses;519 520  for (auto &I : instructions(F)) {521    unsigned NumOperands = I.getNumOperands();522    for (unsigned OpIndex = 0; OpIndex < NumOperands; ++OpIndex) {523      Value *V = I.getOperand(OpIndex);524 525      bool FoundAnyGVUses = false;526 527      switch (V->getValueID()) {528      case Value::GlobalVariableVal: {529        // Discover all uses of GlobalVariable, these will need to be replaced.530        GlobalVariable *GV = cast<GlobalVariable>(V);531        if (globalVariableNeedsRedirect(GV)) {532          GVLoadMap.insert(std::make_pair(GV, nullptr));533          FoundAnyGVUses = true;534        }535        break;536      }537 538      case Value::ConstantExprVal: {539        ConstantExpr *CE = cast<ConstantExpr>(V);540        if (searchConstantExprForGlobalVariables(CE, GVLoadMap, GVUses)) {541          FoundAnyGVUses = true;542        }543        break;544      }545 546      default:547        break;548      }549 550      if (FoundAnyGVUses) {551        GVUses.push_back(GlobalVariableUse{&I, OpIndex});552      }553    }554  }555 556  // If this function did not reference any global variables then we have no557  // work to do. Most functions do not access global variables.558  if (GVUses.empty()) {559    return false;560  }561 562  // We know that there is at least one instruction that needs to be rewritten.563  // Generate a Load instruction for each unique GlobalVariable used by this564  // function. The Load instructions are inserted at the beginning of the565  // entry block. Since entry blocks cannot contain PHI instructions, there is566  // no need to skip PHI instructions.567 568  // We use a single IRBuilder for inserting Load instructions as well as the569  // constants that we convert to instructions. Because constants do not570  // depend on any dynamic values (they're constant, after all!), it is safe571  // to move them to the start of entry BB.572 573  auto &EntryBlock = F.getEntryBlock();574  IRBuilder<> IRBuilderAtEntry(&EntryBlock, EntryBlock.begin());575 576  for (auto &[GV, LoadValue] : GVLoadMap) {577    assert(LoadValue == nullptr);578    GlobalVariable *RefGV = getOrCreateRefVariable(F, RefMapping, GV);579    LoadValue = IRBuilderAtEntry.CreateLoad(RefGV->getValueType(), RefGV);580  }581 582  const DISubprogram *Subprogram = F.getSubprogram();583  DICompileUnit *Unit = Subprogram != nullptr ? Subprogram->getUnit() : nullptr;584  DIBuilder DebugInfo{*F.getParent(), true, Unit};585 586  // Go back to the instructions and rewrite their uses of GlobalVariable.587  // Because a ConstantExpr can be a tree, it may reference more than one588  // GlobalVariable.589 590  for (auto &GVUse : GVUses) {591    Value *OldOperandValue = GVUse.User->getOperand(GVUse.Op);592    Value *NewOperandValue;593 594    switch (OldOperandValue->getValueID()) {595    case Value::GlobalVariableVal: {596      // This is easy. Look up the replacement value and store the operand.597      Value *OperandValue = GVUse.User->getOperand(GVUse.Op);598      GlobalVariable *GV = cast<GlobalVariable>(OperandValue);599      NewOperandValue = GVLoadMap.at(GV);600      break;601    }602 603    case Value::ConstantExprVal: {604      // Walk the recursive tree of the ConstantExpr. If we find a605      // GlobalVariable then replace it with the loaded value and rewrite606      // the ConstantExpr to an Instruction and insert it before the607      // current instruction.608      Value *OperandValue = GVUse.User->getOperand(GVUse.Op);609      ConstantExpr *CE = cast<ConstantExpr>(OperandValue);610      NewOperandValue =611          rewriteGlobalVariablesInConstant(CE, GVLoadMap, IRBuilderAtEntry);612      assert(NewOperandValue != nullptr);613      break;614    }615 616    default:617      // We should only ever get here because a GVUse was created in the first618      // pass, and this only happens for GlobalVariableVal and ConstantExprVal.619      llvm_unreachable_internal(620          "unexpected Value in second pass of hot-patching");621      break;622    }623 624    GVUse.User->setOperand(GVUse.Op, NewOperandValue);625  }626 627  return true;628}629