This is the mail archive of the
binutils@sources.redhat.com
mailing list for the binutils project.
[RFC PATCH] Smarter aligning of data segment
- From: Jakub Jelinek <jakub at redhat dot com>
- To: binutils at sources dot redhat dot com
- Cc: drepper at redhat dot com
- Date: Fri, 8 Feb 2002 17:30:37 +0100
- Subject: [RFC PATCH] Smarter aligning of data segment
- Reply-to: Jakub Jelinek <jakub at redhat dot com>
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