Skip to content

Commit 59d3fbc

Browse files
committed
[ELF] Suggest extern "C" when the definition is mangled while an undefined reference is not
The definition may be mangled while an undefined reference is not. This may come up when (1) the reference is from a C file or (2) the definition misses an extern "C". (2) is more common. Suggest an arbitrary mangled name that matches the undefined reference, if such a definition exists. ld.lld: error: undefined symbol: foo >>> referenced by a.o:(.text+0x1) >>> did you mean to declare foo(int) as extern "C"? >>> defined in: a1.o Reviewed By: dblaikie, ruiu Differential Revision: https://reviews.llvm.org/D69650
1 parent 70e62a4 commit 59d3fbc

File tree

2 files changed

+57
-4
lines changed

2 files changed

+57
-4
lines changed

lld/ELF/Relocations.cpp

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -697,11 +697,26 @@ struct UndefinedDiag {
697697

698698
static std::vector<UndefinedDiag> undefs;
699699

700+
// Check whether the definition name def is a mangled function name that matches
701+
// the reference name ref.
702+
static bool canSuggestExternCForCXX(StringRef ref, StringRef def) {
703+
llvm::ItaniumPartialDemangler d;
704+
if (d.partialDemangle(def.str().c_str()))
705+
return false;
706+
char *buf = d.getFunctionName(nullptr, nullptr);
707+
if (!buf)
708+
return false;
709+
bool ret = ref == buf;
710+
free(buf);
711+
return ret;
712+
}
713+
700714
// Suggest an alternative spelling of an "undefined symbol" diagnostic. Returns
701715
// the suggested symbol, which is either in the symbol table, or in the same
702716
// file of sym.
703717
static const Symbol *getAlternativeSpelling(const Undefined &sym,
704-
std::string &pre_hint) {
718+
std::string &pre_hint,
719+
std::string &post_hint) {
705720
// Build a map of local defined symbols.
706721
DenseMap<StringRef, const Symbol *> map;
707722
if (sym.file && !isa<SharedFile>(sym.file)) {
@@ -774,6 +789,23 @@ static const Symbol *getAlternativeSpelling(const Undefined &sym,
774789
return s;
775790
}
776791
}
792+
} else {
793+
const Symbol *s = nullptr;
794+
for (auto &it : map)
795+
if (canSuggestExternCForCXX(name, it.first)) {
796+
s = it.second;
797+
break;
798+
}
799+
if (!s)
800+
symtab->forEachSymbol([&](Symbol *sym) {
801+
if (!s && canSuggestExternCForCXX(name, sym->getName()))
802+
s = sym;
803+
});
804+
if (s) {
805+
pre_hint = " to declare ";
806+
post_hint = " as extern \"C\"?";
807+
return s;
808+
}
777809
}
778810

779811
return nullptr;
@@ -822,10 +854,10 @@ static void reportUndefinedSymbol(const UndefinedDiag &undef,
822854
.str();
823855

824856
if (correctSpelling) {
825-
std::string pre_hint = ": ";
857+
std::string pre_hint = ": ", post_hint;
826858
if (const Symbol *corrected =
827-
getAlternativeSpelling(cast<Undefined>(sym), pre_hint)) {
828-
msg += "\n>>> did you mean" + pre_hint + toString(*corrected);
859+
getAlternativeSpelling(cast<Undefined>(sym), pre_hint, post_hint)) {
860+
msg += "\n>>> did you mean" + pre_hint + toString(*corrected) + post_hint;
829861
if (corrected->file)
830862
msg += "\n>>> defined in: " + toString(corrected->file);
831863
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# REQUIRES: x86
2+
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
3+
4+
## The definition is mangled while the reference is not, suggest an arbitrary
5+
## C++ overload.
6+
# RUN: echo '.globl _Z3fooi; _Z3fooi:' | llvm-mc -filetype=obj -triple=x86_64 - -o %t1.o
7+
# RUN: not ld.lld %t.o %t1.o -o /dev/null 2>&1 | FileCheck %s
8+
9+
## Check that we can suggest a local definition.
10+
# RUN: echo '_Z3fooi: call foo' | llvm-mc -filetype=obj -triple=x86_64 - -o %t2.o
11+
# RUN: not ld.lld %t2.o -o /dev/null 2>&1 | FileCheck %s
12+
13+
# CHECK: error: undefined symbol: foo
14+
# CHECK-NEXT: >>> referenced by {{.*}}
15+
# CHECK-NEXT: >>> did you mean to declare foo(int) as extern "C"?
16+
17+
## Don't suggest nested names whose base name is "foo", e.g. F::foo().
18+
# RUN: echo '.globl _ZN1F3fooEv; _ZN1F3fooEv:' | llvm-mc -filetype=obj -triple=x86_64 - -o %t3.o
19+
# RUN: not ld.lld %t.o %t3.o -o /dev/null 2>&1 | FileCheck /dev/null --implicit-check-not='did you mean'
20+
21+
call foo

0 commit comments

Comments
 (0)