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]

[RFC PATCH] Smarter aligning of data segment


Hi!

As pointed out by Ulrich, current data segment alignment in the linker
script
  /* Adjust the address for the data segment.  We want to adjust up to
     the same address within the page on the next page up.  */
  . = ALIGN(0x1000) + (. & (0x1000 - 1));
is ideal for on-disk file size, but in certain cases less than ideal for
memory usage.
E.g. consider a small shared library which has:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .hash             HASH            000000b4 0000b4 0000a4 04   A  2   0  4
  [ 2] .dynsym           DYNSYM          00000158 000158 000160 10   A  3   b  4
  [ 3] .dynstr           STRTAB          000002b8 0002b8 00004b 00   A  0   0  1
  [ 4] .text             PROGBITS        00000304 000304 000000 00  AX  0   0  4
  [ 5] .data             PROGBITS        00001304 000304 000000 00  WA  0   0  4
  [ 6] .tdata            PROGBITS        00001304 000304 00000c 00 WAT  0   0  1
  [ 7] .tbss             PROGBITS        00001340 000340 000000 00 WAT  0   0 64
  [ 8] .dynamic          DYNAMIC         00001340 000340 000058 08  WA  3   0  4
  [ 9] .got              PROGBITS        00001398 000398 00000c 04  WA  0   0  4
  [10] .bss              NOBITS          000013b0 0003b0 002f00 00  WA  0   0 16
  [11] .shstrtab         STRTAB          00000000 0003e0 00005d 00      0   0  1
  [12] .symtab           SYMTAB          00000000 000670 000190 10     13   e  4
  [13] .strtab           STRTAB          00000000 000800 00004b 00      0   0  1

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x00000000 0x00000000 0x00304 0x00304 R E 0x1000
  LOAD           0x000304 0x00001304 0x00001304 0x000a0 0x02fac RW  0x1000
  DYNAMIC        0x000340 0x00001340 0x00001340 0x00058 0x00058 RW  0x4
  TLS            0x000304 0x00001304 0x00001304 0x0003c 0x3004d R   0x40

on IA-32. This has 2123 bytes on disk, but when ld.so maps this in it takes
one R 4KB page and 4 RW 4KB pages.
The following patch changes this to:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .hash             HASH            000000b4 0000b4 0000a4 04   A  2   0  4
  [ 2] .dynsym           DYNSYM          00000158 000158 000160 10   A  3   b  4
  [ 3] .dynstr           STRTAB          000002b8 0002b8 00004b 00   A  0   0  1
  [ 4] .text             PROGBITS        00000304 000304 000000 00  AX  0   0  4
  [ 5] .data             PROGBITS        00001000 001000 000000 00  WA  0   0  4
  [ 6] .tdata            PROGBITS        00001000 001000 00000c 00 WAT  0   0  1
  [ 7] .tbss             PROGBITS        00001040 001040 000000 00 WAT  0   0 64
  [ 8] .dynamic          DYNAMIC         00001040 001040 000058 08  WA  3   0  4
  [ 9] .got              PROGBITS        00001098 001098 00000c 04  WA  0   0  4
  [10] .bss              NOBITS          000010b0 0010b0 002f00 00  WA  0   0 16
  [11] .shstrtab         STRTAB          00000000 0010e4 00005d 00      0   0  1
  [12] .symtab           SYMTAB          00000000 001374 000190 10     13   e  4
  [13] .strtab           STRTAB          00000000 001504 00004b 00      0   0  1

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x00000000 0x00000000 0x00304 0x00304 R E 0x1000
  LOAD           0x001000 0x00001000 0x00001000 0x000a4 0x02fb0 RW  0x1000
  DYNAMIC        0x001040 0x00001040 0x00001040 0x00058 0x00058 RW  0x4
  TLS            0x001000 0x00001000 0x00001000 0x00040 0x30051 R   0x40

which is 5455 bytes long library which will occupy one less page of run-time memory
(one R 4KB page and 3 RW 4KB pages).
The patch introduces
  . = DATA_SEGMENT_ALIGN (0x1000, 0x1000);
instead of the above alignment expression, which either acts like the above,
or, if it finds out it could save one page of virtual memory vs. enlarging the
library/binary by up to one page bytes, it just aligns up to page size.
It takes 2 arguments, so that it is possible to use different value for
max page size and common page size.
E.g. sparc64 has ABI mandated max page size 0x100000 with which it is obviously
not worth using DATA_SEGMENT_ALIGN because lots of disk space would be wasted,
but all systems use 8KB pages for which it is useful to optimize (wasting up to 8KB
of disk space per library is bearable, if the gain is to save 8KB of runtime memory
per any running process linking against that library).
So, sparc64 could use . = DATA_SEGMENT_ALIGN (0x100000, 0x2000);.
What do you think?

2002-02-08  Jakub Jelinek  <jakub@redhat.com>

	* ldlex.l (DATA_SEGMENT_ALIGN, DATA_SEGMENT_END): New tokens.
	* ldgram.y (DATA_SEGMENT_ALIGN, DATA_SEGMENT_END): New tokens.
	(exp): Add DATA_SEGMENT_ALIGN (exp, exp) and DATA_SEGMENT_END (exp).
	* ldexp.c (exp_data_seg): New variable.
	(exp_print_token): Handle DATA_SEGMENT_ALIGN and DATA_SEGMENT_END.
	(fold_binary): Handle DATA_SEGMENT_ALIGN.
	(exp_fold_tree): Handle DATA_SEGMENT_END.
	Pass allocation_done when recursing instead of hardcoding
	lang_allocating_phase_enum.
	* ldexp.h (exp_data_seg): New.
	* ldlang.c (lang_size_sections_1): Renamed from lang_size_sections.
	(lang_size_sections): New.
	* ld.texinfo (DATA_SEGMENT_ALIGN, DATA_SEGMENT_END): Document.
	* scripttempl/elf.sc: If COMMONPAGESIZE is defined or if MAXPAGESIZE
	is 4KB, use DATA_SEGMENT_ALIGN and DATA_SEGMENT_END.

--- ld/ldlex.l.jj	Mon Jan 28 14:46:22 2002
+++ ld/ldlex.l	Fri Feb  8 13:18:18 2002
@@ -239,6 +239,8 @@ V_IDENTIFIER [*?.$_a-zA-Z]([*?.$_a-zA-Z0
 <EXPRESSION,BOTH,SCRIPT>"BIND"		{ RTOKEN(BIND);}
 <BOTH,SCRIPT>"LENGTH"		{ RTOKEN(LENGTH);}
 <EXPRESSION,BOTH,SCRIPT>"ALIGN"			{ RTOKEN(ALIGN_K);}
+<EXPRESSION,BOTH,SCRIPT>"DATA_SEGMENT_ALIGN"	{ RTOKEN(DATA_SEGMENT_ALIGN);}
+<EXPRESSION,BOTH,SCRIPT>"DATA_SEGMENT_END"	{ RTOKEN(DATA_SEGMENT_END);}
 <EXPRESSION,BOTH,SCRIPT>"ADDR"			{ RTOKEN(ADDR);}
 <EXPRESSION,BOTH,SCRIPT>"LOADADDR"		{ RTOKEN(LOADADDR);}
 <EXPRESSION,BOTH>"MAX"			{ RTOKEN(MAX_K); }
--- ld/ldgram.y.jj	Tue Dec 18 14:30:32 2001
+++ ld/ldgram.y	Fri Feb  8 13:21:23 2002
@@ -122,7 +122,7 @@ static int error_index;
 %token END 
 %left <token> '('
 %token <token> ALIGN_K BLOCK BIND QUAD SQUAD LONG SHORT BYTE
-%token SECTIONS PHDRS SORT
+%token SECTIONS PHDRS SORT DATA_SEGMENT_ALIGN DATA_SEGMENT_END
 %token '{' '}'
 %token SIZEOF_HEADERS OUTPUT_FORMAT FORCE_COMMON_ALLOCATION OUTPUT_ARCH
 %token INHIBIT_COMMON_ALLOCATION
@@ -795,6 +795,10 @@ exp	:
 			{ $$ = exp_unop(ABSOLUTE, $3); }
 	|	ALIGN_K '(' exp ')'
 			{ $$ = exp_unop(ALIGN_K,$3); }
+	|	DATA_SEGMENT_ALIGN '(' exp ',' exp ')'
+			{ $$ = exp_binop (DATA_SEGMENT_ALIGN, $3, $5); }
+	|	DATA_SEGMENT_END '(' exp ')'
+			{ $$ = exp_unop(DATA_SEGMENT_END, $3); }
 	|	BLOCK '(' exp ')'
 			{ $$ = exp_unop(ALIGN_K,$3); }
 	|	NAME
--- ld/ldexp.c.jj	Wed Dec  5 14:45:44 2001
+++ ld/ldexp.c	Fri Feb  8 16:11:11 2002
@@ -64,6 +64,8 @@ static etree_value_type exp_fold_tree_no
 	   lang_output_section_statement_type *current_section,
 	   lang_phase_type allocation_done));
 
+struct exp_data_seg exp_data_seg;
+
 static void
 exp_print_token (code)
      token_code_type code;
@@ -114,6 +116,8 @@ exp_print_token (code)
     { LOADADDR, "LOADADDR" },
     { MAX_K, "MAX_K" },
     { REL, "relocateable" },
+    { DATA_SEGMENT_ALIGN, "DATA_SEGMENT_ALIGN" },
+    { DATA_SEGMENT_END, "DATA_SEGMENT_END" }
   };
   unsigned int idx;
 
@@ -314,6 +318,33 @@ fold_binary (tree, current_section, allo
 		result = other;
 	      break;
 
+	    case DATA_SEGMENT_ALIGN:
+	      if (allocation_done != lang_first_phase_enum
+		  && current_section == abs_output_section
+		  && (exp_data_seg.phase == exp_dataseg_none
+		      || exp_data_seg.phase == exp_dataseg_adjust
+		      || allocation_done != lang_allocating_phase_enum))
+		{
+		  bfd_vma maxpage = result.value;
+
+		  result.value = ALIGN_N (dot, maxpage);
+		  if (exp_data_seg.phase != exp_dataseg_adjust)
+		    {
+		      result.value += dot & (maxpage - 1);
+		      if (allocation_done == lang_allocating_phase_enum)
+			{
+			  exp_data_seg.phase = exp_dataseg_align_seen;
+			  exp_data_seg.base = result.value;
+			  exp_data_seg.pagesize = other.value;
+			}
+		    }
+		  else if (other.value < maxpage)
+		    result.value += dot & (maxpage - other.value);
+		}
+	      else
+		result.valid_p = false;
+	      break;
+
 	    default:
 	      FAIL ();
 	    }
@@ -578,6 +609,23 @@ exp_fold_tree (tree, current_section, al
 		result.valid_p = false;
 	      break;
 
+	    case DATA_SEGMENT_END:
+	      if (allocation_done != lang_first_phase_enum
+		  && current_section == abs_output_section
+		  && (exp_data_seg.phase == exp_dataseg_align_seen
+		      || exp_data_seg.phase == exp_dataseg_adjust
+		      || allocation_done != lang_allocating_phase_enum))
+		{
+		  if (exp_data_seg.phase == exp_dataseg_align_seen)
+		    {
+		      exp_data_seg.phase = exp_dataseg_end_seen;
+		      exp_data_seg.end = result.value;
+		    }
+		}
+	      else
+		result.valid_p = false;
+	      break;
+
 	    default:
 	      FAIL ();
 	      break;
@@ -615,7 +663,7 @@ exp_fold_tree (tree, current_section, al
 	    {
 	      result = exp_fold_tree (tree->assign.src,
 				      current_section,
-				      lang_allocating_phase_enum, dot,
+				      allocation_done, dot,
 				      dotp);
 	      if (! result.valid_p)
 		einfo (_("%F%S invalid assignment to location counter\n"));
--- ld/ldexp.h.jj	Tue Mar 13 07:14:27 2001
+++ ld/ldexp.h	Fri Feb  8 14:24:40 2002
@@ -88,6 +88,16 @@ typedef union etree_union {
   } assert_s;
 } etree_type;
 
+extern struct exp_data_seg {
+  enum {
+    exp_dataseg_none,
+    exp_dataseg_align_seen,
+    exp_dataseg_end_seen,
+    exp_dataseg_adjust
+  } phase;
+  bfd_vma base, end, pagesize;
+} exp_data_seg;
+
 etree_type *exp_intop PARAMS ((bfd_vma));
 etree_type *exp_relop PARAMS ((asection *, bfd_vma));
 etree_value_type invalid PARAMS ((void));
--- ld/ldlang.c.jj	Thu Feb  7 01:29:20 2002
+++ ld/ldlang.c	Fri Feb  8 15:25:04 2002
@@ -151,6 +151,9 @@ static void lang_check_section_addresses
 static void os_region_check
   PARAMS ((lang_output_section_statement_type *,
 	   struct memory_region_struct *, etree_type *, bfd_vma));
+static bfd_vma lang_size_sections_1
+  PARAMS ((lang_statement_union_type *, lang_output_section_statement_type *,
+	   lang_statement_union_type **, fill_type, bfd_vma, boolean *));
 
 typedef void (*callback_t) PARAMS ((lang_wild_statement_type *,
 				    struct wildcard_list *,
@@ -2829,8 +2832,8 @@ os_region_check (os, region, tree, base)
 
 /* Set the sizes for all the output sections.  */
 
-bfd_vma
-lang_size_sections (s, output_section_statement, prev, fill, dot, relax)
+static bfd_vma
+lang_size_sections_1 (s, output_section_statement, prev, fill, dot, relax)
      lang_statement_union_type *s;
      lang_output_section_statement_type *output_section_statement;
      lang_statement_union_type **prev;
@@ -2955,8 +2958,8 @@ lang_size_sections (s, output_section_st
 		os->bfd_section->output_offset = 0;
 	      }
 
-	    lang_size_sections (os->children.head, os, &os->children.head,
-				os->fill, dot, relax);
+	    lang_size_sections_1 (os->children.head, os, &os->children.head,
+				  os->fill, dot, relax);
 
 	    /* Put the section within the requested block size, or
 	       align at the block boundary.  */
@@ -3027,10 +3030,10 @@ lang_size_sections (s, output_section_st
 	  break;
 
 	case lang_constructors_statement_enum:
-	  dot = lang_size_sections (constructor_list.head,
-				    output_section_statement,
-				    &s->wild_statement.children.head,
-				    fill, dot, relax);
+	  dot = lang_size_sections_1 (constructor_list.head,
+				      output_section_statement,
+				      &s->wild_statement.children.head,
+				      fill, dot, relax);
 	  break;
 
 	case lang_data_statement_enum:
@@ -3091,10 +3094,10 @@ lang_size_sections (s, output_section_st
 
 	case lang_wild_statement_enum:
 
-	  dot = lang_size_sections (s->wild_statement.children.head,
-				    output_section_statement,
-				    &s->wild_statement.children.head,
-				    fill, dot, relax);
+	  dot = lang_size_sections_1 (s->wild_statement.children.head,
+				      output_section_statement,
+				      &s->wild_statement.children.head,
+				      fill, dot, relax);
 
 	  break;
 
@@ -3189,10 +3192,10 @@ lang_size_sections (s, output_section_st
 	  break;
 
 	case lang_group_statement_enum:
-	  dot = lang_size_sections (s->group_statement.children.head,
-				    output_section_statement,
-				    &s->group_statement.children.head,
-				    fill, dot, relax);
+	  dot = lang_size_sections_1 (s->group_statement.children.head,
+				      output_section_statement,
+				      &s->group_statement.children.head,
+				      fill, dot, relax);
 	  break;
 
 	default:
@@ -3209,6 +3212,42 @@ lang_size_sections (s, output_section_st
 }
 
 bfd_vma
+lang_size_sections (s, output_section_statement, prev, fill, dot, relax)
+     lang_statement_union_type *s;
+     lang_output_section_statement_type *output_section_statement;
+     lang_statement_union_type **prev;
+     fill_type fill;
+     bfd_vma dot;
+     boolean *relax;
+{
+  bfd_vma result;
+
+  exp_data_seg.phase = exp_dataseg_none;
+  result = lang_size_sections_1 (s, output_section_statement, prev, fill,
+				 dot, relax);
+  if (exp_data_seg.phase == exp_dataseg_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;
+
+      first = -exp_data_seg.base & (exp_data_seg.pagesize - 1);
+      last = exp_data_seg.end & (exp_data_seg.pagesize - 1);
+      if (first && last
+	  && ((exp_data_seg.base & ~(exp_data_seg.pagesize - 1))
+	      != (exp_data_seg.end & ~(exp_data_seg.pagesize - 1)))
+	  && first + last <= exp_data_seg.pagesize)
+	{
+	  exp_data_seg.phase = exp_dataseg_adjust;
+	  result = lang_size_sections_1 (s, output_section_statement, prev,
+					 fill, dot, relax);
+	}
+    }
+
+  return result;
+}
+
+bfd_vma
 lang_do_assignments (s, output_section_statement, fill, dot)
      lang_statement_union_type *s;
      lang_output_section_statement_type *output_section_statement;
--- ld/ld.texinfo.jj	Mon Jan 14 17:05:21 2002
+++ ld/ld.texinfo	Fri Feb  8 17:06:35 2002
@@ -4153,6 +4153,45 @@ This is a synonym for @code{ALIGN}, for 
 scripts.  It is most often seen when setting the address of an output
 section.
 
+@item DATA_SEGMENT_ALIGN(@var{maxpagesize}, @var{commonpagesize})
+@kindex DATA_SEGMENT_ALIGN(@var{maxpagesize}, @var{commonpagesize})
+This is equivalent to either
+@smallexample
+(ALIGN(@var{maxpagesize}) + (. & (@var{maxpagesize} - 1)))
+@end smallexample
+or
+@smallexample
+(ALIGN(@var{maxpagesize}) + (. & (@var{maxpagesize} - @var{commonpagesize})))
+@end smallexample
+@noindent
+depending on whether the latter uses fewer @var{commonpagesize} sized pages
+for the data segment (area between the result of this expression and
+@code{DATA_SEGMENT_END}) than the former or not.
+If the latter form is used, it means @var{commonpagesize} bytes of runtime
+memory will be saved at the expense of up to @var{commonpagesize} wasted
+bytes in the on-disk file.
+
+This expression can only be used directly in @code{SECTIONS} commands, not in
+any output section descriptions and only once in the linker script.
+@var{commonpagesize} should be less or equal to @var{maxpagesize} and should
+be the system page size the object wants to be optimized for (while still
+working on system page sizes up to @var{maxpagesize}).
+
+@noindent
+Example:
+@smallexample
+  . = DATA_SEGMENT_ALIGN(0x10000, 0x2000);
+@end smallexample
+
+@item DATA_SEGMENT_END(@var{exp})
+@kindex DATA_SEGMENT_END(@var{exp})
+This defines the end of data segment for @code{DATA_SEGMENT_ALIGN}
+evaluation purposes.
+
+@smallexample
+  . = DATA_SEGMENT_END(.);
+@end smallexample
+
 @item DEFINED(@var{symbol})
 @kindex DEFINED(@var{symbol})
 @cindex symbol defaults
--- ld/scripttempl/elf.sc.jj	Wed Feb  6 18:51:42 2002
+++ ld/scripttempl/elf.sc	Fri Feb  8 16:36:32 2002
@@ -70,6 +70,11 @@ if [ -z "$MACHINE" ]; then OUTPUT_ARCH=$
 test -z "${ELFSIZE}" && ELFSIZE=32
 test -z "${ALIGNMENT}" && ALIGNMENT="${ELFSIZE} / 8"
 test "$LD_FLAG" = "N" && DATA_ADDR=.
+DATA_SEGMENT_ALIGN="ALIGN(${MAXPAGESIZE}) + (. & (${MAXPAGESIZE} - 1))"
+test -z "${COMMONPAGESIZE}" && test "${MAXPAGESIZE}" = 0x1000 && COMMONPAGESIZE=0x1000
+if [ -n "${COMMONPAGESIZE}" ]; then
+  DATA_SEGMENT_ALIGN="DATA_SEGMENT_ALIGN(${MAXPAGESIZE}, ${COMMONPAGESIZE})"
+fi
 INTERP=".interp       ${RELOCATING-0} : { *(.interp) }"
 PLT=".plt          ${RELOCATING-0} : { *(.plt) }"
 DYNAMIC=".dynamic      ${RELOCATING-0} : { *(.dynamic) }"
@@ -271,8 +276,8 @@ cat <<EOF
 
   /* Adjust the address for the data segment.  We want to adjust up to
      the same address within the page on the next page up.  */
-  ${CREATE_SHLIB-${RELOCATING+. = ${DATA_ADDR-ALIGN(${MAXPAGESIZE}) + (. & (${MAXPAGESIZE} - 1))};}}
-  ${CREATE_SHLIB+${RELOCATING+. = ${SHLIB_DATA_ADDR-ALIGN(${MAXPAGESIZE}) + (. & (${MAXPAGESIZE} - 1))};}}
+  ${CREATE_SHLIB-${RELOCATING+. = ${DATA_ADDR-${DATA_SEGMENT_ALIGN}};}}
+  ${CREATE_SHLIB+${RELOCATING+. = ${SHLIB_DATA_ADDR-${DATA_SEGMENT_ALIGN}};}}
 
   .data         ${RELOCATING-0} :
   {
@@ -320,6 +325,7 @@ cat <<EOF
   ${RELOCATING+_end = .;}
   ${RELOCATING+${OTHER_BSS_END_SYMBOLS}}
   ${RELOCATING+PROVIDE (end = .);}
+  ${COMMONPAGESIZE+${RELOCATING+. = DATA_SEGMENT_END (.);}}
 
   /* Stabs debugging sections.  */
   .stab          0 : { *(.stab) }

	Jakub


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