| /* Linker command language support. |
| Copyright (C) 1991-2024 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" |
| #include "bfdver.h" |
| |
| #if BFD_SUPPORTS_PLUGINS |
| #include "plugin.h" |
| #endif |
| |
| #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; |
| static struct obstack pt_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 bool wont_add_section_p (asection *, |
| lang_output_section_statement_type *); |
| static void insert_undefined (const char *); |
| static bool sort_def_symbol (struct bfd_link_hash_entry *, void *); |
| static lang_statement_union_type *new_statement (enum statement_enum type, |
| size_t size, |
| lang_statement_list_type *list); |
| 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; |
| bool enable_linker_version = 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); |
| } |
| |
| /* 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; |
| } |
| |
| static int |
| name_match (const char *pattern, const char *name) |
| { |
| if (is_simple_wild (pattern)) |
| return !match_simple_wild (pattern, name); |
| if (wildcardp (pattern)) |
| return fnmatch (pattern, name, 0); |
| return strcmp (pattern, name); |
| } |
| |
| /* Given an analyzed wildcard_spec SPEC, match it against NAME, |
| returns zero on a match, non-zero if there's no match. */ |
| |
| static int |
| spec_match (const struct wildcard_spec *spec, const char *name) |
| { |
| size_t nl = spec->namelen; |
| size_t pl = spec->prefixlen; |
| size_t sl = spec->suffixlen; |
| size_t inputlen = strlen (name); |
| int r; |
| |
| if (pl) |
| { |
| if (inputlen < pl) |
| return 1; |
| |
| r = memcmp (spec->name, name, pl); |
| if (r) |
| return r; |
| } |
| |
| if (sl) |
| { |
| if (inputlen < sl) |
| return 1; |
| |
| r = memcmp (spec->name + nl - sl, name + inputlen - sl, sl); |
| if (r) |
| return r; |
| } |
| |
| if (nl == pl + sl + 1 && spec->name[pl] == '*') |
| return 0; |
| |
| if (nl > pl) |
| return fnmatch (spec->name + pl, name + pl, 0); |
| |
| if (inputlen >= nl) |
| return name[nl]; |
| |
| return 0; |
| } |
| |
| 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; |
| } |
| |
| /* Add SECTION (from input FILE) to the list of matching sections |
| within PTR (the matching wildcard is SEC). */ |
| |
| static void |
| add_matching_section (lang_wild_statement_type *ptr, |
| struct wildcard_list *sec, |
| asection *section, |
| lang_input_statement_type *file) |
| { |
| lang_input_matcher_type *new_section; |
| /* Add a section reference to the list. */ |
| new_section = new_stat (lang_input_matcher, &ptr->matching_sections); |
| new_section->section = section; |
| new_section->pattern = sec; |
| new_section->input_stmt = file; |
| } |
| |
| /* Process section S (from input file FILE) in relation to wildcard |
| statement PTR. We already know that a prefix of the name of S matches |
| some wildcard in PTR's wildcard list. Here we check if the filename |
| matches as well (if it's specified) and if any of the wildcards in fact |
| does match. */ |
| |
| static void |
| walk_wild_section_match (lang_wild_statement_type *ptr, |
| lang_input_statement_type *file, |
| asection *s) |
| { |
| struct wildcard_list *sec; |
| const char *file_spec = ptr->filename; |
| char *p; |
| |
| /* Check if filenames match. */ |
| if (file_spec == NULL) |
| ; |
| else if ((p = archive_path (file_spec)) != NULL) |
| { |
| if (!input_statement_is_archive_path (file_spec, p, file)) |
| return; |
| } |
| else if (wildcardp (file_spec)) |
| { |
| if (fnmatch (file_spec, file->filename, 0) != 0) |
| return; |
| } |
| else |
| { |
| /* XXX Matching against non-wildcard filename in wild statements |
| was done by going through lookup_name, which uses |
| ->local_sym_name to compare against, not ->filename. We retain |
| this behaviour even though the above code paths use filename. |
| It would be more logical to use it here as well, in which |
| case the above wildcard() arm could be folded into this by using |
| name_match. This would also solve the worry of what to do |
| about unset local_sym_name (in which case lookup_name simply adds |
| the input file again). */ |
| const char *filename = file->local_sym_name; |
| lang_input_statement_type *arch_is; |
| if (filename && filename_cmp (filename, file_spec) == 0) |
| ; |
| /* FIXME: see also walk_wild_file_in_exclude_list for why we |
| also check parents BFD (local_sym_)name to match input statements |
| with unadorned archive names. */ |
| else if (file->the_bfd |
| && file->the_bfd->my_archive |
| && (arch_is = bfd_usrdata (file->the_bfd->my_archive)) |
| && arch_is->local_sym_name |
| && filename_cmp (arch_is->local_sym_name, file_spec) == 0) |
| ; |
| else |
| return; |
| } |
| |
| /* If filename is excluded we're done. */ |
| if (walk_wild_file_in_exclude_list (ptr->exclude_name_list, file)) |
| return; |
| |
| /* Check section name against each wildcard spec. If there's no |
| wildcard all sections match. */ |
| sec = ptr->section_list; |
| if (sec == NULL) |
| add_matching_section (ptr, sec, s, file); |
| else |
| { |
| const char *sname = bfd_section_name (s); |
| for (; sec != NULL; sec = sec->next) |
| { |
| if (sec->spec.name != NULL |
| && spec_match (&sec->spec, sname) != 0) |
| continue; |
| |
| /* Don't process sections from files which were excluded. */ |
| if (!walk_wild_file_in_exclude_list (sec->spec.exclude_name_list, |
| file)) |
| add_matching_section (ptr, sec, s, file); |
| } |
| } |
| } |
| |
| /* 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, bool reversed) |
| { |
| 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; |
| if (reversed) |
| ret = b_priority - a_priority; |
| else |
| 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: |
| if (reversed) |
| ret = strcmp (bfd_section_name (bsec), bfd_section_name (asec)); |
| else |
| ret = strcmp (bfd_section_name (asec), bfd_section_name (bsec)); |
| break; |
| |
| case by_name_alignment: |
| if (reversed) |
| ret = strcmp (bfd_section_name (bsec), bfd_section_name (asec)); |
| else |
| 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; |
| } |
| |
| /* PE puts the sort key in the input statement. */ |
| |
| static const char * |
| sort_filename (bfd *abfd) |
| { |
| lang_input_statement_type *is = bfd_usrdata (abfd); |
| if (is->sort_key) |
| return is->sort_key; |
| return bfd_get_filename (abfd); |
| } |
| |
| /* Handle wildcard sorting. This returns the place in a binary search tree |
| where this FILE:SECTION should be inserted for wild statement WILD where |
| the spec SEC was the matching one. The tree is later linearized. */ |
| |
| static lang_section_bst_type ** |
| wild_sort (lang_wild_statement_type *wild, |
| struct wildcard_list *sec, |
| lang_input_statement_type *file, |
| asection *section) |
| { |
| lang_section_bst_type **tree; |
| |
| if (!wild->filenames_sorted |
| && (sec == NULL || sec->spec.sorted == none |
| || sec->spec.sorted == by_none)) |
| { |
| /* We might be called even if _this_ spec doesn't need sorting, |
| in which case we simply append at the right end of tree. */ |
| return wild->rightmost; |
| } |
| |
| tree = &wild->tree; |
| while (*tree) |
| { |
| /* Sorting by filename takes precedence over sorting by section |
| name. */ |
| |
| if (wild->filenames_sorted) |
| { |
| const char *fn, *ln; |
| bool fa, la; |
| int i; |
| asection *lsec = (*tree)->section; |
| |
| /* 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. */ |
| |
| fa = file->the_bfd->my_archive != NULL; |
| if (fa) |
| fn = sort_filename (file->the_bfd->my_archive); |
| else |
| fn = sort_filename (file->the_bfd); |
| |
| la = lsec->owner->my_archive != NULL; |
| if (la) |
| ln = sort_filename (lsec->owner->my_archive); |
| else |
| ln = sort_filename (lsec->owner); |
| |
| if (wild->filenames_reversed) |
| i = filename_cmp (ln, fn); |
| else |
| i = filename_cmp (fn, ln); |
| |
| if (i > 0) |
| { tree = &((*tree)->right); continue; } |
| else if (i < 0) |
| { tree = &((*tree)->left); continue; } |
| |
| if (fa || la) |
| { |
| if (fa) |
| fn = sort_filename (file->the_bfd); |
| if (la) |
| ln = sort_filename (lsec->owner); |
| |
| if (wild->filenames_reversed) |
| i = filename_cmp (ln, fn); |
| else |
| i = filename_cmp (fn, ln); |
| |
| if (i > 0) |
| { tree = &((*tree)->right); continue; } |
| else if (i < 0) |
| { tree = &((*tree)->left); continue; } |
| } |
| } |
| |
| /* Here either the files are not sorted by name, or we are |
| looking at the sections for this file. */ |
| |
| /* Find the correct node to append this section. */ |
| if (sec && sec->spec.sorted != none && sec->spec.sorted != by_none |
| && compare_section (sec->spec.sorted, section, (*tree)->section, sec->spec.reversed) < 0) |
| tree = &((*tree)->left); |
| else |
| tree = &((*tree)->right); |
| } |
| |
| return tree; |
| } |
| |
| /* Use wild_sort to build a BST to sort sections. */ |
| |
| static void |
| output_section_callback_sort (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; |
| |
| /* Don't add sections to the tree when we already know that |
| lang_add_section won't do anything with it. */ |
| if (wont_add_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 (ptr, sec, file, section); |
| if (tree != NULL) |
| { |
| *tree = node; |
| if (tree == ptr->rightmost) |
| ptr->rightmost = &node->right; |
| } |
| } |
| |
| /* 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, |
| ptr->section_flag_list, |
| (lang_output_section_statement_type *) output); |
| |
| if (tree->right) |
| output_section_callback_tree_to_list (ptr, tree->right, output); |
| |
| free (tree); |
| } |
| |
| |
| /* Sections are matched against wildcard statements via a prefix tree. |
| The prefix tree holds prefixes of all matching patterns (up to the first |
| wildcard character), and the wild statement from which those patterns |
| came. When matching a section name against the tree we're walking through |
| the tree character by character. Each statement we hit is one that |
| potentially matches. This is checked by actually going through the |
| (glob) matching routines. |
| |
| When the section name turns out to actually match we record that section |
| in the wild statements list of matching sections. */ |
| |
| /* A prefix can be matched by multiple statement, so we need a list of them. */ |
| struct wild_stmt_list |
| { |
| lang_wild_statement_type *stmt; |
| struct wild_stmt_list *next; |
| }; |
| |
| /* The prefix tree itself. */ |
| struct prefixtree |
| { |
| /* The list of all children (linked via .next). */ |
| struct prefixtree *child; |
| struct prefixtree *next; |
| /* This tree node is responsible for the prefix of parent plus 'c'. */ |
| char c; |
| /* The statements that potentially can match this prefix. */ |
| struct wild_stmt_list *stmt; |
| }; |
| |
| /* We always have a root node in the prefix tree. It corresponds to the |
| empty prefix. E.g. a glob like "*" would sit in this root. */ |
| static struct prefixtree the_root, *ptroot = &the_root; |
| |
| /* Given a prefix tree in *TREE, corresponding to prefix P, find or |
| INSERT the tree node corresponding to prefix P+C. */ |
| |
| static struct prefixtree * |
| get_prefix_tree (struct prefixtree **tree, char c, bool insert) |
| { |
| struct prefixtree *t; |
| for (t = *tree; t; t = t->next) |
| if (t->c == c) |
| return t; |
| if (!insert) |
| return NULL; |
| t = (struct prefixtree *) obstack_alloc (&pt_obstack, sizeof *t); |
| t->child = NULL; |
| t->next = *tree; |
| t->c = c; |
| t->stmt = NULL; |
| *tree = t; |
| return t; |
| } |
| |
| /* Add STMT to the set of statements that can be matched by the prefix |
| corresponding to prefix tree T. */ |
| |
| static void |
| pt_add_stmt (struct prefixtree *t, lang_wild_statement_type *stmt) |
| { |
| struct wild_stmt_list *sl, **psl; |
| sl = (struct wild_stmt_list *) obstack_alloc (&pt_obstack, sizeof *sl); |
| sl->stmt = stmt; |
| sl->next = NULL; |
| psl = &t->stmt; |
| while (*psl) |
| psl = &(*psl)->next; |
| *psl = sl; |
| } |
| |
| /* Insert STMT into the global prefix tree. */ |
| |
| static void |
| insert_prefix_tree (lang_wild_statement_type *stmt) |
| { |
| struct wildcard_list *sec; |
| struct prefixtree *t; |
| |
| if (!stmt->section_list) |
| { |
| /* If we have no section_list (no wildcards in the wild STMT), |
| then every section name will match, so add this to the root. */ |
| pt_add_stmt (ptroot, stmt); |
| return; |
| } |
| |
| for (sec = stmt->section_list; sec; sec = sec->next) |
| { |
| const char *name = sec->spec.name ? sec->spec.name : "*"; |
| char c; |
| t = ptroot; |
| for (; (c = *name); name++) |
| { |
| if (c == '*' || c == '[' || c == '?') |
| break; |
| t = get_prefix_tree (&t->child, c, true); |
| } |
| /* If we hit a glob character, the matching prefix is what we saw |
| until now. If we hit the end of pattern (hence it's no glob) then |
| we can do better: we only need to record a match when a section name |
| completely matches, not merely a prefix, so record the trailing 0 |
| as well. */ |
| if (!c) |
| t = get_prefix_tree (&t->child, 0, true); |
| pt_add_stmt (t, stmt); |
| } |
| } |
| |
| /* Dump T indented by INDENT spaces. */ |
| |
| static void |
| debug_prefix_tree_rec (struct prefixtree *t, int indent) |
| { |
| for (; t; t = t->next) |
| { |
| struct wild_stmt_list *sl; |
| printf ("%*s %c", indent, "", t->c); |
| for (sl = t->stmt; sl; sl = sl->next) |
| { |
| struct wildcard_list *curr; |
| printf (" %p ", sl->stmt); |
| for (curr = sl->stmt->section_list; curr; curr = curr->next) |
| printf ("%s ", curr->spec.name ? curr->spec.name : "*"); |
| } |
| printf ("\n"); |
| debug_prefix_tree_rec (t->child, indent + 2); |
| } |
| } |
| |
| /* Dump the global prefix tree. */ |
| |
| static void |
| debug_prefix_tree (void) |
| { |
| debug_prefix_tree_rec (ptroot, 2); |
| } |
| |
| /* Like strcspn() but start to look from the end to beginning of |
| S. Returns the length of the suffix of S consisting entirely |
| of characters not in REJECT. */ |
| |
| static size_t |
| rstrcspn (const char *s, const char *reject) |
| { |
| size_t len = strlen (s), sufflen = 0; |
| while (len--) |
| { |
| char c = s[len]; |
| if (strchr (reject, c) != 0) |
| break; |
| sufflen++; |
| } |
| return sufflen; |
| } |
| |
| /* Analyze the wildcards in wild statement PTR to setup various |
| things for quick matching. */ |
| |
| static void |
| analyze_walk_wild_section_handler (lang_wild_statement_type *ptr) |
| { |
| struct wildcard_list *sec; |
| |
| ptr->tree = NULL; |
| ptr->rightmost = &ptr->tree; |
| |
| for (sec = ptr->section_list; sec != NULL; sec = sec->next) |
| { |
| if (sec->spec.name) |
| { |
| sec->spec.namelen = strlen (sec->spec.name); |
| sec->spec.prefixlen = strcspn (sec->spec.name, "?*["); |
| sec->spec.suffixlen = rstrcspn (sec->spec.name + sec->spec.prefixlen, |
| "?*]"); |
| } |
| else |
| sec->spec.namelen = sec->spec.prefixlen = sec->spec.suffixlen = 0; |
| } |
| |
| insert_prefix_tree (ptr); |
| } |
| |
| /* Match all sections from FILE against the global prefix tree, |
| and record them into each wild statement that has a match. */ |
| |
| static void |
| resolve_wild_sections (lang_input_statement_type *file) |
| { |
| asection *s; |
| |
| if (file->flags.just_syms) |
| return; |
| |
| for (s = file->the_bfd->sections; s != NULL; s = s->next) |
| { |
| const char *sname = bfd_section_name (s); |
| char c = 1; |
| struct prefixtree *t = ptroot; |
| //printf (" YYY consider %s of %s\n", sname, file->the_bfd->filename); |
| do |
| { |
| if (t->stmt) |
| { |
| struct wild_stmt_list *sl; |
| for (sl = t->stmt; sl; sl = sl->next) |
| { |
| walk_wild_section_match (sl->stmt, file, s); |
| //printf (" ZZZ maybe place into %p\n", sl->stmt); |
| } |
| } |
| if (!c) |
| break; |
| c = *sname++; |
| t = get_prefix_tree (&t->child, c, false); |
| } |
| while (t); |
| } |
| } |
| |
| /* Match all sections from all input files against the global prefix tree. */ |
| |
| static void |
| resolve_wilds (void) |
| { |
| LANG_FOR_EACH_INPUT_STATEMENT (f) |
| { |
| //printf("XXX %s\n", f->filename); |
| if (f->the_bfd == NULL |
| || !bfd_check_format (f->the_bfd, bfd_archive)) |
| resolve_wild_sections (f); |
| 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) |
| resolve_wild_sections (bfd_usrdata (member)); |
| |
| member = bfd_openr_next_archived_file (f->the_bfd, member); |
| } |
| } |
| } |
| } |
| |
| /* For each input section that matches wild statement S calls |
| CALLBACK with DATA. */ |
| |
| static void |
| walk_wild (lang_wild_statement_type *s, callback_t callback, void *data) |
| { |
| lang_statement_union_type *l; |
| |
| for (l = s->matching_sections.head; l; l = l->header.next) |
| { |
| (*callback) (s, l->input_matcher.pattern, l->input_matcher.section, |
| l->input_matcher.input_stmt, 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; |
| |
| /* PR 30632: It is OK for name to be NULL. For example |
| see the initialization of first_file in lang_init(). */ |
| if (name != NULL) |
| { |
| name = ldfile_possibly_remap_input (name); |
| /* But if a name is remapped to NULL, it should be ignored. */ |
| if (name == NULL) |
| return NULL; |
| } |
| |
| 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; |
| p->sort_key = NULL; |
| |
| 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_list_init (&ret->s.output_section_statement.sort_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); |
| obstack_init (&pt_obstack); |
| |
| 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 (); |
| ldfile_remap_input_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) |
| 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_statement_union_type *after) |
| { |
| lang_statement_union_type **where; |
| lang_statement_union_type **assign = NULL; |
| bool ignore_first; |
| |
| ignore_first = after == 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; |
| case lang_input_matcher_enum: |
| FAIL (); |
| } |
| 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, 0, 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->owner != 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; |
| |
| where = insert_os_after ((lang_statement_union_type *) 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) |
| { |
| int len; |
| |
| minfo ("%s", m->soname); |
| len = strlen (m->soname); |
| |
| if (len >= 29) |
| { |
| print_nl (); |
| len = 0; |
| } |
| print_spaces (30 - 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; |
| |
| ldfile_print_input_remaps (); |
| |
| 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) |
| { |
| minfo (_("\nDiscarded input sections\n\n")); |
| dis_header_printed = true; |
| } |
| |
| print_input_section (s, true); |
| } |
| } |
| if (config.print_map_discarded && ! dis_header_printed) |
| minfo (_("\nThere are no discarded input sections\n")); |
| |
| 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) |
| { |
| fprintf (config.map_file, "%-16s", m->name_list.name); |
| |
| char buf[32]; |
| bfd_sprintf_vma (link_info.output_bfd, buf, m->origin); |
| fprintf (config.map_file, " 0x%-16s", buf); |
| bfd_sprintf_vma (link_info.output_bfd, buf, m->length); |
| fprintf (config.map_file, |
| " 0x%*s", m->flags || m->not_flags ? -17 : 0, buf); |
| if (m->flags) |
| lang_map_flags (m->flags); |
| |
| if (m->not_flags) |
| { |
| minfo ("!"); |
| lang_map_flags (m->not_flags); |
| } |
| |
| print_nl (); |
| } |
| |
| minfo (_("\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, s, |
| "section alignment"); |
| } |
| |
| static flagword |
| get_os_init_flag (lang_output_section_statement_type * os) |
| { |
| if (os != NULL) |
| switch (os->sectype) |
| { |
| case readonly_section: return SEC_READONLY; |
| case noload_section: return SEC_NEVER_LOAD; |
| default: break; |
| } |
| |
| return 0; |
| } |
| |
| /* 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: |
| { |
| lang_output_section_statement_type *os; |
| |
| os = lang_output_section_find (exp->name.name); |
| if (os != NULL && os->bfd_section == NULL) |
| init_os (os, get_os_init_flag (os)); |
| } |
| } |
| 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; |
| |
| /* Discard non-alloc sections if we are stripping section headers. */ |
| else if (config.no_section_header && (flags & SEC_ALLOC) == 0) |
| discard = true; |
| |
| return discard; |
| } |
| |
| /* Return TRUE if SECTION is never going to be added to output statement |
| OUTPUT. lang_add_section() definitely won't do anything with SECTION |
| if this returns TRUE. It may do something (or not) if this returns FALSE. |
| |
| Can be used as early-out to filter matches. This may set |
| output_section of SECTION, if it was unset, to the abs section in case |
| we discover SECTION to be always discarded. This may also give |
| warning messages. */ |
| |
| static bool |
| wont_add_section_p (asection *section, |
| lang_output_section_statement_type *output) |
| { |
| bool discard; |
| |
| /* 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 or |
| warning about it again. */ |
| section->output_section = bfd_abs_section_ptr; |
| } |
| else if (bfd_is_abs_section (section->output_section)) |
| ; |
| 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 true; |
| } |
| |
| if (section->output_section != NULL) |
| { |
| if (!link_info.non_contiguous_regions) |
| return true; |
| |
| /* SECTION has already been handled in a special way |
| (eg. LINK_ONCE): skip it. */ |
| if (bfd_is_abs_section (section->output_section)) |
| return true; |
| |
| /* 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 true; |
| |
| 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. */ |
| } |
| |
| return false; |
| } |
| |
| /* 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; |
| |
| lang_input_section_type *new_section; |
| bfd *abfd = link_info.output_bfd; |
| |
| if (wont_add_section_p (section, output)) |
| return; |
| |
| if (sflag_info) |
| { |
| bool keep; |
| |
| keep = bfd_lookup_section_flags (&link_info, sflag_info, section); |
| if (!keep) |
| return; |
| } |
| |
| /* 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: |
| case type_section: |
| break; |
| case noalloc_section: |
| flags &= ~SEC_ALLOC; |
| break; |
| case typed_readonly_section: |
| 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; |
| } |
| |
| /* When a .ctors section is placed in .init_array it must be copied |
| in reverse order. Similarly for .dtors. Set that up. */ |
| if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour |
| && ((startswith (section->name, ".ctors") |
| && strcmp (output->bfd_section->name, ".init_array") == 0) |
| || (startswith (section->name, ".dtors") |
| && strcmp (output->bfd_section->name, ".fini_array") == 0)) |
| && (section->name[6] == 0 || section->name[6] == '.')) |
| section->flags |= SEC_ELF_REVERSE_COPY; |
| |
| 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; |
| } |
| |
| /* Expand a wild statement for a particular FILE. SECTION may be |
| NULL, in which case it is a wild card. This assumes that the |
| wild statement doesn't need any sorting (of filenames or sections). */ |
| |
| static void |
| output_section_callback_nosort (lang_wild_statement_type *ptr, |
| 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; |
| |
| lang_add_section (&ptr->children, section, ptr->section_list, |
| ptr->section_flag_list, os); |
| } |
| |
| /* 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)) |
| { |
| if (err == bfd_error_file_ambiguously_recognized) |
| free (matching); |
| 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); |
| free (matching); |
| 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; |
| |
| if (!bfd_check_format (member, bfd_object)) |
| { |
| einfo (_("%F%P: %pB: member %pB in archive is not an object\n"), |
| entry->the_bfd, member); |
| loaded = false; |
| } |
| |
| subsbfd = member; |
| if (!(*link_info.callbacks |
| ->add_archive_element) (&link_info, member, |
| "--whole-archive", &subsbfd)) |
| abort (); |
| |
| /* Potentially, the add_archive_element hook may have set a |
| substitute BFD for us. */ |
| if (!bfd_link_add_symbols (subsbfd, &link_info)) |
| { |
| einfo (_("%F%P: %pB: error adding symbols: %E\n"), member); |
| loaded = false; |
| } |
| } |
| |
| entry->flags.loaded = loaded; |
| return loaded; |
| } |
| break; |
| } |
| |
| if (bfd_link_add_symbols (entry->the_bfd, &link_info)) |
| entry->flags.loaded = true; |
|