Skip to content

Commit 8174f33

Browse files
committed
[lld/mac] Add support for -flat_namespace
-flat_namespace makes lld emit binaries that use name lookup that's more in line with other POSIX systems: Instead of looking up symbols as (dylib,name) pairs by dyld, they're instead looked up just by name. -flat_namespace has three effects: 1. MH_TWOLEVEL and MH_NNOUNDEFS are no longer set in the Mach-O header 2. All symbols use BIND_SPECIAL_DYLIB_FLAT_LOOKUP as ordinal 3. When a dylib is added to the link, its dependent dylibs are also added, so that lld can verify that no undefined symbols remain at the end of a link with -flat_namespace. These transitive dylibs are added for symbol resolution, but they are not emitted in LC_LOAD_COMMANDs. -undefined with -flat_namespace still isn't implemented. Before this change, it was impossible to hit that combination because -flat_namespace caused a diagnostic. Now that it no longer does, emit a dedicated temporary diagnostic when both flags are used. Differential Revision: https://reviews.llvm.org/D97641
1 parent 3e6b6ce commit 8174f33

File tree

7 files changed

+132
-31
lines changed

7 files changed

+132
-31
lines changed

lld/MachO/Driver.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -796,11 +796,6 @@ bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
796796
config->namespaceKind = arg->getOption().getID() == OPT_twolevel_namespace
797797
? NamespaceKind::twolevel
798798
: NamespaceKind::flat;
799-
if (config->namespaceKind == NamespaceKind::flat) {
800-
warn("Option '" + arg->getOption().getPrefixedName() +
801-
"' is not yet implemented. Stay tuned...");
802-
config->namespaceKind = NamespaceKind::twolevel;
803-
}
804799
}
805800

806801
config->systemLibraryRoots = getSystemLibraryRoots(args);

lld/MachO/InputFiles.cpp

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -587,15 +587,14 @@ const InterfaceFile *currentTopLevelTapi = nullptr;
587587

588588
// Re-exports can either refer to on-disk files, or to documents within .tbd
589589
// files.
590-
static Optional<DylibFile *> loadReexportHelper(StringRef path,
591-
DylibFile *umbrella) {
590+
static Optional<DylibFile *> findDylib(StringRef path, DylibFile *umbrella) {
592591
if (path::is_absolute(path, path::Style::posix))
593592
for (StringRef root : config->systemLibraryRoots)
594593
if (Optional<std::string> dylibPath =
595594
resolveDylibPath((root + path).str()))
596595
return loadDylib(*dylibPath, umbrella);
597596

598-
// TODO: Expand @loader_path, @executable_path etc
597+
// TODO: Expand @loader_path, @executable_path, @rpath etc, handle -dylib_path
599598

600599
if (currentTopLevelTapi) {
601600
for (InterfaceFile &child :
@@ -609,7 +608,6 @@ static Optional<DylibFile *> loadReexportHelper(StringRef path,
609608
if (Optional<std::string> dylibPath = resolveDylibPath(path))
610609
return loadDylib(*dylibPath, umbrella);
611610

612-
error("unable to locate re-export with install name " + path);
613611
return {};
614612
}
615613

@@ -634,8 +632,10 @@ static bool isImplicitlyLinked(StringRef path) {
634632
}
635633

636634
void loadReexport(StringRef path, DylibFile *umbrella) {
637-
Optional<DylibFile *> reexport = loadReexportHelper(path, umbrella);
638-
if (reexport && isImplicitlyLinked(path))
635+
Optional<DylibFile *> reexport = findDylib(path, umbrella);
636+
if (!reexport)
637+
error("unable to locate re-export with install name " + path);
638+
else if (isImplicitlyLinked(path))
639639
inputFiles.insert(*reexport);
640640
}
641641

@@ -679,21 +679,33 @@ DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella,
679679
return;
680680
}
681681

682-
if (hdr->flags & MH_NO_REEXPORTED_DYLIBS)
683-
return;
684-
685682
const uint8_t *p =
686683
reinterpret_cast<const uint8_t *>(hdr) + sizeof(mach_header_64);
687684
for (uint32_t i = 0, n = hdr->ncmds; i < n; ++i) {
688685
auto *cmd = reinterpret_cast<const load_command *>(p);
689686
p += cmd->cmdsize;
690-
if (cmd->cmd != LC_REEXPORT_DYLIB)
691-
continue;
692687

693-
auto *c = reinterpret_cast<const dylib_command *>(cmd);
694-
StringRef reexportPath =
695-
reinterpret_cast<const char *>(c) + read32le(&c->dylib.name);
696-
loadReexport(reexportPath, umbrella);
688+
if (!(hdr->flags & MH_NO_REEXPORTED_DYLIBS) &&
689+
cmd->cmd == LC_REEXPORT_DYLIB) {
690+
const auto *c = reinterpret_cast<const dylib_command *>(cmd);
691+
StringRef reexportPath =
692+
reinterpret_cast<const char *>(c) + read32le(&c->dylib.name);
693+
loadReexport(reexportPath, umbrella);
694+
}
695+
696+
// FIXME: What about LC_LOAD_UPWARD_DYLIB, LC_LAZY_LOAD_DYLIB,
697+
// LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB (..are reexports from dylibs with
698+
// MH_NO_REEXPORTED_DYLIBS loaded for -flat_namespace)?
699+
if (config->namespaceKind == NamespaceKind::flat &&
700+
cmd->cmd == LC_LOAD_DYLIB) {
701+
const auto *c = reinterpret_cast<const dylib_command *>(cmd);
702+
StringRef dylibPath =
703+
reinterpret_cast<const char *>(c) + read32le(&c->dylib.name);
704+
Optional<DylibFile *> dylib = findDylib(dylibPath, umbrella);
705+
if (!dylib)
706+
error(Twine("unable to locate library '") + dylibPath +
707+
"' loaded from '" + toString(this) + "' for -flat_namespace");
708+
}
697709
}
698710
}
699711

lld/MachO/Options.td

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,6 @@ def alias_list : Separate<["-"], "alias_list">,
448448
Group<grp_resolve>;
449449
def flat_namespace : Flag<["-"], "flat_namespace">,
450450
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">,
451-
Flags<[HelpHidden]>,
452451
Group<grp_resolve>;
453452
def twolevel_namespace : Flag<["-"], "twolevel_namespace">,
454453
HelpText<"Make dyld look up symbols by (dylib,name) pairs (default)">,

lld/MachO/SymbolTable.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,14 @@ void lld::macho::treatUndefinedSymbol(const Undefined &sym) {
181181
message += "\n>>> referenced by " + fileName;
182182
switch (config->undefinedSymbolTreatment) {
183183
case UndefinedSymbolTreatment::suppress:
184+
error("-undefined suppress unimplemented");
184185
break;
185186
case UndefinedSymbolTreatment::error:
186187
error(message);
187188
break;
188189
case UndefinedSymbolTreatment::warning:
189190
warn(message);
191+
error("-undefined warning unimplemented");
190192
break;
191193
case UndefinedSymbolTreatment::dynamic_lookup:
192194
error("dynamic_lookup unimplemented for " + message);

lld/MachO/SyntheticSections.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,10 @@ void MachHeaderSection::writeTo(uint8_t *buf) const {
7878
hdr->filetype = config->outputType;
7979
hdr->ncmds = loadCommands.size();
8080
hdr->sizeofcmds = sizeOfCmds;
81-
hdr->flags = MachO::MH_NOUNDEFS | MachO::MH_DYLDLINK | MachO::MH_TWOLEVEL;
81+
hdr->flags = MachO::MH_DYLDLINK;
82+
83+
if (config->namespaceKind == NamespaceKind::twolevel)
84+
hdr->flags |= MachO::MH_NOUNDEFS | MachO::MH_TWOLEVEL;
8285

8386
if (config->outputType == MachO::MH_DYLIB && !config->hasReexports)
8487
hdr->flags |= MachO::MH_NO_REEXPORTED_DYLIBS;
@@ -280,8 +283,9 @@ static void encodeBinding(const Symbol *sym, const OutputSection *osec,
280283

281284
// Non-weak bindings need to have their dylib ordinal encoded as well.
282285
static int16_t ordinalForDylibSymbol(const DylibSymbol &dysym) {
283-
return dysym.isDynamicLookup() ? MachO::BIND_SPECIAL_DYLIB_FLAT_LOOKUP
284-
: dysym.getFile()->ordinal;
286+
return config->namespaceKind == NamespaceKind::flat || dysym.isDynamicLookup()
287+
? MachO::BIND_SPECIAL_DYLIB_FLAT_LOOKUP
288+
: dysym.getFile()->ordinal;
285289
}
286290

287291
static void encodeDylibOrdinal(int16_t ordinal, raw_svector_ostream &os) {
@@ -816,13 +820,15 @@ void SymtabSection::writeTo(uint8_t *buf) const {
816820
nList->n_desc |= defined->isExternalWeakDef() ? MachO::N_WEAK_DEF : 0;
817821
} else if (auto *dysym = dyn_cast<DylibSymbol>(entry.sym)) {
818822
uint16_t n_desc = nList->n_desc;
819-
if (dysym->isDynamicLookup())
823+
int16_t ordinal = ordinalForDylibSymbol(*dysym);
824+
if (ordinal == MachO::BIND_SPECIAL_DYLIB_FLAT_LOOKUP)
820825
MachO::SET_LIBRARY_ORDINAL(n_desc, MachO::DYNAMIC_LOOKUP_ORDINAL);
821-
else if (dysym->getFile()->isBundleLoader)
826+
else if (ordinal == MachO::BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE)
822827
MachO::SET_LIBRARY_ORDINAL(n_desc, MachO::EXECUTABLE_ORDINAL);
823-
else
824-
MachO::SET_LIBRARY_ORDINAL(
825-
n_desc, static_cast<uint8_t>(dysym->getFile()->ordinal));
828+
else {
829+
assert(ordinal > 0);
830+
MachO::SET_LIBRARY_ORDINAL(n_desc, static_cast<uint8_t>(ordinal));
831+
}
826832

827833
nList->n_type = MachO::N_EXT;
828834
n_desc |= dysym->isWeakDef() ? MachO::N_WEAK_DEF : 0;

lld/test/MachO/flat-namespace.s

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# REQUIRES: x86
2+
# RUN: rm -rf %t
3+
# RUN: split-file %s %t
4+
5+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/foo.o %t/foo.s
6+
# RUN: %lld -dylib -o %t/foo.dylib %t/foo.o
7+
8+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/bar.o %t/bar.s
9+
# RUN: %lld -lSystem -dylib -o %t/bar.dylib %t/bar.o %t/foo.dylib
10+
11+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/baz.o %t/baz.s
12+
# RUN: %lld -lSystem -dylib -o %t/baz.dylib %t/baz.o %t/bar.dylib
13+
14+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/main.o %t/main.s
15+
16+
# With flat_namespace, the linker automatically looks in foo.dylib and
17+
# bar.dylib too, but it doesn't add a LC_LOAD_DYLIB for it.
18+
# RUN: %lld -flat_namespace -lSystem %t/main.o %t/baz.dylib -o %t/out
19+
# RUN: llvm-objdump --macho --all-headers %t/out \
20+
# RUN: | FileCheck --check-prefix=HEADERBITS %s
21+
# RUN: llvm-objdump --macho --bind --lazy-bind --weak-bind %t/out \
22+
# RUN: | FileCheck --check-prefix=FLAT %s
23+
# RUN: llvm-nm -m %t/out | FileCheck --check-prefix=FLATSYM %s
24+
# RUN: llvm-readobj --syms %t/out | FileCheck --check-prefix=FLATSYM-READOBJ %s
25+
26+
# HEADERBITS-NOT: NOUNDEFS
27+
# HEADERBITS-NOT: TWOLEVEL
28+
# HEADERBITS: DYLDLINK
29+
# HEADERBITS-NOT: foo.dylib
30+
# HEADERBITS-NOT: bar.dylib
31+
32+
# FLAT: Bind table:
33+
# FLAT: __DATA_CONST __got 0x{{[0-9a-f]*}} pointer 0 flat-namespace dyld_stub_binder
34+
# FLAT: Lazy bind table:
35+
# FLAT-DAG: __DATA __la_symbol_ptr 0x{{[0-9a-f]*}} flat-namespace _bar
36+
# FLAT-DAG: __DATA __la_symbol_ptr 0x{{[0-9a-f]*}} flat-namespace _baz
37+
# FLAT-DAG: __DATA __la_symbol_ptr 0x{{[0-9a-f]*}} flat-namespace _foo
38+
39+
# No "(dynamically looked up)" because llvm-nm -m doesn't print that
40+
# for files without MH_TWOLEVEL for some reason.
41+
# FLATSYM: (undefined) external _bar
42+
# FLATSYM: (undefined) external _baz
43+
# FLATSYM: (undefined) external _foo
44+
45+
# ...but `llvm-readobj --syms` does, so verify we put the right thing there.
46+
# FLATSYM-READOBJ: Flags [ (0xFE00)
47+
48+
# Undefined symbols should still cause errors by default.
49+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos \
50+
# RUN: -o %t/main-with-undef.o %t/main-with-undef.s
51+
# RUN: not %lld -flat_namespace -lSystem %t/main-with-undef.o %t/bar.dylib \
52+
# RUN: -o %t/out 2>&1 | FileCheck --check-prefix=UNDEF %s
53+
# UNDEF: error: undefined symbol: _quux
54+
55+
#--- foo.s
56+
.globl _foo
57+
_foo:
58+
ret
59+
60+
#--- bar.s
61+
.globl _bar
62+
_bar:
63+
callq _foo
64+
ret
65+
66+
#--- baz.s
67+
.globl _baz
68+
_baz:
69+
callq _bar
70+
ret
71+
72+
#--- main.s
73+
.globl _main
74+
_main:
75+
callq _foo
76+
callq _bar
77+
callq _baz
78+
ret
79+
80+
#--- main-with-undef.s
81+
.globl _main
82+
_main:
83+
callq _foo
84+
callq _bar
85+
callq _baz
86+
callq _quux
87+
ret

lld/test/MachO/header.s

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
# RUN: llvm-objdump --macho --all-headers %t/x86-64-dylib | FileCheck %s -DCAPS=0x00
1313
# RUN: llvm-objdump --macho --all-headers %t/arm64-dylib | FileCheck %s -DCAPS=0x00
1414

15-
# CHECK: magic cputype cpusubtype caps filetype
16-
# CHECK-NEXT: MH_MAGIC_64 {{.*}} ALL [[CAPS]] {{.*}}
15+
# CHECK: magic cputype cpusubtype caps filetype {{.*}} flags
16+
# CHECK-NEXT: MH_MAGIC_64 {{.*}} ALL [[CAPS]] {{.*}} NOUNDEFS {{.*}} TWOLEVEL
1717

1818
.globl _main
1919
_main:

0 commit comments

Comments
 (0)