(Re-submitting a patch that I never got a reply to, originally at http://sourceware.org/ml/binutils/2004-08/msg00361.html.) Looking further at what ld does when building Linux I discovered more anomalies (all sections in the below descriptions are considered to be page size aligned and a multiple of a page size large): 1) A non-writable section following a writable one would result in the non-writable section to be merged into the writable segment, thus losing the write protection. The inverse case was dealt with properly. 2) A zero-sized bss section following a writable section and preceding a non-writable one would result in the latter again losing its write protection. 3) A zero-sized bss section between two writable sections would result in a needlessly splitting the sections into two segments. The patch attempts to address all these. Built and tested on x86_64-unknown-linux-gnu and as cross tools for a large number of targets. Two additional questions: Does it really make sense to have a zero-sized section contribute to the access permissions of the segment it gets assigned to? Shouldn't the executable permission be handled like the writeable one, i.e. attempting to not merge sections with different permissions into one segment? If the answer to the first one is 'no' and/or that to the second is 'yes', then perhaps some more adjustments to this code might be desirable. Jan bfd/ 2004-05-10 Jan Beulich * elf.c (map_sections_to_segments): Symmetrically handle segment splits between non-writable and writable sections when a writable section preceeds a non-writable one. Consider loadable/non-loadable state of section only when it has non-zero size. (elf_sort_sections): Consider loadable state only after comparing sizes. ld/testsuite/ 2005-05-10 Jan Beulich * ld-elf/seg.*: New. --- /home/jbeulich/src/binutils/mainline/2005-05-06/bfd/elf.c 2005-05-06 08:24:21.000000000 +0200 +++ 2005-05-06/bfd/elf.c 2005-05-10 09:24:55.000000000 +0200 @@ -3442,13 +3442,14 @@ map_sections_to_segments (bfd *abfd) struct elf_segment_map *mfirst; struct elf_segment_map **pm; struct elf_segment_map *m; - asection *last_hdr; + asection *last_hdr, *last_nz; bfd_vma last_size; unsigned int phdr_index; bfd_vma maxpagesize; asection **hdrpp; bfd_boolean phdr_in_segment = TRUE; bfd_boolean writable; + int loadable; /* 0: indifferent, < 0: no, > 0: yes */ int tls_count = 0; asection *first_tls = NULL; asection *dynsec, *eh_frame_hdr; @@ -3524,9 +3525,11 @@ map_sections_to_segments (bfd *abfd) a few bytes of the end of the first section. */ last_hdr = NULL; last_size = 0; + last_nz = NULL; phdr_index = 0; maxpagesize = get_elf_backend_data (abfd)->maxpagesize; writable = FALSE; + loadable = 0; dynsec = bfd_get_section_by_name (abfd, ".dynamic"); if (dynsec != NULL && (dynsec->flags & SEC_LOAD) == 0) @@ -3579,11 +3582,12 @@ map_sections_to_segments (bfd *abfd) skip a page in the segment, then we need a new segment. */ new_segment = TRUE; } - else if ((last_hdr->flags & (SEC_LOAD | SEC_THREAD_LOCAL)) == 0 - && (hdr->flags & (SEC_LOAD | SEC_THREAD_LOCAL)) != 0) + else if (loadable < 0 + && (hdr->flags & (SEC_LOAD | SEC_THREAD_LOCAL)) != 0 + && hdr->size != 0) { - /* We don't want to put a loadable section after a - nonloadable section in the same segment. + /* We don't want to put a non-empty loadable section after + a nonloadable section in the same segment. Consider .tbss sections as loadable for this purpose. */ new_segment = TRUE; } @@ -3594,9 +3598,9 @@ map_sections_to_segments (bfd *abfd) file, then there is no other reason for a new segment. */ new_segment = FALSE; } - else if (! writable - && (hdr->flags & SEC_READONLY) == 0 - && (((last_hdr->lma + last_size - 1) + else if (! writable == ! (hdr->flags & SEC_READONLY) + && last_nz != NULL + && (((last_nz->lma + last_nz->size - 1) & ~(maxpagesize - 1)) != (hdr->lma & ~(maxpagesize - 1)))) { @@ -3615,33 +3619,33 @@ map_sections_to_segments (bfd *abfd) new_segment = FALSE; } - if (! new_segment) + if (new_segment) { - if ((hdr->flags & SEC_READONLY) == 0) - writable = TRUE; - last_hdr = hdr; - /* .tbss sections effectively have zero size. */ - if ((hdr->flags & (SEC_THREAD_LOCAL | SEC_LOAD)) != SEC_THREAD_LOCAL) - last_size = hdr->size; - else - last_size = 0; - continue; - } + /* We need a new program segment. We must create a new program + header holding all the sections from phdr_index until hdr. */ - /* We need a new program segment. We must create a new program - header holding all the sections from phdr_index until hdr. */ - - m = make_mapping (abfd, sections, phdr_index, i, phdr_in_segment); - if (m == NULL) - goto error_return; + m = make_mapping (abfd, sections, phdr_index, i, phdr_in_segment); + if (m == NULL) + goto error_return; - *pm = m; - pm = &m->next; + *pm = m; + pm = &m->next; + phdr_index = i; + phdr_in_segment = FALSE; + writable = FALSE; + loadable = 0; + last_nz = NULL; + } if ((hdr->flags & SEC_READONLY) == 0) writable = TRUE; - else - writable = FALSE; + if (hdr->size != 0) + { + if ((hdr->flags & (SEC_LOAD | SEC_THREAD_LOCAL)) != 0) + loadable = 1; + else + loadable = -1; + } last_hdr = hdr; /* .tbss sections effectively have zero size. */ @@ -3649,8 +3653,8 @@ map_sections_to_segments (bfd *abfd) last_size = hdr->size; else last_size = 0; - phdr_index = i; - phdr_in_segment = FALSE; + if (last_size != 0) + last_nz = hdr; } /* Create a final PT_LOAD program segment. */ @@ -3815,27 +3819,6 @@ elf_sort_sections (const void *arg1, con else if (sec1->vma > sec2->vma) return 1; - /* Put !SEC_LOAD sections after SEC_LOAD ones. */ - -#define TOEND(x) (((x)->flags & (SEC_LOAD | SEC_THREAD_LOCAL)) == 0) - - if (TOEND (sec1)) - { - if (TOEND (sec2)) - { - /* If the indicies are the same, do not return 0 - here, but continue to try the next comparison. */ - if (sec1->target_index - sec2->target_index != 0) - return sec1->target_index - sec2->target_index; - } - else - return 1; - } - else if (TOEND (sec2)) - return -1; - -#undef TOEND - /* Sort by size, to put zero sized sections before others at the same address. */ @@ -3847,6 +3830,15 @@ elf_sort_sections (const void *arg1, con if (size1 > size2) return 1; + /* Put !SEC_LOAD sections after SEC_LOAD ones. */ + +#define TOEND(x) (((x)->flags & (SEC_LOAD | SEC_THREAD_LOCAL)) == 0) + + if (TOEND (sec1) != TOEND (sec2)) + return TOEND (sec1) - TOEND (sec2); + +#undef TOEND + return sec1->target_index - sec2->target_index; } --- /home/jbeulich/src/binutils/mainline/2005-05-06/ld/testsuite/ld-elf/seg.d 1970-01-01 01:00:00.000000000 +0100 +++ 2005-05-06-elf-seg/ld/testsuite/ld-elf/seg.d 2005-05-09 12:34:37.000000000 +0200 @@ -0,0 +1,20 @@ +#source: seg.s +#ld: -T seg.ld +#objdump: -p +#xfail: "avr-*-*" "cr16c-*-*" "crx-*-*" "dlx-*-*" "h8300-*-*" "hppa64-*-*" +#xfail: "i960-*-*" "ip2k-*-*" "m32r-*-*" "m88k-*-*" "mips-*-*" "msp430-*-*" + +.*:.*elf.* +#... +Program Header: +[ ]*LOAD[ ].* +[ ]*filesz (0x0*1000+) memsz \1 flags r-x +[ ]*LOAD[ ].* +[ ]*filesz (0x0*3000+) memsz \1 flags rw- +[ ]*LOAD[ ].* +[ ]*filesz (0x0*1000+) memsz \1 flags r-x +[ ]*LOAD[ ].* +[ ]*filesz (0x0*1000+) memsz \1 flags rw- +[ ]*LOAD[ ].* +[ ]*filesz (0x0*1000+) memsz \1 flags r-x +#pass --- /home/jbeulich/src/binutils/mainline/2005-05-06/ld/testsuite/ld-elf/seg.ld 1970-01-01 01:00:00.000000000 +0100 +++ 2005-05-06-elf-seg/ld/testsuite/ld-elf/seg.ld 2004-08-27 13:22:05.000000000 +0200 @@ -0,0 +1,13 @@ +SECTIONS { + . = 0xABC00000; + .text : { *(.text) } + .rodata : { *(.rodata) } + .data : { *(.data) } + .bss : { *(.bss) } + .write1 : { *(.write1) } + .write2 : { *(.write2) } + .exec1 : { *(.exec1) } + .write3 : { *(.write3) } + .bss3 : { *(.bss3) } + .exec2 : { *(.exec2) } +} --- /home/jbeulich/src/binutils/mainline/2005-05-06/ld/testsuite/ld-elf/seg.s 1970-01-01 01:00:00.000000000 +0100 +++ 2005-05-06-elf-seg/ld/testsuite/ld-elf/seg.s 2005-05-09 10:48:55.000000000 +0200 @@ -0,0 +1,39 @@ + .equiv PAGESIZE, 0x100000 + +.global _start + + .text +_start: + .byte 0x11 + + .section .rodata, "a", "progbits" + .p2align 4 +const: + .fill PAGESIZE - 16, 1, 0x22 + + .data +data: + .fill PAGESIZE, 1, 0x33 + + .section .write1, "aw", "progbits" +write1: + .fill PAGESIZE, 1, 0x44 + + .section .write2, "aw", "progbits" +write2: + .fill PAGESIZE, 1, 0x55 + + .section .exec1, "ax", "progbits" +exec1: + .fill PAGESIZE, 1, 0x66 + + .section .write3, "aw", "progbits" +write3: + .fill PAGESIZE, 1, 0x77 + + .section .bss3, "aw", "nobits" +bss3: + + .section .exec2, "ax", "progbits" +exec2: + .fill PAGESIZE, 1, 0x88