This is the mail archive of the
binutils@sources.redhat.com
mailing list for the binutils project.
[PATCH] Fix -z relro
- From: Jakub Jelinek <jakub at redhat dot com>
- To: binutils at sources dot redhat dot com
- Cc: drepper at redhat dot com
- Date: Mon, 20 Sep 2004 13:24:46 +0200
- Subject: [PATCH] Fix -z relro
- Reply-to: Jakub Jelinek <jakub at redhat dot com>
Hi!
The current simplistic approach for -z relro doesn't seem to work very well.
E.g. current IA-32 ld.so has unusable PT_GNU_RELRO segment:
[10] .rodata PROGBITS 000119e0 0119e0 0024f5 00 A 0 0 32
[11] .eh_frame_hdr PROGBITS 00013ed8 013ed8 00005c 00 A 0 0 4
[12] .eh_frame PROGBITS 00013f34 013f34 00015c 00 A 0 0 4
[13] .data.rel.ro PROGBITS 00015ca0 014ca0 000270 00 WA 0 0 32
[14] .dynamic DYNAMIC 00015f10 014f10 0000c0 08 WA 3 0 4
[15] .got PROGBITS 00015fd0 014fd0 00002c 04 WA 0 0 4
[16] .data PROGBITS 00016000 015000 0002c8 00 WA 0 0 32
[17] .bss NOBITS 000162c8 0152c8 0000b4 00 WA 0 0 8
LOAD 0x000000 0x00000000 0x00000000 0x14090 0x14090 R E 0x1000
LOAD 0x014ca0 0x00015ca0 0x00015ca0 0x00628 0x006dc RW 0x1000
...
GNU_RELRO 0x014ca0 0x00015ca0 0x00015ca0 0x0035c 0x0035c R 0x1
Note the PT_GNU_RELRO header doesn't end at 0x16000, but 0x15ffc.
DATA_SEGMENT_ALIGN simply moved the start of RW (and thus also RELRO) segment
up, but this relied on the move not changing relro_end - base difference.
But that difference can change, in both ways (be bigger or smaller), I have
successfully created examples of both.
The following patch changes DATA_SEGMENT_RELRO_END to insert padding
to make the end really COMMONPAGESIZE aligned.
If the base += -relro_end & (COMMONPAGESIZE-1) adjustement results in
too big padding (i.e. relro_end is not old
(relro_end + (COMMONPAGESIZE - 1)) & ~(COMMONPAGESIZE - 1), but bigger),
another base is attempted.
Ok to commit?
2004-09-20 Jakub Jelinek <jakub@redhat.com>
* ldgram.y (DATA_SEGMENT_RELRO_END): Add one argument.
* scripttempl/elf.sc (DATA_SEGMENT_RELRO_END): Add 0 as first
argument.
(DATA_SEGMENT_RELRO_GOTPLT_END): Pass $SEPARATE_GOTPLT as first
and . as second argument.
(GOTPLT): Move $DATA_SEGMENT_RELRO_GOTPLT_END before the section.
* ldexp.c (fold_unary): Remove DATA_SEGMENT_RELRO_END handling here.
(fold_binary): Add it here. Insert padding to make relro_end
COMMONPAGESIZE bytes aligned. For DATA_SEGMENT_ALIGN in
exp_dataseg_relro_adjust phase just use previously computed
exp_data_seg.base.
* ldlang.c (lang_size_sections): Set exp_data_seg.base for
relro_adjust here. Call lang_size_sections_1 once more if there
was too big padding at DATA_SEGMENT_RELRO_END.
--- ld/ldgram.y.jj 2004-05-19 15:59:47.000000000 +0200
+++ ld/ldgram.y 2004-09-20 11:29:27.101105963 +0200
@@ -808,8 +808,8 @@ exp :
{ $$ = exp_binop(ALIGN_K,$3,$5); }
| DATA_SEGMENT_ALIGN '(' exp ',' exp ')'
{ $$ = exp_binop (DATA_SEGMENT_ALIGN, $3, $5); }
- | DATA_SEGMENT_RELRO_END '(' exp ')'
- { $$ = exp_unop(DATA_SEGMENT_RELRO_END, $3); }
+ | DATA_SEGMENT_RELRO_END '(' exp ',' exp ')'
+ { $$ = exp_binop (DATA_SEGMENT_RELRO_END, $5, $3); }
| DATA_SEGMENT_END '(' exp ')'
{ $$ = exp_unop(DATA_SEGMENT_END, $3); }
| BLOCK '(' exp ')'
--- ld/scripttempl/elf.sc.jj 2004-07-30 11:43:27.000000000 +0200
+++ ld/scripttempl/elf.sc 2004-09-20 12:35:09.298284462 +0200
@@ -90,9 +90,9 @@ if test -n "${COMMONPAGESIZE}"; then
DATA_SEGMENT_ALIGN="ALIGN (${SEGMENT_SIZE}) - ((${MAXPAGESIZE} - .) & (${MAXPAGESIZE} - 1)); . = DATA_SEGMENT_ALIGN (${MAXPAGESIZE}, ${COMMONPAGESIZE})"
DATA_SEGMENT_END=". = DATA_SEGMENT_END (.);"
if test -n "${SEPARATE_GOTPLT}"; then
- DATA_SEGMENT_RELRO_GOTPLT_END=". = DATA_SEGMENT_RELRO_END (. + ${SEPARATE_GOTPLT});"
+ DATA_SEGMENT_RELRO_GOTPLT_END=". = DATA_SEGMENT_RELRO_END (${SEPARATE_GOTPLT}, .);"
else
- DATA_SEGMENT_RELRO_END=". = DATA_SEGMENT_RELRO_END (.);"
+ DATA_SEGMENT_RELRO_END=". = DATA_SEGMENT_RELRO_END (0, .);"
fi
fi
INTERP=".interp ${RELOCATING-0} : { *(.interp) }"
@@ -102,7 +102,8 @@ if test -z "$GOT"; then
GOT=".got ${RELOCATING-0} : { *(.got.plt) *(.got) }"
else
GOT=".got ${RELOCATING-0} : { *(.got) }"
- GOTPLT=".got.plt ${RELOCATING-0} : { ${RELOCATING+${DATA_SEGMENT_RELRO_GOTPLT_END}} *(.got.plt) }"
+ GOTPLT="${RELOCATING+${DATA_SEGMENT_RELRO_GOTPLT_END}}
+ .got.plt ${RELOCATING-0} : { *(.got.plt) }"
fi
fi
DYNAMIC=".dynamic ${RELOCATING-0} : { *(.dynamic) }"
--- ld/ldlang.c.jj 2004-09-19 16:28:08.000000000 +0200
+++ ld/ldlang.c 2004-09-20 12:20:23.314497360 +0200
@@ -3286,10 +3286,44 @@ lang_size_sections
{
/* If DATA_SEGMENT_ALIGN DATA_SEGMENT_RELRO_END pair was seen, try
to put exp_data_seg.relro on a (common) page boundary. */
+ bfd_vma old_base, relro_end;
exp_data_seg.phase = exp_dataseg_relro_adjust;
+ old_base = exp_data_seg.base;
+ exp_data_seg.base += (-exp_data_seg.relro_end
+ & (exp_data_seg.pagesize - 1));
+ /* Compute the expected PT_GNU_RELRO segment end. */
+ relro_end = (exp_data_seg.relro_end + exp_data_seg.pagesize - 1)
+ & (exp_data_seg.pagesize - 1);
result = lang_size_sections_1 (s, output_section_statement, prev, fill,
dot, relax, check_regions);
+ if (exp_data_seg.relro_end > relro_end)
+ {
+ /* The alignment of sections between DATA_SEGMENT_ALIGN
+ and DATA_SEGMENT_RELRO_END caused huge padding to be
+ inserted at DATA_SEGMENT_RELRO_END. Try some other base. */
+ asection *sec;
+ unsigned int max_alignment_power = 0;
+
+ /* Find maximum alignment power of sections between
+ DATA_SEGMENT_ALIGN and DATA_SEGMENT_RELRO_END. */
+ for (sec = output_bfd->sections; sec; sec = sec->next)
+ if (sec->vma >= exp_data_seg.base
+ && sec->vma < exp_data_seg.relro_end
+ && sec->alignment_power > max_alignment_power)
+ max_alignment_power = sec->alignment_power;
+
+ if (((bfd_vma) 1 << max_alignment_power) < exp_data_seg.pagesize)
+ {
+ if (exp_data_seg.base - (1 << max_alignment_power)
+ < old_base)
+ exp_data_seg.base += exp_data_seg.pagesize;
+ exp_data_seg.base -= (1 << max_alignment_power);
+ result = lang_size_sections_1 (s, output_section_statement,
+ prev, fill, dot, relax,
+ check_regions);
+ }
+ }
link_info.relro_start = exp_data_seg.base;
link_info.relro_end = exp_data_seg.relro_end;
}
--- ld/ldexp.c.jj 2004-09-19 17:46:14.000000000 +0200
+++ ld/ldexp.c 2004-09-20 11:44:32.931321442 +0200
@@ -265,25 +265,6 @@ fold_unary (etree_type *tree,
result.valid_p = FALSE;
break;
- case DATA_SEGMENT_RELRO_END:
- if (allocation_done != lang_first_phase_enum
- && (exp_data_seg.phase == exp_dataseg_align_seen
- || exp_data_seg.phase == exp_dataseg_adjust
- || exp_data_seg.phase == exp_dataseg_relro_adjust
- || allocation_done != lang_allocating_phase_enum))
- {
- if (exp_data_seg.phase == exp_dataseg_align_seen
- || exp_data_seg.phase == exp_dataseg_relro_adjust)
- exp_data_seg.relro_end
- = result.value + current_section->bfd_section->vma;
- if (exp_data_seg.phase == exp_dataseg_align_seen)
- exp_data_seg.phase = exp_dataseg_relro_seen;
- result.value = dot - current_section->bfd_section->vma;
- }
- else
- result.valid_p = FALSE;
- break;
-
case DATA_SEGMENT_END:
if (allocation_done != lang_first_phase_enum
&& current_section == abs_output_section
@@ -422,13 +403,7 @@ fold_binary (etree_type *tree,
result.value = align_n (dot, maxpage);
if (exp_data_seg.phase == exp_dataseg_relro_adjust)
- {
- /* Attempt to align DATA_SEGMENT_RELRO_END at
- a common page boundary. */
- exp_data_seg.base += (-exp_data_seg.relro_end
- & (other.value - 1));
- result.value = exp_data_seg.base;
- }
+ result.value = exp_data_seg.base;
else if (exp_data_seg.phase != exp_dataseg_adjust)
{
result.value += dot & (maxpage - 1);
@@ -448,6 +423,32 @@ fold_binary (etree_type *tree,
result.valid_p = FALSE;
break;
+ case DATA_SEGMENT_RELRO_END:
+ if (allocation_done != lang_first_phase_enum
+ && (exp_data_seg.phase == exp_dataseg_align_seen
+ || exp_data_seg.phase == exp_dataseg_adjust
+ || exp_data_seg.phase == exp_dataseg_relro_adjust
+ || allocation_done != lang_allocating_phase_enum))
+ {
+ if (exp_data_seg.phase == exp_dataseg_align_seen
+ || exp_data_seg.phase == exp_dataseg_relro_adjust)
+ exp_data_seg.relro_end
+ = result.value + other.value;
+ if (exp_data_seg.phase == exp_dataseg_relro_adjust
+ && (exp_data_seg.relro_end
+ & (exp_data_seg.pagesize - 1)))
+ {
+ exp_data_seg.relro_end += exp_data_seg.pagesize - 1;
+ exp_data_seg.relro_end &= ~(exp_data_seg.pagesize - 1);
+ result.value = exp_data_seg.relro_end - other.value;
+ }
+ if (exp_data_seg.phase == exp_dataseg_align_seen)
+ exp_data_seg.phase = exp_dataseg_relro_seen;
+ }
+ else
+ result.valid_p = FALSE;
+ break;
+
default:
FAIL ();
}
Jakub