[clang] [lld] [llvm] [clang] [lld] Apple Kext support (PR #204805)
Visual Ehrmanntraut via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 23 02:01:43 PDT 2026
https://github.com/VisualEhrmanntraut updated https://github.com/llvm/llvm-project/pull/204805
>From 528a1425cb26cebc400a6deb32604b04da392805 Mon Sep 17 00:00:00 2001
From: Visual <affects-boson6g at icloud.com>
Date: Fri, 19 Jun 2026 15:25:37 +0300
Subject: [PATCH 1/4] Somewhat finished Apple Kext support
---
clang/lib/Driver/ToolChains/Darwin.cpp | 23 +++--
.../InstallAPI/DiagnosticBuilderWrappers.cpp | 3 +
lld/MachO/Driver.cpp | 9 +-
lld/MachO/InputSection.cpp | 8 ++
lld/MachO/InputSection.h | 2 +
lld/MachO/Options.td | 7 +-
lld/MachO/SyntheticSections.cpp | 98 +++++++++++++++----
lld/MachO/SyntheticSections.h | 45 +++++++++
lld/MachO/Writer.cpp | 48 +++++++--
llvm/include/llvm/TextAPI/FileTypes.h | 13 ++-
llvm/lib/CodeGen/TargetPassConfig.cpp | 10 +-
llvm/lib/TextAPI/BinaryReader/DylibReader.cpp | 3 +
12 files changed, 219 insertions(+), 50 deletions(-)
diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp
index 59744d1cb3e8c..ee970b3d3aa89 100644
--- a/clang/lib/Driver/ToolChains/Darwin.cpp
+++ b/clang/lib/Driver/ToolChains/Darwin.cpp
@@ -312,10 +312,6 @@ void darwin::Linker::AddLinkArgs(Compilation &C, const ArgList &Args,
// FIXME: Why do this only on this path?
Args.AddLastArg(CmdArgs, options::OPT_force__cpusubtype__ALL);
- Args.AddLastArg(CmdArgs, options::OPT_bundle);
- Args.AddAllArgs(CmdArgs, options::OPT_bundle__loader);
- Args.AddAllArgs(CmdArgs, options::OPT_client__name);
-
Arg *A;
if ((A = Args.getLastArg(options::OPT_compatibility__version)) ||
(A = Args.getLastArg(options::OPT_current__version)) ||
@@ -323,14 +319,25 @@ void darwin::Linker::AddLinkArgs(Compilation &C, const ArgList &Args,
D.Diag(diag::err_drv_argument_only_allowed_with) << A->getAsString(Args)
<< "-dynamiclib";
- Args.AddLastArg(CmdArgs, options::OPT_force__flat__namespace);
+ if ((A = Args.getLastArg(options::OPT_fapple_kext))) {
+ CmdArgs.push_back("-kext");
+ CmdArgs.push_back("-undefined");
+ CmdArgs.push_back("dynamic_lookup");
+ } else {
+ Args.AddLastArg(CmdArgs, options::OPT_bundle);
+ Args.AddAllArgs(CmdArgs, options::OPT_bundle__loader);
+ Args.AddLastArg(CmdArgs, options::OPT_force__flat__namespace);
+ Args.AddLastArg(CmdArgs, options::OPT_private__bundle);
+ }
+
+ Args.AddAllArgs(CmdArgs, options::OPT_client__name);
Args.AddLastArg(CmdArgs, options::OPT_keep__private__externs);
- Args.AddLastArg(CmdArgs, options::OPT_private__bundle);
} else {
CmdArgs.push_back("-dylib");
Arg *A;
- if ((A = Args.getLastArg(options::OPT_bundle)) ||
+ if ((A = Args.getLastArg(options::OPT_fapple_kext)) ||
+ (A = Args.getLastArg(options::OPT_bundle)) ||
(A = Args.getLastArg(options::OPT_bundle__loader)) ||
(A = Args.getLastArg(options::OPT_client__name)) ||
(A = Args.getLastArg(options::OPT_force__flat__namespace)) ||
@@ -4023,6 +4030,8 @@ void Darwin::addStartObjectFileArgs(const ArgList &Args,
// Derived from startfile spec.
if (Args.hasArg(options::OPT_dynamiclib))
addDynamicLibLinkArgs(*this, Args, CmdArgs);
+ else if (Args.hasArg(options::OPT_fapple_kext))
+ CmdArgs.push_back("-kext"); // TODO ?
else if (Args.hasArg(options::OPT_bundle))
addBundleLinkArgs(*this, Args, CmdArgs);
else if (Args.hasArg(options::OPT_pg) && SupportsProfiling())
diff --git a/clang/lib/InstallAPI/DiagnosticBuilderWrappers.cpp b/clang/lib/InstallAPI/DiagnosticBuilderWrappers.cpp
index 37b428216c91e..4e093392f5c34 100644
--- a/clang/lib/InstallAPI/DiagnosticBuilderWrappers.cpp
+++ b/clang/lib/InstallAPI/DiagnosticBuilderWrappers.cpp
@@ -57,6 +57,9 @@ const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
case FileType::MachO_Bundle:
DB.AddString("mach-o bundle");
return DB;
+ case FileType::MachO_KextBundle:
+ DB.AddString("mach-o kext bundle");
+ return DB;
case FileType::MachO_DynamicLibrary:
DB.AddString("mach-o dynamic library");
return DB;
diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp
index 2864c6d28fa49..9b821deba2922 100644
--- a/lld/MachO/Driver.cpp
+++ b/lld/MachO/Driver.cpp
@@ -72,11 +72,14 @@ std::unique_ptr<DependencyTracker> macho::depTracker;
static HeaderFileType getOutputType(const InputArgList &args) {
// TODO: -r, -dylinker, -preload...
- Arg *outputArg = args.getLastArg(OPT_bundle, OPT_dylib, OPT_execute);
+ Arg *outputArg =
+ args.getLastArg(OPT_kext, OPT_bundle, OPT_dylib, OPT_execute);
if (outputArg == nullptr)
return MH_EXECUTE;
switch (outputArg->getOption().getID()) {
+ case OPT_kext:
+ return MH_KEXT_BUNDLE;
case OPT_bundle:
return MH_BUNDLE;
case OPT_dylib:
@@ -1269,6 +1272,7 @@ static bool dataConstDefault(const InputArgList &args) {
switch (config->outputType) {
case MH_EXECUTE:
+ case MH_KEXT_BUNDLE:
return !(args.hasArg(OPT_no_pie) && supportsNoPie());
case MH_BUNDLE:
// FIXME: return false when -final_name ...
@@ -1888,7 +1892,8 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
pie = true;
}
- config->isPic = config->outputType == MH_DYLIB ||
+ config->isPic = config->outputType == MH_KEXT_BUNDLE ||
+ config->outputType == MH_DYLIB ||
config->outputType == MH_BUNDLE ||
(config->outputType == MH_EXECUTE && pie);
diff --git a/lld/MachO/InputSection.cpp b/lld/MachO/InputSection.cpp
index 4c4f644889d5f..2aa2836c4f3a4 100644
--- a/lld/MachO/InputSection.cpp
+++ b/lld/MachO/InputSection.cpp
@@ -247,6 +247,14 @@ void ConcatInputSection::writeTo(uint8_t *buf) {
if (target->hasAttr(r.type, RelocAttrBits::LOAD) &&
!referentSym->isInGot())
target->relaxGotLoad(loc, r.type);
+ if (config->outputType == MH_KEXT_BUNDLE && !needsFixup &&
+ needsBinding(referentSym)) {
+ if (target->hasAttr(r.type, RelocAttrBits::BRANCH)) {
+ continue;
+ }
+ if (!referentSym->isInGot())
+ continue;
+ }
// For dtrace symbols, do not handle them as normal undefined symbols
if (referentSym->getName().starts_with("___dtrace_")) {
// Change dtrace call site to pre-defined instructions
diff --git a/lld/MachO/InputSection.h b/lld/MachO/InputSection.h
index 8fcd16a1de35f..27b96a19b6d96 100644
--- a/lld/MachO/InputSection.h
+++ b/lld/MachO/InputSection.h
@@ -381,6 +381,8 @@ constexpr const char unwindInfo[] = "__unwind_info";
constexpr const char weakBinding[] = "__weak_binding";
constexpr const char zeroFill[] = "__zerofill";
constexpr const char addrSig[] = "__llvm_addrsig";
+constexpr const char extRelocs[] = "__ext_relocs";
+constexpr const char localRelocs[] = "__local_relocs";
} // namespace section_names
diff --git a/lld/MachO/Options.td b/lld/MachO/Options.td
index b7686d66a258e..99ad8436f0d44 100644
--- a/lld/MachO/Options.td
+++ b/lld/MachO/Options.td
@@ -238,6 +238,9 @@ def dylib : Flag<["-"], "dylib">,
def bundle : Flag<["-"], "bundle">,
HelpText<"Produce a bundle">,
Group<grp_kind>;
+def kext : Flag<["-"], "kext">,
+ HelpText<"Produce a kext bundle">,
+ Group<grp_kind>;
def r : Flag<["-"], "r">,
HelpText<"Merge multiple object files into one, retaining relocations">,
Flags<[HelpHidden]>,
@@ -1426,10 +1429,6 @@ def no_keep_dwarf_unwind : Flag<["-"], "no_keep_dwarf_unwind">,
HelpText<"This option is undocumented in ld64">,
Flags<[HelpHidden]>,
Group<grp_undocumented>;
-def kext : Flag<["-"], "kext">,
- HelpText<"This option is undocumented in ld64">,
- Flags<[HelpHidden]>,
- Group<grp_undocumented>;
def kext_objects_dir : Flag<["-"], "kext_objects_dir">,
HelpText<"This option is undocumented in ld64">,
Flags<[HelpHidden]>,
diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp
index ba06a95bb753c..29fec8b1ced7f 100644
--- a/lld/MachO/SyntheticSections.cpp
+++ b/lld/MachO/SyntheticSections.cpp
@@ -106,12 +106,14 @@ static uint32_t cpuSubtype() {
static bool hasWeakBinding() {
return config->emitChainedFixups ? in.chainedFixups->hasWeakBinding()
- : in.weakBinding->hasEntry();
+ : config->outputType != MH_KEXT_BUNDLE &&
+ in.weakBinding->hasEntry();
}
static bool hasNonWeakDefinition() {
return config->emitChainedFixups ? in.chainedFixups->hasNonWeakDefinition()
- : in.weakBinding->hasNonWeakDefinition();
+ : config->outputType != MH_KEXT_BUNDLE &&
+ in.weakBinding->hasNonWeakDefinition();
}
void MachHeaderSection::writeTo(uint8_t *buf) const {
@@ -317,6 +319,18 @@ void macho::addNonLazyBindingEntries(const Symbol *sym,
return;
}
+ if (config->outputType == MH_KEXT_BUNDLE) {
+ if (needsBinding(sym))
+ in.extRelocs->addEntry(sym, isec, offset, target->unsignedRelocType,
+ /*pcrel=*/false, target->p2WordSize);
+ else if (isa<Defined>(sym))
+ in.localRelocs->addEntry(sym, isec, offset, target->unsignedRelocType,
+ /*pcrel=*/false, target->p2WordSize);
+ else
+ llvm_unreachable("cannot bind to an undefined symbol");
+ return;
+ }
+
if (const auto *dysym = dyn_cast<DylibSymbol>(sym)) {
in.binding->addEntry(dysym, isec, offset, addend);
if (dysym->isWeakDef())
@@ -758,7 +772,7 @@ void StubsSection::addEntry(Symbol *sym) {
if (inserted) {
sym->stubsIndex = entries.size() - 1;
- if (config->emitChainedFixups)
+ if (config->emitChainedFixups || config->outputType == MH_KEXT_BUNDLE)
in.got->addEntry(sym);
else
addBindingsForStub(sym);
@@ -816,6 +830,40 @@ void StubHelperSection::setUp() {
dyldPrivate->used = true;
}
+RelocSection::RelocSection(const char *name)
+ : LinkEditSection(segment_names::linkEdit, name) {}
+
+void RelocSection::writeTo(uint8_t *buf) const {
+ for (const Entry &e : entries) {
+ write32le(buf, e.isec->getVA(e.offset));
+
+ const bool ext = this->isExternal();
+ uint32_t symOrSectNum;
+ if (ext)
+ symOrSectNum = e.sym->symtabIndex;
+ else {
+ const auto *def = dyn_cast_or_null<Defined>(e.sym);
+ const InputSection *targetIsec = def ? def->isec() : e.isec;
+ symOrSectNum = targetIsec->parent->index;
+ }
+
+ write32le(buf + sizeof(uint32_t),
+ (symOrSectNum & 0x00ffffffu) |
+ (static_cast<uint32_t>(e.pcrel) << 24) |
+ (static_cast<uint32_t>(e.length) << 25) |
+ (static_cast<uint32_t>(ext) << 27) |
+ (static_cast<uint32_t>(e.type) << 28));
+
+ buf += sizeof(uint32_t) * 2;
+ }
+}
+
+ExternalRelocSection::ExternalRelocSection()
+ : RelocSection(section_names::extRelocs) {}
+
+LocalRelocSection::LocalRelocSection()
+ : RelocSection(section_names::localRelocs) {}
+
llvm::DenseMap<llvm::CachedHashStringRef, ConcatInputSection *>
ObjCSelRefsHelper::methnameToSelref;
void ObjCSelRefsHelper::initialize() {
@@ -1479,28 +1527,31 @@ IndirectSymtabSection::IndirectSymtabSection()
section_names::indirectSymbolTable) {}
uint32_t IndirectSymtabSection::getNumSymbols() const {
- uint32_t size = in.got->getEntries().size() +
- in.tlvPointers->getEntries().size() +
- in.stubs->getEntries().size();
- if (!config->emitChainedFixups)
- size += in.stubs->getEntries().size();
+ uint32_t size = in.got->getEntries().size();
+ if (config->outputType != MH_KEXT_BUNDLE)
+ size += in.tlvPointers->getEntries().size() +
+ in.stubs->getEntries().size() * (config->emitChainedFixups ? 1 : 2);
return size;
}
bool IndirectSymtabSection::isNeeded() const {
- return in.got->isNeeded() || in.tlvPointers->isNeeded() ||
- in.stubs->isNeeded();
+ return in.got->isNeeded() || (in.tlvPointers && in.tlvPointers->isNeeded()) ||
+ (in.stubs && in.stubs->isNeeded());
}
void IndirectSymtabSection::finalizeContents() {
uint32_t off = 0;
in.got->reserved1 = off;
off += in.got->getEntries().size();
- in.tlvPointers->reserved1 = off;
- off += in.tlvPointers->getEntries().size();
- in.stubs->reserved1 = off;
- if (in.lazyPointers) {
+ if (in.tlvPointers) {
+ in.tlvPointers->reserved1 = off;
+ off += in.tlvPointers->getEntries().size();
+ }
+ if (in.stubs) {
+ in.stubs->reserved1 = off;
off += in.stubs->getEntries().size();
+ }
+ if (in.lazyPointers) {
in.lazyPointers->reserved1 = off;
}
}
@@ -1517,13 +1568,17 @@ void IndirectSymtabSection::writeTo(uint8_t *buf) const {
write32le(buf + off * sizeof(uint32_t), indirectValue(sym));
++off;
}
- for (const Symbol *sym : in.tlvPointers->getEntries()) {
- write32le(buf + off * sizeof(uint32_t), indirectValue(sym));
- ++off;
+ if (in.tlvPointers) {
+ for (const Symbol *sym : in.tlvPointers->getEntries()) {
+ write32le(buf + off * sizeof(uint32_t), indirectValue(sym));
+ ++off;
+ }
}
- for (const Symbol *sym : in.stubs->getEntries()) {
- write32le(buf + off * sizeof(uint32_t), indirectValue(sym));
- ++off;
+ if (in.stubs) {
+ for (const Symbol *sym : in.stubs->getEntries()) {
+ write32le(buf + off * sizeof(uint32_t), indirectValue(sym));
+ ++off;
+ }
}
if (in.lazyPointers) {
@@ -2321,6 +2376,9 @@ void macho::createSyntheticSymbols() {
// The following symbols are N_SECT symbols, even though the header is not
// part of any section and that they are private to the bundle/dylib/object
// they are part of.
+ case MH_KEXT_BUNDLE:
+ addHeaderSymbol("__mh_kext_bundle_header");
+ break;
case MH_BUNDLE:
addHeaderSymbol("__mh_bundle_header");
break;
diff --git a/lld/MachO/SyntheticSections.h b/lld/MachO/SyntheticSections.h
index e649d1275f821..504fa14e9d8b3 100644
--- a/lld/MachO/SyntheticSections.h
+++ b/lld/MachO/SyntheticSections.h
@@ -300,6 +300,49 @@ class StubsSection final : public SyntheticSection {
llvm::SetVector<Symbol *> entries;
};
+class RelocSection : public LinkEditSection {
+public:
+ RelocSection(const char *name);
+ bool isNeeded() const override { return !entries.empty(); }
+ uint64_t getRawSize() const override {
+ return entries.size() * (sizeof(uint32_t) * 2);
+ }
+ void addEntry(const Symbol *sym, const InputSection *isec, uint32_t offset,
+ uint8_t type, bool pcrel, uint8_t length) {
+ entries.emplace_back(sym, isec, offset, type, pcrel, length);
+ }
+ void writeTo(uint8_t *buf) const override;
+
+ virtual bool isExternal() const = 0;
+
+ struct Entry {
+ const Symbol *sym;
+ const InputSection *isec;
+ uint32_t offset;
+ uint8_t type;
+ bool pcrel;
+ uint8_t length;
+
+ Entry(const Symbol *sym, const InputSection *isec, uint32_t offset,
+ uint8_t type, bool pcrel, uint8_t length)
+ : sym(sym), isec(isec), offset(offset), type(type), pcrel(pcrel),
+ length(length) {}
+ };
+ std::vector<Entry> entries;
+};
+
+class ExternalRelocSection final : public RelocSection {
+public:
+ ExternalRelocSection();
+ bool isExternal() const override { return true; }
+};
+
+class LocalRelocSection final : public RelocSection {
+public:
+ LocalRelocSection();
+ bool isExternal() const override { return false; }
+};
+
class StubHelperSection final : public SyntheticSection {
public:
StubHelperSection();
@@ -850,6 +893,8 @@ struct InStruct {
InitOffsetsSection *initOffsets = nullptr;
ObjCMethListSection *objcMethList = nullptr;
ChainedFixupsSection *chainedFixups = nullptr;
+ ExternalRelocSection *extRelocs = nullptr;
+ LocalRelocSection *localRelocs = nullptr;
CStringSection *getOrCreateCStringSection(StringRef name,
bool forceDedupStrings = false) {
diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp
index 89b6d467d0d44..936dea8658de6 100644
--- a/lld/MachO/Writer.cpp
+++ b/lld/MachO/Writer.cpp
@@ -212,6 +212,16 @@ class LCDysymtab final : public LoadCommand {
c->indirectsymoff = indirectSymtabSection->fileOff;
c->nindirectsyms = indirectSymtabSection->getNumSymbols();
+
+ // For kext bundles
+ if (in.extRelocs && in.extRelocs->isNeeded()) {
+ c->extreloff = in.extRelocs->fileOff;
+ c->nextrel = in.extRelocs->entries.size();
+ }
+ if (in.localRelocs && in.localRelocs->isNeeded()) {
+ c->locreloff = in.localRelocs->fileOff;
+ c->nlocrel = in.localRelocs->entries.size();
+ }
}
SymtabSection *symtabSection;
@@ -678,12 +688,23 @@ static void prepareSymbolRelocation(Symbol *sym, const InputSection *isec,
const RelocAttrs &relocAttrs = target->getRelocAttrs(r.type);
if (relocAttrs.hasAttr(RelocAttrBits::BRANCH)) {
- if (needsBinding(sym))
- in.stubs->addEntry(sym);
+ if (needsBinding(sym)) {
+ if (config->outputType == MH_KEXT_BUNDLE) {
+ in.extRelocs->addEntry(sym, isec, r.offset, r.type, r.pcrel, r.length);
+ } else {
+ in.stubs->addEntry(sym);
+ }
+ }
} else if (relocAttrs.hasAttr(RelocAttrBits::GOT)) {
if (relocAttrs.hasAttr(RelocAttrBits::POINTER) || needsBinding(sym))
in.got->addEntry(sym);
} else if (relocAttrs.hasAttr(RelocAttrBits::TLV)) {
+ if (config->outputType == MH_KEXT_BUNDLE) {
+ fatal(isec->getLocation(r.offset) +
+ ": TLV reference to external symbol " + sym->getName() +
+ " is not supported in kext bundles");
+ return;
+ }
if (needsBinding(sym))
in.tlvPointers->addEntry(sym);
} else if (relocAttrs.hasAttr(RelocAttrBits::UNSIGNED)) {
@@ -735,6 +756,9 @@ void Writer::scanRelocations() {
if (!r.pcrel) {
if (config->emitChainedFixups)
in.chainedFixups->addRebase(isec, r.offset);
+ else if (config->outputType == MH_KEXT_BUNDLE)
+ in.localRelocs->addEntry(sym, isec, r.offset, r.type, false,
+ r.length);
else
in.rebase->addEntry(isec, r.offset);
}
@@ -824,8 +848,10 @@ template <class LP> void Writer::createLoadCommands() {
if (config->emitChainedFixups) {
in.header->addLoadCommand(make<LCChainedFixups>(in.chainedFixups));
- in.header->addLoadCommand(make<LCExportsTrie>(in.exports));
- } else {
+ if (in.exports->isNeeded()) {
+ in.header->addLoadCommand(make<LCExportsTrie>(in.exports));
+ }
+ } else if (config->outputType != MH_KEXT_BUNDLE) {
in.header->addLoadCommand(make<LCDyldInfo>(
in.rebase, in.binding, in.weakBinding, in.lazyBinding, in.exports));
}
@@ -851,6 +877,7 @@ template <class LP> void Writer::createLoadCommands() {
in.header->addLoadCommand(make<LCSubClient>(client));
break;
case MH_BUNDLE:
+ case MH_KEXT_BUNDLE:
break;
default:
llvm_unreachable("unhandled output file type");
@@ -1042,6 +1069,7 @@ template <class LP> void Writer::createOutputSections() {
break;
case MH_DYLIB:
case MH_BUNDLE:
+ case MH_KEXT_BUNDLE:
break;
default:
llvm_unreachable("unhandled output file type");
@@ -1147,10 +1175,11 @@ void Writer::finalizeAddresses() {
void Writer::finalizeLinkEditSegment() {
TimeTraceScope timeScope("Finalize __LINKEDIT segment");
// Fill __LINKEDIT contents.
- std::array<LinkEditSection *, 10> linkEditSections{
+ std::array<LinkEditSection *, 12> linkEditSections{
in.rebase, in.binding,
in.weakBinding, in.lazyBinding,
in.exports, in.chainedFixups,
+ in.extRelocs, in.localRelocs,
symtabSection, indirectSymtabSection,
dataInCodeSection, functionStartsSection,
};
@@ -1389,6 +1418,9 @@ void macho::createSyntheticSections() {
in.wordLiteralSection = make<WordLiteralSection>();
if (config->emitChainedFixups) {
in.chainedFixups = make<ChainedFixupsSection>();
+ } else if (config->outputType == MH_KEXT_BUNDLE) {
+ in.extRelocs = make<ExternalRelocSection>();
+ in.localRelocs = make<LocalRelocSection>();
} else {
in.rebase = make<RebaseSection>();
in.binding = make<BindingSection>();
@@ -1399,8 +1431,10 @@ void macho::createSyntheticSections() {
}
in.exports = make<ExportSection>();
in.got = make<GotSection>();
- in.tlvPointers = make<TlvPointerSection>();
- in.stubs = make<StubsSection>();
+ if (config->outputType != MH_KEXT_BUNDLE) {
+ in.tlvPointers = make<TlvPointerSection>();
+ in.stubs = make<StubsSection>();
+ }
in.objcStubs = make<ObjCStubsSection>();
in.unwindInfo = makeUnwindInfoSection();
in.objCImageInfo = make<ObjCImageInfoSection>();
diff --git a/llvm/include/llvm/TextAPI/FileTypes.h b/llvm/include/llvm/TextAPI/FileTypes.h
index 5876e9d5a5304..e0a41603fca20 100644
--- a/llvm/include/llvm/TextAPI/FileTypes.h
+++ b/llvm/include/llvm/TextAPI/FileTypes.h
@@ -25,20 +25,23 @@ enum FileType : unsigned {
/// \brief MachO Bundle file.
MachO_Bundle = 1U << 2,
+ /// \brief MachO Kext Bundle file.
+ MachO_KextBundle = 1U << 3,
+
/// Text-based stub file (.tbd) version 1.0
- TBD_V1 = 1U << 3,
+ TBD_V1 = 1U << 4,
/// Text-based stub file (.tbd) version 2.0
- TBD_V2 = 1U << 4,
+ TBD_V2 = 1U << 5,
/// Text-based stub file (.tbd) version 3.0
- TBD_V3 = 1U << 5,
+ TBD_V3 = 1U << 6,
/// Text-based stub file (.tbd) version 4.0
- TBD_V4 = 1U << 6,
+ TBD_V4 = 1U << 7,
/// Text-based stub file (.tbd) version 5.0
- TBD_V5 = 1U << 7,
+ TBD_V5 = 1U << 8,
All = ~0U,
diff --git a/llvm/lib/CodeGen/TargetPassConfig.cpp b/llvm/lib/CodeGen/TargetPassConfig.cpp
index 4a76aba55b78b..13fdfc7de5f6d 100644
--- a/llvm/lib/CodeGen/TargetPassConfig.cpp
+++ b/llvm/lib/CodeGen/TargetPassConfig.cpp
@@ -872,11 +872,11 @@ void TargetPassConfig::addIRPasses() {
addPass(&GCLoweringID);
addPass(&ShadowStackGCLoweringID);
- // For MachO, lower @llvm.global_dtors into @llvm.global_ctors with
- // __cxa_atexit() calls to avoid emitting the deprecated __mod_term_func.
- if (TM->getTargetTriple().isOSBinFormatMachO() &&
- !DisableAtExitBasedGlobalDtorLowering)
- addPass(createLowerGlobalDtorsLegacyPass());
+ // // For MachO, lower @llvm.global_dtors into @llvm.global_ctors with
+ // // __cxa_atexit() calls to avoid emitting the deprecated __mod_term_func.
+ // if (TM->getTargetTriple().isOSBinFormatMachO() &&
+ // !DisableAtExitBasedGlobalDtorLowering)
+ // addPass(createLowerGlobalDtorsLegacyPass());
// Make sure that no unreachable blocks are instruction selected.
addPass(createUnreachableBlockEliminationPass());
diff --git a/llvm/lib/TextAPI/BinaryReader/DylibReader.cpp b/llvm/lib/TextAPI/BinaryReader/DylibReader.cpp
index f55bc9c1a28c2..b072f6349e317 100644
--- a/llvm/lib/TextAPI/BinaryReader/DylibReader.cpp
+++ b/llvm/lib/TextAPI/BinaryReader/DylibReader.cpp
@@ -157,6 +157,9 @@ static Error readMachOHeader(MachOObjectFile *Obj, RecordsSlice &Slice) {
case MachO::MH_BUNDLE:
BA.File = FileType::MachO_Bundle;
break;
+ case MachO::MH_KEXT_BUNDLE:
+ BA.File = FileType::MachO_KextBundle;
+ break;
}
if (H.flags & MachO::MH_TWOLEVEL)
>From f271660688448e60234a145adced07afd64b1b95 Mon Sep 17 00:00:00 2001
From: Visual <affects-boson6g at icloud.com>
Date: Tue, 23 Jun 2026 09:24:25 +0300
Subject: [PATCH 2/4] shouldEmitChainedFixups should always return false on
x86_64 for kexts
---
lld/MachO/Driver.cpp | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp
index 9b821deba2922..23f9b73a6079d 100644
--- a/lld/MachO/Driver.cpp
+++ b/lld/MachO/Driver.cpp
@@ -1310,6 +1310,14 @@ static bool shouldEmitChainedFixups(const InputArgList &args) {
return false;
}
+ if (args.hasArg(OPT_kext) &&
+ is_contained({AK_x86_64, AK_x86_64h}, config->arch())) {
+ if (requested)
+ error("-fixup_chains with -kext is only supported on arm64 targets");
+
+ return false;
+ }
+
if (args.hasArg(OPT_preload)) {
if (requested)
error("-fixup_chains is incompatible with -preload");
>From 760107b67adfa888987177a9be39ba3cf9a032e7 Mon Sep 17 00:00:00 2001
From: Visual <affects-boson6g at icloud.com>
Date: Tue, 23 Jun 2026 10:11:22 +0300
Subject: [PATCH 3/4] Use finalizeContents for RelocSection
---
lld/MachO/SyntheticSections.cpp | 39 +++++++++++++++++++++++----------
lld/MachO/SyntheticSections.h | 13 +++++------
lld/MachO/Writer.cpp | 8 +++++--
3 files changed, 40 insertions(+), 20 deletions(-)
diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp
index 29fec8b1ced7f..c64b32081bee9 100644
--- a/lld/MachO/SyntheticSections.cpp
+++ b/lld/MachO/SyntheticSections.cpp
@@ -833,13 +833,27 @@ void StubHelperSection::setUp() {
RelocSection::RelocSection(const char *name)
: LinkEditSection(segment_names::linkEdit, name) {}
-void RelocSection::writeTo(uint8_t *buf) const {
+void RelocSection::addEntry(const Symbol *sym, const InputSection *isec,
+ uint32_t offset, uint8_t type, bool pcrel,
+ uint8_t length) {
+ assert(!this->isFinal && "RelocSection entry added after finalizeContents");
+ this->entries.emplace_back(sym, isec, offset, type, pcrel, length);
+}
+
+void RelocSection::finalizeContents() {
+ assert(!this->isFinal && "RelocSection finalized twice");
+ this->isFinal = true;
+
+ const bool isExternal = this->isExternal();
+
+ raw_svector_ostream os{contents};
for (const Entry &e : entries) {
+ char buf[sizeof(uint32_t)];
write32le(buf, e.isec->getVA(e.offset));
+ os.write(buf, sizeof(buf));
- const bool ext = this->isExternal();
uint32_t symOrSectNum;
- if (ext)
+ if (isExternal)
symOrSectNum = e.sym->symtabIndex;
else {
const auto *def = dyn_cast_or_null<Defined>(e.sym);
@@ -847,17 +861,20 @@ void RelocSection::writeTo(uint8_t *buf) const {
symOrSectNum = targetIsec->parent->index;
}
- write32le(buf + sizeof(uint32_t),
- (symOrSectNum & 0x00ffffffu) |
- (static_cast<uint32_t>(e.pcrel) << 24) |
- (static_cast<uint32_t>(e.length) << 25) |
- (static_cast<uint32_t>(ext) << 27) |
- (static_cast<uint32_t>(e.type) << 28));
-
- buf += sizeof(uint32_t) * 2;
+ write32le(buf, (symOrSectNum & 0x00ffffffu) |
+ (static_cast<uint32_t>(e.pcrel) << 24) |
+ (static_cast<uint32_t>(e.length) << 25) |
+ (static_cast<uint32_t>(isExternal) << 27) |
+ (static_cast<uint32_t>(e.type) << 28));
+ os.write(buf, sizeof(buf));
}
}
+void RelocSection::writeTo(uint8_t *buf) const {
+ assert(this->isFinal && "RelocSection contents written before finalization");
+ memcpy(buf, contents.data(), contents.size());
+}
+
ExternalRelocSection::ExternalRelocSection()
: RelocSection(section_names::extRelocs) {}
diff --git a/lld/MachO/SyntheticSections.h b/lld/MachO/SyntheticSections.h
index 504fa14e9d8b3..1727fbd721020 100644
--- a/lld/MachO/SyntheticSections.h
+++ b/lld/MachO/SyntheticSections.h
@@ -303,14 +303,11 @@ class StubsSection final : public SyntheticSection {
class RelocSection : public LinkEditSection {
public:
RelocSection(const char *name);
- bool isNeeded() const override { return !entries.empty(); }
- uint64_t getRawSize() const override {
- return entries.size() * (sizeof(uint32_t) * 2);
- }
void addEntry(const Symbol *sym, const InputSection *isec, uint32_t offset,
- uint8_t type, bool pcrel, uint8_t length) {
- entries.emplace_back(sym, isec, offset, type, pcrel, length);
- }
+ uint8_t type, bool pcrel, uint8_t length);
+ bool isNeeded() const override { return !entries.empty(); }
+ void finalizeContents() override;
+ uint64_t getRawSize() const override { return contents.size(); }
void writeTo(uint8_t *buf) const override;
virtual bool isExternal() const = 0;
@@ -328,7 +325,9 @@ class RelocSection : public LinkEditSection {
: sym(sym), isec(isec), offset(offset), type(type), pcrel(pcrel),
length(length) {}
};
+ bool isFinal = false;
std::vector<Entry> entries;
+ SmallVector<char, 128> contents;
};
class ExternalRelocSection final : public RelocSection {
diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp
index 936dea8658de6..4476f3a768416 100644
--- a/lld/MachO/Writer.cpp
+++ b/lld/MachO/Writer.cpp
@@ -1175,11 +1175,10 @@ void Writer::finalizeAddresses() {
void Writer::finalizeLinkEditSegment() {
TimeTraceScope timeScope("Finalize __LINKEDIT segment");
// Fill __LINKEDIT contents.
- std::array<LinkEditSection *, 12> linkEditSections{
+ std::array<LinkEditSection *, 10> linkEditSections{
in.rebase, in.binding,
in.weakBinding, in.lazyBinding,
in.exports, in.chainedFixups,
- in.extRelocs, in.localRelocs,
symtabSection, indirectSymtabSection,
dataInCodeSection, functionStartsSection,
};
@@ -1190,6 +1189,11 @@ void Writer::finalizeLinkEditSegment() {
osec->finalizeContents();
});
+ if (in.extRelocs)
+ in.extRelocs->finalizeContents();
+ if (in.localRelocs)
+ in.localRelocs->finalizeContents();
+
// Now that __LINKEDIT is filled out, do a proper calculation of its
// addresses and offsets.
linkEditSegment->addr = addr;
>From 16433bd598d0824113393adf0b0ff352e74ecb6c Mon Sep 17 00:00:00 2001
From: Visual <affects-boson6g at icloud.com>
Date: Tue, 23 Jun 2026 12:01:27 +0300
Subject: [PATCH 4/4] Fix section order
Before:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/strip: fatal error: file not in an order that can be processed (local relocation entries out of place): Test.kext/Contents/MacOS/Test
---
lld/MachO/OutputSegment.cpp | 20 +++++++++++---------
lld/MachO/SyntheticSections.cpp | 4 ++--
lld/MachO/Writer.cpp | 13 ++++++++-----
3 files changed, 21 insertions(+), 16 deletions(-)
diff --git a/lld/MachO/OutputSegment.cpp b/lld/MachO/OutputSegment.cpp
index 5824b5c7772b3..d705a0e3994d2 100644
--- a/lld/MachO/OutputSegment.cpp
+++ b/lld/MachO/OutputSegment.cpp
@@ -155,15 +155,17 @@ static int sectionOrder(OutputSection *osec) {
}
} else if (segname == segment_names::linkEdit) {
return StringSwitch<int>(osec->name)
- .Case(section_names::chainFixups, -11)
- .Case(section_names::rebase, -10)
- .Case(section_names::binding, -9)
- .Case(section_names::weakBinding, -8)
- .Case(section_names::lazyBinding, -7)
- .Case(section_names::export_, -6)
- .Case(section_names::functionStarts, -5)
- .Case(section_names::dataInCode, -4)
- .Case(section_names::symbolTable, -3)
+ .Case(section_names::chainFixups, -13)
+ .Case(section_names::rebase, -12)
+ .Case(section_names::binding, -11)
+ .Case(section_names::weakBinding, -10)
+ .Case(section_names::lazyBinding, -9)
+ .Case(section_names::export_, -8)
+ .Case(section_names::localRelocs, -7)
+ .Case(section_names::functionStarts, -6)
+ .Case(section_names::dataInCode, -5)
+ .Case(section_names::symbolTable, -4)
+ .Case(section_names::extRelocs, -3)
.Case(section_names::indirectSymbolTable, -2)
.Case(section_names::stringTable, -1)
.Case(section_names::codeSignature, std::numeric_limits<int>::max())
diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp
index c64b32081bee9..4d16c6011749b 100644
--- a/lld/MachO/SyntheticSections.cpp
+++ b/lld/MachO/SyntheticSections.cpp
@@ -141,10 +141,10 @@ void MachHeaderSection::writeTo(uint8_t *buf) const {
if (config->outputType == MH_DYLIB && config->applicationExtension)
hdr->flags |= MH_APP_EXTENSION_SAFE;
- if (in.exports->hasWeakSymbol || hasNonWeakDefinition())
+ if (in.exports && (in.exports->hasWeakSymbol || hasNonWeakDefinition()))
hdr->flags |= MH_WEAK_DEFINES;
- if (in.exports->hasWeakSymbol || hasWeakBinding())
+ if (in.exports && (in.exports->hasWeakSymbol || hasWeakBinding()))
hdr->flags |= MH_BINDS_TO_WEAK;
for (const OutputSegment *seg : outputSegments) {
diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp
index 4476f3a768416..2cffde01a3b65 100644
--- a/lld/MachO/Writer.cpp
+++ b/lld/MachO/Writer.cpp
@@ -1422,18 +1422,18 @@ void macho::createSyntheticSections() {
in.wordLiteralSection = make<WordLiteralSection>();
if (config->emitChainedFixups) {
in.chainedFixups = make<ChainedFixupsSection>();
- } else if (config->outputType == MH_KEXT_BUNDLE) {
- in.extRelocs = make<ExternalRelocSection>();
- in.localRelocs = make<LocalRelocSection>();
- } else {
+ } else if (config->outputType != MH_KEXT_BUNDLE) {
in.rebase = make<RebaseSection>();
in.binding = make<BindingSection>();
in.weakBinding = make<WeakBindingSection>();
in.lazyBinding = make<LazyBindingSection>();
in.lazyPointers = make<LazyPointerSection>();
in.stubHelper = make<StubHelperSection>();
+ in.exports = make<ExportSection>();
+ }
+ if (config->outputType == MH_KEXT_BUNDLE) {
+ in.localRelocs = make<LocalRelocSection>();
}
- in.exports = make<ExportSection>();
in.got = make<GotSection>();
if (config->outputType != MH_KEXT_BUNDLE) {
in.tlvPointers = make<TlvPointerSection>();
@@ -1444,6 +1444,9 @@ void macho::createSyntheticSections() {
in.objCImageInfo = make<ObjCImageInfoSection>();
in.initOffsets = make<InitOffsetsSection>();
in.objcMethList = make<ObjCMethListSection>();
+ if (config->outputType == MH_KEXT_BUNDLE) {
+ in.extRelocs = make<ExternalRelocSection>();
+ }
// This section contains space for just a single word, and will be used by
// dyld to cache an address to the image loader it uses.
More information about the cfe-commits
mailing list