From 00809c8889ed34a5fe014167aa473216dcb63a47 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Wed, 4 Aug 2021 23:52:55 -0700 Subject: [PATCH] [ELF] Apply version script patterns to non-default version symbols Currently version script patterns are ignored for .symver produced non-default version (single @) symbols. This makes such symbols not localizable by `local:`, e.g. ``` .symver foo3_v1,foo3@v1 .globl foo_v1 foo3_v1: ld.lld --version-script=a.ver -shared a.o ``` This patch adds the support: * Move `config->versionDefinitions[VER_NDX_LOCAL].patterns` to `config->versionDefinitions[versionId].localPatterns` * Rename `config->versionDefinitions[versionId].patterns` to `config->versionDefinitions[versionId].nonLocalPatterns` * Allow `findAllByVersion` to find non-default version symbols when `includeNonDefault` is true. (Note: `symtab` keys do not have `@@`) * Make each pattern check both the unversioned `pat.name` and the versioned `${pat.name}@${v.name}` * `localPatterns` can localize `${pat.name}@${v.name}`. `nonLocalPatterns` can prevent localization by assigning `verdefIndex` (before `parseSymbolVersion`). --- If a user notices new `undefined symbol` errors with a version script containing `local: *;`, the issue is likely due to a missing `global:` pattern. Reviewed By: peter.smith Differential Revision: https://reviews.llvm.org/D107234 --- lld/ELF/Config.h | 3 +- lld/ELF/Driver.cpp | 9 +- lld/ELF/ScriptParser.cpp | 9 +- lld/ELF/SymbolTable.cpp | 118 +++++++++++++++++------- lld/ELF/SymbolTable.h | 10 ++- lld/ELF/Symbols.cpp | 3 + lld/test/ELF/verdef-defaultver.s | 2 +- lld/test/ELF/version-script-extern-exact.s | 30 ------- lld/test/ELF/version-script-extern-wildcards.s | 28 ------ lld/test/ELF/version-script-extern.s | 120 ------------------------- lld/test/ELF/version-script-noundef.s | 4 + lld/test/ELF/version-script-symver-extern.s | 45 ++++++++++ lld/test/ELF/version-script-symver.s | 2 - 13 files changed, 154 insertions(+), 229 deletions(-) delete mode 100644 lld/test/ELF/version-script-extern-exact.s delete mode 100644 lld/test/ELF/version-script-extern-wildcards.s delete mode 100644 lld/test/ELF/version-script-extern.s create mode 100644 lld/test/ELF/version-script-symver-extern.s diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index a996a81..e1abb4d 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -86,7 +86,8 @@ struct SymbolVersion { struct VersionDefinition { llvm::StringRef name; uint16_t id; - std::vector patterns; + std::vector nonLocalPatterns; + std::vector localPatterns; }; // This struct contains the global configuration for the linker. diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 53e5415..551a3d3 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1351,18 +1351,19 @@ static void readConfigs(opt::InputArgList &args) { } assert(config->versionDefinitions.empty()); - config->versionDefinitions.push_back({"local", (uint16_t)VER_NDX_LOCAL, {}}); config->versionDefinitions.push_back( - {"global", (uint16_t)VER_NDX_GLOBAL, {}}); + {"local", (uint16_t)VER_NDX_LOCAL, {}, {}}); + config->versionDefinitions.push_back( + {"global", (uint16_t)VER_NDX_GLOBAL, {}, {}}); // If --retain-symbol-file is used, we'll keep only the symbols listed in // the file and discard all others. if (auto *arg = args.getLastArg(OPT_retain_symbols_file)) { - config->versionDefinitions[VER_NDX_LOCAL].patterns.push_back( + config->versionDefinitions[VER_NDX_LOCAL].nonLocalPatterns.push_back( {"*", /*isExternCpp=*/false, /*hasWildcard=*/true}); if (Optional buffer = readFile(arg->getValue())) for (StringRef s : args::getLines(*buffer)) - config->versionDefinitions[VER_NDX_GLOBAL].patterns.push_back( + config->versionDefinitions[VER_NDX_GLOBAL].nonLocalPatterns.push_back( {s, /*isExternCpp=*/false, /*hasWildcard=*/false}); } diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp index 2c980eb..1c743fd 100644 --- a/lld/ELF/ScriptParser.cpp +++ b/lld/ELF/ScriptParser.cpp @@ -1496,9 +1496,9 @@ void ScriptParser::readAnonymousDeclaration() { std::vector globals; std::tie(locals, globals) = readSymbols(); for (const SymbolVersion &pat : locals) - config->versionDefinitions[VER_NDX_LOCAL].patterns.push_back(pat); + config->versionDefinitions[VER_NDX_LOCAL].localPatterns.push_back(pat); for (const SymbolVersion &pat : globals) - config->versionDefinitions[VER_NDX_GLOBAL].patterns.push_back(pat); + config->versionDefinitions[VER_NDX_GLOBAL].nonLocalPatterns.push_back(pat); expect(";"); } @@ -1510,13 +1510,12 @@ void ScriptParser::readVersionDeclaration(StringRef verStr) { std::vector locals; std::vector globals; std::tie(locals, globals) = readSymbols(); - for (const SymbolVersion &pat : locals) - config->versionDefinitions[VER_NDX_LOCAL].patterns.push_back(pat); // Create a new version definition and add that to the global symbols. VersionDefinition ver; ver.name = verStr; - ver.patterns = globals; + ver.nonLocalPatterns = std::move(globals); + ver.localPatterns = std::move(locals); ver.id = config->versionDefinitions.size(); config->versionDefinitions.push_back(ver); diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp index 70aea28..22e6b4f 100644 --- a/lld/ELF/SymbolTable.cpp +++ b/lld/ELF/SymbolTable.cpp @@ -134,9 +134,20 @@ static bool canBeVersioned(const Symbol &sym) { StringMap> &SymbolTable::getDemangledSyms() { if (!demangledSyms) { demangledSyms.emplace(); + std::string demangled; for (Symbol *sym : symVector) - if (canBeVersioned(*sym)) - (*demangledSyms)[demangleItanium(sym->getName())].push_back(sym); + if (canBeVersioned(*sym)) { + StringRef name = sym->getName(); + size_t pos = name.find('@'); + if (pos == std::string::npos) + demangled = demangleItanium(name); + else if (pos + 1 == name.size() || name[pos + 1] == '@') + demangled = demangleItanium(name.substr(0, pos)); + else + demangled = + (demangleItanium(name.substr(0, pos)) + name.substr(pos)).str(); + (*demangledSyms)[demangled].push_back(sym); + } } return *demangledSyms; } @@ -150,19 +161,29 @@ std::vector SymbolTable::findByVersion(SymbolVersion ver) { return {}; } -std::vector SymbolTable::findAllByVersion(SymbolVersion ver) { +std::vector SymbolTable::findAllByVersion(SymbolVersion ver, + bool includeNonDefault) { std::vector res; SingleStringMatcher m(ver.name); + auto check = [&](StringRef name) { + size_t pos = name.find('@'); + if (!includeNonDefault) + return pos == StringRef::npos; + return !(pos + 1 < name.size() && name[pos + 1] == '@'); + }; if (ver.isExternCpp) { for (auto &p : getDemangledSyms()) if (m.match(p.first())) - res.insert(res.end(), p.second.begin(), p.second.end()); + for (Symbol *sym : p.second) + if (check(sym->getName())) + res.push_back(sym); return res; } for (Symbol *sym : symVector) - if (canBeVersioned(*sym) && m.match(sym->getName())) + if (canBeVersioned(*sym) && check(sym->getName()) && + m.match(sym->getName())) res.push_back(sym); return res; } @@ -172,7 +193,7 @@ void SymbolTable::handleDynamicList() { for (SymbolVersion &ver : config->dynamicList) { std::vector syms; if (ver.hasWildcard) - syms = findAllByVersion(ver); + syms = findAllByVersion(ver, /*includeNonDefault=*/true); else syms = findByVersion(ver); @@ -181,21 +202,13 @@ void SymbolTable::handleDynamicList() { } } -// Set symbol versions to symbols. This function handles patterns -// containing no wildcard characters. -void SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId, - StringRef versionName) { - if (ver.hasWildcard) - return; - +// Set symbol versions to symbols. This function handles patterns containing no +// wildcard characters. Return false if no symbol definition matches ver. +bool SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId, + StringRef versionName, + bool includeNonDefault) { // Get a list of symbols which we need to assign the version to. std::vector syms = findByVersion(ver); - if (syms.empty()) { - if (!config->undefinedVersion) - error("version script assignment of '" + versionName + "' to symbol '" + - ver.name + "' failed: symbol not defined"); - return; - } auto getName = [](uint16_t ver) -> std::string { if (ver == VER_NDX_LOCAL) @@ -207,10 +220,11 @@ void SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId, // Assign the version. for (Symbol *sym : syms) { - // Skip symbols containing version info because symbol versions - // specified by symbol names take precedence over version scripts. - // See parseSymbolVersion(). - if (sym->getName().contains('@')) + // For a non-local versionId, skip symbols containing version info because + // symbol versions specified by symbol names take precedence over version + // scripts. See parseSymbolVersion(). + if (!includeNonDefault && versionId != VER_NDX_LOCAL && + sym->getName().contains('@')) continue; // If the version has not been assigned, verdefIndex is -1. Use an arbitrary @@ -225,13 +239,15 @@ void SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId, warn("attempt to reassign symbol '" + ver.name + "' of " + getName(sym->versionId) + " to " + getName(versionId)); } + return !syms.empty(); } -void SymbolTable::assignWildcardVersion(SymbolVersion ver, uint16_t versionId) { +void SymbolTable::assignWildcardVersion(SymbolVersion ver, uint16_t versionId, + bool includeNonDefault) { // Exact matching takes precedence over fuzzy matching, // so we set a version to a symbol only if no version has been assigned // to the symbol. This behavior is compatible with GNU. - for (Symbol *sym : findAllByVersion(ver)) + for (Symbol *sym : findAllByVersion(ver, includeNonDefault)) if (sym->verdefIndex == UINT32_C(-1)) { sym->verdefIndex = 0; sym->versionId = versionId; @@ -244,26 +260,60 @@ void SymbolTable::assignWildcardVersion(SymbolVersion ver, uint16_t versionId) { // script file, the script does not actually define any symbol version, // but just specifies symbols visibilities. void SymbolTable::scanVersionScript() { + SmallString<128> buf; // First, we assign versions to exact matching symbols, // i.e. version definitions not containing any glob meta-characters. - for (VersionDefinition &v : config->versionDefinitions) - for (SymbolVersion &pat : v.patterns) - assignExactVersion(pat, v.id, v.name); + std::vector syms; + for (VersionDefinition &v : config->versionDefinitions) { + auto assignExact = [&](SymbolVersion pat, uint16_t id, StringRef ver) { + bool found = + assignExactVersion(pat, id, ver, /*includeNonDefault=*/false); + buf.clear(); + found |= assignExactVersion({(pat.name + "@" + v.name).toStringRef(buf), + pat.isExternCpp, /*hasWildCard=*/false}, + id, ver, /*includeNonDefault=*/true); + if (!found && !config->undefinedVersion) + errorOrWarn("version script assignment of '" + ver + "' to symbol '" + + pat.name + "' failed: symbol not defined"); + }; + for (SymbolVersion &pat : v.nonLocalPatterns) + if (!pat.hasWildcard) + assignExact(pat, v.id, v.name); + for (SymbolVersion pat : v.localPatterns) + if (!pat.hasWildcard) + assignExact(pat, VER_NDX_LOCAL, "local"); + } // Next, assign versions to wildcards that are not "*". Note that because the // last match takes precedence over previous matches, we iterate over the // definitions in the reverse order. - for (VersionDefinition &v : llvm::reverse(config->versionDefinitions)) - for (SymbolVersion &pat : v.patterns) + auto assignWildcard = [&](SymbolVersion pat, uint16_t id, StringRef ver) { + assignWildcardVersion(pat, id, /*includeNonDefault=*/false); + buf.clear(); + assignWildcardVersion({(pat.name + "@" + ver).toStringRef(buf), + pat.isExternCpp, /*hasWildCard=*/true}, + id, + /*includeNonDefault=*/true); + }; + for (VersionDefinition &v : llvm::reverse(config->versionDefinitions)) { + for (SymbolVersion &pat : v.nonLocalPatterns) if (pat.hasWildcard && pat.name != "*") - assignWildcardVersion(pat, v.id); + assignWildcard(pat, v.id, v.name); + for (SymbolVersion &pat : v.localPatterns) + if (pat.hasWildcard && pat.name != "*") + assignWildcard(pat, VER_NDX_LOCAL, v.name); + } // Then, assign versions to "*". In GNU linkers they have lower priority than // other wildcards. - for (VersionDefinition &v : config->versionDefinitions) - for (SymbolVersion &pat : v.patterns) + for (VersionDefinition &v : config->versionDefinitions) { + for (SymbolVersion &pat : v.nonLocalPatterns) if (pat.hasWildcard && pat.name == "*") - assignWildcardVersion(pat, v.id); + assignWildcard(pat, v.id, v.name); + for (SymbolVersion &pat : v.localPatterns) + if (pat.hasWildcard && pat.name == "*") + assignWildcard(pat, VER_NDX_LOCAL, v.name); + } // Symbol themselves might know their versions because symbols // can contain versions in the form of @. diff --git a/lld/ELF/SymbolTable.h b/lld/ELF/SymbolTable.h index 507af8d..54c4b11 100644 --- a/lld/ELF/SymbolTable.h +++ b/lld/ELF/SymbolTable.h @@ -65,12 +65,14 @@ public: private: std::vector findByVersion(SymbolVersion ver); - std::vector findAllByVersion(SymbolVersion ver); + std::vector findAllByVersion(SymbolVersion ver, + bool includeNonDefault); llvm::StringMap> &getDemangledSyms(); - void assignExactVersion(SymbolVersion ver, uint16_t versionId, - StringRef versionName); - void assignWildcardVersion(SymbolVersion ver, uint16_t versionId); + bool assignExactVersion(SymbolVersion ver, uint16_t versionId, + StringRef versionName, bool includeNonDefault); + void assignWildcardVersion(SymbolVersion ver, uint16_t versionId, + bool includeNonDefault); // The order the global symbols are in is not defined. We can use an arbitrary // order, but it has to be reproducible. That is true even when cross linking. diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp index 496be33..cef303f 100644 --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -208,6 +208,9 @@ OutputSection *Symbol::getOutputSection() const { // If a symbol name contains '@', the characters after that is // a symbol version name. This function parses that. void Symbol::parseSymbolVersion() { + // Return if localized by a local: pattern in a version script. + if (versionId == VER_NDX_LOCAL) + return; StringRef s = getName(); size_t pos = s.find('@'); if (pos == 0 || pos == StringRef::npos) diff --git a/lld/test/ELF/verdef-defaultver.s b/lld/test/ELF/verdef-defaultver.s index 05bf733..36913ad 100644 --- a/lld/test/ELF/verdef-defaultver.s +++ b/lld/test/ELF/verdef-defaultver.s @@ -1,7 +1,7 @@ # REQUIRES: x86 # RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/verdef-defaultver.s -o %t1 -# RUN: echo "V1 { global: a; local: *; };" > %t.script +# RUN: echo "V1 { global: a; b; local: *; };" > %t.script # RUN: echo "V2 { global: b; c; } V1;" >> %t.script # RUN: ld.lld --hash-style=sysv -shared -soname shared %t1 --version-script %t.script -o %t.so # RUN: llvm-readobj -V --dyn-syms %t.so | FileCheck --check-prefix=DSO %s diff --git a/lld/test/ELF/version-script-extern-exact.s b/lld/test/ELF/version-script-extern-exact.s deleted file mode 100644 index f00b3ac..0000000 --- a/lld/test/ELF/version-script-extern-exact.s +++ /dev/null @@ -1,30 +0,0 @@ -# REQUIRES: x86 - -# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o -# RUN: echo "FOO { global: extern \"C++\" { \"aaa*\"; }; };" > %t.script -# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so -# RUN: llvm-readobj --dyn-syms %t.so | FileCheck %s --check-prefix=NOMATCH - -# NOMATCH: DynamicSymbols [ -# NOMATCH-NOT: _Z3aaaPi@@FOO -# NOMATCH-NOT: _Z3aaaPf@@FOO -# NOMATCH: ] - -# RUN: echo "FOO { global: extern \"C++\" { \"aaa*\"; aaa*; }; };" > %t2.script -# RUN: ld.lld --version-script %t2.script -shared %t.o -o %t2.so -# RUN: llvm-readobj --dyn-syms %t2.so | FileCheck %s --check-prefix=MATCH -# MATCH: DynamicSymbols [ -# MATCH: _Z3aaaPi@@FOO -# MATCH: _Z3aaaPf@@FOO -# MATCH: ] - -.text -.globl _Z3aaaPi -.type _Z3aaaPi,@function -_Z3aaaPi: -retq - -.globl _Z3aaaPf -.type _Z3aaaPf,@function -_Z3aaaPf: -retq diff --git a/lld/test/ELF/version-script-extern-wildcards.s b/lld/test/ELF/version-script-extern-wildcards.s deleted file mode 100644 index 18ce20b..0000000 --- a/lld/test/ELF/version-script-extern-wildcards.s +++ /dev/null @@ -1,28 +0,0 @@ -# REQUIRES: x86 - -# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o -# RUN: echo "FOO { global: extern \"C++\" { foo*; }; };" > %t.script -# RUN: echo "BAR { global: extern \"C++\" { zed*; bar; }; };" >> %t.script -# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so -# RUN: llvm-readobj -V --dyn-syms %t.so | FileCheck %s - -# CHECK: VersionSymbols [ -# CHECK: Name: _Z3fooi@@FOO -# CHECK: Name: _Z3bari -# CHECK: Name: _Z3zedi@@BAR - -.text -.globl _Z3fooi -.type _Z3fooi,@function -_Z3fooi: -retq - -.globl _Z3bari -.type _Z3bari,@function -_Z3bari: -retq - -.globl _Z3zedi -.type _Z3zedi,@function -_Z3zedi: -retq diff --git a/lld/test/ELF/version-script-extern.s b/lld/test/ELF/version-script-extern.s deleted file mode 100644 index 29059ea..0000000 --- a/lld/test/ELF/version-script-extern.s +++ /dev/null @@ -1,120 +0,0 @@ -# REQUIRES: x86 - -# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o -# RUN: echo "LIBSAMPLE_1.0 { global:" > %t.script -# RUN: echo ' extern "C++" { "foo(int)"; "zed(int)"; "abc::abc()"; };' >> %t.script -# RUN: echo "};" >> %t.script -# RUN: echo "LIBSAMPLE_2.0 { global:" >> %t.script -# RUN: echo ' extern "C" { _Z3bari; };' >> %t.script -# RUN: echo "};" >> %t.script -# RUN: ld.lld --hash-style=sysv --version-script %t.script -soname fixed-length-string -shared %t.o -o %t.so -# RUN: llvm-readobj -V --dyn-syms %t.so | FileCheck --check-prefix=DSO %s - -# DSO: DynamicSymbols [ -# DSO-NEXT: Symbol { -# DSO-NEXT: Name: -# DSO-NEXT: Value: 0x0 -# DSO-NEXT: Size: 0 -# DSO-NEXT: Binding: Local -# DSO-NEXT: Type: None -# DSO-NEXT: Other: 0 -# DSO-NEXT: Section: Undefined -# DSO-NEXT: } -# DSO-NEXT: Symbol { -# DSO-NEXT: Name: _Z3fooi@@LIBSAMPLE_1.0 -# DSO-NEXT: Value: -# DSO-NEXT: Size: 0 -# DSO-NEXT: Binding: Global -# DSO-NEXT: Type: Function -# DSO-NEXT: Other: 0 -# DSO-NEXT: Section: .text -# DSO-NEXT: } -# DSO-NEXT: Symbol { -# DSO-NEXT: Name: _Z3bari@@LIBSAMPLE_2.0 -# DSO-NEXT: Value: -# DSO-NEXT: Size: 0 -# DSO-NEXT: Binding: Global -# DSO-NEXT: Type: Function -# DSO-NEXT: Other: 0 -# DSO-NEXT: Section: .text -# DSO-NEXT: } -# DSO-NEXT: Symbol { -# DSO-NEXT: Name: _Z3zedi@@LIBSAMPLE_1.0 -# DSO-NEXT: Value: -# DSO-NEXT: Size: 0 -# DSO-NEXT: Binding: Global (0x1) -# DSO-NEXT: Type: Function (0x2) -# DSO-NEXT: Other: 0 -# DSO-NEXT: Section: .text (0x6) -# DSO-NEXT: } -# DSO-NEXT: Symbol { -# DSO-NEXT: Name: _ZN3abcC1Ev@@LIBSAMPLE_1.0 -# DSO-NEXT: Value: -# DSO-NEXT: Size: 0 -# DSO-NEXT: Binding: Global (0x1) -# DSO-NEXT: Type: Function (0x2) -# DSO-NEXT: Other: 0 -# DSO-NEXT: Section: .text (0x6) -# DSO-NEXT: } -# DSO-NEXT: Symbol { -# DSO-NEXT: Name: _ZN3abcC2Ev@@LIBSAMPLE_1.0 -# DSO-NEXT: Value: -# DSO-NEXT: Size: 0 -# DSO-NEXT: Binding: Global (0x1) -# DSO-NEXT: Type: Function (0x2) -# DSO-NEXT: Other: 0 -# DSO-NEXT: Section: .text (0x6) -# DSO-NEXT: } -# DSO-NEXT: ] -# DSO-NEXT: VersionSymbols [ -# DSO-NEXT: Symbol { -# DSO-NEXT: Version: 0 -# DSO-NEXT: Name: -# DSO-NEXT: } -# DSO-NEXT: Symbol { -# DSO-NEXT: Version: 2 -# DSO-NEXT: Name: _Z3fooi@@LIBSAMPLE_1.0 -# DSO-NEXT: } -# DSO-NEXT: Symbol { -# DSO-NEXT: Version: 3 -# DSO-NEXT: Name: _Z3bari@@LIBSAMPLE_2.0 -# DSO-NEXT: } -# DSO-NEXT: Symbol { -# DSO-NEXT: Version: 2 -# DSO-NEXT: Name: _Z3zedi@@LIBSAMPLE_1.0 -# DSO-NEXT: } -# DSO-NEXT: Symbol { -# DSO-NEXT: Version: 2 -# DSO-NEXT: Name: _ZN3abcC1Ev@@LIBSAMPLE_1.0 -# DSO-NEXT: } -# DSO-NEXT: Symbol { -# DSO-NEXT: Version: 2 -# DSO-NEXT: Name: _ZN3abcC2Ev@@LIBSAMPLE_1.0 -# DSO-NEXT: } -# DSO-NEXT: ] - -.text -.globl _Z3fooi -.type _Z3fooi,@function -_Z3fooi: -retq - -.globl _Z3bari -.type _Z3bari,@function -_Z3bari: -retq - -.globl _Z3zedi -.type _Z3zedi,@function -_Z3zedi: -retq - -.globl _ZN3abcC1Ev -.type _ZN3abcC1Ev,@function -_ZN3abcC1Ev: -retq - -.globl _ZN3abcC2Ev -.type _ZN3abcC2Ev,@function -_ZN3abcC2Ev: -retq diff --git a/lld/test/ELF/version-script-noundef.s b/lld/test/ELF/version-script-noundef.s index 0c48622..18916b6 100644 --- a/lld/test/ELF/version-script-noundef.s +++ b/lld/test/ELF/version-script-noundef.s @@ -18,6 +18,10 @@ # RUN: %t.o -o %t.so 2>&1 | FileCheck -check-prefix=ERR3 %s # ERR3: version script assignment of 'local' to symbol 'und' failed: symbol not defined +## Wildcard patterns do not error. +# RUN: echo "VERSION_1.0 { global: b*; local: u*; };" > %t4.script +# RUN: ld.lld --version-script %t4.script -shared --no-undefined-version --fatal-warnings %t.o -o /dev/null + .text .globl foo .type foo,@function diff --git a/lld/test/ELF/version-script-symver-extern.s b/lld/test/ELF/version-script-symver-extern.s new file mode 100644 index 0000000..bae3336 --- /dev/null +++ b/lld/test/ELF/version-script-symver-extern.s @@ -0,0 +1,45 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o + +# RUN: echo 'v1 { local: extern "C++" { "foo1()"; "foo3(int)"; }; }; v2 { local: extern "C++" { "foo2()"; "foo4(int)"; }; };' > %t1.script +# RUN: ld.lld --version-script %t1.script -shared %t.o -o %t1.so +# RUN: llvm-readelf --dyn-syms %t1.so | FileCheck --check-prefix=EXACT %s +# EXACT: UND +# EXACT-NEXT: [[#]] _start{{$}} +# EXACT-NOT: {{.}} + +# RUN: echo 'v1 { global: *; local: extern "C++" {foo*;}; }; v2 { global: extern "C++" {"foo2()";}; };' > %t2.script +# RUN: ld.lld --version-script %t2.script -shared %t.o -o %t2.so +# RUN: llvm-readelf --dyn-syms %t2.so | FileCheck --check-prefix=MIX1 %s +# MIX1: UND +# MIX1-NEXT: [[#]] _Z4foo2v@@v2 +# MIX1-NEXT: [[#]] _start@@v1 +# MIX1-NEXT: [[#]] _Z4foo4i@@v2 +# MIX1-NOT: {{.}} + +# RUN: echo 'v1 { local: extern "C++" {foo*;}; }; v2 { global: extern "C++" {"foo2()";}; };' > %t3.script +# RUN: ld.lld --version-script %t3.script -shared %t.o -o %t3.so +# RUN: llvm-readelf --dyn-syms %t3.so | FileCheck --check-prefix=MIX2 %s +# MIX2: UND +# MIX2-NEXT: [[#]] _Z4foo2v@@v2 +# MIX2-NEXT: [[#]] _start{{$}} +# MIX2-NEXT: [[#]] _Z4foo4i@@v2 +# MIX2-NOT: {{.}} + +# RUN: echo 'v1 { global: extern "C++" {foo*;}; local: *; }; v2 { global: extern "C++" {"foo4(int)";}; local: *; };' > %t4.script +# RUN: ld.lld --version-script %t4.script -shared %t.o -o %t4.so +# RUN: llvm-readelf --dyn-syms %t4.so | FileCheck --check-prefix=MIX3 %s +# MIX3: UND +# MIX3-NEXT: [[#]] _Z4foo1v@@v1 +# MIX3-NEXT: [[#]] _Z4foo2v@@v1 +# MIX3-NEXT: [[#]] _Z4foo3i@v1 +# MIX3-NEXT: [[#]] _Z4foo4i@@v2 +# MIX3-NOT: {{.}} + +.globl _Z4foo1v; _Z4foo1v: ret +.globl _Z4foo2v; _Z4foo2v: ret +.globl _Z4foo3i; .symver _Z4foo3i,_Z4foo3i@v1,remove; _Z4foo3i: ret +.globl _Z4foo4i; .symver _Z4foo4i,_Z4foo4i@@@v2; _Z4foo4i: ret + +.globl _start; _start: ret diff --git a/lld/test/ELF/version-script-symver.s b/lld/test/ELF/version-script-symver.s index cbf6e14..a3a820f 100644 --- a/lld/test/ELF/version-script-symver.s +++ b/lld/test/ELF/version-script-symver.s @@ -19,7 +19,6 @@ # WC: UND # WC-NEXT: [[#]] foo4@@v2 # WC-NEXT: [[#]] _start{{$}} -# WC-NEXT: [[#]] foo3@v1 # WC-NOT: {{.}} # RUN: echo 'v1 { global: *; local: foo*; }; v2 {};' > %t3.script @@ -28,7 +27,6 @@ # MIX1: UND # MIX1-NEXT: [[#]] foo4@@v2 # MIX1-NEXT: [[#]] _start@@v1 -# MIX1-NEXT: [[#]] foo3@v1 # MIX1-NOT: {{.}} # RUN: echo 'v1 { global: foo*; local: *; }; v2 { global: foo4; local: *; };' > %t4.script -- 2.7.4