Start of relro segment adjustment

Adjusting the start of the relro segment in order to make it end
exactly on a page boundary runs into difficulties when sections in the
relro segment are aligned;  Adjusting the start by (next_page - end)
sometimes results in more than that adjustment occurring at the end,
overrunning the page boundary.  So when that occurs we try a new lower
start position by masking the adjusted start with the maximum section
alignment.  However, we didn't consider that this masked start address
may in fact be before the initial relro base, which is silly since
that can only increase padding at the relro end.

I've also moved some calculations closer to where they are used, and
comments closer to the relevant statements.

	* ldlang.c (lang_size_sections): When alignment of sections
	results in relro base adjustment being too large, don't go lower
	than the initial value.
	* ldexp.c (fold_binary <DATA_SEGMENT_RELRO_END>): Comment.
	* scripttempl/elf.sc (DATA_SEGMENT_ALIGN): Omit SEGMENT_SIZE
	alignment when SEGMENT_SIZE is the same as MAXPAGESIZE.
This commit is contained in:
Alan Modra
2015-04-01 19:19:27 +10:30
parent 269e9c185f
commit 6c1aca3e2d
4 changed files with 33 additions and 17 deletions

View File

@@ -5382,20 +5382,20 @@ lang_size_sections (bfd_boolean *relax, bfd_boolean check_regions)
if (expld.dataseg.phase == exp_dataseg_end_seen
&& link_info.relro && expld.dataseg.relro_end)
{
/* If DATA_SEGMENT_ALIGN DATA_SEGMENT_RELRO_END pair was seen, try
to put expld.dataseg.relro_end on a (common) page boundary. */
bfd_vma min_base, relro_end, maxpage;
bfd_vma initial_base, min_base, relro_end, maxpage;
expld.dataseg.phase = exp_dataseg_relro_adjust;
maxpage = expld.dataseg.maxpagesize;
/* MIN_BASE is the absolute minimum address we are allowed to start the
read-write segment (byte before will be mapped read-only). */
min_base = (expld.dataseg.min_base + maxpage - 1) & ~(maxpage - 1);
initial_base = expld.dataseg.base;
/* Try to put expld.dataseg.relro_end on a (common) page boundary. */
expld.dataseg.base += (-expld.dataseg.relro_end
& (expld.dataseg.pagesize - 1));
/* Compute the expected PT_GNU_RELRO segment end. */
relro_end = ((expld.dataseg.relro_end + expld.dataseg.pagesize - 1)
& ~(expld.dataseg.pagesize - 1));
/* MIN_BASE is the absolute minimum address we are allowed to start the
read-write segment (byte before will be mapped read-only). */
min_base = (expld.dataseg.min_base + maxpage - 1) & ~(maxpage - 1);
if (min_base + maxpage < expld.dataseg.base)
{
expld.dataseg.base -= maxpage;
@@ -5420,16 +5420,17 @@ lang_size_sections (bfd_boolean *relax, bfd_boolean check_regions)
&& sec->alignment_power > max_alignment_power)
max_alignment_power = sec->alignment_power;
if (((bfd_vma) 1 << max_alignment_power) < expld.dataseg.pagesize)
{
/* Aligning the adjusted base guarantees the padding
between sections won't change. This is better than
simply subtracting 1 << max_alignment_power which is
what we used to do here. */
expld.dataseg.base &= ~((1 << max_alignment_power) - 1);
lang_reset_memory_regions ();
one_lang_size_sections_pass (relax, check_regions);
}
/* Aligning the adjusted base guarantees the padding
between sections won't change. This is better than
simply subtracting 1 << max_alignment_power which is
what we used to do here. */
expld.dataseg.base &= ~((1 << max_alignment_power) - 1);
/* It doesn't make much sense to go lower than the initial
base. That can only increase padding. */
if (expld.dataseg.base < initial_base)
expld.dataseg.base = initial_base;
lang_reset_memory_regions ();
one_lang_size_sections_pass (relax, check_regions);
}
link_info.relro_start = expld.dataseg.base;
link_info.relro_end = expld.dataseg.relro_end;