This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

RFC: ELF: Add a "-z readonly" option to ld


We currently put read-only sections in read-execute segment:

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

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x00000000 0x00000000 0x00524 0x00524 R E 0x200000
  LOAD           0x000ef4 0x00200ef4 0x00200ef4 0x0012c 0x00130 RW  0x200000
  DYNAMIC        0x000f00 0x00200f00 0x00200f00 0x000e0 0x000e0 RW  0x4
  NOTE           0x000114 0x00000114 0x00000114 0x00024 0x00024 R   0x4
  GNU_EH_FRAME   0x00048c 0x0000048c 0x0000048c 0x00024 0x00024 R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10
  GNU_RELRO      0x000ef4 0x00200ef4 0x00200ef4 0x0010c 0x0010c R   0x1

 Section to Segment mapping:
  Segment Sections...
   00     .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame
   01     .init_array .fini_array .data.rel.ro .dynamic .got .got.plt .bss
   02     .dynamic
   03     .note.gnu.build-id
   04     .eh_frame_hdr
   05
   06     .init_array .fini_array .data.rel.ro .dynamic .got

This patch adds a "-z readonly" option to ELF linker to place read-only
in separate read-only segments:

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

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x00000000 0x00000000 0x003a4 0x003a4 R   0x1000
  LOAD           0x001000 0x00001000 0x00001000 0x00125 0x00125 R E 0x1000
  LOAD           0x002000 0x00002000 0x00002000 0x000a0 0x000a0 R   0x1000
  LOAD           0x002ef4 0x00003ef4 0x00003ef4 0x0012c 0x00130 RW  0x1000
  DYNAMIC        0x002f00 0x00003f00 0x00003f00 0x000e0 0x000e0 RW  0x4
  NOTE           0x000154 0x00000154 0x00000154 0x00024 0x00024 R   0x4
  GNU_EH_FRAME   0x002008 0x00002008 0x00002008 0x00024 0x00024 R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10
  GNU_RELRO      0x002ef4 0x00003ef4 0x00003ef4 0x0010c 0x0010c R   0x1

 Section to Segment mapping:
  Segment Sections...
   00     .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
   01     .init .plt .plt.got .text .fini
   02     .rodata .eh_frame_hdr .eh_frame
   03     .init_array .fini_array .data.rel.ro .dynamic .got .got.plt .bss
   04     .dynamic
   05     .note.gnu.build-id
   06     .eh_frame_hdr
   07
   08     .init_array .fini_array .data.rel.ro .dynamic .got

This is to prevent executing data in read-only sections as instructions.
To reduce the file size, I changed the default max page size to the
common page size when -z readonly is used.

Any comments?


H.J.
---
 bfd/elf.c             | 43 +++++++++++++++++++++++++++++++++++++++----
 include/bfdlink.h     |  3 +++
 ld/emultempl/elf32.em |  5 +++++
 ld/ld.h               |  1 +
 ld/lexsup.c           |  7 +++++++
 5 files changed, 55 insertions(+), 4 deletions(-)

diff --git a/bfd/elf.c b/bfd/elf.c
index 9cecb24922..164eee9c06 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -4382,6 +4382,29 @@ get_program_header_size (bfd *abfd, struct bfd_link_info *info)
 	     s->alignment_power = page_align_power;
 	   segs ++;
 	 }
+
+     if (info != NULL && info->readonly)
+       {
+	 page_align_power = bfd_log2 (bed->maxpagesize);
+	 for (s = abfd->sections; s != NULL; s = s->next)
+	   if ((s->flags & SEC_CODE))
+	     {
+	       /* Align the first text section to page size.  */
+	       if (s->alignment_power < page_align_power)
+		 s->alignment_power = page_align_power;
+
+	       for (s = s->next; s != NULL; s = s->next)
+		 if ((s->flags & (SEC_CODE | SEC_ALLOC)) == SEC_ALLOC)
+		   {
+		     /* Align the next non-text section to page size.  */
+		     if (s->alignment_power < page_align_power)
+		       s->alignment_power = page_align_power;
+		     break;
+		   }
+
+	       break;
+	     }
+       }
    }
 
  /* Let the backend count up any program headers it might need.  */
@@ -4555,6 +4578,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;
@@ -4644,6 +4668,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)
@@ -4746,10 +4771,13 @@ _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 (! writable
-		   && (hdr->flags & SEC_READONLY) == 0
-		   && (((last_hdr->lma + last_size - 1) & -maxpagesize)
-		       != (hdr->lma & -maxpagesize)))
+	  else if ((info != NULL
+		    && info->readonly
+		    && executable != ((hdr->flags & SEC_CODE) != 0))
+		   || (! writable
+		       && (hdr->flags & SEC_READONLY) == 0
+		       && (((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
@@ -4779,6 +4807,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))
@@ -4804,6 +4834,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)
diff --git a/include/bfdlink.h b/include/bfdlink.h
index 2370c0d45a..eb0db2cae2 100644
--- a/include/bfdlink.h
+++ b/include/bfdlink.h
@@ -386,6 +386,9 @@ struct bfd_link_info
   /* TRUE if PT_GNU_RELRO segment should be created.  */
   unsigned int relro: 1;
 
+  /* TRUE if read-only PT_LOAD segment should be created.  */
+  unsigned int readonly: 1;
+
   /* Nonzero if .eh_frame_hdr section and PT_GNU_EH_FRAME ELF segment
      should be created.  1 for DWARF2 tables, 2 for compact tables.  */
   unsigned int eh_frame_hdr_type: 2;
diff --git a/ld/emultempl/elf32.em b/ld/emultempl/elf32.em
index ff4d565a21..02ec420d61 100644
--- a/ld/emultempl/elf32.em
+++ b/ld/emultempl/elf32.em
@@ -2651,6 +2651,7 @@ fragment <<EOF
 	{
 	  char *end;
 
+	  config.maxpagesize_set = TRUE;
 	  config.maxpagesize = strtoul (optarg + 14, &end, 0);
 	  if (*end || (config.maxpagesize & (config.maxpagesize - 1)) != 0)
 	    einfo (_("%P%F: invalid maxium page size \`%s'\n"),
@@ -2735,6 +2736,10 @@ fragment <<EOF
 	link_info.relro = TRUE;
       else if (strcmp (optarg, "norelro") == 0)
 	link_info.relro = FALSE;
+      else if (strcmp (optarg, "readonly") == 0)
+	link_info.readonly = TRUE;
+      else if (strcmp (optarg, "noreadonly") == 0)
+	link_info.readonly = FALSE;
       else if (strcmp (optarg, "common") == 0)
 	link_info.elf_stt_common = elf_stt_common;
       else if (strcmp (optarg, "nocommon") == 0)
diff --git a/ld/ld.h b/ld/ld.h
index 26e7f89a28..a128db0447 100644
--- a/ld/ld.h
+++ b/ld/ld.h
@@ -315,6 +315,7 @@ typedef struct
 
   /* The maximum page size for ELF.  */
   bfd_vma maxpagesize;
+  bfd_boolean maxpagesize_set;
 
   /* The common page size for ELF.  */
   bfd_vma commonpagesize;
diff --git a/ld/lexsup.c b/ld/lexsup.c
index cb9edaf7a6..46f47b34f2 100644
--- a/ld/lexsup.c
+++ b/ld/lexsup.c
@@ -1571,6 +1571,9 @@ parse_args (unsigned argc, char **argv)
 	}
     }
 
+  if (link_info.readonly && !config.maxpagesize_set)
+    config.maxpagesize = config.commonpagesize;
+
   if (command_line.soname && command_line.soname[0] == '\0')
     {
       einfo (_("%P: SONAME must not be empty string; ignored\n"));
@@ -1788,6 +1791,10 @@ elf_shlib_list_options (FILE *file)
   -z norelro                  Don't create RELRO program header (default)\n"));
 #endif
   fprintf (file, _("\
+  -z readonly                 Create read-only LOAD program header\n"));
+  fprintf (file, _("\
+  -z noreadonly               Don't create read-only LOAD program header (default)\n"));
+  fprintf (file, _("\
   -z common                   Generate common symbols with STT_COMMON type\n"));
   fprintf (file, _("\
   -z nocommon                 Generate common symbols with STT_OBJECT type\n"));
-- 
2.13.6


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]