From 1309c181a85233c80051544f43008963c3d5fe71 Mon Sep 17 00:00:00 2001 From: Alexander Shaposhnikov Date: Fri, 4 Jun 2021 23:31:40 -0700 Subject: [PATCH] [lld][MachO] Add first bits to support special symbols This diff adds first bits to support special symbols $ld$previous* in LLD. $ld$* symbols modify properties/behavior of the library (e.g. its install name, compatibility version or hide/add symbols) for specific target versions. Test plan: make check-lld-macho Differential revision: https://reviews.llvm.org/D103505 --- lld/MachO/Config.h | 6 +++ lld/MachO/InputFiles.cpp | 77 ++++++++++++++++++++++++++++- lld/MachO/InputFiles.h | 3 ++ lld/MachO/Writer.cpp | 6 --- lld/test/MachO/special-symbol-ld-previous.s | 67 +++++++++++++++++++++++++ 5 files changed, 151 insertions(+), 8 deletions(-) create mode 100644 lld/test/MachO/special-symbol-ld-previous.s diff --git a/lld/MachO/Config.h b/lld/MachO/Config.h index 93e88cf..8c5a184 100644 --- a/lld/MachO/Config.h +++ b/lld/MachO/Config.h @@ -38,6 +38,12 @@ struct PlatformInfo { llvm::VersionTuple sdk; }; +inline uint32_t encodeVersion(const llvm::VersionTuple &version) { + return ((version.getMajor() << 020) | + (version.getMinor().getValueOr(0) << 010) | + version.getSubminor().getValueOr(0)); +} + enum class NamespaceKind { twolevel, flat, diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp index 5aedf00..4c2c9fd 100644 --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -848,10 +848,13 @@ DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella, auto *c = reinterpret_cast(cmd); 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( - saver.save(name), exportingFile, isWeakDef, isTlv)); + symbols.push_back(symtab->addDylib(savedName, exportingFile, + isWeakDef, isTlv)); }); } else { error("LC_DYLD_INFO_ONLY not found in " + toString(this)); @@ -934,6 +937,9 @@ DylibFile::DylibFile(const InterfaceFile &interface, DylibFile *umbrella, if (!symbol->getArchitectures().has(config->arch())) continue; + if (handleLdSymbol(symbol->getName())) + continue; + switch (symbol->getKind()) { case SymbolKind::GlobalSymbol: addSymbol(symbol->getName()); @@ -965,6 +971,73 @@ void DylibFile::parseReexports(const InterfaceFile &interface) { } } +// $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) { + // $ld$ previous $ $ $ $ + // $ $ $ + if (!originalName.startswith("$ld$")) + return false; + + StringRef action; + StringRef name; + std::tie(action, name) = originalName.drop_front(4 /* $ld$ */).split('$'); + if (action.empty() || action != "previous") + return true; + + StringRef installName; + StringRef compatVersion; + StringRef platformStr; + StringRef startVersion; + StringRef endVersion; + StringRef symbolName; + StringRef rest; + + std::tie(installName, name) = name.split('$'); + std::tie(compatVersion, name) = name.split('$'); + std::tie(platformStr, name) = name.split('$'); + std::tie(startVersion, name) = name.split('$'); + std::tie(endVersion, symbolName) = name.split('$'); + std::tie(symbolName, rest) = symbolName.split('$'); + // TODO: ld64 contains some logic for non-empty symbolName as well. + if (!symbolName.empty()) + return true; + unsigned platform; + if (platformStr.getAsInteger(10, platform) || + platform != static_cast(config->platform())) + return true; + + VersionTuple start; + if (start.tryParse(startVersion)) { + warn("failed to parse start version, symbol '" + originalName + + "' ignored"); + return true; + } + VersionTuple end; + if (end.tryParse(endVersion)) { + warn("failed to parse end version, symbol '" + originalName + "' ignored"); + return true; + } + if (config->platformInfo.minimum < start || + config->platformInfo.minimum >= end) + return true; + + dylibName = saver.save(installName); + + if (!compatVersion.empty()) { + VersionTuple cVersion; + if (cVersion.tryParse(compatVersion)) { + warn("failed to parse compatibility version, symbol '" + originalName + + "' ignored"); + return true; + } + compatibilityVersion = encodeVersion(cVersion); + } + + return true; +} + ArchiveFile::ArchiveFile(std::unique_ptr &&f) : InputFile(ArchiveKind, f->getMemoryBufferRef()), file(std::move(f)) { for (const object::Archive::Symbol &sym : file->symbols()) diff --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h index 5665ee2..21a5434 100644 --- a/lld/MachO/InputFiles.h +++ b/lld/MachO/InputFiles.h @@ -173,6 +173,9 @@ public: // implemented in the bundle. When used like this, it is very similar // to a Dylib, so we re-used the same class to represent it. bool isBundleLoader; + +private: + bool handleLdSymbol(StringRef name); }; // .a file diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp index 477ccb7..f01aeb2 100644 --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -359,12 +359,6 @@ private: StringRef path; }; -static uint32_t encodeVersion(const VersionTuple &version) { - return ((version.getMajor() << 020) | - (version.getMinor().getValueOr(0) << 010) | - version.getSubminor().getValueOr(0)); -} - class LCMinVersion : public LoadCommand { public: explicit LCMinVersion(const PlatformInfo &platformInfo) diff --git a/lld/test/MachO/special-symbol-ld-previous.s b/lld/test/MachO/special-symbol-ld-previous.s new file mode 100644 index 0000000..62d6272 --- /dev/null +++ b/lld/test/MachO/special-symbol-ld-previous.s @@ -0,0 +1,67 @@ +# REQUIRES: x86 + +# RUN: rm -rf %t; split-file --no-leading-lines %s %t + +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/foo.s -o %t/foo.o + +## Case 1: special symbol $ld$previous affects the install name / compatibility version +## since the specified version 11.0.0 is within the affected range [3.0, 14.0). + +# RUN: %lld -o %t/libfoo1.dylib %t/libLDPreviousInstallName.tbd %t/foo.o -dylib -platform_version macos 11.0.0 11.0.0 +# RUN: llvm-objdump --macho --dylibs-used %t/libfoo1.dylib | FileCheck --check-prefix=CASE1 %s +# CASE1: /New (compatibility version 1.2.3, current version 5.0.0) + +## Case 2: special symbol $ld$previous does not affect the install name / compatibility version +## since the specified version 2.0.0 is lower than the affected range [3.0, 14.0). + +# RUN: %lld -o %t/libfoo2.dylib %t/libLDPreviousInstallName.tbd %t.o -dylib -platform_version macos 2.0.0 2.0.0 +# RUN: llvm-objdump --macho --dylibs-used %t/libfoo2.dylib | FileCheck --check-prefix=CASE2 %s +# CASE2: /Old (compatibility version 1.1.1, current version 5.0.0) + +## Case 3: special symbol $ld$previous does not affect the install name / compatibility version +## since the specified version 14.0.0 is higher than the affected range [3.0, 14.0). + +# RUN: %lld -o %t/libfoo3.dylib %t/libLDPreviousInstallName.tbd %t.o -dylib -platform_version macos 2.0.0 2.0.0 +# RUN: llvm-objdump --macho --dylibs-used %t/libfoo3.dylib | FileCheck --check-prefix=CASE3 %s +# CASE3: /Old (compatibility version 1.1.1, current version 5.0.0) + +## Check that we emit a warning for an invalid start, end and compatibility versions. + +# RUN: %no_fatal_warnings_lld -o %t/libfoo1.dylib %t/libLDPreviousInvalid.tbd %t/foo.o -dylib \ +# RUN: -platform_version macos 11.0.0 11.0.0 2>&1 | FileCheck --check-prefix=INVALID-VERSION %s + +# INVALID-VERSION-DAG: failed to parse start version, symbol '$ld$previous$/New$1.2.3$1$3.a$14.0$$' ignored +# INVALID-VERSION-DAG: failed to parse end version, symbol '$ld$previous$/New$1.2.3$1$3.0$14.b$$' ignored +# INVALID-VERSION-DAG: failed to parse compatibility version, symbol '$ld$previous$/New$1.2.c$1$3.0$14.0$$' ignored + +#--- foo.s +.long _xxx@GOTPCREL + +#--- libLDPreviousInstallName.tbd +--- !tapi-tbd-v3 +archs: [ x86_64 ] +uuids: [ 'x86_64: 19311019-01AB-342E-812B-73A74271A715' ] +platform: macosx +install-name: '/Old' +current-version: 5 +compatibility-version: 1.1.1 +exports: + - archs: [ x86_64 ] + symbols: [ '$ld$previous$/New$1.2.3$1$3.0$14.0$$', _xxx ] +... + +#--- libLDPreviousInvalid.tbd +--- !tapi-tbd-v3 +archs: [ x86_64 ] +uuids: [ 'x86_64: 19311019-01AB-342E-112B-73A74271A715' ] +platform: macosx +install-name: '/Old' +current-version: 5 +compatibility-version: 1.1.1 +exports: + - archs: [ x86_64 ] + symbols: [ '$ld$previous$/New$1.2.3$1$3.a$14.0$$', + '$ld$previous$/New$1.2.3$1$3.0$14.b$$', + '$ld$previous$/New$1.2.c$1$3.0$14.0$$', + _xxx ] +... -- 2.7.4