[llvm] [llvm] Support IFuncs on Darwin platforms (PR #73686)
Jon Roelofs via llvm-commits
llvm-commits at lists.llvm.org
Tue Nov 28 11:38:58 PST 2023
================
@@ -1809,6 +1838,292 @@ void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) {
EmitToStreamer(*OutStreamer, TmpInst);
}
+void AArch64AsmPrinter::emitLinkerSymbolResolver(Module &M,
+ const GlobalIFunc &GI) {
+ OutStreamer->switchSection(OutContext.getObjectFileInfo()->getTextSection());
+
+ MCSymbol *Name = getSymbol(&GI);
+
+ // NOTE: non-global .symbol_resolvers are not yet supported by Darwin linkers
+
+ if (GI.hasExternalLinkage() || !MAI->getWeakRefDirective())
+ OutStreamer->emitSymbolAttribute(Name, MCSA_Global);
+ else if (GI.hasWeakLinkage() || GI.hasLinkOnceLinkage())
+ OutStreamer->emitSymbolAttribute(Name, MCSA_WeakReference);
+ else
+ assert(GI.hasLocalLinkage() && "Invalid ifunc linkage");
+
+ OutStreamer->emitCodeAlignment(Align(4), STI);
+ OutStreamer->emitLabel(Name);
+ OutStreamer->emitSymbolAttribute(Name, MCSA_SymbolResolver);
+ emitVisibility(Name, GI.getVisibility());
+
+ // ld-prime does not seem to support aliases of symbol resolvers, so we have
+ // to tail call the resolver manually.
+ OutStreamer->emitInstruction(
+ MCInstBuilder(AArch64::B)
+ .addOperand(MCOperand::createExpr(lowerConstant(GI.getResolver()))),
+ *STI);
+}
+
+/// \brief Emit a manually-constructed .symbol_resolver that implements the
+/// symbol resolution duties of the IFunc.
+///
+/// Normally, this would be handled by linker magic, but unfortunately there are
+/// a few limitations in ld64 and ld-prime's implementation of .symbol_resolver
+/// that mean we can't always use them:
+///
+/// * resolvers cannot be the target of an alias
+/// * resolvers cannot have private linkage
+/// * resolvers cannot have linkonce linkage
+/// * resolvers cannot appear in executables
+/// * resolvers cannot appear in bundles
+///
+/// This works around that by emitting a close approximation of what the linker
+/// would have done.
+void AArch64AsmPrinter::emitManualSymbolResolver(Module &M,
+ const GlobalIFunc &GI) {
+ auto EmitLinkage = [&](MCSymbol *Sym) {
+ if (GI.hasExternalLinkage() || !MAI->getWeakRefDirective())
+ OutStreamer->emitSymbolAttribute(Sym, MCSA_Global);
+ else if (GI.hasWeakLinkage() || GI.hasLinkOnceLinkage())
+ OutStreamer->emitSymbolAttribute(Sym, MCSA_WeakReference);
+ else
+ assert(GI.hasLocalLinkage() && "Invalid ifunc linkage");
+ };
+
+ MCSymbol *LazyPointer =
+ TM.getObjFileLowering()->getContext().getOrCreateSymbol(
+ "_" + GI.getName() + ".lazy_pointer");
+ MCSymbol *StubHelper =
+ TM.getObjFileLowering()->getContext().getOrCreateSymbol(
+ "_" + GI.getName() + ".stub_helper");
+
+ OutStreamer->switchSection(OutContext.getObjectFileInfo()->getDataSection());
+
+ EmitLinkage(LazyPointer);
+ OutStreamer->emitLabel(LazyPointer);
+ emitVisibility(LazyPointer, GI.getVisibility());
+ OutStreamer->emitValue(MCSymbolRefExpr::create(StubHelper, OutContext), 8);
+
+ OutStreamer->switchSection(OutContext.getObjectFileInfo()->getTextSection());
+
+ MCSymbol *Stub = getSymbol(&GI);
+
+ EmitLinkage(Stub);
+ OutStreamer->emitCodeAlignment(Align(4), STI);
+ OutStreamer->emitLabel(Stub);
+ emitVisibility(Stub, GI.getVisibility());
+
+ // adrp x16, lazy_pointer at GOTPAGE
+ // ldr x16, [x16, lazy_pointer at GOTPAGEOFF]
+ // ldr x16, [x16]
+ // br x16
+
+ {
+ MCInst Adrp;
+ Adrp.setOpcode(AArch64::ADRP);
+ Adrp.addOperand(MCOperand::createReg(AArch64::X16));
+ MCOperand SymPage;
+ MCInstLowering.lowerOperand(
+ MachineOperand::CreateES(LazyPointer->getName().data() + 1,
+ AArch64II::MO_GOT | AArch64II::MO_PAGE),
+ SymPage);
+ Adrp.addOperand(SymPage);
+ OutStreamer->emitInstruction(Adrp, *STI);
+ }
+
+ {
+ MCInst Ldr;
+ Ldr.setOpcode(AArch64::LDRXui);
+ Ldr.addOperand(MCOperand::createReg(AArch64::X16));
+ Ldr.addOperand(MCOperand::createReg(AArch64::X16));
+ MCOperand SymPageOff;
+ MCInstLowering.lowerOperand(
+ MachineOperand::CreateES(LazyPointer->getName().data() + 1,
+ AArch64II::MO_GOT | AArch64II::MO_PAGEOFF),
+ SymPageOff);
+ Ldr.addOperand(SymPageOff);
+ Ldr.addOperand(MCOperand::createImm(0));
+ OutStreamer->emitInstruction(Ldr, *STI);
+ }
+
+ OutStreamer->emitInstruction(MCInstBuilder(AArch64::LDRXui)
+ .addReg(AArch64::X16)
+ .addReg(AArch64::X16)
+ .addImm(0),
+ *STI);
+
+ OutStreamer->emitInstruction(MCInstBuilder(TM.getTargetTriple().isArm64e()
+ ? AArch64::BRAAZ
+ : AArch64::BR)
+ .addReg(AArch64::X16),
+ *STI);
+
+ EmitLinkage(StubHelper);
+ OutStreamer->emitCodeAlignment(Align(4), STI);
+ OutStreamer->emitLabel(StubHelper);
+ emitVisibility(StubHelper, GI.getVisibility());
+
+ // stp fp, lr, [sp, #-16]!
+ // mov fp, sp
+ // stp x1, x0, [sp, #-16]!
+ // stp x3, x2, [sp, #-16]!
+ // stp x5, x4, [sp, #-16]!
+ // stp x7, x6, [sp, #-16]!
+ // stp d1, d0, [sp, #-16]!
+ // stp d3, d2, [sp, #-16]!
+ // stp d5, d4, [sp, #-16]!
+ // stp d7, d6, [sp, #-16]!
----------------
jroelofs wrote:
Good point. This pretty closely matches what ld64 does, but if we can do better, we should.
https://github.com/llvm/llvm-project/pull/73686
More information about the llvm-commits
mailing list