RHEL-45873 fixed unintended gaps due to a bug in RELRO handling. I believe this addresses the gap issue for single page size targets such as s390x and x86-64. However, on aarch64 and pp64le, the minimum page size is 4096 bytes, but we build the binaries for compatibility with 65,536 byte pages (as required by the respective ABIs). This may or may not be a problem on ppc64le, where the page size is more or less fixed to 65,536 bytes in practice (I assume) because not even Fedora ships a 4096 byte page kernel.
However, on aarch64, I think we have a real issue because the page size is expected to vary in practice, and 4096 byte pages are supported even in RHEL. This results in gaps because the way ld lays out the binaries, there are no gaps only if the page size is 65,546 bytes:
# eu-readelf -l /lib/ld-linux-aarch64.so.1 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x028620 0x028620 R E 0x10000 LOAD 0x02e818 0x000000000003e818 0x000000000003e818 0x002908 0x002a98 RW 0x10000 DYNAMIC 0x02fdf0 0x000000000003fdf0 0x000000000003fdf0 0x0001b0 0x0001b0 RW 0x8 NOTE 0x0001c8 0x00000000000001c8 0x00000000000001c8 0x000024 0x000024 R 0x4 GNU_EH_FRAME 0x0247b8 0x00000000000247b8 0x00000000000247b8 0x0008d4 0x0008d4 R 0x4 GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10 GNU_RELRO 0x02e818 0x000000000003e818 0x000000000003e818 0x0017e8 0x0017e8 R 0x1 Section to Segment mapping: Segment Sections... 00 [RO: .note.gnu.build-id .hash .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_d .rela.dyn .rela.plt .plt .text .rodata .stapsdt.base .eh_frame_hdr .eh_frame] 01 [RELRO: .init_array .data.rel.ro .dynamic .got] .data .bss 02 [RELRO: .dynamic] 03 [RO: .note.gnu.build-id] 04 [RO: .eh_frame_hdr] 05 06 [RELRO: .init_array .data.rel.ro .dynamic .got]
And:
info: LOAD segment 0: address 0x0, size 164244, range [0x0,0x29000) info: LOAD segment 1: address 0x3e8e0, size 10704, range [0x3e000,0x42000) error: gap between load segments: 86016 bytes
This is from glibc-2.34-132.el9.aarch64, built with binutils-2.35.2-54.el9.aarch64.
I think we only need a way to link ld.so so that there no gaps, possibly using custom linker options. (But it would be preferable if we had a solution for the main program, too.) I tried using -z common-page-size=65536 with binutils-2.41-48.el10.aarch64, but it did not have any effect. And -z separate-code does not fill the gaps, either, it just produces more of them.
The issue is also present in ld version 2.43.1.20240927 (upstream) across multiple architectures. There is a related glibc dynamic linker bug that requests a glibc change in the other direction (create more gaps):
- Bug 31076 - Extra struct vm_area_struct with ---p created when PAGE_SIZE < max-page-size
I assume if there was a way to avoid the gaps, it would also solve the VMA issue because people who don't want the extra VMAs can use that option.