diff --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp --- a/lld/ELF/Arch/PPC64.cpp +++ b/lld/ELF/Arch/PPC64.cpp @@ -727,15 +727,38 @@ writeFromHalf16(loc, 0x3c6d0000); // addis r3, r13 relocateNoSym(loc, R_PPC64_TPREL16_HA, val); break; - case R_PPC64_TLSGD: - write32(loc, NOP); - write32(loc + 4, 0x38630000); // addi r3, r3 - // Since we are relocating a half16 type relocation and Loc + 4 points to - // the start of an instruction we need to advance the buffer by an extra - // 2 bytes on BE. - relocateNoSym(loc + 4 + (config->ekind == ELF64BEKind ? 2 : 0), - R_PPC64_TPREL16_LO, val); + case R_PPC64_GOT_TLSGD_PCREL34: + // Relax from paddi r3, 0, x@got@tlsgd@pcrel, 1 to + // paddi r3, r13, x@tprel, 0 + writePrefixedInstruction(loc, 0x06000000386d0000); + relocateNoSym(loc, R_PPC64_TPREL34, val); + break; + case R_PPC64_TLSGD: { + // PC Relative Relaxation: + // Relax from bl __tls_get_addr@notoc(x@tlsgd) to + // nop + // TOC Relaxation: + // Relax from bl __tls_get_addr(x@tlsgd) + // nop + // to + // nop + // addi r3, r3, x@tprel@l + const uintptr_t locAsInt = reinterpret_cast(loc); + if (locAsInt % 4 == 0) { + write32(loc, NOP); // nop + write32(loc + 4, 0x38630000); // addi r3, r3 + // Since we are relocating a half16 type relocation and Loc + 4 points to + // the start of an instruction we need to advance the buffer by an extra + // 2 bytes on BE. + relocateNoSym(loc + 4 + (config->ekind == ELF64BEKind ? 2 : 0), + R_PPC64_TPREL16_LO, val); + } else if (locAsInt % 4 == 1) { + write32(loc - 1, NOP); + } else { + errorOrWarn("R_PPC64_TLSGD has unexpected byte alignment"); + } break; + } default: llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); } @@ -947,6 +970,8 @@ case R_PPC64_GOT_TLSGD16_HI: case R_PPC64_GOT_TLSGD16_LO: return R_TLSGD_GOT; + case R_PPC64_GOT_TLSGD_PCREL34: + return R_TLSGD_PC; case R_PPC64_GOT_TLSLD16: case R_PPC64_GOT_TLSLD16_HA: case R_PPC64_GOT_TLSLD16_HI: @@ -1261,6 +1286,7 @@ break; case R_PPC64_PCREL34: case R_PPC64_GOT_PCREL34: + case R_PPC64_GOT_TLSGD_PCREL34: case R_PPC64_GOT_TPREL_PCREL34: case R_PPC64_TPREL34: { const uint64_t si0Mask = 0x00000003ffff0000; @@ -1340,7 +1366,8 @@ if ((readPrefixedInstruction(data) & 0xfc000000) == 0xe4000000) return R_PPC64_RELAX_GOT_PC; } - if (expr == R_RELAX_TLS_GD_TO_IE) + + if (type != R_PPC64_GOT_TLSGD_PCREL34 && expr == R_RELAX_TLS_GD_TO_IE) return R_RELAX_TLS_GD_TO_IE_GOT_OFF; if (expr == R_RELAX_TLS_LD_TO_LE) return R_RELAX_TLS_LD_TO_LE_ABS; @@ -1381,10 +1408,35 @@ relocateNoSym(loc, R_PPC64_GOT_TPREL16_LO_DS, val); return; } - case R_PPC64_TLSGD: - write32(loc, NOP); // bl __tls_get_addr(sym@tlsgd) --> nop - write32(loc + 4, 0x7c636A14); // nop --> add r3, r3, r13 + case R_PPC64_GOT_TLSGD_PCREL34: { + // Relax from paddi r3, 0, sym@got@tlsgd@pcrel, 1 to + // pld r3, sym@got@tprel@pcrel + writePrefixedInstruction(loc, 0x04100000e4600000); + relocateNoSym(loc, R_PPC64_GOT_TPREL_PCREL34, val); + return; + } + case R_PPC64_TLSGD: { + // PC Relative Relaxation: + // Relax from bl __tls_get_addr@notoc(x@tlsgd) to + // nop + // TOC Relaxation: + // Relax from bl __tls_get_addr(x@tlsgd) + // nop + // to + // nop + // add r3, r3, r13 + const uintptr_t locAsInt = reinterpret_cast(loc); + if (locAsInt % 4 == 0) { + write32(loc, NOP); // bl __tls_get_addr(sym@tlsgd) --> nop + write32(loc + 4, 0x7c636A14); // nop --> add r3, r3, r13 + } else if (locAsInt % 4 == 1) { + // bl __tls_get_addr(sym@tlsgd) --> add r3, r3, r13 + write32(loc - 1, 0x7c636a14); + } else { + errorOrWarn("R_PPC64_TLSGD has unexpected byte alignment"); + } return; + } default: llvm_unreachable("unsupported relocation for TLS GD to IE relaxation"); } diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -1357,6 +1357,24 @@ if (type == R_PPC64_TOC16_LO && sym.isSection() && isa(sym) && cast(sym).section->name == ".toc") ppc64noTocRelax.insert({&sym, addend}); + + if (type == R_PPC64_TLSGD && expr == R_TLSDESC_CALL) { + if (i == end) { + errorOrWarn("R_PPC64_TLSGD may not be the last relocation" + + getLocation(sec, sym, offset)); + return; + } + + // Offset R_PPC64_TLSGD by one byte in the NOTOC case. + // Here we check the next relocation to see if it is R_PPC64_REL24_NOTOC. + // If the next relocation is a NOTOC then the R_PPC64_TLSGD is part of a + // NOTOC sequence and it needs to be handled differently later on. + // For the TOC case the relocaiton should always be 4 byte aligned + // by adding 1 we can mark the NOTOC case as it will be one byte offset + // from the 4 byte alignment. + if (i->getType(/*isMips64EL=*/false) == R_PPC64_REL24_NOTOC) + ++offset; + } } // Relax relocations. diff --git a/lld/test/ELF/ppc64-tls-pcrel-gd.s b/lld/test/ELF/ppc64-tls-pcrel-gd.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/ppc64-tls-pcrel-gd.s @@ -0,0 +1,94 @@ +# REQUIRES: ppc +# RUN: split-file %s %t + +# RUN: llvm-mc -filetype=obj -triple=powerpc64le %t/asm -o %t.o +# RUN: llvm-mc -filetype=obj -triple=powerpc64le %t/defs -o %t-defs.o +# RUN: ld.lld --shared %t-defs.o -o %t-defs.so +# RUN: ld.lld -T %t/lds --shared %t.o -o %t-gd.so +# RUN: ld.lld -T %t/lds %t.o %t-defs.so -o %t-gdtoie +# RUN: ld.lld -T %t/lds %t.o %t-defs.o -o %t-gdtole + +# RUN: llvm-readelf -r %t-gd.so | FileCheck %s --check-prefix=GD-RELOC +# RUN: llvm-readelf -s %t-gd.so | FileCheck %s --check-prefix=GD-SYM +# RUN: llvm-objdump -d --no-show-raw-insn --mcpu=pwr10 %t-gd.so | FileCheck %s --check-prefix=GD + +# RUN: llvm-readelf -r %t-gdtoie | FileCheck %s --check-prefix=GDTOIE-RELOC +# RUN: llvm-readelf -s %t-gdtoie | FileCheck %s --check-prefix=GDTOIE-SYM +# RUN: llvm-objdump -d --no-show-raw-insn --mcpu=pwr10 %t-gdtoie | FileCheck %s --check-prefix=GDTOIE + +# RUN: llvm-readelf -r %t-gdtole | FileCheck %s --check-prefix=GDTOLE-RELOC +# RUN: llvm-readelf -s %t-gdtole | FileCheck %s --check-prefix=GDTOLE-SYM +# RUN: llvm-objdump -d --no-show-raw-insn --mcpu=pwr10 %t-gdtole | FileCheck %s --check-prefix=GDTOLE + +## This test checks the General Dynamic PC Relative TLS implementation for lld. +## GD - General Dynamic with no relaxation possible +## GDTOIE - General Dynamic relaxed to Initial Exec +## GDTOLE - General Dynamic relaxed to Local Exec + +#--- lds +SECTIONS { + .text_addr 0x1001000 : { *(.text_addr) } +} + +#--- defs +.section .tbss,"awT",@nobits +.globl x +x: + .long 0 +.globl y +y: + .long 0 + +#--- asm + +# GD-RELOC: Relocation section '.rela.dyn' at offset 0x100b8 contains 4 entries: +# GD-RELOC: 0000000001001160 0000000200000044 R_PPC64_DTPMOD64 0000000000000000 x + 0 +# GD-RELOC: 0000000001001168 000000020000004e R_PPC64_DTPREL64 0000000000000000 x + 0 +# GD-RELOC: 0000000001001170 0000000300000044 R_PPC64_DTPMOD64 0000000000000000 y + 0 +# GD-RELOC: 0000000001001178 000000030000004e R_PPC64_DTPREL64 0000000000000000 y + 0 + +# GD-SYM: Symbol table '.dynsym' contains 4 entries: +# GD-SYM: 2: 0000000000000000 0 TLS GLOBAL DEFAULT UND x +# GD-SYM: 3: 0000000000000000 0 TLS GLOBAL DEFAULT UND y + + +# GDTOIE-RELOC: Relocation section '.rela.dyn' at offset 0x10118 contains 2 entries: +# GDTOIE-RELOC: 00000000010010e0 0000000200000049 R_PPC64_TPREL64 0000000000000000 x + 0 +# GDTOIE-RELOC: 00000000010010e8 0000000300000049 R_PPC64_TPREL64 0000000000000000 y + 0 + +# GDTOIE-SYM: Symbol table '.dynsym' contains 4 entries: +# GDTOIE-SYM: 2: 0000000000000000 0 TLS GLOBAL DEFAULT UND x +# GDTOIE-SYM: 3: 0000000000000000 0 TLS GLOBAL DEFAULT UND y + + +# GDTOLE-RELOC: There are no relocations in this file. + +# GDTOLE-SYM: Symbol table '.symtab' contains 5 entries: +# GDTOLE-SYM: 3: 0000000000000000 0 TLS GLOBAL DEFAULT 3 x +# GDTOLE-SYM: 4: 0000000000000004 0 TLS GLOBAL DEFAULT 3 y + +# GD-LABEL: : +# GD-NEXT: paddi 3, 0, 352, 1 +# GD-NEXT: bl +# GD-NEXT: paddi 3, 0, 356, 1 +# GD-NEXT: bl +# GD-NEXT: blr +# GDTOIE-LABEL: : +# GDTOIE-NEXT: pld 3, 224(0), 1 +# GDTOIE-NEXT: add 3, 3, 13 +# GDTOIE-NEXT: pld 3, 220(0), 1 +# GDTOIE-NEXT: add 3, 3, 13 +# GDTOIE-NEXT: blr +# GDTOLE-LABEL: : +# GDTOLE-NEXT: paddi 3, 13, -28672, 0 +# GDTOLE-NEXT: nop +# GDTOLE-NEXT: paddi 3, 13, -28668, 0 +# GDTOLE-NEXT: nop +# GDTOLE-NEXT: blr +.section .text_addr, "ax", %progbits +GDTwoVal: + paddi 3, 0, x@got@tlsgd@pcrel, 1 + bl __tls_get_addr@notoc(x@tlsgd) + paddi 3, 0, y@got@tlsgd@pcrel, 1 + bl __tls_get_addr@notoc(y@tlsgd) + blr