Index: lld/MachO/SymbolTable.h =================================================================== --- lld/MachO/SymbolTable.h +++ lld/MachO/SymbolTable.h @@ -69,6 +69,9 @@ std::vector symVector; }; +void reportPendingUndefinedSymbols(); + +// Call reportPendingUndefinedSymbols() to emit diagnostics. void treatUndefinedSymbol(const Undefined &, StringRef source); void treatUndefinedSymbol(const Undefined &, const InputSection *, uint64_t offset); Index: lld/MachO/SymbolTable.cpp =================================================================== --- lld/MachO/SymbolTable.cpp +++ lld/MachO/SymbolTable.cpp @@ -345,35 +345,79 @@ return false; } -static void printUndefinedDiagnostic(StringRef name, StringRef source) { - std::string message = "undefined symbol"; - if (config->archMultiple) - message += (" for arch " + getArchitectureName(config->arch())).str(); - message += (": " + name + "\n>>> referenced by " + source).str(); - - if (config->undefinedSymbolTreatment == UndefinedSymbolTreatment::error) - error(message); - else if (config->undefinedSymbolTreatment == - UndefinedSymbolTreatment::warning) - warn(message); - else - assert(false && "diagnostics make sense for -undefined error|warning only"); +struct UndefinedDiag { + struct SectionAndOffset { + const InputSection *isec; + uint64_t offset; + }; + + std::vector namedSources; + std::vector relocLocs; +}; + +static MapVector undefs; + +void macho::reportPendingUndefinedSymbols() { + for (const auto &undef : undefs) { + const UndefinedDiag &locations = undef.second; + + std::string message = "undefined symbol"; + if (config->archMultiple) + message += (" for arch " + getArchitectureName(config->arch())).str(); + message += ": " + toString(*undef.first); + + const size_t maxUndefinedReferences = 3; + size_t i = 0; + for (const std::string &source : locations.namedSources) { + if (i >= maxUndefinedReferences) + break; + message += "\n>>> referenced by " + source; + ++i; + } + + for (const UndefinedDiag::SectionAndOffset &loc : locations.relocLocs) { + if (i >= maxUndefinedReferences) + break; + // TODO: Get source file/line from debug information. + message += "\n>>> referenced by " + loc.isec->getLocation(loc.offset); + ++i; + } + + size_t totalReferences = + locations.namedSources.size() + locations.relocLocs.size(); + if (totalReferences > i) + message += + ("\n>>> referenced " + Twine(totalReferences - i) + " more times") + .str(); + + if (config->undefinedSymbolTreatment == UndefinedSymbolTreatment::error) + error(message); + else if (config->undefinedSymbolTreatment == + UndefinedSymbolTreatment::warning) + warn(message); + else + assert(false && + "diagnostics make sense for -undefined error|warning only"); + } + + // This function is called multiple times during execution. Clear the printed + // diagnostics to avoid printing the same things again the next time. + undefs.clear(); } -void lld::macho::treatUndefinedSymbol(const Undefined &sym, StringRef source) { +void macho::treatUndefinedSymbol(const Undefined &sym, StringRef source) { if (recoverFromUndefinedSymbol(sym)) return; - printUndefinedDiagnostic(sym.getName(), source); + + undefs[&sym].namedSources.push_back(source.str()); } -void lld::macho::treatUndefinedSymbol(const Undefined &sym, - const InputSection *isec, - uint64_t offset) { +void macho::treatUndefinedSymbol(const Undefined &sym, const InputSection *isec, + uint64_t offset) { if (recoverFromUndefinedSymbol(sym)) return; - // TODO: Get source file/line from debug information. - printUndefinedDiagnostic(toString(sym), isec->getLocation(offset)); + undefs[&sym].relocLocs.push_back({isec, offset}); } std::unique_ptr macho::symtab; Index: lld/MachO/Writer.cpp =================================================================== --- lld/MachO/Writer.cpp +++ lld/MachO/Writer.cpp @@ -1133,6 +1133,7 @@ void Writer::writeOutputFile() { TimeTraceScope timeScope("Write output file"); openFile(); + reportPendingUndefinedSymbols(); if (errorCount()) return; writeSections(); @@ -1155,6 +1156,7 @@ scanRelocations(); // Do not proceed if there was an undefined symbol. + reportPendingUndefinedSymbols(); if (errorCount()) return; Index: lld/test/MachO/invalid/undef-multi.s =================================================================== --- /dev/null +++ lld/test/MachO/invalid/undef-multi.s @@ -0,0 +1,28 @@ +# REQUIRES: aarch64 + +# RUN: llvm-mc -filetype=obj -triple=arm64-apple-darwin %s -o %t.o +# RUN: not %lld -arch arm64 %t.o -o /dev/null 2>&1 | FileCheck -DFILE=%t.o %s + +# CHECK: error: undefined symbol: _undef +# CHECK-NEXT: >>> referenced by [[FILE]]:(symbol _main+0x0) +# CHECK-NEXT: >>> referenced by [[FILE]]:(symbol _foo+0x0) +# CHECK-NEXT: >>> referenced by [[FILE]]:(symbol _bar+0x0) +# CHECK-NEXT: >>> referenced 1 more times + +.globl _main +_main: + b _undef + +.globl _foo +_foo: + b _undef + +.global _bar +_bar: + b _undef + +.globl _baz +_baz: + b _undef + +.subsections_via_symbols