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,45 @@ } 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); + { + // 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, + /*isDefined*/ true); + +#define addUndefSyn(name) \ + symtab->addSynthetic(name, in.header->isec, 0, /*privateExtern=*/false, \ + SymbolTablePresence::ForcedPresence, \ + /*isDefined*/ false); + switch (config->outputType) { + case MH_BUNDLE: + addUndefSyn("__mh_bundle_header"); + break; + case MH_DYLIB: + addUndefSyn("__mh_dylib_header"); + break; + case MH_DYLINKER: + addUndefSyn("__mh_dylinker_header"); + break; + case MH_EXECUTE: + addUndefSyn("__mh_execute_header"); + break; + case MH_OBJECT: + addUndefSyn("__mh_object_header"); + break; + case MH_PRELOAD: + addUndefSyn("__mh_preload_header"); + break; + default: + break; + } +#undef addUndefSyn + } for (const Arg *arg : args.filtered(OPT_sectcreate)) { StringRef segName = arg->getValue(0); 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,9 @@ 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, + bool isDefined); 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,14 @@ 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, + bool isDefined) { + Symbol *s = isDefined ? addDefined(name, nullptr, isec, value, + /*isWeakDef=*/false, isPrivateExtern) + : addUndefined(name, nullptr, /*isWeakRef*/ false); + 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.cpp b/lld/MachO/SyntheticSections.cpp --- a/lld/MachO/SyntheticSections.cpp +++ b/lld/MachO/SyntheticSections.cpp @@ -766,13 +766,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); } } 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.