Index: lld/MachO/Driver.cpp =================================================================== --- lld/MachO/Driver.cpp +++ lld/MachO/Driver.cpp @@ -794,11 +794,6 @@ config->namespaceKind = arg->getOption().getID() == OPT_twolevel_namespace ? NamespaceKind::twolevel : NamespaceKind::flat; - if (config->namespaceKind == NamespaceKind::flat) { - warn("Option '" + arg->getOption().getPrefixedName() + - "' is not yet implemented. Stay tuned..."); - config->namespaceKind = NamespaceKind::twolevel; - } } config->systemLibraryRoots = getSystemLibraryRoots(args); Index: lld/MachO/InputFiles.cpp =================================================================== --- lld/MachO/InputFiles.cpp +++ lld/MachO/InputFiles.cpp @@ -680,21 +680,30 @@ return; } - if (hdr->flags & MH_NO_REEXPORTED_DYLIBS) - return; - const uint8_t *p = reinterpret_cast(hdr) + sizeof(mach_header_64); for (uint32_t i = 0, n = hdr->ncmds; i < n; ++i) { auto *cmd = reinterpret_cast(p); p += cmd->cmdsize; - if (cmd->cmd != LC_REEXPORT_DYLIB) - continue; - auto *c = reinterpret_cast(cmd); - StringRef reexportPath = - reinterpret_cast(c) + read32le(&c->dylib.name); - loadReexport(reexportPath, umbrella); + if (!(hdr->flags & MH_NO_REEXPORTED_DYLIBS) && + cmd->cmd == LC_REEXPORT_DYLIB) { + auto *c = reinterpret_cast(cmd); + StringRef reexportPath = + reinterpret_cast(c) + read32le(&c->dylib.name); + loadReexport(reexportPath, umbrella); + } + + // FIXME: What about LC_LOAD_UPWARD_DYLIB, LC_LAZY_LOAD_DYLIB, + // LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB (..are reexports from dylibs with + // MH_NO_REEXPORTED_DYLIBS loaded for -flat_namespace)? + if (config->namespaceKind == NamespaceKind::flat && + cmd->cmd == LC_LOAD_DYLIB) { + auto *c = reinterpret_cast(cmd); + StringRef dylibPath = + reinterpret_cast(c) + read32le(&c->dylib.name); + ::loadDylib(dylibPath, umbrella); + } } } Index: lld/MachO/Options.td =================================================================== --- lld/MachO/Options.td +++ lld/MachO/Options.td @@ -448,7 +448,6 @@ Group; def flat_namespace : Flag<["-"], "flat_namespace">, HelpText<"Resolve symbols from all dylibs, both direct and transitive. Do not record source libraries: dyld must re-search at runtime and use the first definition found">, - Flags<[HelpHidden]>, Group; def twolevel_namespace : Flag<["-"], "twolevel_namespace">, HelpText<"Make dyld look up symbols by (dylib,name) pairs (default)">, Index: lld/MachO/SymbolTable.cpp =================================================================== --- lld/MachO/SymbolTable.cpp +++ lld/MachO/SymbolTable.cpp @@ -181,12 +181,14 @@ message += "\n>>> referenced by " + fileName; switch (config->undefinedSymbolTreatment) { case UndefinedSymbolTreatment::suppress: + error("-undefined suppress unimplemented"); break; case UndefinedSymbolTreatment::error: error(message); break; case UndefinedSymbolTreatment::warning: warn(message); + error("-undefined warning unimplemented"); break; case UndefinedSymbolTreatment::dynamic_lookup: error("dynamic_lookup unimplemented for " + message); Index: lld/MachO/SyntheticSections.cpp =================================================================== --- lld/MachO/SyntheticSections.cpp +++ lld/MachO/SyntheticSections.cpp @@ -78,7 +78,10 @@ hdr->filetype = config->outputType; hdr->ncmds = loadCommands.size(); hdr->sizeofcmds = sizeOfCmds; - hdr->flags = MachO::MH_NOUNDEFS | MachO::MH_DYLDLINK | MachO::MH_TWOLEVEL; + hdr->flags = MachO::MH_DYLDLINK; + + if (config->namespaceKind == NamespaceKind::twolevel) + hdr->flags |= MachO::MH_NOUNDEFS | MachO::MH_TWOLEVEL; if (config->outputType == MachO::MH_DYLIB && !config->hasReexports) hdr->flags |= MachO::MH_NO_REEXPORTED_DYLIBS; @@ -280,8 +283,9 @@ // Non-weak bindings need to have their dylib ordinal encoded as well. static int16_t ordinalForDylibSymbol(const DylibSymbol &dysym) { - return dysym.isDynamicLookup() ? MachO::BIND_SPECIAL_DYLIB_FLAT_LOOKUP - : dysym.getFile()->ordinal; + return config->namespaceKind == NamespaceKind::flat || dysym.isDynamicLookup() + ? MachO::BIND_SPECIAL_DYLIB_FLAT_LOOKUP + : dysym.getFile()->ordinal; } static void encodeDylibOrdinal(int16_t ordinal, raw_svector_ostream &os) { @@ -816,13 +820,15 @@ nList->n_desc |= defined->isExternalWeakDef() ? MachO::N_WEAK_DEF : 0; } else if (auto *dysym = dyn_cast(entry.sym)) { uint16_t n_desc = nList->n_desc; - if (dysym->isDynamicLookup()) + int16_t ordinal = ordinalForDylibSymbol(*dysym); + if (ordinal == MachO::BIND_SPECIAL_DYLIB_FLAT_LOOKUP) MachO::SET_LIBRARY_ORDINAL(n_desc, MachO::DYNAMIC_LOOKUP_ORDINAL); - else if (dysym->getFile()->isBundleLoader) + else if (ordinal == MachO::BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE) MachO::SET_LIBRARY_ORDINAL(n_desc, MachO::EXECUTABLE_ORDINAL); - else - MachO::SET_LIBRARY_ORDINAL( - n_desc, static_cast(dysym->getFile()->ordinal)); + else { + assert(ordinal > 0); + MachO::SET_LIBRARY_ORDINAL(n_desc, static_cast(ordinal)); + } nList->n_type = MachO::N_EXT; n_desc |= dysym->isWeakDef() ? MachO::N_WEAK_DEF : 0; Index: lld/test/MachO/flat-namespace.s =================================================================== --- /dev/null +++ lld/test/MachO/flat-namespace.s @@ -0,0 +1,83 @@ +# REQUIRES: x86 +# RUN: rm -rf %t +# RUN: split-file %s %t + +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/foo.o %t/foo.s +# RUN: %lld -dylib -o %t/foo.dylib %t/foo.o + +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/bar.o %t/bar.s +# RUN: %lld -lSystem -dylib -o %t/bar.dylib %t/bar.o %t/foo.dylib + +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/baz.o %t/baz.s +# RUN: %lld -lSystem -dylib -o %t/baz.dylib %t/baz.o %t/bar.dylib + +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/main.o %t/main.s + +# With flat_namespace, the linker automatically looks in foo.dylib and +# bar.dylib too, but it doesn't add a LC_LOAD_DYLIB for it. +# RUN: %lld -flat_namespace -lSystem %t/main.o %t/baz.dylib -o %t/out +# RUN: llvm-objdump --macho --all-headers %t/out \ +# RUN: | FileCheck --check-prefix=HEADERBITS %s +# RUN: llvm-objdump --macho --bind --lazy-bind --weak-bind %t/out \ +# RUN: | FileCheck --check-prefix=FLAT %s +# RUN: llvm-nm -m %t/out | FileCheck --check-prefix=FLATSYM %s + +# HEADERBITS-NOT: NOUNDEFS +# HEADERBITS-NOT: TWOLEVEL +# HEADERBITS: DYLDLINK +# HEADERBITS-NOT: foo.dylib +# HEADERBITS-NOT: bar.dylib + +# FLAT: Bind table: +# FLAT: __DATA_CONST __got 0x{{[0-9a-f]*}} pointer 0 flat-namespace dyld_stub_binder +# FLAT: Lazy bind table: +# FLAT-DAG: __DATA __la_symbol_ptr 0x{{[0-9a-f]*}} flat-namespace _bar +# FLAT-DAG: __DATA __la_symbol_ptr 0x{{[0-9a-f]*}} flat-namespace _baz +# FLAT-DAG: __DATA __la_symbol_ptr 0x{{[0-9a-f]*}} flat-namespace _foo + +# No "(dynamically looked up)" because llvm-nm -m doesn't print that +# for files without MH_TWOLEVEL for some reason. +# FLATSYM: (undefined) external _bar +# FLATSYM: (undefined) external _baz +# FLATSYM: (undefined) external _foo + +# Undefined symbols should still cause errors by default. +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos \ +# RUN: -o %t/main-with-undef.o %t/main-with-undef.s +# RUN: not %lld -flat_namespace -lSystem %t/main-with-undef.o %t/bar.dylib \ +# RUN: -o %t/out 2>&1 | FileCheck --check-prefix=UNDEF %s +# UNDEF: error: undefined symbol: _quux + +#--- foo.s +.globl _foo +_foo: + ret + +#--- bar.s +.globl _bar +_bar: + callq _foo + ret + +#--- baz.s +.globl _baz +_baz: + callq _bar + ret + +#--- main.s +.globl _main +_main: + callq _foo + callq _bar + callq _baz + ret + +#--- main-with-undef.s +.globl _main +_main: + callq _foo + callq _bar + callq _baz + callq _quux + ret Index: lld/test/MachO/header.s =================================================================== --- lld/test/MachO/header.s +++ lld/test/MachO/header.s @@ -13,7 +13,7 @@ # RUN: llvm-objdump --macho --all-headers %t/arm64-dylib | FileCheck %s -DCAPS=0x00 # CHECK: magic cputype cpusubtype caps filetype -# CHECK-NEXT: MH_MAGIC_64 {{.*}} ALL [[CAPS]] {{.*}} +# CHECK-NEXT: MH_MAGIC_64 {{.*}} ALL [[CAPS]] {{.*}} TWOLEVEL .globl _main _main: