[hjl@gnu-cfl-1 max-page-size]$ cat foo.c int data = 3; int foo (void) { return data; } [hjl@gnu-cfl-1 max-page-size]$ make gcc -g -Wall -fpic -c -o foo.o foo.c ./ld -z max-page-size=0x200000 -m elf_x86_64 -z separate-code -shared -o libfoo2.so foo.o readelf -lW libfoo2.so Elf file type is DYN (Shared object file) Entry point 0x200000 There are 9 program headers, starting at offset 64 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x0002e8 0x0002e8 R 0x200000 LOAD 0x200000 0x0000000000200000 0x0000000000200000 0x00000f 0x00000f R E 0x200000 LOAD 0x400000 0x0000000000400000 0x0000000000400000 0x000068 0x000068 R 0x200000 LOAD 0x400f08 0x0000000000600f08 0x0000000000600f08 0x000114 0x000114 RW 0x200000 DYNAMIC 0x400f08 0x0000000000600f08 0x0000000000600f08 0x0000f0 0x0000f0 RW 0x8 NOTE 0x400000 0x0000000000400000 0x0000000000400000 0x000030 0x000030 R 0x8 GNU_PROPERTY 0x400000 0x0000000000400000 0x0000000000400000 0x000030 0x000030 R 0x8 GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10 GNU_RELRO 0x400f08 0x0000000000600f08 0x0000000000600f08 0x0000f8 0x0000f8 R 0x1 Section to Segment mapping: Segment Sections... 00 .hash .gnu.hash .dynsym .dynstr .rela.dyn 01 .text 02 .note.gnu.property .eh_frame 03 .dynamic .got .got.plt .data 04 .dynamic 05 .note.gnu.property 06 .note.gnu.property 07 08 .dynamic .got ls -l libfoo2.so -rwxrwxr-x 1 hjl hjl 4201160 Apr 26 08:14 libfoo2.so [hjl@gnu-cfl-1 max-page-size]$ Since all PT_LOAD segments are very small, they can be fit into a single 4K or 2MB page. Ld should use the smallest of minpagesize and maxpagesize which can cover the whole PT_LOAD segment.
If you specify -z max-page-size=0x200000 then there is an expectation that the binary produced will work with an actual hardware page size of 0x200000. If ld were instead to use minpagesize then the binary may not even run when the hardware page size is 0x200000, and certainly won't have the page protection you expect with -z separate-code. I don't see any way around this. As ld.text says of -z separate-code: "Create separate code @code{PT_LOAD} segment header in the object. This specifies a memory segment that should contain only instructions and must be in wholly disjoint pages from any other data." The presumption here is that data can't appear in the text memory pages. So you can't use tricks like that done between rodata and data by DATA_SEGMENT_ALIGN (ie. starting rw data at the end of ro data but on the next page in order to save disk), because that allows part of rw data to leak into ro data and vice versa.
On targets where the maximum page size is optional and only the minimum page size is required, we can set p_align to the minimum page size if the segment is less than the maximum page size. At run-time, these segments can be aligned at the maximum page size, but their p_align is the minimum page size.
Two R-- PT_LOAD segments are wasteful. Is it possible to move .rodata .eh_frame .eh_frame_hdr etc before the R-E PT_LOAD?