Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ ELF/InputSection.h @@ -55,6 +55,8 @@ R_TLS, R_TLSGD, R_TLSGD_PC, + R_TLSDESC, + R_TLSDESC_PAGE, R_TLSLD, R_TLSLD_PC }; @@ -63,7 +65,7 @@ return Expr == R_GOT || Expr == R_GOT_OFF || Expr == R_MIPS_GOT || Expr == R_MIPS_GOT_LOCAL || Expr == R_GOT_PAGE_PC || Expr == R_GOT_PC || Expr == R_GOT_FROM_END || Expr == R_TLSGD || - Expr == R_TLSGD_PC; + Expr == R_TLSGD_PC || Expr == R_TLSDESC || Expr == R_TLSDESC_PAGE; } struct Relocation { Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -155,6 +155,11 @@ Out::Got->getNumEntries() * sizeof(uintX_t); case R_TLSGD_PC: return Out::Got->getGlobalDynAddr(Body) + A - P; + case R_TLSDESC: + return Body.getGotPltTlsDescVA() + A; + case R_TLSDESC_PAGE: + return getAArch64Page(Body.getGotPltTlsDescVA() + A) - + getAArch64Page(P); case R_PLT: return Body.getPltVA() + A; case R_PLT_PC: Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -112,6 +112,7 @@ void writeTo(uint8_t *Buf) override; void addEntry(SymbolBody &Sym); bool addDynTlsEntry(SymbolBody &Sym); + void addTlsDescEntry(); bool addTlsIndex(); bool empty() const { return MipsLocalEntries == 0 && Entries.empty(); } uintX_t getMipsLocalEntryOffset(uintX_t EntryValue); @@ -132,10 +133,13 @@ uintX_t getTlsIndexVA() { return Base::getVA() + TlsIndexOff; } uint32_t getTlsIndexOff() { return TlsIndexOff; } + uintX_t getTlsDescEntryVA() const { return Base::getVA() + TlsDescEntryOff; } + bool hasTlsDescEntry() const { return TlsDescEntryOff != (uintX_t)-1; } private: std::vector Entries; uint32_t TlsIndexOff = -1; + uintX_t TlsDescEntryOff = -1; uint32_t MipsLocalEntries = 0; // Output sections referenced by MIPS GOT relocations. llvm::SmallPtrSet *, 10> MipsOutSections; @@ -151,10 +155,12 @@ void finalize() override; void writeTo(uint8_t *Buf) override; void addEntry(SymbolBody &Sym); + void addTlsDescEntry(SymbolBody &Sym); bool empty() const; private: std::vector Entries; + std::vector TlsDescEntries; }; template class PltSection final : public OutputSectionBase { @@ -166,7 +172,8 @@ void finalize() override; void writeTo(uint8_t *Buf) override; void addEntry(SymbolBody &Sym); - bool empty() const { return Entries.empty(); } + bool empty() const; + uintX_t getTlsDescEntryVA() const; private: std::vector> Entries; @@ -276,15 +283,18 @@ public: RelocationSection(StringRef Name); void addReloc(const DynamicReloc &Reloc); + void addTlsDescReloc(const DynamicReloc &Reloc); unsigned getRelocOffset(); void finalize() override; void writeTo(uint8_t *Buf) override; - bool hasRelocs() const { return !Relocs.empty(); } + bool hasRelocs() const { return getNumRelocs() != 0; } bool Static = false; private: + size_t getNumRelocs() const; std::vector> Relocs; + std::vector> TlsDescRelocs; }; template @@ -454,7 +464,9 @@ // The .dynamic section contains information for the dynamic linker. // The section consists of fixed size entries, which consist of // type and value fields. Value are one of plain integers, symbol - // addresses, or section addresses. This struct represents the entry. + // addresses, or section addresses. Also value can be of special type. + // Such ones are handled depending on Tag value in writeTo(). This struct + // represents the entry. struct Entry { int32_t Tag; union { @@ -462,12 +474,13 @@ uint64_t Val; const SymbolBody *Sym; }; - enum KindT { SecAddr, SymAddr, PlainInt } Kind; + enum KindT { SecAddr, SymAddr, PlainInt, Special } Kind; Entry(int32_t Tag, OutputSectionBase *OutSec) : Tag(Tag), OutSec(OutSec), Kind(SecAddr) {} Entry(int32_t Tag, uint64_t Val) : Tag(Tag), Val(Val), Kind(PlainInt) {} Entry(int32_t Tag, const SymbolBody *Sym) : Tag(Tag), Sym(Sym), Kind(SymAddr) {} + Entry(int32_t Tag) : Tag(Tag), Kind(Special) {} }; // finalize() fills this vector with the section contents. finalize() Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -65,18 +65,28 @@ Entries.push_back(&Sym); } +template +void GotPltSection::addTlsDescEntry(SymbolBody &Sym) { + Sym.symbol()->GlobalDynIndex = Target->GotPltHeaderEntriesNum + Entries.size(); + Sym.GotPltIndex = TlsDescEntries.size(); + TlsDescEntries.insert(TlsDescEntries.end(), {nullptr, nullptr}); +} + template bool GotPltSection::empty() const { - return Entries.empty(); + return Entries.empty() && TlsDescEntries.empty(); } template void GotPltSection::finalize() { + size_t EntriesSize = Entries.size() + TlsDescEntries.size(); this->Header.sh_size = - (Target->GotPltHeaderEntriesNum + Entries.size()) * sizeof(uintX_t); + (Target->GotPltHeaderEntriesNum + EntriesSize) * sizeof(uintX_t); } template void GotPltSection::writeTo(uint8_t *Buf) { Target->writeGotPltHeader(Buf); Buf += Target->GotPltHeaderEntriesNum * sizeof(uintX_t); + // Since TLSDESC entries will contains nullptr SymbolBody there is no + // need to write them down. for (const SymbolBody *B : Entries) { Target->writeGotPlt(Buf, B->getPltVA()); Buf += sizeof(uintX_t); @@ -153,6 +163,13 @@ return true; } +template void GotSection::addTlsDescEntry() { + if (TlsDescEntryOff != uintX_t(-1)) + return; + Entries.push_back(nullptr); + TlsDescEntryOff = (Entries.size() - 1) * sizeof(uintX_t); +} + // Reserves TLS entries for a TLS module ID and a TLS block offset. // In total it takes two GOT slots. template bool GotSection::addTlsIndex() { @@ -285,6 +302,13 @@ Target->writePlt(Buf + Off, Got, Plt, B->PltIndex, RelOff); Off += Target->PltEntrySize; } + + // If module uses TLS description relocations writes down the architecture + // specific lazy relocation entrypoint. + if (Out::Got->hasTlsDescEntry()) + Target->writePltTlsDescEntry(Buf + Off, this->getVA() + Off, + Out::Got->getTlsDescEntryVA(), + Out::GotPlt->getVA()); } template void PltSection::addEntry(SymbolBody &Sym) { @@ -295,9 +319,21 @@ Entries.push_back(std::make_pair(&Sym, RelOff)); } +template bool PltSection::empty() const { + return Entries.empty() && !Out::Got->hasTlsDescEntry(); +} + +template +typename PltSection::uintX_t PltSection::getTlsDescEntryVA() const { + return this->getVA() + Target->PltZeroSize + + Entries.size() * Target->PltEntrySize; +} + template void PltSection::finalize() { this->Header.sh_size = Target->PltZeroSize + Entries.size() * Target->PltEntrySize; + if (Out::Got->hasTlsDescEntry()) + this->Header.sh_size += Target->PltTlsDescSize; } template @@ -313,28 +349,39 @@ Relocs.push_back(Reloc); } +template +void RelocationSection::addTlsDescReloc(const DynamicReloc &Reloc) { + TlsDescRelocs.push_back(Reloc); +} + template void RelocationSection::writeTo(uint8_t *Buf) { - for (const DynamicReloc &Rel : Relocs) { - auto *P = reinterpret_cast(Buf); - Buf += Config->Rela ? sizeof(Elf_Rela) : sizeof(Elf_Rel); - SymbolBody *Sym = Rel.Sym; - - if (Config->Rela) - P->r_addend = Rel.UseSymVA ? Sym->getVA(Rel.Addend) : Rel.Addend; - P->r_offset = Rel.OffsetInSec + Rel.OffsetSec->getVA(); - uint32_t SymIdx = (!Rel.UseSymVA && Sym) ? Sym->DynsymIndex : 0; - P->setSymbolAndType(SymIdx, Rel.Type, Config->Mips64EL); + for (const std::vector> &Vec : {Relocs, TlsDescRelocs}) { + for (const DynamicReloc &Rel : Vec) { + auto *P = reinterpret_cast(Buf); + Buf += Config->Rela ? sizeof(Elf_Rela) : sizeof(Elf_Rel); + SymbolBody *Sym = Rel.Sym; + + if (Config->Rela) + P->r_addend = Rel.UseSymVA ? Sym->getVA(Rel.Addend) : Rel.Addend; + P->r_offset = Rel.OffsetInSec + Rel.OffsetSec->getVA(); + uint32_t SymIdx = (!Rel.UseSymVA && Sym) ? Sym->DynsymIndex : 0; + P->setSymbolAndType(SymIdx, Rel.Type, Config->Mips64EL); + } } } +template size_t RelocationSection::getNumRelocs() const { + return Relocs.size() + TlsDescRelocs.size(); +} + template unsigned RelocationSection::getRelocOffset() { - return this->Header.sh_entsize * Relocs.size(); + return this->Header.sh_entsize * getNumRelocs(); } template void RelocationSection::finalize() { this->Header.sh_link = Static ? Out::SymTab->SectionIndex : Out::DynSymTab->SectionIndex; - this->Header.sh_size = Relocs.size() * this->Header.sh_entsize; + this->Header.sh_size = getNumRelocs() * this->Header.sh_entsize; } template @@ -604,6 +651,11 @@ Add({DT_PLTREL, uint64_t(Config->Rela ? DT_RELA : DT_REL)}); } + if (Out::Got->hasTlsDescEntry()) { + Add({DT_TLSDESC_GOT}); + Add({DT_TLSDESC_PLT}); + } + Add({DT_SYMTAB, Out::DynSymTab}); Add({DT_SYMENT, sizeof(Elf_Sym)}); Add({DT_STRTAB, Out::DynStrTab}); @@ -679,6 +731,20 @@ Header.sh_size = (Entries.size() + 1) * Header.sh_entsize; } +template +static void handleSpecialDynamic(const Entry &E, ElfDyn *P) { + switch (E.Tag) { + case DT_TLSDESC_GOT: + P->d_un.d_ptr = Out::Got->getTlsDescEntryVA(); + break; + case DT_TLSDESC_PLT: + P->d_un.d_ptr = Out::Plt->getTlsDescEntryVA(); + break; + default: + assert(false && "Unknown special dynamic section entry"); + } +} + template void DynamicSection::writeTo(uint8_t *Buf) { auto *P = reinterpret_cast(Buf); @@ -694,6 +760,9 @@ case Entry::PlainInt: P->d_un.d_val = E.Val; break; + case Entry::Special: + handleSpecialDynamic(E, P); + break; } ++P; } Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -94,6 +94,7 @@ uint32_t PltIndex = -1; uint32_t ThunkIndex = -1; bool isInGot() const { return GotIndex != -1U; } + bool isInGotPlt() const { return GotPltIndex != -1U; } bool isInPlt() const { return PltIndex != -1U; } bool hasThunk() const { return ThunkIndex != -1U; } @@ -104,6 +105,7 @@ template typename ELFT::uint getGotVA() const; template typename ELFT::uint getGotPltOffset() const; template typename ELFT::uint getGotPltVA() const; + template typename ELFT::uint getGotPltTlsDescVA() const; template typename ELFT::uint getPltVA() const; template typename ELFT::uint getThunkVA() const; template typename ELFT::uint getSize() const; Index: ELF/Symbols.cpp =================================================================== --- ELF/Symbols.cpp +++ ELF/Symbols.cpp @@ -152,6 +152,13 @@ return GotPltIndex * sizeof(typename ELFT::uint); } +template +typename ELFT::uint SymbolBody::getGotPltTlsDescVA() const { + return Out::GotPlt->getVA() + + (symbol()->GlobalDynIndex + GotPltIndex) * + sizeof(typename ELFFile::uintX_t); +} + template typename ELFT::uint SymbolBody::getPltVA() const { return Out::Plt->getVA() + Target->PltZeroSize + PltIndex * Target->PltEntrySize; @@ -283,6 +290,11 @@ template uint64_t SymbolBody::template getGotPltOffset() const; template uint64_t SymbolBody::template getGotPltOffset() const; +template uint32_t SymbolBody::template getGotPltTlsDescVA() const; +template uint32_t SymbolBody::template getGotPltTlsDescVA() const; +template uint64_t SymbolBody::template getGotPltTlsDescVA() const; +template uint64_t SymbolBody::template getGotPltTlsDescVA() const; + template uint32_t SymbolBody::template getPltVA() const; template uint32_t SymbolBody::template getPltVA() const; template uint64_t SymbolBody::template getPltVA() const; Index: ELF/Target.h =================================================================== --- ELF/Target.h +++ ELF/Target.h @@ -28,6 +28,7 @@ virtual bool isTlsLocalDynamicRel(uint32_t Type) const; virtual bool isTlsGlobalDynamicRel(uint32_t Type) const; virtual uint32_t getDynRel(uint32_t Type) const { return Type; } + virtual bool isTlsDescRel(uint32_t Type) const { return false; } virtual uint32_t getTlsGotRel(uint32_t Type) const { return TlsGotRel; } virtual void writeGotPltHeader(uint8_t *Buf) const {} virtual void writeGotPlt(uint8_t *Buf, uint64_t Plt) const {}; @@ -42,6 +43,13 @@ uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const {} + // This function writes down the PLT entrypoint for default lazy tls + // descriptor relocation. It is used if GOT section contains a TLS + // descriptor entry. + virtual void writePltTlsDescEntry(uint8_t *Buf, uint64_t PltEntryAddr, + uint64_t GotTlsDescEntryAddr, + uint64_t GotPltVA) const {}; + // Returns true if a relocation is just a hint for linker to make for example // some code optimization. Such relocations should not be handled as a regular // ones and lead to dynamic relocation creation etc. @@ -79,10 +87,12 @@ uint32_t PltRel; uint32_t RelativeRel; uint32_t IRelativeRel; + uint32_t TlsDescRel; uint32_t TlsGotRel = 0; uint32_t TlsModuleIndexRel; uint32_t TlsOffsetRel; unsigned PltEntrySize = 8; + unsigned PltTlsDescSize = 0; unsigned PltZeroSize = 0; unsigned GotPltHeaderEntriesNum = 3; uint32_t ThunkSize = 0; Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -278,6 +278,51 @@ }; } +template +static unsigned handleTlsGDReloc(uint32_t Type, SymbolBody &Body, + InputSectionBase &C, + typename ELFT::uint Offset, + typename ELFT::uint Addend, RelExpr Expr) { + typedef typename ELFT::uint uintX_t; + if (Out::Got->addDynTlsEntry(Body)) { + 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, Offset, Addend, &Body}); + return 1; +} + +template +static unsigned handleTLSDescReloc(uint32_t Type, SymbolBody &Body, + InputSectionBase &C, + typename ELFT::uint Offset, + typename ELFT::uint Addend, RelExpr Expr) { + // If module uses TLS descriptor relocations, then special entries are + // created for module: + // 1) .got entry to be filled in by the dynamic loader with the + // address of the internal function to be used for lazy relocation of TLS + // descriptors. + Out::Got->addTlsDescEntry(); + if (!Body.isInGotPlt()) { + // 2) Special .plt entry that pushes onto the stack the module's link map + // address, located in the GOT portion reserved for the dynamic loader + // to use, and then jumps to the lazy relocation function, using the + // address stored in the TLSDESC_GOT entry. + // For each Body itself two words are allocated in .got.plt instead of + // usual .got, because these relocations are lazy ones. + Out::GotPlt->addTlsDescEntry(Body); + Out::RelaPlt->addTlsDescReloc({Target->TlsDescRel, Out::GotPlt, + Body.getGotPltTlsDescVA(), false, + &Body, 0}); + } + C.Relocations.push_back({Expr, Type, Offset, Addend, &Body}); + return 1; +} + // Returns the number of relocations processed. template static unsigned handleTlsRelocation(uint32_t Type, SymbolBody &Body, @@ -290,7 +335,6 @@ if (!Body.isTls()) return 0; - typedef typename ELFT::uint uintX_t; if (Expr == R_TLSLD_PC || Expr == R_TLSLD) { // Local-Dynamic relocs can be relaxed to Local-Exec. if (!Config->Shared) { @@ -313,18 +357,14 @@ return 1; } - if (Target->isTlsGlobalDynamicRel(Type)) { + bool IsTlsGlobal = Target->isTlsGlobalDynamicRel(Type); + bool IsTlsDesc = Target->isTlsDescRel(Type); + if (IsTlsGlobal || IsTlsDesc) { if (Config->Shared) { - if (Out::Got->addDynTlsEntry(Body)) { - 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, Offset, Addend, &Body}); - return 1; + if (IsTlsGlobal) + return handleTlsGDReloc(Type, Body, C, Offset, Addend, Expr); + else + return handleTLSDescReloc(Type, Body, C, Offset, Addend, Expr); } // Global-Dynamic relocs can be relaxed to Initial-Exec or Local-Exec @@ -354,6 +394,7 @@ {R_RELAX_TLS_IE_TO_LE, Type, Offset, Addend, &Body}); return 1; } + return 0; }