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 @@ -840,16 +840,49 @@ relocateNoSym(loc, R_PPC64_TPREL16_HA, val); break; } + case R_PPC64_GOT_TPREL_PCREL34: { + const uint64_t pldRT = readPrefixedInstruction(loc) & 0x0000000003e00000; + // paddi RT(from pld), r13, symbol@tprel, 0 + writePrefixedInstruction(loc, 0x06000000380d0000 | pldRT); + relocateNoSym(loc, R_PPC64_TPREL34, val); + break; + } case R_PPC64_TLS: { - uint32_t primaryOp = getPrimaryOpCode(read32(loc)); - if (primaryOp != 31) - error("unrecognized instruction for IE to LE R_PPC64_TLS"); - uint32_t secondaryOp = (read32(loc) & 0x000007FE) >> 1; // bits 21-30 - uint32_t dFormOp = getPPCDFormOp(secondaryOp); - if (dFormOp == 0) - error("unrecognized instruction for IE to LE R_PPC64_TLS"); - write32(loc, ((dFormOp << 26) | (read32(loc) & 0x03FFFFFF))); - relocateNoSym(loc + offset, R_PPC64_TPREL16_LO, val); + const uintptr_t locAsInt = reinterpret_cast(loc); + if (locAsInt % 4 == 0) { + uint32_t primaryOp = getPrimaryOpCode(read32(loc)); + if (primaryOp != 31) + error("unrecognized instruction for IE to LE R_PPC64_TLS"); + uint32_t secondaryOp = (read32(loc) & 0x000007FE) >> 1; // bits 21-30 + uint32_t dFormOp = getPPCDFormOp(secondaryOp); + if (dFormOp == 0) + error("unrecognized instruction for IE to LE R_PPC64_TLS"); + write32(loc, ((dFormOp << 26) | (read32(loc) & 0x03FFFFFF))); + relocateNoSym(loc + offset, R_PPC64_TPREL16_LO, val); + } else if (locAsInt % 4 == 1) { + // If the offset is not 4 byte aligned then we have a PCRel type reloc. + // This version of the relocation is offset by one byte from the + // instruction it references. + uint32_t tlsInstr = read32(loc - 1); + uint32_t primaryOp = getPrimaryOpCode(tlsInstr); + if (primaryOp != 31) + errorOrWarn("unrecognized instruction for IE to LE R_PPC64_TLS"); + uint32_t secondaryOp = (tlsInstr & 0x000007FE) >> 1; // bits 21-30 + // The add is a special case and should be turned into a nop. The paddi + // that comes before it will already have computed the address of the + // symbol. + if (secondaryOp == 266) { + write32(loc - 1, NOP); + } else { + uint32_t dFormOp = getPPCDFormOp(secondaryOp); + if (dFormOp == 0) + errorOrWarn("unrecognized instruction for IE to LE R_PPC64_TLS"); + write32(loc - 1, ((dFormOp << 26) | (tlsInstr & 0x03FF0000))); + } + } else { + error("R_PPC64_TLS must be either 4 byte aligned or one byte offset " + "from 4 byte aligned"); + } break; } default: @@ -889,6 +922,7 @@ case R_PPC64_TOC16_LO: return R_GOTREL; case R_PPC64_GOT_PCREL34: + case R_PPC64_GOT_TPREL_PCREL34: case R_PPC64_PCREL_OPT: return R_GOT_PC; case R_PPC64_TOC16_HA: @@ -1237,6 +1271,7 @@ break; } case R_PPC64_GOT_PCREL34: + case R_PPC64_GOT_TPREL_PCREL34: case R_PPC64_TPREL34: { const uint64_t si0Mask = 0x00000003ffff0000; const uint64_t si1Mask = 0x000000000000ffff; diff --git a/lld/test/ELF/Inputs/ppc64-tls-vardef.s b/lld/test/ELF/Inputs/ppc64-tls-vardef.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/Inputs/ppc64-tls-vardef.s @@ -0,0 +1,11 @@ +## Thread Local Storage variable definitions. +.section .tbss,"awT",@nobits +.globl x +x: + .long 0 + .size x, 4 + +.globl y +y: + .long 0 + .size y, 4 diff --git a/lld/test/ELF/ppc64-tls-pcrel-ie.s b/lld/test/ELF/ppc64-tls-pcrel-ie.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/ppc64-tls-pcrel-ie.s @@ -0,0 +1,117 @@ +# REQUIRES: ppc +# RUN: echo 'SECTIONS { \ +# RUN: .text_addr 0x1001000 : { *(.text_addr) } \ +# RUN: .text_val 0x1002000 : { *(.text_val) } \ +# RUN: .text_twoval 0x1003000 : { *(.text_twoval) } \ +# RUN: .text_incrval 0x1004000 : { *(.text_incrval) } \ +# RUN: }' > %t.script + +# RUN: llvm-mc -filetype=obj -triple=powerpc64le %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=powerpc64le %p/Inputs/ppc64-tls-vardef.s -o %t-defs.o +# RUN: ld.lld --shared %t-defs.o --soname=t-defs -o %t-defs.so +# RUN: ld.lld -T %t.script %t.o %t-defs.so -o %t-shared +# RUN: ld.lld -T %t.script %t.o %t-defs.o -o %t-static + +# RUN: llvm-readelf -r %t-shared | FileCheck %s --check-prefix=SHARE-RELOC +# RUN: llvm-readelf -s %t-shared | FileCheck %s --check-prefix=SHARE-SYM +# RUN: llvm-objdump -d --no-show-raw-insn --mcpu=pwr10 %t-shared | FileCheck %s --check-prefix=SHARE + +# RUN: llvm-readelf -r %t-static | FileCheck %s --check-prefix=STATIC-RELOC +# RUN: llvm-readelf -s %t-static | FileCheck %s --check-prefix=STATIC-SYM +# RUN: llvm-objdump -d --no-show-raw-insn --mcpu=pwr10 %t-static | FileCheck %s --check-prefix=STATIC + +## This test checks the Initial Exec PC Relative TLS implementation for lld. +## The SHARE version checks that the relocations are generated correctly. +## The STATIC version checks that the Initial Exec to Local Exec relaxation is +## done correctly. + +# SHARE-RELOC: Relocation section '.rela.dyn' at offset 0x10090 contains 2 entries: +# SHARE-RELOC: 00000000010040e0 0000000100000049 R_PPC64_TPREL64 0000000000000000 x + 0 +# SHARE-RELOC: 00000000010040e8 0000000200000049 R_PPC64_TPREL64 0000000000000000 y + 0 + +# SHARE-SYM: Symbol table '.dynsym' contains 3 entries: +# SHARE-SYM: 1: 0000000000000000 0 TLS GLOBAL DEFAULT UND x +# SHARE-SYM: 2: 0000000000000000 0 TLS GLOBAL DEFAULT UND y + +# STATIC-RELOC: There are no relocations in this file. + +# STATIC-SYM: Symbol table '.symtab' contains 7 entries: +# STATIC-SYM: 5: 0000000000000000 4 TLS GLOBAL DEFAULT 6 x +# STATIC-SYM: 6: 0000000000000004 4 TLS GLOBAL DEFAULT 6 y + +# SHARE-LABEL: : +# SHARE-NEXT: pld 3, 12512(0), 1 +# SHARE-NEXT: add 3, 3, 13 +# SHARE-NEXT: blr +# STATIC-LABEL: : +# STATIC-NEXT: paddi 3, 13, -28672, 0 +# STATIC-NEXT: nop +# STATIC-NEXT: blr +.section .text_addr, "ax", %progbits +IEAddr: + pld 3, x@got@tprel@pcrel(0), 1 + add 3, 3, x@tls@pcrel + blr + +# SHARE-LABEL: : +# SHARE-NEXT: pld 3, 8416(0), 1 +# SHARE-NEXT: lwzx 3, 3, 13 +# SHARE-NEXT: blr +# STATIC-LABEL: : +# STATIC-NEXT: paddi 3, 13, -28672, 0 +# STATIC-NEXT: lwz 3, 0(3) +# STATIC-NEXT: blr +.section .text_val, "ax", %progbits +IEVal: + pld 3, x@got@tprel@pcrel(0), 1 + lwzx 3, 3, x@tls@pcrel + blr + +# SHARE-LABEL: : +# SHARE-NEXT: pld 3, 4320(0), 1 +# SHARE-NEXT: pld 4, 4320(0), 1 +# SHARE-NEXT: lwzx 3, 3, 13 +# SHARE-NEXT: lwzx 4, 4, 13 +# SHARE-NEXT: add 3, 4, 3 +# SHARE-NEXT: clrldi 3, 3, 32 +# SHARE-NEXT: blr +# STATIC-LABEL: : +# STATIC-NEXT: paddi 3, 13, -28672, 0 +# STATIC-NEXT: paddi 4, 13, -28668, 0 +# STATIC-NEXT: lwz 3, 0(3) +# STATIC-NEXT: lwz 4, 0(4) +# STATIC-NEXT: add 3, 4, 3 +# STATIC-NEXT: clrldi 3, 3, 32 +# STATIC-NEXT: blr +.section .text_twoval, "ax", %progbits +IETwoVal: + pld 3, x@got@tprel@pcrel(0), 1 + pld 4, y@got@tprel@pcrel(0), 1 + lwzx 3, 3, x@tls@pcrel + lwzx 4, 4, y@tls@pcrel + add 3, 4, 3 + clrldi 3, 3, 32 + blr + +# SHARE-LABEL: : +# SHARE-NEXT: pld 4, 232(0), 1 +# SHARE-NEXT: lwzx 3, 4, 13 +# SHARE-NEXT: addi 5, 3, 1 +# SHARE-NEXT: clrldi 3, 5, 32 +# SHARE-NEXT: stwx 5, 4, 13 +# SHARE-NEXT: blr +# STATIC-LABEL: : +# STATIC-NEXT: paddi 4, 13, -28668, 0 +# STATIC-NEXT: lwz 3, 0(4) +# STATIC-NEXT: addi 5, 3, 1 +# STATIC-NEXT: clrldi 3, 5, 32 +# STATIC-NEXT: stw 5, 0(4) +# STATIC-NEXT: blr +.section .text_incrval, "ax", %progbits +IEIncrementVal: + pld 4, y@got@tprel@pcrel(0), 1 + lwzx 3, 4, y@tls@pcrel + addi 5, 3, 1 + clrldi 3, 5, 32 + stwx 5, 4, y@tls@pcrel + blr