Skip to content

Commit 277c740

Browse files
committed
Support IFuncs on Darwin platforms (llvm#73686)
... by lowering them as lazy resolve-on-first-use symbol resolvers. Note that this is subtly different timing than on ELF platforms, where ifunc resolution happens at load time. Since ld64 and ld-prime don't support all the cases we need for these, we lower them manually in the AsmPrinter.
1 parent 5adc301 commit 277c740

File tree

9 files changed

+551
-51
lines changed

9 files changed

+551
-51
lines changed

llvm/docs/LangRef.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -939,10 +939,11 @@ IFuncs
939939
-------
940940

941941
IFuncs, like as aliases, don't create any new data or func. They are just a new
942-
symbol that dynamic linker resolves at runtime by calling a resolver function.
942+
symbol that is resolved at runtime by calling a resolver function.
943943

944-
IFuncs have a name and a resolver that is a function called by dynamic linker
945-
that returns address of another function associated with the name.
944+
On ELF platforms, IFuncs are resolved by the dynamic linker at load time. On
945+
Mach-O platforms, they are lowered in terms of ``.symbol_resolver`` functions,
946+
which lazily resolve the callee the first time they are called.
946947

947948
IFunc may have an optional :ref:`linkage type <linkage>` and an optional
948949
:ref:`visibility style <visibility>`.

llvm/include/llvm/CodeGen/AsmPrinter.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,26 @@ class AsmPrinter : public MachineFunctionPass {
611611
virtual const MCExpr *
612612
lowerBlockAddressConstant(const BlockAddress *BA);
613613

614+
/// getSubtargetInfo() cannot be used where this is needed because we don't
615+
/// have a MachineFunction when we're lowering a GlobalIFunc, and
616+
/// getSubtargetInfo requires one. Override the implementation in targets
617+
/// that support the Mach-O IFunc lowering.
618+
virtual const MCSubtargetInfo *getIFuncMCSubtargetInfo() const {
619+
return nullptr;
620+
}
621+
622+
virtual void emitMachOIFuncStubBody(Module &M, const GlobalIFunc &GI,
623+
MCSymbol *LazyPointer) {
624+
llvm_unreachable(
625+
"Mach-O IFunc lowering is not yet supported on this target");
626+
}
627+
628+
virtual void emitMachOIFuncStubHelperBody(Module &M, const GlobalIFunc &GI,
629+
MCSymbol *LazyPointer) {
630+
llvm_unreachable(
631+
"Mach-O IFunc lowering is not yet supported on this target");
632+
}
633+
614634
/// Emit N NOP instructions.
615635
void emitNops(unsigned N);
616636

@@ -626,7 +646,7 @@ class AsmPrinter : public MachineFunctionPass {
626646
StringRef Suffix) const;
627647

628648
/// Return the MCSymbol for the specified ExternalSymbol.
629-
MCSymbol *GetExternalSymbolSymbol(StringRef Sym) const;
649+
MCSymbol *GetExternalSymbolSymbol(Twine Sym) const;
630650

631651
/// Return the symbol for the specified jump table entry.
632652
MCSymbol *GetJTISymbol(unsigned JTID, bool isLinkerPrivate = false) const;
@@ -884,6 +904,7 @@ class AsmPrinter : public MachineFunctionPass {
884904
void emitGlobalAlias(Module &M, const GlobalAlias &GA);
885905
void emitGlobalIFunc(Module &M, const GlobalIFunc &GI);
886906

907+
private:
887908
/// This method decides whether the specified basic block requires a label.
888909
bool shouldEmitLabelForBasicBlock(const MachineBasicBlock &MBB) const;
889910

llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp

Lines changed: 99 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
#include "llvm/CodeGen/TargetLowering.h"
6060
#include "llvm/CodeGen/TargetOpcodes.h"
6161
#include "llvm/CodeGen/TargetRegisterInfo.h"
62+
#include "llvm/CodeGen/TargetSubtargetInfo.h"
6263
#include "llvm/Config/config.h"
6364
#include "llvm/IR/BasicBlock.h"
6465
#include "llvm/IR/Comdat.h"
@@ -2125,24 +2126,80 @@ void AsmPrinter::emitGlobalIFunc(Module &M, const GlobalIFunc &GI) {
21252126
assert(!TM.getTargetTriple().isOSBinFormatXCOFF() &&
21262127
"IFunc is not supported on AIX.");
21272128

2128-
MCSymbol *Name = getSymbol(&GI);
2129+
auto EmitLinkage = [&](MCSymbol *Sym) {
2130+
if (GI.hasExternalLinkage() || !MAI->getWeakRefDirective())
2131+
OutStreamer->emitSymbolAttribute(Sym, MCSA_Global);
2132+
else if (GI.hasWeakLinkage() || GI.hasLinkOnceLinkage())
2133+
OutStreamer->emitSymbolAttribute(Sym, MCSA_WeakReference);
2134+
else
2135+
assert(GI.hasLocalLinkage() && "Invalid ifunc linkage");
2136+
};
21292137

2130-
if (GI.hasExternalLinkage() || !MAI->getWeakRefDirective())
2131-
OutStreamer->emitSymbolAttribute(Name, MCSA_Global);
2132-
else if (GI.hasWeakLinkage() || GI.hasLinkOnceLinkage())
2133-
OutStreamer->emitSymbolAttribute(Name, MCSA_WeakReference);
2134-
else
2135-
assert(GI.hasLocalLinkage() && "Invalid ifunc linkage");
2138+
if (TM.getTargetTriple().isOSBinFormatELF()) {
2139+
MCSymbol *Name = getSymbol(&GI);
2140+
EmitLinkage(Name);
2141+
OutStreamer->emitSymbolAttribute(Name, MCSA_ELF_TypeIndFunction);
2142+
emitVisibility(Name, GI.getVisibility());
2143+
2144+
// Emit the directives as assignments aka .set:
2145+
const MCExpr *Expr = lowerConstant(GI.getResolver());
2146+
OutStreamer->emitAssignment(Name, Expr);
2147+
MCSymbol *LocalAlias = getSymbolPreferLocal(GI);
2148+
if (LocalAlias != Name)
2149+
OutStreamer->emitAssignment(LocalAlias, Expr);
2150+
2151+
return;
2152+
}
21362153

2137-
OutStreamer->emitSymbolAttribute(Name, MCSA_ELF_TypeIndFunction);
2138-
emitVisibility(Name, GI.getVisibility());
2154+
if (!TM.getTargetTriple().isOSBinFormatMachO() || !getIFuncMCSubtargetInfo())
2155+
llvm::report_fatal_error("IFuncs are not supported on this platform");
21392156

2140-
// Emit the directives as assignments aka .set:
2141-
const MCExpr *Expr = lowerConstant(GI.getResolver());
2142-
OutStreamer->emitAssignment(Name, Expr);
2143-
MCSymbol *LocalAlias = getSymbolPreferLocal(GI);
2144-
if (LocalAlias != Name)
2145-
OutStreamer->emitAssignment(LocalAlias, Expr);
2157+
// On Darwin platforms, emit a manually-constructed .symbol_resolver that
2158+
// implements the symbol resolution duties of the IFunc.
2159+
//
2160+
// Normally, this would be handled by linker magic, but unfortunately there
2161+
// are a few limitations in ld64 and ld-prime's implementation of
2162+
// .symbol_resolver that mean we can't always use them:
2163+
//
2164+
// * resolvers cannot be the target of an alias
2165+
// * resolvers cannot have private linkage
2166+
// * resolvers cannot have linkonce linkage
2167+
// * resolvers cannot appear in executables
2168+
// * resolvers cannot appear in bundles
2169+
//
2170+
// This works around that by emitting a close approximation of what the
2171+
// linker would have done.
2172+
2173+
MCSymbol *LazyPointer =
2174+
GetExternalSymbolSymbol(GI.getName() + ".lazy_pointer");
2175+
MCSymbol *StubHelper = GetExternalSymbolSymbol(GI.getName() + ".stub_helper");
2176+
2177+
OutStreamer->switchSection(OutContext.getObjectFileInfo()->getDataSection());
2178+
2179+
const DataLayout &DL = M.getDataLayout();
2180+
emitAlignment(Align(DL.getPointerSize()));
2181+
OutStreamer->emitLabel(LazyPointer);
2182+
emitVisibility(LazyPointer, GI.getVisibility());
2183+
OutStreamer->emitValue(MCSymbolRefExpr::create(StubHelper, OutContext), 8);
2184+
2185+
OutStreamer->switchSection(OutContext.getObjectFileInfo()->getTextSection());
2186+
2187+
const TargetSubtargetInfo *STI =
2188+
TM.getSubtargetImpl(*GI.getResolverFunction());
2189+
const TargetLowering *TLI = STI->getTargetLowering();
2190+
Align TextAlign(TLI->getMinFunctionAlignment());
2191+
2192+
MCSymbol *Stub = getSymbol(&GI);
2193+
EmitLinkage(Stub);
2194+
OutStreamer->emitCodeAlignment(TextAlign, getIFuncMCSubtargetInfo());
2195+
OutStreamer->emitLabel(Stub);
2196+
emitVisibility(Stub, GI.getVisibility());
2197+
emitMachOIFuncStubBody(M, GI, LazyPointer);
2198+
2199+
OutStreamer->emitCodeAlignment(TextAlign, getIFuncMCSubtargetInfo());
2200+
OutStreamer->emitLabel(StubHelper);
2201+
emitVisibility(StubHelper, GI.getVisibility());
2202+
emitMachOIFuncStubHelperBody(M, GI, LazyPointer);
21462203
}
21472204

21482205
void AsmPrinter::emitRemarksSection(remarks::RemarkStreamer &RS) {
@@ -2289,6 +2346,32 @@ bool AsmPrinter::doFinalization(Module &M) {
22892346
// through user plugins.
22902347
emitStackMaps();
22912348

2349+
// Print aliases in topological order, that is, for each alias a = b,
2350+
// b must be printed before a.
2351+
// This is because on some targets (e.g. PowerPC) linker expects aliases in
2352+
// such an order to generate correct TOC information.
2353+
SmallVector<const GlobalAlias *, 16> AliasStack;
2354+
SmallPtrSet<const GlobalAlias *, 16> AliasVisited;
2355+
for (const auto &Alias : M.aliases()) {
2356+
if (Alias.hasAvailableExternallyLinkage())
2357+
continue;
2358+
for (const GlobalAlias *Cur = &Alias; Cur;
2359+
Cur = dyn_cast<GlobalAlias>(Cur->getAliasee())) {
2360+
if (!AliasVisited.insert(Cur).second)
2361+
break;
2362+
AliasStack.push_back(Cur);
2363+
}
2364+
for (const GlobalAlias *AncestorAlias : llvm::reverse(AliasStack))
2365+
emitGlobalAlias(M, *AncestorAlias);
2366+
AliasStack.clear();
2367+
}
2368+
2369+
// IFuncs must come before deubginfo in case the backend decides to emit them
2370+
// as actual functions, since on Mach-O targets, we cannot create regular
2371+
// sections after DWARF.
2372+
for (const auto &IFunc : M.ifuncs())
2373+
emitGlobalIFunc(M, IFunc);
2374+
22922375
// Finalize debug and EH information.
22932376
for (const HandlerInfo &HI : Handlers) {
22942377
NamedRegionTimer T(HI.TimerName, HI.TimerDescription, HI.TimerGroupName,
@@ -2328,28 +2411,6 @@ bool AsmPrinter::doFinalization(Module &M) {
23282411
}
23292412
}
23302413

2331-
// Print aliases in topological order, that is, for each alias a = b,
2332-
// b must be printed before a.
2333-
// This is because on some targets (e.g. PowerPC) linker expects aliases in
2334-
// such an order to generate correct TOC information.
2335-
SmallVector<const GlobalAlias *, 16> AliasStack;
2336-
SmallPtrSet<const GlobalAlias *, 16> AliasVisited;
2337-
for (const auto &Alias : M.aliases()) {
2338-
if (Alias.hasAvailableExternallyLinkage())
2339-
continue;
2340-
for (const GlobalAlias *Cur = &Alias; Cur;
2341-
Cur = dyn_cast<GlobalAlias>(Cur->getAliasee())) {
2342-
if (!AliasVisited.insert(Cur).second)
2343-
break;
2344-
AliasStack.push_back(Cur);
2345-
}
2346-
for (const GlobalAlias *AncestorAlias : llvm::reverse(AliasStack))
2347-
emitGlobalAlias(M, *AncestorAlias);
2348-
AliasStack.clear();
2349-
}
2350-
for (const auto &IFunc : M.ifuncs())
2351-
emitGlobalIFunc(M, IFunc);
2352-
23532414
GCModuleInfo *MI = getAnalysisIfAvailable<GCModuleInfo>();
23542415
assert(MI && "AsmPrinter didn't require GCModuleInfo?");
23552416
for (GCModuleInfo::iterator I = MI->end(), E = MI->begin(); I != E; )
@@ -3737,7 +3798,7 @@ MCSymbol *AsmPrinter::getSymbolWithGlobalValueBase(const GlobalValue *GV,
37373798
}
37383799

37393800
/// Return the MCSymbol for the specified ExternalSymbol.
3740-
MCSymbol *AsmPrinter::GetExternalSymbolSymbol(StringRef Sym) const {
3801+
MCSymbol *AsmPrinter::GetExternalSymbolSymbol(Twine Sym) const {
37413802
SmallString<60> NameStr;
37423803
Mangler::getNameWithPrefix(NameStr, Sym, getDataLayout());
37433804
return OutContext.getOrCreateSymbol(NameStr);

0 commit comments

Comments
 (0)