aboutsummaryrefslogtreecommitdiff
path: root/ld/emultempl
diff options
context:
space:
mode:
authorNick Clifton <nickc@redhat.com>2003-04-01 15:50:31 +0000
committerNick Clifton <nickc@redhat.com>2003-04-01 15:50:31 +0000
commite0001a05d2e4967ee86f4468cdc4fafea66b92d1 (patch)
tree4676b72e452f4dfc81e8d6646fb43f63a108da1b /ld/emultempl
parentce0c72625ad0f6497718b4293572b2b6be711714 (diff)
downloadgdb-e0001a05d2e4967ee86f4468cdc4fafea66b92d1.zip
gdb-e0001a05d2e4967ee86f4468cdc4fafea66b92d1.tar.gz
gdb-e0001a05d2e4967ee86f4468cdc4fafea66b92d1.tar.bz2
Add Xtensa port
Diffstat (limited to 'ld/emultempl')
-rw-r--r--ld/emultempl/xtensaelf.em1586
1 files changed, 1586 insertions, 0 deletions
diff --git a/ld/emultempl/xtensaelf.em b/ld/emultempl/xtensaelf.em
new file mode 100644
index 0000000..b070075
--- /dev/null
+++ b/ld/emultempl/xtensaelf.em
@@ -0,0 +1,1586 @@
+# This shell script emits a C file. -*- C -*-
+# Copyright 2003
+# Free Software Foundation, Inc.
+#
+# This file is part of GLD, the Gnu Linker.
+#
+# 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 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+# This file is sourced from elf32.em, and defines extra xtensa-elf
+# specific routines.
+#
+cat >>e${EMULATION_NAME}.c <<EOF
+
+#include <xtensa-config.h>
+
+static char *elf_xtensa_choose_target
+ PARAMS ((int, char **));
+static bfd_boolean elf_xtensa_place_orphan
+ PARAMS ((lang_input_statement_type *, asection *));
+static void elf_xtensa_before_parse
+ PARAMS ((void));
+static void elf_xtensa_before_allocation
+ PARAMS ((void));
+static void xtensa_wild_group_interleave
+ PARAMS ((lang_statement_union_type *));
+static void xtensa_wild_group_interleave_callback
+ PARAMS ((lang_statement_union_type *));
+static void xtensa_colocate_output_literals
+ PARAMS ((lang_statement_union_type *));
+static void xtensa_colocate_output_literals_callback
+ PARAMS ((lang_statement_union_type *));
+
+
+/* Flag for the emulation-specific "--no-relax" option. */
+static bfd_boolean disable_relaxation = FALSE;
+
+/* 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 (argc, argv)
+ int argc ATTRIBUTE_UNUSED;
+ char **argv ATTRIBUTE_UNUSED;
+{
+ if (XCHAL_HAVE_BE)
+ return "${BIG_OUTPUT_FORMAT}";
+ else
+ return "${LITTLE_OUTPUT_FORMAT}";
+}
+
+
+static bfd_boolean
+elf_xtensa_place_orphan (file, s)
+ lang_input_statement_type *file;
+ asection *s;
+{
+ /* Early exit for relocatable links. */
+ if (link_info.relocateable)
+ return FALSE;
+
+ return gld${EMULATION_NAME}_place_orphan (file, s);
+}
+
+
+static void
+elf_xtensa_before_parse ()
+{
+ /* 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 ();
+}
+
+
+/* This is called after the sections have been attached to output
+ sections, but before any sizes or addresses have been set. */
+
+void
+elf_xtensa_before_allocation ()
+{
+ bfd *in_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 && output_bfd->xvec->byteorder == BFD_ENDIAN_LITTLE)
+ {
+ einfo (_("%F%P: little endian output does not match "
+ "Xtensa configuration\n"));
+ }
+ if (!is_big_endian && output_bfd->xvec->byteorder == BFD_ENDIAN_BIG)
+ {
+ einfo (_("%F%P: big endian output does not match "
+ "Xtensa configuration\n"));
+ }
+
+ /* 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. */
+
+ for (in_bfd = link_info.input_bfds;
+ in_bfd != NULL;
+ in_bfd = in_bfd->link_next)
+ {
+ if ((is_big_endian && in_bfd->xvec->byteorder == BFD_ENDIAN_LITTLE)
+ || (!is_big_endian && in_bfd->xvec->byteorder == BFD_ENDIAN_BIG))
+ einfo (_("%F%P: cross-endian linking not supported\n"));
+ }
+
+ /* 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 (!disable_relaxation)
+ command_line.relax = TRUE;
+
+ gld${EMULATION_NAME}_before_allocation ();
+
+ xtensa_wild_group_interleave (stat_ptr->head);
+ if (command_line.relax)
+ 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 relocateable 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
+ PARAMS ((const reloc_deps_graph *, lang_wild_statement_type *));
+
+typedef void (*deps_callback_t)
+ PARAMS ((asection *, /* src_sec */
+ bfd_vma, /* src_offset */
+ asection *, /* target_sec */
+ bfd_vma, /* target_offset */
+ PTR)); /* closure */
+
+static void build_deps_graph_callback
+ PARAMS ((asection *, bfd_vma, asection *, bfd_vma, PTR));
+extern bfd_boolean xtensa_callback_required_dependence
+ PARAMS ((bfd *, asection *, struct bfd_link_info *,
+ deps_callback_t, PTR));
+static void xtensa_ldlang_clear_addresses
+ PARAMS ((lang_statement_union_type *));
+static bfd_boolean ld_local_file_relocations_fit
+ PARAMS ((lang_statement_union_type *, const reloc_deps_graph *));
+static bfd_vma ld_assign_relative_paged_dot
+ PARAMS ((bfd_vma, lang_statement_union_type *,
+ const reloc_deps_graph *, bfd_boolean));
+static bfd_vma ld_xtensa_insert_page_offsets
+ PARAMS ((bfd_vma, lang_statement_union_type *, reloc_deps_graph *,
+ bfd_boolean));
+static void lang_for_each_statement_worker
+ PARAMS ((void (*) (lang_statement_union_type *),
+ lang_statement_union_type *));
+static void xtensa_move_dependencies_to_front
+ PARAMS ((reloc_deps_graph *, lang_wild_statement_type *));
+static reloc_deps_graph *ld_build_required_section_dependence
+ PARAMS ((lang_statement_union_type *));
+static bfd_boolean section_is_source
+ PARAMS ((const reloc_deps_graph *, lang_statement_union_type *));
+static bfd_boolean section_is_target
+ PARAMS ((const reloc_deps_graph *, lang_statement_union_type *));
+static bfd_boolean section_is_source_or_target
+ PARAMS ((const reloc_deps_graph *, lang_statement_union_type *));
+static bfd_boolean deps_has_sec_edge
+ PARAMS ((const reloc_deps_graph *, asection *, asection *));
+static bfd_boolean deps_has_edge
+ PARAMS ((const reloc_deps_graph *, lang_statement_union_type *,
+ lang_statement_union_type *));
+static void add_deps_edge
+ PARAMS ((reloc_deps_graph *, asection *, asection *));
+#if EXTRA_VALIDATION
+static size_t ld_count_children
+ PARAMS ((lang_statement_union_type *));
+#endif
+static void free_reloc_deps_graph
+ PARAMS ((reloc_deps_graph *));
+static void xtensa_colocate_literals
+ PARAMS ((reloc_deps_graph *, lang_statement_union_type *));
+static reloc_deps_section *xtensa_get_section_deps
+ PARAMS ((const reloc_deps_graph *, asection *));
+static void xtensa_set_section_deps
+ PARAMS ((const reloc_deps_graph *, asection *, reloc_deps_section *));
+static void xtensa_append_section_deps
+ PARAMS ((reloc_deps_graph *, asection *));
+
+extern lang_statement_list_type constructor_list;
+
+/* Begin verbatim code from ldlang.c:
+ the following are copied from ldlang.c because they are defined
+ there statically. */
+
+static void
+lang_for_each_statement_worker (func, s)
+ void (*func) PARAMS ((lang_statement_union_type *));
+ lang_statement_union_type *s;
+{
+ for (; s != (lang_statement_union_type *) 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:
+ 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:
+ break;
+ default:
+ FAIL ();
+ break;
+ }
+ }
+}
+
+/* End of verbatim code from ldlang.c. */
+
+
+reloc_deps_section *
+xtensa_get_section_deps (deps, sec)
+ 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 = (reloc_deps_section *) sec->userdata;
+ return sec_deps;
+}
+
+void
+xtensa_set_section_deps (deps, sec, deps_section)
+ const reloc_deps_graph *deps ATTRIBUTE_UNUSED;
+ asection *sec;
+ reloc_deps_section *deps_section;
+{
+ sec->userdata = (void *) 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 (deps, sec)
+ 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 = (asection**) 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 (deps)
+ 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);
+}
+
+
+bfd_boolean
+section_is_source (deps, s)
+ 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);
+}
+
+
+bfd_boolean
+section_is_target (deps, s)
+ 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);
+}
+
+bfd_boolean
+section_is_source_or_target (deps, s)
+ 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
+ PARAMS ((xtensa_ld_iter *, xtensa_ld_iter *));
+
+
+void
+ld_xtensa_move_section_after (to, current)
+ 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 = &current->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
+ PARAMS ((xtensa_ld_iter_stack **));
+static bfd_boolean iter_stack_push
+ PARAMS ((xtensa_ld_iter_stack **, lang_statement_union_type *));
+static void iter_stack_pop
+ PARAMS ((xtensa_ld_iter_stack **));
+static void iter_stack_update
+ PARAMS ((xtensa_ld_iter_stack **));
+static void iter_stack_next
+ PARAMS ((xtensa_ld_iter_stack **));
+static lang_statement_union_type *iter_stack_current
+ PARAMS ((xtensa_ld_iter_stack **));
+static void iter_stack_create
+ PARAMS ((xtensa_ld_iter_stack **, lang_statement_union_type *));
+static void iter_stack_copy_current
+ PARAMS ((xtensa_ld_iter_stack **, xtensa_ld_iter *));
+
+
+static bfd_boolean
+iter_stack_empty (stack_p)
+ xtensa_ld_iter_stack **stack_p;
+{
+ return (*stack_p == NULL);
+}
+
+
+static bfd_boolean
+iter_stack_push (stack_p, parent)
+ 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 = (xtensa_ld_iter_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 (stack_p)
+ 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 (stack_p)
+ 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 (stack_p)
+ 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 (stack_p)
+ xtensa_ld_iter_stack **stack_p;
+{
+ return *((*stack_p)->iterloc.loc);
+}
+
+
+/* The iter stack is a preorder. */
+
+static void
+iter_stack_create (stack_p, parent)
+ xtensa_ld_iter_stack **stack_p;
+ lang_statement_union_type *parent;
+{
+ iter_stack_push (stack_p, parent);
+}
+
+
+static void
+iter_stack_copy_current (stack_p, front)
+ xtensa_ld_iter_stack **stack_p;
+ xtensa_ld_iter *front;
+{
+ *front = (*stack_p)->iterloc;
+}
+
+
+void
+xtensa_colocate_literals (deps, statement)
+ 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;
+
+#if 0
+ ld_assign_relative_paged_dot (0x100000, statement, deps,
+ xtensa_use_literal_pages);
+
+ if (!ld_local_file_relocations_fit (statement, deps))
+ fprintf (stderr, "initial relocation placement does not fit\n");
+
+ lang_for_each_statement_worker (xtensa_ldlang_clear_addresses, statement);
+#endif
+
+ 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 = &current;
+ 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);
+}
+
+
+void
+xtensa_move_dependencies_to_front (deps, w)
+ 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, &current_iter);
+ front = &(*front)->header.next;
+ }
+ else
+ {
+ current = &(*current)->header.next;
+ }
+ }
+}
+
+
+static bfd_boolean
+deps_has_sec_edge (deps, src, tgt)
+ 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 (deps, src, tgt)
+ 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 (deps, src_sec, tgt_sec)
+ 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 = (reloc_deps_section *)
+ 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 = (reloc_deps_section *)
+ 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 = (reloc_deps_e *) 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 = (reloc_deps_e *) 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;
+}
+
+
+void
+build_deps_graph_callback (src_sec, src_offset,
+ target_sec, target_offset, closure)
+ asection *src_sec;
+ bfd_vma src_offset ATTRIBUTE_UNUSED;
+ asection *target_sec;
+ bfd_vma target_offset ATTRIBUTE_UNUSED;
+ PTR closure;
+{
+ reloc_deps_graph *deps;
+ deps = (reloc_deps_graph*) closure;
+
+ /* If the target is defined. */
+ if (target_sec != NULL)
+ add_deps_edge (deps, src_sec, target_sec);
+}
+
+
+reloc_deps_graph *
+ld_build_required_section_dependence (s)
+ lang_statement_union_type *s;
+{
+ reloc_deps_graph *deps;
+ xtensa_ld_iter_stack *stack = NULL;
+
+ deps = (reloc_deps_graph*) 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->ifile->the_bfd,
+ input->section,
+ &link_info,
+ /* Use the same closure. */
+ build_deps_graph_callback,
+ (PTR) deps);
+ }
+ }
+ return deps;
+}
+
+
+#if EXTRA_VALIDATION
+size_t
+ld_count_children (s)
+ 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 */
+
+
+void
+xtensa_wild_group_interleave_callback (statement)
+ 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);
+ }
+}
+
+
+void
+xtensa_wild_group_interleave (s)
+ lang_statement_union_type *s;
+{
+ lang_for_each_statement_worker (xtensa_wild_group_interleave_callback, s);
+}
+
+
+void
+xtensa_layout_wild (deps, w)
+ 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
+}
+
+
+void
+xtensa_colocate_output_literals_callback (statement)
+ lang_statement_union_type * statement;
+{
+ lang_output_section_statement_type *os;
+ 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;
+
+ os = &statement->output_section_statement;
+
+#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 ((bfd_vma) 0, statement, deps,
+ xtensa_use_literal_pages);
+ lang_for_each_statement_worker (xtensa_ldlang_clear_addresses,
+ statement);
+ }
+
+ /* Clean up. */
+ free_reloc_deps_graph (deps);
+ }
+}
+
+
+void
+xtensa_colocate_output_literals (s)
+ lang_statement_union_type *s;
+{
+ lang_for_each_statement_worker (xtensa_colocate_output_literals_callback, s);
+}
+
+
+void
+xtensa_ldlang_clear_addresses (statement)
+ 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;
+ }
+}
+
+
+bfd_vma
+ld_assign_relative_paged_dot (dot, s, deps, lit_align)
+ 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->_raw_size != 0
+ && (first_section
+ || (in_literals && !sec_is_target)
+ || (!in_literals && sec_is_target)))
+ {
+ do_xtensa_alignment = TRUE;
+ }
+ first_section = FALSE;
+ if (section->_raw_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->_raw_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;
+}
+
+
+bfd_boolean
+ld_local_file_relocations_fit (statement, deps)
+ 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->_raw_size, 2);
+ bfd_vma target_addr = e->tgt->output_offset & (~3);
+ if (l32r_addr < target_addr)
+ {
+ fprintf (stderr, "Warning: "
+ "l32r target section before l32r\n");
+ return FALSE;
+ }
+
+ if ((l32r_addr - target_addr) > (256*1024 - align_penalty))
+ return FALSE;
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+bfd_vma
+ld_xtensa_insert_page_offsets (dot, s, deps, lit_align)
+ 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->_raw_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->_raw_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_assop ('=', ".", add_op);
+
+ lang_assignment_statement_type *assign_stmt;
+ lang_statement_union_type *assign_union;
+ lang_statement_list_type tmplist;
+ lang_statement_list_type *old_stat_ptr = stat_ptr;
+
+ /* 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. */
+
+ tmplist.head = NULL;
+ tmplist.tail = &tmplist.head;
+
+ 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;
+ stat_ptr = old_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_NO_RELAX 301
+'
+
+PARSE_AND_LIST_LONGOPTS='
+ { "no-relax", no_argument, NULL, OPTION_NO_RELAX},
+'
+
+PARSE_AND_LIST_OPTIONS='
+ fprintf (file, _(" --no-relax\t\tDo not relax branches or coalesce literals\n"));
+'
+
+PARSE_AND_LIST_ARGS_CASES='
+ case OPTION_NO_RELAX:
+ disable_relaxation = TRUE;
+ break;
+'
+
+# Replace some of the standard ELF functions with our own versions.
+#
+LDEMUL_BEFORE_PARSE=elf_xtensa_before_parse
+LDEMUL_CHOOSE_TARGET=elf_xtensa_choose_target
+LDEMUL_PLACE_ORPHAN=elf_xtensa_place_orphan
+LDEMUL_BEFORE_ALLOCATION=elf_xtensa_before_allocation
+