Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -108,7 +108,7 @@ this->Header.sh_size = Entries.size() * this->getAddrSize(); } void writeTo(uint8_t *Buf) override; - void addEntry(SymbolBody *Sym); + void addEntry(SymbolBody *Sym, int Size = 1); bool empty() const { return Entries.empty(); } uintX_t getEntryAddr(const SymbolBody &B) const; @@ -294,6 +294,7 @@ static StringTableSection *StrTab; static SymbolTableSection *DynSymTab; static SymbolTableSection *SymTab; + static typename llvm::object::ELFFile::uintX_t ThreadDataStart; }; template DynamicSection *Out::Dynamic; @@ -307,6 +308,8 @@ template StringTableSection *Out::StrTab; template SymbolTableSection *Out::DynSymTab; template SymbolTableSection *Out::SymTab; +template +typename llvm::object::ELFFile::uintX_t Out::ThreadDataStart; } } #endif Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -37,9 +37,11 @@ this->Header.sh_addralign = this->getAddrSize(); } -template void GotSection::addEntry(SymbolBody *Sym) { +template +void GotSection::addEntry(SymbolBody *Sym, int Size) { Sym->GotIndex = Entries.size(); Entries.push_back(Sym); + Entries.insert(Entries.end(), Size - 1, nullptr); } template @@ -52,6 +54,8 @@ for (const SymbolBody *B : Entries) { uint8_t *Entry = Buf; Buf += sizeof(uintX_t); + if (!B) + continue; if (canBePreempted(B)) continue; // The dynamic linker will take care of it. uintX_t VA = getSymVA(*B); @@ -121,7 +125,7 @@ uint32_t Type = RI.getType(IsMips64EL); - bool CanBePreempted = canBePreempted(Body); + bool CanBePreempted = canBePreempted(Body) || Type == R_X86_64_TLSLD; uintX_t Addend = 0; if (!CanBePreempted) { if (IsRela) { @@ -138,7 +142,7 @@ P->r_offset = Out::Got->getEntryAddr(*Body); if (CanBePreempted) P->setSymbolAndType(Body->getDynamicSymbolTableIndex(), - Target->getGotReloc(), IsMips64EL); + Target->getGotReloc(Type), IsMips64EL); } else { if (IsRela) Addend += static_cast(RI).r_addend; @@ -402,6 +406,10 @@ const auto &DR = cast>(S); const InputSection *SC = &DR.Section; OutputSection *OS = SC->getOutputSection(); + if (DR.Sym.getType() == STT_TLS) { + return (OS->getVA() + SC->getOutputSectionOff() + DR.Sym.st_value) - + Out::ThreadDataStart; + } return OS->getVA() + SC->getOutputSectionOff() + DR.Sym.st_value; } case SymbolBody::DefinedCommonKind: Index: ELF/Target.h =================================================================== --- ELF/Target.h +++ ELF/Target.h @@ -24,14 +24,16 @@ unsigned getPageSize() const { return PageSize; } uint64_t getVAStart() const { return VAStart; } unsigned getPCRelReloc() const { return PCRelReloc; } - unsigned getGotReloc() const { return GotReloc; } unsigned getGotRefReloc() const { return GotRefReloc; } unsigned getRelativeReloc() const { return RelativeReloc; } unsigned getPltEntrySize() const { return PltEntrySize; } + virtual uint32_t getGotReloc(uint32_t Type) const { return GotReloc; } virtual void writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr) const = 0; virtual bool isRelRelative(uint32_t Type) const; - virtual bool relocNeedsGot(uint32_t Type, const SymbolBody &S) const = 0; + /// /returns the number of got slots needed for this relocation, if any. 0 + /// means reloc doesn't require a got. + virtual int relocNeedsGot(uint32_t Type, const SymbolBody &S) const = 0; virtual bool relocPointsToGot(uint32_t Type) const; virtual bool relocNeedsPlt(uint32_t Type, const SymbolBody &S) const = 0; virtual void relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type, @@ -55,7 +57,7 @@ X86TargetInfo(); void writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr) const override; - bool relocNeedsGot(uint32_t Type, const SymbolBody &S) const override; + int relocNeedsGot(uint32_t Type, const SymbolBody &S) const override; bool relocPointsToGot(uint32_t Type) const override; bool relocNeedsPlt(uint32_t Type, const SymbolBody &S) const override; void relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type, @@ -65,9 +67,10 @@ class X86_64TargetInfo final : public TargetInfo { public: X86_64TargetInfo(); + uint32_t getGotReloc(uint32_t Type) const override; void writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr) const override; - bool relocNeedsGot(uint32_t Type, const SymbolBody &S) const override; + int relocNeedsGot(uint32_t Type, const SymbolBody &S) const override; bool relocNeedsPlt(uint32_t Type, const SymbolBody &S) const override; void relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type, uint64_t BaseAddr, uint64_t SymVA) const override; @@ -79,7 +82,7 @@ PPC64TargetInfo(); void writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr) const override; - bool relocNeedsGot(uint32_t Type, const SymbolBody &S) const override; + int relocNeedsGot(uint32_t Type, const SymbolBody &S) const override; bool relocNeedsPlt(uint32_t Type, const SymbolBody &S) const override; void relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type, uint64_t BaseAddr, uint64_t SymVA) const override; @@ -90,7 +93,7 @@ PPCTargetInfo(); void writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr) const override; - bool relocNeedsGot(uint32_t Type, const SymbolBody &S) const override; + int relocNeedsGot(uint32_t Type, const SymbolBody &S) const override; bool relocNeedsPlt(uint32_t Type, const SymbolBody &S) const override; void relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type, uint64_t BaseAddr, uint64_t SymVA) const override; @@ -101,7 +104,7 @@ ARMTargetInfo(); void writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr) const override; - bool relocNeedsGot(uint32_t Type, const SymbolBody &S) const override; + int relocNeedsGot(uint32_t Type, const SymbolBody &S) const override; bool relocNeedsPlt(uint32_t Type, const SymbolBody &S) const override; void relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type, uint64_t BaseAddr, uint64_t SymVA) const override; @@ -112,7 +115,7 @@ AArch64TargetInfo(); void writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr) const override; - bool relocNeedsGot(uint32_t Type, const SymbolBody &S) const override; + int relocNeedsGot(uint32_t Type, const SymbolBody &S) const override; bool relocNeedsPlt(uint32_t Type, const SymbolBody &S) const override; void relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type, uint64_t BaseAddr, uint64_t SymVA) const override; @@ -123,7 +126,7 @@ MipsTargetInfo(); void writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr) const override; - bool relocNeedsGot(uint32_t Type, const SymbolBody &S) const override; + int relocNeedsGot(uint32_t Type, const SymbolBody &S) const override; bool relocNeedsPlt(uint32_t Type, const SymbolBody &S) const override; void relocateOne(uint8_t *Buf, const void *RelP, uint32_t Type, uint64_t BaseAddr, uint64_t SymVA) const override; Index: ELF/Target.cpp =================================================================== --- ELF/Target.cpp +++ ELF/Target.cpp @@ -49,7 +49,7 @@ write32le(Buf + 2, GotEntryAddr); } -bool X86TargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const { +int X86TargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const { return Type == R_386_GOT32 || relocNeedsPlt(Type, S); } @@ -102,6 +102,12 @@ VAStart = 0x10000; } +uint32_t X86_64TargetInfo::getGotReloc(uint32_t Type) const { + if (Type == R_X86_64_TLSLD) + return R_X86_64_DTPMOD64; + return GotReloc; +} + void X86_64TargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr) const { // jmpq *val(%rip); nop; nop @@ -114,7 +120,9 @@ write32le(Buf + 2, Delta); } -bool X86_64TargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const { +int X86_64TargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const { + if (Type == R_X86_64_TLSLD) + return 2; return Type == R_X86_64_GOTPCREL || relocNeedsPlt(Type, S); } @@ -161,6 +169,7 @@ case R_X86_64_PC32: case R_X86_64_PC16: case R_X86_64_PC8: + case R_X86_64_DTPOFF32: return true; } } @@ -192,6 +201,9 @@ write32le(Loc, VA); break; } + case R_X86_64_DTPOFF32: + write32le(Loc, SymVA); + break; default: error(Twine("unrecognized reloc ") + Twine(Type)); break; @@ -207,7 +219,7 @@ } void PPC64TargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr) const {} -bool PPC64TargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const { +int PPC64TargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const { return false; } bool PPC64TargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const { @@ -241,7 +253,7 @@ } void PPCTargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr) const {} -bool PPCTargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const { +int PPCTargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const { return false; } bool PPCTargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const { @@ -257,7 +269,7 @@ } void ARMTargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr) const {} -bool ARMTargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const { +int ARMTargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const { return false; } bool ARMTargetInfo::relocNeedsPlt(uint32_t Type, const SymbolBody &S) const { @@ -273,7 +285,7 @@ } void AArch64TargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr) const {} -bool AArch64TargetInfo::relocNeedsGot(uint32_t Type, +int AArch64TargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const { return false; } @@ -356,7 +368,7 @@ void MipsTargetInfo::writePltEntry(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr) const {} -bool MipsTargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const { +int MipsTargetInfo::relocNeedsGot(uint32_t Type, const SymbolBody &S) const { return false; } Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -107,6 +107,7 @@ ProgramHeader FileHeaderPHDR{PT_LOAD, PF_R, 0, 0}; ProgramHeader InterpPHDR{PT_INTERP, 0, 0, 0}; ProgramHeader DynamicPHDR{PT_DYNAMIC, 0, 0, 0}; + ProgramHeader TlsPHDR{ PT_TLS, PF_R, 0, 0 }; uintX_t FileSize; uintX_t ProgramHeaderOff; @@ -139,6 +140,7 @@ Out::RelaDyn = &RelaDyn; DynamicSection Dynamic(*Symtab); Out::Dynamic = &Dynamic; + Out::ThreadDataStart = 0; Writer(*Symtab).run(); } @@ -209,13 +211,13 @@ continue; Out::Plt->addEntry(Body); } - if (Target->relocNeedsGot(Type, *Body)) { + if (int GotCount = Target->relocNeedsGot(Type, *Body)) { if (Body->isInGot()) continue; - Out::Got->addEntry(Body); + Out::Got->addEntry(Body, GotCount); } } - if (canBePreempted(Body)) { + if (canBePreempted(Body) || Type == R_X86_64_TLSLD) { Body->setUsedInDynamicReloc(); Out::RelaDyn->addReloc({C, RI}); } else if (Config->Shared && !Target->isRelRelative(Type)) { @@ -287,34 +289,53 @@ OutputSectionBase *B) { typedef typename ELFFile::uintX_t uintX_t; - uintX_t AFlags = A->getFlags(); - uintX_t BFlags = B->getFlags(); - - // Allocatable sections go first to reduce the total PT_LOAD size and - // so debug info doesn't change addresses in actual code. - bool AIsAlloc = AFlags & SHF_ALLOC; - bool BIsAlloc = BFlags & SHF_ALLOC; - if (AIsAlloc != BIsAlloc) - return AIsAlloc; + auto CalcRank = [](OutputSectionBase *Sec) { + uintX_t Flags = Sec->getFlags(); + // The TLS initialization block goes at the start of the read/write PT_LOAD + // as it needs to first be loaded into memory so relocations can be applied. + // The rest of the flags are ignored as there can only be one TLS + // initialization block per binary. + if ((Flags & (SHF_ALLOC | SHF_TLS)) == (SHF_ALLOC | SHF_TLS)) + return 2; + + switch (Flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR)) { + case SHF_ALLOC: + // We want the read only sections first so that they go in the PT_LOAD + // covering the program headers at the start of the file. + return 0; + + // The relative order of the following 3 cases is arbitrary. + + case SHF_ALLOC | SHF_EXECINSTR: + return 1; + case SHF_ALLOC | SHF_WRITE: + return 3; + case SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR: + return 4; + + case 0: + case SHF_WRITE | SHF_EXECINSTR: + case SHF_WRITE: + case SHF_EXECINSTR: + // Non allocatable sections go last to reduce the total PT_LOAD size and + // so debug info doesn't change addresses in actual code. There are also + // no special requirements for the relative order of non allocatable + // sections. + return 5; + } + llvm_unreachable("Covered switch."); + }; - // We don't have any special requirements for the relative order of - // two non allocatable sections. - if (!AIsAlloc) - return false; + int ARank = CalcRank(A); + int BRank = CalcRank(B); - // We want the read only sections first so that they go in the PT_LOAD - // covering the program headers at the start of the file. - bool AIsWritable = AFlags & SHF_WRITE; - bool BIsWritable = BFlags & SHF_WRITE; - if (AIsWritable != BIsWritable) - return BIsWritable; + if (ARank != BRank) + return ARank < BRank; - // For a corresponding reason, put non exec sections first (the program - // header PT_LOAD is not executable). - bool AIsExec = AFlags & SHF_EXECINSTR; - bool BIsExec = BFlags & SHF_EXECINSTR; - if (AIsExec != BIsExec) - return BIsExec; + // There are no special requirements for the relative order of non allocatable + // sections. + if (ARank == 5) + return false; // If we got here we know that both A and B and in the same PT_LOAD. // The last requirement we have is to put nobits section last. The @@ -508,6 +529,10 @@ FileHeaderPHDR.Header.p_paddr = getVAStart(); FileHeaderPHDR.Header.p_align = Target->getPageSize(); + // Since .tbss doesn't take up virtual address space, we have to track the + // offset from the first .tbss section manually. + uintX_t ThreadBSSOffset = 0; + for (OutputSectionBase *Sec : OutputSections) { if (Sec->getSize()) { uintX_t Flags = toPHDRFlags(Sec->getFlags()); @@ -531,20 +556,41 @@ uintX_t Align = Sec->getAlign(); uintX_t Size = Sec->getSize(); - if (Sec->getFlags() & SHF_ALLOC) { - VA = RoundUpToAlignment(VA, Align); - Sec->setVA(VA); - VA += Size; - } FileOff = RoundUpToAlignment(FileOff, Align); Sec->setFileOffset(FileOff); if (Sec->getType() != SHT_NOBITS) FileOff += Size; + if ((Sec->getFlags() & (SHF_ALLOC | SHF_TLS)) == (SHF_ALLOC | SHF_TLS)) { + if (Sec->getType() != SHT_NOBITS) + VA = RoundUpToAlignment(VA, Align); + uintX_t TVA = RoundUpToAlignment(VA + ThreadBSSOffset, Align); + Sec->setVA(TVA); + ThreadBSSOffset = TVA - VA; + if (Sec->getType() == SHT_NOBITS) + ThreadBSSOffset += Size; + else + VA += Size; + if (TlsPHDR.Header.p_offset == 0) { + TlsPHDR.Header.p_offset = Sec->getFileOff(); + TlsPHDR.Header.p_vaddr = Sec->getVA(); + TlsPHDR.Header.p_paddr = TlsPHDR.Header.p_vaddr; + Out::ThreadDataStart = TlsPHDR.Header.p_vaddr; + } + TlsPHDR.Header.p_filesz += Sec->getType() == SHT_NOBITS ? 0 : Size; + TlsPHDR.Header.p_memsz += Size; + TlsPHDR.Header.p_align = std::max(TlsPHDR.Header.p_align, Align); + } else if (Sec->getFlags() & SHF_ALLOC) { + VA = RoundUpToAlignment(VA, Align); + Sec->setVA(VA); + VA += Size; + } } // Add a PHDR for the dynamic table. if (needsDynamicSections()) PHDRs.push_back(&DynamicPHDR); + if (TlsPHDR.Header.p_memsz > 0) + PHDRs.push_back(&TlsPHDR); FileOff += OffsetToAlignment(FileOff, ELFT::Is64Bits ? 8 : 4); Index: test/elf2/section-layout.s =================================================================== --- test/elf2/section-layout.s +++ test/elf2/section-layout.s @@ -9,10 +9,25 @@ .text _start: -.section h,"" -.section g,"",@nobits -.section f,"aw",@nobits -.section e,"aw" +.section v,"T",@nobits +.section u,"T" +.section t,"x",@nobits +.section s,"x" +.section r,"w",@nobits +.section q,"w" +.section p,"wx",@nobits +.section o,"wx" +.section n,"",@nobits +.section m,"" + +.section l,"awx",@nobits +.section k,"awx" +.section j,"aw",@nobits +.section i,"aw" +.section h,"aT",@nobits +.section g,"awT",@nobits +.section f,"aT" +.section e,"awT" .section d,"ax",@nobits .section c,"ax" .section b,"a",@nobits @@ -22,7 +37,26 @@ // CHECK: Name: b // CHECK: Name: c // CHECK: Name: d -// CHECK: Name: e + +// TLS sections are only sorted on NOBITS. // CHECK: Name: f +// CHECK: Name: e // CHECK: Name: h // CHECK: Name: g + +// CHECK: Name: i +// CHECK: Name: j +// CHECK: Name: k +// CHECK: Name: l + +// Non allocated sections are in input order. +// CHECK: Name: v +// CHECK: Name: u +// CHECK: Name: t +// CHECK: Name: s +// CHECK: Name: r +// CHECK: Name: q +// CHECK: Name: p +// CHECK: Name: o +// CHECK: Name: n +// CHECK: Name: m Index: test/elf2/tls-dynamic.s =================================================================== --- /dev/null +++ test/elf2/tls-dynamic.s @@ -0,0 +1,197 @@ +// REQUIRES: x86 +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +// RUN: ld.lld2 -shared %t -o %tout +// RUN: llvm-readobj -symbols -sections -program-headers -relocations %tout | \ +// RUN: FileCheck %s +// RUN: llvm-objdump -d %tout | FileCheck %s --check-prefix=DIS + + .text + .globl _start +_start: + leaq a@TLSLD(%rip), %rdi + callq __tls_get_addr@PLT + movl $1, a@DTPOFF(%rax) + movl $2, c@DTPOFF(%rax) + movl b@DTPOFF(%rax), %esi + addl a@DTPOFF(%rax), %esi + addl c@DTPOFF(%rax), %esi + addl d@DTPOFF(%rax), %esi + retq + + .hidden a + .section .tbss,"awT",@nobits + .globl a + .align 4 +a: + .long 0 + + .hidden b + .section .tdata,"awT",@progbits + .globl b + .align 4 +b: + .long 1 + + .hidden c + .section .thread_bss,"awT",@nobits + .globl c + .align 4 +c: + .long 0 + + .hidden d + .section .thread_data,"awT",@progbits + .globl d + .align 4 +d: + .long 2 + +// CHECK: Name: .tdata +// CHECK-NEXT: Type: SHT_PROGBITS +// CHECK-NEXT: Flags [ +// CHECK-NEXT: SHF_ALLOC +// CHECK-NEXT: SHF_TLS +// CHECK-NEXT: SHF_WRITE +// CHECK-NEXT: ] +// CHECK-NEXT: Address: [[TDATA_ADDR:0x.*]] +// CHECK-NEXT: Offset: +// CHECK-NEXT: Size: 4 +// CHECK-NEXT: Link: +// CHECK-NEXT: Info: +// CHECK-NEXT: AddressAlignment: +// CHECK-NEXT: EntrySize: +// CHECK-NEXT: } +// CHECK-NEXT: Section { +// CHECK-NEXT: Index: +// CHECK-NEXT: Name: .thread_data +// CHECK-NEXT: Type: SHT_PROGBITS +// CHECK-NEXT: Flags [ +// CHECK-NEXT: SHF_ALLOC +// CHECK-NEXT: SHF_TLS +// CHECK-NEXT: SHF_WRITE +// CHECK-NEXT: ] +// CHECK-NEXT: Address: +// CHECK-NEXT: Offset: +// CHECK-NEXT: Size: 4 +// CHECK-NEXT: Link: +// CHECK-NEXT: Info: +// CHECK-NEXT: AddressAlignment: +// CHECK-NEXT: EntrySize: +// CHECK-NEXT: } +// CHECK-NEXT: Section { +// CHECK-NEXT: Index: +// CHECK-NEXT: Name: .tbss +// CHECK-NEXT: Type: SHT_NOBITS +// CHECK-NEXT: Flags [ +// CHECK-NEXT: SHF_ALLOC +// CHECK-NEXT: SHF_TLS +// CHECK-NEXT: SHF_WRITE +// CHECK-NEXT: ] +// CHECK-NEXT: Address: [[TBSS_ADDR:0x.*]] +// CHECK-NEXT: Offset: +// CHECK-NEXT: Size: 4 +// CHECK-NEXT: Link: +// CHECK-NEXT: Info: +// CHECK-NEXT: AddressAlignment: +// CHECK-NEXT: EntrySize: +// CHECK-NEXT: } +// CHECK-NEXT: Section { +// CHECK-NEXT: Index: +// CHECK-NEXT: Name: .thread_bss +// CHECK-NEXT: Type: SHT_NOBITS +// CHECK-NEXT: Flags [ +// CHECK-NEXT: SHF_ALLOC +// CHECK-NEXT: SHF_TLS +// CHECK-NEXT: SHF_WRITE +// CHECK-NEXT: ] +// CHECK-NEXT: Address: +// CHECK-NEXT: Offset: +// CHECK-NEXT: Size: 4 +// CHECK-NEXT: Link: +// CHECK-NEXT: Info: +// CHECK-NEXT: AddressAlignment: +// CHECK-NEXT: EntrySize: +// CHECK-NEXT: } +// CHECK-NEXT: Section { +// CHECK-NEXT: Index: +// CHECK-NEXT: Name: +// CHECK-NEXT: Type: +// CHECK-NEXT: Flags [ +// CHECK-NEXT: SHF_ALLOC +// CHECK-NEXT: SHF_WRITE +// CHECK-NEXT: ] +// CHECK-NEXT: Address: [[TBSS_ADDR]] +// CHECK: Name: .got +// CHECK-NEXT: Type: SHT_PROGBITS +// CHECK-NEXT: Flags [ +// CHECK-NEXT: SHF_ALLOC +// CHECK-NEXT: SHF_WRITE +// CHECK-NEXT: ] +// CHECK-NEXT: Address: 0x3098 + +// CHECK: Relocations [ +// CHECK-NOT: R_X86_64_RELATIVE +// CHECK: {{.*}} R_X86_64_DTPMOD64 - 0x0 +// CHECK-NOT: R_X86_64_RELATIVE + +// CHECK: Symbol { +// CHECK: Name: a +// CHECK-NEXT: Value: 0x8 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local +// CHECK-NEXT: Type: TLS +// CHECK-NEXT: Other: 2 +// CHECK-NEXT: Section: .tbss +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: b +// CHECK-NEXT: Value: 0x0 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local +// CHECK-NEXT: Type: TLS +// CHECK-NEXT: Other: 2 +// CHECK-NEXT: Section: .tdata +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: c +// CHECK-NEXT: Value: 0xC +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local +// CHECK-NEXT: Type: TLS +// CHECK-NEXT: Other: 2 +// CHECK-NEXT: Section: .thread_bss +// CHECK-NEXT: } +// CHECK-NEXT: Symbol { +// CHECK-NEXT: Name: d +// CHECK-NEXT: Value: 0x4 +// CHECK-NEXT: Size: 0 +// CHECK-NEXT: Binding: Local +// CHECK-NEXT: Type: TLS +// CHECK-NEXT: Other: 2 +// CHECK-NEXT: Section: .thread_data +// CHECK-NEXT: } + +// CHECK: Type: PT_TLS +// CHECK-NEXT: Offset: +// CHECK-NEXT: VirtualAddress: [[TDATA_ADDR]] +// CHECK-NEXT: PhysicalAddress: [[TDATA_ADDR]] +// CHECK-NEXT: FileSize: 8 +// CHECK-NEXT: MemSize: 16 +// CHECK-NEXT: Flags [ +// CHECK-NEXT: PF_R +// CHECK-NEXT: ] +// CHECK-NEXT: Alignment: +// CHECK-NEXT: } + +// = 0x3098, offset = 0x3098 - (0x2000 + 7) = 4241 + +// DIS: Disassembly of section .text: +// DIS-NEXT: _start: +// DIS-NEXT: 2000: 48 8d 3d {{.*}} leaq 4241(%rip), %rdi +// DIS-NEXT: 2007: e8 {{.*}} callq +// DIS-NEXT: 200c: c7 80 08 00 00 00 01 00 00 00 movl $1, 8(%rax) +// DIS-NEXT: 2016: c7 80 0c 00 00 00 02 00 00 00 movl $2, 12(%rax) +// DIS-NEXT: 2020: 8b b0 00 00 00 00 movl (%rax), %esi +// DIS-NEXT: 2026: 03 b0 08 00 00 00 addl 8(%rax), %esi +// DIS-NEXT: 202c: 03 b0 0c 00 00 00 addl 12(%rax), %esi +// DIS-NEXT: 2032: 03 b0 04 00 00 00 addl 4(%rax), %esi