Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -292,6 +292,16 @@ continue; } + // TLS descriptor uses two words in the .got.plt which describes a + // structure of a function pointer and its arguments. Both fields will + // be adjusted at runtime by the dynamic loader with a single + // relocation (usually R__TLSDESC). + if (Target->isTlsDescRel(Type)) { + Target->relocateOne(BufLoc, BufEnd, Type, AddrLoc, + Body.getGotPltTlsDescVA() + A); + continue; + } + if (!RelTy::IsRela) A += Target->getImplicitAddend(BufLoc, Type); if (Config->EMachine == EM_MIPS) Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -107,6 +107,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 getMipsLocalFullAddr(const SymbolBody &B); @@ -125,10 +126,13 @@ unsigned getMipsLocalEntriesNum() const; uintX_t getTlsIndexVA() { return Base::getVA() + 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; @@ -137,6 +141,12 @@ uintX_t getMipsLocalEntryAddr(uintX_t EntryValue); }; +enum DynamicRelocKind { + DynReloc_JumpSlot = 0, + DynReloc_TlsDesc = 1, + DynReloc_NumKind = DynReloc_TlsDesc + 1 +}; + template class GotPltSection final : public OutputSectionBase { typedef typename ELFT::uint uintX_t; @@ -146,10 +156,11 @@ 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> Entries; }; template class PltSection final : public OutputSectionBase { @@ -161,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; @@ -175,6 +187,7 @@ enum OffsetKind { Off_Got, // The got entry of Sym. Off_GotPlt, // The got.plt entry of Sym. + Off_GotPltTlsdesc, // The .got.plt entry for a TLS descriptor. Off_Bss, // The bss entry of Sym (copy reloc). Off_Sec, // The final position of the given input section and offset. Off_LTlsIndex, // The local tls index. @@ -248,15 +261,17 @@ 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 getRelocsSize() != 0; } bool Static = false; private: - std::vector> Relocs; + size_t getRelocsSize() const; + std::vector>> Relocs; }; template @@ -423,7 +438,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 { @@ -431,12 +448,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 @@ -54,28 +54,43 @@ template GotPltSection::GotPltSection() - : OutputSectionBase(".got.plt", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE) { + : OutputSectionBase(".got.plt", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE), + Entries(DynReloc_NumKind) { this->Header.sh_addralign = sizeof(uintX_t); } template void GotPltSection::addEntry(SymbolBody &Sym) { - Sym.GotPltIndex = Target->GotPltHeaderEntriesNum + Entries.size(); - Entries.push_back(&Sym); + Sym.GotPltIndex = Target->GotPltHeaderEntriesNum + + Entries[DynReloc_JumpSlot].size(); + Entries[DynReloc_JumpSlot].push_back(&Sym); +} + +template +void GotPltSection::addTlsDescEntry(SymbolBody &Sym) { + Sym.GlobalDynIndex = Target->GotPltHeaderEntriesNum + + Entries[DynReloc_JumpSlot].size(); + Sym.GotPltIndex = Entries[DynReloc_TlsDesc].size(); + Entries[DynReloc_TlsDesc].push_back(nullptr); + Entries[DynReloc_TlsDesc].push_back(nullptr); } template bool GotPltSection::empty() const { - return Entries.empty(); + return Entries[DynReloc_JumpSlot].empty() && Entries[DynReloc_TlsDesc].empty(); } template void GotPltSection::finalize() { + size_t entriesSize = Entries[DynReloc_JumpSlot].size() + + Entries[DynReloc_TlsDesc].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); - for (const SymbolBody *B : Entries) { + // Since TLSDESC entries will contains nullptr there is no need to + // write them down. + for (const SymbolBody *B : Entries[DynReloc_JumpSlot]) { Target->writeGotPlt(Buf, B->getPltVA()); Buf += sizeof(uintX_t); } @@ -154,6 +169,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() { @@ -266,6 +288,15 @@ Target->writePlt(Buf + Off, Got, Plt, B->PltIndex, RelOff); Off += Target->PltEntrySize; } + + // If module uses TLS descriptor relocations, it requires double .got.plt + // entry and a special .plt entry as well. This plt entry is used for TLS + // descriptor resolver calls, and also dynamic entry DT_TLSDESC_PLT should be + // created to hold it's address. + if (Out::Got->hasTlsDescEntry()) + Target->writePltTlsDescEntry(Buf + Off, this->getVA() + Off, + Out::Got->getTlsDescEntryVA(), + Out::GotPlt->getVA()); } template void PltSection::addEntry(SymbolBody &Sym) { @@ -276,25 +307,47 @@ 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 RelocationSection::RelocationSection(StringRef Name) : OutputSectionBase(Name, Config->Rela ? SHT_RELA : SHT_REL, - SHF_ALLOC) { + SHF_ALLOC), Relocs(DynReloc_NumKind) { this->Header.sh_entsize = Config->Rela ? sizeof(Elf_Rela) : sizeof(Elf_Rel); this->Header.sh_addralign = sizeof(uintX_t); } -template -void RelocationSection::addReloc(const DynamicReloc &Reloc) { +template static void checkReloc(const DynamicReloc &Reloc) { SymbolBody *Sym = Reloc.Sym; if (!Reloc.UseSymVA && Sym) Sym->MustBeInDynSym = true; - Relocs.push_back(Reloc); +} + +template +void RelocationSection::addReloc(const DynamicReloc &Reloc) { + checkReloc(Reloc); + Relocs[DynReloc_JumpSlot].push_back(Reloc); +} + +template +void RelocationSection::addTlsDescReloc(const DynamicReloc &Reloc) { + checkReloc(Reloc); + Relocs[DynReloc_TlsDesc].push_back(Reloc); } template @@ -314,32 +367,40 @@ return Sym->getGotVA(); case Off_GotPlt: return Sym->getGotPltVA(); + case Off_GotPltTlsdesc: + return Sym->getGotPltTlsDescVA(); } llvm_unreachable("invalid offset kind"); } 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.getOffset(); - uint32_t SymIdx = (!Rel.UseSymVA && Sym) ? Sym->DynsymIndex : 0; - P->setSymbolAndType(SymIdx, Rel.Type, Config->Mips64EL); + for (auto RelocVec : Relocs) { + for (const DynamicReloc &Rel : RelocVec) { + 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.getOffset(); + uint32_t SymIdx = (!Rel.UseSymVA && Sym) ? Sym->DynsymIndex : 0; + P->setSymbolAndType(SymIdx, Rel.Type, Config->Mips64EL); + } } } +template size_t RelocationSection::getRelocsSize() const { + return Relocs[DynReloc_JumpSlot].size() + Relocs[DynReloc_TlsDesc].size(); +} + template unsigned RelocationSection::getRelocOffset() { - return this->Header.sh_entsize * Relocs.size(); + return this->Header.sh_entsize * getRelocsSize(); } 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 = getRelocsSize() * this->Header.sh_entsize; } template @@ -609,6 +670,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}); @@ -678,6 +744,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); @@ -693,6 +773,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 @@ -96,6 +96,7 @@ uint32_t ThunkIndex = -1; bool hasGlobalDynIndex() { return GlobalDynIndex != uint32_t(-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; } @@ -106,6 +107,7 @@ template typename ELFT::uint getGotVA() 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 @@ -159,6 +159,13 @@ return Out::GotPlt->getVA() + GotPltIndex * sizeof(typename ELFT::uint); } +template +typename ELFT::uint SymbolBody::getGotPltTlsDescVA() const { + return Out::GotPlt->getVA() + + (GlobalDynIndex + GotPltIndex) * + sizeof(typename ELFFile::uintX_t); +} + template typename ELFT::uint SymbolBody::getPltVA() const { return Out::Plt->getVA() + Target->PltZeroSize + PltIndex * Target->PltEntrySize; @@ -345,6 +352,11 @@ template uint64_t SymbolBody::template getGotPltVA() const; template uint64_t SymbolBody::template getGotPltVA() 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,9 @@ 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 writeGotHeader(uint8_t *Buf) const {} virtual void writeGotPltHeader(uint8_t *Buf) const {} @@ -43,6 +46,10 @@ uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const {} + 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. @@ -93,10 +100,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 GotHeaderEntriesNum = 0; unsigned GotPltHeaderEntriesNum = 3; Index: ELF/Target.cpp =================================================================== --- ELF/Target.cpp +++ ELF/Target.cpp @@ -173,6 +173,7 @@ bool needsPltImpl(uint32_t Type) const override; void relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type, uint64_t P, uint64_t SA) const override; + size_t relaxTlsGdToLe(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type, uint64_t P, uint64_t SA) const override; size_t relaxTlsIeToLe(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type, @@ -255,7 +256,7 @@ // Global-Dynamic relocs can be relaxed to Initial-Exec or Local-Exec // depending on the symbol being locally defined or not. - if (isTlsGlobalDynamicRel(Type)) + if (isTlsDescRel(Type) || isTlsGlobalDynamicRel(Type)) return true; // Local-Dynamic relocs can be relaxed to Local-Exec. @@ -356,7 +357,7 @@ size_t TargetInfo::relaxTls(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type, uint64_t P, uint64_t SA, const SymbolBody &S) const { - if (isTlsGlobalDynamicRel(Type)) { + if (isTlsDescRel(Type) || isTlsGlobalDynamicRel(Type)) { if (S.isPreemptible()) return relaxTlsGdToIe(Loc, BufEnd, Type, P, SA); return relaxTlsGdToLe(Loc, BufEnd, Type, P, SA); Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -300,6 +300,31 @@ } return 2; } + + // 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. + // 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. + if (Target->isTlsDescRel(Type)) { + if (!Target->canRelaxTls(Type, &Body)) { + Out::Got->addTlsDescEntry(); + if (Body.isInGotPlt()) + return 1; + Out::GotPlt->addTlsDescEntry(Body); + Out::RelaPlt->addTlsDescReloc( + {Target->TlsDescRel, DynamicReloc::Off_GotPltTlsdesc, &Body}); + return 2; + } + } + return 0; }