[lld-macho] Add support for exporting no symbols
authorKeith Smiley <keithbsmiley@gmail.com>
Sat, 11 Jun 2022 06:34:54 +0000 (23:34 -0700)
committerKeith Smiley <keithbsmiley@gmail.com>
Wed, 15 Jun 2022 22:07:27 +0000 (15:07 -0700)
As an optimization for ld64 sometimes it can be useful to not export any
symbols for top level binaries that don't need any exports, to do this
you can pass `-exported_symbols_list /dev/null`, or new with Xcode 14
(ld64 816) there is a `-no_exported_symbols` flag for the same behavior.
This reproduces this behavior where previously an empty exported symbols
list file would have been ignored.

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

lld/MachO/Config.h
lld/MachO/Driver.cpp
lld/MachO/Options.td
lld/test/MachO/export-options.s

index 69e42c5..87fdbf0 100644 (file)
@@ -185,6 +185,7 @@ struct Configuration {
   SectionRenameMap sectionRenameMap;
   SegmentRenameMap segmentRenameMap;
 
+  bool hasExplicitExports = false;
   SymbolPatterns exportedSymbols;
   SymbolPatterns unexportedSymbols;
   SymbolPatterns whyLive;
index 0a86b09..eb661af 100644 (file)
@@ -1403,15 +1403,18 @@ bool macho::link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
     config->segmentProtections.push_back({segName, maxProt, initProt});
   }
 
+  config->hasExplicitExports =
+      args.hasArg(OPT_no_exported_symbols) ||
+      args.hasArgNoClaim(OPT_exported_symbol, OPT_exported_symbols_list);
   handleSymbolPatterns(args, config->exportedSymbols, OPT_exported_symbol,
                        OPT_exported_symbols_list);
   handleSymbolPatterns(args, config->unexportedSymbols, OPT_unexported_symbol,
                        OPT_unexported_symbols_list);
-  if (!config->exportedSymbols.empty() && !config->unexportedSymbols.empty()) {
-    error("cannot use both -exported_symbol* and -unexported_symbol* options\n"
-          ">>> ignoring unexports");
-    config->unexportedSymbols.clear();
-  }
+  if (config->hasExplicitExports && !config->unexportedSymbols.empty())
+    error("cannot use both -exported_symbol* and -unexported_symbol* options");
+
+  if (args.hasArg(OPT_no_exported_symbols) && !config->exportedSymbols.empty())
+    error("cannot use both -exported_symbol* and -no_exported_symbols options");
 
   // Imitating LD64's:
   // -non_global_symbols_no_strip_list and -non_global_symbols_strip_list can't
@@ -1555,7 +1558,7 @@ bool macho::link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
     createSyntheticSections();
     createSyntheticSymbols();
 
-    if (!config->exportedSymbols.empty()) {
+    if (config->hasExplicitExports) {
       parallelForEach(symtab->getSymbols(), [](Symbol *sym) {
         if (auto *defined = dyn_cast<Defined>(sym)) {
           StringRef symbolName = defined->getName();
index 291e55c..b4092c7 100644 (file)
@@ -483,6 +483,9 @@ def exported_symbols_list : Separate<["-"], "exported_symbols_list">,
     MetaVarName<"<file>">,
     HelpText<"Symbols specified in <file> remain global, while others become private externs">,
     Group<grp_resolve>;
+def no_exported_symbols : Flag<["-"], "no_exported_symbols">,
+    HelpText<"Don't export any symbols from the binary, useful for main executables that don't have plugins">,
+    Group<grp_resolve>;
 def unexported_symbol : Separate<["-"], "unexported_symbol">,
     MetaVarName<"<symbol>">,
     HelpText<"Global <symbol> becomes private extern">,
index 654834e..5ec52d1 100644 (file)
@@ -9,9 +9,26 @@
 # RUN: not %lld -dylib %t/default.o -o /dev/null \
 # RUN:         -exported_symbol a -unexported_symbol b 2>&1 | \
 # RUN:     FileCheck --check-prefix=CONFLICT %s
+#
+# RUN: not %lld -dylib %t/default.o -o /dev/null \
+# RUN:         -exported_symbols_list /dev/null -unexported_symbol b 2>&1 | \
+# RUN:     FileCheck --check-prefix=CONFLICT %s
+#
+# RUN: not %lld -dylib %t/default.o -o /dev/null \
+# RUN:         -no_exported_symbols -unexported_symbol b 2>&1 | \
+# RUN:     FileCheck --check-prefix=CONFLICT %s
+#
+# RUN: not %lld -dylib %t/default.o -o /dev/null \
+# RUN:         -no_exported_symbols -exported_symbol b 2>&1 | \
+# RUN:     FileCheck --check-prefix=CONFLICT-NO-EXPORTS %s
+#
+# RUN: not %lld -dylib %t/default.o -o /dev/null \
+# RUN:         -no_exported_symbols -exported_symbols_list %t/literals.txt 2>&1 | \
+# RUN:     FileCheck --check-prefix=CONFLICT-NO-EXPORTS %s
 
 # CONFLICT: error: cannot use both -exported_symbol* and -unexported_symbol* options
-# CONFLICT-NEXT: >>> ignoring unexports
+
+# CONFLICT-NO-EXPORTS: error: cannot use both -exported_symbol* and -no_exported_symbols options
 
 ## Check that an exported literal name with no symbol definition yields an error
 ## but that an exported glob-pattern with no matching symbol definition is OK
 # AUTOHIDE-PRIVATE: error: cannot export hidden symbol _foo
 # AUTOHIDE-PRIVATE-DEAD-STRIP: (__TEXT,__text) non-external (was a private external) _foo
 
+## Test not exporting any symbols
+# RUN: %lld -dylib %t/symdefs.o -o %t/noexports -exported_symbols_list /dev/null
+# RUN: llvm-objdump --macho --exports-trie %t/noexports | FileCheck --check-prefix=NOEXPORTS %s
+# RUN: %lld -dylib %t/symdefs.o -o %t/noexports -no_exported_symbols
+# RUN: llvm-objdump --macho --exports-trie %t/noexports | FileCheck --check-prefix=NOEXPORTS %s
+
+# NOEXPORTS-NOT: globby_also
+# NOEXPORTS-NOT: globby_only
+# NOEXPORTS-NOT: literal_also
+# NOEXPORTS-NOT: literal_only
+
 #--- default.s
 
 .globl _keep_globl, _hide_globl