Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -1073,7 +1073,7 @@ std::vector readVersionExtern(); void readAnonymousDeclaration(); void readVersionDeclaration(StringRef VerStr); - std::vector readSymbols(); + std::vector readSymbols(bool Locals); void readLocals(); ScriptConfiguration &Opt = *ScriptConfig; @@ -1938,24 +1938,21 @@ // 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()) + if (consumeLabel("local")) { + readLocals(); + } else { + // "global:" is default, so if there's no label, we assume global symbols. + consumeLabel("global"); + for (SymbolVersion V : readSymbols(false)) Config->VersionScriptGlobals.push_back(V); } - readLocals(); + expect("}"); expect(";"); } void ScriptParser::readLocals() { - if (!consume("local")) - return; - expect(":"); - std::vector Locals = readSymbols(); + std::vector Locals = readSymbols(true); for (SymbolVersion V : Locals) { if (V.Name == "*") { Config->DefaultSymbolVersion = VER_NDX_LOCAL; @@ -1972,13 +1969,13 @@ 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(); + if (consumeLabel("local")) { + readLocals(); + } else { + // "global:" is default, so if there's no label, we assume global symbols. + consumeLabel("global"); + Config->VersionDefinitions.back().Globals = readSymbols(false); } - readLocals(); expect("}"); // Each version may have a parent version. For example, "Ver2" @@ -1991,8 +1988,8 @@ expect(";"); } -// Reads a list of symbols for a versions cript. -std::vector ScriptParser::readSymbols() { +// Reads a list of symbols for a versions script. +std::vector ScriptParser::readSymbols(bool Locals) { std::vector Ret; for (;;) { if (consume("extern")) { @@ -2001,8 +1998,16 @@ continue; } - if (peek() == "}" || (peek() == "local" && peek(1) == ":") || Error) + if (peek() == "}" || Error) break; + + if (consumeLabel("local")) { + if (Locals) + setError("nested local label"); + readLocals(); + return Ret; + } + StringRef Tok = next(); Ret.push_back({unquote(Tok), false, hasWildcard(Tok)}); expect(";"); Index: ELF/ScriptLexer.h =================================================================== --- ELF/ScriptLexer.h +++ ELF/ScriptLexer.h @@ -30,10 +30,15 @@ StringRef next(); StringRef peek(unsigned N = 0); void skip(); - bool consume(StringRef Tok); + bool consume(const Twine& 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("\"")) @@ -240,14 +240,23 @@ return Tok; } -bool ScriptLexer::consume(StringRef Tok) { - if (peek() == Tok) { +bool ScriptLexer::consume(const Twine& Tok) { + if (peek() == Tok.str()) { skip(); return true; } return false; } +bool ScriptLexer::consumeLabel(StringRef Tok) { + if (peek() == Tok && peek(1) == ":") { + skip(); + skip(); + return true; + } + return consume(Twine(Tok) + ":"); +} + void ScriptLexer::skip() { (void)next(); } void ScriptLexer::expect(StringRef Expect) { 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: nested local label + # 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 | \