[lld] 1309c18 - [lld][MachO] Add first bits to support special symbols

Alexander Shaposhnikov via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 4 23:34:35 PDT 2021


Author: Alexander Shaposhnikov
Date: 2021-06-04T23:32:26-07:00
New Revision: 1309c181a85233c80051544f43008963c3d5fe71

URL: https://github.com/llvm/llvm-project/commit/1309c181a85233c80051544f43008963c3d5fe71
DIFF: https://github.com/llvm/llvm-project/commit/1309c181a85233c80051544f43008963c3d5fe71.diff

LOG: [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

Added: 
    lld/test/MachO/special-symbol-ld-previous.s

Modified: 
    lld/MachO/Config.h
    lld/MachO/InputFiles.cpp
    lld/MachO/InputFiles.h
    lld/MachO/Writer.cpp

Removed: 
    


################################################################################
diff  --git a/lld/MachO/Config.h b/lld/MachO/Config.h
index 93e88cfe7eb6..8c5a18484776 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 5aedf003458f..4c2c9fde76d4 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<const dyld_info_command *>(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 $ <installname> $ <compatversion> $ <platformstr> $
+  // <startversion> $ <endversion> $ <symbol-name> $
+  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<unsigned>(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<object::Archive> &&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 5665ee20a641..21a54347fdac 100644
--- a/lld/MachO/InputFiles.h
+++ b/lld/MachO/InputFiles.h
@@ -173,6 +173,9 @@ class DylibFile : public InputFile {
   // 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 477ccb761789..f01aeb22e18c 100644
--- a/lld/MachO/Writer.cpp
+++ b/lld/MachO/Writer.cpp
@@ -359,12 +359,6 @@ class LCRPath : public LoadCommand {
   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 000000000000..62d6272d45fc
--- /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 at 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 ]
+...


        


More information about the llvm-commits mailing list