Skip to content

Commit 1037f57

Browse files
authored
[lld][elf] Warn if '*' pattern is used multiple times in version scripts (#102669)
If this pattern is used more than once in version script(s), only one will have an effect, so it's probably a user error and can be diagnosed.
1 parent 6640dac commit 1037f57

File tree

3 files changed

+70
-3
lines changed

3 files changed

+70
-3
lines changed

lld/ELF/SymbolTable.cpp

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,13 +309,43 @@ void SymbolTable::scanVersionScript() {
309309

310310
// Then, assign versions to "*". In GNU linkers they have lower priority than
311311
// other wildcards.
312+
bool globalAsteriskFound = false;
313+
bool localAsteriskFound = false;
314+
bool asteriskReported = false;
315+
auto assignAsterisk = [&](SymbolVersion &pat, VersionDefinition *ver,
316+
bool isLocal) {
317+
// Avoid issuing a warning if both '--retain-symbol-file' and a version
318+
// script with `global: *` are used.
319+
//
320+
// '--retain-symbol-file' adds a "*" pattern to
321+
// 'config->versionDefinitions[VER_NDX_LOCAL].nonLocalPatterns', see
322+
// 'readConfigs()' in 'Driver.cpp'. Note that it is not '.localPatterns',
323+
// and may seem counterintuitive, but still works as expected. Here we can
324+
// exploit that and skip analyzing the pattern added for this option.
325+
if (!asteriskReported && (isLocal || ver->id > VER_NDX_LOCAL)) {
326+
if ((isLocal && globalAsteriskFound) ||
327+
(!isLocal && localAsteriskFound)) {
328+
warn("wildcard pattern '*' is used for both 'local' and 'global' "
329+
"scopes in version script");
330+
asteriskReported = true;
331+
} else if (!isLocal && globalAsteriskFound) {
332+
warn("wildcard pattern '*' is used for multiple version definitions in "
333+
"version script");
334+
asteriskReported = true;
335+
} else {
336+
localAsteriskFound = isLocal;
337+
globalAsteriskFound = !isLocal;
338+
}
339+
}
340+
assignWildcard(pat, isLocal ? VER_NDX_LOCAL : ver->id, ver->name);
341+
};
312342
for (VersionDefinition &v : llvm::reverse(ctx.arg.versionDefinitions)) {
313343
for (SymbolVersion &pat : v.nonLocalPatterns)
314344
if (pat.hasWildcard && pat.name == "*")
315-
assignWildcard(pat, v.id, v.name);
345+
assignAsterisk(pat, &v, false);
316346
for (SymbolVersion &pat : v.localPatterns)
317347
if (pat.hasWildcard && pat.name == "*")
318-
assignWildcard(pat, VER_NDX_LOCAL, v.name);
348+
assignAsterisk(pat, &v, true);
319349
}
320350

321351
// Symbol themselves might know their versions because symbols

lld/test/ELF/version-script-reassign-glob.s

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
# RUN: llvm-readelf --dyn-syms %t.so | FileCheck --check-prefix=BAR %s
1111

1212
# RUN: echo 'bar1 { *; }; bar2 { *; };' > %t2.ver
13-
# RUN: ld.lld --version-script %t2.ver %t.o -shared -o %t2.so --fatal-warnings
13+
# RUN: ld.lld --version-script %t2.ver %t.o -shared -o %t2.so 2>&1 | \
14+
# RUN: FileCheck --check-prefix=DUPWARN %s
1415
# RUN: llvm-readelf --dyn-syms %t2.so | FileCheck --check-prefix=BAR2 %s
1516

1617
## If both a non-* glob and a * match, non-* wins.
@@ -21,6 +22,7 @@
2122

2223
## When there are multiple * patterns, the last wins.
2324
# BAR2: GLOBAL DEFAULT 7 foo@@bar2
25+
# DUPWARN: warning: wildcard pattern '*' is used for multiple version definitions in version script
2426

2527
.globl foo
2628
foo:

lld/test/ELF/version-script-warn.s

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# REQUIRES: x86
2+
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
3+
4+
# RUN: echo 'foo { *; }; bar { *; };' > %t.ver
5+
# RUN: ld.lld --version-script %t.ver %t.o -shared -o %t.so 2>&1 | \
6+
# RUN: FileCheck --check-prefix=MULTVER %s
7+
8+
# RUN: echo '{ global: *; local: *;};' > %t.ver
9+
# RUN: ld.lld --version-script %t.ver %t.o -shared -o %t.so 2>&1 | \
10+
# RUN: FileCheck --check-prefix=LOCGLOB %s
11+
12+
# RUN: echo 'V1 { global: *; }; V2 { local: *;};' > %t.ver
13+
# RUN: ld.lld --version-script %t.ver %t.o -shared -o %t.so 2>&1 | \
14+
# RUN: FileCheck --check-prefix=LOCGLOB %s
15+
16+
# RUN: echo 'V1 { local: *; }; V2 { global: *;};' > %t.ver
17+
# RUN: ld.lld --version-script %t.ver %t.o -shared -o %t.so 2>&1 | \
18+
# RUN: FileCheck --check-prefix=LOCGLOB %s
19+
20+
# RUN: echo 'V1 { local: *; }; V2 { local: *;};' > %t.ver
21+
# RUN: ld.lld --version-script %t.ver %t.o -shared -o %t.so --fatal-warnings
22+
23+
## --retain-symbols-file uses the same internal infrastructure as the support
24+
## for version scripts. Do not show the warings if they both are used.
25+
# RUN: echo 'foo' > %t_retain.txt
26+
# RUN: echo '{ local: *; };' > %t_local.ver
27+
# RUN: echo '{ global: *; };' > %t_global.ver
28+
# RUN: ld.lld --retain-symbols-file=%t_retain.txt --version-script %t_local.ver %t.o -shared -o %t.so --fatal-warnings
29+
# RUN: ld.lld --retain-symbols-file=%t_retain.txt --version-script %t_global.ver %t.o -shared -o %t.so --fatal-warnings
30+
31+
# MULTVER: warning: wildcard pattern '*' is used for multiple version definitions in version script
32+
# LOCGLOB: warning: wildcard pattern '*' is used for both 'local' and 'global' scopes in version script
33+
34+
.globl foo
35+
foo:

0 commit comments

Comments
 (0)