[lld] bb62ef9 - [lld-macho] Ensure segments are laid out contiguously

Jez Ng via llvm-commits llvm-commits at lists.llvm.org
Tue Apr 20 13:59:09 PDT 2021


Author: Jez Ng
Date: 2021-04-20T16:58:57-04:00
New Revision: bb62ef9943008281a2223c942c71e67b3902a07e

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

LOG: [lld-macho] Ensure segments are laid out contiguously

codesign/libstuff checks that the `__LLVM` segment is directly
before `__LINKEDIT` by checking that `fileOff + fileSize == next segment
fileOff`. Previously, there would be gaps between the segments due to
the fact that their fileOffs are page-aligned but their fileSizes
aren't. In order to satisfy codesign, we page-align fileOff *before*
calculating fileSize. (I don't think codesign checks for the relative
ordering of other segments, so in theory we could do this just for
`__LLVM`, but ld64 seems to do it for all segments.)

Note that we *don't* round up the fileSize of the `__LINKEDIT` segment.
Since it's the last segment, it doesn't need to worry about contiguity;
in addition, codesign checks that the last (hidden) section in
`__LINKEDIT` covers the last byte of the segment, so if we rounded up
`__LINKEDIT`'s size we would have to do the same for its last section,
which is a bother.

While at it, I also addressed a FIXME in the linkedit-contiguity.s test
to cover more `__LINKEDIT` sections.

Reviewed By: #lld-macho, thakis, alexshap

Differential Revision: https://reviews.llvm.org/D100848

Added: 
    

Modified: 
    lld/MachO/OutputSegment.h
    lld/MachO/Writer.cpp
    lld/test/MachO/bitcode-bundle.ll
    lld/test/MachO/bss.s
    lld/test/MachO/linkedit-contiguity.s
    lld/test/MachO/section-headers.s
    lld/test/MachO/segments.s

Removed: 
    


################################################################################
diff  --git a/lld/MachO/OutputSegment.h b/lld/MachO/OutputSegment.h
index 559aae2af75b9..1db3f0ac79a3b 100644
--- a/lld/MachO/OutputSegment.h
+++ b/lld/MachO/OutputSegment.h
@@ -47,6 +47,7 @@ class OutputSegment {
 
   uint64_t fileOff = 0;
   uint64_t fileSize = 0;
+  uint64_t vmSize = 0;
   StringRef name;
   uint32_t maxProt = 0;
   uint32_t initProt = 0;

diff  --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp
index 6ba7ed0eaba9d..b2ce72e84fc3f 100644
--- a/lld/MachO/Writer.cpp
+++ b/lld/MachO/Writer.cpp
@@ -197,17 +197,11 @@ template <class LP> class LCSegment : public LoadCommand {
       return;
 
     c->vmaddr = seg->firstSection()->addr;
-    c->vmsize =
-        seg->lastSection()->addr + seg->lastSection()->getSize() - c->vmaddr;
+    c->vmsize = seg->vmSize;
+    c->filesize = seg->fileSize;
     c->nsects = seg->numNonHiddenSections();
 
     for (const OutputSection *osec : seg->getSections()) {
-      if (!isZeroFill(osec->flags)) {
-        assert(osec->fileOff >= seg->fileOff);
-        c->filesize = std::max<uint64_t>(
-            c->filesize, osec->fileOff + osec->getFileSize() - seg->fileOff);
-      }
-
       if (osec->isHidden())
         continue;
 
@@ -682,6 +676,7 @@ static int segmentOrder(OutputSegment *seg) {
       .Case(segment_names::text, -3)
       .Case(segment_names::dataConst, -2)
       .Case(segment_names::data, -1)
+      .Case(segment_names::llvm, std::numeric_limits<int>::max() - 1)
       // Make sure __LINKEDIT is the last segment (i.e. all its hidden
       // sections must be ordered after other sections).
       .Case(segment_names::linkEdit, std::numeric_limits<int>::max())
@@ -855,15 +850,26 @@ template <class LP> void Writer::createOutputSections() {
 
 void Writer::finalizeAddresses() {
   TimeTraceScope timeScope("Finalize addresses");
+  uint64_t pageSize = target->getPageSize();
   // Ensure that segments (and the sections they contain) are allocated
   // addresses in ascending order, which dyld requires.
   //
   // Note that at this point, __LINKEDIT sections are empty, but we need to
   // determine addresses of other segments/sections before generating its
   // contents.
-  for (OutputSegment *seg : outputSegments)
-    if (seg != linkEditSegment)
-      assignAddresses(seg);
+  for (OutputSegment *seg : outputSegments) {
+    if (seg == linkEditSegment)
+      continue;
+    assignAddresses(seg);
+    // codesign / libstuff checks for segment ordering by verifying that
+    // `fileOff + fileSize == next segment fileOff`. So we call alignTo() before
+    // (instead of after) computing fileSize to ensure that the segments are
+    // contiguous. We handle addr / vmSize similarly for the same reason.
+    fileOff = alignTo(fileOff, pageSize);
+    addr = alignTo(addr, pageSize);
+    seg->vmSize = addr - seg->firstSection()->addr;
+    seg->fileSize = fileOff - seg->fileOff;
+  }
 
   // FIXME(gkm): create branch-extension thunks here, then adjust addresses
 }
@@ -883,12 +889,12 @@ void Writer::finalizeLinkEditSegment() {
   // Now that __LINKEDIT is filled out, do a proper calculation of its
   // addresses and offsets.
   assignAddresses(linkEditSegment);
+  // No need to page-align fileOff / addr here since this is the last segment.
+  linkEditSegment->vmSize = addr - linkEditSegment->firstSection()->addr;
+  linkEditSegment->fileSize = fileOff - linkEditSegment->fileOff;
 }
 
 void Writer::assignAddresses(OutputSegment *seg) {
-  uint64_t pageSize = target->getPageSize();
-  addr = alignTo(addr, pageSize);
-  fileOff = alignTo(fileOff, pageSize);
   seg->fileOff = fileOff;
 
   for (OutputSection *osec : seg->getSections()) {
@@ -903,7 +909,6 @@ void Writer::assignAddresses(OutputSegment *seg) {
     addr += osec->getSize();
     fileOff += osec->getFileSize();
   }
-  seg->fileSize = fileOff - seg->fileOff;
 }
 
 void Writer::openFile() {

diff  --git a/lld/test/MachO/bitcode-bundle.ll b/lld/test/MachO/bitcode-bundle.ll
index 7daf96fc24c09..5deedbfb7b952 100644
--- a/lld/test/MachO/bitcode-bundle.ll
+++ b/lld/test/MachO/bitcode-bundle.ll
@@ -4,6 +4,7 @@
 ; RUN: opt -module-summary %t/foo.ll -o %t/foo.o
 ; RUN: %lld -lSystem -bitcode_bundle %t/test.o %t/foo.o -o %t/test
 ; RUN: llvm-objdump --macho --section=__LLVM,__bundle %t/test | FileCheck %s
+; RUN: llvm-readobj --macho-segment %t/test | FileCheck %s --check-prefix=SEGMENT
 
 ; CHECK:      Contents of (__LLVM,__bundle) section
 ; CHECK-NEXT: For (__LLVM,__bundle) section: xar header
@@ -25,6 +26,32 @@
 ; CHECK-NEXT:  </toc>
 ; CHECK-NEXT: </xar>
 
+;; __LLVM must directly precede __LINKEDIT.
+; SEGMENT:        Name: __LLVM
+; SEGMENT-NEXT:   Size: 152
+; SEGMENT-NEXT:   vmaddr: 0x[[#%X,LLVM_ADDR:]]
+; SEGMENT-NEXT:   vmsize: 0x[[#%X,LLVM_VMSIZE:]]
+; SEGMENT-NEXT:   fileoff: [[#LLVM_OFF:]]
+; SEGMENT-NEXT:   filesize: [[#LLVM_FILESIZE:]]
+; SEGMENT-NEXT:   maxprot: rw-
+; SEGMENT-NEXT:   initprot: rw-
+; SEGMENT-NEXT:   nsects: 1
+; SEGMENT-NEXT:   flags: 0x0
+; SEGMENT-NEXT: }
+; SEGMENT-NEXT: Segment {
+; SEGMENT-NEXT:   Cmd: LC_SEGMENT_64
+; SEGMENT-NEXT:   Name: __LINKEDIT
+; SEGMENT-NEXT:   Size: 72
+; SEGMENT-NEXT:   vmaddr: 0x[[#LLVM_ADDR + LLVM_VMSIZE]]
+; SEGMENT-NEXT:   vmsize:
+; SEGMENT-NEXT:   fileoff: [[#LLVM_OFF + LLVM_FILESIZE]]
+; SEGMENT-NEXT:   filesize:
+; SEGMENT-NEXT:   maxprot: r--
+; SEGMENT-NEXT:   initprot: r--
+; SEGMENT-NEXT:   nsects: 0
+; SEGMENT-NEXT:   flags: 0x0
+; SEGMENT-NEXT: }
+
 ;--- foo.ll
 target triple = "x86_64-apple-darwin"
 target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"

diff  --git a/lld/test/MachO/bss.s b/lld/test/MachO/bss.s
index 480065be550d9..d773e6762b14c 100644
--- a/lld/test/MachO/bss.s
+++ b/lld/test/MachO/bss.s
@@ -45,7 +45,7 @@
 # CHECK-NEXT:   Name: __bss
 # CHECK-NEXT:   Segment: __DATA
 # CHECK-NEXT:   Address:
-# CHECK-NEXT:   Size: 0x8
+# CHECK-NEXT:   Size: 0x10000
 # CHECK-NEXT:   Offset: 0
 # CHECK-NEXT:   Alignment: 0
 # CHECK-NEXT:   RelocationOffset: 0x0
@@ -92,16 +92,16 @@
 # CHECK:      Name: __DATA
 # CHECK-NEXT: Size:
 # CHECK-NEXT: vmaddr:
-# CHECK-NEXT: vmsize: 0x14
+# CHECK-NEXT: vmsize: 0x11000
 # CHECK-NEXT: fileoff:
-# CHECK-NEXT: filesize: 8
+# CHECK-NEXT: filesize: 4096
 
 # CHECK:      Name: FOO
 # CHECK-NEXT: Size:
 # CHECK-NEXT: vmaddr:
-# CHECK-NEXT: vmsize: 0x10
+# CHECK-NEXT: vmsize: 0x9000
 # CHECK-NEXT: fileoff:
-# CHECK-NEXT: filesize: 8
+# CHECK-NEXT: filesize: 4096
 
 .globl _main
 
@@ -111,15 +111,15 @@ _main:
   retq
 
 .bss
-.zero 4
+.zero 0x8000
 
 .tbss _foo, 4
-.zero 4
+.zero 0x8000
 
 .data
 .quad 0x1234
 
-.zerofill FOO,bss,_zero_foo,0x8
+.zerofill FOO,bss,_zero_foo,0x8000
 
 .section FOO,foo
 .quad 123

diff  --git a/lld/test/MachO/linkedit-contiguity.s b/lld/test/MachO/linkedit-contiguity.s
index 3fb9053d4828a..63a11dbc7551a 100644
--- a/lld/test/MachO/linkedit-contiguity.s
+++ b/lld/test/MachO/linkedit-contiguity.s
@@ -1,33 +1,36 @@
 # REQUIRES: x86
-# RUN: mkdir -p %t
+# RUN: rm -rf %t; split-file %s %t
 
 ## codesign requires that each setion in __LINKEDIT ends where the next one
 ## starts. This test enforces that invariant.
-## TODO: Test other __LINKEDIT sections here as support for them gets added.
-## Examples of such sections include the data for LC_CODE_SIGNATURE and
-## LC_DATA_IN_CODE.
+## It also checks that the last section in __LINKEDIT covers the last byte of
+## the segment.
 
-# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %p/Inputs/libhello.s \
-# RUN:   -o %t/libhello.o
-# RUN: %lld -dylib \
-# RUN:   -install_name @executable_path/libhello.dylib %t/libhello.o \
-# RUN:   -o %t/libhello.dylib
+## FIXME: Include LC_DATA_IN_CODE in this test when we add support for it.
 
-# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t/test.o
-# RUN: %lld -o %t/test \
-# RUN:   -L%t -lhello %t/test.o -lSystem
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/foo.s -o %t/foo.o
+# RUN: %lld %t/foo.o -dylib -o %t/libfoo.dylib
+
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/test.s -o %t/test.o
+# RUN: %lld -lSystem -pie -adhoc_codesign -o %t/test %t/libfoo.dylib %t/test.o
 
 # RUN: llvm-objdump --macho --all-headers %t/test | FileCheck %s
 
+# CHECK:      segname __LINKEDIT
+# CHECK-NEXT: vmaddr
+# CHECK-NEXT: vmsize
+# CHECK-NEXT: fileoff [[#LINKEDIT_OFF:]]
+# CHECK-NEXT: filesize [[#LINKEDIT_SIZE:]]
+
 # CHECK:      cmd LC_DYLD_INFO_ONLY
 # CHECK-NEXT: cmdsize 48
-# CHECK-NEXT: rebase_off 0
-# CHECK-NEXT: rebase_size 0
-# CHECK-NEXT: bind_off [[#BIND_OFF:]]
+# CHECK-NEXT: rebase_off [[#REBASE_OFF:]]
+# CHECK-NEXT: rebase_size [[#REBASE_SIZE:]]
+# CHECK-NEXT: bind_off [[#BIND_OFF: REBASE_OFF + REBASE_SIZE]]
 # CHECK-NEXT: bind_size [[#BIND_SIZE:]]
-# CHECK-NEXT: weak_bind_off 0
-# CHECK-NEXT: weak_bind_size 0
-# CHECK-NEXT: lazy_bind_off [[#LAZY_OFF: BIND_OFF + BIND_SIZE]]
+# CHECK-NEXT: weak_bind_off [[#WEAK_OFF: BIND_OFF + BIND_SIZE]]
+# CHECK-NEXT: weak_bind_size [[#WEAK_SIZE:]]
+# CHECK-NEXT: lazy_bind_off [[#LAZY_OFF: WEAK_OFF + WEAK_SIZE]]
 # CHECK-NEXT: lazy_bind_size [[#LAZY_SIZE:]]
 # CHECK-NEXT: export_off [[#EXPORT_OFF: LAZY_OFF + LAZY_SIZE]]
 # CHECK-NEXT: export_size [[#EXPORT_SIZE:]]
@@ -36,10 +39,20 @@
 # CHECK-NEXT: cmdsize
 # CHECK-NEXT: dataoff [[#FUNCSTARTS_OFF: EXPORT_OFF + EXPORT_SIZE]]
 
-.text
+# CHECK:      cmd LC_CODE_SIGNATURE
+# CHECK-NEXT: cmdsize 16
+# CHECK-NEXT: dataoff [[#SIG_OFF:]]
+# CHECK-NEXT: datasize [[#SIG_SIZE: LINKEDIT_OFF + LINKEDIT_SIZE - SIG_OFF]]
+
+#--- foo.s
+.globl _foo, _weak_foo
+.weak_definition _weak_foo
+_foo:
+_weak_foo:
+
+#--- test.s
 .globl _main
 _main:
-  sub $8, %rsp # 16-byte-align the stack; dyld checks for this
-  callq _print_hello
-  add $8, %rsp
+  callq _foo
+  callq _weak_foo
   ret

diff  --git a/lld/test/MachO/section-headers.s b/lld/test/MachO/section-headers.s
index f44f0398c0346..c8a9689d35028 100644
--- a/lld/test/MachO/section-headers.s
+++ b/lld/test/MachO/section-headers.s
@@ -26,8 +26,8 @@
 # CHECK:      Name: maxlen_16ch_name
 # CHECK-NEXT: Segment: __TEXT
 # CHECK-NEXT: Address:
-# CHECK-NEXT: Size: [[#%x, LAST_SEC_SIZE:]]
-# CHECK-NEXT: Offset: [[#%u, LAST_SEC_OFF:]]
+# CHECK-NEXT: Size:
+# CHECK-NEXT: Offset:
 # CHECK-NEXT: Alignment: 3
 # CHECK-NOT:  }
 # CHECK:      Type: Regular (0x0)
@@ -38,7 +38,7 @@
 # CHECK-NEXT:  vmaddr:
 # CHECK-NEXT:  vmsize:
 # CHECK-NEXT:  fileoff: 0
-# CHECK-NEXT:  filesize: [[#%u, LAST_SEC_SIZE + LAST_SEC_OFF]]
+# CHECK-NEXT:  filesize: 4096
 
 .text
 .align 1

diff  --git a/lld/test/MachO/segments.s b/lld/test/MachO/segments.s
index e63848baf3a44..b167813d40188 100644
--- a/lld/test/MachO/segments.s
+++ b/lld/test/MachO/segments.s
@@ -13,7 +13,7 @@
 # RUN: llvm-readobj --macho-segment %t/arm64_32 > %t/arm64-32.out
 # RUN: echo "Total file size" >> %t/arm64-32.out
 # RUN: wc -c %t/arm64_32 >> %t/arm64-32.out
-# RUN: FileCheck %s -DSUFFIX= -DPAGEZERO_SIZE=0x1000 -DTEXT_ADDR=0x4000 < %t/arm64-32.out
+# RUN: FileCheck %s -DSUFFIX= -DPAGEZERO_SIZE=0x4000 -DTEXT_ADDR=0x4000 < %t/arm64-32.out
 
 ## These two segments must always be present at the start of an executable.
 # CHECK-NOT:  Segment {


        


More information about the llvm-commits mailing list