Index: lld/ELF/Arch/PPC64.cpp =================================================================== --- lld/ELF/Arch/PPC64.cpp +++ lld/ELF/Arch/PPC64.cpp @@ -22,6 +22,29 @@ static uint64_t PPC64TocOffset = 0x8000; static uint64_t DynamicThreadPointerOffset = 0x8000; +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 @@ -51,6 +74,7 @@ void writeGotHeader(uint8_t *Buf) const override; bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File, uint64_t BranchAddr, const Symbol &S) const override; + void relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; }; } // namespace @@ -156,6 +180,69 @@ return 2; } +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 { + // Initial Exec relocations are in the form: + // addis r9, r2, x@got@tprel@ha [R_PPC64_GOT_TPREL16_HA] + // ld r9, x@got@tprel@l(r9) [R_PPC64_GOT_TPREL16_LO_DS] + // add r9, r9, x@tls [R_PPC64_TLS] + // And can be optimized to: + // nop + // addis r9, r13, x@tprel@ha [R_PPC64_TPREL16_HA] + // addi r9, r9, x@tprel@l [R_PPC64_TPREL16_LO] + + // The last instruction in the sequence with the relocation R_PPC64_TLS + // has multiple options: + // add -> addi + // lbzx -> lbz + // lhzx -> lhz + // lwzx -> lwz + // ldx -> ld + // stbx -> stb + // sthx -> sth + // stwx -> stw + // stdx -> std + 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: { + 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: { + unsigned PrimaryOp = (read32(Loc) & 0xFC000000) >> 26; // bits 0-5 + if (PrimaryOp != 31) + error("Unrecognized instruction for IE to LE R_PPC64_TLS"); + unsigned SecondaryOp = (read32(Loc) & 0x000007FE) >> 1; // bits 21-30 + unsigned DFormOp = getDFormOp (SecondaryOp); + write32(Loc, ((DFormOp << 26) | (read32(Loc) & 0x03FFFFFF))); + relocateOne(Loc + Offset, R_PPC64_TPREL16_LO, Val); + break; + } + } +} + RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const { switch (Type) { @@ -214,8 +301,9 @@ return R_ABS; case R_PPC64_TLSGD: case R_PPC64_TLSLD: - case R_PPC64_TLS: return R_HINT; + case R_PPC64_TLS: + return R_TLS_IE_HINT; default: return R_ABS; } Index: lld/ELF/InputSection.cpp =================================================================== --- lld/ELF/InputSection.cpp +++ lld/ELF/InputSection.cpp @@ -512,6 +512,7 @@ case R_RELAX_TLS_GD_TO_IE: return Sym.getGotVA() + A - P; case R_HINT: + case R_TLS_IE_HINT: case R_NONE: case R_TLSDESC_CALL: llvm_unreachable("cannot relocate hint relocs"); Index: lld/ELF/Relocations.h =================================================================== --- lld/ELF/Relocations.h +++ lld/ELF/Relocations.h @@ -44,6 +44,7 @@ R_GOT_PAGE_PC, R_GOT_PC, R_HINT, + R_TLS_IE_HINT, R_MIPS_GOTREL, R_MIPS_GOT_GP, R_MIPS_GOT_GP_PC, Index: lld/ELF/Relocations.cpp =================================================================== --- lld/ELF/Relocations.cpp +++ lld/ELF/Relocations.cpp @@ -251,13 +251,14 @@ // 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_TLSDESC_CALL) + if (Expr == R_TLSDESC_CALL || Expr == R_TLS_IE_HINT) return 1; return 0; } @@ -340,7 +341,7 @@ R_MIPS_GOT_GP_PC, 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>(E)) + R_TLSDESC_CALL, R_TLSDESC_PAGE, R_HINT, R_TLS_IE_HINT>(E)) return true; // These never do, except if the entire file is position dependent or if Index: lld/test/ELF/Inputs/ppc64-tls-ie-le.s =================================================================== --- /dev/null +++ lld/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/test/ELF/ppc64-tls-ie-le.s =================================================================== --- /dev/null +++ lld/test/ELF/ppc64-tls-ie-le.s @@ -0,0 +1,264 @@ +// 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 + .globl test1 # -- Begin function test1 + .p2align 4 + .type test1,@function +test1: # @test1 +.Lfunc_begin0: +.Lfunc_gep0: + addis 2, 12, .TOC.-.Lfunc_gep0@ha + addi 2, 2, .TOC.-.Lfunc_gep0@l +.Lfunc_lep0: + .localentry test1, .Lfunc_lep0-.Lfunc_gep0 +# %bb.0: # %entry + addis 3, 2, c@got@tprel@ha + ld 3, c@got@tprel@l(3) + lbzx 3, 3, c@tls + blr + .long 0 + .quad 0 +.Lfunc_end0: + .size test1, .Lfunc_end0-.Lfunc_begin0 + # -- End function + .globl test2 # -- Begin function test2 + .p2align 4 + .type test2,@function +test2: # @test2 +.Lfunc_begin1: +.Lfunc_gep1: + addis 2, 12, .TOC.-.Lfunc_gep1@ha + addi 2, 2, .TOC.-.Lfunc_gep1@l +.Lfunc_lep1: + .localentry test2, .Lfunc_lep1-.Lfunc_gep1 +# %bb.0: # %entry + addis 3, 2, s@got@tprel@ha + ld 3, s@got@tprel@l(3) + lhzx 3, 3, s@tls + blr + .long 0 + .quad 0 +.Lfunc_end1: + .size test2, .Lfunc_end1-.Lfunc_begin1 + # -- End function + .globl test3 # -- Begin function test3 + .p2align 4 + .type test3,@function +test3: # @test3 +.Lfunc_begin2: +.Lfunc_gep2: + addis 2, 12, .TOC.-.Lfunc_gep2@ha + addi 2, 2, .TOC.-.Lfunc_gep2@l +.Lfunc_lep2: + .localentry test3, .Lfunc_lep2-.Lfunc_gep2 +# %bb.0: # %entry + addis 3, 2, i@got@tprel@ha + ld 3, i@got@tprel@l(3) + lwzx 3, 3, i@tls + blr + .long 0 + .quad 0 +.Lfunc_end2: + .size test3, .Lfunc_end2-.Lfunc_begin2 + # -- End function + .globl test4 # -- Begin function test4 + .p2align 4 + .type test4,@function +test4: # @test4 +.Lfunc_begin3: +.Lfunc_gep3: + addis 2, 12, .TOC.-.Lfunc_gep3@ha + addi 2, 2, .TOC.-.Lfunc_gep3@l +.Lfunc_lep3: + .localentry test4, .Lfunc_lep3-.Lfunc_gep3 +# %bb.0: # %entry + addis 3, 2, l@got@tprel@ha + ld 3, l@got@tprel@l(3) + ldx 3, 3, l@tls + blr + .long 0 + .quad 0 +.Lfunc_end3: + .size test4, .Lfunc_end3-.Lfunc_begin3 + # -- End function + + .globl test5 # -- Begin function test5 + .p2align 4 + .type test5,@function +test5: # @test5 +.Lfunc_begin4: +.Lfunc_gep4: + addis 2, 12, .TOC.-.Lfunc_gep4@ha + addi 2, 2, .TOC.-.Lfunc_gep4@l +.Lfunc_lep4: + .localentry test5, .Lfunc_lep4-.Lfunc_gep4 +# %bb.0: # %entry + addis 4, 2, c@got@tprel@ha + ld 4, c@got@tprel@l(4) + stbx 3, 4, c@tls + blr + .long 0 + .quad 0 +.Lfunc_end4: + .size test5, .Lfunc_end4-.Lfunc_begin4 + # -- End function + .globl test6 # -- Begin function test6 + .p2align 4 + .type test6,@function +test6: # @test6 +.Lfunc_begin5: +.Lfunc_gep5: + addis 2, 12, .TOC.-.Lfunc_gep5@ha + addi 2, 2, .TOC.-.Lfunc_gep5@l +.Lfunc_lep5: + .localentry test6, .Lfunc_lep5-.Lfunc_gep5 +# %bb.0: # %entry + addis 4, 2, s@got@tprel@ha + ld 4, s@got@tprel@l(4) + sthx 3, 4, s@tls + blr + .long 0 + .quad 0 +.Lfunc_end5: + .size test6, .Lfunc_end5-.Lfunc_begin5 + # -- End function + .globl test7 # -- Begin function test7 + .p2align 4 + .type test7,@function +test7: # @test7 +.Lfunc_begin6: +.Lfunc_gep6: + addis 2, 12, .TOC.-.Lfunc_gep6@ha + addi 2, 2, .TOC.-.Lfunc_gep6@l +.Lfunc_lep6: + .localentry test7, .Lfunc_lep6-.Lfunc_gep6 +# %bb.0: # %entry + addis 4, 2, i@got@tprel@ha + ld 4, i@got@tprel@l(4) + stwx 3, 4, i@tls + blr + .long 0 + .quad 0 +.Lfunc_end6: + .size test7, .Lfunc_end6-.Lfunc_begin6 + # -- End function + .globl test8 # -- Begin function test8 + .p2align 4 + .type test8,@function +test8: # @test8 +.Lfunc_begin7: +.Lfunc_gep7: + addis 2, 12, .TOC.-.Lfunc_gep7@ha + addi 2, 2, .TOC.-.Lfunc_gep7@l +.Lfunc_lep7: + .localentry test8, .Lfunc_lep7-.Lfunc_gep7 +# %bb.0: # %entry + addis 4, 2, l@got@tprel@ha + ld 4, l@got@tprel@l(4) + stdx 3, 4, l@tls + blr + .long 0 + .quad 0 +.Lfunc_end7: + .size test8, .Lfunc_end7-.Lfunc_begin7 + .globl test9 # -- Begin function test9 + .p2align 4 + .type test9,@function +test9: # @test9 +.Lfunc_begin8: +.Lfunc_gep8: + addis 2, 12, .TOC.-.Lfunc_gep8@ha + addi 2, 2, .TOC.-.Lfunc_gep8@l +.Lfunc_lep8: + .localentry test9, .Lfunc_lep8-.Lfunc_gep8 +# %bb.0: # %entry + addis 3, 2, i@got@tprel@ha + ld 3, i@got@tprel@l(3) + add 3, 3, i@tls + blr + .long 0 + .quad 0 +.Lfunc_end8: + .size test9, .Lfunc_end8-.Lfunc_begin8 + # -- End function + +// 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 + +// 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