Skip to content

Commit dc48eeb

Browse files
committed
[aarch64][x86][win] Add support for MSVC's /funcoverride flag (Windows kernel loader replaceable functions)
1 parent c3c3262 commit dc48eeb

File tree

16 files changed

+253
-63
lines changed

16 files changed

+253
-63
lines changed

clang/include/clang/Basic/CodeGenOptions.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,9 @@ class CodeGenOptions : public CodeGenOptionsBase {
489489
/// The name of a file to use with \c .secure_log_unique directives.
490490
std::string AsSecureLogFile;
491491

492+
/// A list of functions that are replacable by the loader.
493+
std::vector<std::string> LoaderReplaceableFunctionNames;
494+
492495
public:
493496
// Define accessors/mutators for code generation options of enumeration type.
494497
#define CODEGENOPT(Name, Bits, Default)
@@ -561,6 +564,12 @@ class CodeGenOptions : public CodeGenOptionsBase {
561564
/// Reset all of the options that are not considered when building a
562565
/// module.
563566
void resetNonModularOptions(StringRef ModuleFormat);
567+
568+
// Is the given function name one of the functions that can be replaced by the
569+
// loader?
570+
bool isLoaderReplaceableFunctionName(StringRef FuncName) const {
571+
return llvm::is_contained(LoaderReplaceableFunctionNames, FuncName);
572+
}
564573
};
565574

566575
} // end namespace clang

clang/include/clang/Driver/Options.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7592,6 +7592,9 @@ def import_call_optimization : Flag<["-"], "import-call-optimization">,
75927592
"by the Windows kernel to enable import call optimization">,
75937593
MarshallingInfoFlag<CodeGenOpts<"ImportCallOptimization">>;
75947594

7595+
def replaceable_function: Joined<["-"], "loader-replaceable-function=">,
7596+
MarshallingInfoStringVector<CodeGenOpts<"LoaderReplaceableFunctionNames">>;
7597+
75957598
} // let Visibility = [CC1Option]
75967599

75977600
//===----------------------------------------------------------------------===//
@@ -8838,6 +8841,10 @@ def _SLASH_Gregcall : CLFlag<"Gregcall">,
88388841
def _SLASH_Gregcall4 : CLFlag<"Gregcall4">,
88398842
HelpText<"Set __regcall4 as a default calling convention to respect __regcall ABI v.4">;
88408843

8844+
def _SLASH_funcoverride : CLCompileJoined<"funcoverride:">,
8845+
HelpText<"Mark <function> as being replaceable by the Windows kernel loader">,
8846+
MetaVarName<"<function>">;
8847+
88418848
// GNU Driver aliases
88428849

88438850
def : Separate<["-"], "Xmicrosoft-visualc-tools-root">, Alias<_SLASH_vctoolsdir>;

clang/lib/CodeGen/CGCall.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2574,6 +2574,10 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
25742574
GetCPUAndFeaturesAttributes(CalleeInfo.getCalleeDecl(), FuncAttrs);
25752575
}
25762576

2577+
// Mark functions that are replaceable by the loader.
2578+
if (CodeGenOpts.isLoaderReplaceableFunctionName(Name))
2579+
FuncAttrs.addAttribute("loader-replaceable");
2580+
25772581
// Collect attributes from arguments and return values.
25782582
ClangToLLVMArgMapping IRFunctionArgs(getContext(), FI);
25792583

clang/lib/Driver/ToolChains/Clang.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8504,6 +8504,12 @@ void Clang::AddClangCLArgs(const ArgList &Args, types::ID InputType,
85048504
}
85058505
A->claim();
85068506
}
8507+
8508+
for (const auto &FuncOverride :
8509+
Args.getAllArgValues(options::OPT__SLASH_funcoverride)) {
8510+
CmdArgs.push_back(Args.MakeArgString(
8511+
Twine("-loader-replaceable-function=") + FuncOverride));
8512+
}
85078513
}
85088514

85098515
const char *Clang::getBaseInputName(const ArgList &Args,

clang/test/Driver/cl-options.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,4 +813,8 @@
813813
// RUN: %clang_cl -vctoolsdir "" /arm64EC /c -target x86_64-pc-windows-msvc -### -- %s 2>&1 | FileCheck --check-prefix=ARM64EC_OVERRIDE %s
814814
// ARM64EC_OVERRIDE: warning: /arm64EC has been overridden by specified target: x86_64-pc-windows-msvc; option ignored
815815

816+
// RUN: %clang_cl /funcoverride:override_me1 /funcoverride:override_me2 /c -### -- %s 2>&1 | FileCheck %s --check-prefix=FUNCOVERRIDE
817+
// FUNCOVERRIDE: -loader-replaceable-function=override_me1
818+
// FUNCOVERRIDE-SAME: -loader-replaceable-function=override_me2
819+
816820
void f(void) { }

llvm/include/llvm/CodeGen/AsmPrinter.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,17 @@ class AsmPrinter : public MachineFunctionPass {
796796
getCodeViewJumpTableInfo(int JTI, const MachineInstr *BranchInstr,
797797
const MCSymbol *BranchLabel) const;
798798

799+
//===------------------------------------------------------------------===//
800+
// COFF Helper Routines
801+
//===------------------------------------------------------------------===//
802+
803+
/// Emits symbols and data to allow functions marked with the
804+
/// loader-replaceable attribute to be replaceable.
805+
void emitCOFFReplaceableFunctionData(Module &M);
806+
807+
/// Emits the @feat.00 symbol indicating the features enabled in this module.
808+
void emitCOFFFeatureSymbol(Module &M);
809+
799810
//===------------------------------------------------------------------===//
800811
// Inline Asm Support
801812
//===------------------------------------------------------------------===//

llvm/include/llvm/IR/Attributes.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ def NoJumpTables : StrBoolAttr<"no-jump-tables">;
400400
def NoInlineLineTables : StrBoolAttr<"no-inline-line-tables">;
401401
def ProfileSampleAccurate : StrBoolAttr<"profile-sample-accurate">;
402402
def UseSampleProfile : StrBoolAttr<"use-sample-profile">;
403+
def LoaderReplaceable : StrBoolAttr<"loader-replaceable">;
403404

404405
def DenormalFPMath : ComplexStrAttr<"denormal-fp-math", [FnAttr]>;
405406
def DenormalFPMathF32 : ComplexStrAttr<"denormal-fp-math-f32", [FnAttr]>;

llvm/include/llvm/IR/Mangler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ class Triple;
2525
class Twine;
2626
class raw_ostream;
2727

28+
constexpr std::string_view HybridPatchableTargetSuffix = "$hp_target";
29+
2830
class Mangler {
2931
/// We need to give global values the same name every time they are mangled.
3032
/// This keeps track of the number we give to anonymous ones.

llvm/lib/Analysis/InlineCost.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3078,6 +3078,10 @@ std::optional<InlineResult> llvm::getAttributeBasedInliningDecision(
30783078
if (Call.isNoInline())
30793079
return InlineResult::failure("noinline call site attribute");
30803080

3081+
// Don't inline functions that are loader replaceable.
3082+
if (Callee->hasFnAttribute("loader-replaceable"))
3083+
return InlineResult::failure("loader replaceable function attribute");
3084+
30813085
return std::nullopt;
30823086
}
30833087

llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4661,3 +4661,109 @@ AsmPrinter::getCodeViewJumpTableInfo(int JTI, const MachineInstr *BranchInstr,
46614661
return std::make_tuple(Base, 0, BranchLabel,
46624662
codeview::JumpTableEntrySize::Int32);
46634663
}
4664+
4665+
void AsmPrinter::emitCOFFReplaceableFunctionData(Module &M) {
4666+
const Triple &TT = TM.getTargetTriple();
4667+
assert(TT.isOSBinFormatCOFF());
4668+
4669+
bool IsTargetArm64EC = TT.isWindowsArm64EC();
4670+
SmallVector<char> Buf;
4671+
SmallVector<MCSymbol *> FuncOverrideDefaultSymbols;
4672+
bool SwitchedToDirectiveSection = false;
4673+
for (const Function &F : M.functions()) {
4674+
if (F.hasFnAttribute("loader-replaceable")) {
4675+
if (!SwitchedToDirectiveSection) {
4676+
OutStreamer->switchSection(
4677+
OutContext.getObjectFileInfo()->getDrectveSection());
4678+
SwitchedToDirectiveSection = true;
4679+
}
4680+
4681+
StringRef Name = F.getName();
4682+
4683+
// For hybrid-patchable targets, strip the prefix so that we can mark
4684+
// the real function as replaceable.
4685+
if (IsTargetArm64EC && Name.ends_with(HybridPatchableTargetSuffix)) {
4686+
Name = Name.substr(0, Name.size() - HybridPatchableTargetSuffix.size());
4687+
}
4688+
4689+
MCSymbol *FuncOverrideSymbol =
4690+
MMI->getContext().getOrCreateSymbol(Name + "_$fo$");
4691+
OutStreamer->beginCOFFSymbolDef(FuncOverrideSymbol);
4692+
OutStreamer->emitCOFFSymbolStorageClass(COFF::IMAGE_SYM_CLASS_EXTERNAL);
4693+
OutStreamer->emitCOFFSymbolType(COFF::IMAGE_SYM_DTYPE_NULL);
4694+
OutStreamer->endCOFFSymbolDef();
4695+
4696+
MCSymbol *FuncOverrideDefaultSymbol =
4697+
MMI->getContext().getOrCreateSymbol(Name + "_$fo_default$");
4698+
OutStreamer->beginCOFFSymbolDef(FuncOverrideDefaultSymbol);
4699+
OutStreamer->emitCOFFSymbolStorageClass(COFF::IMAGE_SYM_CLASS_EXTERNAL);
4700+
OutStreamer->emitCOFFSymbolType(COFF::IMAGE_SYM_DTYPE_NULL);
4701+
OutStreamer->endCOFFSymbolDef();
4702+
FuncOverrideDefaultSymbols.push_back(FuncOverrideDefaultSymbol);
4703+
4704+
OutStreamer->emitBytes((Twine(" /ALTERNATENAME:") +
4705+
FuncOverrideSymbol->getName() + "=" +
4706+
FuncOverrideDefaultSymbol->getName())
4707+
.toStringRef(Buf));
4708+
Buf.clear();
4709+
}
4710+
}
4711+
4712+
if (SwitchedToDirectiveSection)
4713+
OutStreamer->popSection();
4714+
4715+
if (FuncOverrideDefaultSymbols.empty())
4716+
return;
4717+
4718+
// MSVC emits the symbols for the default variables pointing at the start of
4719+
// the .data section, but doesn't actually allocate any space for them. LLVM
4720+
// can't do this, so have all of the variables pointing at a single byte
4721+
// instead.
4722+
OutStreamer->switchSection(OutContext.getObjectFileInfo()->getDataSection());
4723+
for (MCSymbol *Symbol : FuncOverrideDefaultSymbols) {
4724+
OutStreamer->emitLabel(Symbol);
4725+
}
4726+
OutStreamer->emitZeros(1);
4727+
OutStreamer->popSection();
4728+
}
4729+
4730+
void AsmPrinter::emitCOFFFeatureSymbol(Module &M) {
4731+
const Triple &TT = TM.getTargetTriple();
4732+
assert(TT.isOSBinFormatCOFF());
4733+
4734+
// Emit an absolute @feat.00 symbol.
4735+
MCSymbol *S = MMI->getContext().getOrCreateSymbol(StringRef("@feat.00"));
4736+
OutStreamer->beginCOFFSymbolDef(S);
4737+
OutStreamer->emitCOFFSymbolStorageClass(COFF::IMAGE_SYM_CLASS_STATIC);
4738+
OutStreamer->emitCOFFSymbolType(COFF::IMAGE_SYM_DTYPE_NULL);
4739+
OutStreamer->endCOFFSymbolDef();
4740+
int64_t Feat00Value = 0;
4741+
4742+
if (TT.getArch() == Triple::x86) {
4743+
// According to the PE-COFF spec, the LSB of this value marks the object
4744+
// for "registered SEH". This means that all SEH handler entry points
4745+
// must be registered in .sxdata. Use of any unregistered handlers will
4746+
// cause the process to terminate immediately. LLVM does not know how to
4747+
// register any SEH handlers, so its object files should be safe.
4748+
Feat00Value |= COFF::Feat00Flags::SafeSEH;
4749+
}
4750+
4751+
if (M.getModuleFlag("cfguard")) {
4752+
// Object is CFG-aware.
4753+
Feat00Value |= COFF::Feat00Flags::GuardCF;
4754+
}
4755+
4756+
if (M.getModuleFlag("ehcontguard")) {
4757+
// Object also has EHCont.
4758+
Feat00Value |= COFF::Feat00Flags::GuardEHCont;
4759+
}
4760+
4761+
if (M.getModuleFlag("ms-kernel")) {
4762+
// Object is compiled with /kernel.
4763+
Feat00Value |= COFF::Feat00Flags::Kernel;
4764+
}
4765+
4766+
OutStreamer->emitSymbolAttribute(S, MCSA_Global);
4767+
OutStreamer->emitAssignment(
4768+
S, MCConstantExpr::create(Feat00Value, MMI->getContext()));
4769+
}

0 commit comments

Comments
 (0)