Question about ELF_COMMONPAGESIZE and Fedora downstream change to it
Fangrui Song
i@maskray.me
Tue Aug 19 07:09:53 GMT 2025
On Sun, Aug 17, 2025 at 3:43 PM Alan Modra <amodra@gmail.com> wrote:
>
> On Sun, Aug 17, 2025 at 11:04:06PM +0800, Xi Ruoyao via Binutils wrote:
> > Hi,
> >
> > In Fedora recipe for Binutils there is a command changing
> > ELF_COMMONPAGESIZE for ppc64 and arm64:
> >
> > # On ppc64 and aarch64, we might use 64KiB pages
> > sed -i -e '/#define.*ELF_COMMONPAGESIZE/s/0x1000$/0x10000/' bfd/elf*ppc.c
> > sed -i -e '/#define.*ELF_COMMONPAGESIZE/s/0x1000$/0x10000/' bfd/elf*aarch64.c
> >
> > So does it mean to support a 64KiB-page configuration on ppc64 and
> > aarch64 we must use ELF_COMMONPAGESIZE = 64Ki? But if so what's the
> > point of ELF_MAXPAGESIZE then?
> >
> > I tried to find some clue in the BFD info page but I found nothing about
> > ELF_COMMONPAGESIZE there.
>
> My guess is that this change was to avoid bugs in relro segment
> layout. It used to be that the relro segment was allowed to finish on
> a commonpagesize boundary, but if hardware pages are actually
> maxpagesize then the end of the relro segment became read/write. See
> commit 9833b7757d24.
>
> --
> Alan Modra
I have some notes on -z relro
https://maskray.me/blog/2020-11-15-explain-gnu-linker-options#z-relro
### `-z relro`
Place RELRO sections in the `PT_GNU_RELRO` program header.
GNU ld uses one RW `PT_LOAD` program header with padding at the start.
The first half of the `PT_LOAD` overlaps with `PT_GNU_RELRO`.
The padding is added so that the end of `PT_GNU_RELRO` is [aligned by
max-page-size](https://sourceware.org/bugzilla/show_bug.cgi?id=28824).
(See `ld.bfd --verbose` output.)
Prior to GNU ld 2.39, the end was aligned by common-page-size.
GNU ld's one RW `PT_LOAD` layout makes the alignment increase the file
size. max-page-size can be large, such as 65536 for many systems,
causing [wasted
space](https://sourceware.org/bugzilla/show_bug.cgi?id=30612).
lld utilitizes two RW `PT_LOAD` program headers: one for RELRO
sections and the other for non-RELRO sections.
Although this might appear unusual initially, it eliminates the need
for alignment padding as seen in GNU ld's layout.
Key changes:
* <https://reviews.llvm.org/D58892> switched from
`PT_LOAD(PT_GNU_RELRO(.data.rel.ro .bss.rel.ro) .data .bss)` to
`PT_LOAD(PT_GNU_RELRO(.data.rel.ro .bss.rel.ro)) PT_LOAD(.data.
.bss)`.
* The end of the `PT_GNU_RELRO` segment and the associated RW
`PT_LOAD` segment is [padded to a common-page-size
boundary](https://github.com/llvm/llvm-project/pull/66042). The
padding section `.relro_padding` is like mold.
Before LLD 18, there is an issue that runtime_page_size <
common-page-size does not work.
The layout used by mold is similar to that of lld.
In mold's case, the end of `PT_GNU_RELRO` is padded to max-page-size
by appending a `SHT_NOBITS` `.relro_padding` section.
This approach ensures that the last page of `PT_GNU_RELRO` is
protected, regardless of the system page size.
However, when the system page size is less than max-page-size, the map
from the first `RW` `PT_LOAD` is larger than needed.
In my opinion, losing protection for the last page when the runtime
page size is larger than common-page-size is not really an issue.
Double mapping a page of up to max-common-page for the protection
could cause undesired VM waste.
Protecting `.got.plt` is the main purpose of `-z now`. Protecting a
small portion of `.data.rel.ro` doesn't really make the program more
secure, given that `.data` and `.bss` are so huge and full of attach
targets.
If users are really anxious, they can set common-page-size to match
their system page size.
GNU ld's internal linker scripts place RELRO sections between
`DATA_SEGMENT_ALIGN` and `DATA_SEGMENT_RELRO_END` (built-in
functions).
`DATA_SEGMENT_ALIGN` is where padding is added so that
`DATA_SEGMENT_RELRO_END` aligns to a max-page-size boundary.
```
. = DATA_SEGMENT_ALIGN(CONSTANT(MAXPAGESIZE), CONSTANT(COMMONPAGESIZE));
. = DATA_SEGMENT_RELRO_END(0, .);
. = DATA_SEGMENT_END(.);
```
ld.lld emulates these built-in functions:
* `DATA_SEGMENT_ALIGN`: set the current location to
`alignTo(script->getDot(), + align)`
* `DATA_RELRO_END`: set the current location to
`alignTo(script->getDot(), MAXPAGESIZE)`. `.relro_padding` is placed
immediately before `DATA_RELRO_END`.
More information about the Binutils
mailing list