Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -1073,8 +1073,8 @@ std::vector readVersionExtern(); void readAnonymousDeclaration(); void readVersionDeclaration(StringRef VerStr); - std::vector readSymbols(); - void readLocals(); + void readLocalsAndGlobals(std::vector &Globals); + void readSymbols(std::vector &Out, bool IsGlobals); ScriptConfiguration &Opt = *ScriptConfig; bool IsUnderSysroot; @@ -1937,32 +1937,24 @@ } // Reads a list of symbols, e.g. "{ global: foo; bar; local: *; };". -void ScriptParser::readAnonymousDeclaration() { - // Read global symbols first. "global:" is default, so if there's - // no label, we assume global symbols. - if (peek() != "local") { - if (consume("global")) - expect(":"); - for (SymbolVersion V : readSymbols()) - Config->VersionScriptGlobals.push_back(V); +void ScriptParser::readLocalsAndGlobals(std::vector &Globals) { + if (consumeLabel("local:")) { + readSymbols(Config->VersionScriptLocals, false /*IsGlobals*/); + } else { + consumeLabel("global:"); + readSymbols(Globals, true /*IsGlobals*/); } - readLocals(); expect("}"); - expect(";"); + auto I = llvm::find_if(Config->VersionScriptLocals, + [](SymbolVersion &Ver) { return Ver.Name == "*"; }); + if (I != Config->VersionScriptLocals.end()) + Config->DefaultSymbolVersion = VER_NDX_LOCAL; } -void ScriptParser::readLocals() { - if (!consume("local")) - return; - expect(":"); - std::vector Locals = readSymbols(); - for (SymbolVersion V : Locals) { - if (V.Name == "*") { - Config->DefaultSymbolVersion = VER_NDX_LOCAL; - continue; - } - Config->VersionScriptLocals.push_back(V); - } +// Reads a list of symbols, e.g. "{ global: foo; bar; local: *; };". +void ScriptParser::readAnonymousDeclaration() { + readLocalsAndGlobals(Config->VersionScriptGlobals); + expect(";"); } // Reads a list of symbols, e.g. "VerStr { global: foo; bar; local: *; };". @@ -1971,15 +1963,7 @@ // for VER_NDX_LOCAL and VER_NDX_GLOBAL constants. uint16_t VersionId = Config->VersionDefinitions.size() + 2; Config->VersionDefinitions.push_back({VerStr, VersionId}); - - // Read global symbols. - if (peek() != "local") { - if (consume("global")) - expect(":"); - Config->VersionDefinitions.back().Globals = readSymbols(); - } - readLocals(); - expect("}"); + readLocalsAndGlobals(Config->VersionDefinitions.back().Globals); // Each version may have a parent version. For example, "Ver2" // defined as "Ver2 { global: foo; local: *; } Ver1;" has "Ver1" @@ -1991,23 +1975,26 @@ expect(";"); } -// Reads a list of symbols for a versions cript. -std::vector ScriptParser::readSymbols() { - std::vector Ret; +void ScriptParser::readSymbols(std::vector &Out, bool IsGlobals) { for (;;) { if (consume("extern")) { for (SymbolVersion V : readVersionExtern()) - Ret.push_back(V); + Out.push_back(V); continue; } - if (peek() == "}" || (peek() == "local" && peek(1) == ":") || Error) + if (peek() == "}" || Error) break; + + if (IsGlobals && consumeLabel("local:")) { + readSymbols(Config->VersionScriptLocals, false /*IsGlobals*/); + return; + } + StringRef Tok = next(); - Ret.push_back({unquote(Tok), false, hasWildcard(Tok)}); + Out.push_back({unquote(Tok), false, hasWildcard(Tok)}); expect(";"); } - return Ret; } // Reads an "extern C++" directive, e.g., Index: ELF/ScriptLexer.h =================================================================== --- ELF/ScriptLexer.h +++ ELF/ScriptLexer.h @@ -29,11 +29,16 @@ bool atEOF(); StringRef next(); StringRef peek(unsigned N = 0); - void skip(); + void skip(unsigned N = 1); bool consume(StringRef Tok); void expect(StringRef Expect); std::string getCurrentLocation(); + // This is the only method that use LL(2) grammar. + // Required to parse labels that may consist of + // one or two tokens, ex: "local :" vs "local:". + bool consumeLabel(StringRef Tok); + std::vector MBs; std::vector Tokens; bool InExpr = false; Index: ELF/ScriptLexer.cpp =================================================================== --- ELF/ScriptLexer.cpp +++ ELF/ScriptLexer.cpp @@ -124,7 +124,7 @@ // so that you can write "file-name.cpp" as one bare token, for example. 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. @@ -169,7 +169,7 @@ // Split a given string as an expression. // This function returns "3", "*" and "5" for "3*5" for example. static std::vector tokenizeExpr(StringRef S) { - StringRef Ops = "+-*/"; // List of operators + StringRef Ops = "+-*/:"; // List of operators // Quoted strings are literal strings, so we don't want to split it. if (S.startswith("\"")) @@ -248,7 +248,19 @@ return false; } -void ScriptLexer::skip() { (void)next(); } +bool ScriptLexer::consumeLabel(StringRef Tok) { + if (peek() == Tok || + (peek() == Tok.drop_back(1) && peek(1) == Tok.take_back(1))) { + skip(peek() == Tok ? 1 : 2); + return true; + } + return false; +} + +void ScriptLexer::skip(unsigned N) { + while (N--) + (void)next(); +} void ScriptLexer::expect(StringRef Expect) { if (Error) Index: test/ELF/version-script-extern-wildcards-anon.s =================================================================== --- test/ELF/version-script-extern-wildcards-anon.s +++ test/ELF/version-script-extern-wildcards-anon.s @@ -7,6 +7,7 @@ # RUN: extern "C++" { \ # RUN: "foo(int)"; \ # RUN: z*; \ +# RUN: std::q*; \ # RUN: }; \ # RUN: local: *; \ # RUN: }; ' > %t.script @@ -50,6 +51,15 @@ # CHECK-NEXT: Other: # CHECK-NEXT: Section: # CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: _ZSt3qux +# CHECK-NEXT: Value: +# CHECK-NEXT: Size: +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: +# CHECK-NEXT: Other: +# CHECK-NEXT: Section: +# CHECK-NEXT: } # CHECK-NEXT: ] .global _Z3fooi @@ -60,3 +70,5 @@ _Z3zedi: .global _Z3bazi _Z3bazi: +.global _ZSt3qux +_ZSt3qux: Index: test/ELF/version-script.s =================================================================== --- test/ELF/version-script.s +++ test/ELF/version-script.s @@ -32,6 +32,11 @@ # RUN: FileCheck -check-prefix=ERR2 %s # ERR2: EOF expected, but got VERSION_2.0 +# RUN: echo "VERSION_1.0 { global: foo1; local: local: *; *; };" > %t7.script +# RUN: not ld.lld --version-script %t7.script -shared %t.o %t2.so -o %t6.so 2>&1 | \ +# RUN: FileCheck -check-prefix=ERR3 %s +# ERR3: ; expected, but got * + # RUN: echo "VERSION_1.0 { global: foo1; local: *; };" > %t6.script # RUN: echo "VERSION_2.0 { global: foo1; local: *; };" >> %t6.script # RUN: ld.lld --version-script %t6.script -shared %t.o %t2.so -o %t6.so 2>&1 | \