Skip to content

Commit

Permalink
[lld-macho] Print the name of functions containing undefined references
Browse files Browse the repository at this point in the history
The error used to look like this:

  ld64.lld: error: undefined symbol: _foo
  >>> referenced by /path/to/bar.o

Now it displays the name of the function that contains the undefined
reference as well:

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

Differential Revision: https://reviews.llvm.org/D127696
  • Loading branch information
BertalanD authored and nico committed Jun 14, 2022
1 parent cbcce82 commit f2e92cf
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 40 deletions.
94 changes: 61 additions & 33 deletions lld/MachO/SymbolTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,50 +302,78 @@ static void handleSegmentBoundarySymbol(const Undefined &sym, StringRef segName,
seg->segmentEndSymbols.push_back(createBoundarySymbol(sym));
}

void lld::macho::treatUndefinedSymbol(const Undefined &sym, StringRef source) {
// Try to find a definition for an undefined symbol.
// Returns true if a definition was found and no diagnostics are needed.
static bool recoverFromUndefinedSymbol(const Undefined &sym) {
// Handle start/end symbols.
StringRef name = sym.getName();
if (name.consume_front("section$start$"))
return handleSectionBoundarySymbol(sym, name, Boundary::Start);
if (name.consume_front("section$end$"))
return handleSectionBoundarySymbol(sym, name, Boundary::End);
if (name.consume_front("segment$start$"))
return handleSegmentBoundarySymbol(sym, name, Boundary::Start);
if (name.consume_front("segment$end$"))
return handleSegmentBoundarySymbol(sym, name, Boundary::End);
if (name.consume_front("section$start$")) {
handleSectionBoundarySymbol(sym, name, Boundary::Start);
return true;
}
if (name.consume_front("section$end$")) {
handleSectionBoundarySymbol(sym, name, Boundary::End);
return true;
}
if (name.consume_front("segment$start$")) {
handleSegmentBoundarySymbol(sym, name, Boundary::Start);
return true;
}
if (name.consume_front("segment$end$")) {
handleSegmentBoundarySymbol(sym, name, Boundary::End);
return true;
}

// Handle -U.
if (config->explicitDynamicLookups.count(sym.getName())) {
symtab->addDynamicLookup(sym.getName());
return;
return true;
}

// Handle -undefined.
auto message = [source, &sym]() {
std::string message = "undefined symbol";
if (config->archMultiple)
message += (" for arch " + getArchitectureName(config->arch())).str();
message += ": " + toString(sym);
if (!source.empty())
message += "\n>>> referenced by " + source.str();
else
message += "\n>>> referenced by " + toString(sym.getFile());
return message;
};
switch (config->undefinedSymbolTreatment) {
case UndefinedSymbolTreatment::error:
error(message());
break;
case UndefinedSymbolTreatment::warning:
warn(message());
LLVM_FALLTHROUGH;
case UndefinedSymbolTreatment::dynamic_lookup:
case UndefinedSymbolTreatment::suppress:
if (config->undefinedSymbolTreatment ==
UndefinedSymbolTreatment::dynamic_lookup ||
config->undefinedSymbolTreatment == UndefinedSymbolTreatment::suppress) {
symtab->addDynamicLookup(sym.getName());
break;
case UndefinedSymbolTreatment::unknown:
llvm_unreachable("unknown -undefined TREATMENT");
return true;
}

// We do not return true here, as we still need to print diagnostics.
if (config->undefinedSymbolTreatment == UndefinedSymbolTreatment::warning)
symtab->addDynamicLookup(sym.getName());

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");
}

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) {
if (recoverFromUndefinedSymbol(sym))
return;

// TODO: Get source file/line from debug information.
printUndefinedDiagnostic(toString(sym), isec->getLocation(offset));
}

std::unique_ptr<SymbolTable> macho::symtab;
4 changes: 3 additions & 1 deletion lld/MachO/SymbolTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ class SymbolTable {
std::vector<Symbol *> symVector;
};

void treatUndefinedSymbol(const Undefined &, StringRef source = "");
void treatUndefinedSymbol(const Undefined &, StringRef source);
void treatUndefinedSymbol(const Undefined &, const InputSection *,
uint64_t offset);

extern std::unique_ptr<SymbolTable> symtab;

Expand Down
2 changes: 1 addition & 1 deletion lld/MachO/UnwindInfoSection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ void UnwindInfoSectionImpl::prepareRelocations(ConcatInputSection *isec) {
r.referent = s = sym;
}
if (auto *undefined = dyn_cast<Undefined>(s)) {
treatUndefinedSymbol(*undefined);
treatUndefinedSymbol(*undefined, isec, r.offset);
// treatUndefinedSymbol() can replace s with a DylibSymbol; re-check.
if (isa<Undefined>(s))
continue;
Expand Down
2 changes: 1 addition & 1 deletion lld/MachO/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -659,7 +659,7 @@ void Writer::scanRelocations() {
}
if (auto *sym = r.referent.dyn_cast<Symbol *>()) {
if (auto *undefined = dyn_cast<Undefined>(sym))
treatUndefinedSymbol(*undefined);
treatUndefinedSymbol(*undefined, isec, r.offset);
// treatUndefinedSymbol() can replace sym with a DylibSymbol; re-check.
if (!isa<Undefined>(sym) && validateSymbolRelocation(sym, isec, r))
prepareSymbolRelocation(sym, isec, r);
Expand Down
8 changes: 4 additions & 4 deletions lld/test/MachO/invalid/undefined-symbol.s
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/foo.s -o %t/foo.o
# RUN: llvm-ar crs %t/foo.a %t/foo.o
# RUN: not %lld --icf=all -o /dev/null %t/main.o 2>&1 | \
# RUN: FileCheck %s -DSYM=_foo -DFILENAME=%t/main.o
# RUN: FileCheck %s -DSYM=_foo -DLOC='%t/main.o:(symbol _main+0x1)'
# RUN: not %lld -o /dev/null %t/main.o %t/foo.a 2>&1 | \
# RUN: FileCheck %s -DSYM=_bar -DFILENAME='%t/foo.a(foo.o)'
# RUN: FileCheck %s -DSYM=_bar -DLOC='%t/foo.a(foo.o):(symbol _foo+0x1)'
# RUN: not %lld -o /dev/null %t/main.o -force_load %t/foo.a 2>&1 | \
# RUN: FileCheck %s -DSYM=_bar -DFILENAME='%t/foo.a(foo.o)'
# RUN: FileCheck %s -DSYM=_bar -DLOC='%t/foo.a(foo.o):(symbol _foo+0x1)'
# CHECK: error: undefined symbol: [[SYM]]
# CHECK-NEXT: >>> referenced by [[FILENAME]]
# CHECK-NEXT: >>> referenced by [[LOC]]

#--- foo.s
.globl _foo
Expand Down

0 comments on commit f2e92cf

Please sign in to comment.