summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lld/ELF/Arch/PPC64.cpp103
-rw-r--r--lld/ELF/Relocations.cpp7
-rw-r--r--lld/ELF/Relocations.h1
-rw-r--r--lld/test/ELF/Inputs/ppc64-tls-ie-le.s29
-rw-r--r--lld/test/ELF/ppc64-tls-ie-le.s140
5 files changed, 277 insertions, 3 deletions
diff --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp
index fa3bf6c62a0..cf116828dbf 100644
--- a/lld/ELF/Arch/PPC64.cpp
+++ b/lld/ELF/Arch/PPC64.cpp
@@ -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;
}
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 8f60aa3d2fa..9d8fb2e7f02 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -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
diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h
index 76bb41d9abf..081f13bab07 100644
--- a/lld/ELF/Relocations.h
+++ b/lld/ELF/Relocations.h
@@ -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,
diff --git a/lld/test/ELF/Inputs/ppc64-tls-ie-le.s b/lld/test/ELF/Inputs/ppc64-tls-ie-le.s
new file mode 100644
index 00000000000..1d7ccc8f887
--- /dev/null
+++ b/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
diff --git a/lld/test/ELF/ppc64-tls-ie-le.s b/lld/test/ELF/ppc64-tls-ie-le.s
new file mode 100644
index 00000000000..0c3c305511a
--- /dev/null
+++ b/lld/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)
OpenPOWER on IntegriCloud