[lld] [ELF,test] Cover wildcard precedence in version scripts and dynamic lists (PR #223195)

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Sat Sep 12 18:55:27 PDT 2026


https://github.com/MaskRay created https://github.com/llvm/llvm-project/pull/223195

version-script-wildcard-precedence.s, renamed from
version-script-reassign-glob.s, covers more cases.

dynamic-list-glob.s absorbs dynamic-list-cpp.s and adds an extern "C++"
wildcard, which had no --dynamic-list coverage.

>From 63b7687fdde5d8886732c9bd96a117882e4849b6 Mon Sep 17 00:00:00 2001
From: Fangrui Song <i at maskray.me>
Date: Sat, 12 Sep 2026 18:42:08 -0700
Subject: [PATCH] [ELF,test] Cover wildcard precedence in version scripts and
 dynamic lists

version-script-wildcard-precedence.s, renamed from
version-script-reassign-glob.s, covers more cases.

dynamic-list-glob.s absorbs dynamic-list-cpp.s and adds an extern "C++"
wildcard, which had no --dynamic-list coverage.
---
 lld/test/ELF/dynamic-list-cpp.s               | 18 ----
 lld/test/ELF/dynamic-list-glob.s              | 36 ++++++--
 lld/test/ELF/version-script-reassign-glob.s   | 28 ------
 .../ELF/version-script-wildcard-precedence.s  | 89 +++++++++++++++++++
 4 files changed, 116 insertions(+), 55 deletions(-)
 delete mode 100644 lld/test/ELF/dynamic-list-cpp.s
 delete mode 100644 lld/test/ELF/version-script-reassign-glob.s
 create mode 100644 lld/test/ELF/version-script-wildcard-precedence.s

diff --git a/lld/test/ELF/dynamic-list-cpp.s b/lld/test/ELF/dynamic-list-cpp.s
deleted file mode 100644
index 05f11e0079d34f..00000000000000
--- a/lld/test/ELF/dynamic-list-cpp.s
+++ /dev/null
@@ -1,18 +0,0 @@
-# REQUIRES: x86
-
-## Confirm both mangled and unmangled names may appear in
-## the --dynamic-list file.
-
-# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
-
-# RUN: echo '{ _Z1fv; extern "C++" { "g()"; }; };' > %t.list
-# RUN: ld.lld -pie --dynamic-list %t.list %t.o -o %t
-# RUN: llvm-readelf --dyn-syms %t | FileCheck %s
-
-# CHECK:      Symbol table '.dynsym' contains 3 entries:
-# CHECK:      _Z1fv
-# CHECK-NEXT: _Z1gv
-
-.globl _Z1fv, _Z1gv
-_Z1fv:
-_Z1gv:
diff --git a/lld/test/ELF/dynamic-list-glob.s b/lld/test/ELF/dynamic-list-glob.s
index 1a849782074aea..51c29497293388 100644
--- a/lld/test/ELF/dynamic-list-glob.s
+++ b/lld/test/ELF/dynamic-list-glob.s
@@ -1,22 +1,40 @@
 # REQUIRES: x86
-
-## Confirm --dynamic-list identifies symbols by entries, including wildcards.
-## Entries need not match a symbol. 
+## --dynamic-list entries may be wildcards. Entries need not match a symbol.
+## extern "C++" entries match demangled names, which for a non-mangled symbol
+## is the name itself.
 
 # RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
 
 # RUN: echo '{ [fb]o?1*; };' > %t.list
 # RUN: ld.lld -pie --dynamic-list %t.list %t.o -o %t
 # RUN: llvm-readelf --dyn-syms %t | FileCheck %s
+# CHECK:      UND
+# CHECK-NEXT: [[#]] boo1
+# CHECK-NEXT: [[#]] foo1
+# CHECK-NEXT: [[#]] foo11
+# CHECK-NOT:  {{.}}
 
-# CHECK:      Symbol table '.dynsym' contains 4 entries:
-# CHECK:      boo1
-# CHECK-NEXT: foo1
-# CHECK-NEXT: foo11
+## Both mangled and unmangled names may appear.
+# RUN: echo '{ _Z1fv; extern "C++" { "g()"; h*; }; };' > %t.list
+# RUN: ld.lld -pie --dynamic-list %t.list %t.o -o %t
+# RUN: llvm-readelf --dyn-syms %t | FileCheck %s --check-prefix=CXX
+# CXX:      UND
+# CXX-NEXT: [[#]] _Z1fv
+# CXX-NEXT: [[#]] _Z1gv
+# CXX-NEXT: [[#]] _Z1hv
+# CXX-NEXT: [[#]] _Z2hhv
+# CXX-NEXT: [[#]] hello
+# CXX-NOT:  {{.}}
 
-.globl _start, boo1, foo1, foo11, foo2
+.globl _start, boo1, foo1, foo11, foo2, _Z1fv, _Z1gv, _Z1hv, _Z2hhv, _Z1iv, hello
 _start:
+boo1:
 foo1:
 foo11:
 foo2:
-boo1:
+_Z1fv:
+_Z1gv:
+_Z1hv:
+_Z2hhv:
+_Z1iv:
+hello:
diff --git a/lld/test/ELF/version-script-reassign-glob.s b/lld/test/ELF/version-script-reassign-glob.s
deleted file mode 100644
index 8de36467bd8ee6..00000000000000
--- a/lld/test/ELF/version-script-reassign-glob.s
+++ /dev/null
@@ -1,28 +0,0 @@
-# REQUIRES: x86
-# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
-
-# RUN: echo 'foo { foo*; }; bar { *; };' > %t.ver
-# RUN: ld.lld --version-script %t.ver %t.o -shared -o %t.so --fatal-warnings
-# RUN: llvm-readelf --dyn-syms %t.so | FileCheck --check-prefix=FOO %s
-
-# RUN: echo 'foo { foo*; }; bar { f*; };' > %t.ver
-# RUN: ld.lld --version-script %t.ver %t.o -shared -o %t.so --fatal-warnings
-# RUN: llvm-readelf --dyn-syms %t.so | FileCheck --check-prefix=BAR %s
-
-# RUN: echo 'bar1 { *; }; bar2 { *; };' > %t2.ver
-# RUN: ld.lld --version-script %t2.ver %t.o -shared -o %t2.so 2>&1 | \
-# RUN:   FileCheck --check-prefix=DUPWARN %s
-# RUN: llvm-readelf --dyn-syms %t2.so | FileCheck --check-prefix=BAR2 %s
-
-## If both a non-* glob and a * match, non-* wins.
-## This is GNU linkers' behavior. We don't feel strongly this should be supported.
-# FOO: GLOBAL DEFAULT 7 foo@@foo
-
-# BAR: GLOBAL DEFAULT 7 foo@@bar
-
-## When there are multiple * patterns, the last wins.
-# BAR2: GLOBAL DEFAULT 7 foo@@bar2
-# DUPWARN: warning: wildcard pattern '*' is used for multiple version definitions in version script
-
-.globl foo
-foo:
diff --git a/lld/test/ELF/version-script-wildcard-precedence.s b/lld/test/ELF/version-script-wildcard-precedence.s
new file mode 100644
index 00000000000000..bdd28363b69502
--- /dev/null
+++ b/lld/test/ELF/version-script-wildcard-precedence.s
@@ -0,0 +1,89 @@
+# REQUIRES: x86
+## Test precedence among version script wildcards.
+
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64 a.s -o a.o
+
+# RUN: ld.lld --version-script later.ver a.o -shared -o later.so --fatal-warnings
+# RUN: llvm-readelf --dyn-syms later.so | FileCheck %s -DV=bar
+# RUN: ld.lld --version-script node.ver a.o -shared -o node.so --fatal-warnings
+# RUN: llvm-readelf --dyn-syms node.so | FileCheck %s -DV=v1
+# RUN: ld.lld --version-script local.ver a.o -shared -o local.so --fatal-warnings
+# RUN: llvm-readelf --dyn-syms local.so | FileCheck %s -DV=v2
+
+# CHECK:      UND
+# CHECK-NEXT: [[#]] foo1@@[[V]]
+# CHECK-NEXT: [[#]] foo2@@[[V]]
+# CHECK-NEXT: [[#]] bar{{$}}
+# CHECK-NEXT: [[#]] _Z3fooi{{$}}
+# CHECK-NOT:  {{.}}
+
+## If both a non-* pattern and * match, non-* wins even if * comes later.
+## This is GNU linkers' behavior. We don't feel strongly this should be supported.
+# RUN: ld.lld --version-script star.ver a.o -shared -o star.so --fatal-warnings
+# RUN: llvm-readelf --dyn-syms star.so | FileCheck %s --check-prefix=STAR
+# STAR:      UND
+# STAR-NEXT: [[#]] foo1@@foo
+# STAR-NEXT: [[#]] foo2@@foo
+# STAR-NEXT: [[#]] bar@@bar
+# STAR-NEXT: [[#]] _Z3fooi@@bar
+# STAR-NOT:  {{.}}
+
+## When there are multiple * patterns, the last wins.
+# RUN: ld.lld --version-script star2.ver a.o -shared -o star2.so 2>&1 | \
+# RUN:   FileCheck %s --check-prefix=DUPWARN
+# RUN: llvm-readelf --dyn-syms star2.so | FileCheck %s --check-prefix=STAR2
+# DUPWARN: warning: wildcard pattern '*' is used for multiple version definitions in version script
+# STAR2:      UND
+# STAR2-NEXT: [[#]] foo1@@bar2
+# STAR2-NEXT: [[#]] foo2@@bar2
+# STAR2-NEXT: [[#]] bar@@bar2
+# STAR2-NEXT: [[#]] _Z3fooi@@bar2
+# STAR2-NOT:  {{.}}
+
+## An exact name beats a wildcard in a later definition.
+# RUN: ld.lld --version-script exact.ver a.o -shared -o exact.so --fatal-warnings
+# RUN: llvm-readelf --dyn-syms exact.so | FileCheck %s --check-prefix=EXACT
+# EXACT:      UND
+# EXACT-NEXT: [[#]] foo1@@v1
+# EXACT-NEXT: [[#]] foo2@@v2
+# EXACT-NOT:  {{.}}
+
+## An extern "C++" wildcard matches demangled names, which for a non-mangled
+## symbol is the name itself, and takes part in the same precedence.
+# RUN: ld.lld --version-script cxx.ver a.o -shared -o cxx.so --fatal-warnings
+# RUN: llvm-readelf --dyn-syms cxx.so | FileCheck %s --check-prefix=CXX
+# CXX:      UND
+# CXX-NEXT: [[#]] foo1@@v2
+# CXX-NEXT: [[#]] foo2@@v2
+# CXX-NEXT: [[#]] bar{{$}}
+# CXX-NEXT: [[#]] _Z3fooi@@v2
+# CXX-NOT:  {{.}}
+
+#--- a.s
+.globl foo1, foo2, bar, _Z3fooi
+foo1:
+foo2:
+bar:
+_Z3fooi:
+
+#--- later.ver
+foo { foo*; }; bar { f*; };
+
+#--- node.ver
+v1 { global: foo*; local: f*; };
+
+#--- local.ver
+v1 { local: f*; }; v2 { global: foo*; };
+
+#--- star.ver
+foo { foo*; }; bar { *; };
+
+#--- star2.ver
+bar1 { *; }; bar2 { *; };
+
+#--- exact.ver
+v1 { global: foo1; local: *; }; v2 { global: foo*; };
+
+#--- cxx.ver
+v1 { global: foo*; }; v2 { global: extern "C++" { foo*; }; };



More information about the llvm-commits mailing list