From 272bf0fc41e6a50f89dd01b55a33a3aabcdaf5a8 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Fri, 10 Jun 2022 23:34:54 -0700 Subject: [PATCH] [lld-macho] Add support for exporting no symbols 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 | 1 + lld/MachO/Driver.cpp | 15 +++++++++------ lld/MachO/Options.td | 3 +++ lld/test/MachO/export-options.s | 30 +++++++++++++++++++++++++++++- 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/lld/MachO/Config.h b/lld/MachO/Config.h index 69e42c5..87fdbf0 100644 --- a/lld/MachO/Config.h +++ b/lld/MachO/Config.h @@ -185,6 +185,7 @@ struct Configuration { SectionRenameMap sectionRenameMap; SegmentRenameMap segmentRenameMap; + bool hasExplicitExports = false; SymbolPatterns exportedSymbols; SymbolPatterns unexportedSymbols; SymbolPatterns whyLive; diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp index 0a86b09..eb661af 100644 --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -1403,15 +1403,18 @@ bool macho::link(ArrayRef 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 argsArr, llvm::raw_ostream &stdoutOS, createSyntheticSections(); createSyntheticSymbols(); - if (!config->exportedSymbols.empty()) { + if (config->hasExplicitExports) { parallelForEach(symtab->getSymbols(), [](Symbol *sym) { if (auto *defined = dyn_cast(sym)) { StringRef symbolName = defined->getName(); diff --git a/lld/MachO/Options.td b/lld/MachO/Options.td index 291e55c..b4092c7 100644 --- a/lld/MachO/Options.td +++ b/lld/MachO/Options.td @@ -483,6 +483,9 @@ def exported_symbols_list : Separate<["-"], "exported_symbols_list">, MetaVarName<"">, HelpText<"Symbols specified in remain global, while others become private externs">, Group; +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; def unexported_symbol : Separate<["-"], "unexported_symbol">, MetaVarName<"">, HelpText<"Global becomes private extern">, diff --git a/lld/test/MachO/export-options.s b/lld/test/MachO/export-options.s index 654834e..5ec52d1 100644 --- a/lld/test/MachO/export-options.s +++ b/lld/test/MachO/export-options.s @@ -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 @@ -162,6 +179,17 @@ # 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 -- 2.7.4