Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -182,6 +182,7 @@ bool WarnCommon; bool WarnIfuncTextrel; bool WarnMissingEntry; + bool WarnOnce; bool WarnSymbolOrdering; bool WriteAddends; bool ZCombreloc; Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -868,6 +868,7 @@ Args.hasFlag(OPT_warn_ifunc_textrel, OPT_no_warn_ifunc_textrel, false); Config->WarnSymbolOrdering = Args.hasFlag(OPT_warn_symbol_ordering, OPT_no_warn_symbol_ordering, true); + Config->WarnOnce = Args.hasArg(OPT_warn_once); Config->ZCombreloc = getZFlag(Args, "combreloc", "nocombreloc", true); Config->ZCopyreloc = getZFlag(Args, "copyreloc", "nocopyreloc", true); Config->ZExecstack = getZFlag(Args, "execstack", "noexecstack", false); Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -379,6 +379,9 @@ def warn_unresolved_symbols: F<"warn-unresolved-symbols">, HelpText<"Report unresolved symbols as warnings">; + +def warn_once: F<"warn-once">, + HelpText<"Warn only once for each undefined symbol">; defm whole_archive: B<"whole-archive", "Force load of all members in a static library", @@ -510,7 +513,6 @@ def: F<"sort-common">; def: F<"stats">; def: F<"warn-execstack">; -def: F<"warn-once">; def: F<"warn-shared-textrel">; def: F<"EB">; def: F<"EL">; Index: ELF/Relocations.cpp =================================================================== --- ELF/Relocations.cpp +++ ELF/Relocations.cpp @@ -654,6 +654,12 @@ if (Config->UnresolvedSymbols == UnresolvedPolicy::Ignore && CanBeExternal) return false; + if (Config->WarnOnce) { + if (Sym.Warned) + return false; + Sym.Warned = true; + } + std::string Msg = "undefined symbol: " + toString(Sym) + "\n>>> referenced by "; Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -183,7 +183,7 @@ Type(Type), StOther(StOther), SymbolKind(K), NeedsPltAddr(false), IsInIplt(false), IsInIgot(false), IsPreemptible(false), Used(!Config->GcSections), NeedsTocRestore(false), - ScriptDefined(false) {} + ScriptDefined(false), Warned(false) {} public: // True the symbol should point to its PLT entry. @@ -209,6 +209,10 @@ // True if this symbol is defined by a linker script. unsigned ScriptDefined : 1; + // True if this symbol is already reported as undefined. This + // bit is used for --warn-once option. + unsigned Warned : 1; + bool isSection() const { return Type == llvm::ELF::STT_SECTION; } bool isTls() const { return Type == llvm::ELF::STT_TLS; } bool isFunc() const { return Type == llvm::ELF::STT_FUNC; } Index: test/ELF/warn-once.s =================================================================== --- test/ELF/warn-once.s +++ test/ELF/warn-once.s @@ -0,0 +1,13 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o +# RUN: not ld.lld -warn-once %t.o -o %t.exe 2>&1 | FileCheck %s + +# CHECK-COUNT-1: error: undefined symbol: foo +# CHECK-COUNT-1: >>> referenced by warn-once.s + +.file "warn-once.s" + + .globl _start +_start: + call foo + call foo