[lld-macho] Group undefined symbol diagnostics by symbol

ld64.lld used to print the "undefined symbol" line for each reference to
an undefined symbol previously:

  ld64.lld: error: undefined symbol: _foo
  >>> referenced by /path/to/bar.o:(symbol _baz+0x0)

  ld64.lld: error: undefined symbol: _foo
  >>> referenced by /path/to/bar.o:(symbol _quux+0x1)

Now they are deduplicated:

  ld64.lld: error: undefined symbol: _foo
  >>> referenced by /path/to/bar.o:(symbol _baz+0x0)
  >>> referenced by /path/to/bar.o:(symbol _quux+0x1)

As with the other lld ports, only the first 3 references are printed.

Differential Revision: https://reviews.llvm.org/D127753
This commit is contained in:
Daniel Bertalan 2022-06-14 16:35:09 -04:00 committed by Nico Weber
parent 7c0089d735
commit d61341768c
4 changed files with 101 additions and 23 deletions

View file

@ -345,35 +345,80 @@ static bool recoverFromUndefinedSymbol(const Undefined &sym) {
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();
struct UndefinedDiag {
struct SectionAndOffset {
const InputSection *isec;
uint64_t offset;
};
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");
std::vector<SectionAndOffset> codeReferences;
std::vector<std::string> otherReferences;
};
static MapVector<const Undefined *, UndefinedDiag> 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) {
if (recoverFromUndefinedSymbol(sym))
return;
printUndefinedDiagnostic(sym.getName(), source);
}
void lld::macho::treatUndefinedSymbol(const Undefined &sym,
const InputSection *isec,
uint64_t offset) {
void macho::treatUndefinedSymbol(const Undefined &sym, StringRef source) {
if (recoverFromUndefinedSymbol(sym))
return;
// TODO: Get source file/line from debug information.
printUndefinedDiagnostic(toString(sym), isec->getLocation(offset));
undefs[&sym].otherReferences.push_back(source.str());
}
void macho::treatUndefinedSymbol(const Undefined &sym, const InputSection *isec,
uint64_t offset) {
if (recoverFromUndefinedSymbol(sym))
return;
undefs[&sym].codeReferences.push_back({isec, offset});
}
std::unique_ptr<SymbolTable> macho::symtab;

View file

@ -69,6 +69,9 @@ private:
std::vector<Symbol *> symVector;
};
void reportPendingUndefinedSymbols();
// Call reportPendingUndefinedSymbols() to emit diagnostics.
void treatUndefinedSymbol(const Undefined &, StringRef source);
void treatUndefinedSymbol(const Undefined &, const InputSection *,
uint64_t offset);

View file

@ -1133,6 +1133,7 @@ void Writer::writeCodeSignature() {
void Writer::writeOutputFile() {
TimeTraceScope timeScope("Write output file");
openFile();
reportPendingUndefinedSymbols();
if (errorCount())
return;
writeSections();
@ -1155,6 +1156,7 @@ template <class LP> void Writer::run() {
scanRelocations();
// Do not proceed if there was an undefined symbol.
reportPendingUndefinedSymbols();
if (errorCount())
return;

View file

@ -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