Index: lld/trunk/COFF/Chunks.cpp =================================================================== --- lld/trunk/COFF/Chunks.cpp +++ lld/trunk/COFF/Chunks.cpp @@ -336,8 +336,15 @@ File->getCOFFObj()->getSymbolName(COFFSym, Name); } - error("relocation against symbol in discarded section: " + Name + - getSymbolLocations(File, Rel.SymbolTableIndex)); + std::vector SymbolLocations = + getSymbolLocations(File, Rel.SymbolTableIndex); + + std::string Out; + llvm::raw_string_ostream OS(Out); + OS << "relocation against symbol in discarded section: " + Name; + for (const std::string &S : SymbolLocations) + OS << S; + error(OS.str()); } void SectionChunk::writeTo(uint8_t *Buf) const { Index: lld/trunk/COFF/SymbolTable.h =================================================================== --- lld/trunk/COFF/SymbolTable.h +++ lld/trunk/COFF/SymbolTable.h @@ -123,7 +123,7 @@ extern SymbolTable *Symtab; -std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex); +std::vector getSymbolLocations(ObjFile *File, uint32_t SymIndex); } // namespace coff } // namespace lld Index: lld/trunk/COFF/SymbolTable.cpp =================================================================== --- lld/trunk/COFF/SymbolTable.cpp +++ lld/trunk/COFF/SymbolTable.cpp @@ -79,7 +79,11 @@ return Candidate; } -std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex) { +// Given a file and the index of a symbol in that file, returns a description +// of all references to that symbol from that file. If no debug information is +// available, returns just the name of the file, else one string per actual +// reference as described in the debug info. +std::vector getSymbolLocations(ObjFile *File, uint32_t SymIndex) { struct Location { Symbol *Sym; std::pair FileLine; @@ -102,11 +106,12 @@ } if (Locations.empty()) - return "\n>>> referenced by " + toString(File); + return std::vector({"\n>>> referenced by " + toString(File)}); - std::string Out; - llvm::raw_string_ostream OS(Out); + std::vector SymbolLocations(Locations.size()); + size_t I = 0; for (Location Loc : Locations) { + llvm::raw_string_ostream OS(SymbolLocations[I++]); OS << "\n>>> referenced by "; if (!Loc.FileLine.first.empty()) OS << Loc.FileLine.first << ":" << Loc.FileLine.second @@ -115,7 +120,41 @@ if (Loc.Sym) OS << ":(" << toString(*Loc.Sym) << ')'; } - return OS.str(); + return SymbolLocations; +} + +// For an undefined symbol, stores all files referencing it and the index of +// the undefined symbol in each file. +struct UndefinedDiag { + Symbol *Sym; + struct File { + ObjFile *File; + uint64_t SymIndex; + }; + std::vector Files; +}; + +static void reportUndefinedSymbol(const UndefinedDiag &UndefDiag) { + std::string Out; + llvm::raw_string_ostream OS(Out); + OS << "undefined symbol: " << toString(*UndefDiag.Sym); + + const size_t MaxUndefReferences = 10; + size_t I = 0, NumRefs = 0; + for (const UndefinedDiag::File &Ref : UndefDiag.Files) { + std::vector SymbolLocations = + getSymbolLocations(Ref.File, Ref.SymIndex); + NumRefs += SymbolLocations.size(); + for (const std::string &S : SymbolLocations) { + if (I >= MaxUndefReferences) + break; + OS << S; + I++; + } + } + if (I < NumRefs) + OS << "\n>>> referenced " << NumRefs - I << " more times"; + errorOrWarn(OS.str()); } void SymbolTable::loadMinGWAutomaticImports() { @@ -263,15 +302,24 @@ " (defined in " + toString(Imp->getFile()) + ") [LNK4217]"); } + std::vector UndefDiags; + DenseMap FirstDiag; + for (ObjFile *File : ObjFile::Instances) { size_t SymIndex = (size_t)-1; for (Symbol *Sym : File->getSymbols()) { ++SymIndex; if (!Sym) continue; - if (Undefs.count(Sym)) - errorOrWarn("undefined symbol: " + toString(*Sym) + - getSymbolLocations(File, SymIndex)); + if (Undefs.count(Sym)) { + auto it = FirstDiag.find(Sym); + if (it == FirstDiag.end()) { + FirstDiag[Sym] = UndefDiags.size(); + UndefDiags.push_back({Sym, {{File, SymIndex}}}); + } else { + UndefDiags[it->second].Files.push_back({File, SymIndex}); + } + } if (Config->WarnLocallyDefinedImported) if (Symbol *Imp = LocalImports.lookup(Sym)) warn(toString(File) + @@ -279,6 +327,9 @@ " (defined in " + toString(Imp->getFile()) + ") [LNK4217]"); } } + + for (const UndefinedDiag& UndefDiag : UndefDiags) + reportUndefinedSymbol(UndefDiag); } std::pair SymbolTable::insert(StringRef Name) { Index: lld/trunk/test/COFF/undefined-symbol-multi.s =================================================================== --- lld/trunk/test/COFF/undefined-symbol-multi.s +++ lld/trunk/test/COFF/undefined-symbol-multi.s @@ -0,0 +1,47 @@ +# REQUIRES: x86 +# RUN: llvm-mc -triple=x86_64-windows-msvc -filetype=obj -o %t.obj %s + +# All references to a single undefined symbol count as a single error -- but +# at most 10 references are printed. +# RUN: echo ".globl bar" > %t.moreref.s +# RUN: echo "bar:" >> %t.moreref.s +# RUN: echo ' call "?foo@@YAHXZ"' >> %t.moreref.s +# RUN: echo ' call "?foo@@YAHXZ"' >> %t.moreref.s +# RUN: echo ' call "?foo@@YAHXZ"' >> %t.moreref.s +# RUN: echo ' call "?foo@@YAHXZ"' >> %t.moreref.s +# RUN: echo ' call "?foo@@YAHXZ"' >> %t.moreref.s +# RUN: echo ' call "?foo@@YAHXZ"' >> %t.moreref.s +# RUN: echo ' call "?foo@@YAHXZ"' >> %t.moreref.s +# RUN: echo ' call "?foo@@YAHXZ"' >> %t.moreref.s +# RUN: echo ' call "?foo@@YAHXZ"' >> %t.moreref.s +# RUN: echo ' call "?foo@@YAHXZ"' >> %t.moreref.s +# RUN: llvm-mc -triple=x86_64-windows-msvc -filetype=obj -o %t2.obj %t.moreref.s +# RUN: not lld-link /out:/dev/null %t.obj %t2.obj 2>&1 | FileCheck %s + +# CHECK: error: undefined symbol: int __cdecl foo(void) +# CHECK-NEXT: >>> referenced by {{.*}}tmp.obj:(main) +# CHECK-NEXT: >>> referenced by {{.*}}tmp.obj:(main) +# CHECK-NEXT: >>> referenced by {{.*}}tmp2.obj:(bar) +# CHECK-NEXT: >>> referenced by {{.*}}tmp2.obj:(bar) +# CHECK-NEXT: >>> referenced by {{.*}}tmp2.obj:(bar) +# CHECK-NEXT: >>> referenced by {{.*}}tmp2.obj:(bar) +# CHECK-NEXT: >>> referenced by {{.*}}tmp2.obj:(bar) +# CHECK-NEXT: >>> referenced by {{.*}}tmp2.obj:(bar) +# CHECK-NEXT: >>> referenced by {{.*}}tmp2.obj:(bar) +# CHECK-NEXT: >>> referenced by {{.*}}tmp2.obj:(bar) +# CHECK-NEXT: >>> referenced 2 more times +# CHECK-EMPTY: +# CHECK-NEXT: error: undefined symbol: int __cdecl bar(void) +# CHECK-NEXT: >>> referenced by {{.*}}.obj:(main) +# CHECK-NEXT: >>> referenced by {{.*}}.obj:(f1) + + .section .text,"xr",one_only,main +.globl main +main: + call "?foo@@YAHXZ" + call "?foo@@YAHXZ" + call "?bar@@YAHXZ" + +f1: + call "?bar@@YAHXZ" +.Lfunc_end1: