Separate header PT_LOAD for -z separate-code
authorAlan Modra <amodra@gmail.com>
Fri, 5 Oct 2018 02:10:54 +0000 (11:40 +0930)
committerAlan Modra <amodra@gmail.com>
Mon, 8 Oct 2018 09:56:08 +0000 (20:26 +1030)
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 <SIZEOF_HEADERS>): 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.

17 files changed:
bfd/ChangeLog
bfd/elf-nacl.c
bfd/elf.c
include/ChangeLog
include/bfdlink.h
ld/ChangeLog
ld/ldexp.c
ld/testsuite/ld-elf/loadaddr1.d
ld/testsuite/ld-elf/loadaddr2.d
ld/testsuite/ld-elf/overlay.d
ld/testsuite/ld-i386/vxworks2.sd
ld/testsuite/ld-powerpc/vxworks2.sd
ld/testsuite/ld-spu/ovl.lnk
ld/testsuite/ld-tic6x/dsbt-be.ld
ld/testsuite/ld-tic6x/dsbt-inrange.ld
ld/testsuite/ld-tic6x/dsbt-overflow.ld
ld/testsuite/ld-tic6x/dsbt.ld

index 5276305d802376bac8e87fa035ab1cacf1d3499e..a4a7b4fd9df0262591a01fb100f6802acad06727 100644 (file)
@@ -1,3 +1,14 @@
+2018-10-08  Alan Modra  <amodra@gmail.com>
+
+       * 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  <amodra@gmail.com>
 
        * elf.c (assign_file_positions_for_load_sections): Set p_vaddr
index 5f446b42425284fd0f6cbcf6ff41cd8f3f78b3eb..71b2ad8687202114bc3c2df91bf1e62c9ebde514 100644 (file)
@@ -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;
index 742a52e7bf064867ef00c877f0e5edd9a1b36e4d..5b3d27c67396c584aad88c352b9bf2270279c8c9 100644 (file)
--- 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;
            }
        }
 
index 4d129acb7f59db08c382682f50b923fc9baa912c..ce95a616627d088b5b9c3586ac0c9aea63510976 100644 (file)
@@ -1,3 +1,7 @@
+2018-10-08  Alan Modra  <amodra@gmail.com>
+
+       * bfdlink.h (struct bfd_link_info): Add load_phdrs field.
+
 2018-10-05  Sudakshina Das  <sudi.das@arm.com>
 
        * opcode/arm.h (ARM_EXT2_PREDRES): New.
index 249108132cccc7686e534aa46e7dd8f21f834e89..630f7342a95d075680bdc7bf5db1281daac506c1 100644 (file)
@@ -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;
index 55efbbfb17a2f59002821559593ce92363acd743..e0a6f7209e3bb5f2924245a674cd9c630bb1cf5e 100644 (file)
@@ -1,3 +1,17 @@
+2018-10-08  Alan Modra  <amodra@gmail.com>
+
+       * ldexp.c (fold_name <SIZEOF_HEADERS>): 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  <amodra@gmail.com>
 
        * ldlang.c (insert_os_after): Clear ignore_first on assignment to
index f6446dcd202738de6dcb9c198ec289f4944e04ba..3ae86a27629ada4d0d6717dfe85be7e421b5db09 100644 (file)
@@ -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;
index c4c5ed80688774b3e8e10ff7469d161a3f9861fe..0e38b64cdc43ee59f3789e04585a6f1c510843ae 100644 (file)
@@ -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
 
index 577fb6af5cfb3dec9a4057b5f954f1eca5397f56..5fbfa54e55e2d331f5d6d5e9cac2c760e2bdb4d7 100644 (file)
@@ -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
 
index a258eda0a7686779e5f9c22d80917ab6b6b5f238..00d25d5e2046cb81a3a63793b13e453739146550 100644 (file)
@@ -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
index 4f56f2ac7ec1b98b2394fffadafb44977016ddcd..dc1a6e6639d48d53aefbaa439cee02563d5124ac 100644 (file)
@@ -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 .*
-
 #...
index 2294cb63bdeed2d18c1d71533aa5ae3847837682..04c678e7167c9d3d1df1b75abe6bc6f48b84a52c 100644 (file)
@@ -1,13 +1,8 @@
 #...
 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 .*
-
 #...
index 00156522c32838e07ce3fb51e94f7a6d220b6fd3..045f986212839c6215ac6b8386097410b8662e91 100644 (file)
@@ -1,6 +1,6 @@
 SECTIONS
 {
-  . = SIZEOF_HEADERS;
+  . = 0x100;
   .text : { *(.text) *(.stub) }
   .data : { *(.data) *(.ovtab) }
   .bss : { *(.bss) }
index 40ba14cd75291fcd971232f07c2643576195e583..b78d91006d40ad8e03ef0a755f3408bc32cf382d 100644 (file)
@@ -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) }
index 603f0205f2a6ce0725a268a5f29447fc644f42ad..e5a2c870160ba2efe2307c3ac501360d6e2ac47a 100644 (file)
@@ -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) }
index 3128a35686e023a8edc370ada3babfbca54eebd9..fecd865cafd3ccaa05c4f6a123d2405566e45af5 100644 (file)
@@ -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) }
index fdde8ba5740fbfd5a283e770ad70d8079e39c17a..83b93ffc808ff5bd3b9190bde3a41a25ca36d1e2 100644 (file)
@@ -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) }