[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
This commit is contained in:
Fangrui Song 2019-04-22 03:10:40 +00:00
parent 1233c15be5
commit bc4b159bb1
7 changed files with 53 additions and 9 deletions

View File

@ -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:

View File

@ -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:

View File

@ -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<RelTy> Rels) {
if (Expr == R_NONE)
continue;
if (Expr != R_ABS) {
if (Expr != R_ABS && Expr != R_DTPREL) {
std::string Msg = getLocation<ELFT>(Offset) +
": has non-ABS relocation " + toString(Type) +
" against symbol '" + toString(Sym) + "'";

View File

@ -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<R_GOTPLT, R_GOT_OFF, R_HEXAGON_GOT, R_TLSLD_GOT_OFF,
if (oneof<R_DTPREL, R_GOTPLT, R_GOT_OFF, R_HEXAGON_GOT, R_TLSLD_GOT_OFF,
R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOTREL, R_MIPS_GOT_OFF,
R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC, R_MIPS_TLSGD,
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))
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

View File

@ -31,6 +31,7 @@ using RelType = uint32_t;
enum RelExpr {
R_ABS,
R_ADDEND,
R_DTPREL,
R_GOT,
R_GOT_OFF,
R_GOT_PC,

View File

@ -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

View File

@ -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