[lld] [lld][ELF] Support the 32-bit GOTTPOFF forms used by ILP32 code (PR #221557)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 8 01:29:14 PDT 2026
https://github.com/HomuHomu833 updated https://github.com/llvm/llvm-project/pull/221557
>From 77e2d17a827c6b3cc2fdbb27ae6ab99a5fd9ee06 Mon Sep 17 00:00:00 2001
From: Homura <egzozu.be.bas at gmail.com>
Date: Sun, 6 Sep 2026 16:54:20 +0300
Subject: [PATCH] [lld][ELF] Support the 32-bit GOTTPOFF forms used by ILP32
code
relaxTlsIeToLe only handled the REX.W movq/addq encodings. ILP32 code
materializes the TLS offset with a 32-bit operand, so the relocation lands on
movl/addl and linking failed with "R_X86_64_GOTTPOFF must be used in MOVQ or
ADDQ instructions only".
Select the expected encodings from the ELF class rather than probing the
instruction bytes, so ILP32 accepts only movl/addl and LP64 only movq/addq,
and each reports its own diagnostic. Every rewrite keeps the original
instruction length: movl becomes "movl $foo", addl becomes "leal foo(%reg)",
and addl with ESP or R12D becomes "addl $foo" because LEA with those registers
would need a SIB byte and no longer fit.
Fixes #161059
---
lld/ELF/Arch/X86_64.cpp | 35 ++++++++++++++++++++++++-
lld/test/ELF/x86-64-tls-ie-x32.s | 45 ++++++++++++++++++++++++++++++++
2 files changed, 79 insertions(+), 1 deletion(-)
create mode 100644 lld/test/ELF/x86-64-tls-ie-x32.s
diff --git a/lld/ELF/Arch/X86_64.cpp b/lld/ELF/Arch/X86_64.cpp
index f172080dc2b6a..458952ed2d2e2 100644
--- a/lld/ELF/Arch/X86_64.cpp
+++ b/lld/ELF/Arch/X86_64.cpp
@@ -917,7 +917,40 @@ void X86_64::relaxTlsIeToLe(uint8_t *loc, const Relocation &rel,
// because LEA with these registers needs 4 bytes to encode and thus
// wouldn't fit the space.
- if (memcmp(inst, "\x48\x03\x25", 3) == 0) {
+ if (!ctx.arg.is64) {
+ // ILP32 code uses a 32-bit operand, so there is no REX.W and, for
+ // %eax-%edi, no REX prefix at all. The memory operand is RIP-relative,
+ // so REX.R is the only bit that can appear here.
+ const bool hasRex = loc[-3] == 0x40 || loc[-3] == 0x44;
+ if (loc[-2] == 0x8b) {
+ // "movl foo at gottpoff(%rip),%reg" -> "movl $foo,%reg"
+ // "movl foo at gottpoff(%rip),%r[8-15]d" -> "movl $foo,%r[8-15]d"
+ loc[-2] = 0xc7;
+ *regSlot = 0xc0 | reg;
+ // Move the R bit to the B bit.
+ if (hasRex)
+ loc[-3] = 0x41;
+ } else if (loc[-2] == 0x03 && reg == 4) {
+ // "addl foo at gottpoff(%rip),%esp" -> "addl $foo,%esp"
+ // "addl foo at gottpoff(%rip),%r12d" -> "addl $foo,%r12d"
+ loc[-2] = 0x81;
+ *regSlot = 0xc0 | reg;
+ if (hasRex)
+ loc[-3] = 0x41;
+ } else if (loc[-2] == 0x03) {
+ // "addl foo at gottpoff(%rip),%reg" -> "leal foo(%reg),%reg"
+ // "addl foo at gottpoff(%rip),%r[8-15]d" -> "leal foo(%r[8-15]),%r[8-15]d"
+ loc[-2] = 0x8d;
+ *regSlot = 0x80 | (reg << 3) | reg;
+ if (hasRex)
+ loc[-3] = 0x45;
+ } else {
+ Err(ctx)
+ << getErrorLoc(ctx, loc - 2)
+ << "R_X86_64_GOTTPOFF must be used in MOVL or ADDL instructions "
+ "only";
+ }
+ } else if (memcmp(inst, "\x48\x03\x25", 3) == 0) {
// "addq foo at gottpoff(%rip),%rsp" -> "addq $foo,%rsp"
memcpy(inst, "\x48\x81\xc4", 3);
} else if (memcmp(inst, "\x4c\x03\x25", 3) == 0) {
diff --git a/lld/test/ELF/x86-64-tls-ie-x32.s b/lld/test/ELF/x86-64-tls-ie-x32.s
new file mode 100644
index 0000000000000..2415b80323dea
--- /dev/null
+++ b/lld/test/ELF/x86-64-tls-ie-x32.s
@@ -0,0 +1,45 @@
+# REQUIRES: x86
+## ILP32 code materializes the TLS offset with a 32-bit operand, so GOTTPOFF
+## lands on movl/addl rather than the REX.W movq/addq forms.
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux-gnux32 %s -o %t.o
+# RUN: ld.lld -m elf32_x86_64 %t.o -o %t1
+# RUN: llvm-readobj -r %t1 | FileCheck --check-prefix=NORELOC %s
+# RUN: llvm-objdump --no-print-imm-hex -d --no-show-raw-insn %t1 | FileCheck --check-prefix=DISASM %s
+
+# NORELOC: Relocations [
+# NORELOC-NEXT: ]
+
+# DISASM: <_start>:
+# DISASM-NEXT: movl $-4, %eax
+# DISASM-NEXT: leal -4(%rcx), %ecx
+## ADD is used instead of LEA for ESP, which would need a SIB byte.
+# DISASM-NEXT: addl $-4, %esp
+# DISASM-NEXT: movl $-4, %r8d
+# DISASM-NEXT: leal -4(%r9), %r9d
+
+.type tls0, at object
+.section .tbss,"awT", at nobits
+.globl tls0
+.align 4
+tls0:
+ .long 0
+ .size tls0, 4
+
+.text
+.globl _start
+_start:
+ movl 0(%rip), %eax
+ .reloc .-4, R_X86_64_GOTTPOFF, tls0-4
+
+ addl 0(%rip), %ecx
+ .reloc .-4, R_X86_64_GOTTPOFF, tls0-4
+
+ addl 0(%rip), %esp
+ .reloc .-4, R_X86_64_GOTTPOFF, tls0-4
+
+ movl 0(%rip), %r8d
+ .reloc .-4, R_X86_64_GOTTPOFF, tls0-4
+
+ addl 0(%rip), %r9d
+ .reloc .-4, R_X86_64_GOTTPOFF, tls0-4
More information about the llvm-commits
mailing list