From bc4b159bb1121ed9e1c00df35a4f5dd5712de8de Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Mon, 22 Apr 2019 03:10:40 +0000 Subject: [PATCH] [ELF][X86] Allow R_386_TLS_LDO_32 and R_X86_64_DTPOFF{32,64} to preemptable local-dynamic symbols Summary: Fixes PR35242. A simplified reproduce: thread_local int i; int f() { return i; } % {g++,clang++} -fPIC -shared -ftls-model=local-dynamic -fuse-ld=lld a.cc ld.lld: error: can't create dynamic relocation R_X86_64_DTPOFF32 against symbol: i in readonly segment; recompile object files with -fPIC or pass '-Wl,-z,notext' to allow text relocations in the output In isStaticLinkTimeConstant(), Syn.IsPreemptible is true, so it is not seen as a constant. The error is then issued in processRelocAux(). A symbol of the local-dynamic TLS model cannot be preempted but it can preempt symbols of the global-dynamic TLS model in other DSOs. So it makes some sense that the variable is not static. This patch fixes the linking error by changing getRelExpr() on R_386_TLS_LDO_32 and R_X86_64_DTPOFF{32,64} from R_ABS to R_DTPREL. R_PPC64_DTPREL_* and R_MIPS_TLS_DTPREL_* need similar fixes, but they are not handled in this patch. As a bonus, we use `if (Expr == R_ABS && !Config->Shared)` to find ld-to-le opportunities. R_ABS is overloaded here for such STT_TLS symbols. A dedicated R_DTPREL is clearer. Differential Revision: https://reviews.llvm.org/D60945 llvm-svn: 358870 --- lld/ELF/Arch/X86.cpp | 3 ++- lld/ELF/Arch/X86_64.cpp | 3 ++- lld/ELF/InputSection.cpp | 4 +++- lld/ELF/Relocations.cpp | 13 +++++++------ lld/ELF/Relocations.h | 1 + lld/test/ELF/i386-tls-ld-preemptable.s | 18 ++++++++++++++++++ lld/test/ELF/x86-64-tls-ld-preemptable.s | 20 ++++++++++++++++++++ 7 files changed, 53 insertions(+), 9 deletions(-) create mode 100644 lld/test/ELF/i386-tls-ld-preemptable.s create mode 100644 lld/test/ELF/x86-64-tls-ld-preemptable.s diff --git a/lld/ELF/Arch/X86.cpp b/lld/ELF/Arch/X86.cpp index 7001eb4..468ca20 100644 --- a/lld/ELF/Arch/X86.cpp +++ b/lld/ELF/Arch/X86.cpp @@ -84,8 +84,9 @@ RelExpr X86::getRelExpr(RelType Type, const Symbol &S, case R_386_8: case R_386_16: case R_386_32: - case R_386_TLS_LDO_32: return R_ABS; + case R_386_TLS_LDO_32: + return R_DTPREL; case R_386_TLS_GD: return R_TLSGD_GOTPLT; case R_386_TLS_LDM: diff --git a/lld/ELF/Arch/X86_64.cpp b/lld/ELF/Arch/X86_64.cpp index 668b408..4ac9454 100644 --- a/lld/ELF/Arch/X86_64.cpp +++ b/lld/ELF/Arch/X86_64.cpp @@ -82,9 +82,10 @@ RelExpr X86_64::getRelExpr(RelType Type, const Symbol &S, case R_X86_64_32: case R_X86_64_32S: case R_X86_64_64: + return R_ABS; case R_X86_64_DTPOFF32: case R_X86_64_DTPOFF64: - return R_ABS; + return R_DTPREL; case R_X86_64_TPOFF32: return R_TLS; case R_X86_64_TLSLD: diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index a32015f4..060374a 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -616,6 +616,8 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A, case R_RELAX_TLS_LD_TO_LE_ABS: case R_RELAX_GOT_PC_NOPIC: return Sym.getVA(A); + case R_DTPREL: + return Sym.getVA(A); case R_ADDEND: return A; case R_ARM_SBREL: @@ -806,7 +808,7 @@ void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef Rels) { if (Expr == R_NONE) continue; - if (Expr != R_ABS) { + if (Expr != R_ABS && Expr != R_DTPREL) { std::string Msg = getLocation(Offset) + ": has non-ABS relocation " + toString(Type) + " against symbol '" + toString(Sym) + "'"; diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index 14d3c1d..d5c36b6 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -248,7 +248,8 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C, } // Local-Dynamic relocs can be relaxed to Local-Exec. - if (Expr == R_ABS && !Config->Shared) { + // TODO Delete R_ABS after all R_*_DTPREL_* relocations migrate to R_DTPREL. + if ((Expr == R_ABS || Expr == R_DTPREL) && !Config->Shared) { C.Relocations.push_back( {Target->adjustRelaxExpr(Type, nullptr, R_RELAX_TLS_LD_TO_LE), Type, Offset, Addend, &Sym}); @@ -398,13 +399,13 @@ static bool isRelExpr(RelExpr Expr) { static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym, InputSectionBase &S, uint64_t RelOff) { // These expressions always compute a constant - if (oneof(E)) + R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC, R_GOTPLTONLY_PC, + R_PLT_PC, R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC, R_PPC_CALL_PLT, + R_TLSDESC_CALL, R_AARCH64_TLSDESC_PAGE, R_HINT, R_TLSLD_HINT, + R_TLSIE_HINT>(E)) return true; // These never do, except if the entire file is position dependent or if diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h index 48ed362..240dc68 100644 --- a/lld/ELF/Relocations.h +++ b/lld/ELF/Relocations.h @@ -31,6 +31,7 @@ using RelType = uint32_t; enum RelExpr { R_ABS, R_ADDEND, + R_DTPREL, R_GOT, R_GOT_OFF, R_GOT_PC, diff --git a/lld/test/ELF/i386-tls-ld-preemptable.s b/lld/test/ELF/i386-tls-ld-preemptable.s new file mode 100644 index 0000000..8f9d91a --- /dev/null +++ b/lld/test/ELF/i386-tls-ld-preemptable.s @@ -0,0 +1,18 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=i386 %s -o %t.o +# RUN: ld.lld %t.o -shared -o %t.so +# RUN: llvm-objdump -d --no-show-raw-insn %t.so | FileCheck %s + +# CHECK: 100b: movl (%eax), %eax + +# We used to error on R_386_TLS_LDO_32 to preemptable symbols. +# i is STB_GLOBAL and preemptable. + leal i@TLSLDM(%ebx), %eax + calll __tls_get_addr@PLT + movl i@DTPOFF(%eax), %eax # R_386_TLS_LDO_32 + +.section .tbss,"awT",@nobits +.globl i +i: + .long 0 + .size i, 4 diff --git a/lld/test/ELF/x86-64-tls-ld-preemptable.s b/lld/test/ELF/x86-64-tls-ld-preemptable.s new file mode 100644 index 0000000..50f9b24 --- /dev/null +++ b/lld/test/ELF/x86-64-tls-ld-preemptable.s @@ -0,0 +1,20 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o +# RUN: ld.lld %t.o -shared -o %t.so +# RUN: llvm-objdump -d --no-show-raw-insn %t.so | FileCheck %s + +# CHECK: 100c: leaq (%rax), %rax +# CHECK-NEXT: 1013: movabsq 0, %rax + +# We used to error on R_X86_64_DTPOFF{32,64} to preemptable symbols. +# i is STB_GLOBAL and preemptable. + leaq i@TLSLD(%rip), %rdi + callq __tls_get_addr@PLT + leaq i@DTPOFF(%rax), %rax # R_X86_64_DTPOFF32 + movabsq i@DTPOFF, %rax # R_X86_64_DTPOFF64 + +.section .tbss,"awT",@nobits +.globl i +i: + .long 0 + .size i, 4 -- 2.7.4