Some linker scripts, in particular the default ld.bfd linker script can match the InX::BssRelRo section in the .bss OutputSection . This causes the OutputSections for which isRelroSection() is true to be non-contiguous in the memory map. If we add all the OutputSections to the PT_GNU_RELRO PHDR then the size of said PHDR will include read-write sections. This may cause segfaults if the PT_GNU_RELRO PHDR covers read-write data that is written to.
This change only adds OutputSections for which isRelroSection() is true to the PT_GNU_RELRO PHDR if they are contiguous in the memory map. Later OutputSections for which isRelroSection() is true will be read-write.
Fixes PR35265
Implementation Notes:
I found this problem when using the default linker script from ld.bfd (script obtainable via ld --verbose). Even with an empty main the program would segfault when writing back the result of lazy PLT resolution into the .got.plt. The segfault was caused by the .got.plt section being marked read-only as it was covered by the PT_GNU_RELRO PHDR. The reason it was covered was due to the zero sized InX::BssRelRo section matching the .bss Output Section in the linker script extract below. As the current code calculates the size of the PT_GNU_RELRO PHDR as P->p_memsz = Last->Addr + Last->Size - First->Addr; we get the size of .got.plt and .data included in the PT_GNU_RELRO PHDR as InX::BssRelRo is Last.
The impact of the fix is that if the linker script does produce non-contiguous isRelroSections then only the first contiguous chunk will be covered by the PT_GNU_RELRO PHDR. Any remaining isRelroSections will be read-write in the output. I've chosen this in favour of compatibility of the default ld.bfd linker script; however a stricter view would be that it is an error if isRelroSections are not contiguous as this could be a potential security hole. I note that it was a 0 sized InX::BssRelRo matching in *(.bss .bss.* .gnu.linkonce.b.*) that passed the if (needsPtLoad(Sec) && isRelroSection(Sec)) test. Perhaps if non-contiguous isRelroSections is an error then 0 sized InX::BssRelRo can be an exception as this is not an obvious user-error.
.got : { *(.got) *(.igot) } . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .); .got.plt : { *(.got.plt) *(.igot.plt) } .data : { *(.data .data.* .gnu.linkonce.d.*) SORT(CONSTRUCTORS) } .data1 : { *(.data1) } _edata = .; PROVIDE (edata = .); . = .; __bss_start = .; .bss : { *(.dynbss) *(.bss .bss.* .gnu.linkonce.b.*) *(COMMON) /* Align here to ensure that the .bss section occupies space up to _end. Align after .bss to ensure correct alignment even if the .bss section disappears because there are no input sections. FIXME: Why do we need it? When there is no .bss section, we don't pad the .data section. */ . = ALIGN(. != 0 ? 64 / 8 : 1); }