Commit 70e62a4f authored by Fangrui Song's avatar Fangrui Song
Browse files

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

When missing an extern "C" declaration, an undefined reference may be
mangled while the definition is not. Suggest the missing
extern "C" and the base name.

Reviewed By: ruiu

Differential Revision: https://reviews.llvm.org/D69592
parent 9aff5e1c
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ add_lld_library(lldELF
  BitWriter
  Core
  DebugInfoDWARF
  Demangle
  LTO
  MC
  Object
+23 −4
Original line number Diff line number Diff line
@@ -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 <algorithm>
@@ -699,7 +700,8 @@ static std::vector<UndefinedDiag> undefs;
// 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 &pre_hint) {
  // Build a map of local defined symbols.
  DenseMap<StringRef, const Symbol *> map;
  if (sym.file && !isa<SharedFile>(sym.file)) {
@@ -759,6 +761,21 @@ static const Symbol *getAlternativeSpelling(const Undefined &sym) {
      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) {
          pre_hint = ": extern \"C\" ";
          return s;
        }
      }
  }

  return nullptr;
}

@@ -804,13 +821,15 @@ static void reportUndefinedSymbol(const UndefinedDiag &undef,
    msg += ("\n>>> referenced " + Twine(undef.locs.size() - i) + " more times")
               .str();

  if (correctSpelling)
  if (correctSpelling) {
    std::string pre_hint = ": ";
    if (const Symbol *corrected =
            getAlternativeSpelling(cast<Undefined>(sym))) {
      msg += "\n>>> did you mean: " + toString(*corrected);
            getAlternativeSpelling(cast<Undefined>(sym), pre_hint)) {
      msg += "\n>>> did you mean" + pre_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 "
+19 −0
Original line number Diff line number Diff line
# 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: