[lld] [LLD][COFF] Avoid resolving symbols with -alternatename if the target is undefined (PR #149496)
Jacek Caban via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 18 04:11:57 PDT 2025
https://github.com/cjacek created https://github.com/llvm/llvm-project/pull/149496
This change fixes an issue with the use of `-alternatename` in the MSVC CRT, where both mangled and demangled symbol names are specified. Without this patch, the demangled name could be resolved to an anti-dependency alias of the target. Since chaining anti-dependency aliases is not allowed, this results in an undefined symbol.
The root cause isn't specific to ARM64EC, it can affect other targets as well, even when anti-dependency aliases aren't involved. The accompanying test case demonstrates a scenario where the symbol could be resolved from an archive. However, because the archive member is pulled in after the first pass of alternate name resolution, and archive members don't override weak aliases, eager resolution would incorrectly skip it.
>From 211bc2e6e37227995c51ed40bcc09dc026875248 Mon Sep 17 00:00:00 2001
From: Jacek Caban <jacek at codeweavers.com>
Date: Fri, 18 Jul 2025 12:11:55 +0200
Subject: [PATCH 1/2] [LLD][COFF] Move resolving alternate names to SymbolTable
(NFC)
---
lld/COFF/Driver.cpp | 23 +----------------------
lld/COFF/SymbolTable.cpp | 25 +++++++++++++++++++++++++
lld/COFF/SymbolTable.h | 3 +++
3 files changed, 29 insertions(+), 22 deletions(-)
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 283aeed1a19cd..f31cf0a42abbb 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -2527,28 +2527,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
e.symbolName = symtab.mangleMaybe(e.sym);
}
- // Add weak aliases. Weak aliases is a mechanism to give remaining
- // undefined symbols final chance to be resolved successfully.
- for (auto pair : symtab.alternateNames) {
- StringRef from = pair.first;
- StringRef to = pair.second;
- Symbol *sym = symtab.find(from);
- if (!sym)
- continue;
- if (auto *u = dyn_cast<Undefined>(sym)) {
- if (u->weakAlias) {
- // On ARM64EC, anti-dependency aliases are treated as undefined
- // symbols unless a demangled symbol aliases a defined one, which
- // is part of the implementation.
- if (!symtab.isEC() || !u->isAntiDep)
- continue;
- if (!isa<Undefined>(u->weakAlias) &&
- !isArm64ECMangledFunctionName(u->getName()))
- continue;
- }
- u->setWeakAlias(symtab.addUndefined(to));
- }
- }
+ symtab.resolveAlternateNames();
});
ctx.forEachActiveSymtab([&](SymbolTable &symtab) {
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index 0062df5820e63..1cf750393e6b3 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -1344,6 +1344,31 @@ void SymbolTable::parseAlternateName(StringRef s) {
alternateNames.insert(it, std::make_pair(from, to));
}
+void SymbolTable::resolveAlternateNames() {
+ // Add weak aliases. Weak aliases is a mechanism to give remaining
+ // undefined symbols final chance to be resolved successfully.
+ for (auto pair : alternateNames) {
+ StringRef from = pair.first;
+ StringRef to = pair.second;
+ Symbol *sym = find(from);
+ if (!sym)
+ continue;
+ if (auto *u = dyn_cast<Undefined>(sym)) {
+ if (u->weakAlias) {
+ // On ARM64EC, anti-dependency aliases are treated as undefined
+ // symbols unless a demangled symbol aliases a defined one, which
+ // is part of the implementation.
+ if (!isEC() || !u->isAntiDep)
+ continue;
+ if (!isa<Undefined>(u->weakAlias) &&
+ !isArm64ECMangledFunctionName(u->getName()))
+ continue;
+ }
+ u->setWeakAlias(addUndefined(to));
+ }
+ }
+}
+
// Parses /aligncomm option argument.
void SymbolTable::parseAligncomm(StringRef s) {
auto [name, align] = s.split(',');
diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h
index 15e2644a6f519..7eb067640dc85 100644
--- a/lld/COFF/SymbolTable.h
+++ b/lld/COFF/SymbolTable.h
@@ -69,6 +69,9 @@ class SymbolTable {
// symbols and warn about imported local symbols.
void resolveRemainingUndefines();
+ // Try to resolve undefined symbols with alternate names.
+ void resolveAlternateNames();
+
// Load lazy objects that are needed for MinGW automatic import and for
// doing stdcall fixups.
void loadMinGWSymbols();
>From c191d93dec9bb0bb6703241c4aff0d64edb2fd3e Mon Sep 17 00:00:00 2001
From: Jacek Caban <jacek at codeweavers.com>
Date: Fri, 18 Jul 2025 12:27:26 +0200
Subject: [PATCH 2/2] [LLD][COFF] Avoid resolving symbols with -alternatename
if the target is undefined
This change fixes an issue with the use of -alternatename in the MSVC CRT, where both mangled
and demangled symbol names are specified. Without this patch, the demangled name could be resolved
to an anti-dependency alias of the target. Since chaining anti-dependency aliases is not allowed,
this results in an undefined symbol.
The root cause isn't specific to ARM64EC, it can affect other targets as well, even when
anti-dependency aliases aren't involved. The accompanying test case demonstrates a scenario where
the symbol could be resolved from an archive. However, because the archive member is pulled in
after the first pass of alternate name resolution, and archive members don't override weak aliases,
eager resolution would incorrectly skip it.
---
lld/COFF/SymbolTable.cpp | 14 ++++++++-
lld/test/COFF/alternatename-alias.s | 15 ++++++++++
lld/test/COFF/alternatename-antidep.s | 16 ++++++++++
lld/test/COFF/alternatename-lib.s | 43 +++++++++++++++++++++++++++
lld/test/COFF/arm64ec-altnames.s | 39 ++++++++++++++++++++++++
5 files changed, 126 insertions(+), 1 deletion(-)
create mode 100644 lld/test/COFF/alternatename-alias.s
create mode 100644 lld/test/COFF/alternatename-antidep.s
create mode 100644 lld/test/COFF/alternatename-lib.s
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index 1cf750393e6b3..189e75dfc3ff5 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -1364,7 +1364,19 @@ void SymbolTable::resolveAlternateNames() {
!isArm64ECMangledFunctionName(u->getName()))
continue;
}
- u->setWeakAlias(addUndefined(to));
+
+ // Check if the destination symbol is defined. If not, skip it.
+ // It may still be resolved later if more input files are added.
+ // Also skip anti-dependency targets, as they can't be chained anyway.
+ Symbol *toSym = find(to);
+ if (!toSym)
+ continue;
+ auto toUndef = dyn_cast<Undefined>(toSym);
+ if (toUndef && (!toUndef->weakAlias || toUndef->isAntiDep))
+ continue;
+ if (toSym->isLazy())
+ forceLazy(toSym);
+ u->setWeakAlias(toSym);
}
}
}
diff --git a/lld/test/COFF/alternatename-alias.s b/lld/test/COFF/alternatename-alias.s
new file mode 100644
index 0000000000000..bd0a861380e94
--- /dev/null
+++ b/lld/test/COFF/alternatename-alias.s
@@ -0,0 +1,15 @@
+// REQUIRES: x86
+
+// Check that a weak alias can be used as an alternate name target.
+// RUN: llvm-mc -filetype=obj -triple=x86_64-windows %s -o %t.obj
+// RUN: lld-link -dll -noentry %t.obj -alternatename:sym=altsym
+
+ .data
+ .rva sym
+
+ .weak altsym
+ .set altsym,a
+
+ .globl a
+a:
+ .word 1
diff --git a/lld/test/COFF/alternatename-antidep.s b/lld/test/COFF/alternatename-antidep.s
new file mode 100644
index 0000000000000..1188a9b75d481
--- /dev/null
+++ b/lld/test/COFF/alternatename-antidep.s
@@ -0,0 +1,16 @@
+// REQUIRES: x86
+
+// Check that an anti-dependency alias can't be used as an alternate name target.
+// RUN: llvm-mc -filetype=obj -triple=x86_64-windows %s -o %t.obj
+// RUN: not lld-link -dll -noentry %t.obj -alternatename:sym=altsym 2>&1 | FileCheck %s
+// CHECK: error: undefined symbol: sym
+
+ .data
+ .rva sym
+
+ .weak_anti_dep altsym
+ .set altsym,a
+
+ .globl a
+a:
+ .word 1
diff --git a/lld/test/COFF/alternatename-lib.s b/lld/test/COFF/alternatename-lib.s
new file mode 100644
index 0000000000000..206fe6bc23978
--- /dev/null
+++ b/lld/test/COFF/alternatename-lib.s
@@ -0,0 +1,43 @@
+// REQUIRES: x86
+// RUN: split-file %s %t.dir && cd %t.dir
+
+// RUN: llvm-mc -filetype=obj -triple=x86_64-windows refab.s -o refab.obj
+// RUN: llvm-mc -filetype=obj -triple=x86_64-windows aa.s -o aa.obj
+// RUN: llvm-mc -filetype=obj -triple=x86_64-windows b.s -o b.obj
+// RUN: llvm-mc -filetype=obj -triple=x86_64-windows antidep.s -o antidep.obj
+// RUN: llvm-lib -out:aa.lib aa.obj
+// RUN: llvm-lib -out:b.lib b.obj
+
+// Check that -alternatename with an undefined target does not prevent the symbol from being resolved to a library,
+// once another alternate name is resolved and pulls in the source symbol.
+// RUN: lld-link -out:out.dll -dll -noentry -machine:amd64 refab.obj aa.lib -alternatename:a=aa -alternatename:b=undef
+
+// Check that -alternatename with an anti-dependency target does not prevent the symbol from being resolved to a library,
+// after another alternate name is resolved and pulls in the source symbol.
+// RUN: lld-link -out:out2.dll -dll -noentry -machine:amd64 antidep.obj refab.obj aa.lib -alternatename:a=aa -alternatename:b=u
+
+#--- refab.s
+ .data
+ .rva a
+ .rva b
+
+#--- aa.s
+ .globl aa
+aa:
+ .word 1
+
+ .section .drectve, "yn"
+ .ascii "/defaultlib:b.lib"
+
+#--- b.s
+ .globl b
+b:
+ .word 2
+
+#--- antidep.s
+ .weak_anti_dep u
+ .set u,d
+
+ .globl d
+d:
+ .word 3
diff --git a/lld/test/COFF/arm64ec-altnames.s b/lld/test/COFF/arm64ec-altnames.s
index b2abb24efe4c3..cca778ab8dc64 100644
--- a/lld/test/COFF/arm64ec-altnames.s
+++ b/lld/test/COFF/arm64ec-altnames.s
@@ -2,6 +2,7 @@ REQUIRES: aarch64
RUN: split-file %s %t.dir && cd %t.dir
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows ext.s -o ext.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows ext-mangled.s -o ext-mangled.obj
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows impl.s -o impl.obj
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows impl-cpp.s -o impl-cpp.obj
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig.obj
@@ -49,6 +50,20 @@ RUN: lld-link -machine:arm64ec -dll -noentry -out:out4.dll impl-cpp.obj loadconf
RUN: llvm-objdump -d out4.dll | FileCheck --check-prefix=DISASM %s
RUN: llvm-readobj --hex-dump=.test out4.dll | FileCheck --check-prefix=TESTSEC %s
+# Check that when both mangled and demangled alternate names are used,
+# only the one whose target is defined is used (the mangled one in this case).
+
+RUN: lld-link -machine:arm64ec -dll -noentry -out:out5.dll ext-mangled.obj loadconfig.obj "-alternatename:#func=#altsym" -alternatename:func=altsym
+RUN: llvm-objdump -d out5.dll | FileCheck --check-prefix=DISASM %s
+RUN: llvm-readobj --hex-dump=.test out5.dll | FileCheck --check-prefix=TESTSEC %s
+
+# Check that when both mangled and demangled alternate names are used,
+# only the one whose target is defined is used (the demangled one in this case).
+
+RUN: lld-link -machine:arm64ec -dll -noentry -out:out6.dll ext.obj loadconfig.obj "-alternatename:#func=#altsym" -alternatename:func=altsym
+RUN: llvm-objdump -d out6.dll | FileCheck --check-prefix=DISASM2 %s
+RUN: llvm-readobj --hex-dump=.test out6.dll | FileCheck --check-prefix=TESTSEC2 %s
+
#--- ext.s
.weak_anti_dep func
.set func, "#func"
@@ -70,6 +85,30 @@ altsym:
mov w0, #1
ret
+#--- ext-mangled.s
+ .weak_anti_dep func
+.set func, "#func"
+ .weak_anti_dep "#func"
+.set "#func", thunksym
+
+ .section .test, "r"
+ .rva func
+ .rva "#func"
+
+ .section .thnk,"xr",discard,thunksym
+thunksym:
+ mov w0, #2
+ ret
+
+ .section .text,"xr",discard,"#altsym"
+ .globl "#altsym"
+"#altsym":
+ mov w0, #1
+ ret
+
+ .weak_anti_dep altsym
+ .set altsym,"#altsym"
+
#--- impl.s
.weak_anti_dep func
.set func, "#func"
More information about the llvm-commits
mailing list