diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -1179,7 +1179,11 @@ Out::elfHeader->ptLoad = nullptr; Out::programHeaders->ptLoad = nullptr; - firstPTLoad->firstSec = findFirstSection(firstPTLoad); + llvm::erase_if(firstPTLoad->sections, [](const OutputSection *s) { + return s == Out::elfHeader || s == Out::programHeaders; + }); + firstPTLoad->firstSec = + firstPTLoad->sections.empty() ? nullptr : firstPTLoad->sections.front(); llvm::erase_if(phdrs, [](const PhdrEntry *e) { return e->p_type == PT_PHDR; }); diff --git a/lld/ELF/Writer.h b/lld/ELF/Writer.h --- a/lld/ELF/Writer.h +++ b/lld/ELF/Writer.h @@ -33,6 +33,7 @@ : p_align(type == llvm::ELF::PT_LOAD ? config->maxPageSize : 0), p_type(type), p_flags(flags) {} void add(OutputSection *sec); + void finalize(); uint64_t p_paddr = 0; uint64_t p_vaddr = 0; @@ -43,6 +44,7 @@ uint32_t p_type = 0; uint32_t p_flags = 0; + std::vector sections; OutputSection *firstSec = nullptr; OutputSection *lastSec = nullptr; bool hasLMA = false; diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1078,6 +1078,17 @@ p_align = std::max(p_align, sec->alignment); if (p_type == PT_LOAD) sec->ptLoad = this; + sections.push_back(sec); +} + +void PhdrEntry::finalize() { + if (sections.empty()) + return; + llvm::stable_sort(sections, [](OutputSection *l, OutputSection *r) { + return l->addr < r->addr; + }); + firstSec = sections.front(); + lastSec = sections.back(); } // The beginning and the ending of .rel[a].plt section are marked @@ -2211,6 +2222,12 @@ // of finalizing other sections. for (OutputSection *sec : outputSections) sec->finalize(); + + // Sort segment sections by address. For one segment, we layout sections in + // the order of addresses. + for (Partition &part : partitions) + for (PhdrEntry *p : part.phdrs) + p->finalize(); } // Ensure data sections are not mixed with executable sections when @@ -2631,11 +2648,7 @@ 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; + auto placeSection = [&](OutputSection *sec) { off = setFileOffset(sec, off); // If this is a last section of the last executable segment and that @@ -2644,9 +2657,27 @@ if (config->zSeparate != SeparateSegmentKind::None && lastRX && lastRX->lastSec == sec) off = alignTo(off, config->commonPageSize); - } + }; + + // If a linker script is in use sections can be at arbitrary addresses and + // can be interleaved across segments. We handle this by placing output + // sections in load segment order. The sections in the segment are already + // sorted by address. + if (script->hasPhdrsCommands()) + for (Partition &part : partitions) + for (PhdrEntry *p : part.phdrs) + for (OutputSection *sec : p->sections) + if (sec != Out::elfHeader && !sec->offset) + placeSection(sec); + + // Layout SHF_ALLOC sections before non-SHF_ALLOC sections. A non-SHF_ALLOC + // will not occupy file offsets contained by a PT_LOAD. + // Skip sections which are already assigned an offsets. + for (OutputSection *sec : outputSections) + if ((sec->flags & SHF_ALLOC) && !sec->offset) + placeSection(sec); 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,31 @@ +# REQUIRES: x86 +# RUN: split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %t/test.s -o %t.o +# RUN: ld.lld -o %t1 --script %t/test.lds %t.o +# RUN: llvm-readelf -l %t1 | FileCheck %s + +# CHECK: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# CHECK-NEXT: LOAD 0x001000 0x0000000000001000 0x0000000000001000 0x001001 0x001001 R E 0x1000 +# CHECK-NEXT: LOAD 0x003000 0x0000000000000000 0x0000000000000000 0x000001 0x000001 R 0x1000 + +#--- test.s +.global _start +_start: + nop + +.section .foo, "a" + .byte 0 + +.section .bar, "a" + .byte 0 + +#--- test.lds +PHDRS { + ph1 PT_LOAD; + ph2 PT_LOAD; +} +SECTIONS { + .text 0x2000 : {*(.text*)} :ph1 + .foo 0 : {*(.foo*)} :ph2 + .bar 0x1000 : {*(.bar*)} :ph1 +} diff --git a/lld/test/ELF/linkerscript/sections-va-overflow.test b/lld/test/ELF/linkerscript/sections-va-overflow.test --- a/lld/test/ELF/linkerscript/sections-va-overflow.test +++ b/lld/test/ELF/linkerscript/sections-va-overflow.test @@ -18,5 +18,5 @@ ## with VA 0xffffffff20000000. That might be technically correct, but most probably ## is a result of a broken script file and causes file offset calculation overflow. ## It seems we do not have to support it, so we don't and we report an error in this case. -# ERR: error: unable to place section .text at file offset [0xFFFFFFFF20000000, 0xFFFFFFFF20000000]; check your linker script for overflows +# ERR: error: output file too large: 18446744069951455872 bytes # ERR-NOT: unable to place section .bss