ld: Create a new LOAD segment for separate code segment
authorH.J. Lu <hjl.tools@gmail.com>
Fri, 12 Jan 2018 03:04:55 +0000 (19:04 -0800)
committerH.J. Lu <hjl.tools@gmail.com>
Fri, 12 Jan 2018 03:05:06 +0000 (19:05 -0800)
When generating separate code LOAD segment, create a new LOAD segment
if the previous section contains text and the current section doesn't
or vice versa:

Elf file type is DYN (Shared object file)
Entry point 0x200020
There are 7 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x00000000 0x00000000 0x00200 0x00200 R   0x200000
  LOAD           0x200000 0x00200000 0x00200000 0x00036 0x00036 R E 0x200000
  LOAD           0x400000 0x00400000 0x00400000 0x00064 0x00064 R   0x200000
  LOAD           0x400f80 0x00600f80 0x00600f80 0x000a0 0x000a0 RW  0x200000
  DYNAMIC        0x400f80 0x00600f80 0x00600f80 0x00080 0x00080 RW  0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10
  GNU_RELRO      0x400f80 0x00600f80 0x00600f80 0x00080 0x00080 R   0x1

 Section to Segment mapping:
  Segment Sections...
   00     .hash .gnu.hash .dynsym .dynstr .rela.plt
   01     .plt .text
   02     .rodata .eh_frame
   03     .dynamic .got.plt
   04     .dynamic
   05
   06     .dynamic

to prevent fetching or executing data in code pages as instructions.

Also don't put a writable section in a read-only segment if there is a
RELRO segment.

Since code segment is aligned and padded to the maximum page size on
disk, the minimum file size is bigger than the maximum page size which
is 2MB (0x200000):

-rwxr-xr-x 1 hjl hjl 4201932 Jan 10 10:41 libfoo.so

"-z max-page-size=0x1000" can be used to reduce the maximum page size to
4KB (0x1000):

-rwxr-xr-x 1 hjl hjl   15820 Jan 10 10:44 libfoo.so

PR ld/22393
* elf.c (_bfd_elf_map_sections_to_segments): When generating
separate code and read-only data LOAD segments, create a new
LOAD segment if the previous section contains text and the
current section doesn't or vice versa.  Don't put a writable
section in a read-only segment if there is a RELRO segment.

bfd/ChangeLog
bfd/elf.c

index 5ceb5a0e661549a62b2be0eb879b18a7702cb80f..fa82d46641208b39ad59a85df3bd3db3d3af378b 100644 (file)
@@ -1,3 +1,12 @@
+2018-01-11  H.J. Lu  <hongjiu.lu@intel.com>
+
+       PR ld/22393
+       * elf.c (_bfd_elf_map_sections_to_segments): When generating
+       separate code and read-only data LOAD segments, create a new
+       LOAD segment if the previous section contains text and the
+       current section doesn't or vice versa.  Don't put a writable
+       section in a read-only segment if there is a RELRO segment.
+
 2018-01-11  H.J. Lu  <hongjiu.lu@intel.com>
 
        PR ld/22649
index 9f44ff978ad8d5bb539b531415d7293a4a8d5443..1d0eefd053694889812b6390a0c9fbb33a0f2a9c 100644 (file)
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -4566,6 +4566,7 @@ _bfd_elf_map_sections_to_segments (bfd *abfd, struct bfd_link_info *info)
       asection **hdrpp;
       bfd_boolean phdr_in_segment = TRUE;
       bfd_boolean writable;
+      bfd_boolean executable;
       int tls_count = 0;
       asection *first_tls = NULL;
       asection *first_mbind = NULL;
@@ -4654,6 +4655,7 @@ _bfd_elf_map_sections_to_segments (bfd *abfd, struct bfd_link_info *info)
       if (maxpagesize == 0)
        maxpagesize = 1;
       writable = FALSE;
+      executable = FALSE;
       dynsec = bfd_get_section_by_name (abfd, ".dynamic");
       if (dynsec != NULL
          && (dynsec->flags & SEC_LOAD) == 0)
@@ -4756,18 +4758,27 @@ _bfd_elf_map_sections_to_segments (bfd *abfd, struct bfd_link_info *info)
                 file, then there is no other reason for a new segment.  */
              new_segment = FALSE;
            }
+         else if (info != NULL
+                  && info->separate_code
+                  && executable != ((hdr->flags & SEC_CODE) != 0))
+           {
+             new_segment = TRUE;
+           }
          else if (! writable
                   && (hdr->flags & SEC_READONLY) == 0
-                  && (((last_hdr->lma + last_size - 1) & -maxpagesize)
-                      != (hdr->lma & -maxpagesize)))
+                  && ((info != NULL
+                       && info->relro_end > info->relro_start)
+                      || (((last_hdr->lma + last_size - 1) & -maxpagesize)
+                          != (hdr->lma & -maxpagesize))))
            {
              /* We don't want to put a writable section in a read only
                 segment, unless they are on the same page in memory
-                anyhow.  We already know that the last section does not
-                bring us past the current section on the page, so the
-                only case in which the new section is not on the same
-                page as the previous section is when the previous section
-                ends precisely on a page boundary.  */
+                anyhow and there is no RELRO segment.  We already
+                know that the last section does not bring us past the
+                current section on the page, so the only case in which
+                the new section is not on the same page as the previous
+                section is when the previous section ends precisely on
+                a page boundary.  */
              new_segment = TRUE;
            }
          else
@@ -4789,6 +4800,8 @@ _bfd_elf_map_sections_to_segments (bfd *abfd, struct bfd_link_info *info)
            {
              if ((hdr->flags & SEC_READONLY) == 0)
                writable = TRUE;
+             if ((hdr->flags & SEC_CODE) != 0)
+               executable = TRUE;
              last_hdr = hdr;
              /* .tbss sections effectively have zero size.  */
              if ((hdr->flags & (SEC_THREAD_LOCAL | SEC_LOAD))
@@ -4814,6 +4827,11 @@ _bfd_elf_map_sections_to_segments (bfd *abfd, struct bfd_link_info *info)
          else
            writable = FALSE;
 
+         if ((hdr->flags & SEC_CODE) == 0)
+           executable = FALSE;
+         else
+           executable = TRUE;
+
          last_hdr = hdr;
          /* .tbss sections effectively have zero size.  */
          if ((hdr->flags & (SEC_THREAD_LOCAL | SEC_LOAD)) != SEC_THREAD_LOCAL)