Skip to content

Commit 74e315d

Browse files
committed
elf: Set p_align to the minimum page size if possible
Currently, on 32-bit and 64-bit ARM, it seems that ld generates p_align values of 0x10000 even if no section alignment is greater than 0x1000. The issue is more general and probably affects other targets with multiple page sizes. While file layout absolutely must take 64K page size into account, that does not have to be reflected in the p_align value. If running on a 64K kernel, the file will be loaded at a 64K page boundary by necessity. On a 4K kernel, 64K alignment is not needed. The glibc loader has been fixed to honor p_align: https://sourceware.org/bugzilla/show_bug.cgi?id=28676 similar to kernel: commit ce81bb256a224259ab686742a6284930cbe4f1fa Author: Chris Kennelly <[email protected]> Date: Thu Oct 15 20:12:32 2020 -0700 fs/binfmt_elf: use PT_LOAD p_align values for suitable start address This means that on 4K kernels, we will start to do extra work for 64K p_align, but this pointless for pretty much all binaries (whose section alignment rarely exceeds 16). The minimum page size is used, instead of the maximum section alignment due to this glibc bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28688 It has been fixed in glibc 2.35. But linker output must work on existing glibc binaries. 1. Set p_align to the minimum page size while laying out segments aligning to the maximum page size or section alignment. The run-time loader can align segments to the minimum page size or above, depending on system page size. 2. If -z max-page-size=NNN is used, p_align will be set to the maximum page size or the largest section alignment. 3. If a section requires alignment higher than the minimum page size, don't set p_align to the minimum page size. 4. If a section requires alignment higher than the maximum page size, set p_align to the section alignment. 5. For objcopy, when the minimum page size != the maximum page size, p_align may be set to the minimum page size while segments are aligned to the maximum page size. In this case, the input p_align will be ignored and the maximum page size will be used to align the ouput segments. 6. Update linker to disallow the common page size > the maximum page size. 7. Update linker to avoid the common page size > the maximum page size. 8. Adjust pru_irq_map-1.d to expect p_align == sh_addralign: Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .text PROGBITS 20000000 00007c 000004 00 AX 0 0 4 ... Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000074 0x00000000 0x00000000 0x00008 0x00008 RW 0x1 LOAD 0x00007c 0x20000000 0x20000000 0x00004 0x00004 R E 0x4 vs. Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .text PROGBITS 20000000 00007c 000004 00 AX 0 0 4 ... Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000074 0x00000000 0x00000000 0x00008 0x00008 RW 0x1 LOAD 0x00007c 0x20000000 0x20000000 0x00004 0x00004 R E 0x1 To enable this linker optimization, the backend should define ELF_P_ALIGN to ELF_MINPAGESIZE. bfd/ PR ld/28689 PR ld/28695 * elf-bfd.h (elf_backend_data): Add p_align. * elf.c (assign_file_positions_for_load_sections): Set p_align to the default p_align value while laying out segments aligning to maximum page size or section alignment. (elf_is_p_align_valid): New function. (copy_elf_program_header): Call elf_is_p_align_valid to determine if p_align is valid. * elfxx-target.h (ELF_P_ALIGN): New. Default to 0. (elfNN_bed): Add ELF_P_ALIGN. * elfxx-x86.h (ELF_P_ALIGN): New. Set to ELF_MINPAGESIZE. include/ PR ld/28689 PR ld/28695 * bfdlink.h (bfd_link_info): Add maxpagesize_is_set. ld/ PR ld/28689 PR ld/28695 * emultempl/elf.em (gld${EMULATION_NAME}_handle_option): Set link_info.maxpagesize_is_set for -z max-page-size=NNN. * ldelf.c (ldelf_after_parse): Disallow link_info.commonpagesize > link_info.maxpagesize. * testsuite/ld-elf/elf.exp: Pass -z max-page-size=0x4000 to linker to build mbind2a and mbind2b. * testsuite/ld-elf/header.d: Add -z common-page-size=0x100. * testsuite/ld-elf/linux-x86.exp: Add PR ld/28689 tests. * testsuite/ld-elf/p_align-1.c: New file. * testsuite/ld-elf/page-size-1.d: New test. * testsuite/ld-elf/pr26936.d: Add -z common-page-size=0x1000. * testsuite/ld-elf/seg.d: Likewise. * testsuite/ld-scripts/rgn-at5.d: Likewise. * testsuite/ld-pru/pru_irq_map-1.d: Append 1 to name. Adjust expected PT_LOAD segment alignment. * testsuite/ld-pru/pru_irq_map-2.d: Append 2 to name. * testsuite/ld-scripts/pr23571.d: Add -z max-page-size=0x1000.
1 parent 1037150 commit 74e315d

18 files changed

+162
-12
lines changed

bfd/elf-bfd.h

+4
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,10 @@ struct elf_backend_data
945945
/* The value of commonpagesize to use when -z relro for this backend. */
946946
bfd_vma relropagesize;
947947

948+
/* The p_align value for this backend. If it is set, p_align of
949+
PT_LOAD alignment will be to p_align by default. */
950+
bfd_vma p_align;
951+
948952
/* The BFD flags applied to sections created for dynamic linking. */
949953
flagword dynamic_sec_flags;
950954

bfd/elf.c

+69-2
Original file line numberDiff line numberDiff line change
@@ -5407,6 +5407,8 @@ assign_file_positions_for_load_sections (bfd *abfd,
54075407
Elf_Internal_Phdr *p;
54085408
file_ptr off; /* Octets. */
54095409
bfd_size_type maxpagesize;
5410+
bfd_size_type p_align;
5411+
bool p_align_p = false;
54105412
unsigned int alloc, actual;
54115413
unsigned int i, j;
54125414
struct elf_segment_map **sorted_seg_map;
@@ -5491,6 +5493,7 @@ assign_file_positions_for_load_sections (bfd *abfd,
54915493
qsort (sorted_seg_map, alloc, sizeof (*sorted_seg_map),
54925494
elf_sort_segments);
54935495

5496+
p_align = bed->p_align;
54945497
maxpagesize = 1;
54955498
if ((abfd->flags & D_PAGED) != 0)
54965499
{
@@ -5561,6 +5564,15 @@ assign_file_positions_for_load_sections (bfd *abfd,
55615564
segment. */
55625565
if (m->p_align_valid)
55635566
maxpagesize = m->p_align;
5567+
else if (p_align != 0
5568+
&& (link_info == NULL
5569+
|| !link_info->maxpagesize_is_set))
5570+
/* Set p_align to the default p_align value while laying
5571+
out segments aligning to the maximum page size or the
5572+
largest section alignment. The run-time loader can
5573+
align segments to the default p_align value or the
5574+
maximum page size, depending on system page size. */
5575+
p_align_p = true;
55645576

55655577
p->p_align = maxpagesize;
55665578
}
@@ -5598,7 +5610,22 @@ assign_file_positions_for_load_sections (bfd *abfd,
55985610
}
55995611
align = (bfd_size_type) 1 << align_power;
56005612
if (align < maxpagesize)
5601-
align = maxpagesize;
5613+
{
5614+
/* If a section requires alignment higher than the
5615+
default p_align value, don't set p_align to the
5616+
default p_align value. */
5617+
if (align > p_align)
5618+
p_align_p = false;
5619+
align = maxpagesize;
5620+
}
5621+
else
5622+
{
5623+
/* If a section requires alignment higher than the
5624+
maximum page size, set p_align to the section
5625+
alignment. */
5626+
p_align_p = true;
5627+
p_align = align;
5628+
}
56025629
}
56035630

56045631
for (i = 0; i < m->count; i++)
@@ -5977,6 +6004,9 @@ assign_file_positions_for_load_sections (bfd *abfd,
59776004
print_segment_map (m);
59786005
}
59796006
}
6007+
6008+
if (p_align_p)
6009+
p->p_align = p_align;
59806010
}
59816011
}
59826012

@@ -7484,6 +7514,40 @@ rewrite_elf_program_header (bfd *ibfd, bfd *obfd, bfd_vma maxpagesize)
74847514
return true;
74857515
}
74867516

7517+
/* Return true if p_align in the ELF program header in ABFD is valid. */
7518+
7519+
static bool
7520+
elf_is_p_align_valid (bfd *abfd)
7521+
{
7522+
unsigned int i;
7523+
Elf_Internal_Phdr *segment;
7524+
unsigned int num_segments;
7525+
const struct elf_backend_data *bed = get_elf_backend_data (abfd);
7526+
bfd_size_type maxpagesize = bed->maxpagesize;
7527+
bfd_size_type p_align = bed->p_align;
7528+
7529+
/* Return true if the default p_align value isn't set or the maximum
7530+
page size is the same as the minimum page size. */
7531+
if (p_align == 0 || maxpagesize == bed->minpagesize)
7532+
return true;
7533+
7534+
/* When the default p_align value is set, p_align may be set to the
7535+
default p_align value while segments are aligned to the maximum
7536+
page size. In this case, the input p_align will be ignored and
7537+
the maximum page size will be used to align the output segments. */
7538+
segment = elf_tdata (abfd)->phdr;
7539+
num_segments = elf_elfheader (abfd)->e_phnum;
7540+
for (i = 0; i < num_segments; i++, segment++)
7541+
if (segment->p_type == PT_LOAD
7542+
&& (segment->p_align != p_align
7543+
|| vma_page_aligned_bias (segment->p_vaddr,
7544+
segment->p_offset,
7545+
maxpagesize) != 0))
7546+
return true;
7547+
7548+
return false;
7549+
}
7550+
74877551
/* Copy ELF program header information. */
74887552

74897553
static bool
@@ -7498,6 +7562,7 @@ copy_elf_program_header (bfd *ibfd, bfd *obfd)
74987562
unsigned int num_segments;
74997563
bool phdr_included = false;
75007564
bool p_paddr_valid;
7565+
bool p_palign_valid;
75017566
unsigned int opb = bfd_octets_per_byte (ibfd, NULL);
75027567

75037568
iehdr = elf_elfheader (ibfd);
@@ -7518,6 +7583,8 @@ copy_elf_program_header (bfd *ibfd, bfd *obfd)
75187583
break;
75197584
}
75207585

7586+
p_palign_valid = elf_is_p_align_valid (ibfd);
7587+
75217588
for (i = 0, segment = elf_tdata (ibfd)->phdr;
75227589
i < num_segments;
75237590
i++, segment++)
@@ -7560,7 +7627,7 @@ copy_elf_program_header (bfd *ibfd, bfd *obfd)
75607627
map->p_paddr = segment->p_paddr;
75617628
map->p_paddr_valid = p_paddr_valid;
75627629
map->p_align = segment->p_align;
7563-
map->p_align_valid = 1;
7630+
map->p_align_valid = p_palign_valid;
75647631
map->p_vaddr_offset = 0;
75657632

75667633
if (map->p_type == PT_GNU_RELRO

bfd/elfxx-target.h

+5
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,10 @@
400400
# error ELF_MINPAGESIZE > ELF_RELROPAGESIZE
401401
#endif
402402

403+
#ifndef ELF_P_ALIGN
404+
#define ELF_P_ALIGN 0
405+
#endif
406+
403407
#ifndef ELF_DYNAMIC_SEC_FLAGS
404408
/* Note that we set the SEC_IN_MEMORY flag for these sections. */
405409
#define ELF_DYNAMIC_SEC_FLAGS \
@@ -813,6 +817,7 @@ static const struct elf_backend_data elfNN_bed =
813817
ELF_MINPAGESIZE, /* minpagesize */
814818
ELF_COMMONPAGESIZE, /* commonpagesize */
815819
ELF_RELROPAGESIZE, /* commonpagesize to use with -z relro */
820+
ELF_P_ALIGN, /* p_align */
816821
ELF_DYNAMIC_SEC_FLAGS, /* dynamic_sec_flags */
817822
elf_backend_arch_data,
818823
elf_info_to_howto,

bfd/elfxx-x86.h

+2
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,8 @@ extern void _bfd_x86_elf_link_report_relative_reloc
755755
#define elf_backend_fixup_gnu_properties \
756756
_bfd_x86_elf_link_fixup_gnu_properties
757757

758+
#define ELF_P_ALIGN ELF_MINPAGESIZE
759+
758760
/* Return true if H is a __start_SECNAME/__stop_SECNAME symbol for the
759761
SECNAME section which has been garbage collected by --gc-sections
760762
-z start-stop-gc. */

include/bfdlink.h

+3
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,9 @@ struct bfd_link_info
525525
/* TRUE if all symbol names should be unique. */
526526
unsigned int unique_symbol : 1;
527527

528+
/* TRUE if maxpagesize is set on command-line. */
529+
unsigned int maxpagesize_is_set : 1;
530+
528531
/* Char that may appear as the first char of a symbol, but should be
529532
skipped (like symbol_leading_char) when looking up symbols in
530533
wrap_hash. Used by PowerPC Linux for 'dot' symbols. */

ld/emultempl/elf.em

+1
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,7 @@ fragment <<EOF
721721
|| (link_info.maxpagesize & (link_info.maxpagesize - 1)) != 0)
722722
einfo (_("%F%P: invalid maximum page size \`%s'\n"),
723723
optarg + 14);
724+
link_info.maxpagesize_is_set = true;
724725
}
725726
else if (startswith (optarg, "common-page-size="))
726727
{

ld/ldelf.c

+3
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ ldelf_after_parse (void)
7272
link_info.dynamic_undefined_weak = 0;
7373
}
7474
after_parse_default ();
75+
if (link_info.commonpagesize > link_info.maxpagesize)
76+
einfo (_("%F%P: common page size (0x%v) > maximum page size (0x%v)\n"),
77+
link_info.commonpagesize, link_info.maxpagesize);
7578
}
7679

7780
/* Handle the generation of DT_NEEDED tags. */

ld/testsuite/ld-elf/elf.exp

+2-2
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ if { [istarget *-*-linux*]
365365
run_ld_link_exec_tests [list \
366366
[list \
367367
"Run mbind2a" \
368-
"$NOPIE_LDFLAGS -Wl,-z,common-page-size=0x4000" \
368+
"$NOPIE_LDFLAGS -Wl,-z,common-page-size=0x4000,-z,max-page-size=0x4000" \
369369
"" \
370370
{ mbind2a.s mbind2b.c } \
371371
"mbind2a" \
@@ -374,7 +374,7 @@ if { [istarget *-*-linux*]
374374
] \
375375
[list \
376376
"Run mbind2b" \
377-
"-static -Wl,-z,common-page-size=0x4000" \
377+
"-static -Wl,-z,common-page-size=0x4000,-z,max-page-size=0x4000" \
378378
"" \
379379
{ mbind2a.s mbind2b.c } \
380380
"mbind2b" \

ld/testsuite/ld-elf/header.d

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# target: *-*-linux* *-*-gnu* *-*-vxworks arm*-*-uclinuxfdpiceabi
2-
# ld: -T header.t -z max-page-size=0x100
2+
# ld: -T header.t -z max-page-size=0x100 -z common-page-size=0x100
33
# objdump: -hpw
44

55
#...

ld/testsuite/ld-elf/linux-x86.exp

+36
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,42 @@ run_ld_link_exec_tests [list \
185185
"" \
186186
"tmpdir/indirect-extern-access-2.so" \
187187
] \
188+
[list \
189+
"Run p_align-1a without PIE" \
190+
"$NOPIE_LDFLAGS" \
191+
"" \
192+
{ p_align-1.c } \
193+
"p_align-1a" \
194+
"pass.out" \
195+
"$NOPIE_CFLAGS" \
196+
] \
197+
[list \
198+
"Run p_align-1b with PIE" \
199+
"-pie" \
200+
"" \
201+
{ p_align-1.c } \
202+
"p_align-1b" \
203+
"pass.out" \
204+
"-fpie" \
205+
] \
206+
[list \
207+
"Run p_align-1c with -Wl,-z,max-page-size=0x1000 without PIE" \
208+
"$NOPIE_LDFLAGS -Wl,-z,max-page-size=0x1000" \
209+
"" \
210+
{ p_align-1.c } \
211+
"p_align-1c" \
212+
"pass.out" \
213+
"$NOPIE_CFLAGS" \
214+
] \
215+
[list \
216+
"Run p_align-1d with -Wl,-z,max-page-size=0x1000 with PIE" \
217+
"-pie -Wl,-z,max-page-size=0x1000" \
218+
"" \
219+
{ p_align-1.c } \
220+
"p_align-1d" \
221+
"pass.out" \
222+
"-fpie" \
223+
] \
188224
]
189225

190226
proc elfedit_test { options test output } {

ld/testsuite/ld-elf/p_align-1.c

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include <stdio.h>
2+
#include <stdint.h>
3+
#include <stdlib.h>
4+
5+
#ifndef ALIGN
6+
# define ALIGN 0x800000
7+
#endif
8+
9+
int
10+
__attribute__ ((weak))
11+
is_aligned (void *p, int align)
12+
{
13+
return (((uintptr_t) p) & (align - 1)) == 0;
14+
}
15+
16+
int foo __attribute__ ((aligned (ALIGN))) = 1;
17+
18+
int
19+
main (void)
20+
{
21+
if (!is_aligned (&foo, ALIGN))
22+
abort ();
23+
printf ("PASS\n");
24+
return 0;
25+
}

ld/testsuite/ld-elf/page-size-1.d

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#source: dummy.s
2+
#ld: -z common-page-size=0x4000 -z max-page-size=0x1000
3+
#error: common page size \(0x4000\) > maximum page size \(0x1000\)
4+
#target: *-*-linux-gnu *-*-gnu* arm*-*-uclinuxfdpiceabi

ld/testsuite/ld-elf/pr26936.d

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#source: pr26936b.s
33
#source: pr26936c.s
44
#as: --gen-debug
5-
#ld: -z noseparate-code -Ttext-segment 0x10000 -z max-page-size=0x1000
5+
#ld: -z noseparate-code -Ttext-segment 0x10000 -z max-page-size=0x1000 -z common-page-size=0x1000
66
#readelf: -wL -W
77
#target: [check_shared_lib_support]
88
# Assembly source file for the HPPA assembler is renamed and modifed by

ld/testsuite/ld-elf/seg.d

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#target: *-*-linux* *-*-gnu* *-*-vxworks arm*-*-uclinuxfdpiceabi
22
#source: seg.s
3-
#ld: -T seg.t -z max-page-size=0x1000
3+
#ld: -T seg.t -z max-page-size=0x1000 -z common-page-size=0x1000
44
#readelf: -l --wide
55

66
#...

ld/testsuite/ld-pru/pru_irq_map-1.d

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#name: pru_irq_map special section for host
1+
#name: pru_irq_map special section for host 1
22
#source: pru_irq_map.s
33
#ld: --defsym=__HEAP_SIZE=0 --defsym=__STACK_SIZE=0
44
#readelf: -l --wide
@@ -9,7 +9,7 @@
99
Program Headers:
1010
+Type +Offset +VirtAddr +PhysAddr +FileSiz +MemSiz +Flg +Align
1111
+LOAD +0x[0-9a-f]+ 0x0+ 0x0+ 0x0+8 0x0+8 RW 0x1
12-
+LOAD +0x[0-9a-f]+ 0x20+ 0x20+ 0x0+4 0x0+4 R E 0x1
12+
+LOAD +0x[0-9a-f]+ 0x20+ 0x20+ 0x0+4 0x0+4 R E 0x4
1313

1414
Section to Segment mapping:
1515
+Segment Sections...

ld/testsuite/ld-pru/pru_irq_map-2.d

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#name: pru_irq_map special section for host
1+
#name: pru_irq_map special section for host 2
22
#source: pru_irq_map.s
33
#ld: --defsym=__HEAP_SIZE=0 --defsym=__STACK_SIZE=0
44
#readelf: -S --wide

ld/testsuite/ld-scripts/pr23571.d

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#source: align2a.s
2-
#ld: -T pr23571.t -z common-page-size=0x1000
2+
#ld: -T pr23571.t -z common-page-size=0x1000 -z max-page-size=0x1000
33
#objdump: -h -w
44

55
.*: +file format .*

ld/testsuite/ld-scripts/rgn-at5.d

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# name: rgn-at5
22
# source: rgn-at5.s
3-
# ld: -T rgn-at5.t -z max-page-size=0x1000
3+
# ld: -T rgn-at5.t -z max-page-size=0x1000 -z common-page-size=0x1000
44
# objdump: -w -h
55
# target: *-*-linux* *-*-gnu* arm*-*-uclinuxfdpiceabi
66
# xfail: rx-*-*

0 commit comments

Comments
 (0)