diff --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h --- a/lld/MachO/InputFiles.h +++ b/lld/MachO/InputFiles.h @@ -187,10 +187,14 @@ bool isBundleLoader; private: - bool handleLDSymbol(StringRef originalName); + bool handleLDSymbol(StringRef originalName, InputFile *file); void handleLDPreviousSymbol(StringRef name, StringRef originalName); void handleLDInstallNameSymbol(StringRef name, StringRef originalName); + void handleLDHideSymbol(StringRef name, StringRef originalName, + InputFile *file); 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 @@ -1150,10 +1150,15 @@ parseTrie(buf + c->export_off, c->export_size, [&](const Twine &name, uint64_t flags) { StringRef savedName = saver.save(name); - if (handleLDSymbol(savedName)) + if (handleLDSymbol(savedName, exportingFile)) return; bool isWeakDef = flags & EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION; bool isTlv = flags & EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL; + + if (exportingFile->hiddenSymbols.contains( + CachedHashStringRef(savedName))) + return; + symbols.push_back(symtab->addDylib(savedName, exportingFile, isWeakDef, isTlv)); }); @@ -1231,7 +1236,11 @@ 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)); }; @@ -1240,8 +1249,10 @@ for (const auto *symbol : interface.symbols()) { if (!symbol->getArchitectures().has(config->arch())) continue; - - if (handleLDSymbol(symbol->getName())) + // FIXME: We could process all the $ld$hide symbols first + // then the "normal" symbols to avoid inserting then deleting from + // symtab. + if (handleLDSymbol(symbol->getName(), exportingFile)) continue; switch (symbol->getKind()) { @@ -1278,7 +1289,7 @@ // $ld$ symbols modify the properties/behavior of the library (e.g. its install // name, compatibility version or hide/add symbols) for specific target // versions. -bool DylibFile::handleLDSymbol(StringRef originalName) { +bool DylibFile::handleLDSymbol(StringRef originalName, InputFile *file) { if (!originalName.startswith("$ld$")) return false; @@ -1289,6 +1300,8 @@ handleLDPreviousSymbol(name, originalName); else if (action == "install_name") handleLDInstallNameSymbol(name, originalName); + else if (action == "hide") + handleLDHideSymbol(name, originalName, file); return true; } @@ -1357,6 +1370,31 @@ this->installName = saver.save(installName); } +void DylibFile::handleLDHideSymbol(StringRef name, StringRef originalName, + InputFile *file) { + 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) { + hiddenSymbols.insert(CachedHashStringRef(symbolName)); + symtab->removeSymbol(symbolName, file); + } +} + void DylibFile::checkAppExtensionSafety(bool dylibIsAppExtensionSafe) const { if (config->applicationExtension && !dylibIsAppExtensionSafe) warn("using '-application_extension' with unsafe dylib: " + toString(this)); diff --git a/lld/MachO/SymbolTable.h b/lld/MachO/SymbolTable.h --- a/lld/MachO/SymbolTable.h +++ b/lld/MachO/SymbolTable.h @@ -50,6 +50,7 @@ Symbol *addDylib(StringRef name, DylibFile *file, bool isWeakDef, bool isTlv); Symbol *addDynamicLookup(StringRef name); + void removeSymbol(StringRef name, const InputFile *file); Symbol *addLazy(StringRef name, ArchiveFile *file, const llvm::object::Archive::Symbol &sym); diff --git a/lld/MachO/SymbolTable.cpp b/lld/MachO/SymbolTable.cpp --- a/lld/MachO/SymbolTable.cpp +++ b/lld/MachO/SymbolTable.cpp @@ -27,6 +27,22 @@ return symVector[it->second]; } +void SymbolTable::removeSymbol(StringRef name, const InputFile *file) { + CachedHashStringRef cached(name); + Symbol *sym = find(cached); + + if (!sym || sym->getFile() == nullptr || + !sym->getFile()->getName().equals(file->getName())) + return; + + // This function should only be called while we're still parsing the file. + // If the symbol is already referenced then something is wrong. + assert(isa(sym)); + assert(!dyn_cast(sym)->isReferenced()); + symMap.erase(cached); + // XXX: We don't remove it from the vector to avoid having to adjust indices. +} + std::pair SymbolTable::insert(StringRef name, const InputFile *file) { auto p = symMap.insert({CachedHashStringRef(name), (int)symVector.size()}); 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] +... +