Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -89,6 +89,12 @@ } else if (Target->relocPointsToGot(Type)) { SymVA = Out::Got->getVA(); Type = Target->getPCRelReloc(); + } else if (Body.isTLS()) { + const Elf_Sym &Sym = cast>(Body).Sym; + InputSectionBase *Section = File.getSection(Sym); + uintX_t SectionVA = Section->OutSec->getVA(); + SymVA = SectionVA - Out::TLS->p_paddr - Out::TLS->p_memsz + + Section->getOffset(Sym); } else if (isa>(Body)) { continue; } Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -327,6 +327,7 @@ static StringTableSection *StrTab; static SymbolTableSection *DynSymTab; static SymbolTableSection *SymTab; + static typename llvm::object::ELFFile::Elf_Phdr *TLS; }; template DynamicSection *Out::Dynamic; @@ -345,6 +346,7 @@ template StringTableSection *Out::StrTab; template SymbolTableSection *Out::DynSymTab; template SymbolTableSection *Out::SymTab; +template typename llvm::object::ELFFile::Elf_Phdr *Out::TLS; } } #endif Index: ELF/Target.cpp =================================================================== --- ELF/Target.cpp +++ ELF/Target.cpp @@ -343,6 +343,10 @@ write32le(Loc, SA); break; } + case R_X86_64_TPOFF32: + case R_X86_64_DTPOFF32: + write32le(Loc, SA); + break; default: error("unrecognized reloc " + Twine(Type)); } Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -609,16 +609,37 @@ if (isOutputDynamic()) ++NumPhdrs; uintX_t Last = PF_R; + + bool HasTbss = false; + bool HasTdata = false; + bool InTls = false; for (OutputSectionBase *Sec : OutputSections) { if (!Sec->getSize() || !needsPhdr(Sec)) continue; + + bool EndPhdr = false; + if (Sec->getFlags() & SHF_TLS) { + if (Sec->getType() == SHT_PROGBITS) + HasTdata = true; + else + HasTbss = true; + EndPhdr = true; + InTls = true; + } else if (InTls) { + EndPhdr = true; + InTls = false; + } + uintX_t Flags = toPhdrFlags(Sec->getFlags()); - if (Last != Flags) { + if (EndPhdr || Last != Flags) { Last = Flags; ++NumPhdrs; } } + if (!HasTbss && HasTdata) + ++NumPhdrs; + // Reserve space needed for the program header so that the array // will never be resized. Phdrs.reserve(NumPhdrs); @@ -642,24 +663,74 @@ Elf_Phdr *FileHeader = &Phdrs.back(); setPhdr(FileHeader, PT_LOAD, PF_R, 0, getVAStart(), Target->getPageSize()); + InTls = false; + bool EmitTlsSec = false; + uintX_t TlsAlign = 0; + Elf_Phdr *TlsBegin = nullptr; SmallPtrSet Closed; for (OutputSectionBase *Sec : OutputSections) { if (Sec->getSize()) { + + bool EndPhdr = false; + // When we have no .tdata/.tbss then no special TLS headers required. + // If we have only .tdata then 2 headers will be created: LOAD (.tdata) + // and TLS (emited). + // If we have only .tbss then its converted to TLS. + // For case when we have both: LOAD is created (from .tdata), .tbss is + // converted to TLS. + if (Sec->getFlags() & SHF_TLS) { + if (!HasTbss && HasTdata) + EmitTlsSec = true; + EndPhdr = true; + InTls = true; + } else if (InTls) { + EndPhdr = true; + InTls = false; + } + uintX_t Flags = toPhdrFlags(Sec->getFlags()); Elf_Phdr *Last = &Phdrs.back(); - if (Last->p_flags != Flags || !needsPhdr(Sec)) { + if (EndPhdr || Last->p_flags != Flags || !needsPhdr(Sec)) { // Flags changed. End current Phdr and potentially create a new one. if (Closed.insert(Last).second) { Last->p_filesz = FileOff - Last->p_offset; Last->p_memsz = VA - Last->p_vaddr; + + // We closed the .tdata header and need to emit PT_TLS now + if (EmitTlsSec && !InTls) { + EmitTlsSec = false; + assert(Phdrs.capacity() >= Phdrs.size() + 1); + Phdrs.emplace_back(); + Elf_Phdr *PH = &Phdrs.back(); + setPhdr(PH, PT_TLS, PF_R, TlsBegin->p_offset, TlsBegin->p_vaddr, TlsAlign); + PH->p_filesz = Last->p_filesz; + PH->p_memsz = Last->p_memsz; + Out::TLS = PH; + Closed.insert(PH); + } } if (needsPhdr(Sec)) { - VA = RoundUpToAlignment(VA, Target->getPageSize()); - FileOff = RoundUpToAlignment(FileOff, Target->getPageSize()); + uint64_t Align = Target->getPageSize(); + if (InTls && TlsBegin) + Align = Sec->getAlign(); + VA = RoundUpToAlignment(VA, Align); + FileOff = RoundUpToAlignment(FileOff, Align); + assert(Phdrs.capacity() >= Phdrs.size() + 1); Phdrs.emplace_back(); Elf_Phdr *PH = &Phdrs.back(); - setPhdr(PH, PT_LOAD, Flags, FileOff, VA, Target->getPageSize()); + if (InTls && (Sec->getType() != SHT_PROGBITS)) { + setPhdr(PH, PT_TLS, PF_R, TlsBegin ? TlsBegin->p_offset : FileOff, + TlsBegin ? TlsBegin->p_vaddr : VA, Sec->getAlign()); + Out::TLS = PH; + } else { + setPhdr(PH, PT_LOAD, Flags, FileOff, VA, Align); + } + + if (InTls && !TlsBegin) { + TlsBegin = PH; + TlsAlign = Sec->getAlign(); + } } } } Index: test/elf2/tls-tbss-tdata.s =================================================================== --- test/elf2/tls-tbss-tdata.s +++ test/elf2/tls-tbss-tdata.s @@ -0,0 +1,107 @@ +// REQUIRES: x86 +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t +// RUN: ld.lld2 %t -e main -o %t2 +// RUN: llvm-readobj -s %t2 | FileCheck --check-prefix=SECTION %s +// RUN: llvm-readobj -program-headers %t2 | FileCheck --check-prefix=HEADER %s +// RUN: llvm-objdump -d %t2 | FileCheck --check-prefix=RELOCS %s + +.globl TDATA1 +.section .tdata,"awT",@progbits +.align 4 +.type TDATA1, @object +.size TDATA1, 4 +TDATA1: +.long 10 +.globl TDATA2 +.align 4 +.type TDATA2, @object +.size TDATA2, 4 +TDATA2: +.long 20 +.globl TBSS1 +.section .tbss,"awT",@nobits +.align 4 +.type TBSS1, @object +.size TBSS1, 4 +TBSS1: +.zero 4 +.globl TBSS2 +.align 4 +.type TBSS2, @object +.size TBSS2, 4 +TBSS2: +.zero 4 +.text +.globl main +.type main, @function +main: +.LFB0: +movl $1, %fs:TDATA1@tpoff +movl $1, %fs:TDATA2@tpoff +movl $1, %fs:TBSS1@tpoff +movl $1, %fs:TBSS2@tpoff + +// SECTION: Name: .tdata +// SECTION-NEXT: Type: SHT_PROGBITS +// SECTION-NEXT: Flags [ +// SECTION-NEXT: SHF_ALLOC +// SECTION-NEXT: SHF_TLS +// SECTION-NEXT: SHF_WRITE +// SECTION-NEXT: ] +// SECTION-NEXT: Address: 0x12000 +// SECTION-NEXT: Offset: 0x2000 +// SECTION-NEXT: Size: 8 +// SECTION-NEXT: Link: 0 +// SECTION-NEXT: Info: 0 +// SECTION-NEXT: AddressAlignment: 4 +// SECTION-NEXT: EntrySize: 0 +// SECTION-NEXT:} +// SECTION-NEXT:Section { +// SECTION-NEXT: Index: 3 +// SECTION-NEXT: Name: .tbss +// SECTION-NEXT: Type: SHT_NOBITS +// SECTION-NEXT: Flags [ +// SECTION-NEXT: SHF_ALLOC +// SECTION-NEXT: SHF_TLS +// SECTION-NEXT: SHF_WRITE +// SECTION-NEXT: ] +// SECTION-NEXT: Address: 0x12008 +// SECTION-NEXT: Offset: 0x2008 +// SECTION-NEXT: Size: 8 +// SECTION-NEXT: Link: 0 +// SECTION-NEXT: Info: 0 +// SECTION-NEXT: AddressAlignment: 4 +// SECTION-NEXT: EntrySize: 0 + +// HEADER: ProgramHeaders [ +// HEADER: Type: PT_LOAD +// HEADER: Offset: 0x2000 +// HEADER-NEXT: VirtualAddress: 0x12000 +// HEADER-NEXT: PhysicalAddress: 0x12000 +// HEADER-NEXT: FileSize: 8 +// HEADER-NEXT: MemSize: 8 +// HEADER-NEXT: Flags [ +// HEADER-NEXT: PF_R +// HEADER-NEXT: PF_W +// HEADER-NEXT: ] +// HEADER-NEXT: Alignment: 4096 +// HEADER-NEXT: } +// HEADER-NEXT: ProgramHeader { +// HEADER-NEXT: Type: PT_TLS +// HEADER-NEXT: Offset: 0x2000 +// HEADER-NEXT: VirtualAddress: 0x12000 +// HEADER-NEXT: PhysicalAddress: 0x12000 +// HEADER-NEXT: FileSize: 8 +// HEADER-NEXT: MemSize: 16 +// HEADER-NEXT: Flags [ +// HEADER-NEXT: PF_R (0x4) +// HEADER-NEXT: ] +// HEADER-NEXT: Alignment: 4 +// HEADER-NEXT: } + +// RELOCS: Disassembly of section .text: +// RELOCS-NEXT: main: +// RELOCS-NEXT: 11000: 64 c7 04 25 f0 ff ff ff 01 00 00 00 movl $1, %fs:-16 +// RELOCS-NEXT: 1100c: 64 c7 04 25 f4 ff ff ff 01 00 00 00 movl $1, %fs:-12 +// RELOCS-NEXT: 11018: 64 c7 04 25 f8 ff ff ff 01 00 00 00 movl $1, %fs:-8 +// RELOCS-NEXT: 11024: 64 c7 04 25 fc ff ff ff 01 00 00 00 movl $1, %fs:-4 Index: test/elf2/tls-tbss.s =================================================================== --- test/elf2/tls-tbss.s +++ test/elf2/tls-tbss.s @@ -0,0 +1,59 @@ +// REQUIRES: x86 +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t +// RUN: ld.lld2 %t -e main -o %t2 +// RUN: llvm-readobj -s %t2 | FileCheck --check-prefix=SECTION %s +// RUN: llvm-readobj -program-headers %t2 | FileCheck --check-prefix=HEADER %s +// RUN: llvm-objdump -d %t2 | FileCheck --check-prefix=RELOCS %s + +.globl BSS1 +.section .tbss,"awT",@nobits +.align 4 +.type BSS1, @object +.size BSS1, 4 +BSS1: +.zero 4 +.globl BSS2 +.align 4 +.type BSS2, @object +.size BSS2, 4 +BSS2: +.zero 4 +.text +.globl main +.type main, @function + +main: + movl $1, %fs:BSS1@tpoff + movl $1, %fs:BSS2@tpoff + +// SECTION: Name: .tbss +// SECTION-NEXT: Type: SHT_NOBITS +// SECTION-NEXT: Flags [ +// SECTION-NEXT: SHF_ALLOC +// SECTION-NEXT: SHF_TLS +// SECTION-NEXT: SHF_WRITE +// SECTION-NEXT: ] +// SECTION-NEXT: Address: 0x12000 +// SECTION-NEXT: Offset: 0x2000 +// SECTION-NEXT: Size: 8 +// SECTION-NEXT: Link: 0 +// SECTION-NEXT: Info: 0 +// SECTION-NEXT: AddressAlignment: 4 +// SECTION-NEXT: EntrySize: 0 + +// HEADER: ProgramHeaders [ +// HEADER: Type: PT_TLS +// HEADER-NEXT: Offset: 0x2000 +// HEADER-NEXT: VirtualAddress: 0x12000 +// HEADER-NEXT: PhysicalAddress: 0x12000 +// HEADER-NEXT: FileSize: 0 +// HEADER-NEXT: MemSize: 8 +// HEADER-NEXT: Flags [ +// HEADER-NEXT: PF_R +// HEADER-NEXT: ] +// HEADER-NEXT: Alignment: 4 + +// RELOCS: Disassembly of section .text: +// RELOCS-NEXT: main: +// RELOCS-NEXT: 11000: 64 c7 04 25 f8 ff ff ff 01 00 00 00 movl $1, %fs:-8 +// RELOCS-NEXT: 1100c: 64 c7 04 25 fc ff ff ff 01 00 00 00 movl $1, %fs:-4 Index: test/elf2/tls-tdata.s =================================================================== --- test/elf2/tls-tdata.s +++ test/elf2/tls-tdata.s @@ -0,0 +1,73 @@ +// REQUIRES: x86 +// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t +// RUN: ld.lld2 %t -e main -o %t2 +// RUN: llvm-readobj -s %t2 | FileCheck --check-prefix=SECTION %s +// RUN: llvm-readobj -program-headers %t2 | FileCheck --check-prefix=HEADER %s +// RUN: llvm-objdump -d %t2 | FileCheck --check-prefix=RELOCS %s + +.globl TDATA1 +.section .tdata,"awT",@progbits +.align 4 +.type TDATA1, @object +.size TDATA1, 4 +TDATA1: +.long 10 +.globl TDATA2 +.align 4 +.type TDATA2, @object +.size TDATA2, 4 +TDATA2: +.long 20 +.text +.globl main +.type main, @function +main: +movl $1, %fs:TDATA1@tpoff +movl $1, %fs:TDATA2@tpoff + +// SECTION: Name: .tdata +// SECTION-NEXT: Type: SHT_PROGBITS +// SECTION-NEXT: Flags [ +// SECTION-NEXT: SHF_ALLOC +// SECTION-NEXT: SHF_TLS +// SECTION-NEXT: SHF_WRITE +// SECTION-NEXT: ] +// SECTION-NEXT: Address: 0x12000 +// SECTION-NEXT: Offset: 0x2000 +// SECTION-NEXT: Size: 8 +// SECTION-NEXT: Link: 0 +// SECTION-NEXT: Info: 0 +// SECTION-NEXT: AddressAlignment: 4 +// SECTION-NEXT: EntrySize: 0 +// SECTION-NEXT: } + +// HEADER: ProgramHeader { +// HEADER: Type: PT_LOAD +// HEADER: Offset: 0x2000 +// HEADER-NEXT: VirtualAddress: 0x12000 +// HEADER-NEXT: PhysicalAddress: 0x12000 +// HEADER-NEXT: FileSize: 8 +// HEADER-NEXT: MemSize: 8 +// HEADER-NEXT: Flags [ +// HEADER-NEXT: PF_R +// HEADER-NEXT: PF_W +// HEADER-NEXT: ] +// HEADER-NEXT: Alignment: 4096 +// HEADER-NEXT: } +// HEADER-NEXT: ProgramHeader { +// HEADER-NEXT: Type: PT_TLS +// HEADER-NEXT: Offset: 0x2000 +// HEADER-NEXT: VirtualAddress: 0x12000 +// HEADER-NEXT: PhysicalAddress: 0x12000 +// HEADER-NEXT: FileSize: 8 +// HEADER-NEXT: MemSize: 8 +// HEADER-NEXT: Flags [ +// HEADER-NEXT: PF_R +// HEADER-NEXT: ] +// HEADER-NEXT: Alignment: 4 +// HEADER-NEXT: } + +// RELOCS: Disassembly of section .text: +// RELOCS-NEXT: main: +// RELOCS-NEXT: 11000: 64 c7 04 25 f8 ff ff ff 01 00 00 00 movl $1, %fs:-8 +// RELOCS-NEXT: 1100c: 64 c7 04 25 fc ff ff ff 01 00 00 00 movl $1, %fs:-4