diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -226,6 +226,7 @@ bool warnCommon; bool warnMissingEntry; bool warnSymbolOrdering; + bool whyExtract; bool writeAddends; bool zCombreloc; bool zCopyreloc; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1203,6 +1203,10 @@ setUnresolvedSymbolPolicy(args); config->Power10Stub = getP10StubOpt(args); + config->whyExtract = args.hasArg(OPT_why_extract); + if (config->whyExtract) + lld::outs() << "reference\textracted\tsymbol\n"; + if (opt::Arg *arg = args.getLastArg(OPT_eb, OPT_el)) { if (arg->getOption().matches(OPT_eb)) config->optEB = true; @@ -1694,13 +1698,17 @@ } // Force Sym to be entered in the output. -static void handleUndefined(Symbol *sym) { +static void handleUndefined(Symbol *sym, const char *option) { // Since a symbol may not be used inside the program, LTO may // eliminate it. Mark the symbol as "used" to prevent it. sym->isUsedInRegularObj = true; - if (sym->isLazy()) - sym->fetch(); + if (!sym->isLazy()) + return; + sym->fetch(); + if (config->whyExtract) + lld::outs() << option << '\t' << toString(sym->file) << '\t' + << toString(*sym) << '\n'; } // As an extension to GNU linkers, lld supports a variant of `-u` @@ -1723,7 +1731,7 @@ } for (Symbol *sym : syms) - handleUndefined(sym); + handleUndefined(sym, "--undefined-glob"); } static void handleLibcall(StringRef name) { @@ -2244,7 +2252,7 @@ // If an entry symbol is in a static archive, pull out that file now. if (Symbol *sym = symtab->find(config->entry)) - handleUndefined(sym); + handleUndefined(sym, "--entry"); // Handle the `--undefined-glob ` options. for (StringRef pat : args::getStrings(args, OPT_undefined_glob)) diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -492,6 +492,8 @@ "Force load of all members in a static library", "Do not force load of all members in a static library (default)">; +def why_extract: FF<"why-extract">, HelpText<"Report why archive members are extracted">; + defm wrap : Eq<"wrap", "Redirect symbol references to __wrap_symbol and " "__real_symbol references to symbol">, MetaVarName<"">; diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -321,6 +321,12 @@ message(toString(sym->file) + s + sym->getName()); } +static void printWhyExtract(const InputFile *reference, + const InputFile &extracted, const Symbol &sym) { + lld::outs() << toString(reference) << '\t' << toString(&extracted) << '\t' + << toString(sym) << '\n'; +} + void elf::maybeWarnUnorderableSymbol(const Symbol *sym) { if (!config->warnSymbolOrdering) return; @@ -533,6 +539,9 @@ file->groupId < other.file->groupId; fetch(); + if (config->whyExtract) + printWhyExtract(other.file, *file, *this); + // We don't report backward references to weak symbols as they can be // overridden later. // @@ -742,7 +751,10 @@ return; } + const InputFile *oldFile = file; other.fetch(); + if (config->whyExtract) + printWhyExtract(oldFile, *file, *this); } void Symbol::resolveShared(const SharedSymbol &other) { diff --git a/lld/test/ELF/why-extract.s b/lld/test/ELF/why-extract.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/why-extract.s @@ -0,0 +1,65 @@ +# REQUIRES: x86 + +# RUN: rm -rf %t && split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/main.s -o %t/main.o +# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/a.s -o %t/a.o +# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/a_b.s -o %t/a_b.o +# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/b.s -o %t/b.o +# RUN: llvm-ar rc %t/a.a %t/a.o +# RUN: llvm-ar rc %t/a_b.a %t/a_b.o +# RUN: llvm-ar rc %t/b.a %t/b.o +# RUN: cd %t + +# RUN: ld.lld main.o a_b.a b.a -o /dev/null --why-extract | FileCheck %s --match-full-lines --strict-whitespace + +# CHECK:reference extracted symbol +# CHECK-NEXT:main.o a_b.a(a_b.o) a +# CHECK-NEXT:a_b.a(a_b.o) b.a(b.o) b() + +# RUN: ld.lld main.o a_b.a b.a -o /dev/null --no-demangle --why-extract | FileCheck %s --check-prefix=MANGLED + +# MANGLED: a_b.a(a_b.o) b.a(b.o) _Z1bv + +# RUN: ld.lld main.o a.a b.a -o /dev/null -u _Z1bv --why-extract | FileCheck %s --check-prefix=UNDEFINED + +## We insert -u symbol before processing other files, so its name is . +## This is not ideal. +# UNDEFINED: b.a(b.o) b() + +# RUN: ld.lld main.o a.a b.a -o /dev/null --undefined-glob '_Z1b*' --why-extract | FileCheck %s --check-prefix=UNDEFINED_GLOB + +# UNDEFINED_GLOB: --undefined-glob b.a(b.o) b() + +# RUN: ld.lld main.o a.a b.a -o /dev/null -e _Z1bv --why-extract | FileCheck %s --check-prefix=ENTRY + +# ENTRY: --entry b.a(b.o) b() + +# RUN: ld.lld main.o b.a -o /dev/null -T a.lds --why-extract | FileCheck %s --check-prefix=SCRIPT + +# SCRIPT: b.a(b.o) b() + +# RUN: ld.lld main.o --start-lib a_b.o b.o --end-lib -o /dev/null --why-extract | FileCheck %s --check-prefix=LAZY + +# LAZY: main.o a_b.o a +# LAZY: a_b.o b.o b() + +#--- main.s +.globl _start +_start: + call a + +#--- a.s +.globl a +a: + +#--- a_b.s +.globl a +a: + call _Z1bv + +#--- b.s +.globl _Z1bv +_Z1bv: + +#--- a.lds +a = _Z1bv;