[lld] aeae3e0 - [lld/mac] Emit only one LC_LOAD_DYLIB per dylib

Nico Weber via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 1 15:15:45 PDT 2021


Author: Nico Weber
Date: 2021-06-01T18:15:35-04:00
New Revision: aeae3e0ba9061a40209987d6256e489146c2bffb

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

LOG: [lld/mac] Emit only one LC_LOAD_DYLIB per dylib

In some cases, we end up with several distinct DylibFiles that
have the same install name. Only emit a single LC_LOAD_DYLIB in
those cases.

This happens in 3 cases I know of:

1. Some tbd files are symlinks. libpthread.tbd is a symlink against
   libSystem.tbd for example, so `-lSystem -lpthread` loads
   libSystem.tbd twice. We could (and maybe should) cache loaded
   dylibs by realpath() to catch this.

2. Some tbd files are copies of each other. For example,
   CFNetwork.framework/CFNetwork.tbd and
   CFNetwork.framework/Versions/A/CFNetwork.tbd are two distinct
   copies of the same file. The former is found by
   `-framework CFNetwork` and the latter by the reexport in
   CoreServices.tbd. We could conceivably catch this by
   making `-framework` search look in `Versions/Current` instead
   of in the root, and/or by using a content hash to cache
   tbd files, but that's starting to sound complicated.

3. Magic $ld$ symbol processing can change the install name of
   a dylib based on the target platform_version. Here, two
   truly distinct dylibs can have the same install name.

So we need this code to deal with (3) anyways. Might as well use
it for 1 and 2, at least for now :)

With this (and D103430), clang-format links in the same dylibs
when linked with lld and ld64.

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

Added: 
    lld/test/MachO/dylink-ordinal.s

Modified: 
    lld/MachO/Writer.cpp

Removed: 
    


################################################################################
diff  --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp
index 05cb10d16df5..b9686ce0fd86 100644
--- a/lld/MachO/Writer.cpp
+++ b/lld/MachO/Writer.cpp
@@ -678,6 +678,7 @@ template <class LP> void Writer::createLoadCommands() {
     in.header->addLoadCommand(make<LCMinVersion>(config->platformInfo));
 
   int64_t dylibOrdinal = 1;
+  DenseMap<StringRef, int64_t> ordinalForInstallName;
   for (InputFile *file : inputFiles) {
     if (auto *dylibFile = dyn_cast<DylibFile>(file)) {
       if (dylibFile->isBundleLoader) {
@@ -701,7 +702,29 @@ template <class LP> void Writer::createLoadCommands() {
            config->deadStripDylibs))
         continue;
 
-      dylibFile->ordinal = dylibOrdinal++;
+      // Several DylibFiles can have the same installName. Only emit a single
+      // load command for that installName and give all these DylibFiles the
+      // same ordinal.
+      // This can happen if:
+      // - a new framework could change its installName to an older
+      //   framework name via an $ld$ symbol depending on platform_version
+      // - symlink (eg libpthread.tbd is a symlink to libSystem.tbd)
+      // - a framework can be linked both explicitly on the linker
+      //   command line and implicitly as a reexport from a 
diff erent
+      //   framework. The re-export will usually point to the tbd file
+      //   in Foo.framework/Versions/A/Foo.tbd, while the explicit link will
+      //   usually find Foo.framwork/Foo.tbd. These are usually two identical
+      //   but distinct files (concrete example: CFNetwork.framework, reexported
+      //   from CoreServices.framework).
+      // In the first case, *semantically distinct* DylibFiles will have the
+      // same installName.
+      int64_t &ordinal = ordinalForInstallName[dylibFile->dylibName];
+      if (ordinal) {
+        dylibFile->ordinal = ordinal;
+        continue;
+      }
+
+      ordinal = dylibFile->ordinal = dylibOrdinal++;
       LoadCommandType lcType =
           dylibFile->forceWeakImport || dylibFile->refState == RefState::Weak
               ? LC_LOAD_WEAK_DYLIB

diff  --git a/lld/test/MachO/dylink-ordinal.s b/lld/test/MachO/dylink-ordinal.s
new file mode 100644
index 000000000000..26a988f31266
--- /dev/null
+++ b/lld/test/MachO/dylink-ordinal.s
@@ -0,0 +1,65 @@
+# REQUIRES: x86
+
+## --no-leading-lines needed for .tbd files.
+# RUN: rm -rf %t; split-file --no-leading-lines %s %t
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/main.s -o %t/main.o
+# RUN: %lld -o %t/main -L%t -lFoo -lBar -lSystem %t/main.o
+# RUN: llvm-objdump --lazy-bind -d --no-show-raw-insn %t/main | FileCheck %s
+
+# CHECK: callq 0x[[#%x,FOO_OFF:]]
+# CHECK-NEXT: callq 0x[[#%x,BAR_OFF:]]
+
+# CHECK: [[#%x,BAR_OFF]]: jmpq {{.*}} # [[#%x,BAR_BIND:]]
+# CHECK: [[#%x,FOO_OFF]]: jmpq {{.*}} # [[#%x,FOO_BIND:]]
+
+# CHECK-LABEL: Lazy bind table:
+# CHECK-DAG: __DATA __la_symbol_ptr 0x[[#%x,FOO_BIND]] Foo _foo
+# CHECK-DAG: __DATA __la_symbol_ptr 0x[[#%x,BAR_BIND]] Foo _bar
+
+# RUN: llvm-nm -m %t/main | FileCheck --check-prefix=NM %s
+
+# NM-DAG: _bar (from Foo)
+# NM-DAG: _foo (from Foo)
+
+# RUN: llvm-otool -L %t/main | FileCheck %s --check-prefix=LOAD
+
+# LOAD: Foo.dylib
+# LOAD-NOT: Foo.dylib
+
+#--- libFoo.tbd
+--- !tapi-tbd
+tbd-version:      4
+targets:          [ x86_64-macos ]
+uuids:
+  - target:       x86_64-macos
+    value:        00000000-0000-0000-0000-000000000000
+install-name:     'Foo.dylib'
+current-version:  0001.001.1
+exports:
+  - targets:      [ x86_64-macos ]
+    symbols:      [ _foo ]
+
+#--- libBar.tbd
+--- !tapi-tbd
+tbd-version:      4
+targets:          [ x86_64-macos ]
+uuids:
+  - target:       x86_64-macos
+    value:        00000000-0000-0000-0000-000000000000
+## Also uses Foo.dylib as install-name!
+## Normally, this would happen conditionally via an $ld$ symbol.
+install-name:     'Foo.dylib'
+current-version:  0001.001.1
+exports:
+  - targets:      [ x86_64-macos ]
+    symbols:      [ _bar ]
+
+#--- main.s
+.section __TEXT,__text
+.globl _main, _foo, _bar
+
+_main:
+  callq _foo
+  callq _bar
+  ret


        


More information about the llvm-commits mailing list