Index: lld/trunk/ELF/SymbolTable.cpp =================================================================== --- lld/trunk/ELF/SymbolTable.cpp +++ lld/trunk/ELF/SymbolTable.cpp @@ -163,6 +163,32 @@ return std::min(VA, VB); } +// A symbol version may be included in a symbol name as a prefix 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->VersionScriptGlobalByDefault ? VER_NDX_GLOBAL + : VER_NDX_LOCAL; + + // If symbol name contains '@' or '@@' we can assign its version id right + // here. '@@' means version by default. 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(); + + size_t I = 2; + for (elf::Version &V : Config->SymbolVersions) { + if (V.Name == Version) + return Default ? I : (I | VERSYM_HIDDEN); + ++I; + } + 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) { @@ -175,10 +201,9 @@ Sym->Visibility = STV_DEFAULT; Sym->IsUsedInRegularObj = false; Sym->ExportDynamic = false; - if (Config->VersionScriptGlobalByDefault) - Sym->VersionId = VER_NDX_GLOBAL; - else - Sym->VersionId = VER_NDX_LOCAL; + Sym->VersionId = getVersionId(Sym, Name); + Sym->VersionedName = + Sym->VersionId != VER_NDX_LOCAL && Sym->VersionId != VER_NDX_GLOBAL; SymVector.push_back(Sym); } else { Sym = SymVector[P.first->second]; Index: lld/trunk/ELF/Symbols.h =================================================================== --- lld/trunk/ELF/Symbols.h +++ lld/trunk/ELF/Symbols.h @@ -77,10 +77,7 @@ bool isPreemptible() const; // Returns the symbol name. - StringRef getName() const { - assert(!isLocal()); - return StringRef(Name.S, Name.Len); - } + StringRef getName() const; uint32_t getNameOffset() const { assert(isLocal()); return NameOffset; @@ -422,6 +419,11 @@ // --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 @@ -101,6 +101,14 @@ IsInGlobalMipsGot(false), Type(Type), StOther(StOther), Name({Name.data(), Name.size()}) {} +StringRef SymbolBody::getName() const { + assert(!isLocal()); + StringRef S = StringRef(Name.S, Name.Len); + if (!symbol()->VersionedName) + return S; + return S.substr(0, S.find('@')); +} + // Returns true if a symbol can be replaced at load-time by a symbol // with the same name defined in other ELF executable or DSO. bool SymbolBody::isPreemptible() const { Index: lld/trunk/test/ELF/Inputs/verdef-defaultver.s =================================================================== --- lld/trunk/test/ELF/Inputs/verdef-defaultver.s +++ lld/trunk/test/ELF/Inputs/verdef-defaultver.s @@ -0,0 +1,22 @@ +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 Index: lld/trunk/test/ELF/verdef-defaultver.s =================================================================== --- lld/trunk/test/ELF/verdef-defaultver.s +++ lld/trunk/test/ELF/verdef-defaultver.s @@ -0,0 +1,210 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/verdef-defaultver.s -o %t1 +# RUN: echo "LIBSAMPLE_1.0{ \ +# RUN: global: a; \ +# RUN: local: *; }; \ +# RUN: LIBSAMPLE_2.0{ \ +# RUN: global: b; c; \ +# RUN: }LIBSAMPLE_1.0;" > %t.script +# RUN: ld.lld -shared -soname shared %t1 --version-script %t.script -o %t.so +# RUN: llvm-readobj -V -dyn-symbols %t.so | FileCheck --check-prefix=DSO %s + +# DSO: DynamicSymbols [ +# DSO-NEXT: Symbol { +# DSO-NEXT: Name: @ +# DSO-NEXT: Value: 0x0 +# DSO-NEXT: Size: 0 +# DSO-NEXT: Binding: Local +# DSO-NEXT: Type: None +# DSO-NEXT: Other: 0 +# DSO-NEXT: Section: Undefined +# DSO-NEXT: } +# DSO-NEXT: Symbol { +# DSO-NEXT: Name: a@@LIBSAMPLE_1.0 +# DSO-NEXT: Value: 0x1000 +# DSO-NEXT: Size: 0 +# DSO-NEXT: Binding: Global +# DSO-NEXT: Type: Function +# DSO-NEXT: Other: 0 +# DSO-NEXT: Section: .text +# DSO-NEXT: } +# DSO-NEXT: Symbol { +# DSO-NEXT: Name: b@@LIBSAMPLE_2.0 +# DSO-NEXT: Value: 0x1002 +# DSO-NEXT: Size: 0 +# DSO-NEXT: Binding: Global +# DSO-NEXT: Type: Function +# DSO-NEXT: Other: 0 +# DSO-NEXT: Section: .text +# DSO-NEXT: } +# DSO-NEXT: Symbol { +# DSO-NEXT: Name: b@LIBSAMPLE_1.0 +# DSO-NEXT: Value: 0x1001 +# DSO-NEXT: Size: 0 +# DSO-NEXT: Binding: Global +# DSO-NEXT: Type: Function +# DSO-NEXT: Other: 0 +# DSO-NEXT: Section: .text +# DSO-NEXT: } +# DSO-NEXT: Symbol { +# DSO-NEXT: Name: c@@LIBSAMPLE_2.0 +# DSO-NEXT: Value: 0x1003 +# DSO-NEXT: Size: 0 +# DSO-NEXT: Binding: Global +# DSO-NEXT: Type: Function +# DSO-NEXT: Other: 0 +# DSO-NEXT: Section: .text +# DSO-NEXT: } +# DSO-NEXT: ] +# DSO-NEXT: Version symbols { +# DSO-NEXT: Section Name: .gnu.version +# DSO-NEXT: Address: 0x240 +# DSO-NEXT: Offset: 0x240 +# DSO-NEXT: Link: 1 +# DSO-NEXT: Symbols [ +# DSO-NEXT: Symbol { +# DSO-NEXT: Version: 0 +# DSO-NEXT: Name: @ +# DSO-NEXT: } +# DSO-NEXT: Symbol { +# DSO-NEXT: Version: 2 +# DSO-NEXT: Name: a@@LIBSAMPLE_1.0 +# DSO-NEXT: } +# DSO-NEXT: Symbol { +# DSO-NEXT: Version: 3 +# DSO-NEXT: Name: b@@LIBSAMPLE_2.0 +# DSO-NEXT: } +# DSO-NEXT: Symbol { +# DSO-NEXT: Version: 2 +# DSO-NEXT: Name: b@LIBSAMPLE_1.0 +# DSO-NEXT: } +# DSO-NEXT: Symbol { +# DSO-NEXT: Version: 3 +# DSO-NEXT: Name: c@@LIBSAMPLE_2.0 +# DSO-NEXT: } +# DSO-NEXT: ] +# DSO-NEXT: } +# DSO-NEXT: SHT_GNU_verdef { +# DSO-NEXT: Definition { +# DSO-NEXT: Version: 1 +# DSO-NEXT: Flags: Base +# DSO-NEXT: Index: 1 +# DSO-NEXT: Hash: 127830196 +# DSO-NEXT: Name: shared +# DSO-NEXT: } +# DSO-NEXT: Definition { +# DSO-NEXT: Version: 1 +# DSO-NEXT: Flags: 0x0 +# DSO-NEXT: Index: 2 +# DSO-NEXT: Hash: 98457184 +# DSO-NEXT: Name: LIBSAMPLE_1.0 +# DSO-NEXT: } +# DSO-NEXT: Definition { +# DSO-NEXT: Version: 1 +# DSO-NEXT: Flags: 0x0 +# DSO-NEXT: Index: 3 +# DSO-NEXT: Hash: 98456416 +# DSO-NEXT: Name: LIBSAMPLE_2.0 +# DSO-NEXT: Predecessor: LIBSAMPLE_1.0 +# DSO-NEXT: } +# DSO-NEXT: } + +## Check that we can link against DSO produced. +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t2 +# RUN: ld.lld %t2 %t.so -o %t3 +# RUN: llvm-readobj -V -dyn-symbols %t3 | FileCheck --check-prefix=EXE %s + +# EXE: DynamicSymbols [ +# EXE-NEXT: Symbol { +# EXE-NEXT: Name: @ +# EXE-NEXT: Value: 0x0 +# EXE-NEXT: Size: 0 +# EXE-NEXT: Binding: Local +# EXE-NEXT: Type: None +# EXE-NEXT: Other: 0 +# EXE-NEXT: Section: Undefined +# EXE-NEXT: } +# EXE-NEXT: Symbol { +# EXE-NEXT: Name: a@LIBSAMPLE_1.0 +# EXE-NEXT: Value: 0x11020 +# EXE-NEXT: Size: 0 +# EXE-NEXT: Binding: Global +# EXE-NEXT: Type: Function +# EXE-NEXT: Other: 0 +# EXE-NEXT: Section: Undefined +# EXE-NEXT: } +# EXE-NEXT: Symbol { +# EXE-NEXT: Name: b@LIBSAMPLE_2.0 +# EXE-NEXT: Value: 0x11030 +# EXE-NEXT: Size: 0 +# EXE-NEXT: Binding: Global +# EXE-NEXT: Type: Function +# EXE-NEXT: Other: 0 +# EXE-NEXT: Section: Undefined +# EXE-NEXT: } +# EXE-NEXT: Symbol { +# EXE-NEXT: Name: c@LIBSAMPLE_2.0 +# EXE-NEXT: Value: 0x11040 +# EXE-NEXT: Size: 0 +# EXE-NEXT: Binding: Global +# EXE-NEXT: Type: Function +# EXE-NEXT: Other: 0 +# EXE-NEXT: Section: Undefined +# EXE-NEXT: } +# EXE-NEXT: ] +# EXE-NEXT: Version symbols { +# EXE-NEXT: Section Name: .gnu.version +# EXE-NEXT: Address: 0x10228 +# EXE-NEXT: Offset: 0x228 +# EXE-NEXT: Link: 1 +# EXE-NEXT: Symbols [ +# EXE-NEXT: Symbol { +# EXE-NEXT: Version: 0 +# EXE-NEXT: Name: @ +# EXE-NEXT: } +# EXE-NEXT: Symbol { +# EXE-NEXT: Version: 2 +# EXE-NEXT: Name: a@LIBSAMPLE_1.0 +# EXE-NEXT: } +# EXE-NEXT: Symbol { +# EXE-NEXT: Version: 3 +# EXE-NEXT: Name: b@LIBSAMPLE_2.0 +# EXE-NEXT: } +# EXE-NEXT: Symbol { +# EXE-NEXT: Version: 3 +# EXE-NEXT: Name: c@LIBSAMPLE_2.0 +# EXE-NEXT: } +# EXE-NEXT: ] +# EXE-NEXT: } +# EXE-NEXT: SHT_GNU_verdef { +# EXE-NEXT: } +# EXE-NEXT: SHT_GNU_verneed { +# EXE-NEXT: Dependency { +# EXE-NEXT: Version: 1 +# EXE-NEXT: Count: 2 +# EXE-NEXT: FileName: shared +# EXE-NEXT: Entry { +# EXE-NEXT: Hash: 98457184 +# EXE-NEXT: Flags: 0x0 +# EXE-NEXT: Index: 2 +# EXE-NEXT: Name: LIBSAMPLE_1.0 +# EXE-NEXT: } +# EXE-NEXT: Entry { +# EXE-NEXT: Hash: 98456416 +# EXE-NEXT: Flags: 0x0 +# EXE-NEXT: Index: 3 +# EXE-NEXT: Name: LIBSAMPLE_2.0 +# EXE-NEXT: } +# 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 + callq b + callq c