From 64029e93683a266c38d19789e780f3748bd6a188 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Fri, 5 Oct 2018 11:40:54 +0930 Subject: [PATCH] Separate header PT_LOAD for -z separate-code This patch, along with previous patches in the series, supports putting the ELF file header and program headers in a PT_LOAD without sections. Logic governing whether headers a loaded has changed a little: The primary reason to include headers is now the presence of SIZEOF_HEADERS in a linker script. However, to support scripts that may have reserved space for headers by hand, we continue to add headers whenever the first section address is past the end of headers modulo page size. include/ * bfdlink.h (struct bfd_link_info): Add load_phdrs field. bfd/ * elf-nacl.c (nacl_modify_segment_map): Cope with header PT_LOAD lacking sections. * elf.c (_bfd_elf_map_sections_to_segments): Assume file and program headers are required when info->load_phdrs. Reorganize code handling program headers. Generate a mapping without sections just for file and program headers when -z separate-code would indicate they should be on a different page to the first section. ld/ * ldexp.c (fold_name ): Set link_info.load_phdrs. * testsuite/ld-elf/loadaddr1.d: Pass -z noseparate-code. * testsuite/ld-elf/loadaddr2.d: Likewise. * testsuite/ld-i386/vxworks2.sd: Adjust expected output. * testsuite/ld-powerpc/vxworks2.sd: Likewise. * testsuite/ld-elf/overlay.d: Remove spu xfail. * testsuite/ld-spu/ovl.lnk: Don't use SIZEOF_HEADERS. * testsuite/ld-tic6x/dsbt-be.ld: Likewise. * testsuite/ld-tic6x/dsbt-inrange.ld: Likewise. * testsuite/ld-tic6x/dsbt-overflow.ld: Likewise. * testsuite/ld-tic6x/dsbt.ld: Likewise. --- bfd/ChangeLog | 11 ++++ bfd/elf-nacl.c | 88 +++++++++++++++--------------- bfd/elf.c | 99 +++++++++++++++++++++++----------- include/ChangeLog | 4 ++ include/bfdlink.h | 3 ++ ld/ChangeLog | 14 +++++ ld/ldexp.c | 1 + ld/testsuite/ld-elf/loadaddr1.d | 2 +- ld/testsuite/ld-elf/loadaddr2.d | 2 +- ld/testsuite/ld-elf/overlay.d | 2 - ld/testsuite/ld-i386/vxworks2.sd | 7 +-- ld/testsuite/ld-powerpc/vxworks2.sd | 5 -- ld/testsuite/ld-spu/ovl.lnk | 2 +- ld/testsuite/ld-tic6x/dsbt-be.ld | 1 - ld/testsuite/ld-tic6x/dsbt-inrange.ld | 1 - ld/testsuite/ld-tic6x/dsbt-overflow.ld | 1 - ld/testsuite/ld-tic6x/dsbt.ld | 1 - 17 files changed, 151 insertions(+), 93 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 5276305..a4a7b4f 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,5 +1,16 @@ 2018-10-08 Alan Modra + * elf-nacl.c (nacl_modify_segment_map): Cope with header PT_LOAD + lacking sections. + * elf.c (_bfd_elf_map_sections_to_segments): Assume file and + program headers are required when info->load_phdrs. Reorganize + code handling program headers. Generate a mapping without + sections just for file and program headers when -z separate-code + would indicate they should be on a different page to the first + section. + +2018-10-08 Alan Modra + * elf.c (assign_file_positions_for_load_sections): Set p_vaddr from m->p_vaddr_offset for segments without sections. Invert sign of p_vaddr_offset. diff --git a/bfd/elf-nacl.c b/bfd/elf-nacl.c index 5f446b4..71b2ad8 100644 --- a/bfd/elf-nacl.c +++ b/bfd/elf-nacl.c @@ -70,8 +70,7 @@ nacl_modify_segment_map (bfd *abfd, struct bfd_link_info *info) const struct elf_backend_data *const bed = get_elf_backend_data (abfd); struct elf_segment_map **m = &elf_seg_map (abfd); struct elf_segment_map **first_load = NULL; - struct elf_segment_map **last_load = NULL; - bfd_boolean moved_headers = FALSE; + struct elf_segment_map **headers = NULL; int sizeof_headers; if (info != NULL && info->user_phdrs) @@ -170,56 +169,61 @@ nacl_modify_segment_map (bfd *abfd, struct bfd_link_info *info) } /* First, we're just finding the earliest PT_LOAD. - By the normal rules, this will be the lowest-addressed one. - We only have anything interesting to do if it's executable. */ - last_load = m; + By the normal rules, this will be the lowest-addressed one. */ if (first_load == NULL) - { - if (!executable) - goto next; - first_load = m; - } + first_load = m; + /* Now that we've noted the first PT_LOAD, we're looking for the first non-executable PT_LOAD with a nonempty p_filesz. */ - else if (!moved_headers + else if (headers == NULL && segment_eligible_for_headers (seg, bed->minpagesize, sizeof_headers)) - { - /* This is the one we were looking for! - - First, clear the flags on previous segments that - say they include the file header and phdrs. */ - struct elf_segment_map *prevseg; - for (prevseg = *first_load; - prevseg != seg; - prevseg = prevseg->next) - if (prevseg->p_type == PT_LOAD) - { - prevseg->includes_filehdr = 0; - prevseg->includes_phdrs = 0; - } - - /* This segment will include those headers instead. */ - seg->includes_filehdr = 1; - seg->includes_phdrs = 1; - - moved_headers = TRUE; - } + headers = m; } - - next: m = &seg->next; } - if (first_load != last_load && moved_headers) + if (headers != NULL) { - /* Now swap the first and last PT_LOAD segments' - positions in segment_map. */ - struct elf_segment_map *first = *first_load; - struct elf_segment_map *last = *last_load; - *first_load = first->next; - first->next = last->next; - last->next = first; + struct elf_segment_map **last_load = NULL; + struct elf_segment_map *seg; + + m = first_load; + while ((seg = *m) != NULL) + { + if (seg->p_type == PT_LOAD) + { + /* Clear the flags on any previous segment that + included the file header and phdrs. */ + seg->includes_filehdr = 0; + seg->includes_phdrs = 0; + /* Also strip out empty segments. */ + if (seg->count == 0) + { + if (headers == &seg->next) + headers = m; + *m = seg->next; + continue; + } + last_load = m; + } + m = &seg->next; + } + + /* This segment will include those headers instead. */ + seg = *headers; + seg->includes_filehdr = 1; + seg->includes_phdrs = 1; + + if (last_load != NULL && first_load != last_load && first_load != headers) + { + /* Put the first PT_LOAD header last. */ + struct elf_segment_map *first = *first_load; + struct elf_segment_map *last = *last_load; + *first_load = first->next; + first->next = last->next; + last->next = first; + } } return TRUE; diff --git a/bfd/elf.c b/bfd/elf.c index 742a52e..5b3d27c 100644 --- a/bfd/elf.c +++ b/bfd/elf.c @@ -4597,7 +4597,7 @@ _bfd_elf_map_sections_to_segments (bfd *abfd, struct bfd_link_info *info) unsigned int hdr_index; bfd_vma maxpagesize; asection **hdrpp; - bfd_boolean phdr_in_segment = TRUE; + bfd_boolean phdr_in_segment; bfd_boolean writable; bfd_boolean executable; int tls_count = 0; @@ -4606,7 +4606,7 @@ _bfd_elf_map_sections_to_segments (bfd *abfd, struct bfd_link_info *info) asection *dynsec, *eh_frame_hdr; bfd_size_type amt; bfd_vma addr_mask, wrap_to = 0; - bfd_boolean linker_created_pt_phdr_segment = FALSE; + bfd_size_type phdr_size; /* Select the allocated sections, and sort them. */ @@ -4638,8 +4638,23 @@ _bfd_elf_map_sections_to_segments (bfd *abfd, struct bfd_link_info *info) qsort (sections, (size_t) count, sizeof (asection *), elf_sort_sections); - /* Build the mapping. */ + phdr_size = elf_program_header_size (abfd); + if (phdr_size == (bfd_size_type) -1) + phdr_size = get_program_header_size (abfd, info); + phdr_size += bed->s->sizeof_ehdr; + maxpagesize = bed->maxpagesize; + if (maxpagesize == 0) + maxpagesize = 1; + phdr_in_segment = info != NULL && info->load_phdrs; + if (count != 0 + && (((sections[0]->lma & addr_mask) & (maxpagesize - 1)) + >= (phdr_size & (maxpagesize - 1)))) + /* For compatibility with old scripts that may not be using + SIZEOF_HEADERS, add headers when it looks like space has + been left for them. */ + phdr_in_segment = TRUE; + /* Build the mapping. */ mfirst = NULL; pm = &mfirst; @@ -4658,7 +4673,7 @@ _bfd_elf_map_sections_to_segments (bfd *abfd, struct bfd_link_info *info) m->p_flags = PF_R; m->p_flags_valid = 1; m->includes_phdrs = 1; - linker_created_pt_phdr_segment = TRUE; + phdr_in_segment = TRUE; *pm = m; pm = &m->next; @@ -4681,12 +4696,6 @@ _bfd_elf_map_sections_to_segments (bfd *abfd, struct bfd_link_info *info) last_hdr = NULL; last_size = 0; hdr_index = 0; - maxpagesize = bed->maxpagesize; - /* PR 17512: file: c8455299. - Avoid divide-by-zero errors later on. - FIXME: Should we abort if the maxpagesize is zero ? */ - if (maxpagesize == 0) - maxpagesize = 1; writable = FALSE; executable = FALSE; dynsec = bfd_get_section_by_name (abfd, ".dynamic"); @@ -4694,34 +4703,62 @@ _bfd_elf_map_sections_to_segments (bfd *abfd, struct bfd_link_info *info) && (dynsec->flags & SEC_LOAD) == 0) dynsec = NULL; + if ((abfd->flags & D_PAGED) == 0) + phdr_in_segment = FALSE; + /* Deal with -Ttext or something similar such that the first section is not adjacent to the program headers. This is an approximation, since at this point we don't know exactly how many program headers we will need. */ - if (count > 0) + if (phdr_in_segment && count > 0) { - bfd_size_type phdr_size = elf_program_header_size (abfd); + bfd_vma phdr_lma; + bfd_boolean separate_phdr = FALSE; - if (phdr_size == (bfd_size_type) -1) - phdr_size = get_program_header_size (abfd, info); - phdr_size += bed->s->sizeof_ehdr; - if ((abfd->flags & D_PAGED) == 0 - || (sections[0]->lma & addr_mask) < phdr_size - || ((sections[0]->lma & addr_mask) % maxpagesize - < phdr_size % maxpagesize) - || (sections[0]->lma & addr_mask & -maxpagesize) < wrap_to) + phdr_lma = (sections[0]->lma - phdr_size) & addr_mask & -maxpagesize; + if (info != NULL + && info->separate_code + && (sections[0]->flags & SEC_CODE) != 0) { - /* PR 20815: The ELF standard says that a PT_PHDR segment, if - present, must be included as part of the memory image of the - program. Ie it must be part of a PT_LOAD segment as well. - If we have had to create our own PT_PHDR segment, but it is - not going to be covered by the first PT_LOAD segment, then - force the inclusion if we can... */ - if ((abfd->flags & D_PAGED) != 0 - && linker_created_pt_phdr_segment) - phdr_in_segment = TRUE; - else - phdr_in_segment = FALSE; + /* If data sections should be separate from code and + thus not executable, and the first section is + executable then put the file and program headers in + their own PT_LOAD. */ + separate_phdr = TRUE; + if ((((phdr_lma + phdr_size - 1) & addr_mask & -maxpagesize) + == (sections[0]->lma & addr_mask & -maxpagesize))) + { + /* The file and program headers are currently on the + same page as the first section. Put them on the + previous page if we can. */ + if (phdr_lma >= maxpagesize) + phdr_lma -= maxpagesize; + else + separate_phdr = FALSE; + } + } + if ((sections[0]->lma & addr_mask) < phdr_lma + || (sections[0]->lma & addr_mask) < phdr_size) + /* If file and program headers would be placed at the end + of memory then it's probably better to omit them. */ + phdr_in_segment = FALSE; + else if (phdr_lma < wrap_to) + /* If a section wraps around to where we'll be placing + file and program headers, then the headers will be + overwritten. */ + phdr_in_segment = FALSE; + else if (separate_phdr) + { + m = make_mapping (abfd, sections, 0, 0, phdr_in_segment); + if (m == NULL) + goto error_return; + m->p_paddr = phdr_lma; + m->p_vaddr_offset + = (sections[0]->vma - phdr_size) & addr_mask & -maxpagesize; + m->p_paddr_valid = 1; + *pm = m; + pm = &m->next; + phdr_in_segment = FALSE; } } diff --git a/include/ChangeLog b/include/ChangeLog index 4d129ac..ce95a61 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,7 @@ +2018-10-08 Alan Modra + + * bfdlink.h (struct bfd_link_info): Add load_phdrs field. + 2018-10-05 Sudakshina Das * opcode/arm.h (ARM_EXT2_PREDRES): New. diff --git a/include/bfdlink.h b/include/bfdlink.h index 2491081..630f734 100644 --- a/include/bfdlink.h +++ b/include/bfdlink.h @@ -475,6 +475,9 @@ struct bfd_link_info /* TRUE if the linker script contained an explicit PHDRS command. */ unsigned int user_phdrs: 1; + /* TRUE if program headers ought to be loaded. */ + unsigned int load_phdrs: 1; + /* TRUE if we should check relocations after all input files have been opened. */ unsigned int check_relocs_after_open_input: 1; diff --git a/ld/ChangeLog b/ld/ChangeLog index 55efbbf..e0a6f72 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,5 +1,19 @@ 2018-10-08 Alan Modra + * ldexp.c (fold_name ): Set link_info.load_phdrs. + * testsuite/ld-elf/loadaddr1.d: Pass -z noseparate-code. + * testsuite/ld-elf/loadaddr2.d: Likewise. + * testsuite/ld-i386/vxworks2.sd: Adjust expected output. + * testsuite/ld-powerpc/vxworks2.sd: Likewise. + * testsuite/ld-elf/overlay.d: Remove spu xfail. + * testsuite/ld-spu/ovl.lnk: Don't use SIZEOF_HEADERS. + * testsuite/ld-tic6x/dsbt-be.ld: Likewise. + * testsuite/ld-tic6x/dsbt-inrange.ld: Likewise. + * testsuite/ld-tic6x/dsbt-overflow.ld: Likewise. + * testsuite/ld-tic6x/dsbt.ld: Likewise. + +2018-10-08 Alan Modra + * ldlang.c (insert_os_after): Clear ignore_first on assignment to dot, not any assignment. Clear ignore_first on output section statement contents too. diff --git a/ld/ldexp.c b/ld/ldexp.c index f6446dc..3ae86a2 100644 --- a/ld/ldexp.c +++ b/ld/ldexp.c @@ -692,6 +692,7 @@ fold_name (etree_type *tree) switch (tree->type.node_code) { case SIZEOF_HEADERS: + link_info.load_phdrs = 1; if (expld.phase != lang_first_phase_enum) { bfd_vma hdr_size = 0; diff --git a/ld/testsuite/ld-elf/loadaddr1.d b/ld/testsuite/ld-elf/loadaddr1.d index c4c5ed8..0e38b64 100644 --- a/ld/testsuite/ld-elf/loadaddr1.d +++ b/ld/testsuite/ld-elf/loadaddr1.d @@ -1,5 +1,5 @@ #source: loadaddr.s -#ld: -T loadaddr1.t -T loadaddr.t -z max-page-size=0x200000 +#ld: -T loadaddr1.t -T loadaddr.t -z max-page-size=0x200000 -z noseparate-code #readelf: -l --wide #target: *-*-linux* *-*-gnu* arm*-*-uclinuxfdpiceabi diff --git a/ld/testsuite/ld-elf/loadaddr2.d b/ld/testsuite/ld-elf/loadaddr2.d index 577fb6a..5fbfa54 100644 --- a/ld/testsuite/ld-elf/loadaddr2.d +++ b/ld/testsuite/ld-elf/loadaddr2.d @@ -1,5 +1,5 @@ #source: loadaddr.s -#ld: -T loadaddr2.t -T loadaddr.t -z max-page-size=0x200000 +#ld: -T loadaddr2.t -T loadaddr.t -z max-page-size=0x200000 -z noseparate-code #readelf: -l --wide #target: *-*-linux* *-*-gnu* arm*-*-uclinuxfdpiceabi diff --git a/ld/testsuite/ld-elf/overlay.d b/ld/testsuite/ld-elf/overlay.d index a258eda..00d25d5 100644 --- a/ld/testsuite/ld-elf/overlay.d +++ b/ld/testsuite/ld-elf/overlay.d @@ -1,7 +1,5 @@ # ld: -T overlay.t -u __load_start_text1 -u __load_start_text2 -u __load_stop_text1 -u __load_stop_text2 #readelf: -s -#xfail: spu-*-* -# The SPU adds its own LOAD segments, out of order, at the start of the program header table. #... [ ]+[0-9]+:[ ]+0*4000[ ]+0[ ]+NOTYPE[ ]+GLOBAL[ ]+DEFAULT[ ]+ABS __load_start_text1 diff --git a/ld/testsuite/ld-i386/vxworks2.sd b/ld/testsuite/ld-i386/vxworks2.sd index 4f56f2a..dc1a6e6 100644 --- a/ld/testsuite/ld-i386/vxworks2.sd +++ b/ld/testsuite/ld-i386/vxworks2.sd @@ -2,12 +2,7 @@ Elf file type is EXEC \(Executable file\) Entry point 0x80400 #... -Program Headers: - Type .* - PHDR .* -#... - LOAD .* 0x0007f000 0x0007f000 .* R E 0x1000 + LOAD .* 0x00080000 0x00080000 .* R E 0x1000 LOAD .* 0x00081000 0x00081000 .* RW 0x1000 DYNAMIC .* - #... diff --git a/ld/testsuite/ld-powerpc/vxworks2.sd b/ld/testsuite/ld-powerpc/vxworks2.sd index 2294cb6..04c678e 100644 --- a/ld/testsuite/ld-powerpc/vxworks2.sd +++ b/ld/testsuite/ld-powerpc/vxworks2.sd @@ -2,12 +2,7 @@ Elf file type is EXEC \(Executable file\) Entry point 0x80400 #... -Program Headers: - Type .* - PHDR .* -#... LOAD .* 0x00070000 0x00070000 .* R E 0x10000 LOAD .* 0x00090000 0x00090000 .* RW 0x10000 DYNAMIC .* - #... diff --git a/ld/testsuite/ld-spu/ovl.lnk b/ld/testsuite/ld-spu/ovl.lnk index 0015652..045f986 100644 --- a/ld/testsuite/ld-spu/ovl.lnk +++ b/ld/testsuite/ld-spu/ovl.lnk @@ -1,6 +1,6 @@ SECTIONS { - . = SIZEOF_HEADERS; + . = 0x100; .text : { *(.text) *(.stub) } .data : { *(.data) *(.ovtab) } .bss : { *(.bss) } diff --git a/ld/testsuite/ld-tic6x/dsbt-be.ld b/ld/testsuite/ld-tic6x/dsbt-be.ld index 40ba14c..b78d910 100644 --- a/ld/testsuite/ld-tic6x/dsbt-be.ld +++ b/ld/testsuite/ld-tic6x/dsbt-be.ld @@ -3,7 +3,6 @@ OUTPUT_FORMAT("elf32-tic6x-be", "elf32-tic6x-be", EXTERN (__c6xabi_DSBT_BASE); SECTIONS { - PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x0)); . = SEGMENT_START("text-segment", 0x0) + SIZEOF_HEADERS; . = 0x8000; .hash : { *(.hash) } .gnu.hash : { *(.gnu.hash) } diff --git a/ld/testsuite/ld-tic6x/dsbt-inrange.ld b/ld/testsuite/ld-tic6x/dsbt-inrange.ld index 603f020..e5a2c87 100644 --- a/ld/testsuite/ld-tic6x/dsbt-inrange.ld +++ b/ld/testsuite/ld-tic6x/dsbt-inrange.ld @@ -1,7 +1,6 @@ EXTERN (__c6xabi_DSBT_BASE); SECTIONS { - PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x0)); . = SEGMENT_START("text-segment", 0x0) + SIZEOF_HEADERS; . = 0x8000; .hash : { *(.hash) } .gnu.hash : { *(.gnu.hash) } diff --git a/ld/testsuite/ld-tic6x/dsbt-overflow.ld b/ld/testsuite/ld-tic6x/dsbt-overflow.ld index 3128a35..fecd865 100644 --- a/ld/testsuite/ld-tic6x/dsbt-overflow.ld +++ b/ld/testsuite/ld-tic6x/dsbt-overflow.ld @@ -1,7 +1,6 @@ EXTERN (__c6xabi_DSBT_BASE); SECTIONS { - PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x0)); . = SEGMENT_START("text-segment", 0x0) + SIZEOF_HEADERS; . = 0x8000; .hash : { *(.hash) } .gnu.hash : { *(.gnu.hash) } diff --git a/ld/testsuite/ld-tic6x/dsbt.ld b/ld/testsuite/ld-tic6x/dsbt.ld index fdde8ba..83b93ff 100644 --- a/ld/testsuite/ld-tic6x/dsbt.ld +++ b/ld/testsuite/ld-tic6x/dsbt.ld @@ -3,7 +3,6 @@ OUTPUT_FORMAT("elf32-tic6x-le", "elf32-tic6x-le", EXTERN (__c6xabi_DSBT_BASE); SECTIONS { - PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x0)); . = SEGMENT_START("text-segment", 0x0) + SIZEOF_HEADERS; . = 0x8000; .hash : { *(.hash) } .gnu.hash : { *(.gnu.hash) } -- 2.7.4