[lld] 59d3fbc - [ELF] Suggest extern "C" when the definition is mangled while an undefined reference is not

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Fri Nov 8 09:49:36 PST 2019


Author: Fangrui Song
Date: 2019-11-08T09:46:45-08:00
New Revision: 59d3fbc227cca41e3e7b213ea744ca3a48d5244f

URL: https://github.com/llvm/llvm-project/commit/59d3fbc227cca41e3e7b213ea744ca3a48d5244f
DIFF: https://github.com/llvm/llvm-project/commit/59d3fbc227cca41e3e7b213ea744ca3a48d5244f.diff

LOG: [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

Added: 
    lld/test/ELF/undef-suggest-extern-c2.s

Modified: 
    lld/ELF/Relocations.cpp

Removed: 
    


################################################################################
diff  --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 54035d94926c..98abbe13fba5 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -697,11 +697,26 @@ struct UndefinedDiag {
 
 static std::vector<UndefinedDiag> undefs;
 
+// Check whether the definition name def is a mangled function name that matches
+// the reference name ref.
+static bool canSuggestExternCForCXX(StringRef ref, StringRef def) {
+  llvm::ItaniumPartialDemangler d;
+  if (d.partialDemangle(def.str().c_str()))
+    return false;
+  char *buf = d.getFunctionName(nullptr, nullptr);
+  if (!buf)
+    return false;
+  bool ret = ref == buf;
+  free(buf);
+  return ret;
+}
+
 // 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,
-                                            std::string &pre_hint) {
+                                            std::string &pre_hint,
+                                            std::string &post_hint) {
   // Build a map of local defined symbols.
   DenseMap<StringRef, const Symbol *> map;
   if (sym.file && !isa<SharedFile>(sym.file)) {
@@ -774,6 +789,23 @@ static const Symbol *getAlternativeSpelling(const Undefined &sym,
           return s;
         }
       }
+  } else {
+    const Symbol *s = nullptr;
+    for (auto &it : map)
+      if (canSuggestExternCForCXX(name, it.first)) {
+        s = it.second;
+        break;
+      }
+    if (!s)
+      symtab->forEachSymbol([&](Symbol *sym) {
+        if (!s && canSuggestExternCForCXX(name, sym->getName()))
+          s = sym;
+      });
+    if (s) {
+      pre_hint = " to declare ";
+      post_hint = " as extern \"C\"?";
+      return s;
+    }
   }
 
   return nullptr;
@@ -822,10 +854,10 @@ static void reportUndefinedSymbol(const UndefinedDiag &undef,
                .str();
 
   if (correctSpelling) {
-    std::string pre_hint = ": ";
+    std::string pre_hint = ": ", post_hint;
     if (const Symbol *corrected =
-            getAlternativeSpelling(cast<Undefined>(sym), pre_hint)) {
-      msg += "\n>>> did you mean" + pre_hint + toString(*corrected);
+            getAlternativeSpelling(cast<Undefined>(sym), pre_hint, post_hint)) {
+      msg += "\n>>> did you mean" + pre_hint + toString(*corrected) + post_hint;
       if (corrected->file)
         msg += "\n>>> defined in: " + toString(corrected->file);
     }

diff  --git a/lld/test/ELF/undef-suggest-extern-c2.s b/lld/test/ELF/undef-suggest-extern-c2.s
new file mode 100644
index 000000000000..f017e38c2499
--- /dev/null
+++ b/lld/test/ELF/undef-suggest-extern-c2.s
@@ -0,0 +1,21 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
+
+## The definition is mangled while the reference is not, suggest an arbitrary
+## C++ overload.
+# RUN: echo '.globl _Z3fooi; _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 that we can suggest a local definition.
+# RUN: echo '_Z3fooi: call foo' | llvm-mc -filetype=obj -triple=x86_64 - -o %t2.o
+# RUN: not ld.lld %t2.o -o /dev/null 2>&1 | FileCheck %s
+
+# CHECK:      error: undefined symbol: foo
+# CHECK-NEXT: >>> referenced by {{.*}}
+# CHECK-NEXT: >>> did you mean to declare foo(int) as extern "C"?
+
+## Don't suggest nested names whose base name is "foo", e.g. F::foo().
+# RUN: echo '.globl _ZN1F3fooEv; _ZN1F3fooEv:' | llvm-mc -filetype=obj -triple=x86_64 - -o %t3.o
+# RUN: not ld.lld %t.o %t3.o -o /dev/null 2>&1 | FileCheck /dev/null --implicit-check-not='did you mean'
+
+call foo


        


More information about the llvm-commits mailing list