[lld] [lld][ELF][x86-64] Do not relax large-model TLS sequences (PR #216188)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 13 14:57:16 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lld
Author: Farid Zakaria (fzakaria)
<details>
<summary>Changes</summary>
GCC uses an indirect PLTOFF64 call to __tls_get_addr for large-model TLSGD and TLSLD sequences. These sequences do not match the fixed-size psABI templates that lld rewrites when relaxing TLS to IE or LE.
Detect the PLTOFF64 relocation following TLSGD or TLSLD and preserve the original sequence instead of rewriting unrelated instruction bytes.
Assisted-by: Codex
---
Full diff: https://github.com/llvm/llvm-project/pull/216188.diff
3 Files Affected:
- (modified) lld/ELF/Arch/X86_64.cpp (+10-3)
- (modified) lld/ELF/RelocScan.h (+2-2)
- (added) lld/test/ELF/x86-64-tls-large-model.s (+55)
``````````diff
diff --git a/lld/ELF/Arch/X86_64.cpp b/lld/ELF/Arch/X86_64.cpp
index fee33b03ffa6a..8ea974cc7cccf 100644
--- a/lld/ELF/Arch/X86_64.cpp
+++ b/lld/ELF/Arch/X86_64.cpp
@@ -659,6 +659,10 @@ void X86_64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
continue;
int64_t addend = rs.getAddend<ELFT>(rel, type);
RelExpr expr;
+ auto isLargeModelTlsCall = [&] {
+ auto next = std::next(it);
+ return next != rels.end() && next->getType(false) == R_X86_64_PLTOFF64;
+ };
// Relocation types that only need a RelExpr set `expr` and break out of
// the switch to reach rs.process(). Types that need special handling
// (fast-path helpers, TLS) call a handler and use `continue`.
@@ -727,12 +731,15 @@ void X86_64::scanSectionImpl(InputSectionBase &sec, Relocs<RelTy> rels,
rs.handleTlsIe(R_GOT_PC, type, offset, addend, sym);
continue;
case R_X86_64_TLSGD:
- if (rs.handleTlsGd(R_TLSGD_PC, R_GOT_PC, R_TPREL, type, offset, addend,
- sym))
+ // GCC's large-model sequence uses PLTOFF64 to call __tls_get_addr and
+ // does not match the fixed-size sequence required by TLS relaxation.
+ if (rs.handleTlsGd(R_TLSGD_PC, isLargeModelTlsCall() ? R_NONE : R_GOT_PC,
+ R_TPREL, type, offset, addend, sym))
++it;
continue;
case R_X86_64_TLSLD:
- if (rs.handleTlsLd(R_TLSLD_PC, type, offset, addend, sym))
+ if (rs.handleTlsLd(R_TLSLD_PC, type, offset, addend, sym,
+ !isLargeModelTlsCall()))
++it;
continue;
case R_X86_64_DTPOFF32:
diff --git a/lld/ELF/RelocScan.h b/lld/ELF/RelocScan.h
index c39089840d2fb..fe3bafab8e373 100644
--- a/lld/ELF/RelocScan.h
+++ b/lld/ELF/RelocScan.h
@@ -127,8 +127,8 @@ class RelocScan {
// Handle TLS Local-Dynamic relocation. Returns true if the __tls_get_addr
// call should be skipped (i.e., caller should ++it).
bool handleTlsLd(RelExpr sharedExpr, RelType type, uint64_t offset,
- int64_t addend, Symbol &sym) {
- if (ctx.arg.shared) {
+ int64_t addend, Symbol &sym, bool enableLdToLe = true) {
+ if (ctx.arg.shared || !enableLdToLe) {
ctx.needsTlsLd.store(true, std::memory_order_relaxed);
sec->addReloc({sharedExpr, type, offset, addend, &sym});
return false;
diff --git a/lld/test/ELF/x86-64-tls-large-model.s b/lld/test/ELF/x86-64-tls-large-model.s
new file mode 100644
index 0000000000000..e10765acf46dd
--- /dev/null
+++ b/lld/test/ELF/x86-64-tls-large-model.s
@@ -0,0 +1,55 @@
+# REQUIRES: x86
+
+## GCC uses PLTOFF64 in large-model TLSGD and TLSLD sequences. They do not
+## match the fixed-size sequences that the linker can relax to IE or LE.
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
+# RUN: ld.lld -e _start %t.o -o %t.exe
+# RUN: llvm-objdump --no-print-imm-hex --no-show-raw-insn -d %t.exe | FileCheck %s
+
+# CHECK-LABEL: <_start>:
+# CHECK: leaq {{.*}}(%rip), %rdi
+# CHECK-NEXT: movabsq ${{.*}}, %rax
+# CHECK-NEXT: addq %rbx, %rax
+# CHECK-NEXT: callq *%rax
+# CHECK-NEXT: leaq {{.*}}(%rip), %rdi
+# CHECK-NEXT: movabsq ${{.*}}, %rax
+# CHECK-NEXT: addq %rbx, %rax
+# CHECK-NEXT: callq *%rax
+# CHECK-NEXT: leaq -4(%rax), %rax
+# CHECK-NEXT: retq
+
+.section .ltext,"axl", at progbits
+.globl _start
+.type _start, @function
+_start:
+.Lpic:
+ movabsq $_GLOBAL_OFFSET_TABLE_-.Lpic, %r11
+ leaq .Lpic(%rip), %rbx
+ addq %r11, %rbx
+
+ leaq x at tlsgd(%rip), %rdi
+ movabsq $__tls_get_addr at PLTOFF, %rax
+ addq %rbx, %rax
+ callq *%rax
+
+ leaq x at tlsld(%rip), %rdi
+ movabsq $__tls_get_addr at PLTOFF, %rax
+ addq %rbx, %rax
+ callq *%rax
+ leaq x at dtpoff(%rax), %rax
+ retq
+.size _start, .-_start
+
+.hidden __tls_get_addr
+.globl __tls_get_addr
+.type __tls_get_addr, @function
+__tls_get_addr:
+ retq
+.size __tls_get_addr, .-__tls_get_addr
+
+.section .tbss,"awT", at nobits
+.hidden x
+.globl x
+x:
+ .zero 4
``````````
</details>
https://github.com/llvm/llvm-project/pull/216188
More information about the llvm-commits
mailing list