| /* Linker command language support. |
| Copyright (C) 1991-2021 Free Software Foundation, Inc. |
| |
| This file is part of the GNU Binutils. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
| MA 02110-1301, USA. */ |
| |
| #include "sysdep.h" |
| #include <limits.h> |
| #include "bfd.h" |
| #include "libiberty.h" |
| #include "filenames.h" |
| #include "safe-ctype.h" |
| #include "obstack.h" |
| #include "bfdlink.h" |
| #include "ctf-api.h" |
| |
| #include "ld.h" |
| #include "ldmain.h" |
| #include "ldexp.h" |
| #include "ldlang.h" |
| #include <ldgram.h> |
| #include "ldlex.h" |
| #include "ldmisc.h" |
| #include "ldctor.h" |
| #include "ldfile.h" |
| #include "ldemul.h" |
| #include "fnmatch.h" |
| #include "demangle.h" |
| #include "hashtab.h" |
| #include "elf-bfd.h" |
| #if BFD_SUPPORTS_PLUGINS |
| #include "plugin.h" |
| #endif /* BFD_SUPPORTS_PLUGINS */ |
| |
| #ifndef offsetof |
| #define offsetof(TYPE, MEMBER) ((size_t) & (((TYPE*) 0)->MEMBER)) |
| #endif |
| |
| /* Convert between addresses in bytes and sizes in octets. |
| For currently supported targets, octets_per_byte is always a power |
| of two, so we can use shifts. */ |
| #define TO_ADDR(X) ((X) >> opb_shift) |
| #define TO_SIZE(X) ((X) << opb_shift) |
| |
| /* Local variables. */ |
| static struct obstack stat_obstack; |
| static struct obstack map_obstack; |
| |
| #define obstack_chunk_alloc xmalloc |
| #define obstack_chunk_free free |
| static const char *entry_symbol_default = "start"; |
| static bool map_head_is_link_order = false; |
| static lang_output_section_statement_type *default_common_section; |
| static bool map_option_f; |
| static bfd_vma print_dot; |
| static lang_input_statement_type *first_file; |
| static const char *current_target; |
| static lang_statement_list_type *stat_save[10]; |
| static lang_statement_list_type **stat_save_ptr = &stat_save[0]; |
| static struct unique_sections *unique_section_list; |
| static struct asneeded_minfo *asneeded_list_head; |
| static unsigned int opb_shift = 0; |
| |
| /* Forward declarations. */ |
| static void exp_init_os (etree_type *); |
| static lang_input_statement_type *lookup_name (const char *); |
| static void insert_undefined (const char *); |
| static bool sort_def_symbol (struct bfd_link_hash_entry *, void *); |
| static void print_statement (lang_statement_union_type *, |
| lang_output_section_statement_type *); |
| static void print_statement_list (lang_statement_union_type *, |
| lang_output_section_statement_type *); |
| static void print_statements (void); |
| static void print_input_section (asection *, bool); |
| static bool lang_one_common (struct bfd_link_hash_entry *, void *); |
| static void lang_record_phdrs (void); |
| static void lang_do_version_exports_section (void); |
| static void lang_finalize_version_expr_head |
| (struct bfd_elf_version_expr_head *); |
| static void lang_do_memory_regions (bool); |
| |
| /* Exported variables. */ |
| const char *output_target; |
| lang_output_section_statement_type *abs_output_section; |
| /* Header for list of statements corresponding to any files involved in the |
| link, either specified from the command-line or added implicitely (eg. |
| archive member used to resolved undefined symbol, wildcard statement from |
| linker script, etc.). Next pointer is in next field of a |
| lang_statement_header_type (reached via header field in a |
| lang_statement_union). */ |
| lang_statement_list_type statement_list; |
| lang_statement_list_type lang_os_list; |
| lang_statement_list_type *stat_ptr = &statement_list; |
| /* Header for list of statements corresponding to files used in the final |
| executable. This can be either object file specified on the command-line |
| or library member resolving an undefined reference. Next pointer is in next |
| field of a lang_input_statement_type (reached via input_statement field in a |
| lang_statement_union). */ |
| lang_statement_list_type file_chain = { NULL, NULL }; |
| /* Header for list of statements corresponding to files specified on the |
| command-line for linking. It thus contains real object files and archive |
| but not archive members. Next pointer is in next_real_file field of a |
| lang_input_statement_type statement (reached via input_statement field in a |
| lang_statement_union). */ |
| lang_statement_list_type input_file_chain; |
| static const char *current_input_file; |
| struct bfd_elf_dynamic_list **current_dynamic_list_p; |
| struct bfd_sym_chain entry_symbol = { NULL, NULL }; |
| const char *entry_section = ".text"; |
| struct lang_input_statement_flags input_flags; |
| bool entry_from_cmdline; |
| bool lang_has_input_file = false; |
| bool had_output_filename = false; |
| bool lang_float_flag = false; |
| bool delete_output_file_on_failure = false; |
| struct lang_phdr *lang_phdr_list; |
| struct lang_nocrossrefs *nocrossref_list; |
| struct asneeded_minfo **asneeded_list_tail; |
| #ifdef ENABLE_LIBCTF |
| static ctf_dict_t *ctf_output; |
| #endif |
| |
| /* Functions that traverse the linker script and might evaluate |
| DEFINED() need to increment this at the start of the traversal. */ |
| int lang_statement_iteration = 0; |
| |
| /* Count times through one_lang_size_sections_pass after mark phase. */ |
| static int lang_sizing_iteration = 0; |
| |
| /* Return TRUE if the PATTERN argument is a wildcard pattern. |
| Although backslashes are treated specially if a pattern contains |
| wildcards, we do not consider the mere presence of a backslash to |
| be enough to cause the pattern to be treated as a wildcard. |
| That lets us handle DOS filenames more naturally. */ |
| #define wildcardp(pattern) (strpbrk ((pattern), "?*[") != NULL) |
| |
| #define new_stat(x, y) \ |
| (x##_type *) new_statement (x##_enum, sizeof (x##_type), y) |
| |
| #define outside_section_address(q) \ |
| ((q)->output_offset + (q)->output_section->vma) |
| |
| #define outside_symbol_address(q) \ |
| ((q)->value + outside_section_address (q->section)) |
| |
| /* CTF sections smaller than this are not compressed: compression of |
| dictionaries this small doesn't gain much, and this lets consumers mmap the |
| sections directly out of the ELF file and use them with no decompression |
| overhead if they want to. */ |
| #define CTF_COMPRESSION_THRESHOLD 4096 |
| |
| void * |
| stat_alloc (size_t size) |
| { |
| return obstack_alloc (&stat_obstack, size); |
| } |
| |
| static int |
| name_match (const char *pattern, const char *name) |
| { |
| if (wildcardp (pattern)) |
| return fnmatch (pattern, name, 0); |
| return strcmp (pattern, name); |
| } |
| |
| static char * |
| ldirname (const char *name) |
| { |
| const char *base = lbasename (name); |
| char *dirname; |
| |
| while (base > name && IS_DIR_SEPARATOR (base[-1])) |
| --base; |
| if (base == name) |
| return strdup ("."); |
| dirname = strdup (name); |
| dirname[base - name] = '\0'; |
| return dirname; |
| } |
| |
| /* If PATTERN is of the form archive:file, return a pointer to the |
| separator. If not, return NULL. */ |
| |
| static char * |
| archive_path (const char *pattern) |
| { |
| char *p = NULL; |
| |
| if (link_info.path_separator == 0) |
| return p; |
| |
| p = strchr (pattern, link_info.path_separator); |
| #ifdef HAVE_DOS_BASED_FILE_SYSTEM |
| if (p == NULL || link_info.path_separator != ':') |
| return p; |
| |
| /* Assume a match on the second char is part of drive specifier, |
| as in "c:\silly.dos". */ |
| if (p == pattern + 1 && ISALPHA (*pattern)) |
| p = strchr (p + 1, link_info.path_separator); |
| #endif |
| return p; |
| } |
| |
| /* Given that FILE_SPEC results in a non-NULL SEP result from archive_path, |
| return whether F matches FILE_SPEC. */ |
| |
| static bool |
| input_statement_is_archive_path (const char *file_spec, char *sep, |
| lang_input_statement_type *f) |
| { |
| bool match = false; |
| |
| if ((*(sep + 1) == 0 |
| || name_match (sep + 1, f->filename) == 0) |
| && ((sep != file_spec) |
| == (f->the_bfd != NULL && f->the_bfd->my_archive != NULL))) |
| { |
| match = true; |
| |
| if (sep != file_spec) |
| { |
| const char *aname = bfd_get_filename (f->the_bfd->my_archive); |
| *sep = 0; |
| match = name_match (file_spec, aname) == 0; |
| *sep = link_info.path_separator; |
| } |
| } |
| return match; |
| } |
| |
| static bool |
| unique_section_p (const asection *sec, |
| const lang_output_section_statement_type *os) |
| { |
| struct unique_sections *unam; |
| const char *secnam; |
| |
| if (!link_info.resolve_section_groups |
| && sec->owner != NULL |
| && bfd_is_group_section (sec->owner, sec)) |
| return !(os != NULL |
| && strcmp (os->name, DISCARD_SECTION_NAME) == 0); |
| |
| secnam = sec->name; |
| for (unam = unique_section_list; unam; unam = unam->next) |
| if (name_match (unam->name, secnam) == 0) |
| return true; |
| |
| return false; |
| } |
| |
| /* Generic traversal routines for finding matching sections. */ |
| |
| /* Return true if FILE matches a pattern in EXCLUDE_LIST, otherwise return |
| false. */ |
| |
| static bool |
| walk_wild_file_in_exclude_list (struct name_list *exclude_list, |
| lang_input_statement_type *file) |
| { |
| struct name_list *list_tmp; |
| |
| for (list_tmp = exclude_list; |
| list_tmp; |
| list_tmp = list_tmp->next) |
| { |
| char *p = archive_path (list_tmp->name); |
| |
| if (p != NULL) |
| { |
| if (input_statement_is_archive_path (list_tmp->name, p, file)) |
| return true; |
| } |
| |
| else if (name_match (list_tmp->name, file->filename) == 0) |
| return true; |
| |
| /* FIXME: Perhaps remove the following at some stage? Matching |
| unadorned archives like this was never documented and has |
| been superceded by the archive:path syntax. */ |
| else if (file->the_bfd != NULL |
| && file->the_bfd->my_archive != NULL |
| && name_match (list_tmp->name, |
| bfd_get_filename (file->the_bfd->my_archive)) == 0) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* Try processing a section against a wildcard. This just calls |
| the callback unless the filename exclusion list is present |
| and excludes the file. It's hardly ever present so this |
| function is very fast. */ |
| |
| static void |
| walk_wild_consider_section (lang_wild_statement_type *ptr, |
| lang_input_statement_type *file, |
| asection *s, |
| struct wildcard_list *sec, |
| callback_t callback, |
| void *data) |
| { |
| /* Don't process sections from files which were excluded. */ |
| if (walk_wild_file_in_exclude_list (sec->spec.exclude_name_list, file)) |
| return; |
| |
| (*callback) (ptr, sec, s, file, data); |
| } |
| |
| /* Lowest common denominator routine that can handle everything correctly, |
| but slowly. */ |
| |
| static void |
| walk_wild_section_general (lang_wild_statement_type *ptr, |
| lang_input_statement_type *file, |
| callback_t callback, |
| void *data) |
| { |
| asection *s; |
| struct wildcard_list *sec; |
| |
| for (s = file->the_bfd->sections; s != NULL; s = s->next) |
| { |
| sec = ptr->section_list; |
| if (sec == NULL) |
| (*callback) (ptr, sec, s, file, data); |
| |
| while (sec != NULL) |
| { |
| bool skip = false; |
| |
| if (sec->spec.name != NULL) |
| { |
| const char *sname = bfd_section_name (s); |
| |
| skip = name_match (sec->spec.name, sname) != 0; |
| } |
| |
| if (!skip) |
| walk_wild_consider_section (ptr, file, s, sec, callback, data); |
| |
| sec = sec->next; |
| } |
| } |
| } |
| |
| /* Routines to find a single section given its name. If there's more |
| than one section with that name, we report that. */ |
| |
| typedef struct |
| { |
| asection *found_section; |
| bool multiple_sections_found; |
| } section_iterator_callback_data; |
| |
| static bool |
| section_iterator_callback (bfd *abfd ATTRIBUTE_UNUSED, asection *s, void *data) |
| { |
| section_iterator_callback_data *d = (section_iterator_callback_data *) data; |
| |
| if (d->found_section != NULL) |
| { |
| d->multiple_sections_found = true; |
| return true; |
| } |
| |
| d->found_section = s; |
| return false; |
| } |
| |
| static asection * |
| find_section (lang_input_statement_type *file, |
| struct wildcard_list *sec, |
| bool *multiple_sections_found) |
| { |
| section_iterator_callback_data cb_data = { NULL, false }; |
| |
| bfd_get_section_by_name_if (file->the_bfd, sec->spec.name, |
| section_iterator_callback, &cb_data); |
| *multiple_sections_found = cb_data.multiple_sections_found; |
| return cb_data.found_section; |
| } |
| |
| /* Code for handling simple wildcards without going through fnmatch, |
| which can be expensive because of charset translations etc. */ |
| |
| /* A simple wild is a literal string followed by a single '*', |
| where the literal part is at least 4 characters long. */ |
| |
| static bool |
| is_simple_wild (const char *name) |
| { |
| size_t len = strcspn (name, "*?["); |
| return len >= 4 && name[len] == '*' && name[len + 1] == '\0'; |
| } |
| |
| static bool |
| match_simple_wild (const char *pattern, const char *name) |
| { |
| /* The first four characters of the pattern are guaranteed valid |
| non-wildcard characters. So we can go faster. */ |
| if (pattern[0] != name[0] || pattern[1] != name[1] |
| || pattern[2] != name[2] || pattern[3] != name[3]) |
| return false; |
| |
| pattern += 4; |
| name += 4; |
| while (*pattern != '*') |
| if (*name++ != *pattern++) |
| return false; |
| |
| return true; |
| } |
| |
| /* Return the numerical value of the init_priority attribute from |
| section name NAME. */ |
| |
| static int |
| get_init_priority (const asection *sec) |
| { |
| const char *name = bfd_section_name (sec); |
| const char *dot; |
| |
| /* GCC uses the following section names for the init_priority |
| attribute with numerical values 101 to 65535 inclusive. A |
| lower value means a higher priority. |
| |
| 1: .init_array.NNNNN/.fini_array.NNNNN: Where NNNNN is the |
| decimal numerical value of the init_priority attribute. |
| The order of execution in .init_array is forward and |
| .fini_array is backward. |
| 2: .ctors.NNNNN/.dtors.NNNNN: Where NNNNN is 65535 minus the |
| decimal numerical value of the init_priority attribute. |
| The order of execution in .ctors is backward and .dtors |
| is forward. |
| |
| .init_array.NNNNN sections would normally be placed in an output |
| .init_array section, .fini_array.NNNNN in .fini_array, |
| .ctors.NNNNN in .ctors, and .dtors.NNNNN in .dtors. This means |
| we should sort by increasing number (and could just use |
| SORT_BY_NAME in scripts). However if .ctors.NNNNN sections are |
| being placed in .init_array (which may also contain |
| .init_array.NNNNN sections) or .dtors.NNNNN sections are being |
| placed in .fini_array then we need to extract the init_priority |
| attribute and sort on that. */ |
| dot = strrchr (name, '.'); |
| if (dot != NULL && ISDIGIT (dot[1])) |
| { |
| char *end; |
| unsigned long init_priority = strtoul (dot + 1, &end, 10); |
| if (*end == 0) |
| { |
| if (dot == name + 6 |
| && (strncmp (name, ".ctors", 6) == 0 |
| || strncmp (name, ".dtors", 6) == 0)) |
| init_priority = 65535 - init_priority; |
| if (init_priority <= INT_MAX) |
| return init_priority; |
| } |
| } |
| return -1; |
| } |
| |
| /* Compare sections ASEC and BSEC according to SORT. */ |
| |
| static int |
| compare_section (sort_type sort, asection *asec, asection *bsec) |
| { |
| int ret; |
| int a_priority, b_priority; |
| |
| switch (sort) |
| { |
| default: |
| abort (); |
| |
| case by_init_priority: |
| a_priority = get_init_priority (asec); |
| b_priority = get_init_priority (bsec); |
| if (a_priority < 0 || b_priority < 0) |
| goto sort_by_name; |
| ret = a_priority - b_priority; |
| if (ret) |
| break; |
| else |
| goto sort_by_name; |
| |
| case by_alignment_name: |
| ret = bfd_section_alignment (bsec) - bfd_section_alignment (asec); |
| if (ret) |
| break; |
| /* Fall through. */ |
| |
| case by_name: |
| sort_by_name: |
| ret = strcmp (bfd_section_name (asec), bfd_section_name (bsec)); |
| break; |
| |
| case by_name_alignment: |
| ret = strcmp (bfd_section_name (asec), bfd_section_name (bsec)); |
| if (ret) |
| break; |
| /* Fall through. */ |
| |
| case by_alignment: |
| ret = bfd_section_alignment (bsec) - bfd_section_alignment (asec); |
| break; |
| } |
| |
| return ret; |
| } |
| |
| /* Build a Binary Search Tree to sort sections, unlike insertion sort |
| used in wild_sort(). BST is considerably faster if the number of |
| of sections are large. */ |
| |
| static lang_section_bst_type ** |
| wild_sort_fast (lang_wild_statement_type *wild, |
| struct wildcard_list *sec, |
| lang_input_statement_type *file ATTRIBUTE_UNUSED, |
| asection *section) |
| { |
| lang_section_bst_type **tree; |
| |
| tree = &wild->tree; |
| if (!wild->filenames_sorted |
| && (sec == NULL || sec->spec.sorted == none)) |
| { |
| /* Append at the right end of tree. */ |
| while (*tree) |
| tree = &((*tree)->right); |
| return tree; |
| } |
| |
| while (*tree) |
| { |
| /* Find the correct node to append this section. */ |
| if (compare_section (sec->spec.sorted, section, (*tree)->section) < 0) |
| tree = &((*tree)->left); |
| else |
| tree = &((*tree)->right); |
| } |
| |
| return tree; |
| } |
| |
| /* Use wild_sort_fast to build a BST to sort sections. */ |
| |
| static void |
| output_section_callback_fast (lang_wild_statement_type *ptr, |
| struct wildcard_list *sec, |
| asection *section, |
| lang_input_statement_type *file, |
| void *output) |
| { |
| lang_section_bst_type *node; |
| lang_section_bst_type **tree; |
| lang_output_section_statement_type *os; |
| |
| os = (lang_output_section_statement_type *) output; |
| |
| if (unique_section_p (section, os)) |
| return; |
| |
| node = (lang_section_bst_type *) xmalloc (sizeof (lang_section_bst_type)); |
| node->left = 0; |
| node->right = 0; |
| node->section = section; |
| node->pattern = ptr->section_list; |
| |
| tree = wild_sort_fast (ptr, sec, file, section); |
| if (tree != NULL) |
| *tree = node; |
| } |
| |
| /* Convert a sorted sections' BST back to list form. */ |
| |
| static void |
| output_section_callback_tree_to_list (lang_wild_statement_type *ptr, |
| lang_section_bst_type *tree, |
| void *output) |
| { |
| if (tree->left) |
| output_section_callback_tree_to_list (ptr, tree->left, output); |
| |
| lang_add_section (&ptr->children, tree->section, tree->pattern, NULL, |
| (lang_output_section_statement_type *) output); |
| |
| if (tree->right) |
| output_section_callback_tree_to_list (ptr, tree->right, output); |
| |
| free (tree); |
| } |
| |
| /* Specialized, optimized routines for handling different kinds of |
| wildcards */ |
| |
| static void |
| walk_wild_section_specs1_wild0 (lang_wild_statement_type *ptr, |
| lang_input_statement_type *file, |
| callback_t callback, |
| void *data) |
| { |
| /* We can just do a hash lookup for the section with the right name. |
| But if that lookup discovers more than one section with the name |
| (should be rare), we fall back to the general algorithm because |
| we would otherwise have to sort the sections to make sure they |
| get processed in the bfd's order. */ |
| bool multiple_sections_found; |
| struct wildcard_list *sec0 = ptr->handler_data[0]; |
| asection *s0 = find_section (file, sec0, &multiple_sections_found); |
| |
| if (multiple_sections_found) |
| walk_wild_section_general (ptr, file, callback, data); |
| else if (s0) |
| walk_wild_consider_section (ptr, file, s0, sec0, callback, data); |
| } |
| |
| static void |
| walk_wild_section_specs1_wild1 (lang_wild_statement_type *ptr, |
| lang_input_statement_type *file, |
| callback_t callback, |
| void *data) |
| { |
| asection *s; |
| struct wildcard_list *wildsec0 = ptr->handler_data[0]; |
| |
| for (s = file->the_bfd->sections; s != NULL; s = s->next) |
| { |
| const char *sname = bfd_section_name (s); |
| bool skip = !match_simple_wild (wildsec0->spec.name, sname); |
| |
| if (!skip) |
| walk_wild_consider_section (ptr, file, s, wildsec0, callback, data); |
| } |
| } |
| |
| static void |
| walk_wild_section_specs2_wild1 (lang_wild_statement_type *ptr, |
| lang_input_statement_type *file, |
| callback_t callback, |
| void *data) |
| { |
| asection *s; |
| struct wildcard_list *sec0 = ptr->handler_data[0]; |
| struct wildcard_list *wildsec1 = ptr->handler_data[1]; |
| bool multiple_sections_found; |
| asection *s0 = find_section (file, sec0, &multiple_sections_found); |
| |
| if (multiple_sections_found) |
| { |
| walk_wild_section_general (ptr, file, callback, data); |
| return; |
| } |
| |
| /* Note that if the section was not found, s0 is NULL and |
| we'll simply never succeed the s == s0 test below. */ |
| for (s = file->the_bfd->sections; s != NULL; s = s->next) |
| { |
| /* Recall that in this code path, a section cannot satisfy more |
| than one spec, so if s == s0 then it cannot match |
| wildspec1. */ |
| if (s == s0) |
| walk_wild_consider_section (ptr, file, s, sec0, callback, data); |
| else |
| { |
| const char *sname = bfd_section_name (s); |
| bool skip = !match_simple_wild (wildsec1->spec.name, sname); |
| |
| if (!skip) |
| walk_wild_consider_section (ptr, file, s, wildsec1, callback, |
| data); |
| } |
| } |
| } |
| |
| static void |
| walk_wild_section_specs3_wild2 (lang_wild_statement_type *ptr, |
| lang_input_statement_type *file, |
| callback_t callback, |
| void *data) |
| { |
| asection *s; |
| struct wildcard_list *sec0 = ptr->handler_data[0]; |
| struct wildcard_list *wildsec1 = ptr->handler_data[1]; |
| struct wildcard_list *wildsec2 = ptr->handler_data[2]; |
| bool multiple_sections_found; |
| asection *s0 = find_section (file, sec0, &multiple_sections_found); |
| |
| if (multiple_sections_found) |
| { |
| walk_wild_section_general (ptr, file, callback, data); |
| return; |
| } |
| |
| for (s = file->the_bfd->sections; s != NULL; s = s->next) |
| { |
| if (s == s0) |
| walk_wild_consider_section (ptr, file, s, sec0, callback, data); |
| else |
| { |
| const char *sname = bfd_section_name (s); |
| bool skip = !match_simple_wild (wildsec1->spec.name, sname); |
| |
| if (!skip) |
| walk_wild_consider_section (ptr, file, s, wildsec1, callback, data); |
| else |
| { |
| skip = !match_simple_wild (wildsec2->spec.name, sname); |
| if (!skip) |
| walk_wild_consider_section (ptr, file, s, wildsec2, callback, |
| data); |
| } |
| } |
| } |
| } |
| |
| static void |
| walk_wild_section_specs4_wild2 (lang_wild_statement_type *ptr, |
| lang_input_statement_type *file, |
| callback_t callback, |
| void *data) |
| { |
| asection *s; |
| struct wildcard_list *sec0 = ptr->handler_data[0]; |
| struct wildcard_list *sec1 = ptr->handler_data[1]; |
| struct wildcard_list *wildsec2 = ptr->handler_data[2]; |
| struct wildcard_list *wildsec3 = ptr->handler_data[3]; |
| bool multiple_sections_found; |
| asection *s0 = find_section (file, sec0, &multiple_sections_found), *s1; |
| |
| if (multiple_sections_found) |
| { |
| walk_wild_section_general (ptr, file, callback, data); |
| return; |
| } |
| |
| s1 = find_section (file, sec1, &multiple_sections_found); |
| if (multiple_sections_found) |
| { |
| walk_wild_section_general (ptr, file, callback, data); |
| return; |
| } |
| |
| for (s = file->the_bfd->sections; s != NULL; s = s->next) |
| { |
| if (s == s0) |
| walk_wild_consider_section (ptr, file, s, sec0, callback, data); |
| else |
| if (s == s1) |
| walk_wild_consider_section (ptr, file, s, sec1, callback, data); |
| else |
| { |
| const char *sname = bfd_section_name (s); |
| bool skip = !match_simple_wild (wildsec2->spec.name, sname); |
| |
| if (!skip) |
| walk_wild_consider_section (ptr, file, s, wildsec2, callback, |
| data); |
| else |
| { |
| skip = !match_simple_wild (wildsec3->spec.name, sname); |
| if (!skip) |
| walk_wild_consider_section (ptr, file, s, wildsec3, |
| callback, data); |
| } |
| } |
| } |
| } |
| |
| static void |
| walk_wild_section (lang_wild_statement_type *ptr, |
| lang_input_statement_type *file, |
| callback_t callback, |
| void *data) |
| { |
| if (file->flags.just_syms) |
| return; |
| |
| (*ptr->walk_wild_section_handler) (ptr, file, callback, data); |
| } |
| |
| /* Returns TRUE when name1 is a wildcard spec that might match |
| something name2 can match. We're conservative: we return FALSE |
| only if the prefixes of name1 and name2 are different up to the |
| first wildcard character. */ |
| |
| static bool |
| wild_spec_can_overlap (const char *name1, const char *name2) |
| { |
| size_t prefix1_len = strcspn (name1, "?*["); |
| size_t prefix2_len = strcspn (name2, "?*["); |
| size_t min_prefix_len; |
| |
| /* Note that if there is no wildcard character, then we treat the |
| terminating 0 as part of the prefix. Thus ".text" won't match |
| ".text." or ".text.*", for example. */ |
| if (name1[prefix1_len] == '\0') |
| prefix1_len++; |
| if (name2[prefix2_len] == '\0') |
| prefix2_len++; |
| |
| min_prefix_len = prefix1_len < prefix2_len ? prefix1_len : prefix2_len; |
| |
| return memcmp (name1, name2, min_prefix_len) == 0; |
| } |
| |
| /* Select specialized code to handle various kinds of wildcard |
| statements. */ |
| |
| static void |
| analyze_walk_wild_section_handler (lang_wild_statement_type *ptr) |
| { |
| int sec_count = 0; |
| int wild_name_count = 0; |
| struct wildcard_list *sec; |
| int signature; |
| int data_counter; |
| |
| ptr->walk_wild_section_handler = walk_wild_section_general; |
| ptr->handler_data[0] = NULL; |
| ptr->handler_data[1] = NULL; |
| ptr->handler_data[2] = NULL; |
| ptr->handler_data[3] = NULL; |
| ptr->tree = NULL; |
| |
| /* Count how many wildcard_specs there are, and how many of those |
| actually use wildcards in the name. Also, bail out if any of the |
| wildcard names are NULL. (Can this actually happen? |
| walk_wild_section used to test for it.) And bail out if any |
| of the wildcards are more complex than a simple string |
| ending in a single '*'. */ |
| for (sec = ptr->section_list; sec != NULL; sec = sec->next) |
| { |
| ++sec_count; |
| if (sec->spec.name == NULL) |
| return; |
| if (wildcardp (sec->spec.name)) |
| { |
| ++wild_name_count; |
| if (!is_simple_wild (sec->spec.name)) |
| return; |
| } |
| } |
| |
| /* The zero-spec case would be easy to optimize but it doesn't |
| happen in practice. Likewise, more than 4 specs doesn't |
| happen in practice. */ |
| if (sec_count == 0 || sec_count > 4) |
| return; |
| |
| /* Check that no two specs can match the same section. */ |
| for (sec = ptr->section_list; sec != NULL; sec = sec->next) |
| { |
| struct wildcard_list *sec2; |
| for (sec2 = sec->next; sec2 != NULL; sec2 = sec2->next) |
| { |
| if (wild_spec_can_overlap (sec->spec.name, sec2->spec.name)) |
| return; |
| } |
| } |
| |
| signature = (sec_count << 8) + wild_name_count; |
| switch (signature) |
| { |
| case 0x0100: |
| ptr->walk_wild_section_handler = walk_wild_section_specs1_wild0; |
| break; |
| case 0x0101: |
| ptr->walk_wild_section_handler = walk_wild_section_specs1_wild1; |
| break; |
| case 0x0201: |
| ptr->walk_wild_section_handler = walk_wild_section_specs2_wild1; |
| break; |
| case 0x0302: |
| ptr->walk_wild_section_handler = walk_wild_section_specs3_wild2; |
| break; |
| case 0x0402: |
| ptr->walk_wild_section_handler = walk_wild_section_specs4_wild2; |
| break; |
| default: |
| return; |
| } |
| |
| /* Now fill the data array with pointers to the specs, first the |
| specs with non-wildcard names, then the specs with wildcard |
| names. It's OK to process the specs in different order from the |
| given order, because we've already determined that no section |
| will match more than one spec. */ |
| data_counter = 0; |
| for (sec = ptr->section_list; sec != NULL; sec = sec->next) |
| if (!wildcardp (sec->spec.name)) |
| ptr->handler_data[data_counter++] = sec; |
| for (sec = ptr->section_list; sec != NULL; sec = sec->next) |
| if (wildcardp (sec->spec.name)) |
| ptr->handler_data[data_counter++] = sec; |
| } |
| |
| /* Handle a wild statement for a single file F. */ |
| |
| static void |
| walk_wild_file (lang_wild_statement_type *s, |
| lang_input_statement_type *f, |
| callback_t callback, |
| void *data) |
| { |
| if (walk_wild_file_in_exclude_list (s->exclude_name_list, f)) |
| return; |
| |
| if (f->the_bfd == NULL |
| || !bfd_check_format (f->the_bfd, bfd_archive)) |
| walk_wild_section (s, f, callback, data); |
| else |
| { |
| bfd *member; |
| |
| /* This is an archive file. We must map each member of the |
| archive separately. */ |
| member = bfd_openr_next_archived_file (f->the_bfd, NULL); |
| while (member != NULL) |
| { |
| /* When lookup_name is called, it will call the add_symbols |
| entry point for the archive. For each element of the |
| archive which is included, BFD will call ldlang_add_file, |
| which will set the usrdata field of the member to the |
| lang_input_statement. */ |
| if (bfd_usrdata (member) != NULL) |
| walk_wild_section (s, bfd_usrdata (member), callback, data); |
| |
| member = bfd_openr_next_archived_file (f->the_bfd, member); |
| } |
| } |
| } |
| |
| static void |
| walk_wild (lang_wild_statement_type *s, callback_t callback, void *data) |
| { |
| const char *file_spec = s->filename; |
| char *p; |
| |
| if (file_spec == NULL) |
| { |
| /* Perform the iteration over all files in the list. */ |
| LANG_FOR_EACH_INPUT_STATEMENT (f) |
| { |
| walk_wild_file (s, f, callback, data); |
| } |
| } |
| else if ((p = archive_path (file_spec)) != NULL) |
| { |
| LANG_FOR_EACH_INPUT_STATEMENT (f) |
| { |
| if (input_statement_is_archive_path (file_spec, p, f)) |
| walk_wild_file (s, f, callback, data); |
| } |
| } |
| else if (wildcardp (file_spec)) |
| { |
| LANG_FOR_EACH_INPUT_STATEMENT (f) |
| { |
| if (fnmatch (file_spec, f->filename, 0) == 0) |
| walk_wild_file (s, f, callback, data); |
| } |
| } |
| else |
| { |
| lang_input_statement_type *f; |
| |
| /* Perform the iteration over a single file. */ |
| f = lookup_name (file_spec); |
| if (f) |
| walk_wild_file (s, f, callback, data); |
| } |
| } |
| |
| /* lang_for_each_statement walks the parse tree and calls the provided |
| function for each node, except those inside output section statements |
| with constraint set to -1. */ |
| |
| void |
| lang_for_each_statement_worker (void (*func) (lang_statement_union_type *), |
| lang_statement_union_type *s) |
| { |
| for (; s != NULL; s = s->header.next) |
| { |
| func (s); |
| |
| switch (s->header.type) |
| { |
| case lang_constructors_statement_enum: |
| lang_for_each_statement_worker (func, constructor_list.head); |
| break; |
| case lang_output_section_statement_enum: |
| if (s->output_section_statement.constraint != -1) |
| lang_for_each_statement_worker |
| (func, s->output_section_statement.children.head); |
| break; |
| case lang_wild_statement_enum: |
| lang_for_each_statement_worker (func, |
| s->wild_statement.children.head); |
| break; |
| case lang_group_statement_enum: |
| lang_for_each_statement_worker (func, |
| s->group_statement.children.head); |
| break; |
| case lang_data_statement_enum: |
| case lang_reloc_statement_enum: |
| case lang_object_symbols_statement_enum: |
| case lang_output_statement_enum: |
| case lang_target_statement_enum: |
| case lang_input_section_enum: |
| case lang_input_statement_enum: |
| case lang_assignment_statement_enum: |
| case lang_padding_statement_enum: |
| case lang_address_statement_enum: |
| case lang_fill_statement_enum: |
| case lang_insert_statement_enum: |
| break; |
| default: |
| FAIL (); |
| break; |
| } |
| } |
| } |
| |
| void |
| lang_for_each_statement (void (*func) (lang_statement_union_type *)) |
| { |
| lang_for_each_statement_worker (func, statement_list.head); |
| } |
| |
| /*----------------------------------------------------------------------*/ |
| |
| void |
| lang_list_init (lang_statement_list_type *list) |
| { |
| list->head = NULL; |
| list->tail = &list->head; |
| } |
| |
| static void |
| lang_statement_append (lang_statement_list_type *list, |
| void *element, |
| void *field) |
| { |
| *(list->tail) = element; |
| list->tail = field; |
| } |
| |
| void |
| push_stat_ptr (lang_statement_list_type *new_ptr) |
| { |
| if (stat_save_ptr >= stat_save + sizeof (stat_save) / sizeof (stat_save[0])) |
| abort (); |
| *stat_save_ptr++ = stat_ptr; |
| stat_ptr = new_ptr; |
| } |
| |
| void |
| pop_stat_ptr (void) |
| { |
| if (stat_save_ptr <= stat_save) |
| abort (); |
| stat_ptr = *--stat_save_ptr; |
| } |
| |
| /* Build a new statement node for the parse tree. */ |
| |
| static lang_statement_union_type * |
| new_statement (enum statement_enum type, |
| size_t size, |
| lang_statement_list_type *list) |
| { |
| lang_statement_union_type *new_stmt; |
| |
| new_stmt = stat_alloc (size); |
| new_stmt->header.type = type; |
| new_stmt->header.next = NULL; |
| lang_statement_append (list, new_stmt, &new_stmt->header.next); |
| return new_stmt; |
| } |
| |
| /* Build a new input file node for the language. There are several |
| ways in which we treat an input file, eg, we only look at symbols, |
| or prefix it with a -l etc. |
| |
| We can be supplied with requests for input files more than once; |
| they may, for example be split over several lines like foo.o(.text) |
| foo.o(.data) etc, so when asked for a file we check that we haven't |
| got it already so we don't duplicate the bfd. */ |
| |
| static lang_input_statement_type * |
| new_afile (const char *name, |
| lang_input_file_enum_type file_type, |
| const char *target, |
| const char *from_filename) |
| { |
| lang_input_statement_type *p; |
| |
| lang_has_input_file = true; |
| |
| p = new_stat (lang_input_statement, stat_ptr); |
| memset (&p->the_bfd, 0, |
| sizeof (*p) - offsetof (lang_input_statement_type, the_bfd)); |
| p->extra_search_path = NULL; |
| p->target = target; |
| p->flags.dynamic = input_flags.dynamic; |
| p->flags.add_DT_NEEDED_for_dynamic = input_flags.add_DT_NEEDED_for_dynamic; |
| p->flags.add_DT_NEEDED_for_regular = input_flags.add_DT_NEEDED_for_regular; |
| p->flags.whole_archive = input_flags.whole_archive; |
| p->flags.sysrooted = input_flags.sysrooted; |
| |
| switch (file_type) |
| { |
| case lang_input_file_is_symbols_only_enum: |
| p->filename = name; |
| p->local_sym_name = name; |
| p->flags.real = true; |
| p->flags.just_syms = true; |
| break; |
| case lang_input_file_is_fake_enum: |
| p->filename = name; |
| p->local_sym_name = name; |
| break; |
| case lang_input_file_is_l_enum: |
| if (name[0] == ':' && name[1] != '\0') |
| { |
| p->filename = name + 1; |
| p->flags.full_name_provided = true; |
| } |
| else |
| p->filename = name; |
| p->local_sym_name = concat ("-l", name, (const char *) NULL); |
| p->flags.maybe_archive = true; |
| p->flags.real = true; |
| p->flags.search_dirs = true; |
| break; |
| case lang_input_file_is_marker_enum: |
| p->filename = name; |
| p->local_sym_name = name; |
| p->flags.search_dirs = true; |
| break; |
| case lang_input_file_is_search_file_enum: |
| p->filename = name; |
| p->local_sym_name = name; |
| /* If name is a relative path, search the directory of the current linker |
| script first. */ |
| if (from_filename && !IS_ABSOLUTE_PATH (name)) |
| p->extra_search_path = ldirname (from_filename); |
| p->flags.real = true; |
| p->flags.search_dirs = true; |
| break; |
| case lang_input_file_is_file_enum: |
| p->filename = name; |
| p->local_sym_name = name; |
| p->flags.real = true; |
| break; |
| default: |
| FAIL (); |
| } |
| |
| lang_statement_append (&input_file_chain, p, &p->next_real_file); |
| return p; |
| } |
| |
| lang_input_statement_type * |
| lang_add_input_file (const char *name, |
| lang_input_file_enum_type file_type, |
| const char *target) |
| { |
| if (name != NULL |
| && (*name == '=' || startswith (name, "$SYSROOT"))) |
| { |
| lang_input_statement_type *ret; |
| char *sysrooted_name |
| = concat (ld_sysroot, |
| name + (*name == '=' ? 1 : strlen ("$SYSROOT")), |
| (const char *) NULL); |
| |
| /* We've now forcibly prepended the sysroot, making the input |
| file independent of the context. Therefore, temporarily |
| force a non-sysrooted context for this statement, so it won't |
| get the sysroot prepended again when opened. (N.B. if it's a |
| script, any child nodes with input files starting with "/" |
| will be handled as "sysrooted" as they'll be found to be |
| within the sysroot subdirectory.) */ |
| unsigned int outer_sysrooted = input_flags.sysrooted; |
| input_flags.sysrooted = 0; |
| ret = new_afile (sysrooted_name, file_type, target, NULL); |
| input_flags.sysrooted = outer_sysrooted; |
| return ret; |
| } |
| |
| return new_afile (name, file_type, target, current_input_file); |
| } |
| |
| struct out_section_hash_entry |
| { |
| struct bfd_hash_entry root; |
| lang_statement_union_type s; |
| }; |
| |
| /* The hash table. */ |
| |
| static struct bfd_hash_table output_section_statement_table; |
| |
| /* Support routines for the hash table used by lang_output_section_find, |
| initialize the table, fill in an entry and remove the table. */ |
| |
| static struct bfd_hash_entry * |
| output_section_statement_newfunc (struct bfd_hash_entry *entry, |
| struct bfd_hash_table *table, |
| const char *string) |
| { |
| lang_output_section_statement_type **nextp; |
| struct out_section_hash_entry *ret; |
| |
| if (entry == NULL) |
| { |
| entry = (struct bfd_hash_entry *) bfd_hash_allocate (table, |
| sizeof (*ret)); |
| if (entry == NULL) |
| return entry; |
| } |
| |
| entry = bfd_hash_newfunc (entry, table, string); |
| if (entry == NULL) |
| return entry; |
| |
| ret = (struct out_section_hash_entry *) entry; |
| memset (&ret->s, 0, sizeof (ret->s)); |
| ret->s.header.type = lang_output_section_statement_enum; |
| ret->s.output_section_statement.subsection_alignment = NULL; |
| ret->s.output_section_statement.section_alignment = NULL; |
| ret->s.output_section_statement.block_value = 1; |
| lang_list_init (&ret->s.output_section_statement.children); |
| lang_statement_append (stat_ptr, &ret->s, &ret->s.header.next); |
| |
| /* For every output section statement added to the list, except the |
| first one, lang_os_list.tail points to the "next" |
| field of the last element of the list. */ |
| if (lang_os_list.head != NULL) |
| ret->s.output_section_statement.prev |
| = ((lang_output_section_statement_type *) |
| ((char *) lang_os_list.tail |
| - offsetof (lang_output_section_statement_type, next))); |
| |
| /* GCC's strict aliasing rules prevent us from just casting the |
| address, so we store the pointer in a variable and cast that |
| instead. */ |
| nextp = &ret->s.output_section_statement.next; |
| lang_statement_append (&lang_os_list, &ret->s, nextp); |
| return &ret->root; |
| } |
| |
| static void |
| output_section_statement_table_init (void) |
| { |
| if (!bfd_hash_table_init_n (&output_section_statement_table, |
| output_section_statement_newfunc, |
| sizeof (struct out_section_hash_entry), |
| 61)) |
| einfo (_("%F%P: can not create hash table: %E\n")); |
| } |
| |
| static void |
| output_section_statement_table_free (void) |
| { |
| bfd_hash_table_free (&output_section_statement_table); |
| } |
| |
| /* Build enough state so that the parser can build its tree. */ |
| |
| void |
| lang_init (void) |
| { |
| obstack_begin (&stat_obstack, 1000); |
| |
| stat_ptr = &statement_list; |
| |
| output_section_statement_table_init (); |
| |
| lang_list_init (stat_ptr); |
| |
| lang_list_init (&input_file_chain); |
| lang_list_init (&lang_os_list); |
| lang_list_init (&file_chain); |
| first_file = lang_add_input_file (NULL, lang_input_file_is_marker_enum, |
| NULL); |
| abs_output_section = |
| lang_output_section_statement_lookup (BFD_ABS_SECTION_NAME, 0, 1); |
| |
| abs_output_section->bfd_section = bfd_abs_section_ptr; |
| |
| asneeded_list_head = NULL; |
| asneeded_list_tail = &asneeded_list_head; |
| } |
| |
| void |
| lang_finish (void) |
| { |
| output_section_statement_table_free (); |
| } |
| |
| /*---------------------------------------------------------------------- |
| A region is an area of memory declared with the |
| MEMORY { name:org=exp, len=exp ... } |
| syntax. |
| |
| We maintain a list of all the regions here. |
| |
| If no regions are specified in the script, then the default is used |
| which is created when looked up to be the entire data space. |
| |
| If create is true we are creating a region inside a MEMORY block. |
| In this case it is probably an error to create a region that has |
| already been created. If we are not inside a MEMORY block it is |
| dubious to use an undeclared region name (except DEFAULT_MEMORY_REGION) |
| and so we issue a warning. |
| |
| Each region has at least one name. The first name is either |
| DEFAULT_MEMORY_REGION or the name given in the MEMORY block. You can add |
| alias names to an existing region within a script with |
| REGION_ALIAS (alias, region_name). Each name corresponds to at most one |
| region. */ |
| |
| static lang_memory_region_type *lang_memory_region_list; |
| static lang_memory_region_type **lang_memory_region_list_tail |
| = &lang_memory_region_list; |
| |
| lang_memory_region_type * |
| lang_memory_region_lookup (const char *const name, bool create) |
| { |
| lang_memory_region_name *n; |
| lang_memory_region_type *r; |
| lang_memory_region_type *new_region; |
| |
| /* NAME is NULL for LMA memspecs if no region was specified. */ |
| if (name == NULL) |
| return NULL; |
| |
| for (r = lang_memory_region_list; r != NULL; r = r->next) |
| for (n = &r->name_list; n != NULL; n = n->next) |
| if (strcmp (n->name, name) == 0) |
| { |
| if (create) |
| einfo (_("%P:%pS: warning: redeclaration of memory region `%s'\n"), |
| NULL, name); |
| return r; |
| } |
| |
| if (!create && strcmp (name, DEFAULT_MEMORY_REGION)) |
| einfo (_("%P:%pS: warning: memory region `%s' not declared\n"), |
| NULL, name); |
| |
| new_region = stat_alloc (sizeof (lang_memory_region_type)); |
| |
| new_region->name_list.name = xstrdup (name); |
| new_region->name_list.next = NULL; |
| new_region->next = NULL; |
| new_region->origin_exp = NULL; |
| new_region->origin = 0; |
| new_region->length_exp = NULL; |
| new_region->length = ~(bfd_size_type) 0; |
| new_region->current = 0; |
| new_region->last_os = NULL; |
| new_region->flags = 0; |
| new_region->not_flags = 0; |
| new_region->had_full_message = false; |
| |
| *lang_memory_region_list_tail = new_region; |
| lang_memory_region_list_tail = &new_region->next; |
| |
| return new_region; |
| } |
| |
| void |
| lang_memory_region_alias (const char *alias, const char *region_name) |
| { |
| lang_memory_region_name *n; |
| lang_memory_region_type *r; |
| lang_memory_region_type *region; |
| |
| /* The default region must be unique. This ensures that it is not necessary |
| to iterate through the name list if someone wants the check if a region is |
| the default memory region. */ |
| if (strcmp (region_name, DEFAULT_MEMORY_REGION) == 0 |
| || strcmp (alias, DEFAULT_MEMORY_REGION) == 0) |
| einfo (_("%F%P:%pS: error: alias for default memory region\n"), NULL); |
| |
| /* Look for the target region and check if the alias is not already |
| in use. */ |
| region = NULL; |
| for (r = lang_memory_region_list; r != NULL; r = r->next) |
| for (n = &r->name_list; n != NULL; n = n->next) |
| { |
| if (region == NULL && strcmp (n->name, region_name) == 0) |
| region = r; |
| if (strcmp (n->name, alias) == 0) |
| einfo (_("%F%P:%pS: error: redefinition of memory region " |
| "alias `%s'\n"), |
| NULL, alias); |
| } |
| |
| /* Check if the target region exists. */ |
| if (region == NULL) |
| einfo (_("%F%P:%pS: error: memory region `%s' " |
| "for alias `%s' does not exist\n"), |
| NULL, region_name, alias); |
| |
| /* Add alias to region name list. */ |
| n = stat_alloc (sizeof (lang_memory_region_name)); |
| n->name = xstrdup (alias); |
| n->next = region->name_list.next; |
| region->name_list.next = n; |
| } |
| |
| static lang_memory_region_type * |
| lang_memory_default (asection *section) |
| { |
| lang_memory_region_type *p; |
| |
| flagword sec_flags = section->flags; |
| |
| /* Override SEC_DATA to mean a writable section. */ |
| if ((sec_flags & (SEC_ALLOC | SEC_READONLY | SEC_CODE)) == SEC_ALLOC) |
| sec_flags |= SEC_DATA; |
| |
| for (p = lang_memory_region_list; p != NULL; p = p->next) |
| { |
| if ((p->flags & sec_flags) != 0 |
| && (p->not_flags & sec_flags) == 0) |
| { |
| return p; |
| } |
| } |
| return lang_memory_region_lookup (DEFAULT_MEMORY_REGION, false); |
| } |
| |
| /* Get the output section statement directly from the userdata. */ |
| |
| lang_output_section_statement_type * |
| lang_output_section_get (const asection *output_section) |
| { |
| return bfd_section_userdata (output_section); |
| } |
| |
| /* Find or create an output_section_statement with the given NAME. |
| If CONSTRAINT is non-zero match one with that constraint, otherwise |
| match any non-negative constraint. If CREATE is 0 return NULL when |
| no match exists. If CREATE is 1, create an output_section_statement |
| when no match exists or if CONSTRAINT is SPECIAL. If CREATE is 2, |
| always make a new output_section_statement. */ |
| |
| lang_output_section_statement_type * |
| lang_output_section_statement_lookup (const char *name, |
| int constraint, |
| int create) |
| { |
| struct out_section_hash_entry *entry; |
| |
| entry = ((struct out_section_hash_entry *) |
| bfd_hash_lookup (&output_section_statement_table, name, |
| create != 0, false)); |
| if (entry == NULL) |
| { |
| if (create) |
| einfo (_("%F%P: failed creating section `%s': %E\n"), name); |
| return NULL; |
| } |
| |
| if (entry->s.output_section_statement.name != NULL) |
| { |
| /* We have a section of this name, but it might not have the correct |
| constraint. */ |
| struct out_section_hash_entry *last_ent; |
| |
| name = entry->s.output_section_statement.name; |
| do |
| { |
| if (create != 2 |
| && !(create && constraint == SPECIAL) |
| && (constraint == entry->s.output_section_statement.constraint |
| || (constraint == 0 |
| && entry->s.output_section_statement.constraint >= 0))) |
| return &entry->s.output_section_statement; |
| last_ent = entry; |
| entry = (struct out_section_hash_entry *) entry->root.next; |
| } |
| while (entry != NULL |
| && name == entry->s.output_section_statement.name); |
| |
| if (!create) |
| return NULL; |
| |
| entry |
| = ((struct out_section_hash_entry *) |
| output_section_statement_newfunc (NULL, |
| &output_section_statement_table, |
| name)); |
| if (entry == NULL) |
| { |
| einfo (_("%F%P: failed creating section `%s': %E\n"), name); |
| return NULL; |
| } |
| entry->root = last_ent->root; |
| last_ent->root.next = &entry->root; |
| } |
| |
| entry->s.output_section_statement.name = name; |
| entry->s.output_section_statement.constraint = constraint; |
| entry->s.output_section_statement.dup_output = (create == 2 |
| || constraint == SPECIAL); |
| return &entry->s.output_section_statement; |
| } |
| |
| /* Find the next output_section_statement with the same name as OS. |
| If CONSTRAINT is non-zero, find one with that constraint otherwise |
| match any non-negative constraint. */ |
| |
| lang_output_section_statement_type * |
| next_matching_output_section_statement (lang_output_section_statement_type *os, |
| int constraint) |
| { |
| /* All output_section_statements are actually part of a |
| struct out_section_hash_entry. */ |
| struct out_section_hash_entry *entry = (struct out_section_hash_entry *) |
| ((char *) os |
| - offsetof (struct out_section_hash_entry, s.output_section_statement)); |
| const char *name = os->name; |
| |
| ASSERT (name == entry->root.string); |
| do |
| { |
| entry = (struct out_section_hash_entry *) entry->root.next; |
| if (entry == NULL |
| || name != entry->s.output_section_statement.name) |
| return NULL; |
| } |
| while (constraint != entry->s.output_section_statement.constraint |
| && (constraint != 0 |
| || entry->s.output_section_statement.constraint < 0)); |
| |
| return &entry->s.output_section_statement; |
| } |
| |
| /* A variant of lang_output_section_find used by place_orphan. |
| Returns the output statement that should precede a new output |
| statement for SEC. If an exact match is found on certain flags, |
| sets *EXACT too. */ |
| |
| lang_output_section_statement_type * |
| lang_output_section_find_by_flags (const asection *sec, |
| flagword sec_flags, |
| lang_output_section_statement_type **exact, |
| lang_match_sec_type_func match_type) |
| { |
| lang_output_section_statement_type *first, *look, *found; |
| flagword look_flags, differ; |
| |
| /* We know the first statement on this list is *ABS*. May as well |
| skip it. */ |
| first = (void *) lang_os_list.head; |
| first = first->next; |
| |
| /* First try for an exact match. */ |
| found = NULL; |
| for (look = first; look; look = look->next) |
| { |
| look_flags = look->flags; |
| if (look->bfd_section != NULL) |
| { |
| look_flags = look->bfd_section->flags; |
| if (match_type && !match_type (link_info.output_bfd, |
| look->bfd_section, |
| sec->owner, sec)) |
| continue; |
| } |
| differ = look_flags ^ sec_flags; |
| if (!(differ & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY |
| | SEC_CODE | SEC_SMALL_DATA | SEC_THREAD_LOCAL))) |
| found = look; |
| } |
| if (found != NULL) |
| { |
| if (exact != NULL) |
| *exact = found; |
| return found; |
| } |
| |
| if ((sec_flags & SEC_CODE) != 0 |
| && (sec_flags & SEC_ALLOC) != 0) |
| { |
| /* Try for a rw code section. */ |
| for (look = first; look; look = look->next) |
| { |
| look_flags = look->flags; |
| if (look->bfd_section != NULL) |
| { |
| look_flags = look->bfd_section->flags; |
| if (match_type && !match_type (link_info.output_bfd, |
| look->bfd_section, |
| sec->owner, sec)) |
| continue; |
| } |
| differ = look_flags ^ sec_flags; |
| if (!(differ & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD |
| | SEC_CODE | SEC_SMALL_DATA | SEC_THREAD_LOCAL))) |
| found = look; |
| } |
| } |
| else if ((sec_flags & SEC_READONLY) != 0 |
| && (sec_flags & SEC_ALLOC) != 0) |
| { |
| /* .rodata can go after .text, .sdata2 after .rodata. */ |
| for (look = first; look; look = look->next) |
| { |
| look_flags = look->flags; |
| if (look->bfd_section != NULL) |
| { |
| look_flags = look->bfd_section->flags; |
| if (match_type && !match_type (link_info.output_bfd, |
| look->bfd_section, |
| sec->owner, sec)) |
| continue; |
| } |
| differ = look_flags ^ sec_flags; |
| if (!(differ & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD |
| | SEC_READONLY | SEC_SMALL_DATA)) |
| || (!(differ & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD |
| | SEC_READONLY)) |
| && !(look_flags & SEC_SMALL_DATA))) |
| found = look; |
| } |
| } |
| else if ((sec_flags & SEC_THREAD_LOCAL) != 0 |
| && (sec_flags & SEC_ALLOC) != 0) |
| { |
| /* .tdata can go after .data, .tbss after .tdata. Treat .tbss |
| as if it were a loaded section, and don't use match_type. */ |
| bool seen_thread_local = false; |
| |
| match_type = NULL; |
| for (look = first; look; look = look->next) |
| { |
| look_flags = look->flags; |
| if (look->bfd_section != NULL) |
| look_flags = look->bfd_section->flags; |
| |
| differ = look_flags ^ (sec_flags | SEC_LOAD | SEC_HAS_CONTENTS); |
| if (!(differ & (SEC_THREAD_LOCAL | SEC_ALLOC))) |
| { |
| /* .tdata and .tbss must be adjacent and in that order. */ |
| if (!(look_flags & SEC_LOAD) |
| && (sec_flags & SEC_LOAD)) |
| /* ..so if we're at a .tbss section and we're placing |
| a .tdata section stop looking and return the |
| previous section. */ |
| break; |
| found = look; |
| seen_thread_local = true; |
| } |
| else if (seen_thread_local) |
| break; |
| else if (!(differ & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD))) |
| found = look; |
| } |
| } |
| else if ((sec_flags & SEC_SMALL_DATA) != 0 |
| && (sec_flags & SEC_ALLOC) != 0) |
| { |
| /* .sdata goes after .data, .sbss after .sdata. */ |
| for (look = first; look; look = look->next) |
| { |
| look_flags = look->flags; |
| if (look->bfd_section != NULL) |
| { |
| look_flags = look->bfd_section->flags; |
| if (match_type && !match_type (link_info.output_bfd, |
| look->bfd_section, |
| sec->owner, sec)) |
| continue; |
| } |
| differ = look_flags ^ sec_flags; |
| if (!(differ & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD |
| | SEC_THREAD_LOCAL)) |
| || ((look_flags & SEC_SMALL_DATA) |
| && !(sec_flags & SEC_HAS_CONTENTS))) |
| found = look; |
| } |
| } |
| else if ((sec_flags & SEC_HAS_CONTENTS) != 0 |
| && (sec_flags & SEC_ALLOC) != 0) |
| { |
| /* .data goes after .rodata. */ |
| for (look = first; look; look = look->next) |
| { |
| look_flags = look->flags; |
| if (look->bfd_section != NULL) |
| { |
| look_flags = look->bfd_section->flags; |
| if (match_type && !match_type (link_info.output_bfd, |
| look->bfd_section, |
| sec->owner, sec)) |
| continue; |
| } |
| differ = look_flags ^ sec_flags; |
| if (!(differ & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD |
| | SEC_SMALL_DATA | SEC_THREAD_LOCAL))) |
| found = look; |
| } |
| } |
| else if ((sec_flags & SEC_ALLOC) != 0) |
| { |
| /* .bss goes after any other alloc section. */ |
| for (look = first; look; look = look->next) |
| { |
| look_flags = look->flags; |
| if (look->bfd_section != NULL) |
| { |
| look_flags = look->bfd_section->flags; |
| if (match_type && !match_type (link_info.output_bfd, |
| look->bfd_section, |
| sec->owner, sec)) |
| continue; |
| } |
| differ = look_flags ^ sec_flags; |
| if (!(differ & SEC_ALLOC)) |
| found = look; |
| } |
| } |
| else |
| { |
| /* non-alloc go last. */ |
| for (look = first; look; look = look->next) |
| { |
| look_flags = look->flags; |
| if (look->bfd_section != NULL) |
| look_flags = look->bfd_section->flags; |
| differ = look_flags ^ sec_flags; |
| if (!(differ & SEC_DEBUGGING)) |
| found = look; |
| } |
| return found; |
| } |
| |
| if (found || !match_type) |
| return found; |
| |
| return lang_output_section_find_by_flags (sec, sec_flags, NULL, NULL); |
| } |
| |
| /* Find the last output section before given output statement. |
| Used by place_orphan. */ |
| |
| static asection * |
| output_prev_sec_find (lang_output_section_statement_type *os) |
| { |
| lang_output_section_statement_type *lookup; |
| |
| for (lookup = os->prev; lookup != NULL; lookup = lookup->prev) |
| { |
| if (lookup->constraint < 0) |
| continue; |
| |
| if (lookup->bfd_section != NULL && lookup->bfd_section->owner != NULL) |
| return lookup->bfd_section; |
| } |
| |
| 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, except in two cases; The first |
| assignment to dot, and assignments before non-alloc sections. |
| Otherwise we might put an orphan before . = . + SIZEOF_HEADERS or |
| similar assignments that set the initial address, or we might |
| insert non-alloc note sections among assignments setting end of |
| image symbols. */ |
| |
| 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; |
| bool ignore_first; |
| |
| ignore_first = after == (void *) lang_os_list.head; |
| |
| 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) |
| { |
| if (!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; |
| ignore_first = false; |
| continue; |
| case lang_output_section_statement_enum: |
| if (assign != NULL) |
| { |
| asection *s = (*where)->output_section_statement.bfd_section; |
| |
| if (s == NULL |
| || s->map_head.s == NULL |
| || (s->flags & SEC_ALLOC) != 0) |
| 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, |
| int constraint, |
| lang_output_section_statement_type *after, |
| struct orphan_save *place, |
| etree_type *address, |
| lang_statement_list_type *add_child) |
| { |
| lang_statement_list_type add; |
| lang_output_section_statement_type *os; |
| lang_output_section_statement_type **os_tail; |
| |
| /* If we have found an appropriate place for the output section |
| statements for this orphan, add them to our own private list, |
| inserting them later into the global statement list. */ |
| if (after != NULL) |
| { |
| lang_list_init (&add); |
| push_stat_ptr (&add); |
| } |
| |
| if (bfd_link_relocatable (&link_info) |
| || (s->flags & (SEC_LOAD | SEC_ALLOC)) == 0) |
| address = exp_intop (0); |
| |
| os_tail = (lang_output_section_statement_type **) lang_os_list.tail; |
| os = lang_enter_output_section_statement (secname, address, normal_section, |
| NULL, NULL, NULL, constraint, 0); |
| |
| if (add_child == NULL) |
| add_child = &os->children; |
| lang_add_section (add_child, s, NULL, NULL, os); |
| |
| if (after && (s->flags & (SEC_LOAD | SEC_ALLOC)) != 0) |
| { |
| const char *region = (after->region |
| ? after->region->name_list.name |
| : DEFAULT_MEMORY_REGION); |
| const char *lma_region = (after->lma_region |
| ? after->lma_region->name_list.name |
| : NULL); |
| lang_leave_output_section_statement (NULL, region, after->phdrs, |
| lma_region); |
| } |
| else |
| lang_leave_output_section_statement (NULL, DEFAULT_MEMORY_REGION, NULL, |
| NULL); |
| |
| /* Restore the global list pointer. */ |
| if (after != NULL) |
| pop_stat_ptr (); |
| |
| if (after != NULL && os->bfd_section != NULL) |
| { |
| asection *snew, *as; |
| bool place_after = place->stmt == NULL; |
| bool insert_after = true; |
| |
| snew = os->bfd_section; |
| |
| /* Shuffle the bfd section list to make the output file look |
| neater. This is really only cosmetic. */ |
| if (place->section == NULL |
| && after != (void *) lang_os_list.head) |
| { |
| asection *bfd_section = after->bfd_section; |
| |
| /* If the output statement hasn't been used to place any input |
| sections (and thus doesn't have an output bfd_section), |
| look for the closest prior output statement having an |
| output section. */ |
| if (bfd_section == NULL) |
| bfd_section = output_prev_sec_find (after); |
| |
| if (bfd_section != NULL && bfd_section != snew) |
| place->section = &bfd_section->next; |
| } |
| |
| if (place->section == NULL) |
| place->section = &link_info.output_bfd->sections; |
| |
| as = *place->section; |
| |
| if (!as) |
| { |
| /* Put the section at the end of the list. */ |
| |
| /* Unlink the section. */ |
| bfd_section_list_remove (link_info.output_bfd, snew); |
| |
| /* Now tack it back on in the right place. */ |
| bfd_section_list_append (link_info.output_bfd, snew); |
| } |
| else if ((bfd_get_flavour (link_info.output_bfd) |
| == bfd_target_elf_flavour) |
| && (bfd_get_flavour (s->owner) |
| == bfd_target_elf_flavour) |
| && ((elf_section_type (s) == SHT_NOTE |
| && (s->flags & SEC_LOAD) != 0) |
| || (elf_section_type (as) == SHT_NOTE |
| && (as->flags & SEC_LOAD) != 0))) |
| { |
| /* Make sure that output note sections are grouped and sorted |
| by alignments when inserting a note section or insert a |
| section after a note section, */ |
| asection *sec; |
| /* A specific section after which the output note section |
| should be placed. */ |
| asection *after_sec; |
| /* True if we need to insert the orphan section after a |
| specific section to maintain output note section order. */ |
| bool after_sec_note = false; |
| |
| static asection *first_orphan_note = NULL; |
| |
| /* Group and sort output note section by alignments in |
| ascending order. */ |
| after_sec = NULL; |
| if (elf_section_type (s) == SHT_NOTE |
| && (s->flags & SEC_LOAD) != 0) |
| { |
| /* Search from the beginning for the last output note |
| section with equal or larger alignments. NB: Don't |
| place orphan note section after non-note sections. */ |
| |
| first_orphan_note = NULL; |
| for (sec = link_info.output_bfd->sections; |
| (sec != NULL |
| && !bfd_is_abs_section (sec)); |
| sec = sec->next) |
| if (sec != snew |
| && elf_section_type (sec) == SHT_NOTE |
| && (sec->flags & SEC_LOAD) != 0) |
| { |
| if (!first_orphan_note) |
| first_orphan_note = sec; |
| if (sec->alignment_power >= s->alignment_power) |
| after_sec = sec; |
| } |
| else if (first_orphan_note) |
| { |
| /* Stop if there is non-note section after the first |
| orphan note section. */ |
| break; |
| } |
| |
| /* If this will be the first orphan note section, it can |
| be placed at the default location. */ |
| after_sec_note = first_orphan_note != NULL; |
| if (after_sec == NULL && after_sec_note) |
| { |
| /* If all output note sections have smaller |
| alignments, place the section before all |
| output orphan note sections. */ |
| after_sec = first_orphan_note; |
| insert_after = false; |
| } |
| } |
| else if (first_orphan_note) |
| { |
| /* Don't place non-note sections in the middle of orphan |
| note sections. */ |
| after_sec_note = true; |
| after_sec = as; |
| for (sec = as->next; |
| (sec != NULL |
| && !bfd_is_abs_section (sec)); |
| sec = sec->next) |
| if (elf_section_type (sec) == SHT_NOTE |
| && (sec->flags & SEC_LOAD) != 0) |
| after_sec = sec; |
| } |
| |
| if (after_sec_note) |
| { |
| if (after_sec) |
| { |
| /* Search forward to insert OS after AFTER_SEC output |
| statement. */ |
| lang_output_section_statement_type *stmt, *next; |
| bool found = false; |
| for (stmt = after; stmt != NULL; stmt = next) |
| { |
| next = stmt->next; |
| if (insert_after) |
| { |
| if (stmt->bfd_section == after_sec) |
| { |
| place_after = true; |
| found = true; |
| after = stmt; |
| break; |
| } |
| } |
| else |
| { |
| /* If INSERT_AFTER is FALSE, place OS before |
| AFTER_SEC output statement. */ |
| if (next && next->bfd_section == after_sec) |
| { |
| place_after = true; |
| found = true; |
| after = stmt; |
| break; |
| } |
| } |
| } |
| |
| /* Search backward to insert OS after AFTER_SEC output |
| statement. */ |
| if (!found) |
| for (stmt = after; stmt != NULL; stmt = stmt->prev) |
| { |
| if (insert_after) |
| { |
| if (stmt->bfd_section == after_sec) |
| { |
| place_after = true; |
| after = stmt; |
| break; |
| } |
| } |
| else |
| { |
| /* If INSERT_AFTER is FALSE, place OS before |
| AFTER_SEC output statement. */ |
| if (stmt->next->bfd_section == after_sec) |
| { |
| place_after = true; |
| after = stmt; |
| break; |
| } |
| } |
| } |
| } |
| |
| if (after_sec == NULL |
| || (insert_after && after_sec->next != snew) |
| || (!insert_after && after_sec->prev != snew)) |
| { |
| /* Unlink the section. */ |
| bfd_section_list_remove (link_info.output_bfd, snew); |
| |
| /* Place SNEW after AFTER_SEC. If AFTER_SEC is NULL, |
| prepend SNEW. */ |
| if (after_sec) |
| { |
| if (insert_after) |
| bfd_section_list_insert_after (link_info.output_bfd, |
| after_sec, snew); |
| else |
| bfd_section_list_insert_before (link_info.output_bfd, |
| after_sec, snew); |
| } |
| else |
| bfd_section_list_prepend (link_info.output_bfd, snew); |
| } |
| } |
| else if (as != snew && as->prev != snew) |
| { |
| /* Unlink the section. */ |
| bfd_section_list_remove (link_info.output_bfd, snew); |
| |
| /* Now tack it back on in the right place. */ |
| bfd_section_list_insert_before (link_info.output_bfd, |
| as, snew); |
| } |
| } |
| else if (as != snew && as->prev != snew) |
| { |
| /* Unlink the section. */ |
| bfd_section_list_remove (link_info.output_bfd, snew); |
| |
| /* Now tack it back on in the right place. */ |
| bfd_section_list_insert_before (link_info.output_bfd, as, snew); |
| } |
| |
| /* Save the end of this list. Further ophans of this type will |
| follow the one we've just added. */ |
| place->section = &snew->next; |
| |
| /* The following is non-cosmetic. We try to put the output |
| statements in some sort of reasonable order here, because they |
| determine the final load addresses of the orphan sections. |
| In addition, placing output statements in the wrong order may |
| require extra segments. For instance, given a typical |
| situation of all read-only sections placed in one segment and |
| following that a segment containing all the read-write |
| sections, we wouldn't want to place an orphan read/write |
| section before or amongst the read-only ones. */ |
| if (add.head != NULL) |
| { |
| lang_output_section_statement_type *newly_added_os; |
| |
| /* Place OS after AFTER if AFTER_NOTE is TRUE. */ |
| if (place_after) |
| { |
| lang_statement_union_type **where = insert_os_after (after); |
| |
| *add.tail = *where; |
| *where = add.head; |
| |
| place->os_tail = &after->next; |
| } |
| else |
| { |
| /* Put it after the last orphan statement we added. */ |
| *add.tail = *place->stmt; |
| *place->stmt = add.head; |
| } |
| |
| /* Fix the global list pointer if we happened to tack our |
| new list at the tail. */ |
| if (*stat_ptr->tail == add.head) |
| stat_ptr->tail = add.tail; |
| |
| /* Save the end of this list. */ |
| place->stmt = add.tail; |
| |
| /* Do the same for the list of output section statements. */ |
| newly_added_os = *os_tail; |
| *os_tail = NULL; |
| newly_added_os->prev = (lang_output_section_statement_type *) |
| ((char *) place->os_tail |
| - offsetof (lang_output_section_statement_type, next)); |
| newly_added_os->next = *place->os_tail; |
| if (newly_added_os->next != NULL) |
| newly_added_os->next->prev = newly_added_os; |
| *place->os_tail = newly_added_os; |
| place->os_tail = &newly_added_os->next; |
| |
| /* Fixing the global list pointer here is a little different. |
| We added to the list in lang_enter_output_section_statement, |
| trimmed off the new output_section_statment above when |
| assigning *os_tail = NULL, but possibly added it back in |
| the same place when assigning *place->os_tail. */ |
| if (*os_tail == NULL) |
| lang_os_list.tail = (lang_statement_union_type **) os_tail; |
| } |
| } |
| return os; |
| } |
| |
| static void |
| lang_print_asneeded (void) |
| { |
| struct asneeded_minfo *m; |
| |
| if (asneeded_list_head == NULL) |
| return; |
| |
| minfo (_("\nAs-needed library included to satisfy reference by file (symbol)\n\n")); |
| |
| for (m = asneeded_list_head; m != NULL; m = m->next) |
| { |
| size_t len; |
| |
| minfo ("%s", m->soname); |
| len = strlen (m->soname); |
| |
| if (len >= 29) |
| { |
| print_nl (); |
| len = 0; |
| } |
| while (len < 30) |
| { |
| print_space (); |
| ++len; |
| } |
| |
| if (m->ref != NULL) |
| minfo ("%pB ", m->ref); |
| minfo ("(%pT)\n", m->name); |
| } |
| } |
| |
| static void |
| lang_map_flags (flagword flag) |
| { |
| if (flag & SEC_ALLOC) |
| minfo ("a"); |
| |
| if (flag & SEC_CODE) |
| minfo ("x"); |
| |
| if (flag & SEC_READONLY) |
| minfo ("r"); |
| |
| if (flag & SEC_DATA) |
| minfo ("w"); |
| |
| if (flag & SEC_LOAD) |
| minfo ("l"); |
| } |
| |
| void |
| lang_map (void) |
| { |
| lang_memory_region_type *m; |
| bool dis_header_printed = false; |
| |
| LANG_FOR_EACH_INPUT_STATEMENT (file) |
| { |
| asection *s; |
| |
| if ((file->the_bfd->flags & (BFD_LINKER_CREATED | DYNAMIC)) != 0 |
| || file->flags.just_syms) |
| continue; |
| |
| if (config.print_map_discarded) |
| for (s = file->the_bfd->sections; s != NULL; s = s->next) |
| if ((s->output_section == NULL |
| || s->output_section->owner != link_info.output_bfd) |
| && (s->flags & (SEC_LINKER_CREATED | SEC_KEEP)) == 0) |
| { |
| if (! dis_header_printed) |
| { |
| fprintf (config.map_file, _("\nDiscarded input sections\n\n")); |
| dis_header_printed = true; |
| } |
| |
| print_input_section (s, true); |
| } |
| } |
| |
| minfo (_("\nMemory Configuration\n\n")); |
| fprintf (config.map_file, "%-16s %-18s %-18s %s\n", |
| _("Name"), _("Origin"), _("Length"), _("Attributes")); |
| |
| for (m = lang_memory_region_list; m != NULL; m = m->next) |
| { |
| char buf[100]; |
| int len; |
| |
| fprintf (config.map_file, "%-16s ", m->name_list.name); |
| |
| sprintf_vma (buf, m->origin); |
| minfo ("0x%s ", buf); |
| len = strlen (buf); |
| while (len < 16) |
| { |
| print_space (); |
| ++len; |
| } |
| |
| minfo ("0x%V", m->length); |
| if (m->flags || m->not_flags) |
| { |
| #ifndef BFD64 |
| minfo (" "); |
| #endif |
| if (m->flags) |
| { |
| print_space (); |
| lang_map_flags (m->flags); |
| } |
| |
| if (m->not_flags) |
| { |
| minfo (" !"); |
| lang_map_flags (m->not_flags); |
| } |
| } |
| |
| print_nl (); |
| } |
| |
| fprintf (config.map_file, _("\nLinker script and memory map\n\n")); |
| |
| if (!link_info.reduce_memory_overheads) |
| { |
| obstack_begin (&map_obstack, 1000); |
| bfd_link_hash_traverse (link_info.hash, sort_def_symbol, 0); |
| } |
| expld.phase = lang_fixed_phase_enum; |
| lang_statement_iteration++; |
| print_statements (); |
| |
| ldemul_extra_map_file_text (link_info.output_bfd, &link_info, |
| config.map_file); |
| } |
| |
| static bool |
| sort_def_symbol (struct bfd_link_hash_entry *hash_entry, |
| void *info ATTRIBUTE_UNUSED) |
| { |
| if ((hash_entry->type == bfd_link_hash_defined |
| || hash_entry->type == bfd_link_hash_defweak) |
| && hash_entry->u.def.section->owner != link_info.output_bfd |
| && hash_entry->u.def.section->owner != NULL) |
| { |
| input_section_userdata_type *ud; |
| struct map_symbol_def *def; |
| |
| ud = bfd_section_userdata (hash_entry->u.def.section); |
| if (!ud) |
| { |
| ud = stat_alloc (sizeof (*ud)); |
| bfd_set_section_userdata (hash_entry->u.def.section, ud); |
| ud->map_symbol_def_tail = &ud->map_symbol_def_head; |
| ud->map_symbol_def_count = 0; |
| } |
| else if (!ud->map_symbol_def_tail) |
| ud->map_symbol_def_tail = &ud->map_symbol_def_head; |
| |
| def = (struct map_symbol_def *) obstack_alloc (&map_obstack, sizeof *def); |
| def->entry = hash_entry; |
| *(ud->map_symbol_def_tail) = def; |
| ud->map_symbol_def_tail = &def->next; |
| ud->map_symbol_def_count++; |
| } |
| return true; |
| } |
| |
| /* Initialize an output section. */ |
| |
| static void |
| init_os (lang_output_section_statement_type *s, flagword flags) |
| { |
| if (strcmp (s->name, DISCARD_SECTION_NAME) == 0) |
| einfo (_("%F%P: illegal use of `%s' section\n"), DISCARD_SECTION_NAME); |
| |
| if (!s->dup_output) |
| s->bfd_section = bfd_get_section_by_name (link_info.output_bfd, s->name); |
| if (s->bfd_section == NULL) |
| s->bfd_section = bfd_make_section_anyway_with_flags (link_info.output_bfd, |
| s->name, flags); |
| if (s->bfd_section == NULL) |
| { |
| einfo (_("%F%P: output format %s cannot represent section" |
| " called %s: %E\n"), |
| link_info.output_bfd->xvec->name, s->name); |
| } |
| s->bfd_section->output_section = s->bfd_section; |
| s->bfd_section->output_offset = 0; |
| |
| /* Set the userdata of the output section to the output section |
| statement to avoid lookup. */ |
| bfd_set_section_userdata (s->bfd_section, s); |
| |
| /* If there is a base address, make sure that any sections it might |
| mention are initialized. */ |
| if (s->addr_tree != NULL) |
| exp_init_os (s->addr_tree); |
| |
| if (s->load_base != NULL) |
| exp_init_os (s->load_base); |
| |
| /* If supplied an alignment, set it. */ |
| if (s->section_alignment != NULL) |
| s->bfd_section->alignment_power = exp_get_power (s->section_alignment, |
| "section alignment"); |
| } |
| |
| /* Make sure that all output sections mentioned in an expression are |
| initialized. */ |
| |
| static void |
| exp_init_os (etree_type *exp) |
| { |
| switch (exp->type.node_class) |
| { |
| case etree_assign: |
| case etree_provide: |
| case etree_provided: |
| exp_init_os (exp->assign.src); |
| break; |
| |
| case etree_binary: |
| exp_init_os (exp->binary.lhs); |
| exp_init_os (exp->binary.rhs); |
| break; |
| |
| case etree_trinary: |
| exp_init_os (exp->trinary.cond); |
| exp_init_os (exp->trinary.lhs); |
| exp_init_os (exp->trinary.rhs); |
| break; |
| |
| case etree_assert: |
| exp_init_os (exp->assert_s.child); |
| break; |
| |
| case etree_unary: |
| exp_init_os (exp->unary.child); |
| break; |
| |
| case etree_name: |
| switch (exp->type.node_code) |
| { |
| case ADDR: |
| case LOADADDR: |
| case SIZEOF: |
| { |
| lang_output_section_statement_type *os; |
| |
| os = lang_output_section_find (exp->name.name); |
| if (os != NULL && os->bfd_section == NULL) |
| init_os (os, 0); |
| } |
| } |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| static void |
| section_already_linked (bfd *abfd, asection *sec, void *data) |
| { |
| lang_input_statement_type *entry = (lang_input_statement_type *) data; |
| |
| /* If we are only reading symbols from this object, then we want to |
| discard all sections. */ |
| if (entry->flags.just_syms) |
| { |
| bfd_link_just_syms (abfd, sec, &link_info); |
| return; |
| } |
| |
| /* Deal with SHF_EXCLUDE ELF sections. */ |
| if (!bfd_link_relocatable (&link_info) |
| && (abfd->flags & BFD_PLUGIN) == 0 |
| && (sec->flags & (SEC_GROUP | SEC_KEEP | SEC_EXCLUDE)) == SEC_EXCLUDE) |
| sec->output_section = bfd_abs_section_ptr; |
| |
| if (!(abfd->flags & DYNAMIC)) |
| bfd_section_already_linked (abfd, sec, &link_info); |
| } |
| |
| |
| /* Returns true if SECTION is one we know will be discarded based on its |
| section flags, otherwise returns false. */ |
| |
| static bool |
| lang_discard_section_p (asection *section) |
| { |
| bool discard; |
| flagword flags = section->flags; |
| |
| /* Discard sections marked with SEC_EXCLUDE. */ |
| discard = (flags & SEC_EXCLUDE) != 0; |
| |
| /* Discard the group descriptor sections when we're finally placing the |
| sections from within the group. */ |
| if ((flags & SEC_GROUP) != 0 |
| && link_info.resolve_section_groups) |
| discard = true; |
| |
| /* Discard debugging sections if we are stripping debugging |
| information. */ |
| if ((link_info.strip == strip_debugger || link_info.strip == strip_all) |
| && (flags & SEC_DEBUGGING) != 0) |
| discard = true; |
| |
| return discard; |
| } |
| |
| /* The wild routines. |
| |
| These expand statements like *(.text) and foo.o to a list of |
| explicit actions, like foo.o(.text), bar.o(.text) and |
| foo.o(.text, .data). */ |
| |
| /* Add SECTION to the output section OUTPUT. Do this by creating a |
| lang_input_section statement which is placed at PTR. */ |
| |
| void |
| lang_add_section (lang_statement_list_type *ptr, |
| asection *section, |
| struct wildcard_list *pattern, |
| struct flag_info *sflag_info, |
| lang_output_section_statement_type *output) |
| { |
| flagword flags = section->flags; |
| |
| bool discard; |
| lang_input_section_type *new_section; |
| bfd *abfd = link_info.output_bfd; |
| |
| /* Is this section one we know should be discarded? */ |
| discard = lang_discard_section_p (section); |
| |
| /* Discard input sections which are assigned to a section named |
| DISCARD_SECTION_NAME. */ |
| if (strcmp (output->name, DISCARD_SECTION_NAME) == 0) |
| discard = true; |
| |
| if (discard) |
| { |
| if (section->output_section == NULL) |
| { |
| /* This prevents future calls from assigning this section. */ |
| section->output_section = bfd_abs_section_ptr; |
| } |
| else if (link_info.non_contiguous_regions_warnings) |
| einfo (_("%P:%pS: warning: --enable-non-contiguous-regions makes " |
| "section `%pA' from '%pB' match /DISCARD/ clause.\n"), |
| NULL, section, section->owner); |
| |
| return; |
| } |
| |
| if (sflag_info) |
| { |
| bool keep; |
| |
| keep = bfd_lookup_section_flags (&link_info, sflag_info, section); |
| if (!keep) |
| return; |
| } |
| |
| if (section->output_section != NULL) |
| { |
| if (!link_info.non_contiguous_regions) |
| return; |
| |
| /* SECTION has already been handled in a special way |
| (eg. LINK_ONCE): skip it. */ |
| if (bfd_is_abs_section (section->output_section)) |
| return; |
| |
| /* Already assigned to the same output section, do not process |
| it again, to avoid creating loops between duplicate sections |
| later. */ |
| if (section->output_section == output->bfd_section) |
| return; |
| |
| if (link_info.non_contiguous_regions_warnings && output->bfd_section) |
| einfo (_("%P:%pS: warning: --enable-non-contiguous-regions may " |
| "change behaviour for section `%pA' from '%pB' (assigned to " |
| "%pA, but additional match: %pA)\n"), |
| NULL, section, section->owner, section->output_section, |
| output->bfd_section); |
| |
| /* SECTION has already been assigned to an output section, but |
| the user allows it to be mapped to another one in case it |
| overflows. We'll later update the actual output section in |
| size_input_section as appropriate. */ |
| } |
| |
| /* We don't copy the SEC_NEVER_LOAD flag from an input section |
| to an output section, because we want to be able to include a |
| SEC_NEVER_LOAD section in the middle of an otherwise loaded |
| section (I don't know why we want to do this, but we do). |
| build_link_order in ldwrite.c handles this case by turning |
| the embedded SEC_NEVER_LOAD section into a fill. */ |
| flags &= ~ SEC_NEVER_LOAD; |
| |
| /* If final link, don't copy the SEC_LINK_ONCE flags, they've |
| already been processed. One reason to do this is that on pe |
| format targets, .text$foo sections go into .text and it's odd |
| to see .text with SEC_LINK_ONCE set. */ |
| if ((flags & (SEC_LINK_ONCE | SEC_GROUP)) == (SEC_LINK_ONCE | SEC_GROUP)) |
| { |
| if (link_info.resolve_section_groups) |
| flags &= ~(SEC_LINK_ONCE | SEC_LINK_DUPLICATES | SEC_RELOC); |
| else |
| flags &= ~(SEC_LINK_DUPLICATES | SEC_RELOC); |
| } |
| else if (!bfd_link_relocatable (&link_info)) |
| flags &= ~(SEC_LINK_ONCE | SEC_LINK_DUPLICATES | SEC_RELOC); |
| |
| switch (output->sectype) |
| { |
| case normal_section: |
| case overlay_section: |
| case first_overlay_section: |
| break; |
| case noalloc_section: |
| flags &= ~SEC_ALLOC; |
| break; |
| case readonly_section: |
| flags |= SEC_READONLY; |
| break; |
| case noload_section: |
| flags &= ~SEC_LOAD; |
| flags |= SEC_NEVER_LOAD; |
| /* Unfortunately GNU ld has managed to evolve two different |
| meanings to NOLOAD in scripts. ELF gets a .bss style noload, |
| alloc, no contents section. All others get a noload, noalloc |
| section. */ |
| if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour) |
| flags &= ~SEC_HAS_CONTENTS; |
| else |
| flags &= ~SEC_ALLOC; |
| break; |
| } |
| |
| if (output->bfd_section == NULL) |
| init_os (output, flags); |
| |
| /* If SEC_READONLY is not set in the input section, then clear |
| it from the output section. */ |
| output->bfd_section->flags &= flags | ~SEC_READONLY; |
| |
| if (output->bfd_section->linker_has_input) |
| { |
| /* Only set SEC_READONLY flag on the first input section. */ |
| flags &= ~ SEC_READONLY; |
| |
| /* Keep SEC_MERGE and SEC_STRINGS only if they are the same. */ |
| if ((output->bfd_section->flags & (SEC_MERGE | SEC_STRINGS)) |
| != (flags & (SEC_MERGE | SEC_STRINGS)) |
| || ((flags & SEC_MERGE) != 0 |
| && output->bfd_section->entsize != section->entsize)) |
| { |
| output->bfd_section->flags &= ~ (SEC_MERGE | SEC_STRINGS); |
| flags &= ~ (SEC_MERGE | SEC_STRINGS); |
| } |
| } |
| output->bfd_section->flags |= flags; |
| |
| if (!output->bfd_section->linker_has_input) |
| { |
| output->bfd_section->linker_has_input = 1; |
| /* This must happen after flags have been updated. The output |
| section may have been created before we saw its first input |
| section, eg. for a data statement. */ |
| bfd_init_private_section_data (section->owner, section, |
| link_info.output_bfd, |
| output->bfd_section, |
| &link_info); |
| if ((flags & SEC_MERGE) != 0) |
| output->bfd_section->entsize = section->entsize; |
| } |
| |
| if ((flags & SEC_TIC54X_BLOCK) != 0 |
| && bfd_get_arch (section->owner) == bfd_arch_tic54x) |
| { |
| /* FIXME: This value should really be obtained from the bfd... */ |
| output->block_value = 128; |
| } |
| |
| if (section->alignment_power > output->bfd_section->alignment_power) |
| output->bfd_section->alignment_power = section->alignment_power; |
| |
| section->output_section = output->bfd_section; |
| |
| if (!map_head_is_link_order) |
| { |
| asection *s = output->bfd_section->map_tail.s; |
| output->bfd_section->map_tail.s = section; |
| section->map_head.s = NULL; |
| section->map_tail.s = s; |
| if (s != NULL) |
| s->map_head.s = section; |
| else |
| output->bfd_section->map_head.s = section; |
| } |
| |
| /* Add a section reference to the list. */ |
| new_section = new_stat (lang_input_section, ptr); |
| new_section->section = section; |
| new_section->pattern = pattern; |
| } |
| |
| /* Handle wildcard sorting. This returns the lang_input_section which |
| should follow the one we are going to create for SECTION and FILE, |
| based on the sorting requirements of WILD. It returns NULL if the |
| new section should just go at the end of the current list. */ |
| |
| static lang_statement_union_type * |
| wild_sort (lang_wild_statement_type *wild, |
| struct wildcard_list *sec, |
| lang_input_statement_type *file, |
| asection *section) |
| { |
| lang_statement_union_type *l; |
| |
| if (!wild->filenames_sorted |
| && (sec == NULL || sec->spec.sorted == none)) |
| return NULL; |
| |
| for (l = wild->children.head; l != NULL; l = l->header.next) |
| { |
| lang_input_section_type *ls; |
| |
| if (l->header.type != lang_input_section_enum) |
| continue; |
| ls = &l->input_section; |
| |
| /* Sorting by filename takes precedence over sorting by section |
| name. */ |
| |
| if (wild->filenames_sorted) |
| { |
| const char *fn, *ln; |
| bool fa, la; |
| int i; |
| |
| /* The PE support for the .idata section as generated by |
| dlltool assumes that files will be sorted by the name of |
| the archive and then the name of the file within the |
| archive. */ |
| |
| if (file->the_bfd != NULL |
| && file->the_bfd->my_archive != NULL) |
| { |
| fn = bfd_get_filename (file->the_bfd->my_archive); |
| fa = true; |
| } |
| else |
| { |
| fn = file->filename; |
| fa = false; |
| } |
| |
| if (ls->section->owner->my_archive != NULL) |
| { |
| ln = bfd_get_filename (ls->section->owner->my_archive); |
| la = true; |
| } |
| else |
| { |
| ln = bfd_get_filename (ls->section->owner); |
| la = false; |
| } |
| |
| i = filename_cmp (fn, ln); |
| if (i > 0) |
| continue; |
| else if (i < 0) |
| break; |
| |
| if (fa || la) |
| { |
| if (fa) |
| fn = file->filename; |
| if (la) |
| ln = bfd_get_filename (ls->section->owner); |
| |
| i = filename_cmp (fn, ln); |
| if (i > 0) |
| continue; |
| else if (i < 0) |
| break; |
| } |
| } |
| |
| /* Here either the files are not sorted by name, or we are |
| looking at the sections for this file. */ |
| |
| if (sec != NULL |
| && sec->spec.sorted != none |
| && sec->spec.sorted != by_none) |
| if (compare_section (sec->spec.sorted, section, ls->section) < 0) |
| break; |
| } |
| |
| return l; |
| } |
| |
| /* Expand a wild statement for a particular FILE. SECTION may be |
| NULL, in which case it is a wild card. */ |
| |
| static void |
| output_section_callback (lang_wild_statement_type *ptr, |
| struct wildcard_list *sec, |
| asection *section, |
| lang_input_statement_type *file, |
| void *output) |
| { |
| lang_statement_union_type *before; |
| lang_output_section_statement_type *os; |
| |
| os = (lang_output_section_statement_type *) output; |
| |
| /* Exclude sections that match UNIQUE_SECTION_LIST. */ |
| if (unique_section_p (section, os)) |
| return; |
| |
| before = wild_sort (ptr, sec, file, section); |
| |
| /* Here BEFORE points to the lang_input_section which |
| should follow the one we are about to add. If BEFORE |
| is NULL, then the section should just go at the end |
| of the current list. */ |
| |
| if (before == NULL) |
| lang_add_section (&ptr->children, section, ptr->section_list, |
| ptr->section_flag_list, os); |
| else |
| { |
| lang_statement_list_type list; |
| lang_statement_union_type **pp; |
| |
| lang_list_init (&list); |
| lang_add_section (&list, section, ptr->section_list, |
| ptr->section_flag_list, os); |
| |
| /* If we are discarding the section, LIST.HEAD will |
| be NULL. */ |
| if (list.head != NULL) |
| { |
| ASSERT (list.head->header.next == NULL); |
| |
| for (pp = &ptr->children.head; |
| *pp != before; |
| pp = &(*pp)->header.next) |
| ASSERT (*pp != NULL); |
| |
| list.head->header.next = *pp; |
| *pp = list.head; |
| } |
| } |
| } |
| |
| /* Check if all sections in a wild statement for a particular FILE |
| are readonly. */ |
| |
| static void |
| check_section_callback (lang_wild_statement_type *ptr ATTRIBUTE_UNUSED, |
| struct wildcard_list *sec ATTRIBUTE_UNUSED, |
| asection *section, |
| lang_input_statement_type *file ATTRIBUTE_UNUSED, |
| void *output) |
| { |
| lang_output_section_statement_type *os; |
| |
| os = (lang_output_section_statement_type *) output; |
| |
| /* Exclude sections that match UNIQUE_SECTION_LIST. */ |
| if (unique_section_p (section, os)) |
| return; |
| |
| if (section->output_section == NULL && (section->flags & SEC_READONLY) == 0) |
| os->all_input_readonly = false; |
| } |
| |
| /* This is passed a file name which must have been seen already and |
| added to the statement tree. We will see if it has been opened |
| already and had its symbols read. If not then we'll read it. */ |
| |
| static lang_input_statement_type * |
| lookup_name (const char *name) |
| { |
| lang_input_statement_type *search; |
| |
| for (search = (void *) input_file_chain.head; |
| search != NULL; |
| search = search->next_real_file) |
| { |
| /* Use the local_sym_name as the name of the file that has |
| already been loaded as filename might have been transformed |
| via the search directory lookup mechanism. */ |
| const char *filename = search->local_sym_name; |
| |
| if (filename != NULL |
| && filename_cmp (filename, name) == 0) |
| break; |
| } |
| |
| if (search == NULL) |
| { |
| /* Arrange to splice the input statement added by new_afile into |
| statement_list after the current input_file_chain tail. |
| We know input_file_chain is not an empty list, and that |
| lookup_name was called via open_input_bfds. Later calls to |
| lookup_name should always match an existing input_statement. */ |
| lang_statement_union_type **tail = stat_ptr->tail; |
| lang_statement_union_type **after |
| = (void *) ((char *) input_file_chain.tail |
| - offsetof (lang_input_statement_type, next_real_file) |
| + offsetof (lang_input_statement_type, header.next)); |
| lang_statement_union_type *rest = *after; |
| stat_ptr->tail = after; |
| search = new_afile (name, lang_input_file_is_search_file_enum, |
| default_target, NULL); |
| *stat_ptr->tail = rest; |
| if (*tail == NULL) |
| stat_ptr->tail = tail; |
| } |
| |
| /* If we have already added this file, or this file is not real |
| don't add this file. */ |
| if (search->flags.loaded || !search->flags.real) |
| return search; |
| |
| if (!load_symbols (search, NULL)) |
| return NULL; |
| |
| return search; |
| } |
| |
| /* Save LIST as a list of libraries whose symbols should not be exported. */ |
| |
| struct excluded_lib |
| { |
| char *name; |
| struct excluded_lib *next; |
| }; |
| static struct excluded_lib *excluded_libs; |
| |
| void |
| add_excluded_libs (const char *list) |
| { |
| const char *p = list, *end; |
| |
| while (*p != '\0') |
| { |
| struct excluded_lib *entry; |
| end = strpbrk (p, ",:"); |
| if (end == NULL) |
| end = p + strlen (p); |
| entry = (struct excluded_lib *) xmalloc (sizeof (*entry)); |
| entry->next = excluded_libs; |
| entry->name = (char *) xmalloc (end - p + 1); |
| memcpy (entry->name, p, end - p); |
| entry->name[end - p] = '\0'; |
| excluded_libs = entry; |
| if (*end == '\0') |
| break; |
| p = end + 1; |
| } |
| } |
| |
| static void |
| check_excluded_libs (bfd *abfd) |
| { |
| struct excluded_lib *lib = excluded_libs; |
| |
| while (lib) |
| { |
| int len = strlen (lib->name); |
| const char *filename = lbasename (bfd_get_filename (abfd)); |
| |
| if (strcmp (lib->name, "ALL") == 0) |
| { |
| abfd->no_export = true; |
| return; |
| } |
| |
| if (filename_ncmp (lib->name, filename, len) == 0 |
| && (filename[len] == '\0' |
| || (filename[len] == '.' && filename[len + 1] == 'a' |
| && filename[len + 2] == '\0'))) |
| { |
| abfd->no_export = true; |
| return; |
| } |
| |
| lib = lib->next; |
| } |
| } |
| |
| /* Get the symbols for an input file. */ |
| |
| bool |
| load_symbols (lang_input_statement_type *entry, |
| lang_statement_list_type *place) |
| { |
| char **matching; |
| |
| if (entry->flags.loaded) |
| return true; |
| |
| ldfile_open_file (entry); |
| |
| /* Do not process further if the file was missing. */ |
| if (entry->flags.missing_file) |
| return true; |
| |
| if (trace_files || verbose) |
| info_msg ("%pI\n", entry); |
| |
| if (!bfd_check_format (entry->the_bfd, bfd_archive) |
| && !bfd_check_format_matches (entry->the_bfd, bfd_object, &matching)) |
| { |
| bfd_error_type err; |
| struct lang_input_statement_flags save_flags; |
| extern FILE *yyin; |
| |
| err = bfd_get_error (); |
| |
| /* See if the emulation has some special knowledge. */ |
| if (ldemul_unrecognized_file (entry)) |
| return true; |
| |
| if (err == bfd_error_file_ambiguously_recognized) |
| { |
| char **p; |
| |
| einfo (_("%P: %pB: file not recognized: %E;" |
| " matching formats:"), entry->the_bfd); |
| for (p = matching; *p != NULL; p++) |
| einfo (" %s", *p); |
| einfo ("%F\n"); |
| } |
| else if (err != bfd_error_file_not_recognized |
| || place == NULL) |
| einfo (_("%F%P: %pB: file not recognized: %E\n"), entry->the_bfd); |
| |
| bfd_close (entry->the_bfd); |
| entry->the_bfd = NULL; |
| |
| /* Try to interpret the file as a linker script. */ |
| save_flags = input_flags; |
| ldfile_open_command_file (entry->filename); |
| |
| push_stat_ptr (place); |
| input_flags.add_DT_NEEDED_for_regular |
| = entry->flags.add_DT_NEEDED_for_regular; |
| input_flags.add_DT_NEEDED_for_dynamic |
| = entry->flags.add_DT_NEEDED_for_dynamic; |
| input_flags.whole_archive = entry->flags.whole_archive; |
| input_flags.dynamic = entry->flags.dynamic; |
| |
| ldfile_assumed_script = true; |
| parser_input = input_script; |
| current_input_file = entry->filename; |
| yyparse (); |
| current_input_file = NULL; |
| ldfile_assumed_script = false; |
| |
| /* missing_file is sticky. sysrooted will already have been |
| restored when seeing EOF in yyparse, but no harm to restore |
| again. */ |
| save_flags.missing_file |= input_flags.missing_file; |
| input_flags = save_flags; |
| pop_stat_ptr (); |
| fclose (yyin); |
| yyin = NULL; |
| entry->flags.loaded = true; |
| |
| return true; |
| } |
| |
| if (ldemul_recognized_file (entry)) |
| return true; |
| |
| /* We don't call ldlang_add_file for an archive. Instead, the |
| add_symbols entry point will call ldlang_add_file, via the |
| add_archive_element callback, for each element of the archive |
| which is used. */ |
| switch (bfd_get_format (entry->the_bfd)) |
| { |
| default: |
| break; |
| |
| case bfd_object: |
| if (!entry->flags.reload) |
| ldlang_add_file (entry); |
| break; |
| |
| case bfd_archive: |
| check_excluded_libs (entry->the_bfd); |
| |
| bfd_set_usrdata (entry->the_bfd, entry); |
| if (entry->flags.whole_archive) |
| { |
| bfd *member = NULL; |
| bool loaded = true; |
| |
| for (;;) |
| { |
| bfd *subsbfd; |
| member = bfd_openr_next_archived_file (entry->the_bfd, member); |
| |
| if (member == NULL) |
| break; |
| |
| |