Skip to content

Commit daaaed6

Browse files
committed
[lld-macho] Fix TLV data initialization
We were mishandling the case where both `__tbss` and `__thread_data` sections were present. TLVP relocations should be encoded as offsets from the start of `__thread_data`, even if the symbol is actually located in `__thread_bss`. Previously, we were writing the offset from the start of the containing section, which doesn't really make sense since there's no way `tlv_get_addr()` can know which section a given `tlv$init` symbol is in at runtime. In addition, this patch ensures that we place `__thread_data` immediately before `__thread_bss`. This is what ld64 does, likely for performance reasons. Zerofill sections must also be at the end of their segments; we were already doing this, but now we ensure that `__thread_bss` occurs before `__bss`, so that it's always possible to have it contiguous with `__thread_data`. Fixes llvm.org/PR48657. Reviewed By: #lld-macho, thakis Differential Revision: https://reviews.llvm.org/D94329
1 parent 756dd70 commit daaaed6

File tree

6 files changed

+189
-59
lines changed

6 files changed

+189
-59
lines changed

lld/MachO/InputSection.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "OutputSegment.h"
1212
#include "Symbols.h"
1313
#include "Target.h"
14+
#include "Writer.h"
1415
#include "lld/Common/Memory.h"
1516
#include "llvm/Support/Endian.h"
1617

@@ -45,11 +46,12 @@ void InputSection::writeTo(uint8_t *buf) {
4546
target->resolveSymbolVA(buf + r.offset, *referentSym, r.type);
4647

4748
if (isThreadLocalVariables(flags)) {
48-
// References from thread-local variable sections are treated
49-
// as offsets relative to the start of the referent section,
50-
// instead of as absolute addresses.
49+
// References from thread-local variable sections are treated as offsets
50+
// relative to the start of the thread-local data memory area, which
51+
// is initialized via copying all the TLV data sections (which are all
52+
// contiguous).
5153
if (auto *defined = dyn_cast<Defined>(referentSym))
52-
referentVA -= defined->isec->parent->addr;
54+
referentVA -= firstTLVDataSection->addr;
5355
}
5456
} else if (auto *referentIsec = r.referent.dyn_cast<InputSection *>()) {
5557
referentVA = referentIsec->getVA();

lld/MachO/InputSection.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,22 @@ class InputSection {
6060
std::vector<Reloc> relocs;
6161
};
6262

63+
inline uint8_t sectionType(uint32_t flags) {
64+
return flags & llvm::MachO::SECTION_TYPE;
65+
}
66+
6367
inline bool isZeroFill(uint32_t flags) {
64-
return llvm::MachO::isVirtualSection(flags & llvm::MachO::SECTION_TYPE);
68+
return llvm::MachO::isVirtualSection(sectionType(flags));
6569
}
6670

6771
inline bool isThreadLocalVariables(uint32_t flags) {
68-
return (flags & llvm::MachO::SECTION_TYPE) ==
69-
llvm::MachO::S_THREAD_LOCAL_VARIABLES;
72+
return sectionType(flags) == llvm::MachO::S_THREAD_LOCAL_VARIABLES;
73+
}
74+
75+
// These sections contain the data for initializing thread-local variables.
76+
inline bool isThreadLocalData(uint32_t flags) {
77+
return sectionType(flags) == llvm::MachO::S_THREAD_LOCAL_REGULAR ||
78+
sectionType(flags) == llvm::MachO::S_THREAD_LOCAL_ZEROFILL;
7079
}
7180

7281
inline bool isDebugSection(uint32_t flags) {

lld/MachO/Writer.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,24 @@ static int sectionOrder(OutputSection *osec) {
557557
.Case(section_names::unwindInfo, std::numeric_limits<int>::max() - 1)
558558
.Case(section_names::ehFrame, std::numeric_limits<int>::max())
559559
.Default(0);
560+
} else if (segname == segment_names::data) {
561+
// For each thread spawned, dyld will initialize its TLVs by copying the
562+
// address range from the start of the first thread-local data section to
563+
// the end of the last one. We therefore arrange these sections contiguously
564+
// to minimize the amount of memory used. Additionally, since zerofill
565+
// sections must be at the end of their segments, and since TLV data
566+
// sections can be zerofills, we end up putting all TLV data sections at the
567+
// end of the segment.
568+
switch (sectionType(osec->flags)) {
569+
case S_THREAD_LOCAL_REGULAR:
570+
return std::numeric_limits<int>::max() - 2;
571+
case S_THREAD_LOCAL_ZEROFILL:
572+
return std::numeric_limits<int>::max() - 1;
573+
case S_ZEROFILL:
574+
return std::numeric_limits<int>::max();
575+
default:
576+
return 0;
577+
}
560578
} else if (segname == segment_names::linkEdit) {
561579
return StringSwitch<int>(osec->name)
562580
.Case(section_names::rebase, -8)
@@ -571,7 +589,7 @@ static int sectionOrder(OutputSection *osec) {
571589
}
572590
// ZeroFill sections must always be the at the end of their segments,
573591
// otherwise subsequent sections may get overwritten with zeroes at runtime.
574-
if (isZeroFill(osec->flags))
592+
if (sectionType(osec->flags) == S_ZEROFILL)
575593
return std::numeric_limits<int>::max();
576594
return 0;
577595
}
@@ -600,6 +618,9 @@ static void sortSegmentsAndSections() {
600618
if (!osec->isHidden())
601619
osec->index = ++sectionIndex;
602620

621+
if (!firstTLVDataSection && isThreadLocalData(osec->flags))
622+
firstTLVDataSection = osec;
623+
603624
if (!isecPriorities.empty()) {
604625
if (auto *merged = dyn_cast<MergedOutputSection>(osec)) {
605626
llvm::stable_sort(merged->inputs,
@@ -777,3 +798,5 @@ void macho::createSyntheticSections() {
777798
in.stubHelper = make<StubHelperSection>();
778799
in.imageLoaderCache = make<ImageLoaderCacheSection>();
779800
}
801+
802+
OutputSection *macho::firstTLVDataSection = nullptr;

lld/MachO/Writer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
namespace lld {
1515
namespace macho {
1616

17+
class OutputSection;
18+
1719
class LoadCommand {
1820
public:
1921
virtual ~LoadCommand() = default;
@@ -25,6 +27,8 @@ void writeResult();
2527

2628
void createSyntheticSections();
2729

30+
extern OutputSection *firstTLVDataSection;
31+
2832
} // namespace macho
2933
} // namespace lld
3034

lld/test/MachO/bss.s

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
# RUN: %lld -o %t %t.o
44
# RUN: llvm-readobj --section-headers --macho-segment %t | FileCheck %s
55

6-
## Check that __bss takes up zero file size, is at file offset zero, and
7-
## appears at the end of its segment.
6+
## Check that __bss takes up zero file size, is at file offset zero, and appears
7+
## at the end of its segment. Also check that __tbss is placed immediately
8+
## before it.
9+
## Zerofill sections in other segments (i.e. not __DATA) should also be placed
10+
## at the end.
811

912
# CHECK: Index: 1
1013
# CHECK-NEXT: Name: __data
@@ -23,6 +26,22 @@
2326
# CHECK-NEXT: Reserved3: 0x0
2427

2528
# CHECK: Index: 2
29+
# CHECK-NEXT: Name: __thread_bss
30+
# CHECK-NEXT: Segment: __DATA
31+
# CHECK-NEXT: Address:
32+
# CHECK-NEXT: Size: 0x4
33+
# CHECK-NEXT: Offset: 0
34+
# CHECK-NEXT: Alignment: 0
35+
# CHECK-NEXT: RelocationOffset: 0x0
36+
# CHECK-NEXT: RelocationCount: 0
37+
# CHECK-NEXT: Type: ThreadLocalZerofill (0x12)
38+
# CHECK-NEXT: Attributes [ (0x0)
39+
# CHECK-NEXT: ]
40+
# CHECK-NEXT: Reserved1: 0x0
41+
# CHECK-NEXT: Reserved2: 0x0
42+
# CHECK-NEXT: Reserved3: 0x0
43+
44+
# CHECK: Index: 3
2645
# CHECK-NEXT: Name: __bss
2746
# CHECK-NEXT: Segment: __DATA
2847
# CHECK-NEXT: Address:
@@ -38,16 +57,32 @@
3857
# CHECK-NEXT: Reserved2: 0x0
3958
# CHECK-NEXT: Reserved3: 0x0
4059

41-
# CHECK: Index: 3
42-
# CHECK-NEXT: Name: __thread_bss
43-
# CHECK-NEXT: Segment: __DATA
44-
# CHECK-NEXT: Address: 0x100001010
45-
# CHECK-NEXT: Size: 0x4
60+
# CHECK: Index: 4
61+
# CHECK-NEXT: Name: foo
62+
# CHECK-NEXT: Segment: FOO
63+
# CHECK-NEXT: Address:
64+
# CHECK-NEXT: Size: 0x8
65+
# CHECK-NEXT: Offset: 8192
66+
# CHECK-NEXT: Alignment: 0
67+
# CHECK-NEXT: RelocationOffset: 0x0
68+
# CHECK-NEXT: RelocationCount: 0
69+
# CHECK-NEXT: Type: Regular (0x0)
70+
# CHECK-NEXT: Attributes [ (0x0)
71+
# CHECK-NEXT: ]
72+
# CHECK-NEXT: Reserved1: 0x0
73+
# CHECK-NEXT: Reserved2: 0x0
74+
# CHECK-NEXT: Reserved3: 0x0
75+
76+
# CHECK: Index: 5
77+
# CHECK-NEXT: Name: bss
78+
# CHECK-NEXT: Segment: FOO
79+
# CHECK-NEXT: Address:
80+
# CHECK-NEXT: Size: 0x8
4681
# CHECK-NEXT: Offset: 0
4782
# CHECK-NEXT: Alignment: 0
4883
# CHECK-NEXT: RelocationOffset: 0x0
4984
# CHECK-NEXT: RelocationCount: 0
50-
# CHECK-NEXT: Type: ThreadLocalZerofill (0x12)
85+
# CHECK-NEXT: Type: ZeroFill (0x1)
5186
# CHECK-NEXT: Attributes [ (0x0)
5287
# CHECK-NEXT: ]
5388
# CHECK-NEXT: Reserved1: 0x0
@@ -61,6 +96,13 @@
6196
# CHECK-NEXT: fileoff:
6297
# CHECK-NEXT: filesize: 8
6398

99+
# CHECK: Name: FOO
100+
# CHECK-NEXT: Size:
101+
# CHECK-NEXT: vmaddr:
102+
# CHECK-NEXT: vmsize: 0x10
103+
# CHECK-NEXT: fileoff:
104+
# CHECK-NEXT: filesize: 8
105+
64106
.globl _main
65107

66108
.text
@@ -76,3 +118,8 @@ _main:
76118

77119
.data
78120
.quad 0x1234
121+
122+
.zerofill FOO,bss,_zero_foo,0x8
123+
124+
.section FOO,foo
125+
.quad 123

lld/test/MachO/tlv.s

Lines changed: 88 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,75 @@
11
# REQUIRES: x86
2-
# RUN: mkdir -p %t
3-
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t/test.o
2+
# RUN: rm -rf %t; split-file %s %t
3+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/regular.s -o %t/regular.o
4+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/tbss.s -o %t/tbss.o
45

5-
# RUN: %lld -lSystem -o %t/test %t/test.o
6-
# RUN: llvm-readobj --file-headers %t/test | FileCheck %s --check-prefix=HEADER
7-
# RUN: llvm-objdump -D --bind --rebase %t/test | FileCheck %s
6+
# RUN: %lld -lSystem -no_pie -o %t/regular %t/regular.o
7+
# RUN: llvm-readobj --file-headers %t/regular | FileCheck %s --check-prefix=HEADER
8+
# RUN: llvm-objdump -d --bind --rebase %t/regular | FileCheck %s --check-prefixes=REG,LINKEDIT
9+
# RUN: llvm-objdump --macho --section=__DATA,__thread_vars %t/regular | \
10+
# RUN: FileCheck %s --check-prefix=REG-TLVP
811

9-
# RUN: %lld -lSystem -pie -o %t/test %t/test.o
10-
# RUN: llvm-readobj --file-headers %t/test | FileCheck %s --check-prefix=HEADER
11-
# RUN: llvm-objdump -D --bind --rebase %t/test | FileCheck %s
12+
# RUN: %lld -lSystem -pie %t/regular.o -o %t/regular-pie
13+
# RUN: llvm-readobj --file-headers %t/regular-pie | FileCheck %s --check-prefix=HEADER
14+
# RUN: llvm-objdump -d --bind --rebase %t/regular-pie | FileCheck %s --check-prefixes=REG,LINKEDIT
15+
# RUN: llvm-objdump --macho --section=__DATA,__thread_vars %t/regular-pie | \
16+
# RUN: FileCheck %s --check-prefix=REG-TLVP
17+
18+
# RUN: %lld -lSystem %t/tbss.o -o %t/tbss -e _f
19+
# RUN: llvm-objdump -d --bind --rebase %t/tbss | FileCheck %s --check-prefixes=TBSS,LINKEDIT
20+
# RUN: llvm-objdump --macho --section=__DATA,__thread_vars %t/tbss | \
21+
# RUN: FileCheck %s --check-prefix=TBSS-TLVP
22+
23+
# RUN: %lld -lSystem %t/regular.o %t/tbss.o -o %t/regular-and-tbss
24+
# RUN: llvm-objdump -d --bind --rebase %t/regular-and-tbss | FileCheck %s --check-prefixes=REG,TBSS,LINKEDIT
25+
# RUN: llvm-objdump --macho --section=__DATA,__thread_vars %t/regular-and-tbss | \
26+
# RUN: FileCheck %s --check-prefix=REG-TBSS-TLVP
27+
# RUN: llvm-objdump --section-headers %t/regular-and-tbss | FileCheck %s --check-prefix=SECTION-ORDER
28+
29+
## Check that we always put __thread_bss immediately after __thread_data,
30+
## regardless of the order of the input files.
31+
# RUN: %lld -lSystem %t/tbss.o %t/regular.o -o %t/regular-and-tbss
32+
# RUN: llvm-objdump --section-headers %t/regular-and-tbss | FileCheck %s --check-prefix=SECTION-ORDER
1233

1334
# HEADER: MH_HAS_TLV_DESCRIPTORS
1435

15-
# CHECK: Disassembly of section __TEXT,__text:
16-
# CHECK-EMPTY:
17-
# CHECK-NEXT: <_main>:
18-
# CHECK-NEXT: leaq {{.*}}(%rip), %rax # {{.*}} <_foo>
19-
# CHECK-NEXT: leaq {{.*}}(%rip), %rax # {{.*}} <_bar>
20-
# CHECK-NEXT: retq
21-
# CHECK-EMPTY:
22-
# CHECK-NEXT: Disassembly of section __DATA,__thread_data:
23-
# CHECK-EMPTY:
24-
# CHECK-NEXT: <_foo$tlv$init>:
25-
# CHECK-NEXT: 00 00
26-
# CHECK-NEXT: 00 00
27-
# CHECK-EMPTY:
28-
# CHECK-NEXT: <_bar$tlv$init>:
29-
# CHECK-NEXT: 00 00
30-
# CHECK-NEXT: 00 00
31-
# CHECK-EMPTY:
32-
# CHECK-NEXT: Disassembly of section __DATA,__thread_vars:
33-
# CHECK-EMPTY:
34-
# CHECK-NEXT: <_foo>:
35-
# CHECK-NEXT: ...
36-
# CHECK-EMPTY:
37-
# CHECK-NEXT: <_bar>:
38-
# CHECK-NEXT: ...
39-
# CHECK-NEXT: 04 00
40-
# CHECK-NEXT: 00 00
41-
# CHECK-NEXT: 00 00
42-
# CHECK-NEXT: 00 00
36+
# REG: <_main>:
37+
# REG-NEXT: leaq {{.*}}(%rip), %rax # {{.*}} <_foo>
38+
# REG-NEXT: leaq {{.*}}(%rip), %rax # {{.*}} <_bar>
39+
# REG-NEXT: retq
40+
41+
# TBSS: <_f>:
42+
# TBSS-NEXT: leaq {{.*}}(%rip), %rax # {{.*}} <_baz>
43+
# TBSS-NEXT: leaq {{.*}}(%rip), %rax # {{.*}} <_qux>
44+
# TBSS-NEXT: retq
45+
46+
# REG-TLVP: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
47+
# REG-TLVP-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
48+
# REG-TLVP-NEXT: 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00
49+
50+
# TBSS-TLVP: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
51+
# TBSS-TLVP-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
52+
# TBSS-TLVP-NEXT: 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00
53+
54+
# REG-TBSS-TLVP: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
55+
# REG-TBSS-TLVP-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
56+
# REG-TBSS-TLVP-NEXT: 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00
57+
# REG-TBSS-TLVP-NEXT: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
58+
# REG-TBSS-TLVP-NEXT: 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
59+
# REG-TBSS-TLVP-NEXT: 00 00 00 00 00 00 00 00 18 00 00 00 00 00 00 00
4360

4461
## Make sure we don't emit rebase opcodes for relocations in __thread_vars.
45-
# CHECK: Rebase table:
46-
# CHECK-NEXT: segment section address type
47-
# CHECK-NEXT: Bind table:
48-
# CHECK: __DATA __thread_vars 0x{{[0-9a-f]*}} pointer 0 libSystem __tlv_bootstrap
49-
# CHECK: __DATA __thread_vars 0x{{[0-9a-f]*}} pointer 0 libSystem __tlv_bootstrap
62+
# LINKEDIT: Rebase table:
63+
# LINKEDIT-NEXT: segment section address type
64+
# LINKEDIT-NEXT: Bind table:
65+
# LINKEDIT: __DATA __thread_vars 0x{{[0-9a-f]*}} pointer 0 libSystem __tlv_bootstrap
66+
# LINKEDIT: __DATA __thread_vars 0x{{[0-9a-f]*}} pointer 0 libSystem __tlv_bootstrap
67+
68+
# SECTION-ORDER: __thread_data
69+
# SECTION-ORDER: more_thread_data
70+
# SECTION-ORDER-NEXT: __thread_bss
5071

72+
#--- regular.s
5173
.globl _main
5274
_main:
5375
mov _foo@TLVP(%rip), %rax
@@ -56,9 +78,11 @@ _main:
5678

5779
.section __DATA,__thread_data,thread_local_regular
5880
_foo$tlv$init:
59-
.space 4
81+
.quad 123
82+
83+
.section __DATA,more_thread_data,thread_local_regular
6084
_bar$tlv$init:
61-
.space 4
85+
.quad 123
6286

6387
.section __DATA,__thread_vars,thread_local_variables
6488
.globl _foo, _bar
@@ -70,3 +94,24 @@ _bar:
7094
.quad __tlv_bootstrap
7195
.quad 0
7296
.quad _bar$tlv$init
97+
98+
#--- tbss.s
99+
100+
.globl _f
101+
_f:
102+
mov _baz@TLVP(%rip), %rax
103+
mov _qux@TLVP(%rip), %rax
104+
ret
105+
106+
.tbss _baz$tlv$init, 8, 3
107+
.tbss _qux$tlv$init, 8, 3
108+
109+
.section __DATA,__thread_vars,thread_local_variables
110+
_baz:
111+
.quad __tlv_bootstrap
112+
.quad 0
113+
.quad _baz$tlv$init
114+
_qux:
115+
.quad __tlv_bootstrap
116+
.quad 0
117+
.quad _qux$tlv$init

0 commit comments

Comments
 (0)