[lld] [LLD][COFF] Replace ARM64EC TLS symbols with native symbols when available (PR #212845)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 29 11:57:35 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-platform-windows
Author: Jacek Caban (cjacek)
<details>
<summary>Changes</summary>
Following MSVC, we don't emit separate TLS directories for EC and native views and instead use native one for both. CRT uses -arm64xsameaddress feature to ensure that it works well for callbacks. Additionally, linker needs to replace EC symbols with native ones to ensure that object files reference them as expected.
Even through EC symbols end up being unused, they still need to be present and defined, so the replacement happens after symbols are resolved. MSVC seems to have some dedicated logic to skip chunks defining EC _tls_used and _tls_index symbols. The logic seems incomplete; for example, if the same chunks defines more symbols, those symbols become equal to __ImageBase (0 RVA). Instead of trying to replicate that, I made sure that GC has a chance to clean up such sections.
---
Full diff: https://github.com/llvm/llvm-project/pull/212845.diff
2 Files Affected:
- (modified) lld/COFF/Driver.cpp (+17-4)
- (added) lld/test/COFF/arm64x-tls.s (+104)
``````````diff
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 4eedefbf2ba5e..dc3fd46323032 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -2805,10 +2805,6 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
ltoCompilationDone = true;
ctx.forEachSymtab([](SymbolTable &symtab) { symtab.compileBitcodeFiles(); });
- if (Defined *d =
- dyn_cast_or_null<Defined>(ctx.symtab.findUnderscore("_tls_used")))
- config->gcroot.push_back(d);
-
// If -thinlto-index-only is given, we should create only "index
// files" and not object files. Index file creation is already done
// in addCombinedLTOObject, so we are done if that's the case.
@@ -2837,6 +2833,23 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
if (errorCount())
return;
+ if (ctx.hybridSymtab) {
+ // On ARM64X, use native _tls_used and _tls_index for both views.
+ auto maybeReplaceWithNative = [&](StringRef name) {
+ Symbol *nativeSym = ctx.hybridSymtab->findUnderscore(name);
+ if (!nativeSym || !nativeSym->getDefined())
+ return;
+ if (Symbol *ecSym = ctx.symtab.findUnderscore(name))
+ ecSym->replaceKeepingName(nativeSym, sizeof(SymbolUnion));
+ };
+ maybeReplaceWithNative("_tls_used");
+ maybeReplaceWithNative("_tls_index");
+ }
+
+ if (Defined *d =
+ dyn_cast_or_null<Defined>(ctx.symtab.findUnderscore("_tls_used")))
+ config->gcroot.push_back(d);
+
ctx.forEachActiveSymtab([](SymbolTable &symtab) {
symtab.initializeECThunks();
symtab.initializeLoadConfig();
diff --git a/lld/test/COFF/arm64x-tls.s b/lld/test/COFF/arm64x-tls.s
new file mode 100644
index 0000000000000..21ff0ab26d2af
--- /dev/null
+++ b/lld/test/COFF/arm64x-tls.s
@@ -0,0 +1,104 @@
+// REQUIRES: aarch64
+// RUN: split-file %s %t.dir && cd %t.dir
+
+// RUN: llvm-mc -filetype=obj -triple=aarch64-windows aarch64.s -o aarch64.obj
+// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows arm64ec.s -o arm64ec.obj
+// RUN: llvm-mc -filetype=obj -triple=aarch64-windows tls-aarch64.s -o tls-aarch64.obj
+// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows tls-arm64ec.s -o tls-arm64ec.obj
+// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
+// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %S/Inputs/loadconfig-arm64.s -o loadconfig-arm64.obj
+
+// RUN: rm -f tls.lib
+// RUN: llvm-lib -machine:arm64x -out:tls.lib tls-aarch64.obj tls-arm64ec.obj loadconfig-arm64ec.obj loadconfig-arm64.obj
+// RUN: lld-link -machine:arm64x -dll -noentry aarch64.obj arm64ec.obj tls.lib -out:out.dll
+
+// Check that we're using native _tls_index and _tls_used for both views.
+
+// RUN: llvm-readobj --coff-tls-directory --hex-dump=.ec --hex-dump=.a64 out.dll | FileCheck %s
+// CHECK: Format: COFF-ARM64X
+// CHECK-NEXT: Arch: aarch64
+// CHECK-NEXT: AddressSize: 64bit
+// CHECK-EMPTY:
+// CHECK-NEXT: Hex dump of section '.a64':
+// CHECK-NEXT: 0x180004000 00500000 04500000
+// CHECK-EMPTY:
+// CHECK-NEXT: Hex dump of section '.ec':
+// CHECK-NEXT: 0x180006000 00500000 04500000
+// CHECK-NEXT: TLSDirectory {
+// CHECK-NEXT: StartAddressOfRawData: 0x0
+// CHECK-NEXT: EndAddressOfRawData: 0x0
+// CHECK-NEXT: AddressOfIndex: 0x180005000
+// CHECK-NEXT: AddressOfCallBacks: 0x0
+// CHECK-NEXT: SizeOfZeroFill: 0x0
+// CHECK-NEXT: Characteristics [ (0x0)
+// CHECK-NEXT: ]
+// CHECK-NEXT: }
+// CHECK-NEXT: HybridObject {
+// CHECK-NEXT: Format: COFF-ARM64EC
+// CHECK-NEXT: Arch: aarch64
+// CHECK-NEXT: AddressSize: 64bit
+// CHECK-EMPTY:
+// CHECK-NEXT: Hex dump of section '.a64':
+// CHECK-NEXT: 0x180004000 00500000 04500000
+// CHECK-EMPTY:
+// CHECK-NEXT: Hex dump of section '.ec':
+// CHECK-NEXT: 0x180006000 00500000 04500000
+// CHECK-NEXT: TLSDirectory {
+// CHECK-NEXT: StartAddressOfRawData: 0x0
+// CHECK-NEXT: EndAddressOfRawData: 0x0
+// CHECK-NEXT: AddressOfIndex: 0x180005000
+// CHECK-NEXT: AddressOfCallBacks: 0x0
+// CHECK-NEXT: SizeOfZeroFill: 0x0
+// CHECK-NEXT: Characteristics [ (0x0)
+// CHECK-NEXT: ]
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+
+// Check that EC _tls_index and _tls_used pull the object file, but their chunks are collected by GC.
+// RUN: llvm-readobj --sections out.dll | FileCheck --check-prefix=DEFEC %s
+// DEFEC-NOT: defec
+// DEFEC: .test
+// DEFEC-NOT: defec
+
+#--- tls-aarch64.s
+ .section .defa64,"dr",discard,_tls_index
+ .globl _tls_index
+_tls_index:
+ .long 0
+
+ .section .defa64,"dr",discard,_tls_used
+ .globl _tls_used
+_tls_used:
+ .xword 0
+ .xword 0
+ .xword _tls_index
+ .xword 0
+ .xword 0
+
+#--- aarch64.s
+ .section .a64,"dr"
+ .rva _tls_index
+ .rva _tls_used
+
+#--- tls-arm64ec.s
+ .section .defec,"dr",discard,_tls_index
+ .globl _tls_index
+_tls_index:
+ .long 0
+
+ .section .defec,"dr",discard,_tls_used
+ .globl _tls_used
+_tls_used:
+ .xword 1
+ .xword 1
+ .xword _tls_index
+ .xword 1
+ .xword 1
+
+ .section .test
+ .long 0
+
+#--- arm64ec.s
+ .section .ec,"dr"
+ .rva _tls_index
+ .rva _tls_used
``````````
</details>
https://github.com/llvm/llvm-project/pull/212845
More information about the llvm-commits
mailing list