Skip to content

Commit c1c679e

Browse files
committed
[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
1 parent 98bb7fd commit c1c679e

File tree

3 files changed

+59
-10
lines changed

3 files changed

+59
-10
lines changed

lld/ELF/SymbolTable.cpp

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ Symbol *SymbolTable::find(StringRef name) {
103103
return sym;
104104
}
105105

106+
// A version script/dynamic list is only meaningful for a Defined symbol.
107+
// A CommonSymbol will be converted to a Defined in replaceCommonSymbols().
108+
// A lazy symbol may be made Defined if an LTO libcall fetches it.
109+
static bool canBeVersioned(const Symbol &sym) {
110+
return sym.isDefined() || sym.isCommon() || sym.isLazy();
111+
}
112+
106113
// Initialize demangledSyms with a map from demangled symbols to symbol
107114
// objects. Used to handle "extern C++" directive in version scripts.
108115
//
@@ -119,21 +126,19 @@ Symbol *SymbolTable::find(StringRef name) {
119126
StringMap<std::vector<Symbol *>> &SymbolTable::getDemangledSyms() {
120127
if (!demangledSyms) {
121128
demangledSyms.emplace();
122-
for (Symbol *sym : symVector) {
123-
if (!sym->isDefined() && !sym->isCommon())
124-
continue;
125-
(*demangledSyms)[demangleItanium(sym->getName())].push_back(sym);
126-
}
129+
for (Symbol *sym : symVector)
130+
if (canBeVersioned(*sym))
131+
(*demangledSyms)[demangleItanium(sym->getName())].push_back(sym);
127132
}
128133
return *demangledSyms;
129134
}
130135

131136
std::vector<Symbol *> SymbolTable::findByVersion(SymbolVersion ver) {
132137
if (ver.isExternCpp)
133138
return getDemangledSyms().lookup(ver.name);
134-
if (Symbol *b = find(ver.name))
135-
if (b->isDefined() || b->isCommon())
136-
return {b};
139+
if (Symbol *sym = find(ver.name))
140+
if (canBeVersioned(*sym))
141+
return {sym};
137142
return {};
138143
}
139144

@@ -149,7 +154,7 @@ std::vector<Symbol *> SymbolTable::findAllByVersion(SymbolVersion ver) {
149154
}
150155

151156
for (Symbol *sym : symVector)
152-
if ((sym->isDefined() || sym->isCommon()) && m.match(sym->getName()))
157+
if (canBeVersioned(*sym) && m.match(sym->getName()))
153158
res.push_back(sym);
154159
return res;
155160
}

lld/ELF/Symbols.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ uint8_t Symbol::computeBinding() const {
276276
if (config->relocatable)
277277
return binding;
278278
if ((visibility != STV_DEFAULT && visibility != STV_PROTECTED) ||
279-
versionId == VER_NDX_LOCAL)
279+
(versionId == VER_NDX_LOCAL && isDefined()))
280280
return STB_LOCAL;
281281
if (!config->gnuUnique && binding == STB_GNU_UNIQUE)
282282
return STB_GLOBAL;

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

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
; REQUIRES: x86
2+
;; The LTO code generator may create references which will fetch lazy symbols.
3+
;; Test that version script local: directives can change the binding of such
4+
;; symbols to STB_LOCAL. This is a bit complex because the LTO code generator
5+
;; happens after version script scanning and can change symbols from Lazy to Defined.
6+
7+
; RUN: llvm-as %s -o %t.bc
8+
; RUN: echo '.globl __udivti3; __udivti3:' | llvm-mc -filetype=obj -triple=x86_64 - -o %t1.o
9+
10+
;; An exact pattern can localize a libcall.
11+
; RUN: echo '{ global: foo; local: __udivti3; };' > %t.exact.ver
12+
; RUN: ld.lld -shared --version-script %t.exact.ver %t.bc --start-lib %t1.o --end-lib -o %t.exact.so
13+
; RUN: llvm-nm %t.exact.so | FileCheck %s
14+
15+
;; A wildcard pattern can localize a libcall.
16+
; RUN: echo '{ global: foo; local: *; };' > %t.wild.ver
17+
; RUN: ld.lld -shared --version-script %t.wild.ver %t.bc --start-lib %t1.o --end-lib -o %t.wild.so
18+
; RUN: llvm-nm %t.wild.so | FileCheck %s
19+
20+
; CHECK: t __udivti3
21+
; CHECK: T foo
22+
23+
;; Test that --dynamic-list works on such libcall fetched symbols.
24+
; RUN: echo '{ foo; __udivti3; };' > %t.exact.list
25+
; RUN: ld.lld -pie --dynamic-list %t.exact.list %t.bc --start-lib %t1.o --end-lib -o %t.exact
26+
; RUN: llvm-nm %t.exact | FileCheck --check-prefix=LIST %s
27+
; RUN: echo '{ foo; __udiv*; };' > %t.wild.list
28+
; RUN: ld.lld -pie --dynamic-list %t.wild.list %t.bc --start-lib %t1.o --end-lib -o %t.wild
29+
; RUN: llvm-nm %t.wild | FileCheck --check-prefix=LIST %s
30+
31+
; LIST: T __udivti3
32+
; LIST: T foo
33+
34+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
35+
target triple = "x86_64-unknown-linux-gnu"
36+
37+
declare i64 @llvm.udiv.fix.i64(i64, i64, i32)
38+
39+
;; The symbol table does not record __udivti3, but the reference will be created
40+
;; on the fly.
41+
define i64 @foo(i64 %x, i64 %y) {
42+
%ret = call i64 @llvm.udiv.fix.i64(i64 %x, i64 %y, i32 31)
43+
ret i64 %ret
44+
}

0 commit comments

Comments
 (0)