Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -146,6 +146,7 @@ bool Trace; bool Verbose; bool WarnCommon; + bool WarnOnce; bool WarnMissingEntry; bool ZCombreloc; bool ZExecstack; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -681,6 +681,7 @@ Config->UnresolvedSymbols = getUnresolvedSymbolPolicy(Args); Config->Verbose = Args.hasArg(OPT_verbose); Config->WarnCommon = Args.hasArg(OPT_warn_common); + Config->WarnOnce = Args.hasArg(OPT_warn_once); Config->ZCombreloc = !hasZOption(Args, "nocombreloc"); Config->ZExecstack = hasZOption(Args, "execstack"); Config->ZNocopyreloc = hasZOption(Args, "nocopyreloc"); Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -289,6 +289,9 @@ def warn_common: F<"warn-common">, HelpText<"Warn about duplicate common symbols">; +def warn_once: F<"warn-once">, + HelpText<"Show error or warning for each undefined symbol only once">; + def warn_unresolved_symbols: F<"warn-unresolved-symbols">, HelpText<"Report unresolved symbols as warnings">; Index: ELF/Relocations.cpp =================================================================== --- ELF/Relocations.cpp +++ ELF/Relocations.cpp @@ -675,6 +675,7 @@ template static void reportUndefined(SymbolBody &Sym, InputSectionBase &S, uint64_t Offset) { + assert(Sym.isUndefined()); if (Config->UnresolvedSymbols == UnresolvedPolicy::IgnoreAll) return; @@ -683,6 +684,11 @@ if (Config->UnresolvedSymbols == UnresolvedPolicy::Ignore && CanBeExternal) return; + Undefined *U = cast(&Sym); + if (U->Reported && Config->WarnOnce) + return; + U->Reported = true; + std::string Msg = "undefined symbol: " + toString(Sym) + "\n>>> referenced by "; Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -206,6 +206,9 @@ static bool classof(const SymbolBody *S) { return S->kind() == UndefinedKind; } + + // Used for error reporting. True if symbol was reported. + unsigned Reported : 1; }; class SharedSymbol : public Defined { Index: ELF/Symbols.cpp =================================================================== --- ELF/Symbols.cpp +++ ELF/Symbols.cpp @@ -290,7 +290,8 @@ Undefined::Undefined(StringRefZ Name, bool IsLocal, uint8_t StOther, uint8_t Type, InputFile *File) - : SymbolBody(SymbolBody::UndefinedKind, Name, IsLocal, StOther, Type) { + : SymbolBody(SymbolBody::UndefinedKind, Name, IsLocal, StOther, Type), + Reported(false) { this->File = File; } Index: test/ELF/warn-once.s =================================================================== --- test/ELF/warn-once.s +++ test/ELF/warn-once.s @@ -0,0 +1,17 @@ +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: not ld.lld %t.o -o %t1 2>&1 | FileCheck -check-prefix=ERR1 %s +# ERR1: error: undefined symbol: foo +# ERR1: error: undefined symbol: foo +# ERR1: error: undefined symbol: boo +# ERR1: error: undefined symbol: boo + +# RUN: not ld.lld --warn-once %t.o -o %t1 2>&1 | FileCheck -check-prefix=ERR2 %s +# ERR2: error: undefined symbol: foo +# ERR2-NOT: foo +# ERR2: error: undefined symbol: boo +# ERR2-NOT: boo + +callq foo@PLT +callq foo@PLT +callq boo@PLT +callq boo@PLT