Index: ELF/Config.h =================================================================== --- ELF/Config.h +++ ELF/Config.h @@ -35,13 +35,18 @@ enum class UnresolvedPolicy { NoUndef, Error, Warn, Ignore }; +struct VersionSymbol { + llvm::StringRef Name; + bool HasWildcard; +}; + // This struct contains symbols version definition that // can be found in version script if it is used for link. struct Version { Version(llvm::StringRef Name) : Name(Name) {} llvm::StringRef Name; llvm::StringRef Parent; - std::vector Globals; + std::vector Globals; size_t NameOff; // Offset in string table. }; @@ -68,7 +73,7 @@ std::vector DynamicList; std::vector SearchPaths; std::vector Undefined; - std::vector VersionScriptGlobals; + std::vector VersionScriptGlobals; std::vector BuildIdVector; bool AllowMultipleDefinition; bool AsNeeded = false; Index: ELF/SymbolListFile.cpp =================================================================== --- ELF/SymbolListFile.cpp +++ ELF/SymbolListFile.cpp @@ -108,7 +108,7 @@ } void VersionScriptParser::parseVersionSymbols(StringRef Version) { - std::vector *Globals; + std::vector *Globals; if (Version.empty()) Globals = &Config->VersionScriptGlobals; else @@ -121,7 +121,7 @@ if (Cur == "}" || Cur == "local:" || Error) return; next(); - Globals->push_back(Cur); + Globals->push_back({Cur, Cur.find_first_of("?*") != StringRef::npos}); expect(";"); } } Index: ELF/SymbolTable.h =================================================================== --- ELF/SymbolTable.h +++ ELF/SymbolTable.h @@ -88,7 +88,7 @@ void wrap(StringRef Name); private: - std::vector findAll(StringRef Pattern); + std::vector findAll(StringRef Pattern, bool NoWildcard); std::pair insert(StringRef Name); std::pair insert(StringRef Name, uint8_t Type, uint8_t Visibility, bool CanOmitFromDynSym, @@ -97,6 +97,8 @@ std::string conflictMsg(SymbolBody *Existing, InputFile *NewFile); void reportDuplicate(SymbolBody *Existing, InputFile *NewFile); + void applySymbolVersion(VersionSymbol &Sym, size_t Version); + // 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. // The default hashing of StringRef produces different results on 32 and 64 Index: ELF/SymbolTable.cpp =================================================================== --- ELF/SymbolTable.cpp +++ ELF/SymbolTable.cpp @@ -459,10 +459,11 @@ // Returns a list of defined symbols that match with a given glob pattern. template -std::vector SymbolTable::findAll(StringRef Pattern) { +std::vector SymbolTable::findAll(StringRef Pattern, + bool NoWildcard) { // Fast-path. Fallback to find() if Pattern doesn't contain any wildcard // characters. - if (Pattern.find_first_of("?*") == StringRef::npos) { + if (NoWildcard) { if (SymbolBody *B = find(Pattern)) if (!B->isUndefined()) return {B}; @@ -561,6 +562,44 @@ B->symbol()->ExportDynamic = true; } +template +void SymbolTable::applySymbolVersion(VersionSymbol &Sym, size_t Version) { + std::vector Syms = findAll(Sym.Name, !Sym.HasWildcard); + if (Syms.empty()) { + if (Config->NoUndefinedVersion) + error("version script assignment of " + + Config->SymbolVersions[Version].Name + " to symbol " + Sym.Name + + " failed: symbol not defined"); + return; + } + + for (SymbolBody *B : Syms) { + if (B->symbol()->VersionId == VER_NDX_GLOBAL || + B->symbol()->VersionId == VER_NDX_LOCAL) { + B->symbol()->VersionId = Version + 2; + continue; + } + + // It is ok that wildcard matches the symbol that already was assigned + // some version. Just skip it. + if (Sym.HasWildcard) + continue; + + warning("duplicate symbol " + Sym.Name + " in version script"); + } +} + +template struct Reverser { + T &Rev; + Reverser(T &R) : Rev(R) {} + typename T::reverse_iterator begin() { return Rev.rbegin(); } + typename T::reverse_iterator end() { return Rev.rend(); } +}; + +template Reverser Reverse(T &R) { + return Reverser(R); +} + // This function processes the --version-script option by marking all global // symbols with the VersionScriptGlobal flag, which acts as a filter on the // dynamic symbol table. @@ -568,34 +607,28 @@ // If version script does not contain versions declarations, // we just should mark global symbols. if (!Config->VersionScriptGlobals.empty()) { - for (StringRef S : Config->VersionScriptGlobals) - if (SymbolBody *B = find(S)) + for (VersionSymbol &S : Config->VersionScriptGlobals) + if (SymbolBody *B = find(S.Name)) B->symbol()->VersionId = VER_NDX_GLOBAL; return; } // If we have symbols version declarations, we should // assign version references for each symbol. - size_t I = 2; - for (Version &V : Config->SymbolVersions) { - for (StringRef Name : V.Globals) { - std::vector Syms = findAll(Name); - if (Syms.empty()) { - if (Config->NoUndefinedVersion) - error("version script assignment of " + V.Name + " to symbol " + - Name + " failed: symbol not defined"); - continue; - } - - for (SymbolBody *B : Syms) { - if (B->symbol()->VersionId != VER_NDX_GLOBAL && - B->symbol()->VersionId != VER_NDX_LOCAL) - warning("duplicate symbol " + Name + " in version script"); - B->symbol()->VersionId = I; - } - } - ++I; - } + // Current rules are: + // * If there is an exact match for the mangled name, we use it. + // * Otherwise, we look through the wildcard patterns. We look through the + // version tags in reverse order. We use the first match we find (the last + // matching version tag in the file). + for (Version &V : Config->SymbolVersions) + for (VersionSymbol &Sym : V.Globals) + if (!Sym.HasWildcard) + applySymbolVersion(Sym, &V - Config->SymbolVersions.data()); + + for (Version &V : Reverse(Config->SymbolVersions)) + for (VersionSymbol &Sym : V.Globals) + if (Sym.HasWildcard) + applySymbolVersion(Sym, &V - Config->SymbolVersions.data()); } // Print the module names which define the notified Index: test/ELF/version-wildcard.test =================================================================== --- test/ELF/version-wildcard.test +++ test/ELF/version-wildcard.test @@ -46,6 +46,55 @@ # CHECK-NEXT: } # CHECK-NEXT: ] +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: echo "VERSION_1.0{ \ +# RUN: global: foo2; \ +# RUN: local: *; }; \ +# RUN: VERSION_2.0{ \ +# RUN: global: foo*; \ +# RUN: }; " > %t2.script +# RUN: ld.lld --version-script %t2.script -shared %t.o -o %t2.so +# RUN: llvm-readobj -dyn-symbols %t2.so | FileCheck --check-prefix=MIX %s + +# MIX: DynamicSymbols [ +# MIX-NEXT: Symbol { +# MIX-NEXT: Name: @ +# MIX-NEXT: Value: 0x0 +# MIX-NEXT: Size: 0 +# MIX-NEXT: Binding: Local +# MIX-NEXT: Type: None +# MIX-NEXT: Other: 0 +# MIX-NEXT: Section: Undefined +# MIX-NEXT: } +# MIX-NEXT: Symbol { +# MIX-NEXT: Name: foo1@@VERSION_2.0 +# MIX-NEXT: Value: 0x1000 +# MIX-NEXT: Size: 0 +# MIX-NEXT: Binding: Global +# MIX-NEXT: Type: None +# MIX-NEXT: Other: 0 +# MIX-NEXT: Section: .text +# MIX-NEXT: } +# MIX-NEXT: Symbol { +# MIX-NEXT: Name: foo2@@VERSION_1.0 +# MIX-NEXT: Value: 0x1001 +# MIX-NEXT: Size: 0 +# MIX-NEXT: Binding: Global +# MIX-NEXT: Type: None +# MIX-NEXT: Other: 0 +# MIX-NEXT: Section: .text +# MIX-NEXT: } +# MIX-NEXT: Symbol { +# MIX-NEXT: Name: foo3@@VERSION_2.0 +# MIX-NEXT: Value: 0x1007 +# MIX-NEXT: Size: 0 +# MIX-NEXT: Binding: Global +# MIX-NEXT: Type: None +# MIX-NEXT: Other: 0 +# MIX-NEXT: Section: .text +# MIX-NEXT: } +# MIX-NEXT: ] + .globl foo1 foo1: ret