diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp index a5921feb18299..240a6d0cd4b2b 100644 --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -1676,8 +1676,9 @@ template void SharedFile::parse() { const uint16_t ver = versyms[i], idx = ver & ~VERSYM_HIDDEN; if (sym.isUndefined()) { - // For unversioned undefined symbols, VER_NDX_GLOBAL makes more sense but - // as of binutils 2.34, GNU ld produces VER_NDX_LOCAL. + // Index 0 (VER_NDX_LOCAL) is used for unversioned undefined symbols. + // GNU ld versions between 2.35 and 2.45 also generate VER_NDX_GLOBAL + // for this case (https://sourceware.org/PR33577). if (ver != VER_NDX_LOCAL && ver != VER_NDX_GLOBAL) { if (idx >= verneeds.size()) { ErrAlways(ctx) << "corrupt input file: version need index " << idx diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index c117e3b716c1b..a7d61f48ed3d5 100644 --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -313,6 +313,8 @@ class Symbol { // represents the Verdef index within the input DSO, which will be converted // to a Verneed index in the output. Otherwise, this represents the Verdef // index (VER_NDX_LOCAL, VER_NDX_GLOBAL, or a named version). + // VER_NDX_LOCAL indicates a defined symbol that has been localized by a + // version script's local: directive or --exclude-libs. uint16_t versionId; LLVM_PREFERRED_TYPE(bool) uint8_t versionScriptAssigned : 1; diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index 9a70c0d19c41d..0a4888fd0b196 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -3784,9 +3784,10 @@ void VersionTableSection::writeTo(uint8_t *buf) { buf += 2; for (const SymbolTableEntry &s : getPartition(ctx).dynSymTab->getSymbols()) { // For an unextracted lazy symbol (undefined weak), it must have been - // converted to Undefined and have VER_NDX_GLOBAL version here. + // converted to Undefined. assert(!s.sym->isLazy()); - write16(ctx, buf, s.sym->versionId); + // Undefined symbols should use index 0 when unversioned. + write16(ctx, buf, s.sym->isUndefined() ? 0 : s.sym->versionId); buf += 2; } } diff --git a/lld/test/ELF/dso-undef-extract-lazy.s b/lld/test/ELF/dso-undef-extract-lazy.s index 40b0758957d7a..2c17bf7d9500e 100644 --- a/lld/test/ELF/dso-undef-extract-lazy.s +++ b/lld/test/ELF/dso-undef-extract-lazy.s @@ -25,6 +25,11 @@ # CHECK-FETCH: GLOBAL DEFAULT {{[0-9]+}} foo +## Unversioned undefined symbols also extract the archive definitions. +# RUN: yaml2obj %t/ver.yaml -o %t4.so +# RUN: ld.lld %t1.o %t4.so %t2.a -o %t.exe +# RUN: llvm-readelf --dyn-symbols %t.exe | FileCheck %s --check-prefix=CHECK-FETCH + #--- main.s .text .globl _start @@ -38,3 +43,39 @@ foo: #--- shlib.s .global foo + +#--- ver.yaml +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 +Sections: + - Name: .gnu.version + Type: SHT_GNU_versym + Flags: [ SHF_ALLOC ] + Address: 0x0000000000200210 + AddressAlign: 0x0000000000000002 + EntSize: 0x0000000000000002 +## We use both index 0 and 1 for unversioned undefined symbols. +## Index 1 simulates older LLD and GNU ld versions between 2.35 and 2.45. + Entries: [ 0, 0, 1 ] + - Name: .gnu.version_r + Type: SHT_GNU_verneed + Flags: [ SHF_ALLOC ] + Address: 0x0000000000200250 + AddressAlign: 0x0000000000000004 + Dependencies: + - Version: 1 + File: dso.so.0 + Entries: + - Name: v1 + Hash: 1937 + Flags: 0 + Other: 3 +DynamicSymbols: + - Name: _start + Binding: STB_GLOBAL + - Name: foo + Binding: STB_GLOBAL diff --git a/lld/test/ELF/linkerscript/version-script.s b/lld/test/ELF/linkerscript/version-script.s index 52382eeb1245c..6b97fede00c37 100644 --- a/lld/test/ELF/linkerscript/version-script.s +++ b/lld/test/ELF/linkerscript/version-script.s @@ -17,7 +17,7 @@ # CHECK-NEXT: Name: # CHECK-NEXT: } # CHECK-NEXT: Symbol { -# CHECK-NEXT: Version: 1 +# CHECK-NEXT: Version: 0 # CHECK-NEXT: Name: und # CHECK-NEXT: } # CHECK-NEXT: Symbol { diff --git a/lld/test/ELF/version-script-extern-undefined.s b/lld/test/ELF/version-script-extern-undefined.s index 38114229e0ce3..010b4d5d6b63d 100644 --- a/lld/test/ELF/version-script-extern-undefined.s +++ b/lld/test/ELF/version-script-extern-undefined.s @@ -11,7 +11,7 @@ # CHECK-NEXT: Name: # CHECK-NEXT: } # CHECK-NEXT: Symbol { -# CHECK-NEXT: Version: 1 +# CHECK-NEXT: Version: 0 # CHECK-NEXT: Name: _Z3abbi # CHECK-NEXT: } # CHECK-NEXT: ] diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index 39e9611c7190e..bfcf5dab47722 100644 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -1710,8 +1710,8 @@ enum { VER_FLG_BASE = 0x1, VER_FLG_WEAK = 0x2, VER_FLG_INFO = 0x4 }; // Special constants for the version table. (SHT_GNU_versym/.gnu.version) enum { - VER_NDX_LOCAL = 0, // Unversioned local symbol - VER_NDX_GLOBAL = 1, // Unversioned global symbol + VER_NDX_LOCAL = 0, // Unversioned undefined or localized defined symbol + VER_NDX_GLOBAL = 1, // Unversioned non-local defined symbol VERSYM_VERSION = 0x7fff, // Version Index mask VERSYM_HIDDEN = 0x8000 // Hidden bit (non-default version) };