| # This shell script emits a C file. -*- C -*- |
| # Copyright (C) 2003-2016 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. |
| # |
| |
| # This file is sourced from elf32.em, and defines extra xtensa-elf |
| # specific routines. |
| # |
| fragment <<EOF |
| |
| #include <xtensa-config.h> |
| #include "../bfd/elf-bfd.h" |
| #include "../bfd/libbfd.h" |
| #include "elf/xtensa.h" |
| #include "bfd.h" |
| |
| /* Provide default values for new configuration settings. */ |
| #ifndef XSHAL_ABI |
| #define XSHAL_ABI 0 |
| #endif |
| |
| static void xtensa_wild_group_interleave (lang_statement_union_type *); |
| static void xtensa_colocate_output_literals (lang_statement_union_type *); |
| static void xtensa_strip_inconsistent_linkonce_sections |
| (lang_statement_list_type *); |
| |
| |
| /* This number is irrelevant until we turn on use_literal_pages */ |
| static bfd_vma xtensa_page_power = 12; /* 4K pages. */ |
| |
| /* To force a page break between literals and text, change |
| xtensa_use_literal_pages to "TRUE". */ |
| static bfd_boolean xtensa_use_literal_pages = FALSE; |
| |
| #define EXTRA_VALIDATION 0 |
| |
| |
| static char * |
| elf_xtensa_choose_target (int argc ATTRIBUTE_UNUSED, |
| char **argv ATTRIBUTE_UNUSED) |
| { |
| if (XCHAL_HAVE_BE) |
| return "${BIG_OUTPUT_FORMAT}"; |
| else |
| return "${LITTLE_OUTPUT_FORMAT}"; |
| } |
| |
| |
| static void |
| elf_xtensa_before_parse (void) |
| { |
| /* Just call the default hook.... Tensilica's version of this function |
| does some other work that isn't relevant here. */ |
| gld${EMULATION_NAME}_before_parse (); |
| } |
| |
| |
| static void |
| remove_section (bfd *abfd, asection *os) |
| { |
| asection **spp; |
| for (spp = &abfd->sections; *spp; spp = &(*spp)->next) |
| if (*spp == os) |
| { |
| *spp = os->next; |
| os->owner->section_count--; |
| break; |
| } |
| } |
| |
| |
| static bfd_boolean |
| replace_insn_sec_with_prop_sec (bfd *abfd, |
| const char *insn_sec_name, |
| const char *prop_sec_name, |
| char **error_message) |
| { |
| asection *insn_sec; |
| asection *prop_sec; |
| bfd_byte *prop_contents = NULL; |
| bfd_byte *insn_contents = NULL; |
| unsigned entry_count; |
| unsigned entry; |
| Elf_Internal_Shdr *rel_hdr; |
| Elf_Internal_Rela *internal_relocs = NULL; |
| unsigned reloc_count; |
| |
| *error_message = ""; |
| insn_sec = bfd_get_section_by_name (abfd, insn_sec_name); |
| if (insn_sec == NULL) |
| return TRUE; |
| entry_count = insn_sec->size / 8; |
| |
| prop_sec = bfd_get_section_by_name (abfd, prop_sec_name); |
| if (prop_sec != NULL && insn_sec != NULL) |
| { |
| *error_message = _("file already has property tables"); |
| return FALSE; |
| } |
| |
| if (insn_sec->size != 0) |
| { |
| insn_contents = (bfd_byte *) bfd_malloc (insn_sec->size); |
| if (insn_contents == NULL) |
| { |
| *error_message = _("out of memory"); |
| goto cleanup; |
| } |
| if (! bfd_get_section_contents (abfd, insn_sec, insn_contents, |
| (file_ptr) 0, insn_sec->size)) |
| { |
| *error_message = _("failed to read section contents"); |
| goto cleanup; |
| } |
| } |
| |
| /* Create a property table section for it. */ |
| prop_sec_name = strdup (prop_sec_name); |
| prop_sec = bfd_make_section_with_flags |
| (abfd, prop_sec_name, bfd_get_section_flags (abfd, insn_sec)); |
| if (prop_sec == NULL |
| || ! bfd_set_section_alignment (abfd, prop_sec, 2)) |
| { |
| *error_message = _("could not create new section"); |
| goto cleanup; |
| } |
| |
| prop_sec->size = entry_count * 12; |
| prop_contents = (bfd_byte *) bfd_zalloc (abfd, prop_sec->size); |
| elf_section_data (prop_sec)->this_hdr.contents = prop_contents; |
| |
| /* The entry size and size must be set to allow the linker to compute |
| the number of relocations since it does not use reloc_count. */ |
| rel_hdr = _bfd_elf_single_rel_hdr (prop_sec); |
| rel_hdr->sh_entsize = sizeof (Elf32_External_Rela); |
| rel_hdr->sh_size = _bfd_elf_single_rel_hdr (insn_sec)->sh_size; |
| |
| if (prop_contents == NULL && prop_sec->size != 0) |
| { |
| *error_message = _("could not allocate section contents"); |
| goto cleanup; |
| } |
| |
| /* Read the relocations. */ |
| reloc_count = insn_sec->reloc_count; |
| if (reloc_count != 0) |
| { |
| /* If there is already an internal_reloc, then save it so that the |
| read_relocs function freshly allocates a copy. */ |
| Elf_Internal_Rela *saved_relocs = elf_section_data (insn_sec)->relocs; |
| |
| elf_section_data (insn_sec)->relocs = NULL; |
| internal_relocs = |
| _bfd_elf_link_read_relocs (abfd, insn_sec, NULL, NULL, FALSE); |
| elf_section_data (insn_sec)->relocs = saved_relocs; |
| |
| if (internal_relocs == NULL) |
| { |
| *error_message = _("out of memory"); |
| goto cleanup; |
| } |
| } |
| |
| /* Create a relocation section for the property section. */ |
| if (internal_relocs != NULL) |
| { |
| elf_section_data (prop_sec)->relocs = internal_relocs; |
| prop_sec->reloc_count = reloc_count; |
| } |
| |
| /* Now copy each insn table entry to the prop table entry with |
| appropriate flags. */ |
| for (entry = 0; entry < entry_count; ++entry) |
| { |
| unsigned value; |
| unsigned flags = (XTENSA_PROP_INSN | XTENSA_PROP_NO_TRANSFORM |
| | XTENSA_PROP_INSN_NO_REORDER); |
| value = bfd_get_32 (abfd, insn_contents + entry * 8 + 0); |
| bfd_put_32 (abfd, value, prop_contents + entry * 12 + 0); |
| value = bfd_get_32 (abfd, insn_contents + entry * 8 + 4); |
| bfd_put_32 (abfd, value, prop_contents + entry * 12 + 4); |
| bfd_put_32 (abfd, flags, prop_contents + entry * 12 + 8); |
| } |
| |
| /* Now copy all of the relocations. Change offsets for the |
| instruction table section to offsets in the property table |
| section. */ |
| if (internal_relocs) |
| { |
| unsigned i; |
| |
| for (i = 0; i < reloc_count; i++) |
| { |
| Elf_Internal_Rela *rela; |
| unsigned r_offset; |
| |
| rela = &internal_relocs[i]; |
| |
| /* If this relocation is to the .xt.insn section, |
| change the section number and the offset. */ |
| r_offset = rela->r_offset; |
| r_offset += 4 * (r_offset / 8); |
| rela->r_offset = r_offset; |
| } |
| } |
| |
| remove_section (abfd, insn_sec); |
| |
| if (insn_contents) |
| free (insn_contents); |
| |
| return TRUE; |
| |
| cleanup: |
| if (prop_sec && prop_sec->owner) |
| remove_section (abfd, prop_sec); |
| if (insn_contents) |
| free (insn_contents); |
| if (internal_relocs) |
| free (internal_relocs); |
| |
| return FALSE; |
| } |
| |
| |
| #define PROP_SEC_BASE_NAME ".xt.prop" |
| #define INSN_SEC_BASE_NAME ".xt.insn" |
| #define LINKONCE_SEC_OLD_TEXT_BASE_NAME ".gnu.linkonce.x." |
| |
| |
| static void |
| replace_instruction_table_sections (bfd *abfd, asection *sec) |
| { |
| char *message = ""; |
| const char *insn_sec_name = NULL; |
| char *prop_sec_name = NULL; |
| char *owned_prop_sec_name = NULL; |
| const char *sec_name; |
| |
| sec_name = bfd_get_section_name (abfd, sec); |
| if (strcmp (sec_name, INSN_SEC_BASE_NAME) == 0) |
| { |
| insn_sec_name = INSN_SEC_BASE_NAME; |
| prop_sec_name = PROP_SEC_BASE_NAME; |
| } |
| else if (CONST_STRNEQ (sec_name, LINKONCE_SEC_OLD_TEXT_BASE_NAME)) |
| { |
| insn_sec_name = sec_name; |
| owned_prop_sec_name = (char *) xmalloc (strlen (sec_name) + 20); |
| prop_sec_name = owned_prop_sec_name; |
| strcpy (prop_sec_name, ".gnu.linkonce.prop.t."); |
| strcat (prop_sec_name, |
| sec_name + strlen (LINKONCE_SEC_OLD_TEXT_BASE_NAME)); |
| } |
| if (insn_sec_name != NULL) |
| { |
| if (! replace_insn_sec_with_prop_sec (abfd, insn_sec_name, prop_sec_name, |
| &message)) |
| { |
| einfo (_("%P: warning: failed to convert %s table in %B (%s); subsequent disassembly may be incomplete\n"), |
| insn_sec_name, abfd, message); |
| } |
| } |
| if (owned_prop_sec_name) |
| free (owned_prop_sec_name); |
| } |
| |
| |
| /* This is called after all input sections have been opened to convert |
| instruction tables (.xt.insn, gnu.linkonce.x.*) tables into property |
| tables (.xt.prop) before any section placement. */ |
| |
| static void |
| elf_xtensa_after_open (void) |
| { |
| /* First call the ELF version. */ |
| gld${EMULATION_NAME}_after_open (); |
| |
| /* Now search the input files looking for instruction table sections. */ |
| LANG_FOR_EACH_INPUT_STATEMENT (f) |
| { |
| asection *sec = f->the_bfd->sections; |
| asection *next_sec; |
| |
| /* Do not use bfd_map_over_sections here since we are removing |
| sections as we iterate. */ |
| while (sec != NULL) |
| { |
| next_sec = sec->next; |
| replace_instruction_table_sections (f->the_bfd, sec); |
| sec = next_sec; |
| } |
| } |
| } |
| |
| |
| static bfd_boolean |
| xt_config_info_unpack_and_check (char *data, |
| bfd_boolean *pmismatch, |
| char **pmsg) |
| { |
| char *d, *key; |
| unsigned num; |
| |
| *pmismatch = FALSE; |
| |
| d = data; |
| while (*d) |
| { |
| key = d; |
| d = strchr (d, '='); |
| if (! d) |
| goto error; |
| |
| /* Overwrite the equal sign. */ |
| *d++ = 0; |
| |
| /* Check if this is a quoted string or a number. */ |
| if (*d == '"') |
| { |
| /* No string values are currently checked by LD; |
| just skip over the quotes. */ |
| d++; |
| d = strchr (d, '"'); |
| if (! d) |
| goto error; |
| /* Overwrite the trailing quote. */ |
| *d++ = 0; |
| } |
| else |
| { |
| if (*d == 0) |
| goto error; |
| num = strtoul (d, &d, 0); |
| |
| if (! strcmp (key, "ABI")) |
| { |
| if (num != XSHAL_ABI) |
| { |
| *pmismatch = TRUE; |
| *pmsg = "ABI does not match"; |
| } |
| } |
| else if (! strcmp (key, "USE_ABSOLUTE_LITERALS")) |
| { |
| if (num != XSHAL_USE_ABSOLUTE_LITERALS) |
| { |
| *pmismatch = TRUE; |
| *pmsg = "incompatible use of the Extended L32R option"; |
| } |
| } |
| } |
| |
| if (*d++ != '\n') |
| goto error; |
| } |
| |
| return TRUE; |
| |
| error: |
| return FALSE; |
| } |
| |
| |
| #define XTINFO_NAME "Xtensa_Info" |
| #define XTINFO_NAMESZ 12 |
| #define XTINFO_TYPE 1 |
| |
| static void |
| check_xtensa_info (bfd *abfd, asection *info_sec) |
| { |
| char *data, *errmsg = ""; |
| bfd_boolean mismatch; |
| |
| data = xmalloc (info_sec->size); |
| if (! bfd_get_section_contents (abfd, info_sec, data, 0, info_sec->size)) |
| einfo (_("%F%P:%B: cannot read contents of section %A\n"), abfd, info_sec); |
| |
| if (info_sec->size > 24 |
| && info_sec->size >= 24 + bfd_get_32 (abfd, data + 4) |
| && bfd_get_32 (abfd, data + 0) == XTINFO_NAMESZ |
| && bfd_get_32 (abfd, data + 8) == XTINFO_TYPE |
| && strcmp (data + 12, XTINFO_NAME) == 0 |
| && xt_config_info_unpack_and_check (data + 12 + XTINFO_NAMESZ, |
| &mismatch, &errmsg)) |
| { |
| if (mismatch) |
| einfo (_("%P:%B: warning: incompatible Xtensa configuration (%s)\n"), |
| abfd, errmsg); |
| } |
| else |
| einfo (_("%P:%B: warning: cannot parse .xtensa.info section\n"), abfd); |
| |
| free (data); |
| } |
| |
| |
| /* This is called after the sections have been attached to output |
| sections, but before any sizes or addresses have been set. */ |
| |
| static void |
| elf_xtensa_before_allocation (void) |
| { |
| asection *info_sec, *first_info_sec; |
| bfd *first_bfd; |
| bfd_boolean is_big_endian = XCHAL_HAVE_BE; |
| |
| /* Check that the output endianness matches the Xtensa |
| configuration. The BFD library always includes both big and |
| little endian target vectors for Xtensa, but it only supports the |
| detailed instruction encode/decode operations (such as are |
| required to process relocations) for the selected Xtensa |
| configuration. */ |
| |
| if (is_big_endian |
| && link_info.output_bfd->xvec->byteorder == BFD_ENDIAN_LITTLE) |
| { |
| einfo (_("%F%P: little endian output does not match " |
| "Xtensa configuration\n")); |
| } |
| if (!is_big_endian |
| && link_info.output_bfd->xvec->byteorder == BFD_ENDIAN_BIG) |
| { |
| einfo (_("%F%P: big endian output does not match " |
| "Xtensa configuration\n")); |
| } |
| |
| /* Keep track of the first input .xtensa.info section, and as a fallback, |
| the first input bfd where a .xtensa.info section could be created. |
| After the input .xtensa.info has been checked, the contents of the |
| first one will be replaced with the output .xtensa.info table. */ |
| first_info_sec = 0; |
| first_bfd = 0; |
| |
| LANG_FOR_EACH_INPUT_STATEMENT (f) |
| { |
| /* Check that the endianness for each input file matches the output. |
| The merge_private_bfd_data hook has already reported any mismatches |
| as errors, but those errors are not fatal. At this point, we |
| cannot go any further if there are any mismatches. */ |
| if ((is_big_endian && f->the_bfd->xvec->byteorder == BFD_ENDIAN_LITTLE) |
| || (!is_big_endian && f->the_bfd->xvec->byteorder == BFD_ENDIAN_BIG)) |
| einfo (_("%F%P: cross-endian linking for %B not supported\n"), |
| f->the_bfd); |
| |
| if (! first_bfd) |
| first_bfd = f->the_bfd; |
| |
| info_sec = bfd_get_section_by_name (f->the_bfd, ".xtensa.info"); |
| if (! info_sec) |
| continue; |
| |
| if (! first_info_sec) |
| first_info_sec = info_sec; |
| |
| /* Unpack the .xtensa.info section and check it against the current |
| Xtensa configuration. */ |
| check_xtensa_info (f->the_bfd, info_sec); |
| |
| /* Do not include this copy of .xtensa.info in the output. */ |
| info_sec->size = 0; |
| info_sec->flags |= SEC_EXCLUDE; |
| } |
| |
| /* Reuse the first .xtensa.info input section to hold the output |
| .xtensa.info; or, if none were found, create a new section in the |
| first input bfd (assuming there is one). */ |
| info_sec = first_info_sec; |
| if (! info_sec && first_bfd) |
| { |
| info_sec = bfd_make_section_with_flags (first_bfd, ".xtensa.info", |
| SEC_HAS_CONTENTS | SEC_READONLY); |
| if (! info_sec) |
| einfo (_("%F%P: failed to create .xtensa.info section\n")); |
| } |
| if (info_sec) |
| { |
| int xtensa_info_size; |
| char *data; |
| |
| info_sec->flags &= ~SEC_EXCLUDE; |
| info_sec->flags |= SEC_IN_MEMORY; |
| |
| data = xmalloc (100); |
| sprintf (data, "USE_ABSOLUTE_LITERALS=%d\nABI=%d\n", |
| XSHAL_USE_ABSOLUTE_LITERALS, XSHAL_ABI); |
| xtensa_info_size = strlen (data) + 1; |
| |
| /* Add enough null terminators to pad to a word boundary. */ |
| do |
| data[xtensa_info_size++] = 0; |
| while ((xtensa_info_size & 3) != 0); |
| |
| info_sec->size = 12 + XTINFO_NAMESZ + xtensa_info_size; |
| info_sec->contents = xmalloc (info_sec->size); |
| bfd_put_32 (info_sec->owner, XTINFO_NAMESZ, info_sec->contents + 0); |
| bfd_put_32 (info_sec->owner, xtensa_info_size, info_sec->contents + 4); |
| bfd_put_32 (info_sec->owner, XTINFO_TYPE, info_sec->contents + 8); |
| memcpy (info_sec->contents + 12, XTINFO_NAME, XTINFO_NAMESZ); |
| memcpy (info_sec->contents + 12 + XTINFO_NAMESZ, data, xtensa_info_size); |
| free (data); |
| } |
| |
| /* Enable relaxation by default if the "--no-relax" option was not |
| specified. This is done here instead of in the before_parse hook |
| because there is a check in main() to prohibit use of --relax and |
| -r together and that combination should be allowed for Xtensa. */ |
| if (RELAXATION_DISABLED_BY_DEFAULT) |
| ENABLE_RELAXATION; |
| |
| xtensa_strip_inconsistent_linkonce_sections (stat_ptr); |
| |
| gld${EMULATION_NAME}_before_allocation (); |
| |
| xtensa_wild_group_interleave (stat_ptr->head); |
| |
| if (RELAXATION_ENABLED) |
| xtensa_colocate_output_literals (stat_ptr->head); |
| |
| /* TBD: We need to force the page alignments to here and only do |
| them as needed for the entire output section. Finally, if this |
| is a relocatable link then we need to add alignment notes so |
| that the literals can be separated later. */ |
| } |
| |
| |
| typedef struct wildcard_list section_name_list; |
| |
| typedef struct reloc_deps_e_t reloc_deps_e; |
| typedef struct reloc_deps_section_t reloc_deps_section; |
| typedef struct reloc_deps_graph_t reloc_deps_graph; |
| |
| |
| struct reloc_deps_e_t |
| { |
| asection *src; /* Contains l32rs. */ |
| asection *tgt; /* Contains literals. */ |
| reloc_deps_e *next; |
| }; |
| |
| /* Place these in the userdata field. */ |
| struct reloc_deps_section_t |
| { |
| reloc_deps_e *preds; |
| reloc_deps_e *succs; |
| bfd_boolean is_only_literal; |
| }; |
| |
| |
| struct reloc_deps_graph_t |
| { |
| size_t count; |
| size_t size; |
| asection **sections; |
| }; |
| |
| static void xtensa_layout_wild |
| (const reloc_deps_graph *, lang_wild_statement_type *); |
| |
| typedef void (*deps_callback_t) (asection *, /* src_sec */ |
| bfd_vma, /* src_offset */ |
| asection *, /* target_sec */ |
| bfd_vma, /* target_offset */ |
| void *); /* closure */ |
| |
| extern bfd_boolean xtensa_callback_required_dependence |
| (bfd *, asection *, struct bfd_link_info *, deps_callback_t, void *); |
| static void xtensa_ldlang_clear_addresses (lang_statement_union_type *); |
| static bfd_boolean ld_local_file_relocations_fit |
| (lang_statement_union_type *, const reloc_deps_graph *); |
| static bfd_vma ld_assign_relative_paged_dot |
| (bfd_vma, lang_statement_union_type *, const reloc_deps_graph *, |
| bfd_boolean); |
| static bfd_vma ld_xtensa_insert_page_offsets |
| (bfd_vma, lang_statement_union_type *, reloc_deps_graph *, bfd_boolean); |
| #if EXTRA_VALIDATION |
| static size_t ld_count_children (lang_statement_union_type *); |
| #endif |
| |
| extern lang_statement_list_type constructor_list; |
| |
| static reloc_deps_section * |
| xtensa_get_section_deps (const reloc_deps_graph *deps ATTRIBUTE_UNUSED, |
| asection *sec) |
| { |
| /* We have a separate function for this so that |
| we could in the future keep a completely independent |
| structure that maps a section to its dependence edges. |
| For now, we place these in the sec->userdata field. */ |
| reloc_deps_section *sec_deps = sec->userdata; |
| return sec_deps; |
| } |
| |
| static void |
| xtensa_set_section_deps (const reloc_deps_graph *deps ATTRIBUTE_UNUSED, |
| asection *sec, |
| reloc_deps_section *deps_section) |
| { |
| sec->userdata = deps_section; |
| } |
| |
| |
| /* This is used to keep a list of all of the sections participating in |
| the graph so we can clean them up quickly. */ |
| |
| static void |
| xtensa_append_section_deps (reloc_deps_graph *deps, asection *sec) |
| { |
| if (deps->size <= deps->count) |
| { |
| asection **new_sections; |
| size_t i; |
| size_t new_size; |
| |
| new_size = deps->size * 2; |
| if (new_size == 0) |
| new_size = 20; |
| |
| new_sections = xmalloc (sizeof (asection *) * new_size); |
| memset (new_sections, 0, sizeof (asection *) * new_size); |
| for (i = 0; i < deps->count; i++) |
| { |
| new_sections[i] = deps->sections[i]; |
| } |
| if (deps->sections != NULL) |
| free (deps->sections); |
| deps->sections = new_sections; |
| deps->size = new_size; |
| } |
| deps->sections[deps->count] = sec; |
| deps->count++; |
| } |
| |
| |
| static void |
| free_reloc_deps_graph (reloc_deps_graph *deps) |
| { |
| size_t i; |
| for (i = 0; i < deps->count; i++) |
| { |
| asection *sec = deps->sections[i]; |
| reloc_deps_section *sec_deps; |
| sec_deps = xtensa_get_section_deps (deps, sec); |
| if (sec_deps) |
| { |
| reloc_deps_e *next; |
| while (sec_deps->succs != NULL) |
| { |
| next = sec_deps->succs->next; |
| free (sec_deps->succs); |
| sec_deps->succs = next; |
| } |
| |
| while (sec_deps->preds != NULL) |
| { |
| next = sec_deps->preds->next; |
| free (sec_deps->preds); |
| sec_deps->preds = next; |
| } |
| free (sec_deps); |
| } |
| xtensa_set_section_deps (deps, sec, NULL); |
| } |
| if (deps->sections) |
| free (deps->sections); |
| |
| free (deps); |
| } |
| |
| |
| static bfd_boolean |
| section_is_source (const reloc_deps_graph *deps ATTRIBUTE_UNUSED, |
| lang_statement_union_type *s) |
| { |
| asection *sec; |
| const reloc_deps_section *sec_deps; |
| |
| if (s->header.type != lang_input_section_enum) |
| return FALSE; |
| sec = s->input_section.section; |
| |
| sec_deps = xtensa_get_section_deps (deps, sec); |
| return sec_deps && sec_deps->succs != NULL; |
| } |
| |
| |
| static bfd_boolean |
| section_is_target (const reloc_deps_graph *deps ATTRIBUTE_UNUSED, |
| lang_statement_union_type *s) |
| { |
| asection *sec; |
| const reloc_deps_section *sec_deps; |
| |
| if (s->header.type != lang_input_section_enum) |
| return FALSE; |
| sec = s->input_section.section; |
| |
| sec_deps = xtensa_get_section_deps (deps, sec); |
| return sec_deps && sec_deps->preds != NULL; |
| } |
| |
| |
| static bfd_boolean |
| section_is_source_or_target (const reloc_deps_graph *deps ATTRIBUTE_UNUSED, |
| lang_statement_union_type *s) |
| { |
| return (section_is_source (deps, s) |
| || section_is_target (deps, s)); |
| } |
| |
| |
| typedef struct xtensa_ld_iter_stack_t xtensa_ld_iter_stack; |
| typedef struct xtensa_ld_iter_t xtensa_ld_iter; |
| |
| struct xtensa_ld_iter_t |
| { |
| lang_statement_union_type *parent; /* Parent of the list. */ |
| lang_statement_list_type *l; /* List that holds it. */ |
| lang_statement_union_type **loc; /* Place in the list. */ |
| }; |
| |
| struct xtensa_ld_iter_stack_t |
| { |
| xtensa_ld_iter iterloc; /* List that hold it. */ |
| |
| xtensa_ld_iter_stack *next; /* Next in the stack. */ |
| xtensa_ld_iter_stack *prev; /* Back pointer for stack. */ |
| }; |
| |
| |
| static void |
| ld_xtensa_move_section_after (xtensa_ld_iter *to, xtensa_ld_iter *current) |
| { |
| lang_statement_union_type *to_next; |
| lang_statement_union_type *current_next; |
| lang_statement_union_type **e; |
| |
| #if EXTRA_VALIDATION |
| size_t old_to_count, new_to_count; |
| size_t old_current_count, new_current_count; |
| #endif |
| |
| if (to == current) |
| return; |
| |
| #if EXTRA_VALIDATION |
| old_to_count = ld_count_children (to->parent); |
| old_current_count = ld_count_children (current->parent); |
| #endif |
| |
| to_next = *(to->loc); |
| current_next = (*current->loc)->header.next; |
| |
| *(to->loc) = *(current->loc); |
| |
| *(current->loc) = current_next; |
| (*(to->loc))->header.next = to_next; |
| |
| /* reset "to" list tail */ |
| for (e = &to->l->head; *e != NULL; e = &(*e)->header.next) |
| ; |
| to->l->tail = e; |
| |
| /* reset "current" list tail */ |
| for (e = ¤t->l->head; *e != NULL; e = &(*e)->header.next) |
| ; |
| current->l->tail = e; |
| |
| #if EXTRA_VALIDATION |
| new_to_count = ld_count_children (to->parent); |
| new_current_count = ld_count_children (current->parent); |
| |
| ASSERT ((old_to_count + old_current_count) |
| == (new_to_count + new_current_count)); |
| #endif |
| } |
| |
| |
| /* Can only be called with lang_statements that have lists. Returns |
| FALSE if the list is empty. */ |
| |
| static bfd_boolean |
| iter_stack_empty (xtensa_ld_iter_stack **stack_p) |
| { |
| return *stack_p == NULL; |
| } |
| |
| |
| static bfd_boolean |
| iter_stack_push (xtensa_ld_iter_stack **stack_p, |
| lang_statement_union_type *parent) |
| { |
| xtensa_ld_iter_stack *stack; |
| lang_statement_list_type *l = NULL; |
| |
| switch (parent->header.type) |
| { |
| case lang_output_section_statement_enum: |
| l = &parent->output_section_statement.children; |
| break; |
| case lang_wild_statement_enum: |
| l = &parent->wild_statement.children; |
| break; |
| case lang_group_statement_enum: |
| l = &parent->group_statement.children; |
| break; |
| default: |
| ASSERT (0); |
| return FALSE; |
| } |
| |
| /* Empty. do not push. */ |
| if (l->tail == &l->head) |
| return FALSE; |
| |
| stack = xmalloc (sizeof (xtensa_ld_iter_stack)); |
| memset (stack, 0, sizeof (xtensa_ld_iter_stack)); |
| stack->iterloc.parent = parent; |
| stack->iterloc.l = l; |
| stack->iterloc.loc = &l->head; |
| |
| stack->next = *stack_p; |
| stack->prev = NULL; |
| if (*stack_p != NULL) |
| (*stack_p)->prev = stack; |
| *stack_p = stack; |
| return TRUE; |
| } |
| |
| |
| static void |
| iter_stack_pop (xtensa_ld_iter_stack **stack_p) |
| { |
| xtensa_ld_iter_stack *stack; |
| |
| stack = *stack_p; |
| |
| if (stack == NULL) |
| { |
| ASSERT (stack != NULL); |
| return; |
| } |
| |
| if (stack->next != NULL) |
| stack->next->prev = NULL; |
| |
| *stack_p = stack->next; |
| free (stack); |
| } |
| |
| |
| /* This MUST be called if, during iteration, the user changes the |
| underlying structure. It will check for a NULL current and advance |
| accordingly. */ |
| |
| static void |
| iter_stack_update (xtensa_ld_iter_stack **stack_p) |
| { |
| if (!iter_stack_empty (stack_p) |
| && (*(*stack_p)->iterloc.loc) == NULL) |
| { |
| iter_stack_pop (stack_p); |
| |
| while (!iter_stack_empty (stack_p) |
| && ((*(*stack_p)->iterloc.loc)->header.next == NULL)) |
| { |
| iter_stack_pop (stack_p); |
| } |
| if (!iter_stack_empty (stack_p)) |
| (*stack_p)->iterloc.loc = &(*(*stack_p)->iterloc.loc)->header.next; |
| } |
| } |
| |
| |
| static void |
| iter_stack_next (xtensa_ld_iter_stack **stack_p) |
| { |
| xtensa_ld_iter_stack *stack; |
| lang_statement_union_type *current; |
| stack = *stack_p; |
| |
| current = *stack->iterloc.loc; |
| /* If we are on the first element. */ |
| if (current != NULL) |
| { |
| switch (current->header.type) |
| { |
| case lang_output_section_statement_enum: |
| case lang_wild_statement_enum: |
| case lang_group_statement_enum: |
| /* If the list if not empty, we are done. */ |
| if (iter_stack_push (stack_p, *stack->iterloc.loc)) |
| return; |
| /* Otherwise increment the pointer as normal. */ |
| break; |
| default: |
| break; |
| } |
| } |
| |
| while (!iter_stack_empty (stack_p) |
| && ((*(*stack_p)->iterloc.loc)->header.next == NULL)) |
| { |
| iter_stack_pop (stack_p); |
| } |
| if (!iter_stack_empty (stack_p)) |
| (*stack_p)->iterloc.loc = &(*(*stack_p)->iterloc.loc)->header.next; |
| } |
| |
| |
| static lang_statement_union_type * |
| iter_stack_current (xtensa_ld_iter_stack **stack_p) |
| { |
| return *((*stack_p)->iterloc.loc); |
| } |
| |
| |
| /* The iter stack is a preorder. */ |
| |
| static void |
| iter_stack_create (xtensa_ld_iter_stack **stack_p, |
| lang_statement_union_type *parent) |
| { |
| iter_stack_push (stack_p, parent); |
| } |
| |
| |
| static void |
| iter_stack_copy_current (xtensa_ld_iter_stack **stack_p, xtensa_ld_iter *front) |
| { |
| *front = (*stack_p)->iterloc; |
| } |
| |
| |
| static void |
| xtensa_colocate_literals (reloc_deps_graph *deps, |
| lang_statement_union_type *statement) |
| { |
| /* Keep a stack of pointers to control iteration through the contours. */ |
| xtensa_ld_iter_stack *stack = NULL; |
| xtensa_ld_iter_stack **stack_p = &stack; |
| |
| xtensa_ld_iter front; /* Location where new insertion should occur. */ |
| xtensa_ld_iter *front_p = NULL; |
| |
| xtensa_ld_iter current; /* Location we are checking. */ |
| xtensa_ld_iter *current_p = NULL; |
| bfd_boolean in_literals = FALSE; |
| |
| if (deps->count == 0) |
| return; |
| |
| iter_stack_create (stack_p, statement); |
| |
| while (!iter_stack_empty (stack_p)) |
| { |
| bfd_boolean skip_increment = FALSE; |
| lang_statement_union_type *l = iter_stack_current (stack_p); |
| |
| switch (l->header.type) |
| { |
| case lang_assignment_statement_enum: |
| /* Any assignment statement should block reordering across it. */ |
| front_p = NULL; |
| in_literals = FALSE; |
| break; |
| |
| case lang_input_section_enum: |
| if (front_p == NULL) |
| { |
| in_literals = (section_is_target (deps, l) |
| && !section_is_source (deps, l)); |
| if (in_literals) |
| { |
| front_p = &front; |
| iter_stack_copy_current (stack_p, front_p); |
| } |
| } |
| else |
| { |
| bfd_boolean is_target; |
| current_p = ¤t; |
| iter_stack_copy_current (stack_p, current_p); |
| is_target = (section_is_target (deps, l) |
| && !section_is_source (deps, l)); |
| |
| if (in_literals) |
| { |
| iter_stack_copy_current (stack_p, front_p); |
| if (!is_target) |
| in_literals = FALSE; |
| } |
| else |
| { |
| if (is_target) |
| { |
| /* Try to insert in place. */ |
| ld_xtensa_move_section_after (front_p, current_p); |
| ld_assign_relative_paged_dot (0x100000, |
| statement, |
| deps, |
| xtensa_use_literal_pages); |
| |
| /* We use this code because it's already written. */ |
| if (!ld_local_file_relocations_fit (statement, deps)) |
| { |
| /* Move it back. */ |
| ld_xtensa_move_section_after (current_p, front_p); |
| /* Reset the literal placement. */ |
| iter_stack_copy_current (stack_p, front_p); |
| } |
| else |
| { |
| /* Move front pointer up by one. */ |
| front_p->loc = &(*front_p->loc)->header.next; |
| |
| /* Do not increment the current pointer. */ |
| skip_increment = TRUE; |
| } |
| } |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| |
| if (!skip_increment) |
| iter_stack_next (stack_p); |
| else |
| /* Be careful to update the stack_p if it now is a null. */ |
| iter_stack_update (stack_p); |
| } |
| |
| lang_for_each_statement_worker (xtensa_ldlang_clear_addresses, statement); |
| } |
| |
| |
| static void |
| xtensa_move_dependencies_to_front (reloc_deps_graph *deps, |
| lang_wild_statement_type *w) |
| { |
| /* Keep a front pointer and a current pointer. */ |
| lang_statement_union_type **front; |
| lang_statement_union_type **current; |
| |
| /* Walk to the end of the targets. */ |
| for (front = &w->children.head; |
| (*front != NULL) && section_is_source_or_target (deps, *front); |
| front = &(*front)->header.next) |
| ; |
| |
| if (*front == NULL) |
| return; |
| |
| current = &(*front)->header.next; |
| while (*current != NULL) |
| { |
| if (section_is_source_or_target (deps, *current)) |
| { |
| /* Insert in place. */ |
| xtensa_ld_iter front_iter; |
| xtensa_ld_iter current_iter; |
| |
| front_iter.parent = (lang_statement_union_type *) w; |
| front_iter.l = &w->children; |
| front_iter.loc = front; |
| |
| current_iter.parent = (lang_statement_union_type *) w; |
| current_iter.l = &w->children; |
| current_iter.loc = current; |
| |
| ld_xtensa_move_section_after (&front_iter, ¤t_iter); |
| front = &(*front)->header.next; |
| } |
| else |
| { |
| current = &(*current)->header.next; |
| } |
| } |
| } |
| |
| |
| static bfd_boolean |
| deps_has_sec_edge (const reloc_deps_graph *deps, asection *src, asection *tgt) |
| { |
| const reloc_deps_section *sec_deps; |
| const reloc_deps_e *sec_deps_e; |
| |
| sec_deps = xtensa_get_section_deps (deps, src); |
| if (sec_deps == NULL) |
| return FALSE; |
| |
| for (sec_deps_e = sec_deps->succs; |
| sec_deps_e != NULL; |
| sec_deps_e = sec_deps_e->next) |
| { |
| ASSERT (sec_deps_e->src == src); |
| if (sec_deps_e->tgt == tgt) |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| |
| static bfd_boolean |
| deps_has_edge (const reloc_deps_graph *deps, |
| lang_statement_union_type *src, |
| lang_statement_union_type *tgt) |
| { |
| if (!section_is_source (deps, src)) |
| return FALSE; |
| if (!section_is_target (deps, tgt)) |
| return FALSE; |
| |
| if (src->header.type != lang_input_section_enum) |
| return FALSE; |
| if (tgt->header.type != lang_input_section_enum) |
| return FALSE; |
| |
| return deps_has_sec_edge (deps, src->input_section.section, |
| tgt->input_section.section); |
| } |
| |
| |
| static void |
| add_deps_edge (reloc_deps_graph *deps, asection *src_sec, asection *tgt_sec) |
| { |
| reloc_deps_section *src_sec_deps; |
| reloc_deps_section *tgt_sec_deps; |
| |
| reloc_deps_e *src_edge; |
| reloc_deps_e *tgt_edge; |
| |
| if (deps_has_sec_edge (deps, src_sec, tgt_sec)) |
| return; |
| |
| src_sec_deps = xtensa_get_section_deps (deps, src_sec); |
| if (src_sec_deps == NULL) |
| { |
| /* Add a section. */ |
| src_sec_deps = xmalloc (sizeof (reloc_deps_section)); |
| memset (src_sec_deps, 0, sizeof (reloc_deps_section)); |
| src_sec_deps->is_only_literal = 0; |
| src_sec_deps->preds = NULL; |
| src_sec_deps->succs = NULL; |
| xtensa_set_section_deps (deps, src_sec, src_sec_deps); |
| xtensa_append_section_deps (deps, src_sec); |
| } |
| |
| tgt_sec_deps = xtensa_get_section_deps (deps, tgt_sec); |
| if (tgt_sec_deps == NULL) |
| { |
| /* Add a section. */ |
| tgt_sec_deps = xmalloc (sizeof (reloc_deps_section)); |
| memset (tgt_sec_deps, 0, sizeof (reloc_deps_section)); |
| tgt_sec_deps->is_only_literal = 0; |
| tgt_sec_deps->preds = NULL; |
| tgt_sec_deps->succs = NULL; |
| xtensa_set_section_deps (deps, tgt_sec, tgt_sec_deps); |
| xtensa_append_section_deps (deps, tgt_sec); |
| } |
| |
| /* Add the edges. */ |
| src_edge = xmalloc (sizeof (reloc_deps_e)); |
| memset (src_edge, 0, sizeof (reloc_deps_e)); |
| src_edge->src = src_sec; |
| src_edge->tgt = tgt_sec; |
| src_edge->next = src_sec_deps->succs; |
| src_sec_deps->succs = src_edge; |
| |
| tgt_edge = xmalloc (sizeof (reloc_deps_e)); |
| memset (tgt_edge, 0, sizeof (reloc_deps_e)); |
| tgt_edge->src = src_sec; |
| tgt_edge->tgt = tgt_sec; |
| tgt_edge->next = tgt_sec_deps->preds; |
| tgt_sec_deps->preds = tgt_edge; |
| } |
| |
| |
| static void |
| build_deps_graph_callback (asection *src_sec, |
| bfd_vma src_offset ATTRIBUTE_UNUSED, |
| asection *target_sec, |
| bfd_vma target_offset ATTRIBUTE_UNUSED, |
| void *closure) |
| { |
| reloc_deps_graph *deps = closure; |
| |
| /* If the target is defined. */ |
| if (target_sec != NULL) |
| add_deps_edge (deps, src_sec, target_sec); |
| } |
| |
| |
| static reloc_deps_graph * |
| ld_build_required_section_dependence (lang_statement_union_type *s) |
| { |
| reloc_deps_graph *deps; |
| xtensa_ld_iter_stack *stack = NULL; |
| |
| deps = xmalloc (sizeof (reloc_deps_graph)); |
| deps->sections = NULL; |
| deps->count = 0; |
| deps->size = 0; |
| |
| for (iter_stack_create (&stack, s); |
| !iter_stack_empty (&stack); |
| iter_stack_next (&stack)) |
| { |
| lang_statement_union_type *l = iter_stack_current (&stack); |
| |
| if (l->header.type == lang_input_section_enum) |
| { |
| lang_input_section_type *input; |
| input = &l->input_section; |
| xtensa_callback_required_dependence (input->section->owner, |
| input->section, |
| &link_info, |
| /* Use the same closure. */ |
| build_deps_graph_callback, |
| deps); |
| } |
| } |
| return deps; |
| } |
| |
| |
| #if EXTRA_VALIDATION |
| static size_t |
| ld_count_children (lang_statement_union_type *s) |
| { |
| size_t count = 0; |
| xtensa_ld_iter_stack *stack = NULL; |
| for (iter_stack_create (&stack, s); |
| !iter_stack_empty (&stack); |
| iter_stack_next (&stack)) |
| { |
| lang_statement_union_type *l = iter_stack_current (&stack); |
| ASSERT (l != NULL); |
| count++; |
| } |
| return count; |
| } |
| #endif /* EXTRA_VALIDATION */ |
| |
| |
| /* Check if a particular section is included in the link. This will only |
| be true for one instance of a particular linkonce section. */ |
| |
| static bfd_boolean input_section_found = FALSE; |
| static asection *input_section_target = NULL; |
| |
| static void |
| input_section_linked_worker (lang_statement_union_type *statement) |
| { |
| if ((statement->header.type == lang_input_section_enum |
| && (statement->input_section.section == input_section_target))) |
| input_section_found = TRUE; |
| } |
| |
| static bfd_boolean |
| input_section_linked (asection *sec) |
| { |
| input_section_found = FALSE; |
| input_section_target = sec; |
| lang_for_each_statement_worker (input_section_linked_worker, stat_ptr->head); |
| return input_section_found; |
| } |
| |
| |
| /* Strip out any linkonce property tables or XCC exception tables where the |
| associated linkonce text is from a different object file. Normally, |
| a matching set of linkonce sections is taken from the same object file, |
| but sometimes the files are compiled differently so that some of the |
| linkonce sections are not present in all files. Stripping the |
| inconsistent sections like this is not completely robust -- a much |
| better solution is to use comdat groups. */ |
| |
| static int linkonce_len = sizeof (".gnu.linkonce.") - 1; |
| |
| static bfd_boolean |
| is_inconsistent_linkonce_section (asection *sec) |
| { |
| bfd *abfd = sec->owner; |
| const char *sec_name = bfd_get_section_name (abfd, sec); |
| const char *name; |
| |
| if ((bfd_get_section_flags (abfd, sec) & SEC_LINK_ONCE) == 0 |
| || strncmp (sec_name, ".gnu.linkonce.", linkonce_len) != 0) |
| return FALSE; |
| |
| /* Check if this is an Xtensa property section or an exception table |
| for Tensilica's XCC compiler. */ |
| name = sec_name + linkonce_len; |
| if (CONST_STRNEQ (name, "prop.")) |
| name = strchr (name + 5, '.') ? strchr (name + 5, '.') + 1 : name + 5; |
| else if (name[1] == '.' |
| && (name[0] == 'p' || name[0] == 'e' || name[0] == 'h')) |
| name += 2; |
| else |
| name = 0; |
| |
| if (name) |
| { |
| char *dep_sec_name = xmalloc (strlen (sec_name) + 1); |
| asection *dep_sec; |
| |
| /* Get the associated linkonce text section and check if it is |
| included in the link. If not, this section is inconsistent |
| and should be stripped. */ |
| strcpy (dep_sec_name, ".gnu.linkonce.t."); |
| strcat (dep_sec_name, name); |
| dep_sec = bfd_get_section_by_name (abfd, dep_sec_name); |
| if (dep_sec == NULL || ! input_section_linked (dep_sec)) |
| { |
| free (dep_sec_name); |
| return TRUE; |
| } |
| free (dep_sec_name); |
| } |
| |
| return FALSE; |
| } |
| |
| |
| static void |
| xtensa_strip_inconsistent_linkonce_sections (lang_statement_list_type *slist) |
| { |
| lang_statement_union_type **s_p = &slist->head; |
| while (*s_p) |
| { |
| lang_statement_union_type *s = *s_p; |
| lang_statement_union_type *s_next = (*s_p)->header.next; |
| |
| switch (s->header.type) |
| { |
| case lang_input_section_enum: |
| if (is_inconsistent_linkonce_section (s->input_section.section)) |
| { |
| s->input_section.section->output_section = bfd_abs_section_ptr; |
| *s_p = s_next; |
| continue; |
| } |
| break; |
| |
| case lang_constructors_statement_enum: |
| xtensa_strip_inconsistent_linkonce_sections (&constructor_list); |
| break; |
| |
| case lang_output_section_statement_enum: |
| if (s->output_section_statement.children.head) |
| xtensa_strip_inconsistent_linkonce_sections |
| (&s->output_section_statement.children); |
| break; |
| |
| case lang_wild_statement_enum: |
| xtensa_strip_inconsistent_linkonce_sections |
| (&s->wild_statement.children); |
| break; |
| |
| case lang_group_statement_enum: |
| xtensa_strip_inconsistent_linkonce_sections |
| (&s->group_statement.children); |
| 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_statement_enum: |
| case lang_assignment_statement_enum: |
| case lang_padding_statement_enum: |
| case lang_address_statement_enum: |
| case lang_fill_statement_enum: |
| break; |
| |
| default: |
| FAIL (); |
| break; |
| } |
| |
| s_p = &(*s_p)->header.next; |
| } |
| |
| /* Reset the tail of the list, in case the last entry was removed. */ |
| if (s_p != slist->tail) |
| slist->tail = s_p; |
| } |
| |
| |
| static void |
| xtensa_wild_group_interleave_callback (lang_statement_union_type *statement) |
| { |
| lang_wild_statement_type *w; |
| reloc_deps_graph *deps; |
| if (statement->header.type == lang_wild_statement_enum) |
| { |
| #if EXTRA_VALIDATION |
| size_t old_child_count; |
| size_t new_child_count; |
| #endif |
| bfd_boolean no_reorder; |
| |
| w = &statement->wild_statement; |
| |
| no_reorder = FALSE; |
| |
| /* If it has 0 or 1 section bound, then do not reorder. */ |
| if (w->children.head == NULL |
| || (w->children.head->header.type == lang_input_section_enum |
| && w->children.head->header.next == NULL)) |
| no_reorder = TRUE; |
| |
| if (w->filenames_sorted) |
| no_reorder = TRUE; |
| |
| /* Check for sorting in a section list wildcard spec as well. */ |
| if (!no_reorder) |
| { |
| struct wildcard_list *l; |
| for (l = w->section_list; l != NULL; l = l->next) |
| { |
| if (l->spec.sorted == TRUE) |
| { |
| no_reorder = TRUE; |
| break; |
| } |
| } |
| } |
| |
| /* Special case until the NOREORDER linker directive is supported: |
| *(.init) output sections and *(.fini) specs may NOT be reordered. */ |
| |
| /* Check for sorting in a section list wildcard spec as well. */ |
| if (!no_reorder) |
| { |
| struct wildcard_list *l; |
| for (l = w->section_list; l != NULL; l = l->next) |
| { |
| if (l->spec.name |
| && ((strcmp (".init", l->spec.name) == 0) |
| || (strcmp (".fini", l->spec.name) == 0))) |
| { |
| no_reorder = TRUE; |
| break; |
| } |
| } |
| } |
| |
| #if EXTRA_VALIDATION |
| old_child_count = ld_count_children (statement); |
| #endif |
| |
| /* It is now officially a target. Build the graph of source |
| section -> target section (kept as a list of edges). */ |
| deps = ld_build_required_section_dependence (statement); |
| |
| /* If this wildcard does not reorder.... */ |
| if (!no_reorder && deps->count != 0) |
| { |
| /* First check for reverse dependences. Fix if possible. */ |
| xtensa_layout_wild (deps, w); |
| |
| xtensa_move_dependencies_to_front (deps, w); |
| #if EXTRA_VALIDATION |
| new_child_count = ld_count_children (statement); |
| ASSERT (new_child_count == old_child_count); |
| #endif |
| |
| xtensa_colocate_literals (deps, statement); |
| |
| #if EXTRA_VALIDATION |
| new_child_count = ld_count_children (statement); |
| ASSERT (new_child_count == old_child_count); |
| #endif |
| } |
| |
| /* Clean up. */ |
| free_reloc_deps_graph (deps); |
| } |
| } |
| |
| |
| static void |
| xtensa_wild_group_interleave (lang_statement_union_type *s) |
| { |
| lang_for_each_statement_worker (xtensa_wild_group_interleave_callback, s); |
| } |
| |
| |
| static void |
| xtensa_layout_wild (const reloc_deps_graph *deps, lang_wild_statement_type *w) |
| { |
| /* If it does not fit initially, we need to do this step. Move all |
| of the wild literal sections to a new list, then move each of |
| them back in just before the first section they depend on. */ |
| lang_statement_union_type **s_p; |
| #if EXTRA_VALIDATION |
| size_t old_count, new_count; |
| size_t ct1, ct2; |
| #endif |
| |
| lang_wild_statement_type literal_wild; |
| literal_wild.header.next = NULL; |
| literal_wild.header.type = lang_wild_statement_enum; |
| literal_wild.filename = NULL; |
| literal_wild.filenames_sorted = FALSE; |
| literal_wild.section_list = NULL; |
| literal_wild.keep_sections = FALSE; |
| literal_wild.children.head = NULL; |
| literal_wild.children.tail = &literal_wild.children.head; |
| |
| #if EXTRA_VALIDATION |
| old_count = ld_count_children ((lang_statement_union_type*) w); |
| #endif |
| |
| s_p = &w->children.head; |
| while (*s_p != NULL) |
| { |
| lang_statement_union_type *l = *s_p; |
| if (l->header.type == lang_input_section_enum) |
| { |
| if (section_is_target (deps, l) |
| && ! section_is_source (deps, l)) |
| { |
| /* Detach. */ |
| *s_p = l->header.next; |
| if (*s_p == NULL) |
| w->children.tail = s_p; |
| l->header.next = NULL; |
| |
| /* Append. */ |
| *literal_wild.children.tail = l; |
| literal_wild.children.tail = &l->header.next; |
| continue; |
| } |
| } |
| s_p = &(*s_p)->header.next; |
| } |
| |
| #if EXTRA_VALIDATION |
| ct1 = ld_count_children ((lang_statement_union_type*) w); |
| ct2 = ld_count_children ((lang_statement_union_type*) &literal_wild); |
| |
| ASSERT (old_count == (ct1 + ct2)); |
| #endif |
| |
| /* Now place them back in front of their dependent sections. */ |
| |
| while (literal_wild.children.head != NULL) |
| { |
| lang_statement_union_type *lit = literal_wild.children.head; |
| bfd_boolean placed = FALSE; |
| |
| #if EXTRA_VALIDATION |
| ASSERT (ct2 > 0); |
| ct2--; |
| #endif |
| |
| /* Detach. */ |
| literal_wild.children.head = lit->header.next; |
| if (literal_wild.children.head == NULL) |
| literal_wild.children.tail = &literal_wild.children.head; |
| lit->header.next = NULL; |
| |
| /* Find a spot to place it. */ |
| for (s_p = &w->children.head; *s_p != NULL; s_p = &(*s_p)->header.next) |
| { |
| lang_statement_union_type *src = *s_p; |
| if (deps_has_edge (deps, src, lit)) |
| { |
| /* Place it here. */ |
| lit->header.next = *s_p; |
| *s_p = lit; |
| placed = TRUE; |
| break; |
| } |
| } |
| |
| if (!placed) |
| { |
| /* Put it at the end. */ |
| *w->children.tail = lit; |
| w->children.tail = &lit->header.next; |
| } |
| } |
| |
| #if EXTRA_VALIDATION |
| new_count = ld_count_children ((lang_statement_union_type*) w); |
| ASSERT (new_count == old_count); |
| #endif |
| } |
| |
| |
| static void |
| xtensa_colocate_output_literals_callback (lang_statement_union_type *statement) |
| { |
| reloc_deps_graph *deps; |
| if (statement->header.type == lang_output_section_statement_enum) |
| { |
| /* Now, we walk over the contours of the output section statement. |
| |
| First we build the literal section dependences as before. |
| |
| At the first uniquely_literal section, we mark it as a good |
| spot to place other literals. Continue walking (and counting |
| sizes) until we find the next literal section. If this |
| section can be moved to the first one, then we move it. If |
| we every find a modification of ".", start over. If we find |
| a labeling of the current location, start over. Finally, at |
| the end, if we require page alignment, add page alignments. */ |
| |
| #if EXTRA_VALIDATION |
| size_t old_child_count; |
| size_t new_child_count; |
| #endif |
| bfd_boolean no_reorder = FALSE; |
| |
| #if EXTRA_VALIDATION |
| old_child_count = ld_count_children (statement); |
| #endif |
| |
| /* It is now officially a target. Build the graph of source |
| section -> target section (kept as a list of edges). */ |
| |
| deps = ld_build_required_section_dependence (statement); |
| |
| /* If this wildcard does not reorder.... */ |
| if (!no_reorder) |
| { |
| /* First check for reverse dependences. Fix if possible. */ |
| xtensa_colocate_literals (deps, statement); |
| |
| #if EXTRA_VALIDATION |
| new_child_count = ld_count_children (statement); |
| ASSERT (new_child_count == old_child_count); |
| #endif |
| } |
| |
| /* Insert align/offset assignment statement. */ |
| if (xtensa_use_literal_pages) |
| { |
| ld_xtensa_insert_page_offsets (0, statement, deps, |
| xtensa_use_literal_pages); |
| lang_for_each_statement_worker (xtensa_ldlang_clear_addresses, |
| statement); |
| } |
| |
| /* Clean up. */ |
| free_reloc_deps_graph (deps); |
| } |
| } |
| |
| |
| static void |
| xtensa_colocate_output_literals (lang_statement_union_type *s) |
| { |
| lang_for_each_statement_worker (xtensa_colocate_output_literals_callback, s); |
| } |
| |
| |
| static void |
| xtensa_ldlang_clear_addresses (lang_statement_union_type *statement) |
| { |
| switch (statement->header.type) |
| { |
| case lang_input_section_enum: |
| { |
| asection *bfd_section = statement->input_section.section; |
| bfd_section->output_offset = 0; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| |
| static bfd_vma |
| ld_assign_relative_paged_dot (bfd_vma dot, |
| lang_statement_union_type *s, |
| const reloc_deps_graph *deps ATTRIBUTE_UNUSED, |
| bfd_boolean lit_align) |
| { |
| /* Walk through all of the input statements in this wild statement |
| assign dot to all of them. */ |
| |
| xtensa_ld_iter_stack *stack = NULL; |
| xtensa_ld_iter_stack **stack_p = &stack; |
| |
| bfd_boolean first_section = FALSE; |
| bfd_boolean in_literals = FALSE; |
| |
| for (iter_stack_create (stack_p, s); |
| !iter_stack_empty (stack_p); |
| iter_stack_next (stack_p)) |
| { |
| lang_statement_union_type *l = iter_stack_current (stack_p); |
| |
| switch (l->header.type) |
| { |
| case lang_input_section_enum: |
| { |
| asection *section = l->input_section.section; |
| size_t align_pow = section->alignment_power; |
| bfd_boolean do_xtensa_alignment = FALSE; |
| |
| if (lit_align) |
| { |
| bfd_boolean sec_is_target = section_is_target (deps, l); |
| bfd_boolean sec_is_source = section_is_source (deps, l); |
| |
| if (section->size != 0 |
| && (first_section |
| || (in_literals && !sec_is_target) |
| || (!in_literals && sec_is_target))) |
| { |
| do_xtensa_alignment = TRUE; |
| } |
| first_section = FALSE; |
| if (section->size != 0) |
| in_literals = (sec_is_target && !sec_is_source); |
| } |
| |
| if (do_xtensa_alignment && xtensa_page_power != 0) |
| dot += (1 << xtensa_page_power); |
| |
| dot = align_power (dot, align_pow); |
| section->output_offset = dot; |
| dot += section->size; |
| } |
| break; |
| case lang_fill_statement_enum: |
| dot += l->fill_statement.size; |
| break; |
| case lang_padding_statement_enum: |
| dot += l->padding_statement.size; |
| break; |
| default: |
| break; |
| } |
| } |
| return dot; |
| } |
| |
| |
| static bfd_boolean |
| ld_local_file_relocations_fit (lang_statement_union_type *statement, |
| const reloc_deps_graph *deps ATTRIBUTE_UNUSED) |
| { |
| /* Walk over all of the dependencies that we identified and make |
| sure that IF the source and target are here (addr != 0): |
| 1) target addr < source addr |
| 2) (roundup(source + source_size, 4) - rounddown(target, 4)) |
| < (256K - (1 << bad align)) |
| Need a worst-case proof.... */ |
| |
| xtensa_ld_iter_stack *stack = NULL; |
| xtensa_ld_iter_stack **stack_p = &stack; |
| size_t max_align_power = 0; |
| size_t align_penalty = 256; |
| reloc_deps_e *e; |
| size_t i; |
| |
| /* Find the worst-case alignment requirement for this set of statements. */ |
| for (iter_stack_create (stack_p, statement); |
| !iter_stack_empty (stack_p); |
| iter_stack_next (stack_p)) |
| { |
| lang_statement_union_type *l = iter_stack_current (stack_p); |
| if (l->header.type == lang_input_section_enum) |
| { |
| lang_input_section_type *input = &l->input_section; |
| asection *section = input->section; |
| if (section->alignment_power > max_align_power) |
| max_align_power = section->alignment_power; |
| } |
| } |
| |
| /* Now check that everything fits. */ |
| for (i = 0; i < deps->count; i++) |
| { |
| asection *sec = deps->sections[i]; |
| const reloc_deps_section *deps_section = |
| xtensa_get_section_deps (deps, sec); |
| if (deps_section) |
| { |
| /* We choose to walk through the successors. */ |
| for (e = deps_section->succs; e != NULL; e = e->next) |
| { |
| if (e->src != e->tgt |
| && e->src->output_section == e->tgt->output_section |
| && e->src->output_offset != 0 |
| && e->tgt->output_offset != 0) |
| { |
| bfd_vma l32r_addr = |
| align_power (e->src->output_offset + e->src->size, 2); |
| bfd_vma target_addr = e->tgt->output_offset & ~3; |
| if (l32r_addr < target_addr) |
| { |
| fflush (stdout); |
| fprintf (stderr, "Warning: " |
| "l32r target section before l32r\n"); |
| fflush (stderr); |
| return FALSE; |
| } |
| |
| if (l32r_addr - target_addr > 256 * 1024 - align_penalty) |
| return FALSE; |
| } |
| } |
| } |
| } |
| |
| return TRUE; |
| } |
| |
| |
| static bfd_vma |
| ld_xtensa_insert_page_offsets (bfd_vma dot, |
| lang_statement_union_type *s, |
| reloc_deps_graph *deps, |
| bfd_boolean lit_align) |
| { |
| xtensa_ld_iter_stack *stack = NULL; |
| xtensa_ld_iter_stack **stack_p = &stack; |
| |
| bfd_boolean first_section = FALSE; |
| bfd_boolean in_literals = FALSE; |
| |
| if (!lit_align) |
| return FALSE; |
| |
| for (iter_stack_create (stack_p, s); |
| !iter_stack_empty (stack_p); |
| iter_stack_next (stack_p)) |
| { |
| lang_statement_union_type *l = iter_stack_current (stack_p); |
| |
| switch (l->header.type) |
| { |
| case lang_input_section_enum: |
| { |
| asection *section = l->input_section.section; |
| bfd_boolean do_xtensa_alignment = FALSE; |
| |
| if (lit_align) |
| { |
| if (section->size != 0 |
| && (first_section |
| || (in_literals && !section_is_target (deps, l)) |
| || (!in_literals && section_is_target (deps, l)))) |
| { |
| do_xtensa_alignment = TRUE; |
| } |
| first_section = FALSE; |
| if (section->size != 0) |
| { |
| in_literals = (section_is_target (deps, l) |
| && !section_is_source (deps, l)); |
| } |
| } |
| |
| if (do_xtensa_alignment && xtensa_page_power != 0) |
| { |
| /* Create an expression that increments the current address, |
| i.e., "dot", by (1 << xtensa_align_power). */ |
| etree_type *name_op = exp_nameop (NAME, "."); |
| etree_type *addend_op = exp_intop (1 << xtensa_page_power); |
| etree_type *add_op = exp_binop ('+', name_op, addend_op); |
| etree_type *assign_op = exp_assign (".", add_op, FALSE); |
| |
| lang_assignment_statement_type *assign_stmt; |
| lang_statement_union_type *assign_union; |
| lang_statement_list_type tmplist; |
| |
| /* There is hidden state in "lang_add_assignment". It |
| appends the new assignment statement to the stat_ptr |
| list. Thus, we swap it before and after the call. */ |
| |
| lang_list_init (&tmplist); |
| push_stat_ptr (&tmplist); |
| /* Warning: side effect; statement appended to stat_ptr. */ |
| assign_stmt = lang_add_assignment (assign_op); |
| assign_union = (lang_statement_union_type *) assign_stmt; |
| pop_stat_ptr (); |
| |
| assign_union->header.next = l; |
| *(*stack_p)->iterloc.loc = assign_union; |
| iter_stack_next (stack_p); |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| return dot; |
| } |
| |
| EOF |
| |
| # Define some shell vars to insert bits of code into the standard ELF |
| # parse_args and list_options functions. |
| # |
| PARSE_AND_LIST_PROLOGUE=' |
| #define OPTION_OPT_SIZEOPT (300) |
| #define OPTION_LITERAL_MOVEMENT (OPTION_OPT_SIZEOPT + 1) |
| #define OPTION_NO_LITERAL_MOVEMENT (OPTION_LITERAL_MOVEMENT + 1) |
| extern int elf32xtensa_size_opt; |
| extern int elf32xtensa_no_literal_movement; |
| ' |
| |
| PARSE_AND_LIST_LONGOPTS=' |
| { "size-opt", no_argument, NULL, OPTION_OPT_SIZEOPT}, |
| { "literal-movement", no_argument, NULL, OPTION_LITERAL_MOVEMENT}, |
| { "no-literal-movement", no_argument, NULL, OPTION_NO_LITERAL_MOVEMENT}, |
| ' |
| |
| PARSE_AND_LIST_OPTIONS=' |
| fprintf (file, _("\ |
| --size-opt When relaxing longcalls, prefer size\n\ |
| optimization over branch target alignment\n")); |
| ' |
| |
| PARSE_AND_LIST_ARGS_CASES=' |
| case OPTION_OPT_SIZEOPT: |
| elf32xtensa_size_opt = 1; |
| break; |
| case OPTION_LITERAL_MOVEMENT: |
| elf32xtensa_no_literal_movement = 0; |
| break; |
| case OPTION_NO_LITERAL_MOVEMENT: |
| elf32xtensa_no_literal_movement = 1; |
| break; |
| ' |
| |
| # Replace some of the standard ELF functions with our own versions. |
| # |
| LDEMUL_BEFORE_PARSE=elf_xtensa_before_parse |
| LDEMUL_AFTER_OPEN=elf_xtensa_after_open |
| LDEMUL_CHOOSE_TARGET=elf_xtensa_choose_target |
| LDEMUL_BEFORE_ALLOCATION=elf_xtensa_before_allocation |