https://github.com/VisualEhrmanntraut updated https://github.com/llvm/llvm-project/pull/204805
>From 9a186beceb883e86a54d5dfd05b4a823d3dc091f Mon Sep 17 00:00:00 2001 From: Visual <[email protected]> Date: Fri, 19 Jun 2026 15:25:37 +0300 Subject: [PATCH 1/7] 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 2d307f5ee6366..add8da0b1c7e9 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)) || @@ -4018,6 +4025,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 04f6a6d7e775e..31da2d0d5ef6c 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 c69ba506edd101e05235a758b91d891acadd1d00 Mon Sep 17 00:00:00 2001 From: Visual <[email protected]> Date: Tue, 23 Jun 2026 09:24:25 +0300 Subject: [PATCH 2/7] 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 1baa3a331999afe91b057dd8caa3cb07a11652c3 Mon Sep 17 00:00:00 2001 From: Visual <[email protected]> Date: Tue, 23 Jun 2026 10:11:22 +0300 Subject: [PATCH 3/7] 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 ee0a35016d222892033333195183b4b352939686 Mon Sep 17 00:00:00 2001 From: Visual <[email protected]> Date: Tue, 23 Jun 2026 12:01:27 +0300 Subject: [PATCH 4/7] 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. >From 68310b4429c915e831a66f3b429ddd089526152a Mon Sep 17 00:00:00 2001 From: Visual <[email protected]> Date: Tue, 23 Jun 2026 13:27:07 +0300 Subject: [PATCH 5/7] Uncomment dtor pass, the issue seems to have disappeared --- llvm/lib/CodeGen/TargetPassConfig.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/llvm/lib/CodeGen/TargetPassConfig.cpp b/llvm/lib/CodeGen/TargetPassConfig.cpp index 31da2d0d5ef6c..04f6a6d7e775e 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()); >From c6c14605c179ca65d9f07665d20985c62da7cf81 Mon Sep 17 00:00:00 2001 From: Visual <[email protected]> Date: Tue, 23 Jun 2026 17:49:25 +0300 Subject: [PATCH 6/7] Fix unintended behaviour changes --- lld/MachO/Writer.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp index 2cffde01a3b65..7567dec8521f1 100644 --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -848,9 +848,8 @@ template <class LP> void Writer::createLoadCommands() { if (config->emitChainedFixups) { in.header->addLoadCommand(make<LCChainedFixups>(in.chainedFixups)); - if (in.exports->isNeeded()) { + if (config->outputType != MH_KEXT_BUNDLE) 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)); @@ -1420,19 +1419,21 @@ void macho::createSyntheticSections() { in.getOrCreateCStringSection(section_names::objcMethname, /*forceDedupStrings=*/true)); in.wordLiteralSection = make<WordLiteralSection>(); - if (config->emitChainedFixups) { + if (config->emitChainedFixups) in.chainedFixups = make<ChainedFixupsSection>(); - } else if (config->outputType != MH_KEXT_BUNDLE) { + 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>(); + if (!config->emitChainedFixups) + in.localRelocs = make<LocalRelocSection>(); + } else { + in.exports = make<ExportSection>(); } in.got = make<GotSection>(); if (config->outputType != MH_KEXT_BUNDLE) { @@ -1444,9 +1445,8 @@ void macho::createSyntheticSections() { in.objCImageInfo = make<ObjCImageInfoSection>(); in.initOffsets = make<InitOffsetsSection>(); in.objcMethList = make<ObjCMethListSection>(); - if (config->outputType == MH_KEXT_BUNDLE) { + 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. >From dba2fa2eb226349bf747eec188fccbf2588f77cf Mon Sep 17 00:00:00 2001 From: Visual <[email protected]> Date: Tue, 23 Jun 2026 18:39:43 +0300 Subject: [PATCH 7/7] Fix unintended behaviour change in TextAPI --- llvm/include/llvm/TextAPI/FileTypes.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/llvm/include/llvm/TextAPI/FileTypes.h b/llvm/include/llvm/TextAPI/FileTypes.h index e0a41603fca20..b8d35130a39d7 100644 --- a/llvm/include/llvm/TextAPI/FileTypes.h +++ b/llvm/include/llvm/TextAPI/FileTypes.h @@ -25,23 +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 << 4, + TBD_V1 = 1U << 3, /// Text-based stub file (.tbd) version 2.0 - TBD_V2 = 1U << 5, + TBD_V2 = 1U << 4, /// Text-based stub file (.tbd) version 3.0 - TBD_V3 = 1U << 6, + TBD_V3 = 1U << 5, /// Text-based stub file (.tbd) version 4.0 - TBD_V4 = 1U << 7, + TBD_V4 = 1U << 6, /// Text-based stub file (.tbd) version 5.0 - TBD_V5 = 1U << 8, + TBD_V5 = 1U << 7, + + /// \brief MachO Kernel Extension Bundle file. + MachO_KextBundle = 1U << 8, All = ~0U, _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
