From 2d20f7bf674ff3f0f43842677c70fc2879c5e2c7 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Tue, 12 Feb 2002 14:50:08 +0000 Subject: * 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: Use DATA_SEGMENT_ALIGN and DATA_SEGMENT_END if COMMONPAGESIZE is defined. * emulparams/elf_i386.sh (COMMONPAGESIZE): Set to 4K. * emulparams/elf32_sparc.sh (COMMONPAGESIZE): Set to 8K. * emulparams/elf64_sparc.sh (COMMONPAGESIZE): Set to 8K. * emulparams/elf64alpha.sh (COMMONPAGESIZE): Set to 8K. * emulparams/elf64_ia64.sh (COMMONPAGESIZE): Set to 16K for shared libraries only. --- ld/ChangeLog | 24 +++++++++++++++ ld/emulparams/elf32_sparc.sh | 1 + ld/emulparams/elf64_ia64.sh | 4 +++ ld/emulparams/elf64_sparc.sh | 1 + ld/emulparams/elf64alpha.sh | 1 + ld/emulparams/elf_i386.sh | 1 + ld/ld.texinfo | 39 ++++++++++++++++++++++++ ld/ldexp.c | 50 ++++++++++++++++++++++++++++++- ld/ldexp.h | 10 +++++++ ld/ldgram.y | 6 +++- ld/ldlang.c | 71 ++++++++++++++++++++++++++++++++++---------- ld/ldlex.l | 2 ++ ld/scripttempl/elf.sc | 9 ++++-- 13 files changed, 199 insertions(+), 20 deletions(-) diff --git a/ld/ChangeLog b/ld/ChangeLog index b4dfe65..261618c 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,27 @@ +2002-02-12 Jakub Jelinek + + * 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: Use DATA_SEGMENT_ALIGN and DATA_SEGMENT_END + if COMMONPAGESIZE is defined. + * emulparams/elf_i386.sh (COMMONPAGESIZE): Set to 4K. + * emulparams/elf32_sparc.sh (COMMONPAGESIZE): Set to 8K. + * emulparams/elf64_sparc.sh (COMMONPAGESIZE): Set to 8K. + * emulparams/elf64alpha.sh (COMMONPAGESIZE): Set to 8K. + * emulparams/elf64_ia64.sh (COMMONPAGESIZE): Set to 16K for shared + libraries only. + 2002-02-11 Alan Modra * Makefile.in: Regenerate. diff --git a/ld/emulparams/elf32_sparc.sh b/ld/emulparams/elf32_sparc.sh index d8b81e7..15a837d 100644 --- a/ld/emulparams/elf32_sparc.sh +++ b/ld/emulparams/elf32_sparc.sh @@ -2,6 +2,7 @@ SCRIPT_NAME=elf OUTPUT_FORMAT="elf32-sparc" TEXT_START_ADDR=0x10000 MAXPAGESIZE=0x10000 +COMMONPAGESIZE=0x2000 NONPAGED_TEXT_START_ADDR=0x10000 ALIGNMENT=8 ARCH=sparc diff --git a/ld/emulparams/elf64_ia64.sh b/ld/emulparams/elf64_ia64.sh index 0699d3d..4ff0ca9 100644 --- a/ld/emulparams/elf64_ia64.sh +++ b/ld/emulparams/elf64_ia64.sh @@ -7,6 +7,10 @@ OUTPUT_FORMAT="elf64-ia64-little" ARCH=ia64 MACHINE= MAXPAGESIZE=0x10000 +if test -n "$CREATE_SHLIB"; then + # Optimize shared libraries for 16K page size + COMMONPAGESIZE=0x4000 +fi TEXT_START_ADDR="0x4000000000000000" DATA_ADDR="0x6000000000000000 + (. & (${MAXPAGESIZE} - 1))" GENERATE_SHLIB_SCRIPT=yes diff --git a/ld/emulparams/elf64_sparc.sh b/ld/emulparams/elf64_sparc.sh index dae3f21..a4706b5 100644 --- a/ld/emulparams/elf64_sparc.sh +++ b/ld/emulparams/elf64_sparc.sh @@ -3,6 +3,7 @@ ELFSIZE=64 TEMPLATE_NAME=elf32 OUTPUT_FORMAT="elf64-sparc" MAXPAGESIZE=0x100000 +COMMONPAGESIZE=0x2000 ARCH="sparc:v9" MACHINE= DATA_PLT= diff --git a/ld/emulparams/elf64alpha.sh b/ld/emulparams/elf64alpha.sh index 39247ea..d0ca139 100644 --- a/ld/emulparams/elf64alpha.sh +++ b/ld/emulparams/elf64alpha.sh @@ -5,6 +5,7 @@ TEMPLATE_NAME=elf32 OUTPUT_FORMAT="elf64-alpha" TEXT_START_ADDR="0x120000000" MAXPAGESIZE=0x10000 +COMMONPAGESIZE=0x2000 NONPAGED_TEXT_START_ADDR="0x120000000" ARCH=alpha MACHINE= diff --git a/ld/emulparams/elf_i386.sh b/ld/emulparams/elf_i386.sh index 53dd54b..f1b8522 100644 --- a/ld/emulparams/elf_i386.sh +++ b/ld/emulparams/elf_i386.sh @@ -2,6 +2,7 @@ SCRIPT_NAME=elf OUTPUT_FORMAT="elf32-i386" TEXT_START_ADDR=0x08048000 MAXPAGESIZE=0x1000 +COMMONPAGESIZE=0x1000 NONPAGED_TEXT_START_ADDR=0x08048000 ARCH=i386 MACHINE= diff --git a/ld/ld.texinfo b/ld/ld.texinfo index 78afbbe..b7c289a 100644 --- a/ld/ld.texinfo +++ b/ld/ld.texinfo @@ -4159,6 +4159,45 @@ This is a synonym for @code{ALIGN}, for compatibility with older linker 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 diff --git a/ld/ldexp.c b/ld/ldexp.c index ec449fd..4023639 100644 --- a/ld/ldexp.c +++ b/ld/ldexp.c @@ -64,6 +64,8 @@ static etree_value_type exp_fold_tree_no_dot 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, allocation_done, dot, dotp) 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, allocation_done, dot, dotp) 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, allocation_done, dot, dotp) { 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")); diff --git a/ld/ldexp.h b/ld/ldexp.h index 36f88f8..fe152d8 100644 --- a/ld/ldexp.h +++ b/ld/ldexp.h @@ -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)); diff --git a/ld/ldgram.y b/ld/ldgram.y index f1924a0..ad26902 100644 --- a/ld/ldgram.y +++ b/ld/ldgram.y @@ -122,7 +122,7 @@ static int error_index; %token END %left '(' %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 diff --git a/ld/ldlang.c b/ld/ldlang.c index bc705ad..eab970d 100644 --- a/ld/ldlang.c +++ b/ld/ldlang.c @@ -150,6 +150,9 @@ static void lang_check_section_addresses PARAMS ((void)); 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 *, @@ -2823,8 +2826,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; @@ -2949,8 +2952,8 @@ lang_size_sections (s, output_section_statement, prev, fill, dot, relax) 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. */ @@ -3018,10 +3021,10 @@ lang_size_sections (s, output_section_statement, prev, fill, dot, relax) 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: @@ -3082,10 +3085,10 @@ lang_size_sections (s, output_section_statement, prev, fill, dot, relax) 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; @@ -3180,10 +3183,10 @@ lang_size_sections (s, output_section_statement, prev, fill, dot, relax) 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: @@ -3200,6 +3203,42 @@ lang_size_sections (s, output_section_statement, prev, fill, dot, relax) } 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; diff --git a/ld/ldlex.l b/ld/ldlex.l index 0b15ca2..1220852 100644 --- a/ld/ldlex.l +++ b/ld/ldlex.l @@ -239,6 +239,8 @@ V_IDENTIFIER [*?.$_a-zA-Z]([*?.$_a-zA-Z0-9]|::)* "BIND" { RTOKEN(BIND);} "LENGTH" { RTOKEN(LENGTH);} "ALIGN" { RTOKEN(ALIGN_K);} +"DATA_SEGMENT_ALIGN" { RTOKEN(DATA_SEGMENT_ALIGN);} +"DATA_SEGMENT_END" { RTOKEN(DATA_SEGMENT_END);} "ADDR" { RTOKEN(ADDR);} "LOADADDR" { RTOKEN(LOADADDR);} "MAX" { RTOKEN(MAX_K); } diff --git a/ld/scripttempl/elf.sc b/ld/scripttempl/elf.sc index fe8e242..be6fc09 100644 --- a/ld/scripttempl/elf.sc +++ b/ld/scripttempl/elf.sc @@ -70,6 +70,10 @@ if [ -z "$MACHINE" ]; then OUTPUT_ARCH=${ARCH}; else OUTPUT_ARCH=${ARCH}:${MACHI test -z "${ELFSIZE}" && ELFSIZE=32 test -z "${ALIGNMENT}" && ALIGNMENT="${ELFSIZE} / 8" test "$LD_FLAG" = "N" && DATA_ADDR=. +DATA_SEGMENT_ALIGN="ALIGN(${MAXPAGESIZE}) + (. & (${MAXPAGESIZE} - 1))" +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) }" @@ -269,8 +273,8 @@ cat <