diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -1032,14 +1032,7 @@ } createSyntheticSections(); - - // The Itanium C++ ABI requires dylibs to pass a pointer to __cxa_atexit - // which does e.g. cleanup of static global variables. The ABI document says - // that the pointer can point to any address in one of the dylib's segments, - // but in practice ld64 seems to set it to point to the header, so that's - // what's implemented here. - symtab->addSynthetic("___dso_handle", in.header->isec, 0, - /*privateExtern=*/true, /*linkerInternal=*/true); + createSyntheticSymbols(); for (const Arg *arg : args.filtered(OPT_sectcreate)) { StringRef segName = arg->getValue(0); diff --git a/lld/MachO/OutputSegment.h b/lld/MachO/OutputSegment.h --- a/lld/MachO/OutputSegment.h +++ b/lld/MachO/OutputSegment.h @@ -24,6 +24,7 @@ constexpr const char dataConst[] = "__DATA_CONST"; constexpr const char ld[] = "__LD"; // output only with -r constexpr const char dwarf[] = "__DWARF"; +constexpr const char header[] = "__HEADER"; } // namespace segment_names diff --git a/lld/MachO/SymbolTable.h b/lld/MachO/SymbolTable.h --- a/lld/MachO/SymbolTable.h +++ b/lld/MachO/SymbolTable.h @@ -9,6 +9,8 @@ #ifndef LLD_MACHO_SYMBOL_TABLE_H #define LLD_MACHO_SYMBOL_TABLE_H +#include "Symbols.h" + #include "lld/Common/LLVM.h" #include "llvm/ADT/CachedHashString.h" #include "llvm/ADT/DenseMap.h" @@ -49,8 +51,8 @@ Symbol *addLazy(StringRef name, ArchiveFile *file, const llvm::object::Archive::Symbol &sym); - Defined *addSynthetic(StringRef name, InputSection *, uint32_t value, - bool isPrivateExtern, bool isLinkerInternal); + Symbol *addSynthetic(StringRef name, InputSection *, uint32_t value, + bool isPrivateExtern, SymbolTablePresence presence); ArrayRef getSymbols() const { return symVector; } Symbol *find(StringRef name); diff --git a/lld/MachO/SymbolTable.cpp b/lld/MachO/SymbolTable.cpp --- a/lld/MachO/SymbolTable.cpp +++ b/lld/MachO/SymbolTable.cpp @@ -158,12 +158,12 @@ return s; } -Defined *SymbolTable::addSynthetic(StringRef name, InputSection *isec, - uint32_t value, bool isPrivateExtern, - bool isLinkerInternal) { - Defined *s = addDefined(name, nullptr, isec, value, /*isWeakDef=*/false, - isPrivateExtern); - s->linkerInternal = isLinkerInternal; +Symbol *SymbolTable::addSynthetic(StringRef name, InputSection *isec, + uint32_t value, bool isPrivateExtern, + SymbolTablePresence presence) { + Symbol *s = addDefined(name, nullptr, isec, value, + /*isWeakDef=*/false, isPrivateExtern); + s->presence = presence; return s; } diff --git a/lld/MachO/Symbols.h b/lld/MachO/Symbols.h --- a/lld/MachO/Symbols.h +++ b/lld/MachO/Symbols.h @@ -31,6 +31,8 @@ const uint32_t size; }; +enum SymbolTablePresence { Unset, ForcedPresence, ForcedAbsence }; + class Symbol { public: enum Kind { @@ -81,11 +83,16 @@ uint32_t symtabIndex = UINT32_MAX; + // Whether this symbol should be forced to appear in the output binary's + // symbol table. + SymbolTablePresence presence : 2; + InputFile *getFile() const { return file; } protected: Symbol(Kind k, StringRefZ name, InputFile *file) - : symbolKind(k), nameData(name.data), nameSize(name.size), file(file) {} + : presence(Unset), symbolKind(k), nameData(name.data), + nameSize(name.size), file(file) {} Kind symbolKind; const char *nameData; @@ -99,7 +106,7 @@ bool isWeakDef, bool isExternal, bool isPrivateExtern) : Symbol(DefinedKind, name, file), isec(isec), value(value), overridesWeakDef(false), privateExtern(isPrivateExtern), - linkerInternal(false), weakDef(isWeakDef), external(isExternal) {} + weakDef(isWeakDef), external(isExternal) {} bool isWeakDef() const override { return weakDef; } bool isExternalWeakDef() const { @@ -124,8 +131,6 @@ bool overridesWeakDef : 1; // Whether this symbol should appear in the output binary's export trie. bool privateExtern : 1; - // Whether this symbol should appear in the output binary's symbol table. - bool linkerInternal : 1; private: const bool weakDef : 1; diff --git a/lld/MachO/SyntheticSections.h b/lld/MachO/SyntheticSections.h --- a/lld/MachO/SyntheticSections.h +++ b/lld/MachO/SyntheticSections.h @@ -83,11 +83,21 @@ uint64_t getSize() const override; void writeTo(uint8_t *buf) const override; +protected: + MachHeaderSection(const char *segname, const char *name); + private: std::vector loadCommands; uint32_t sizeOfCmds = 0; }; +// This is very similar to MachHeaderSection, except +// it is in __HEADER segment. +class PreloadHeaderSection : public MachHeaderSection { +public: + PreloadHeaderSection(); +}; + // A hidden section that exists solely for the purpose of creating the // __PAGEZERO segment, which is used to catch null pointer dereferences. class PageZeroSection : public SyntheticSection { @@ -506,11 +516,14 @@ StubsSection *stubs = nullptr; StubHelperSection *stubHelper = nullptr; ImageLoaderCacheSection *imageLoaderCache = nullptr; + PreloadHeaderSection *preload = nullptr; }; extern InStruct in; extern std::vector syntheticSections; +void createSyntheticSymbols(); + } // namespace macho } // namespace lld diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp --- a/lld/MachO/SyntheticSections.cpp +++ b/lld/MachO/SyntheticSections.cpp @@ -55,6 +55,9 @@ MachHeaderSection::MachHeaderSection() : SyntheticSection(segment_names::text, section_names::header) {} +MachHeaderSection::MachHeaderSection(const char *segname, const char *name) + : SyntheticSection(segname, name) {} + void MachHeaderSection::addLoadCommand(LoadCommand *lc) { loadCommands.push_back(lc); sizeOfCmds += lc->getSize(); @@ -64,6 +67,9 @@ return sizeof(mach_header_64) + sizeOfCmds + config->headerPad; } +PreloadHeaderSection::PreloadHeaderSection() + : MachHeaderSection(segment_names::header, section_names::header) {} + static uint32_t cpuSubtype() { uint32_t subtype = target->cpuSubtype; @@ -766,13 +772,16 @@ for (Symbol *sym : symtab->getSymbols()) { if (auto *defined = dyn_cast(sym)) { - if (defined->linkerInternal) + if (defined->presence == ForcedAbsence) continue; assert(defined->isExternal()); addSymbol(externalSymbols, defined); } else if (auto *dysym = dyn_cast(sym)) { if (dysym->isReferenced()) addSymbol(undefinedSymbols, sym); + } else if (auto *undef = dyn_cast(sym)) { + if (undef->presence == ForcedPresence) + addSymbol(undefinedSymbols, undef); } } @@ -1005,3 +1014,59 @@ memcpy(id, fileName.begin(), fileName.size()); memset(id + fileName.size(), 0, fileNamePad); } + +macho::InputSection text_mach_header_section = []() { + macho::InputSection ret; + ret.segname = "__TEXT"; + ret.name = section_names::header; +}(); + +void macho::createSyntheticSymbols() { + // The Itanium C++ ABI requires dylibs to pass a pointer to __cxa_atexit + // which does e.g. cleanup of static global variables. The ABI document + // says that the pointer can point to any address in one of the dylib's + // segments, but in practice ld64 seems to set it to point to the header, + // so that's what's implemented here. + symtab->addSynthetic("___dso_handle", in.header->isec, 0, + /*privateExtern=*/true, + SymbolTablePresence::ForcedAbsence); + +#define addSynHelper(name) \ + symtab->addSynthetic(name, in.header->isec, 0, /*privateExtern=*/false, \ + SymbolTablePresence::ForcedAbscence) + + switch (config->outputType) { + // From the Apple documentation, this symbol is an absolute + // symbol. + case MH_EXECUTE: + symtab->addSynthetic("__mh_execute_header", /*isec*/ nullptr, 0, + /*privateExtern*/ false, + SymbolTablePresence::ForcedPresence); + break; + + // From the Apple documentation, the following symbols are + // N_SECT symbol, even though the header is not part of any section + // and that they have private to the TU they are part of. + + case MH_BUNDLE: + addSynHelper("__mh_bundle_header"); + break; + case MH_DYLIB: + addSynHelper("__mh_dylib_header"); + break; + case MH_DYLINKER: + addSynHelper("__mh_dylinker_header"); + break; + case MH_OBJECT: + addSynHelper("__mh_object_header"); + break; + case MH_PRELOAD: + symtab->addSynthetic("__mh_execute_header", in.preload->isec, 0, + /*privateExtern*/ false, + SymbolTablePresence::ForcedAbscence); + break; + default: + break; + } +#undef addSynHelper +} diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -914,6 +914,7 @@ in.stubs = make(); in.stubHelper = make(); in.imageLoaderCache = make(); + in.preload = make(); } OutputSection *macho::firstTLVDataSection = nullptr; diff --git a/lld/test/MachO/load-commands.s b/lld/test/MachO/load-commands.s --- a/lld/test/MachO/load-commands.s +++ b/lld/test/MachO/load-commands.s @@ -41,6 +41,7 @@ ## Check for the presence / absence of load commands for the bundle. # RUN: llvm-objdump --macho --all-headers %t/bundle | FileCheck %s --check-prefix=COMMON # RUN: llvm-objdump --macho --all-headers %t/bundle | FileCheck %s --check-prefix=BUNDLE +# BUNDLE: __mh_bundle_header # BUNDLE: magic cputype cpusubtype caps filetype # BUNDLE-NEXT: MH_MAGIC_64 X86_64 ALL {{.*}} BUNDLE diff --git a/lld/test/MachO/objc.s b/lld/test/MachO/objc.s --- a/lld/test/MachO/objc.s +++ b/lld/test/MachO/objc.s @@ -32,6 +32,7 @@ # NO-OBJC-EMPTY: # NO-OBJC-NEXT: SYMBOL TABLE: # NO-OBJC-NEXT: g F __TEXT,__text _main +# NO-OBJC-NEXT: 0000000000000000 *UND* __mh_execute_header # NO-OBJC-EMPTY: #--- has-objc-symbol.s diff --git a/lld/test/MachO/stabs.s b/lld/test/MachO/stabs.s --- a/lld/test/MachO/stabs.s +++ b/lld/test/MachO/stabs.s @@ -60,6 +60,7 @@ # CHECK-NEXT: [[#ZERO]] S _zero # CHECK-NEXT: [[#FOO]] T _foo # CHECK-NEXT: {{[0-9af]+}} T _no_debug +# CHECK-NEXT: {{0*}} ? __mh_execute_header # CHECK-EMPTY: ## Check that we don't attempt to emit rebase opcodes for the debug sections diff --git a/lld/test/MachO/symtab.s b/lld/test/MachO/symtab.s --- a/lld/test/MachO/symtab.s +++ b/lld/test/MachO/symtab.s @@ -77,6 +77,15 @@ # CHECK-NEXT: ] # CHECK-NEXT: Value: 0x0 # CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: __mh_execute_header +# CHECK-NEXT: Type: Undef (0x0) +# CHECK-NEXT: Section: (0x0) +# CHECK-NEXT: RefType: UndefinedNonLazy (0x0) +# CHECK-NEXT: Flags [ (0x0) +# CHECK-NEXT: ] +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: } # CHECK-NEXT: ] # CHECK-NEXT: Dysymtab { # CHECK-NEXT: ilocalsym: 0 @@ -84,7 +93,7 @@ # CHECK-NEXT: iextdefsym: 2 # CHECK-NEXT: nextdefsym: 3 # CHECK-NEXT: iundefsym: 5 -# CHECK-NEXT: nundefsym: 2 +# CHECK-NEXT: nundefsym: 3 ## Verify that the first entry in the StringTable is a space, and that ## unreferenced symbols aren't emitted.