From cba6246d3ad332ab353afeff0016b195ffc69e05 Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Sun, 12 Nov 2017 17:22:15 -0800 Subject: [PATCH] ld: Add lang_size_relro_segment Break lang_size_sections down into separate functions so that they can also be used for text-only LOAD segment. lang_size_relro_segment will call lang_size_relro_segment_1 and lang_size_segment for both GNU_RELRO segment and text-only LOAD segment. * ldlang.c (lang_size_segment): New function. (lang_size_relro_segment_1): Likewise. (lang_size_relro_segment): Likewise. (lang_size_sections): Rewrite to call lang_size_relro_segment. --- ld/ChangeLog | 7 +++ ld/ldlang.c | 174 +++++++++++++++++++++++++++++++++++++---------------------- 2 files changed, 117 insertions(+), 64 deletions(-) diff --git a/ld/ChangeLog b/ld/ChangeLog index bee75c1..16fed16 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,5 +1,12 @@ 2017-11-12 H.J. Lu + * ldlang.c (lang_size_segment): New function. + (lang_size_relro_segment_1): Likewise. + (lang_size_relro_segment): Likewise. + (lang_size_sections): Rewrite to call lang_size_relro_segment. + +2017-11-12 H.J. Lu + * ldexp.c (fold_unary): Extract the DATA_SEGMENT_END case to ... (fold_segment_end): New function. (fold_binary): Extract the DATA_SEGMENT_ALIGN case to ... diff --git a/ld/ldlang.c b/ld/ldlang.c index 2cb1647..6bb9e47 100644 --- a/ld/ldlang.c +++ b/ld/ldlang.c @@ -5589,90 +5589,136 @@ one_lang_size_sections_pass (bfd_boolean *relax, bfd_boolean check_regions) 0, 0, relax, check_regions); } -void -lang_size_sections (bfd_boolean *relax, bfd_boolean check_regions) +static bfd_boolean +lang_size_segment (seg_align_type *seg) { - expld.phase = lang_allocating_phase_enum; - expld.dataseg.phase = exp_seg_none; + /* If XXX_SEGMENT_ALIGN XXX_SEGMENT_END pair was seen, check whether + a page could be saved in the data segment. */ + bfd_vma first, last; - one_lang_size_sections_pass (relax, check_regions); - if (expld.dataseg.phase == exp_seg_end_seen - && link_info.relro && expld.dataseg.relro_end) + first = -seg->base & (seg->pagesize - 1); + last = seg->end & (seg->pagesize - 1); + if (first && last + && ((seg->base & ~(seg->pagesize - 1)) + != (seg->end & ~(seg->pagesize - 1))) + && first + last <= seg->pagesize) { - bfd_vma initial_base, relro_end, desired_end; - asection *sec; + seg->phase = exp_seg_adjust; + return TRUE; + } - /* Compute the expected PT_GNU_RELRO segment end. */ - relro_end = ((expld.dataseg.relro_end + expld.dataseg.pagesize - 1) - & ~(expld.dataseg.pagesize - 1)); + seg->phase = exp_seg_done; + return FALSE; +} - /* Adjust by the offset arg of DATA_SEGMENT_RELRO_END. */ - desired_end = relro_end - expld.dataseg.relro_offset; +static bfd_vma +lang_size_relro_segment_1 (seg_align_type *seg) +{ + bfd_vma relro_end, desired_end; + asection *sec; - /* For sections in the relro segment.. */ - for (sec = link_info.output_bfd->section_last; sec; sec = sec->prev) - if ((sec->flags & SEC_ALLOC) != 0 - && sec->vma >= expld.dataseg.base - && sec->vma < expld.dataseg.relro_end - expld.dataseg.relro_offset) - { - /* Where do we want to put this section so that it ends as - desired? */ - bfd_vma start, end, bump; - - end = start = sec->vma; - if (!IS_TBSS (sec)) - end += TO_ADDR (sec->size); - bump = desired_end - end; - /* We'd like to increase START by BUMP, but we must heed - alignment so the increase might be less than optimum. */ - start += bump; - start &= ~(((bfd_vma) 1 << sec->alignment_power) - 1); - /* This is now the desired end for the previous section. */ - desired_end = start; - } + /* Compute the expected PT_GNU_RELRO/PT_LOAD segment end. */ + relro_end = ((seg->relro_end + seg->pagesize - 1) + & ~(seg->pagesize - 1)); + + /* Adjust by the offset arg of XXX_SEGMENT_RELRO_END. */ + desired_end = relro_end - seg->relro_offset; + + /* For sections in the relro segment.. */ + for (sec = link_info.output_bfd->section_last; sec; sec = sec->prev) + if ((sec->flags & SEC_ALLOC) != 0 + && sec->vma >= seg->base + && sec->vma < seg->relro_end - seg->relro_offset) + { + /* Where do we want to put this section so that it ends as + desired? */ + bfd_vma start, end, bump; + + end = start = sec->vma; + if (!IS_TBSS (sec)) + end += TO_ADDR (sec->size); + bump = desired_end - end; + /* We'd like to increase START by BUMP, but we must heed + alignment so the increase might be less than optimum. */ + start += bump; + start &= ~(((bfd_vma) 1 << sec->alignment_power) - 1); + /* This is now the desired end for the previous section. */ + desired_end = start; + } + + seg->phase = exp_seg_relro_adjust; + ASSERT (desired_end >= seg->base); + seg->base = desired_end; + return relro_end; +} + +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; + + if (link_info.relro && expld.dataseg.relro_end) + { + 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; + } - expld.dataseg.phase = exp_seg_relro_adjust; - ASSERT (desired_end >= expld.dataseg.base); - initial_base = expld.dataseg.base; - expld.dataseg.base = desired_end; + if (do_data_relro) + { lang_reset_memory_regions (); one_lang_size_sections_pass (relax, check_regions); - if (expld.dataseg.relro_end > relro_end) + /* 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) { - /* Assignments to dot, or to output section address in a - user script have increased padding over the original. - Revert. */ - expld.dataseg.base = initial_base; - lang_reset_memory_regions (); - one_lang_size_sections_pass (relax, check_regions); + expld.dataseg.base = data_initial_base;; + do_reset = TRUE; } - - link_info.relro_start = expld.dataseg.base; - link_info.relro_end = expld.dataseg.relro_end; } - else if (expld.dataseg.phase == exp_seg_end_seen) + + if (!do_data_relro && lang_size_segment (&expld.dataseg)) + do_reset = TRUE; + + return do_reset; +} + +void +lang_size_sections (bfd_boolean *relax, bfd_boolean check_regions) +{ + expld.phase = lang_allocating_phase_enum; + expld.dataseg.phase = exp_seg_none; + + one_lang_size_sections_pass (relax, check_regions); + + if (expld.dataseg.phase != exp_seg_end_seen) + expld.dataseg.phase = exp_seg_done; + + if (expld.dataseg.phase == exp_seg_end_seen) { - /* If DATA_SEGMENT_ALIGN DATA_SEGMENT_END pair was seen, check whether - a page could be saved in the data segment. */ - bfd_vma first, last; + bfd_boolean do_reset + = lang_size_relro_segment (relax, check_regions); - first = -expld.dataseg.base & (expld.dataseg.pagesize - 1); - last = expld.dataseg.end & (expld.dataseg.pagesize - 1); - if (first && last - && ((expld.dataseg.base & ~(expld.dataseg.pagesize - 1)) - != (expld.dataseg.end & ~(expld.dataseg.pagesize - 1))) - && first + last <= expld.dataseg.pagesize) + if (do_reset) { - expld.dataseg.phase = exp_seg_adjust; lang_reset_memory_regions (); one_lang_size_sections_pass (relax, check_regions); } - else - expld.dataseg.phase = exp_seg_done; + + if (link_info.relro && expld.dataseg.relro_end) + { + link_info.relro_start = expld.dataseg.base; + link_info.relro_end = expld.dataseg.relro_end; + } } - else - expld.dataseg.phase = exp_seg_done; } static lang_output_section_statement_type *current_section; -- 2.7.4