summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lld/ELF/Target.cpp198
-rw-r--r--lld/ELF/Target.h6
-rw-r--r--lld/test/ELF/aarch64-tls-gdle.s26
-rw-r--r--lld/test/ELF/aarch64-tls-iele.s21
-rw-r--r--lld/test/ELF/aarch64-tls-le.s31
5 files changed, 261 insertions, 21 deletions
diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp
index 444cbb3cbb1..afaa9b5ad4b 100644
--- a/lld/ELF/Target.cpp
+++ b/lld/ELF/Target.cpp
@@ -83,6 +83,8 @@ public:
void writeGotPltHeader(uint8_t *Buf) const override;
unsigned getDynRel(unsigned Type) const override;
unsigned getTlsGotRel(unsigned Type) const override;
+ bool isTlsLocalDynamicRel(unsigned Type) const override;
+ bool isTlsGlobalDynamicRel(unsigned Type) const override;
bool isTlsDynRel(unsigned Type, const SymbolBody &S) const override;
void writeGotPlt(uint8_t *Buf, uint64_t Plt) const override;
void writePltZero(uint8_t *Buf) const override;
@@ -115,6 +117,8 @@ class X86_64TargetInfo final : public TargetInfo {
public:
X86_64TargetInfo();
unsigned getTlsGotRel(unsigned Type) const override;
+ bool isTlsLocalDynamicRel(unsigned Type) const override;
+ bool isTlsGlobalDynamicRel(unsigned Type) const override;
bool isTlsDynRel(unsigned Type, const SymbolBody &S) const override;
void writeGotPltHeader(uint8_t *Buf) const override;
void writeGotPlt(uint8_t *Buf, uint64_t Plt) const override;
@@ -170,6 +174,7 @@ class AArch64TargetInfo final : public TargetInfo {
public:
AArch64TargetInfo();
unsigned getDynRel(unsigned Type) const override;
+ bool isTlsGlobalDynamicRel(unsigned Type) const override;
void writeGotPlt(uint8_t *Buf, uint64_t Plt) const override;
void writePltZero(uint8_t *Buf) const override;
void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr,
@@ -182,6 +187,17 @@ public:
void relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type, uint64_t P,
uint64_t SA, uint64_t ZA = 0,
uint8_t *PairedLoc = nullptr) const override;
+ unsigned relaxTls(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type, uint64_t P,
+ uint64_t SA, const SymbolBody *S) const override;
+ bool canRelaxTls(unsigned Type, const SymbolBody *S) const override;
+
+private:
+ void relocateTlsGdToLe(unsigned Type, uint8_t *Loc, uint8_t *BufEnd,
+ uint64_t P, uint64_t SA) const;
+ void relocateTlsIeToLe(unsigned Type, uint8_t *Loc, uint8_t *BufEnd,
+ uint64_t P, uint64_t SA) const;
+
+ static const uint64_t TcbSize = 16;
};
class AMDGPUTargetInfo final : public TargetInfo {
@@ -251,14 +267,6 @@ bool TargetInfo::needsCopyRel(uint32_t Type, const SymbolBody &S) const {
return false;
}
-bool TargetInfo::isTlsLocalDynamicRel(unsigned Type) const {
- return Type == TlsLocalDynamicRel;
-}
-
-bool TargetInfo::isTlsGlobalDynamicRel(unsigned Type) const {
- return Type == TlsGlobalDynamicRel;
-}
-
bool TargetInfo::isTlsDynRel(unsigned Type, const SymbolBody &S) const {
return false;
}
@@ -272,6 +280,14 @@ bool TargetInfo::needsGot(uint32_t Type, SymbolBody &S) const { return false; }
bool TargetInfo::needsPlt(uint32_t Type, SymbolBody &S) const { return false; }
+bool TargetInfo::isTlsLocalDynamicRel(unsigned Type) const {
+ return false;
+}
+
+bool TargetInfo::isTlsGlobalDynamicRel(unsigned Type) const {
+ return false;
+}
+
unsigned TargetInfo::relaxTls(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type,
uint64_t P, uint64_t SA,
const SymbolBody *S) const {
@@ -285,8 +301,6 @@ X86TargetInfo::X86TargetInfo() {
IRelativeRel = R_386_IRELATIVE;
RelativeRel = R_386_RELATIVE;
TlsGotRel = R_386_TLS_TPOFF;
- TlsGlobalDynamicRel = R_386_TLS_GD;
- TlsLocalDynamicRel = R_386_TLS_LDM;
TlsModuleIndexRel = R_386_TLS_DTPMOD32;
TlsOffsetRel = R_386_TLS_DTPOFF32;
UseLazyBinding = true;
@@ -318,6 +332,14 @@ unsigned X86TargetInfo::getTlsGotRel(unsigned Type) const {
return TlsGotRel;
}
+bool X86TargetInfo::isTlsGlobalDynamicRel(unsigned Type) const {
+ return Type == R_386_TLS_GD;
+}
+
+bool X86TargetInfo::isTlsLocalDynamicRel(unsigned Type) const {
+ return Type == R_386_TLS_LDM;
+}
+
bool X86TargetInfo::isTlsDynRel(unsigned Type, const SymbolBody &S) const {
if (Type == R_386_TLS_LE || Type == R_386_TLS_LE_32 ||
Type == R_386_TLS_GOTIE)
@@ -593,8 +615,6 @@ X86_64TargetInfo::X86_64TargetInfo() {
RelativeRel = R_X86_64_RELATIVE;
IRelativeRel = R_X86_64_IRELATIVE;
TlsGotRel = R_X86_64_TPOFF64;
- TlsLocalDynamicRel = R_X86_64_TLSLD;
- TlsGlobalDynamicRel = R_X86_64_TLSGD;
TlsModuleIndexRel = R_X86_64_DTPMOD64;
TlsOffsetRel = R_X86_64_DTPOFF64;
UseLazyBinding = true;
@@ -662,6 +682,14 @@ unsigned X86_64TargetInfo::getTlsGotRel(unsigned Type) const {
return R_X86_64_PC32;
}
+bool X86_64TargetInfo::isTlsGlobalDynamicRel(unsigned Type) const {
+ return Type == R_X86_64_TLSGD;
+}
+
+bool X86_64TargetInfo::isTlsLocalDynamicRel(unsigned Type) const {
+ return Type == R_X86_64_TLSLD;
+}
+
bool X86_64TargetInfo::isTlsDynRel(unsigned Type, const SymbolBody &S) const {
return Type == R_X86_64_GOTTPOFF || Type == R_X86_64_TLSGD;
}
@@ -1153,11 +1181,20 @@ AArch64TargetInfo::AArch64TargetInfo() {
GotRel = R_AARCH64_GLOB_DAT;
PltRel = R_AARCH64_JUMP_SLOT;
TlsGotRel = R_AARCH64_TLS_TPREL64;
+ TlsModuleIndexRel = R_AARCH64_TLS_DTPMOD64;
+ TlsOffsetRel = R_AARCH64_TLS_DTPREL64;
UseLazyBinding = true;
PltEntrySize = 16;
PltZeroSize = 32;
}
+bool AArch64TargetInfo::isTlsGlobalDynamicRel(unsigned Type) const {
+ return Type == R_AARCH64_TLSDESC_ADR_PAGE21 ||
+ Type == R_AARCH64_TLSDESC_LD64_LO12_NC ||
+ Type == R_AARCH64_TLSDESC_ADD_LO12_NC ||
+ Type == R_AARCH64_TLSDESC_CALL;
+}
+
unsigned AArch64TargetInfo::getDynRel(unsigned Type) const {
if (Type == R_AARCH64_ABS32 || Type == R_AARCH64_ABS64)
return Type;
@@ -1220,7 +1257,11 @@ unsigned AArch64TargetInfo::getTlsGotRel(unsigned Type) const {
}
bool AArch64TargetInfo::isTlsDynRel(unsigned Type, const SymbolBody &S) const {
- return Type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 ||
+ return Type == R_AARCH64_TLSDESC_ADR_PAGE21 ||
+ Type == R_AARCH64_TLSDESC_LD64_LO12_NC ||
+ Type == R_AARCH64_TLSDESC_ADD_LO12_NC ||
+ Type == R_AARCH64_TLSDESC_CALL ||
+ Type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 ||
Type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC;
}
@@ -1273,13 +1314,17 @@ bool AArch64TargetInfo::needsPlt(uint32_t Type, SymbolBody &S) const {
}
}
-static void updateAArch64Adr(uint8_t *L, uint64_t Imm) {
+static void updateAArch64Addr(uint8_t *L, uint64_t Imm) {
uint32_t ImmLo = (Imm & 0x3) << 29;
uint32_t ImmHi = ((Imm & 0x1FFFFC) >> 2) << 5;
uint64_t Mask = (0x3 << 29) | (0x7FFFF << 5);
write32le(L, (read32le(L) & ~Mask) | ImmLo | ImmHi);
}
+static inline void updateAArch64Add(uint8_t *L, uint64_t Imm) {
+ or32le(L, (Imm & 0xFFF) << 10);
+}
+
// Page(Expr) is the page address of the expression Expr, defined
// as (Expr & ~0xFFF). (This applies even if the machine page size
// supported by the platform has a different value.)
@@ -1312,20 +1357,20 @@ void AArch64TargetInfo::relocateOne(uint8_t *Loc, uint8_t *BufEnd,
case R_AARCH64_ADR_GOT_PAGE: {
uint64_t X = getAArch64Page(SA) - getAArch64Page(P);
checkInt<33>(X, Type);
- updateAArch64Adr(Loc, (X >> 12) & 0x1FFFFF); // X[32:12]
+ updateAArch64Addr(Loc, (X >> 12) & 0x1FFFFF); // X[32:12]
break;
}
case R_AARCH64_ADR_PREL_LO21: {
uint64_t X = SA - P;
checkInt<21>(X, Type);
- updateAArch64Adr(Loc, X & 0x1FFFFF);
+ updateAArch64Addr(Loc, X & 0x1FFFFF);
break;
}
case R_AARCH64_ADR_PREL_PG_HI21:
case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: {
uint64_t X = getAArch64Page(SA) - getAArch64Page(P);
checkInt<33>(X, Type);
- updateAArch64Adr(Loc, (X >> 12) & 0x1FFFFF); // X[32:12]
+ updateAArch64Addr(Loc, (X >> 12) & 0x1FFFFF); // X[32:12]
break;
}
case R_AARCH64_CALL26:
@@ -1378,11 +1423,130 @@ void AArch64TargetInfo::relocateOne(uint8_t *Loc, uint8_t *BufEnd,
or32le(Loc, (X & 0xFFFC) << 3);
break;
}
+ case R_AARCH64_TLSLE_ADD_TPREL_HI12: {
+ uint64_t V = llvm::alignTo(TcbSize, Out<ELF64LE>::TlsPhdr->p_align) + SA;
+ checkInt<24>(V, Type);
+ updateAArch64Add(Loc, (V & 0xFFF000) >> 12);
+ break;
+ }
+ case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC: {
+ uint64_t V = llvm::alignTo(TcbSize, Out<ELF64LE>::TlsPhdr->p_align) + SA;
+ updateAArch64Add(Loc, V & 0xFFF);
+ break;
+ }
default:
fatal("unrecognized reloc " + Twine(Type));
}
}
+bool AArch64TargetInfo::canRelaxTls(unsigned Type, const SymbolBody *S) const {
+ if (Config->Shared || (S && !S->isTls()))
+ return false;
+
+ // Global-Dynamic relocs can be relaxed to Initial-Exec if the target is
+ // an executable. And if the target is local it can also be fully relaxed to
+ // Local-Exec.
+ if (isTlsGlobalDynamicRel(Type))
+ return !canBePreempted(S, true);
+
+ // Initial-Exec relocs can be relaxed to Local-Exec if the target is a local
+ // symbol.
+ if (Type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 ||
+ Type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC)
+ return !canBePreempted(S, true);
+
+ return false;
+}
+
+unsigned AArch64TargetInfo::relaxTls(uint8_t *Loc, uint8_t *BufEnd,
+ uint32_t Type, uint64_t P, uint64_t SA,
+ const SymbolBody *S) const {
+ switch (Type) {
+ case R_AARCH64_TLSDESC_ADR_PAGE21:
+ case R_AARCH64_TLSDESC_LD64_LO12_NC:
+ case R_AARCH64_TLSDESC_ADD_LO12_NC:
+ case R_AARCH64_TLSDESC_CALL: {
+ if (canBePreempted(S, true))
+ fatal("Unsupported TLS optimization");
+ uint64_t X = S ? S->getVA<ELF64LE>() : SA;
+ relocateTlsGdToLe(Type, Loc, BufEnd, P, X);
+ return 0;
+ }
+ case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
+ case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
+ relocateTlsIeToLe(Type, Loc, BufEnd, P, S->getVA<ELF64LE>());
+ return 0;
+ }
+ llvm_unreachable("Unknown TLS optimization");
+}
+
+// Global-Dynamic relocations can be relaxed to Local-Exec if both binary is
+// an executable and target is final (can notbe preempted).
+void AArch64TargetInfo::relocateTlsGdToLe(unsigned Type, uint8_t *Loc,
+ uint8_t *BufEnd, uint64_t P,
+ uint64_t SA) const {
+ // TLSDESC Global-Dynamic relocation are in the form:
+ // adrp x0, :tlsdesc:v [R_AARCH64_TLSDESC_ADR_PAGE21]
+ // ldr x1, [x0, #:tlsdesc_lo12:v [R_AARCH64_TLSDESC_LD64_LO12_NC]
+ // add x0, x0, :tlsdesc_los:v [_AARCH64_TLSDESC_ADD_LO12_NC]
+ // .tlsdesccall [R_AARCH64_TLSDESC_CALL]
+ // And it can optimized to:
+ // movz x0, #0x0, lsl #16
+ // movk x0, #0x10
+ // nop
+ // nop
+
+ uint64_t TPOff = llvm::alignTo(TcbSize, Out<ELF64LE>::TlsPhdr->p_align);
+ uint64_t X = SA + TPOff;
+ checkUInt<32>(X, Type);
+
+ uint32_t NewInst;
+ switch (Type) {
+ case R_AARCH64_TLSDESC_ADD_LO12_NC:
+ case R_AARCH64_TLSDESC_CALL:
+ // nop
+ NewInst = 0xd503201f;
+ break;
+ case R_AARCH64_TLSDESC_ADR_PAGE21:
+ // movz
+ NewInst = 0xd2a00000 | (((X >> 16) & 0xffff) << 5);
+ break;
+ case R_AARCH64_TLSDESC_LD64_LO12_NC:
+ // movk
+ NewInst = 0xf2800000 | ((X & 0xffff) << 5);
+ break;
+ default:
+ llvm_unreachable("Unsupported Relocation for TLS GD to LE relax");
+ }
+ write32le(Loc, NewInst);
+}
+
+// Initial-Exec relocations can be relaxed to Local-Exec if symbol is final
+// (can not be preempted).
+void AArch64TargetInfo::relocateTlsIeToLe(unsigned Type, uint8_t *Loc,
+ uint8_t *BufEnd, uint64_t P,
+ uint64_t SA) const {
+ uint64_t TPOff = llvm::alignTo(TcbSize, Out<ELF64LE>::TlsPhdr->p_align);
+ uint64_t X = SA + TPOff;
+ checkUInt<32>(X, Type);
+
+ uint32_t Inst = read32le (Loc);
+ uint32_t NewInst;
+ if (Type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21) {
+ // Generate movz.
+ unsigned RegNo = (Inst & 0x1f);
+ NewInst = (0xd2a00000 | RegNo) | (((X >> 16) & 0xffff) << 5);
+ } else if (Type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC) {
+ // Generate movk
+ unsigned RegNo = (Inst & 0x1f);
+ NewInst = (0xf2800000 | RegNo) | ((X & 0xffff) << 5);
+ } else {
+ llvm_unreachable("Invalid Relocation for TLS IE to LE Relax");
+ }
+ write32le(Loc, NewInst);
+}
+
+
// Implementing relocations for AMDGPU is low priority since most
// programs don't use relocations now. Thus, this function is not
// actually called (relocateOne is called for each relocation).
diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index c58fc672888..7f0dcdc5440 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -22,8 +22,8 @@ class SymbolBody;
class TargetInfo {
public:
uint64_t getVAStart() const;
- bool isTlsLocalDynamicRel(unsigned Type) const;
- bool isTlsGlobalDynamicRel(unsigned Type) const;
+ virtual bool isTlsLocalDynamicRel(unsigned Type) const;
+ virtual bool isTlsGlobalDynamicRel(unsigned Type) const;
virtual unsigned getDynRel(unsigned Type) const { return Type; }
virtual bool isTlsDynRel(unsigned Type, const SymbolBody &S) const;
virtual unsigned getTlsGotRel(unsigned Type) const { return TlsGotRel; }
@@ -82,8 +82,6 @@ public:
unsigned RelativeRel;
unsigned IRelativeRel;
unsigned TlsGotRel = 0;
- unsigned TlsLocalDynamicRel = 0;
- unsigned TlsGlobalDynamicRel = 0;
unsigned TlsModuleIndexRel;
unsigned TlsOffsetRel;
unsigned PltEntrySize = 8;
diff --git a/lld/test/ELF/aarch64-tls-gdle.s b/lld/test/ELF/aarch64-tls-gdle.s
new file mode 100644
index 00000000000..e5f8515afca
--- /dev/null
+++ b/lld/test/ELF/aarch64-tls-gdle.s
@@ -0,0 +1,26 @@
+# RUN: llvm-mc -filetype=obj -triple=aarch64-unknown-linux %p/Inputs/aarch64-tls-ie.s -o %ttlsie.o
+# RUN: llvm-mc -filetype=obj -triple=aarch64-unknown-linux %s -o %tmain.o
+# RUN: ld.lld %tmain.o %ttlsie.o -o %tout
+# RUN: llvm-objdump -d %tout | FileCheck %s
+# RUN: llvm-readobj -s -r %tout | FileCheck -check-prefix=RELOC %s
+# REQUIRES: aarch64
+
+#Local-Dynamic to Initial-Exec relax creates no
+#RELOC: Relocations [
+#RELOC-NEXT: ]
+
+# TCB size = 0x16 and foo is first element from TLS register.
+#CHECK: Disassembly of section .text:
+#CHECK: _start:
+#CHECK: 11000: 00 00 a0 d2 movz x0, #0, lsl #16
+#CHECK: 11004: 00 02 80 f2 movk x0, #0x10
+#CHECK: 11008: 1f 20 03 d5 nop
+#CHECK: 1100c: 1f 20 03 d5 nop
+
+.globl _start
+_start:
+ adrp x0, :tlsdesc:foo
+ ldr x1, [x0, :tlsdesc_lo12:foo]
+ add x0, x0, :tlsdesc_lo12:foo
+ .tlsdesccall foo
+ blr x1
diff --git a/lld/test/ELF/aarch64-tls-iele.s b/lld/test/ELF/aarch64-tls-iele.s
new file mode 100644
index 00000000000..42ee90a9660
--- /dev/null
+++ b/lld/test/ELF/aarch64-tls-iele.s
@@ -0,0 +1,21 @@
+# RUN: llvm-mc -filetype=obj -triple=aarch64-unknown-linux %p/Inputs/aarch64-tls-ie.s -o %ttlsie.o
+# RUN: llvm-mc -filetype=obj -triple=aarch64-unknown-linux %s -o %tmain.o
+# RUN: ld.lld %tmain.o %ttlsie.o -o %tout
+# RUN: llvm-objdump -d %tout | FileCheck %s
+# RUN: llvm-readobj -s -r %tout | FileCheck -check-prefix=RELOC %s
+# REQUIRES: aarch64
+
+#Local-Dynamic to Initial-Exec relax creates no
+#RELOC: Relocations [
+#RELOC-NEXT: ]
+
+# TCB size = 0x16 and foo is first element from TLS register.
+#CHECK: Disassembly of section .text:
+#CHECK: _start:
+#CHECK: 11000: 00 00 a0 d2 movz x0, #0, lsl #16
+#CHECK: 11004: 00 02 80 f2 movk x0, #0x10
+
+.globl _start
+_start:
+ adrp x0, :gottprel:foo
+ ldr x0, [x0, :gottprel_lo12:foo]
diff --git a/lld/test/ELF/aarch64-tls-le.s b/lld/test/ELF/aarch64-tls-le.s
new file mode 100644
index 00000000000..53000b6336f
--- /dev/null
+++ b/lld/test/ELF/aarch64-tls-le.s
@@ -0,0 +1,31 @@
+# RUN: llvm-mc -filetype=obj -triple=aarch64-unknown-freebsd %s -o %tmain.o
+# RUN: ld.lld %tmain.o -o %tout
+# RUN: llvm-objdump -d %tout | FileCheck %s
+# RUN: llvm-readobj -s -r %tout | FileCheck -check-prefix=RELOC %s
+# REQUIRES: aarch64
+
+#Local-Dynamic to Initial-Exec relax creates no
+#RELOC: Relocations [
+#RELOC-NEXT: ]
+
+.globl _start
+_start:
+ mrs x0, TPIDR_EL0
+ add x0, x0, :tprel_hi12:v1
+ add x0, x0, :tprel_lo12_nc:v1
+
+# TCB size = 0x16 and foo is first element from TLS register.
+#CHECK: Disassembly of section .text:
+#CHECK: _start:
+#CHECK: 11000: 40 d0 3b d5 mrs x0, TPIDR_EL0
+#CHECK: 11004: 00 00 00 91 add x0, x0, #0
+#CHECK: 11008: 00 40 00 91 add x0, x0, #16
+
+.type v1,@object
+.section .tbss,"awT",@nobits
+.globl v1
+.p2align 2
+v1:
+.word 0
+.size v1, 4
+
OpenPOWER on IntegriCloud