Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -733,6 +733,15 @@ Sec->assignOffsets(); } +template +static bool canSharePtLoad(OutputSectionBase *S1, OutputSectionBase *S2) { + if (!(S1->getFlags() & SHF_ALLOC) || !(S2->getFlags() & SHF_ALLOC)) + return false; + + return (S1->getFlags() & (SHF_WRITE | SHF_EXECINSTR)) == + (S2->getFlags() & (SHF_WRITE | SHF_EXECINSTR)); +} + template void Writer::sortSections() { if (!ScriptConfig->HasSections) { std::stable_sort(OutputSections.begin(), OutputSections.end(), @@ -767,19 +776,49 @@ std::stable_sort(OutputSections.begin(), OutputSections.end(), compareSections); - auto I = OutputSections.begin(); auto E = OutputSections.end(); - auto NonScriptI = std::find_if(I, E, [](OutputSectionBase *S) { - return Script::X->getSectionIndex(S->getName()) == INT_MAX; - }); + auto NonScriptI = + std::find_if(OutputSections.begin(), E, [](OutputSectionBase *S) { + return Script::X->getSectionIndex(S->getName()) == INT_MAX; + }); + + // Find first greater element in a filtered range [I, NonScriptI]. + auto findGreater = + [&](std::function *)> Filter) { + auto Ret = E; + for (auto I = OutputSections.begin(); I != NonScriptI; ++I) + if (Filter(*I)) { + // If filtered element is before *NonScriptI we'd + // prefer to insert *NonScriptI immediately before it. + if (compareSectionsNonScript(*NonScriptI, *I)) + return I; + // If filtered element is less than *NonScriptI we'd + // prefer to insert *NonScriptI immediately after it. + Ret = I + 1; + } + return Ret; + }; + while (NonScriptI != E) { - auto FirstGreater = - std::find_if(I, NonScriptI, [&](OutputSectionBase *S) { - return compareSectionsNonScript(*NonScriptI, S); - }); - std::rotate(FirstGreater, NonScriptI, NonScriptI + 1); + // Try to find best place to put orphan (non-scripted) section N before + // some scripted section S in order to minimize number of PT_LOAD segments. + // We'll try to use following places, according to their priorities (1,2,3): + // 1) Before section S with same access attributes, + // if compareSectionsNonScript(N,S) is true. + // 2) After the last section with same access attributes, if any + // 3) Before any section for which compareSectionsNonScript(N, S) is true. + // In such case there are no sections which can share segment with section N. + + auto FirstGreater = findGreater([&](OutputSectionBase *S) { + return canSharePtLoad(S, *NonScriptI); + }); + if (FirstGreater == E) + FirstGreater = + findGreater([&](OutputSectionBase *) { return true; }); + + if (FirstGreater != E) + std::rotate(FirstGreater, NonScriptI, NonScriptI + 1); ++NonScriptI; - ++I; } } Index: test/ELF/linkerscript/merge-sections.s =================================================================== --- test/ELF/linkerscript/merge-sections.s +++ test/ELF/linkerscript/merge-sections.s @@ -61,7 +61,6 @@ # CHECK-NEXT: Type: # CHECK-NEXT: Flags [ # CHECK-NEXT: SHF_ALLOC -# CHECK-NEXT: SHF_EXECINSTR # CHECK-NEXT: ] # CHECK-NEXT: Address: 0x[[ADDR2:.*]] # CHECK-NEXT: Offset: 0x[[ADDR2]] Index: test/ELF/linkerscript/no-space.s =================================================================== --- test/ELF/linkerscript/no-space.s +++ test/ELF/linkerscript/no-space.s @@ -20,7 +20,7 @@ # CHECK: Section to Segment mapping: # CHECK-NEXT: Segment Sections... # CHECK-NEXT: 00 -# CHECK-NEXT: 01 foo .text .dynsym .hash .dynstr +# CHECK-NEXT: 01 foo .dynsym .hash .dynstr .section foo, "a" .quad 0 Index: test/ELF/linkerscript/orphan.s =================================================================== --- test/ELF/linkerscript/orphan.s +++ test/ELF/linkerscript/orphan.s @@ -0,0 +1,28 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: echo "SECTIONS { \ +# RUN: .rw : { *(.rw) } \ +# RUN: .text : { *(.text) } \ +# RUN: .rx : { *(.ro) } \ +# RUN: }" > %t.script +# RUN: ld.lld -o %t1 --script %t.script %t +# RUN: llvm-objdump -section-headers %t1 | FileCheck %s + +# CHECK: 0 00000000 0000000000000000 +# CHECK-NEXT: 1 .rw 00000008 0000000000000000 DATA +# CHECK-NEXT: 2 .orphan.2 00000008 0000000000000008 DATA +# CHECK-NEXT: 3 .text 00000000 0000000000000010 TEXT DATA +# CHECK-NEXT: 4 .rx 00000001 0000000000000010 TEXT DATA +# CHECK-NEXT: 5 .orphan.1 00000008 0000000000000011 TEXT DATA + +.section .rw, "aw" + .quad 0 + +.section .rx, "ax" + nop + +.section .orphan.1, "ax" + .quad 0 + +.section .orphan.2, "aw" + .quad 0 Index: test/ELF/linkerscript/sections-constraint.s =================================================================== --- test/ELF/linkerscript/sections-constraint.s +++ test/ELF/linkerscript/sections-constraint.s @@ -23,9 +23,9 @@ # NO1-NEXT: Idx Name Size # NO1-NEXT: 0 00000000 # NO1: .writable 00000004 +# NO1: .foo.2 00000004 # NO1: .readable 00000004 # NO1: .foo.1 00000004 -# NO1: .foo.2 00000004 .global _start _start: Index: test/ELF/linkerscript/sections-keep.s =================================================================== --- test/ELF/linkerscript/sections-keep.s +++ test/ELF/linkerscript/sections-keep.s @@ -42,13 +42,13 @@ # RUN: llvm-objdump -section-headers %t1 | FileCheck -check-prefix=MIXED1 %s # MIXED1: Sections: # MIXED1-NEXT: Idx Name Size -# MIXED1-NEXT: 0 00000000 -# MIXED1-NEXT: 1 .keep 00000004 -# MIXED1-NEXT: 2 .text 00000007 00000000000000ec TEXT DATA -# MIXED1-NEXT: 3 .temp 00000004 00000000000000f3 DATA -# MIXED1-NEXT: 4 .symtab 00000060 0000000000000000 -# MIXED1-NEXT: 5 .shstrtab 0000002d 0000000000000000 -# MIXED1-NEXT: 6 .strtab 00000012 0000000000000000 +# MIXED1-NEXT: 0 00000000 0000000000000000 +# MIXED1-NEXT: 1 .keep 00000004 00000000000000e8 DATA +# MIXED1-NEXT: 2 .temp 00000004 00000000000000ec DATA +# MIXED1-NEXT: 3 .text 00000007 00000000000000f0 TEXT DATA +# MIXED1-NEXT: 4 .symtab 00000060 0000000000000000 +# MIXED1-NEXT: 5 .shstrtab 0000002d 0000000000000000 +# MIXED1-NEXT: 6 .strtab 00000012 0000000000000000 ## The same, but now section without KEEP is at first place. ## gold and bfd linkers disagree here. gold collects .keep while @@ -62,15 +62,14 @@ # RUN: llvm-objdump -section-headers %t1 | FileCheck -check-prefix=MIXED2 %s # MIXED2: Sections: # MIXED2-NEXT: Idx Name Size -# MIXED2-NEXT: 0 00000000 -# MIXED2-NEXT: 1 .nokeep 00000004 00000000000000e8 DATA -# MIXED2-NEXT: 2 .text 00000007 00000000000000ec TEXT DATA -# MIXED2-NEXT: 3 .temp 00000004 00000000000000f3 DATA -# MIXED2-NEXT: 4 .symtab 00000060 0000000000000000 -# MIXED2-NEXT: 5 .shstrtab 0000002f 0000000000000000 -# MIXED2-NEXT: 6 .strtab 00000012 0000000000000000 +# MIXED2-NEXT: 0 00000000 0000000000000000 +# MIXED2-NEXT: 1 .nokeep 00000004 00000000000000e8 DATA +# MIXED2-NEXT: 2 .temp 00000004 00000000000000ec DATA +# MIXED2-NEXT: 3 .text 00000007 00000000000000f0 TEXT DATA +# MIXED2-NEXT: 4 .symtab 00000060 0000000000000000 +# MIXED2-NEXT: 5 .shstrtab 0000002f 0000000000000000 +# MIXED2-NEXT: 6 .strtab 00000012 0000000000000000 -# Check file pattern for kept sections. # RUN: echo "SECTIONS { \ # RUN: . = SIZEOF_HEADERS; \ # RUN: .keep : { KEEP(*2.o(.keep)) } \