[lld] 2c19034 - [lld/mac] Implement removal of unused dylibs

Nico Weber via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 1 13:11:59 PDT 2021


Author: Nico Weber
Date: 2021-06-01T16:06:30-04:00
New Revision: 2c1903412b0a73ed65e9feb272af9c07f32970d0

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

LOG: [lld/mac] Implement removal of unused dylibs

This omits load commands for unreferenced dylibs if:
- the dylib was loaded implicitly,
- it is marked MH_DEAD_STRIPPABLE_DYLIB
- or -dead_strip_dylibs is passed

This matches ld64.

Currently, the "is dylib referenced" state is computed before dead code
stripping and is not updated after dead code stripping. This too matches ld64.
We should do better here.

With this, clang-format linked with lld (like with ld64) no longer has
libobjc.A.dylib in `otool -L` output. (It was implicitly loaded as a reexport
of CoreFoundation.framework, but it's not needed.)

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

Added: 
    lld/test/MachO/dead-strip-dylibs.s

Modified: 
    lld/MachO/Config.h
    lld/MachO/Driver.cpp
    lld/MachO/InputFiles.cpp
    lld/MachO/InputFiles.h
    lld/MachO/Options.td
    lld/MachO/SymbolTable.cpp
    lld/MachO/Symbols.h
    lld/MachO/SyntheticSections.cpp
    lld/MachO/Writer.cpp
    lld/test/MachO/implicit-dylibs.s
    lld/test/MachO/lc-linker-option.ll

Removed: 
    


################################################################################
diff  --git a/lld/MachO/Config.h b/lld/MachO/Config.h
index fba8ba0ed4a2..da263ec3252c 100644
--- a/lld/MachO/Config.h
+++ b/lld/MachO/Config.h
@@ -109,6 +109,7 @@ struct Configuration {
   llvm::StringRef outputFile;
   llvm::StringRef ltoObjPath;
   llvm::StringRef thinLTOJobs;
+  bool deadStripDylibs = false;
   bool demangle = false;
   PlatformInfo platformInfo;
   NamespaceKind namespaceKind = NamespaceKind::twolevel;

diff  --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp
index 9fff29be1b0c..07cd065f961e 100644
--- a/lld/MachO/Driver.cpp
+++ b/lld/MachO/Driver.cpp
@@ -293,8 +293,10 @@ static InputFile *addFile(StringRef path, bool forceLoadArchive,
   case file_magic::macho_dynamically_linked_shared_lib:
   case file_magic::macho_dynamically_linked_shared_lib_stub:
   case file_magic::tapi_file:
-    if (Optional<DylibFile *> dylibFile = loadDylib(mbref))
+    if (Optional<DylibFile *> dylibFile = loadDylib(mbref)) {
+      (*dylibFile)->explicitlyLinked = true;
       newFile = *dylibFile;
+    }
     break;
   case file_magic::bitcode:
     newFile = make<BitcodeFile>(mbref);
@@ -322,21 +324,25 @@ static InputFile *addFile(StringRef path, bool forceLoadArchive,
   return newFile;
 }
 
-static void addLibrary(StringRef name, bool isWeak) {
+static void addLibrary(StringRef name, bool isWeak, bool isExplicit = true) {
   if (Optional<StringRef> path = findLibrary(name)) {
-    auto *dylibFile = dyn_cast_or_null<DylibFile>(addFile(*path, false));
-    if (isWeak && dylibFile)
-      dylibFile->forceWeakImport = true;
+    if (auto *dylibFile = dyn_cast_or_null<DylibFile>(addFile(*path, false))) {
+      dylibFile->explicitlyLinked = isExplicit;
+      if (isWeak)
+        dylibFile->forceWeakImport = true;
+    }
     return;
   }
   error("library not found for -l" + name);
 }
 
-static void addFramework(StringRef name, bool isWeak) {
+static void addFramework(StringRef name, bool isWeak, bool isExplicit = true) {
   if (Optional<std::string> path = findFramework(name)) {
-    auto *dylibFile = dyn_cast_or_null<DylibFile>(addFile(*path, false));
-    if (isWeak && dylibFile)
-      dylibFile->forceWeakImport = true;
+    if (auto *dylibFile = dyn_cast_or_null<DylibFile>(addFile(*path, false))) {
+      dylibFile->explicitlyLinked = isExplicit;
+      if (isWeak)
+        dylibFile->forceWeakImport = true;
+    }
     return;
   }
   error("framework not found for -framework " + name);
@@ -365,10 +371,10 @@ void macho::parseLCLinkerOption(InputFile *f, unsigned argc, StringRef data) {
   for (const Arg *arg : args) {
     switch (arg->getOption().getID()) {
     case OPT_l:
-      addLibrary(arg->getValue(), false);
+      addLibrary(arg->getValue(), /*isWeak=*/false, /*isExplicit=*/false);
       break;
     case OPT_framework:
-      addFramework(arg->getValue(), false);
+      addFramework(arg->getValue(), /*isWeak=*/false, /*isExplicit=*/false);
       break;
     default:
       error(arg->getSpelling() + " is not allowed in LC_LINKER_OPTION");
@@ -981,6 +987,7 @@ bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
   config->runtimePaths = args::getStrings(args, OPT_rpath);
   config->allLoad = args.hasArg(OPT_all_load);
   config->forceLoadObjC = args.hasArg(OPT_ObjC);
+  config->deadStripDylibs = args.hasArg(OPT_dead_strip_dylibs);
   config->demangle = args.hasArg(OPT_demangle);
   config->implicitDylibs = !args.hasArg(OPT_no_implicit_dylibs);
   config->emitFunctionStarts = !args.hasArg(OPT_no_function_starts);

diff  --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp
index d55776611ca9..52ad2eb19804 100644
--- a/lld/MachO/InputFiles.cpp
+++ b/lld/MachO/InputFiles.cpp
@@ -828,6 +828,8 @@ DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella,
     return;
   }
 
+  deadStrippable = hdr->flags & MH_DEAD_STRIPPABLE_DYLIB;
+
   if (!checkCompatibility(this))
     return;
 

diff  --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h
index cc57591e0758..fa31fe370318 100644
--- a/lld/MachO/InputFiles.h
+++ b/lld/MachO/InputFiles.h
@@ -157,6 +157,14 @@ class DylibFile : public InputFile {
   RefState refState;
   bool reexport = false;
   bool forceWeakImport = false;
+  bool deadStrippable = false;
+  bool explicitlyLinked = false;
+
+  unsigned numReferencedSymbols = 0;
+
+  bool isReferenced() const {
+    return numReferencedSymbols > 0;
+  }
 
   // An executable can be used as a bundle loader that will load the output
   // file being linked, and that contains symbols referenced, but not

diff  --git a/lld/MachO/Options.td b/lld/MachO/Options.td
index 725af21804da..fffdeb4af69d 100644
--- a/lld/MachO/Options.td
+++ b/lld/MachO/Options.td
@@ -702,7 +702,6 @@ def objc_gc_only : Flag<["-"], "objc_gc_only">,
     Group<grp_rare>;
 def dead_strip_dylibs : Flag<["-"], "dead_strip_dylibs">,
     HelpText<"Remove dylibs that are unreachable by the entry point or exported symbols">,
-    Flags<[HelpHidden]>,
     Group<grp_rare>;
 def allow_sub_type_mismatches : Flag<["-"], "allow_sub_type_mismatches">,
     HelpText<"Permit mixing objects compiled for 
diff erent ARM CPU subtypes">,

diff  --git a/lld/MachO/SymbolTable.cpp b/lld/MachO/SymbolTable.cpp
index 565137e570bd..1a9cc4e2c99d 100644
--- a/lld/MachO/SymbolTable.cpp
+++ b/lld/MachO/SymbolTable.cpp
@@ -81,6 +81,7 @@ Defined *SymbolTable::addDefined(StringRef name, InputFile *file,
               toString(file));
     } else if (auto *dysym = dyn_cast<DylibSymbol>(s)) {
       overridesWeakDef = !isWeakDef && dysym->isWeakDef();
+      dysym->unreference();
     }
     // Defined symbols take priority over other types of symbols, so in case
     // of a name conflict, we fall through to the replaceSymbol() call below.
@@ -106,7 +107,7 @@ Symbol *SymbolTable::addUndefined(StringRef name, InputFile *file,
   else if (auto *lazy = dyn_cast<LazySymbol>(s))
     lazy->fetchArchiveMember();
   else if (auto *dynsym = dyn_cast<DylibSymbol>(s))
-    dynsym->refState = std::max(dynsym->refState, refState);
+    dynsym->reference(refState);
   else if (auto *undefined = dyn_cast<Undefined>(s))
     undefined->refState = std::max(undefined->refState, refState);
   return s;
@@ -147,7 +148,7 @@ Symbol *SymbolTable::addDylib(StringRef name, DylibFile *file, bool isWeakDef,
     } else if (auto *undefined = dyn_cast<Undefined>(s)) {
       refState = undefined->refState;
     } else if (auto *dysym = dyn_cast<DylibSymbol>(s)) {
-      refState = dysym->refState;
+      refState = dysym->getRefState();
     }
   }
 
@@ -155,8 +156,11 @@ Symbol *SymbolTable::addDylib(StringRef name, DylibFile *file, bool isWeakDef,
   if (wasInserted || isa<Undefined>(s) ||
       (isa<DylibSymbol>(s) &&
        ((!isWeakDef && s->isWeakDef()) ||
-        (!isDynamicLookup && cast<DylibSymbol>(s)->isDynamicLookup()))))
+        (!isDynamicLookup && cast<DylibSymbol>(s)->isDynamicLookup())))) {
+    if (auto *dynsym = dyn_cast<DylibSymbol>(s))
+      dynsym->unreference();
     replaceSymbol<DylibSymbol>(s, file, name, isWeakDef, refState, isTlv);
+  }
 
   return s;
 }

diff  --git a/lld/MachO/Symbols.h b/lld/MachO/Symbols.h
index 82637ee15b5f..42a9aee116e5 100644
--- a/lld/MachO/Symbols.h
+++ b/lld/MachO/Symbols.h
@@ -221,7 +221,10 @@ class DylibSymbol : public Symbol {
   DylibSymbol(DylibFile *file, StringRefZ name, bool isWeakDef,
               RefState refState, bool isTlv)
       : Symbol(DylibKind, name, file), refState(refState), weakDef(isWeakDef),
-        tlv(isTlv) {}
+        tlv(isTlv) {
+    if (file && refState > RefState::Unreferenced)
+      file->numReferencedSymbols++;
+  }
 
   uint64_t getVA() const override;
   bool isWeakDef() const override { return weakDef; }
@@ -241,9 +244,25 @@ class DylibSymbol : public Symbol {
   uint32_t stubsHelperIndex = UINT32_MAX;
   uint32_t lazyBindOffset = UINT32_MAX;
 
-  RefState refState : 2;
+  RefState getRefState() const { return refState; }
+
+  void reference(RefState newState) {
+    assert(newState > RefState::Unreferenced);
+    if (refState == RefState::Unreferenced && file)
+      getFile()->numReferencedSymbols++;
+    refState = std::max(refState, newState);
+  }
+
+  void unreference() {
+    // dynamic_lookup symbols have no file.
+    if (refState > RefState::Unreferenced && file) {
+      assert(getFile()->numReferencedSymbols > 0);
+      getFile()->numReferencedSymbols--;
+    }
+  }
 
 private:
+  RefState refState : 2;
   const bool weakDef : 1;
   const bool tlv : 1;
 };

diff  --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp
index e949ca88da7d..a0ed37b3e050 100644
--- a/lld/MachO/SyntheticSections.cpp
+++ b/lld/MachO/SyntheticSections.cpp
@@ -312,9 +312,10 @@ static void encodeBinding(const Symbol *sym, const OutputSection *osec,
 
 // Non-weak bindings need to have their dylib ordinal encoded as well.
 static int16_t ordinalForDylibSymbol(const DylibSymbol &dysym) {
-  return config->namespaceKind == NamespaceKind::flat || dysym.isDynamicLookup()
-             ? static_cast<int16_t>(BIND_SPECIAL_DYLIB_FLAT_LOOKUP)
-             : dysym.getFile()->ordinal;
+  if (config->namespaceKind == NamespaceKind::flat || dysym.isDynamicLookup())
+    return static_cast<int16_t>(BIND_SPECIAL_DYLIB_FLAT_LOOKUP);
+  assert(dysym.getFile()->isReferenced());
+  return dysym.getFile()->ordinal;
 }
 
 static void encodeDylibOrdinal(int16_t ordinal, raw_svector_ostream &os) {
@@ -464,7 +465,7 @@ void StubHelperSection::setup() {
           "Needed to perform lazy binding.");
     return;
   }
-  stubBinder->refState = RefState::Strong;
+  stubBinder->reference(RefState::Strong);
   in.got->addEntry(stubBinder);
 
   inputSections.push_back(in.imageLoaderCache);

diff  --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp
index fc5f239d6f9e..05cb10d16df5 100644
--- a/lld/MachO/Writer.cpp
+++ b/lld/MachO/Writer.cpp
@@ -615,7 +615,7 @@ void Writer::scanSymbols() {
       if (dysym->isDynamicLookup())
         continue;
       dysym->getFile()->refState =
-          std::max(dysym->getFile()->refState, dysym->refState);
+          std::max(dysym->getFile()->refState, dysym->getRefState());
     }
   }
 }
@@ -688,6 +688,19 @@ template <class LP> void Writer::createLoadCommands() {
         continue;
       }
 
+      // Don't emit load commands for a dylib that is not referenced if:
+      // - it was added implicitly (via a reexport, an LC_LOAD_DYLINKER --
+      //   if it's on the linker command line, it's explicit)
+      // - or it's marked MH_DEAD_STRIPPABLE_DYLIB
+      // - or the flag -dead_strip_dylibs is used
+      // FIXME: `isReferenced()` is currently computed before dead code
+      // stripping, so references from dead code keep a dylib alive. This
+      // matches ld64, but it's something we should do better.
+      if (!dylibFile->isReferenced() &&
+          (!dylibFile->explicitlyLinked || dylibFile->deadStrippable ||
+           config->deadStripDylibs))
+        continue;
+
       dylibFile->ordinal = dylibOrdinal++;
       LoadCommandType lcType =
           dylibFile->forceWeakImport || dylibFile->refState == RefState::Weak

diff  --git a/lld/test/MachO/dead-strip-dylibs.s b/lld/test/MachO/dead-strip-dylibs.s
new file mode 100644
index 000000000000..ef19e5aaecb2
--- /dev/null
+++ b/lld/test/MachO/dead-strip-dylibs.s
@@ -0,0 +1,115 @@
+# REQUIRES: x86
+
+# RUN: rm -rf %t; split-file %s %t
+
+# RUN: llvm-mc %t/bar.s -triple=x86_64-apple-macos -filetype=obj -o %t/bar.o
+# RUN: %lld -dylib %t/bar.o -o %t/bar.dylib
+# RUN: %lld -dylib -mark_dead_strippable_dylib %t/bar.o -o %t/bar-strip.dylib
+
+# RUN: llvm-mc %t/foo.s -triple=x86_64-apple-macos -filetype=obj -o %t/foo.o
+# RUN: %lld -dylib %t/foo.o -o %t/foo_with_bar.dylib %t/bar.dylib -sub_library bar
+# RUN: %lld -dylib %t/foo.o -o %t/foo.dylib
+
+# RUN: llvm-mc %t/weak-foo.s -triple=x86_64-apple-macos -filetype=obj -o %t/weak-foo.o
+# RUN: %lld -dylib %t/weak-foo.o -o %t/weak-foo.dylib
+
+# RUN: llvm-mc %t/main.s -triple=x86_64-apple-macos -filetype=obj -o %t/main.o
+
+## foo_with_bar.dylib's reexport should be dropped since it's linked implicitly.
+# RUN: %lld -lSystem %t/main.o -o %t/main %t/foo_with_bar.dylib
+# RUN: llvm-otool -L %t/main | FileCheck --check-prefix=NOBAR %s
+# NOBAR-NOT: bar.dylib
+# NOBAR: /usr/lib/libSystem.dylib
+# NOBAR-NOT: bar.dylib
+# NOBAR: foo_with_bar.dylib
+# NOBAR-NOT: bar.dylib
+
+## If bar.dylib is linked explicitly, it should not be dropped.
+# RUN: %lld -lSystem %t/main.o -o %t/main %t/foo_with_bar.dylib %t/bar.dylib
+# RUN: llvm-otool -L %t/main | FileCheck --check-prefix=BAR %s
+# BAR: /usr/lib/libSystem.dylib
+# BAR: foo_with_bar.dylib
+# BAR: bar.dylib
+
+## ...except if -dead-strip_dylibs is passed...
+# RUN: %lld -lSystem %t/main.o -o %t/main %t/foo_with_bar.dylib %t/bar.dylib \
+# RUN:      -dead_strip_dylibs
+# RUN: llvm-otool -L %t/main | FileCheck --check-prefix=NOBAR %s
+
+## ...or bar is explicitly marked dead-strippable
+# RUN: %lld -lSystem %t/main.o -o %t/main %t/foo.dylib %t/bar-strip.dylib
+# RUN: llvm-otool -L %t/main | FileCheck --check-prefix=NOBARSTRIP %s
+# NOBARSTRIP-NOT: bar-strip.dylib
+# NOBARSTRIP: /usr/lib/libSystem.dylib
+# NOBARSTRIP-NOT: bar-strip.dylib
+# NOBARSTRIP: foo.dylib
+# NOBARSTRIP-NOT: bar-strip.dylib
+
+## LC_LINKER_OPTION does not count as an explicit reference.
+# RUN: llvm-mc %t/linkopt_bar.s -triple=x86_64-apple-macos -filetype=obj -o %t/linkopt_bar.o
+# RUN: %lld -dylib %t/bar.o -o %t/libbar.dylib
+# RUN: %lld -lSystem %t/main.o %t/linkopt_bar.o -o %t/main -L %t %t/foo_with_bar.dylib
+# RUN: llvm-otool -L %t/main | FileCheck --check-prefix=NOBAR %s
+
+## Test that a DylibSymbol being replaced by a DefinedSymbol marks the
+## dylib as unreferenced.
+## (Note: Since there's no dynamic linking in this example, libSystem is
+## stripped too. Since libSystem.dylib is needed to run an executable for
+## LC_MAIN, the output would crash when run. This matches ld64's behavior (!)
+## In practice, every executable uses dynamic linking, which uses
+## dyld_stub_binder, which keeps libSystem alive.)
+## Test all permutations of (Undefined, Defined, DylibSymbol).
+# RUN: %lld -lSystem -dead_strip_dylibs %t/main.o %t/foo.o %t/foo.dylib -o %t/main
+# RUN: llvm-otool -L %t/main | FileCheck --check-prefix=NOFOO %s
+# RUN: %lld -lSystem -dead_strip_dylibs %t/main.o %t/foo.dylib %t/foo.dylib %t/foo.o -o %t/main
+# RUN: llvm-otool -L %t/main | FileCheck --check-prefix=NOFOO %s
+
+# RUN: %lld -lSystem -dead_strip_dylibs %t/foo.o %t/main.o %t/foo.dylib -o %t/main
+# RUN: llvm-otool -L %t/main | FileCheck --check-prefix=NOFOO %s
+# RUN: %lld -lSystem -dead_strip_dylibs %t/foo.dylib %t/foo.dylib %t/main.o %t/foo.o -o %t/main
+# RUN: llvm-otool -L %t/main | FileCheck --check-prefix=NOFOO %s
+
+# RUN: %lld -lSystem -dead_strip_dylibs %t/foo.o %t/foo.dylib %t/main.o -o %t/main
+# RUN: llvm-otool -L %t/main | FileCheck --check-prefix=NOFOO %s
+# RUN: %lld -lSystem -dead_strip_dylibs %t/foo.dylib %t/foo.dylib %t/foo.o %t/main.o -o %t/main
+# RUN: llvm-otool -L %t/main | FileCheck --check-prefix=NOFOO %s
+# NOFOO-NOT: foo.dylib
+
+## When linking a weak and a strong symbol from two dylibs, we should keep the
+## strong one.
+# RUN: %lld -lSystem -dead_strip_dylibs %t/main.o %t/foo.dylib %t/weak-foo.dylib -o %t/main
+# RUN: llvm-otool -L %t/main | FileCheck --check-prefix=NOWEAK %s
+# RUN: %lld -lSystem -dead_strip_dylibs %t/main.o %t/weak-foo.dylib %t/foo.dylib -o %t/main
+# RUN: llvm-otool -L %t/main | FileCheck --check-prefix=NOWEAK %s
+# RUN: %lld -lSystem -dead_strip_dylibs %t/foo.dylib %t/weak-foo.dylib %t/main.o -o %t/main
+# RUN: llvm-otool -L %t/main | FileCheck --check-prefix=NOWEAK %s
+# RUN: %lld -lSystem -dead_strip_dylibs %t/weak-foo.dylib %t/foo.dylib %t/main.o -o %t/main
+# RUN: llvm-otool -L %t/main | FileCheck --check-prefix=NOWEAK %s
+# NOWEAK-NOT: weak-foo.dylib
+# NOWEAK: /foo.dylib
+# NOWEAK-NOT: weak-foo.dylib
+
+#--- main.s
+.globl _foo, _main
+_main:
+  callq _foo
+  retq
+
+#--- foo.s
+.globl _foo
+_foo:
+  retq
+
+#--- bar.s
+.globl _bar
+_bar:
+  retq
+
+#--- linkopt_bar.s
+.linker_option "-lbar"
+
+#--- weak-foo.s
+.globl _foo
+.weak_definition _foo
+_foo:
+  retq

diff  --git a/lld/test/MachO/implicit-dylibs.s b/lld/test/MachO/implicit-dylibs.s
index f9568f79ad65..fbc18f7e55f7 100644
--- a/lld/test/MachO/implicit-dylibs.s
+++ b/lld/test/MachO/implicit-dylibs.s
@@ -58,11 +58,11 @@
 # CHECK-DAG: __DATA __data {{.*}} pointer 0 libreexporter _framework_baz
 # CHECK-DAG: __DATA __data {{.*}} pointer 0 libc++abi     ___gxx_personality_v0
 
-# RUN: llvm-objdump --macho --all-headers %t/test | FileCheck %s \
+# RUN: llvm-otool -l %t/test | FileCheck %s \
 # RUN:   --check-prefix=LOAD -DDIR=%t --implicit-check-not LC_LOAD_DYLIB
 ## Check that we don't create duplicate LC_LOAD_DYLIBs.
 # RUN: %lld -syslibroot %t -o %t/test -lSystem -L%t -lreexporter -ltoplevel %t/test.o
-# RUN: llvm-objdump --macho --all-headers %t/test | FileCheck %s \
+# RUN: llvm-otool -l %t/test | FileCheck %s \
 # RUN:   --check-prefix=LOAD -DDIR=%t --implicit-check-not LC_LOAD_DYLIB
 
 # LOAD:          cmd LC_LOAD_DYLIB
@@ -76,15 +76,9 @@
 # LOAD-NEXT:    name /usr/lib/libc++abi.dylib
 # LOAD:          cmd LC_LOAD_DYLIB
 # LOAD-NEXT: cmdsize
-# LOAD-NEXT:    name /usr/lib/libc++.dylib
-# LOAD:          cmd LC_LOAD_DYLIB
-# LOAD-NEXT: cmdsize
 # LOAD-NEXT:    name /usr/lib/libtoplevel.dylib
 # LOAD:          cmd LC_LOAD_DYLIB
 # LOAD-NEXT: cmdsize
-# LOAD-NEXT:    name /usr/lib/libunused.dylib
-# LOAD:          cmd LC_LOAD_DYLIB
-# LOAD-NEXT: cmdsize
 # LOAD-NEXT:    name [[DIR]]/libreexporter.dylib
 
 # RUN: %lld -no_implicit_dylibs -syslibroot %t -o %t/no-implicit -lSystem -L%t -lreexporter %t/test.o

diff  --git a/lld/test/MachO/lc-linker-option.ll b/lld/test/MachO/lc-linker-option.ll
index 5906f90a8df8..7bcef677ae36 100644
--- a/lld/test/MachO/lc-linker-option.ll
+++ b/lld/test/MachO/lc-linker-option.ll
@@ -2,30 +2,38 @@
 ; RUN: rm -rf %t; split-file %s %t
 ;
 ; RUN: llvm-as %t/framework.ll -o %t/framework.o
-; RUN: %lld %t/framework.o -o %t/frame
-; RUN: llvm-objdump --macho --all-headers %t/frame | FileCheck --check-prefix=FRAME %s \
+; RUN: %lld -lSystem %t/framework.o -o %t/frame
+; RUN: llvm-otool -l %t/frame | FileCheck --check-prefix=FRAME %s \
 ; RUN:  --implicit-check-not LC_LOAD_DYLIB
 ; FRAME:          cmd LC_LOAD_DYLIB
 ; FRAME-NEXT: cmdsize
+; FRAME-NEXT:    name /usr/lib/libSystem.dylib
+; FRAME:          cmd LC_LOAD_DYLIB
+; FRAME-NEXT: cmdsize
 ; FRAME-NEXT:    name /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
-;
+
 ; RUN: llvm-as %t/l.ll -o %t/l.o
-; RUN: %lld %t/l.o -o %t/l
-; RUN: llvm-objdump --macho --all-headers %t/l | FileCheck --check-prefix=LIB %s \
-; RUN:  --implicit-check-not LC_LOAD_DYLIB
-;
-;; Check that we don't create duplicate LC_LOAD_DYLIBs.
-; RUN: %lld -lSystem %t/l.o -o %t/l
-; RUN: llvm-objdump --macho --all-headers %t/l | FileCheck --check-prefix=LIB %s \
+;; The dynamic call to _CFBigNumGetInt128 uses dyld_stub_binder,
+;; which needs -lSystem from LC_LINKER_OPTION to get resolved.
+; RUN: %lld %t/l.o -o %t/l -framework CoreFoundation
+; RUN: llvm-otool -l %t/l | FileCheck --check-prefix=LIB %s \
 ; RUN:  --implicit-check-not LC_LOAD_DYLIB
 ; LIB:          cmd LC_LOAD_DYLIB
 ; LIB-NEXT: cmdsize
+; LIB-NEXT:    name /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
+; LIB:          cmd LC_LOAD_DYLIB
+; LIB-NEXT: cmdsize
 ; LIB-NEXT:    name /usr/lib/libSystem.dylib
+
+;; Check that we don't create duplicate LC_LOAD_DYLIBs.
+; RUN: %lld -lSystem %t/l.o -o %t/l -framework CoreFoundation
+; RUN: llvm-otool -l %t/l | FileCheck --check-prefix=FRAME %s \
+; RUN:  --implicit-check-not LC_LOAD_DYLIB
 ;
 ; RUN: llvm-as %t/invalid.ll -o %t/invalid.o
 ; RUN: not %lld %t/invalid.o -o /dev/null 2>&1 | FileCheck --check-prefix=INVALID %s
 ; INVALID: error: -why_load is not allowed in LC_LINKER_OPTION
-;
+
 ;--- framework.ll
 target triple = "x86_64-apple-macosx10.15.0"
 target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
@@ -33,7 +41,10 @@ target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16
 !0 = !{!"-framework", !"CoreFoundation"}
 !llvm.linker.options = !{!0}
 
+declare void @_CFBigNumGetInt128(...)
+
 define void @main() {
+  call void bitcast (void (...)* @_CFBigNumGetInt128 to void ()*)()
   ret void
 }
 
@@ -44,7 +55,10 @@ target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16
 !0 = !{!"-lSystem"}
 !llvm.linker.options = !{!0, !0}
 
+declare void @_CFBigNumGetInt128(...)
+
 define void @main() {
+  call void bitcast (void (...)* @_CFBigNumGetInt128 to void ()*)()
   ret void
 }
 


        


More information about the llvm-commits mailing list