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