Index: ELF/ScriptParser.cpp =================================================================== --- ELF/ScriptParser.cpp +++ ELF/ScriptParser.cpp @@ -75,7 +75,7 @@ // Unquoted token size_t Pos = S.find_first_not_of( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - "0123456789_.$/\\~=+[]*?-:!<>"); + "0123456789_.$/\\~=+[]*?-:!<>^"); // A character that cannot start a word (which is usually a // punctuation) forms a single character token. if (Pos == 0) Index: ELF/Strings.cpp =================================================================== --- ELF/Strings.cpp +++ ELF/Strings.cpp @@ -13,6 +13,7 @@ #include "llvm/ADT/Twine.h" #include "llvm/Config/config.h" #include +#include #ifdef HAVE_CXXABI_H #include @@ -22,6 +23,40 @@ using namespace lld; using namespace lld::elf; +static char peek(StringRef S) { + if (S.empty()) + return 0; + return S.front(); +} + +static char next(StringRef &S) { + char C = peek(S); + S = S.drop_front(1); + return C; +} + +static bool checkRange(StringRef &S, char Val) { + char L = next(S); + if (L == '^') + return !checkRange(S, Val); + + char R = next(S); + if (R == ']') + return L == Val; + + if (R == '-') { + R = next(S); + bool RangeL = Val >= L && Val <= R; + if (peek(S) == ']') { + next(S); + return RangeL; + } + return checkRange(S, Val) || RangeL; + } + + return false; +} + // Returns true if S matches T. S can contain glob meta-characters. // The asterisk ('*') matches zero or more characters, and the question // mark ('?') matches one character. @@ -29,6 +64,16 @@ for (;;) { if (S.empty()) return T.empty(); + if (S[0] == '[') { + if (T.empty()) + return false; + char Val = T.front(); + T = T.substr(1); + S = S.substr(1); + if (!checkRange(S, Val)) + return false; + continue; + } if (S[0] == '*') { S = S.substr(1); if (S.empty()) Index: ELF/SymbolTable.cpp =================================================================== --- ELF/SymbolTable.cpp +++ ELF/SymbolTable.cpp @@ -577,7 +577,7 @@ } static bool hasWildcard(StringRef S) { - return S.find_first_of("?*") != StringRef::npos; + return S.find_first_of("?*[]-^") != StringRef::npos; } static void setVersionId(SymbolBody *Body, StringRef VersionName, @@ -595,9 +595,19 @@ Sym->VersionId = Version; } +static bool hasExternCpp() { + for (VersionDefinition &V : Config->VersionDefinitions) + for (SymbolVersion Sym : V.Globals) + if (Sym.IsExternCpp) + return true; + return false; +} + template std::map SymbolTable::getDemangledSyms() { std::map Result; + if (!hasExternCpp()) + return Result; for (Symbol *Sym : SymVector) { SymbolBody *B = Sym->body(); Result[demangle(B->getName())] = B; @@ -605,12 +615,24 @@ return Result; } -static bool hasExternCpp() { - for (VersionDefinition &V : Config->VersionDefinitions) - for (SymbolVersion Sym : V.Globals) - if (Sym.IsExternCpp) - return true; - return false; +static SymbolBody *findDemangled(const std::map &D, + StringRef Name) { + auto I = D.find(Name); + if (I != D.end()) + return I->second; + return nullptr; +} + +static std::vector +findAllDemangled(const std::map &D, + StringRef Pattern) { + std::vector Res; + for (auto &P : D) { + SymbolBody *Body = P.second; + if (!Body->isUndefined() && globMatch(Pattern, P.first)) + Res.push_back(Body); + } + return Res; } // This function processes the --version-script option by marking all global @@ -639,16 +661,15 @@ // matching version tag in the file). // Handle exact matches and build a map of demangled externs for // quick search during next step. - std::map Demangled; - if (hasExternCpp()) - Demangled = getDemangledSyms(); + const std::map Demangled = getDemangledSyms(); for (VersionDefinition &V : Config->VersionDefinitions) { for (SymbolVersion Sym : V.Globals) { if (hasWildcard(Sym.Name)) continue; - SymbolBody *B = Sym.IsExternCpp ? Demangled[Sym.Name] : find(Sym.Name); - setVersionId(B, V.Name, Sym.Name, V.Id); + StringRef N = Sym.Name; + SymbolBody *B = Sym.IsExternCpp ? findDemangled(Demangled, N) : find(N); + setVersionId(B, V.Name, N, V.Id); } } @@ -656,10 +677,15 @@ for (size_t I = Config->VersionDefinitions.size() - 1; I != (size_t)-1; --I) { VersionDefinition &V = Config->VersionDefinitions[I]; for (SymbolVersion &Sym : V.Globals) - if (hasWildcard(Sym.Name)) - for (SymbolBody *B : findAll(Sym.Name)) + if (hasWildcard(Sym.Name)) { + std::vector All = + Sym.IsExternCpp ? findAllDemangled(Demangled, Sym.Name) + : findAll(Sym.Name); + + for (SymbolBody *B : All) if (B->symbol()->VersionId == Config->DefaultSymbolVersion) B->symbol()->VersionId = V.Id; + } } } Index: test/ELF/version-script-complex-wildcards.s =================================================================== --- test/ELF/version-script-complex-wildcards.s +++ test/ELF/version-script-complex-wildcards.s @@ -0,0 +1,102 @@ +# REQUIRES: shell + +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o +# RUN: echo "LIBSAMPLE_2.0 { \ +# RUN: global: \ +# RUN: extern "C++" { \ +# RUN: ab[c]*; \ +# RUN: }; \ +# RUN: }; " > %t.script +# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so +# RUN: llvm-readobj -V %t.so | FileCheck %s --check-prefix=ABC +# ABC: Name: _Z3abbi@ +# ABC: Name: _Z3abci@@LIBSAMPLE_2.0 + +# RUN: echo "LIBSAMPLE_2.0 { \ +# RUN: global: \ +# RUN: extern "C++" { \ +# RUN: ab[b]*; \ +# RUN: }; \ +# RUN: }; " > %t1.script +# RUN: ld.lld --version-script %t1.script -shared %t.o -o %t1.so +# RUN: llvm-readobj -V %t1.so | FileCheck %s --check-prefix=ABB +# ABB: Name: _Z3abbi@@LIBSAMPLE_2.0 +# ABB: Name: _Z3abci@ + +# RUN: echo "LIBSAMPLE_2.0 { \ +# RUN: global: \ +# RUN: extern "C++" { \ +# RUN: ab[a-b]*; \ +# RUN: }; \ +# RUN: }; " > %t2.script +# RUN: ld.lld --version-script %t2.script -shared %t.o -o %t2.so +# RUN: llvm-readobj -V %t2.so | FileCheck %s --check-prefix=ABB + +# RUN: echo "LIBSAMPLE_2.0 { \ +# RUN: global: \ +# RUN: extern "C++" { \ +# RUN: ab[a-c]*; \ +# RUN: }; \ +# RUN: }; " > %t3.script +# RUN: ld.lld --version-script %t3.script -shared %t.o -o %t3.so +# RUN: llvm-readobj -V %t3.so | FileCheck %s --check-prefix=ABBABC +# ABBABC: Name: _Z3abbi@@LIBSAMPLE_2.0 +# ABBABC: Name: _Z3abci@@LIBSAMPLE_2.0 + +# RUN: echo "LIBSAMPLE_2.0 { \ +# RUN: global: \ +# RUN: extern "C++" { \ +# RUN: ab[a-bc-d]*; \ +# RUN: }; \ +# RUN: }; " > %t4.script +# RUN: ld.lld --version-script %t4.script -shared %t.o -o %t4.so +# RUN: llvm-readobj -V %t4.so | FileCheck %s --check-prefix=ABBABC + +# RUN: echo "LIBSAMPLE_2.0 { \ +# RUN: global: \ +# RUN: extern "C++" { \ +# RUN: ab[a-bd-e]*; \ +# RUN: }; \ +# RUN: }; " > %t5.script +# RUN: ld.lld --version-script %t5.script -shared %t.o -o %t5.so +# RUN: llvm-readobj -V %t5.so | FileCheck %s --check-prefix=ABB + +# RUN: echo "LIBSAMPLE_2.0 { \ +# RUN: global: \ +# RUN: extern "C++" { \ +# RUN: ab[^a-c]*; \ +# RUN: }; \ +# RUN: }; " > %t6.script +# RUN: ld.lld --version-script %t6.script -shared %t.o -o %t6.so +# RUN: llvm-readobj -V %t6.so | FileCheck %s --check-prefix=NO +# NO: Name: _Z3abbi@ +# NO: Name: _Z3abci@ + +# RUN: echo "LIBSAMPLE_2.0 { \ +# RUN: global: \ +# RUN: extern "C++" { \ +# RUN: ab[^c-z]*; \ +# RUN: }; \ +# RUN: }; " > %t7.script +# RUN: ld.lld --version-script %t7.script -shared %t.o -o %t7.so +# RUN: llvm-readobj -V %t7.so | FileCheck %s --check-prefix=ABB + +# RUN: echo "LIBSAMPLE_2.0 { \ +# RUN: global: \ +# RUN: extern "C++" { \ +# RUN: a[x-za-b][a-c]*; \ +# RUN: }; \ +# RUN: }; " > %t8.script +# RUN: ld.lld --version-script %t8.script -shared %t.o -o %t8.so +# RUN: llvm-readobj -V %t8.so | FileCheck %s --check-prefix=ABBABC + +.text +.globl _Z3abci +.type _Z3abci,@function +_Z3abci: +retq + +.globl _Z3abbi +.type _Z3abbi,@function +_Z3abbi: +retq