diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -1331,8 +1331,7 @@ if (config->hasExplicitExports) { parallelForEach(symtab->getSymbols(), [](Symbol *sym) { if (auto *defined = dyn_cast(sym)) { - StringRef symbolName = defined->getName(); - if (config->exportedSymbols.match(symbolName)) { + if (config->exportedSymbols.match(sym->getName())) { if (defined->privateExtern) { if (defined->weakDefCanBeHidden) { // weak_def_can_be_hidden symbols behave similarly to @@ -1348,6 +1347,8 @@ } else { defined->privateExtern = true; } + } else if (auto *dysym = dyn_cast(sym)) { + dysym->shouldReexport = config->exportedSymbols.match(sym->getName()); } }); } else if (!config->unexportedSymbols.empty()) { diff --git a/lld/MachO/ExportTrie.cpp b/lld/MachO/ExportTrie.cpp --- a/lld/MachO/ExportTrie.cpp +++ b/lld/MachO/ExportTrie.cpp @@ -58,21 +58,24 @@ struct ExportInfo { uint64_t address; + uint64_t ordinal = 0; uint8_t flags = 0; ExportInfo(const Symbol &sym, uint64_t imageBase) : address(sym.getVA() - imageBase) { using namespace llvm::MachO; - // Set the symbol type. if (sym.isWeakDef()) flags |= EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; - // TODO: Add proper support for re-exports & stub-and-resolver flags. - - // Set the symbol kind. - if (sym.isTlv()) { + if (sym.isTlv()) flags |= EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL; - } else if (auto *defined = dyn_cast(&sym)) { + // TODO: Add proper support for stub-and-resolver flags. + + if (auto *defined = dyn_cast(&sym)) { if (defined->isAbsolute()) flags |= EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE; + } else if (auto *dysym = dyn_cast(&sym)) { + flags |= EXPORT_SYMBOL_FLAGS_REEXPORT; + if (!dysym->isDynamicLookup()) + ordinal = dysym->getFile()->ordinal; } } }; @@ -87,19 +90,57 @@ // fixpoint. size_t offset = 0; + uint32_t getTerminalSize() const; // Returns whether the new estimated offset differs from the old one. bool updateOffset(size_t &nextOffset); void writeTo(uint8_t *buf) const; }; +// For regular symbols, the node layout (excluding the children) is +// +// uleb128 terminalSize; +// uleb128 flags; +// uleb128 address; +// +// For re-exported symbols, the layout is +// +// uleb128 terminalSize; +// uleb128 flags; +// uleb128 ordinal; +// char[] originalName; +// +// If libfoo.dylib is linked against libbar.dylib, and libfoo exports an alias +// _foo to a symbol _bar in libbar, then originalName will be "_bar". If libfoo +// re-exports _bar directly (i.e. not via an alias), then originalName will be +// the empty string. +// +// TODO: Support aliased re-exports. (Since we don't yet support these, +// originalName will always be the empty string.) +// +// For stub-and-resolver nodes, the layout is +// +// uleb128 terminalSize; +// uleb128 flags; +// uleb128 stubAddress; +// uleb128 resolverAddress; +// +// TODO: Support stub-and-resolver nodes. +uint32_t TrieNode::getTerminalSize() const { + uint32_t size = getULEB128Size(info->flags); + if (info->flags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT) + size += getULEB128Size(info->ordinal) + 1; // + 1 for the null-terminator + else + size += getULEB128Size(info->address); + return size; +} + bool TrieNode::updateOffset(size_t &nextOffset) { // Size of the whole node (including the terminalSize and the outgoing edges.) // In contrast, terminalSize only records the size of the other data in the // node. size_t nodeSize; if (info) { - uint32_t terminalSize = - getULEB128Size(info->flags) + getULEB128Size(info->address); + uint32_t terminalSize = getTerminalSize(); // Overall node size so far is the uleb128 size of the length of the symbol // info + the symbol info itself. nodeSize = terminalSize + getULEB128Size(terminalSize); @@ -123,12 +164,15 @@ void TrieNode::writeTo(uint8_t *buf) const { buf += offset; if (info) { - // TrieNodes with Symbol info: size, flags address - uint32_t terminalSize = - getULEB128Size(info->flags) + getULEB128Size(info->address); + uint32_t terminalSize = getTerminalSize(); buf += encodeULEB128(terminalSize, buf); buf += encodeULEB128(info->flags, buf); - buf += encodeULEB128(info->address, buf); + if (info->flags & MachO::EXPORT_SYMBOL_FLAGS_REEXPORT) { + buf += encodeULEB128(info->ordinal, buf); + *buf++ = 0; // empty originalName string + } else { + buf += encodeULEB128(info->address, buf); + } } else { // TrieNode with no Symbol info. *buf++ = 0; // terminalSize diff --git a/lld/MachO/Symbols.h b/lld/MachO/Symbols.h --- a/lld/MachO/Symbols.h +++ b/lld/MachO/Symbols.h @@ -254,8 +254,8 @@ public: DylibSymbol(DylibFile *file, StringRefZ name, bool isWeakDef, RefState refState, bool isTlv) - : Symbol(DylibKind, name, file), refState(refState), weakDef(isWeakDef), - tlv(isTlv) { + : Symbol(DylibKind, name, file), shouldReexport(false), + refState(refState), weakDef(isWeakDef), tlv(isTlv) { if (file && refState > RefState::Unreferenced) file->numReferencedSymbols++; } @@ -297,6 +297,7 @@ } } + bool shouldReexport : 1; private: RefState refState : 2; 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 @@ -978,6 +978,9 @@ continue; trieBuilder.addSymbol(*defined); hasWeakSymbol = hasWeakSymbol || sym->isWeakDef(); + } else if (auto *dysym = dyn_cast(sym)) { + if (dysym->shouldReexport) + trieBuilder.addSymbol(*dysym); } } size = trieBuilder.build(); diff --git a/lld/test/MachO/export-options.s b/lld/test/MachO/export-options.s --- a/lld/test/MachO/export-options.s +++ b/lld/test/MachO/export-options.s @@ -1,6 +1,7 @@ # REQUIRES: x86 # RUN: rm -rf %t; split-file %s %t +# RUN: echo "" | llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/empty.o # RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos %t/default.s -o %t/default.o # RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos %t/lazydef.s -o %t/lazydef.o # RUN: llvm-ar --format=darwin rcs %t/lazydef.a %t/lazydef.o @@ -190,9 +191,27 @@ # NOEXPORTS-NOT: literal_also # NOEXPORTS-NOT: literal_only +# RUN: %lld -dylib %t/default.o -o %t/libdefault.dylib +# RUN: %lld -dylib %t/empty.o %t/libdefault.dylib -exported_symbol _keep_globl \ +# RUN: -exported_symbol _undef -exported_symbol _tlv \ +# RUN: -undefined dynamic_lookup -o %t/reexport-dylib +# RUN: llvm-objdump --macho --exports-trie %t/reexport-dylib + +# REEXPORT: Exports trie: +# REEXPORT-NEXT: [re-export] _tlv [per-thread] (from libdefault) +# REEXPORT-NEXT: [re-export] _keep_globl (from libdefault) +# REEXPORT-NEXT: [re-export] _undef (from unknown) + +## -unexported_symbol will not make us re-export symbols in dylibs. +# RUN: %lld -dylib %t/default.o -o %t/libdefault.dylib +# RUN: %lld -dylib %t/empty.o %t/libdefault.dylib -unexported_symbol _tlv \ +# RUN: -o %t/unexport-dylib +# RUN: llvm-objdump --macho --exports-trie %t/unexport-dylib | FileCheck %s \ +# RUN: --check-prefix=EMPTY-TRIE + #--- default.s -.globl _keep_globl, _hide_globl +.globl _keep_globl, _hide_globl, _tlv _keep_globl: retq _hide_globl: @@ -203,6 +222,9 @@ _private: retq +.section __DATA,__thread_vars,thread_local_variables +_tlv: + #--- lazydef.s .globl _keep_lazy