From f7445dd9303a5aa4a99bd8ecfecb16d7eb5ce463 Mon Sep 17 00:00:00 2001 From: quic-areg Date: Thu, 21 May 2026 13:54:05 -0700 Subject: [PATCH] [X86] Emit R_X86_64_TPOFF64 for IE-mode TLS in shared objects. For IE TLS, x86_64 accesses a GOT entry containing the symbol's offset from the thread pointer. For executable outputs, the TP offset is fixed at link time, so the GOT entry can be resolved statically. For shared objects, the offset is assigned by the dynamic loader. ELD was still writing the link-time static TLS offset into the GOT entry for local IE TLS references, leaving no dynamic relocation for the loader to apply. Emit `R_X86_64_TPOFF64` for IE TLS GOT entries in shared objects, and mark the output with `DF_STATIC_TLS` when static TLS is required. Keep PIE output statically resolved. Signed-off-by: quic-areg --- lib/Target/X86/x86_64Relocator.cpp | 15 ++++++++++--- .../TLSIEHiddenGlobal/TLSIEHiddenGlobal.test | 19 +++++++++++++++++ .../TLSIELocalSymbol/TLSIELocalSymbol.test | 21 +++++++++++++++++++ 3 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 test/x86_64/linux/TLSIEHiddenGlobal/TLSIEHiddenGlobal.test create mode 100644 test/x86_64/linux/TLSIELocalSymbol/TLSIELocalSymbol.test diff --git a/lib/Target/X86/x86_64Relocator.cpp b/lib/Target/X86/x86_64Relocator.cpp index 3af4b4d6f..990416bd4 100644 --- a/lib/Target/X86/x86_64Relocator.cpp +++ b/lib/Target/X86/x86_64Relocator.cpp @@ -274,9 +274,17 @@ void x86_64Relocator::scanLocalReloc(InputFile &pInputFile, Relocation &pReloc, std::lock_guard relocGuard(m_RelocMutex); if (rsym->reserved() & ReserveGOT) return; - // Create a TLS IE GOT entry. x86_64GOT *G = m_Target.createGOT(GOT::TLS_IE, Obj, rsym); - G->setValueType(GOT::TLSStaticSymbolValue); + // For executables, the symbol's offset from the thread pointer is fixed at + // link time. For shared objects, the dynamic loader must compute the offset + // at load time, so emit R_X86_64_TPOFF64. + if (config().isBuildingExecutable()) { + G->setValueType(GOT::TLSStaticSymbolValue); + } else { + helper_DynRel_init(Obj, &pReloc, rsym, G, 0x0, + llvm::ELF::R_X86_64_TPOFF64, m_Target); + m_Target.setHasStaticTLS(); + } rsym->setReserved(rsym->reserved() | ReserveGOT); return; } @@ -408,13 +416,14 @@ void x86_64Relocator::scanGlobalReloc(InputFile &pInputFile, Relocation &pReloc, if (rsym->reserved() & ReserveGOT) return; x86_64GOT *G = m_Target.createGOT(GOT::TLS_IE, Obj, rsym); - const bool isExec = (config().codeGenType() == LinkerConfig::Exec); + const bool isExec = config().isBuildingExecutable(); const bool preemptible = m_Target.isSymbolPreemptible(*rsym); if (isExec && !preemptible) { G->setValueType(GOT::TLSStaticSymbolValue); } else { helper_DynRel_init(Obj, &pReloc, rsym, G, 0x0, llvm::ELF::R_X86_64_TPOFF64, m_Target); + m_Target.setHasStaticTLS(); } rsym->setReserved(rsym->reserved() | ReserveGOT); return; diff --git a/test/x86_64/linux/TLSIEHiddenGlobal/TLSIEHiddenGlobal.test b/test/x86_64/linux/TLSIEHiddenGlobal/TLSIEHiddenGlobal.test new file mode 100644 index 000000000..31ea691ba --- /dev/null +++ b/test/x86_64/linux/TLSIEHiddenGlobal/TLSIEHiddenGlobal.test @@ -0,0 +1,19 @@ +/// Verifies the global-symbol path of R_X86_64_GOTTPOFF for a non-preemptible +/// TLS variable. In -pie the symbol's TP offset is fixed at link time, +/// so no R_X86_64_TPOFF64 should be emitted. In -shared the +/// dynamic loader resolves the offset at load time, so the dyn reloc and +/// DF_STATIC_TLS are both required. + +// RUN: %clang %clangopts -c -x c %s -fPIC -ftls-model=initial-exec -o %t.o +// RUN: %link %linkopts -shared %t.o -o %t.shared.so +// RUN: %readelf -r -d %t.shared.so | %filecheck %s --check-prefix=SHARED +// RUN: %link %linkopts -pie %t.o -o %t.pie --defsym __libc_start_main=0 +// RUN: %readelf -r %t.pie | %filecheck %s --check-prefix=PIE + +// SHARED: STATIC_TLS +// SHARED: R_X86_64_TPOFF64 + +// PIE-NOT: R_X86_64_TPOFF64 + +__attribute__((visibility("hidden"))) __thread int g = 20; +int bar(void) { return g; } diff --git a/test/x86_64/linux/TLSIELocalSymbol/TLSIELocalSymbol.test b/test/x86_64/linux/TLSIELocalSymbol/TLSIELocalSymbol.test new file mode 100644 index 000000000..07e856414 --- /dev/null +++ b/test/x86_64/linux/TLSIELocalSymbol/TLSIELocalSymbol.test @@ -0,0 +1,21 @@ +/// Verifies that R_X86_64_GOTTPOFF against a file-local TLS symbol emits a +/// R_X86_64_TPOFF64 dynamic relocation in -shared output, and is statically +/// resolved into the GOT slot in -pie. + +// RUN: %clang %clangopts -c -x c %s -fPIC -ftls-model=initial-exec -o %t.o +// RUN: %link %linkopts -shared %t.o -o %t.shared.so +// RUN: %readelf -r -x .got -d %t.shared.so | %filecheck %s --check-prefix=SHARED +// RUN: %link %linkopts -pie %t.o -o %t.pie --defsym __libc_start_main=0 +// RUN: %readelf -r -x .got %t.pie | %filecheck %s --check-prefix=PIE + +// SHARED: STATIC_TLS +// SHARED: R_X86_64_TPOFF64 +// SHARED-LABEL: Hex dump of section '.got': +// SHARED-NEXT: 0x{{[0-9a-f]+}} 00000000 00000000 + +// PIE-NOT: R_X86_64_TPOFF64 +// PIE-LABEL: Hex dump of section '.got': +// PIE-NEXT: 0x{{[0-9a-f]+}} fcffffff ffffffff + +static __thread int a = 10; +int foo(void) { return a; }