This is the mail archive of the binutils@sources.redhat.com mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Fix -z relro


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]