[llvm-branch-commits] [lld] daaaed6 - [lld-macho] Fix TLV data initialization

Jez Ng via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Jan 8 15:52:21 PST 2021


Author: Jez Ng
Date: 2021-01-08T18:48:12-05:00
New Revision: daaaed6bb89044ac58a23f1bb1ccdd12342a5a58

URL: https://github.com/llvm/llvm-project/commit/daaaed6bb89044ac58a23f1bb1ccdd12342a5a58
DIFF: https://github.com/llvm/llvm-project/commit/daaaed6bb89044ac58a23f1bb1ccdd12342a5a58.diff

LOG: [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

Added: 
    

Modified: 
    lld/MachO/InputSection.cpp
    lld/MachO/InputSection.h
    lld/MachO/Writer.cpp
    lld/MachO/Writer.h
    lld/test/MachO/bss.s
    lld/test/MachO/tlv.s

Removed: 
    


################################################################################
diff  --git a/lld/MachO/InputSection.cpp b/lld/MachO/InputSection.cpp
index 8477d78d2f3f..4cb322a058f9 100644
--- 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,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();

diff  --git a/lld/MachO/InputSection.h b/lld/MachO/InputSection.h
index 5829936ece29..00b523fb8d46 100644
--- a/lld/MachO/InputSection.h
+++ b/lld/MachO/InputSection.h
@@ -60,13 +60,22 @@ class InputSection {
   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) {

diff  --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp
index c9d700658371..53267c072c59 100644
--- a/lld/MachO/Writer.cpp
+++ b/lld/MachO/Writer.cpp
@@ -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;

diff  --git a/lld/MachO/Writer.h b/lld/MachO/Writer.h
index 7f846233107a..88baa8a1e4bb 100644
--- 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 writeResult();
 
 void createSyntheticSections();
 
+extern OutputSection *firstTLVDataSection;
+
 } // namespace macho
 } // namespace lld
 

diff  --git a/lld/test/MachO/bss.s b/lld/test/MachO/bss.s
index 0a036ae18b0b..480065be550d 100644
--- 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 @@ _main:
 
 .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
index d8d0a950d794..1e1835bf78b9 100644
--- 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 at 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 at TLVP(%rip), %rax
+  mov _qux at 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


        


More information about the llvm-branch-commits mailing list