diff --git a/lld/ELF/Arch/PPC.cpp b/lld/ELF/Arch/PPC.cpp index ee15575bfd5f..8f68d12abe8e 100644 --- a/lld/ELF/Arch/PPC.cpp +++ b/lld/ELF/Arch/PPC.cpp @@ -39,12 +39,27 @@ public: RelExpr getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const override; void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, + RelExpr Expr) const override; + int getTlsGdRelaxSkip(RelType Type) const override; + 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 static uint16_t lo(uint32_t V) { return V; } static uint16_t ha(uint32_t V) { return (V + 0x8000) >> 16; } +static uint32_t readFromHalf16(const uint8_t *Loc) { + return read32(Loc - (Config->EKind == ELF32BEKind ? 2 : 0)); +} + +static void writeFromHalf16(uint8_t *Loc, uint32_t Insn) { + write32(Loc - (Config->EKind == ELF32BEKind ? 2 : 0), Insn); +} + void elf::writePPC32GlinkSection(uint8_t *Buf, size_t NumEntries) { // On PPC Secure PLT ABI, bl foo@plt jumps to a call stub, which loads an // absolute address from a specific .plt slot (usually called .got.plt on @@ -128,6 +143,10 @@ PPC::PPC() { NeedsThunks = true; + TlsModuleIndexRel = R_PPC_DTPMOD32; + TlsOffsetRel = R_PPC_DTPREL32; + TlsGotRel = R_PPC_TPREL32; + DefaultMaxPageSize = 65536; DefaultImageBase = 0x10000000; @@ -169,6 +188,12 @@ bool PPC::inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const { RelExpr PPC::getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const { switch (Type) { + case R_PPC_DTPREL16: + case R_PPC_DTPREL16_HA: + case R_PPC_DTPREL16_HI: + case R_PPC_DTPREL16_LO: + case R_PPC_DTPREL32: + return R_DTPREL; case R_PPC_REL14: case R_PPC_REL32: case R_PPC_LOCAL24PC: @@ -182,28 +207,84 @@ RelExpr PPC::getRelExpr(RelType Type, const Symbol &S, return R_PLT_PC; case R_PPC_PLTREL24: return R_PPC32_PLTREL; + case R_PPC_GOT_TLSGD16: + return R_TLSGD_GOT; + case R_PPC_GOT_TLSLD16: + return R_TLSLD_GOT; + case R_PPC_GOT_TPREL16: + return R_GOT_OFF; + case R_PPC_TLS: + return R_TLSIE_HINT; + case R_PPC_TLSGD: + return R_TLSDESC_CALL; + case R_PPC_TLSLD: + return R_TLSLD_HINT; + case R_PPC_TPREL16: + case R_PPC_TPREL16_HA: + case R_PPC_TPREL16_LO: + case R_PPC_TPREL16_HI: + return R_TLS; default: return R_ABS; } } -void PPC::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +static std::pair fromDTPREL(RelType Type, uint64_t Val) { + uint64_t DTPBiasedVal = Val - 0x8000; switch (Type) { + case R_PPC_DTPREL16: + return {R_PPC64_ADDR16, DTPBiasedVal}; + case R_PPC_DTPREL16_HA: + return {R_PPC_ADDR16_HA, DTPBiasedVal}; + case R_PPC_DTPREL16_HI: + return {R_PPC_ADDR16_HI, DTPBiasedVal}; + case R_PPC_DTPREL16_LO: + return {R_PPC_ADDR16_LO, DTPBiasedVal}; + case R_PPC_DTPREL32: + return {R_PPC_ADDR32, DTPBiasedVal}; + default: + return {Type, Val}; + } +} + +void PPC::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { + RelType NewType; + std::tie(NewType, Val) = fromDTPREL(Type, Val); + switch (NewType) { case R_PPC_ADDR16: case R_PPC_GOT16: + case R_PPC_GOT_TLSGD16: + case R_PPC_GOT_TLSLD16: + case R_PPC_GOT_TPREL16: + case R_PPC_TPREL16: checkInt(Loc, Val, 16, Type); write16(Loc, Val); break; case R_PPC_ADDR16_HA: + case R_PPC_DTPREL16_HA: + case R_PPC_GOT_TLSGD16_HA: + case R_PPC_GOT_TLSLD16_HA: + case R_PPC_GOT_TPREL16_HA: case R_PPC_REL16_HA: + case R_PPC_TPREL16_HA: write16(Loc, ha(Val)); break; case R_PPC_ADDR16_HI: + case R_PPC_DTPREL16_HI: + case R_PPC_GOT_TLSGD16_HI: + case R_PPC_GOT_TLSLD16_HI: + case R_PPC_GOT_TPREL16_HI: case R_PPC_REL16_HI: + case R_PPC_TPREL16_HI: write16(Loc, Val >> 16); break; case R_PPC_ADDR16_LO: + case R_PPC_DTPREL16_LO: + case R_PPC_GOT_TLSGD16_LO: + case R_PPC_GOT_TLSLD16_LO: + case R_PPC_GOT_TPREL16_LO: case R_PPC_REL16_LO: + case R_PPC_TPREL16_LO: write16(Loc, Val); break; case R_PPC_ADDR32: @@ -232,6 +313,109 @@ void PPC::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { } } +RelExpr PPC::adjustRelaxExpr(RelType Type, const uint8_t *Data, + RelExpr Expr) const { + if (Expr == R_RELAX_TLS_GD_TO_IE) + return R_RELAX_TLS_GD_TO_IE_GOT_OFF; + if (Expr == R_RELAX_TLS_LD_TO_LE) + return R_RELAX_TLS_LD_TO_LE_ABS; + return Expr; +} + +int PPC::getTlsGdRelaxSkip(RelType Type) const { + // A __tls_get_addr call instruction is marked with 2 relocations: + // + // R_PPC_TLSGD / R_PPC_TLSLD: marker relocation + // R_PPC_REL24: __tls_get_addr + // + // After the relaxation we no longer call __tls_get_addr and should skip both + // relocations to not create a false dependence on __tls_get_addr being + // defined. + if (Type == R_PPC_TLSGD || Type == R_PPC_TLSLD) + return 2; + return 1; +} + +void PPC::relaxTlsGdToIe(uint8_t *Loc, RelType Type, uint64_t Val) const { + switch (Type) { + case R_PPC_GOT_TLSGD16: { + // addi rT, rA, x@got@tlsgd --> lwz rT, x@got@tprel(rA) + uint32_t Insn = readFromHalf16(Loc); + writeFromHalf16(Loc, 0x80000000 | (Insn & 0x03ff0000)); + relocateOne(Loc, R_PPC_GOT_TPREL16, Val); + break; + } + case R_PPC_TLSGD: + // bl __tls_get_addr(x@tldgd) --> add r3, r3, r2 + write32(Loc, 0x7c631214); + break; + default: + llvm_unreachable("unsupported relocation for TLS GD to IE relaxation"); + } +} + +void PPC::relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { + switch (Type) { + case R_PPC_GOT_TLSGD16: + // addi r3, r31, x@got@tlsgd --> addis r3, r2, x@tprel@ha + writeFromHalf16(Loc, 0x3c620000 | ha(Val)); + break; + case R_PPC_TLSGD: + // bl __tls_get_addr(x@tldgd) --> add r3, r3, x@tprel@l + write32(Loc, 0x38630000 | lo(Val)); + break; + default: + llvm_unreachable("unsupported relocation for TLS GD to LE relaxation"); + } +} + +void PPC::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { + switch (Type) { + case R_PPC_GOT_TLSLD16: + // addi r3, rA, x@got@tlsgd --> addis r3, r2, 0 + writeFromHalf16(Loc, 0x3c620000); + break; + case R_PPC_TLSLD: + // r3+x@dtprel computes r3+x-0x8000, while we want it to compute r3+x@tprel + // = r3+x-0x7000, so add 4096 to r3. + // bl __tls_get_addr(x@tlsld) --> addi r3, r3, 4096 + write32(Loc, 0x38631000); + break; + case R_PPC_DTPREL16: + case R_PPC_DTPREL16_HA: + case R_PPC_DTPREL16_HI: + case R_PPC_DTPREL16_LO: + relocateOne(Loc, Type, Val); + break; + default: + llvm_unreachable("unsupported relocation for TLS LD to LE relaxation"); + } +} + +void PPC::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { + switch (Type) { + case R_PPC_GOT_TPREL16: { + // lwz rT, x@got@tprel(rA) --> addis rT, r2, x@tprel@ha + uint32_t RT = readFromHalf16(Loc) & 0x03e00000; + writeFromHalf16(Loc, 0x3c020000 | RT | ha(Val)); + break; + } + case R_PPC_TLS: { + uint32_t Insn = read32(Loc); + if (Insn >> 26 != 31) + error("unrecognized instruction for IE to LE R_PPC_TLS"); + // addi rT, rT, x@tls --> addi rT, rT, x@tprel@l + uint32_t DFormOp = getPPCDFormOp((read32(Loc) & 0x000007fe) >> 1); + if (DFormOp == 0) + error("unrecognized instruction for IE to LE R_PPC_TLS"); + write32(Loc, (DFormOp << 26) | (Insn & 0x03ff0000) | lo(Val)); + break; + } + default: + llvm_unreachable("unsupported relocation for TLS IE to LE relaxation"); + } +} + TargetInfo *elf::getPPCTargetInfo() { static PPC Target; return &Target; diff --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp index 9e2543e9eb86..c574b8b50f25 100644 --- a/lld/ELF/Arch/PPC64.cpp +++ b/lld/ELF/Arch/PPC64.cpp @@ -452,7 +452,7 @@ void PPC64::relaxTlsLdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { } } -static unsigned getDFormOp(unsigned SecondaryOp) { +unsigned elf::getPPCDFormOp(unsigned SecondaryOp) { switch (SecondaryOp) { case LBZX: return LBZ; @@ -473,7 +473,6 @@ static unsigned getDFormOp(unsigned SecondaryOp) { case ADD: return ADDI; default: - error("unrecognized instruction for IE to LE R_PPC64_TLS"); return 0; } } @@ -515,7 +514,9 @@ void PPC64::relaxTlsIeToLe(uint8_t *Loc, RelType Type, uint64_t Val) const { 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); + uint32_t DFormOp = getPPCDFormOp(SecondaryOp); + if (DFormOp == 0) + error("unrecognized instruction for IE to LE R_PPC64_TLS"); write32(Loc, ((DFormOp << 26) | (read32(Loc) & 0x03FFFFFF))); relocateOne(Loc + Offset, R_PPC64_TPREL16_LO, Val); break; diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp index dde7ec0fd416..d09eae1a2f42 100644 --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -611,6 +611,7 @@ static int64_t getTlsTpOffset(const Symbol &S) { case EM_X86_64: // Variant 2. The TLS segment is located just before the thread pointer. return S.getVA(0) - alignTo(Out::TlsPhdr->p_memsz, Out::TlsPhdr->p_align); + case EM_PPC: case EM_PPC64: // The thread pointer points to a fixed offset from the start of the // executable's TLS segment. An offset of 0x7000 allows a signed 16-bit diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h index d7ab91f48d6b..bb6c9f860144 100644 --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -166,6 +166,7 @@ void writePPC32GlinkSection(uint8_t *Buf, size_t NumEntries); bool tryRelaxPPC64TocIndirection(RelType Type, const Relocation &Rel, uint8_t *BufLoc); +unsigned getPPCDFormOp(unsigned SecondaryOp); // In the PowerPC64 Elf V2 abi a function can have 2 entry points. The first // is a global entry point (GEP) which typically is used to initialize the TOC diff --git a/lld/test/ELF/ppc32-tls-gd.s b/lld/test/ELF/ppc32-tls-gd.s new file mode 100644 index 000000000000..02ae0785b464 --- /dev/null +++ b/lld/test/ELF/ppc32-tls-gd.s @@ -0,0 +1,98 @@ +# REQUIES: ppc +# RUN: llvm-mc -filetype=obj -triple=powerpc %s -o %t.o +# RUN: echo '.tbss; .globl b, c; b: .zero 4; c:' | llvm-mc -filetype=obj -triple=powerpc - -o %t1.o +# RUN: ld.lld -shared -soname=t1.so %t1.o -o %t1.so +# RUN: echo '.globl __tls_get_addr; __tls_get_addr:' | llvm-mc -filetype=obj -triple=powerpc - -o %tga.o + +# RUN: ld.lld -shared %t.o %t1.o -o %t.so +# RUN: llvm-readobj -d %t.so | FileCheck --check-prefix=GD-DYN %s +# RUN: llvm-readobj -r %t.so | FileCheck --check-prefix=GD-REL %s +# RUN: llvm-objdump -d --no-show-raw-insn %t.so | FileCheck --check-prefix=GD %s + +# RUN: ld.lld %t.o %t1.o %tga.o -o %t +# RUN: llvm-readelf -r %t | FileCheck --check-prefix=NOREL %s +# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck --check-prefix=LE %s + +# RUN: ld.lld %t.o %t1.so -o %t +# RUN: llvm-readobj -r %t | FileCheck --check-prefix=IE-REL %s +# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck --check-prefix=IE %s + +## DT_PPC_GOT represents the address of _GLOBAL_OFFSET_TABLE_. +# GD-DYN: PPC_GOT 0x20078 + +# GD-REL: .rela.dyn { +# GD-REL-NEXT: 0x20078 R_PPC_DTPMOD32 a 0x0 +# GD-REL-NEXT: 0x2007C R_PPC_DTPREL32 a 0x0 +# GD-REL-NEXT: 0x20080 R_PPC_DTPMOD32 b 0x0 +# GD-REL-NEXT: 0x20084 R_PPC_DTPREL32 b 0x0 +# GD-REL-NEXT: 0x20088 R_PPC_DTPMOD32 c 0x0 +# GD-REL-NEXT: 0x2008C R_PPC_DTPREL32 c 0x0 +# GD-REL-NEXT: } + +## &DTPMOD(a) - _GLOBAL_OFFSET_TABLE_ = 0x20078 - 0x20078 = 0 +# GD: addi 3, 31, 0 +# GD-NEXT: bl .+32 +# GD-NEXT: lwz 3, 0(3) + +## &DTPMOD(b) - _GLOBAL_OFFSET_TABLE_ = 0x20080 - 0x20078 = 8 +# GD-NEXT: addi 3, 31, 8 +# GD-NEXT: bl .+20 +# GD-NEXT: lwz 3, 0(3) + +## &DTPMOD(c) - _GLOBAL_OFFSET_TABLE_ = 0x20088 - 0x20078 = 16 +# GD-NEXT: addi 3, 9, 16 +# GD-NEXT: bl .+8 +# GD-NEXT: lwz 3, 0(3) + +# NOREL: no relocations + +## a@tprel = 8-0x7000 = -28664 +# LE: addis 3, 2, 0 +# LE-NEXT: addi 3, 3, -28664 +# LE-NEXT: lwz 3, 0(3) +## b@tprel = 12-0x7000 = -28660 +# LE-NEXT: addis 3, 2, 0 +# LE-NEXT: addi 3, 3, -28660 +# LE-NEXT: lwz 3, 0(3) +## c@tprel = 16-0x7000 = -28656 +# LE-NEXT: addis 3, 2, 0 +# LE-NEXT: addi 3, 3, -28656 +# LE-NEXT: lwz 3, 0(3) + +# IE-REL: .rela.dyn { +# IE-REL-NEXT: 0x10020068 R_PPC_TPREL32 b 0x0 +# IE-REL-NEXT: 0x1002006C R_PPC_TPREL32 c 0x0 +# IE-REL-NEXT: } + +## a is relaxed to use LE. +## a@tprel = st_value(a)-0x8000 = -28664 +# IE: addis 3, 2, 0 +# IE-NEXT: addi 3, 3, -28664 +# IE-NEXT: lwz 3, 0(3) +## &.got[0] - _GLOBAL_OFFSET_TABLE_ = 0 +# IE-NEXT: lwz 3, 0(31) +# IE-NEXT: add 3, 3, 2 +# IE-NEXT: lwz 3, 0(3) +## &.got[1] - _GLOBAL_OFFSET_TABLE_ = 4 +# IE-NEXT: lwz 3, 4(9) +# IE-NEXT: add 3, 3, 2 +# IE-NEXT: lwz 3, 0(3) + +addi 3, 31, a@got@tlsgd +bl __tls_get_addr(a@tlsgd) +lwz 3, 0(3) + +addi 3, 31, b@got@tlsgd +bl __tls_get_addr(b@tlsgd) +lwz 3, 0(3) + +## -fpic may use a different register (e.g. r9). +addi 3, 9, c@got@tlsgd +bl __tls_get_addr(c@tlsgd) +lwz 3, 0(3) + +.section .tbss +.globl a +.zero 8 +a: +.zero 4 diff --git a/lld/test/ELF/ppc32-tls-ie.s b/lld/test/ELF/ppc32-tls-ie.s new file mode 100644 index 000000000000..0efaad259f7a --- /dev/null +++ b/lld/test/ELF/ppc32-tls-ie.s @@ -0,0 +1,67 @@ +# REQUIES: ppc +# RUN: llvm-mc -filetype=obj -triple=powerpc %s -o %t.o + +# RUN: ld.lld -shared %t.o -o %t.so +# RUN: llvm-readobj -r %t.so | FileCheck --check-prefix=IE-REL %s +# RUN: llvm-objdump -d --no-show-raw-insn %t.so | FileCheck --check-prefix=IE %s + +# RUN: ld.lld %t.o -o %t +# RUN: llvm-readelf -r %t | FileCheck --check-prefix=NOREL %s +# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck --check-prefix=LE %s + +## A non-preemptable symbol (b) has 0 st_shndx. +# IE-REL: .rela.dyn { +# IE-REL-NEXT: 0x2005C R_PPC_TPREL32 - 0xC +# IE-REL-NEXT: 0x20058 R_PPC_TPREL32 a 0x0 +# IE-REL-NEXT: } + +## &.got[0] - _GLOBAL_OFFSET_TABLE_ = 0 +# IE: lwz 10, 0(9) +# IE-NEXT: add 10, 10, 2 +## &.got[1] - _GLOBAL_OFFSET_TABLE_ = 4 +# IE-NEXT: lwz 8, 4(7) +# IE-NEXT: lbzx 10, 8, 2 + +# NOREL: no relocations + +## a@tprel = st_value(a)-0x7000 = -28664 +## b@tprel = st_value(b)-0x7000 = -28660 +# LE: addis 10, 2, 0 +# LE-NEXT: addi 10, 10, -28664 +# LE-NEXT: addis 8, 2, 0 +# LE-NEXT: lbz 10, -28660(8) + +lwz 10, a@got@tprel(9) +add 10, 10, a@tls + +lwz 8, c@got@tprel(7) +lbzx 10, 8, c@tls + +## In IE, these instructions (op rT, rA, x@tls) are not changed. +# IE-NEXT: lhzx 12, 2, 2 +# IE-NEXT: lwzx 13, 3, 2 +# IE-NEXT: stbx 14, 4, 2 +# IE-NEXT: sthx 15, 5, 2 +# IE-NEXT: stwx 16, 6, 2 + +## In LE, these X-Form instructions are changed to their corresponding D-Form. +# LE-NEXT: lhz 12, -28660(2) +# LE-NEXT: lwz 13, -28660(3) +# LE-NEXT: stb 14, -28660(4) +# LE-NEXT: sth 15, -28660(5) +# LE-NEXT: stw 16, -28660(6) + +lhzx 12, 2, s@tls +lwzx 13, 3, i@tls +stbx 14, 4, c@tls +sthx 15, 5, s@tls +stwx 16, 6, i@tls + +.section .tbss +.globl a +.zero 8 +a: +.zero 4 +c: +s: +i: diff --git a/lld/test/ELF/ppc32-tls-ld.s b/lld/test/ELF/ppc32-tls-ld.s new file mode 100644 index 000000000000..cf6be367c8c0 --- /dev/null +++ b/lld/test/ELF/ppc32-tls-ld.s @@ -0,0 +1,82 @@ +# REQUIRES: ppc +# RUN: llvm-mc -filetype=obj -triple=powerpc %s -o %t.o +# RUN: echo '.globl __tls_get_addr; __tls_get_addr:' | llvm-mc -filetype=obj -triple=powerpc - -o %tga.o + +# RUN: ld.lld -shared %t.o -o %t.so +# RUN: llvm-readobj -r %t.so | FileCheck --check-prefix=LD-REL %s +# RUN: llvm-objdump -d --no-show-raw-insn %t.so | FileCheck --check-prefix=LD %s + +# RUN: ld.lld %t.o %tga.o -o %t +# RUN: llvm-readelf -r %t | FileCheck --check-prefix=NOREL %s +# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck --check-prefix=LE %s + +# LD-REL: .rela.dyn { +# LD-REL-NEXT: 0x20078 R_PPC_DTPMOD32 - 0x0 +# LD-REL-NEXT: } + +## .got - _GLOBAL_OFFSET_TABLE_ = 0 +# LD: addi 3, 30, 0 +# LD-NEXT: bl .+40 +## a@dtprel = st_value(a)-0x8000 = 65540-0x8000 = 65536*1-32764 +## b@dtprel = st_value(a)-0x8000 = 131080-0x8000 = 65536*2-32760 +# LD-NEXT: addis 9, 3, 1 +# LD-NEXT: addis 10, 3, 2 +# LD-NEXT: addi 9, 9, -32764 +# LD-NEXT: addi 10, 10, -32760 +## small@dtprel = st_value(small)-0x8000 = 4-0x8000 = -32764 +# LD-NEXT: addi 9, 3, -32764 + +## Check that b@got@tlsld does not allocate another GOT entry. +## It shares In.Got->TlsIndexOff allocated when processing a@got@tlsld. +## .got - _GLOBAL_OFFSET_TABLE_ = 0 +# LD-NEXT: addi 3, 9, 0 +# LD-NEXT: bl .+12 +## b@dtprel = st_value(a)-0x8000 = 131080-0x8000 = 65536*2-32760 +# LD-NEXT: addis 29, 3, 2 +# LD-NEXT: addi 29, 29, -32760 + +## When producing an executable, the LD code sequence can be relaxed to LE. +## It is the same as GD->LE. +## tpoff(_TLS_MODULE_BASE_) = 0, tpoff(a) = -8, tpoff(b) = -4 + +# NOREL: no relocations + +## Set r3 to r2+4096 +# LE: addis 3, 2, 0 +# LE-NEXT: addi 3, 3, 4096 +## a@tprel = 65540-0x7000 = 65536*1-32764 +## b@tprel = 131080-0x7000 = 65536*2-32760 +# LE-NEXT: addis 9, 3, 1 +# LE-NEXT: addis 10, 3, 2 +# LE-NEXT: addi 9, 9, -32764 +# LE-NEXT: addi 10, 10, -32760 +## small@tprel = 4-0x7000 = -32764 +# LE-NEXT: addi 9, 3, -32764 + +## Set r3 to r2+4096 +# LE-NEXT: addis 3, 2, 0 +# LE-NEXT: addi 3, 3, 4096 +## b@tprel = 131080-0x7000 = 65536*2-32760 +# LE-NEXT: addis 29, 3, 2 +# LE-NEXT: addi 29, 29, -32760 + +addi 3, 30, a@got@tlsld +bl __tls_get_addr(a@tlsld) +addis 9, 3, a@dtprel@ha +addis 10, 3, b@dtprel@ha +addi 9, 9, a@dtprel@l +addi 10, 10, b@dtprel@l +addi 9, 3, small@dtprel + +addi 3, 9, b@got@tlsld +bl __tls_get_addr(b@tlsld) +addis 29, 3, b@dtprel@ha +addi 29, 29, b@dtprel@l + +.section .tbss +.zero 4 +small: +.zero 65536 +a: +.zero 65540 +b: diff --git a/lld/test/ELF/ppc32-tls-le.s b/lld/test/ELF/ppc32-tls-le.s new file mode 100644 index 000000000000..12bb1a70515b --- /dev/null +++ b/lld/test/ELF/ppc32-tls-le.s @@ -0,0 +1,24 @@ +# REQUIES: ppc +# RUN: llvm-mc -filetype=obj -triple=powerpc %s -o %t.o +# RUN: ld.lld %t.o -o %t +# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck --check-prefix=LE %s + +## a@tprel = st_value(a)-0x7000 = -28664 +## b@tprel = st_value(b)-0x7000 = -28660 +# LE: addis 9, 2, 0 +# LE-NEXT: addi 9, 9, -28664 +# LE-NEXT: addis 10, 2, 0 +# LE-NEXT: lwz 9, -28660(10) + +addis 9, 2, a@tprel@ha +addi 9, 9, a@tprel@l + +addis 10, 2, b@tprel@ha +lwz 9,b@tprel@l(10) + +.section .tbss +.globl a +.zero 8 +a: +.zero 4 +b: