Index: lld/trunk/ELF/Config.h =================================================================== --- lld/trunk/ELF/Config.h +++ lld/trunk/ELF/Config.h @@ -90,7 +90,6 @@ bool FatalWarnings; bool GcSections; bool GnuHash = false; - bool HasVersionScript = false; bool ICF; bool Mips64EL = false; bool NoGnuUnique; Index: lld/trunk/ELF/Driver.cpp =================================================================== --- lld/trunk/ELF/Driver.cpp +++ lld/trunk/ELF/Driver.cpp @@ -443,11 +443,9 @@ for (auto *Arg : Args.filtered(OPT_export_dynamic_symbol)) Config->DynamicList.push_back(Arg->getValue()); - if (auto *Arg = Args.getLastArg(OPT_version_script)) { - Config->HasVersionScript = true; + if (auto *Arg = Args.getLastArg(OPT_version_script)) if (Optional Buffer = readFile(Arg->getValue())) parseVersionScript(*Buffer); - } for (auto *Arg : Args.filtered(OPT_trace_symbol)) Config->TraceSymbol.insert(Arg->getValue()); @@ -557,6 +555,7 @@ Symtab.scanShlibUndefined(); Symtab.scanDynamicList(); Symtab.scanVersionScript(); + Symtab.scanSymbolVersions(); Symtab.traceDefined(); Symtab.addCombinedLtoObject(); Index: lld/trunk/ELF/SymbolTable.h =================================================================== --- lld/trunk/ELF/SymbolTable.h +++ lld/trunk/ELF/SymbolTable.h @@ -82,6 +82,7 @@ void scanShlibUndefined(); void scanDynamicList(); void scanVersionScript(); + void scanSymbolVersions(); void traceDefined(); SymbolBody *find(StringRef Name); Index: lld/trunk/ELF/SymbolTable.cpp =================================================================== --- lld/trunk/ELF/SymbolTable.cpp +++ lld/trunk/ELF/SymbolTable.cpp @@ -165,39 +165,6 @@ return std::min(VA, VB); } -// A symbol version may be included in a symbol name as a suffix after '@'. -// This function parses that part and returns a version ID number. -static uint16_t getVersionId(Symbol *Sym, StringRef Name) { - size_t VersionBegin = Name.find('@'); - if (VersionBegin == StringRef::npos) - return Config->DefaultSymbolVersion; - - // If symbol name contains '@' or '@@' we can assign its version id right - // here. '@@' means the default version. It is usually the most recent one. - // VERSYM_HIDDEN flag should be set for all non-default versions. - StringRef Version = Name.drop_front(VersionBegin + 1); - bool Default = Version.startswith("@"); - if (Default) - Version = Version.drop_front(); - - for (VersionDefinition &V : Config->VersionDefinitions) - if (V.Name == Version) - return Default ? V.Id : (V.Id | VERSYM_HIDDEN); - - - // If we are not building shared and version script - // is not specified, then it is not a error, it is - // in common not to use script for linking executables. - // In this case we just create new version. - if (!Config->Shared && !Config->HasVersionScript) { - size_t Id = defineSymbolVersion(Version); - return Default ? Id : (Id | VERSYM_HIDDEN); - } - - error("symbol " + Name + " has undefined version " + Version); - return 0; -} - // Find an existing symbol or create and insert a new one. template std::pair SymbolTable::insert(StringRef Name) { @@ -210,9 +177,7 @@ Sym->Visibility = STV_DEFAULT; Sym->IsUsedInRegularObj = false; Sym->ExportDynamic = false; - Sym->VersionId = getVersionId(Sym, Name); - Sym->VersionedName = - Sym->VersionId != VER_NDX_LOCAL && Sym->VersionId != VER_NDX_GLOBAL; + Sym->VersionId = Config->DefaultSymbolVersion; SymVector.push_back(Sym); } else { Sym = SymVector[P.first->second]; @@ -646,6 +611,84 @@ } } +// Returns the size of the longest version name. +static int getMaxVersionLen() { + size_t Len = 0; + for (VersionDefinition &V : Config->VersionDefinitions) + Len = std::max(Len, V.Name.size()); + return Len; +} + +// Parses a symbol name in the form of @ or @@. +static std::pair +getSymbolVersion(SymbolBody *B, int MaxVersionLen) { + StringRef S = B->getName(); + + // MaxVersionLen was passed so that we don't need to scan + // all characters in a symbol name. It is effective because + // versions are usually short and symbol names can be very long. + size_t Pos = S.find('@', std::max(0, int(S.size()) - MaxVersionLen - 2)); + if (Pos == 0 || Pos == StringRef::npos) + return {"", 0}; + + StringRef Name = S.substr(0, Pos); + StringRef Verstr = S.substr(Pos + 1); + if (Verstr.empty()) + return {"", 0}; + + // '@@' in a symbol name means the default version. + // It is usually the most recent one. + bool IsDefault = (Verstr[0] == '@'); + if (IsDefault) + Verstr = Verstr.substr(1); + + for (VersionDefinition &V : Config->VersionDefinitions) { + if (V.Name == Verstr) + return {Name, IsDefault ? V.Id : (V.Id | VERSYM_HIDDEN)}; + } + + // It is an error if the specified version was not defined. + error("symbol " + S + " has undefined version " + Verstr); + return {"", 0}; +} + +// Versions are usually assigned to symbols using version scripts, +// but there's another way to assign versions to symbols. +// If a symbol name contains '@', the string after it is not +// actually a part of the symbol name but specifies a version. +// This function takes care of it. +template void SymbolTable::scanSymbolVersions() { + if (Config->VersionDefinitions.empty()) + return; + + int MaxVersionLen = getMaxVersionLen(); + + // Unfortunately there's no way other than iterating over all + // symbols to look for '@' characters in symbol names. + // So this is inherently slow. A good news is that we do this + // only when versions have been defined. + for (Symbol *Sym : SymVector) { + // Symbol versions for exported symbols are by nature + // only for defined global symbols. + SymbolBody *B = Sym->body(); + if (!B->isDefined()) + continue; + uint8_t Visibility = B->getVisibility(); + if (Visibility != STV_DEFAULT && Visibility != STV_PROTECTED) + continue; + + // Look for '@' in the symbol name. + StringRef Name; + uint16_t Version; + std::tie(Name, Version) = getSymbolVersion(B, MaxVersionLen); + if (Name.empty()) + continue; + + B->setName(Name); + Sym->VersionId = Version; + } +} + // Print the module names which define the notified // symbols provided through -y or --trace-symbol option. template void SymbolTable::traceDefined() { Index: lld/trunk/ELF/Symbols.h =================================================================== --- lld/trunk/ELF/Symbols.h +++ lld/trunk/ELF/Symbols.h @@ -70,8 +70,9 @@ bool isLocal() const { return IsLocal; } bool isPreemptible() const; - // Returns the symbol name. StringRef getName() const; + void setName(StringRef S); + uint32_t getNameOffset() const { assert(isLocal()); return NameOffset; @@ -419,11 +420,6 @@ // --export-dynamic, and by dynamic lists. unsigned ExportDynamic : 1; - // If this flag is true then symbol name also contains version name. Such - // name can have single or double symbol @. Double means that version is - // used as default. Single signals about depricated symbol version. - unsigned VersionedName : 1; - bool includeInDynsym() const; bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; } Index: lld/trunk/ELF/Symbols.cpp =================================================================== --- lld/trunk/ELF/Symbols.cpp +++ lld/trunk/ELF/Symbols.cpp @@ -98,10 +98,12 @@ StringRef SymbolBody::getName() const { assert(!isLocal()); - StringRef S = StringRef(Name.S, Name.Len); - if (!symbol()->VersionedName) - return S; - return S.substr(0, S.find('@')); + return StringRef(Name.S, Name.Len); +} + +void SymbolBody::setName(StringRef S) { + Name.S = S.data(); + Name.Len = S.size(); } // Returns true if a symbol can be replaced at load-time by a symbol Index: lld/trunk/test/ELF/verdef-defaultver.s =================================================================== --- lld/trunk/test/ELF/verdef-defaultver.s +++ lld/trunk/test/ELF/verdef-defaultver.s @@ -198,10 +198,6 @@ # EXE-NEXT: } # EXE-NEXT: } -# RUN: not ld.lld -shared %t1 -o %terr.so 2>&1 | \ -# RUN: FileCheck -check-prefix=ERR1 %s -# ERR1: symbol b@@LIBSAMPLE_2.0 has undefined version LIBSAMPLE_2.0 - .globl _start _start: callq a Index: lld/trunk/test/ELF/verdef-executable.s =================================================================== --- lld/trunk/test/ELF/verdef-executable.s +++ lld/trunk/test/ELF/verdef-executable.s @@ -1,269 +0,0 @@ -# REQUIRES: x86 - -# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1 -# RUN: ld.lld %t1 -o %t -# RUN: llvm-readobj -t -V -dyn-symbols %t | FileCheck --check-prefix=EXE %s - -# EXE: Symbols [ -# EXE-NEXT: Symbol { -# EXE-NEXT: Name: -# EXE-NEXT: Value: 0x0 -# EXE-NEXT: Size: 0 -# EXE-NEXT: Binding: Local -# EXE-NEXT: Type: None (0x0) -# EXE-NEXT: Other: 0 -# EXE-NEXT: Section: Undefined (0x0) -# EXE-NEXT: } -# EXE-NEXT: Symbol { -# EXE-NEXT: Name: _start -# EXE-NEXT: Value: 0x11004 -# EXE-NEXT: Size: 0 -# EXE-NEXT: Binding: Global -# EXE-NEXT: Type: None -# EXE-NEXT: Other: 0 -# EXE-NEXT: Section: .text -# EXE-NEXT: } -# EXE-NEXT: Symbol { -# EXE-NEXT: Name: a -# EXE-NEXT: Value: 0x11000 -# EXE-NEXT: Size: 0 -# EXE-NEXT: Binding: Global -# EXE-NEXT: Type: Function -# EXE-NEXT: Other: 0 -# EXE-NEXT: Section: .text -# EXE-NEXT: } -# EXE-NEXT: Symbol { -# EXE-NEXT: Name: b -# EXE-NEXT: Value: 0x11002 -# EXE-NEXT: Size: 0 -# EXE-NEXT: Binding: Global -# EXE-NEXT: Type: Function -# EXE-NEXT: Other: 0 -# EXE-NEXT: Section: .text -# EXE-NEXT: } -# EXE-NEXT: Symbol { -# EXE-NEXT: Name: b -# EXE-NEXT: Value: 0x11001 -# EXE-NEXT: Size: 0 -# EXE-NEXT: Binding: Global -# EXE-NEXT: Type: Function -# EXE-NEXT: Other: 0 -# EXE-NEXT: Section: .text -# EXE-NEXT: } -# EXE-NEXT: Symbol { -# EXE-NEXT: Name: b_1 -# EXE-NEXT: Value: 0x11001 -# EXE-NEXT: Size: 0 -# EXE-NEXT: Binding: Global -# EXE-NEXT: Type: Function -# EXE-NEXT: Other: 0 -# EXE-NEXT: Section: .text -# EXE-NEXT: } -# EXE-NEXT: Symbol { -# EXE-NEXT: Name: b_2 -# EXE-NEXT: Value: 0x11002 -# EXE-NEXT: Size: 0 -# EXE-NEXT: Binding: Global -# EXE-NEXT: Type: Function -# EXE-NEXT: Other: 0 -# EXE-NEXT: Section: .text -# EXE-NEXT: } -# EXE-NEXT: Symbol { -# EXE-NEXT: Name: c -# EXE-NEXT: Value: 0x11003 -# EXE-NEXT: Size: 0 -# EXE-NEXT: Binding: Global -# EXE-NEXT: Type: Function -# EXE-NEXT: Other: 0 -# EXE-NEXT: Section: .text -# EXE-NEXT: } -# EXE-NEXT: ] -# EXE-NEXT: DynamicSymbols [ -# EXE-NEXT: ] -# EXE-NEXT: Version symbols { -# EXE-NEXT: } -# EXE-NEXT: SHT_GNU_verdef { -# EXE-NEXT: } -# EXE-NEXT: SHT_GNU_verneed { -# EXE-NEXT: } - -# RUN: ld.lld -pie --export-dynamic %t1 -o %t2 -# RUN: llvm-readobj -t -V -dyn-symbols %t2 | \ -# RUN: FileCheck --check-prefix=EXEDYN %s - -# EXEDYN: DynamicSymbols [ -# EXEDYN-NEXT: Symbol { -# EXEDYN-NEXT: Name: @ -# EXEDYN-NEXT: Value: 0x0 -# EXEDYN-NEXT: Size: 0 -# EXEDYN-NEXT: Binding: Local -# EXEDYN-NEXT: Type: None -# EXEDYN-NEXT: Other: 0 -# EXEDYN-NEXT: Section: Undefined -# EXEDYN-NEXT: } -# EXEDYN-NEXT: Symbol { -# EXEDYN-NEXT: Name: _start@ -# EXEDYN-NEXT: Value: 0x1004 -# EXEDYN-NEXT: Size: 0 -# EXEDYN-NEXT: Binding: Global -# EXEDYN-NEXT: Type: None -# EXEDYN-NEXT: Other: 0 -# EXEDYN-NEXT: Section: .text -# EXEDYN-NEXT: } -# EXEDYN-NEXT: Symbol { -# EXEDYN-NEXT: Name: a@ -# EXEDYN-NEXT: Value: 0x1000 -# EXEDYN-NEXT: Size: 0 -# EXEDYN-NEXT: Binding: Global -# EXEDYN-NEXT: Type: Function -# EXEDYN-NEXT: Other: 0 -# EXEDYN-NEXT: Section: .text -# EXEDYN-NEXT: } -# EXEDYN-NEXT: Symbol { -# EXEDYN-NEXT: Name: b@@LIBSAMPLE_2.0 -# EXEDYN-NEXT: Value: 0x1002 -# EXEDYN-NEXT: Size: 0 -# EXEDYN-NEXT: Binding: Global -# EXEDYN-NEXT: Type: Function -# EXEDYN-NEXT: Other: 0 -# EXEDYN-NEXT: Section: .text -# EXEDYN-NEXT: } -# EXEDYN-NEXT: Symbol { -# EXEDYN-NEXT: Name: b@LIBSAMPLE_1.0 -# EXEDYN-NEXT: Value: 0x1001 -# EXEDYN-NEXT: Size: 0 -# EXEDYN-NEXT: Binding: Global -# EXEDYN-NEXT: Type: Function -# EXEDYN-NEXT: Other: 0 -# EXEDYN-NEXT: Section: .text -# EXEDYN-NEXT: } -# EXEDYN-NEXT: Symbol { -# EXEDYN-NEXT: Name: b_1@ -# EXEDYN-NEXT: Value: 0x1001 -# EXEDYN-NEXT: Size: 0 -# EXEDYN-NEXT: Binding: Global -# EXEDYN-NEXT: Type: Function -# EXEDYN-NEXT: Other: 0 -# EXEDYN-NEXT: Section: .text -# EXEDYN-NEXT: } -# EXEDYN-NEXT: Symbol { -# EXEDYN-NEXT: Name: b_2@ -# EXEDYN-NEXT: Value: 0x1002 -# EXEDYN-NEXT: Size: 0 -# EXEDYN-NEXT: Binding: Global -# EXEDYN-NEXT: Type: Function -# EXEDYN-NEXT: Other: 0 -# EXEDYN-NEXT: Section: .text -# EXEDYN-NEXT: } -# EXEDYN-NEXT: Symbol { -# EXEDYN-NEXT: Name: c@ -# EXEDYN-NEXT: Value: 0x1003 -# EXEDYN-NEXT: Size: 0 -# EXEDYN-NEXT: Binding: Global -# EXEDYN-NEXT: Type: Function -# EXEDYN-NEXT: Other: 0 -# EXEDYN-NEXT: Section: .text -# EXEDYN-NEXT: } -# EXEDYN-NEXT: ] -# EXEDYN-NEXT: Version symbols { -# EXEDYN-NEXT: Section Name: .gnu.version -# EXEDYN-NEXT: Address: 0x288 -# EXEDYN-NEXT: Offset: 0x288 -# EXEDYN-NEXT: Link: 1 -# EXEDYN-NEXT: Symbols [ -# EXEDYN-NEXT: Symbol { -# EXEDYN-NEXT: Version: 0 -# EXEDYN-NEXT: Name: @ -# EXEDYN-NEXT: } -# EXEDYN-NEXT: Symbol { -# EXEDYN-NEXT: Version: 1 -# EXEDYN-NEXT: Name: _start@ -# EXEDYN-NEXT: } -# EXEDYN-NEXT: Symbol { -# EXEDYN-NEXT: Version: 1 -# EXEDYN-NEXT: Name: a@ -# EXEDYN-NEXT: } -# EXEDYN-NEXT: Symbol { -# EXEDYN-NEXT: Version: 2 -# EXEDYN-NEXT: Name: b@@LIBSAMPLE_2.0 -# EXEDYN-NEXT: } -# EXEDYN-NEXT: Symbol { -# EXEDYN-NEXT: Version: 3 -# EXEDYN-NEXT: Name: b@LIBSAMPLE_1.0 -# EXEDYN-NEXT: } -# EXEDYN-NEXT: Symbol { -# EXEDYN-NEXT: Version: 1 -# EXEDYN-NEXT: Name: b_1@ -# EXEDYN-NEXT: } -# EXEDYN-NEXT: Symbol { -# EXEDYN-NEXT: Version: 1 -# EXEDYN-NEXT: Name: b_2@ -# EXEDYN-NEXT: } -# EXEDYN-NEXT: Symbol { -# EXEDYN-NEXT: Version: 1 -# EXEDYN-NEXT: Name: c@ -# EXEDYN-NEXT: } -# EXEDYN-NEXT: ] -# EXEDYN-NEXT: } -# EXEDYN-NEXT: SHT_GNU_verdef { -# EXEDYN-NEXT: Definition { -# EXEDYN-NEXT: Version: 1 -# EXEDYN-NEXT: Flags: Base -# EXEDYN-NEXT: Index: 1 -# EXEDYN-NEXT: Hash: -# EXEDYN-NEXT: Name: -# EXEDYN-NEXT: } -# EXEDYN-NEXT: Definition { -# EXEDYN-NEXT: Version: 1 -# EXEDYN-NEXT: Flags: 0x0 -# EXEDYN-NEXT: Index: 2 -# EXEDYN-NEXT: Hash: 98456416 -# EXEDYN-NEXT: Name: LIBSAMPLE_2.0 -# EXEDYN-NEXT: } -# EXEDYN-NEXT: Definition { -# EXEDYN-NEXT: Version: 1 -# EXEDYN-NEXT: Flags: 0x0 -# EXEDYN-NEXT: Index: 3 -# EXEDYN-NEXT: Hash: 98457184 -# EXEDYN-NEXT: Name: LIBSAMPLE_1.0 -# EXEDYN-NEXT: } -# EXEDYN-NEXT: } -# EXEDYN-NEXT: SHT_GNU_verneed { -# EXEDYN-NEXT: } - -## Check when linking executable that error produced if version script -## was specified and there are symbols with version in name that -## is absent in script. -# RUN: echo "VERSION { \ -# RUN: global: foo; \ -# RUN: local: *; }; " > %t.script -# RUN: not ld.lld --version-script %t.script %t1 -o %t3 2>&1 | \ -# RUN: FileCheck -check-prefix=ERR %s -# ERR: symbol b@LIBSAMPLE_1.0 has undefined version LIBSAMPLE_1.0 - -b@LIBSAMPLE_1.0 = b_1 -b@@LIBSAMPLE_2.0 = b_2 - -.globl a -.type a,@function -a: -retq - -.globl b_1 -.type b_1,@function -b_1: -retq - -.globl b_2 -.type b_2,@function -b_2: -retq - -.globl c -.type c,@function -c: -retq - -.globl _start -_start: - nop