[lld] c1c679e - [ELF] Make --version-script/--dynamic-list work for lazy symbols fetched by LTO libcalls

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Mon Apr 6 09:47:15 PDT 2020


Author: Fangrui Song
Date: 2020-04-06T09:47:06-07:00
New Revision: c1c679e2d2d31cb33007b162f92e117d3a4507f4

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

LOG: [ELF] Make --version-script/--dynamic-list work for lazy symbols fetched by LTO libcalls

Fixes https://bugs.llvm.org/show_bug.cgi?id=45391

The LTO code generator happens after version script scanning and may
create references which will fetch some lazy symbols.

Currently a version script does not assign VER_NDX_LOCAL to lazy symbols
and such symbols will be made global after they are fetched.

Change findByVersion and findAllByVersion to work on lazy symbols.
For unfetched lazy symbols, we should keep them non-local (D35263).
Check isDefined() in computeBinding() as a compensation.

This patch fixes a companion bug that --dynamic-list does not export
libcall fetched symbols.

Reviewed By: grimar

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

Added: 
    lld/test/ELF/lto/version-libcall.ll

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

Removed: 
    


################################################################################
diff  --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp
index 1f5d5981928c..60be6043b6c0 100644
--- a/lld/ELF/SymbolTable.cpp
+++ b/lld/ELF/SymbolTable.cpp
@@ -103,6 +103,13 @@ Symbol *SymbolTable::find(StringRef name) {
   return sym;
 }
 
+// A version script/dynamic list is only meaningful for a Defined symbol.
+// A CommonSymbol will be converted to a Defined in replaceCommonSymbols().
+// A lazy symbol may be made Defined if an LTO libcall fetches it.
+static bool canBeVersioned(const Symbol &sym) {
+  return sym.isDefined() || sym.isCommon() || sym.isLazy();
+}
+
 // Initialize demangledSyms with a map from demangled symbols to symbol
 // objects. Used to handle "extern C++" directive in version scripts.
 //
@@ -119,11 +126,9 @@ Symbol *SymbolTable::find(StringRef name) {
 StringMap<std::vector<Symbol *>> &SymbolTable::getDemangledSyms() {
   if (!demangledSyms) {
     demangledSyms.emplace();
-    for (Symbol *sym : symVector) {
-      if (!sym->isDefined() && !sym->isCommon())
-        continue;
-      (*demangledSyms)[demangleItanium(sym->getName())].push_back(sym);
-    }
+    for (Symbol *sym : symVector)
+      if (canBeVersioned(*sym))
+        (*demangledSyms)[demangleItanium(sym->getName())].push_back(sym);
   }
   return *demangledSyms;
 }
@@ -131,9 +136,9 @@ StringMap<std::vector<Symbol *>> &SymbolTable::getDemangledSyms() {
 std::vector<Symbol *> SymbolTable::findByVersion(SymbolVersion ver) {
   if (ver.isExternCpp)
     return getDemangledSyms().lookup(ver.name);
-  if (Symbol *b = find(ver.name))
-    if (b->isDefined() || b->isCommon())
-      return {b};
+  if (Symbol *sym = find(ver.name))
+    if (canBeVersioned(*sym))
+      return {sym};
   return {};
 }
 
@@ -149,7 +154,7 @@ std::vector<Symbol *> SymbolTable::findAllByVersion(SymbolVersion ver) {
   }
 
   for (Symbol *sym : symVector)
-    if ((sym->isDefined() || sym->isCommon()) && m.match(sym->getName()))
+    if (canBeVersioned(*sym) && m.match(sym->getName()))
       res.push_back(sym);
   return res;
 }

diff  --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp
index 42c8a71c4185..50c153d63ab7 100644
--- a/lld/ELF/Symbols.cpp
+++ b/lld/ELF/Symbols.cpp
@@ -276,7 +276,7 @@ uint8_t Symbol::computeBinding() const {
   if (config->relocatable)
     return binding;
   if ((visibility != STV_DEFAULT && visibility != STV_PROTECTED) ||
-      versionId == VER_NDX_LOCAL)
+      (versionId == VER_NDX_LOCAL && isDefined()))
     return STB_LOCAL;
   if (!config->gnuUnique && binding == STB_GNU_UNIQUE)
     return STB_GLOBAL;

diff  --git a/lld/test/ELF/lto/version-libcall.ll b/lld/test/ELF/lto/version-libcall.ll
new file mode 100644
index 000000000000..17d4d0dee0f4
--- /dev/null
+++ b/lld/test/ELF/lto/version-libcall.ll
@@ -0,0 +1,44 @@
+; REQUIRES: x86
+;; The LTO code generator may create references which will fetch lazy symbols.
+;; Test that version script local: directives can change the binding of such
+;; symbols to STB_LOCAL. This is a bit complex because the LTO code generator
+;; happens after version script scanning and can change symbols from Lazy to Defined.
+
+; RUN: llvm-as %s -o %t.bc
+; RUN: echo '.globl __udivti3; __udivti3:' | llvm-mc -filetype=obj -triple=x86_64 - -o %t1.o
+
+;; An exact pattern can localize a libcall.
+; RUN: echo '{ global: foo; local: __udivti3; };' > %t.exact.ver
+; RUN: ld.lld -shared --version-script %t.exact.ver %t.bc --start-lib %t1.o --end-lib -o %t.exact.so
+; RUN: llvm-nm %t.exact.so | FileCheck %s
+
+;; A wildcard pattern can localize a libcall.
+; RUN: echo '{ global: foo; local: *; };' > %t.wild.ver
+; RUN: ld.lld -shared --version-script %t.wild.ver %t.bc --start-lib %t1.o --end-lib -o %t.wild.so
+; RUN: llvm-nm %t.wild.so | FileCheck %s
+
+; CHECK: t __udivti3
+; CHECK: T foo
+
+;; Test that --dynamic-list works on such libcall fetched symbols.
+; RUN: echo '{ foo; __udivti3; };' > %t.exact.list
+; RUN: ld.lld -pie --dynamic-list %t.exact.list %t.bc --start-lib %t1.o --end-lib -o %t.exact
+; RUN: llvm-nm %t.exact | FileCheck --check-prefix=LIST %s
+; RUN: echo '{ foo; __udiv*; };' > %t.wild.list
+; RUN: ld.lld -pie --dynamic-list %t.wild.list %t.bc --start-lib %t1.o --end-lib -o %t.wild
+; RUN: llvm-nm %t.wild | FileCheck --check-prefix=LIST %s
+
+; LIST: T __udivti3
+; LIST: T foo
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+declare i64 @llvm.udiv.fix.i64(i64, i64, i32)
+
+;; The symbol table does not record __udivti3, but the reference will be created
+;; on the fly.
+define i64 @foo(i64 %x, i64 %y) {
+  %ret = call i64 @llvm.udiv.fix.i64(i64 %x, i64 %y, i32 31)
+  ret i64 %ret
+}


        


More information about the llvm-commits mailing list