diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp index db8ee8f4d7b3b..b9ef28f0436f8 100644 --- a/lld/ELF/SymbolTable.cpp +++ b/lld/ELF/SymbolTable.cpp @@ -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 diff --git a/lld/test/ELF/version-script-reassign-glob.s b/lld/test/ELF/version-script-reassign-glob.s index 39d19a26fc449..8de36467bd8ee 100644 --- a/lld/test/ELF/version-script-reassign-glob.s +++ b/lld/test/ELF/version-script-reassign-glob.s @@ -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: diff --git a/lld/test/ELF/version-script-warn.s b/lld/test/ELF/version-script-warn.s new file mode 100644 index 0000000000000..9aba596165796 --- /dev/null +++ b/lld/test/ELF/version-script-warn.s @@ -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: