Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -269,6 +269,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; + } + uintX_t SymVA = Body.getVA(A); uint8_t *PairedLoc = nullptr; if (Config->EMachine == EM_MIPS) Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -101,6 +101,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); @@ -119,10 +120,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; llvm::DenseMap MipsLocalGotPos; @@ -138,9 +142,11 @@ void finalize() override; void writeTo(uint8_t *Buf) override; void addEntry(SymbolBody &Sym); + void addTlsDescEntry(SymbolBody &Sym); bool empty() const; private: + size_t Tail = 0; std::vector Entries; }; @@ -153,7 +159,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; @@ -167,6 +174,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. @@ -240,6 +248,7 @@ 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; @@ -248,6 +257,7 @@ bool Static = false; private: + size_t Tail = 0; std::vector> Relocs; }; @@ -416,7 +426,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 { @@ -424,12 +436,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 @@ -63,6 +63,17 @@ Entries.push_back(&Sym); } +template +void GotPltSection::addTlsDescEntry(SymbolBody &Sym) { + Sym.GlobalDynIndex = Target->GotPltHeaderEntriesNum + Entries.size() - Tail; + Entries.push_back(nullptr); + Entries.push_back(nullptr); + // Some relocations are placed after all others ones. + // GotPlt records for them also follows that rule. + Sym.GotPltIndex = Tail; + Tail += 2; +} + template bool GotPltSection::empty() const { return Entries.empty(); } @@ -76,7 +87,8 @@ Target->writeGotPltHeader(Buf); Buf += Target->GotPltHeaderEntriesNum * sizeof(uintX_t); for (const SymbolBody *B : Entries) { - Target->writeGotPlt(Buf, B->getPltVA()); + if (B) // This might be TLS descriptor entry. + Target->writeGotPlt(Buf, B->getPltVA()); Buf += sizeof(uintX_t); } } @@ -140,6 +152,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() { @@ -244,6 +263,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) { @@ -254,9 +282,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 @@ -267,12 +307,26 @@ 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; +} + +template +void RelocationSection::addReloc(const DynamicReloc &Reloc) { + checkReloc(Reloc); + // Some relocations should be added after all others. + // Like R_*_TLSDESC must go after R_*_JUMP_SLOT ones. + // Tail here is amount of such special relocations. + Relocs.insert(Relocs.end() - Tail, Reloc); +} + +template +void RelocationSection::addTlsDescReloc(const DynamicReloc &Reloc) { + checkReloc(Reloc); Relocs.push_back(Reloc); + ++Tail; } template @@ -292,6 +346,8 @@ return Sym->getGotVA(); case Off_GotPlt: return Sym->getGotPltVA(); + case Off_GotPltTlsdesc: + return Sym->getGotPltTlsDescVA(); } llvm_unreachable("invalid offset kind"); } @@ -587,6 +643,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}); @@ -656,6 +717,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); @@ -671,6 +746,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 @@ -87,6 +87,7 @@ uint32_t PltIndex = -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; } template @@ -94,6 +95,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 getSize() const; Index: ELF/Symbols.cpp =================================================================== --- ELF/Symbols.cpp +++ ELF/Symbols.cpp @@ -128,6 +128,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; @@ -288,6 +295,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 @@ -27,6 +27,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(unsigned 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 {} @@ -41,6 +44,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. @@ -56,7 +63,7 @@ virtual bool isSizeRel(uint32_t Type) const; virtual bool needsDynRelative(uint32_t Type) const { return false; } virtual bool needsGot(uint32_t Type, SymbolBody &S) const; - virtual bool refersToGotEntry(uint32_t Type) const; + virtual bool refersToGotEntry(uint32_t Type, const SymbolBody &S) const; enum PltNeed { Plt_No, Plt_Explicit, Plt_Implicit }; PltNeed needsPlt(uint32_t Type, const SymbolBody &S) const; @@ -87,10 +94,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 @@ -104,7 +104,7 @@ uint64_t P, uint64_t SA) const override; bool isGotRelative(uint32_t Type) const override; - bool refersToGotEntry(uint32_t Type) const override; + bool refersToGotEntry(uint32_t Type, const SymbolBody &S) const override; }; class X86_64TargetInfo final : public TargetInfo { @@ -122,7 +122,7 @@ int32_t Index, unsigned RelOff) const override; bool needsCopyRelImpl(uint32_t Type) const override; bool needsGot(uint32_t Type, SymbolBody &S) const override; - bool refersToGotEntry(uint32_t Type) const override; + bool refersToGotEntry(uint32_t Type, const SymbolBody &S) const override; bool needsPltImpl(uint32_t Type) const override; void relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type, uint64_t P, uint64_t SA, uint64_t ZA = 0, @@ -166,12 +166,15 @@ public: AArch64TargetInfo(); uint32_t getDynRel(uint32_t Type) const override; - bool isTlsGlobalDynamicRel(uint32_t Type) const override; + bool isTlsDescRel(unsigned Type) const override; bool isTlsInitialExecRel(uint32_t Type) const override; void writeGotPlt(uint8_t *Buf, uint64_t Plt) const override; void writePltZero(uint8_t *Buf) const override; void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; + void writePltTlsDescEntry(uint8_t *Buf, uint64_t PltEntryAddr, + uint64_t GotTlsDescEntryAddr, + uint64_t GotPltVA) const override; uint32_t getTlsGotRel(uint32_t Type) const override; bool isRelRelative(uint32_t Type) const override; bool needsCopyRelImpl(uint32_t Type) const override; @@ -180,6 +183,7 @@ void relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type, uint64_t P, uint64_t SA, uint64_t ZA = 0, uint8_t *PairedLoc = nullptr) const override; + bool refersToGotEntry(uint32_t Type, const SymbolBody &) const override; size_t relaxTlsGdToLe(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type, uint64_t P, uint64_t SA) const override; @@ -215,7 +219,7 @@ uint8_t *PairedLoc = nullptr) const override; bool isHintRel(uint32_t Type) const override; bool isRelRelative(uint32_t Type) const override; - bool refersToGotEntry(uint32_t Type) const override; + bool refersToGotEntry(uint32_t Type, const SymbolBody &) const override; }; } // anonymous namespace @@ -256,7 +260,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. @@ -298,7 +302,9 @@ bool TargetInfo::needsPltImpl(uint32_t Type) const { return false; } -bool TargetInfo::refersToGotEntry(uint32_t Type) const { return false; } +bool TargetInfo::refersToGotEntry(uint32_t Type, const SymbolBody&) const { + return false; +} TargetInfo::PltNeed TargetInfo::needsPlt(uint32_t Type, const SymbolBody &S) const { @@ -329,7 +335,7 @@ // plt. That is identified by special relocation types (R_X86_64_JUMP_SLOT, // R_386_JMP_SLOT, etc). if (S.isShared()) - if (!Config->Pic && S.IsFunc && !refersToGotEntry(Type)) + if (!Config->Pic && S.IsFunc && !refersToGotEntry(Type, S)) return Plt_Implicit; return Plt_No; @@ -350,7 +356,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); @@ -512,7 +518,7 @@ return Type == R_386_GOTOFF; } -bool X86TargetInfo::refersToGotEntry(uint32_t Type) const { +bool X86TargetInfo::refersToGotEntry(uint32_t Type, const SymbolBody &S) const { return Type == R_386_GOT32; } @@ -745,7 +751,8 @@ Type == R_X86_64_64; } -bool X86_64TargetInfo::refersToGotEntry(uint32_t Type) const { +bool X86_64TargetInfo::refersToGotEntry(uint32_t Type, + const SymbolBody &S) const { return Type == R_X86_64_GOTPCREL || Type == R_X86_64_GOTPCRELX || Type == R_X86_64_REX_GOTPCRELX; } @@ -755,7 +762,7 @@ return Target->canRelaxTls(Type, &S) && S.isPreemptible(); if (Type == R_X86_64_GOTTPOFF) return !canRelaxTls(Type, &S); - return refersToGotEntry(Type) || needsPlt(Type, S); + return refersToGotEntry(Type, S) || needsPlt(Type, S); } uint32_t X86_64TargetInfo::getTlsGotRel(uint32_t Type) const { @@ -1214,12 +1221,21 @@ IRelativeRel = R_AARCH64_IRELATIVE; GotRel = R_AARCH64_GLOB_DAT; PltRel = R_AARCH64_JUMP_SLOT; + TlsDescRel = R_AARCH64_TLSDESC; TlsGotRel = R_AARCH64_TLS_TPREL64; TlsModuleIndexRel = R_AARCH64_TLS_DTPMOD64; TlsOffsetRel = R_AARCH64_TLS_DTPREL64; UseLazyBinding = true; PltEntrySize = 16; PltZeroSize = 32; + PltTlsDescSize = 32; +} + +bool AArch64TargetInfo::isTlsDescRel(unsigned Type) const { + return Type == R_AARCH64_TLSDESC_ADR_PAGE21 || + Type == R_AARCH64_TLSDESC_LD64_LO12_NC || + Type == R_AARCH64_TLSDESC_ADD_LO12_NC || + Type == R_AARCH64_TLSDESC_CALL; } bool AArch64TargetInfo::isRelRelative(uint32_t Type) const { @@ -1244,19 +1260,17 @@ } } -bool AArch64TargetInfo::isTlsGlobalDynamicRel(uint32_t Type) const { - return Type == R_AARCH64_TLSDESC_ADR_PAGE21 || - Type == R_AARCH64_TLSDESC_LD64_LO12_NC || - Type == R_AARCH64_TLSDESC_ADD_LO12_NC || - Type == R_AARCH64_TLSDESC_CALL; -} - bool AArch64TargetInfo::isTlsInitialExecRel(uint32_t Type) const { return Type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 || Type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC; } uint32_t AArch64TargetInfo::getDynRel(uint32_t Type) const { + if (Type == R_AARCH64_TLSDESC_ADR_PAGE21 || + Type == R_AARCH64_TLSDESC_LD64_LO12_NC || + Type == R_AARCH64_TLSDESC_ADD_LO12_NC || + Type == R_AARCH64_TLSDESC_CALL) + return R_AARCH64_TLSDESC; if (Type == R_AARCH64_ABS32 || Type == R_AARCH64_ABS64) return Type; StringRef S = getELFRelocationTypeName(EM_AARCH64, Type); @@ -1311,6 +1325,31 @@ GotEntryAddr); } +void AArch64TargetInfo::writePltTlsDescEntry(uint8_t *Buf, + uint64_t PltEntryAddr, + uint64_t GotTlsDescEntryAddr, + uint64_t GotPltVA) const { + const uint8_t Inst[] = { + 0xe2, 0x0f, 0xbf, 0xa9, // stp x2, x3, [sp, #-16]! + 0x02, 0x00, 0x00, 0x90, // adrp x2, Page(DT_TLSDESC_GOT) + 0x03, 0x00, 0x00, 0x90, // adrp x3, Page(&.got.plt[0]) + 0x42, 0x00, 0x40, 0xf9, // ldr x2, [x2, #0] + 0x63, 0x00, 0x00, 0x91, // add x3, x3, 0 + 0x40, 0x00, 0x1f, 0xd6, // br x2 + 0x1f, 0x20, 0x03, 0xd5, // nop + 0x1f, 0x20, 0x03, 0xd5 // nop + }; + memcpy(Buf, Inst, sizeof(Inst)); + relocateOne(Buf + 4, Buf + 8, R_AARCH64_ADR_PREL_PG_HI21, PltEntryAddr + 4, + GotTlsDescEntryAddr); + relocateOne(Buf + 8, Buf + 12, R_AARCH64_ADR_PREL_PG_HI21, PltEntryAddr + 8, + GotPltVA); + relocateOne(Buf + 12, Buf + 16, R_AARCH64_LDST64_ABS_LO12_NC, + PltEntryAddr + 12, GotTlsDescEntryAddr); + relocateOne(Buf + 16, Buf + 20, R_AARCH64_ADD_ABS_LO12_NC, PltEntryAddr + 16, + GotPltVA); +} + uint32_t AArch64TargetInfo::getTlsGotRel(uint32_t Type) const { assert(Type == R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 || Type == R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC); @@ -1336,7 +1375,8 @@ } } -bool AArch64TargetInfo::needsGot(uint32_t Type, SymbolBody &S) const { +bool AArch64TargetInfo::refersToGotEntry(uint32_t Type, + const SymbolBody &S) const { switch (Type) { case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: @@ -1345,10 +1385,14 @@ case R_AARCH64_LD64_GOT_LO12_NC: return true; default: - return needsPlt(Type, S); + return false; } } +bool AArch64TargetInfo::needsGot(uint32_t Type, SymbolBody &S) const { + return refersToGotEntry(Type, S) || needsPlt(Type, S); +} + bool AArch64TargetInfo::needsPltImpl(uint32_t Type) const { switch (Type) { default: @@ -1414,7 +1458,8 @@ break; } case R_AARCH64_ADR_PREL_PG_HI21: - case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: { + case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: + case R_AARCH64_TLSDESC_ADR_PAGE21: { uint64_t X = getAArch64Page(SA) - getAArch64Page(P); checkInt<33>(X, Type); updateAArch64Addr(Loc, (X >> 12) & 0x1FFFFF); // X[32:12] @@ -1435,6 +1480,7 @@ } case R_AARCH64_LD64_GOT_LO12_NC: case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: + case R_AARCH64_TLSDESC_LD64_LO12_NC: checkAlignment<8>(SA, Type); or32le(Loc, (SA & 0xFF8) << 7); break; @@ -1445,6 +1491,7 @@ or32le(Loc, (SA & 0x0FFC) << 9); break; case R_AARCH64_LDST8_ABS_LO12_NC: + case R_AARCH64_TLSDESC_ADD_LO12_NC: or32le(Loc, (SA & 0xFFF) << 10); break; case R_AARCH64_LDST32_ABS_LO12_NC: @@ -1481,6 +1528,11 @@ updateAArch64Add(Loc, V & 0xFFF); break; } + case R_AARCH64_TLSDESC_CALL: + // For relaxation only. Must be used to identify a + // BLR instruction which performs an indirect call + // to the TLS descriptor function for S + A. + break; default: fatal("unrecognized reloc " + Twine(Type)); } @@ -1499,6 +1551,10 @@ // movk x0, #0x10 // nop // nop + + // AArch64 uses Variant I of TLS data structures. Thread Control Block is + // placed before data. Read "ELF Handling For Thread-Local Storage, 3. Run + // Time Handling of TLS" by Ulrich Drepper for details. uint64_t TPOff = llvm::alignTo(TcbSize, Out::TlsPhdr->p_align); uint64_t X = SA + TPOff; checkUInt<32>(X, Type); @@ -1686,11 +1742,12 @@ template bool MipsTargetInfo::needsGot(uint32_t Type, SymbolBody &S) const { - return needsPlt(Type, S) || refersToGotEntry(Type); + return needsPlt(Type, S) || refersToGotEntry(Type, S); } template -bool MipsTargetInfo::refersToGotEntry(uint32_t Type) const { +bool MipsTargetInfo::refersToGotEntry(uint32_t Type, + const SymbolBody &S) const { return Type == R_MIPS_GOT16 || Type == R_MIPS_CALL16; } Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -293,6 +293,31 @@ {Target->TlsGotRel, DynamicReloc::Off_Got, false, &Body}); 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; } Index: test/ELF/aarch64-tls-desc.s =================================================================== --- /dev/null +++ test/ELF/aarch64-tls-desc.s @@ -0,0 +1,164 @@ +# RUN: llvm-mc -filetype=obj -triple=aarch64-unknown-freebsd %s -o %tmain.o +# RUN: ld.lld %tmain.o -shared -o %tout +# RUN: llvm-objdump -d %tout | FileCheck %s +# RUN: llvm-readobj -s -r -dynamic-table %tout | FileCheck -check-prefix=READOBJ %s +# REQUIRES: aarch64 + +#READOBJ: Section { +#READOBJ: Index: +#READOBJ: Name: .plt +#READOBJ-NEXT: Type: SHT_PROGBITS +#READOBJ-NEXT: Flags [ +#READOBJ-NEXT: SHF_ALLOC +#READOBJ-NEXT: SHF_EXECINSTR +#READOBJ-NEXT: ] +#READOBJ-NEXT: Address: 0x1030 +#READOBJ-NEXT: Offset: +#READOBJ-NEXT: Size: 80 +#READOBJ-NEXT: Link: +#READOBJ-NEXT: Info: +#READOBJ-NEXT: AddressAlignment: +#READOBJ-NEXT: EntrySize: +#READOBJ-NEXT: } +#READOBJ: Section { +#READOBJ: Index: +#READOBJ: Name: .got +#READOBJ-NEXT: Type: SHT_PROGBITS +#READOBJ-NEXT: Flags [ +#READOBJ-NEXT: SHF_ALLOC +#READOBJ-NEXT: SHF_WRITE +#READOBJ-NEXT: ] +#READOBJ-NEXT: Address: 0x20C8 +#READOBJ-NEXT: Offset: +#READOBJ-NEXT: Size: 8 +#READOBJ-NEXT: Link: +#READOBJ-NEXT: Info: +#READOBJ-NEXT: AddressAlignment: +#READOBJ-NEXT: EntrySize: +#READOBJ-NEXT: } +#READOBJ: Section { +#READOBJ: Index: +#READOBJ: Name: .got.plt +#READOBJ-NEXT: Type: SHT_PROGBITS +#READOBJ-NEXT: Flags [ +#READOBJ-NEXT: SHF_ALLOC +#READOBJ-NEXT: SHF_WRITE +#READOBJ-NEXT: ] +#READOBJ-NEXT: Address: 0x3000 +#READOBJ-NEXT: Offset: 0x3000 +#READOBJ-NEXT: Size: 64 +#READOBJ-NEXT: Link: 0 +#READOBJ-NEXT: Info: 0 +#READOBJ-NEXT: AddressAlignment: 8 +#READOBJ-NEXT: EntrySize: 0 +#READOBJ-NEXT: } +#READOBJ: Relocations [ +#READOBJ-NEXT: Section ({{.*}}) .rela.plt { +## This also checks that SLOT relocations are placed vefore TLSDESC +## 0x3018 = .got.plt + 0x18 (reserved 3 entries) +## 0x3020 = next entry (first was single, 8 bytes) +## 0x3030 = 0x3018 + double entry size (16) +#READOBJ-NEXT: 0x3018 R_AARCH64_JUMP_SLOT slot 0x0 +#READOBJ-NEXT: 0x3020 R_AARCH64_TLSDESC foo 0x0 +#READOBJ-NEXT: 0x3030 R_AARCH64_TLSDESC bar 0x0 +#READOBJ-NEXT: } +#READOBJ-NEXT:] +#READOBJ: DynamicSection [ +#READOBJ-NEXT: Tag Type Name/Value +#READOBJ-NEXT: 0x0000000000000017 JMPREL 0x2C0 +#READOBJ-NEXT: 0x0000000000000002 PLTRELSZ 72 (bytes) +#READOBJ-NEXT: 0x0000000000000003 PLTGOT 0x3000 +#READOBJ-NEXT: 0x0000000000000014 PLTREL RELA +## 0x20A8 = Location of GOT entry used by TLS descriptor resolver PLT entry +#READOBJ-NEXT: 0x000000006FFFFEF7 TLSDESC_GOT 0x20C8 +## 0x1040 = Location of PLT entry for TLS descriptor resolver calls. +#READOBJ-NEXT: 0x000000006FFFFEF6 TLSDESC_PLT 0x1060 +#READOBJ-NEXT: 0x0000000000000006 SYMTAB 0x200 +#READOBJ-NEXT: 0x000000000000000B SYMENT 24 (bytes) +#READOBJ-NEXT: 0x0000000000000005 STRTAB 0x2A8 +#READOBJ-NEXT: 0x000000000000000A STRSZ 21 (bytes) +#READOBJ-NEXT: 0x0000000000000004 HASH 0x278 +#READOBJ-NEXT: 0x0000000000000000 NULL 0x0 +#READOBJ-NEXT: ] + +#CHECK: Disassembly of section .text: +#CHECK-NEXT: _start: +#CHECK-NEXT: 1000: 80 02 00 54 b.eq #80 +#CHECK-NEXT: 1004: 00 00 00 d0 adrp x0, #8192 +## Page(.got.plt[N]) - Page(0x1000) = Page(0x3020) - 0x1000 = +## 0x3000 - 0x1000 = 0x2000 = 8192 +## 0x20 = 32 +#CHECK-NEXT: 1008: 02 10 40 f9 ldr x2, [x0, #32] +#CHECK-NEXT: 100c: 00 80 00 91 add x0, x0, #32 +#CHECK-NEXT: 1010: 40 00 3f d6 blr x2 +## Page(.got.plt[N]) - Page(0x1000) = Page(0x3030) - 0x1000 = +## 0x3000 - 0x1000 = 0x2000 = 8192 +## 0x30 = 48 +#CHECK-NEXT: 1014: 00 00 00 d0 adrp x0, #8192 +#CHECK-NEXT: 1018: 02 18 40 f9 ldr x2, [x0, #48] +#CHECK-NEXT: 101c: 00 c0 00 91 add x0, x0, #48 +#CHECK-NEXT: 1020: 40 00 3f d6 blr x2 +#CHECK-NEXT: Disassembly of section .plt: +#CHECK-NEXT: .plt: +#CHECK-NEXT: 1030: f0 7b bf a9 stp x16, x30, [sp, #-16]! +#CHECK-NEXT: 1034: 10 00 00 d0 adrp x16, #8192 +#CHECK-NEXT: 1038: 11 0a 40 f9 ldr x17, [x16, #16] +#CHECK-NEXT: 103c: 10 42 00 91 add x16, x16, #16 +#CHECK-NEXT: 1040: 20 02 1f d6 br x17 +#CHECK-NEXT: 1044: 1f 20 03 d5 nop +#CHECK-NEXT: 1048: 1f 20 03 d5 nop +#CHECK-NEXT: 104c: 1f 20 03 d5 nop +#CHECK-NEXT: 1050: 10 00 00 d0 adrp x16, #8192 +#CHECK-NEXT: 1054: 11 0e 40 f9 ldr x17, [x16, #24] +#CHECK-NEXT: 1058: 10 62 00 91 add x16, x16, #24 +#CHECK-NEXT: 105c: 20 02 1f d6 br x17 +## Page(.got[N]) - Page(P) = Page(0x20C8) - Page(0x1044) = +## 0x2000 - 0x1000 = 4096 +## Page(.got.plt) - Page(P) = Page(0x3000) - Page(0x1048) = +## 0x3000 - 0x1000 = 8192 +## 0xC8 = 200 +## 0x0 = 0 +#CHECK-NEXT: 1060: {{.*}} stp x2, x3, [sp, #-16]! +#CHECK-NEXT: 1064: {{.*}} adrp x2, #4096 +#CHECK-NEXT: 1068: {{.*}} adrp x3, #8192 +#CHECK-NEXT: 106c: {{.*}} ldr x2, [x2, #200] +#CHECK-NEXT: 1070: {{.*}} add x3, x3, #0 +#CHECK-NEXT: 1074: {{.*}} br x2 +#CHECK-NEXT: 1078: {{.*}} nop +#CHECK-NEXT: 107c: {{.*}} nop + +.text + .global foo + .section .tdata,"awT",%progbits + .align 2 + .type foo, %object + .size foo, 4 +foo: + .word 5 + .text + +.text + .global bar + .section .tdata,"awT",%progbits + .align 2 + .type bar, %object + .size bar, 4 +bar: + .word 5 + .text + +.globl _start +_start: + b.eq slot + + adrp x0, :tlsdesc:foo + ldr x2, [x0, #:tlsdesc_lo12:foo] + add x0, x0, :tlsdesc_lo12:foo + .tlsdesccall foo + blr x2 + + adrp x0, :tlsdesc:bar + ldr x2, [x0, #:tlsdesc_lo12:bar] + add x0, x0, :tlsdesc_lo12:bar + .tlsdesccall bar + blr x2