diff options
| author | Fangrui Song <maskray@google.com> | 2019-05-30 10:00:20 +0000 |
|---|---|---|
| committer | Fangrui Song <maskray@google.com> | 2019-05-30 10:00:20 +0000 |
| commit | 0526c0cd8e73317ddef71b81b78631b16f4e9cd0 (patch) | |
| tree | 5917968d4b095704674af6abb731d0a1a8378fe5 | |
| parent | 3475a46ec23d170d086e98a470f3a3f5492845bd (diff) | |
| download | bcm5719-llvm-0526c0cd8e73317ddef71b81b78631b16f4e9cd0.tar.gz bcm5719-llvm-0526c0cd8e73317ddef71b81b78631b16f4e9cd0.zip | |
[ELF] Implement Local Dynamic style TLSDESC for x86-64
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
| -rw-r--r-- | lld/ELF/InputSection.cpp | 16 | ||||
| -rw-r--r-- | lld/ELF/Symbols.cpp | 1 | ||||
| -rw-r--r-- | lld/ELF/Symbols.h | 3 | ||||
| -rw-r--r-- | lld/ELF/Writer.cpp | 21 | ||||
| -rw-r--r-- | lld/test/ELF/x86-64-tlsdesc-ld.s | 45 |
5 files changed, 80 insertions, 6 deletions
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index 2ff6b4800b7..45bafd321de 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -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"); } @@ -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: diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp index e8c6377fb59..e82d1ac1fdf 100644 --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -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()) { diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h index 03de7009ce5..685f25494b5 100644 --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -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 diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 00d676a3c89..975fddf618b 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -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. diff --git a/lld/test/ELF/x86-64-tlsdesc-ld.s b/lld/test/ELF/x86-64-tlsdesc-ld.s new file mode 100644 index 00000000000..c478f0baa7f --- /dev/null +++ b/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 |

