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 handleHideSymbol(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,34 +1249,54 @@ 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 + struct InterfaceSymbol { + StringRef name; + SymbolKind kind; + }; + 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->getName(), symbol->getKind()}); + } + } + + // TODO(compnerd) filter out symbols based on the target platform + // TODO: handle weak defs, thread locals + for (InterfaceSymbol &symbol : normalSymbols) { + + switch (symbol.kind) { case SymbolKind::GlobalSymbol: - addSymbol(symbol->getName()); + addSymbol(symbol.name); break; case SymbolKind::ObjectiveCClass: // XXX ld64 only creates these symbols when -ObjC is passed in. We may // want to emulate that. - addSymbol(objc::klass + symbol->getName()); - addSymbol(objc::metaclass + symbol->getName()); + addSymbol(objc::klass + symbol.name); + addSymbol(objc::metaclass + symbol.name); break; case SymbolKind::ObjectiveCClassEHType: - addSymbol(objc::ehtype + symbol->getName()); + addSymbol(objc::ehtype + symbol.name); break; case SymbolKind::ObjectiveCInstanceVariable: - addSymbol(objc::ivar + symbol->getName()); + addSymbol(objc::ivar + symbol.name); break; } } @@ -1289,6 +1327,8 @@ handleLDPreviousSymbol(name, originalName); else if (action == "install_name") handleLDInstallNameSymbol(name, originalName); + else if (action == "hide") + handleHideSymbol(name, originalName); return true; } @@ -1357,6 +1397,29 @@ this->installName = saver.save(installName); } +void DylibFile::handleHideSymbol(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,58 @@ +# 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 + +## Case 1: special symbol $ld$install_name affects the install name +## since the specified version 11.0.0 matches the target version 11.0.0 + +# 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 + +# 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 +## Check that foo11 comes from the tbd where it is visible. +# RUN: llvm-objdump --macho --bind %t/ref-all.dylib | FileCheck %s --check-prefix=FOO + +# ERROR: error: undefined symbol: _OBJC_CLASS_$_foo11 + +# FOO: __DATA_CONST __got 0x00001010 pointer 0 /HasFoo _OBJC_CLASS_$_foo11 + + +#--- ref-all.s +.long _xxx@GOTPCREL +.long _OBJC_CLASS_$_foo11@GOTPCREL +.long _OBJC_CLASS_$_foo10@GOTPCREL +.long _OBJC_CLASS_$_bar@GOTPCREL + +#--- 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] +... + 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();