[PPC64] Add TLS initial exec to local exec relaxation

This patch adds the target call back relaxTlsIeToLe to support TLS relaxation
from initial exec to local exec model.

Differential Revision: https://reviews.llvm.org/D48091

llvm-svn: 340281
This commit is contained in:
Zaara Syeda 2018-08-21 15:13:53 +00:00
parent 06209cb466
commit 662d146c5b
5 changed files with 277 additions and 3 deletions

View File

@ -23,6 +23,32 @@ using namespace lld::elf;
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 @@ public:
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 @@ void PPC64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const {
}
}
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 @@ RelExpr PPC64::getRelExpr(RelType Type, const Symbol &S,
case R_PPC64_TLSLD:
return R_TLSLD_HINT;
case R_PPC64_TLS:
return R_HINT;
return R_TLSIE_HINT;
default:
return R_ABS;
}

View File

@ -270,12 +270,15 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C,
// Initial-Exec relocs can be relaxed to Local-Exec if the symbol is locally
// defined.
if (isRelExprOneOf<R_GOT, R_GOT_FROM_END, R_GOT_PC, R_GOT_PAGE_PC>(Expr) &&
if (isRelExprOneOf<R_GOT, R_GOT_FROM_END, R_GOT_PC, R_GOT_PAGE_PC, R_GOT_OFF,
R_TLSIE_HINT>(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 @@ static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym,
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

View File

@ -83,6 +83,7 @@ enum RelExpr {
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,

View File

@ -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

View File

@ -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)