Index: lld/ELF/SymbolTable.h =================================================================== --- lld/ELF/SymbolTable.h +++ lld/ELF/SymbolTable.h @@ -92,6 +92,7 @@ private: std::vector findByVersion(SymbolVersion Ver); std::vector findAllByVersion(SymbolVersion Ver); + void scanSymbolVersions(); llvm::StringMap> &getDemangledSyms(); void handleAnonymousVersion(); Index: lld/ELF/SymbolTable.cpp =================================================================== --- lld/ELF/SymbolTable.cpp +++ lld/ELF/SymbolTable.cpp @@ -29,6 +29,10 @@ using namespace lld; using namespace lld::elf; +static void copy(Symbol *A, Symbol *B) { + memcpy(A->Body.buffer, B->Body.buffer, sizeof(Symbol::Body)); +} + // All input object files must be for the same architecture // (e.g. it does not make sense to link x86 object files with // MIPS object files.) This function checks for that error. @@ -200,7 +204,7 @@ // We rename symbols by replacing the old symbol's SymbolBody with the new // symbol's SymbolBody. This causes all SymbolBody pointers referring to the // old symbol to instead refer to the new symbol. - memcpy(Sym->Body.buffer, Rename->Body.buffer, sizeof(Sym->Body)); + copy(Sym, Rename); } } @@ -718,12 +722,8 @@ // This function processes version scripts by updating VersionId // member of symbols. template void SymbolTable::scanVersionScript() { - // Symbol themselves might know their versions because symbols - // can contain versions in the form of @. - // Let them parse their names. if (!Config->VersionDefinitions.empty()) - for (Symbol *Sym : SymVector) - Sym->body()->parseSymbolVersion(); + scanSymbolVersions(); // Handle edge cases first. handleAnonymousVersion(); @@ -750,6 +750,68 @@ assignWildcardVersion(Ver, V.Id); } +// Parse a symbol name as a versioned symbol. It recognizes +// @ or @@. +static void parseSymbolVersion(const SymbolBody &B, uint16_t &Version, + bool &IsDefault, StringRef &NewName) { + StringRef S = B.getName(); + size_t Pos = S.find('@'); + if (Pos == 0 || Pos == StringRef::npos) + return; + StringRef Verstr = S.substr(Pos + 1); + if (Verstr.empty()) + return; + + // Truncate the symbol name so that it doesn't include the version string. + NewName = S.substr(0, Pos); + + // If this is not in this DSO, it is not a definition. + if (!B.isInCurrentDSO()) + return; + + // '@@' in a symbol name means the default version. + // It is usually the most recent one. + IsDefault = (Verstr[0] == '@'); + if (IsDefault) + Verstr = Verstr.substr(1); + + for (VersionDefinition &Ver : Config->VersionDefinitions) { + if (Ver.Name == Verstr) { + Version = Ver.Id; + return; + } + } + + // It is an error if the specified version is not defined. + error(toString(B.File) + ": symbol " + S + " has undefined version " + + Verstr); +} + +// There is a way to specify sybmol versions besides version scripts. +// If a symbol name is in the form of @ or @@, +// the strings after '@' specifies versions. +template void SymbolTable::scanSymbolVersions() { + for (Symbol *Sym : SymVector) { + SymbolBody *B = Sym->body(); + uint16_t Ver = 0; + bool IsDefault = false; + StringRef NewName; + + // Parse the symbol name. + parseSymbolVersion(*B, Ver, IsDefault, NewName); + if (!NewName.empty()) + B->setName(NewName); + if (Ver != 0) + Sym->VersionId = IsDefault ? Ver : (Ver | VERSYM_HIDDEN); + + // If versioned symbols are default ones (which contains "@@" as opposed + // to "@"), they are used to resolved remaining unversioned symbols. + if (IsDefault) + if (auto *U = dyn_cast_or_null(find(NewName))) + copy(U->symbol(), Sym); + } +} + template class elf::SymbolTable; template class elf::SymbolTable; template class elf::SymbolTable; Index: lld/ELF/Symbols.h =================================================================== --- lld/ELF/Symbols.h +++ lld/ELF/Symbols.h @@ -69,8 +69,8 @@ bool isLocal() const { return IsLocal; } bool isPreemptible() const; StringRef getName() const { return Name; } + void setName(StringRef S) { Name = {S.data(), S.size()}; } uint8_t getVisibility() const { return StOther & 0x3; } - void parseSymbolVersion(); bool isInGot() const { return GotIndex != -1U; } bool isInPlt() const { return PltIndex != -1U; } Index: lld/ELF/Symbols.cpp =================================================================== --- lld/ELF/Symbols.cpp +++ lld/ELF/Symbols.cpp @@ -220,45 +220,6 @@ return nullptr; } -// If a symbol name contains '@', the characters after that is -// a symbol version name. This function parses that. -void SymbolBody::parseSymbolVersion() { - StringRef S = getName(); - size_t Pos = S.find('@'); - if (Pos == 0 || Pos == StringRef::npos) - return; - StringRef Verstr = S.substr(Pos + 1); - if (Verstr.empty()) - return; - - // Truncate the symbol name so that it doesn't include the version string. - Name = {S.data(), Pos}; - - // If this is not in this DSO, it is not a definition. - if (!isInCurrentDSO()) - return; - - // '@@' 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 &Ver : Config->VersionDefinitions) { - if (Ver.Name != Verstr) - continue; - - if (IsDefault) - symbol()->VersionId = Ver.Id; - else - symbol()->VersionId = Ver.Id | VERSYM_HIDDEN; - return; - } - - // It is an error if the specified version is not defined. - error(toString(File) + ": symbol " + S + " has undefined version " + Verstr); -} - Defined::Defined(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther, uint8_t Type) : SymbolBody(K, Name, IsLocal, StOther, Type) {} Index: lld/ELF/version-script-symver-err.s =================================================================== --- /dev/null +++ lld/ELF/version-script-symver-err.s @@ -0,0 +1,15 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: not ld.lld %t.o -o %t 2>&1 | FileCheck %s +# CHECK: error: duplicate symbol: bar +# CHECK-NEXT: >>> defined at {{.*}}.o +# CHECK-NEXT: >>> defined at {{.*}}.o + +.global _start +.global bar +.symver _start, bar@@VERSION +_start: + jmp bar + +bar: Index: lld/ELF/version-script-symver.s =================================================================== --- /dev/null +++ lld/ELF/version-script-symver.s @@ -0,0 +1,10 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +# RUN: ld.lld %t.o -o %t + +.global _start +.global bar +.symver _start, bar@@VERSION +_start: + jmp bar