diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp --- a/lld/ELF/SymbolTable.cpp +++ b/lld/ELF/SymbolTable.cpp @@ -120,7 +120,7 @@ if (!demangledSyms) { demangledSyms.emplace(); for (Symbol *sym : symVector) { - if (!sym->isDefined() && !sym->isCommon()) + if (!sym->isDefined() && !sym->isCommon() && !sym->isLazy()) continue; (*demangledSyms)[demangleItanium(sym->getName())].push_back(sym); } @@ -131,8 +131,10 @@ std::vector SymbolTable::findByVersion(SymbolVersion ver) { if (ver.isExternCpp) return getDemangledSyms().lookup(ver.name); + // A version script/dynamic list is only meaningful for a Defined symbol. + // A lazy symbol can be made Defined if an LTO libcall references it. if (Symbol *b = find(ver.name)) - if (b->isDefined() || b->isCommon()) + if (b->isDefined() || b->isCommon() || b->isLazy()) return {b}; return {}; } @@ -148,8 +150,10 @@ return res; } + // See the comment in findByVersion() for the matched symbols. for (Symbol *sym : symVector) - if ((sym->isDefined() || sym->isCommon()) && m.match(sym->getName())) + if ((sym->isDefined() || sym->isCommon() || sym->isLazy()) && + m.match(sym->getName())) res.push_back(sym); return res; } diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -276,7 +276,7 @@ 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 --- /dev/null +++ b/lld/test/ELF/lto/version-libcall.ll @@ -0,0 +1,42 @@ +; 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) + +define i64 @foo(i64 %x, i64 %y) { + %ret = call i64 @llvm.udiv.fix.i64(i64 %x, i64 %y, i32 31) + ret i64 %ret +}