diff --git a/lld/MachO/SymbolTable.h b/lld/MachO/SymbolTable.h --- a/lld/MachO/SymbolTable.h +++ b/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); diff --git a/lld/MachO/SymbolTable.cpp b/lld/MachO/SymbolTable.cpp --- a/lld/MachO/SymbolTable.cpp +++ b/lld/MachO/SymbolTable.cpp @@ -345,35 +345,80 @@ 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 codeReferences; + std::vector otherReferences; +}; + +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 &loc : locations.otherReferences) { + if (i >= maxUndefinedReferences) + break; + message += "\n>>> referenced by " + loc; + ++i; + } + + for (const UndefinedDiag::SectionAndOffset &loc : + locations.codeReferences) { + 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.otherReferences.size() + locations.codeReferences.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].otherReferences.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].codeReferences.push_back({isec, offset}); } std::unique_ptr macho::symtab; diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp --- a/lld/MachO/Writer.cpp +++ b/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; diff --git a/lld/test/MachO/invalid/undef-multi.s b/lld/test/MachO/invalid/undef-multi.s new file mode 100644 --- /dev/null +++ b/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