Index: lld/ELF/Arch/SPARCV9.cpp =================================================================== --- lld/ELF/Arch/SPARCV9.cpp +++ lld/ELF/Arch/SPARCV9.cpp @@ -32,6 +32,11 @@ uint64_t val) const override; void relaxTlsIeToLe(uint8_t *loc, const Relocation &, uint64_t val) const override; + void relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + void relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const override; + RelExpr adjustTlsExpr(RelType type, RelExpr expr) const override; }; } // namespace @@ -46,6 +51,8 @@ pltHeaderSize = 4 * pltEntrySize; tlsGotRel = R_SPARC_TLS_TPOFF64; + tlsModuleIndexRel = R_SPARC_TLS_DTPMOD64; + tlsOffsetRel = R_SPARC_TLS_DTPOFF64; defaultCommonPageSize = 8192; defaultMaxPageSize = 0x100000; @@ -62,6 +69,12 @@ write32(buf, mainPart->dynamic->getVA()); } +RelExpr SPARCV9::adjustTlsExpr(RelType type, RelExpr expr) const { + if (expr == R_RELAX_TLS_GD_TO_IE) + return R_RELAX_TLS_GD_TO_IE_GOT_OFF; + return expr; +} + RelExpr SPARCV9::getRelExpr(RelType type, const Symbol &s, const uint8_t *loc) const { switch (type) { @@ -99,6 +112,11 @@ case R_SPARC_TLS_IE_LDX: case R_SPARC_TLS_IE_ADD: return R_GOT_OFF; + case R_SPARC_TLS_GD_HI22: + case R_SPARC_TLS_GD_LO10: + case R_SPARC_TLS_GD_ADD: + case R_SPARC_TLS_GD_CALL: + return R_TLSGD_GOT; default: error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) + ") against symbol " + toString(s)); @@ -120,6 +138,7 @@ checkInt(loc, val, 32, rel); write32be(loc, val); break; + case R_SPARC_TLS_GD_CALL: case R_SPARC_WDISP30: case R_SPARC_WPLT30: // V-disp30 @@ -132,6 +151,7 @@ write32be(loc, (read32be(loc) & ~0x003fffff) | (val & 0x003fffff)); break; case R_SPARC_TLS_IE_HI22: + case R_SPARC_TLS_GD_HI22: case R_SPARC_GOT22: case R_SPARC_PC22: case R_SPARC_LM22: @@ -154,12 +174,14 @@ write32be(loc, (read32be(loc) & ~0x000003ff) | (val & 0x000003ff)); break; case R_SPARC_TLS_IE_LO10: + case R_SPARC_TLS_GD_LO10: case R_SPARC_LO10: // T-simm13 write32be(loc, (read32be(loc) & ~0x00001fff) | (val & 0x000003ff)); break; case R_SPARC_64: case R_SPARC_UA64: + case R_SPARC_TLS_DTPOFF64: // V-xword64 write64be(loc, val); break; @@ -196,8 +218,59 @@ case R_SPARC_TLS_IE_LDX: case R_SPARC_TLS_IE_ADD: break; + case R_SPARC_TLS_GD_ADD: + break; default: - llvm_unreachable("unknown relocation"); + error(getErrorLocation(loc) + "unknown relocation (" + Twine(rel.type) + + ") against symbol " + toString(*rel.sym)); + } +} + +void SPARCV9::relaxTlsGdToIe(uint8_t *loc, const Relocation &rel, + uint64_t val) const { + switch (rel.type) { + case R_SPARC_TLS_GD_ADD: + // add %r1, %r2, %r3 -> ldx [ %r1 + %r2 ], %r3 + write32be(loc, read32be(loc) | 0xc0580000); + break; + case R_SPARC_TLS_GD_CALL: + // call __tls_get_addr -> add %g7, %o0, %o0 + write32(loc, 0x9001c008); + break; + case R_SPARC_TLS_GD_HI22: + relocateNoSym(loc, R_SPARC_TLS_IE_HI22, val); + return; + case R_SPARC_TLS_GD_LO10: + // add %r1, %tgd_lo10(sym), %r2 -> or %r1, %tie_lo10(sym), %r2 + write32be(loc, (read32be(loc) & ~0x01f80000) | 0x100000); + relocateNoSym(loc, R_SPARC_TLS_IE_LO10, val); + return; + default: + llvm_unreachable("unsupported relocation for TLS GD to IE relaxation"); + } +} + +void SPARCV9::relaxTlsGdToLe(uint8_t *loc, const Relocation &rel, + uint64_t val) const { + switch (rel.type) { + case R_SPARC_TLS_GD_ADD: + // add %r1, %r2, %r3 -> add %g7, %r2, %r3 + write32be(loc, (read32be(loc) & ~0x0007c000) | 0x0001c000); + break; + case R_SPARC_TLS_GD_CALL: + // call __tls_get_addr -> nop + write32(loc, 0x01000000); + break; + case R_SPARC_TLS_GD_HI22: + relocateNoSym(loc, R_SPARC_TLS_LE_HIX22, val); + return; + case R_SPARC_TLS_GD_LO10: + // add %r1, %tgd_lo10(sym), %r2 -> xor %r1, %tle_lox10(sym), %r2 + write32be(loc, (read32be(loc) & ~0x01f80000) | 0x180000); + relocateNoSym(loc, R_SPARC_TLS_LE_LOX10, val); + return; + default: + llvm_unreachable("unsupported relocation for TLS GD to IE relaxation"); } } Index: lld/ELF/Relocations.cpp =================================================================== --- lld/ELF/Relocations.cpp +++ lld/ELF/Relocations.cpp @@ -178,6 +178,15 @@ return 0; } +template +static void addPltEntry(PltSection *plt, GotPltSection *gotPlt, + RelocationBaseSection *rel, RelType type, Symbol &sym) { + plt->addEntry(sym); + gotPlt->addEntry(sym); + rel->addReloc( + {type, gotPlt, sym.getGotPltOffset(), !sym.isPreemptible, &sym, 0}); +} + // Notes about General Dynamic and Local Dynamic TLS models below. They may // require the generation of a pair of GOT entries that have associated dynamic // relocations. The pair of GOT entries created are of the form GOT[e0] Module @@ -275,6 +284,24 @@ if (oneof(expr)) { if (!toExecRelax) { + if (config->emachine == EM_SPARCV9 && + (type == R_SPARC_TLS_GD_CALL || type == R_SPARC_TLS_LDM_CALL)) { + // These relocations are applied on call instructions like + // call __tls_get_addr, %rel(sym) + // If this relocation won't be relaxed we have to replace it with a + // PLT-relative one to __tls_get_addr. + Symbol *tlsGetAddr = symtab->find("__tls_get_addr"); + if (!tlsGetAddr) + fatal("__tls_get_addr symbol is not defined"); + + if (!tlsGetAddr->isInPlt()) + addPltEntry(in.plt, in.gotPlt, in.relaPlt, target->pltRel, + *tlsGetAddr); + + c.relocations.push_back({R_PLT_PC, type, offset, 0, tlsGetAddr}); + return 1; + } + if (in.got->addDynTlsEntry(sym)) { uint64_t off = in.got->getGlobalDynOffset(sym); @@ -1066,15 +1093,6 @@ expr, type); } -template -static void addPltEntry(PltSection *plt, GotPltSection *gotPlt, - RelocationBaseSection *rel, RelType type, Symbol &sym) { - plt->addEntry(sym); - gotPlt->addEntry(sym); - rel->addReloc( - {type, gotPlt, sym.getGotPltOffset(), !sym.isPreemptible, &sym, 0}); -} - static void addGotEntry(Symbol &sym) { in.got->addEntry(sym); @@ -1374,7 +1392,7 @@ (type == R_HEX_GD_PLT_B22_PCREL || type == R_HEX_GD_PLT_B22_PCREL_X || type == R_HEX_GD_PLT_B32_PCREL_X))) - expr = fromPlt(expr); + expr = fromPlt(expr); } else if (!isAbsoluteValue(sym)) { expr = target->adjustGotPcExpr(type, addend, relocatedAddr); } Index: lld/test/ELF/sparcv9-tls-gd.s =================================================================== --- /dev/null +++ lld/test/ELF/sparcv9-tls-gd.s @@ -0,0 +1,67 @@ +# REQUIRES: sparc +# RUN: echo '.tbss; .globl y; y: .word 0' > %t.s +# RUN: llvm-mc -filetype=obj -triple=sparcv9 %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=sparcv9 %t.s -o %tx.o +# RUN: ld.lld %tx.o -shared -o %tx.so +# RUN: ld.lld %t.o %tx.o -shared -o %t.so +# RUN: ld.lld %t.o %tx.so -o %t1.o +# RUN: llvm-readobj -r %t.so | FileCheck --check-prefix=RELOC %s +# RUN: llvm-objdump -d --no-show-raw-insn %t.so | FileCheck %s +# RUN: llvm-objdump -d --no-show-raw-insn %t1.o | FileCheck --check-prefix=RELAX %s + +# Call __tls_get_addr + +# CHECK: sethi 0, %o0 +# CHECK-NEXT: add %o0, 24, %o0 +# CHECK-NEXT: add %l7, %o0, %o0 +# CHECK-NEXT: call 1048732 +# CHECK-NEXT: nop +# CHECK-NEXT: sethi 0, %o0 +# CHECK-NEXT: add %o0, 40, %o0 +# CHECK-NEXT: add %l7, %o0, %o0 +# CHECK-NEXT: call 1048712 +# CHECK-NEXT: nop + +# GD -> LE Relaxation + +# RELAX: sethi 1, %o0 +# RELAX-NEXT: xor %o0, -2, %o0 +# RELAX-NEXT: add %g7, %o0, %o0 +# RELAX-NEXT: nop +# RELAX-NEXT: nop + +# GD -> IE Relaxation + +# RELAX-NEXT: sethi 0, %o0 +# RELAX-NEXT: or %o0, 24, %o0 +# RELAX-NEXT: ldx [%l7+%o0], %o0 +# RELAX-NEXT: add %g7, %o0, %o0 +# RELAX-NEXT: nop + +sethi %tgd_hi22(x), %o0 +add %o0, %tgd_lo10(x), %o0 +add %l7, %o0, %o0, %tgd_add(x) +call __tls_get_addr, %tgd_call(x) +nop + +sethi %tgd_hi22(y), %o0 +add %o0, %tgd_lo10(y), %o0 +add %l7, %o0, %o0, %tgd_add(y) +call __tls_get_addr, %tgd_call(y) +nop + +.section .tbss +.globl x +x: +.zero 1024+2 +z: + +# RELOC: .rela.dyn { +# RELOC-NEXT: R_SPARC_TLS_DTPMOD64 x 0x0 +# RELOC-NEXT: R_SPARC_TLS_DTPOFF64 x 0x0 +# RELOC-NEXT: R_SPARC_TLS_DTPMOD64 y 0x0 +# RELOC-NEXT: R_SPARC_TLS_DTPOFF64 y 0x0 +# RELOC-NEXT: } +# RELOC: .rela.plt { +# RELOC-NEXT: R_SPARC_JMP_SLOT __tls_get_addr +# RELOC-NEXT: }