aboutsummaryrefslogtreecommitdiff
path: root/ld
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
parentce0c72625ad0f6497718b4293572b2b6be711714 (diff)
downloadgdb-e0001a05d2e4967ee86f4468cdc4fafea66b92d1.zip
gdb-e0001a05d2e4967ee86f4468cdc4fafea66b92d1.tar.gz
gdb-e0001a05d2e4967ee86f4468cdc4fafea66b92d1.tar.bz2
Add Xtensa port
Diffstat (limited to 'ld')
-rw-r--r--ld/ChangeLog13
-rw-r--r--ld/Makefile.am6
-rw-r--r--ld/Makefile.in6
-rw-r--r--ld/configure.tgt1
-rw-r--r--ld/emulparams/elf32xtensa.sh32
-rw-r--r--ld/emulparams/xtensa-config.sh8
-rw-r--r--ld/emultempl/xtensaelf.em1586
-rw-r--r--ld/gen-doc.texi1
-rw-r--r--ld/ld.texinfo77
-rw-r--r--ld/scripttempl/elfxtensa.sc397
-rw-r--r--ld/testsuite/ChangeLog15
-rw-r--r--ld/testsuite/ld-elf/merge.d2
-rw-r--r--ld/testsuite/ld-scripts/crossref.exp7
-rw-r--r--ld/testsuite/ld-srec/srec.exp5
-rw-r--r--ld/testsuite/ld-xtensa/coalesce.exp93
-rw-r--r--ld/testsuite/ld-xtensa/coalesce.t6
-rw-r--r--ld/testsuite/ld-xtensa/coalesce1.s15
-rw-r--r--ld/testsuite/ld-xtensa/coalesce2.s9
-rw-r--r--ld/testsuite/ld-xtensa/lcall.exp107
-rw-r--r--ld/testsuite/ld-xtensa/lcall.t6
-rw-r--r--ld/testsuite/ld-xtensa/lcall1.s12
-rw-r--r--ld/testsuite/ld-xtensa/lcall2.s5
22 files changed, 2407 insertions, 2 deletions
diff --git a/ld/ChangeLog b/ld/ChangeLog
index 9836da4..2ea63d6 100644
--- a/ld/ChangeLog
+++ b/ld/ChangeLog
@@ -1,3 +1,16 @@
+2003-04-01 Bob Wilson <bob.wilson@acm.org>
+
+ * Makefile.am (ALL_EMULATIONS): Add eelf32xtensa.o.
+ (eelf32xtensa.c): New target.
+ * Makefile.in: Regenerate.
+ * configure.tgt: Handle xtensa-*-*.
+ * gen-doc.texi: Set XTENSA variable.
+ * ld.texinfo: Set XTENSA variable. Add new Xtensa node.
+ * emulparams/elf32xtensa.sh: New file.
+ * emulparams/xtensa-config.sh: Likewise.
+ * emultempl/xtensaelf.em: Likewise.
+ * scripttempl/elfxtensa.sc: Likewise.
+
2003-04-01 Jakub Jelinek <jakub@redhat.com>
* configure.tgt (powerpc*-*-linux*): Add elf32ppc to ppc64
diff --git a/ld/Makefile.am b/ld/Makefile.am
index 41af307..6e35d38 100644
--- a/ld/Makefile.am
+++ b/ld/Makefile.am
@@ -183,6 +183,7 @@ ALL_EMULATIONS = \
eelf32ppcwindiss.o \
eelf32vax.o \
eelf32xstormy16.o \
+ eelf32xtensa.o \
eelf_i386.o \
eelf_i386_be.o \
eelf_i386_chaos.o \
@@ -589,6 +590,11 @@ eelf32xstormy16.c: $(srcdir)/emulparams/elf32xstormy16.sh \
eelf32vax.c: $(srcdir)/emulparams/elf32vax.sh \
$(srcdir)/emultempl/elf32.em $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
${GENSCRIPTS} elf32vax "$(tdir_elf32vax)"
+eelf32xtensa.c: $(srcdir)/emulparams/elf32xtensa.sh \
+ $(srcdir)/emulparams/xtensa-config.sh \
+ $(srcdir)/emultempl/elf32.em $(srcdir)/emultempl/xtensaelf.em \
+ $(srcdir)/scripttempl/elfxtensa.sc ${GEN_DEPENDS}
+ ${GENSCRIPTS} elf32xtensa "$(tdir_elf32xtensa)"
eelf32fr30.c: $(srcdir)/emulparams/elf32fr30.sh \
$(srcdir)/emultempl/elf32.em $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
${GENSCRIPTS} elf32fr30 "$(tdir_fr30)"
diff --git a/ld/Makefile.in b/ld/Makefile.in
index 23b7d5b..dc226af 100644
--- a/ld/Makefile.in
+++ b/ld/Makefile.in
@@ -297,6 +297,7 @@ ALL_EMULATIONS = \
eelf32ppcwindiss.o \
eelf32vax.o \
eelf32xstormy16.o \
+ eelf32xtensa.o \
eelf_i386.o \
eelf_i386_be.o \
eelf_i386_chaos.o \
@@ -1315,6 +1316,11 @@ eelf32xstormy16.c: $(srcdir)/emulparams/elf32xstormy16.sh \
eelf32vax.c: $(srcdir)/emulparams/elf32vax.sh \
$(srcdir)/emultempl/elf32.em $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
${GENSCRIPTS} elf32vax "$(tdir_elf32vax)"
+eelf32xtensa.c: $(srcdir)/emulparams/elf32xtensa.sh \
+ $(srcdir)/emulparams/xtensa-config.sh \
+ $(srcdir)/emultempl/elf32.em $(srcdir)/emultempl/xtensaelf.em \
+ $(srcdir)/scripttempl/elfxtensa.sc ${GEN_DEPENDS}
+ ${GENSCRIPTS} elf32xtensa "$(tdir_elf32xtensa)"
eelf32fr30.c: $(srcdir)/emulparams/elf32fr30.sh \
$(srcdir)/emultempl/elf32.em $(srcdir)/scripttempl/elf.sc ${GEN_DEPENDS}
${GENSCRIPTS} elf32fr30 "$(tdir_fr30)"
diff --git a/ld/configure.tgt b/ld/configure.tgt
index 3e4fa25..8c10d42 100644
--- a/ld/configure.tgt
+++ b/ld/configure.tgt
@@ -525,6 +525,7 @@ iq2000-*-elf) targ_emul=elf32iq2000 ; targ_extra_emuls="elf32iq10" ;;
frv-*-*) targ_emul=elf32frv ;;
w65-*-*) targ_emul=w65 ;;
xstormy16-*-*) targ_emul=elf32xstormy16 ;;
+xtensa-*-*) targ_emul=elf32xtensa;;
fr30-*-*) targ_emul=elf32fr30 ;;
mcore-*-pe) targ_emul=mcorepe ;
targ_extra_ofiles="deffilep.o pe-dll.o" ;;
diff --git a/ld/emulparams/elf32xtensa.sh b/ld/emulparams/elf32xtensa.sh
new file mode 100644
index 0000000..12e2882
--- /dev/null
+++ b/ld/emulparams/elf32xtensa.sh
@@ -0,0 +1,32 @@
+# First set some configuration-specific variables
+. ${srcdir}/emulparams/xtensa-config.sh
+
+# See genscripts.sh and ../scripttempl/elfxtensa.sc for the meaning of these.
+SCRIPT_NAME=elfxtensa
+TEMPLATE_NAME=elf32
+EXTRA_EM_FILE=xtensaelf
+OUTPUT_FORMAT=undefined
+BIG_OUTPUT_FORMAT="elf32-xtensa-be"
+LITTLE_OUTPUT_FORMAT="elf32-xtensa-le"
+TEXT_START_ADDR=0x400000
+NONPAGED_TEXT_START_ADDR=0x400000
+ARCH=xtensa
+MACHINE=
+GENERATE_SHLIB_SCRIPT=yes
+GENERATE_COMBRELOC_SCRIPT=yes
+NO_SMALL_DATA=yes
+OTHER_READONLY_SECTIONS='
+ .xt_except_table : { KEEP (*(.xt_except_table)) }
+ .xt.lit : { *(.xt.lit*) *(.gnu.linkonce.p*) }
+'
+OTHER_READWRITE_SECTIONS='
+ .xt_except_desc :
+ {
+ *(.xt_except_desc)
+ *(.gnu.linkonce.h.*)
+ *(.xt_except_desc_end)
+ }
+'
+OTHER_SECTIONS='
+ .xt.insn : { *(.xt.insn) *(.gnu.linkonce.x*) }
+'
diff --git a/ld/emulparams/xtensa-config.sh b/ld/emulparams/xtensa-config.sh
new file mode 100644
index 0000000..b273df5
--- /dev/null
+++ b/ld/emulparams/xtensa-config.sh
@@ -0,0 +1,8 @@
+# Xtensa configuration settings.
+
+## NOTE: This file was automatically generated by the Xtensa Processor
+## Generator. Changes made here will be lost when this file is
+## updated or replaced with the settings for a different Xtensa
+## processor configuration. DO NOT EDIT!
+
+MAXPAGESIZE=0x1000
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
+
diff --git a/ld/gen-doc.texi b/ld/gen-doc.texi
index 0a5acb2..5add195 100644
--- a/ld/gen-doc.texi
+++ b/ld/gen-doc.texi
@@ -11,6 +11,7 @@
@set MSP430
@set TICOFF
@set WIN32
+@set XTENSA
@c 3. Properties of this configuration
@clear SingleFormat
diff --git a/ld/ld.texinfo b/ld/ld.texinfo
index 96a87f4..ce9bf90 100644
--- a/ld/ld.texinfo
+++ b/ld/ld.texinfo
@@ -45,6 +45,7 @@
@set V850
@set VAX
@set WIN32
+@set XTENSA
@end ifset
@c man end
@@ -157,6 +158,9 @@ section entitled ``GNU Free Documentation License''.
@ifset WIN32
* Win32:: ld and WIN32 (cygwin/mingw)
@end ifset
+@ifset XTENSA
+* Xtensa:: ld and Xtensa Processors
+@end ifset
@end ifclear
@ifclear SingleFormat
* BFD:: BFD
@@ -1227,7 +1231,9 @@ This option is only supported on a few targets.
@ifset I960
@xref{i960,, @command{ld} and the Intel 960 family}.
@end ifset
-
+@ifset XTENSA
+@xref{Xtensa,, @command{ld} and Xtensa Processors}.
+@end ifset
On some platforms, the @samp{--relax} option performs global
optimizations that become possible when the linker resolves addressing
@@ -4446,6 +4452,9 @@ functionality are not listed.
@ifset WIN32
* WIN32:: @command{ld} and WIN32 (cygwin/mingw)
@end ifset
+@ifset XTENSA
+* Xtensa:: @command{ld} and Xtensa Processors
+@end ifset
@end menu
@end ifset
@@ -5077,6 +5086,72 @@ which is probably not what you wanted.
@end ifclear
@end ifset
+@ifset XTENSA
+@ifclear GENERIC
+@raisesections
+@end ifclear
+
+@node Xtensa
+@section @code{ld} and Xtensa Processors
+
+@cindex Xtensa processors
+The default @command{ld} behavior for Xtensa processors is to interpret
+@code{SECTIONS} commands so that lists of explicitly named sections in a
+specification with a wildcard file will be interleaved when necessary to
+keep literal pools within the range of PC-relative load offsets. For
+example, with the command:
+
+@smallexample
+SECTIONS
+@{
+ .text : @{
+ *(.literal .text)
+ @}
+@}
+@end smallexample
+
+@noindent
+@command{ld} may interleave some of the @code{.literal}
+and @code{.text} sections from different object files to ensure that the
+literal pools are within the range of PC-relative load offsets. A valid
+interleaving might place the @code{.literal} sections from an initial
+group of files followed by the @code{.text} sections of that group of
+files. Then, the @code{.literal} sections from the rest of the files
+and the @code{.text} sections from the rest of the files would follow.
+The non-interleaved order can still be specified as:
+
+@smallexample
+SECTIONS
+@{
+ .text : @{
+ *(.literal) *(.text)
+ @}
+@}
+@end smallexample
+
+@cindex @code{--relax} on Xtensa
+@cindex relaxing on Xtensa
+@kindex --no-relax
+The Xtensa version of @command{ld} enables the @option{--relax} option by
+default to attempt to reduce space in the output image by combining
+literals with identical values. It also provides the
+@option{--no-relax} option to disable this optimization. When enabled,
+the relaxation algorithm ensures that a literal will only be merged with
+another literal when the new merged literal location is within the
+offset range of all of its uses.
+
+The relaxation mechanism will also attempt to optimize
+assembler-generated ``longcall'' sequences of
+@code{L32R}/@code{CALLX@var{n}} when the target is known to fit into a
+@code{CALL@var{n}} instruction encoding. The current optimization
+converts the sequence into @code{NOP}/@code{CALL@var{n}} and removes the
+literal referenced by the @code{L32R} instruction.
+
+@ifclear GENERIC
+@lowersections
+@end ifclear
+@end ifset
+
@ifclear SingleFormat
@node BFD
@chapter BFD
diff --git a/ld/scripttempl/elfxtensa.sc b/ld/scripttempl/elfxtensa.sc
new file mode 100644
index 0000000..a4ba5c8
--- /dev/null
+++ b/ld/scripttempl/elfxtensa.sc
@@ -0,0 +1,397 @@
+#
+# Unusual variables checked by this code:
+# NOP - four byte opcode for no-op (defaults to 0)
+# NO_SMALL_DATA - no .sbss/.sbss2/.sdata/.sdata2 sections if not
+# empty.
+# DATA_ADDR - if end-of-text-plus-one-page isn't right for data start
+# INITIAL_READONLY_SECTIONS - at start of text segment
+# OTHER_READONLY_SECTIONS - other than .text .init .rodata ...
+# (e.g., .PARISC.milli)
+# OTHER_TEXT_SECTIONS - these get put in .text when relocating
+# OTHER_READWRITE_SECTIONS - other than .data .bss .ctors .sdata ...
+# (e.g., .PARISC.global)
+# OTHER_BSS_SECTIONS - other than .bss .sbss ...
+# OTHER_SECTIONS - at the end
+# EXECUTABLE_SYMBOLS - symbols that must be defined for an
+# executable (e.g., _DYNAMIC_LINK)
+# TEXT_START_SYMBOLS - symbols that appear at the start of the
+# .text section.
+# DATA_START_SYMBOLS - symbols that appear at the start of the
+# .data section.
+# OTHER_GOT_SYMBOLS - symbols defined just before .got.
+# OTHER_GOT_SECTIONS - sections just after .got.
+# OTHER_SDATA_SECTIONS - sections just after .sdata.
+# OTHER_BSS_SYMBOLS - symbols that appear at the start of the
+# .bss section besides __bss_start.
+# TEXT_DYNAMIC - .dynamic in text segment, not data segment.
+# EMBEDDED - whether this is for an embedded system.
+# SHLIB_TEXT_START_ADDR - if set, add to SIZEOF_HEADERS to set
+# start address of shared library.
+# INPUT_FILES - INPUT command of files to always include
+# WRITABLE_RODATA - if set, the .rodata section should be writable
+# INIT_START, INIT_END - statements just before and just after
+# combination of .init sections.
+# FINI_START, FINI_END - statements just before and just after
+# combination of .fini sections.
+# STACK_ADDR - start of a .stack section.
+# OTHER_END_SYMBOLS - symbols to place right at the end of the script.
+#
+# When adding sections, do note that the names of some sections are used
+# when specifying the start address of the next.
+#
+
+# Many sections come in three flavours. There is the 'real' section,
+# like ".data". Then there are the per-procedure or per-variable
+# sections, generated by -ffunction-sections and -fdata-sections in GCC,
+# and useful for --gc-sections, which for a variable "foo" might be
+# ".data.foo". Then there are the linkonce sections, for which the linker
+# eliminates duplicates, which are named like ".gnu.linkonce.d.foo".
+# The exact correspondences are:
+#
+# Section Linkonce section
+# .text .gnu.linkonce.t.foo
+# .rodata .gnu.linkonce.r.foo
+# .data .gnu.linkonce.d.foo
+# .bss .gnu.linkonce.b.foo
+# .sdata .gnu.linkonce.s.foo
+# .sbss .gnu.linkonce.sb.foo
+# .sdata2 .gnu.linkonce.s2.foo
+# .sbss2 .gnu.linkonce.sb2.foo
+# .debug_info .gnu.linkonce.wi.foo
+# .tdata .gnu.linkonce.td.foo
+# .tbss .gnu.linkonce.tb.foo
+#
+# Each of these can also have corresponding .rel.* and .rela.* sections.
+
+test -z "$ENTRY" && ENTRY=_start
+test -z "${ELFSIZE}" && ELFSIZE=32
+test -z "${ALIGNMENT}" && ALIGNMENT="${ELFSIZE} / 8"
+test "$LD_FLAG" = "N" && DATA_ADDR=.
+test -n "$CREATE_SHLIB" && test -n "$SHLIB_DATA_ADDR" && COMMONPAGESIZE=""
+test -z "$CREATE_SHLIB" && test -n "$DATA_ADDR" && COMMONPAGESIZE=""
+DATA_SEGMENT_ALIGN="ALIGN(${SEGMENT_SIZE}) + (. & (${MAXPAGESIZE} - 1))"
+DATA_SEGMENT_END=""
+if test -n "${COMMONPAGESIZE}"; then
+ DATA_SEGMENT_ALIGN="ALIGN (${SEGMENT_SIZE}) - ((${MAXPAGESIZE} - .) & (${MAXPAGESIZE} - 1)); . = DATA_SEGMENT_ALIGN (${MAXPAGESIZE}, ${COMMONPAGESIZE})"
+ DATA_SEGMENT_END=". = DATA_SEGMENT_END (.);"
+fi
+INTERP=".interp ${RELOCATING-0} : { *(.interp) }"
+DYNAMIC=".dynamic ${RELOCATING-0} : { *(.dynamic) }"
+RODATA=".rodata ${RELOCATING-0} : { *(.rodata${RELOCATING+ .rodata.* .gnu.linkonce.r.*}) }"
+INIT_LIT=".init.literal 0 : { *(.init.literal) }"
+INIT=".init 0 : { *(.init) }"
+FINI_LIT=".fini.literal 0 : { *(.fini.literal) }"
+FINI=".fini 0 : { *(.fini) }"
+if test -z "${NO_SMALL_DATA}"; then
+ SBSS=".sbss ${RELOCATING-0} :
+ {
+ ${RELOCATING+PROVIDE (__sbss_start = .);}
+ ${RELOCATING+PROVIDE (___sbss_start = .);}
+ *(.dynsbss)
+ *(.sbss${RELOCATING+ .sbss.* .gnu.linkonce.sb.*})
+ *(.scommon)
+ ${RELOCATING+PROVIDE (__sbss_end = .);}
+ ${RELOCATING+PROVIDE (___sbss_end = .);}
+ }"
+ SBSS2=".sbss2 ${RELOCATING-0} : { *(.sbss2${RELOCATING+ .sbss2.* .gnu.linkonce.sb2.*}) }"
+ SDATA="/* We want the small data sections together, so single-instruction offsets
+ can access them all, and initialized data all before uninitialized, so
+ we can shorten the on-disk segment size. */
+ .sdata ${RELOCATING-0} :
+ {
+ ${RELOCATING+${SDATA_START_SYMBOLS}}
+ *(.sdata${RELOCATING+ .sdata.* .gnu.linkonce.s.*})
+ }"
+ SDATA2=".sdata2 ${RELOCATING-0} : { *(.sdata2${RELOCATING+ .sdata2.* .gnu.linkonce.s2.*}) }"
+ REL_SDATA=".rel.sdata ${RELOCATING-0} : { *(.rel.sdata${RELOCATING+ .rel.sdata.* .rel.gnu.linkonce.s.*}) }
+ .rela.sdata ${RELOCATING-0} : { *(.rela.sdata${RELOCATING+ .rela.sdata.* .rela.gnu.linkonce.s.*}) }"
+ REL_SBSS=".rel.sbss ${RELOCATING-0} : { *(.rel.sbss${RELOCATING+ .rel.sbss.* .rel.gnu.linkonce.sb.*}) }
+ .rela.sbss ${RELOCATING-0} : { *(.rela.sbss${RELOCATING+ .rela.sbss.* .rela.gnu.linkonce.sb.*}) }"
+ REL_SDATA2=".rel.sdata2 ${RELOCATING-0} : { *(.rel.sdata2${RELOCATING+ .rel.sdata2.* .rel.gnu.linkonce.s2.*}) }
+ .rela.sdata2 ${RELOCATING-0} : { *(.rela.sdata2${RELOCATING+ .rela.sdata2.* .rela.gnu.linkonce.s2.*}) }"
+ REL_SBSS2=".rel.sbss2 ${RELOCATING-0} : { *(.rel.sbss2${RELOCATING+ .rel.sbss2.* .rel.gnu.linkonce.sb2.*}) }
+ .rela.sbss2 ${RELOCATING-0} : { *(.rela.sbss2${RELOCATING+ .rela.sbss2.* .rela.gnu.linkonce.sb2.*}) }"
+fi
+CTOR=".ctors ${CONSTRUCTING-0} :
+ {
+ ${CONSTRUCTING+${CTOR_START}}
+ /* gcc uses crtbegin.o to find the start of
+ the constructors, so we make sure it is
+ first. Because this is a wildcard, it
+ doesn't matter if the user does not
+ actually link against crtbegin.o; the
+ linker won't look for a file to match a
+ wildcard. The wildcard also means that it
+ doesn't matter which directory crtbegin.o
+ is in. */
+
+ KEEP (*crtbegin.o(.ctors))
+
+ /* We don't want to include the .ctor section from
+ from the crtend.o file until after the sorted ctors.
+ The .ctor section from the crtend file contains the
+ end of ctors marker and it must be last */
+
+ KEEP (*(EXCLUDE_FILE (*crtend.o $OTHER_EXCLUDE_FILES) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ ${CONSTRUCTING+${CTOR_END}}
+ }"
+DTOR=".dtors ${CONSTRUCTING-0} :
+ {
+ ${CONSTRUCTING+${DTOR_START}}
+ KEEP (*crtbegin.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend.o $OTHER_EXCLUDE_FILES) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ ${CONSTRUCTING+${DTOR_END}}
+ }"
+STACK=" .stack ${RELOCATING-0}${RELOCATING+${STACK_ADDR}} :
+ {
+ ${RELOCATING+_stack = .;}
+ *(.stack)
+ }"
+
+# if this is for an embedded system, don't add SIZEOF_HEADERS.
+if [ -z "$EMBEDDED" ]; then
+ test -z "${TEXT_BASE_ADDRESS}" && TEXT_BASE_ADDRESS="${TEXT_START_ADDR} + SIZEOF_HEADERS"
+else
+ test -z "${TEXT_BASE_ADDRESS}" && TEXT_BASE_ADDRESS="${TEXT_START_ADDR}"
+fi
+
+cat <<EOF
+ENTRY(${ENTRY})
+
+${RELOCATING+${LIB_SEARCH_DIRS}}
+${RELOCATING+/* Do we need any of these for elf?
+ __DYNAMIC = 0; ${STACKZERO+${STACKZERO}} ${SHLIB_PATH+${SHLIB_PATH}} */}
+${RELOCATING+${EXECUTABLE_SYMBOLS}}
+${RELOCATING+${INPUT_FILES}}
+${RELOCATING- /* For some reason, the Solaris linker makes bad executables
+ if gld -r is used and the intermediate file has sections starting
+ at non-zero addresses. Could be a Solaris ld bug, could be a GNU ld
+ bug. But for now assigning the zero vmas works. */}
+
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ ${CREATE_SHLIB-${RELOCATING+. = ${TEXT_BASE_ADDRESS};}}
+ ${CREATE_SHLIB+${RELOCATING+. = ${SHLIB_TEXT_START_ADDR:-0} + SIZEOF_HEADERS;}}
+ ${CREATE_SHLIB-${INTERP}}
+ ${INITIAL_READONLY_SECTIONS}
+ ${TEXT_DYNAMIC+${DYNAMIC}}
+ .hash ${RELOCATING-0} : { *(.hash) }
+ .dynsym ${RELOCATING-0} : { *(.dynsym) }
+ .dynstr ${RELOCATING-0} : { *(.dynstr) }
+ .gnu.version ${RELOCATING-0} : { *(.gnu.version) }
+ .gnu.version_d ${RELOCATING-0}: { *(.gnu.version_d) }
+ .gnu.version_r ${RELOCATING-0}: { *(.gnu.version_r) }
+
+EOF
+if [ "x$COMBRELOC" = x ]; then
+ COMBRELOCCAT=cat
+else
+ COMBRELOCCAT="cat > $COMBRELOC"
+fi
+eval $COMBRELOCCAT <<EOF
+ .rel.init ${RELOCATING-0} : { *(.rel.init) }
+ .rela.init ${RELOCATING-0} : { *(.rela.init) }
+ .rel.text ${RELOCATING-0} : { *(.rel.text${RELOCATING+ .rel.text.* .rel.gnu.linkonce.t.*}) }
+ .rela.text ${RELOCATING-0} : { *(.rela.text${RELOCATING+ .rela.text.* .rela.gnu.linkonce.t.*}) }
+ .rel.fini ${RELOCATING-0} : { *(.rel.fini) }
+ .rela.fini ${RELOCATING-0} : { *(.rela.fini) }
+ .rel.rodata ${RELOCATING-0} : { *(.rel.rodata${RELOCATING+ .rel.rodata.* .rel.gnu.linkonce.r.*}) }
+ .rela.rodata ${RELOCATING-0} : { *(.rela.rodata${RELOCATING+ .rela.rodata.* .rela.gnu.linkonce.r.*}) }
+ ${OTHER_READONLY_RELOC_SECTIONS}
+ .rel.data ${RELOCATING-0} : { *(.rel.data${RELOCATING+ .rel.data.* .rel.gnu.linkonce.d.*}) }
+ .rela.data ${RELOCATING-0} : { *(.rela.data${RELOCATING+ .rela.data.* .rela.gnu.linkonce.d.*}) }
+ .rel.tdata ${RELOCATING-0} : { *(.rel.tdata${RELOCATING+ .rel.tdata.* .rel.gnu.linkonce.td.*}) }
+ .rela.tdata ${RELOCATING-0} : { *(.rela.tdata${RELOCATING+ .rela.tdata.* .rela.gnu.linkonce.td.*}) }
+ .rel.tbss ${RELOCATING-0} : { *(.rel.tbss${RELOCATING+ .rel.tbss.* .rel.gnu.linkonce.tb.*}) }
+ .rela.tbss ${RELOCATING-0} : { *(.rela.tbss${RELOCATING+ .rela.tbss.* .rela.gnu.linkonce.tb.*}) }
+ .rel.ctors ${RELOCATING-0} : { *(.rel.ctors) }
+ .rela.ctors ${RELOCATING-0} : { *(.rela.ctors) }
+ .rel.dtors ${RELOCATING-0} : { *(.rel.dtors) }
+ .rela.dtors ${RELOCATING-0} : { *(.rela.dtors) }
+ .rel.got ${RELOCATING-0} : { *(.rel.got) }
+ .rela.got ${RELOCATING-0} : { *(.rela.got) }
+ ${OTHER_GOT_RELOC_SECTIONS}
+ ${REL_SDATA}
+ ${REL_SBSS}
+ ${REL_SDATA2}
+ ${REL_SBSS2}
+ .rel.bss ${RELOCATING-0} : { *(.rel.bss${RELOCATING+ .rel.bss.* .rel.gnu.linkonce.b.*}) }
+ .rela.bss ${RELOCATING-0} : { *(.rela.bss${RELOCATING+ .rela.bss.* .rela.gnu.linkonce.b.*}) }
+EOF
+if [ -n "$COMBRELOC" ]; then
+cat <<EOF
+ .rel.dyn ${RELOCATING-0} :
+ {
+EOF
+sed -e '/^[ ]*[{}][ ]*$/d;/:[ ]*$/d;/\.rela\./d;s/^.*: { *\(.*\)}$/ \1/' $COMBRELOC
+cat <<EOF
+ }
+ .rela.dyn ${RELOCATING-0} :
+ {
+EOF
+sed -e '/^[ ]*[{}][ ]*$/d;/:[ ]*$/d;/\.rel\./d;s/^.*: { *\(.*\)}/ \1/' $COMBRELOC
+cat <<EOF
+ }
+EOF
+fi
+cat <<EOF
+ .rel.plt ${RELOCATING-0} : { *(.rel.plt) }
+ .rela.plt ${RELOCATING-0} : { *(.rela.plt) }
+ ${OTHER_PLT_RELOC_SECTIONS}
+
+ ${RELOCATING-$INIT_LIT}
+ ${RELOCATING-$INIT}
+
+ .text ${RELOCATING-0} :
+ {
+ *(.got.plt* .plt*)
+
+ ${RELOCATING+${INIT_START}}
+ ${RELOCATING+KEEP (*(.init.literal))}
+ ${RELOCATING+KEEP (*(.init))}
+ ${RELOCATING+${INIT_END}}
+
+ ${RELOCATING+${TEXT_START_SYMBOLS}}
+ *(.literal .text .stub${RELOCATING+ .text.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*})
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ ${RELOCATING+${OTHER_TEXT_SECTIONS}}
+
+ ${RELOCATING+${FINI_START}}
+ ${RELOCATING+KEEP (*(.fini.literal))}
+ ${RELOCATING+KEEP (*(.fini))}
+ ${RELOCATING+${FINI_END}}
+ } =${NOP-0}
+
+ ${RELOCATING-$FINI_LIT}
+ ${RELOCATING-$FINI}
+
+ ${RELOCATING+PROVIDE (__etext = .);}
+ ${RELOCATING+PROVIDE (_etext = .);}
+ ${RELOCATING+PROVIDE (etext = .);}
+ ${WRITABLE_RODATA-${RODATA}}
+ .rodata1 ${RELOCATING-0} : { *(.rodata1) }
+ ${CREATE_SHLIB-${SDATA2}}
+ ${CREATE_SHLIB-${SBSS2}}
+ ${OTHER_READONLY_SECTIONS}
+ .eh_frame_hdr : { *(.eh_frame_hdr) }
+
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ ${CREATE_SHLIB-${RELOCATING+. = ${DATA_ADDR-${DATA_SEGMENT_ALIGN}};}}
+ ${CREATE_SHLIB+${RELOCATING+. = ${SHLIB_DATA_ADDR-${DATA_SEGMENT_ALIGN}};}}
+
+ /* Ensure the __preinit_array_start label is properly aligned. We
+ could instead move the label definition inside the section, but
+ the linker would then create the section even if it turns out to
+ be empty, which isn't pretty. */
+ ${RELOCATING+. = ALIGN(${ALIGNMENT});}
+ ${RELOCATING+${CREATE_SHLIB-PROVIDE (__preinit_array_start = .);}}
+ .preinit_array ${RELOCATING-0} : { *(.preinit_array) }
+ ${RELOCATING+${CREATE_SHLIB-PROVIDE (__preinit_array_end = .);}}
+
+ ${RELOCATING+${CREATE_SHLIB-PROVIDE (__init_array_start = .);}}
+ .init_array ${RELOCATING-0} : { *(.init_array) }
+ ${RELOCATING+${CREATE_SHLIB-PROVIDE (__init_array_end = .);}}
+
+ ${RELOCATING+${CREATE_SHLIB-PROVIDE (__fini_array_start = .);}}
+ .fini_array ${RELOCATING-0} : { *(.fini_array) }
+ ${RELOCATING+${CREATE_SHLIB-PROVIDE (__fini_array_end = .);}}
+
+ .data ${RELOCATING-0} :
+ {
+ ${RELOCATING+${DATA_START_SYMBOLS}}
+ *(.data${RELOCATING+ .data.* .gnu.linkonce.d.*})
+ ${CONSTRUCTING+SORT(CONSTRUCTORS)}
+ }
+ .data1 ${RELOCATING-0} : { *(.data1) }
+ .tdata ${RELOCATING-0} : { *(.tdata${RELOCATING+ .tdata.* .gnu.linkonce.td.*}) }
+ .tbss ${RELOCATING-0} : { *(.tbss${RELOCATING+ .tbss.* .gnu.linkonce.tb.*})${RELOCATING+ *(.tcommon)} }
+ .eh_frame ${RELOCATING-0} : { KEEP (*(.eh_frame)) }
+ .gcc_except_table ${RELOCATING-0} : { *(.gcc_except_table) }
+ ${WRITABLE_RODATA+${RODATA}}
+ ${OTHER_READWRITE_SECTIONS}
+ ${TEXT_DYNAMIC-${DYNAMIC}}
+ ${RELOCATING+${CTOR}}
+ ${RELOCATING+${DTOR}}
+ .jcr ${RELOCATING-0} : { KEEP (*(.jcr)) }
+ ${RELOCATING+${OTHER_GOT_SYMBOLS}}
+ .got ${RELOCATING-0} : { *(.got) }
+ ${OTHER_GOT_SECTIONS}
+ ${CREATE_SHLIB+${SDATA2}}
+ ${CREATE_SHLIB+${SBSS2}}
+ ${SDATA}
+ ${OTHER_SDATA_SECTIONS}
+ ${RELOCATING+_edata = .;}
+ ${RELOCATING+PROVIDE (edata = .);}
+ ${RELOCATING+__bss_start = .;}
+ ${RELOCATING+${OTHER_BSS_SYMBOLS}}
+ ${SBSS}
+ .bss ${RELOCATING-0} :
+ {
+ *(.dynbss)
+ *(.bss${RELOCATING+ .bss.* .gnu.linkonce.b.*})
+ *(COMMON)
+ /* Align here to ensure that the .bss section occupies space up to
+ _end. Align after .bss to ensure correct alignment even if the
+ .bss section disappears because there are no input sections. */
+ ${RELOCATING+. = ALIGN(${ALIGNMENT});}
+ }
+ ${OTHER_BSS_SECTIONS}
+ ${RELOCATING+. = ALIGN(${ALIGNMENT});}
+ ${RELOCATING+_end = .;}
+ ${RELOCATING+${OTHER_BSS_END_SYMBOLS}}
+ ${RELOCATING+PROVIDE (end = .);}
+ ${RELOCATING+${DATA_SEGMENT_END}}
+
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+
+ .comment 0 : { *(.comment) }
+
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+
+ ${STACK_ADDR+${STACK}}
+ ${OTHER_SECTIONS}
+ ${RELOCATING+${OTHER_END_SYMBOLS}}
+}
+EOF
diff --git a/ld/testsuite/ChangeLog b/ld/testsuite/ChangeLog
index 145c3c0..d3cf2ca 100644
--- a/ld/testsuite/ChangeLog
+++ b/ld/testsuite/ChangeLog
@@ -1,3 +1,18 @@
+2003-04-01 Bob Wilson <bob.wilson@acm.org>
+
+ * ld-elf/merge.d: xfail xtensa-*-*.
+ * ld-scripts/crossref.exp: Add -mtext-section-literals to CFLAGS
+ for Xtensa targets.
+ * ld-srec/srec.exp: Add -no-relax flag for Xtensa targets.
+ * ld-xtensa/coalesce1.s: New file.
+ * ld-xtensa/coalesce2.s: Likewise.
+ * ld-xtensa/coalesce.exp: Likewise.
+ * ld-xtensa/coalesce.t: Likewise.
+ * ld-xtensa/lcall1.s: Likewise.
+ * ld-xtensa/lcall2.s: Likewise.
+ * ld-xtensa/lcall.exp: Likewise.
+ * ld-xtensa/lcall.t: Likewise.
+
2003-03-25 Alexandre Oliva <aoliva@redhat.com>
* ld-mips-elf/mips-elf.exp: Added...
diff --git a/ld/testsuite/ld-elf/merge.d b/ld/testsuite/ld-elf/merge.d
index cbefcaf..6331927 100644
--- a/ld/testsuite/ld-elf/merge.d
+++ b/ld/testsuite/ld-elf/merge.d
@@ -3,7 +3,7 @@
#objdump: -s
#xfail: "arc-*-*" "avr-*-*" "cris-*-*" "dlx-*-*" "fr30-*-*" "frv-*-*"
#xfail: "hppa*-*-*" "h8300-*-*" "i960-*-*" "ip2k-*-*" "m32r-*-*" "mcore-*-*"
-#xfail: "mn10*-*-*" "openrisc-*-*" "pj-*-*" "sparc*-*-*"
+#xfail: "mn10*-*-*" "openrisc-*-*" "pj-*-*" "sparc*-*-*" "xtensa-*-*"
.*: file format .*elf.*
diff --git a/ld/testsuite/ld-scripts/crossref.exp b/ld/testsuite/ld-scripts/crossref.exp
index fbc3151..4fd81b3 100644
--- a/ld/testsuite/ld-scripts/crossref.exp
+++ b/ld/testsuite/ld-scripts/crossref.exp
@@ -26,6 +26,13 @@ if { [which $CC] == 0 } {
return
}
+# Xtensa targets currently default to putting literal values in a separate
+# section and that requires linker script support, so put literals in text.
+global CFLAGS
+if [istarget xtensa*-*-*] {
+ set CFLAGS "$CFLAGS -mtext-section-literals"
+}
+
if { ![ld_compile $CC "$srcdir/$subdir/cross1.c" tmpdir/cross1.o] \
|| ![ld_compile $CC "$srcdir/$subdir/cross2.c" tmpdir/cross2.o] } {
unresolved $test1
diff --git a/ld/testsuite/ld-srec/srec.exp b/ld/testsuite/ld-srec/srec.exp
index a965c22..979cfda 100644
--- a/ld/testsuite/ld-srec/srec.exp
+++ b/ld/testsuite/ld-srec/srec.exp
@@ -288,6 +288,11 @@ proc run_srec_test { test objs } {
if [istarget v850*-*-elf] {
set objs "$objs -L ../gcc -lgcc"
}
+
+ # Xtensa ELF targets relax by default; S-Record linker does not
+ if [istarget xtensa*-*-*] {
+ set flags "$flags -no-relax"
+ }
if { ![ld_simple_link $ld tmpdir/sr1 "$flags $objs"] \
|| ![ld_simple_link $ld tmpdir/sr2.sr "$flags --oformat srec $objs"] } {
diff --git a/ld/testsuite/ld-xtensa/coalesce.exp b/ld/testsuite/ld-xtensa/coalesce.exp
new file mode 100644
index 0000000..ef51d6f
--- /dev/null
+++ b/ld/testsuite/ld-xtensa/coalesce.exp
@@ -0,0 +1,93 @@
+# Test literal coaslescing for Xtensa targets.
+# By David Heine, Tensilica, Inc.
+# Copyright 2002, 2003
+# Free Software Foundation, Inc.
+#
+# This file 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.
+
+set testname "COALESCE"
+
+set OBJDUMPFLAGS "-dr"
+
+#
+# default_ld_objdump
+# run objdump on a file
+#
+proc default_ld_objdump { objdump object outputfile } {
+ global OBJDUMPFLAGS
+ global objdump_output
+ global host_triplet
+
+ if {[which $objdump] == 0} then {
+ perror "$objdump does not exist"
+ return 0
+ }
+
+ if ![info exists OBJDUMPFLAGS] { set OBJDUMPFLAGS "" }
+
+ verbose -log "$objdump $OBJDUMPFLAGS $object >$outputfile"
+
+ catch "exec $objdump $OBJDUMPFLAGS $object >$outputfile" exec_output
+ set exec_output [prune_warnings $exec_output]
+ if [string match "" $exec_output] then {
+ return 1
+ } else {
+ verbose -log "$exec_output"
+ perror "$object: objdump failed"
+ return 0
+ }
+}
+
+
+if ![ld_assemble $as $srcdir/$subdir/coalesce1.s tmpdir/coalesce1.o] {
+ unresolved $testname
+ return
+}
+if ![ld_assemble $as $srcdir/$subdir/coalesce2.s tmpdir/coalesce2.o] {
+ unresolved $testname
+ return
+}
+
+set object "tmpdir/coalesce"
+set outputfile "$object.txt"
+
+if ![ld_simple_link $ld $object "-T $srcdir/$subdir/coalesce.t tmpdir/coalesce1.o tmpdir/coalesce2.o"] {
+ verbose -log "failure in ld"
+ fail $testname
+ return
+}
+
+if ![default_ld_objdump $objdump $object $outputfile ] {
+ verbose -log "failure in objdump"
+ fail $testname
+ return
+}
+
+set file [open $outputfile r]
+set found 0
+
+while { [gets $file line] != -1 } {
+ # verbose "$line" 2
+ if [regexp "^0000000c <main>:" $line] {
+ set found 1
+ }
+}
+close $file
+if $found {
+ pass $testname
+} else {
+ fail $testname
+}
+
diff --git a/ld/testsuite/ld-xtensa/coalesce.t b/ld/testsuite/ld-xtensa/coalesce.t
new file mode 100644
index 0000000..7bff69f
--- /dev/null
+++ b/ld/testsuite/ld-xtensa/coalesce.t
@@ -0,0 +1,6 @@
+SECTIONS
+{
+ .text 0x00000000 : {
+ *(.literal .text)
+ }
+}
diff --git a/ld/testsuite/ld-xtensa/coalesce1.s b/ld/testsuite/ld-xtensa/coalesce1.s
new file mode 100644
index 0000000..4374463
--- /dev/null
+++ b/ld/testsuite/ld-xtensa/coalesce1.s
@@ -0,0 +1,15 @@
+ .global foo
+ .data
+ .global g_name
+ .align 4
+g_name:
+ .word 0xffffffff
+ .text
+ .global main
+ .align 4
+main:
+ entry a5,16
+ movi a5,20000
+ movi a6,g_name
+ call8 foo
+ ret
diff --git a/ld/testsuite/ld-xtensa/coalesce2.s b/ld/testsuite/ld-xtensa/coalesce2.s
new file mode 100644
index 0000000..962915c
--- /dev/null
+++ b/ld/testsuite/ld-xtensa/coalesce2.s
@@ -0,0 +1,9 @@
+ .text
+ .global foo
+ .global g_name
+foo:
+ entry a5,16
+ movi a5,20000
+ movi a6,g_name
+ movi a7,50000
+ ret
diff --git a/ld/testsuite/ld-xtensa/lcall.exp b/ld/testsuite/ld-xtensa/lcall.exp
new file mode 100644
index 0000000..9879a55
--- /dev/null
+++ b/ld/testsuite/ld-xtensa/lcall.exp
@@ -0,0 +1,107 @@
+# Test Xtensa longcall optimization.
+# By David Heine, Tensilica, Inc.
+# Copyright 2002, 2003
+# Free Software Foundation, Inc.
+#
+# This file 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.
+
+set testname "LCALL"
+
+set OBJDUMPFLAGS "-dr"
+
+#
+# default_ld_objdump
+# run objdump on a file
+#
+proc default_ld_objdump { objdump object outputfile } {
+ global OBJDUMPFLAGS
+ global objdump_output
+ global host_triplet
+
+ if {[which $objdump] == 0} then {
+ perror "$objdump does not exist"
+ return 0
+ }
+
+ if ![info exists OBJDUMPFLAGS] { set OBJDUMPFLAGS "" }
+
+ verbose -log "$objdump $OBJDUMPFLAGS $object >$outputfile"
+
+ catch "exec $objdump $OBJDUMPFLAGS $object >$outputfile" exec_output
+ set exec_output [prune_warnings $exec_output]
+ if [string match "" $exec_output] then {
+ return 1
+ } else {
+ verbose -log "$exec_output"
+ perror "$object: objdump failed"
+ return 0
+ }
+}
+
+
+if ![ld_assemble $as $srcdir/$subdir/lcall1.s tmpdir/lcall1.o] {
+ unresolved $testname
+ return
+}
+if ![ld_assemble $as $srcdir/$subdir/lcall2.s tmpdir/lcall2.o] {
+ unresolved $testname
+ return
+}
+
+set object "tmpdir/lcall"
+set outputfile "$object.txt"
+
+if ![ld_simple_link $ld $object "-T $srcdir/$subdir/lcall.t tmpdir/lcall1.o tmpdir/lcall2.o"] {
+ verbose -log "failure in ld"
+ fail $testname
+ return
+}
+
+if ![default_ld_objdump $objdump $object $outputfile ] {
+ verbose -log "failure in objdump"
+ fail $testname
+ return
+}
+
+set file [open $outputfile r]
+while { [gets $file line] != -1 } {
+ # verbose "$line" 2
+ if [regexp "l32r" $line] {
+ verbose -log "Found an l32r in the linked object"
+ verbose -log "$line"
+ fail $testname
+ }
+}
+close $file
+pass $testname
+
+
+set testname "LCALL2"
+set file [open $outputfile r]
+set found 0
+
+while { [gets $file line] != -1 } {
+ # verbose "$line" 2
+ if [regexp "^00000004 <label1>:" $line] {
+ set found 1
+ }
+}
+close $file
+if $found {
+ pass $testname
+} else {
+ fail $testname
+}
+
diff --git a/ld/testsuite/ld-xtensa/lcall.t b/ld/testsuite/ld-xtensa/lcall.t
new file mode 100644
index 0000000..7bff69f
--- /dev/null
+++ b/ld/testsuite/ld-xtensa/lcall.t
@@ -0,0 +1,6 @@
+SECTIONS
+{
+ .text 0x00000000 : {
+ *(.literal .text)
+ }
+}
diff --git a/ld/testsuite/ld-xtensa/lcall1.s b/ld/testsuite/ld-xtensa/lcall1.s
new file mode 100644
index 0000000..1056c6a
--- /dev/null
+++ b/ld/testsuite/ld-xtensa/lcall1.s
@@ -0,0 +1,12 @@
+.global foo
+.text
+ .align 4
+label1:
+ .begin literal
+ .word 0xffffffff
+ .end literal
+ entry a5,16
+.begin longcalls
+ call4 foo
+.end longcalls
+ nop
diff --git a/ld/testsuite/ld-xtensa/lcall2.s b/ld/testsuite/ld-xtensa/lcall2.s
new file mode 100644
index 0000000..f4784f0
--- /dev/null
+++ b/ld/testsuite/ld-xtensa/lcall2.s
@@ -0,0 +1,5 @@
+.global foo
+foo:
+ entry a5,16
+ nop
+ ret