mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-25 18:16:04 +00:00
[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
This commit is contained in:
parent
1aa29dffce
commit
bb62ef9943
@ -47,6 +47,7 @@ public:
|
||||
|
||||
uint64_t fileOff = 0;
|
||||
uint64_t fileSize = 0;
|
||||
uint64_t vmSize = 0;
|
||||
StringRef name;
|
||||
uint32_t maxProt = 0;
|
||||
uint32_t initProt = 0;
|
||||
|
@ -197,17 +197,11 @@ public:
|
||||
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() {
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user