[lld] 9260760 - [lld-macho] Support loading of zippered dylibs

Jez Ng via llvm-commits llvm-commits at lists.llvm.org
Thu May 6 08:19:52 PDT 2021


Author: Jez Ng
Date: 2021-05-06T11:19:40-04:00
New Revision: 9260760235261a5cd150b15a3499f7988da65a02

URL: https://github.com/llvm/llvm-project/commit/9260760235261a5cd150b15a3499f7988da65a02
DIFF: https://github.com/llvm/llvm-project/commit/9260760235261a5cd150b15a3499f7988da65a02.diff

LOG: [lld-macho] Support loading of zippered dylibs

ld64 can emit dylibs that support more than one platform (typically macOS and
macCatalyst). This diff allows LLD to read in those dylibs. Note that this is a
super bare-bones implementation -- in particular, I haven't added support for
LLD to emit those multi-platform dylibs, nor have I added a variety of
validation checks that ld64 does. Until we have a use-case for emitting zippered
dylibs, I think this is good enough.

Fixes PR49597.

Reviewed By: #lld-macho, oontvoo

Differential Revision: https://reviews.llvm.org/D101954

Added: 
    lld/test/MachO/zippered.yaml

Modified: 
    lld/MachO/InputFiles.cpp
    lld/MachO/InputFiles.h
    lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libSystem.tbd

Removed: 
    


################################################################################
diff  --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp
index adc1bb20a47c..7df21616aa3f 100644
--- a/lld/MachO/InputFiles.cpp
+++ b/lld/MachO/InputFiles.cpp
@@ -102,41 +102,42 @@ static VersionTuple decodeVersion(uint32_t version) {
   return VersionTuple(major, minor, subMinor);
 }
 
-static Optional<PlatformInfo> getPlatformInfo(const InputFile *input) {
+static std::vector<PlatformInfo> getPlatformInfos(const InputFile *input) {
   if (!isa<ObjFile>(input) && !isa<DylibFile>(input))
-    return None;
+    return {};
 
   const char *hdr = input->mb.getBufferStart();
 
-  PlatformInfo platformInfo;
-  if (const auto *cmd =
-          findCommand<build_version_command>(hdr, LC_BUILD_VERSION)) {
-    platformInfo.target.Platform = static_cast<PlatformKind>(cmd->platform);
-    platformInfo.minimum = decodeVersion(cmd->minos);
-    return platformInfo;
+  std::vector<PlatformInfo> platformInfos;
+  for (auto *cmd : findCommands<build_version_command>(hdr, LC_BUILD_VERSION)) {
+    PlatformInfo info;
+    info.target.Platform = static_cast<PlatformKind>(cmd->platform);
+    info.minimum = decodeVersion(cmd->minos);
+    platformInfos.emplace_back(std::move(info));
   }
-  if (const auto *cmd = findCommand<version_min_command>(
-          hdr, LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS,
-          LC_VERSION_MIN_TVOS, LC_VERSION_MIN_WATCHOS)) {
+  for (auto *cmd : findCommands<version_min_command>(
+           hdr, LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS,
+           LC_VERSION_MIN_TVOS, LC_VERSION_MIN_WATCHOS)) {
+    PlatformInfo info;
     switch (cmd->cmd) {
     case LC_VERSION_MIN_MACOSX:
-      platformInfo.target.Platform = PlatformKind::macOS;
+      info.target.Platform = PlatformKind::macOS;
       break;
     case LC_VERSION_MIN_IPHONEOS:
-      platformInfo.target.Platform = PlatformKind::iOS;
+      info.target.Platform = PlatformKind::iOS;
       break;
     case LC_VERSION_MIN_TVOS:
-      platformInfo.target.Platform = PlatformKind::tvOS;
+      info.target.Platform = PlatformKind::tvOS;
       break;
     case LC_VERSION_MIN_WATCHOS:
-      platformInfo.target.Platform = PlatformKind::watchOS;
+      info.target.Platform = PlatformKind::watchOS;
       break;
     }
-    platformInfo.minimum = decodeVersion(cmd->version);
-    return platformInfo;
+    info.minimum = decodeVersion(cmd->version);
+    platformInfos.emplace_back(std::move(info));
   }
 
-  return None;
+  return platformInfos;
 }
 
 static PlatformKind removeSimulator(PlatformKind platform) {
@@ -153,22 +154,33 @@ static PlatformKind removeSimulator(PlatformKind platform) {
 }
 
 static bool checkCompatibility(const InputFile *input) {
-  Optional<PlatformInfo> platformInfo = getPlatformInfo(input);
-  if (!platformInfo)
+  std::vector<PlatformInfo> platformInfos = getPlatformInfos(input);
+  if (platformInfos.empty())
     return true;
 
-  if (removeSimulator(config->platform()) !=
-      removeSimulator(platformInfo->target.Platform)) {
-    error(toString(input) + " has platform " +
-          getPlatformName(platformInfo->target.Platform) +
+  auto it = find_if(platformInfos, [&](const PlatformInfo &info) {
+    return removeSimulator(info.target.Platform) ==
+           removeSimulator(config->platform());
+  });
+  if (it == platformInfos.end()) {
+    std::string platformNames;
+    raw_string_ostream os(platformNames);
+    interleave(
+        platformInfos, os,
+        [&](const PlatformInfo &info) {
+          os << getPlatformName(info.target.Platform);
+        },
+        "/");
+    error(toString(input) + " has platform " + platformNames +
           Twine(", which is 
diff erent from target platform ") +
           getPlatformName(config->platform()));
     return false;
   }
-  if (platformInfo->minimum <= config->platformInfo.minimum)
+
+  if (it->minimum <= config->platformInfo.minimum)
     return true;
-  error(toString(input) + " has version " +
-        platformInfo->minimum.getAsString() +
+
+  error(toString(input) + " has version " + it->minimum.getAsString() +
         ", which is newer than target minimum of " +
         config->platformInfo.minimum.getAsString());
   return false;

diff  --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h
index d57e174f15c1..6e58601ae56a 100644
--- a/lld/MachO/InputFiles.h
+++ b/lld/MachO/InputFiles.h
@@ -187,20 +187,42 @@ extern llvm::SetVector<InputFile *> inputFiles;
 
 llvm::Optional<MemoryBufferRef> readFile(StringRef path);
 
-// anyHdr should be a pointer to either mach_header or mach_header_64
-template <class CommandType = llvm::MachO::load_command, class... Types>
-const CommandType *findCommand(const void *anyHdr, Types... types) {
+namespace detail {
+
+template <class CommandType, class... Types>
+std::vector<const CommandType *>
+findCommands(const void *anyHdr, size_t maxCommands, Types... types) {
+  std::vector<const CommandType *> cmds;
   std::initializer_list<uint32_t> typesList{types...};
   const auto *hdr = reinterpret_cast<const llvm::MachO::mach_header *>(anyHdr);
   const uint8_t *p =
       reinterpret_cast<const uint8_t *>(hdr) + target->headerSize;
   for (uint32_t i = 0, n = hdr->ncmds; i < n; ++i) {
     auto *cmd = reinterpret_cast<const CommandType *>(p);
-    if (llvm::is_contained(typesList, cmd->cmd))
-      return cmd;
+    if (llvm::is_contained(typesList, cmd->cmd)) {
+      cmds.push_back(cmd);
+      if (cmds.size() == maxCommands)
+        return cmds;
+    }
     p += cmd->cmdsize;
   }
-  return nullptr;
+  return cmds;
+}
+
+} // namespace detail
+
+// anyHdr should be a pointer to either mach_header or mach_header_64
+template <class CommandType = llvm::MachO::load_command, class... Types>
+const CommandType *findCommand(const void *anyHdr, Types... types) {
+  std::vector<const CommandType *> cmds =
+      detail::findCommands<CommandType>(anyHdr, 1, types...);
+  return cmds.size() ? cmds[0] : nullptr;
+}
+
+template <class CommandType = llvm::MachO::load_command, class... Types>
+std::vector<const CommandType *> findCommands(const void *anyHdr,
+                                              Types... types) {
+  return detail::findCommands<CommandType>(anyHdr, 0, types...);
 }
 
 } // namespace macho

diff  --git a/lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libSystem.tbd b/lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libSystem.tbd
index b333678cc8c3..716905997a91 100644
--- a/lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libSystem.tbd
+++ b/lld/test/MachO/Inputs/MacOSX.sdk/usr/lib/libSystem.tbd
@@ -1,64 +1,72 @@
 --- !tapi-tbd
 tbd-version:      4
-targets:          [ x86_64-macos, arm64-macos ]
+targets:          [ x86_64-macos, x86_64-maccatalyst, arm64-macos ]
 uuids:
   - target:       x86_64-macos
     value:        00000000-0000-0000-0000-000000000000
+  - target:       x86_64-maccatalyst
+    value:        00000000-0000-0000-0000-000000000000
   - target:       arm64-macos
     value:        00000000-0000-0000-0000-000000000001
 install-name:     '/usr/lib/libSystem.dylib'
 current-version:  0001.001.1
 reexported-libraries:
-  - targets:      [ x86_64-macos, arm64-macos ]
+  - targets:      [ x86_64-macos, x86_64-maccatalyst, arm64-macos ]
     libraries:    [ '/usr/lib/system/libdyld.dylib',
                     '/usr/lib/system/libsystem_c.dylib',
                     '/usr/lib/system/libsystem_m.dylib' ]
 --- !tapi-tbd
 tbd-version:      4
-targets:          [ x86_64-macos, arm64-macos ]
+targets:          [ x86_64-macos, x86_64-maccatalyst, arm64-macos ]
 uuids:
   - target:       x86_64-macos
     value:        00000000-0000-0000-0000-000000000002
+  - target:       x86_64-maccatalyst
+    value:        00000000-0000-0000-0000-000000000000
   - target:       arm64-macos
     value:        00000000-0000-0000-0000-000000000003
 install-name:     '/usr/lib/system/libdyld.dylib'
 current-version:  0001.001.1
 parent-umbrella:
-  - targets:      [ x86_64-macos, arm64-macos ]
+  - targets:      [ x86_64-macos, x86_64-maccatalyst, arm64-macos ]
     umbrella:     System
 exports:
-  - targets:      [ x86_64-macos, arm64-macos ]
+  - targets:      [ x86_64-macos, x86_64-maccatalyst, arm64-macos ]
     symbols:      [ dyld_stub_binder, __tlv_bootstrap ]
 --- !tapi-tbd
 tbd-version:      4
-targets:          [ x86_64-macos, arm64-macos ]
+targets:          [ x86_64-macos, x86_64-maccatalyst, arm64-macos ]
 uuids:
   - target:       x86_64-macos
     value:        00000000-0000-0000-0000-000000000003
+  - target:       x86_64-maccatalyst
+    value:        00000000-0000-0000-0000-000000000000
   - target:       arm64-macos
     value:        00000000-0000-0000-0000-000000000004
 install-name:     '/usr/lib/system/libsystem_c.dylib'
 current-version:  0001.001.1
 parent-umbrella:
-  - targets:      [ x86_64-macos, arm64-macos ]
+  - targets:      [ x86_64-macos, x86_64-maccatalyst, arm64-macos ]
     umbrella:     System
 exports:
-  - targets:      [ x86_64-macos, arm64-macos ]
+  - targets:      [ x86_64-macos, x86_64-maccatalyst, arm64-macos ]
     symbols:      [ ]
 --- !tapi-tbd
 tbd-version:      4
-targets:          [ x86_64-macos, arm64-macos ]
+targets:          [ x86_64-macos, x86_64-maccatalyst, arm64-macos ]
 uuids:
   - target:       x86_64-macos
     value:        00000000-0000-0000-0000-000000000004
+  - target:       x86_64-maccatalyst
+    value:        00000000-0000-0000-0000-000000000000
   - target:       arm64-macos
     value:        00000000-0000-0000-0000-000000000005
 install-name:     '/usr/lib/system/libsystem_m.dylib'
 current-version:  0001.001.1
 parent-umbrella:
-  - targets:      [ x86_64-macos, arm64-macos ]
+  - targets:      [ x86_64-macos, x86_64-maccatalyst, arm64-macos ]
     umbrella:     System
 exports:
-  - targets:      [ x86_64-macos, arm64-macos ]
+  - targets:      [ x86_64-macos, x86_64-maccatalyst, arm64-macos ]
     symbols:      [ ___nan ]
 ...

diff  --git a/lld/test/MachO/zippered.yaml b/lld/test/MachO/zippered.yaml
new file mode 100644
index 000000000000..40fd1efba314
--- /dev/null
+++ b/lld/test/MachO/zippered.yaml
@@ -0,0 +1,64 @@
+# REQUIRES: x86
+# RUN: rm -rf %t; mkdir %t
+# RUN: yaml2obj %s > %t/test.dylib
+# RUN: echo "" | llvm-mc -filetype=obj -triple=x86_64-apple-macos10.15 -o %t/test_macos.o
+# RUN: echo "" | llvm-mc -filetype=obj -triple=x86_64-apple-ios13.15.0-macabi -o %t/test_maccatalyst.o
+# RUN: echo "" | llvm-mc -filetype=obj -triple=x86_64-apple-ios13.15.0 -o %t/test_ios.o
+
+# RUN: %lld -lSystem -dylib %t/test.dylib %t/test_macos.o -o /dev/null
+# RUN: %lld -lSystem -dylib -platform_version mac-catalyst 13.15.0 14.0 %t/test.dylib %t/test_maccatalyst.o -o /dev/null
+
+# RUN: not %lld -lSystem -dylib -platform_version ios 13.15.0 14.0 %t/test.dylib %t/test_ios.o -o /dev/null 2>&1 | FileCheck %s
+# CHECK: test.dylib has platform macOS/macCatalyst, which is 
diff erent from target platform iOS
+
+--- !mach-o
+FileHeader:
+  magic:           0xFEEDFACF
+  cputype:         0x1000007
+  cpusubtype:      0x3
+  filetype:        0x6
+  ncmds:           4
+  sizeofcmds:      600
+  flags:           0x100085
+  reserved:        0x0
+LoadCommands:
+  - cmd:             LC_ID_DYLIB
+    cmdsize:         32
+    dylib:
+      name:            24
+      timestamp:       1
+      current_version: 0
+      compatibility_version: 0
+    PayloadString:   test
+    ZeroPadBytes:    4
+  - cmd:             LC_DYLD_INFO_ONLY
+    cmdsize:         48
+    rebase_off:      0
+    rebase_size:     0
+    bind_off:        0
+    bind_size:       0
+    weak_bind_off:   0
+    weak_bind_size:  0
+    lazy_bind_off:   0
+    lazy_bind_size:  0
+    export_off:      0
+    export_size:     0
+  - cmd:             LC_BUILD_VERSION
+    cmdsize:         32
+    platform:        1
+    minos:           659200
+    sdk:             720896
+    ntools:          1
+    Tools:
+      - tool:            3
+        version:         39913472
+  - cmd:             LC_BUILD_VERSION
+    cmdsize:         32
+    platform:        6
+    minos:           855808
+    sdk:             917504
+    ntools:          1
+    Tools:
+      - tool:            3
+        version:         39913472
+...


        


More information about the llvm-commits mailing list