Skip to content

Commit

Permalink
[ELF] Implement Local Dynamic style TLSDESC for x86-64
Browse files Browse the repository at this point in the history
For the Local Dynamic case of TLSDESC, _TLS_MODULE_BASE_ is defined as a
special TLS symbol that makes:

1) Without relaxation: it produces a dynamic TLSDESC relocation that
computes 0. Adding @dtpoff to access a TLS symbol.
2) With LD->LE relaxation: _TLS_MODULE_BASE_@tpoff = 0 (lowest address in
the TLS block). Adding @tpoff to access a TLS symbol.

For 1), this saves dynamic relocations and GOT slots as otherwise
(General Dynamic) we would create an R_X86_64_TLSDESC and reserve two
GOT slots for each symbol.

Add ElfSym::TlsModuleBase and change the signature of getTlsTpOffset()
to special case _TLS_MODULE_BASE_.

Reviewed By: ruiu

Differential Revision: https://reviews.llvm.org/D62577

llvm-svn: 362078
  • Loading branch information
MaskRay committed May 30, 2019
1 parent 3475a46 commit 0526c0c
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 6 deletions.
16 changes: 10 additions & 6 deletions lld/ELF/InputSection.cpp
Expand Up @@ -584,24 +584,28 @@ static Relocation *getRISCVPCRelHi20(const Symbol *Sym, uint64_t Addend) {

// A TLS symbol's virtual address is relative to the TLS segment. Add a
// target-specific adjustment to produce a thread-pointer-relative offset.
static int64_t getTlsTpOffset() {
static int64_t getTlsTpOffset(const Symbol &S) {
// On targets that support TLSDESC, _TLS_MODULE_BASE_@tpoff = 0.
if (&S == ElfSym::TlsModuleBase)
return 0;

switch (Config->EMachine) {
case EM_ARM:
case EM_AARCH64:
// Variant 1. The thread pointer points to a TCB with a fixed 2-word size,
// followed by a variable amount of alignment padding, followed by the TLS
// segment.
return alignTo(Config->Wordsize * 2, Out::TlsPhdr->p_align);
return S.getVA(0) + alignTo(Config->Wordsize * 2, Out::TlsPhdr->p_align);
case EM_386:
case EM_X86_64:
// Variant 2. The TLS segment is located just before the thread pointer.
return -alignTo(Out::TlsPhdr->p_memsz, Out::TlsPhdr->p_align);
return S.getVA(0) - alignTo(Out::TlsPhdr->p_memsz, Out::TlsPhdr->p_align);
case EM_PPC64:
// The thread pointer points to a fixed offset from the start of the
// executable's TLS segment. An offset of 0x7000 allows a signed 16-bit
// offset to reach 0x1000 of TCB/thread-library data and 0xf000 of the
// program's TLS segment.
return -0x7000;
return S.getVA(0) - 0x7000;
default:
llvm_unreachable("unhandled Config->EMachine");
}
Expand Down Expand Up @@ -745,12 +749,12 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A,
// loaders.
if (Sym.isUndefined())
return A;
return Sym.getVA(A) + getTlsTpOffset();
return getTlsTpOffset(Sym) + A;
case R_RELAX_TLS_GD_TO_LE_NEG:
case R_NEG_TLS:
if (Sym.isUndefined())
return A;
return -Sym.getVA(0) - getTlsTpOffset() + A;
return -getTlsTpOffset(Sym) + A;
case R_SIZE:
return Sym.getSize() + A;
case R_TLSDESC:
Expand Down
1 change: 1 addition & 0 deletions lld/ELF/Symbols.cpp
Expand Up @@ -39,6 +39,7 @@ Defined *ElfSym::MipsGpDisp;
Defined *ElfSym::MipsLocalGp;
Defined *ElfSym::RelaIpltStart;
Defined *ElfSym::RelaIpltEnd;
Defined *ElfSym::TlsModuleBase;

static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
switch (Sym.kind()) {
Expand Down
3 changes: 3 additions & 0 deletions lld/ELF/Symbols.h
Expand Up @@ -436,6 +436,9 @@ struct ElfSym {
// __rel{,a}_iplt_{start,end} symbols.
static Defined *RelaIpltStart;
static Defined *RelaIpltEnd;

// _TLS_MODULE_BASE_ on targets that support TLSDESC.
static Defined *TlsModuleBase;
};

// A buffer class that is large enough to hold any Symbol-derived
Expand Down
21 changes: 21 additions & 0 deletions lld/ELF/Writer.cpp
Expand Up @@ -1606,6 +1606,27 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
if (!dyn_cast_or_null<Defined>(Symtab->find("__global_pointer$")))
addOptionalRegular("__global_pointer$", findSection(".sdata"), 0x800);

if (Config->EMachine == EM_X86_64) {
// On targets that support TLSDESC, _TLS_MODULE_BASE_ is defined in such a
// way that:
//
// 1) Without relaxation: it produces a dynamic TLSDESC relocation that
// computes 0.
// 2) With LD->LE relaxation: _TLS_MODULE_BASE_@tpoff = 0 (lowest address in
// the TLS block).
//
// 2) is special cased in @tpoff computation. To satisfy 1), we define it as
// an absolute symbol of zero. This is different from GNU linkers which
// define _TLS_MODULE_BASE_ relative to the first TLS section.
Symbol *S = Symtab->find("_TLS_MODULE_BASE_");
if (S && S->isUndefined()) {
S->resolve(Defined{/*File=*/nullptr, S->getName(), STB_GLOBAL, STV_HIDDEN,
STT_TLS, /*Value=*/0, 0,
/*Section=*/nullptr});
ElfSym::TlsModuleBase = cast<Defined>(S);
}
}

// This responsible for splitting up .eh_frame section into
// pieces. The relocation scan uses those pieces, so this has to be
// earlier.
Expand Down
45 changes: 45 additions & 0 deletions lld/test/ELF/x86-64-tlsdesc-ld.s
@@ -0,0 +1,45 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o

# RUN: ld.lld -shared %t.o -o %t.so
# RUN: llvm-readobj -r %t.so | FileCheck --check-prefix=LD-REL %s
# RUN: llvm-objdump -d --no-show-raw-insn %t.so | FileCheck --check-prefix=LD %s

# RUN: ld.lld %t.o -o %t
# RUN: llvm-readelf -r %t | FileCheck --check-prefix=NOREL %s
# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck --check-prefix=LE %s

## Check _TLS_MODULE_BASE_ used by LD produces a dynamic relocation with a value of 0.
# LD-REL: .rela.dyn {
# LD-REL-NEXT: 0x20A0 R_X86_64_TLSDESC - 0x0
# LD-REL-NEXT: }

## 0x20a0-0x1007 = 4249
## dtpoff(a) = 8, dtpoff(b) = 12
# LD: leaq 4249(%rip), %rax
# LD-NEXT: 1007: callq *(%rax)
# LD-NEXT: movl %fs:8(%rax), %edx
# LD-NEXT: addl %fs:12(%rax), %edx

## When producing an executable, the LD code sequence can be relaxed to LE.
## It is the same as GD->LE.
## tpoff(_TLS_MODULE_BASE_) = 0, tpoff(a) = -8, tpoff(b) = -4

# NOREL: no relocations

# LE: movq $0, %rax
# LE-NEXT: nop
# LE-NEXT: movl %fs:-8(%rax), %edx
# LE-NEXT: addl %fs:-4(%rax), %edx

leaq _TLS_MODULE_BASE_@tlsdesc(%rip), %rax
call *_TLS_MODULE_BASE_@tlscall(%rax)
movl %fs:a@dtpoff(%rax), %edx
addl %fs:b@dtpoff(%rax), %edx

.section .tbss
.zero 8
a:
.zero 4
b:
.zero 4

0 comments on commit 0526c0c

Please sign in to comment.