Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -651,6 +651,8 @@ VersymIndex = Versym->vs_index; ++Versym; } + bool Hidden = VersymIndex & VERSYM_HIDDEN; + VersymIndex = VersymIndex & ~VERSYM_HIDDEN; StringRef Name = check(Sym.getName(this->StringTable)); if (Sym.isUndefined()) { @@ -658,15 +660,23 @@ continue; } - if (Versym) { - // Ignore local symbols and non-default versions. - if (VersymIndex == VER_NDX_LOCAL || (VersymIndex & VERSYM_HIDDEN)) - continue; - } + // Ignore local symbols. + if (Versym && VersymIndex == VER_NDX_LOCAL) + continue; const Elf_Verdef *V = VersymIndex == VER_NDX_GLOBAL ? nullptr : Verdefs[VersymIndex]; - elf::Symtab::X->addShared(this, Name, Sym, V); + + if (!Hidden) + elf::Symtab::X->addShared(this, Name, Sym, V); + + // Also add the symbol with the versioned name to handle undefined symbols + // with explicit versions. + if (V) { + StringRef VerName = this->StringTable.data() + V->getAux()->vda_name; + Name = Saver.save(Twine(Name) + "@" + VerName); + elf::Symtab::X->addShared(this, Name, Sym, V); + } } } Index: ELF/Symbols.cpp =================================================================== --- ELF/Symbols.cpp +++ ELF/Symbols.cpp @@ -202,6 +202,10 @@ // Truncate the symbol name so that it doesn't include the version string. Name = {S.data(), Pos}; + // If this is an undefined or shared symbol it is not a definition. + if (isUndefined() || isShared()) + return; + // '@@' in a symbol name means the default version. // It is usually the most recent one. bool IsDefault = (Verstr[0] == '@'); Index: test/ELF/undefined-versioned-symbol.s =================================================================== --- /dev/null +++ test/ELF/undefined-versioned-symbol.s @@ -0,0 +1,74 @@ +// REQUIRES: x86 +// RUN: echo ".data; \ +// RUN: .quad \"basename\"; \ +// RUN: .quad \"basename@FBSD_1.0\"; \ +// RUN: .quad \"basename@FBSD_1.1\" " > %t.s +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %t.s -o %t.o +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t2.o +// RUN: echo "FBSD_1.0 { local: *; }; FBSD_1.1 { };" > %t2.ver +// RUN: ld.lld --shared --version-script %t2.ver %t2.o -o %t2.so +// RUN: echo "LIBPKG_1.3 { };" > %t.ver +// RUN: ld.lld --shared %t.o --version-script %t.ver %t2.so -o %t.so +// RUN: llvm-readobj --dyn-symbols -r --expand-relocs %t.so | FileCheck %s + +// Test that each relocation points to the correct version. + +// CHECK: Section ({{.*}}) .rela.dyn { +// CHECK-NEXT: Relocation { +// CHECK-NEXT: Offset: 0x2000 +// CHECK-NEXT: Type: R_X86_64_64 (1) +// CHECK-NEXT: Symbol: basename (1) +// CHECK-NEXT: Addend: 0x0 +// CHECK-NEXT: } +// CHECK-NEXT: Relocation { +// CHECK-NEXT: Offset: 0x2008 +// CHECK-NEXT: Type: R_X86_64_64 (1) +// CHECK-NEXT: Symbol: basename (2) +// CHECK-NEXT: Addend: 0x0 +// CHECK-NEXT: } +// CHECK-NEXT: Relocation { +// CHECK-NEXT: Offset: 0x2010 +// CHECK-NEXT: Type: R_X86_64_64 (1) +// CHECK-NEXT: Symbol: basename (3) +// CHECK-NEXT: Addend: 0x0 +// CHECK-NEXT: } +// CHECK-NEXT: } + + +// CHECK: DynamicSymbols [ +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: +// CHECK-NEXT: Value: +// CHECK-NEXT: Size: +// CHECK-NEXT: Binding: +// CHECK-NEXT: Type: +// CHECK-NEXT: Other: +// CHECK-NEXT: Section: +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: basename@FBSD_1.1 +// CHECK-NEXT: Value: +// CHECK-NEXT: Size: +// CHECK-NEXT: Binding: +// CHECK-NEXT: Type: +// CHECK-NEXT: Other: +// CHECK-NEXT: Section: +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: basename@FBSD_1.0 +// CHECK-NEXT: Value: +// CHECK-NEXT: Size: +// CHECK-NEXT: Binding: +// CHECK-NEXT: Type: +// CHECK-NEXT: Other: +// CHECK-NEXT: Section: +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: basename@FBSD_1.1 + + +.global "basename@FBSD_1.0" +"basename@FBSD_1.0": + +.global "basename@@FBSD_1.1" +"basename@@FBSD_1.1":