diff --git a/lld/ELF/CMakeLists.txt b/lld/ELF/CMakeLists.txt --- a/lld/ELF/CMakeLists.txt +++ b/lld/ELF/CMakeLists.txt @@ -52,6 +52,7 @@ BitWriter Core DebugInfoDWARF + Demangle LTO MC Object diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -53,6 +53,7 @@ #include "lld/Common/Memory.h" #include "lld/Common/Strings.h" #include "llvm/ADT/SmallSet.h" +#include "llvm/Demangle/Demangle.h" #include "llvm/Support/Endian.h" #include "llvm/Support/raw_ostream.h" #include @@ -699,7 +700,8 @@ // Suggest an alternative spelling of an "undefined symbol" diagnostic. Returns // the suggested symbol, which is either in the symbol table, or in the same // file of sym. -static const Symbol *getAlternativeSpelling(const Undefined &sym) { +static const Symbol *getAlternativeSpelling(const Undefined &sym, + std::string &hint) { // Build a map of local defined symbols. DenseMap map; if (sym.file && !isa(sym.file)) { @@ -759,6 +761,21 @@ return s; } + // The reference may be a mangled name while the definition is not. Suggest a + // missing extern "C". + if (name.startswith("_Z")) { + llvm::ItaniumPartialDemangler d; + if (!d.partialDemangle(name.str().c_str())) + if (char *buf = d.getFunctionName(nullptr, nullptr)) { + const Symbol *s = suggest(buf); + free(buf); + if (s) { + hint = "extern \"C\" "; + return s; + } + } + } + return nullptr; } @@ -804,13 +821,15 @@ msg += ("\n>>> referenced " + Twine(undef.locs.size() - i) + " more times") .str(); - if (correctSpelling) + if (correctSpelling) { + std::string hint; if (const Symbol *corrected = - getAlternativeSpelling(cast(sym))) { - msg += "\n>>> did you mean: " + toString(*corrected); + getAlternativeSpelling(cast(sym), hint)) { + msg += "\n>>> did you mean: " + hint + toString(*corrected); if (corrected->file) msg += "\n>>> defined in: " + toString(corrected->file); } + } if (sym.getName().startswith("_ZTV")) msg += "\nthe vtable symbol may be undefined because the class is missing " diff --git a/lld/test/ELF/undef-suggest-extern-c.s b/lld/test/ELF/undef-suggest-extern-c.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/undef-suggest-extern-c.s @@ -0,0 +1,19 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o + +## The reference is mangled while the definition is not, suggest a missing +## extern "C". +# RUN: echo 'call _Z3fooi' | llvm-mc -filetype=obj -triple=x86_64 - -o %t1.o +# RUN: not ld.lld %t.o %t1.o -o /dev/null 2>&1 | FileCheck %s + +# CHECK: error: undefined symbol: foo(int) +# CHECK-NEXT: >>> referenced by {{.*}} +# CHECK-NEXT: >>> did you mean: extern "C" foo + +## Don't suggest for nested names like F::foo() and foo::foo(). +# RUN: echo 'call _ZN1F3fooEv; call _ZN3fooC1Ev' | llvm-mc -filetype=obj -triple=x86_64 - -o %t2.o +# RUN: not ld.lld %t.o %t2.o -o /dev/null 2>&1 | FileCheck /dev/null --implicit-check-not='did you mean' + +.globl _start, foo +_start: +foo: