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; }