Skip to content

Commit 69be013

Browse files
committed
Windows hotpatching support
1 parent 6babd63 commit 69be013

34 files changed

+657
-2
lines changed

clang/include/clang/Basic/CodeGenOptions.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,14 @@ class CodeGenOptions : public CodeGenOptionsBase {
493493
/// The name of a file to use with \c .secure_log_unique directives.
494494
std::string AsSecureLogFile;
495495

496+
/// The name of a file that contains functions which will be compiled for
497+
/// hotpatching. See -fms-hot-patch-functions-file.
498+
std::string MSHotPatchFunctionsFile;
499+
500+
/// A list of functions which will be compiled for hotpatching.
501+
/// See -fms-hot-patch-functions-list.
502+
std::vector<std::string> MSHotPatchFunctionsList;
503+
496504
public:
497505
// Define accessors/mutators for code generation options of enumeration type.
498506
#define CODEGENOPT(Name, Bits, Default)

clang/include/clang/Driver/Options.td

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3793,6 +3793,20 @@ def fms_hotpatch : Flag<["-"], "fms-hotpatch">, Group<f_Group>,
37933793
Visibility<[ClangOption, CC1Option, CLOption]>,
37943794
HelpText<"Ensure that all functions can be hotpatched at runtime">,
37953795
MarshallingInfoFlag<CodeGenOpts<"HotPatch">>;
3796+
def fms_hotpatch_functions_file
3797+
: Joined<["-"], "fms-hotpatch-functions-file=">,
3798+
Group<f_Group>,
3799+
Visibility<[ClangOption, CC1Option, CLOption]>,
3800+
MarshallingInfoString<CodeGenOpts<"MSHotPatchFunctionsFile">>,
3801+
HelpText<"Path to a file that contains a list of mangled symbol names of "
3802+
"functions that should be hot-patched">;
3803+
def fms_hotpatch_functions_list
3804+
: CommaJoined<["-"], "fms-hotpatch-functions-list=">,
3805+
Group<f_Group>,
3806+
Visibility<[ClangOption, CC1Option, CLOption]>,
3807+
MarshallingInfoStringVector<CodeGenOpts<"MSHotPatchFunctionsList">>,
3808+
HelpText<"List of mangled symbol names of functions that should be "
3809+
"hot-patched">;
37963810
def fpcc_struct_return : Flag<["-"], "fpcc-struct-return">, Group<f_Group>,
37973811
Visibility<[ClangOption, CC1Option]>,
37983812
HelpText<"Override the default ABI to return all structs on the stack">;

clang/lib/CodeGen/CGCall.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2636,6 +2636,15 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
26362636
// CPU/feature overrides. addDefaultFunctionDefinitionAttributes
26372637
// handles these separately to set them based on the global defaults.
26382638
GetCPUAndFeaturesAttributes(CalleeInfo.getCalleeDecl(), FuncAttrs);
2639+
2640+
// Windows hotpatching support
2641+
if (!MSHotPatchFunctions.empty()) {
2642+
bool IsHotPatched = std::binary_search(MSHotPatchFunctions.begin(),
2643+
MSHotPatchFunctions.end(), Name);
2644+
if (IsHotPatched) {
2645+
FuncAttrs.addAttribute(llvm::Attribute::MarkedForWindowsHotPatching);
2646+
}
2647+
}
26392648
}
26402649

26412650
// Collect attributes from arguments and return values.

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,37 @@ CodeGenModule::CodeGenModule(ASTContext &C,
453453
if (Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86)
454454
getModule().addModuleFlag(llvm::Module::Error, "NumRegisterParameters",
455455
CodeGenOpts.NumRegisterParameters);
456+
457+
// If there are any functions that are marked for Windows hot-patching,
458+
// then build the list of functions now.
459+
if (!CGO.MSHotPatchFunctionsFile.empty() ||
460+
!CGO.MSHotPatchFunctionsList.empty()) {
461+
if (!CGO.MSHotPatchFunctionsFile.empty()) {
462+
auto BufOrErr = llvm::MemoryBuffer::getFile(CGO.MSHotPatchFunctionsFile);
463+
if (BufOrErr) {
464+
const llvm::MemoryBuffer &FileBuffer = **BufOrErr;
465+
for (llvm::line_iterator I(FileBuffer.getMemBufferRef(), true), E;
466+
I != E; ++I) {
467+
this->MSHotPatchFunctions.push_back(std::string{*I});
468+
}
469+
} else {
470+
auto &DE = Context.getDiagnostics();
471+
unsigned DiagID =
472+
DE.getCustomDiagID(DiagnosticsEngine::Error,
473+
"failed to open hotpatch functions file "
474+
"(-fms-hotpatch-functions-file): %0 : %1");
475+
DE.Report(DiagID) << CGO.MSHotPatchFunctionsFile
476+
<< BufOrErr.getError().message();
477+
}
478+
}
479+
480+
for (const auto &FuncName : CGO.MSHotPatchFunctionsList) {
481+
this->MSHotPatchFunctions.push_back(FuncName);
482+
}
483+
484+
std::sort(this->MSHotPatchFunctions.begin(),
485+
this->MSHotPatchFunctions.end());
486+
}
456487
}
457488

458489
CodeGenModule::~CodeGenModule() {}

clang/lib/CodeGen/CodeGenModule.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,11 @@ class CodeGenModule : public CodeGenTypeCache {
678678

679679
AtomicOptions AtomicOpts;
680680

681+
// A set of functions which should be hot-patched; see
682+
// -fms-hotpatch-functions-file (and -list). This will nearly always be empty.
683+
// The list is sorted for binary-searching.
684+
std::vector<std::string> MSHotPatchFunctions;
685+
681686
public:
682687
CodeGenModule(ASTContext &C, IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
683688
const HeaderSearchOptions &headersearchopts,

clang/lib/Driver/ToolChains/Clang.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6945,6 +6945,16 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
69456945

69466946
Args.AddLastArg(CmdArgs, options::OPT_fms_hotpatch);
69476947

6948+
if (Arg *A = Args.getLastArg(options::OPT_fms_hotpatch_functions_file)) {
6949+
Args.AddLastArg(CmdArgs, options::OPT_fms_hotpatch_functions_file);
6950+
}
6951+
6952+
for (const auto &A :
6953+
Args.getAllArgValues(options::OPT_fms_hotpatch_functions_list)) {
6954+
CmdArgs.push_back(
6955+
Args.MakeArgString("-fms-hotpatch-functions-list=" + Twine(A)));
6956+
}
6957+
69486958
if (TC.SupportsProfiling()) {
69496959
Args.AddLastArg(CmdArgs, options::OPT_pg);
69506960

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// This verifies that we correctly handle a -fms-hotpatch-functions-file argument that points
2+
// to a missing file.
3+
//
4+
// RUN: not %clang_cl -c --target=x86_64-windows-msvc -O2 /Z7 -fms-hotpatch-functions-file=%S/this-file-is-intentionally-missing-do-not-create-it.txt /Fo%t.obj %s 2>&1 | FileCheck %s
5+
// CHECK: failed to open hotpatch functions file
6+
7+
void this_might_have_side_effects();
8+
9+
int __declspec(noinline) this_gets_hotpatched() {
10+
this_might_have_side_effects();
11+
return 42;
12+
}
13+
14+
int __declspec(noinline) this_does_not_get_hotpatched() {
15+
return this_gets_hotpatched() + 100;
16+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
this_gets_hotpatched

clang/test/CodeGen/ms-hotpatch-lto.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// This verifies that hotpatch function attributes are correctly propagated through LLVM IR when compiling with LTO.
2+
//
3+
// RUN: %clang_cl -c --target=x86_64-windows-msvc -O2 /Z7 -fms-hotpatch-functions-file=%S/ms-hotpatch-functions.txt -flto /Fo%t.bc %s
4+
// RUN: llvm-dis %t.bc -o - | FileCheck %s
5+
//
6+
// CHECK: ; Function Attrs: marked_for_windows_hot_patching mustprogress nofree noinline norecurse nosync nounwind sspstrong willreturn memory(none) uwtable
7+
// CHECK-NEXT: define dso_local noundef i32 @this_gets_hotpatched() local_unnamed_addr #0 !dbg !13 {
8+
//
9+
// CHECK: ; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind sspstrong willreturn memory(none) uwtable
10+
// CHECK-NEXT: define dso_local noundef i32 @this_does_not_get_hotpatched() local_unnamed_addr #1 !dbg !19 {
11+
12+
int __declspec(noinline) this_gets_hotpatched() {
13+
return 42;
14+
}
15+
16+
int __declspec(noinline) this_does_not_get_hotpatched() {
17+
return this_gets_hotpatched() + 100;
18+
}

clang/test/CodeGen/ms-hotpatch.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// This verifies that hotpatch function attributes are correctly propagated when compiling directly to OBJ.
2+
//
3+
// RUN: %clang_cl -c --target=x86_64-windows-msvc -O2 /Z7 -fms-hotpatch-functions-file=%S/ms-hotpatch-functions.txt /Fo%t.obj %s
4+
// RUN: llvm-readobj --codeview %t.obj | FileCheck %s
5+
// CHECK: Kind: S_HOTPATCHFUNC (0x1169)
6+
// CHECK-NEXT: Function: this_gets_hotpatched
7+
8+
void this_might_have_side_effects();
9+
10+
int __declspec(noinline) this_gets_hotpatched() {
11+
this_might_have_side_effects();
12+
return 42;
13+
}
14+
15+
int __declspec(noinline) this_does_not_get_hotpatched() {
16+
return this_gets_hotpatched() + 100;
17+
}

llvm/include/llvm/Bitcode/LLVMBitCodes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,8 @@ enum AttributeKindCodes {
793793
ATTR_KIND_NO_DIVERGENCE_SOURCE = 100,
794794
ATTR_KIND_SANITIZE_TYPE = 101,
795795
ATTR_KIND_CAPTURES = 102,
796+
ATTR_KIND_ALLOW_DIRECT_ACCESS_IN_HOT_PATCH_FUNCTION = 103,
797+
ATTR_KIND_MARKED_FOR_WINDOWS_HOT_PATCHING = 104,
796798
};
797799

798800
enum ComdatSelectionKindCodes {

llvm/include/llvm/CodeGen/Passes.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,9 @@ namespace llvm {
617617

618618
/// Lowers KCFI operand bundles for indirect calls.
619619
FunctionPass *createKCFIPass();
620+
621+
/// Creates Windows Hot Patch pass. \see WindowsHotPatch.cpp
622+
ModulePass *createWindowsHotPatch();
620623
} // End llvm namespace
621624

622625
#endif

llvm/include/llvm/DebugInfo/CodeView/CodeViewSymbols.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,8 @@ SYMBOL_RECORD_ALIAS(S_GTHREAD32 , 0x1113, GlobalTLS, ThreadLocalDataSym)
256256
SYMBOL_RECORD(S_UNAMESPACE , 0x1124, UsingNamespaceSym)
257257
SYMBOL_RECORD(S_ANNOTATION , 0x1019, AnnotationSym)
258258

259+
SYMBOL_RECORD(S_HOTPATCHFUNC , 0x1169, HotPatchFuncSym)
260+
259261
#undef CV_SYMBOL
260262
#undef SYMBOL_RECORD
261263
#undef SYMBOL_RECORD_ALIAS

llvm/include/llvm/DebugInfo/CodeView/SymbolRecord.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,21 @@ class CallerSym : public SymbolRecord {
176176
uint32_t RecordOffset = 0;
177177
};
178178

179+
class HotPatchFuncSym : public SymbolRecord {
180+
public:
181+
explicit HotPatchFuncSym(SymbolRecordKind Kind) : SymbolRecord(Kind) {}
182+
HotPatchFuncSym(uint32_t RecordOffset)
183+
: SymbolRecord(SymbolRecordKind::HotPatchFuncSym),
184+
RecordOffset(RecordOffset) {}
185+
186+
// This is an ItemID in the IPI stream, which points to an LF_FUNC_ID or
187+
// LF_MFUNC_ID record.
188+
TypeIndex Function;
189+
StringRef Name;
190+
191+
uint32_t RecordOffset = 0;
192+
};
193+
179194
struct DecodedAnnotation {
180195
StringRef Name;
181196
ArrayRef<uint8_t> Bytes;

llvm/include/llvm/IR/Attributes.td

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,17 @@ def CoroDestroyOnlyWhenComplete : EnumAttr<"coro_only_destroy_when_complete", In
389389
/// pipeline to perform elide on the call or invoke instruction.
390390
def CoroElideSafe : EnumAttr<"coro_elide_safe", IntersectPreserve, [FnAttr]>;
391391

392+
/// Function is marked for Windows Hot Patching
393+
def MarkedForWindowsHotPatching
394+
: EnumAttr<"marked_for_windows_hot_patching", IntersectPreserve, [FnAttr]>;
395+
396+
/// Global variable should not be accessed through a "__ref_" global variable in
397+
/// a hot patching function This attribute is applied to the global variable
398+
/// decl, not the hotpatched function.
399+
def AllowDirectAccessInHotPatchFunction
400+
: EnumAttr<"allow_direct_access_in_hot_patch_function",
401+
IntersectPreserve, []>;
402+
392403
/// Target-independent string attributes.
393404
def LessPreciseFPMAD : StrBoolAttr<"less-precise-fpmad">;
394405
def NoInfsFPMath : StrBoolAttr<"no-infs-fp-math">;

llvm/include/llvm/InitializePasses.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ void initializeVirtRegMapWrapperLegacyPass(PassRegistry &);
322322
void initializeVirtRegRewriterLegacyPass(PassRegistry &);
323323
void initializeWasmEHPreparePass(PassRegistry &);
324324
void initializeWinEHPreparePass(PassRegistry &);
325+
void initializeWindowsHotPatchPass(PassRegistry &);
325326
void initializeWriteBitcodePassPass(PassRegistry &);
326327
void initializeXRayInstrumentationLegacyPass(PassRegistry &);
327328

llvm/lib/Bitcode/Reader/BitcodeReader.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2248,6 +2248,10 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
22482248
return Attribute::NoExt;
22492249
case bitc::ATTR_KIND_CAPTURES:
22502250
return Attribute::Captures;
2251+
case bitc::ATTR_KIND_ALLOW_DIRECT_ACCESS_IN_HOT_PATCH_FUNCTION:
2252+
return Attribute::AllowDirectAccessInHotPatchFunction;
2253+
case bitc::ATTR_KIND_MARKED_FOR_WINDOWS_HOT_PATCHING:
2254+
return Attribute::MarkedForWindowsHotPatching;
22512255
}
22522256
}
22532257

llvm/lib/Bitcode/Writer/BitcodeWriter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -919,6 +919,10 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
919919
return bitc::ATTR_KIND_NO_EXT;
920920
case Attribute::Captures:
921921
return bitc::ATTR_KIND_CAPTURES;
922+
case Attribute::AllowDirectAccessInHotPatchFunction:
923+
return bitc::ATTR_KIND_ALLOW_DIRECT_ACCESS_IN_HOT_PATCH_FUNCTION;
924+
case Attribute::MarkedForWindowsHotPatching:
925+
return bitc::ATTR_KIND_MARKED_FOR_WINDOWS_HOT_PATCHING;
922926
case Attribute::EndAttrKinds:
923927
llvm_unreachable("Can not encode end-attribute kinds marker.");
924928
case Attribute::None:

llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -627,8 +627,6 @@ void CodeViewDebug::beginModule(Module *M) {
627627

628628
CurrentSourceLanguage = MapDWLangToCVLang(CU->getSourceLanguage());
629629

630-
collectGlobalVariableInfo();
631-
632630
// Check if we should emit type record hashes.
633631
ConstantInt *GH =
634632
mdconst::extract_or_null<ConstantInt>(M->getModuleFlag("CodeViewGHash"));
@@ -639,6 +637,8 @@ void CodeViewDebug::endModule() {
639637
if (!Asm || !Asm->hasDebugInfo())
640638
return;
641639

640+
collectGlobalVariableInfo();
641+
642642
// The COFF .debug$S section consists of several subsections, each starting
643643
// with a 4-byte control code (e.g. 0xF1, 0xF2, etc) and then a 4-byte length
644644
// of the payload followed by the payload itself. The subsections are 4-byte
@@ -653,6 +653,8 @@ void CodeViewDebug::endModule() {
653653
emitCompilerInformation();
654654
endCVSubsection(CompilerInfo);
655655

656+
emitHotPatchInformation();
657+
656658
emitInlineeLinesSubsection();
657659

658660
// Emit per-function debug information.
@@ -807,6 +809,32 @@ void CodeViewDebug::emitObjName() {
807809
endSymbolRecord(CompilerEnd);
808810
}
809811

812+
void CodeViewDebug::emitHotPatchInformation() {
813+
MCSymbol *hotPatchInfo = nullptr;
814+
for (const auto &F : MMI->getModule()->functions()) {
815+
if (!F.isDeclarationForLinker() &&
816+
F.hasFnAttribute(Attribute::MarkedForWindowsHotPatching)) {
817+
if (hotPatchInfo == nullptr) {
818+
hotPatchInfo = beginCVSubsection(DebugSubsectionKind::Symbols);
819+
}
820+
MCSymbol *HotPatchEnd = beginSymbolRecord(SymbolKind::S_HOTPATCHFUNC);
821+
auto *SP = F.getSubprogram();
822+
OS.AddComment("Function");
823+
OS.emitInt32(getFuncIdForSubprogram(SP).getIndex());
824+
OS.AddComment("Name");
825+
llvm::StringRef Name = SP->getLinkageName();
826+
if (Name.empty()) {
827+
Name = F.getName();
828+
}
829+
emitNullTerminatedSymbolName(OS, Name);
830+
endSymbolRecord(HotPatchEnd);
831+
}
832+
}
833+
if (hotPatchInfo != nullptr) {
834+
endCVSubsection(hotPatchInfo);
835+
}
836+
}
837+
810838
namespace {
811839
struct Version {
812840
int Part[4];

llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,8 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
333333

334334
void emitCompilerInformation();
335335

336+
void emitHotPatchInformation();
337+
336338
void emitBuildInfo();
337339

338340
void emitInlineeLinesSubsection();

llvm/lib/CodeGen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ add_llvm_component_library(LLVMCodeGen
250250
VirtRegMap.cpp
251251
WasmEHPrepare.cpp
252252
WindowScheduler.cpp
253+
WindowsHotPatch.cpp
253254
WinEHPrepare.cpp
254255
XRayInstrumentation.cpp
255256
${GeneratedMLSources}

llvm/lib/CodeGen/TargetPassConfig.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,9 @@ void TargetPassConfig::addIRPasses() {
893893

894894
if (EnableGlobalMergeFunc)
895895
addPass(createGlobalMergeFuncPass());
896+
897+
if (TM->getTargetTriple().isOSBinFormatCOFF())
898+
addPass(createWindowsHotPatch());
896899
}
897900

898901
/// Turn exception handling constructs into something the code generators can

0 commit comments

Comments
 (0)