https://github.com/VisualEhrmanntraut updated https://github.com/llvm/llvm-project/pull/204805
>From d575e43643f7c90a1a2728352a24867d7105d9e9 Mon Sep 17 00:00:00 2001 From: Visual <[email protected]> Date: Sat, 10 Oct 2026 19:19:40 +0300 Subject: [PATCH] [lld-macho][clang][llvm-objcopy] Add x86_64 kext bundle support --- clang/lib/Driver/ToolChains/Darwin.cpp | 21 ++- .../InstallAPI/DiagnosticBuilderWrappers.cpp | 3 + clang/test/Driver/darwin-kext.c | 21 +++ lld/MachO/Driver.cpp | 44 ++++- lld/MachO/InputSection.cpp | 6 + lld/MachO/InputSection.h | 3 +- lld/MachO/Options.td | 7 +- lld/MachO/OutputSegment.cpp | 20 ++- lld/MachO/Symbols.h | 6 +- lld/MachO/SyntheticSections.cpp | 102 +++++++++-- lld/MachO/SyntheticSections.h | 47 +++++ lld/MachO/Writer.cpp | 56 +++++- lld/test/MachO/kext.s | 156 ++++++++++++++++ llvm/include/llvm/TextAPI/FileTypes.h | 3 + llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp | 28 ++- llvm/lib/ObjCopy/MachO/MachOObjcopy.cpp | 10 ++ llvm/lib/ObjCopy/MachO/MachOObject.cpp | 15 ++ llvm/lib/ObjCopy/MachO/MachOObject.h | 4 + llvm/lib/ObjCopy/MachO/MachOReader.cpp | 63 +++++-- llvm/lib/ObjCopy/MachO/MachOReader.h | 1 + llvm/lib/ObjCopy/MachO/MachOWriter.cpp | 69 +++++-- llvm/lib/ObjCopy/MachO/MachOWriter.h | 3 + llvm/lib/TextAPI/BinaryReader/DylibReader.cpp | 3 + .../llvm-objcopy/MachO/kext-relocations.test | 170 ++++++++++++++++++ 24 files changed, 783 insertions(+), 78 deletions(-) create mode 100644 clang/test/Driver/darwin-kext.c create mode 100644 lld/test/MachO/kext.s create mode 100644 llvm/test/tools/llvm-objcopy/MachO/kext-relocations.test diff --git a/clang/lib/Driver/ToolChains/Darwin.cpp b/clang/lib/Driver/ToolChains/Darwin.cpp index 0fb94d8712e69..67027edae1457 100644 --- a/clang/lib/Driver/ToolChains/Darwin.cpp +++ b/clang/lib/Driver/ToolChains/Darwin.cpp @@ -345,8 +345,13 @@ 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); + const bool IsKext = Args.hasArg(options::OPT_fapple_kext); + if (IsKext) { + CmdArgs.push_back("-kext"); + } else { + Args.AddLastArg(CmdArgs, options::OPT_bundle); + Args.AddAllArgs(CmdArgs, options::OPT_bundle__loader); + } Args.AddAllArgs(CmdArgs, options::OPT_client__name); Arg *A; @@ -356,14 +361,17 @@ 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 (!IsKext) + Args.AddLastArg(CmdArgs, options::OPT_force__flat__namespace); Args.AddLastArg(CmdArgs, options::OPT_keep__private__externs); - Args.AddLastArg(CmdArgs, options::OPT_private__bundle); + if (!IsKext) + 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)) || @@ -4084,6 +4092,9 @@ void Darwin::addStartObjectFileArgs(const ArgList &Args, if (isTargetFirmware()) return MachO::addStartObjectFileArgs(Args, CmdArgs); + if (Args.hasArg(options::OPT_fapple_kext)) + return; + // Derived from startfile spec. if (Args.hasArg(options::OPT_dynamiclib)) addDynamicLibLinkArgs(*this, Args, CmdArgs); 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/clang/test/Driver/darwin-kext.c b/clang/test/Driver/darwin-kext.c new file mode 100644 index 0000000000000..475f1c4477c4e --- /dev/null +++ b/clang/test/Driver/darwin-kext.c @@ -0,0 +1,21 @@ +// Check that -fapple-kext links a kext bundle, without start files or runtime +// libraries. The linker looks up undefined symbols of kexts dynamically on its +// own, so no -undefined is passed. + +// RUN: %clang --target=x86_64-apple-macos11 -fapple-kext -### %s 2>&1 | \ +// RUN: FileCheck %s +// CHECK: {{ld(.exe)?"}} +// CHECK-SAME: "-static" "-arch" "x86_64" "-kext" +// CHECK-NOT: "-kext" +// CHECK-NOT: "-undefined" +// CHECK-NOT: crt +// CHECK-NOT: "-lSystem" + +// RUN: %clang --target=x86_64-apple-macos11 -fapple-kext -bundle \ +// RUN: -bundle_loader foo -### %s 2>&1 | FileCheck %s --check-prefix=BUNDLE +// BUNDLE: {{ld(.exe)?"}} +// BUNDLE-NOT: "-bundle + +// RUN: not %clang --target=x86_64-apple-macos11 -fapple-kext -dynamiclib \ +// RUN: -### %s 2>&1 | FileCheck %s --check-prefix=DYLIB +// DYLIB: error: invalid argument '-fapple-kext' not allowed with '-dynamiclib' diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp index 0dfced91c511d..4ab98dec12c76 100644 --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -73,11 +73,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: @@ -101,6 +104,10 @@ static std::optional<StringRef> findLibrary(StringRef name) { // (crt1.o) if (name.ends_with(".o")) return findPathCombination(name, config->librarySearchPaths, {""}); + // Kexts cannot link against dylibs. + if (config->outputType == MH_KEXT_BUNDLE) + return findPathCombination("lib" + name, config->librarySearchPaths, + {".a"}); if (config->searchDylibsFirst) { if (std::optional<StringRef> path = findPathCombination("lib" + name, config->librarySearchPaths, @@ -527,6 +534,10 @@ static InputFile *processFile(std::optional<MemoryBufferRef> buffer, case file_magic::macho_dynamically_linked_shared_lib: case file_magic::macho_dynamically_linked_shared_lib_stub: case file_magic::tapi_file: + if (config->outputType == MH_KEXT_BUNDLE) { + warn("ignoring unexpected dylib '" + path + "'"); + break; + } if (DylibFile *dylibFile = loadDylib(mbref, nullptr, /*isBundleLoader=*/false, isExplicit)) newFile = dylibFile; @@ -995,6 +1006,11 @@ static TargetInfo *createTargetInfo(InputArgList &args) { static UndefinedSymbolTreatment getUndefinedSymbolTreatment(const ArgList &args) { + // The kernel linker resolves undefined symbols of kexts. Like ld64, ignore + // -undefined. + if (config->outputType == MH_KEXT_BUNDLE) + return UndefinedSymbolTreatment::dynamic_lookup; + StringRef treatmentStr = args.getLastArgValue(OPT_undefined); auto treatment = StringSwitch<UndefinedSymbolTreatment>(treatmentStr) @@ -1067,6 +1083,9 @@ static void warnIfDeprecatedOption(const Option &opt) { static void warnIfUnimplementedOption(const Option &opt) { if (!opt.getGroup().isValid() || !opt.hasFlag(DriverFlag::HelpHidden)) return; + // Clang links kexts with -static, which has no effect on them. + if (opt.getID() == OPT_static && config->outputType == MH_KEXT_BUNDLE) + return; switch (opt.getGroup().getID()) { case OPT_grp_deprecated: // warn about deprecated options elsewhere @@ -1258,6 +1277,9 @@ static bool dataConstDefault(const InputArgList &args) { switch (config->outputType) { case MH_EXECUTE: return !(args.hasArg(OPT_no_pie) && supportsNoPie()); + case MH_KEXT_BUNDLE: + // ld64 places constant data of x86_64 kexts in __DATA. + return false; case MH_BUNDLE: // FIXME: return false when -final_name ... // has prefix "/System/Library/UserEventPlugins/" @@ -1280,6 +1302,13 @@ static bool shouldEmitChainedFixups(const InputArgList &args) { return false; bool requested = arg && arg->getOption().matches(OPT_fixup_chains); + if (config->outputType == MH_KEXT_BUNDLE) { + if (requested) + error("-fixup_chains is incompatible with -kext"); + + return false; + } + if (!config->isPic) { if (requested) error("-fixup_chains is incompatible with -no_pie"); @@ -1884,7 +1913,14 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS, pie = true; } - config->isPic = config->outputType == MH_DYLIB || + // arm64 kexts need __TEXT_EXEC, stubs and split segment info, and arm64e + // ones pointer authentication too. + if (config->outputType == MH_KEXT_BUNDLE && + !is_contained({AK_x86_64, AK_x86_64h}, config->arch())) + fatal("-kext is only supported for x86_64 targets"); + + config->isPic = config->outputType == MH_KEXT_BUNDLE || + config->outputType == MH_DYLIB || config->outputType == MH_BUNDLE || (config->outputType == MH_EXECUTE && pie); @@ -2024,9 +2060,11 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS, config->demangle = args.hasArg(OPT_demangle); config->implicitDylibs = !args.hasArg(OPT_no_implicit_dylibs); config->emitFunctionStarts = + config->outputType != MH_KEXT_BUNDLE && args.hasFlag(OPT_function_starts, OPT_no_function_starts, true); config->emitDataInCodeInfo = - args.hasFlag(OPT_data_in_code_info, OPT_no_data_in_code_info, true); + args.hasFlag(OPT_data_in_code_info, OPT_no_data_in_code_info, + config->outputType != MH_KEXT_BUNDLE); config->emitChainedFixups = shouldEmitChainedFixups(args); config->emitInitOffsets = config->emitChainedFixups || args.hasArg(OPT_init_offsets); diff --git a/lld/MachO/InputSection.cpp b/lld/MachO/InputSection.cpp index 6ea4b0ddd58e4..cd261102f72f3 100644 --- a/lld/MachO/InputSection.cpp +++ b/lld/MachO/InputSection.cpp @@ -252,6 +252,12 @@ void ConcatInputSection::writeTo(uint8_t *buf) { if (target->hasAttr(r.type, RelocAttrBits::LOAD) && !referentSym->isInGot()) target->relaxGotLoad(loc, r.type); + // The kernel linker binds these using the external relocations, which + // take the addend from the relocated location. + if (config->outputType == MH_KEXT_BUNDLE && needsBinding(referentSym) && + !target->hasAttr(r.type, RelocAttrBits::GOT) && + !target->hasAttr(r.type, RelocAttrBits::TLV)) + 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 49ea57a24129c..f7966540894e6 100644 --- a/lld/MachO/InputSection.h +++ b/lld/MachO/InputSection.h @@ -380,7 +380,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 void addInputSection(InputSection *inputSection); diff --git a/lld/MachO/Options.td b/lld/MachO/Options.td index 32b6ce0a64a07..c8f6199d87892 100644 --- a/lld/MachO/Options.td +++ b/lld/MachO/Options.td @@ -244,6 +244,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]>, @@ -1432,10 +1435,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/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/Symbols.h b/lld/MachO/Symbols.h index cdeef93475222..1ef3cf99b4859 100644 --- a/lld/MachO/Symbols.h +++ b/lld/MachO/Symbols.h @@ -391,8 +391,12 @@ T *replaceSymbol(Symbol *s, ArgT &&...arg) { inline bool needsBinding(const Symbol *sym) { if (isa<DylibSymbol>(sym)) return true; + // Weak definitions in a kext are not coalesced by the kernel linker, so + // references to them are rebased rather than bound. if (const auto *defined = dyn_cast<Defined>(sym)) - return defined->isExternalWeakDef() || defined->interposable; + return (defined->isExternalWeakDef() && + config->outputType != llvm::MachO::MH_KEXT_BUNDLE) || + defined->interposable; return false; } diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp index 808f32fb2b094..9ba8099154537 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) { @@ -303,7 +303,9 @@ void RebaseSection::writeTo(uint8_t *buf) const { GotSection::GotSection() : SyntheticSection(segment_names::data, section_names::got) { align = target->wordSize; - flags = S_NON_LAZY_SYMBOL_POINTERS; + // ld64 marks the GOT of kexts as a regular section. + flags = config->outputType == MH_KEXT_BUNDLE ? S_REGULAR + : S_NON_LAZY_SYMBOL_POINTERS; } void macho::addNonLazyBindingEntries(const Symbol *sym, @@ -319,6 +321,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()) @@ -808,6 +822,61 @@ void StubHelperSection::setUp() { dyldPrivate->used = true; } +RelocSection::RelocSection(const char *name) + : LinkEditSection(segment_names::linkEdit, name) {} + +void RelocSection::addEntry(Referent referent, const InputSection *isec, + uint32_t offset, uint8_t type, bool pcrel, + uint8_t length) { + assert(!isFinal && "RelocSection entry added after finalizeContents"); + entries.push_back({referent, isec, offset, type, pcrel, length}); +} + +// Must be called after the symbol table is finalized, as external relocations +// refer to symbol table indices. +void RelocSection::finalizeContents() { + assert(!isFinal && "RelocSection finalized twice"); + isFinal = true; + + const bool external = isExternal(); + raw_svector_ostream os(contents); + for (const Entry &e : entries) { + uint32_t symbolNum; + if (external) { + symbolNum = cast<const Symbol *>(e.referent)->symtabIndex; + } else { + // Local relocations name the section of their target. + const InputSection *targetIsec = + dyn_cast<const InputSection *>(e.referent); + if (!targetIsec) + targetIsec = cast<Defined>(cast<const Symbol *>(e.referent))->isec(); + symbolNum = targetIsec ? targetIsec->parent->index : R_ABS; + } + + // Relocation addresses are relative to the first segment. + char buf[2 * sizeof(uint32_t)]; + write32le(buf, e.isec->getVA(e.offset) - in.header->addr); + write32le(buf + sizeof(uint32_t), + (symbolNum & 0x00ffffff) | + (static_cast<uint32_t>(e.pcrel) << 24) | + (static_cast<uint32_t>(e.length) << 25) | + (static_cast<uint32_t>(external) << 27) | + (static_cast<uint32_t>(e.type) << 28)); + os.write(buf, sizeof(buf)); + } +} + +void RelocSection::writeTo(uint8_t *buf) const { + assert(isFinal && "RelocSection contents written before finalization"); + memcpy(buf, contents.data(), contents.size()); +} + +ExternalRelocSection::ExternalRelocSection() + : RelocSection(section_names::extRelocs) {} + +LocalRelocSection::LocalRelocSection() + : RelocSection(section_names::localRelocs) {} + llvm::DenseMap<llvm::CachedHashStringRef, ConcatInputSection *> ObjCSelRefsHelper::methnameToSelref; void ObjCSelRefsHelper::initialize() { @@ -1494,25 +1563,26 @@ IndirectSymtabSection::IndirectSymtabSection() section_names::indirectSymbolTable) {} uint32_t IndirectSymtabSection::getNumSymbols() const { - uint32_t size = in.got->getEntries().size() + in.stubs->getEntries().size(); - if (!config->emitChainedFixups) - size += in.stubs->getEntries().size(); + uint32_t size = in.got->getEntries().size(); + if (in.stubs) + size += in.stubs->getEntries().size() * (config->emitChainedFixups ? 1 : 2); return size; } bool IndirectSymtabSection::isNeeded() const { - return in.got->isNeeded() || in.stubs->isNeeded(); + return in.got->isNeeded() || (in.stubs && in.stubs->isNeeded()); } void IndirectSymtabSection::finalizeContents() { uint32_t off = 0; in.got->reserved1 = off; off += in.got->getEntries().size(); - in.stubs->reserved1 = off; - if (in.lazyPointers) { + if (in.stubs) { + in.stubs->reserved1 = off; off += in.stubs->getEntries().size(); - in.lazyPointers->reserved1 = off; } + if (in.lazyPointers) + in.lazyPointers->reserved1 = off; } static uint32_t indirectValue(const Symbol *sym) { @@ -1527,9 +1597,11 @@ void IndirectSymtabSection::writeTo(uint8_t *buf) const { 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) { @@ -2324,6 +2396,10 @@ void macho::createSyntheticSymbols() { /*referencedDynamically=*/true); break; + case MH_KEXT_BUNDLE: + // ld64 defines no header symbol for kexts. + break; + // 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. diff --git a/lld/MachO/SyntheticSections.h b/lld/MachO/SyntheticSections.h index 2cc71414d56e7..6b553f9d50848 100644 --- a/lld/MachO/SyntheticSections.h +++ b/lld/MachO/SyntheticSections.h @@ -288,6 +288,51 @@ class StubsSection final : public SyntheticSection { llvm::SetVector<Symbol *> entries; }; +// Stores the external or local relocation entries of a kext bundle, which the +// kernel linker uses in place of dyld's binding and rebase opcodes. +class RelocSection : public LinkEditSection { +public: + // The symbol an external relocation binds to, or the symbol or section a + // local relocation points into. + using Referent = llvm::PointerUnion<const Symbol *, const InputSection *>; + + RelocSection(const char *name); + void addEntry(Referent referent, const InputSection *isec, uint32_t offset, + 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; + uint32_t getNumEntries() const { return entries.size(); } + + virtual bool isExternal() const = 0; + +private: + struct Entry { + Referent referent; + const InputSection *isec; + uint32_t offset; + uint8_t type; + bool pcrel; + uint8_t length; + }; + bool isFinal = false; + std::vector<Entry> entries; + SmallVector<char, 128> contents; +}; + +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(); @@ -844,6 +889,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 b97203d656ebd..1a3f95ab48534 100644 --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -212,6 +212,15 @@ class LCDysymtab final : public LoadCommand { c->indirectsymoff = indirectSymtabSection->fileOff; c->nindirectsyms = indirectSymtabSection->getNumSymbols(); + + if (in.extRelocs && in.extRelocs->isNeeded()) { + c->extreloff = in.extRelocs->fileOff; + c->nextrel = in.extRelocs->getNumEntries(); + } + if (in.localRelocs && in.localRelocs->isNeeded()) { + c->locreloff = in.localRelocs->fileOff; + c->nlocrel = in.localRelocs->getNumEntries(); + } } SymtabSection *symtabSection; @@ -684,8 +693,13 @@ 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)) { + // Kexts have no stubs: the kernel linker patches the branch itself. + 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)) addNonLazyPointerEntry(sym); @@ -698,6 +712,11 @@ static void prepareSymbolRelocation(Symbol *sym, const InputSection *isec, // need of rebase opcodes. if (!(isThreadLocalVariables(isec->getFlags()) && isa<Defined>(sym))) addNonLazyBindingEntries(sym, isec, r.offset, r.addend); + } else if (config->outputType == MH_KEXT_BUNDLE && needsBinding(sym)) { + // The kernel linker can only bind branches and pointers. + error(isec->getLocation(r.offset) + ": " + relocAttrs.name + + " relocation to " + toString(*sym) + + " cannot be bound in a kext; access it through the GOT instead"); } } @@ -741,6 +760,10 @@ 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(cast<InputSection *>(r.referent), isec, + r.offset, r.type, /*pcrel=*/false, + r.length); else in.rebase->addEntry(isec, r.offset); } @@ -831,7 +854,7 @@ template <class LP> void Writer::createLoadCommands() { if (config->emitChainedFixups) { in.header->addLoadCommand(make<LCChainedFixups>(in.chainedFixups)); in.header->addLoadCommand(make<LCExportsTrie>(in.exports)); - } else { + } else if (config->outputType != MH_KEXT_BUNDLE) { in.header->addLoadCommand(make<LCDyldInfo>( in.rebase, in.binding, in.weakBinding, in.lazyBinding, in.exports)); } @@ -857,6 +880,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"); @@ -867,10 +891,13 @@ template <class LP> void Writer::createLoadCommands() { in.header->addLoadCommand(uuidCommand); } - if (useLCBuildVersion(config->platformInfo)) - in.header->addLoadCommand(make<LCBuildVersion>(config->platformInfo)); - else - in.header->addLoadCommand(make<LCMinVersion>(config->platformInfo)); + // ld64 does not record the platform of kexts. + if (config->outputType != MH_KEXT_BUNDLE) { + if (useLCBuildVersion(config->platformInfo)) + in.header->addLoadCommand(make<LCBuildVersion>(config->platformInfo)); + else + in.header->addLoadCommand(make<LCMinVersion>(config->platformInfo)); + } if (config->secondaryPlatformInfo) { in.header->addLoadCommand( @@ -1089,6 +1116,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"); @@ -1208,6 +1236,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; @@ -1436,6 +1469,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>(); @@ -1444,9 +1480,11 @@ void macho::createSyntheticSections() { in.lazyPointers = make<LazyPointerSection>(); in.stubHelper = make<StubHelperSection>(); } - in.exports = make<ExportSection>(); + if (config->outputType != MH_KEXT_BUNDLE) + in.exports = make<ExportSection>(); in.got = make<GotSection>(); - in.stubs = make<StubsSection>(); + if (config->outputType != MH_KEXT_BUNDLE) + in.stubs = make<StubsSection>(); in.objcStubs = make<ObjCStubsSection>(); in.unwindInfo = makeUnwindInfoSection(); in.objCImageInfo = make<ObjCImageInfoSection>(); diff --git a/lld/test/MachO/kext.s b/lld/test/MachO/kext.s new file mode 100644 index 0000000000000..f2059f388b40f --- /dev/null +++ b/lld/test/MachO/kext.s @@ -0,0 +1,156 @@ +# REQUIRES: x86 +# RUN: rm -rf %t; split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos11 %t/main.s -o %t/main.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos11 %t/signed.s -o %t/signed.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos11 %t/lib.s -o %t/lib.o + +## Undefined symbols are looked up dynamically regardless of -undefined, and +## -static, which clang passes for kexts, is accepted without a warning. +# RUN: %no-lsystem-lld -kext -static -undefined error -o %t/main.kext %t/main.o +# RUN: llvm-objdump --macho -r --private-headers %t/main.kext | \ +# RUN: FileCheck %s --implicit-check-not="cmd LC_" +# RUN: llvm-objdump --macho -d --no-show-raw-insn %t/main.kext | \ +# RUN: FileCheck %s --check-prefix=DISASM +# RUN: llvm-objdump --macho -s --section=__DATA,__data %t/main.kext | \ +# RUN: FileCheck %s --check-prefix=DATA +# RUN: llvm-nm -m %t/main.kext | FileCheck %s --check-prefix=SYMS + +## Branches and pointers to undefined symbols, including their GOT slots, are +## bound through external relocations. Local relocations name the section that +## they point into, and references to weak definitions are not bound. +# CHECK: External relocation information 3 entries +# CHECK-NEXT: address pcrel length extern type scattered symbolnum/value +# CHECK-NEXT: [[#%.8x,GOT:]] False quad True UNSIGND False _ext_data +# CHECK-NEXT: [[#%.8x,CALL:]] True long True BRANCH False _ext_func +# CHECK-NEXT: [[#%.8x,EXTPTR:]] False quad True UNSIGND False _ext_data +# CHECK-NEXT: Local relocation information 3 entries +# CHECK-NEXT: address pcrel length extern type scattered symbolnum/value +# CHECK-NEXT: [[#%.8x,EXTPTR-16]] False quad False UNSIGND False 1 (__TEXT,__text) +# CHECK-NEXT: [[#%.8x,EXTPTR-8]] False quad False UNSIGND False 2 (__TEXT,__cstring) +# CHECK-NEXT: [[#%.8x,EXTPTR+8]] False quad False UNSIGND False 4 (__DATA,__data) + +## Like ld64, there is no __DATA_CONST segment, and no LC_BUILD_VERSION, +## LC_FUNCTION_STARTS, or dyld info. The GOT is a regular section. +# CHECK: filetype ncmds sizeofcmds flags +# CHECK-NEXT: KEXTBUNDLE 6 {{[0-9]+}} NOUNDEFS DYLDLINK TWOLEVEL{{$}} +# CHECK: cmd LC_SEGMENT_64 +# CHECK-NEXT: cmdsize +# CHECK-NEXT: segname __TEXT +# CHECK-NEXT: vmaddr 0x0000000000000000 +# CHECK: sectname __text +# CHECK-NEXT: segname __TEXT +# CHECK-NEXT: addr 0x[[#%.16x,CALL-1]] +# CHECK: sectname __cstring +# CHECK: cmd LC_SEGMENT_64 +# CHECK-NEXT: cmdsize +# CHECK-NEXT: segname __DATA +# CHECK: sectname __got +# CHECK-NEXT: segname __DATA +# CHECK-NEXT: addr 0x[[#%.16x,GOT]] +# CHECK: type S_REGULAR +# CHECK-NEXT: attributes (none) +# CHECK-NEXT: reserved1 0 +# CHECK: sectname __data +# CHECK-NEXT: segname __DATA +# CHECK-NEXT: addr 0x[[#%.16x,EXTPTR-16]] +# CHECK: cmd LC_SEGMENT_64 +# CHECK-NEXT: cmdsize +# CHECK-NEXT: segname __LINKEDIT +# CHECK: cmd LC_SYMTAB +# CHECK: cmd LC_DYSYMTAB +# CHECK: nindirectsyms 1 +# CHECK-NEXT: extreloff +# CHECK-NEXT: nextrel 3 +# CHECK-NEXT: locreloff +# CHECK-NEXT: nlocrel 3 +# CHECK: cmd LC_UUID + +## The load from the GOT of a weak definition is relaxed, and calls to it are +## direct. +# DISASM: _start_fn: +# DISASM-NEXT: callq _ext_func +# DISASM-NEXT: movq 0x{{[0-9a-f]+}}(%rip), %rax +# DISASM-NEXT: leaq _weak_data(%rip), %rax +# DISASM-NEXT: callq _weak_fn + +## The addend of an external relocation is left in place. +# DATA: Contents of (__DATA,__data) section +# DATA-NEXT: {{^[0-9a-f]+}} +# DATA-NEXT: {{^[0-9a-f]+}} 08 00 00 00 00 00 00 00 + +## ld64 defines no header symbol for kexts. +# SYMS-NOT: __mh_ +# SYMS: (undefined) external _ext_data (dynamically looked up) +# SYMS: (undefined) external _ext_func (dynamically looked up) + +## Like ld64, kexts are never linked against dylibs. +# RUN: %no-lsystem-lld -dylib -o %t/libfoo.dylib %t/lib.o +# RUN: llvm-ar rcs %t/libbar.a %t/lib.o +# RUN: not %no-lsystem-lld -kext -o /dev/null %t/main.o -L%t -lfoo 2>&1 | \ +# RUN: FileCheck %s --check-prefix=NOLIB +# RUN: %no-lsystem-lld -kext -o /dev/null %t/main.o -L%t -lbar +# RUN: %no-arg-lld -arch x86_64 -platform_version macos 11.0 11.0 -kext \ +# RUN: -o /dev/null %t/main.o %t/libfoo.dylib 2>&1 | \ +# RUN: FileCheck %s --check-prefix=DYLIB -DFILE=%t/libfoo.dylib +# NOLIB: error: library not found for -lfoo +# DYLIB: warning: ignoring unexpected dylib '[[FILE]]' + +# RUN: not %no-lsystem-lld -kext -o /dev/null %t/signed.o 2>&1 | \ +# RUN: FileCheck %s --check-prefix=SIGNED +# SIGNED: error: {{.*}}signed.o:(symbol _f+0x3): SIGNED relocation to _ext cannot be bound in a kext; access it through the GOT instead + +# RUN: not %no-lsystem-lld -kext -fixup_chains -o /dev/null %t/main.o 2>&1 | \ +# RUN: FileCheck %s --check-prefix=CHAINED +# CHAINED: error: -fixup_chains is incompatible with -kext + +# RUN: not %no-lsystem-lld -kext -arch arm64 -o /dev/null %t/main.o 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ARM64 +# ARM64: error: -kext is only supported for x86_64 targets + +#--- main.s +.text +.globl _start_fn +_start_fn: + callq _ext_func + movq _ext_data@GOTPCREL(%rip), %rax + movq _weak_data@GOTPCREL(%rip), %rax + callq _weak_fn + retq + +.globl _weak_fn +.weak_definition _weak_fn +_weak_fn: + retq + +.cstring +Lstr: + .asciz "kext" + +.data +_local_ptr: + .quad _start_fn +_section_ptr: + .quad Lstr +_ext_ptr: + .quad _ext_data + 8 +_weak_ptr: + .quad _weak_data + +.globl _weak_data +.weak_definition _weak_data +_weak_data: + .quad 0 + +.subsections_via_symbols + +#--- signed.s +.text +.globl _f +_f: + leaq _ext(%rip), %rax + retq + +#--- lib.s +.globl _ext_func +_ext_func: + retq diff --git a/llvm/include/llvm/TextAPI/FileTypes.h b/llvm/include/llvm/TextAPI/FileTypes.h index 5876e9d5a5304..b8d35130a39d7 100644 --- a/llvm/include/llvm/TextAPI/FileTypes.h +++ b/llvm/include/llvm/TextAPI/FileTypes.h @@ -40,6 +40,9 @@ enum FileType : unsigned { /// Text-based stub file (.tbd) version 5.0 TBD_V5 = 1U << 7, + /// \brief MachO Kernel Extension Bundle file. + MachO_KextBundle = 1U << 8, + All = ~0U, LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/All), diff --git a/llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp b/llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp index 8cd418276f851..5dcbedc5237d7 100644 --- a/llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp +++ b/llvm/lib/ObjCopy/MachO/MachOLayoutBuilder.cpp @@ -263,9 +263,9 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) { // The order of LINKEDIT elements is as follows: // rebase info, binding info, weak binding info, lazy binding info, export - // trie, chained fixups, dyld exports trie, function starts, data-in-code, - // symbol table, indirect symbol table, symbol table strings, - // dylib codesign drs, and code signature. + // trie, chained fixups, dyld exports trie, local relocations, function + // starts, data-in-code, symbol table, external relocations, indirect symbol + // table, symbol table strings, dylib codesign drs, and code signature. auto updateOffset = [&Offset, LinkEditAlign](uint64_t Size) { uint64_t PreviousOffset = Offset; // Match ld64 and lld-macho behavior by aligning all LINKEDIT entries to @@ -289,6 +289,10 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) { updateOffset(O.ChainedFixups.Data.size()); auto [StartOfDyldExportsTrie, DyldExportsTrieSize] = updateOffset(DyldExportsTrieRawSize); + uint64_t StartOfLocalRelocations = + updateOffset(sizeof(MachO::any_relocation_info) * + O.LocalRelocations.size()) + .first; auto [StartOfFunctionStarts, FunctionStartsSize] = updateOffset(O.FunctionStarts.Data.size()); auto [StartOfDataInCode, DataInCodeSize] = @@ -297,6 +301,10 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) { updateOffset(O.LinkerOptimizationHint.Data.size()); uint64_t StartOfSymbols = updateOffset(NListSize * O.SymTable.Symbols.size()).first; + uint64_t StartOfExternalRelocations = + updateOffset(sizeof(MachO::any_relocation_info) * + O.ExternalRelocations.size()) + .first; uint64_t StartOfIndirectSymbols = updateOffset(sizeof(uint32_t) * O.IndirectSymTable.Symbols.size()).first; auto [StartOfSymbolStrings, SymbolStringsSize] = @@ -372,13 +380,23 @@ Error MachOLayoutBuilder::layoutTail(uint64_t Offset) { MLC.symtab_command_data.strsize = SymbolStringsSize; break; case MachO::LC_DYSYMTAB: { + // Dynamic relocations are only read from linked images; relocations of + // an MH_OBJECT file are attached to its sections. + const bool IsObjectFile = + O.Header.FileType == MachO::HeaderFileType::MH_OBJECT; if (MLC.dysymtab_command_data.ntoc != 0 || MLC.dysymtab_command_data.nmodtab != 0 || MLC.dysymtab_command_data.nextrefsyms != 0 || - MLC.dysymtab_command_data.nlocrel != 0 || - MLC.dysymtab_command_data.nextrel != 0) + (IsObjectFile && (MLC.dysymtab_command_data.nlocrel != 0 || + MLC.dysymtab_command_data.nextrel != 0))) return createStringError(llvm::errc::not_supported, "shared library is not yet supported"); + MLC.dysymtab_command_data.extreloff = + O.ExternalRelocations.size() ? StartOfExternalRelocations : 0; + MLC.dysymtab_command_data.nextrel = O.ExternalRelocations.size(); + MLC.dysymtab_command_data.locreloff = + O.LocalRelocations.size() ? StartOfLocalRelocations : 0; + MLC.dysymtab_command_data.nlocrel = O.LocalRelocations.size(); MLC.dysymtab_command_data.indirectsymoff = O.IndirectSymTable.Symbols.size() ? StartOfIndirectSymbols : 0; MLC.dysymtab_command_data.nindirectsyms = diff --git a/llvm/lib/ObjCopy/MachO/MachOObjcopy.cpp b/llvm/lib/ObjCopy/MachO/MachOObjcopy.cpp index 1bca325198e0d..9c100a56eb038 100644 --- a/llvm/lib/ObjCopy/MachO/MachOObjcopy.cpp +++ b/llvm/lib/ObjCopy/MachO/MachOObjcopy.cpp @@ -89,6 +89,11 @@ static void markSymbols(const CommonConfig &Config, Object &Obj) { if (ISE.Symbol) (*ISE.Symbol)->Referenced = true; + // Nor those referenced from external relocations, which --strip-all keeps. + for (const RelocationInfo &R : Obj.ExternalRelocations) + if (R.Symbol && *R.Symbol) + (*R.Symbol)->Referenced = true; + // --strip-all removes relocations, so their symbols need not be preserved. if (Config.StripAll) return; @@ -144,6 +149,11 @@ static void updateAndRemoveSymbols(const CommonConfig &Config, return false; if (N->n_desc & MachO::REFERENCED_DYNAMICALLY) return false; + // The kernel linker looks up the external symbols of a kext, such as + // _kmod_info, and binds other kexts to them. + if (Obj.Header.FileType == MachO::HeaderFileType::MH_KEXT_BUNDLE && + N->isExternalSymbol()) + return false; if (Config.StripAll) return true; if (Config.DiscardMode == DiscardType::All && !(N->n_type & MachO::N_EXT)) diff --git a/llvm/lib/ObjCopy/MachO/MachOObject.cpp b/llvm/lib/ObjCopy/MachO/MachOObject.cpp index e5f36c538ff7d..73bd769f1c904 100644 --- a/llvm/lib/ObjCopy/MachO/MachOObject.cpp +++ b/llvm/lib/ObjCopy/MachO/MachOObject.cpp @@ -123,6 +123,13 @@ Error Object::removeSections( OldIndexToSection[(*I)->Index] = I->get(); (*I)->Index = NextSectionIndex++; } + for (auto I = It, End = LC.Sections.end(); I != End; ++I) + for (const RelocationInfo &R : LocalRelocations) + if (R.Sec && *R.Sec == I->get()) + return createStringError(std::errc::invalid_argument, + "section '%s' cannot be removed because it " + "is referenced by a local relocation", + (*I)->CanonicalName.c_str()); LC.Sections.erase(It, LC.Sections.end()); } @@ -147,6 +154,14 @@ Error Object::removeSections( (*R.Symbol)->Name.c_str(), *((*R.Symbol)->section()), Sec->CanonicalName.c_str()); + for (const RelocationInfo &R : ExternalRelocations) + if (R.Symbol && *R.Symbol && DeadSymbols.count(*R.Symbol)) + return createStringError(std::errc::invalid_argument, + "symbol '%s' defined in section with index " + "'%u' cannot be removed because it is " + "referenced by an external relocation", + (*R.Symbol)->Name.c_str(), + *((*R.Symbol)->section())); SymTable.removeSymbols(IsDead); for (std::unique_ptr<SymbolEntry> &S : SymTable.Symbols) if (S->section()) diff --git a/llvm/lib/ObjCopy/MachO/MachOObject.h b/llvm/lib/ObjCopy/MachO/MachOObject.h index 9895585fcab14..811cbdcf3b21d 100644 --- a/llvm/lib/ObjCopy/MachO/MachOObject.h +++ b/llvm/lib/ObjCopy/MachO/MachOObject.h @@ -309,6 +309,10 @@ struct Object { LazyBindInfo LazyBinds; ExportInfo Exports; IndirectSymbolTable IndirectSymTable; + /// External and local relocation entries of a linked image (e.g. a kext + /// bundle), described by the LC_DYSYMTAB load command. + std::vector<RelocationInfo> ExternalRelocations; + std::vector<RelocationInfo> LocalRelocations; LinkData DataInCode; LinkData LinkerOptimizationHint; LinkData FunctionStarts; diff --git a/llvm/lib/ObjCopy/MachO/MachOReader.cpp b/llvm/lib/ObjCopy/MachO/MachOReader.cpp index dd4d60deccbb8..6db24f9b1fd6a 100644 --- a/llvm/lib/ObjCopy/MachO/MachOReader.cpp +++ b/llvm/lib/ObjCopy/MachO/MachOReader.cpp @@ -45,6 +45,22 @@ static Section constructSectionCommon(const SectionType &Sec, uint32_t Index) { return S; } +static RelocationInfo +constructRelocationInfo(const object::MachOObjectFile &MachOObj, + object::DataRefImpl Rel) { + RelocationInfo R; + R.Symbol = nullptr; // We'll fill this field later. + R.Info = MachOObj.getRelocation(Rel); + R.Scattered = MachOObj.isRelocationScattered(R.Info); + unsigned Type = MachOObj.getAnyRelocationType(R.Info); + // TODO Support CPU_TYPE_ARM. + R.IsAddend = + !R.Scattered && (MachOObj.getHeader().cputype == MachO::CPU_TYPE_ARM64 && + Type == MachO::ARM64_RELOC_ADDEND); + R.Extern = !R.Scattered && MachOObj.getPlainRelocationExternal(R.Info); + return R; +} + Section constructSection(const MachO::section &Sec, uint32_t Index) { return constructSectionCommon(Sec, Index); } @@ -90,22 +106,12 @@ Expected<std::vector<std::unique_ptr<Section>>> static extractSections( S.Content = StringRef(reinterpret_cast<const char *>(Data->data()), Data->size()); - const uint32_t CPUType = MachOObj.getHeader().cputype; S.Relocations.reserve(S.NReloc); for (auto RI = MachOObj.section_rel_begin(SecRef->getRawDataRefImpl()), RE = MachOObj.section_rel_end(SecRef->getRawDataRefImpl()); - RI != RE; ++RI) { - RelocationInfo R; - R.Symbol = nullptr; // We'll fill this field later. - R.Info = MachOObj.getRelocation(RI->getRawDataRefImpl()); - R.Scattered = MachOObj.isRelocationScattered(R.Info); - unsigned Type = MachOObj.getAnyRelocationType(R.Info); - // TODO Support CPU_TYPE_ARM. - R.IsAddend = !R.Scattered && (CPUType == MachO::CPU_TYPE_ARM64 && - Type == MachO::ARM64_RELOC_ADDEND); - R.Extern = !R.Scattered && MachOObj.getPlainRelocationExternal(R.Info); - S.Relocations.push_back(R); - } + RI != RE; ++RI) + S.Relocations.push_back( + constructRelocationInfo(MachOObj, RI->getRawDataRefImpl())); assert(S.NReloc == S.Relocations.size() && "Incorrect number of relocations"); @@ -243,6 +249,22 @@ void MachOReader::readSymbolTable(Object &O) const { } } +void MachOReader::readDynamicRelocations(Object &O) const { + // In an MH_OBJECT file, getRelocation() interprets the reference as a + // section relocation, and such files have no dynamic relocations anyway. + if (!O.DySymTabCommandIndex || + O.Header.FileType == MachO::HeaderFileType::MH_OBJECT) + return; + + for (const object::RelocationRef &Rel : MachOObj.external_relocations()) + O.ExternalRelocations.push_back( + constructRelocationInfo(MachOObj, Rel.getRawDataRefImpl())); + for (auto RI = MachOObj.locrel_begin(), RE = MachOObj.locrel_end(); RI != RE; + ++RI) + O.LocalRelocations.push_back( + constructRelocationInfo(MachOObj, RI->getRawDataRefImpl())); +} + void MachOReader::setSymbolInRelocationInfo(Object &O) const { std::vector<const Section *> Sections; for (auto &LC : O.LoadCommands) @@ -265,6 +287,20 @@ void MachOReader::setSymbolInRelocationInfo(Object &O) const { Reloc.Sec = Sections[SymbolNum - 1]; } } + + // A local relocation may use R_ABS instead of a section number; such + // entries have no section and are written back unchanged. + for (std::vector<RelocationInfo> *Relocs : + {&O.ExternalRelocations, &O.LocalRelocations}) + for (RelocationInfo &Reloc : *Relocs) + if (!Reloc.Scattered && !Reloc.IsAddend) { + const uint32_t SymbolNum = + Reloc.getPlainRelocationSymbolNum(MachOObj.isLittleEndian()); + if (Reloc.Extern) + Reloc.Symbol = O.SymTable.getSymbolByIndex(SymbolNum); + else if (SymbolNum >= 1 && SymbolNum <= Sections.size()) + Reloc.Sec = Sections[SymbolNum - 1]; + } } void MachOReader::readRebaseInfo(Object &O) const { @@ -368,6 +404,7 @@ Expected<std::unique_ptr<Object>> MachOReader::create() const { if (Error E = readLoadCommands(*Obj)) return std::move(E); readSymbolTable(*Obj); + readDynamicRelocations(*Obj); setSymbolInRelocationInfo(*Obj); readRebaseInfo(*Obj); readBindInfo(*Obj); diff --git a/llvm/lib/ObjCopy/MachO/MachOReader.h b/llvm/lib/ObjCopy/MachO/MachOReader.h index a88885d568179..ea84abd12d941 100644 --- a/llvm/lib/ObjCopy/MachO/MachOReader.h +++ b/llvm/lib/ObjCopy/MachO/MachOReader.h @@ -33,6 +33,7 @@ class MachOReader : public Reader { void readHeader(Object &O) const; Error readLoadCommands(Object &O) const; void readSymbolTable(Object &O) const; + void readDynamicRelocations(Object &O) const; void setSymbolInRelocationInfo(Object &O) const; void readRebaseInfo(Object &O) const; void readBindInfo(Object &O) const; diff --git a/llvm/lib/ObjCopy/MachO/MachOWriter.cpp b/llvm/lib/ObjCopy/MachO/MachOWriter.cpp index d83a82cee73ae..c895bcdd7002e 100644 --- a/llvm/lib/ObjCopy/MachO/MachOWriter.cpp +++ b/llvm/lib/ObjCopy/MachO/MachOWriter.cpp @@ -102,6 +102,14 @@ size_t MachOWriter::totalSize() const { if (DySymTabCommand.indirectsymoff) Ends.push_back(DySymTabCommand.indirectsymoff + sizeof(uint32_t) * O.IndirectSymTable.Symbols.size()); + if (DySymTabCommand.extreloff) + Ends.push_back(DySymTabCommand.extreloff + + sizeof(MachO::any_relocation_info) * + O.ExternalRelocations.size()); + if (DySymTabCommand.locreloff) + Ends.push_back(DySymTabCommand.locreloff + + sizeof(MachO::any_relocation_info) * + O.LocalRelocations.size()); } for (std::optional<size_t> LinkEditDataCommandIndex : @@ -260,22 +268,27 @@ void MachOWriter::writeSections() { assert((Sec->Size == Sec->Content.size()) && "Incorrect section size"); memcpy(Buf->getBufferStart() + Sec->Offset, Sec->Content.data(), Sec->Content.size()); - for (size_t Index = 0; Index < Sec->Relocations.size(); ++Index) { - RelocationInfo RelocInfo = Sec->Relocations[Index]; - if (!RelocInfo.Scattered && !RelocInfo.IsAddend) { - const uint32_t SymbolNum = RelocInfo.Extern - ? (*RelocInfo.Symbol)->Index - : (*RelocInfo.Sec)->Index; - RelocInfo.setPlainRelocationSymbolNum(SymbolNum, IsLittleEndian); - } - if (IsLittleEndian != sys::IsLittleEndianHost) - MachO::swapStruct( - reinterpret_cast<MachO::any_relocation_info &>(RelocInfo.Info)); - memcpy(Buf->getBufferStart() + Sec->RelOff + - Index * sizeof(MachO::any_relocation_info), - &RelocInfo.Info, sizeof(RelocInfo.Info)); - } + writeRelocations(Sec->Relocations, Sec->RelOff); + } +} + +void MachOWriter::writeRelocations(ArrayRef<RelocationInfo> Relocations, + uint64_t Offset) { + for (size_t Index = 0; Index < Relocations.size(); ++Index) { + RelocationInfo RelocInfo = Relocations[Index]; + if (!RelocInfo.Scattered && !RelocInfo.IsAddend && + (RelocInfo.Extern || RelocInfo.Sec)) { + const uint32_t SymbolNum = RelocInfo.Extern ? (*RelocInfo.Symbol)->Index + : (*RelocInfo.Sec)->Index; + RelocInfo.setPlainRelocationSymbolNum(SymbolNum, IsLittleEndian); } + if (IsLittleEndian != sys::IsLittleEndianHost) + MachO::swapStruct( + reinterpret_cast<MachO::any_relocation_info &>(RelocInfo.Info)); + memcpy(Buf->getBufferStart() + Offset + + Index * sizeof(MachO::any_relocation_info), + &RelocInfo.Info, sizeof(RelocInfo.Info)); + } } template <typename NListType> @@ -407,6 +420,26 @@ void MachOWriter::writeIndirectSymbolTable() { } } +void MachOWriter::writeExternalRelocations() { + if (!O.DySymTabCommandIndex) + return; + + const MachO::dysymtab_command &DySymTabCommand = + O.LoadCommands[*O.DySymTabCommandIndex] + .MachOLoadCommand.dysymtab_command_data; + writeRelocations(O.ExternalRelocations, DySymTabCommand.extreloff); +} + +void MachOWriter::writeLocalRelocations() { + if (!O.DySymTabCommandIndex) + return; + + const MachO::dysymtab_command &DySymTabCommand = + O.LoadCommands[*O.DySymTabCommandIndex] + .MachOLoadCommand.dysymtab_command_data; + writeRelocations(O.LocalRelocations, DySymTabCommand.locreloff); +} + void MachOWriter::writeLinkData(std::optional<size_t> LCIndex, const LinkData &LD) { if (!LCIndex) @@ -641,6 +674,12 @@ void MachOWriter::writeTail() { if (DySymTabCommand.indirectsymoff) Queue.emplace_back(DySymTabCommand.indirectsymoff, &MachOWriter::writeIndirectSymbolTable); + if (DySymTabCommand.extreloff) + Queue.emplace_back(DySymTabCommand.extreloff, + &MachOWriter::writeExternalRelocations); + if (DySymTabCommand.locreloff) + Queue.emplace_back(DySymTabCommand.locreloff, + &MachOWriter::writeLocalRelocations); } std::initializer_list<std::pair<std::optional<size_t>, WriteHandlerType>> diff --git a/llvm/lib/ObjCopy/MachO/MachOWriter.h b/llvm/lib/ObjCopy/MachO/MachOWriter.h index 5da29dff656a7..b72596d8ed2cf 100644 --- a/llvm/lib/ObjCopy/MachO/MachOWriter.h +++ b/llvm/lib/ObjCopy/MachO/MachOWriter.h @@ -39,6 +39,7 @@ class MachOWriter { void writeLoadCommands(); template <typename StructType> void writeSectionInLoadCommand(const Section &Sec, uint8_t *&Out); + void writeRelocations(ArrayRef<RelocationInfo> Relocations, uint64_t Offset); void writeSections(); void writeSymbolTable(); void writeStringTable(); @@ -48,6 +49,8 @@ class MachOWriter { void writeLazyBindInfo(); void writeExportInfo(); void writeIndirectSymbolTable(); + void writeExternalRelocations(); + void writeLocalRelocations(); void writeLinkData(std::optional<size_t> LCIndex, const LinkData &LD); void writeCodeSignatureData(); void writeDataInCodeData(); 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) diff --git a/llvm/test/tools/llvm-objcopy/MachO/kext-relocations.test b/llvm/test/tools/llvm-objcopy/MachO/kext-relocations.test new file mode 100644 index 0000000000000..ca96d22980c84 --- /dev/null +++ b/llvm/test/tools/llvm-objcopy/MachO/kext-relocations.test @@ -0,0 +1,170 @@ +## Check that the external and local relocations of a kext bundle are kept, +## and that the symbols and sections they refer to are renumbered. + +## The input is linked from: +## .text +## .globl _f +## _f: +## callq _ext_func +## retq +## _local_fn: +## retq +## .section __TEXT,__unused +## .quad 0 +## .data +## .globl _p +## _p: +## .quad _ext_data +## _q: +## .quad _local_fn +## _r: +## .quad _q + +# RUN: yaml2obj %s -o %t + +# RUN: llvm-objcopy %t %t.copy +# RUN: llvm-objdump --macho -r %t.copy | FileCheck %s -DDATA=3 + +## Stripping removes the local symbols, but keeps the external ones, which the +## kernel linker looks up. +# RUN: llvm-strip %t -o %t.strip +# RUN: llvm-objdump --macho -r %t.strip | FileCheck %s -DDATA=3 +# RUN: llvm-nm -m %t.strip | FileCheck %s --check-prefix=SYMS +# RUN: llvm-strip -x %t -o %t.strip-x +# RUN: cmp %t.strip %t.strip-x + +# CHECK: External relocation information 2 entries +# CHECK-NEXT: address pcrel length extern type scattered symbolnum/value +# CHECK-NEXT: 00000271 True long True BRANCH False _ext_func +# CHECK-NEXT: 00001000 False quad True UNSIGND False _ext_data +# CHECK-NEXT: Local relocation information 2 entries +# CHECK-NEXT: address pcrel length extern type scattered symbolnum/value +# CHECK-NEXT: 00001008 False quad False UNSIGND False 1 (__TEXT,__text) +# CHECK-NEXT: 00001010 False quad False UNSIGND False [[DATA]] (__DATA,__data) + +# SYMS: (undefined) external _ext_data +# SYMS-NEXT: (undefined) external _ext_func +# SYMS-NEXT: (__TEXT,__text) external _f +# SYMS-NEXT: (__DATA,__data) external _p +# SYMS-EMPTY: + +# RUN: llvm-objcopy --remove-section __TEXT,__unused %t %t.removed +# RUN: llvm-objdump --macho -r %t.removed | FileCheck %s -DDATA=2 + +# RUN: not llvm-objcopy --remove-section __TEXT,__text %t /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ERROR -DFILE=%t +# ERROR: error: '[[FILE]]': section '__TEXT,__text' cannot be removed because it is referenced by a local relocation + +--- !mach-o +FileHeader: + magic: 0xFEEDFACF + cputype: 0x1000007 + cpusubtype: 0x3 + filetype: 0xB + ncmds: 5 + sizeofcmds: 560 + flags: 0x85 + reserved: 0x0 +LoadCommands: + - cmd: LC_SEGMENT_64 + cmdsize: 232 + segname: __TEXT + vmaddr: 0 + vmsize: 4096 + fileoff: 0 + filesize: 4096 + maxprot: 5 + initprot: 5 + nsects: 2 + flags: 0 + Sections: + - sectname: __text + segname: __TEXT + addr: 0x270 + size: 7 + offset: 0x270 + align: 0 + reloff: 0x0 + nreloc: 0 + flags: 0x80000400 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: E800000000C3C3 + - sectname: __unused + segname: __TEXT + addr: 0x277 + size: 8 + offset: 0x277 + align: 0 + reloff: 0x0 + nreloc: 0 + flags: 0x0 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: '0000000000000000' + - cmd: LC_SEGMENT_64 + cmdsize: 152 + segname: __DATA + vmaddr: 4096 + vmsize: 4096 + fileoff: 4096 + filesize: 4096 + maxprot: 3 + initprot: 3 + nsects: 1 + flags: 0 + Sections: + - sectname: __data + segname: __DATA + addr: 0x1000 + size: 24 + offset: 0x1000 + align: 0 + reloff: 0x0 + nreloc: 0 + flags: 0x0 + reserved1: 0x0 + reserved2: 0x0 + reserved3: 0x0 + content: '000000000000000076020000000000000810000000000000' + - cmd: LC_SEGMENT_64 + cmdsize: 72 + segname: __LINKEDIT + vmaddr: 8192 + vmsize: 192 + fileoff: 8192 + filesize: 192 + maxprot: 1 + initprot: 1 + nsects: 0 + flags: 0 + - cmd: LC_SYMTAB + cmdsize: 24 + symoff: 8208 + nsyms: 7 + stroff: 8336 + strsize: 48 + - cmd: LC_DYSYMTAB + cmdsize: 80 + ilocalsym: 0 + nlocalsym: 3 + iextdefsym: 3 + nextdefsym: 2 + iundefsym: 5 + nundefsym: 2 + tocoff: 0 + ntoc: 0 + modtaboff: 0 + nmodtab: 0 + extrefsymoff: 0 + nextrefsyms: 0 + indirectsymoff: 0 + nindirectsyms: 0 + extreloff: 8320 + nextrel: 2 + locreloff: 8192 + nlocrel: 2 +__LINKEDIT: 08100000010000061010000003000006020000000E01000076020000000000000C0000000E03000008100000000000000F0000000E0300001010000000000000120000000F0100007002000000000000150000000F030000001000000000000018000000010000FE000000000000000022000000010000FE0000000000000000710200000600002D001000000500000E20005F6C6F63616C5F666E005F71005F72005F66005F70005F6578745F64617461005F6578745F66756E630000000000 +... _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
