[lld-macho] Support -non_global_symbols_strip_list, -non_global_symbols_no_strip_list, -x
PR/55600 Differential Revision: https://reviews.llvm.org/D126046
This commit is contained in:
parent
d5ebba2aa6
commit
fae6bd7563
|
@ -94,6 +94,13 @@ public:
|
|||
bool match(llvm::StringRef symbolName) const;
|
||||
};
|
||||
|
||||
enum class SymtabPresence {
|
||||
All,
|
||||
None,
|
||||
SelectivelyIncluded,
|
||||
SelectivelyExcluded,
|
||||
};
|
||||
|
||||
struct Configuration {
|
||||
Symbol *entry = nullptr;
|
||||
bool hasReexports = false;
|
||||
|
@ -179,6 +186,9 @@ struct Configuration {
|
|||
SymbolPatterns unexportedSymbols;
|
||||
SymbolPatterns whyLive;
|
||||
|
||||
SymtabPresence localSymbolsPresence = SymtabPresence::All;
|
||||
SymbolPatterns localSymbolPatterns;
|
||||
|
||||
bool zeroModTime = false;
|
||||
|
||||
llvm::StringRef osoPrefix;
|
||||
|
|
|
@ -938,26 +938,29 @@ bool SymbolPatterns::match(StringRef symbolName) const {
|
|||
return matchLiteral(symbolName) || matchGlob(symbolName);
|
||||
}
|
||||
|
||||
static void handleSymbolPatternsListHelper(const Arg *arg,
|
||||
SymbolPatterns &symbolPatterns) {
|
||||
StringRef path = arg->getValue();
|
||||
Optional<MemoryBufferRef> buffer = readFile(path);
|
||||
if (!buffer) {
|
||||
error("Could not read symbol file: " + path);
|
||||
return;
|
||||
}
|
||||
MemoryBufferRef mbref = *buffer;
|
||||
for (StringRef line : args::getLines(mbref)) {
|
||||
line = line.take_until([](char c) { return c == '#'; }).trim();
|
||||
if (!line.empty())
|
||||
symbolPatterns.insert(line);
|
||||
}
|
||||
}
|
||||
static void handleSymbolPatterns(InputArgList &args,
|
||||
SymbolPatterns &symbolPatterns,
|
||||
unsigned singleOptionCode,
|
||||
unsigned listFileOptionCode) {
|
||||
for (const Arg *arg : args.filtered(singleOptionCode))
|
||||
symbolPatterns.insert(arg->getValue());
|
||||
for (const 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);
|
||||
}
|
||||
}
|
||||
for (const Arg *arg : args.filtered(listFileOptionCode))
|
||||
handleSymbolPatternsListHelper(arg, symbolPatterns);
|
||||
}
|
||||
|
||||
static void createFiles(const InputArgList &args) {
|
||||
|
@ -1406,9 +1409,53 @@ bool macho::link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
|
|||
">>> ignoring unexports");
|
||||
config->unexportedSymbols.clear();
|
||||
}
|
||||
|
||||
// Imitating LD64's:
|
||||
// -non_global_symbols_no_strip_list and -non_global_symbols_strip_list can't
|
||||
// both be present.
|
||||
// But -x can be used with either of these two, in which case, the last arg
|
||||
// takes effect.
|
||||
// (TODO: This is kind of confusing - considering disallowing using them
|
||||
// together for a more straightforward behaviour)
|
||||
{
|
||||
bool includeLocal = false;
|
||||
bool excludeLocal = false;
|
||||
for (const Arg *arg :
|
||||
args.filtered(OPT_x, OPT_non_global_symbols_no_strip_list,
|
||||
OPT_non_global_symbols_strip_list)) {
|
||||
switch (arg->getOption().getID()) {
|
||||
case OPT_x:
|
||||
config->localSymbolsPresence = SymtabPresence::None;
|
||||
|
||||
break;
|
||||
case OPT_non_global_symbols_no_strip_list:
|
||||
if (excludeLocal) {
|
||||
error("cannot use both -non_global_symbols_no_strip_list and "
|
||||
"-non_global_symbols_strip_list");
|
||||
} else {
|
||||
includeLocal = true;
|
||||
config->localSymbolsPresence = SymtabPresence::SelectivelyIncluded;
|
||||
handleSymbolPatternsListHelper(arg, config->localSymbolPatterns);
|
||||
}
|
||||
break;
|
||||
case OPT_non_global_symbols_strip_list:
|
||||
if (includeLocal) {
|
||||
error("cannot use both -non_global_symbols_no_strip_list and "
|
||||
"-non_global_symbols_strip_list");
|
||||
} else {
|
||||
excludeLocal = true;
|
||||
config->localSymbolsPresence = SymtabPresence::SelectivelyExcluded;
|
||||
handleSymbolPatternsListHelper(arg, config->localSymbolPatterns);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("unexpected option");
|
||||
}
|
||||
}
|
||||
}
|
||||
// Explicitly-exported literal symbols must be defined, but might
|
||||
// languish in an archive if unreferenced elsewhere. Light a fire
|
||||
// under those lazy symbols!
|
||||
// languish in an archive if unreferenced elsewhere or if they are in the
|
||||
// non-global strip list. Light a fire under those lazy symbols!
|
||||
for (const CachedHashStringRef &cachedName : config->exportedSymbols.literals)
|
||||
symtab->addUndefined(cachedName.val(), /*file=*/nullptr,
|
||||
/*isWeakRef=*/false);
|
||||
|
|
|
@ -581,17 +581,14 @@ def S : Flag<["-"], "S">,
|
|||
Group<grp_symtab>;
|
||||
def x : Flag<["-"], "x">,
|
||||
HelpText<"Exclude non-global symbols from the output symbol table">,
|
||||
Flags<[HelpHidden]>,
|
||||
Group<grp_symtab>;
|
||||
def non_global_symbols_strip_list : Separate<["-"], "non_global_symbols_strip_list">,
|
||||
MetaVarName<"<path>">,
|
||||
HelpText<"Specify in <path> the non-global symbols that should be removed from the output symbol table">,
|
||||
Flags<[HelpHidden]>,
|
||||
Group<grp_symtab>;
|
||||
def non_global_symbols_no_strip_list : Separate<["-"], "non_global_symbols_no_strip_list">,
|
||||
MetaVarName<"<path>">,
|
||||
HelpText<"Specify in <path> the non-global symbols that should remain in the output symbol table">,
|
||||
Flags<[HelpHidden]>,
|
||||
Group<grp_symtab>;
|
||||
def oso_prefix : Separate<["-"], "oso_prefix">,
|
||||
MetaVarName<"<path>">,
|
||||
|
|
|
@ -963,16 +963,41 @@ void SymtabSection::finalizeContents() {
|
|||
symbols.push_back({sym, strx});
|
||||
};
|
||||
|
||||
std::function<void(Symbol *)> localSymbolsHandler;
|
||||
switch (config->localSymbolsPresence) {
|
||||
case SymtabPresence::All:
|
||||
localSymbolsHandler = [&](Symbol *sym) { addSymbol(localSymbols, sym); };
|
||||
break;
|
||||
case SymtabPresence::None:
|
||||
localSymbolsHandler = [&](Symbol *) { /* Do nothing*/ };
|
||||
break;
|
||||
case SymtabPresence::SelectivelyIncluded:
|
||||
localSymbolsHandler = [&](Symbol *sym) {
|
||||
if (config->localSymbolPatterns.match(sym->getName()))
|
||||
addSymbol(localSymbols, sym);
|
||||
};
|
||||
break;
|
||||
case SymtabPresence::SelectivelyExcluded:
|
||||
localSymbolsHandler = [&](Symbol *sym) {
|
||||
if (!config->localSymbolPatterns.match(sym->getName()))
|
||||
addSymbol(localSymbols, sym);
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
// Local symbols aren't in the SymbolTable, so we walk the list of object
|
||||
// files to gather them.
|
||||
for (const InputFile *file : inputFiles) {
|
||||
if (auto *objFile = dyn_cast<ObjFile>(file)) {
|
||||
for (Symbol *sym : objFile->symbols) {
|
||||
if (auto *defined = dyn_cast_or_null<Defined>(sym)) {
|
||||
if (defined->isExternal() || !defined->isLive() ||
|
||||
!defined->includeInSymtab)
|
||||
continue;
|
||||
addSymbol(localSymbols, sym);
|
||||
// But if `-x` is set, then we don't need to.
|
||||
if (config->localSymbolsPresence != SymtabPresence::None) {
|
||||
for (const InputFile *file : inputFiles) {
|
||||
if (auto *objFile = dyn_cast<ObjFile>(file)) {
|
||||
for (Symbol *sym : objFile->symbols) {
|
||||
if (auto *defined = dyn_cast_or_null<Defined>(sym)) {
|
||||
if (defined->isExternal() || !defined->isLive() ||
|
||||
!defined->includeInSymtab)
|
||||
continue;
|
||||
localSymbolsHandler(sym);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -981,7 +1006,7 @@ void SymtabSection::finalizeContents() {
|
|||
// __dyld_private is a local symbol too. It's linker-created and doesn't
|
||||
// exist in any object file.
|
||||
if (Defined *dyldPrivate = in.stubHelper->dyldPrivate)
|
||||
addSymbol(localSymbols, dyldPrivate);
|
||||
localSymbolsHandler(dyldPrivate);
|
||||
|
||||
for (Symbol *sym : symtab->getSymbols()) {
|
||||
if (!sym->isLive())
|
||||
|
@ -991,7 +1016,7 @@ void SymtabSection::finalizeContents() {
|
|||
continue;
|
||||
assert(defined->isExternal());
|
||||
if (defined->privateExtern)
|
||||
addSymbol(localSymbols, defined);
|
||||
localSymbolsHandler(defined);
|
||||
else
|
||||
addSymbol(externalSymbols, defined);
|
||||
} else if (auto *dysym = dyn_cast<DylibSymbol>(sym)) {
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
# REQUIRES: x86
|
||||
|
||||
# RUN: rm -rf %t; split-file %s %t
|
||||
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos %t/main.s -o %t/main.o
|
||||
|
||||
## Check that -non_global_symbols_no_strip_list and -non_global_symbols_strip_list
|
||||
## can't be used at the same time.
|
||||
# RUN: not %lld %t/main.o -o /dev/null \
|
||||
# RUN: -non_global_symbols_no_strip_list %t/foo.txt \
|
||||
# RUN: -non_global_symbols_strip_list %t/foo.txt 2>&1 | \
|
||||
# RUN: FileCheck --check-prefix=CONFLICT %s
|
||||
|
||||
# CONFLICT: error: cannot use both -non_global_symbols_no_strip_list and -non_global_symbols_strip_list
|
||||
|
||||
## Check that -x causes none of the local symbols to be emitted.
|
||||
# RUN: %lld %t/main.o -x -o %t/no_local.out
|
||||
# RUN: llvm-nm %t/no_local.out | FileCheck --check-prefix NO_LOCAL %s
|
||||
|
||||
# NO_LOCAL-NOT: t _foo
|
||||
# NO_LOCAL-NOT: t _bar
|
||||
# NO_LOCAL-NOT: t _baz
|
||||
# NO_LOCAL: T _main
|
||||
|
||||
## Check that when using -x with -non_global_symbols_no_strip_list, whichever appears
|
||||
## last in the command line arg list will take precedence.
|
||||
# RUN: %lld %t/main.o -x -non_global_symbols_no_strip_list %t/foo.txt -o %t/x_then_no_strip.out
|
||||
# RUN: llvm-nm %t/x_then_no_strip.out | FileCheck --check-prefix X-NO-STRIP %s
|
||||
|
||||
# RUN: %lld %t/main.o -non_global_symbols_no_strip_list %t/foo.txt -x -o %t/no_strip_then_x.out
|
||||
# RUN: llvm-nm %t/no_strip_then_x.out | FileCheck --check-prefix NO_LOCAL %s
|
||||
|
||||
# X-NO-STRIP-NOT: t _bar
|
||||
# X-NO-STRIP-DAG: t _foo
|
||||
# X-NO-STRIP-DAG: T _main
|
||||
|
||||
## Check that -non_global_symbols_no_strip_list can be specified more than once
|
||||
## (The final no-strip list is the union of all these)
|
||||
# RUN: %lld %t/main.o -o %t/no_strip_multi.out \
|
||||
# RUN: -non_global_symbols_no_strip_list %t/foo.txt \
|
||||
# RUN: -non_global_symbols_no_strip_list %t/bar.txt
|
||||
# RUN: llvm-nm %t/no_strip_multi.out | FileCheck --check-prefix NO-STRIP-MULTI %s
|
||||
|
||||
# NO-STRIP-MULTI-NOT: t _baz
|
||||
# NO-STRIP-MULTI-DAG: t _foo
|
||||
# NO-STRIP-MULTI-DAG: t _bar
|
||||
# NO-STRIP-MULTI-DAG: T _main
|
||||
|
||||
## Check that when using -x with -non_global_symbols_strip_list, whichever appears
|
||||
## last in the command line arg list will take precedence.
|
||||
# RUN: %lld %t/main.o -x -non_global_symbols_strip_list %t/foo.txt -o %t/x_then_strip.out
|
||||
# RUN: llvm-nm %t/x_then_strip.out | FileCheck --check-prefix X-STRIP %s
|
||||
|
||||
# RUN: %lld %t/main.o -non_global_symbols_strip_list %t/foo.txt -x -o %t/strip_then_x.out
|
||||
# RUN: llvm-nm %t/no_strip_then_x.out | FileCheck --check-prefix NO_LOCAL %s
|
||||
|
||||
# X-STRIP-NOT: t _foo
|
||||
# X-STRIP-DAG: t _bar
|
||||
# X-STRIP-DAG: t _baz
|
||||
# X-STRIP-DAG: T _main
|
||||
|
||||
## Check that -non_global_symbols_strip_list can be specified more than once
|
||||
## (The final strip list is the union of all these)
|
||||
# RUN: %lld %t/main.o -o %t/strip_multi.out \
|
||||
# RUN: -non_global_symbols_strip_list %t/foo.txt \
|
||||
# RUN: -non_global_symbols_strip_list %t/bar.txt
|
||||
# RUN: llvm-nm %t/strip_multi.out | FileCheck --check-prefix STRIP-MULTI %s
|
||||
|
||||
# STRIP-MULTI-NOT: t _foo
|
||||
# STRIP-MULTI-NOT: t _bar
|
||||
# STRIP-MULTI-DAG: t _baz
|
||||
# STRIP-MULTI-DAG: T _main
|
||||
|
||||
## Test interactions with exported_symbol.
|
||||
# RUN: %lld %t/main.o -o %t/strip_all_export_one.out \
|
||||
# RUN: -x -exported_symbol _foo \
|
||||
# RUN: -undefined dynamic_lookup
|
||||
# RUN: llvm-nm %t/strip_all_export_one.out | FileCheck --check-prefix STRIP-EXP %s
|
||||
|
||||
# STRIP-EXP: U _foo
|
||||
# STRIP-EXP-EMPTY:
|
||||
|
||||
## Test interactions of -x and -non_global_symbols_strip_list with unexported_symbol.
|
||||
# RUN: %lld %t/main.o -o %t/strip_x_unexport_one.out \
|
||||
# RUN: -x -unexported_symbol _globby \
|
||||
# RUN: -undefined dynamic_lookup
|
||||
|
||||
# RUN: %lld %t/main.o -o %t/strip_all_unexport_one.out \
|
||||
# RUN: -non_global_symbols_strip_list %t/globby.txt \
|
||||
# RUN: -non_global_symbols_strip_list %t/foo.txt \
|
||||
# RUN: -non_global_symbols_strip_list %t/bar.txt \
|
||||
# RUN: -unexported_symbol _globby \
|
||||
# RUN: -undefined dynamic_lookup
|
||||
|
||||
# RUN: llvm-nm %t/strip_x_unexport_one.out | FileCheck --check-prefix STRIP-UNEXP %s
|
||||
# RUN: llvm-nm %t/strip_all_unexport_one.out | FileCheck --check-prefix STRIP-UNEXP %s
|
||||
|
||||
## -unexported_symbol made _globby a local, therefore it should be stripped by -x too
|
||||
# STRIP-UNEXP: T __mh_execute_header
|
||||
# STRIP-UNEXP-DAG: T _main
|
||||
# STRIP-UNEXP-EMPTY:
|
||||
|
||||
## Test interactions of -non_global_symbols_strip_list and unexported_symbol.
|
||||
# RUN: %lld %t/main.o -undefined dynamic_lookup -o %t/no_strip_unexport.out \
|
||||
# RUN: -non_global_symbols_no_strip_list %t/globby.txt \
|
||||
# RUN: -unexported_symbol _globby
|
||||
|
||||
# RUN: llvm-nm %t/no_strip_unexport.out | FileCheck --check-prefix NOSTRIP-UNEXP %s
|
||||
|
||||
# NOSTRIP-UNEXP: T __mh_execute_header
|
||||
# NOSTRIP-UNEXP-DAG: T _main
|
||||
# NOSTRIP-UNEXP-DAG: t _globby
|
||||
# NOSTRIP-UNEXP-EMPTY:
|
||||
|
||||
#--- foo.txt
|
||||
_foo
|
||||
|
||||
#--- bar.txt
|
||||
_bar
|
||||
|
||||
#--- globby.txt
|
||||
_globby
|
||||
|
||||
#--- main.s
|
||||
.globl _main
|
||||
.globl _globby
|
||||
|
||||
_foo:
|
||||
ret
|
||||
|
||||
_bar:
|
||||
ret
|
||||
|
||||
_baz:
|
||||
ret
|
||||
|
||||
_main:
|
||||
callq _foo
|
||||
ret
|
||||
|
||||
_globby:
|
||||
ret
|
Loading…
Reference in New Issue