From 11fa8634cf14b6dab1890e6bc46b3bbfcb7546d4 Mon Sep 17 00:00:00 2001 From: deepakshirkem Date: Sat, 23 May 2026 18:13:34 +0530 Subject: [PATCH 1/6] [AArch64] Fix IE->LE TLS relaxation for global symbols Extend IE->LE relaxation in tls_gottprel_page and tls_gottprel_lo to cover global TLS symbols in static executables. Previously only local symbols (without ReserveGOT) were relaxed. Fixes part of #1004 Signed-off-by: deepakshirkem --- lib/Target/AArch64/AArch64Relocator.cpp | 36 +++++++++++-------------- test/AArch64/standalone/TLS_IE/IE.test | 4 +-- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/lib/Target/AArch64/AArch64Relocator.cpp b/lib/Target/AArch64/AArch64Relocator.cpp index b298704f4..946e744db 100644 --- a/lib/Target/AArch64/AArch64Relocator.cpp +++ b/lib/Target/AArch64/AArch64Relocator.cpp @@ -4,7 +4,6 @@ // SPDX-License-Identifier: BSD-3-Clause //===----------------------------------------------------------------------===// - #include "AArch64Relocator.h" #include "AArch64InsnHelpers.h" #include "AArch64PLT.h" @@ -52,8 +51,7 @@ static uint64_t getSigningSchema(const Relocation &pReloc) { /// helper_DynRel - Get an relocation entry in .rela.dyn Relocation *helper_DynRel_init(ELFObjectFile *Obj, Relocation *R, ResolveInfo *pSym, Fragment *F, uint32_t pOffset, - Relocator::Type pType, - AArch64LDBackend &B) { + Relocator::Type pType, AArch64LDBackend &B) { Relocation *rela_entry = nullptr; if (pType == R_AARCH64_TLSDESC) @@ -190,9 +188,8 @@ bool AArch64Relocator::relocNeedsDynRel(Relocation &pReloc) const { pReloc.type() == llvm::ELF::R_AARCH64_ABS32 || pReloc.type() == llvm::ELF::R_AARCH64_ABS16 || pReloc.type() == llvm::ELF::R_AARCH64_AUTH_ABS64; - return getTarget().symbolNeedsDynRel( - *rsym, (rsym->reserved() & ReservePLT), - isAbsReloc); + return getTarget().symbolNeedsDynRel(*rsym, (rsym->reserved() & ReservePLT), + isAbsReloc); } Relocator::Result AArch64Relocator::applyRelocation(Relocation &pRelocation) { @@ -252,12 +249,10 @@ void AArch64Relocator::scanLocalReloc(InputFile &pInput, Relocation &pReloc, rsym->setReserved(rsym->reserved() | ReserveRel); getTarget().checkAndSetHasTextRel(pSection); // set up the dyn rel directly - Relocation::Type relType = - isAuthAbs ? llvm::ELF::R_AARCH64_AUTH_RELATIVE - : llvm::ELF::R_AARCH64_RELATIVE; + Relocation::Type relType = isAuthAbs ? llvm::ELF::R_AARCH64_AUTH_RELATIVE + : llvm::ELF::R_AARCH64_RELATIVE; helper_DynRel_init(Obj, &pReloc, rsym, pReloc.targetRef()->frag(), - pReloc.targetRef()->offset(), relType, - m_Target); + pReloc.targetRef()->offset(), relType, m_Target); } } return; @@ -389,8 +384,8 @@ void AArch64Relocator::scanGlobalReloc(InputFile &pInput, Relocation &pReloc, // for signed pointers" if (isAuthAbs) { config().raise(Diag::non_pic_relocation) - << getName(pReloc.type()) << pReloc.symInfo()->name() - << pReloc.getSourcePath(config().options()); + << getName(pReloc.type()) << pReloc.symInfo()->name() + << pReloc.getSourcePath(config().options()); m_Target.getModule().setFailure(true); return; } @@ -416,10 +411,8 @@ void AArch64Relocator::scanGlobalReloc(InputFile &pInput, Relocation &pReloc, relType = isAuthAbs ? llvm::ELF::R_AARCH64_AUTH_RELATIVE : llvm::ELF::R_AARCH64_RELATIVE; } - helper_DynRel_init( - Obj, &pReloc, rsym, pReloc.targetRef()->frag(), - pReloc.targetRef()->offset(), - relType, m_Target); + helper_DynRel_init(Obj, &pReloc, rsym, pReloc.targetRef()->frag(), + pReloc.targetRef()->offset(), relType, m_Target); } } } @@ -1032,7 +1025,8 @@ Relocator::Result ld64_got_lo12(Relocation &pReloc, AArch64Relocator &pParent) { } // R_AARCH64_LD64_GOTPAGE_LO15: G(GDAT(S)) - Page(GOT) -Relocator::Result ld64_gotpage_lo15(Relocation &pReloc, AArch64Relocator &pParent) { +Relocator::Result ld64_gotpage_lo15(Relocation &pReloc, + AArch64Relocator &pParent) { if (!(pReloc.symInfo()->reserved() & Relocator::ReserveGOT)) { return Relocator::BadReloc; } @@ -1157,7 +1151,8 @@ Relocator::Result tls_gottprel_page(Relocation &pReloc, DiagnosticEngine *DiagEngine = pParent.config().getDiagEngine(); Relocator::DWord A = pReloc.addend(); - if (!(pReloc.symInfo()->reserved() & Relocator::ReserveGOT)) { + if (!(pReloc.symInfo()->reserved() & Relocator::ReserveGOT) || + pParent.config().isCodeStatic()) { Relocator::DWord X = pParent.getSymValue(&pReloc) + AArch64LDBackend::getStaticTCBSize(); // Convert to movz @@ -1182,7 +1177,8 @@ Relocator::Result tls_gottprel_lo(Relocation &pReloc, AArch64Relocator &pParent) { Relocator::DWord A = pReloc.addend(); - if (!(pReloc.symInfo()->reserved() & Relocator::ReserveGOT)) { + if (!(pReloc.symInfo()->reserved() & Relocator::ReserveGOT) || + pParent.config().isCodeStatic()) { Relocator::DWord X = pParent.getSymValue(&pReloc) + AArch64LDBackend::getStaticTCBSize(); // Convert to movk diff --git a/test/AArch64/standalone/TLS_IE/IE.test b/test/AArch64/standalone/TLS_IE/IE.test index e5b0050ef..600ccedf9 100644 --- a/test/AArch64/standalone/TLS_IE/IE.test +++ b/test/AArch64/standalone/TLS_IE/IE.test @@ -8,8 +8,8 @@ RUN: %clang %clangopts -target aarch64 %p/Inputs/f.c -c -o %t3.o RUN: %link %linkopts -static -march aarch64 %t2.o %t3.o -z max-page-size=0x1000 -o %t2.out CHECK: _test_tls_IE -CHECK: b0000000 adrp -CHECK: f9400000 ldr +CHECK: d2a00000 movz +CHECK: f2800200 movk CHECK: _test_tls_IE_local CHECK: d2a00000 movz CHECK: f2800280 movk From c4dacb18d51ee7470c6689cd7ab81ca6f2a23b3c Mon Sep 17 00:00:00 2001 From: deepakshirkem Date: Sat, 23 May 2026 20:17:23 +0530 Subject: [PATCH 2/6] [AArch64] Fix register preservation in TLSDESC->LE relaxation Preserve destination register (Rd) when relaxing TLSDESC_LD64_LO12 to movk in static executables. Previously x0 was hardcoded. Also extend relaxation to cover global symbols in static executables by adding isCodeStatic() check, matching the IE->LE fix. Fixes part of #1004 Signed-off-by: deepakshirkem --- lib/Target/AArch64/AArch64Relocator.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/Target/AArch64/AArch64Relocator.cpp b/lib/Target/AArch64/AArch64Relocator.cpp index 946e744db..65740703e 100644 --- a/lib/Target/AArch64/AArch64Relocator.cpp +++ b/lib/Target/AArch64/AArch64Relocator.cpp @@ -1247,11 +1247,12 @@ Relocator::Result tls_tlsdesc_lo(Relocation &pReloc, AArch64Relocator &pParent) { Relocator::DWord A = pReloc.addend(); - if (!(pReloc.symInfo()->reserved() & Relocator::ReserveGOT)) { + if (!(pReloc.symInfo()->reserved() & Relocator::ReserveGOT) || + pParent.config().isCodeStatic()) { Relocator::DWord X = pParent.getSymValue(&pReloc) + AArch64LDBackend::getStaticTCBSize(); - // Convert to movk, save to x0 - uint32_t movk = 0xF2800000; + // Convert to movk, preserve original register + uint32_t movk = 0xF2800000 | (pReloc.target() & 0x0000001F); pReloc.target() = helper_reencode_movzk_imm(movk, X); return Relocator::OK; } @@ -1262,10 +1263,6 @@ Relocator::Result tls_tlsdesc_lo(Relocation &pReloc, Relocator::DWord GX = helper_get_page_offset(GOT_S + A); pReloc.target() = helper_reencode_ldst_pos_imm(pReloc.target(), GX >> 3); - // Convert Rt to X0 if static - if (pParent.config().isCodeStatic()) - pReloc.target() = pReloc.target() & ~0x1F; - return Relocator::OK; } From a15b12f562ab143382842870cf680ca60e32777b Mon Sep 17 00:00:00 2001 From: deepakshirkem Date: Sat, 23 May 2026 20:40:14 +0530 Subject: [PATCH 3/6] [AArch64] Fix TLSDESC->LE relaxation for global symbols in static executables Extend TLSDESC->LE relaxation in tls_tlsdesc_page to cover global TLS symbols in static executables by adding isCodeStatic() check. Also fix tls_tlsdesc_lo to always use x0 for the relaxed movk instruction, matching LLD behavior and ensuring the TLS offset is in the correct register after relaxation. Add test for TLSDESC->LE relaxation covering both global and local TLS symbols. Fixes part of #1004 Signed-off-by: deepakshirkem --- lib/Target/AArch64/AArch64Relocator.cpp | 5 +++-- test/AArch64/standalone/TLS_DESC/TLS_DESC.test | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 test/AArch64/standalone/TLS_DESC/TLS_DESC.test diff --git a/lib/Target/AArch64/AArch64Relocator.cpp b/lib/Target/AArch64/AArch64Relocator.cpp index 65740703e..466e65d2f 100644 --- a/lib/Target/AArch64/AArch64Relocator.cpp +++ b/lib/Target/AArch64/AArch64Relocator.cpp @@ -1221,7 +1221,8 @@ Relocator::Result tls_tlsdesc_page(Relocation &pReloc, AArch64Relocator &pParent) { Relocator::DWord A = pReloc.addend(); - if (!(pReloc.symInfo()->reserved() & Relocator::ReserveGOT)) { + if (!(pReloc.symInfo()->reserved() & Relocator::ReserveGOT) || + pParent.config().isCodeStatic()) { Relocator::DWord X = pParent.getSymValue(&pReloc) + AArch64LDBackend::getStaticTCBSize(); // Convert to movz @@ -1252,7 +1253,7 @@ Relocator::Result tls_tlsdesc_lo(Relocation &pReloc, Relocator::DWord X = pParent.getSymValue(&pReloc) + AArch64LDBackend::getStaticTCBSize(); // Convert to movk, preserve original register - uint32_t movk = 0xF2800000 | (pReloc.target() & 0x0000001F); + uint32_t movk = 0xF2800000; pReloc.target() = helper_reencode_movzk_imm(movk, X); return Relocator::OK; } diff --git a/test/AArch64/standalone/TLS_DESC/TLS_DESC.test b/test/AArch64/standalone/TLS_DESC/TLS_DESC.test new file mode 100644 index 000000000..f5096958b --- /dev/null +++ b/test/AArch64/standalone/TLS_DESC/TLS_DESC.test @@ -0,0 +1,14 @@ +RUN: %clangas %clangasopts -filetype obj -target-cpu generic -target-feature +neon -mrelax-all %p/Inputs/t.s -o %t.o +RUN: %link %linkopts -march aarch64 -static -z max-page-size=0x1000 %t.o -o %t.out +RUN: llvm-objdump -d %t.out | %filecheck %s + +CHECK: _test_tls_desc +CHECK: d2a00000 movz +CHECK: f2800200 movk +CHECK: d503201f nop +CHECK: d503201f nop +CHECK: _test_tls_desc_local +CHECK: d2a00000 movz +CHECK: f2800280 movk +CHECK: d503201f nop +CHECK: d503201f nop From a163b2b1693e4f4c24bcf627f23e2922869e0cf1 Mon Sep 17 00:00:00 2001 From: deepakshirkem Date: Sun, 24 May 2026 01:27:37 +0530 Subject: [PATCH 4/6] [AArch64] Implement TLSDESC->IE relaxation for PIE executables For PIE executables, relax TLSDESC sequences to Initial Exec (IE) instead of keeping the expensive TLSDESC runtime resolver call. Changes: - scanLocalReloc/scanGlobalReloc: detect isPIE() and create IE GOT entry with R_AARCH64_TLS_TPREL64 dynamic relocation instead of TLSDESC GOT with R_AARCH64_TLSDESC - tls_tlsdesc_lo: rewrite ldr to IE GOT load into x0 for PIE - tls_tlsdesc_add: emit nop for PIE (matching static behavior) - tls_call: emit nop for PIE (matching static behavior) - Update DESC.test placeholder with correct CHECK lines - Add TLS_DESC_PIE.test to verify TLSDESC->IE relaxation Fixes part of #1004 Signed-off-by: deepakshirkem --- lib/Target/AArch64/AArch64Relocator.cpp | 33 +++++++++++++++---- test/AArch64/standalone/TLS_DESC/DESC.test | 18 ++++++---- .../standalone/TLS_DESC/TLS_DESC_PIE.test | 14 ++++++++ 3 files changed, 52 insertions(+), 13 deletions(-) create mode 100644 test/AArch64/standalone/TLS_DESC/TLS_DESC_PIE.test diff --git a/lib/Target/AArch64/AArch64Relocator.cpp b/lib/Target/AArch64/AArch64Relocator.cpp index 466e65d2f..1db47f2ab 100644 --- a/lib/Target/AArch64/AArch64Relocator.cpp +++ b/lib/Target/AArch64/AArch64Relocator.cpp @@ -332,6 +332,14 @@ void AArch64Relocator::scanLocalReloc(InputFile &pInput, Relocation &pReloc, G->setValueType(GOT::TLSStaticSymbolValue); return; } + if (config().options().isPIE()) { + // PIE executable: relax TLSDESC to IE + AArch64GOT *G = m_Target.createGOT(GOT::TLS_IE, Obj, rsym); + helper_DynRel_init(Obj, &pReloc, rsym, G, 0x0, + llvm::ELF::R_AARCH64_TLS_TPREL64, m_Target); + rsym->setReserved(rsym->reserved() | ReserveGOT); + return; + } AArch64GOT *G = m_Target.createGOT(GOT::TLS_DESC, Obj, rsym); helper_DynRel_init(Obj, &pReloc, rsym, G->getFirst(), 0x0, llvm::ELF::R_AARCH64_TLSDESC, m_Target); @@ -564,6 +572,14 @@ void AArch64Relocator::scanGlobalReloc(InputFile &pInput, Relocation &pReloc, G->setValueType(GOT::TLSStaticSymbolValue); return; } + if (config().options().isPIE()) { + // PIE executable: relax TLSDESC to IE + AArch64GOT *G = m_Target.createGOT(GOT::TLS_IE, Obj, rsym); + helper_DynRel_init(Obj, &pReloc, rsym, G, 0x0, + llvm::ELF::R_AARCH64_TLS_TPREL64, m_Target); + rsym->setReserved(rsym->reserved() | ReserveGOT); + return; + } AArch64GOT *G = m_Target.createGOT(GOT::TLS_DESC, Obj, rsym); helper_DynRel_init(Obj, &pReloc, rsym, G->getFirst(), 0x0, llvm::ELF::R_AARCH64_TLSDESC, m_Target); @@ -1247,23 +1263,28 @@ Relocator::Result tls_tlsdesc_page(Relocation &pReloc, Relocator::Result tls_tlsdesc_lo(Relocation &pReloc, AArch64Relocator &pParent) { Relocator::DWord A = pReloc.addend(); - if (!(pReloc.symInfo()->reserved() & Relocator::ReserveGOT) || pParent.config().isCodeStatic()) { Relocator::DWord X = pParent.getSymValue(&pReloc) + AArch64LDBackend::getStaticTCBSize(); - // Convert to movk, preserve original register uint32_t movk = 0xF2800000; pReloc.target() = helper_reencode_movzk_imm(movk, X); return Relocator::OK; } - + if (pParent.config().options().isPIE()) { + uint32_t insn = 0xF9400000; // ldr x0, [x0, #0] + Relocator::Address GOT_S = pParent.getTarget() + .findEntryInGOT(pReloc.symInfo()) + ->getAddr(pParent.config().getDiagEngine()); + Relocator::DWord GX = helper_get_page_offset(GOT_S + A); + pReloc.target() = helper_reencode_ldst_pos_imm(insn, GX >> 3); + return Relocator::OK; + } Relocator::Address GOT_S = pParent.getTarget() .findEntryInGOT(pReloc.symInfo()) ->getAddr(pParent.config().getDiagEngine()); Relocator::DWord GX = helper_get_page_offset(GOT_S + A); pReloc.target() = helper_reencode_ldst_pos_imm(pReloc.target(), GX >> 3); - return Relocator::OK; } @@ -1271,7 +1292,7 @@ Relocator::Result tls_tlsdesc_lo(Relocation &pReloc, Relocator::Result tls_tlsdesc_add(Relocation &pReloc, AArch64Relocator &pParent) { Relocator::DWord A = pReloc.addend(); - if (pParent.config().isCodeStatic()) { + if (pParent.config().isCodeStatic() || pParent.config().options().isPIE()) { // Convert to nop pReloc.target() = 0xD503201F; return Relocator::OK; @@ -1288,7 +1309,7 @@ Relocator::Result tls_tlsdesc_add(Relocation &pReloc, // R_AARCH64_TLSDESC_CALL Relocator::Result tls_call(Relocation &pReloc, AArch64Relocator &pParent) { - if (pParent.config().isCodeStatic()) { + if (pParent.config().isCodeStatic() || pParent.config().options().isPIE()) { // Convert to nop pReloc.target() = 0xD503201F; return Relocator::OK; diff --git a/test/AArch64/standalone/TLS_DESC/DESC.test b/test/AArch64/standalone/TLS_DESC/DESC.test index 6adac0826..e02a8ee71 100644 --- a/test/AArch64/standalone/TLS_DESC/DESC.test +++ b/test/AArch64/standalone/TLS_DESC/DESC.test @@ -1,10 +1,14 @@ RUN: %clangas %clangasopts -filetype obj -target-cpu generic -target-feature +neon -mrelax-all %p/Inputs/t.s -o %t.o -RUN: %link %linkopts -march aarch64 -static %t.o -z max-page-size=0x1000 -o %t.out +RUN: %link %linkopts -march aarch64 -static -z max-page-size=0x1000 %t.o -o %t.out RUN: llvm-objdump -d %t.out | %filecheck %s -#CHECK: {{.*}} <_test_tls_desc>: -#CHECK: {{.*}} adrp x0, 0x1000 -#CHECK: {{.*}} ldr x0, [x0] -#CHECK: {{.*}} <_test_tls_desc_local>: -#CHECK: {{.*}} adrp x0, 0x1000 -#CHECK: {{.*}} ldr x0, [x0, #0x8] +CHECK: _test_tls_desc +CHECK: d2a00000 movz +CHECK: f2800200 movk +CHECK: d503201f nop +CHECK: d503201f nop +CHECK: _test_tls_desc_local +CHECK: d2a00000 movz +CHECK: f2800280 movk +CHECK: d503201f nop +CHECK: d503201f nop \ No newline at end of file diff --git a/test/AArch64/standalone/TLS_DESC/TLS_DESC_PIE.test b/test/AArch64/standalone/TLS_DESC/TLS_DESC_PIE.test new file mode 100644 index 000000000..1fa4ae261 --- /dev/null +++ b/test/AArch64/standalone/TLS_DESC/TLS_DESC_PIE.test @@ -0,0 +1,14 @@ +RUN: %clangas %clangasopts -filetype obj -target-cpu generic -target-feature +neon -mrelax-all %p/Inputs/t.s -o %t.o +RUN: %link %linkopts -march aarch64 -pie -z max-page-size=0x1000 %t.o -o %t.out +RUN: llvm-objdump -d %t.out | %filecheck %s + +CHECK: _test_tls_desc +CHECK: b0000000 adrp +CHECK: f9408000 ldr +CHECK: d503201f nop +CHECK: d503201f nop +CHECK: _test_tls_desc_local +CHECK: b0000000 adrp +CHECK: f9408400 ldr +CHECK: d503201f nop +CHECK: d503201f nop From 154847187245105a13731d038d095056602cdd30 Mon Sep 17 00:00:00 2001 From: deepakshirkem Date: Sun, 24 May 2026 01:50:15 +0530 Subject: [PATCH 5/6] [AArch64] Add comprehensive TLS relaxation verification tests Add TLS_Relaxation_Verify test using real compiler-generated TLS relocations to verify end-to-end correctness of relaxations: - TLSDESC->LE for static executables (movz+movk+nop+nop) - TLSDESC->IE for PIE executables (adrp+ldr+nop+nop) - R_AARCH64_TLS_TPREL64 dynamic reloc present in PIE Add IE_PIE test to verify IE relocations remain as GOT-indirect adrp+ldr in PIE executables. Fixes part of #1004 Signed-off-by: deepakshirkem --- .../AArch64/standalone/TLS_IE_PIE/IE_PIE.test | 12 +++++++ .../AArch64/standalone/TLS_IE_PIE/Inputs/ie.s | 29 ++++++++++++++++ .../TLS_Relaxation_Verify/Inputs/tls_test.c | 10 ++++++ .../TLS_Relaxation_Verify.test | 34 +++++++++++++++++++ 4 files changed, 85 insertions(+) create mode 100644 test/AArch64/standalone/TLS_IE_PIE/IE_PIE.test create mode 100644 test/AArch64/standalone/TLS_IE_PIE/Inputs/ie.s create mode 100644 test/AArch64/standalone/TLS_Relaxation_Verify/Inputs/tls_test.c create mode 100644 test/AArch64/standalone/TLS_Relaxation_Verify/TLS_Relaxation_Verify.test diff --git a/test/AArch64/standalone/TLS_IE_PIE/IE_PIE.test b/test/AArch64/standalone/TLS_IE_PIE/IE_PIE.test new file mode 100644 index 000000000..4d19529c5 --- /dev/null +++ b/test/AArch64/standalone/TLS_IE_PIE/IE_PIE.test @@ -0,0 +1,12 @@ +# Verify that IE TLS relocations remain as GOT-indirect adrp+ldr +# in PIE executables (IE stays as IE, no relaxation to LE). +RUN: %clangas %clangasopts -filetype obj -target-cpu generic -target-feature +neon -mrelax-all %p/Inputs/ie.s -o %t.o +RUN: %link %linkopts -march aarch64 -pie -z max-page-size=0x1000 %t.o -o %t.out +RUN: llvm-objdump -d %t.out | %filecheck %s + +CHECK: _test_tls_IE +CHECK: b0000000 adrp +CHECK: f9408000 ldr +CHECK: _test_tls_IE_local +CHECK: b0000000 adrp +CHECK: f9408400 ldr diff --git a/test/AArch64/standalone/TLS_IE_PIE/Inputs/ie.s b/test/AArch64/standalone/TLS_IE_PIE/Inputs/ie.s new file mode 100644 index 000000000..e647b896d --- /dev/null +++ b/test/AArch64/standalone/TLS_IE_PIE/Inputs/ie.s @@ -0,0 +1,29 @@ + .global tlsievar + .section .tbss,"awT",%nobits + .align 2 + .type tlsievar, %object + .size tlsievar, 4 +tlsievar: + .zero 4 + + .align 2 + .type l_tlsievar, %object + .size l_tlsievar, 4 +l_tlsievar: + .zero 4 + +.text +_test_tls_IE: + + // R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 tlsievar + adrp x0, :gottprel:tlsievar + // R_AARCH64_TLSIE_GOTTPREL_LO12_NC tlsievar + ldr x0, [x0, :gottprel_lo12:tlsievar] + +_test_tls_IE_local: + + // R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 l_tlsievar + adrp x0, :gottprel:l_tlsievar + // R_AARCH64_TLSIE_GOTTPREL_LO12_NC l_tlsievar + ldr x0, [x0, :gottprel_lo12:l_tlsievar] + diff --git a/test/AArch64/standalone/TLS_Relaxation_Verify/Inputs/tls_test.c b/test/AArch64/standalone/TLS_Relaxation_Verify/Inputs/tls_test.c new file mode 100644 index 000000000..1bd9ed945 --- /dev/null +++ b/test/AArch64/standalone/TLS_Relaxation_Verify/Inputs/tls_test.c @@ -0,0 +1,10 @@ +/* Verify TLS relaxations for AArch64. + * Global-dynamic (TLSDESC) should be relaxed to: + * - Local Exec (movz+movk) for static executables + * - Initial Exec (adrp+ldr) for PIE executables + */ +__thread int tls_global = 42; +static __thread int tls_local = 100; + +int get_global() { return tls_global; } +int get_local() { return tls_local; } diff --git a/test/AArch64/standalone/TLS_Relaxation_Verify/TLS_Relaxation_Verify.test b/test/AArch64/standalone/TLS_Relaxation_Verify/TLS_Relaxation_Verify.test new file mode 100644 index 000000000..2897debca --- /dev/null +++ b/test/AArch64/standalone/TLS_Relaxation_Verify/TLS_Relaxation_Verify.test @@ -0,0 +1,34 @@ +RUN: %clang %clangopts -target aarch64-unknown-none-elf \ +RUN: -ftls-model=global-dynamic -fPIC \ +RUN: -c %p/Inputs/tls_test.c -o %t.o +RUN: %link %linkopts -march aarch64 -static \ +RUN: -z max-page-size=0x1000 %t.o -o %t.static.out +RUN: llvm-objdump -d %t.static.out | %filecheck %s --check-prefix=STATIC +RUN: %link %linkopts -march aarch64 -pie \ +RUN: -z max-page-size=0x1000 %t.o -o %t.pie.out +RUN: llvm-objdump -d %t.pie.out | %filecheck %s --check-prefix=PIE +RUN: %readelf -r %t.pie.out | %filecheck %s --check-prefix=PIE-RELOC + +STATIC: : +STATIC: d2a00000 movz +STATIC: f2800200 movk +STATIC: d503201f nop +STATIC: d503201f nop +STATIC: : +STATIC: d2a00000 movz +STATIC: f2800280 movk +STATIC: d503201f nop +STATIC: d503201f nop + +PIE: : +PIE: b0000000 adrp +PIE: f9408400 ldr +PIE: d503201f nop +PIE: d503201f nop +PIE: : +PIE: b0000000 adrp +PIE: f9408800 ldr +PIE: d503201f nop +PIE: d503201f nop + +PIE-RELOC: R_AARCH64_TLS_TPREL64 From eb7c74d3e55d49b9d0db66cbf61f804faf35cb66 Mon Sep 17 00:00:00 2001 From: deepakshirkem Date: Fri, 19 Jun 2026 00:31:02 +0530 Subject: [PATCH 6/6] [AArch64] Fix TLSDESC->IE relaxation for local symbols in PIE Local TLS symbols in PIE executables were incorrectly getting R_AARCH64_TLS_TPREL64 dynamic relocations. Since local symbols have no name in the dynamic symbol table, the dynamic linker cannot resolve these relocations, causing a segmentation fault at runtime. Fix by checking rsym->isLocal() in the PIE path: - Local symbols: fill GOT at link time (TLSStaticSymbolValue) - Global symbols: emit R_AARCH64_TLS_TPREL64 dynamic reloc This bug was caught by adding runtime tests that actually execute the linked binary under QEMU. Add TLS_RunTest to verify TLS relaxations work correctly at runtime for static, PIE, and dynamic executables. Fixes part of #1004 Signed-off-by: deepakshirkem --- lib/Target/AArch64/AArch64Relocator.cpp | 20 ++++++++++++--- .../standalone/TLS_RunTest/Inputs/tls_run.c | 12 +++++++++ .../standalone/TLS_RunTest/TLS_RunTest.test | 25 +++++++++++++++++++ 3 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 test/AArch64/standalone/TLS_RunTest/Inputs/tls_run.c create mode 100644 test/AArch64/standalone/TLS_RunTest/TLS_RunTest.test diff --git a/lib/Target/AArch64/AArch64Relocator.cpp b/lib/Target/AArch64/AArch64Relocator.cpp index 1db47f2ab..9c4aec12e 100644 --- a/lib/Target/AArch64/AArch64Relocator.cpp +++ b/lib/Target/AArch64/AArch64Relocator.cpp @@ -335,8 +335,14 @@ void AArch64Relocator::scanLocalReloc(InputFile &pInput, Relocation &pReloc, if (config().options().isPIE()) { // PIE executable: relax TLSDESC to IE AArch64GOT *G = m_Target.createGOT(GOT::TLS_IE, Obj, rsym); - helper_DynRel_init(Obj, &pReloc, rsym, G, 0x0, - llvm::ELF::R_AARCH64_TLS_TPREL64, m_Target); + if (rsym->isLocal()) { + // Local symbol: fill GOT at link time, no dynamic reloc needed + G->setValueType(GOT::TLSStaticSymbolValue); + } else { + // Global symbol: dynamic linker fills GOT + helper_DynRel_init(Obj, &pReloc, rsym, G, 0x0, + llvm::ELF::R_AARCH64_TLS_TPREL64, m_Target); + } rsym->setReserved(rsym->reserved() | ReserveGOT); return; } @@ -575,8 +581,14 @@ void AArch64Relocator::scanGlobalReloc(InputFile &pInput, Relocation &pReloc, if (config().options().isPIE()) { // PIE executable: relax TLSDESC to IE AArch64GOT *G = m_Target.createGOT(GOT::TLS_IE, Obj, rsym); - helper_DynRel_init(Obj, &pReloc, rsym, G, 0x0, - llvm::ELF::R_AARCH64_TLS_TPREL64, m_Target); + if (rsym->isLocal()) { + // Local symbol: no name in dynamic symtab, fill at link time + G->setValueType(GOT::TLSStaticSymbolValue); + } else { + // Global symbol: dynamic linker fills GOT at startup + helper_DynRel_init(Obj, &pReloc, rsym, G, 0x0, + llvm::ELF::R_AARCH64_TLS_TPREL64, m_Target); + } rsym->setReserved(rsym->reserved() | ReserveGOT); return; } diff --git a/test/AArch64/standalone/TLS_RunTest/Inputs/tls_run.c b/test/AArch64/standalone/TLS_RunTest/Inputs/tls_run.c new file mode 100644 index 000000000..58aa7df8b --- /dev/null +++ b/test/AArch64/standalone/TLS_RunTest/Inputs/tls_run.c @@ -0,0 +1,12 @@ +#include + +__thread int tls_global = 42; +static __thread int tls_local = 100; + +int main() { + printf("tls_global = %d\n", tls_global); + printf("tls_local = %d\n", tls_local); + tls_global = 99; + printf("tls_global after write = %d\n", tls_global); + return 0; +} diff --git a/test/AArch64/standalone/TLS_RunTest/TLS_RunTest.test b/test/AArch64/standalone/TLS_RunTest/TLS_RunTest.test new file mode 100644 index 000000000..eba230147 --- /dev/null +++ b/test/AArch64/standalone/TLS_RunTest/TLS_RunTest.test @@ -0,0 +1,25 @@ +REQUIRES: run_test +#---TLS_RunTest.test--------------------- Executable------------------# +#BEGIN_COMMENT +# Verify TLS relaxations work correctly at runtime for all link modes: +# - Static executable: TLSDESC->LE relaxation +# - PIE executable: TLSDESC->IE relaxation +# - Dynamic executable: TLSDESC->IE relaxation +# Requires AArch64 cross compiler, sysroot, and QEMU emulator. +#END_COMMENT +#START_TEST +RUN: %run_cc -o %t.o %p/Inputs/tls_run.c -c -ftls-model=global-dynamic -fPIC + +RUN: %run_cc -o %t.static.out %t.o -static +RUN: %run %t.static.out | %filecheck %s --check-prefix=CHECK + +RUN: %run_cc -o %t.pie.out %t.o -pie +RUN: %run %t.pie.out | %filecheck %s --check-prefix=CHECK + +RUN: %run_cc -o %t.dyn.out %t.o -Wl,-dy +RUN: %run %t.dyn.out | %filecheck %s --check-prefix=CHECK +#END_TEST + +CHECK: tls_global = 42 +CHECK: tls_local = 100 +CHECK: tls_global after write = 99