From c91ad020c2d0c10cb41ecd1516baa0321ce0c895 Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Fri, 10 Nov 2017 13:53:20 -0800 Subject: ld: Add TEXT_SEGMENT_ALIGN/TEXT_SEGMENT_{RELRO_}END Text-only LOAD segment has the same requirement for segment alignment and page sizes as GNU_RELRO segment. But for GNU_RELRO segment, the segment may not end at the same address of the end of data segment. But text-only LOAD segment is exactly the same as text LOAD segment. This patch adds TEXT_SEGMENT_ALIGN, TEXT_SEGMENT_RELRO_END and TEXT_SEGMENT_END, which mimic DATA_SEGMENT_ALIGN, DATA_SEGMENT_RELRO_END and DATA_SEGMENT_END. They work on text segment, instead of data segment. TEXT_SEGMENT_ALIGN is placed at the start of text sections. Both TEXT_SEGMENT_RELRO_END and TEXT_SEGMENT_END are placed at the end of text sections. TEXT_SEGMENT_ALIGN is created from DATA_SEGMENT_ALIGN by replacing DATA_SEGMENT_ALIGN with TEXT_SEGMENT_ALIGN. It simply sets text_start and text_end from TEXT_SEGMENT_ALIGN, TEXT_SEGMENT_RELRO_END and TEXT_SEGMENT_END the same way as relro_start and relro_end are set from DATA_SEGMENT_ALIGN, DATA_SEGMENT_RELRO_END and DATA_SEGMENT_END. include/ PR ld/22393 * bfdlink.h (bfd_link_info): Add text_start and text_end. ld/ PR ld/22393 * ldexp.c (exp_print_token): Add TEXT_SEGMENT_ALIGN, TEXT_SEGMENT_RELRO_END and TEXT_SEGMENT_END. (fold_unary): Handle TEXT_SEGMENT_END. (fold_binary): Handle TEXT_SEGMENT_RELRO_END and TEXT_SEGMENT_END. (exp_unop): Also check TEXT_SEGMENT_END. * ldexp.h (ldexp_control): Add textseg. * ldgram.y: Handle TEXT_SEGMENT_ALIGN, TEXT_SEGMENT_RELRO_END and TEXT_SEGMENT_END. * ldlang.c (strip_excluded_output_sections): Also set expld.textseg.phase to exp_seg_none. (lang_size_sections_1): Also call ldlang_check_relro_region with &expld.textseg. (lang_size_relro_segment): Also handle expld.textseg. (lang_size_sections): Also handle expld.textseg. Set link_info.text_start and link_info.text_end for -z textonly. (lang_find_relro_sections): Also check expld.textseg. * ldlex.l: Add TEXT_SEGMENT_ALIGN, TEXT_SEGMENT_RELRO_END and TEXT_SEGMENT_END. * scripttempl/elf.sc (TEXT_SEGMENT_ALIGN): New. (TEXT_SEGMENT_RELRO_END): Likewise. (TEXT_SEGMENT_END): Likewise. Add ${TEXT_SEGMENT_ALIGN} before text sections and add ${TEXT_SEGMENT_RELRO_END}/${TEXT_SEGMENT_END} after text sections for non-relocatable link. --- include/bfdlink.h | 3 ++ ld/ldexp.c | 18 ++++++++- ld/ldexp.h | 3 ++ ld/ldgram.y | 7 ++++ ld/ldlang.c | 109 +++++++++++++++++++++++++++++++++++++------------- ld/ldlex.l | 3 ++ ld/scripttempl/elf.sc | 21 ++++++++++ 7 files changed, 135 insertions(+), 29 deletions(-) diff --git a/include/bfdlink.h b/include/bfdlink.h index 0fa74a71bdd..a04e7886ae5 100644 --- a/include/bfdlink.h +++ b/include/bfdlink.h @@ -627,6 +627,9 @@ struct bfd_link_info /* May be used to set DT_FLAGS_1 for ELF. */ bfd_vma flags_1; + /* Start and end of text-only region. */ + bfd_vma text_start, text_end; + /* Start and end of RELRO region. */ bfd_vma relro_start, relro_end; diff --git a/ld/ldexp.c b/ld/ldexp.c index 9fbc892e4b5..86ae7110bfb 100644 --- a/ld/ldexp.c +++ b/ld/ldexp.c @@ -131,6 +131,9 @@ exp_print_token (token_code_type code, int infix_p) { DATA_SEGMENT_ALIGN, "DATA_SEGMENT_ALIGN" }, { DATA_SEGMENT_RELRO_END, "DATA_SEGMENT_RELRO_END" }, { DATA_SEGMENT_END, "DATA_SEGMENT_END" }, + { TEXT_SEGMENT_ALIGN, "TEXT_SEGMENT_ALIGN" }, + { TEXT_SEGMENT_RELRO_END, "TEXT_SEGMENT_RELRO_END" }, + { TEXT_SEGMENT_END, "TEXT_SEGMENT_END" }, { ORIGIN, "ORIGIN" }, { LENGTH, "LENGTH" }, { SEGMENT_START, "SEGMENT_START" } @@ -411,6 +414,10 @@ fold_unary (etree_type *tree) fold_segment_end (&expld.dataseg); break; + case TEXT_SEGMENT_END: + fold_segment_end (&expld.textseg); + break; + default: FAIL (); break; @@ -659,6 +666,14 @@ fold_binary (etree_type *tree) fold_segment_relro_end (&expld.dataseg, &lhs); break; + case TEXT_SEGMENT_ALIGN: + fold_segment_align (&expld.textseg, &lhs); + break; + + case TEXT_SEGMENT_RELRO_END: + fold_segment_relro_end (&expld.textseg, &lhs); + break; + default: FAIL (); } @@ -1311,7 +1326,8 @@ exp_unop (int code, etree_type *child) && code != ALIGN_K && code != ABSOLUTE && code != NEXT - && code != DATA_SEGMENT_END) + && code != DATA_SEGMENT_END + && code != TEXT_SEGMENT_END) exp_value_fold (new_e); return new_e; } diff --git a/ld/ldexp.h b/ld/ldexp.h index 572b703c203..d7b6981418b 100644 --- a/ld/ldexp.h +++ b/ld/ldexp.h @@ -178,6 +178,9 @@ struct ldexp_control { /* State machine and results for DATASEG. */ seg_align_type dataseg; + + /* State machine and results for TEXTSEG. */ + seg_align_type textseg; }; extern struct ldexp_control expld; diff --git a/ld/ldgram.y b/ld/ldgram.y index ba89a4fe3a7..0c0bcb4155c 100644 --- a/ld/ldgram.y +++ b/ld/ldgram.y @@ -127,6 +127,7 @@ static int error_index; %token ALIGN_K BLOCK BIND QUAD SQUAD LONG SHORT BYTE %token SECTIONS PHDRS INSERT_K AFTER BEFORE %token DATA_SEGMENT_ALIGN DATA_SEGMENT_RELRO_END DATA_SEGMENT_END +%token TEXT_SEGMENT_ALIGN TEXT_SEGMENT_RELRO_END TEXT_SEGMENT_END %token SORT_BY_NAME SORT_BY_ALIGNMENT SORT_NONE %token SORT_BY_INIT_PRIORITY %token '{' '}' @@ -993,6 +994,12 @@ exp : { $$ = exp_binop (DATA_SEGMENT_RELRO_END, $5, $3); } | DATA_SEGMENT_END '(' exp ')' { $$ = exp_unop (DATA_SEGMENT_END, $3); } + | TEXT_SEGMENT_ALIGN '(' exp ',' exp ')' + { $$ = exp_binop (TEXT_SEGMENT_ALIGN, $3, $5); } + | TEXT_SEGMENT_RELRO_END '(' exp ',' exp ')' + { $$ = exp_binop (TEXT_SEGMENT_RELRO_END, $5, $3); } + | TEXT_SEGMENT_END '(' exp ')' + { $$ = exp_unop (TEXT_SEGMENT_END, $3); } | SEGMENT_START '(' NAME ',' exp ')' { /* The operands to the expression node are placed in the opposite order from the way diff --git a/ld/ldlang.c b/ld/ldlang.c index 294e6323012..eee8b875ce4 100644 --- a/ld/ldlang.c +++ b/ld/ldlang.c @@ -3889,6 +3889,7 @@ strip_excluded_output_sections (void) { expld.phase = lang_mark_phase_enum; expld.dataseg.phase = exp_seg_none; + expld.textseg.phase = exp_seg_none; one_lang_size_sections_pass (NULL, FALSE); lang_reset_memory_regions (); } @@ -5448,14 +5449,17 @@ lang_size_sections_1 bfd_vma newdot = dot; etree_type *tree = s->assignment_statement.exp; + expld.textseg.relro = exp_seg_relro_none; expld.dataseg.relro = exp_seg_relro_none; exp_fold_tree (tree, output_section_statement->bfd_section, &newdot); + ldlang_check_relro_region (s, &expld.textseg); ldlang_check_relro_region (s, &expld.dataseg); + expld.textseg.relro = exp_seg_relro_none; expld.dataseg.relro = exp_seg_relro_none; /* This symbol may be relative to this section. */ @@ -5662,35 +5666,56 @@ static bfd_boolean lang_size_relro_segment (bfd_boolean *relax, bfd_boolean check_regions) { bfd_boolean do_reset = FALSE; - bfd_boolean do_data_relro; - bfd_vma data_initial_base, data_relro_end; + bfd_boolean do_text_relro = FALSE; + bfd_boolean do_data_relro = FALSE; - if (link_info.relro && expld.dataseg.relro_end) + if (link_info.relro) { - do_data_relro = TRUE; - data_initial_base = expld.dataseg.base; - data_relro_end = lang_size_relro_segment_1 (&expld.dataseg); - } - else - { - do_data_relro = FALSE; - data_initial_base = data_relro_end = 0; - } + bfd_vma text_initial_base, text_relro_end; + bfd_vma data_initial_base, data_relro_end; - if (do_data_relro) - { - lang_reset_memory_regions (); - one_lang_size_sections_pass (relax, check_regions); + if (link_info.relro > 1 && expld.textseg.relro_end) + { + do_text_relro = TRUE; + text_initial_base = expld.textseg.base; + text_relro_end = lang_size_relro_segment_1 (&expld.textseg); + } + else + text_initial_base = text_relro_end = 0; - /* Assignments to dot, or to output section address in a user - script have increased padding over the original. Revert. */ - if (do_data_relro && expld.dataseg.relro_end > data_relro_end) + if (expld.dataseg.relro_end) { - expld.dataseg.base = data_initial_base;; - do_reset = TRUE; + do_data_relro = TRUE; + data_initial_base = expld.dataseg.base; + data_relro_end = lang_size_relro_segment_1 (&expld.dataseg); + } + else + data_initial_base = data_relro_end = 0; + + if (do_text_relro || do_data_relro) + { + lang_reset_memory_regions (); + one_lang_size_sections_pass (relax, check_regions); + + /* Assignments to dot, or to output section address in a user + script have increased padding over the original. Revert. */ + if (do_text_relro && expld.textseg.relro_end > text_relro_end) + { + expld.textseg.base = text_initial_base; + do_reset = TRUE; + } + + if (do_data_relro && expld.dataseg.relro_end > data_relro_end) + { + expld.dataseg.base = data_initial_base;; + do_reset = TRUE; + } } } + if (!do_text_relro && lang_size_segment (&expld.textseg)) + do_reset = TRUE; + if (!do_data_relro && lang_size_segment (&expld.dataseg)) do_reset = TRUE; @@ -5702,13 +5727,17 @@ lang_size_sections (bfd_boolean *relax, bfd_boolean check_regions) { expld.phase = lang_allocating_phase_enum; expld.dataseg.phase = exp_seg_none; + expld.textseg.phase = exp_seg_none; one_lang_size_sections_pass (relax, check_regions); + if (expld.textseg.phase != exp_seg_end_seen) + expld.textseg.phase = exp_seg_done; if (expld.dataseg.phase != exp_seg_end_seen) expld.dataseg.phase = exp_seg_done; - if (expld.dataseg.phase == exp_seg_end_seen) + if (expld.textseg.phase == exp_seg_end_seen + || expld.dataseg.phase == exp_seg_end_seen) { bfd_boolean do_reset = lang_size_relro_segment (relax, check_regions); @@ -5719,6 +5748,12 @@ lang_size_sections (bfd_boolean *relax, bfd_boolean check_regions) one_lang_size_sections_pass (relax, check_regions); } + if (link_info.relro > 1 && expld.textseg.relro_end) + { + link_info.text_start = expld.textseg.base; + link_info.text_end = expld.textseg.relro_end; + } + if (link_info.relro && expld.dataseg.relro_end) { link_info.relro_start = expld.dataseg.base; @@ -6904,15 +6939,33 @@ lang_find_relro_sections_1 (lang_statement_union_type *s, static void lang_find_relro_sections (void) { - bfd_boolean has_relro_section = FALSE; - /* Check all sections in the link script. */ + if (link_info.relro) + { + bfd_boolean has_relro_section; - lang_find_relro_sections_1 (expld.dataseg.relro_start_stat, - &expld.dataseg, &has_relro_section); + if (link_info.relro > 1) + { + has_relro_section = FALSE; + lang_find_relro_sections_1 (expld.textseg.relro_start_stat, + &expld.textseg, + &has_relro_section); + if (!has_relro_section) + link_info.relro = 1; + } - if (!has_relro_section) - link_info.relro = FALSE; + /* We can't turn off RELRO if we need to generate read-only + PT_LOAD segment. */ + if (link_info.relro == 1) + { + has_relro_section = FALSE; + lang_find_relro_sections_1 (expld.dataseg.relro_start_stat, + &expld.dataseg, + &has_relro_section); + if (!has_relro_section) + link_info.relro = 0; + } + } } /* Relax all sections until bfd_relax_section gives up. */ diff --git a/ld/ldlex.l b/ld/ldlex.l index 2f59d79d12c..1f4080e6320 100644 --- a/ld/ldlex.l +++ b/ld/ldlex.l @@ -250,6 +250,9 @@ V_IDENTIFIER [*?.$_a-zA-Z\[\]\-\!\^\\]([*?.$_a-zA-Z0-9\[\]\-\!\^\\]|::)* "DATA_SEGMENT_ALIGN" { RTOKEN(DATA_SEGMENT_ALIGN);} "DATA_SEGMENT_RELRO_END" { RTOKEN(DATA_SEGMENT_RELRO_END);} "DATA_SEGMENT_END" { RTOKEN(DATA_SEGMENT_END);} +"TEXT_SEGMENT_ALIGN" { RTOKEN(TEXT_SEGMENT_ALIGN);} +"TEXT_SEGMENT_RELRO_END" { RTOKEN(TEXT_SEGMENT_RELRO_END);} +"TEXT_SEGMENT_END" { RTOKEN(TEXT_SEGMENT_END);} "ADDR" { RTOKEN(ADDR);} "LOADADDR" { RTOKEN(LOADADDR);} "ALIGNOF" { RTOKEN(ALIGNOF); } diff --git a/ld/scripttempl/elf.sc b/ld/scripttempl/elf.sc index 841a831437e..29e5f085b97 100644 --- a/ld/scripttempl/elf.sc +++ b/ld/scripttempl/elf.sc @@ -139,6 +139,23 @@ if test -z "$DATA_SEGMENT_ALIGN"; then DATA_SEGMENT_RELRO_END=". = DATA_SEGMENT_RELRO_END (${SEPARATE_GOTPLT-0}, .);" fi fi +# Don't bother with text-only segment when there are data sections between +# .plt and .text. +if test -n "$TINY_READONLY_SECTION"; then + TEXT_SEGMENT_ALIGN=" " + TEXT_SEGMENT_RELRO_END=" " + TEXT_SEGMENT_END=" " +fi +if test -z "$TEXT_SEGMENT_ALIGN" && test -n "$DATA_SEGMENT_ALIGN"; then + case "$LD_FLAG" in + *textonly*) + TEXT_SEGMENT_ALIGN=`echo $DATA_SEGMENT_ALIGN | sed -e "s/DATA/TEXT/g"` + TEXT_SEGMENT_ALIGN=". = $TEXT_SEGMENT_ALIGN;" + TEXT_SEGMENT_RELRO_END=". = TEXT_SEGMENT_RELRO_END (0, .);" + TEXT_SEGMENT_END=". = TEXT_SEGMENT_END (.);" + ;; + esac +fi if test -z "${INITIAL_READONLY_SECTIONS}${CREATE_SHLIB}"; then INITIAL_READONLY_SECTIONS=".interp ${RELOCATING-0} : { *(.interp) }" fi @@ -478,6 +495,8 @@ emit_dyn() test -n "${NON_ALLOC_DYN}${SEPARATE_CODE}" || emit_dyn cat <