diff --git a/ld/NEWS b/ld/NEWS index f70d2157339..458f6562ca0 100644 --- a/ld/NEWS +++ b/ld/NEWS @@ -2,6 +2,9 @@ * Add -plugin-save-temps to store plugin intermediate files permanently. +* Add the linker option, --section-ordering-file=FILE, for ELF and + PE COFF linker to specify the input section order. + Changes in 2.42: * Add -z mark-plt/-z nomark-plt options to x86-64 ELF linker to mark PLT diff --git a/ld/ld.h b/ld/ld.h index fcdd9a2c083..81e9ee03387 100644 --- a/ld/ld.h +++ b/ld/ld.h @@ -319,8 +319,20 @@ typedef struct /* Compress DWARF debug sections. */ enum compressed_debug_section_type compress_debug; + + /* The optional section ordering file. */ + const char *section_ordering_file; + } ld_config_type; +/* Any "INPUT " directive inside a linekr script that uses + SECTION_ORDERING_FILE as its filename is actually a indicator that + the config.section_ordering_file parameter should be processed and + inserted. */ +#define SECTION_ORDERING_FILE_1 "section-ordering-file" +/* Also accept the name with underscores instead of dashes. */ +#define SECTION_ORDERING_FILE_2 "section_ordering_file" + extern ld_config_type config; extern FILE * saved_script_handle; diff --git a/ld/ld.texi b/ld/ld.texi index ca9574dfc71..a6f04c8a457 100644 --- a/ld/ld.texi +++ b/ld/ld.texi @@ -2552,6 +2552,105 @@ patterns in the linker script. This option will apply @code{SORT_BY_ALIGNMENT} to all wildcard section patterns in the linker script. +@kindex --section-ordering-file=@file{file} +@item --section-ordering-file=@file{file} +This option gives the linker a file which tells it how to order +specific input sections when placing them in an output section. + +The input file consists of one or more entries separated by white +space and/or line separators. Blank lines and line whose +first non-whitespace character is a hash (@samp{#}), are ignored. + +Each entry in the section ordering file is either a section name, a +section name regular expression, or an output section name followed by +a list of section name patterns enclosed in parentheses. + +A section name attempts to map any input section matching that name to +the output section that starts with the same prefix as the input +section. So for example @samp{.text.hot} maps all input sections +called @samp{.text.hot} to the output section @samp{.text}. Note - +this mapping will only work if the linker script being used has +specified that the particular output section allows section ordering +input. See below for more details. + +A section name regular expression entry matches multiple input +sections to the output section whose name starts of the fixed part of +the expression. + +A section name followed by a list of section name expressions enclosed +inside parentheses maps the section name expressions to the output +section named first. So for example @samp{.data(.foo)} maps all +incoming @samp{.foo} sections to the @samp{.data} output section. +Note - if there are multiple section names or section name patterns +inside the parenthesised list, the order of those sections relative to +each other is not guaranteed. + +Most importantly however the order in which entries occur in the +section ordering file will be preserved, within the output section to +which they are mapped. + +Here is an example: + +@smallexample + # A comment + .text.hot .text.cold .text.warm + .data.big + .data(.bar) + .text.foo* +@end smallexample + +This is roughly equivalent to a linker script that looks like this: + +@smallexample + SECTIONS + @{ + .text : @{ + *(.text.hot) + *(.text.cold) + *(.text.warm) + *(.text.foo*) + *(.text) + @} + .data : @{ + *(.data.big) + *(.bar) + *(.data) + @} + @} +@end smallexample + +Note - the syntax used in the section ordering file is similar to, but +different from the syntax used in linker scripts. + +Note - this option works in conjunction with the linker script being +used by the linker. In particular the linker script must use special +directives to indicate where the section order file's mappings should +be placed. So the above example would only work if the linker script +looked something like this: + +@smallexample + SECTIONS + @{ + .text : @{ + INCLUDE section-ordering-file + *(.text) + @} + .data : @{ + INCLUDE section-ordering-file + *(.data) + @} + @} +@end smallexample + +Note - the @samp{--section-ordering-file} option controls the order of +input sections being mapped into an output section. It does not +control the order of output sections in relation to each other. + +If the @option{verbose} option is enabled, the contents of the section +ordering file will be reported. + +This option is supported only in ELF and PE COFF linkers. + @kindex --spare-dynamic-tags @item --spare-dynamic-tags=@var{count} This option specifies the number of empty slots to leave in the @@ -4188,6 +4287,13 @@ with the @option{-L} option. You can nest calls to @code{INCLUDE} up to You can place @code{INCLUDE} directives at the top level, in @code{MEMORY} or @code{SECTIONS} commands, or in output section descriptions. +Note - there is a special case for @code{INCLUDE} directives found +inside the @code{SECTION} command. If the @var{filename} is +@var{section_ordering_file} or @var{section-ordering-file} then this +indicates that the contents of the file specified by the +@option{--section-ordering-file} option should be used at that +particular point in the script. + @item INPUT(@var{file}, @var{file}, @dots{}) @itemx INPUT(@var{file} @var{file} @dots{}) @kindex INPUT(@var{files}) diff --git a/ld/ldfile.c b/ld/ldfile.c index dc9875d8813..991affbc44d 100644 --- a/ld/ldfile.c +++ b/ld/ldfile.c @@ -736,7 +736,7 @@ ldfile_open_file (lang_input_statement_type *entry) /* Try to open NAME. */ static FILE * -try_open (const char *name, bool *sysrooted) +try_open (const char *name, const char *orig_name, bool *sysrooted) { FILE *result; @@ -750,10 +750,44 @@ try_open (const char *name, bool *sysrooted) if (verbose) { - if (result == NULL) - info_msg (_("cannot find script file %s\n"), name); + if (config.section_ordering_file != NULL + && strcmp (orig_name, config.section_ordering_file) == 0) + { + static bool displayed_ordering_file = false; + + if (displayed_ordering_file) + ; + else if (result == NULL) + info_msg (_("cannot find section ordering file: %s\n"), + name); + else + { + static const int ld_bufsz = 8193; + size_t n; + char *buf = (char *) xmalloc (ld_bufsz); + + info_msg (_("opened section ordering file: %s\n"), name); + info_msg ("==================================================\n"); + + while ((n = fread (buf, 1, ld_bufsz - 1, result)) > 0) + { + buf[n] = 0; + info_msg ("%s", buf); + } + rewind (result); + free (buf); + + info_msg ("==================================================\n\n"); + displayed_ordering_file = true; + } + } else - info_msg (_("opened script file %s\n"), name); + { + if (result == NULL) + info_msg (_("cannot find script file %s\n"), name); + else + info_msg (_("opened script file %s\n"), name); + } } return result; @@ -832,7 +866,7 @@ ldfile_find_command_file (const char *name, if (!default_only) { /* First try raw name. */ - result = try_open (name, sysrooted); + result = try_open (name, name, sysrooted); if (result != NULL) return result; } @@ -859,7 +893,7 @@ ldfile_find_command_file (const char *name, search = search->next) { path = concat (search->name, slash, name, (const char *) NULL); - result = try_open (path, sysrooted); + result = try_open (path, name, sysrooted); free (path); if (result) break; @@ -871,7 +905,8 @@ ldfile_find_command_file (const char *name, return result; } -enum script_open_style { +enum script_open_style +{ script_nonT, script_T, script_defaultT @@ -908,6 +943,40 @@ ldfile_open_command_file_1 (const char *name, enum script_open_style open_how) } } + /* Don't allow nested INCLUDEs in the section ordering file. */ + if (in_section_ordering_file) + { + einfo (_("%F%P: error: 'INCLUDE %s' found in the section " + "ordering file: '%s'\n"), name, config.section_ordering_file); + return; + } + + if (strcmp (name, SECTION_ORDERING_FILE_1) == 0 + || strcmp (name, SECTION_ORDERING_FILE_2) == 0) + { + /* Support + + INCLUDE section_ordering_file; + + in input sections in linker script. */ + if (config.section_ordering_file == NULL) + { + /* Skip if the section ordering file isn't specified. */ + lex_push_file (NULL, name, false); + return; + } + + /* FIXME: Check that we are inside the SECTIONS part of the script. */ + + /* Load the section ordering file. */ + name = config.section_ordering_file; + + /* Set the in the section ordering file marker. */ + in_section_ordering_file = true; + } + else + in_section_ordering_file = false; + /* FIXME: This memory is never freed, but that should not really matter. It will be released when the linker exits, and it is unlikely to ever be more than a few tens of bytes. */ @@ -932,7 +1001,12 @@ ldfile_open_command_file_1 (const char *name, enum script_open_style open_how) lineno = 1; - saved_script_handle = ldlex_input_stack; + /* Clear the end of the include file marker. */ + seen_eof_include_file = false; + + /* The section ordering file isn't a real linker script file. */ + if (!in_section_ordering_file) + saved_script_handle = ldlex_input_stack; } /* Open command file NAME in the current directory, -L directories, diff --git a/ld/ldlang.c b/ld/ldlang.c index 54d1af62ebe..61d2f5153ab 100644 --- a/ld/ldlang.c +++ b/ld/ldlang.c @@ -689,7 +689,9 @@ wild_sort (lang_wild_statement_type *wild, looking at the sections for this file. */ /* Find the correct node to append this section. */ - if (sec && sec->spec.sorted != none && sec->spec.sorted != by_none + if (sec + && sec->spec.sorted != none + && sec->spec.sorted != by_none && compare_section (sec->spec.sorted, section, (*tree)->section, sec->spec.reversed) < 0) tree = &((*tree)->left); else @@ -957,7 +959,7 @@ resolve_wild_sections (lang_input_statement_type *file) const char *sname = bfd_section_name (s); char c = 1; struct prefixtree *t = ptroot; - //printf (" YYY consider %s of %s\n", sname, file->the_bfd->filename); + do { if (t->stmt) @@ -966,7 +968,6 @@ resolve_wild_sections (lang_input_statement_type *file) for (sl = t->stmt; sl; sl = sl->next) { walk_wild_section_match (sl->stmt, file, s); - //printf (" ZZZ maybe place into %p\n", sl->stmt); } } if (!c) @@ -985,7 +986,6 @@ resolve_wilds (void) { LANG_FOR_EACH_INPUT_STATEMENT (f) { - //printf("XXX %s\n", f->filename); if (f->the_bfd == NULL || !bfd_check_format (f->the_bfd, bfd_archive)) resolve_wild_sections (f); @@ -8515,12 +8515,101 @@ lang_add_wild (struct wildcard_spec *filespec, if (filespec != NULL && filespec->name != NULL) { - if (strcmp (filespec->name, "*") == 0) - filespec->name = NULL; - else if (!wildcardp (filespec->name)) - lang_has_input_file = true; + if (in_section_ordering_file) + { + if (filespec->sorted != none + || filespec->exclude_name_list != NULL + || filespec->section_flag_list != NULL + || filespec->reversed) + { + einfo (_("%F%P: error: \ +sorting, excluding, reversing and flag selection is not supported in section ordering file entries\n")); + } + else if (current_section == NULL) + { + einfo (_("%F%P: error: \ +using a section ordering file outside of an output section definition is not supported\n")); + } + else if (section_list != NULL) + { + /* We need a way to include input sections whose names do not + start with the output section's name. We do this by accepting + entries of the form " ( )" + The should not be a regexp. */ + if (strcmp (filespec->name, current_section->name) == 0) + { + /* Convert .text(.bar) into *(.bar). */ + filespec->name = NULL; + goto cont; + } + + /* FIXME: We have no way of detecting if an entry in the section + ordering file does not match any output section. */ + } + else if (strncmp (filespec->name, current_section->name, + strlen (current_section->name))) + { + /* Exclude this entry from this section. + Since we have a single section ordering file, but we want it + to be able to provide the ordering for multiple output + sections, we match entries to output sections. So for + example if the ordering file contains: + .text.foo + .data.bar + .text.b* + .data.z* + Then we add the .text.foo and .text.b* patterns to the .text + output section (in that order) and the .data.bar and .data.z* + patterns to the .data output section. + + This behaviour relies upon the linker script containing + "INCLUDE section-ordering-file" directives in all of the + output sections that it wants to be able to be affected by + the --section-ordering-file option. */ + ; + + /* FIXME: We have no way of detecting if an entry in the section + ordering file does not match any output section. */ + } + else + { + /* A section ordering file entry looks like a filename + without a section list. Convert the filename into + a section name and create a wild card as the file + name. */ + struct wildcard_list *single_section = XCNEW (struct wildcard_list); + + single_section->spec.name = filespec->name; + single_section->spec.sorted = none; + /* A NULL indicates the wild card file name, "*". */ + filespec->name = NULL; + section_list = single_section; + + /* Carry on processing the statement. */ + goto cont; + } + + if (seen_eof_include_file) + in_section_ordering_file = false; + + return; + } + else + { + if (strcmp (filespec->name, "*") == 0) + filespec->name = NULL; + else if (!wildcardp (filespec->name)) + lang_has_input_file = true; + } } + cont: + /* NB: Clear the in the section ordering file marker after + processing the last entry when the end of the section + ordering file is reached. */ + if (in_section_ordering_file && seen_eof_include_file) + in_section_ordering_file = false; + new_stmt = new_stat (lang_wild_statement, stat_ptr); new_stmt->filename = NULL; new_stmt->filenames_sorted = false; diff --git a/ld/ldlex.h b/ld/ldlex.h index d575562a357..39454cc764c 100644 --- a/ld/ldlex.h +++ b/ld/ldlex.h @@ -68,6 +68,7 @@ enum option_values OPTION_TASK_LINK, OPTION_TBSS, OPTION_TDATA, + OPTION_SECTION_ORDERING_FILE, OPTION_TTEXT, OPTION_TTEXT_SEGMENT, OPTION_TRODATA_SEGMENT, @@ -485,6 +486,8 @@ extern input_type parser_input; extern unsigned int lineno; extern const char *lex_string; +extern bool in_section_ordering_file; +extern bool seen_eof_include_file; /* In ldlex.l. */ extern int yylex (void); diff --git a/ld/ldlex.l b/ld/ldlex.l index e113c90812b..113e4126d0b 100644 --- a/ld/ldlex.l +++ b/ld/ldlex.l @@ -43,6 +43,12 @@ input_type parser_input; /* Line number in the current input file. */ unsigned int lineno; +/* True if the current input file is the section ordering file. */ +bool in_section_ordering_file = false; + +/* True if the end of the include file is reached. */ +bool seen_eof_include_file = false; + /* The string we are currently lexing, or NULL if we are reading a file. */ const char *lex_string = NULL; @@ -487,6 +493,7 @@ V_IDENTIFIER [*?.$_a-zA-Z\[\]\-\!\^\\]([*?.$_a-zA-Z0-9\[\]\-\!\^\\]|::)* lineno = lineno_stack[include_stack_ptr]; input_flags.sysrooted = sysrooted_stack[include_stack_ptr]; + seen_eof_include_file = true; return END; } diff --git a/ld/lexsup.c b/ld/lexsup.c index dad3b6059ed..15a20fe86ac 100644 --- a/ld/lexsup.c +++ b/ld/lexsup.c @@ -487,6 +487,9 @@ static const struct ld_option ld_options[] = { {"sort-section", required_argument, NULL, OPTION_SORT_SECTION}, '\0', N_("name|alignment"), N_("Sort sections by name or maximum alignment"), TWO_DASHES }, + { {"section-ordering-file", required_argument, NULL, OPTION_SECTION_ORDERING_FILE}, + '\0', N_("FILE"), + N_("Sort sections by statements in FILE"), TWO_DASHES }, { {"spare-dynamic-tags", required_argument, NULL, OPTION_SPARE_DYNAMIC_TAGS}, '\0', N_("COUNT"), N_("How many tags to reserve in .dynamic section"), TWO_DASHES }, @@ -673,6 +676,7 @@ parse_args (unsigned argc, char **argv) dynamic_list } opt_dynamic_list = dynamic_list_unset; struct bfd_elf_dynamic_list *export_list = NULL; + bool seen_linker_script = false; shortopts = (char *) xmalloc (OPTION_COUNT * 3 + 2); longopts = (struct option *) @@ -1400,6 +1404,12 @@ parse_args (unsigned argc, char **argv) einfo (_("%F%P: invalid section sorting option: %s\n"), optarg); break; + case OPTION_SECTION_ORDERING_FILE: + if (seen_linker_script) + einfo (_("%F%P: --section-ordering-file must be placed" + " before -T/--script\n")); + config.section_ordering_file = optarg; + break; case OPTION_STATS: config.stats = true; break; @@ -1416,6 +1426,7 @@ parse_args (unsigned argc, char **argv) ++trace_files; break; case 'T': + seen_linker_script = true; previous_script_handle = saved_script_handle; ldfile_open_script_file (optarg); parser_input = input_script; diff --git a/ld/scripttempl/arclinux.sc b/ld/scripttempl/arclinux.sc index 36ba5a664d3..eabbb90c3d5 100644 --- a/ld/scripttempl/arclinux.sc +++ b/ld/scripttempl/arclinux.sc @@ -487,11 +487,12 @@ cat < rom} @@ -131,6 +132,7 @@ SECTIONS .data : { __DATA_START = .; + ${RELOCATING+INCLUDE section_ordering_file} *(.data_4) *(.data_2) *(.data_1) *(.data) *(.data.*) *(.gnu.linkonce.d.*) __DATA_END = .; }${RELOCATING+ > ram AT > rom} diff --git a/ld/scripttempl/elf32crx.sc b/ld/scripttempl/elf32crx.sc index 1b1316676a7..a1a8186a371 100644 --- a/ld/scripttempl/elf32crx.sc +++ b/ld/scripttempl/elf32crx.sc @@ -77,6 +77,7 @@ SECTIONS .text : { __TEXT_START = .; + ${RELOCATING+INCLUDE section_ordering_file} *(.text) *(.text.*) *(.gnu.linkonce.t.*) __TEXT_END = .; } > rom @@ -129,6 +130,7 @@ SECTIONS .data : { __DATA_START = .; + ${RELOCATING+INCLUDE section_ordering_file} *(.data_4) *(.data_2) *(.data_1) *(.data) *(.data.*) *(.gnu.linkonce.d.*) __DATA_END = .; } > ram AT > rom diff --git a/ld/scripttempl/elf32msp430.sc b/ld/scripttempl/elf32msp430.sc index bed0d673238..2c638f15b00 100644 --- a/ld/scripttempl/elf32msp430.sc +++ b/ld/scripttempl/elf32msp430.sc @@ -163,6 +163,7 @@ SECTIONS *(.lower.text.* .lower.text) . = ALIGN(2);} + ${RELOCATING+INCLUDE section_ordering_file} *(.text) ${RELOCATING+. = ALIGN(2); *(.text.*) @@ -260,6 +261,7 @@ SECTIONS PROVIDE (__data_start = .) ; PROVIDE (__datastart = .) ; + ${RELOCATING+INCLUDE section_ordering_file} KEEP (*(.jcr)) *(.data.rel.ro.local) *(.data.rel.ro*) *(.dynamic) diff --git a/ld/scripttempl/elf64bpf.sc b/ld/scripttempl/elf64bpf.sc index ca62d7c88e0..0ea9b4a9064 100644 --- a/ld/scripttempl/elf64bpf.sc +++ b/ld/scripttempl/elf64bpf.sc @@ -508,11 +508,12 @@ cat <