Index: lld/trunk/ELF/Config.h =================================================================== --- lld/trunk/ELF/Config.h +++ lld/trunk/ELF/Config.h @@ -62,6 +62,7 @@ llvm::StringRef Name; size_t Id; std::vector Globals; + std::vector Locals; size_t NameOff; // Offset in string table. }; Index: lld/trunk/ELF/LinkerScript.cpp =================================================================== --- lld/trunk/ELF/LinkerScript.cpp +++ lld/trunk/ELF/LinkerScript.cpp @@ -968,7 +968,7 @@ void readExtern(std::vector *Globals); void readVersionDeclaration(StringRef VerStr); void readGlobal(StringRef VerStr); - void readLocal(); + void readLocal(StringRef VerStr); ScriptConfiguration &Opt = *ScriptConfig; bool IsUnderSysroot; @@ -1778,7 +1778,7 @@ if (consume("global:") || peek() != "local:") readGlobal(VerStr); if (consume("local:")) - readLocal(); + readLocal(VerStr); expect("}"); // Each version may have a parent version. For example, "Ver2" defined as @@ -1790,10 +1790,22 @@ expect(";"); } -void ScriptParser::readLocal() { - Config->DefaultSymbolVersion = VER_NDX_LOCAL; - expect("*"); - expect(";"); +void ScriptParser::readLocal(StringRef VerStr) { + if (consume("*")) { + Config->DefaultSymbolVersion = VER_NDX_LOCAL; + expect(";"); + return; + } + + if (VerStr.empty()) + setError("locals list for anonymous version is not supported"); + + std::vector &Locals = Config->VersionDefinitions.back().Locals; + while (!Error && peek() != "}") { + StringRef Tok = next(); + Locals.push_back({unquote(Tok), false, hasWildcard(Tok)}); + expect(";"); + } } void ScriptParser::readExtern(std::vector *Globals) { Index: lld/trunk/ELF/SymbolTable.cpp =================================================================== --- lld/trunk/ELF/SymbolTable.cpp +++ lld/trunk/ELF/SymbolTable.cpp @@ -700,28 +700,37 @@ } setVersionId(find(N), V.Name, N, V.Id); } + for (SymbolVersion Sym : V.Locals) { + if (Sym.HasWildcards) + continue; + setVersionId(find(Sym.Name), V.Name, Sym.Name, VER_NDX_LOCAL); + } } // Next, we assign versions to fuzzy matching symbols, // i.e. version definitions containing glob meta-characters. // Note that because the last match takes precedence over previous matches, // we iterate over the definitions in the reverse order. + auto assignFuzzyVersion = [&](SymbolVersion &Sym, size_t Version) { + if (!Sym.HasWildcards) + return; + StringMatcher M({Sym.Name}); + std::vector Syms = + Sym.IsExternCpp ? findAllDemangled(Demangled, M) : findAll(M); + // Exact matching takes precendence 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 (SymbolBody *B : Syms) + if (B->symbol()->VersionId == Config->DefaultSymbolVersion) + B->symbol()->VersionId = Version; + }; + for (size_t I = Config->VersionDefinitions.size() - 1; I != (size_t)-1; --I) { VersionDefinition &V = Config->VersionDefinitions[I]; - for (SymbolVersion &Sym : V.Globals) { - if (!Sym.HasWildcards) - continue; - StringMatcher M({Sym.Name}); - std::vector Syms = - Sym.IsExternCpp ? findAllDemangled(Demangled, M) : findAll(M); - - // Exact matching takes precendence 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 (SymbolBody *B : Syms) - if (B->symbol()->VersionId == Config->DefaultSymbolVersion) - B->symbol()->VersionId = V.Id; - } + for (SymbolVersion &Sym : V.Locals) + assignFuzzyVersion(Sym, VER_NDX_LOCAL); + for (SymbolVersion &Sym : V.Globals) + assignFuzzyVersion(Sym, V.Id); } } Index: lld/trunk/test/ELF/version-script-locals.s =================================================================== --- lld/trunk/test/ELF/version-script-locals.s +++ lld/trunk/test/ELF/version-script-locals.s @@ -0,0 +1,50 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o + +# RUN: echo "VERSION_1.0 { local: foo1; };" > %t.script +# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so +# RUN: llvm-readobj -dyn-symbols %t.so | FileCheck --check-prefix=EXACT %s +# EXACT: DynamicSymbols [ +# EXACT: _start +# EXACT-NOT: foo1 +# EXACT: foo2 +# EXACT: foo3 + +# RUN: echo "VERSION_1.0 { local: foo*; };" > %t.script +# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so +# RUN: llvm-readobj -dyn-symbols %t.so | FileCheck --check-prefix=WC %s +# WC: DynamicSymbols [ +# WC: _start +# WC-NOT: foo1 +# WC-NOT: foo2 +# WC-NOT: foo3 + +# RUN: echo "VERSION_1.0 { global: *; local: foo*; };" > %t.script +# RUN: ld.lld --version-script %t.script -shared %t.o -o %t.so +# RUN: llvm-readobj -dyn-symbols %t.so | FileCheck --check-prefix=MIX %s +# MIX: DynamicSymbols [ +# MIX: _start@@VERSION_1.0 +# MIX-NOT: foo1 +# MIX-NOT: foo2 +# MIX-NOT: foo3 + +# RUN: echo "VERSION_1.0 { global: *; local: extern \"C++\" { foo*; } };" > %t.script +# RUN: not ld.lld --version-script %t.script -shared %t.o -o %t.so 2>&1 \ +# RUN: | FileCheck --check-prefix=EXTERNERR %s +# EXTERNERR: ; expected, but got "C++" + +.globl foo1 +foo1: + ret + +.globl foo2 +foo2: + ret + +.globl foo3 +foo3: + ret + +.globl _start +_start: + ret