/* Linker command language support.
Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
- 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
Free Software Foundation, Inc.
- This file is part of GLD, the Gnu Linker.
+ This file is part of the GNU Binutils.
- GLD is free software; you can redistribute it and/or modify
+ 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, or (at your option)
- any later version.
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
- GLD is distributed in the hope that it will be useful,
+ 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 GLD; see the file COPYING. If not, write to the Free
- Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
- 02110-1301, USA. */
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
#include "sysdep.h"
#include "bfd.h"
case lang_padding_statement_enum:
case lang_address_statement_enum:
case lang_fill_statement_enum:
+ case lang_insert_statement_enum:
break;
default:
FAIL ();
p->is_archive = TRUE;
p->filename = name;
p->real = TRUE;
- p->local_sym_name = concat ("-l", name, NULL);
+ p->local_sym_name = concat ("-l", name, (const char *) NULL);
p->just_syms_flag = FALSE;
p->search_dirs_flag = TRUE;
break;
if (look->bfd_section != NULL)
{
flags = look->bfd_section->flags;
- if (match_type && !match_type (output_bfd, look->bfd_section,
+ if (match_type && !match_type (link_info.output_bfd,
+ look->bfd_section,
sec->owner, sec))
continue;
}
if (look->bfd_section != NULL)
{
flags = look->bfd_section->flags;
- if (match_type && !match_type (output_bfd, look->bfd_section,
+ if (match_type && !match_type (link_info.output_bfd,
+ look->bfd_section,
sec->owner, sec))
continue;
}
if (look->bfd_section != NULL)
{
flags = look->bfd_section->flags;
- if (match_type && !match_type (output_bfd, look->bfd_section,
+ if (match_type && !match_type (link_info.output_bfd,
+ look->bfd_section,
sec->owner, sec))
continue;
}
if (look->bfd_section != NULL)
{
flags = look->bfd_section->flags;
- if (match_type && !match_type (output_bfd, look->bfd_section,
+ if (match_type && !match_type (link_info.output_bfd,
+ look->bfd_section,
sec->owner, sec))
continue;
}
if (look->bfd_section != NULL)
{
flags = look->bfd_section->flags;
- if (match_type && !match_type (output_bfd, look->bfd_section,
+ if (match_type && !match_type (link_info.output_bfd,
+ look->bfd_section,
sec->owner, sec))
continue;
}
if (look->bfd_section != NULL)
{
flags = look->bfd_section->flags;
- if (match_type && !match_type (output_bfd, look->bfd_section,
+ if (match_type && !match_type (link_info.output_bfd,
+ look->bfd_section,
sec->owner, sec))
continue;
}
return NULL;
}
+/* Look for a suitable place for a new output section statement. The
+ idea is to skip over anything that might be inside a SECTIONS {}
+ statement in a script, before we find another output section
+ statement. Assignments to "dot" before an output section statement
+ are assumed to belong to it. An exception to this rule is made for
+ the first assignment to dot, otherwise we might put an orphan
+ before . = . + SIZEOF_HEADERS or similar assignments that set the
+ initial address. */
+
+static lang_statement_union_type **
+insert_os_after (lang_output_section_statement_type *after)
+{
+ lang_statement_union_type **where;
+ lang_statement_union_type **assign = NULL;
+ bfd_boolean ignore_first;
+
+ ignore_first
+ = after == &lang_output_section_statement.head->output_section_statement;
+
+ for (where = &after->header.next;
+ *where != NULL;
+ where = &(*where)->header.next)
+ {
+ switch ((*where)->header.type)
+ {
+ case lang_assignment_statement_enum:
+ if (assign == NULL)
+ {
+ lang_assignment_statement_type *ass;
+
+ ass = &(*where)->assignment_statement;
+ if (ass->exp->type.node_class != etree_assert
+ && ass->exp->assign.dst[0] == '.'
+ && ass->exp->assign.dst[1] == 0
+ && !ignore_first)
+ assign = where;
+ }
+ ignore_first = FALSE;
+ continue;
+ case lang_wild_statement_enum:
+ case lang_input_section_enum:
+ case lang_object_symbols_statement_enum:
+ case lang_fill_statement_enum:
+ case lang_data_statement_enum:
+ case lang_reloc_statement_enum:
+ case lang_padding_statement_enum:
+ case lang_constructors_statement_enum:
+ assign = NULL;
+ continue;
+ case lang_output_section_statement_enum:
+ if (assign != NULL)
+ where = assign;
+ break;
+ case lang_input_statement_enum:
+ case lang_address_statement_enum:
+ case lang_target_statement_enum:
+ case lang_output_statement_enum:
+ case lang_group_statement_enum:
+ case lang_insert_statement_enum:
+ continue;
+ }
+ break;
+ }
+
+ return where;
+}
+
lang_output_section_statement_type *
lang_insert_orphan (asection *s,
const char *secname,
etree_type *e_align;
symname = (char *) xmalloc (ps - secname + sizeof "__start_" + 1);
- symname[0] = bfd_get_symbol_leading_char (output_bfd);
+ symname[0] = bfd_get_symbol_leading_char (link_info.output_bfd);
sprintf (symname + (symname[0] != 0), "__start_%s", secname);
e_align = exp_unop (ALIGN_K,
exp_intop ((bfd_vma) 1 << s->alignment_power));
stat_ptr = &add;
symname = (char *) xmalloc (ps - secname + sizeof "__stop_" + 1);
- symname[0] = bfd_get_symbol_leading_char (output_bfd);
+ symname[0] = bfd_get_symbol_leading_char (link_info.output_bfd);
sprintf (symname + (symname[0] != 0), "__stop_%s", secname);
lang_add_assignment (exp_provide (symname,
exp_nameop (NAME, "."),
}
if (place->section == NULL)
- place->section = &output_bfd->sections;
+ place->section = &link_info.output_bfd->sections;
as = *place->section;
/* Put the section at the end of the list. */
/* Unlink the section. */
- bfd_section_list_remove (output_bfd, snew);
+ bfd_section_list_remove (link_info.output_bfd, snew);
/* Now tack it back on in the right place. */
- bfd_section_list_append (output_bfd, snew);
+ bfd_section_list_append (link_info.output_bfd, snew);
}
else if (as != snew && as->prev != snew)
{
/* Unlink the section. */
- bfd_section_list_remove (output_bfd, snew);
+ bfd_section_list_remove (link_info.output_bfd, snew);
/* Now tack it back on in the right place. */
- bfd_section_list_insert_before (output_bfd, as, snew);
+ bfd_section_list_insert_before (link_info.output_bfd, as, snew);
}
/* Save the end of this list. Further ophans of this type will
if (place->stmt == NULL)
{
- lang_statement_union_type **where;
- lang_statement_union_type **assign = NULL;
- bfd_boolean ignore_first;
-
- /* Look for a suitable place for the new statement list.
- The idea is to skip over anything that might be inside
- a SECTIONS {} statement in a script, before we find
- another output_section_statement. Assignments to "dot"
- before an output section statement are assumed to
- belong to it. An exception to this rule is made for
- the first assignment to dot, otherwise we might put an
- orphan before . = . + SIZEOF_HEADERS or similar
- assignments that set the initial address. */
-
- ignore_first = after == (&lang_output_section_statement.head
- ->output_section_statement);
- for (where = &after->header.next;
- *where != NULL;
- where = &(*where)->header.next)
- {
- switch ((*where)->header.type)
- {
- case lang_assignment_statement_enum:
- if (assign == NULL)
- {
- lang_assignment_statement_type *ass;
- ass = &(*where)->assignment_statement;
- if (ass->exp->type.node_class != etree_assert
- && ass->exp->assign.dst[0] == '.'
- && ass->exp->assign.dst[1] == 0
- && !ignore_first)
- assign = where;
- }
- ignore_first = FALSE;
- continue;
- case lang_wild_statement_enum:
- case lang_input_section_enum:
- case lang_object_symbols_statement_enum:
- case lang_fill_statement_enum:
- case lang_data_statement_enum:
- case lang_reloc_statement_enum:
- case lang_padding_statement_enum:
- case lang_constructors_statement_enum:
- assign = NULL;
- continue;
- case lang_output_section_statement_enum:
- if (assign != NULL)
- where = assign;
- case lang_input_statement_enum:
- case lang_address_statement_enum:
- case lang_target_statement_enum:
- case lang_output_statement_enum:
- case lang_group_statement_enum:
- case lang_afile_asection_pair_statement_enum:
- break;
- }
- break;
- }
+ lang_statement_union_type **where = insert_os_after (after);
*add.tail = *where;
*where = add.head;
for (s = file->the_bfd->sections; s != NULL; s = s->next)
if ((s->output_section == NULL
- || s->output_section->owner != output_bfd)
+ || s->output_section->owner != link_info.output_bfd)
&& (s->flags & (SEC_LINKER_CREATED | SEC_KEEP)) == 0)
{
if (! dis_header_printed)
bfd_map_over_sections (p, init_map_userdata, 0);
bfd_link_hash_traverse (link_info.hash, sort_def_symbol, 0);
}
+ lang_statement_iteration ++;
print_statements ();
}
if (strcmp (s->name, DISCARD_SECTION_NAME) == 0)
einfo (_("%P%F: Illegal use of `%s' section\n"), DISCARD_SECTION_NAME);
- s->bfd_section = bfd_get_section_by_name (output_bfd, s->name);
+ s->bfd_section = bfd_get_section_by_name (link_info.output_bfd, s->name);
if (s->bfd_section == NULL)
- s->bfd_section = bfd_make_section_with_flags (output_bfd, s->name,
- flags);
+ s->bfd_section = bfd_make_section_with_flags (link_info.output_bfd,
+ s->name, flags);
if (s->bfd_section == NULL)
{
einfo (_("%P%F: output format %s cannot represent section called %s\n"),
- output_bfd->xvec->name, s->name);
+ link_info.output_bfd->xvec->name, s->name);
}
s->bfd_section->output_section = s->bfd_section;
s->bfd_section->output_offset = 0;
if (isec)
bfd_init_private_section_data (isec->owner, isec,
- output_bfd, s->bfd_section,
+ link_info.output_bfd, s->bfd_section,
&link_info);
}
/* Open the output file. */
-static bfd *
+static void
open_output (const char *name)
{
- bfd *output;
-
output_target = lang_get_output_target ();
/* Has the user requested a particular endianness on the command
}
}
- output = bfd_openw (name, output_target);
+ link_info.output_bfd = bfd_openw (name, output_target);
- if (output == NULL)
+ if (link_info.output_bfd == NULL)
{
if (bfd_get_error () == bfd_error_invalid_target)
einfo (_("%P%F: target %s not found\n"), output_target);
delete_output_file_on_failure = TRUE;
- if (! bfd_set_format (output, bfd_object))
+ if (! bfd_set_format (link_info.output_bfd, bfd_object))
einfo (_("%P%F:%s: can not make object file: %E\n"), name);
- if (! bfd_set_arch_mach (output,
+ if (! bfd_set_arch_mach (link_info.output_bfd,
ldfile_output_architecture,
ldfile_output_machine))
einfo (_("%P%F:%s: can not set architecture: %E\n"), name);
- link_info.hash = bfd_link_hash_table_create (output);
+ link_info.hash = bfd_link_hash_table_create (link_info.output_bfd);
if (link_info.hash == NULL)
einfo (_("%P%F: can not create hash table: %E\n"));
- bfd_set_gp_size (output, g_switch_value);
- return output;
+ bfd_set_gp_size (link_info.output_bfd, g_switch_value);
}
static void
switch (statement->header.type)
{
case lang_output_statement_enum:
- ASSERT (output_bfd == NULL);
- output_bfd = open_output (statement->output_statement.name);
+ ASSERT (link_info.output_bfd == NULL);
+ open_output (statement->output_statement.name);
ldemul_set_output_arch ();
if (config.magic_demand_paged && !link_info.relocatable)
- output_bfd->flags |= D_PAGED;
+ link_info.output_bfd->flags |= D_PAGED;
else
- output_bfd->flags &= ~D_PAGED;
+ link_info.output_bfd->flags &= ~D_PAGED;
if (config.text_read_only)
- output_bfd->flags |= WP_TEXT;
+ link_info.output_bfd->flags |= WP_TEXT;
else
- output_bfd->flags &= ~WP_TEXT;
+ link_info.output_bfd->flags &= ~WP_TEXT;
if (link_info.traditional_format)
- output_bfd->flags |= BFD_TRADITIONAL_FORMAT;
+ link_info.output_bfd->flags |= BFD_TRADITIONAL_FORMAT;
else
- output_bfd->flags &= ~BFD_TRADITIONAL_FORMAT;
+ link_info.output_bfd->flags &= ~BFD_TRADITIONAL_FORMAT;
break;
case lang_target_statement_enum:
new->name = xstrdup (name);
- if (output_bfd != NULL)
+ if (link_info.output_bfd != NULL)
insert_undefined (new->name);
}
are initialized. */
exp_init_os (s->assignment_statement.exp);
break;
- case lang_afile_asection_pair_statement_enum:
- FAIL ();
- break;
case lang_address_statement_enum:
/* Mark the specified section with the supplied address.
aos->addr_tree = s->address_statement.address;
}
break;
+ case lang_insert_statement_enum:
+ break;
+ }
+ }
+}
+
+/* An insert statement snips out all the linker statements from the
+ start of the list and places them after the output section
+ statement specified by the insert. This operation is complicated
+ by the fact that we keep a doubly linked list of output section
+ statements as well as the singly linked list of all statements. */
+
+static void
+process_insert_statements (void)
+{
+ lang_statement_union_type **s;
+ lang_output_section_statement_type *first_os = NULL;
+ lang_output_section_statement_type *last_os = NULL;
+
+ /* "start of list" is actually the statement immediately after
+ the special abs_section output statement, so that it isn't
+ reordered. */
+ s = &lang_output_section_statement.head;
+ while (*(s = &(*s)->header.next) != NULL)
+ {
+ if ((*s)->header.type == lang_output_section_statement_enum)
+ {
+ /* Keep pointers to the first and last output section
+ statement in the sequence we may be about to move. */
+ last_os = &(*s)->output_section_statement;
+ if (first_os == NULL)
+ first_os = last_os;
+ }
+ else if ((*s)->header.type == lang_insert_statement_enum)
+ {
+ lang_insert_statement_type *i = &(*s)->insert_statement;
+ lang_output_section_statement_type *where;
+ lang_output_section_statement_type *os;
+ lang_statement_union_type **ptr;
+ lang_statement_union_type *first;
+
+ where = lang_output_section_find (i->where);
+ if (where != NULL && i->is_before)
+ {
+ do
+ where = where->prev;
+ while (where != NULL && where->constraint == -1);
+ }
+ if (where == NULL)
+ {
+ einfo (_("%X%P: %s not found for insert\n"), i->where);
+ continue;
+ }
+ /* You can't insert into the list you are moving. */
+ for (os = first_os; os != NULL; os = os->next)
+ if (os == where || os == last_os)
+ break;
+ if (os == where)
+ {
+ einfo (_("%X%P: %s not found for insert\n"), i->where);
+ continue;
+ }
+
+ /* Deal with reordering the output section statement list. */
+ if (last_os != NULL)
+ {
+ asection *first_sec, *last_sec;
+ struct lang_output_section_statement_struct **next;
+
+ /* Snip out the output sections we are moving. */
+ first_os->prev->next = last_os->next;
+ if (last_os->next == NULL)
+ {
+ next = &first_os->prev->next;
+ lang_output_section_statement.tail
+ = (lang_statement_union_type **) next;
+ }
+ else
+ last_os->next->prev = first_os->prev;
+ /* Add them in at the new position. */
+ last_os->next = where->next;
+ if (where->next == NULL)
+ {
+ next = &last_os->next;
+ lang_output_section_statement.tail
+ = (lang_statement_union_type **) next;
+ }
+ else
+ where->next->prev = last_os;
+ first_os->prev = where;
+ where->next = first_os;
+
+ /* Move the bfd sections in the same way. */
+ first_sec = NULL;
+ last_sec = NULL;
+ for (os = first_os; os != NULL; os = os->next)
+ {
+ if (os->bfd_section != NULL
+ && os->bfd_section->owner != NULL)
+ {
+ last_sec = os->bfd_section;
+ if (first_sec == NULL)
+ first_sec = last_sec;
+ }
+ if (os == last_os)
+ break;
+ }
+ if (last_sec != NULL)
+ {
+ asection *sec = where->bfd_section;
+ if (sec == NULL)
+ sec = output_prev_sec_find (where);
+
+ /* The place we want to insert must come after the
+ sections we are moving. So if we find no
+ section or if the section is the same as our
+ last section, then no move is needed. */
+ if (sec != NULL && sec != last_sec)
+ {
+ /* Trim them off. */
+ if (first_sec->prev != NULL)
+ first_sec->prev->next = last_sec->next;
+ else
+ link_info.output_bfd->sections = last_sec->next;
+ if (last_sec->next != NULL)
+ last_sec->next->prev = first_sec->prev;
+ else
+ link_info.output_bfd->section_last = first_sec->prev;
+ /* Add back. */
+ last_sec->next = sec->next;
+ if (sec->next != NULL)
+ sec->next->prev = last_sec;
+ else
+ link_info.output_bfd->section_last = last_sec;
+ first_sec->prev = sec;
+ sec->next = first_sec;
+ }
+ }
+
+ first_os = NULL;
+ last_os = NULL;
+ }
+
+ ptr = insert_os_after (where);
+ /* Snip everything after the abs_section output statement we
+ know is at the start of the list, up to and including
+ the insert statement we are currently processing. */
+ first = lang_output_section_statement.head->header.next;
+ lang_output_section_statement.head->header.next = (*s)->header.next;
+ /* Add them back where they belong. */
+ *s = *ptr;
+ if (*s == NULL)
+ statement_list.tail = s;
+ *ptr = first;
+ s = &lang_output_section_statement.head;
}
}
}
exclude = (output_section->rawsize == 0
&& (output_section->flags & SEC_KEEP) == 0
- && !bfd_section_removed_from_list (output_bfd,
+ && !bfd_section_removed_from_list (link_info.output_bfd,
output_section));
/* Some sections have not yet been sized, notably .gnu.version,
{
/* We don't set bfd_section to NULL since bfd_section of the
removed output section statement may still be used. */
- if (!os->section_relative_symbol)
+ if (!os->section_relative_symbol
+ && !os->update_dot_tree)
os->ignored = TRUE;
output_section->flags |= SEC_EXCLUDE;
- bfd_section_list_remove (output_bfd, output_section);
- output_bfd->section_count--;
+ bfd_section_list_remove (link_info.output_bfd, output_section);
+ link_info.output_bfd->section_count--;
}
}
value = h->u.def.value;
if (expld.result.section)
- value += expld.result.section->vma;
+ value += expld.result.section->vma;
minfo ("[0x%V]", value);
}
++len;
}
- if (i->output_section != NULL && i->output_section->owner == output_bfd)
+ if (i->output_section != NULL
+ && i->output_section->owner == link_info.output_bfd)
addr = i->output_section->vma + i->output_offset;
else
{
minfo (_("%W (size before relaxing)\n"), i->rawsize);
}
- if (i->output_section != NULL && i->output_section->owner == output_bfd)
+ if (i->output_section != NULL
+ && i->output_section->owner == link_info.output_bfd)
{
if (link_info.reduce_memory_overheads)
bfd_link_hash_traverse (link_info.hash, print_one_symbol, i);
case lang_group_statement_enum:
print_group (&s->group_statement, os);
break;
- case lang_afile_asection_pair_statement_enum:
- FAIL ();
+ case lang_insert_statement_enum:
+ minfo ("INSERT %s %s\n",
+ s->insert_statement.is_before ? "BEFORE" : "AFTER",
+ s->insert_statement.where);
break;
}
}
else if (bfd_section_lma (sec1->owner, sec1)
> bfd_section_lma (sec2->owner, sec2))
return 1;
+ else if (sec1->id < sec2->id)
+ return -1;
+ else if (sec1->id > sec2->id)
+ return 1;
return 0;
}
/* Check to see if any allocated sections overlap with other allocated
sections. This can happen if a linker script specifies the output
- section addresses of the two sections. */
+ section addresses of the two sections. Also check whether any memory
+ region has overflowed. */
static void
lang_check_section_addresses (void)
bfd_vma os_start;
bfd_vma os_end;
bfd_size_type amt;
+ lang_memory_region_type *m;
- if (bfd_count_sections (output_bfd) <= 1)
+ if (bfd_count_sections (link_info.output_bfd) <= 1)
return;
- amt = bfd_count_sections (output_bfd) * sizeof (asection *);
+ amt = bfd_count_sections (link_info.output_bfd) * sizeof (asection *);
sections = xmalloc (amt);
/* Scan all sections in the output list. */
count = 0;
- for (s = output_bfd->sections; s != NULL; s = s->next)
+ for (s = link_info.output_bfd->sections; s != NULL; s = s->next)
{
/* Only consider loadable sections with real contents. */
if (IGNORE_SECTION (s) || s->size == 0)
spp = sections;
s = *spp++;
- s_start = bfd_section_lma (output_bfd, s);
+ s_start = bfd_section_lma (link_info.output_bfd, s);
s_end = s_start + TO_ADDR (s->size) - 1;
for (count--; count; count--)
{
os_start = s_start;
os_end = s_end;
s = *spp++;
- s_start = bfd_section_lma (output_bfd, s);
+ s_start = bfd_section_lma (link_info.output_bfd, s);
s_end = s_start + TO_ADDR (s->size) - 1;
/* Look for an overlap. */
}
free (sections);
+
+ /* If any memory region has overflowed, report by how much.
+ We do not issue this diagnostic for regions that had sections
+ explicitly placed outside their bounds; os_region_check's
+ diagnostics are adequate for that case.
+
+ FIXME: It is conceivable that m->current - (m->origin + m->length)
+ might overflow a 32-bit integer. There is, alas, no way to print
+ a bfd_vma quantity in decimal. */
+ for (m = lang_memory_region_list; m; m = m->next)
+ if (m->had_full_message)
+ einfo (_("%X%P: region %s overflowed by %ld bytes\n"),
+ m->name, (long)(m->current - (m->origin + m->length)));
+
}
/* Make sure the new address is within the region. We explicitly permit the
os->bfd_section->name,
region->name);
}
- else
+ else if (!region->had_full_message)
{
- einfo (_("%X%P: region %s is full (%B section %s)\n"),
- region->name,
+ region->had_full_message = TRUE;
+
+ einfo (_("%X%P: %B section %s will not fit in region %s\n"),
os->bfd_section->owner,
- os->bfd_section->name);
+ os->bfd_section->name,
+ region->name);
}
- /* Reset the region pointer. */
- region->current = region->origin;
}
}
os->processed_vma = FALSE;
exp_fold_tree (os->addr_tree, bfd_abs_section_ptr, &dot);
- if (!expld.result.valid_p
- && expld.phase != lang_mark_phase_enum)
+ if (expld.result.valid_p)
+ dot = expld.result.value + expld.result.section->vma;
+ else if (expld.phase != lang_mark_phase_enum)
einfo (_("%F%S: non constant or forward reference"
" address expression for section %s\n"),
os->name);
-
- dot = expld.result.value + expld.result.section->vma;
}
if (os->bfd_section == NULL)
address from the input section. FIXME: This is COFF
specific; it would be cleaner if there were some other way
to do this, but nothing simple comes to mind. */
- if ((bfd_get_flavour (output_bfd) == bfd_target_ecoff_flavour
- || bfd_get_flavour (output_bfd) == bfd_target_coff_flavour)
+ if (((bfd_get_flavour (link_info.output_bfd)
+ == bfd_target_ecoff_flavour)
+ || (bfd_get_flavour (link_info.output_bfd)
+ == bfd_target_coff_flavour))
&& (os->bfd_section->flags & SEC_COFF_SHARED_LIBRARY) != 0)
{
asection *input;
if (command_line.check_section_addresses)
einfo (_("%P%F: error: no memory region specified"
" for loadable section `%s'\n"),
- bfd_get_section_name (output_bfd,
+ bfd_get_section_name (link_info.output_bfd,
os->bfd_section));
else
einfo (_("%P: warning: no memory region specified"
" for loadable section `%s'\n"),
- bfd_get_section_name (output_bfd,
+ bfd_get_section_name (link_info.output_bfd,
os->bfd_section));
}
bfd_vma newdot = dot;
etree_type *tree = s->assignment_statement.exp;
+ expld.dataseg.relro = exp_dataseg_relro_none;
+
exp_fold_tree (tree,
output_section_statement->bfd_section,
&newdot);
+ if (expld.dataseg.relro == exp_dataseg_relro_start)
+ {
+ if (!expld.dataseg.relro_start_stat)
+ expld.dataseg.relro_start_stat = s;
+ else
+ {
+ ASSERT (expld.dataseg.relro_start_stat == s);
+ }
+ }
+ else if (expld.dataseg.relro == exp_dataseg_relro_end)
+ {
+ if (!expld.dataseg.relro_end_stat)
+ expld.dataseg.relro_end_stat = s;
+ else
+ {
+ ASSERT (expld.dataseg.relro_end_stat == s);
+ }
+ }
+ expld.dataseg.relro = exp_dataseg_relro_none;
+
/* This symbol is relative to this section. */
if ((tree->type.node_class == etree_provided
|| tree->type.node_class == etree_assign)
should have space allocated to it, unless the
user has explicitly stated that the section
should never be loaded. */
- if (!(output_section_statement->flags
- & (SEC_NEVER_LOAD | SEC_ALLOC)))
+ if (!(output_section_statement->flags & SEC_NEVER_LOAD))
output_section_statement->bfd_section->flags |= SEC_ALLOC;
}
dot = newdot;
fill, dot, relax, check_regions);
break;
- default:
- FAIL ();
+ case lang_insert_statement_enum:
break;
/* We can only get here when relaxing is turned on. */
case lang_address_statement_enum:
break;
+
+ default:
+ FAIL ();
+ break;
}
prev = &s->header.next;
}
/* Find maximum alignment power of sections between
DATA_SEGMENT_ALIGN and DATA_SEGMENT_RELRO_END. */
- for (sec = output_bfd->sections; sec; sec = sec->next)
+ for (sec = link_info.output_bfd->sections; sec; sec = sec->next)
if (sec->vma >= expld.dataseg.base
&& sec->vma < expld.dataseg.relro_end
&& sec->alignment_power > max_alignment_power)
current_os, fill, dot);
break;
- default:
- FAIL ();
+ case lang_insert_statement_enum:
break;
case lang_address_statement_enum:
break;
+
+ default:
+ FAIL ();
+ break;
}
}
return dot;
if (link_info.relocatable)
return;
- for (s = output_bfd->sections; s != NULL; s = s->next)
+ for (s = link_info.output_bfd->sections; s != NULL; s = s->next)
{
const char *secname;
char *buf;
struct bfd_link_hash_entry *h;
- secname = bfd_get_section_name (output_bfd, s);
+ secname = bfd_get_section_name (link_info.output_bfd, s);
buf = xmalloc (10 + strlen (secname));
sprintf (buf, ".startof.%s", secname);
if (h != NULL && h->type == bfd_link_hash_undefined)
{
h->type = bfd_link_hash_defined;
- h->u.def.value = bfd_get_section_vma (output_bfd, s);
+ h->u.def.value = bfd_get_section_vma (link_info.output_bfd, s);
h->u.def.section = bfd_abs_section_ptr;
}
struct bfd_link_hash_entry *h;
bfd_boolean warn;
- if (link_info.relocatable || link_info.shared)
- warn = FALSE;
+ if ((link_info.relocatable && !link_info.gc_sections)
+ || link_info.shared)
+ warn = entry_from_cmdline;
else
warn = TRUE;
+ /* Force the user to specify a root when generating a relocatable with
+ --gc-sections. */
+ if (link_info.gc_sections && link_info.relocatable
+ && (entry_symbol.name == NULL
+ && ldlang_undef_chain_list_head == NULL))
+ einfo (_("%P%F: gc-sections requires either an entry or "
+ "an undefined symbol\n"));
+
if (entry_symbol.name == NULL)
{
/* No entry has been specified. Look for the default entry, but
bfd_vma val;
val = (h->u.def.value
- + bfd_get_section_vma (output_bfd,
+ + bfd_get_section_vma (link_info.output_bfd,
h->u.def.section->output_section)
+ h->u.def.section->output_offset);
- if (! bfd_set_start_address (output_bfd, val))
+ if (! bfd_set_start_address (link_info.output_bfd, val))
einfo (_("%P%F:%s: can't set start address\n"), entry_symbol.name);
}
else
val = bfd_scan_vma (entry_symbol.name, &send, 0);
if (*send == '\0')
{
- if (! bfd_set_start_address (output_bfd, val))
+ if (! bfd_set_start_address (link_info.output_bfd, val))
einfo (_("%P%F: can't set start address\n"));
}
else
/* Can't find the entry symbol, and it's not a number. Use
the first address in the text section. */
- ts = bfd_get_section_by_name (output_bfd, entry_section);
+ ts = bfd_get_section_by_name (link_info.output_bfd, entry_section);
if (ts != NULL)
{
if (warn)
einfo (_("%P: warning: cannot find entry symbol %s;"
" defaulting to %V\n"),
entry_symbol.name,
- bfd_get_section_vma (output_bfd, ts));
- if (! bfd_set_start_address (output_bfd,
- bfd_get_section_vma (output_bfd,
- ts)))
+ bfd_get_section_vma (link_info.output_bfd, ts));
+ if (!(bfd_set_start_address
+ (link_info.output_bfd,
+ bfd_get_section_vma (link_info.output_bfd, ts))))
einfo (_("%P%F: can't set start address\n"));
}
else
{
input_bfd = file->input_statement.the_bfd;
compatible
- = bfd_arch_get_compatible (input_bfd, output_bfd,
+ = bfd_arch_get_compatible (input_bfd, link_info.output_bfd,
command_line.accept_unknown_input_arch);
/* In general it is not possible to perform a relocatable
relocs for other link purposes than a final link). */
if ((link_info.relocatable || link_info.emitrelocations)
&& (compatible == NULL
- || bfd_get_flavour (input_bfd) != bfd_get_flavour (output_bfd))
+ || (bfd_get_flavour (input_bfd)
+ != bfd_get_flavour (link_info.output_bfd)))
&& (bfd_get_file_flags (input_bfd) & HAS_RELOC) != 0)
{
einfo (_("%P%F: Relocatable linking with relocations from"
" format %s (%B) to format %s (%B) is not supported\n"),
bfd_get_target (input_bfd), input_bfd,
- bfd_get_target (output_bfd), output_bfd);
+ bfd_get_target (link_info.output_bfd), link_info.output_bfd);
/* einfo with %F exits. */
}
if (compatible == NULL)
{
if (command_line.warn_mismatch)
- einfo (_("%P: warning: %s architecture of input file `%B'"
+ einfo (_("%P%X: %s architecture of input file `%B'"
" is incompatible with %s output\n"),
bfd_printable_name (input_bfd), input_bfd,
- bfd_printable_name (output_bfd));
+ bfd_printable_name (link_info.output_bfd));
}
else if (bfd_count_sections (input_bfd))
{
information which is needed in the output file. */
if (! command_line.warn_mismatch)
pfn = bfd_set_error_handler (ignore_bfd_errors);
- if (! bfd_merge_private_bfd_data (input_bfd, output_bfd))
+ if (! bfd_merge_private_bfd_data (input_bfd, link_info.output_bfd))
{
if (command_line.warn_mismatch)
einfo (_("%P%X: failed to merge target specific data"
header_printed = TRUE;
}
- name = bfd_demangle (output_bfd, h->root.string,
+ name = bfd_demangle (link_info.output_bfd, h->root.string,
DMGL_ANSI | DMGL_PARAMS);
- minfo ("%s", name);
- len = strlen (name);
- free (name);
+ if (name == NULL)
+ {
+ minfo ("%s", h->root.string);
+ len = strlen (h->root.string);
+ }
+ else
+ {
+ minfo ("%s", name);
+ len = strlen (name);
+ free (name);
+ }
if (len >= 19)
{
void
ldlang_add_file (lang_input_statement_type *entry)
{
- bfd **pp;
-
lang_statement_append (&file_chain,
(lang_statement_union_type *) entry,
&entry->next);
/* The BFD linker needs to have a list of all input BFDs involved in
a link. */
ASSERT (entry->the_bfd->link_next == NULL);
- ASSERT (entry->the_bfd != output_bfd);
- for (pp = &link_info.input_bfds; *pp != NULL; pp = &(*pp)->link_next)
- ;
- *pp = entry->the_bfd;
+ ASSERT (entry->the_bfd != link_info.output_bfd);
+
+ *link_info.input_bfds_tail = entry->the_bfd;
+ link_info.input_bfds_tail = &entry->the_bfd->link_next;
entry->the_bfd->usrdata = entry;
bfd_set_gp_size (entry->the_bfd, g_switch_value);
os->processed_lma = FALSE;
}
- for (o = output_bfd->sections; o != NULL; o = o->next)
+ for (o = link_info.output_bfd->sections; o != NULL; o = o->next)
{
/* Save the last size for possible use by bfd_relax_section. */
o->rawsize = o->size;
static void
lang_gc_sections (void)
{
- struct bfd_link_hash_entry *h;
- ldlang_undef_chain_list_type *ulist;
-
/* Keep all sections so marked in the link script. */
lang_gc_sections_1 (statement_list.head);
- /* Keep all sections containing symbols undefined on the command-line,
- and the section containing the entry symbol. */
-
- for (ulist = link_info.gc_sym_list; ulist; ulist = ulist->next)
- {
- h = bfd_link_hash_lookup (link_info.hash, ulist->name,
- FALSE, FALSE, FALSE);
-
- if (h != NULL
- && (h->type == bfd_link_hash_defined
- || h->type == bfd_link_hash_defweak)
- && ! bfd_is_abs_section (h->u.def.section))
- {
- h->u.def.section->flags |= SEC_KEEP;
- }
- }
-
/* SEC_EXCLUDE is ignored when doing a relocatable link, except in
the special case of debug info. (See bfd/stabs.c)
Twiddle the flag here, to simplify later linker code. */
}
if (link_info.gc_sections)
- bfd_gc_sections (output_bfd, &link_info);
+ bfd_gc_sections (link_info.output_bfd, &link_info);
+}
+
+/* Worker for lang_find_relro_sections_1. */
+
+static void
+find_relro_section_callback (lang_wild_statement_type *ptr ATTRIBUTE_UNUSED,
+ struct wildcard_list *sec ATTRIBUTE_UNUSED,
+ asection *section,
+ lang_input_statement_type *file ATTRIBUTE_UNUSED,
+ void *data)
+{
+ /* Discarded, excluded and ignored sections effectively have zero
+ size. */
+ if (section->output_section != NULL
+ && section->output_section->owner == link_info.output_bfd
+ && (section->output_section->flags & SEC_EXCLUDE) == 0
+ && !IGNORE_SECTION (section)
+ && section->size != 0)
+ {
+ bfd_boolean *has_relro_section = (bfd_boolean *) data;
+ *has_relro_section = TRUE;
+ }
+}
+
+/* Iterate over sections for relro sections. */
+
+static void
+lang_find_relro_sections_1 (lang_statement_union_type *s,
+ bfd_boolean *has_relro_section)
+{
+ if (*has_relro_section)
+ return;
+
+ for (; s != NULL; s = s->header.next)
+ {
+ if (s == expld.dataseg.relro_end_stat)
+ break;
+
+ switch (s->header.type)
+ {
+ case lang_wild_statement_enum:
+ walk_wild (&s->wild_statement,
+ find_relro_section_callback,
+ has_relro_section);
+ break;
+ case lang_constructors_statement_enum:
+ lang_find_relro_sections_1 (constructor_list.head,
+ has_relro_section);
+ break;
+ case lang_output_section_statement_enum:
+ lang_find_relro_sections_1 (s->output_section_statement.children.head,
+ has_relro_section);
+ break;
+ case lang_group_statement_enum:
+ lang_find_relro_sections_1 (s->group_statement.children.head,
+ has_relro_section);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void
+lang_find_relro_sections (void)
+{
+ bfd_boolean has_relro_section = FALSE;
+
+ /* Check all sections in the link script. */
+
+ lang_find_relro_sections_1 (expld.dataseg.relro_start_stat,
+ &has_relro_section);
+
+ if (!has_relro_section)
+ link_info.relro = FALSE;
}
/* Relax all sections until bfd_relax_section gives up. */
to the correct output sections. */
map_input_to_output_sections (statement_list.head, NULL, NULL);
+ process_insert_statements ();
+
/* Find any sections not attached explicitly and handle them. */
lang_place_orphans ();
sections, so that GCed sections are not merged, but before
assigning dynamic symbols, since removing whole input sections
is hard then. */
- bfd_merge_sections (output_bfd, &link_info);
+ bfd_merge_sections (link_info.output_bfd, &link_info);
/* Look for a text section and set the readonly attribute in it. */
- found = bfd_get_section_by_name (output_bfd, ".text");
+ found = bfd_get_section_by_name (link_info.output_bfd, ".text");
if (found != NULL)
{
section positions, since they will affect SIZEOF_HEADERS. */
lang_record_phdrs ();
+ /* Check relro sections. */
+ if (link_info.relro && ! link_info.relocatable)
+ lang_find_relro_sections ();
+
/* Size up the sections. */
lang_size_sections (NULL, !command_line.relax);
h->type = bfd_link_hash_defined;
- sec = bfd_get_section_by_name (output_bfd, secname);
+ sec = bfd_get_section_by_name (link_info.output_bfd, secname);
if (sec == NULL)
h->u.def.value = 0;
else
- h->u.def.value = bfd_get_section_vma (output_bfd, sec);
+ h->u.def.value = bfd_get_section_vma (link_info.output_bfd, sec);
h->u.def.section = bfd_abs_section_ptr;
}
h->type = bfd_link_hash_defined;
- sec = bfd_get_section_by_name (output_bfd, secname);
+ sec = bfd_get_section_by_name (link_info.output_bfd, secname);
if (sec == NULL)
h->u.def.value = 0;
else
- h->u.def.value = (bfd_get_section_vma (output_bfd, sec)
+ h->u.def.value = (bfd_get_section_vma (link_info.output_bfd, sec)
+ TO_ADDR (sec->size));
h->u.def.section = bfd_abs_section_ptr;
}
}
+void
+lang_add_insert (const char *where, int is_before)
+{
+ lang_insert_statement_type *new;
+
+ new = new_stat (lang_insert_statement, stat_ptr);
+ new->where = where;
+ new->is_before = is_before;
+ saved_script_handle = previous_script_handle;
+}
+
/* Enter a group. This creates a new lang_group_statement, and sets
stat_ptr to build new statements within the group. */
|| (os->bfd_section->flags & SEC_ALLOC) == 0)
continue;
- if (last)
- pl = last;
- else
+ if (last == NULL)
{
lang_output_section_statement_type * tmp_os;
http://sourceware.org/ml/binutils/2007-02/msg00291.html */
for (tmp_os = os; tmp_os; tmp_os = tmp_os->next)
if (tmp_os->phdrs)
- break;
- pl = tmp_os->phdrs;
+ {
+ last = tmp_os->phdrs;
+ break;
+ }
+ if (last == NULL)
+ einfo (_("%F%P: no sections assigned to phdrs\n"));
}
+ pl = last;
}
if (os->bfd_section == NULL)
else
at = exp_get_vma (l->at, 0, "phdr load address");
- if (! bfd_record_phdr (output_bfd, l->type,
+ if (! bfd_record_phdr (link_info.output_bfd, l->type,
l->flags != NULL, flags, l->at != NULL,
at, l->filehdr, l->phdrs, c, secs))
einfo (_("%F%P: bfd_record_phdr failed: %E\n"));