[lld] [llvm] ELF: Use index 0 for unversioned undefined symbols (PR #168189)

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Sat Nov 15 10:45:00 PST 2025


https://github.com/MaskRay updated https://github.com/llvm/llvm-project/pull/168189

>From d2db96cb5855e841674d582a4545bfec4397db7b Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sat, 15 Nov 2025 00:47:21 -0800
Subject: [PATCH 1/2] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20in?=
 =?UTF-8?q?itial=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.5-bogner
---
 lld/ELF/Symbols.h                              | 2 ++
 lld/ELF/SyntheticSections.cpp                  | 5 +++--
 lld/test/ELF/linkerscript/version-script.s     | 2 +-
 lld/test/ELF/version-script-extern-undefined.s | 2 +-
 llvm/include/llvm/BinaryFormat/ELF.h           | 4 ++--
 5 files changed, 9 insertions(+), 6 deletions(-)

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/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)
 };

>From 2e4ac2f40ae59ca82740a5f918e4cc911d7c0701 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sat, 15 Nov 2025 10:44:49 -0800
Subject: [PATCH 2/2] add more test

Created using spr 1.3.5-bogner
---
 lld/ELF/InputFiles.cpp                |  5 ++--
 lld/test/ELF/dso-undef-extract-lazy.s | 41 +++++++++++++++++++++++++++
 2 files changed, 44 insertions(+), 2 deletions(-)

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 <class ELFT> 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/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



More information about the llvm-commits mailing list