diff --git a/lld/MachO/InputSection.h b/lld/MachO/InputSection.h --- a/lld/MachO/InputSection.h +++ b/lld/MachO/InputSection.h @@ -60,13 +60,22 @@ std::vector relocs; }; +inline uint8_t sectionType(uint32_t flags) { + return flags & llvm::MachO::SECTION_TYPE; +} + inline bool isZeroFill(uint32_t flags) { - return llvm::MachO::isVirtualSection(flags & llvm::MachO::SECTION_TYPE); + return llvm::MachO::isVirtualSection(sectionType(flags)); } inline bool isThreadLocalVariables(uint32_t flags) { - return (flags & llvm::MachO::SECTION_TYPE) == - llvm::MachO::S_THREAD_LOCAL_VARIABLES; + return sectionType(flags) == llvm::MachO::S_THREAD_LOCAL_VARIABLES; +} + +// These sections contain the data for initializing thread-local variables. +inline bool isThreadLocalData(uint32_t flags) { + return sectionType(flags) == llvm::MachO::S_THREAD_LOCAL_ZEROFILL || + sectionType(flags) == llvm::MachO::S_THREAD_LOCAL_REGULAR; } inline bool isDebugSection(uint32_t flags) { diff --git a/lld/MachO/InputSection.cpp b/lld/MachO/InputSection.cpp --- a/lld/MachO/InputSection.cpp +++ b/lld/MachO/InputSection.cpp @@ -11,6 +11,7 @@ #include "OutputSegment.h" #include "Symbols.h" #include "Target.h" +#include "Writer.h" #include "lld/Common/Memory.h" #include "llvm/Support/Endian.h" @@ -45,11 +46,11 @@ target->resolveSymbolVA(buf + r.offset, *referentSym, r.type); if (isThreadLocalVariables(flags)) { - // References from thread-local variable sections are treated - // as offsets relative to the start of the referent section, - // instead of as absolute addresses. + // References from thread-local variable sections are treated as offsets + // relative to the start of the first thread-local data section, instead + // of as absolute addresses. if (auto *defined = dyn_cast(referentSym)) - referentVA -= defined->isec->parent->addr; + referentVA -= firstTLVDataSection->addr; } } else if (auto *referentIsec = r.referent.dyn_cast()) { referentVA = referentIsec->getVA(); diff --git a/lld/MachO/Writer.h b/lld/MachO/Writer.h --- a/lld/MachO/Writer.h +++ b/lld/MachO/Writer.h @@ -14,6 +14,8 @@ namespace lld { namespace macho { +class OutputSection; + class LoadCommand { public: virtual ~LoadCommand() = default; @@ -25,6 +27,8 @@ void createSyntheticSections(); +extern OutputSection *firstTLVDataSection; + } // namespace macho } // namespace lld diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -558,6 +558,24 @@ .Case(section_names::unwindInfo, std::numeric_limits::max() - 1) .Case(section_names::ehFrame, std::numeric_limits::max()) .Default(0); + } else if (segname == segment_names::data) { + // For each thread spawned, dyld will initialize its TLVs by creating a copy + // address range from the start of the first thread-local data section to + // the end of the last one. We therefore arrange these sections contiguously + // to minimize the amount of memory used. Additionally, since zerofill + // sections must be at the end of their segments, and since TLV data + // sections can be zerofills, we end up putting all TLV data sections at the + // end of the segment. + switch (sectionType(osec->flags)) { + case S_THREAD_LOCAL_REGULAR: + return std::numeric_limits::max() - 2; + case S_THREAD_LOCAL_ZEROFILL: + return std::numeric_limits::max() - 1; + case S_ZEROFILL: + return std::numeric_limits::max(); + default: + return 0; + } } else if (segname == segment_names::linkEdit) { return StringSwitch(osec->name) .Case(section_names::rebase, -8) @@ -572,7 +590,7 @@ } // ZeroFill sections must always be the at the end of their segments, // otherwise subsequent sections may get overwritten with zeroes at runtime. - if (isZeroFill(osec->flags)) + if (sectionType(osec->flags) == S_ZEROFILL) return std::numeric_limits::max(); return 0; } @@ -601,6 +619,9 @@ if (!osec->isHidden()) osec->index = ++sectionIndex; + if (!firstTLVDataSection && isThreadLocalData(osec->flags)) + firstTLVDataSection = osec; + if (!isecPriorities.empty()) { if (auto *merged = dyn_cast(osec)) { llvm::stable_sort(merged->inputs, @@ -778,3 +799,5 @@ in.stubHelper = make(); in.imageLoaderCache = make(); } + +OutputSection *macho::firstTLVDataSection = nullptr; diff --git a/lld/test/MachO/bss.s b/lld/test/MachO/bss.s --- a/lld/test/MachO/bss.s +++ b/lld/test/MachO/bss.s @@ -3,8 +3,11 @@ # RUN: %lld -o %t %t.o # RUN: llvm-readobj --section-headers --macho-segment %t | FileCheck %s -## Check that __bss takes up zero file size, is at file offset zero, and -## appears at the end of its segment. +## Check that __bss takes up zero file size, is at file offset zero, and appears +## at the end of its segment. Also check that __tbss is placed immediately +## before it. +## Zerofill sections in other segments (i.e. not __DATA) should also be placed +## at the end. # CHECK: Index: 1 # CHECK-NEXT: Name: __data @@ -23,6 +26,22 @@ # CHECK-NEXT: Reserved3: 0x0 # CHECK: Index: 2 +# CHECK-NEXT: Name: __thread_bss +# CHECK-NEXT: Segment: __DATA +# CHECK-NEXT: Address: +# CHECK-NEXT: Size: 0x4 +# CHECK-NEXT: Offset: 0 +# CHECK-NEXT: Alignment: 0 +# CHECK-NEXT: RelocationOffset: 0x0 +# CHECK-NEXT: RelocationCount: 0 +# CHECK-NEXT: Type: ThreadLocalZerofill (0x12) +# CHECK-NEXT: Attributes [ (0x0) +# CHECK-NEXT: ] +# CHECK-NEXT: Reserved1: 0x0 +# CHECK-NEXT: Reserved2: 0x0 +# CHECK-NEXT: Reserved3: 0x0 + +# CHECK: Index: 3 # CHECK-NEXT: Name: __bss # CHECK-NEXT: Segment: __DATA # CHECK-NEXT: Address: @@ -38,16 +57,32 @@ # CHECK-NEXT: Reserved2: 0x0 # CHECK-NEXT: Reserved3: 0x0 -# CHECK: Index: 3 -# CHECK-NEXT: Name: __thread_bss -# CHECK-NEXT: Segment: __DATA -# CHECK-NEXT: Address: 0x100001010 -# CHECK-NEXT: Size: 0x4 +# CHECK: Index: 4 +# CHECK-NEXT: Name: foo +# CHECK-NEXT: Segment: FOO +# CHECK-NEXT: Address: +# CHECK-NEXT: Size: 0x8 +# CHECK-NEXT: Offset: 8192 +# CHECK-NEXT: Alignment: 0 +# CHECK-NEXT: RelocationOffset: 0x0 +# CHECK-NEXT: RelocationCount: 0 +# CHECK-NEXT: Type: Regular (0x0) +# CHECK-NEXT: Attributes [ (0x0) +# CHECK-NEXT: ] +# CHECK-NEXT: Reserved1: 0x0 +# CHECK-NEXT: Reserved2: 0x0 +# CHECK-NEXT: Reserved3: 0x0 + +# CHECK: Index: 5 +# CHECK-NEXT: Name: bss +# CHECK-NEXT: Segment: FOO +# CHECK-NEXT: Address: +# CHECK-NEXT: Size: 0x8 # CHECK-NEXT: Offset: 0 # CHECK-NEXT: Alignment: 0 # CHECK-NEXT: RelocationOffset: 0x0 # CHECK-NEXT: RelocationCount: 0 -# CHECK-NEXT: Type: ThreadLocalZerofill (0x12) +# CHECK-NEXT: Type: ZeroFill (0x1) # CHECK-NEXT: Attributes [ (0x0) # CHECK-NEXT: ] # CHECK-NEXT: Reserved1: 0x0 @@ -61,6 +96,13 @@ # CHECK-NEXT: fileoff: # CHECK-NEXT: filesize: 8 +# CHECK: Name: FOO +# CHECK-NEXT: Size: +# CHECK-NEXT: vmaddr: +# CHECK-NEXT: vmsize: 0x10 +# CHECK-NEXT: fileoff: +# CHECK-NEXT: filesize: 8 + .globl _main .text @@ -76,3 +118,8 @@ .data .quad 0x1234 + +.zerofill FOO,bss,_zero_foo,0x8 + +.section FOO,foo +.quad 123 diff --git a/lld/test/MachO/tlv.s b/lld/test/MachO/tlv.s --- a/lld/test/MachO/tlv.s +++ b/lld/test/MachO/tlv.s @@ -1,53 +1,75 @@ # REQUIRES: x86 -# RUN: mkdir -p %t -# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t/test.o +# RUN: rm -rf %t; split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/regular.s -o %t/regular.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/tbss.s -o %t/tbss.o -# RUN: %lld -lSystem -o %t/test %t/test.o -# RUN: llvm-readobj --file-headers %t/test | FileCheck %s --check-prefix=HEADER -# RUN: llvm-objdump -D --bind --rebase %t/test | FileCheck %s +# RUN: %lld -lSystem -no_pie -o %t/regular %t/regular.o +# RUN: llvm-readobj --file-headers %t/regular | FileCheck %s --check-prefix=HEADER +# RUN: llvm-objdump -d --bind --rebase %t/regular | FileCheck %s --check-prefixes=REG,LINKEDIT +# RUN: llvm-objdump --macho --section=__DATA,__thread_vars %t/regular | \ +# RUN: FileCheck %s --check-prefix=REG-TLVP -# RUN: %lld -lSystem -pie -o %t/test %t/test.o -# RUN: llvm-readobj --file-headers %t/test | FileCheck %s --check-prefix=HEADER -# RUN: llvm-objdump -D --bind --rebase %t/test | FileCheck %s +# RUN: %lld -lSystem -pie %t/regular.o -o %t/regular-pie +# RUN: llvm-readobj --file-headers %t/regular-pie | FileCheck %s --check-prefix=HEADER +# RUN: llvm-objdump -d --bind --rebase %t/regular-pie | FileCheck %s --check-prefixes=REG,LINKEDIT +# RUN: llvm-objdump --macho --section=__DATA,__thread_vars %t/regular-pie | \ +# RUN: FileCheck %s --check-prefix=REG-TLVP + +# RUN: %lld -lSystem %t/tbss.o -o %t/tbss -e _f +# RUN: llvm-objdump -d --bind --rebase %t/tbss | FileCheck %s --check-prefixes=TBSS,LINKEDIT +# RUN: llvm-objdump --macho --section=__DATA,__thread_vars %t/tbss | \ +# RUN: FileCheck %s --check-prefix=TBSS-TLVP + +# RUN: %lld -lSystem %t/regular.o %t/tbss.o -o %t/regular-and-tbss +# RUN: llvm-objdump -d --bind --rebase %t/regular-and-tbss | FileCheck %s --check-prefixes=REG,TBSS,LINKEDIT +# RUN: llvm-objdump --macho --section=__DATA,__thread_vars %t/regular-and-tbss | \ +# RUN: FileCheck %s --check-prefix=REG-TBSS-TLVP +# RUN: llvm-objdump --section-headers %t/regular-and-tbss | FileCheck %s --check-prefix=SECTION-ORDER + +## Check that we always put __thread_bss immediately after __thread_data, +## regardless of the order of the input files. +# RUN: %lld -lSystem %t/tbss.o %t/regular.o -o %t/regular-and-tbss +# RUN: llvm-objdump --section-headers %t/regular-and-tbss | FileCheck %s --check-prefix=SECTION-ORDER # HEADER: MH_HAS_TLV_DESCRIPTORS -# CHECK: Disassembly of section __TEXT,__text: -# CHECK-EMPTY: -# CHECK-NEXT: <_main>: -# CHECK-NEXT: leaq {{.*}}(%rip), %rax # {{.*}} <_foo> -# CHECK-NEXT: leaq {{.*}}(%rip), %rax # {{.*}} <_bar> -# CHECK-NEXT: retq -# CHECK-EMPTY: -# CHECK-NEXT: Disassembly of section __DATA,__thread_data: -# CHECK-EMPTY: -# CHECK-NEXT: <_foo$tlv$init>: -# CHECK-NEXT: 00 00 -# CHECK-NEXT: 00 00 -# CHECK-EMPTY: -# CHECK-NEXT: <_bar$tlv$init>: -# CHECK-NEXT: 00 00 -# CHECK-NEXT: 00 00 -# CHECK-EMPTY: -# CHECK-NEXT: Disassembly of section __DATA,__thread_vars: -# CHECK-EMPTY: -# CHECK-NEXT: <_foo>: -# CHECK-NEXT: ... -# CHECK-EMPTY: -# CHECK-NEXT: <_bar>: -# CHECK-NEXT: ... -# CHECK-NEXT: 04 00 -# CHECK-NEXT: 00 00 -# CHECK-NEXT: 00 00 -# CHECK-NEXT: 00 00 +# REG: <_main>: +# REG-NEXT: leaq {{.*}}(%rip), %rax # {{.*}} <_foo> +# REG-NEXT: leaq {{.*}}(%rip), %rax # {{.*}} <_bar> +# REG-NEXT: retq + +# TBSS: <_f>: +# TBSS-NEXT: leaq {{.*}}(%rip), %rax # {{.*}} <_baz> +# TBSS-NEXT: leaq {{.*}}(%rip), %rax # {{.*}} <_qux> +# TBSS-NEXT: retq + +# REG-TLVP: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# REG-TLVP-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# REG-TLVP-NEXT: 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 + +# TBSS-TLVP: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# TBSS-TLVP-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# TBSS-TLVP-NEXT: 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 + +# REG-TBSS-TLVP: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# REG-TBSS-TLVP-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# REG-TBSS-TLVP-NEXT: 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 +# REG-TBSS-TLVP-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# REG-TBSS-TLVP-NEXT: 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# REG-TBSS-TLVP-NEXT: 00 00 00 00 00 00 00 00 18 00 00 00 00 00 00 00 ## Make sure we don't emit rebase opcodes for relocations in __thread_vars. -# CHECK: Rebase table: -# CHECK-NEXT: segment section address type -# CHECK-NEXT: Bind table: -# CHECK: __DATA __thread_vars 0x{{[0-9a-f]*}} pointer 0 libSystem __tlv_bootstrap -# CHECK: __DATA __thread_vars 0x{{[0-9a-f]*}} pointer 0 libSystem __tlv_bootstrap +# LINKEDIT: Rebase table: +# LINKEDIT-NEXT: segment section address type +# LINKEDIT-NEXT: Bind table: +# LINKEDIT: __DATA __thread_vars 0x{{[0-9a-f]*}} pointer 0 libSystem __tlv_bootstrap +# LINKEDIT: __DATA __thread_vars 0x{{[0-9a-f]*}} pointer 0 libSystem __tlv_bootstrap + +# SECTION-ORDER: __thread_data +# SECTION-ORDER: more_thread_data +# SECTION-ORDER-NEXT: __thread_bss +#--- regular.s .globl _main _main: mov _foo@TLVP(%rip), %rax @@ -56,9 +78,11 @@ .section __DATA,__thread_data,thread_local_regular _foo$tlv$init: - .space 4 + .quad 123 + +.section __DATA,more_thread_data,thread_local_regular _bar$tlv$init: - .space 4 + .quad 123 .section __DATA,__thread_vars,thread_local_variables .globl _foo, _bar @@ -70,3 +94,24 @@ .quad __tlv_bootstrap .quad 0 .quad _bar$tlv$init + +#--- tbss.s + +.globl _f +_f: + mov _baz@TLVP(%rip), %rax + mov _qux@TLVP(%rip), %rax + ret + +.tbss _baz$tlv$init, 8, 3 +.tbss _qux$tlv$init, 8, 3 + +.section __DATA,__thread_vars,thread_local_variables +_baz: + .quad __tlv_bootstrap + .quad 0 + .quad _baz$tlv$init +_qux: + .quad __tlv_bootstrap + .quad 0 + .quad _qux$tlv$init