Index: lld/Common/ErrorHandler.cpp =================================================================== --- lld/Common/ErrorHandler.cpp +++ lld/Common/ErrorHandler.cpp @@ -120,21 +120,21 @@ *ErrorOS << Msg << "\n"; } -void ErrorHandler::error(const Twine &Msg) { +void ErrorHandler::error(const Twine &Msg, int NDiags) { std::lock_guard Lock(Mu); newline(ErrorOS, Msg); if (ErrorLimit == 0 || ErrorCount < ErrorLimit) { print("error: ", raw_ostream::RED); *ErrorOS << Msg << "\n"; - } else if (ErrorCount == ErrorLimit) { + } else if (ErrorCount >= ErrorLimit) { print("error: ", raw_ostream::RED); *ErrorOS << ErrorLimitExceededMsg << "\n"; if (ExitEarly) exitLld(1); } - ++ErrorCount; + ErrorCount += NDiags; } void ErrorHandler::fatal(const Twine &Msg) { Index: lld/ELF/Relocations.cpp =================================================================== --- lld/ELF/Relocations.cpp +++ lld/ELF/Relocations.cpp @@ -710,8 +710,11 @@ // can happen before lld emits diagnostics. struct UndefinedDiag { Symbol *Sym; - InputSectionBase *Sec; - uint64_t Offset; + struct Loc { + InputSectionBase *Sec; + uint64_t Offset; + }; + std::vector Locs; bool IsWarning; }; static std::vector& Undefs() { @@ -723,8 +726,6 @@ template static void reportUndefinedSymbol(const UndefinedDiag &Undef) { Symbol &Sym = *Undef.Sym; - InputSectionBase &Sec = *Undef.Sec; - uint64_t Offset = Undef.Offset; auto Visibility = [&]() -> std::string { switch (Sym.Visibility) { @@ -743,11 +744,19 @@ if (Msg.empty()) Msg = "undefined " + Visibility() + "symbol: " + toString(Sym); - Msg += "\n>>> referenced by "; - std::string Src = Sec.getSrcMsg(Sym, Offset); - if (!Src.empty()) - Msg += Src + "\n>>> "; - Msg += Sec.getObjMsg(Offset); + size_t i = 0; + for (; i < Undef.Locs.size() && errorCount() + i < errorHandler().ErrorLimit; + ++i) { + UndefinedDiag::Loc L = Undef.Locs[i]; + InputSectionBase &Sec = *L.Sec; + uint64_t Offset = L.Offset; + + Msg += "\n>>> referenced by "; + std::string Src = Sec.getSrcMsg(Sym, Offset); + if (!Src.empty()) + Msg += Src + "\n>>> "; + Msg += Sec.getObjMsg(Offset); + } if (Sym.getName().startswith("_ZTV")) Msg += "\nthe vtable symbol may be undefined because the class is missing " @@ -756,13 +765,29 @@ if (Undef.IsWarning) warn(Msg); else - error(Msg); + error(Msg, /*NDiags=*/i); } template void elf::reportUndefinedSymbols() { - for (const auto& Undef: Undefs()) - reportUndefinedSymbol(Undef); + // Find the first "undefined symbol" diagnostic for each diagnostic, and + // collect all "referenced from" lines at the first diagnostic. + std::map FirstRef; + for (size_t i = 0; i < Undefs().size(); ++i) { + auto& Undef = Undefs()[i]; + auto it = FirstRef.find(Undef.Sym); + if (it == FirstRef.end()) { + FirstRef[Undef.Sym] = i; + } else { + Undefs()[it->second].Locs.push_back(Undef.Locs[0]); + Undef.Locs.clear(); + } + } + + for (const auto& Undef : Undefs()) { + if (!Undef.Locs.empty()) + reportUndefinedSymbol(Undef); + } } // Report an undefined symbol if necessary. @@ -788,7 +813,7 @@ return false; - UndefinedDiag Undef = { &Sym, &Sec, Offset, /*IsWarning=*/false }; + UndefinedDiag Undef = { &Sym, {{&Sec, Offset}}, /*IsWarning=*/false }; if ((Config->UnresolvedSymbols == UnresolvedPolicy::Warn && CanBeExternal) || Config->NoinhibitExec) Undef.IsWarning = true; Index: lld/include/lld/Common/ErrorHandler.h =================================================================== --- lld/include/lld/Common/ErrorHandler.h +++ lld/include/lld/Common/ErrorHandler.h @@ -92,7 +92,7 @@ bool FatalWarnings = false; bool Verbose = false; - void error(const Twine &Msg); + void error(const Twine &Msg, int NDiags = 1); LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg); void log(const Twine &Msg); void message(const Twine &Msg); @@ -107,7 +107,9 @@ /// Returns the default error handler. ErrorHandler &errorHandler(); -inline void error(const Twine &Msg) { errorHandler().error(Msg); } +inline void error(const Twine &Msg, int NDiags = 1) { + errorHandler().error(Msg, NDiags); +} inline LLVM_ATTRIBUTE_NORETURN void fatal(const Twine &Msg) { errorHandler().fatal(Msg); } Index: lld/test/ELF/debug-line-obj.s =================================================================== --- lld/test/ELF/debug-line-obj.s +++ lld/test/ELF/debug-line-obj.s @@ -10,7 +10,6 @@ # CHECK: error: undefined symbol: foo() # CHECK-NEXT: >>> referenced by test.cpp:2 # CHECK-NEXT: >>> {{.*}}.o:(bar()) -# CHECK: error: undefined symbol: foo() # CHECK-NEXT: >>> referenced by test.cpp:3 # CHECK-NEXT: >>> {{.*}}.o:(baz()) Index: lld/test/ELF/undef-multi.s =================================================================== --- /dev/null +++ lld/test/ELF/undef-multi.s @@ -0,0 +1,47 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/undef.s -o %t2.o +# RUN: not ld.lld %t.o %t2.o -o %t.exe 2>&1 | FileCheck %s + +# CHECK: error: undefined symbol: zed2 +# CHECK-NEXT: >>> referenced by undef-multi.s +# CHECK-NEXT: >>> {{.*}}:(.text+0x1) +# CHECK-NEXT: >>> referenced by undef-multi.s +# CHECK-NEXT: >>> {{.*}}:(.text+0x6) +# CHECK-NEXT: >>> referenced by undef-multi.s +# CHECK-NEXT: >>> {{.*}}:(.text+0xB) +# CHECK-NEXT: >>> referenced by undef-multi.s +# CHECK-NEXT: >>> {{.*}}:(.text+0x10) +# CHECK-NEXT: >>> referenced by {{.*}}tmp2.o:(.text+0x0) + +# RUN: not ld.lld %t.o %t2.o -o %t.exe -error-limit=3 2>&1 | \ +# RUN: FileCheck --check-prefix=LIMIT %s + +# LIMIT: error: undefined symbol: zed2 +# LIMIT-NEXT: >>> referenced by undef-multi.s +# LIMIT-NEXT: >>> {{.*}}:(.text+0x1) +# LIMIT-NEXT: >>> referenced by undef-multi.s +# LIMIT-NEXT: >>> {{.*}}:(.text+0x6) +# LIMIT-NEXT: >>> referenced by undef-multi.s +# LIMIT-NEXT: >>> {{.*}}:(.text+0xB) +# LIMIT-NOT: >>> referenced by undef-multi.s +# LIMIT-NOT: >>> {{.*}}:(.text+0x10) +# LIMIT-NOT: >>> referenced by {{.*}}tmp2.o:(.text+0x0) + +.file "undef-multi.s" + + .globl _start +_start: + call zed2 + + .globl _f +_f: + call zed2 + + .globl _g +_g: + call zed2 + + .globl _h +_h: + call zed2