Index: lld/ELF/LinkerScript.h =================================================================== --- lld/ELF/LinkerScript.h +++ lld/ELF/LinkerScript.h @@ -272,7 +272,7 @@ std::vector getPhdrIndices(OutputSection *sec); - MemoryRegion *findMemoryRegion(OutputSection *sec); + MemoryRegion *findMemoryRegion(OutputSection *sec, MemoryRegion *prev); void switchTo(OutputSection *sec); uint64_t advance(uint64_t size, unsigned align); Index: lld/ELF/LinkerScript.cpp =================================================================== --- lld/ELF/LinkerScript.cpp +++ lld/ELF/LinkerScript.cpp @@ -883,7 +883,8 @@ // This function searches for a memory region to place the given output // section in. If found, a pointer to the appropriate memory region is // returned. Otherwise, a nullptr is returned. -MemoryRegion *LinkerScript::findMemoryRegion(OutputSection *sec) { +MemoryRegion *LinkerScript::findMemoryRegion(OutputSection *sec, + MemoryRegion *prev) { // If a memory region name was specified in the output section command, // then try to find that region first. if (!sec->memoryRegionName.empty()) { @@ -899,6 +900,10 @@ if (memoryRegions.empty()) return nullptr; + // An allocatable orphan section should continue the previous memory region. + if (sec->sectionIndex == UINT32_MAX && sec->flags & SHF_ALLOC && prev) + return prev; + // See if a region can be found by matching section flags. for (auto &pair : memoryRegions) { MemoryRegion *m = pair.second; @@ -1132,6 +1137,7 @@ void LinkerScript::adjustSectionsAfterSorting() { // Try and find an appropriate memory region to assign offsets in. + MemoryRegion *prev = nullptr; for (BaseCommand *base : sectionCommands) { if (auto *sec = dyn_cast(base)) { if (!sec->lmaRegionName.empty()) { @@ -1140,7 +1146,8 @@ else error("memory region '" + sec->lmaRegionName + "' not declared"); } - sec->memRegion = findMemoryRegion(sec); + sec->memRegion = findMemoryRegion(sec, prev); + prev = sec->memRegion; } } Index: lld/ELF/Writer.cpp =================================================================== --- lld/ELF/Writer.cpp +++ lld/ELF/Writer.cpp @@ -1256,10 +1256,13 @@ // Consider all existing sections with the same proximity. int proximity = getRankProximity(sec, *i); unsigned sortRank = sec->sortRank; - if (script->hasPhdrsCommands()) - // Prevent the orphan section to be placed before the found section because - // that can result in adding it to a previous segment and changing flags of - // that segment, for example, making a read-only segment writable. + if (script->hasPhdrsCommands() || !script->memoryRegions.empty()) + // Prevent the orphan section to be placed before the found section. If + // custom program headers are defined, that helps to avoid adding it to a + // previous segment and changing flags of that segment, for example, making + // a read-only segment writable. If memory regions are defined, an orphan + // section should continue the same region as the found section to better + // resemble the behavior of GNU ld. sortRank = std::max(sortRank, foundSec->sortRank); for (; i != e; ++i) { auto *curSec = dyn_cast(*i); Index: lld/test/ELF/linkerscript/orphan-memory.test =================================================================== --- /dev/null +++ lld/test/ELF/linkerscript/orphan-memory.test @@ -0,0 +1,67 @@ +# REQUIRES: x86 + +# RUN: split-file %s %ts +# RUN: llvm-mc -filetype=obj -triple=x86_64 %ts/s -o %t.o + +## Check that despite having a lower sort rank, an orphan section '.init_array' +## is placed after '.data' and in the same memory region. + +## Also check that a non-SHF_ALLOC orphan section, which is '.nonalloc' in the +## test, is not placed in a memory region. Both defined memory regions are +## exhausted after all expected sections are added, thus, trying to put any +## unexpected section would lead to an error. + +# RUN: ld.lld -o %t -T %ts/t %t.o +# RUN: llvm-readelf -S %t | FileCheck %s + +## Check that attributes of memory regions are ignored for orphan sections. +## This seems to contradict https://sourceware.org/binutils/docs/ld/MEMORY.html, +## but better resembles the way GNU ld actually works. + +# RUN: ld.lld -o %t2 -T %ts/t2 %t.o +# RUN: llvm-readelf -S %t2 | FileCheck %s + +CHECK: Name Type Address Off Size +CHECK: .text PROGBITS 0000000000008000 {{[0-9a-f]+}} 000004 +CHECK: .data PROGBITS 0000000000009000 {{[0-9a-f]+}} 000008 +CHECK: .init_array INIT_ARRAY 0000000000009008 {{[0-9a-f]+}} 000010 +CHECK: .nonalloc PROGBITS 0000000000000000 {{[0-9a-f]+}} 000010 + +#--- s + .text + .zero 4 + + .data + .zero 8 + + .section .init_array,"aw",@init_array + .zero 0x10 + + .section .nonalloc,"" + .zero 0x10 + +#--- t +MEMORY +{ + TEXT : ORIGIN = 0x8000, LENGTH = 0x4 + DATA : ORIGIN = 0x9000, LENGTH = 0x18 +} + +SECTIONS +{ + .text : { *(.text) } > TEXT + .data : { *(.data) } > DATA +} + +#--- t2 +MEMORY +{ + TEXT (rwx) : ORIGIN = 0x8000, LENGTH = 0x4 + DATA (rwx) : ORIGIN = 0x9000, LENGTH = 0x18 +} + +SECTIONS +{ + .text : { *(.text) } > TEXT + .data : { *(.data) } > DATA +}