diff --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h --- a/lld/MachO/InputFiles.h +++ b/lld/MachO/InputFiles.h @@ -190,7 +190,10 @@ bool handleLDSymbol(StringRef originalName); void handleLDPreviousSymbol(StringRef name, StringRef originalName); void handleLDInstallNameSymbol(StringRef name, StringRef originalName); + void handleLDHideSymbol(StringRef name, StringRef originalName); void checkAppExtensionSafety(bool dylibIsAppExtensionSafe) const; + + llvm::DenseSet hiddenSymbols; }; // .a file diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -1147,16 +1147,34 @@ exportingFile = isImplicitlyLinked(installName) ? this : this->umbrella; if (const load_command *cmd = findCommand(hdr, LC_DYLD_INFO_ONLY)) { auto *c = reinterpret_cast(cmd); + struct TrieEntry { + StringRef name; + uint64_t flags; + }; + + std::vector entries; + // Find all the $ld$* symbols to process first. parseTrie(buf + c->export_off, c->export_size, [&](const Twine &name, uint64_t flags) { StringRef savedName = saver.save(name); if (handleLDSymbol(savedName)) return; - bool isWeakDef = flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; - bool isTlv = flags & EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL; - symbols.push_back(symtab->addDylib(savedName, exportingFile, - isWeakDef, isTlv)); + entries.push_back({savedName, flags}); }); + + // Process the "normal" symbols. + for (TrieEntry &entry : entries) { + if (exportingFile->hiddenSymbols.contains( + CachedHashStringRef(entry.name))) + continue; + + bool isWeakDef = entry.flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; + bool isTlv = entry.flags & EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL; + + symbols.push_back( + symtab->addDylib(entry.name, exportingFile, isWeakDef, isTlv)); + } + } else { error("LC_DYLD_INFO_ONLY not found in " + toString(this)); return; @@ -1231,19 +1249,35 @@ exportingFile = isImplicitlyLinked(installName) ? this : umbrella; auto addSymbol = [&](const Twine &name) -> void { - symbols.push_back(symtab->addDylib(saver.save(name), exportingFile, + StringRef savedName = saver.save(name); + if (exportingFile->hiddenSymbols.contains(CachedHashStringRef(savedName))) + return; + + symbols.push_back(symtab->addDylib(savedName, exportingFile, /*isWeakDef=*/false, /*isTlv=*/false)); }; - // TODO(compnerd) filter out symbols based on the target platform - // TODO: handle weak defs, thread locals + + std::vector normalSymbols; + normalSymbols.reserve(interface.symbolsCount()); for (const auto *symbol : interface.symbols()) { if (!symbol->getArchitectures().has(config->arch())) continue; - if (handleLDSymbol(symbol->getName())) continue; + switch (symbol->getKind()) { + case SymbolKind::GlobalSymbol: // Fallthrough + case SymbolKind::ObjectiveCClass: // Fallthrough + case SymbolKind::ObjectiveCClassEHType: // Fallthrough + case SymbolKind::ObjectiveCInstanceVariable: // Fallthrough + normalSymbols.push_back(symbol); + } + } + + // TODO(compnerd) filter out symbols based on the target platform + // TODO: handle weak defs, thread locals + for (const auto *symbol : normalSymbols) { switch (symbol->getKind()) { case SymbolKind::GlobalSymbol: addSymbol(symbol->getName()); @@ -1289,6 +1323,8 @@ handleLDPreviousSymbol(name, originalName); else if (action == "install_name") handleLDInstallNameSymbol(name, originalName); + else if (action == "hide") + handleLDHideSymbol(name, originalName); return true; } @@ -1357,6 +1393,29 @@ this->installName = saver.save(installName); } +void DylibFile::handleLDHideSymbol(StringRef name, StringRef originalName) { + StringRef symbolName; + bool shouldHide = true; + if (name.startswith("os")) { + // If it's hidden based on versions. + name = name.drop_front(2); + StringRef minVersion; + std::tie(minVersion, symbolName) = name.split('$'); + VersionTuple versionTup; + if (versionTup.tryParse(minVersion)) { + warn("Failed to parse hidden version, symbol `" + originalName + + "` ignored."); + return; + } + shouldHide = versionTup == config->platformInfo.minimum; + } else { + symbolName = name; + } + + if (shouldHide) + exportingFile->hiddenSymbols.insert(CachedHashStringRef(symbolName)); +} + void DylibFile::checkAppExtensionSafety(bool dylibIsAppExtensionSafe) const { if (config->applicationExtension && !dylibIsAppExtensionSafe) warn("using '-application_extension' with unsafe dylib: " + toString(this)); diff --git a/lld/test/MachO/special-symbol-ld-hidden.s b/lld/test/MachO/special-symbol-ld-hidden.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/special-symbol-ld-hidden.s @@ -0,0 +1,93 @@ +# REQUIRES: x86 + +# RUN: rm -rf %t; split-file --no-leading-lines %s %t +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/ref-all.s -o %t/ref-all.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/ref-reexported.s -o %t/ref-reexported.o + +## Check that the hidden symbols(foo11) can't be referenced from HideFoo.tbd when current version is 11. +# RUN: not %lld -o /dev/null %t/libHideFoo.tbd %t/ref-all.o -dylib -platform_version macos 11.0.0 11.0.0 2>&1 | FileCheck %s --check-prefix=ERROR + +## Check that the hidden symbol(foo11) can be referenced when the current version is NOT 11. +# RUN: %lld -o %t/ref-foo-12.dylib %t/libHideFoo.tbd %t/ref-all.o -dylib -platform_version macos 12.0.0 12.0.0 +# RUN: llvm-objdump --macho --bind %t/ref-foo-12.dylib | FileCheck %s --check-prefix=HAS-FOO + +## Check that when we link multiple tbd files, foo11 comes from the tbd where it is visible. +# RUN: %lld -o %t/ref-all.dylib %t/libHideFoo.tbd %t/libHasFoo.tbd %t/ref-all.o -dylib -platform_version macos 11.0.0 11.0.0 +# RUN: llvm-objdump --macho --bind %t/ref-all.dylib | FileCheck %s --check-prefix=FOO + +## Check that '$hide$' has no effect on reexported symbols. +# RUN: %lld -o %t/reexport.dylib %t/libReexportSystem2.tbd %t/ref-reexported.o -dylib -platform_version macos 11.0.0 11.0.0 +# RUN: llvm-objdump --macho --bind %t/reexport.dylib | FileCheck %s --check-prefix=REEXP + +# ERROR: error: undefined symbol: _OBJC_CLASS_$_foo11 + +# HAS-FOO: __DATA __data {{.*}} pointer 0 /HideFoo _OBJC_CLASS_$_foo11 + +# FOO: segment section address type addend dylib symbol +# FOO-DAG: __DATA __data {{.*}} pointer 0 /HideFoo _OBJC_CLASS_$_bar +# FOO-DAG: __DATA __data {{.*}} pointer 0 /HideFoo _OBJC_CLASS_$_foo10 +# FOO-DAG: __DATA __data {{.*}} pointer 0 /HasFoo _OBJC_CLASS_$_foo11 +# FOO-DAG: __DATA __data {{.*}} pointer 0 /HideFoo _xxx + +# REEXP: __DATA __data {{.*}} pointer 0 libSystem ___nan + +#--- ref-all.s +.data +.quad _xxx +.quad _OBJC_CLASS_$_foo11 +.quad _OBJC_CLASS_$_foo10 +.quad _OBJC_CLASS_$_bar + +#--- ref-reexported.s +.data +.quad ___nan + +#--- libHideFoo.tbd +--- !tapi-tbd +tbd-version: 4 +targets: [ x86_64-macos ] +uuids: + - target: x86_64-macos + value: 2E994C7F-3F03-3A07-879C-55690D22BEDA +install-name: '/HideFoo' +current-version: 9 +compatibility-version: 4.5.6 +exports: + - targets: [ x86_64-macos ] + symbols: [ '$ld$hide$os11.0$_OBJC_CLASS_$_foo11', '$ld$hide$os10.0$_OBJC_CLASS_$_foo10', _xxx ] + objc-classes: [foo10, foo11, bar] +... + +#--- libHasFoo.tbd +--- !tapi-tbd +tbd-version: 4 +targets: [ x86_64-macos ] +uuids: + - target: x86_64-macos + value: 2E994C7F-3F03-3A07-879C-55690D22BEDB +install-name: '/HasFoo' +current-version: 9 +compatibility-version: 4.5.6 +exports: + - targets: [ x86_64-macos ] + symbols: [ _xxx ] + objc-classes: [foo10, foo11, bar] +... + +#--- libReexportSystem2.tbd +--- !tapi-tbd +tbd-version: 4 +targets: [ x86_64-macos ] +uuids: + - target: x86_64-macos + value: 00000000-0000-0000-0000-000000000002 +install-name: '/libReexportSystem2' +current-version: 9 +exports: + - targets: [ x86_64-macos ] + symbols: [ '$ld$hide$___nan' ] +reexported-libraries: + - targets: [ x86_64-macos ] + libraries: [ '/usr/lib/libSystem.dylib' ] +... + diff --git a/llvm/include/llvm/TextAPI/InterfaceFile.h b/llvm/include/llvm/TextAPI/InterfaceFile.h --- a/llvm/include/llvm/TextAPI/InterfaceFile.h +++ b/llvm/include/llvm/TextAPI/InterfaceFile.h @@ -381,6 +381,8 @@ return {Symbols.begin(), Symbols.end()}; } + size_t symbolsCount() const { return Symbols.size(); } + const_filtered_symbol_range exports() const { std::function fn = [](const Symbol *Symbol) { return !Symbol->isUndefined();