From 719322411ce62662bcd3bdc5ee9deb6c2c84ffb7 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Wed, 29 May 2019 02:03:56 +0000 Subject: [PATCH] [ELF] Implement General Dynamic style TLSDESC for x86-64 This handles two initial relocation types R_X86_64_GOTPC32_TLSDESC and R_X86_64_TLSDESC_CALL, as well as the GD->LE and GD->IE relaxations. Reviewed By: ruiu Differential Revision: https://reviews.llvm.org/D62513 llvm-svn: 361911 --- lld/ELF/Arch/X86_64.cpp | 111 ++++++++++++++++------- lld/ELF/InputSection.cpp | 2 + lld/ELF/Relocations.cpp | 11 ++- lld/ELF/Relocations.h | 1 + lld/test/ELF/invalid/x86-64-tlsdesc-gd.s | 15 +++ lld/test/ELF/x86-64-tlsdesc-gd.s | 69 ++++++++++++++ 6 files changed, 170 insertions(+), 39 deletions(-) create mode 100644 lld/test/ELF/invalid/x86-64-tlsdesc-gd.s create mode 100644 lld/test/ELF/x86-64-tlsdesc-gd.s diff --git a/lld/ELF/Arch/X86_64.cpp b/lld/ELF/Arch/X86_64.cpp index 7a839ebe1e30..fdaf63b11bc3 100644 --- a/lld/ELF/Arch/X86_64.cpp +++ b/lld/ELF/Arch/X86_64.cpp @@ -55,6 +55,7 @@ X86_64::X86_64() { PltRel = R_X86_64_JUMP_SLOT; RelativeRel = R_X86_64_RELATIVE; IRelativeRel = R_X86_64_IRELATIVE; + TlsDescRel = R_X86_64_TLSDESC; TlsGotRel = R_X86_64_TPOFF64; TlsModuleIndexRel = R_X86_64_DTPMOD64; TlsOffsetRel = R_X86_64_DTPOFF64; @@ -88,6 +89,8 @@ RelExpr X86_64::getRelExpr(RelType Type, const Symbol &S, return R_DTPREL; case R_X86_64_TPOFF32: return R_TLS; + case R_X86_64_TLSDESC_CALL: + return R_TLSDESC_CALL; case R_X86_64_TLSLD: return R_TLSLD_PC; case R_X86_64_TLSGD: @@ -105,6 +108,8 @@ RelExpr X86_64::getRelExpr(RelType Type, const Symbol &S, case R_X86_64_GOT32: case R_X86_64_GOT64: return R_GOTPLT; + case R_X86_64_GOTPC32_TLSDESC: + return R_TLSDESC_PC; case R_X86_64_GOTPCREL: case R_X86_64_GOTPCRELX: case R_X86_64_REX_GOTPCRELX: @@ -173,45 +178,82 @@ RelType X86_64::getDynRel(RelType Type) const { } void X86_64::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { - // Convert - // .byte 0x66 - // leaq x@tlsgd(%rip), %rdi - // .word 0x6666 - // rex64 - // call __tls_get_addr@plt - // to - // mov %fs:0x0,%rax - // lea x@tpoff,%rax - const uint8_t Inst[] = { - 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0x0,%rax - 0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea x@tpoff,%rax - }; - memcpy(Loc - 4, Inst, sizeof(Inst)); + if (Type == R_X86_64_TLSGD) { + // Convert + // .byte 0x66 + // leaq x@tlsgd(%rip), %rdi + // .word 0x6666 + // rex64 + // call __tls_get_addr@plt + // to the following two instructions. + const uint8_t Inst[] = { + 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, + 0x00, 0x00, // mov %fs:0x0,%rax + 0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea x@tpoff,%rax + }; + memcpy(Loc - 4, Inst, sizeof(Inst)); - // The original code used a pc relative relocation and so we have to - // compensate for the -4 in had in the addend. - write32le(Loc + 8, Val + 4); + // The original code used a pc relative relocation and so we have to + // compensate for the -4 in had in the addend. + write32le(Loc + 8, Val + 4); + } else { + // Convert + // lea x@tlsgd(%rip), %rax + // call *(%rax) + // to the following two instructions. + assert(Type == R_X86_64_GOTPC32_TLSDESC); + if (memcmp(Loc - 3, "\x48\x8d\x05", 3)) { + error(getErrorLocation(Loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used " + "in callq *x@tlsdesc(%rip), %rax"); + return; + } + // movq $x@tpoff(%rip),%rax + Loc[-2] = 0xc7; + Loc[-1] = 0xc0; + write32le(Loc, Val + 4); + // xchg ax,ax + Loc[4] = 0x66; + Loc[5] = 0x90; + } } void X86_64::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const { - // Convert - // .byte 0x66 - // leaq x@tlsgd(%rip), %rdi - // .word 0x6666 - // rex64 - // call __tls_get_addr@plt - // to - // mov %fs:0x0,%rax - // addq x@tpoff,%rax - const uint8_t Inst[] = { - 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, 0x00, 0x00, // mov %fs:0x0,%rax - 0x48, 0x03, 0x05, 0, 0, 0, 0, // addq x@tpoff,%rax - }; - memcpy(Loc - 4, Inst, sizeof(Inst)); + if (Type == R_X86_64_TLSGD) { + // Convert + // .byte 0x66 + // leaq x@tlsgd(%rip), %rdi + // .word 0x6666 + // rex64 + // call __tls_get_addr@plt + // to the following two instructions. + const uint8_t Inst[] = { + 0x64, 0x48, 0x8b, 0x04, 0x25, 0x00, 0x00, + 0x00, 0x00, // mov %fs:0x0,%rax + 0x48, 0x03, 0x05, 0, 0, 0, 0, // addq x@gottpoff(%rip),%rax + }; + memcpy(Loc - 4, Inst, sizeof(Inst)); - // Both code sequences are PC relatives, but since we are moving the constant - // forward by 8 bytes we have to subtract the value by 8. - write32le(Loc + 8, Val - 8); + // Both code sequences are PC relatives, but since we are moving the + // constant forward by 8 bytes we have to subtract the value by 8. + write32le(Loc + 8, Val - 8); + } else { + // Convert + // lea x@tlsgd(%rip), %rax + // call *(%rax) + // to the following two instructions. + assert(Type == R_X86_64_GOTPC32_TLSDESC); + if (memcmp(Loc - 3, "\x48\x8d\x05", 3)) { + error(getErrorLocation(Loc - 3) + "R_X86_64_GOTPC32_TLSDESC must be used " + "in callq *x@tlsdesc(%rip), %rax"); + return; + } + // movq x@gottpoff(%rip),%rax + Loc[-2] = 0x8b; + write32le(Loc, Val); + // xchg ax,ax + Loc[4] = 0x66; + Loc[5] = 0x90; + } } // In some conditions, R_X86_64_GOTTPOFF relocation can be optimized to @@ -331,6 +373,7 @@ void X86_64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { case R_X86_64_TPOFF32: case R_X86_64_GOT32: case R_X86_64_GOTPC32: + case R_X86_64_GOTPC32_TLSDESC: case R_X86_64_GOTPCREL: case R_X86_64_GOTPCRELX: case R_X86_64_REX_GOTPCRELX: diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index 74878931afb4..9075568f8c1d 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -755,6 +755,8 @@ static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A, return Sym.getSize() + A; case R_TLSDESC: return In.Got->getGlobalDynAddr(Sym) + A; + case R_TLSDESC_PC: + return In.Got->getGlobalDynAddr(Sym) + A - P; case R_AARCH64_TLSDESC_PAGE: return getAArch64Page(In.Got->getGlobalDynAddr(Sym) + A) - getAArch64Page(P); diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index a8ed792164bc..aab5385dad03 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -217,7 +217,8 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C, if (Config->EMachine == EM_MIPS) return handleMipsTlsRelocation(Type, Sym, C, Offset, Addend, Expr); - if (oneof(Expr) && + if (oneof( + Expr) && Config->Shared) { if (In.Got->addDynTlsEntry(Sym)) { uint64_t Off = In.Got->getGlobalDynOffset(Sym); @@ -273,8 +274,8 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C, return 1; } - if (oneof(Expr)) { + if (oneof(Expr)) { if (Config->Shared) { if (In.Got->addDynTlsEntry(Sym)) { uint64_t Off = In.Got->getGlobalDynOffset(Sym); @@ -403,8 +404,8 @@ static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym, 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_PPC64_RELAX_TOC, R_TLSDESC_CALL, R_AARCH64_TLSDESC_PAGE, R_HINT, R_TLSLD_HINT, - R_TLSIE_HINT>(E)) + R_PPC64_RELAX_TOC, R_TLSDESC_CALL, R_TLSDESC_PC, + 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 c856595942d9..923aa4661a39 100644 --- a/lld/ELF/Relocations.h +++ b/lld/ELF/Relocations.h @@ -61,6 +61,7 @@ enum RelExpr { R_TLS, R_TLSDESC, R_TLSDESC_CALL, + R_TLSDESC_PC, R_TLSGD_GOT, R_TLSGD_GOTPLT, R_TLSGD_PC, diff --git a/lld/test/ELF/invalid/x86-64-tlsdesc-gd.s b/lld/test/ELF/invalid/x86-64-tlsdesc-gd.s new file mode 100644 index 000000000000..bd75ff2bc8f3 --- /dev/null +++ b/lld/test/ELF/invalid/x86-64-tlsdesc-gd.s @@ -0,0 +1,15 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o +# RUN: echo '.tbss; .globl a; a:' | llvm-mc -filetype=obj -triple=x86_64 - -o %t1.o +# RUN: ld.lld -shared %t1.o -o %t1.so + +## GD to LE relaxation. +# RUN: not ld.lld %t.o %t1.o -o /dev/null 2>&1 | FileCheck -DINPUT=%t.o %s +## GD to IE relaxation. +# RUN: not ld.lld %t.o %t1.so -o /dev/null 2>&1 | FileCheck -DINPUT=%t.o %s + +# CHECK: error: [[INPUT]]:(.text+0x0): R_X86_64_GOTPC32_TLSDESC must be used in callq *x@tlsdesc(%rip), %rax + +leaq a@tlsdesc(%rip), %rdx +call *a@tlscall(%rdx) +movl %fs:(%rax), %eax diff --git a/lld/test/ELF/x86-64-tlsdesc-gd.s b/lld/test/ELF/x86-64-tlsdesc-gd.s new file mode 100644 index 000000000000..1c72fbe40d04 --- /dev/null +++ b/lld/test/ELF/x86-64-tlsdesc-gd.s @@ -0,0 +1,69 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o +# RUN: echo '.tbss; .globl b; b:' | llvm-mc -filetype=obj -triple=x86_64 - -o %t1.o +# RUN: ld.lld -shared -soname=t1.so %t1.o -o %t1.so + +# RUN: ld.lld -shared %t.o %t1.o -o %t.so +# RUN: llvm-readobj -r %t.so | FileCheck --check-prefix=GD-REL %s +# RUN: llvm-objdump -d --no-show-raw-insn %t.so | FileCheck --check-prefix=GD %s + +# RUN: ld.lld %t.o %t1.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 + +# RUN: ld.lld %t.o %t1.so -o %t +# RUN: llvm-readobj -r %t | FileCheck --check-prefix=IE-REL %s +# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck --check-prefix=IE %s + +# GD-REL: .rela.dyn { +# GD-REL-NEXT: 0x20A0 R_X86_64_TLSDESC a 0x0 +# GD-REL-NEXT: 0x20B0 R_X86_64_TLSDESC b 0x0 +# GD-REL-NEXT: } + +# 0x20a0-0x1007 = 4249 +# GD: leaq 4249(%rip), %rax +# GD-NEXT: 1007: callq *(%rax) +# GD-NEXT: movl %fs:(%rax), %eax + +# 0x20b0-0x1013 = 4253 +# GD-NEXT: leaq 4253(%rip), %rax +# GD-NEXT: 1013: callq *(%rax) +# GD-NEXT: movl %fs:(%rax), %eax + +# NOREL: no relocations + +## offset(a) = -4 +# LE: movq $-4, %rax +# LE-NEXT: nop +# LE-NEXT: movl %fs:(%rax), %eax +## offset(b) = 0 +# LE: movq $0, %rax +# LE-NEXT: nop +# LE-NEXT: movl %fs:(%rax), %eax + +# IE-REL: .rela.dyn { +# IE-REL-NEXT: 0x2020C0 R_X86_64_TPOFF64 b 0x0 +# IE-REL-NEXT: } + +## a is relaxed to use LE. +# IE: movq $-4, %rax +# IE-NEXT: nop +# IE-NEXT: movl %fs:(%rax), %eax +## 0x2020C0 - 0x201013 = 4269 +# IE-NEXT: movq 4269(%rip), %rax +# IE-NEXT: 201013: nop +# IE-NEXT: movl %fs:(%rax), %eax + +leaq a@tlsdesc(%rip), %rax +call *a@tlscall(%rax) +movl %fs:(%rax), %eax + +leaq b@tlsdesc(%rip), %rax +call *b@tlscall(%rax) +movl %fs:(%rax), %eax + +.section .tbss +.globl a +.zero 8 +a: +.zero 4