diff options
-rw-r--r-- | ld/ChangeLog | 26 | ||||
-rw-r--r-- | ld/ld.texinfo | 38 | ||||
-rw-r--r-- | ld/ldgram.y | 12 | ||||
-rw-r--r-- | ld/ldlang.c | 307 | ||||
-rw-r--r-- | ld/ldlang.h | 11 | ||||
-rw-r--r-- | ld/ldlex.l | 3 | ||||
-rw-r--r-- | ld/ldmain.c | 4 | ||||
-rw-r--r-- | ld/ldmain.h | 3 | ||||
-rw-r--r-- | ld/lexsup.c | 2 | ||||
-rw-r--r-- | ld/testsuite/ChangeLog | 8 | ||||
-rw-r--r-- | ld/testsuite/ld-spu/ovl.d | 2 | ||||
-rw-r--r-- | ld/testsuite/ld-spu/ovl.lnk | 7 | ||||
-rw-r--r-- | ld/testsuite/ld-spu/ovl1.lnk | 9 | ||||
-rw-r--r-- | ld/testsuite/ld-spu/ovl2.d | 2 | ||||
-rw-r--r-- | ld/testsuite/ld-spu/ovl2.lnk | 10 |
15 files changed, 363 insertions, 81 deletions
diff --git a/ld/ChangeLog b/ld/ChangeLog index c8e6b5b..ec540ad 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,29 @@ +2008-01-25 Alan Modra <amodra@bigpond.net.au> + + * ld.texinfo (INSERT): Describe. + * ldgram.y (ldgram_in_script, ldgram_had_equals): Delete. + (INSERT_K, AFTER, BEFORE): Add as tokens. + (ifile_p1): Handle INSERT statements. + (saved_script_handle, force_make_executable): Move to.. + * ldmain.c: ..here. + (previous_script_handle): New global var. + * ldmain.h (saved_script_handle, force_make_executable): Declare. + (previous_script_handle): Likewise. + * ldlex.l (INSERT_K, AFTER, BEFORE): Add tokens. + * lexsup.c (parge_args <-T>): Set previous_script_handle. + * ldlang.c (lang_for_each_statement_worker): Handle insert statement. + (map_input_to_output_sections, print_statement): Likewise. + (lang_size_sections_1, lang_do_assignments_1): Likewise. + (insert_os_after): New function, extracted from.. + (lang_insert_orphan): ..here. + (process_insert_statements): New function. + (lang_process): Call it. + (lang_add_insert): New function. + * ldlang.h (lang_insert_statement_enum): New. + (lang_insert_statement_type): New. + (lang_statement_union_type): Add insert_statement. + (lang_add_insert): Declare. + 2008-01-18 Bob Wilson <bob.wilson@acm.org> * scripttempl/elfxtensa.sc: Merge ENTRY and .note.gnu.build-id diff --git a/ld/ld.texinfo b/ld/ld.texinfo index 7d77d1c..00081da 100644 --- a/ld/ld.texinfo +++ b/ld/ld.texinfo @@ -297,10 +297,11 @@ augments the main linker script used for the link (either the default linker script or the one specified by using @samp{-T}). This feature permits the linker to link against a file which appears to be an object or an archive, but actually merely defines some symbol values, or uses -@code{INPUT} or @code{GROUP} to load other objects. Note that -specifying a script in this way merely augments the main linker script; -use the @samp{-T} option to replace the default linker script entirely. -@xref{Scripts}. +@code{INPUT} or @code{GROUP} to load other objects. Specifying a +script in this way merely augments the main linker script, with the +extra commands placed after the main script; use the @samp{-T} option +to replace the default linker script entirely, but note the effect of +the @code{INSERT} command. @xref{Scripts}. For options whose names are a single letter, option arguments must either follow the option letter without intervening @@ -2903,6 +2904,35 @@ This command has the same effect as the @samp{--no-define-common} command-line option: to make @code{ld} omit the assignment of addresses to common symbols even for a non-relocatable output file. +@item INSERT [ AFTER | BEFORE ] @var{output_section} +@kindex INSERT +@cindex insert user script into default script +This command is typically used in a script specified by @samp{-T} to +augment the default @code{SECTIONS} with, for example, overlays. It +inserts all prior linker script statements after (or before) +@var{output_section}, and also causes @samp{-T} to not override the +default linker script. The exact insertion point is as for orphan +sections. @xref{Location Counter}. The insertion happens after the +linker has mapped input sections to output sections. Prior to the +insertion, since @samp{-T} scripts are parsed before the default +linker script, statements in the @samp{-T} script occur before the +default linker script statements in the internal linker representation +of the script. In particular, input section assignments will be made +to @samp{-T} output sections before those in the default script. Here +is an example of how a @samp{-T} script using @code{INSERT} might look: + +@smallexample +SECTIONS +@{ + OVERLAY : + @{ + .ov1 @{ ov1*(.text) @} + .ov2 @{ ov2*(.text) @} + @} +@} +INSERT AFTER .text; +@end smallexample + @item NOCROSSREFS(@var{section} @var{section} @dots{}) @kindex NOCROSSREFS(@var{sections}) @cindex cross references diff --git a/ld/ldgram.y b/ld/ldgram.y index 5b68b6b..f481f54 100644 --- a/ld/ldgram.y +++ b/ld/ldgram.y @@ -49,11 +49,6 @@ static enum section_type sectype; static lang_memory_region_type *region; -FILE *saved_script_handle = NULL; -bfd_boolean force_make_executable = FALSE; - -bfd_boolean ldgram_in_script = FALSE; -bfd_boolean ldgram_had_equals = FALSE; bfd_boolean ldgram_had_keep = FALSE; char *ldgram_vers_current_lang = NULL; @@ -127,7 +122,8 @@ static int error_index; %token END %left <token> '(' %token <token> ALIGN_K BLOCK BIND QUAD SQUAD LONG SHORT BYTE -%token SECTIONS PHDRS DATA_SEGMENT_ALIGN DATA_SEGMENT_RELRO_END DATA_SEGMENT_END +%token SECTIONS PHDRS INSERT_K AFTER BEFORE +%token DATA_SEGMENT_ALIGN DATA_SEGMENT_RELRO_END DATA_SEGMENT_END %token SORT_BY_NAME SORT_BY_ALIGNMENT %token '{' '}' %token SIZEOF_HEADERS OUTPUT_FORMAT FORCE_COMMON_ALLOCATION OUTPUT_ARCH @@ -352,6 +348,10 @@ ifile_p1: lang_add_nocrossref ($3); } | EXTERN '(' extern_name_list ')' + | INSERT_K AFTER NAME + { lang_add_insert ($3, 0); } + | INSERT_K BEFORE NAME + { lang_add_insert ($3, 1); } ; input_list: diff --git a/ld/ldlang.c b/ld/ldlang.c index c5ad76d..de3f64b7 100644 --- a/ld/ldlang.c +++ b/ld/ldlang.c @@ -845,6 +845,7 @@ lang_for_each_statement_worker (void (*func) (lang_statement_union_type *), case lang_padding_statement_enum: case lang_address_statement_enum: case lang_fill_statement_enum: + case lang_insert_statement_enum: break; default: FAIL (); @@ -1451,6 +1452,73 @@ output_prev_sec_find (lang_output_section_statement_type *os) return NULL; } +/* Look for a suitable place for a new output section statement. The + idea is to skip over anything that might be inside a SECTIONS {} + statement in a script, before we find another output section + statement. Assignments to "dot" before an output section statement + are assumed to belong to it. An exception to this rule is made for + the first assignment to dot, otherwise we might put an orphan + before . = . + SIZEOF_HEADERS or similar assignments that set the + initial address. */ + +static lang_statement_union_type ** +insert_os_after (lang_output_section_statement_type *after) +{ + lang_statement_union_type **where; + lang_statement_union_type **assign = NULL; + bfd_boolean ignore_first; + + ignore_first + = after == &lang_output_section_statement.head->output_section_statement; + + for (where = &after->header.next; + *where != NULL; + where = &(*where)->header.next) + { + switch ((*where)->header.type) + { + case lang_assignment_statement_enum: + if (assign == NULL) + { + lang_assignment_statement_type *ass; + + ass = &(*where)->assignment_statement; + if (ass->exp->type.node_class != etree_assert + && ass->exp->assign.dst[0] == '.' + && ass->exp->assign.dst[1] == 0 + && !ignore_first) + assign = where; + } + ignore_first = FALSE; + continue; + case lang_wild_statement_enum: + case lang_input_section_enum: + case lang_object_symbols_statement_enum: + case lang_fill_statement_enum: + case lang_data_statement_enum: + case lang_reloc_statement_enum: + case lang_padding_statement_enum: + case lang_constructors_statement_enum: + assign = NULL; + continue; + case lang_output_section_statement_enum: + if (assign != NULL) + where = assign; + break; + case lang_input_statement_enum: + case lang_address_statement_enum: + case lang_target_statement_enum: + case lang_output_statement_enum: + case lang_group_statement_enum: + case lang_insert_statement_enum: + continue; + } + break; + } + + return where; +} + lang_output_section_statement_type * lang_insert_orphan (asection *s, const char *secname, @@ -1606,64 +1674,7 @@ lang_insert_orphan (asection *s, if (place->stmt == NULL) { - lang_statement_union_type **where; - lang_statement_union_type **assign = NULL; - bfd_boolean ignore_first; - - /* Look for a suitable place for the new statement list. - The idea is to skip over anything that might be inside - a SECTIONS {} statement in a script, before we find - another output_section_statement. Assignments to "dot" - before an output section statement are assumed to - belong to it. An exception to this rule is made for - the first assignment to dot, otherwise we might put an - orphan before . = . + SIZEOF_HEADERS or similar - assignments that set the initial address. */ - - ignore_first = after == (&lang_output_section_statement.head - ->output_section_statement); - for (where = &after->header.next; - *where != NULL; - where = &(*where)->header.next) - { - switch ((*where)->header.type) - { - case lang_assignment_statement_enum: - if (assign == NULL) - { - lang_assignment_statement_type *ass; - ass = &(*where)->assignment_statement; - if (ass->exp->type.node_class != etree_assert - && ass->exp->assign.dst[0] == '.' - && ass->exp->assign.dst[1] == 0 - && !ignore_first) - assign = where; - } - ignore_first = FALSE; - continue; - case lang_wild_statement_enum: - case lang_input_section_enum: - case lang_object_symbols_statement_enum: - case lang_fill_statement_enum: - case lang_data_statement_enum: - case lang_reloc_statement_enum: - case lang_padding_statement_enum: - case lang_constructors_statement_enum: - assign = NULL; - continue; - case lang_output_section_statement_enum: - if (assign != NULL) - where = assign; - break; - case lang_input_statement_enum: - case lang_address_statement_enum: - case lang_target_statement_enum: - case lang_output_statement_enum: - case lang_group_statement_enum: - continue; - } - break; - } + lang_statement_union_type **where = insert_os_after (after); *add.tail = *where; *where = add.head; @@ -3312,6 +3323,154 @@ map_input_to_output_sections aos->addr_tree = s->address_statement.address; } break; + case lang_insert_statement_enum: + break; + } + } +} + +/* An insert statement snips out all the linker statements from the + start of the list and places them after the output section + statement specified by the insert. This operation is complicated + by the fact that we keep a doubly linked list of output section + statements as well as the singly linked list of all statements. */ + +static void +process_insert_statements (void) +{ + lang_statement_union_type **s; + lang_output_section_statement_type *first_os = NULL; + lang_output_section_statement_type *last_os = NULL; + + /* "start of list" is actually the statement immediately after + the special abs_section output statement, so that it isn't + reordered. */ + s = &lang_output_section_statement.head; + while (*(s = &(*s)->header.next) != NULL) + { + if ((*s)->header.type == lang_output_section_statement_enum) + { + /* Keep pointers to the first and last output section + statement in the sequence we may be about to move. */ + last_os = &(*s)->output_section_statement; + if (first_os == NULL) + first_os = last_os; + } + else if ((*s)->header.type == lang_insert_statement_enum) + { + lang_insert_statement_type *i = &(*s)->insert_statement; + lang_output_section_statement_type *where; + lang_output_section_statement_type *os; + lang_statement_union_type **ptr; + lang_statement_union_type *first; + + where = lang_output_section_find (i->where); + if (where != NULL && i->is_before) + { + do + where = where->prev; + while (where != NULL && where->constraint == -1); + } + if (where == NULL) + { + einfo (_("%X%P: %s not found for insert\n"), i->where); + continue; + } + /* You can't insert into the list you are moving. */ + for (os = first_os; os != NULL; os = os->next) + if (os == where || os == last_os) + break; + if (os == where) + { + einfo (_("%X%P: %s not found for insert\n"), i->where); + continue; + } + + /* Deal with reordering the output section statement list. */ + if (last_os != NULL) + { + asection *first_sec, *last_sec; + + /* Snip out the output sections we are moving. */ + first_os->prev->next = last_os->next; + if (last_os->next == NULL) + lang_output_section_statement.tail + = (union lang_statement_union **) &first_os->prev->next; + else + last_os->next->prev = first_os->prev; + /* Add them in at the new position. */ + last_os->next = where->next; + if (where->next == NULL) + lang_output_section_statement.tail + = (union lang_statement_union **) &last_os->next; + else + where->next->prev = last_os; + first_os->prev = where; + where->next = first_os; + + /* Move the bfd sections in the same way. */ + first_sec = NULL; + last_sec = NULL; + for (os = first_os; os != NULL; os = os->next) + { + if (os->bfd_section != NULL + && os->bfd_section->owner != NULL) + { + last_sec = os->bfd_section; + if (first_sec == NULL) + first_sec = last_sec; + } + if (os == last_os) + break; + } + if (last_sec != NULL) + { + asection *sec = where->bfd_section; + if (sec == NULL) + sec = output_prev_sec_find (where); + + /* The place we want to insert must come after the + sections we are moving. So if we find no + section or if the section is the same as our + last section, then no move is needed. */ + if (sec != NULL && sec != last_sec) + { + /* Trim them off. */ + if (first_sec->prev != NULL) + first_sec->prev->next = last_sec->next; + else + output_bfd->sections = last_sec->next; + if (last_sec->next != NULL) + last_sec->next->prev = first_sec->prev; + else + output_bfd->section_last = first_sec->prev; + /* Add back. */ + last_sec->next = sec->next; + if (sec->next != NULL) + sec->next->prev = last_sec; + else + output_bfd->section_last = last_sec; + first_sec->prev = sec; + sec->next = first_sec; + } + } + + first_os = NULL; + last_os = NULL; + } + + ptr = insert_os_after (where); + /* Snip everything after the abs_section output statement we + know is at the start of the list, up to and including + the insert statement we are currently processing. */ + first = lang_output_section_statement.head->header.next; + lang_output_section_statement.head->header.next = (*s)->header.next; + /* Add them back where they belong. */ + *s = *ptr; + if (*s == NULL) + statement_list.tail = s; + *ptr = first; + s = &lang_output_section_statement.head; } } } @@ -3954,6 +4113,11 @@ print_statement (lang_statement_union_type *s, case lang_group_statement_enum: print_group (&s->group_statement, os); break; + case lang_insert_statement_enum: + minfo ("INSERT %s %s\n", + s->insert_statement.is_before ? "BEFORE" : "AFTER", + s->insert_statement.where); + break; } } @@ -4734,13 +4898,16 @@ lang_size_sections_1 fill, dot, relax, check_regions); break; - default: - FAIL (); + case lang_insert_statement_enum: break; /* We can only get here when relaxing is turned on. */ case lang_address_statement_enum: break; + + default: + FAIL (); + break; } prev = &s->header.next; } @@ -4999,12 +5166,15 @@ lang_do_assignments_1 (lang_statement_union_type *s, current_os, fill, dot); break; - default: - FAIL (); + case lang_insert_statement_enum: break; case lang_address_statement_enum: break; + + default: + FAIL (); + break; } } return dot; @@ -5853,6 +6023,8 @@ lang_process (void) to the correct output sections. */ map_input_to_output_sections (statement_list.head, NULL, NULL); + process_insert_statements (); + /* Find any sections not attached explicitly and handle them. */ lang_place_orphans (); @@ -6269,6 +6441,17 @@ lang_add_output_format (const char *format, } } +void +lang_add_insert (const char *where, int is_before) +{ + lang_insert_statement_type *new; + + new = new_stat (lang_insert_statement, stat_ptr); + new->where = where; + new->is_before = is_before; + saved_script_handle = previous_script_handle; +} + /* Enter a group. This creates a new lang_group_statement, and sets stat_ptr to build new statements within the group. */ diff --git a/ld/ldlang.h b/ld/ldlang.h index d0d2cf0..6de4267 100644 --- a/ld/ldlang.h +++ b/ld/ldlang.h @@ -79,6 +79,7 @@ typedef struct lang_statement_header_struct lang_output_statement_enum, lang_padding_statement_enum, lang_group_statement_enum, + lang_insert_statement_enum, lang_constructors_statement_enum } type; } lang_statement_header_type; @@ -352,6 +353,13 @@ typedef struct lang_statement_list_type children; } lang_group_statement_type; +typedef struct +{ + lang_statement_header_type header; + const char *where; + bfd_boolean is_before; +} lang_insert_statement_type; + typedef union lang_statement_union { lang_statement_header_type header; @@ -370,6 +378,7 @@ typedef union lang_statement_union lang_fill_statement_type fill_statement; lang_padding_statement_type padding_statement; lang_group_statement_type group_statement; + lang_insert_statement_type insert_statement; } lang_statement_union_type; /* This structure holds information about a program header, from the @@ -565,6 +574,8 @@ extern void lang_size_sections (bfd_boolean *, bfd_boolean); extern void one_lang_size_sections_pass (bfd_boolean *, bfd_boolean); +extern void lang_add_insert + (const char *, int); extern void lang_enter_group (void); extern void lang_leave_group @@ -274,6 +274,9 @@ V_IDENTIFIER [*?.$_a-zA-Z\[\]\-\!\^\\]([*?.$_a-zA-Z0-9\[\]\-\!\^\\]|::)* <BOTH,SCRIPT>"FORCE_COMMON_ALLOCATION" { RTOKEN(FORCE_COMMON_ALLOCATION);} <BOTH,SCRIPT>"INHIBIT_COMMON_ALLOCATION" { RTOKEN(INHIBIT_COMMON_ALLOCATION);} <BOTH,SCRIPT>"SECTIONS" { RTOKEN(SECTIONS);} +<BOTH,SCRIPT>"INSERT" { RTOKEN(INSERT_K);} +<BOTH,SCRIPT>"AFTER" { RTOKEN(AFTER);} +<BOTH,SCRIPT>"BEFORE" { RTOKEN(BEFORE);} <BOTH,SCRIPT>"FILL" { RTOKEN(FILL);} <BOTH,SCRIPT>"STARTUP" { RTOKEN(STARTUP);} <BOTH,SCRIPT>"OUTPUT_FORMAT" { RTOKEN(OUTPUT_FORMAT);} diff --git a/ld/ldmain.c b/ld/ldmain.c index 29f00d6..a9af255 100644 --- a/ld/ldmain.c +++ b/ld/ldmain.c @@ -60,6 +60,10 @@ extern void *sbrk (); /* EXPORTS */ +FILE *saved_script_handle = NULL; +FILE *previous_script_handle = NULL; +bfd_boolean force_make_executable = FALSE; + char *default_target; const char *output_filename = "a.out"; diff --git a/ld/ldmain.h b/ld/ldmain.h index 5491939..d297e8a 100644 --- a/ld/ldmain.h +++ b/ld/ldmain.h @@ -26,6 +26,9 @@ extern char *program_name; extern const char *ld_sysroot; extern char *ld_canon_sysroot; extern int ld_canon_sysroot_len; +extern FILE *saved_script_handle; +extern FILE *previous_script_handle; +extern bfd_boolean force_make_executable; extern bfd *output_bfd; extern char *default_target; extern bfd_boolean trace_files; diff --git a/ld/lexsup.c b/ld/lexsup.c index cdbfbdf..1e133d9 100644 --- a/ld/lexsup.c +++ b/ld/lexsup.c @@ -1166,9 +1166,11 @@ parse_args (unsigned argc, char **argv) trace_files = TRUE; break; case 'T': + previous_script_handle = saved_script_handle; ldfile_open_command_file (optarg); parser_input = input_script; yyparse (); + previous_script_handle = NULL; break; case OPTION_DEFAULT_SCRIPT: command_line.default_script = optarg; diff --git a/ld/testsuite/ChangeLog b/ld/testsuite/ChangeLog index f55e310..bda99a2 100644 --- a/ld/testsuite/ChangeLog +++ b/ld/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2008-01-25 Alan Modra <amodra@bigpond.net.au> + + * ld-spu/ovl.lnk: Delete overlay. + * ld-spu/ovl1.lnk: New file. + * ld-spu/ovl2.lnk: New file. + * ld-spu/ovl.d: Update. + * ld-spu/ovl2.d: Update. + 2008-01-23 Andreas Schwab <schwab@suse.de> * ld-gc/gc.c: Make sure used_func is not inlined. diff --git a/ld/testsuite/ld-spu/ovl.d b/ld/testsuite/ld-spu/ovl.d index 0f3deb2..c624659 100644 --- a/ld/testsuite/ld-spu/ovl.d +++ b/ld/testsuite/ld-spu/ovl.d @@ -1,5 +1,5 @@ #source: ovl.s -#ld: -N -T ovl.lnk --emit-relocs +#ld: -N -T ovl1.lnk -T ovl.lnk --emit-relocs #objdump: -D -r .*elf32-spu diff --git a/ld/testsuite/ld-spu/ovl.lnk b/ld/testsuite/ld-spu/ovl.lnk index 408ed1e..0015652 100644 --- a/ld/testsuite/ld-spu/ovl.lnk +++ b/ld/testsuite/ld-spu/ovl.lnk @@ -2,13 +2,6 @@ SECTIONS { . = SIZEOF_HEADERS; .text : { *(.text) *(.stub) } - - OVERLAY 0x400 : - { - .ov_a1 { *(.ov_a1) } - .ov_a2 { *(.ov_a2) } - } - .data : { *(.data) *(.ovtab) } .bss : { *(.bss) } } diff --git a/ld/testsuite/ld-spu/ovl1.lnk b/ld/testsuite/ld-spu/ovl1.lnk new file mode 100644 index 0000000..5fd4844 --- /dev/null +++ b/ld/testsuite/ld-spu/ovl1.lnk @@ -0,0 +1,9 @@ +SECTIONS +{ + OVERLAY 0x400 : + { + .ov_a1 { *(.ov_a1) } + .ov_a2 { *(.ov_a2) } + } +} +INSERT AFTER .text; diff --git a/ld/testsuite/ld-spu/ovl2.d b/ld/testsuite/ld-spu/ovl2.d index e4a47a6..bf62e03 100644 --- a/ld/testsuite/ld-spu/ovl2.d +++ b/ld/testsuite/ld-spu/ovl2.d @@ -1,5 +1,5 @@ #source: ovl2.s -#ld: -N -T ovl.lnk --emit-relocs +#ld: -N -T ovl2.lnk -T ovl.lnk --emit-relocs #objdump: -D -r .*elf32-spu diff --git a/ld/testsuite/ld-spu/ovl2.lnk b/ld/testsuite/ld-spu/ovl2.lnk new file mode 100644 index 0000000..9916ab2 --- /dev/null +++ b/ld/testsuite/ld-spu/ovl2.lnk @@ -0,0 +1,10 @@ +SECTIONS +{ + OVERLAY 0x400 : + { + .ov_a1 { *(.ov_a1) } + .ov_a2 { *(.ov_a2) } + .empty { empty.o?(.text) } + } +} +INSERT BEFORE .data; |