# This shell script emits a C file. -*- C -*-
# Copyright 2003, 2004, 2005
# 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>
#include "../bfd/elf-bfd.h"
#include "../bfd/libbfd.h"
#include "elf/xtensa.h"
#include "bfd.h"
static void xtensa_wild_group_interleave (lang_statement_union_type *);
static void xtensa_colocate_output_literals (lang_statement_union_type *);
static bfd_boolean disable_relaxation = FALSE;
static bfd_vma xtensa_page_power = 12;
static bfd_boolean xtensa_use_literal_pages = FALSE;
#define EXTRA_VALIDATION 0
static char *
elf_xtensa_choose_target (int argc ATTRIBUTE_UNUSED,
char **argv ATTRIBUTE_UNUSED)
{
if (XCHAL_HAVE_BE)
return "${BIG_OUTPUT_FORMAT}";
else
return "${LITTLE_OUTPUT_FORMAT}";
}
static bfd_boolean
elf_xtensa_place_orphan (lang_input_statement_type *file, asection *s)
{
if (link_info.relocatable)
return FALSE;
return gld${EMULATION_NAME}_place_orphan (file, s);
}
static void
elf_xtensa_before_parse (void)
{
gld${EMULATION_NAME}_before_parse ();
}
static void
remove_section (bfd *abfd, asection *os)
{
asection **spp;
for (spp = &abfd->sections; *spp; spp = &(*spp)->next)
if (*spp == os)
{
*spp = os->next;
os->owner->section_count--;
break;
}
}
static bfd_boolean
replace_insn_sec_with_prop_sec (bfd *abfd,
const char *insn_sec_name,
const char *prop_sec_name,
char **error_message)
{
asection *insn_sec;
asection *prop_sec;
bfd_byte *prop_contents = NULL;
bfd_byte *insn_contents = NULL;
unsigned entry_count;
unsigned entry;
Elf_Internal_Shdr *symtab_hdr;
Elf_Internal_Rela *internal_relocs = NULL;
unsigned reloc_count;
*error_message = "";
insn_sec = bfd_get_section_by_name (abfd, insn_sec_name);
if (insn_sec == NULL)
return TRUE;
entry_count = insn_sec->size / 8;
prop_sec = bfd_get_section_by_name (abfd, prop_sec_name);
if (prop_sec != NULL && insn_sec != NULL)
{
*error_message = _("file already has property tables");
return FALSE;
}
if (insn_sec->size != 0)
{
insn_contents = (bfd_byte *) bfd_malloc (insn_sec->size);
if (insn_contents == NULL)
{
*error_message = _("out of memory");
goto cleanup;
}
if (! bfd_get_section_contents (abfd, insn_sec, insn_contents,
(file_ptr) 0, insn_sec->size))
{
*error_message = _("failed to read section contents");
goto cleanup;
}
}
prop_sec_name = strdup (prop_sec_name);
prop_sec = bfd_make_section (abfd, prop_sec_name);
if (prop_sec == NULL
|| ! bfd_set_section_flags (abfd, prop_sec,
bfd_get_section_flags (abfd, insn_sec))
|| ! bfd_set_section_alignment (abfd, prop_sec, 2))
{
*error_message = _("could not create new section");
goto cleanup;
}
if (! bfd_set_section_flags (abfd, prop_sec,
bfd_get_section_flags (abfd, insn_sec))
|| ! bfd_set_section_alignment (abfd, prop_sec, 2))
{
*error_message = _("could not set new section properties");
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;
elf_section_data (prop_sec)->rel_hdr.sh_entsize =
sizeof (Elf32_External_Rela);
elf_section_data (prop_sec)->rel_hdr.sh_size =
elf_section_data (insn_sec)->rel_hdr.sh_size;
if (prop_contents == NULL && prop_sec->size != 0)
{
*error_message = _("could not allocate section contents");
goto cleanup;
}
reloc_count = insn_sec->reloc_count;
if (reloc_count != 0)
{
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;
}
}
if (internal_relocs != NULL)
{
elf_section_data (prop_sec)->relocs = internal_relocs;
prop_sec->reloc_count = reloc_count;
}
for (entry = 0; entry < entry_count; ++entry)
{
unsigned value;
unsigned flags = (XTENSA_PROP_INSN | XTENSA_PROP_INSN_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);
}
if (internal_relocs)
{
unsigned i;
symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
for (i = 0; i < reloc_count; i++)
{
Elf_Internal_Rela *rela;
unsigned r_offset;
rela = &internal_relocs[i];
r_offset = rela->r_offset;
r_offset += 4 * (r_offset / 8);
rela->r_offset = r_offset;
}
}
remove_section (abfd, insn_sec);
if (insn_contents)
free (insn_contents);
return TRUE;
cleanup:
if (prop_sec && prop_sec->owner)
remove_section (abfd, prop_sec);
if (insn_contents)
free (insn_contents);
if (internal_relocs)
free (internal_relocs);
return FALSE;
}
#define PROP_SEC_BASE_NAME ".xt.prop"
#define INSN_SEC_BASE_NAME ".xt.insn"
#define LINKONCE_SEC_OLD_TEXT_BASE_NAME ".gnu.linkonce.x."
static void
replace_instruction_table_sections (bfd *abfd, asection *sec)
{
char *message = "";
const char *insn_sec_name = NULL;
char *prop_sec_name = NULL;
char *owned_prop_sec_name = NULL;
const char *sec_name;
sec_name = bfd_get_section_name (abfd, sec);
if (strcmp (sec_name, INSN_SEC_BASE_NAME) == 0)
{
insn_sec_name = INSN_SEC_BASE_NAME;
prop_sec_name = PROP_SEC_BASE_NAME;
}
else if (strncmp (sec_name, LINKONCE_SEC_OLD_TEXT_BASE_NAME,
strlen (LINKONCE_SEC_OLD_TEXT_BASE_NAME)) == 0)
{
insn_sec_name = sec_name;
owned_prop_sec_name = (char *) xmalloc (strlen (sec_name) + 20);
prop_sec_name = owned_prop_sec_name;
strcpy (prop_sec_name, ".gnu.linkonce.prop.t.");
strcat (prop_sec_name,
sec_name + strlen (LINKONCE_SEC_OLD_TEXT_BASE_NAME));
}
if (insn_sec_name != NULL)
{
if (! replace_insn_sec_with_prop_sec (abfd, insn_sec_name, prop_sec_name,
&message))
{
einfo (_("%P: warning: failed to convert %s table in %B (%s); subsequent disassembly may be incomplete\n"),
insn_sec_name, abfd, message);
}
}
if (owned_prop_sec_name)
free (owned_prop_sec_name);
}
static void
elf_xtensa_after_open (void)
{
bfd *abfd;
gld${EMULATION_NAME}_after_open ();
for (abfd = link_info.input_bfds;
abfd != NULL;
abfd = abfd->link_next)
{
asection *sec = abfd->sections;
asection *next_sec;
while (sec != NULL)
{
next_sec = sec->next;
replace_instruction_table_sections (abfd, sec);
sec = next_sec;
}
}
}
static void
elf_xtensa_before_allocation (void)
{
bfd *in_bfd;
bfd_boolean is_big_endian = XCHAL_HAVE_BE;
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"));
}
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"));
}
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);
}
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;
asection *tgt;
reloc_deps_e *next;
};
struct reloc_deps_section_t
{
reloc_deps_e *preds;
reloc_deps_e *succs;
bfd_boolean is_only_literal;
};
struct reloc_deps_graph_t
{
size_t count;
size_t size;
asection **sections;
};
static void xtensa_layout_wild
(const reloc_deps_graph *, lang_wild_statement_type *);
typedef void (*deps_callback_t) (asection *,
bfd_vma,
asection *,
bfd_vma,
void *);
extern bfd_boolean xtensa_callback_required_dependence
(bfd *, asection *, struct bfd_link_info *, deps_callback_t, void *);
static void xtensa_ldlang_clear_addresses (lang_statement_union_type *);
static bfd_boolean ld_local_file_relocations_fit
(lang_statement_union_type *, const reloc_deps_graph *);
static bfd_vma ld_assign_relative_paged_dot
(bfd_vma, lang_statement_union_type *, const reloc_deps_graph *,
bfd_boolean);
static bfd_vma ld_xtensa_insert_page_offsets
(bfd_vma, lang_statement_union_type *, reloc_deps_graph *, bfd_boolean);
#if EXTRA_VALIDATION
static size_t ld_count_children (lang_statement_union_type *);
#endif
extern lang_statement_list_type constructor_list;
static void
lang_for_each_statement_worker (void (*func) (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;
}
}
}
static reloc_deps_section *
xtensa_get_section_deps (const reloc_deps_graph *deps ATTRIBUTE_UNUSED,
asection *sec)
{
reloc_deps_section *sec_deps = sec->userdata;
return sec_deps;
}
static void
xtensa_set_section_deps (const reloc_deps_graph *deps ATTRIBUTE_UNUSED,
asection *sec,
reloc_deps_section *deps_section)
{
sec->userdata = deps_section;
}
static void
xtensa_append_section_deps (reloc_deps_graph *deps, asection *sec)
{
if (deps->size <= deps->count)
{
asection **new_sections;
size_t i;
size_t new_size;
new_size = deps->size * 2;
if (new_size == 0)
new_size = 20;
new_sections = xmalloc (sizeof (asection *) * new_size);
memset (new_sections, 0, sizeof (asection *) * new_size);
for (i = 0; i < deps->count; i++)
{
new_sections[i] = deps->sections[i];
}
if (deps->sections != NULL)
free (deps->sections);
deps->sections = new_sections;
deps->size = new_size;
}
deps->sections[deps->count] = sec;
deps->count++;
}
static void
free_reloc_deps_graph (reloc_deps_graph *deps)
{
size_t i;
for (i = 0; i < deps->count; i++)
{
asection *sec = deps->sections[i];
reloc_deps_section *sec_deps;
sec_deps = xtensa_get_section_deps (deps, sec);
if (sec_deps)
{
reloc_deps_e *next;
while (sec_deps->succs != NULL)
{
next = sec_deps->succs->next;
free (sec_deps->succs);
sec_deps->succs = next;
}
while (sec_deps->preds != NULL)
{
next = sec_deps->preds->next;
free (sec_deps->preds);
sec_deps->preds = next;
}
free (sec_deps);
}
xtensa_set_section_deps (deps, sec, NULL);
}
if (deps->sections)
free (deps->sections);
free (deps);
}
static bfd_boolean
section_is_source (const reloc_deps_graph *deps ATTRIBUTE_UNUSED,
lang_statement_union_type *s)
{
asection *sec;
const reloc_deps_section *sec_deps;
if (s->header.type != lang_input_section_enum)
return FALSE;
sec = s->input_section.section;
sec_deps = xtensa_get_section_deps (deps, sec);
return sec_deps && sec_deps->succs != NULL;
}
static bfd_boolean
section_is_target (const reloc_deps_graph *deps ATTRIBUTE_UNUSED,
lang_statement_union_type *s)
{
asection *sec;
const reloc_deps_section *sec_deps;
if (s->header.type != lang_input_section_enum)
return FALSE;
sec = s->input_section.section;
sec_deps = xtensa_get_section_deps (deps, sec);
return sec_deps && sec_deps->preds != NULL;
}
static bfd_boolean
section_is_source_or_target (const reloc_deps_graph *deps ATTRIBUTE_UNUSED,
lang_statement_union_type *s)
{
return (section_is_source (deps, s)
|| section_is_target (deps, s));
}
typedef struct xtensa_ld_iter_stack_t xtensa_ld_iter_stack;
typedef struct xtensa_ld_iter_t xtensa_ld_iter;
struct xtensa_ld_iter_t
{
lang_statement_union_type *parent;
lang_statement_list_type *l;
lang_statement_union_type **loc;
};
struct xtensa_ld_iter_stack_t
{
xtensa_ld_iter iterloc;
xtensa_ld_iter_stack *next;
xtensa_ld_iter_stack *prev;
};
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;
for (e = &to->l->head; *e != NULL; e = &(*e)->header.next)
;
to->l->tail = e;
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
}
static bfd_boolean
iter_stack_empty (xtensa_ld_iter_stack **stack_p)
{
return *stack_p == NULL;
}
static bfd_boolean
iter_stack_push (xtensa_ld_iter_stack **stack_p,
lang_statement_union_type *parent)
{
xtensa_ld_iter_stack *stack;
lang_statement_list_type *l = NULL;
switch (parent->header.type)
{
case lang_output_section_statement_enum:
l = &parent->output_section_statement.children;
break;
case lang_wild_statement_enum:
l = &parent->wild_statement.children;
break;
case lang_group_statement_enum:
l = &parent->group_statement.children;
break;
default:
ASSERT (0);
return FALSE;
}
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);
}
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 (current != NULL)
{
switch (current->header.type)
{
case lang_output_section_statement_enum:
case lang_wild_statement_enum:
case lang_group_statement_enum:
if (iter_stack_push (stack_p, *stack->iterloc.loc))
return;
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);
}
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)
{
xtensa_ld_iter_stack *stack = NULL;
xtensa_ld_iter_stack **stack_p = &stack;
xtensa_ld_iter front;
xtensa_ld_iter *front_p = NULL;
xtensa_ld_iter current;
xtensa_ld_iter *current_p = NULL;
bfd_boolean in_literals = FALSE;
if (deps->count == 0)
return;
iter_stack_create (stack_p, statement);
while (!iter_stack_empty (stack_p))
{
bfd_boolean skip_increment = FALSE;
lang_statement_union_type *l = iter_stack_current (stack_p);
switch (l->header.type)
{
case lang_assignment_statement_enum:
front_p = NULL;
in_literals = FALSE;
break;
case lang_input_section_enum:
if (front_p == NULL)
{
in_literals = (section_is_target (deps, l)
&& !section_is_source (deps, l));
if (in_literals)
{
front_p = &front;
iter_stack_copy_current (stack_p, front_p);
}
}
else
{
bfd_boolean is_target;
current_p = ¤t;
iter_stack_copy_current (stack_p, current_p);
is_target = (section_is_target (deps, l)
&& !section_is_source (deps, l));
if (in_literals)
{
iter_stack_copy_current (stack_p, front_p);
if (!is_target)
in_literals = FALSE;
}
else
{
if (is_target)
{
ld_xtensa_move_section_after (front_p, current_p);
ld_assign_relative_paged_dot (0x100000,
statement,
deps,
xtensa_use_literal_pages);
if (!ld_local_file_relocations_fit (statement, deps))
{
ld_xtensa_move_section_after (current_p, front_p);
iter_stack_copy_current (stack_p, front_p);
}
else
{
front_p->loc = &(*front_p->loc)->header.next;
skip_increment = TRUE;
}
}
}
}
break;
default:
break;
}
if (!skip_increment)
iter_stack_next (stack_p);
else
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)
{
lang_statement_union_type **front;
lang_statement_union_type **current;
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))
{
xtensa_ld_iter front_iter;
xtensa_ld_iter current_iter;
front_iter.parent = (lang_statement_union_type *) w;
front_iter.l = &w->children;
front_iter.loc = front;
current_iter.parent = (lang_statement_union_type *) w;
current_iter.l = &w->children;
current_iter.loc = current;
ld_xtensa_move_section_after (&front_iter, ¤t_iter);
front = &(*front)->header.next;
}
else
{
current = &(*current)->header.next;
}
}
}
static bfd_boolean
deps_has_sec_edge (const reloc_deps_graph *deps, asection *src, asection *tgt)
{
const reloc_deps_section *sec_deps;
const reloc_deps_e *sec_deps_e;
sec_deps = xtensa_get_section_deps (deps, src);
if (sec_deps == NULL)
return FALSE;
for (sec_deps_e = sec_deps->succs;
sec_deps_e != NULL;
sec_deps_e = sec_deps_e->next)
{
ASSERT (sec_deps_e->src == src);
if (sec_deps_e->tgt == tgt)
return TRUE;
}
return FALSE;
}
static bfd_boolean
deps_has_edge (const reloc_deps_graph *deps,
lang_statement_union_type *src,
lang_statement_union_type *tgt)
{
if (!section_is_source (deps, src))
return FALSE;
if (!section_is_target (deps, tgt))
return FALSE;
if (src->header.type != lang_input_section_enum)
return FALSE;
if (tgt->header.type != lang_input_section_enum)
return FALSE;
return deps_has_sec_edge (deps, src->input_section.section,
tgt->input_section.section);
}
static void
add_deps_edge (reloc_deps_graph *deps, asection *src_sec, asection *tgt_sec)
{
reloc_deps_section *src_sec_deps;
reloc_deps_section *tgt_sec_deps;
reloc_deps_e *src_edge;
reloc_deps_e *tgt_edge;
if (deps_has_sec_edge (deps, src_sec, tgt_sec))
return;
src_sec_deps = xtensa_get_section_deps (deps, src_sec);
if (src_sec_deps == NULL)
{
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)
{
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);
}
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 (target_sec != NULL)
add_deps_edge (deps, src_sec, target_sec);
}
static reloc_deps_graph *
ld_build_required_section_dependence (lang_statement_union_type *s)
{
reloc_deps_graph *deps;
xtensa_ld_iter_stack *stack = NULL;
deps = xmalloc (sizeof (reloc_deps_graph));
deps->sections = NULL;
deps->count = 0;
deps->size = 0;
for (iter_stack_create (&stack, s);
!iter_stack_empty (&stack);
iter_stack_next (&stack))
{
lang_statement_union_type *l = iter_stack_current (&stack);
if (l->header.type == lang_input_section_enum)
{
lang_input_section_type *input;
input = &l->input_section;
xtensa_callback_required_dependence (input->ifile->the_bfd,
input->section,
&link_info,
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
static void
xtensa_wild_group_interleave_callback (lang_statement_union_type *statement)
{
lang_wild_statement_type *w;
reloc_deps_graph *deps;
if (statement->header.type == lang_wild_statement_enum)
{
#if EXTRA_VALIDATION
size_t old_child_count;
size_t new_child_count;
#endif
bfd_boolean no_reorder;
w = &statement->wild_statement;
no_reorder = FALSE;
if (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;
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;
}
}
}
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
deps = ld_build_required_section_dependence (statement);
if (!no_reorder && deps->count != 0)
{
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
}
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)
{
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))
{
*s_p = l->header.next;
if (*s_p == NULL)
w->children.tail = s_p;
l->header.next = NULL;
*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
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
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;
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))
{
lit->header.next = *s_p;
*s_p = lit;
placed = TRUE;
break;
}
}
if (!placed)
{
*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)
{
lang_output_section_statement_type *os;
reloc_deps_graph *deps;
if (statement->header.type == lang_output_section_statement_enum)
{
#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
deps = ld_build_required_section_dependence (statement);
if (!no_reorder)
{
xtensa_colocate_literals (deps, statement);
#if EXTRA_VALIDATION
new_child_count = ld_count_children (statement);
ASSERT (new_child_count == old_child_count);
#endif
}
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);
}
free_reloc_deps_graph (deps);
}
}
static void
xtensa_colocate_output_literals (lang_statement_union_type *s)
{
lang_for_each_statement_worker (xtensa_colocate_output_literals_callback, s);
}
static void
xtensa_ldlang_clear_addresses (lang_statement_union_type *statement)
{
switch (statement->header.type)
{
case lang_input_section_enum:
{
asection *bfd_section = statement->input_section.section;
bfd_section->output_offset = 0;
}
break;
default:
break;
}
}
static bfd_vma
ld_assign_relative_paged_dot (bfd_vma dot,
lang_statement_union_type *s,
const reloc_deps_graph *deps ATTRIBUTE_UNUSED,
bfd_boolean lit_align)
{
xtensa_ld_iter_stack *stack = NULL;
xtensa_ld_iter_stack **stack_p = &stack;
bfd_boolean first_section = FALSE;
bfd_boolean in_literals = FALSE;
for (iter_stack_create (stack_p, s);
!iter_stack_empty (stack_p);
iter_stack_next (stack_p))
{
lang_statement_union_type *l = iter_stack_current (stack_p);
switch (l->header.type)
{
case lang_input_section_enum:
{
asection *section = l->input_section.section;
size_t align_pow = section->alignment_power;
bfd_boolean do_xtensa_alignment = FALSE;
if (lit_align)
{
bfd_boolean sec_is_target = section_is_target (deps, l);
bfd_boolean sec_is_source = section_is_source (deps, l);
if (section->size != 0
&& (first_section
|| (in_literals && !sec_is_target)
|| (!in_literals && sec_is_target)))
{
do_xtensa_alignment = TRUE;
}
first_section = FALSE;
if (section->size != 0)
in_literals = (sec_is_target && !sec_is_source);
}
if (do_xtensa_alignment && xtensa_page_power != 0)
dot += (1 << xtensa_page_power);
dot = align_power (dot, align_pow);
section->output_offset = dot;
dot += section->size;
}
break;
case lang_fill_statement_enum:
dot += l->fill_statement.size;
break;
case lang_padding_statement_enum:
dot += l->padding_statement.size;
break;
default:
break;
}
}
return dot;
}
static bfd_boolean
ld_local_file_relocations_fit (lang_statement_union_type *statement,
const reloc_deps_graph *deps ATTRIBUTE_UNUSED)
{
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;
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;
}
}
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)
{
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)
{
fprintf (stderr, "Warning: "
"l32r target section before l32r\n");
return FALSE;
}
if (l32r_addr - target_addr > 256 * 1024 - align_penalty)
return FALSE;
}
}
}
}
return TRUE;
}
static bfd_vma
ld_xtensa_insert_page_offsets (bfd_vma dot,
lang_statement_union_type *s,
reloc_deps_graph *deps,
bfd_boolean lit_align)
{
xtensa_ld_iter_stack *stack = NULL;
xtensa_ld_iter_stack **stack_p = &stack;
bfd_boolean first_section = FALSE;
bfd_boolean in_literals = FALSE;
if (!lit_align)
return FALSE;
for (iter_stack_create (stack_p, s);
!iter_stack_empty (stack_p);
iter_stack_next (stack_p))
{
lang_statement_union_type *l = iter_stack_current (stack_p);
switch (l->header.type)
{
case lang_input_section_enum:
{
asection *section = l->input_section.section;
bfd_boolean do_xtensa_alignment = FALSE;
if (lit_align)
{
if (section->size != 0
&& (first_section
|| (in_literals && !section_is_target (deps, l))
|| (!in_literals && section_is_target (deps, l))))
{
do_xtensa_alignment = TRUE;
}
first_section = FALSE;
if (section->size != 0)
{
in_literals = (section_is_target (deps, l)
&& !section_is_source (deps, l));
}
}
if (do_xtensa_alignment && xtensa_page_power != 0)
{
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;
tmplist.head = NULL;
tmplist.tail = &tmplist.head;
stat_ptr = &tmplist;
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_OPT_SIZEOPT (300)
#define OPTION_NO_RELAX (OPTION_OPT_SIZEOPT + 1)
#define OPTION_LITERAL_MOVEMENT (OPTION_NO_RELAX + 1)
#define OPTION_NO_LITERAL_MOVEMENT (OPTION_LITERAL_MOVEMENT + 1)
extern int elf32xtensa_size_opt;
extern int elf32xtensa_no_literal_movement;
'
PARSE_AND_LIST_LONGOPTS='
{ "size-opt", no_argument, NULL, OPTION_OPT_SIZEOPT},
{ "no-relax", no_argument, NULL, OPTION_NO_RELAX},
{ "literal-movement", no_argument, NULL, OPTION_LITERAL_MOVEMENT},
{ "no-literal-movement", no_argument, NULL, OPTION_NO_LITERAL_MOVEMENT},
'
PARSE_AND_LIST_OPTIONS='
fprintf (file, _(" --size-opt\t\tWhen relaxing longcalls, prefer size optimization\n\t\t\t over branch target alignment\n"));
fprintf (file, _(" --no-relax\t\tDo not relax branches or coalesce literals\n"));
'
PARSE_AND_LIST_ARGS_CASES='
case OPTION_OPT_SIZEOPT:
elf32xtensa_size_opt = 1;
break;
case OPTION_NO_RELAX:
disable_relaxation = TRUE;
break;
case OPTION_LITERAL_MOVEMENT:
elf32xtensa_no_literal_movement = 0;
break;
case OPTION_NO_LITERAL_MOVEMENT:
elf32xtensa_no_literal_movement = 1;
break;
'
# Replace some of the standard ELF functions with our own versions.
#
LDEMUL_BEFORE_PARSE=elf_xtensa_before_parse
LDEMUL_AFTER_OPEN=elf_xtensa_after_open
LDEMUL_CHOOSE_TARGET=elf_xtensa_choose_target
LDEMUL_PLACE_ORPHAN=elf_xtensa_place_orphan
LDEMUL_BEFORE_ALLOCATION=elf_xtensa_before_allocation