diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -2624,22 +2624,47 @@ if (p->p_type == PT_LOAD && (p->p_flags & PF_X)) lastRX = p; - // Layout SHF_ALLOC sections before non-SHF_ALLOC sections. A non-SHF_ALLOC - // will not occupy file offsets contained by a PT_LOAD. - for (OutputSection *sec : outputSections) { - if (!(sec->flags & SHF_ALLOC)) - continue; - off = setFileOffset(sec, off); + // Place output sections which are part of load segments first, but include + // empty sections in address order. This means that section order does not + // need to match segment order (which can happen when using a link script) + // while avoiding confusing discontinuities in file offsets. + for (Partition &part : partitions) { + for (PhdrEntry *p : part.phdrs) { + if (p->p_type != PT_LOAD) + continue; - // If this is a last section of the last executable segment and that - // segment is the last loadable segment, align the offset of the - // following section to avoid loading non-segments parts of the file. - if (config->zSeparate != SeparateSegmentKind::None && lastRX && - lastRX->lastSec == sec) - off = alignTo(off, config->commonPageSize); + // Write out sections which have no file data to maintain section order + // as much as possible. Do this until we encounter a section with a + // different load segment to the one we're currently writing. + bool write_empty = true; + for (OutputSection *sec : outputSections) { + if (sec->offset) + continue; + if (sec->ptLoad && sec->ptLoad != p) { + write_empty = false; + continue; + } + if (write_empty && (sec->type == SHT_NOBITS || !sec->size)) { + off = setFileOffset(sec, off); + continue; + } + if (sec->ptLoad != p) + continue; + off = setFileOffset(sec, off); + + // If this is a last section of the last executable segment and that + // segment is the last loadable segment, align the offset of the + // following section to avoid loading non-segments parts of the file. + if (config->zSeparate != SeparateSegmentKind::None && lastRX && + lastRX->lastSec == sec) + off = alignTo(off, config->commonPageSize); + } + } } + + // Then place remaining sections for (OutputSection *sec : outputSections) - if (!(sec->flags & SHF_ALLOC)) + if (!sec->offset) off = setFileOffset(sec, off); sectionHeaderOff = alignTo(off, config->wordsize); diff --git a/lld/test/ELF/linkerscript/implicit-program-header.test b/lld/test/ELF/linkerscript/implicit-program-header.test --- a/lld/test/ELF/linkerscript/implicit-program-header.test +++ b/lld/test/ELF/linkerscript/implicit-program-header.test @@ -7,7 +7,7 @@ # RUN: llvm-readelf -l %t1 | FileCheck %s # CHECK: Segment Sections... -# CHECK-NEXT: 00 .dynsym .hash .dynstr .foo .text .dynamic +# CHECK-NEXT: 00 .dynsym .hash .dynstr .text .dynamic # CHECK-NEXT: 01 .foo # CHECK-NEXT: 02 .foo diff --git a/lld/test/ELF/linkerscript/out-of-order-sections.s b/lld/test/ELF/linkerscript/out-of-order-sections.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/linkerscript/out-of-order-sections.s @@ -0,0 +1,49 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: echo "PHDRS { \ +# RUN: ph1 PT_LOAD; \ +# RUN: ph2 PT_LOAD; } \ +# RUN: SECTIONS { \ +# RUN: .text 0 : {*(.text*)} :ph1 \ +# RUN: .foo 0x4000 : {*(.foo*)} :ph2 \ +# RUN: .bar 0x1000 : {*(.bar*)} :ph1 }" > %t.script +# RUN: ld.lld -o %t1 --script %t.script %t +# RUN: llvm-readobj -l %t1 | FileCheck %s + +# CHECK: ProgramHeaders [ +# CHECK-NEXT: ProgramHeader { +# CHECK-NEXT: Type: PT_LOAD (0x1) +# CHECK-NEXT: Offset: 0x1000 +# CHECK-NEXT: VirtualAddress: 0x0 +# CHECK-NEXT: PhysicalAddress: 0x0 +# CHECK-NEXT: FileSize: 4097 +# CHECK-NEXT: MemSize: 4097 +# CHECK-NEXT: Flags [ (0x5) +# CHECK-NEXT: PF_R (0x4) +# CHECK-NEXT: PF_X (0x1) +# CHECK-NEXT: ] +# CHECK-NEXT: Alignment: 4096 +# CHECK-NEXT: } +# CHECK-NEXT: ProgramHeader { +# CHECK-NEXT: Type: PT_LOAD (0x1) +# CHECK-NEXT: Offset: 0x3000 +# CHECK-NEXT: VirtualAddress: 0x4000 +# CHECK-NEXT: PhysicalAddress: 0x4000 +# CHECK-NEXT: FileSize: 1 +# CHECK-NEXT: MemSize: 1 +# CHECK-NEXT: Flags [ (0x4) +# CHECK-NEXT: PF_R (0x4) +# CHECK-NEXT: ] +# CHECK-NEXT: Alignment: 4096 +# CHECK-NEXT: } +# CHECK-NEXT: ] + +.global _start +_start: + nop + +.section .foo, "a" + .byte 0 + +.section .bar, "a" + .byte 0 diff --git a/lld/test/ELF/partition-notes.s b/lld/test/ELF/partition-notes.s --- a/lld/test/ELF/partition-notes.s +++ b/lld/test/ELF/partition-notes.s @@ -37,7 +37,7 @@ // CHECK-NEXT: Owner: GNU // CHECK-NEXT: Data size: // CHECK-NEXT: Type: NT_GNU_BUILD_ID (unique build ID bitstring) -// CHECK-NEXT: Build ID: 08b93eab87177a2356d1b0d1148339463f98dac2 +// CHECK-NEXT: Build ID: 90633653e589c06ee0c7e99559054d460cfc4294 // CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: ] diff --git a/lld/test/ELF/partition-synthetic-sections.s b/lld/test/ELF/partition-synthetic-sections.s --- a/lld/test/ELF/partition-synthetic-sections.s +++ b/lld/test/ELF/partition-synthetic-sections.s @@ -190,9 +190,9 @@ // FILL-NEXT: * // FILL-NEXT: 002000 -// FILL: 004010 cccc cccc cccc cccc cccc cccc cccc cccc +// FILL: 005010 cccc cccc cccc cccc cccc cccc cccc cccc // FILL-NEXT: * -// FILL-NEXT: 005000 +// FILL-NEXT: 006000 .section .llvm_sympart,"",@llvm_sympart .asciz "part1"