Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -65,6 +65,7 @@ void createPhdrs(); void assignAddresses(); void assignFileOffsets(); + void mergePtLoad(); void setPhdrs(); void fixHeaders(); void fixSectionAlignments(); @@ -1200,6 +1201,51 @@ FileSize = SectionHeaderOff + (OutputSections.size() + 1) * sizeof(Elf_Shdr); } +template void Writer::mergePtLoad() { + // Merge PT_LOAD segments + // Sometimes, when linker script is being one can end up having two or more + // PT_LOAD segments with different protection attributes residing in the same + // memory page. This can cause or even prevent image from loading correctly + Phdr* PrevLoad = nullptr; + auto itLast = Phdrs.begin(), itReplace = itLast; + for (; itLast != Phdrs.end(); ++itLast) { + Phdr &P = (*itLast); + Elf_Phdr &H = P.H; + if (H.p_type == PT_LOAD && H.p_memsz == H.p_filesz) { + if (PrevLoad) { + // Check that end of previous and beginning of the current PT_LOAD segment + // reside in the same memory page. To make image loadable their protection + // attributes and sizes are merged. Below is an example: + // BEFORE MERGE: + // LOAD 0x002000 0x0000000011000000 0x0000000011000000 0x182 0x182 R E + // LOAD 0x002184 0x0000000011000184 0x0000000011000184 0x1d0 0x1d0 R + // LOAD 0x002354 0x0000000011000354 0x0000000011000354 0x04c 0x04c R E + // LOAD 0x0023a0 0x00000000110003a0 0x00000000110003a0 0x188 0x188 RW + // This image will not load correctly, because + // segment 0x11000000 - 0x11001000 containing executable code, will be + // memory mapped with RW attributes (last PT_LOAD attributes will be used) + // + // AFTER MERGE: + // LOAD 0x002000 0x0000000011000000 0x0000000011000000 0x528 0x528 RWE + if (H.p_vaddr / Target->PageSize == + (PrevLoad->H.p_vaddr + PrevLoad->H.p_memsz - 1) / Target->PageSize) { + // Do merge segments + PrevLoad->H.p_flags |= H.p_flags; + PrevLoad->H.p_memsz = H.p_vaddr - PrevLoad->H.p_vaddr + H.p_memsz; + PrevLoad->H.p_filesz = PrevLoad->H.p_memsz; + PrevLoad->Last = P.Last; + continue; + } + } + PrevLoad = &(*itReplace); + } + if (itReplace != itLast) + (*itReplace) = (*itLast); + ++itReplace; + } + Phdrs.erase(itReplace, itLast); +} + // Finalize the program headers. We call this function after we assign // file offsets and VAs to all sections. template void Writer::setPhdrs() { @@ -1228,6 +1274,7 @@ H.p_memsz = alignTo(H.p_memsz, H.p_align); } } + mergePtLoad(); } static uint32_t getMipsEFlags(bool Is64Bits) { Index: test/ELF/linkerscript-merge-ptload.s =================================================================== --- test/ELF/linkerscript-merge-ptload.s +++ test/ELF/linkerscript-merge-ptload.s @@ -0,0 +1,34 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t + +# RUN: echo "SECTIONS { . = 0x10000000; .text : {*(.text.*)} .foo : {*(.foo.*)} }" > %t.script +# RUN: ld.lld -o %t1 --script %t.script %t +# RUN: llvm-readobj -program-headers %t1 | FileCheck %s + +# CHECK: ProgramHeaders [ +# CHECK: ProgramHeader { +# CHECK: ProgramHeader { +# CHECK: ProgramHeader { +# CHECK-NEXT: Type: PT_LOAD (0x1) +# CHECK-NEXT: Offset: 0x1000 +# CHECK-NEXT: VirtualAddress: 0x10000000 +# CHECK-NEXT: PhysicalAddress: 0x10000000 +# CHECK-NEXT: FileSize: 9 +# CHECK-NEXT: MemSize: 9 +# CHECK-NEXT: Flags [ (0x7) +# CHECK-NEXT: PF_R (0x4) +# CHECK-NEXT: PF_W (0x2) +# CHECK-NEXT: PF_X (0x1) +# CHECK-NEXT: ] + +.global _start +_start: + nop + +.section .foo.1,"a" +foo1: + .long 0 + +.section .foo.2,"aw" +foo2: + .long 0