Index: lld/trunk/ELF/Arch/PPC64.cpp =================================================================== --- lld/trunk/ELF/Arch/PPC64.cpp +++ lld/trunk/ELF/Arch/PPC64.cpp @@ -23,6 +23,32 @@ static uint64_t PPC64TocOffset = 0x8000; static uint64_t DynamicThreadPointerOffset = 0x8000; +// The instruction encoding of bits 21-30 from the ISA for the Xform and Dform +// instructions that can be used as part of the initial exec TLS sequence. +enum XFormOpcd { + LBZX = 87, + LHZX = 279, + LWZX = 23, + LDX = 21, + STBX = 215, + STHX = 407, + STWX = 151, + STDX = 149, + ADD = 266, +}; + +enum DFormOpcd { + LBZ = 34, + LHZ = 40, + LWZ = 32, + LD = 58, + STB = 38, + STH = 44, + STW = 36, + STD = 62, + ADDI = 14 +}; + uint64_t elf::getPPC64TocBase() { // The TOC consists of sections .got, .toc, .tocbss, .plt in that order. The // TOC starts where the first of these sections starts. We always create a @@ -56,6 +82,7 @@ void relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const override; void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; void relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; }; } // namespace @@ -212,6 +239,80 @@ } } +static unsigned getDFormOp(unsigned SecondaryOp) { + switch (SecondaryOp) { + case LBZX: + return LBZ; + case LHZX: + return LHZ; + case LWZX: + return LWZ; + case LDX: + return LD; + case STBX: + return STB; + case STHX: + return STH; + case STWX: + return STW; + case STDX: + return STD; + case ADD: + return ADDI; + default: + error("unrecognized instruction for IE to LE R_PPC64_TLS"); + return 0; + } +} + +void PPC64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { + // The initial exec code sequence for a global `x` will look like: + // Instruction Relocation Symbol + // addis r9, r2, x@got@tprel@ha R_PPC64_GOT_TPREL16_HA x + // ld r9, x@got@tprel@l(r9) R_PPC64_GOT_TPREL16_LO_DS x + // add r9, r9, x@tls R_PPC64_TLS x + + // Relaxing to local exec entails converting: + // addis r9, r2, x@got@tprel@ha into nop + // ld r9, x@got@tprel@l(r9) into addis r9, r13, x@tprel@ha + // add r9, r9, x@tls into addi r9, r9, x@tprel@l + + // x@tls R_PPC64_TLS is a relocation which does not compute anything, + // it is replaced with r13 (thread pointer). + + // The add instruction in the initial exec sequence has multiple variations + // that need to be handled. If we are building an address it will use an add + // instruction, if we are accessing memory it will use any of the X-form + // indexed load or store instructions. + + unsigned Offset = (Config->EKind == ELF64BEKind) ? 2 : 0; + switch (Type) { + case R_PPC64_GOT_TPREL16_HA: + write32(Loc - Offset, 0x60000000); // nop + break; + case R_PPC64_GOT_TPREL16_LO_DS: + case R_PPC64_GOT_TPREL16_DS: { + uint32_t RegNo = read32(Loc - Offset) & 0x03E00000; // bits 6-10 + write32(Loc - Offset, 0x3C0D0000 | RegNo); // addis RegNo, r13 + relocateOne(Loc, R_PPC64_TPREL16_HA, Val); + break; + } + case R_PPC64_TLS: { + uint32_t PrimaryOp = (read32(Loc) & 0xFC000000) >> 26; // bits 0-5 + 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 = getDFormOp(SecondaryOp); + write32(Loc, ((DFormOp << 26) | (read32(Loc) & 0x03FFFFFF))); + relocateOne(Loc + Offset, R_PPC64_TPREL16_LO, Val); + break; + } + default: + llvm_unreachable("unknown relocation for IE to LE"); + break; + } +} + RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const { switch (Type) { @@ -279,7 +380,7 @@ case R_PPC64_TLSLD: return R_TLSLD_HINT; case R_PPC64_TLS: - return R_HINT; + return R_TLSIE_HINT; default: return R_ABS; } Index: lld/trunk/ELF/Relocations.h =================================================================== --- lld/trunk/ELF/Relocations.h +++ lld/trunk/ELF/Relocations.h @@ -83,6 +83,7 @@ R_TLSGD_GOT, R_TLSGD_GOT_FROM_END, R_TLSGD_PC, + R_TLSIE_HINT, R_TLSLD_GOT, R_TLSLD_GOT_FROM_END, R_TLSLD_GOT_OFF, Index: lld/trunk/ELF/Relocations.cpp =================================================================== --- lld/trunk/ELF/Relocations.cpp +++ lld/trunk/ELF/Relocations.cpp @@ -270,12 +270,15 @@ // Initial-Exec relocs can be relaxed to Local-Exec if the symbol is locally // defined. - if (isRelExprOneOf(Expr) && + if (isRelExprOneOf(Expr) && !Config->Shared && !Sym.IsPreemptible) { C.Relocations.push_back({R_RELAX_TLS_IE_TO_LE, Type, Offset, Addend, &Sym}); return 1; } + if (Expr == R_TLSIE_HINT) + return 1; return 0; } @@ -358,7 +361,7 @@ R_MIPS_TLSGD, R_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC, R_GOTONLY_PC_FROM_END, R_PLT_PC, R_TLSGD_GOT, R_TLSGD_GOT_FROM_END, R_TLSGD_PC, R_PPC_CALL_PLT, R_TLSDESC_CALL, R_TLSDESC_PAGE, R_HINT, - R_TLSLD_HINT>(E)) + R_TLSLD_HINT, R_TLSIE_HINT>(E)) return true; // These never do, except if the entire file is position dependent or if Index: lld/trunk/test/ELF/Inputs/ppc64-tls-ie-le.s =================================================================== --- lld/trunk/test/ELF/Inputs/ppc64-tls-ie-le.s +++ lld/trunk/test/ELF/Inputs/ppc64-tls-ie-le.s @@ -0,0 +1,29 @@ + .text + .abiversion 2 + .type c,@object # @c + .section .tdata,"awT",@progbits + .globl c +c: + .byte 97 # 0x61 + .size c, 1 + + .type s,@object # @s + .globl s + .p2align 1 +s: + .short 55 # 0x37 + .size s, 2 + + .type i,@object # @i + .globl i + .p2align 2 +i: + .long 55 # 0x37 + .size i, 4 + + .type l,@object # @l + .globl l + .p2align 3 +l: + .quad 55 # 0x37 + .size l, 8 Index: lld/trunk/test/ELF/ppc64-tls-ie-le.s =================================================================== --- lld/trunk/test/ELF/ppc64-tls-ie-le.s +++ lld/trunk/test/ELF/ppc64-tls-ie-le.s @@ -0,0 +1,140 @@ +// REQUIRES: ppc + +// RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %s -o %t.o +// RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %p/Inputs/ppc64-tls-ie-le.s -o %t2.o +// RUN: ld.lld -dynamic-linker /lib64/ld64.so.2 %t.o %t2.o -o %t +// RUN: llvm-readelf -relocations --wide %t.o | FileCheck --check-prefix=InputRelocs %s +// RUN: llvm-readelf -relocations --wide %t | FileCheck --check-prefix=OutputRelocs %s +// RUN: llvm-objdump -D %t | FileCheck --check-prefix=Dis %s + +// RUN: llvm-mc -filetype=obj -triple=powerpc64-unknown-linux %s -o %t.o +// RUN: llvm-mc -filetype=obj -triple=powerpc64-unknown-linux %p/Inputs/ppc64-tls-ie-le.s -o %t2.o +// RUN: ld.lld -dynamic-linker /lib64/ld64.so.2 %t.o %t2.o -o %t +// RUN: llvm-readelf -relocations --wide %t.o | FileCheck --check-prefix=InputRelocs %s +// RUN: llvm-readelf -relocations --wide %t | FileCheck --check-prefix=OutputRelocs %s +// RUN: llvm-objdump -D %t | FileCheck --check-prefix=Dis %s + + .text + .abiversion 2 +test1: # @test1 + addis 3, 2, c@got@tprel@ha + ld 3, c@got@tprel@l(3) + lbzx 3, 3, c@tls + blr +test2: # @test2 + addis 3, 2, s@got@tprel@ha + ld 3, s@got@tprel@l(3) + lhzx 3, 3, s@tls + blr +test3: # @test3 + addis 3, 2, i@got@tprel@ha + ld 3, i@got@tprel@l(3) + lwzx 3, 3, i@tls + blr +test4: # @test4 + addis 3, 2, l@got@tprel@ha + ld 3, l@got@tprel@l(3) + ldx 3, 3, l@tls + blr +test5: # @test5 + addis 4, 2, c@got@tprel@ha + ld 4, c@got@tprel@l(4) + stbx 3, 4, c@tls + blr +test6: # @test6 + addis 4, 2, s@got@tprel@ha + ld 4, s@got@tprel@l(4) + sthx 3, 4, s@tls + blr +test7: # @test7 + addis 4, 2, i@got@tprel@ha + ld 4, i@got@tprel@l(4) + stwx 3, 4, i@tls + blr +test8: # @test8 + addis 4, 2, l@got@tprel@ha + ld 4, l@got@tprel@l(4) + stdx 3, 4, l@tls + blr +test9: # @test9 + addis 3, 2, i@got@tprel@ha + ld 3, i@got@tprel@l(3) + add 3, 3, i@tls + blr +test_ds: # @test_ds + ld 4, l@got@tprel(2) + stdx 3, 4, l@tls + blr + + +// Verify that the input has initial-exec tls relocation types. +// InputRelocs: Relocation section '.rela.text' +// InputRelocs: R_PPC64_GOT_TPREL16_HA {{0+}} c + 0 +// InputRelocs: R_PPC64_GOT_TPREL16_LO_DS {{0+}} c + 0 +// InputRelocs: R_PPC64_TLS {{0+}} c + 0 +// InputRelocs: R_PPC64_GOT_TPREL16_HA {{0+}} s + 0 +// InputRelocs: R_PPC64_GOT_TPREL16_LO_DS {{0+}} s + 0 +// InputRelocs: R_PPC64_TLS {{0+}} s + 0 +// InputRelocs: R_PPC64_GOT_TPREL16_HA {{0+}} i + 0 +// InputRelocs: R_PPC64_GOT_TPREL16_LO_DS {{0+}} i + 0 +// InputRelocs: R_PPC64_TLS {{0+}} i + 0 +// InputRelocs: R_PPC64_GOT_TPREL16_HA {{0+}} l + 0 +// InputRelocs: R_PPC64_GOT_TPREL16_LO_DS {{0+}} l + 0 +// InputRelocs: R_PPC64_TLS {{0+}} l + 0 +// InputRelocs: R_PPC64_GOT_TPREL16_DS {{0+}} l + 0 +// InputRelocs: R_PPC64_TLS {{0+}} l + 0 + +// Verify that no initial-exec relocations exist for the dynamic linker. +// OutputRelocs-NOT: R_PPC64_TPREL64 {{0+}} c + 0 +// OutputRelocs-NPT: R_PPC64_TPREL64 {{0+}} s + 0 +// OutputRelocs-NOT: R_PPC64_TPREL64 {{0+}} i + 0 +// OutputRelocs-NOT: R_PPC64_TPREL64 {{0+}} l + 0 + +// Dis: test1: +// Dis: nop +// Dis: addis 3, 13, 0 +// Dis: lbz 3, -28672(3) + +// Dis: test2: +// Dis: nop +// Dis: addis 3, 13, 0 +// Dis: lhz 3, -28670(3) + +// Dis: test3: +// Dis: nop +// Dis: addis 3, 13, 0 +// Dis: lwz 3, -28668(3) + +// Dis: test4: +// Dis: nop +// Dis: addis 3, 13, 0 +// Dis: ld 3, -28664(3) + +// Dis: test5: +// Dis: nop +// Dis: addis 4, 13, 0 +// Dis: stb 3, -28672(4) + +// Dis: test6: +// Dis: nop +// Dis: addis 4, 13, 0 +// Dis: sth 3, -28670(4) + +// Dis: test7: +// Dis: nop +// Dis: addis 4, 13, 0 +// Dis: stw 3, -28668(4) + +// Dis: test8: +// Dis: nop +// Dis: addis 4, 13, 0 +// Dis: std 3, -28664(4) + +// Dis: test9: +// Dis: nop +// Dis: addis 3, 13, 0 +// Dis: addi 3, 3, -28668 + +// Dis: test_ds: +// Dis: addis 4, 13, 0 +// Dis: std 3, -28664(4)