forked from OSchip/llvm-project
[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
This commit is contained in:
parent
756dd70766
commit
daaaed6bb8
|
@ -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,12 @@ void InputSection::writeTo(uint8_t *buf) {
|
|||
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 thread-local data memory area, which
|
||||
// is initialized via copying all the TLV data sections (which are all
|
||||
// contiguous).
|
||||
if (auto *defined = dyn_cast<Defined>(referentSym))
|
||||
referentVA -= defined->isec->parent->addr;
|
||||
referentVA -= firstTLVDataSection->addr;
|
||||
}
|
||||
} else if (auto *referentIsec = r.referent.dyn_cast<InputSection *>()) {
|
||||
referentVA = referentIsec->getVA();
|
||||
|
|
|
@ -60,13 +60,22 @@ public:
|
|||
std::vector<Reloc> 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_REGULAR ||
|
||||
sectionType(flags) == llvm::MachO::S_THREAD_LOCAL_ZEROFILL;
|
||||
}
|
||||
|
||||
inline bool isDebugSection(uint32_t flags) {
|
||||
|
|
|
@ -557,6 +557,24 @@ static int sectionOrder(OutputSection *osec) {
|
|||
.Case(section_names::unwindInfo, std::numeric_limits<int>::max() - 1)
|
||||
.Case(section_names::ehFrame, std::numeric_limits<int>::max())
|
||||
.Default(0);
|
||||
} else if (segname == segment_names::data) {
|
||||
// For each thread spawned, dyld will initialize its TLVs by copying the
|
||||
// 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<int>::max() - 2;
|
||||
case S_THREAD_LOCAL_ZEROFILL:
|
||||
return std::numeric_limits<int>::max() - 1;
|
||||
case S_ZEROFILL:
|
||||
return std::numeric_limits<int>::max();
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
} else if (segname == segment_names::linkEdit) {
|
||||
return StringSwitch<int>(osec->name)
|
||||
.Case(section_names::rebase, -8)
|
||||
|
@ -571,7 +589,7 @@ static int sectionOrder(OutputSection *osec) {
|
|||
}
|
||||
// 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<int>::max();
|
||||
return 0;
|
||||
}
|
||||
|
@ -600,6 +618,9 @@ static void sortSegmentsAndSections() {
|
|||
if (!osec->isHidden())
|
||||
osec->index = ++sectionIndex;
|
||||
|
||||
if (!firstTLVDataSection && isThreadLocalData(osec->flags))
|
||||
firstTLVDataSection = osec;
|
||||
|
||||
if (!isecPriorities.empty()) {
|
||||
if (auto *merged = dyn_cast<MergedOutputSection>(osec)) {
|
||||
llvm::stable_sort(merged->inputs,
|
||||
|
@ -777,3 +798,5 @@ void macho::createSyntheticSections() {
|
|||
in.stubHelper = make<StubHelperSection>();
|
||||
in.imageLoaderCache = make<ImageLoaderCacheSection>();
|
||||
}
|
||||
|
||||
OutputSection *macho::firstTLVDataSection = nullptr;
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
namespace lld {
|
||||
namespace macho {
|
||||
|
||||
class OutputSection;
|
||||
|
||||
class LoadCommand {
|
||||
public:
|
||||
virtual ~LoadCommand() = default;
|
||||
|
@ -25,6 +27,8 @@ void writeResult();
|
|||
|
||||
void createSyntheticSections();
|
||||
|
||||
extern OutputSection *firstTLVDataSection;
|
||||
|
||||
} // namespace macho
|
||||
} // namespace lld
|
||||
|
||||
|
|
|
@ -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 @@ _main:
|
|||
|
||||
.data
|
||||
.quad 0x1234
|
||||
|
||||
.zerofill FOO,bss,_zero_foo,0x8
|
||||
|
||||
.section FOO,foo
|
||||
.quad 123
|
||||
|
|
|
@ -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 @@ _main:
|
|||
|
||||
.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 @@ _bar:
|
|||
.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
|
||||
|
|
Loading…
Reference in New Issue