Index: ELF/Arch/AArch64.cpp =================================================================== --- ELF/Arch/AArch64.cpp +++ ELF/Arch/AArch64.cpp @@ -74,6 +74,7 @@ // It doesn't seem to be documented anywhere, but tls on aarch64 uses variant // 1 of the tls structures and the tcb size is 16. + TlsLayout = TlsLayoutKind::FixedTcbSize; TcbSize = 16; NeedsThunks = true; } Index: ELF/Arch/ARM.cpp =================================================================== --- ELF/Arch/ARM.cpp +++ ELF/Arch/ARM.cpp @@ -63,6 +63,7 @@ PltHeaderSize = 32; TrapInstr = 0xd4d4d4d4; // ARM uses Variant 1 TLS + TlsLayout = TlsLayoutKind::FixedTcbSize; TcbSize = 8; NeedsThunks = true; } Index: ELF/Arch/PPC64.cpp =================================================================== --- ELF/Arch/PPC64.cpp +++ ELF/Arch/PPC64.cpp @@ -207,8 +207,8 @@ GotPltHeaderEntriesNum = 2; PltHeaderSize = 60; NeedsThunks = true; - TcbSize = 8; - TlsTpOffset = 0x7000; + TlsLayout = TlsLayoutKind::FixedOffset; + TlsTpOffset = -0x7000; TlsModuleIndexRel = R_PPC64_DTPMOD64; TlsOffsetRel = R_PPC64_DTPREL64; Index: ELF/Arch/X86.cpp =================================================================== --- ELF/Arch/X86.cpp +++ ELF/Arch/X86.cpp @@ -61,6 +61,7 @@ PltHeaderSize = 16; TlsGdRelaxSkip = 2; TrapInstr = 0xcccccccc; // 0xcc = INT3 + TlsLayout = TlsLayoutKind::Variant2; // Align to the non-PAE large page size (known as a superpage or huge page). // FreeBSD automatically promotes large, superpage-aligned allocations. Index: ELF/Arch/X86_64.cpp =================================================================== --- ELF/Arch/X86_64.cpp +++ ELF/Arch/X86_64.cpp @@ -68,6 +68,7 @@ PltHeaderSize = 16; TlsGdRelaxSkip = 2; TrapInstr = 0xcccccccc; // 0xcc = INT3 + TlsLayout = TlsLayoutKind::Variant2; // Align to the large page size (known as a superpage or huge page). // FreeBSD automatically promotes large, superpage-aligned allocations. Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ ELF/InputSection.h @@ -363,6 +363,8 @@ extern std::vector InputSections; Relocation *getRISCVPCRelHi20(const Symbol *Sym, const uint64_t Addend); + +int64_t getTlsTpOffset(); } // namespace elf std::string toString(const elf::InputSectionBase *); Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -566,6 +566,19 @@ return nullptr; } +int64_t lld::elf::getTlsTpOffset() { + // The TLS symbol's virtual address is relative to the TLS segment. Add a + // target-specific adjustment to produce a thread-pointer-relative offset. + switch (Target->TlsLayout) { + case TlsLayoutKind::FixedOffset: + return Target->TlsTpOffset; + case TlsLayoutKind::FixedTcbSize: + return alignTo(Target->TcbSize, Out::TlsPhdr->p_align); + case TlsLayoutKind::Variant2: + return -Out::TlsPhdr->p_memsz; + } +} + static uint64_t getRelocTargetVA(const InputFile *File, RelType Type, int64_t A, uint64_t P, const Symbol &Sym, RelExpr Expr) { switch (Expr) { @@ -711,24 +724,7 @@ // statically to zero. if (Sym.isTls() && Sym.isUndefWeak()) return 0; - - // For TLS variant 1 the TCB is a fixed size, whereas for TLS variant 2 the - // TCB is on unspecified size and content. Targets that implement variant 1 - // should set TcbSize. - if (Target->TcbSize) { - // PPC64 V2 ABI has the thread pointer offset into the middle of the TLS - // storage area by TlsTpOffset for efficient addressing TCB and up to - // 4KB – 8 B of other thread library information (placed before the TCB). - // Subtracting this offset will get the address of the first TLS block. - if (Target->TlsTpOffset) - return Sym.getVA(A) - Target->TlsTpOffset; - - // If thread pointer is not offset into the middle, the first thing in the - // TLS storage area is the TCB. Add the TcbSize to get the address of the - // first TLS block. - return Sym.getVA(A) + alignTo(Target->TcbSize, Out::TlsPhdr->p_align); - } - return Sym.getVA(A) - Out::TlsPhdr->p_memsz; + return Sym.getVA(A) + getTlsTpOffset(); case R_RELAX_TLS_GD_TO_LE_NEG: case R_NEG_TLS: return Out::TlsPhdr->p_memsz - Sym.getVA(A); Index: ELF/Target.h =================================================================== --- ELF/Target.h +++ ELF/Target.h @@ -23,6 +23,19 @@ class InputFile; class Symbol; +// To calculate LE-model TLS relocations, the linker must know where the +// executable's TLS segment is located relative to the thread pointer. +// Each target has its own method of determining this offset. +enum class TlsLayoutKind { + // The offset is fixed (e.g. -0x7000 for MIPS/PPC). + FixedOffset, + // The thread pointer points to a TCB with a fixed size, followed by a + // variable amount of alignment padding, followed by the TLS segment. + FixedTcbSize, + // The TLS segment is located just before the thread pointer. + Variant2, +}; + class TargetInfo { public: virtual uint32_t calcEFlags() const { return 0; } @@ -117,14 +130,14 @@ // On PPC ELF V2 abi, the first entry in the .got is the .TOC. unsigned GotHeaderEntriesNum = 0; - // For TLS variant 1, the TCB is a fixed size specified by the Target. - // For variant 2, the TCB is an unspecified size. - // Set to 0 for variant 2. - unsigned TcbSize = 0; + TlsLayoutKind TlsLayout = TlsLayoutKind::FixedOffset; - // Set to the offset (in bytes) that the thread pointer is initialized to - // point to, relative to the start of the thread local storage. - unsigned TlsTpOffset = 0; + // For TlsLayoutKind::FixedOffset, the offset (in bytes) from the thread + // pointer to the start of the thread local storage. + int TlsTpOffset = 0; + + // For TlsLayoutKind::FixedTcbSize, the size of the TCB in bytes. + unsigned TcbSize = 0; bool NeedsThunks = false;