InputSection *isec, uint64_t value,
uint64_t size) {
// Symbol scope is determined by sym.n_type & (N_EXT | N_PEXT):
- // N_EXT: Global symbols
- // N_EXT | N_PEXT: Linkage unit (think: dylib) scoped
+ // N_EXT: Global symbols. These go in the symbol table during the link,
+ // and also in the export table of the output so that the dynamic
+ // linker sees them.
+ // N_EXT | N_PEXT: Linkage unit (think: dylib) scoped. These go in the
+ // symbol table during the link so that duplicates are
+ // either reported (for non-weak symbols) or merged
+ // (for weak symbols), but they do not go in the export
+ // table of the output.
// N_PEXT: Does not occur in input files in practice,
// a private extern must be external.
- // 0: Translation-unit scoped. These are not in the symbol table.
+ // 0: Translation-unit scoped. These are not in the symbol table during
+ // link, and not in the export table of the output either.
+
+ bool isWeakDefCanBeHidden =
+ (sym.n_desc & (N_WEAK_DEF | N_WEAK_REF)) == (N_WEAK_DEF | N_WEAK_REF);
if (sym.n_type & (N_EXT | N_PEXT)) {
assert((sym.n_type & N_EXT) && "invalid input");
+ bool isPrivateExtern = sym.n_type & N_PEXT;
+
+ // lld's behavior for merging symbols is slightly different from ld64:
+ // ld64 picks the winning symbol based on several criteria (see
+ // pickBetweenRegularAtoms() in ld64's SymbolTable.cpp), while lld
+ // just merges metadata and keeps the contents of the first symbol
+ // with that name (see SymbolTable::addDefined). For:
+ // * inline function F in a TU built with -fvisibility-inlines-hidden
+ // * and inline function F in another TU built without that flag
+ // ld64 will pick the one from the file built without
+ // -fvisibility-inlines-hidden.
+ // lld will instead pick the one listed first on the link command line and
+ // give it visibility as if the function was built without
+ // -fvisibility-inlines-hidden.
+ // If both functions have the same contents, this will have the same
+ // behavior. If not, it won't, but the input had an ODR violation in
+ // that case.
+ //
+ // Similarly, merging a symbol
+ // that's isPrivateExtern and not isWeakDefCanBeHidden with one
+ // that's not isPrivateExtern but isWeakDefCanBeHidden technically
+ // should produce one
+ // that's not isPrivateExtern but isWeakDefCanBeHidden. That matters
+ // with ld64's semantics, because it means the non-private-extern
+ // definition will continue to take priority if more private extern
+ // definitions are encountered. With lld's semantics there's no observable
+ // difference between a symbol that's isWeakDefCanBeHidden or one that's
+ // privateExtern -- neither makes it into the dynamic symbol table. So just
+ // promote isWeakDefCanBeHidden to isPrivateExtern here.
+ if (isWeakDefCanBeHidden)
+ isPrivateExtern = true;
+
return symtab->addDefined(name, isec->file, isec, value, size,
- sym.n_desc & N_WEAK_DEF, sym.n_type & N_PEXT);
+ sym.n_desc & N_WEAK_DEF, isPrivateExtern);
}
+
+ assert(!isWeakDefCanBeHidden &&
+ "weak_def_can_be_hidden on already-hidden symbol?");
return make<Defined>(name, isec->file, isec, value, size,
sym.n_desc & N_WEAK_DEF,
/*isExternal=*/false, /*isPrivateExtern=*/false);
--- /dev/null
+# REQUIRES: x86
+# RUN: rm -rf %t; split-file %s %t
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos \
+# RUN: %t/weak-foo.s -o %t/weak-foo.o
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos \
+# RUN: %t/weak-autohide-foo.s -o %t/weak-autohide-foo.o
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos \
+# RUN: %t/weak-foo-pe.s -o %t/weak-foo-pe.o
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos \
+# RUN: %t/weak-autohide-foo-pe.s -o %t/weak-autohide-foo-pe.o
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos \
+# RUN: %t/ref-foo.s -o %t/ref-foo.o
+
+## Basics: A weak_def_can_be_hidden symbol should not be in the output's
+## export table, nor in the weak bind table, and references to it from
+## within the dylib should not use weak indirect lookups.
+## Think: Inline function compiled without any -fvisibility flags, inline
+## function does not have its address taken. In -O2 compiles, GlobalOpt will
+## upgrade the inline function from .weak_definition to .weak_def_can_be_hidden.
+# RUN: %lld -dylib -o %t/weak-autohide-foo.dylib \
+# RUN: %t/weak-autohide-foo.o %t/ref-foo.o
+# RUN: llvm-objdump --syms --exports-trie %t/weak-autohide-foo.dylib | \
+# RUN: FileCheck --check-prefix=EXPORTS %s
+# RUN: llvm-nm -m %t/weak-autohide-foo.dylib | \
+# RUN: FileCheck --check-prefix=EXPORTS-NM %s
+# RUN: llvm-objdump --macho --bind --weak-bind %t/weak-autohide-foo.dylib | \
+# RUN: FileCheck --check-prefix=WEAKBIND %s
+# RUN: llvm-objdump --macho --private-header %t/weak-autohide-foo.dylib | \
+# RUN: FileCheck --check-prefix=HEADERS %s
+
+## ld64 doesn't treat .weak_def_can_be_hidden as weak symbols in the
+## exports trie or bind tables, but it claims they are weak in the symbol
+## table. lld marks them as local in the symbol table, see also test
+## weak-private-extern.s. These FileCheck lines match both outputs.
+# EXPORTS-LABEL: SYMBOL TABLE:
+# EXPORTS-DAG: [[#%x, FOO_ADDR:]] {{.*}} _foo
+# EXPORTS-LABEL: Exports trie:
+# EXPORTS-NOT: 0x{{0*}}[[#%X, FOO_ADDR]] _foo
+
+## nm output for .weak_def_can_be_hidden says "was a private external" even
+## though it wasn't .private_extern: It was just .weak_def_can_be_hidden.
+## This matches ld64.
+# EXPORTS-NM: (__TEXT,__text) non-external (was a private external) _foo
+
+# WEAKBIND-NOT: __got
+# WEAKBIND-NOT: __la_symbol_ptr
+
+## ld64 sets WEAKBIND and BINDS_TO_WEAK in the mach-o header even though there
+## are no weak bindings or weak definitions after processing the autohide. That
+## looks like a bug in ld64 (?) If you change lit.local.cfg to set %lld to ld to
+## test compatibility, you have to add some arbitrary suffix to these two lines:
+# HEADERS-NOT: WEAK_DEFINES
+# HEADERS-NOT: BINDS_TO_WEAK
+
+## Same behavior for a symbol that's both .weak_def_can_be_hidden and
+## .private_extern. Think: Inline function compiled with
+## -fvisibility-inlines-hidden.
+# RUN: %lld -dylib -o %t/weak-autohide-foo-pe.dylib \
+# RUN: %t/weak-autohide-foo-pe.o %t/ref-foo.o
+# RUN: llvm-objdump --syms --exports-trie %t/weak-autohide-foo-pe.dylib | \
+# RUN: FileCheck --check-prefix=EXPORTS %s
+# RUN: llvm-nm -m %t/weak-autohide-foo-pe.dylib | \
+# RUN: FileCheck --check-prefix=EXPORTS-NM %s
+# RUN: llvm-objdump --macho --bind --weak-bind %t/weak-autohide-foo-pe.dylib | \
+# RUN: FileCheck --check-prefix=WEAKBIND %s
+# RUN: llvm-objdump --macho --private-header %t/weak-autohide-foo-pe.dylib | \
+# RUN: FileCheck --check-prefix=HEADERS %s
+
+## In fact, a regular weak symbol that's .private_extern behaves the same
+## as well.
+# RUN: %lld -dylib -o %t/weak-foo-pe.dylib %t/weak-foo-pe.o %t/ref-foo.o
+# RUN: llvm-objdump --syms --exports-trie %t/weak-foo-pe.dylib | \
+# RUN: FileCheck --check-prefix=EXPORTS %s
+# RUN: llvm-nm -m %t/weak-foo-pe.dylib | \
+# RUN: FileCheck --check-prefix=EXPORTS-NM %s
+# RUN: llvm-objdump --macho --bind --weak-bind %t/weak-foo-pe.dylib | \
+# RUN: FileCheck --check-prefix=WEAKBIND %s
+# RUN: llvm-objdump --macho --private-header %t/weak-foo-pe.dylib | \
+# RUN: FileCheck --check-prefix=HEADERS %s
+
+## Combining a regular weak_definition with a weak_def_can_be_hidden produces
+## a regular weak external.
+# RUN: %lld -dylib -o %t/weak-foo.dylib -lSystem \
+# RUN: %t/weak-autohide-foo.o %t/weak-foo.o %t/ref-foo.o
+# RUN: llvm-objdump --syms --exports-trie %t/weak-foo.dylib | \
+# RUN: FileCheck --check-prefix=WEAK %s
+# RUN: llvm-nm -m %t/weak-foo.dylib | \
+# RUN: FileCheck --check-prefix=WEAK-NM %s
+# RUN: llvm-objdump --macho --bind --weak-bind %t/weak-foo.dylib | \
+# RUN: FileCheck --check-prefix=WEAK-WEAKBIND %s
+# RUN: llvm-objdump --macho --private-header %t/weak-foo.dylib | \
+# RUN: FileCheck --check-prefix=WEAK-HEADERS %s
+# WEAK-LABEL: SYMBOL TABLE:
+# WEAK-DAG: [[#%x, FOO_ADDR:]] w {{.*}} _foo
+# WEAK-LABEL: Exports trie:
+# WEAK-DAG: 0x{{0*}}[[#%X, FOO_ADDR]] _foo
+# WEAK-NM: (__TEXT,__text) weak external _foo
+# WEAK-WEAKBIND: __la_symbol_ptr 0x{{.*}} pointer 0 _foo
+# WEAK-HEADERS: WEAK_DEFINES
+# WEAK-HEADERS: BINDS_TO_WEAK
+
+#--- weak-foo.s
+.globl _foo
+.weak_definition _foo
+_foo:
+ retq
+
+#--- weak-autohide-foo.s
+.globl _foo
+.weak_def_can_be_hidden _foo
+_foo:
+ retq
+
+#--- weak-foo-pe.s
+.private_extern _foo
+.globl _foo
+.weak_definition _foo
+_foo:
+ retq
+
+#--- weak-autohide-foo-pe.s
+.private_extern _foo
+.globl _foo
+.weak_def_can_be_hidden _foo
+_foo:
+ retq
+
+#--- ref-foo.s
+.globl _bar
+_bar:
+ callq _foo
+ retq