This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
RFC: ELF: Add a "-z readonly" option to ld
- From: "H.J. Lu" <hongjiu dot lu at intel dot com>
- To: binutils at sourceware dot org
- Date: Tue, 7 Nov 2017 16:25:22 -0800
- Subject: RFC: ELF: Add a "-z readonly" option to ld
- Authentication-results: sourceware.org; auth=none
- Reply-to: "H.J. Lu" <hjl dot tools at gmail dot com>
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