[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.
This commit is contained in:
@@ -309,13 +309,43 @@ void SymbolTable::scanVersionScript() {
|
||||
|
||||
// Then, assign versions to "*". In GNU linkers they have lower priority than
|
||||
// other wildcards.
|
||||
bool globalAsteriskFound = false;
|
||||
bool localAsteriskFound = false;
|
||||
bool asteriskReported = false;
|
||||
auto assignAsterisk = [&](SymbolVersion &pat, VersionDefinition *ver,
|
||||
bool isLocal) {
|
||||
// Avoid issuing a warning if both '--retain-symbol-file' and a version
|
||||
// script with `global: *` are used.
|
||||
//
|
||||
// '--retain-symbol-file' adds a "*" pattern to
|
||||
// 'config->versionDefinitions[VER_NDX_LOCAL].nonLocalPatterns', see
|
||||
// 'readConfigs()' in 'Driver.cpp'. Note that it is not '.localPatterns',
|
||||
// and may seem counterintuitive, but still works as expected. Here we can
|
||||
// exploit that and skip analyzing the pattern added for this option.
|
||||
if (!asteriskReported && (isLocal || ver->id > VER_NDX_LOCAL)) {
|
||||
if ((isLocal && globalAsteriskFound) ||
|
||||
(!isLocal && localAsteriskFound)) {
|
||||
warn("wildcard pattern '*' is used for both 'local' and 'global' "
|
||||
"scopes in version script");
|
||||
asteriskReported = true;
|
||||
} else if (!isLocal && globalAsteriskFound) {
|
||||
warn("wildcard pattern '*' is used for multiple version definitions in "
|
||||
"version script");
|
||||
asteriskReported = true;
|
||||
} else {
|
||||
localAsteriskFound = isLocal;
|
||||
globalAsteriskFound = !isLocal;
|
||||
}
|
||||
}
|
||||
assignWildcard(pat, isLocal ? VER_NDX_LOCAL : ver->id, ver->name);
|
||||
};
|
||||
for (VersionDefinition &v : llvm::reverse(ctx.arg.versionDefinitions)) {
|
||||
for (SymbolVersion &pat : v.nonLocalPatterns)
|
||||
if (pat.hasWildcard && pat.name == "*")
|
||||
assignWildcard(pat, v.id, v.name);
|
||||
assignAsterisk(pat, &v, false);
|
||||
for (SymbolVersion &pat : v.localPatterns)
|
||||
if (pat.hasWildcard && pat.name == "*")
|
||||
assignWildcard(pat, VER_NDX_LOCAL, v.name);
|
||||
assignAsterisk(pat, &v, true);
|
||||
}
|
||||
|
||||
// Symbol themselves might know their versions because symbols
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
# RUN: llvm-readelf --dyn-syms %t.so | FileCheck --check-prefix=BAR %s
|
||||
|
||||
# RUN: echo 'bar1 { *; }; bar2 { *; };' > %t2.ver
|
||||
# RUN: ld.lld --version-script %t2.ver %t.o -shared -o %t2.so --fatal-warnings
|
||||
# RUN: ld.lld --version-script %t2.ver %t.o -shared -o %t2.so 2>&1 | \
|
||||
# RUN: FileCheck --check-prefix=DUPWARN %s
|
||||
# RUN: llvm-readelf --dyn-syms %t2.so | FileCheck --check-prefix=BAR2 %s
|
||||
|
||||
## If both a non-* glob and a * match, non-* wins.
|
||||
@@ -21,6 +22,7 @@
|
||||
|
||||
## When there are multiple * patterns, the last wins.
|
||||
# BAR2: GLOBAL DEFAULT 7 foo@@bar2
|
||||
# DUPWARN: warning: wildcard pattern '*' is used for multiple version definitions in version script
|
||||
|
||||
.globl foo
|
||||
foo:
|
||||
|
||||
35
lld/test/ELF/version-script-warn.s
Normal file
35
lld/test/ELF/version-script-warn.s
Normal file
@@ -0,0 +1,35 @@
|
||||
# REQUIRES: x86
|
||||
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
|
||||
|
||||
# RUN: echo 'foo { *; }; bar { *; };' > %t.ver
|
||||
# RUN: ld.lld --version-script %t.ver %t.o -shared -o %t.so 2>&1 | \
|
||||
# RUN: FileCheck --check-prefix=MULTVER %s
|
||||
|
||||
# RUN: echo '{ global: *; local: *;};' > %t.ver
|
||||
# RUN: ld.lld --version-script %t.ver %t.o -shared -o %t.so 2>&1 | \
|
||||
# RUN: FileCheck --check-prefix=LOCGLOB %s
|
||||
|
||||
# RUN: echo 'V1 { global: *; }; V2 { local: *;};' > %t.ver
|
||||
# RUN: ld.lld --version-script %t.ver %t.o -shared -o %t.so 2>&1 | \
|
||||
# RUN: FileCheck --check-prefix=LOCGLOB %s
|
||||
|
||||
# RUN: echo 'V1 { local: *; }; V2 { global: *;};' > %t.ver
|
||||
# RUN: ld.lld --version-script %t.ver %t.o -shared -o %t.so 2>&1 | \
|
||||
# RUN: FileCheck --check-prefix=LOCGLOB %s
|
||||
|
||||
# RUN: echo 'V1 { local: *; }; V2 { local: *;};' > %t.ver
|
||||
# RUN: ld.lld --version-script %t.ver %t.o -shared -o %t.so --fatal-warnings
|
||||
|
||||
## --retain-symbols-file uses the same internal infrastructure as the support
|
||||
## for version scripts. Do not show the warings if they both are used.
|
||||
# RUN: echo 'foo' > %t_retain.txt
|
||||
# RUN: echo '{ local: *; };' > %t_local.ver
|
||||
# RUN: echo '{ global: *; };' > %t_global.ver
|
||||
# RUN: ld.lld --retain-symbols-file=%t_retain.txt --version-script %t_local.ver %t.o -shared -o %t.so --fatal-warnings
|
||||
# RUN: ld.lld --retain-symbols-file=%t_retain.txt --version-script %t_global.ver %t.o -shared -o %t.so --fatal-warnings
|
||||
|
||||
# MULTVER: warning: wildcard pattern '*' is used for multiple version definitions in version script
|
||||
# LOCGLOB: warning: wildcard pattern '*' is used for both 'local' and 'global' scopes in version script
|
||||
|
||||
.globl foo
|
||||
foo:
|
||||
Reference in New Issue
Block a user