[lld] f2036a1 - [ELF] Print symbols with non-default versions for better "undefined symbol" diagnostics

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 1 08:08:34 PDT 2020


Author: Fangrui Song
Date: 2020-04-01T08:04:36-07:00
New Revision: f2036a15d3714fc9cfc8935634814d1c4e4263fa

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

LOG: [ELF] Print symbols with non-default versions for better "undefined symbol" diagnostics

When reporting an "undefined symbol" diagnostic:

* We don't print @ for the reference.
* We don't print @ or @@ for the definition. https://bugs.llvm.org/show_bug.cgi?id=45318

This can lead to confusing diagnostics:

```
// foo may be foo at v2
ld.lld: error: undefined symbol: foo
>>> referenced by t1.o:(.text+0x1)
// foo may be foo at v1 or foo@@v1
>>> did you mean: foo
>>> defined in: t.so
```

There are 2 ways a symbol in symtab may get truncated:

* A @@ definition may be truncated *early* by SymbolTable::insert().
  The name ends with a '\0'.
* A @ definition/reference may be truncated *later* by Symbol::parseSymbolVersion().
  The name ends with a '@'.

This patch detects the second case and improves the diagnostics. The first case is
not improved but the second case is sufficient to make diagnostics not confusing.

Reviewed By: ruiu

Differential Revision: https://reviews.llvm.org/D76999

Added: 
    lld/test/ELF/undef-suggest-version.s

Modified: 
    lld/ELF/Symbols.cpp
    lld/ELF/Symbols.h

Removed: 
    


################################################################################
diff  --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp
index cb7ee8829e53..42c8a71c4185 100644
--- a/lld/ELF/Symbols.cpp
+++ b/lld/ELF/Symbols.cpp
@@ -31,7 +31,18 @@ static std::string demangle(StringRef symName) {
   return std::string(symName);
 }
 
-std::string toString(const elf::Symbol &b) { return demangle(b.getName()); }
+std::string toString(const elf::Symbol &sym) {
+  StringRef name = sym.getName();
+  std::string ret = demangle(name);
+
+  // If sym has a non-default version, its name may have been truncated at '@'
+  // by Symbol::parseSymbolVersion(). Add the trailing part. This check is safe
+  // because every symbol name ends with '\0'.
+  if (name.data()[name.size()] == '@')
+    ret += name.data() + name.size();
+  return ret;
+}
+
 std::string toELFString(const Archive::Symbol &b) {
   return demangle(b.getName());
 }

diff  --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h
index ac606198afd8..ebee4af1fdad 100644
--- a/lld/ELF/Symbols.h
+++ b/lld/ELF/Symbols.h
@@ -21,6 +21,7 @@
 #include "llvm/Object/ELF.h"
 
 namespace lld {
+// Returns a string representation for a symbol for diagnostics.
 std::string toString(const elf::Symbol &);
 
 // There are two 
diff erent ways to convert an Archive::Symbol to a string:

diff  --git a/lld/test/ELF/undef-suggest-version.s b/lld/test/ELF/undef-suggest-version.s
new file mode 100644
index 000000000000..790b9fcab788
--- /dev/null
+++ b/lld/test/ELF/undef-suggest-version.s
@@ -0,0 +1,57 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
+# RUN: echo 'v1 {bar;};' > %t.ver
+# RUN: ld.lld -shared --version-script %t.ver %t.o -o %t.so
+
+## For an unversioned undefined symbol, check we can suggest the symbol with the
+## default version.
+# RUN: echo 'call bat' | llvm-mc -filetype=obj -triple=x86_64 - -o %tdef1.o
+# RUN: not ld.lld %t.so %tdef1.o -o /dev/null 2>&1 | FileCheck --check-prefix=DEFAULT1 %s
+
+# DEFAULT1:      error: undefined symbol: bat
+# DEFAULT1-NEXT: >>> referenced by {{.*}}.o:(.text+0x1)
+# DEFAULT1-NEXT: >>> did you mean: bar{{$}}
+# DEFAULT1-NEXT: >>> defined in: {{.*}}.so
+
+## For a versioned undefined symbol, check we can suggest the symbol with the
+## default version.
+# RUN: echo '.symver bar.v2,bar at v2; call bar.v2' | llvm-mc -filetype=obj -triple=x86_64 - -o %tdef2.o
+# RUN: not ld.lld %t.so %tdef2.o -o /dev/null 2>&1 | FileCheck --check-prefix=DEFAULT2 %s
+
+# DEFAULT2:      error: undefined symbol: bar at v2
+# DEFAULT2-NEXT: >>> referenced by {{.*}}.o:(.text+0x1)
+# DEFAULT2-NEXT: >>> did you mean: bar{{$}}
+# DEFAULT2-NEXT: >>> defined in: {{.*}}.so
+
+## For an unversioned undefined symbol, check we can suggest a symbol with
+## a non-default version.
+# RUN: echo 'call foo; call _Z3fooi' | llvm-mc -filetype=obj -triple=x86_64 - -o %thidden1.o
+# RUN: not ld.lld %t.so %thidden1.o -o /dev/null 2>&1 | FileCheck --check-prefix=HIDDEN1 %s
+
+# HIDDEN1:      error: undefined symbol: foo
+# HIDDEN1-NEXT: >>> referenced by {{.*}}.o:(.text+0x1)
+# HIDDEN1-NEXT: >>> did you mean: foo at v1
+# HIDDEN1-NEXT: >>> defined in: {{.*}}.so
+# HIDDEN1-EMPTY:
+# HIDDEN1-NEXT: error: undefined symbol: foo(int)
+# HIDDEN1-NEXT: >>> referenced by {{.*}}.o:(.text+0x6)
+# HIDDEN1-NEXT: >>> did you mean: foo(int)@v1
+# HIDDEN1-NEXT: >>> defined in: {{.*}}.so
+
+## For a versioned undefined symbol, check we can suggest a symbol with
+## a 
diff erent version.
+# RUN: echo '.symver foo.v2,foo at v2; call foo.v2' | llvm-mc -filetype=obj -triple=x86_64 - -o %thidden2.o
+# RUN: not ld.lld %t.so %thidden2.o -o /dev/null 2>&1 | FileCheck --check-prefix=HIDDEN2 %s
+
+# HIDDEN2:      error: undefined symbol: foo at v2
+# HIDDEN2-NEXT: >>> referenced by {{.*}}.o:(.text+0x1)
+# HIDDEN2-NEXT: >>> did you mean: foo at v1
+# HIDDEN2-NEXT: >>> defined in: {{.*}}.so
+
+## %t.so exports bar@@v1 and two VERSYM_HIDDEN symbols: foo at v1 and _Z3fooi at v1.
+.globl foo.v1, _Z3fooi.v1, bar
+.symver foo.v1,foo at v1
+.symver _Z3fooi.v1,_Z3fooi at v1
+foo.v1:
+_Z3fooi.v1:
+bar:


        


More information about the llvm-commits mailing list