Index: lld/trunk/ELF/InputSection.cpp =================================================================== --- lld/trunk/ELF/InputSection.cpp +++ lld/trunk/ELF/InputSection.cpp @@ -220,6 +220,12 @@ // should be applied to the GOT entry content not to the GOT entry offset. // That is why we use separate expression type. return Out::Got->getMipsGotOffset(Body, A); + case R_MIPS_TLSGD: + return Out::Got->getGlobalDynOffset(Body) + + Out::Got->getMipsTlsOffset() - MipsGPOffset; + case R_MIPS_TLSLD: + return Out::Got->getTlsIndexOff() + + Out::Got->getMipsTlsOffset() - MipsGPOffset; case R_PPC_OPD: { uint64_t SymVA = Body.getVA(A); // If we have an undefined weak symbol, we might get here with a symbol Index: lld/trunk/ELF/OutputSections.h =================================================================== --- lld/trunk/ELF/OutputSections.h +++ lld/trunk/ELF/OutputSections.h @@ -134,6 +134,10 @@ // the number of reserved entries. This method is MIPS-specific. unsigned getMipsLocalEntriesNum() const; + // Returns offset of TLS part of the MIPS GOT table. This part goes + // after 'local' and 'global' entries. + uintX_t getMipsTlsOffset(); + uintX_t getTlsIndexVA() { return Base::getVA() + TlsIndexOff; } uint32_t getTlsIndexOff() { return TlsIndexOff; } @@ -215,6 +219,7 @@ uintX_t getOffset() const; uintX_t getAddend() const; uint32_t getSymIndex() const; + const OutputSectionBase *getOutputSec() const { return OutputSec; } uint32_t Type; Index: lld/trunk/ELF/OutputSections.cpp =================================================================== --- lld/trunk/ELF/OutputSections.cpp +++ lld/trunk/ELF/OutputSections.cpp @@ -135,6 +135,14 @@ MipsOutSections.insert(OutSec); return; } + if (Sym.isTls()) { + // GOT entries created for MIPS TLS relocations behave like + // almost GOT entries from other ABIs. They go to the end + // of the global offset table. + Sym.GotIndex = Entries.size(); + Entries.push_back(&Sym); + return; + } auto AddEntry = [&](SymbolBody &S, uintX_t A, MipsGotEntries &Items) { if (S.isInGot() && !A) return; @@ -192,7 +200,9 @@ typename GotSection::uintX_t GotSection::getMipsGotOffset(const SymbolBody &B, uintX_t Addend) const { uintX_t Off = MipsPageEntries; - if (B.IsInGlobalMipsGot) + if (B.isTls()) + Off += MipsLocal.size() + MipsGlobal.size() + B.GotIndex; + else if (B.IsInGlobalMipsGot) Off += MipsLocal.size() + B.GotIndex; else if (B.isInGot()) Off += B.GotIndex; @@ -205,6 +215,12 @@ } template +typename GotSection::uintX_t GotSection::getMipsTlsOffset() { + return (MipsPageEntries + MipsLocal.size() + MipsGlobal.size()) * + sizeof(uintX_t); +} + +template typename GotSection::uintX_t GotSection::getGlobalDynAddr(const SymbolBody &B) const { return this->getVA() + B.GlobalDynIndex * sizeof(uintX_t); @@ -355,6 +371,11 @@ if (Config->Rela) P->r_addend = Rel.getAddend(); P->r_offset = Rel.getOffset(); + if (Config->EMachine == EM_MIPS && Rel.getOutputSec() == Out::Got) + // Dynamic relocation against MIPS GOT section make deal TLS entries + // allocated in the end of the GOT. We need to adjust the offset to take + // in account 'local' and 'global' GOT entries. + P->r_offset += Out::Got->getMipsTlsOffset(); P->setSymbolAndType(Rel.getSymIndex(), Rel.Type, Config->Mips64EL); } Index: lld/trunk/ELF/Relocations.h =================================================================== --- lld/trunk/ELF/Relocations.h +++ lld/trunk/ELF/Relocations.h @@ -30,6 +30,8 @@ R_HINT, R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOT_OFF, + R_MIPS_TLSGD, + R_MIPS_TLSLD, R_NEG_TLS, R_PAGE_PC, R_PC, Index: lld/trunk/ELF/Relocations.cpp =================================================================== --- lld/trunk/ELF/Relocations.cpp +++ lld/trunk/ELF/Relocations.cpp @@ -60,7 +60,8 @@ static bool refersToGotEntry(RelExpr Expr) { return Expr == R_GOT || Expr == R_GOT_OFF || Expr == R_MIPS_GOT_LOCAL_PAGE || - Expr == R_MIPS_GOT_OFF || Expr == R_GOT_PAGE_PC || Expr == R_GOT_PC || + Expr == R_MIPS_GOT_OFF || Expr == R_MIPS_TLSGD || + Expr == R_MIPS_TLSLD || Expr == R_GOT_PAGE_PC || Expr == R_GOT_PC || Expr == R_GOT_FROM_END || Expr == R_TLSGD || Expr == R_TLSGD_PC || Expr == R_TLSDESC || Expr == R_TLSDESC_PAGE; } @@ -81,6 +82,39 @@ return Body.isPreemptible(); } +// This function is similar to the `handleTlsRelocation`. MIPS does not support +// any relaxations for TLS relocations so by factoring out MIPS handling into +// the separate function we can simplify the code and does not pollute +// `handleTlsRelocation` by MIPS `ifs` statements. +template +static unsigned +handleMipsTlsRelocation(uint32_t Type, SymbolBody &Body, + InputSectionBase &C, typename ELFT::uint Offset, + typename ELFT::uint Addend, RelExpr Expr) { + if (Expr == R_MIPS_TLSLD) { + if (Out::Got->addTlsIndex()) + Out::RelaDyn->addReloc({Target->TlsModuleIndexRel, Out::Got, + Out::Got->getTlsIndexOff(), false, + nullptr, 0}); + C.Relocations.push_back({Expr, Type, &C, Offset, Addend, &Body}); + return 1; + } + if (Target->isTlsGlobalDynamicRel(Type)) { + if (Out::Got->addDynTlsEntry(Body)) { + typedef typename ELFT::uint uintX_t; + uintX_t Off = Out::Got->getGlobalDynOffset(Body); + Out::RelaDyn->addReloc( + {Target->TlsModuleIndexRel, Out::Got, Off, false, &Body, 0}); + Out::RelaDyn->addReloc({Target->TlsOffsetRel, Out::Got, + Off + (uintX_t)sizeof(uintX_t), false, + &Body, 0}); + } + C.Relocations.push_back({Expr, Type, &C, Offset, Addend, &Body}); + return 1; + } + return 0; +} + // Returns the number of relocations processed. template static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body, @@ -95,6 +129,9 @@ typedef typename ELFT::uint uintX_t; + if (Config->EMachine == EM_MIPS) + return handleMipsTlsRelocation(Type, Body, C, Offset, Addend, Expr); + if ((Expr == R_TLSDESC || Expr == R_TLSDESC_PAGE || Expr == R_HINT) && Config->Shared) { if (Out::Got->addDynTlsEntry(Body)) { @@ -254,9 +291,9 @@ const SymbolBody &Body) { // These expressions always compute a constant if (E == R_SIZE || E == R_GOT_FROM_END || E == R_GOT_OFF || - E == R_MIPS_GOT_LOCAL_PAGE || E == R_MIPS_GOT_OFF || E == R_GOT_PAGE_PC || - E == R_GOT_PC || E == R_PLT_PC || E == R_TLSGD_PC || E == R_TLSGD || - E == R_PPC_PLT_OPD || E == R_TLSDESC_PAGE || E == R_HINT) + E == R_MIPS_GOT_LOCAL_PAGE || E == R_MIPS_GOT_OFF || E == R_MIPS_TLSGD || + E == R_GOT_PAGE_PC || E == R_GOT_PC || E == R_PLT_PC || E == R_TLSGD_PC || + E == R_TLSGD || E == R_PPC_PLT_OPD || E == R_TLSDESC_PAGE || E == R_HINT) return true; // These never do, except if the entire file is position dependent or if @@ -603,6 +640,9 @@ // for detailed description: // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf Out::Got->addMipsEntry(Body, Addend, Expr); + if (Body.isTls()) + AddDyn({Target->TlsGotRel, Out::Got, Body.getGotOffset(), + !Preemptible, &Body, 0}); continue; } Index: lld/trunk/ELF/Target.cpp =================================================================== --- lld/trunk/ELF/Target.cpp +++ lld/trunk/ELF/Target.cpp @@ -190,6 +190,8 @@ RelExpr getRelExpr(uint32_t Type, const SymbolBody &S) const override; uint64_t getImplicitAddend(const uint8_t *Buf, uint32_t Type) const override; uint32_t getDynRel(uint32_t Type) const override; + bool isTlsLocalDynamicRel(uint32_t Type) const override; + bool isTlsGlobalDynamicRel(uint32_t Type) const override; void writeGotPlt(uint8_t *Buf, const SymbolBody &S) const override; void writePltHeader(uint8_t *Buf) const override; void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr, @@ -1706,10 +1708,17 @@ ThunkSize = 16; CopyRel = R_MIPS_COPY; PltRel = R_MIPS_JUMP_SLOT; - if (ELFT::Is64Bits) + if (ELFT::Is64Bits) { RelativeRel = (R_MIPS_64 << 8) | R_MIPS_REL32; - else + TlsGotRel = R_MIPS_TLS_TPREL64; + TlsModuleIndexRel = R_MIPS_TLS_DTPMOD64; + TlsOffsetRel = R_MIPS_TLS_DTPREL64; + } else { RelativeRel = R_MIPS_REL32; + TlsGotRel = R_MIPS_TLS_TPREL32; + TlsModuleIndexRel = R_MIPS_TLS_DTPMOD32; + TlsOffsetRel = R_MIPS_TLS_DTPREL32; + } } template @@ -1752,9 +1761,14 @@ // fallthrough case R_MIPS_CALL16: case R_MIPS_GOT_DISP: + case R_MIPS_TLS_GOTTPREL: return R_MIPS_GOT_OFF; case R_MIPS_GOT_PAGE: return R_MIPS_GOT_LOCAL_PAGE; + case R_MIPS_TLS_GD: + return R_MIPS_TLSGD; + case R_MIPS_TLS_LDM: + return R_MIPS_TLSLD; } } @@ -1768,6 +1782,16 @@ } template +bool MipsTargetInfo::isTlsLocalDynamicRel(uint32_t Type) const { + return Type == R_MIPS_TLS_LDM; +} + +template +bool MipsTargetInfo::isTlsGlobalDynamicRel(uint32_t Type) const { + return Type == R_MIPS_TLS_GD; +} + +template void MipsTargetInfo::writeGotPlt(uint8_t *Buf, const SymbolBody &) const { write32(Buf, Out::Plt->getVA()); } @@ -1961,6 +1985,8 @@ case R_MIPS_GOT_PAGE: case R_MIPS_GOT16: case R_MIPS_GPREL16: + case R_MIPS_TLS_GD: + case R_MIPS_TLS_LDM: checkInt<16>(Val, Type); // fallthrough case R_MIPS_CALL16: @@ -1968,6 +1994,7 @@ case R_MIPS_LO16: case R_MIPS_PCLO16: case R_MIPS_TLS_DTPREL_LO16: + case R_MIPS_TLS_GOTTPREL: case R_MIPS_TLS_TPREL_LO16: writeMipsLo16(Loc, Val); break; Index: lld/trunk/test/ELF/Inputs/mips-tls.s =================================================================== --- lld/trunk/test/ELF/Inputs/mips-tls.s +++ lld/trunk/test/ELF/Inputs/mips-tls.s @@ -0,0 +1,5 @@ + .globl foo + .section .tdata,"awT",%progbits + .type foo, %object +foo: + .word 0 Index: lld/trunk/test/ELF/mips-tls-64.s =================================================================== --- lld/trunk/test/ELF/mips-tls-64.s +++ lld/trunk/test/ELF/mips-tls-64.s @@ -0,0 +1,86 @@ +# Check MIPS TLS 64-bit relocations handling. + +# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux \ +# RUN: %p/Inputs/mips-dynamic.s -o %t.so.o +# RUN: ld.lld -shared %t.so.o -o %t.so +# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %t.o +# RUN: ld.lld %t.o %t.so -o %t.exe +# RUN: llvm-objdump -d -s -t %t.exe | FileCheck -check-prefix=DIS %s +# RUN: llvm-readobj -r -mips-plt-got %t.exe | FileCheck %s + +# REQUIRES: mips + +# DIS: __start: +# DIS-NEXT: 20000: 24 62 80 28 addiu $2, $3, -32728 +# DIS-NEXT: 20004: 24 62 80 38 addiu $2, $3, -32712 +# DIS-NEXT: 20008: 8f 82 80 20 lw $2, -32736($gp) +# DIS-NEXT: 2000c: 24 62 80 48 addiu $2, $3, -32696 + +# DIS: Contents of section .got: +# DIS_NEXT: 30008 00000000 00000000 80000000 00000000 +# DIS_NEXT: 30018 00000000 00020000 00000000 00000000 +# DIS_NEXT: 30028 00000000 00000004 00000000 00000000 +# DIS_NEXT: 30038 00000000 00000000 00000000 00000004 + +# DIS: 0000000000030000 l .tdata 00000000 .tdata +# DIS: 0000000000030000 l .tdata 00000000 loc +# DIS: 0000000000000004 g .tdata 00000000 foo + +# CHECK: Relocations [ +# CHECK-NEXT: Section (7) .rela.dyn { +# CHECK-NEXT: 0x30020 R_MIPS_TLS_DTPMOD64/R_MIPS_NONE/R_MIPS_NONE - 0x0 +# CHECK-NEXT: 0x30028 R_MIPS_TLS_DTPREL64/R_MIPS_NONE/R_MIPS_NONE - 0x0 +# CHECK-NEXT: 0x30030 R_MIPS_TLS_DTPMOD64/R_MIPS_NONE/R_MIPS_NONE - 0x0 +# CHECK-NEXT: 0x30040 R_MIPS_TLS_TPREL64/R_MIPS_NONE/R_MIPS_NONE - 0x4 +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: Primary GOT { +# CHECK-NEXT: Canonical gp value: 0x37FF8 +# CHECK-NEXT: Reserved entries [ +# CHECK-NEXT: Entry { +# CHECK-NEXT: Address: 0x30008 +# CHECK-NEXT: Access: -32752 +# CHECK-NEXT: Initial: 0x0 +# CHECK-NEXT: Purpose: Lazy resolver +# CHECK-NEXT: } +# CHECK-NEXT: Entry { +# CHECK-NEXT: Address: 0x30010 +# CHECK-NEXT: Access: -32744 +# CHECK-NEXT: Initial: 0x80000000 +# CHECK-NEXT: Purpose: Module pointer (GNU extension) +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: Local entries [ +# CHECK-NEXT: ] +# CHECK-NEXT: Global entries [ +# CHECK-NEXT: Entry { +# CHECK-NEXT: Address: 0x30018 +# CHECK-NEXT: Access: -32736 +# CHECK-NEXT: Initial: 0x0 +# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Type: Function +# CHECK-NEXT: Section: Undefined +# CHECK-NEXT: Name: foo0 +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: Number of TLS and multi-GOT entries: 5 +# ^-- 0x30020 / -32728 - R_MIPS_TLS_GD - R_MIPS_TLS_DTPMOD32 foo +# ^-- 0x30028 / -32720 - R_MIPS_TLS_DTPREL32 foo +# ^-- 0x30030 / -32712 - R_MIPS_TLS_LDM - R_MIPS_TLS_DTPMOD32 loc +# ^-- 0x30038 / -32704 +# ^-- 0x30040 / -32696 - R_MIPS_TLS_GOTTPREL - R_MIPS_TLS_TPREL32 + + .text + .global __start +__start: + addiu $2, $3, %tlsgd(foo) # R_MIPS_TLS_GD + addiu $2, $3, %tlsldm(loc) # R_MIPS_TLS_LDM + lw $2, %got(foo0)($gp) + addiu $2, $3, %gottprel(foo) # R_MIPS_TLS_GOTTPREL + + .section .tdata,"awT",%progbits + .global foo +loc: + .word 0 +foo: + .word 0 Index: lld/trunk/test/ELF/mips-tls.s =================================================================== --- lld/trunk/test/ELF/mips-tls.s +++ lld/trunk/test/ELF/mips-tls.s @@ -0,0 +1,77 @@ +# Check MIPS TLS relocations handling. + +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ +# RUN: %p/Inputs/mips-tls.s -o %t.so.o +# RUN: ld.lld -shared %t.so.o -o %t.so +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o +# RUN: ld.lld %t.o %t.so -o %t.exe +# RUN: llvm-objdump -d -s -t %t.exe | FileCheck -check-prefix=DIS %s +# RUN: llvm-readobj -r -mips-plt-got %t.exe | FileCheck %s + +# REQUIRES: mips + +# DIS: __start: +# DIS-NEXT: 20000: 24 62 80 1c addiu $2, $3, -32740 +# DIS-NEXT: 20004: 24 62 80 24 addiu $2, $3, -32732 +# DIS-NEXT: 20008: 8f 82 80 18 lw $2, -32744($gp) +# DIS-NEXT: 2000c: 24 62 80 2c addiu $2, $3, -32724 + +# DIS: Contents of section .got: +# DIS_NEXT: 30004 00000000 80000000 00020000 00000000 +# DIS_NEXT: 30014 00000000 00000000 00000000 00000000 + +# DIS: 00030000 l .tdata 00000000 .tdata +# DIS: 00030000 l .tdata 00000000 loc +# DIS: 00000000 g *UND* 00000000 foo + +# CHECK: Relocations [ +# CHECK-NEXT: Section (7) .rel.dyn { +# CHECK-NEXT: 0x30018 R_MIPS_TLS_DTPMOD32 - 0x0 +# CHECK-NEXT: 0x30010 R_MIPS_TLS_DTPMOD32 foo 0x0 +# CHECK-NEXT: 0x30014 R_MIPS_TLS_DTPREL32 foo 0x0 +# CHECK-NEXT: 0x30020 R_MIPS_TLS_TPREL32 foo 0x0 +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: Primary GOT { +# CHECK-NEXT: Canonical gp value: 0x37FF4 +# CHECK-NEXT: Reserved entries [ +# CHECK-NEXT: Entry { +# CHECK-NEXT: Address: 0x30004 +# CHECK-NEXT: Access: -32752 +# CHECK-NEXT: Initial: 0x0 +# CHECK-NEXT: Purpose: Lazy resolver +# CHECK-NEXT: } +# CHECK-NEXT: Entry { +# CHECK-NEXT: Address: 0x30008 +# CHECK-NEXT: Access: -32748 +# CHECK-NEXT: Initial: 0x80000000 +# CHECK-NEXT: Purpose: Module pointer (GNU extension) +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: Local entries [ +# CHECK-NEXT: Entry { +# CHECK-NEXT: Address: 0x3000C +# CHECK-NEXT: Access: -32744 +# CHECK-NEXT: Initial: 0x20000 +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: Global entries [ +# CHECK-NEXT: ] +# CHECK-NEXT: Number of TLS and multi-GOT entries: 5 +# ^-- 0x30010 / -32740 - R_MIPS_TLS_GD - R_MIPS_TLS_DTPMOD32 foo +# ^-- 0x30018 / -32736 - R_MIPS_TLS_DTPREL32 foo +# ^-- 0x3001C / -32732 - R_MIPS_TLS_LDM - R_MIPS_TLS_DTPMOD32 loc +# ^-- 0x30020 / -32728 +# ^-- 0x30024 / -32724 - R_MIPS_TLS_GOTTPREL - R_MIPS_TLS_TPREL32 + + .text + .global __start +__start: + addiu $2, $3, %tlsgd(foo) # R_MIPS_TLS_GD + addiu $2, $3, %tlsldm(loc) # R_MIPS_TLS_LDM + lw $2, %got(__start)($gp) + addiu $2, $3, %gottprel(foo) # R_MIPS_TLS_GOTTPREL + + .section .tdata,"awT",%progbits +loc: + .word 0