#ifndef LLD_MACHO_CONFIG_H
#define LLD_MACHO_CONFIG_H
+#include "llvm/ADT/CachedHashString.h"
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/MachO.h"
+#include "llvm/Support/GlobPattern.h"
#include "llvm/Support/VersionTuple.h"
#include "llvm/TextAPI/MachO/Architecture.h"
#include "llvm/TextAPI/MachO/Platform.h"
dynamic_lookup,
};
+class SymbolPatterns {
+ // GlobPattern can also match literals,
+ // but we prefer the O(1) lookup of DenseSet.
+ llvm::DenseSet<llvm::CachedHashStringRef> literals;
+ std::vector<llvm::GlobPattern> globs;
+
+public:
+ bool empty() const { return literals.empty() && globs.empty(); }
+ void clear();
+ void insert(llvm::StringRef symbolName);
+ bool matchLiteral(llvm::StringRef symbolName) const;
+ bool matchGlob(llvm::StringRef symbolName) const;
+ bool match(llvm::StringRef symbolName) const;
+};
+
struct Configuration {
Symbol *entry;
bool hasReexports = false;
std::vector<llvm::StringRef> frameworkSearchPaths;
std::vector<llvm::StringRef> runtimePaths;
std::vector<Symbol *> explicitUndefineds;
+
llvm::DenseMap<llvm::StringRef, SymbolPriorityEntry> priorities;
SectionRenameMap sectionRenameMap;
SegmentRenameMap segmentRenameMap;
+
+ SymbolPatterns exportedSymbols;
+ SymbolPatterns unexportedSymbols;
};
// The symbol with the highest priority should be ordered first in the output
return version.rawValue();
}
+void SymbolPatterns::clear() {
+ literals.clear();
+ globs.clear();
+}
+
+void SymbolPatterns::insert(StringRef symbolName) {
+ if (symbolName.find_first_of("*?[]") == StringRef::npos)
+ literals.insert(CachedHashStringRef(symbolName));
+ else if (Expected<GlobPattern> pattern = GlobPattern::create(symbolName))
+ globs.emplace_back(*pattern);
+ else
+ error("invalid symbol-name pattern: " + symbolName);
+}
+
+bool SymbolPatterns::matchLiteral(StringRef symbolName) const {
+ return literals.contains(CachedHashStringRef(symbolName));
+}
+
+bool SymbolPatterns::matchGlob(StringRef symbolName) const {
+ for (const llvm::GlobPattern &glob : globs)
+ if (glob.match(symbolName))
+ return true;
+ return false;
+}
+
+bool SymbolPatterns::match(StringRef symbolName) const {
+ return matchLiteral(symbolName) || matchGlob(symbolName);
+}
+
+static void handleSymbolPatterns(opt::InputArgList &args,
+ SymbolPatterns &symbolPatterns,
+ unsigned singleOptionCode,
+ unsigned listFileOptionCode) {
+ for (opt::Arg *arg : args.filtered(singleOptionCode))
+ symbolPatterns.insert(arg->getValue());
+ for (opt::Arg *arg : args.filtered(listFileOptionCode)) {
+ StringRef path = arg->getValue();
+ Optional<MemoryBufferRef> buffer = readFile(path);
+ if (!buffer) {
+ error("Could not read symbol file: " + path);
+ continue;
+ }
+ MemoryBufferRef mbref = *buffer;
+ for (StringRef line : args::getLines(mbref)) {
+ line = line.take_until([](char c) { return c == '#'; }).trim();
+ if (!line.empty())
+ symbolPatterns.insert(line);
+ }
+ }
+}
+
bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
raw_ostream &stdoutOS, raw_ostream &stderrOS) {
lld::stdoutOS = &stdoutOS;
validName(arg->getValue(1));
}
+ 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();
+ }
+
config->saveTemps = args.hasArg(OPT_save_temps);
config->adhocCodesign = args.hasFlag(
def grp_resolve : OptionGroup<"resolve">, HelpText<"SYMBOL RESOLUTION">;
-def exported_symbols_list : Separate<["-"], "exported_symbols_list">,
- MetaVarName<"<file>">,
- HelpText<"Symbols specified in <file> remain global, while others become private externs">,
- Flags<[HelpHidden]>,
- Group<grp_resolve>;
def exported_symbol : Separate<["-"], "exported_symbol">,
MetaVarName<"<symbol>">,
HelpText<"<symbol> remains global, while others become private externs">,
- Flags<[HelpHidden]>,
Group<grp_resolve>;
-def unexported_symbols_list : Separate<["-"], "unexported_symbols_list">,
+def exported_symbols_list : Separate<["-"], "exported_symbols_list">,
MetaVarName<"<file>">,
- HelpText<"Global symbols specified in <file> become private externs">,
- Flags<[HelpHidden]>,
+ HelpText<"Symbols specified in <file> remain global, while others become private externs">,
Group<grp_resolve>;
def unexported_symbol : Separate<["-"], "unexported_symbol">,
MetaVarName<"<symbol>">,
HelpText<"Global <symbol> becomes private extern">,
- Flags<[HelpHidden]>,
+ Group<grp_resolve>;
+def unexported_symbols_list : Separate<["-"], "unexported_symbols_list">,
+ MetaVarName<"<file>">,
+ HelpText<"Global symbols specified in <file> become private externs">,
Group<grp_resolve>;
def reexported_symbols_list : Separate<["-"], "reexported_symbols_list">,
MetaVarName<"<file>">,
trieBuilder.setImageBase(in.header->addr);
for (const Symbol *sym : symtab->getSymbols()) {
if (const auto *defined = dyn_cast<Defined>(sym)) {
- if (defined->privateExtern)
- continue;
+ if (config->exportedSymbols.empty()) {
+ if (defined->privateExtern ||
+ config->unexportedSymbols.match(defined->getName()))
+ continue;
+ } else {
+ if (!config->exportedSymbols.match(defined->getName()))
+ continue;
+ }
trieBuilder.addSymbol(*defined);
hasWeakSymbol = hasWeakSymbol || sym->isWeakDef();
}
--- /dev/null
+# REQUIRES: x86
+
+# RUN: split-file %s %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos %t/default.s -o %t/default.o
+
+## Check that mixing exported and unexported symbol options yields an error
+# 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
+
+# CONFLICT: error: cannot use both -exported_symbol* and -unexported_symbol* options
+# CONFLICT-NEXT: >>> ignoring unexports
+
+#--- default.s
+
+.macro DEFSYM, type, sym
+\type \sym
+\sym:
+ retq
+.endm
+
+DEFSYM .globl, _keep_globl
+DEFSYM .globl, _hide_globl
+DEFSYM .private_extern, _keep_private
+DEFSYM .private_extern, _show_private
+
+## Check that the export trie is unaltered
+# RUN: %lld -dylib %t/default.o -o %t/default
+# RUN: llvm-objdump --macho --exports-trie %t/default | \
+# RUN: FileCheck --check-prefix=DEFAULT %s
+
+# DEFAULT-LABEL: Exports trie:
+# DEFAULT-DAG: _hide_globl
+# DEFAULT-DAG: _keep_globl
+# DEFAULT-NOT: _hide_private
+# DEFAULT-NOT: _show_private
+
+## Check that the export trie is properly augmented
+## Check that non-matching literal pattern has no effect
+# RUN: %lld -dylib %t/default.o -o %t/export \
+# RUN: -exported_symbol _show_private \
+# RUN: -exported_symbol _extra_cruft -exported_symbol '*xtra_cr?ft'
+# RUN: llvm-objdump --macho --exports-trie %t/export | \
+# RUN: FileCheck --check-prefix=EXPORTED %s
+
+# EXPORTED-LABEL: Exports trie:
+# EXPORTED-DAG: _show_private
+# EXPORTED-NOT: _hide_globl
+# EXPORTED-NOT: _keep_globl
+# EXPORTED-NOT: _hide_private
+# EXPORTED-NOT: {{.*}}xtra_cr{{.}}ft
+
+## Check that the export trie is properly diminished
+## Check that non-matching glob pattern has no effect
+# RUN: %lld -dylib %t/default.o -o %t/unexport \
+# RUN: -unexported_symbol _hide_global
+# RUN: llvm-objdump --macho --exports-trie %t/unexport | \
+# RUN: FileCheck --check-prefix=UNEXPORTED %s
+
+# UNEXPORTED-LABEL: Exports trie:
+# UNEXPORTED-DAG: _keep_globl
+# UNEXPORTED-NOT: _hide_globl
+# UNEXPORTED-NOT: _show_private
+# UNEXPORTED-NOT: _hide_private
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos \
+# RUN: %t/symdefs.s -o %t/symdefs.o
+
+#--- symdefs.s
+
+.macro DEFSYM, sym
+.private_extern \sym
+\sym:
+ retq
+.endm
+
+DEFSYM literal_only
+DEFSYM literal_also
+DEFSYM globby_only
+DEFSYM globby_also
+
+#--- literals
+
+ literal_only # comment
+ literal_also
+
+# globby_only
+ globby_also
+
+## Check that only string-literal patterns match
+## Check that comments and blank lines are stripped from symbol list
+# RUN: %lld -dylib %t/symdefs.o -o %t/literal \
+# RUN: -exported_symbols_list %t/literals
+# RUN: llvm-objdump --macho --exports-trie %t/literal | \
+# RUN: FileCheck --check-prefix=LITERAL %s
+
+# LITERAL-DAG: literal_only
+# LITERAL-DAG: literal_also
+# LITERAL-DAG: globby_also
+# LITERAL-NOT: globby_only
+
+#--- globbys
+
+# literal_only
+ l?ter[aeiou]l_*[^y] # comment
+
+ *gl?bby_*
+
+## Check that only glob patterns match
+## Check that comments and blank lines are stripped from symbol list
+# RUN: %lld -dylib %t/symdefs.o -o %t/globby \
+# RUN: -exported_symbols_list %t/globbys
+# RUN: llvm-objdump --macho --exports-trie %t/globby | \
+# RUN: FileCheck --check-prefix=GLOBBY %s
+
+# GLOBBY-DAG: literal_also
+# GLOBBY-DAG: globby_only
+# GLOBBY-DAG: globby_also
+# GLOBBY-NOT: literal_only