Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -33,6 +33,13 @@ enum class BuildIdKind { None, Fnv1, Md5, Sha1, Hexstring }; +enum class UnresolvedSymbols { + IgnoreAll, + ReportAll, + IgnoreInObjectFiles, + IgnoreInSharedLibs +}; + // This struct contains symbols version definition that // can be found in version script if it is used for link. struct Version { @@ -101,6 +108,7 @@ bool SysvHash = true; bool Threads; bool Trace; + UnresolvedSymbols UnresolvedSymbols = UnresolvedSymbols::ReportAll; bool Verbose; bool VersionScriptGlobalByDefault = true; bool WarnCommon; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -414,6 +414,18 @@ for (auto *Arg : Args.filtered(OPT_undefined)) Config->Undefined.push_back(Arg->getValue()); + if (auto *Arg = Args.getLastArg(OPT_unresolved_symbols)) { + StringRef S = Arg->getValue(); + if (S == "ignore-all") + Config->UnresolvedSymbols = UnresolvedSymbols::IgnoreAll; + else if (S == "ignore-in-object-files") + Config->UnresolvedSymbols = UnresolvedSymbols::IgnoreInObjectFiles; + else if (S == "ignore-in-shared-libs") + Config->UnresolvedSymbols = UnresolvedSymbols::IgnoreInSharedLibs; + else if (S != "report-all") + error("unknown --unresolved-symbols value: " + S); + } + if (auto *Arg = Args.getLastArg(OPT_dynamic_list)) if (Optional Buffer = readFile(Arg->getValue())) parseDynamicList(*Buffer); Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -150,6 +150,9 @@ def undefined: J<"undefined=">, HelpText<"Force undefined symbol during linking">; +def unresolved_symbols: J<"unresolved-symbols=">, + HelpText<"Determine how to handle unresolved symbols.">; + def verbose: F<"verbose">, HelpText<"Verbose mode">; def version: F<"version">, HelpText<"Display the version number">; Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -9,6 +9,7 @@ #include "Writer.h" #include "Config.h" +#include "InputFiles.h" #include "LinkerScript.h" #include "OutputSections.h" #include "Relocations.h" @@ -273,21 +274,37 @@ } template +static void showUndefinedError2(StringRef Name, InputFile* File) { + std::string Msg = "undefined symbol: " + Name.str(); + if (File) + Msg += " in " + getFilename(File); + if (Config->NoinhibitExec) + warning(Msg); + else + error(Msg); +} + +template static void showUndefinedError(SymbolBody *Sym) { + showUndefinedError2(Sym->getName().str(), Sym->getSourceFile()); +} + +template static void reportUndefined(SymbolTable &Symtab, SymbolBody *Sym) { if (!Config->NoUndefined) { if (Config->Relocatable) return; + if (Config->UnresolvedSymbols == UnresolvedSymbols::IgnoreAll) + return; + + if (Config->UnresolvedSymbols == UnresolvedSymbols::IgnoreInObjectFiles) + if (InputFile *File = Sym->getSourceFile()) + if (auto *O = dyn_cast>(File)) + return; + if (Config->Shared && Sym->symbol()->Visibility == STV_DEFAULT) return; } - - std::string Msg = "undefined symbol: " + Sym->getName().str(); - if (InputFile *File = Sym->getSourceFile()) - Msg += " in " + getFilename(File); - if (Config->NoinhibitExec) - warning(Msg); - else - error(Msg); + showUndefinedError(Sym); } template @@ -713,6 +730,25 @@ reinterpret_cast *>(S)->sortCtorsDtors(); } +template +static void reportUndefinedFromSharedLibs(SymbolTable &Symtab) { + // In lld we do not report about undefines in DSO, so + // ReportAll option which is default should be ignored and used + // only for symbols from object files. Also we return on IgnoreAll + // and IgnoreInSharedLibs values here, what leave single value to check. + if (Config->UnresolvedSymbols != UnresolvedSymbols::IgnoreInObjectFiles) + return; + + for (const std::unique_ptr> &File : Symtab.getSharedFiles()) + for (StringRef U : File->getUndefinedSymbols()) { + SymbolBody *Sym = Symtab.find(U); + if (!Sym) + showUndefinedError2(U, File.get()); + else if (Sym->isUndefined()) + showUndefinedError(Sym); + } +} + // Create output section objects and add them to OutputSections. template void Writer::createSections() { // Add .interp first because some loaders want to see that section @@ -810,6 +846,8 @@ for (OutputSectionBase *Sec : OutputSections) Sec->assignOffsets(); + reportUndefinedFromSharedLibs(Symtab); + // Now that we have defined all possible symbols including linker- // synthesized ones. Visit all symbols to give the finishing touches. std::vector CommonSymbols; Index: test/ELF/Inputs/unresolved-symbols.s =================================================================== --- test/ELF/Inputs/unresolved-symbols.s +++ test/ELF/Inputs/unresolved-symbols.s @@ -0,0 +1,3 @@ +.globl _shared +_shared: + callq undef@PLT Index: test/ELF/unresolved-symbols.s =================================================================== --- test/ELF/unresolved-symbols.s +++ test/ELF/unresolved-symbols.s @@ -0,0 +1,63 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/unresolved-symbols.s -o %t2.o +# RUN: ld.lld -shared %t2.o -o %t.so + +## Check that %t2.o contains undefined symbol undef. +# RUN: not ld.lld %t1.o %t2.o -o %t 2>&1 | \ +# RUN: FileCheck -check-prefix=UNDCHECK %s +# UNDCHECK: undefined symbol: undef in {{.*}}2.o + +## Error out if unknown option value was set. +# RUN: not ld.lld %t1.o %t2.o -o %t --unresolved-symbols=xxx 2>&1 | \ +# RUN: FileCheck -check-prefix=ERR1 %s +# ERR1: unknown --unresolved-symbols value: xxx + +## Ignore all should not produce error for symbols from object except +## case when --no-undefined specified. +# RUN: ld.lld %t2.o -o %t1_1 --unresolved-symbols=ignore-all +# RUN: llvm-readobj %t1_1 > /dev/null 2>&1 +# RUN: not ld.lld %t2.o -o %t1_2 --unresolved-symbols=ignore-all --no-undefined 2>&1 | \ +# RUN: FileCheck -check-prefix=ERRUND %s +# ERRUND: undefined symbol: undef +## Also ignore all should not produce error for symbols from DSOs. +# RUN: ld.lld %t1.o %t.so -o %t1_3 --unresolved-symbols=ignore-all +# RUN: llvm-readobj %t1_3 > /dev/null 2>&1 + +## Ignoring undefines in objects should not produce error for symbol from object. +# RUN: ld.lld %t1.o %t2.o -o %t2 --unresolved-symbols=ignore-in-object-files +# RUN: llvm-readobj %t2 > /dev/null 2>&1 +## But should produce for undefines from shared libs. +# RUN: not ld.lld %t1.o %t.so -o %t2_1 --unresolved-symbols=ignore-in-object-files 2>&1 | \ +# RUN: FileCheck -check-prefix=ERRUND %s + +## Ignoring undefines in shared should produce error for symbol from object. +# RUN: not ld.lld %t2.o -o %t3 --unresolved-symbols=ignore-in-shared-libs 2>&1 | \ +# RUN: FileCheck -check-prefix=ERRUND %s +## And should not produce errors for symbols from DSO. +# RUN: ld.lld %t1.o %t.so -o %t3_1 --unresolved-symbols=ignore-in-shared-libs +# RUN: llvm-readobj %t3_1 > /dev/null 2>&1 + +## Ignoring undefines in shared libs should not produce error for symbol from object +## if we are linking DSO. +# RUN: ld.lld -shared %t1.o -o %t4 --unresolved-symbols=ignore-in-shared-libs +# RUN: llvm-readobj %t4 > /dev/null 2>&1 + +## Do not report undefines if linking relocatable. +# RUN: ld.lld -r %t1.o %t2.o -o %t5 --unresolved-symbols=report-all +# RUN: llvm-readobj %t5 > /dev/null 2>&1 + +## report-all is the default one. Check that we do not report +## undefines from DSO and do report undefines from object. With +## report-all specified and without. +# RUN: ld.lld -shared %t1.o %t.so -o %t6 --unresolved-symbols=report-all +# RUN: llvm-readobj %t6 > /dev/null 2>&1 +# RUN: ld.lld -shared %t1.o %t.so -o %t6_1 +# RUN: llvm-readobj %t6_1 > /dev/null 2>&1 +# RUN: not ld.lld %t2.o -o %t7 --unresolved-symbols=report-all 2>&1 | \ +# RUN: FileCheck -check-prefix=ERRUND %s +# RUN: not ld.lld %t2.o -o %t7_1 2>&1 | \ +# RUN: FileCheck -check-prefix=ERRUND %s + +.globl _start +_start: