/* Linker command language support.
- Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
- 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
- Free Software Foundation, Inc.
+ Copyright (C) 1991-2016 Free Software Foundation, Inc.
This file is part of the GNU Binutils.
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
MA 02110-1301, USA. */
-#include <limits.h>
-
#include "sysdep.h"
#include "bfd.h"
#include "libiberty.h"
+#include "filenames.h"
#include "safe-ctype.h"
#include "obstack.h"
#include "bfdlink.h"
#include "fnmatch.h"
#include "demangle.h"
#include "hashtab.h"
+#include "libbfd.h"
+#include "elf-bfd.h"
+#ifdef ENABLE_PLUGINS
+#include "plugin.h"
+#endif /* ENABLE_PLUGINS */
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) & (((TYPE*) 0)->MEMBER))
#endif
-/* Locals variables. */
+/* Convert between addresses in bytes and sizes in octets.
+ For currently supported targets, octets_per_byte is always a power
+ of two, so we can use shifts. */
+#define TO_ADDR(X) ((X) >> opb_shift)
+#define TO_SIZE(X) ((X) << opb_shift)
+
+/* Local variables. */
static struct obstack stat_obstack;
static struct obstack map_obstack;
#define obstack_chunk_alloc xmalloc
#define obstack_chunk_free free
-static const char *startup_file;
+static const char *entry_symbol_default = "start";
static bfd_boolean placed_commons = FALSE;
-static bfd_boolean stripped_excluded_sections = FALSE;
+static bfd_boolean map_head_is_link_order = FALSE;
static lang_output_section_statement_type *default_common_section;
static bfd_boolean map_option_f;
static bfd_vma print_dot;
static lang_input_statement_type *first_file;
static const char *current_target;
-static const char *output_target;
static lang_statement_list_type statement_list;
-static struct bfd_hash_table lang_definedness_table;
+static lang_statement_list_type *stat_save[10];
+static lang_statement_list_type **stat_save_ptr = &stat_save[0];
+static struct unique_sections *unique_section_list;
+static struct asneeded_minfo *asneeded_list_head;
+static unsigned int opb_shift = 0;
/* Forward declarations. */
static void exp_init_os (etree_type *);
-static void init_map_userdata (bfd *, asection *, void *);
static lang_input_statement_type *lookup_name (const char *);
-static struct bfd_hash_entry *lang_definedness_newfunc
- (struct bfd_hash_entry *, struct bfd_hash_table *, const char *);
static void insert_undefined (const char *);
static bfd_boolean sort_def_symbol (struct bfd_link_hash_entry *, void *);
static void print_statement (lang_statement_union_type *,
static void print_statement_list (lang_statement_union_type *,
lang_output_section_statement_type *);
static void print_statements (void);
-static void print_input_section (asection *);
+static void print_input_section (asection *, bfd_boolean);
static bfd_boolean lang_one_common (struct bfd_link_hash_entry *, void *);
static void lang_record_phdrs (void);
static void lang_do_version_exports_section (void);
static void lang_finalize_version_expr_head
(struct bfd_elf_version_expr_head *);
+static void lang_do_memory_regions (void);
/* Exported variables. */
+const char *output_target;
lang_output_section_statement_type *abs_output_section;
lang_statement_list_type lang_output_section_statement;
lang_statement_list_type *stat_ptr = &statement_list;
lang_statement_list_type file_chain = { NULL, NULL };
lang_statement_list_type input_file_chain;
struct bfd_sym_chain entry_symbol = { NULL, NULL };
-static const char *entry_symbol_default = "start";
const char *entry_section = ".text";
+struct lang_input_statement_flags input_flags;
bfd_boolean entry_from_cmdline;
+bfd_boolean undef_from_cmdline;
bfd_boolean lang_has_input_file = FALSE;
bfd_boolean had_output_filename = FALSE;
bfd_boolean lang_float_flag = FALSE;
bfd_boolean delete_output_file_on_failure = FALSE;
struct lang_phdr *lang_phdr_list;
struct lang_nocrossrefs *nocrossref_list;
-static struct unique_sections *unique_section_list;
-static bfd_boolean ldlang_sysrooted_script = FALSE;
+struct asneeded_minfo **asneeded_list_tail;
/* Functions that traverse the linker script and might evaluate
- DEFINED() need to increment this. */
+ DEFINED() need to increment this at the start of the traversal. */
int lang_statement_iteration = 0;
-etree_type *base; /* Relocation base - or null */
-
/* Return TRUE if the PATTERN argument is a wildcard pattern.
Although backslashes are treated specially if a pattern contains
wildcards, we do not consider the mere presence of a backslash to
return match;
}
-bfd_boolean
-unique_section_p (const asection *sec)
+static bfd_boolean
+unique_section_p (const asection *sec,
+ const lang_output_section_statement_type *os)
{
struct unique_sections *unam;
const char *secnam;
- if (link_info.relocatable
+ if (bfd_link_relocatable (&link_info)
&& sec->owner != NULL
&& bfd_is_group_section (sec->owner, sec))
- return TRUE;
+ return !(os != NULL
+ && strcmp (os->name, DISCARD_SECTION_NAME) == 0);
secnam = sec->name;
for (unam = unique_section_list; unam; unam = unam->next)
return;
}
- (*callback) (ptr, sec, s, file, data);
+ (*callback) (ptr, sec, s, ptr->section_flag_list, file, data);
}
/* Lowest common denominator routine that can handle everything correctly,
{
sec = ptr->section_list;
if (sec == NULL)
- (*callback) (ptr, sec, s, file, data);
+ (*callback) (ptr, sec, s, ptr->section_flag_list, file, data);
while (sec != NULL)
{
} section_iterator_callback_data;
static bfd_boolean
-section_iterator_callback (bfd *bfd ATTRIBUTE_UNUSED, asection *s, void *data)
+section_iterator_callback (bfd *abfd ATTRIBUTE_UNUSED, asection *s, void *data)
{
- section_iterator_callback_data *d = data;
+ section_iterator_callback_data *d = (section_iterator_callback_data *) data;
if (d->found_section != NULL)
{
return TRUE;
}
+/* Return the numerical value of the init_priority attribute from
+ section name NAME. */
+
+static unsigned long
+get_init_priority (const char *name)
+{
+ char *end;
+ unsigned long init_priority;
+
+ /* GCC uses the following section names for the init_priority
+ attribute with numerical values 101 and 65535 inclusive. A
+ lower value means a higher priority.
+
+ 1: .init_array.NNNN/.fini_array.NNNN: Where NNNN is the
+ decimal numerical value of the init_priority attribute.
+ The order of execution in .init_array is forward and
+ .fini_array is backward.
+ 2: .ctors.NNNN/.dtors.NNNN: Where NNNN is 65535 minus the
+ decimal numerical value of the init_priority attribute.
+ The order of execution in .ctors is backward and .dtors
+ is forward.
+ */
+ if (strncmp (name, ".init_array.", 12) == 0
+ || strncmp (name, ".fini_array.", 12) == 0)
+ {
+ init_priority = strtoul (name + 12, &end, 10);
+ return *end ? 0 : init_priority;
+ }
+ else if (strncmp (name, ".ctors.", 7) == 0
+ || strncmp (name, ".dtors.", 7) == 0)
+ {
+ init_priority = strtoul (name + 7, &end, 10);
+ return *end ? 0 : 65535 - init_priority;
+ }
+
+ return 0;
+}
+
/* Compare sections ASEC and BSEC according to SORT. */
static int
compare_section (sort_type sort, asection *asec, asection *bsec)
{
int ret;
+ unsigned long ainit_priority, binit_priority;
switch (sort)
{
default:
abort ();
+ case by_init_priority:
+ ainit_priority
+ = get_init_priority (bfd_get_section_name (asec->owner, asec));
+ binit_priority
+ = get_init_priority (bfd_get_section_name (bsec->owner, bsec));
+ if (ainit_priority == 0 || binit_priority == 0)
+ goto sort_by_name;
+ ret = ainit_priority - binit_priority;
+ if (ret)
+ break;
+ else
+ goto sort_by_name;
+
case by_alignment_name:
ret = (bfd_section_alignment (bsec->owner, bsec)
- bfd_section_alignment (asec->owner, asec));
/* Fall through. */
case by_name:
+sort_by_name:
ret = strcmp (bfd_get_section_name (asec->owner, asec),
bfd_get_section_name (bsec->owner, bsec));
break;
output_section_callback_fast (lang_wild_statement_type *ptr,
struct wildcard_list *sec,
asection *section,
+ struct flag_info *sflag_list ATTRIBUTE_UNUSED,
lang_input_statement_type *file,
- void *output ATTRIBUTE_UNUSED)
+ void *output)
{
lang_section_bst_type *node;
lang_section_bst_type **tree;
+ lang_output_section_statement_type *os;
+
+ os = (lang_output_section_statement_type *) output;
- if (unique_section_p (section))
+ if (unique_section_p (section, os))
return;
- node = xmalloc (sizeof (lang_section_bst_type));
+ node = (lang_section_bst_type *) xmalloc (sizeof (lang_section_bst_type));
node->left = 0;
node->right = 0;
node->section = section;
if (tree->left)
output_section_callback_tree_to_list (ptr, tree->left, output);
- lang_add_section (&ptr->children, tree->section,
+ lang_add_section (&ptr->children, tree->section, NULL,
(lang_output_section_statement_type *) output);
if (tree->right)
callback_t callback,
void *data)
{
- if (file->just_syms_flag)
+ if (file->flags.just_syms)
return;
(*ptr->walk_wild_section_handler) (ptr, file, callback, data);
void *data)
{
if (f->the_bfd == NULL
- || ! bfd_check_format (f->the_bfd, bfd_archive))
+ || !bfd_check_format (f->the_bfd, bfd_archive))
walk_wild_section (s, f, callback, data);
else
{
lang_input_statement. */
if (member->usrdata != NULL)
{
- walk_wild_section (s, member->usrdata, callback, data);
+ walk_wild_section (s,
+ (lang_input_statement_type *) member->usrdata,
+ callback, data);
}
member = bfd_openr_next_archived_file (f->the_bfd, member);
}
/* lang_for_each_statement walks the parse tree and calls the provided
- function for each node. */
+ function for each node, except those inside output section statements
+ with constraint set to -1. */
-static void
+void
lang_for_each_statement_worker (void (*func) (lang_statement_union_type *),
lang_statement_union_type *s)
{
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);
+ if (s->output_section_statement.constraint != -1)
+ lang_for_each_statement_worker
+ (func, s->output_section_statement.children.head);
break;
case lang_wild_statement_enum:
lang_for_each_statement_worker (func,
list->tail = &list->head;
}
+void
+push_stat_ptr (lang_statement_list_type *new_ptr)
+{
+ if (stat_save_ptr >= stat_save + sizeof (stat_save) / sizeof (stat_save[0]))
+ abort ();
+ *stat_save_ptr++ = stat_ptr;
+ stat_ptr = new_ptr;
+}
+
+void
+pop_stat_ptr (void)
+{
+ if (stat_save_ptr <= stat_save)
+ abort ();
+ stat_ptr = *--stat_save_ptr;
+}
+
/* Build a new statement node for the parse tree. */
static lang_statement_union_type *
size_t size,
lang_statement_list_type *list)
{
- lang_statement_union_type *new;
+ lang_statement_union_type *new_stmt;
- new = stat_alloc (size);
- new->header.type = type;
- new->header.next = NULL;
- lang_statement_append (list, new, &new->header.next);
- return new;
+ new_stmt = (lang_statement_union_type *) stat_alloc (size);
+ new_stmt->header.type = type;
+ new_stmt->header.next = NULL;
+ lang_statement_append (list, new_stmt, &new_stmt->header.next);
+ return new_stmt;
}
/* Build a new input file node for the language. There are several
{
lang_input_statement_type *p;
+ lang_has_input_file = TRUE;
+
if (add_to_list)
- p = new_stat (lang_input_statement, stat_ptr);
+ p = (lang_input_statement_type *) new_stat (lang_input_statement, stat_ptr);
else
{
- p = stat_alloc (sizeof (lang_input_statement_type));
+ p = (lang_input_statement_type *)
+ stat_alloc (sizeof (lang_input_statement_type));
p->header.type = lang_input_statement_enum;
p->header.next = NULL;
}
- lang_has_input_file = TRUE;
+ memset (&p->the_bfd, 0,
+ sizeof (*p) - offsetof (lang_input_statement_type, the_bfd));
p->target = target;
- p->sysrooted = FALSE;
-
- if (file_type == lang_input_file_is_l_enum
- && name[0] == ':' && name[1] != '\0')
- {
- file_type = lang_input_file_is_search_file_enum;
- name = name + 1;
- }
+ p->flags.dynamic = input_flags.dynamic;
+ p->flags.add_DT_NEEDED_for_dynamic = input_flags.add_DT_NEEDED_for_dynamic;
+ p->flags.add_DT_NEEDED_for_regular = input_flags.add_DT_NEEDED_for_regular;
+ p->flags.whole_archive = input_flags.whole_archive;
+ p->flags.sysrooted = input_flags.sysrooted;
switch (file_type)
{
case lang_input_file_is_symbols_only_enum:
p->filename = name;
- p->is_archive = FALSE;
- p->real = TRUE;
p->local_sym_name = name;
- p->just_syms_flag = TRUE;
- p->search_dirs_flag = FALSE;
+ p->flags.real = TRUE;
+ p->flags.just_syms = TRUE;
break;
case lang_input_file_is_fake_enum:
p->filename = name;
- p->is_archive = FALSE;
- p->real = FALSE;
p->local_sym_name = name;
- p->just_syms_flag = FALSE;
- p->search_dirs_flag = FALSE;
break;
case lang_input_file_is_l_enum:
- p->is_archive = TRUE;
- p->filename = name;
- p->real = TRUE;
+ if (name[0] == ':' && name[1] != '\0')
+ {
+ p->filename = name + 1;
+ p->flags.full_name_provided = TRUE;
+ }
+ else
+ p->filename = name;
p->local_sym_name = concat ("-l", name, (const char *) NULL);
- p->just_syms_flag = FALSE;
- p->search_dirs_flag = TRUE;
+ p->flags.maybe_archive = TRUE;
+ p->flags.real = TRUE;
+ p->flags.search_dirs = TRUE;
break;
case lang_input_file_is_marker_enum:
p->filename = name;
- p->is_archive = FALSE;
- p->real = FALSE;
p->local_sym_name = name;
- p->just_syms_flag = FALSE;
- p->search_dirs_flag = TRUE;
+ p->flags.search_dirs = TRUE;
break;
case lang_input_file_is_search_file_enum:
- p->sysrooted = ldlang_sysrooted_script;
p->filename = name;
- p->is_archive = FALSE;
- p->real = TRUE;
p->local_sym_name = name;
- p->just_syms_flag = FALSE;
- p->search_dirs_flag = TRUE;
+ p->flags.real = TRUE;
+ p->flags.search_dirs = TRUE;
break;
case lang_input_file_is_file_enum:
p->filename = name;
- p->is_archive = FALSE;
- p->real = TRUE;
p->local_sym_name = name;
- p->just_syms_flag = FALSE;
- p->search_dirs_flag = FALSE;
+ p->flags.real = TRUE;
break;
default:
FAIL ();
}
- p->the_bfd = NULL;
- p->asymbols = NULL;
- p->next_real_file = NULL;
- p->next = NULL;
- p->symbol_count = 0;
- p->dynamic = config.dynamic_link;
- p->add_needed = add_needed;
- p->as_needed = as_needed;
- p->whole_archive = whole_archive;
- p->loaded = FALSE;
+
lang_statement_append (&input_file_chain,
(lang_statement_union_type *) p,
&p->next_real_file);
lang_input_file_enum_type file_type,
const char *target)
{
+ if (name != NULL && *name == '=')
+ {
+ lang_input_statement_type *ret;
+ char *sysrooted_name
+ = concat (ld_sysroot, name + 1, (const char *) NULL);
+
+ /* We've now forcibly prepended the sysroot, making the input
+ file independent of the context. Therefore, temporarily
+ force a non-sysrooted context for this statement, so it won't
+ get the sysroot prepended again when opened. (N.B. if it's a
+ script, any child nodes with input files starting with "/"
+ will be handled as "sysrooted" as they'll be found to be
+ within the sysroot subdirectory.) */
+ unsigned int outer_sysrooted = input_flags.sysrooted;
+ input_flags.sysrooted = 0;
+ ret = new_afile (sysrooted_name, file_type, target, TRUE);
+ input_flags.sysrooted = outer_sysrooted;
+ return ret;
+ }
+
return new_afile (name, file_type, target, TRUE);
}
if (entry == NULL)
{
- entry = bfd_hash_allocate (table, sizeof (*ret));
+ entry = (struct bfd_hash_entry *) bfd_hash_allocate (table,
+ sizeof (*ret));
if (entry == NULL)
return entry;
}
first_file = lang_add_input_file (NULL, lang_input_file_is_marker_enum,
NULL);
abs_output_section =
- lang_output_section_statement_lookup (BFD_ABS_SECTION_NAME);
+ lang_output_section_statement_lookup (BFD_ABS_SECTION_NAME, 0, TRUE);
abs_output_section->bfd_section = bfd_abs_section_ptr;
- /* The value "3" is ad-hoc, somewhat related to the expected number of
- DEFINED expressions in a linker script. For most default linker
- scripts, there are none. Why a hash table then? Well, it's somewhat
- simpler to re-use working machinery than using a linked list in terms
- of code-complexity here in ld, besides the initialization which just
- looks like other code here. */
- if (!bfd_hash_table_init_n (&lang_definedness_table,
- lang_definedness_newfunc,
- sizeof (struct lang_definedness_hash_entry),
- 3))
- einfo (_("%P%F: can not create hash table: %E\n"));
+ asneeded_list_head = NULL;
+ asneeded_list_tail = &asneeded_list_head;
}
void
In this case it is probably an error to create a region that has
already been created. If we are not inside a MEMORY block it is
dubious to use an undeclared region name (except DEFAULT_MEMORY_REGION)
- and so we issue a warning. */
+ and so we issue a warning.
+
+ Each region has at least one name. The first name is either
+ DEFAULT_MEMORY_REGION or the name given in the MEMORY block. You can add
+ alias names to an existing region within a script with
+ REGION_ALIAS (alias, region_name). Each name corresponds to at most one
+ region. */
static lang_memory_region_type *lang_memory_region_list;
static lang_memory_region_type **lang_memory_region_list_tail
lang_memory_region_type *
lang_memory_region_lookup (const char *const name, bfd_boolean create)
{
- lang_memory_region_type *p;
- lang_memory_region_type *new;
+ lang_memory_region_name *n;
+ lang_memory_region_type *r;
+ lang_memory_region_type *new_region;
/* NAME is NULL for LMA memspecs if no region was specified. */
if (name == NULL)
return NULL;
- for (p = lang_memory_region_list; p != NULL; p = p->next)
- if (strcmp (p->name, name) == 0)
- {
- if (create)
- einfo (_("%P:%S: warning: redeclaration of memory region '%s'\n"),
- name);
- return p;
- }
+ for (r = lang_memory_region_list; r != NULL; r = r->next)
+ for (n = &r->name_list; n != NULL; n = n->next)
+ if (strcmp (n->name, name) == 0)
+ {
+ if (create)
+ einfo (_("%P:%S: warning: redeclaration of memory region `%s'\n"),
+ NULL, name);
+ return r;
+ }
if (!create && strcmp (name, DEFAULT_MEMORY_REGION))
- einfo (_("%P:%S: warning: memory region %s not declared\n"), name);
+ einfo (_("%P:%S: warning: memory region `%s' not declared\n"),
+ NULL, name);
+
+ new_region = (lang_memory_region_type *)
+ stat_alloc (sizeof (lang_memory_region_type));
+
+ new_region->name_list.name = xstrdup (name);
+ new_region->name_list.next = NULL;
+ new_region->next = NULL;
+ new_region->origin_exp = NULL;
+ new_region->origin = 0;
+ new_region->length_exp = NULL;
+ new_region->length = ~(bfd_size_type) 0;
+ new_region->current = 0;
+ new_region->last_os = NULL;
+ new_region->flags = 0;
+ new_region->not_flags = 0;
+ new_region->had_full_message = FALSE;
+
+ *lang_memory_region_list_tail = new_region;
+ lang_memory_region_list_tail = &new_region->next;
+
+ return new_region;
+}
- new = stat_alloc (sizeof (lang_memory_region_type));
+void
+lang_memory_region_alias (const char *alias, const char *region_name)
+{
+ lang_memory_region_name *n;
+ lang_memory_region_type *r;
+ lang_memory_region_type *region;
- new->name = xstrdup (name);
- new->next = NULL;
- new->origin = 0;
- new->length = ~(bfd_size_type) 0;
- new->current = 0;
- new->last_os = NULL;
- new->flags = 0;
- new->not_flags = 0;
- new->had_full_message = FALSE;
+ /* The default region must be unique. This ensures that it is not necessary
+ to iterate through the name list if someone wants the check if a region is
+ the default memory region. */
+ if (strcmp (region_name, DEFAULT_MEMORY_REGION) == 0
+ || strcmp (alias, DEFAULT_MEMORY_REGION) == 0)
+ einfo (_("%F%P:%S: error: alias for default memory region\n"), NULL);
+
+ /* Look for the target region and check if the alias is not already
+ in use. */
+ region = NULL;
+ for (r = lang_memory_region_list; r != NULL; r = r->next)
+ for (n = &r->name_list; n != NULL; n = n->next)
+ {
+ if (region == NULL && strcmp (n->name, region_name) == 0)
+ region = r;
+ if (strcmp (n->name, alias) == 0)
+ einfo (_("%F%P:%S: error: redefinition of memory region "
+ "alias `%s'\n"),
+ NULL, alias);
+ }
- *lang_memory_region_list_tail = new;
- lang_memory_region_list_tail = &new->next;
+ /* Check if the target region exists. */
+ if (region == NULL)
+ einfo (_("%F%P:%S: error: memory region `%s' "
+ "for alias `%s' does not exist\n"),
+ NULL, region_name, alias);
- return new;
+ /* Add alias to region name list. */
+ n = (lang_memory_region_name *) stat_alloc (sizeof (lang_memory_region_name));
+ n->name = xstrdup (alias);
+ n->next = region->name_list.next;
+ region->name_list.next = n;
}
static lang_memory_region_type *
return lang_memory_region_lookup (DEFAULT_MEMORY_REGION, FALSE);
}
+/* Get the output section statement directly from the userdata. */
+
lang_output_section_statement_type *
-lang_output_section_find (const char *const name)
+lang_output_section_get (const asection *output_section)
{
- struct out_section_hash_entry *entry;
- unsigned long hash;
-
- entry = ((struct out_section_hash_entry *)
- bfd_hash_lookup (&output_section_statement_table, name,
- FALSE, FALSE));
- if (entry == NULL)
- return NULL;
-
- hash = entry->root.hash;
- do
- {
- if (entry->s.output_section_statement.constraint != -1)
- return &entry->s.output_section_statement;
- entry = (struct out_section_hash_entry *) entry->root.next;
- }
- while (entry != NULL
- && entry->root.hash == hash
- && strcmp (name, entry->s.output_section_statement.name) == 0);
-
- return NULL;
+ return get_userdata (output_section);
}
-static lang_output_section_statement_type *
-lang_output_section_statement_lookup_1 (const char *const name, int constraint)
+/* Find or create an output_section_statement with the given NAME.
+ If CONSTRAINT is non-zero match one with that constraint, otherwise
+ match any non-negative constraint. If CREATE, always make a
+ new output_section_statement for SPECIAL CONSTRAINT. */
+
+lang_output_section_statement_type *
+lang_output_section_statement_lookup (const char *name,
+ int constraint,
+ bfd_boolean create)
{
struct out_section_hash_entry *entry;
- struct out_section_hash_entry *last_ent;
- unsigned long hash;
entry = ((struct out_section_hash_entry *)
bfd_hash_lookup (&output_section_statement_table, name,
- TRUE, FALSE));
+ create, FALSE));
if (entry == NULL)
{
- einfo (_("%P%F: failed creating section `%s': %E\n"), name);
+ if (create)
+ einfo (_("%P%F: failed creating section `%s': %E\n"), name);
return NULL;
}
{
/* We have a section of this name, but it might not have the correct
constraint. */
- hash = entry->root.hash;
- do
- {
- if (entry->s.output_section_statement.constraint != -1
- && (constraint == 0
- || (constraint == entry->s.output_section_statement.constraint
- && constraint != SPECIAL)))
- return &entry->s.output_section_statement;
- last_ent = entry;
- entry = (struct out_section_hash_entry *) entry->root.next;
- }
- while (entry != NULL
- && entry->root.hash == hash
- && strcmp (name, entry->s.output_section_statement.name) == 0);
+ struct out_section_hash_entry *last_ent;
+
+ name = entry->s.output_section_statement.name;
+ if (create && constraint == SPECIAL)
+ /* Not traversing to the end reverses the order of the second
+ and subsequent SPECIAL sections in the hash table chain,
+ but that shouldn't matter. */
+ last_ent = entry;
+ else
+ do
+ {
+ if (constraint == entry->s.output_section_statement.constraint
+ || (constraint == 0
+ && entry->s.output_section_statement.constraint >= 0))
+ return &entry->s.output_section_statement;
+ last_ent = entry;
+ entry = (struct out_section_hash_entry *) entry->root.next;
+ }
+ while (entry != NULL
+ && name == entry->s.output_section_statement.name);
+
+ if (!create)
+ return NULL;
entry
= ((struct out_section_hash_entry *)
return &entry->s.output_section_statement;
}
+/* Find the next output_section_statement with the same name as OS.
+ If CONSTRAINT is non-zero, find one with that constraint otherwise
+ match any non-negative constraint. */
+
lang_output_section_statement_type *
-lang_output_section_statement_lookup (const char *const name)
+next_matching_output_section_statement (lang_output_section_statement_type *os,
+ int constraint)
{
- return lang_output_section_statement_lookup_1 (name, 0);
+ /* All output_section_statements are actually part of a
+ struct out_section_hash_entry. */
+ struct out_section_hash_entry *entry = (struct out_section_hash_entry *)
+ ((char *) os
+ - offsetof (struct out_section_hash_entry, s.output_section_statement));
+ const char *name = os->name;
+
+ ASSERT (name == entry->root.string);
+ do
+ {
+ entry = (struct out_section_hash_entry *) entry->root.next;
+ if (entry == NULL
+ || name != entry->s.output_section_statement.name)
+ return NULL;
+ }
+ while (constraint != entry->s.output_section_statement.constraint
+ && (constraint != 0
+ || entry->s.output_section_statement.constraint < 0));
+
+ return &entry->s.output_section_statement;
}
/* A variant of lang_output_section_find used by place_orphan.
lang_output_section_statement_type *
lang_output_section_find_by_flags (const asection *sec,
+ flagword sec_flags,
lang_output_section_statement_type **exact,
lang_match_sec_type_func match_type)
{
lang_output_section_statement_type *first, *look, *found;
- flagword flags;
+ flagword look_flags, differ;
/* We know the first statement on this list is *ABS*. May as well
skip it. */
found = NULL;
for (look = first; look; look = look->next)
{
- flags = look->flags;
+ look_flags = look->flags;
if (look->bfd_section != NULL)
{
- flags = look->bfd_section->flags;
+ look_flags = look->bfd_section->flags;
if (match_type && !match_type (link_info.output_bfd,
look->bfd_section,
sec->owner, sec))
continue;
}
- flags ^= sec->flags;
- if (!(flags & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY
- | SEC_CODE | SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
+ differ = look_flags ^ sec_flags;
+ if (!(differ & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY
+ | SEC_CODE | SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
found = look;
}
if (found != NULL)
return found;
}
- if (sec->flags & SEC_CODE)
+ if ((sec_flags & SEC_CODE) != 0
+ && (sec_flags & SEC_ALLOC) != 0)
{
/* Try for a rw code section. */
for (look = first; look; look = look->next)
{
- flags = look->flags;
+ look_flags = look->flags;
if (look->bfd_section != NULL)
{
- flags = look->bfd_section->flags;
+ look_flags = look->bfd_section->flags;
if (match_type && !match_type (link_info.output_bfd,
look->bfd_section,
sec->owner, sec))
continue;
}
- flags ^= sec->flags;
- if (!(flags & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
- | SEC_CODE | SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
+ differ = look_flags ^ sec_flags;
+ if (!(differ & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
+ | SEC_CODE | SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
found = look;
}
}
- else if (sec->flags & (SEC_READONLY | SEC_THREAD_LOCAL))
+ else if ((sec_flags & SEC_READONLY) != 0
+ && (sec_flags & SEC_ALLOC) != 0)
{
/* .rodata can go after .text, .sdata2 after .rodata. */
for (look = first; look; look = look->next)
{
- flags = look->flags;
+ look_flags = look->flags;
if (look->bfd_section != NULL)
{
- flags = look->bfd_section->flags;
+ look_flags = look->bfd_section->flags;
if (match_type && !match_type (link_info.output_bfd,
look->bfd_section,
sec->owner, sec))
continue;
}
- flags ^= sec->flags;
- if (!(flags & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
- | SEC_READONLY))
- && !(look->flags & (SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
+ differ = look_flags ^ sec_flags;
+ if (!(differ & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
+ | SEC_READONLY | SEC_SMALL_DATA))
+ || (!(differ & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
+ | SEC_READONLY))
+ && !(look_flags & SEC_SMALL_DATA)))
+ found = look;
+ }
+ }
+ else if ((sec_flags & SEC_THREAD_LOCAL) != 0
+ && (sec_flags & SEC_ALLOC) != 0)
+ {
+ /* .tdata can go after .data, .tbss after .tdata. Treat .tbss
+ as if it were a loaded section, and don't use match_type. */
+ bfd_boolean seen_thread_local = FALSE;
+
+ match_type = NULL;
+ for (look = first; look; look = look->next)
+ {
+ look_flags = look->flags;
+ if (look->bfd_section != NULL)
+ look_flags = look->bfd_section->flags;
+
+ differ = look_flags ^ (sec_flags | SEC_LOAD | SEC_HAS_CONTENTS);
+ if (!(differ & (SEC_THREAD_LOCAL | SEC_ALLOC)))
+ {
+ /* .tdata and .tbss must be adjacent and in that order. */
+ if (!(look_flags & SEC_LOAD)
+ && (sec_flags & SEC_LOAD))
+ /* ..so if we're at a .tbss section and we're placing
+ a .tdata section stop looking and return the
+ previous section. */
+ break;
+ found = look;
+ seen_thread_local = TRUE;
+ }
+ else if (seen_thread_local)
+ break;
+ else if (!(differ & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD)))
found = look;
}
}
- else if (sec->flags & SEC_SMALL_DATA)
+ else if ((sec_flags & SEC_SMALL_DATA) != 0
+ && (sec_flags & SEC_ALLOC) != 0)
{
/* .sdata goes after .data, .sbss after .sdata. */
for (look = first; look; look = look->next)
{
- flags = look->flags;
+ look_flags = look->flags;
if (look->bfd_section != NULL)
{
- flags = look->bfd_section->flags;
+ look_flags = look->bfd_section->flags;
if (match_type && !match_type (link_info.output_bfd,
look->bfd_section,
sec->owner, sec))
continue;
}
- flags ^= sec->flags;
- if (!(flags & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
- | SEC_THREAD_LOCAL))
- || ((look->flags & SEC_SMALL_DATA)
- && !(sec->flags & SEC_HAS_CONTENTS)))
+ differ = look_flags ^ sec_flags;
+ if (!(differ & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
+ | SEC_THREAD_LOCAL))
+ || ((look_flags & SEC_SMALL_DATA)
+ && !(sec_flags & SEC_HAS_CONTENTS)))
found = look;
}
}
- else if (sec->flags & SEC_HAS_CONTENTS)
+ else if ((sec_flags & SEC_HAS_CONTENTS) != 0
+ && (sec_flags & SEC_ALLOC) != 0)
{
/* .data goes after .rodata. */
for (look = first; look; look = look->next)
{
- flags = look->flags;
+ look_flags = look->flags;
if (look->bfd_section != NULL)
{
- flags = look->bfd_section->flags;
+ look_flags = look->bfd_section->flags;
if (match_type && !match_type (link_info.output_bfd,
look->bfd_section,
sec->owner, sec))
continue;
}
- flags ^= sec->flags;
- if (!(flags & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
- | SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
+ differ = look_flags ^ sec_flags;
+ if (!(differ & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
+ | SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
found = look;
}
}
- else
+ else if ((sec_flags & SEC_ALLOC) != 0)
{
- /* .bss goes last. */
+ /* .bss goes after any other alloc section. */
for (look = first; look; look = look->next)
{
- flags = look->flags;
+ look_flags = look->flags;
if (look->bfd_section != NULL)
{
- flags = look->bfd_section->flags;
+ look_flags = look->bfd_section->flags;
if (match_type && !match_type (link_info.output_bfd,
look->bfd_section,
sec->owner, sec))
continue;
}
- flags ^= sec->flags;
- if (!(flags & SEC_ALLOC))
+ differ = look_flags ^ sec_flags;
+ if (!(differ & SEC_ALLOC))
+ found = look;
+ }
+ }
+ else
+ {
+ /* non-alloc go last. */
+ for (look = first; look; look = look->next)
+ {
+ look_flags = look->flags;
+ if (look->bfd_section != NULL)
+ look_flags = look->bfd_section->flags;
+ differ = look_flags ^ sec_flags;
+ if (!(differ & SEC_DEBUGGING))
found = look;
}
+ return found;
}
if (found || !match_type)
return found;
- return lang_output_section_find_by_flags (sec, NULL, NULL);
+ return lang_output_section_find_by_flags (sec, sec_flags, NULL, NULL);
}
/* Find the last output section before given output statement.
for (lookup = os->prev; lookup != NULL; lookup = lookup->prev)
{
- if (lookup->constraint == -1)
+ if (lookup->constraint < 0)
continue;
if (lookup->bfd_section != NULL && lookup->bfd_section->owner != NULL)
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. */
+ are assumed to belong to it, except in two cases; The first
+ assignment to dot, and assignments before non-alloc sections.
+ Otherwise we might put an orphan before . = . + SIZEOF_HEADERS or
+ similar assignments that set the initial address, or we might
+ insert non-alloc note sections among assignments setting end of
+ image symbols. */
static lang_statement_union_type **
insert_os_after (lang_output_section_statement_type *after)
continue;
case lang_output_section_statement_enum:
if (assign != NULL)
- where = assign;
+ {
+ asection *s = (*where)->output_section_statement.bfd_section;
+
+ if (s == NULL
+ || s->map_head.s == NULL
+ || (s->flags & SEC_ALLOC) != 0)
+ where = assign;
+ }
break;
case lang_input_statement_enum:
case lang_address_statement_enum:
lang_output_section_statement_type *
lang_insert_orphan (asection *s,
const char *secname,
+ int constraint,
lang_output_section_statement_type *after,
struct orphan_save *place,
etree_type *address,
lang_statement_list_type *add_child)
{
- lang_statement_list_type *old;
lang_statement_list_type add;
const char *ps;
+ lang_assignment_statement_type *start_assign;
lang_output_section_statement_type *os;
lang_output_section_statement_type **os_tail;
- /* Start building a list of statements for this section.
- First save the current statement pointer. */
- old = stat_ptr;
-
/* If we have found an appropriate place for the output section
statements for this orphan, add them to our own private list,
inserting them later into the global statement list. */
if (after != NULL)
{
- stat_ptr = &add;
- lang_list_init (stat_ptr);
+ lang_list_init (&add);
+ push_stat_ptr (&add);
}
+ if (bfd_link_relocatable (&link_info)
+ || (s->flags & (SEC_LOAD | SEC_ALLOC)) == 0)
+ address = exp_intop (0);
+
+ os_tail = ((lang_output_section_statement_type **)
+ lang_output_section_statement.tail);
+ os = lang_enter_output_section_statement (secname, address, normal_section,
+ NULL, NULL, NULL, constraint, 0);
+
ps = NULL;
- if (config.build_constructors)
+ start_assign = NULL;
+ if (config.build_constructors && *os_tail == os)
{
/* If the name of the section is representable in C, then create
symbols to mark the start and the end of the section. */
for (ps = secname; *ps != '\0'; ps++)
- if (! ISALNUM ((unsigned char) *ps) && *ps != '_')
+ if (!ISALNUM ((unsigned char) *ps) && *ps != '_')
break;
if (*ps == '\0')
{
char *symname;
- etree_type *e_align;
symname = (char *) xmalloc (ps - secname + sizeof "__start_" + 1);
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));
- lang_add_assignment (exp_assop ('=', ".", e_align));
- lang_add_assignment (exp_provide (symname,
- exp_nameop (NAME, "."),
- FALSE));
+ start_assign
+ = lang_add_assignment (exp_provide (symname,
+ exp_nameop (NAME, "."),
+ FALSE));
}
}
- if (link_info.relocatable || (s->flags & (SEC_LOAD | SEC_ALLOC)) == 0)
- address = exp_intop (0);
-
- os_tail = ((lang_output_section_statement_type **)
- lang_output_section_statement.tail);
- os = lang_enter_output_section_statement (secname, address, 0, NULL, NULL,
- NULL, 0);
-
if (add_child == NULL)
add_child = &os->children;
- lang_add_section (add_child, s, os);
+ lang_add_section (add_child, s, NULL, os);
- lang_leave_output_section_statement (0, "*default*", NULL, NULL);
+ if (after && (s->flags & (SEC_LOAD | SEC_ALLOC)) != 0)
+ {
+ const char *region = (after->region
+ ? after->region->name_list.name
+ : DEFAULT_MEMORY_REGION);
+ const char *lma_region = (after->lma_region
+ ? after->lma_region->name_list.name
+ : NULL);
+ lang_leave_output_section_statement (NULL, region, after->phdrs,
+ lma_region);
+ }
+ else
+ lang_leave_output_section_statement (NULL, DEFAULT_MEMORY_REGION, NULL,
+ NULL);
- if (config.build_constructors && *ps == '\0')
+ if (start_assign != NULL)
{
char *symname;
-
- /* lang_leave_ouput_section_statement resets stat_ptr.
- Put stat_ptr back where we want it. */
- if (after != NULL)
- stat_ptr = &add;
+ lang_assignment_statement_type *stop_assign;
+ bfd_vma dot;
symname = (char *) xmalloc (ps - secname + sizeof "__stop_" + 1);
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, "."),
- FALSE));
+ stop_assign
+ = lang_add_assignment (exp_provide (symname,
+ exp_nameop (NAME, "."),
+ FALSE));
+ /* Evaluate the expression to define the symbol if referenced,
+ before sizing dynamic sections. */
+ dot = os->bfd_section->vma;
+ exp_fold_tree (start_assign->exp, os->bfd_section, &dot);
+ dot += TO_ADDR (s->size);
+ exp_fold_tree (stop_assign->exp, os->bfd_section, &dot);
}
/* Restore the global list pointer. */
if (after != NULL)
- stat_ptr = old;
+ pop_stat_ptr ();
if (after != NULL && os->bfd_section != NULL)
{
/* Fix the global list pointer if we happened to tack our
new list at the tail. */
- if (*old->tail == add.head)
- old->tail = add.tail;
+ if (*stat_ptr->tail == add.head)
+ stat_ptr->tail = add.tail;
/* Save the end of this list. */
place->stmt = add.tail;
return os;
}
+static void
+lang_print_asneeded (void)
+{
+ struct asneeded_minfo *m;
+ char buf[100];
+
+ if (asneeded_list_head == NULL)
+ return;
+
+ sprintf (buf, _("\nAs-needed library included "
+ "to satisfy reference by file (symbol)\n\n"));
+ minfo ("%s", buf);
+
+ for (m = asneeded_list_head; m != NULL; m = m->next)
+ {
+ size_t len;
+
+ minfo ("%s", m->soname);
+ len = strlen (m->soname);
+
+ if (len >= 29)
+ {
+ print_nl ();
+ len = 0;
+ }
+ while (len < 30)
+ {
+ print_space ();
+ ++len;
+ }
+
+ if (m->ref != NULL)
+ minfo ("%B ", m->ref);
+ minfo ("(%T)\n", m->name);
+ }
+}
+
static void
lang_map_flags (flagword flag)
{
{
lang_memory_region_type *m;
bfd_boolean dis_header_printed = FALSE;
- bfd *p;
LANG_FOR_EACH_INPUT_STATEMENT (file)
{
asection *s;
if ((file->the_bfd->flags & (BFD_LINKER_CREATED | DYNAMIC)) != 0
- || file->just_syms_flag)
+ || file->flags.just_syms)
continue;
for (s = file->the_bfd->sections; s != NULL; s = s->next)
|| s->output_section->owner != link_info.output_bfd)
&& (s->flags & (SEC_LINKER_CREATED | SEC_KEEP)) == 0)
{
- if (! dis_header_printed)
+ if (!dis_header_printed)
{
fprintf (config.map_file, _("\nDiscarded input sections\n\n"));
dis_header_printed = TRUE;
}
- print_input_section (s);
+ print_input_section (s, TRUE);
}
}
char buf[100];
int len;
- fprintf (config.map_file, "%-16s ", m->name);
+ fprintf (config.map_file, "%-16s ", m->name_list.name);
sprintf_vma (buf, m->origin);
minfo ("0x%s ", buf);
fprintf (config.map_file, _("\nLinker script and memory map\n\n"));
- if (! link_info.reduce_memory_overheads)
+ if (!link_info.reduce_memory_overheads)
{
obstack_begin (&map_obstack, 1000);
- for (p = link_info.input_bfds; p != (bfd *) NULL; p = p->link_next)
- bfd_map_over_sections (p, init_map_userdata, 0);
bfd_link_hash_traverse (link_info.hash, sort_def_symbol, 0);
}
- lang_statement_iteration ++;
+ lang_statement_iteration++;
print_statements ();
-}
-static void
-init_map_userdata (bfd *abfd ATTRIBUTE_UNUSED,
- asection *sec,
- void *data ATTRIBUTE_UNUSED)
-{
- fat_section_userdata_type *new_data
- = ((fat_section_userdata_type *) (stat_alloc
- (sizeof (fat_section_userdata_type))));
-
- ASSERT (get_userdata (sec) == NULL);
- get_userdata (sec) = new_data;
- new_data->map_symbol_def_tail = &new_data->map_symbol_def_head;
+ ldemul_extra_map_file_text (link_info.output_bfd, &link_info,
+ config.map_file);
}
static bfd_boolean
sort_def_symbol (struct bfd_link_hash_entry *hash_entry,
void *info ATTRIBUTE_UNUSED)
{
- if (hash_entry->type == bfd_link_hash_defined
- || hash_entry->type == bfd_link_hash_defweak)
+ if ((hash_entry->type == bfd_link_hash_defined
+ || hash_entry->type == bfd_link_hash_defweak)
+ && hash_entry->u.def.section->owner != link_info.output_bfd
+ && hash_entry->u.def.section->owner != NULL)
{
- struct fat_user_section_struct *ud;
+ input_section_userdata_type *ud;
struct map_symbol_def *def;
- ud = get_userdata (hash_entry->u.def.section);
- if (! ud)
+ ud = ((input_section_userdata_type *)
+ get_userdata (hash_entry->u.def.section));
+ if (!ud)
{
- /* ??? What do we have to do to initialize this beforehand? */
- /* The first time we get here is bfd_abs_section... */
- init_map_userdata (0, hash_entry->u.def.section, 0);
- ud = get_userdata (hash_entry->u.def.section);
+ ud = (input_section_userdata_type *) stat_alloc (sizeof (*ud));
+ get_userdata (hash_entry->u.def.section) = ud;
+ ud->map_symbol_def_tail = &ud->map_symbol_def_head;
+ ud->map_symbol_def_count = 0;
}
- else if (!ud->map_symbol_def_tail)
+ else if (!ud->map_symbol_def_tail)
ud->map_symbol_def_tail = &ud->map_symbol_def_head;
- def = obstack_alloc (&map_obstack, sizeof *def);
+ def = (struct map_symbol_def *) obstack_alloc (&map_obstack, sizeof *def);
def->entry = hash_entry;
*(ud->map_symbol_def_tail) = def;
ud->map_symbol_def_tail = &def->next;
+ ud->map_symbol_def_count++;
}
return TRUE;
}
/* Initialize an output section. */
static void
-init_os (lang_output_section_statement_type *s, asection *isec,
- flagword flags)
+init_os (lang_output_section_statement_type *s, flagword flags)
{
- if (s->bfd_section != NULL)
- return;
-
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 (link_info.output_bfd, s->name);
+ if (s->constraint != SPECIAL)
+ 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 (link_info.output_bfd,
- s->name, flags);
+ s->bfd_section = bfd_make_section_anyway_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"),
+ einfo (_("%P%F: output format %s cannot represent section"
+ " called %s: %E\n"),
link_info.output_bfd->xvec->name, s->name);
}
s->bfd_section->output_section = s->bfd_section;
s->bfd_section->output_offset = 0;
- if (!link_info.reduce_memory_overheads)
- {
- fat_section_userdata_type *new
- = stat_alloc (sizeof (fat_section_userdata_type));
- memset (new, 0, sizeof (fat_section_userdata_type));
- get_userdata (s->bfd_section) = new;
- }
+ /* Set the userdata of the output section to the output section
+ statement to avoid lookup. */
+ get_userdata (s->bfd_section) = s;
/* If there is a base address, make sure that any sections it might
mention are initialized. */
/* If supplied an alignment, set it. */
if (s->section_alignment != -1)
s->bfd_section->alignment_power = s->section_alignment;
-
- if (isec)
- bfd_init_private_section_data (isec->owner, isec,
- link_info.output_bfd, s->bfd_section,
- &link_info);
}
/* Make sure that all output sections mentioned in an expression are
os = lang_output_section_find (exp->name.name);
if (os != NULL && os->bfd_section == NULL)
- init_os (os, NULL, 0);
+ init_os (os, 0);
}
}
break;
static void
section_already_linked (bfd *abfd, asection *sec, void *data)
{
- lang_input_statement_type *entry = data;
+ lang_input_statement_type *entry = (lang_input_statement_type *) data;
/* If we are only reading symbols from this object, then we want to
discard all sections. */
- if (entry->just_syms_flag)
+ if (entry->flags.just_syms)
{
bfd_link_just_syms (abfd, sec, &link_info);
return;
foo.o(.text, .data). */
/* Add SECTION to the output section OUTPUT. Do this by creating a
- lang_input_section statement which is placed at PTR. FILE is the
- input file which holds SECTION. */
+ lang_input_section statement which is placed at PTR. */
void
lang_add_section (lang_statement_list_type *ptr,
asection *section,
+ struct flag_info *sflag_info,
lang_output_section_statement_type *output)
{
flagword flags = section->flags;
+
bfd_boolean discard;
+ lang_input_section_type *new_section;
+ bfd *abfd = link_info.output_bfd;
/* Discard sections marked with SEC_EXCLUDE. */
discard = (flags & SEC_EXCLUDE) != 0;
return;
}
- if (section->output_section == NULL)
+ if (sflag_info)
{
- bfd_boolean first;
- lang_input_section_type *new;
- flagword flags;
-
- flags = section->flags;
-
- /* We don't copy the SEC_NEVER_LOAD flag from an input section
- to an output section, because we want to be able to include a
- SEC_NEVER_LOAD section in the middle of an otherwise loaded
- section (I don't know why we want to do this, but we do).
- build_link_order in ldwrite.c handles this case by turning
- the embedded SEC_NEVER_LOAD section into a fill. */
-
- flags &= ~ SEC_NEVER_LOAD;
+ bfd_boolean keep;
- switch (output->sectype)
- {
- case normal_section:
- case overlay_section:
- break;
- case noalloc_section:
- flags &= ~SEC_ALLOC;
- break;
- case noload_section:
- flags &= ~SEC_LOAD;
- flags |= SEC_NEVER_LOAD;
- break;
- }
-
- if (output->bfd_section == NULL)
- init_os (output, section, flags);
+ keep = bfd_lookup_section_flags (&link_info, sflag_info, section);
+ if (!keep)
+ return;
+ }
- first = ! output->bfd_section->linker_has_input;
- output->bfd_section->linker_has_input = 1;
+ if (section->output_section != NULL)
+ return;
- if (!link_info.relocatable
- && !stripped_excluded_sections)
- {
- asection *s = output->bfd_section->map_tail.s;
- output->bfd_section->map_tail.s = section;
- section->map_head.s = NULL;
- section->map_tail.s = s;
- if (s != NULL)
- s->map_head.s = section;
- else
- output->bfd_section->map_head.s = section;
- }
+ /* We don't copy the SEC_NEVER_LOAD flag from an input section
+ to an output section, because we want to be able to include a
+ SEC_NEVER_LOAD section in the middle of an otherwise loaded
+ section (I don't know why we want to do this, but we do).
+ build_link_order in ldwrite.c handles this case by turning
+ the embedded SEC_NEVER_LOAD section into a fill. */
+ flags &= ~ SEC_NEVER_LOAD;
- /* Add a section reference to the list. */
- new = new_stat (lang_input_section, ptr);
+ /* If final link, don't copy the SEC_LINK_ONCE flags, they've
+ already been processed. One reason to do this is that on pe
+ format targets, .text$foo sections go into .text and it's odd
+ to see .text with SEC_LINK_ONCE set. */
- new->section = section;
- section->output_section = output->bfd_section;
+ if (!bfd_link_relocatable (&link_info))
+ flags &= ~(SEC_LINK_ONCE | SEC_LINK_DUPLICATES | SEC_RELOC);
- /* If final link, don't copy the SEC_LINK_ONCE flags, they've
- already been processed. One reason to do this is that on pe
- format targets, .text$foo sections go into .text and it's odd
- to see .text with SEC_LINK_ONCE set. */
+ switch (output->sectype)
+ {
+ case normal_section:
+ case overlay_section:
+ break;
+ case noalloc_section:
+ flags &= ~SEC_ALLOC;
+ break;
+ case noload_section:
+ flags &= ~SEC_LOAD;
+ flags |= SEC_NEVER_LOAD;
+ /* Unfortunately GNU ld has managed to evolve two different
+ meanings to NOLOAD in scripts. ELF gets a .bss style noload,
+ alloc, no contents section. All others get a noload, noalloc
+ section. */
+ if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour)
+ flags &= ~SEC_HAS_CONTENTS;
+ else
+ flags &= ~SEC_ALLOC;
+ break;
+ }
- if (! link_info.relocatable)
- flags &= ~ (SEC_LINK_ONCE | SEC_LINK_DUPLICATES);
+ if (output->bfd_section == NULL)
+ init_os (output, flags);
- /* If this is not the first input section, and the SEC_READONLY
- flag is not currently set, then don't set it just because the
- input section has it set. */
+ /* If SEC_READONLY is not set in the input section, then clear
+ it from the output section. */
+ output->bfd_section->flags &= flags | ~SEC_READONLY;
- if (! first && (output->bfd_section->flags & SEC_READONLY) == 0)
- flags &= ~ SEC_READONLY;
+ if (output->bfd_section->linker_has_input)
+ {
+ /* Only set SEC_READONLY flag on the first input section. */
+ flags &= ~ SEC_READONLY;
/* Keep SEC_MERGE and SEC_STRINGS only if they are the same. */
- if (! first
- && ((output->bfd_section->flags & (SEC_MERGE | SEC_STRINGS))
- != (flags & (SEC_MERGE | SEC_STRINGS))
- || ((flags & SEC_MERGE)
- && output->bfd_section->entsize != section->entsize)))
+ if ((output->bfd_section->flags & (SEC_MERGE | SEC_STRINGS))
+ != (flags & (SEC_MERGE | SEC_STRINGS))
+ || ((flags & SEC_MERGE) != 0
+ && output->bfd_section->entsize != section->entsize))
{
output->bfd_section->flags &= ~ (SEC_MERGE | SEC_STRINGS);
flags &= ~ (SEC_MERGE | SEC_STRINGS);
}
+ }
+ output->bfd_section->flags |= flags;
- output->bfd_section->flags |= flags;
-
- if (flags & SEC_MERGE)
+ if (!output->bfd_section->linker_has_input)
+ {
+ output->bfd_section->linker_has_input = 1;
+ /* This must happen after flags have been updated. The output
+ section may have been created before we saw its first input
+ section, eg. for a data statement. */
+ bfd_init_private_section_data (section->owner, section,
+ link_info.output_bfd,
+ output->bfd_section,
+ &link_info);
+ if ((flags & SEC_MERGE) != 0)
output->bfd_section->entsize = section->entsize;
+ }
- /* If SEC_READONLY is not set in the input section, then clear
- it from the output section. */
- if ((section->flags & SEC_READONLY) == 0)
- output->bfd_section->flags &= ~SEC_READONLY;
+ if ((flags & SEC_TIC54X_BLOCK) != 0
+ && bfd_get_arch (section->owner) == bfd_arch_tic54x)
+ {
+ /* FIXME: This value should really be obtained from the bfd... */
+ output->block_value = 128;
+ }
- /* Copy over SEC_SMALL_DATA. */
- if (section->flags & SEC_SMALL_DATA)
- output->bfd_section->flags |= SEC_SMALL_DATA;
+ if (section->alignment_power > output->bfd_section->alignment_power)
+ output->bfd_section->alignment_power = section->alignment_power;
- if (section->alignment_power > output->bfd_section->alignment_power)
- output->bfd_section->alignment_power = section->alignment_power;
+ section->output_section = output->bfd_section;
- if (bfd_get_arch (section->owner) == bfd_arch_tic54x
- && (section->flags & SEC_TIC54X_BLOCK) != 0)
- {
- output->bfd_section->flags |= SEC_TIC54X_BLOCK;
- /* FIXME: This value should really be obtained from the bfd... */
- output->block_value = 128;
- }
+ if (!map_head_is_link_order)
+ {
+ asection *s = output->bfd_section->map_tail.s;
+ output->bfd_section->map_tail.s = section;
+ section->map_head.s = NULL;
+ section->map_tail.s = s;
+ if (s != NULL)
+ s->map_head.s = section;
+ else
+ output->bfd_section->map_head.s = section;
}
+
+ /* Add a section reference to the list. */
+ new_section = new_stat (lang_input_section, ptr);
+ new_section->section = section;
}
/* Handle wildcard sorting. This returns the lang_input_section which
lang_input_statement_type *file,
asection *section)
{
- const char *section_name;
lang_statement_union_type *l;
if (!wild->filenames_sorted
&& (sec == NULL || sec->spec.sorted == none))
return NULL;
- section_name = bfd_get_section_name (file->the_bfd, section);
for (l = wild->children.head; l != NULL; l = l->header.next)
{
lang_input_section_type *ls;
archive. */
if (file->the_bfd != NULL
- && bfd_my_archive (file->the_bfd) != NULL)
+ && file->the_bfd->my_archive != NULL)
{
- fn = bfd_get_filename (bfd_my_archive (file->the_bfd));
+ fn = bfd_get_filename (file->the_bfd->my_archive);
fa = TRUE;
}
else
fa = FALSE;
}
- if (bfd_my_archive (ls->section->owner) != NULL)
+ if (ls->section->owner->my_archive != NULL)
{
- ln = bfd_get_filename (bfd_my_archive (ls->section->owner));
+ ln = bfd_get_filename (ls->section->owner->my_archive);
la = TRUE;
}
else
la = FALSE;
}
- i = strcmp (fn, ln);
+ i = filename_cmp (fn, ln);
if (i > 0)
continue;
else if (i < 0)
if (la)
ln = ls->section->owner->filename;
- i = strcmp (fn, ln);
+ i = filename_cmp (fn, ln);
if (i > 0)
continue;
else if (i < 0)
/* Here either the files are not sorted by name, or we are
looking at the sections for this file. */
- if (sec != NULL && sec->spec.sorted != none)
+ if (sec != NULL
+ && sec->spec.sorted != none
+ && sec->spec.sorted != by_none)
if (compare_section (sec->spec.sorted, section, ls->section) < 0)
break;
}
output_section_callback (lang_wild_statement_type *ptr,
struct wildcard_list *sec,
asection *section,
+ struct flag_info *sflag_info,
lang_input_statement_type *file,
void *output)
{
lang_statement_union_type *before;
+ lang_output_section_statement_type *os;
+
+ os = (lang_output_section_statement_type *) output;
/* Exclude sections that match UNIQUE_SECTION_LIST. */
- if (unique_section_p (section))
+ if (unique_section_p (section, os))
return;
before = wild_sort (ptr, sec, file, section);
of the current list. */
if (before == NULL)
- lang_add_section (&ptr->children, section,
- (lang_output_section_statement_type *) output);
+ lang_add_section (&ptr->children, section, sflag_info, os);
else
{
lang_statement_list_type list;
lang_statement_union_type **pp;
lang_list_init (&list);
- lang_add_section (&list, section,
- (lang_output_section_statement_type *) output);
+ lang_add_section (&list, section, sflag_info, os);
/* If we are discarding the section, LIST.HEAD will
be NULL. */
check_section_callback (lang_wild_statement_type *ptr ATTRIBUTE_UNUSED,
struct wildcard_list *sec ATTRIBUTE_UNUSED,
asection *section,
+ struct flag_info *sflag_info ATTRIBUTE_UNUSED,
lang_input_statement_type *file ATTRIBUTE_UNUSED,
- void *data)
+ void *output)
{
+ lang_output_section_statement_type *os;
+
+ os = (lang_output_section_statement_type *) output;
+
/* Exclude sections that match UNIQUE_SECTION_LIST. */
- if (unique_section_p (section))
+ if (unique_section_p (section, os))
return;
if (section->output_section == NULL && (section->flags & SEC_READONLY) == 0)
- ((lang_output_section_statement_type *) data)->all_input_readonly = FALSE;
+ os->all_input_readonly = FALSE;
}
/* This is passed a file name which must have been seen already and
const char *filename = search->local_sym_name;
if (filename != NULL
- && strcmp (filename, name) == 0)
+ && filename_cmp (filename, name) == 0)
break;
}
/* If we have already added this file, or this file is not real
don't add this file. */
- if (search->loaded || !search->real)
+ if (search->flags.loaded || !search->flags.real)
return search;
- if (! load_symbols (search, NULL))
+ if (!load_symbols (search, NULL))
return NULL;
return search;
end = strpbrk (p, ",:");
if (end == NULL)
end = p + strlen (p);
- entry = xmalloc (sizeof (*entry));
+ entry = (struct excluded_lib *) xmalloc (sizeof (*entry));
entry->next = excluded_libs;
- entry->name = xmalloc (end - p + 1);
+ entry->name = (char *) xmalloc (end - p + 1);
memcpy (entry->name, p, end - p);
entry->name[end - p] = '\0';
excluded_libs = entry;
return;
}
- if (strncmp (lib->name, filename, len) == 0
+ if (filename_ncmp (lib->name, filename, len) == 0
&& (filename[len] == '\0'
|| (filename[len] == '.' && filename[len + 1] == 'a'
&& filename[len + 2] == '\0')))
{
char **matching;
- if (entry->loaded)
+ if (entry->flags.loaded)
return TRUE;
ldfile_open_file (entry);
- if (! bfd_check_format (entry->the_bfd, bfd_archive)
- && ! bfd_check_format_matches (entry->the_bfd, bfd_object, &matching))
+ /* Do not process further if the file was missing. */
+ if (entry->flags.missing_file)
+ return TRUE;
+
+ if (!bfd_check_format (entry->the_bfd, bfd_archive)
+ && !bfd_check_format_matches (entry->the_bfd, bfd_object, &matching))
{
bfd_error_type err;
- lang_statement_list_type *hold;
- bfd_boolean bad_load = TRUE;
- bfd_boolean save_ldlang_sysrooted_script;
- bfd_boolean save_as_needed, save_add_needed;
+ struct lang_input_statement_flags save_flags;
+ extern FILE *yyin;
err = bfd_get_error ();
else if (err != bfd_error_file_not_recognized
|| place == NULL)
einfo (_("%F%B: file not recognized: %E\n"), entry->the_bfd);
- else
- bad_load = FALSE;
bfd_close (entry->the_bfd);
entry->the_bfd = NULL;
/* Try to interpret the file as a linker script. */
+ save_flags = input_flags;
ldfile_open_command_file (entry->filename);
- hold = stat_ptr;
- stat_ptr = place;
- save_ldlang_sysrooted_script = ldlang_sysrooted_script;
- ldlang_sysrooted_script = entry->sysrooted;
- save_as_needed = as_needed;
- as_needed = entry->as_needed;
- save_add_needed = add_needed;
- add_needed = entry->add_needed;
+ push_stat_ptr (place);
+ input_flags.add_DT_NEEDED_for_regular
+ = entry->flags.add_DT_NEEDED_for_regular;
+ input_flags.add_DT_NEEDED_for_dynamic
+ = entry->flags.add_DT_NEEDED_for_dynamic;
+ input_flags.whole_archive = entry->flags.whole_archive;
+ input_flags.dynamic = entry->flags.dynamic;
ldfile_assumed_script = TRUE;
parser_input = input_script;
- /* We want to use the same -Bdynamic/-Bstatic as the one for
- ENTRY. */
- config.dynamic_link = entry->dynamic;
yyparse ();
ldfile_assumed_script = FALSE;
- ldlang_sysrooted_script = save_ldlang_sysrooted_script;
- as_needed = save_as_needed;
- add_needed = save_add_needed;
- stat_ptr = hold;
+ /* missing_file is sticky. sysrooted will already have been
+ restored when seeing EOF in yyparse, but no harm to restore
+ again. */
+ save_flags.missing_file |= input_flags.missing_file;
+ input_flags = save_flags;
+ pop_stat_ptr ();
+ fclose (yyin);
+ yyin = NULL;
+ entry->flags.loaded = TRUE;
- return ! bad_load;
+ return TRUE;
}
if (ldemul_recognized_file (entry))
break;
case bfd_object:
- ldlang_add_file (entry);
- if (trace_files || trace_file_tries)
+ if (!entry->flags.reload)
+ ldlang_add_file (entry);
+ if (trace_files || verbose)
info_msg ("%I\n", entry);
break;
case bfd_archive:
check_excluded_libs (entry->the_bfd);
- if (entry->whole_archive)
+ if (entry->flags.whole_archive)
{
bfd *member = NULL;
bfd_boolean loaded = TRUE;
for (;;)
{
+ bfd *subsbfd;
member = bfd_openr_next_archived_file (entry->the_bfd, member);
if (member == NULL)
break;
- if (! bfd_check_format (member, bfd_object))
+ if (!bfd_check_format (member, bfd_object))
{
einfo (_("%F%B: member %B in archive is not an object\n"),
entry->the_bfd, member);
loaded = FALSE;
}
- if (! ((*link_info.callbacks->add_archive_element)
- (&link_info, member, "--whole-archive")))
+ subsbfd = member;
+ if (!(*link_info.callbacks
+ ->add_archive_element) (&link_info, member,
+ "--whole-archive", &subsbfd))
abort ();
- if (! bfd_link_add_symbols (member, &link_info))
+ /* Potentially, the add_archive_element hook may have set a
+ substitute BFD for us. */
+ if (!bfd_link_add_symbols (subsbfd, &link_info))
{
- einfo (_("%F%B: could not read symbols: %E\n"), member);
+ einfo (_("%F%B: error adding symbols: %E\n"), member);
loaded = FALSE;
}
}
- entry->loaded = loaded;
+ entry->flags.loaded = loaded;
return loaded;
}
break;
}
if (bfd_link_add_symbols (entry->the_bfd, &link_info))
- entry->loaded = TRUE;
+ entry->flags.loaded = TRUE;
else
- einfo (_("%F%B: could not read symbols: %E\n"), entry->the_bfd);
+ einfo (_("%F%B: error adding symbols: %E\n"), entry->the_bfd);
- return entry->loaded;
+ return entry->flags.loaded;
}
/* Handle a wild statement. S->FILENAME or S->SECTION_LIST or both
static int
get_target (const bfd_target *target, void *data)
{
- const char *sought = data;
+ const char *sought = (const char *) data;
return strcmp (target->name, sought) == 0;
}
char *copy2;
int result;
- copy1 = xmalloc (strlen (first) + 1);
- copy2 = xmalloc (strlen (second) + 1);
+ copy1 = (char *) xmalloc (strlen (first) + 1);
+ copy2 = (char *) xmalloc (strlen (second) + 1);
/* Convert the names to lower case. */
stricpy (copy1, first);
static int
closest_target_match (const bfd_target *target, void *data)
{
- const bfd_target *original = data;
+ const bfd_target *original = (const bfd_target *) data;
if (command_line.endian == ENDIAN_BIG
&& target->byteorder != BFD_ENDIAN_BIG)
LANG_FOR_EACH_INPUT_STATEMENT (s)
{
if (s->header.type == lang_input_statement_enum
- && s->real)
+ && s->flags.real)
{
ldfile_open_file (s);
/* No - has the current target been set to something other than
the default? */
- if (current_target != default_target)
+ if (current_target != default_target && current_target != NULL)
return current_target;
/* No - can we determine the format of the first input file? */
delete_output_file_on_failure = TRUE;
- if (! bfd_set_format (link_info.output_bfd, 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 (link_info.output_bfd,
+ 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);
ASSERT (link_info.output_bfd == NULL);
open_output (statement->output_statement.name);
ldemul_set_output_arch ();
- if (config.magic_demand_paged && !link_info.relocatable)
+ if (config.magic_demand_paged
+ && !bfd_link_relocatable (&link_info))
link_info.output_bfd->flags |= D_PAGED;
else
link_info.output_bfd->flags &= ~D_PAGED;
}
}
-/* Convert between addresses in bytes and sizes in octets.
- For currently supported targets, octets_per_byte is always a power
- of two, so we can use shifts. */
-#define TO_ADDR(X) ((X) >> opb_shift)
-#define TO_SIZE(X) ((X) << opb_shift)
-
-/* Support the above. */
-static unsigned int opb_shift = 0;
-
static void
init_opb (void)
{
/* Open all the input files. */
+enum open_bfd_mode
+ {
+ OPEN_BFD_NORMAL = 0,
+ OPEN_BFD_FORCE = 1,
+ OPEN_BFD_RESCAN = 2
+ };
+#ifdef ENABLE_PLUGINS
+static lang_input_statement_type *plugin_insert = NULL;
+#endif
+
static void
-open_input_bfds (lang_statement_union_type *s, bfd_boolean force)
+open_input_bfds (lang_statement_union_type *s, enum open_bfd_mode mode)
{
for (; s != NULL; s = s->header.next)
{
switch (s->header.type)
{
case lang_constructors_statement_enum:
- open_input_bfds (constructor_list.head, force);
+ open_input_bfds (constructor_list.head, mode);
break;
case lang_output_section_statement_enum:
- open_input_bfds (s->output_section_statement.children.head, force);
+ open_input_bfds (s->output_section_statement.children.head, mode);
break;
case lang_wild_statement_enum:
/* Maybe we should load the file's symbols. */
- if (s->wild_statement.filename
+ if ((mode & OPEN_BFD_RESCAN) == 0
+ && s->wild_statement.filename
&& !wildcardp (s->wild_statement.filename)
&& !archive_path (s->wild_statement.filename))
lookup_name (s->wild_statement.filename);
- open_input_bfds (s->wild_statement.children.head, force);
+ open_input_bfds (s->wild_statement.children.head, mode);
break;
case lang_group_statement_enum:
{
do
{
undefs = link_info.hash->undefs_tail;
- open_input_bfds (s->group_statement.children.head, TRUE);
+ open_input_bfds (s->group_statement.children.head,
+ mode | OPEN_BFD_FORCE);
}
while (undefs != link_info.hash->undefs_tail);
}
current_target = s->target_statement.target;
break;
case lang_input_statement_enum:
- if (s->input_statement.real)
+ if (s->input_statement.flags.real)
{
+ lang_statement_union_type **os_tail;
lang_statement_list_type add;
+ bfd *abfd;
s->input_statement.target = current_target;
/* If we are being called from within a group, and this
is an archive which has already been searched, then
force it to be researched unless the whole archive
- has been loaded already. */
- if (force
- && !s->input_statement.whole_archive
- && s->input_statement.loaded
- && bfd_check_format (s->input_statement.the_bfd,
- bfd_archive))
- s->input_statement.loaded = FALSE;
+ has been loaded already. Do the same for a rescan.
+ Likewise reload --as-needed shared libs. */
+ if (mode != OPEN_BFD_NORMAL
+#ifdef ENABLE_PLUGINS
+ && ((mode & OPEN_BFD_RESCAN) == 0
+ || plugin_insert == NULL)
+#endif
+ && s->input_statement.flags.loaded
+ && (abfd = s->input_statement.the_bfd) != NULL
+ && ((bfd_get_format (abfd) == bfd_archive
+ && !s->input_statement.flags.whole_archive)
+ || (bfd_get_format (abfd) == bfd_object
+ && ((abfd->flags) & DYNAMIC) != 0
+ && s->input_statement.flags.add_DT_NEEDED_for_regular
+ && bfd_get_flavour (abfd) == bfd_target_elf_flavour
+ && (elf_dyn_lib_class (abfd) & DYN_AS_NEEDED) != 0)))
+ {
+ s->input_statement.flags.loaded = FALSE;
+ s->input_statement.flags.reload = TRUE;
+ }
+ os_tail = lang_output_section_statement.tail;
lang_list_init (&add);
- if (! load_symbols (&s->input_statement, &add))
+ if (!load_symbols (&s->input_statement, &add))
config.make_executable = FALSE;
if (add.head != NULL)
{
- *add.tail = s->header.next;
- s->header.next = add.head;
+ /* If this was a script with output sections then
+ tack any added statements on to the end of the
+ list. This avoids having to reorder the output
+ section statement list. Very likely the user
+ forgot -T, and whatever we do here will not meet
+ naive user expectations. */
+ if (os_tail != lang_output_section_statement.tail)
+ {
+ einfo (_("%P: warning: %s contains output sections;"
+ " did you forget -T?\n"),
+ s->input_statement.filename);
+ *stat_ptr->tail = add.head;
+ stat_ptr->tail = add.tail;
+ }
+ else
+ {
+ *add.tail = s->header.next;
+ s->header.next = add.head;
+ }
}
}
+#ifdef ENABLE_PLUGINS
+ /* If we have found the point at which a plugin added new
+ files, clear plugin_insert to enable archive rescan. */
+ if (&s->input_statement == plugin_insert)
+ plugin_insert = NULL;
+#endif
+ break;
+ case lang_assignment_statement_enum:
+ if (s->assignment_statement.exp->assign.defsym)
+ /* This is from a --defsym on the command line. */
+ exp_fold_tree_no_dot (s->assignment_statement.exp);
break;
default:
break;
}
}
-}
-
-/* Add a symbol to a hash of symbols used in DEFINED (NAME) expressions. */
-
-void
-lang_track_definedness (const char *name)
-{
- if (bfd_hash_lookup (&lang_definedness_table, name, TRUE, FALSE) == NULL)
- einfo (_("%P%F: bfd_hash_lookup failed creating symbol %s\n"), name);
-}
-
-/* New-function for the definedness hash table. */
-
-static struct bfd_hash_entry *
-lang_definedness_newfunc (struct bfd_hash_entry *entry,
- struct bfd_hash_table *table ATTRIBUTE_UNUSED,
- const char *name ATTRIBUTE_UNUSED)
-{
- struct lang_definedness_hash_entry *ret
- = (struct lang_definedness_hash_entry *) entry;
-
- if (ret == NULL)
- ret = (struct lang_definedness_hash_entry *)
- bfd_hash_allocate (table, sizeof (struct lang_definedness_hash_entry));
-
- if (ret == NULL)
- einfo (_("%P%F: bfd_hash_allocate failed creating symbol %s\n"), name);
-
- ret->iteration = -1;
- return &ret->root;
-}
-
-/* Return the iteration when the definition of NAME was last updated. A
- value of -1 means that the symbol is not defined in the linker script
- or the command line, but may be defined in the linker symbol table. */
-
-int
-lang_symbol_definition_iteration (const char *name)
-{
- struct lang_definedness_hash_entry *defentry
- = (struct lang_definedness_hash_entry *)
- bfd_hash_lookup (&lang_definedness_table, name, FALSE, FALSE);
-
- /* We've already created this one on the presence of DEFINED in the
- script, so it can't be NULL unless something is borked elsewhere in
- the code. */
- if (defentry == NULL)
- FAIL ();
-
- return defentry->iteration;
-}
-
-/* Update the definedness state of NAME. */
-
-void
-lang_update_definedness (const char *name, struct bfd_link_hash_entry *h)
-{
- struct lang_definedness_hash_entry *defentry
- = (struct lang_definedness_hash_entry *)
- bfd_hash_lookup (&lang_definedness_table, name, FALSE, FALSE);
-
- /* We don't keep track of symbols not tested with DEFINED. */
- if (defentry == NULL)
- return;
-
- /* If the symbol was already defined, and not from an earlier statement
- iteration, don't update the definedness iteration, because that'd
- make the symbol seem defined in the linker script at this point, and
- it wasn't; it was defined in some object. If we do anyway, DEFINED
- would start to yield false before this point and the construct "sym =
- DEFINED (sym) ? sym : X;" would change sym to X despite being defined
- in an object. */
- if (h->type != bfd_link_hash_undefined
- && h->type != bfd_link_hash_common
- && h->type != bfd_link_hash_new
- && defentry->iteration == -1)
- return;
-
- defentry->iteration = lang_statement_iteration;
+
+ /* Exit if any of the files were missing. */
+ if (input_flags.missing_file)
+ einfo ("%F");
}
/* Add the supplied name to the symbol table as an undefined reference.
#define ldlang_undef_chain_list_head entry_symbol.next
void
-ldlang_add_undef (const char *const name)
+ldlang_add_undef (const char *const name, bfd_boolean cmdline)
{
- ldlang_undef_chain_list_type *new =
- stat_alloc (sizeof (ldlang_undef_chain_list_type));
+ ldlang_undef_chain_list_type *new_undef;
- new->next = ldlang_undef_chain_list_head;
- ldlang_undef_chain_list_head = new;
+ undef_from_cmdline = undef_from_cmdline || cmdline;
+ new_undef = (ldlang_undef_chain_list_type *) stat_alloc (sizeof (*new_undef));
+ new_undef->next = ldlang_undef_chain_list_head;
+ ldlang_undef_chain_list_head = new_undef;
- new->name = xstrdup (name);
+ new_undef->name = xstrdup (name);
if (link_info.output_bfd != NULL)
- insert_undefined (new->name);
+ insert_undefined (new_undef->name);
}
/* Insert NAME as undefined in the symbol table. */
insert_undefined (ptr->name);
}
+/* Structure used to build the list of symbols that the user has required
+ be defined. */
+
+struct require_defined_symbol
+{
+ const char *name;
+ struct require_defined_symbol *next;
+};
+
+/* The list of symbols that the user has required be defined. */
+
+static struct require_defined_symbol *require_defined_symbol_list;
+
+/* Add a new symbol NAME to the list of symbols that are required to be
+ defined. */
+
+void
+ldlang_add_require_defined (const char *const name)
+{
+ struct require_defined_symbol *ptr;
+
+ ldlang_add_undef (name, TRUE);
+ ptr = (struct require_defined_symbol *) stat_alloc (sizeof (*ptr));
+ ptr->next = require_defined_symbol_list;
+ ptr->name = strdup (name);
+ require_defined_symbol_list = ptr;
+}
+
+/* Check that all symbols the user required to be defined, are defined,
+ raise an error if we find a symbol that is not defined. */
+
+static void
+ldlang_check_require_defined_symbols (void)
+{
+ struct require_defined_symbol *ptr;
+
+ for (ptr = require_defined_symbol_list; ptr != NULL; ptr = ptr->next)
+ {
+ struct bfd_link_hash_entry *h;
+
+ h = bfd_link_hash_lookup (link_info.hash, ptr->name,
+ FALSE, FALSE, TRUE);
+ if (h == NULL
+ || (h->type != bfd_link_hash_defined
+ && h->type != bfd_link_hash_defweak))
+ einfo(_("%P%X: required symbol `%s' not defined\n"), ptr->name);
+ }
+}
+
/* Check for all readonly or some readwrite sections. */
static void
case lang_wild_statement_enum:
walk_wild (&s->wild_statement, check_section_callback,
output_section_statement);
- if (! output_section_statement->all_input_readonly)
+ if (!output_section_statement->all_input_readonly)
return;
break;
case lang_constructors_statement_enum:
check_input_sections (constructor_list.head,
output_section_statement);
- if (! output_section_statement->all_input_readonly)
+ if (!output_section_statement->all_input_readonly)
return;
break;
case lang_group_statement_enum:
check_input_sections (s->group_statement.children.head,
output_section_statement);
- if (! output_section_statement->all_input_readonly)
+ if (!output_section_statement->all_input_readonly)
return;
break;
default:
break;
case lang_wild_statement_enum:
- sec = s->wild_statement.section_list;
for (sec = s->wild_statement.section_list; sec != NULL;
sec = sec->next)
{
break;
case lang_output_section_statement_enum:
- update_wild_statements
- (s->output_section_statement.children.head);
+ /* Don't sort .init/.fini sections. */
+ if (strcmp (s->output_section_statement.name, ".init") != 0
+ && strcmp (s->output_section_statement.name, ".fini") != 0)
+ update_wild_statements
+ (s->output_section_statement.children.head);
break;
case lang_group_statement_enum:
(lang_statement_union_type *s, const char *target,
lang_output_section_statement_type *os)
{
- flagword flags;
-
for (; s != NULL; s = s->header.next)
{
+ lang_output_section_statement_type *tos;
+ flagword flags;
+
switch (s->header.type)
{
case lang_wild_statement_enum:
os);
break;
case lang_output_section_statement_enum:
- if (s->output_section_statement.constraint)
+ tos = &s->output_section_statement;
+ if (tos->constraint != 0)
{
- if (s->output_section_statement.constraint != ONLY_IF_RW
- && s->output_section_statement.constraint != ONLY_IF_RO)
+ if (tos->constraint != ONLY_IF_RW
+ && tos->constraint != ONLY_IF_RO)
break;
- s->output_section_statement.all_input_readonly = TRUE;
- check_input_sections (s->output_section_statement.children.head,
- &s->output_section_statement);
- if ((s->output_section_statement.all_input_readonly
- && s->output_section_statement.constraint == ONLY_IF_RW)
- || (!s->output_section_statement.all_input_readonly
- && s->output_section_statement.constraint == ONLY_IF_RO))
+ tos->all_input_readonly = TRUE;
+ check_input_sections (tos->children.head, tos);
+ if (tos->all_input_readonly != (tos->constraint == ONLY_IF_RO))
{
- s->output_section_statement.constraint = -1;
+ tos->constraint = -1;
break;
}
}
-
- map_input_to_output_sections (s->output_section_statement.children.head,
+ map_input_to_output_sections (tos->children.head,
target,
- &s->output_section_statement);
+ tos);
break;
case lang_output_statement_enum:
break;
/* Make sure that any sections mentioned in the expression
are initialized. */
exp_init_os (s->data_statement.exp);
- flags = SEC_HAS_CONTENTS;
- /* The output section gets contents, and then we inspect for
- any flags set in the input script which override any ALLOC. */
- if (!(os->flags & SEC_NEVER_LOAD))
- flags |= SEC_ALLOC | SEC_LOAD;
+ /* The output section gets CONTENTS, ALLOC and LOAD, but
+ these may be overridden by the script. */
+ flags = SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD;
+ switch (os->sectype)
+ {
+ case normal_section:
+ case overlay_section:
+ break;
+ case noalloc_section:
+ flags = SEC_HAS_CONTENTS;
+ break;
+ case noload_section:
+ if (bfd_get_flavour (link_info.output_bfd)
+ == bfd_target_elf_flavour)
+ flags = SEC_NEVER_LOAD | SEC_ALLOC;
+ else
+ flags = SEC_NEVER_LOAD | SEC_HAS_CONTENTS;
+ break;
+ }
if (os->bfd_section == NULL)
- init_os (os, NULL, flags);
+ init_os (os, flags);
else
os->bfd_section->flags |= flags;
break;
case lang_padding_statement_enum:
case lang_input_statement_enum:
if (os != NULL && os->bfd_section == NULL)
- init_os (os, NULL, 0);
+ init_os (os, 0);
break;
case lang_assignment_statement_enum:
if (os != NULL && os->bfd_section == NULL)
- init_os (os, NULL, 0);
+ init_os (os, 0);
/* Make sure that any sections mentioned in the assignment
are initialized. */
if (!s->address_statement.segment
|| !s->address_statement.segment->used)
{
- lang_output_section_statement_type *aos
- = (lang_output_section_statement_lookup
- (s->address_statement.section_name));
-
- if (aos->bfd_section == NULL)
- init_os (aos, NULL, 0);
- aos->addr_tree = s->address_statement.address;
+ const char *name = s->address_statement.section_name;
+
+ /* Create the output section statement here so that
+ orphans with a set address will be placed after other
+ script sections. If we let the orphan placement code
+ place them in amongst other sections then the address
+ will affect following script sections, which is
+ likely to surprise naive users. */
+ tos = lang_output_section_statement_lookup (name, 0, TRUE);
+ tos->addr_tree = s->address_statement.address;
+ if (tos->bfd_section == NULL)
+ init_os (tos, 0);
}
break;
case lang_insert_statement_enum:
lang_statement_union_type **s;
lang_output_section_statement_type *first_os = NULL;
lang_output_section_statement_type *last_os = NULL;
+ lang_output_section_statement_type *os;
/* "start of list" is actually the statement immediately after
the special abs_section output statement, so that it isn't
{
/* 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;
+ os = &(*s)->output_section_statement;
+
+ ASSERT (last_os == NULL || last_os->next == os);
+ last_os = os;
+
+ /* Set constraint negative so that lang_output_section_find
+ won't match this output section statement. At this
+ stage in linking constraint has values in the range
+ [-1, ONLY_IN_RW]. */
+ last_os->constraint = -2 - last_os->constraint;
if (first_os == NULL)
first_os = last_os;
}
{
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;
{
do
where = where->prev;
- while (where != NULL && where->constraint == -1);
+ while (where != NULL && where->constraint < 0);
}
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;
+ einfo (_("%F%P: %s not found for insert\n"), i->where);
+ return;
}
/* Deal with reordering the output section statement list. */
last_sec = NULL;
for (os = first_os; os != NULL; os = os->next)
{
+ os->constraint = -2 - os->constraint;
if (os->bfd_section != NULL
&& os->bfd_section->owner != NULL)
{
s = &lang_output_section_statement.head;
}
}
+
+ /* Undo constraint twiddling. */
+ for (os = first_os; os != NULL; os = os->next)
+ {
+ os->constraint = -2 - os->constraint;
+ if (os == last_os)
+ break;
+ }
}
/* An output section might have been removed after its statement was
asection *output_section;
bfd_boolean exclude;
- if (os->constraint == -1)
+ if (os->constraint < 0)
continue;
output_section = os->bfd_section;
asection *s;
for (s = output_section->map_head.s; s != NULL; s = s->map_head.s)
- if ((s->flags & SEC_LINKER_CREATED) != 0
- && (s->flags & SEC_EXCLUDE) == 0)
+ if ((s->flags & SEC_EXCLUDE) == 0
+ && ((s->flags & SEC_LINKER_CREATED) != 0
+ || link_info.emitrelocations))
{
exclude = FALSE;
break;
}
}
- /* TODO: Don't just junk map_head.s, turn them into link_orders. */
- output_section->map_head.link_order = NULL;
- output_section->map_tail.link_order = NULL;
-
if (exclude)
{
/* 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
- && !os->update_dot_tree)
+ if (!os->update_dot)
os->ignored = TRUE;
output_section->flags |= SEC_EXCLUDE;
bfd_section_list_remove (link_info.output_bfd, output_section);
link_info.output_bfd->section_count--;
}
}
+}
+
+/* Called from ldwrite to clear out asection.map_head and
+ asection.map_tail for use as link_orders in ldwrite.
+ FIXME: Except for sh64elf.em which starts creating link_orders in
+ its after_allocation routine so needs to call it early. */
+
+void
+lang_clear_os_map (void)
+{
+ lang_output_section_statement_type *os;
+
+ if (map_head_is_link_order)
+ return;
+
+ for (os = &lang_output_section_statement.head->output_section_statement;
+ os != NULL;
+ os = os->next)
+ {
+ asection *output_section;
+
+ if (os->constraint < 0)
+ continue;
+
+ output_section = os->bfd_section;
+ if (output_section == NULL)
+ continue;
+
+ /* TODO: Don't just junk map_head.s, turn them into link_orders. */
+ output_section->map_head.link_order = NULL;
+ output_section->map_tail.link_order = NULL;
+ }
/* Stop future calls to lang_add_section from messing with map_head
and map_tail link_order fields. */
- stripped_excluded_sections = TRUE;
+ map_head_is_link_order = TRUE;
}
static void
++len;
}
- minfo ("0x%V %W", section->vma, section->size);
+ minfo ("0x%V %W", section->vma, TO_ADDR (section->size));
if (section->vma != section->lma)
minfo (_(" load address 0x%V"), section->lma);
+
+ if (output_section_statement->update_dot_tree != NULL)
+ exp_fold_tree (output_section_statement->update_dot_tree,
+ bfd_abs_section_ptr, &print_dot);
}
print_nl ();
output_section_statement);
}
-/* Scan for the use of the destination in the right hand side
- of an expression. In such cases we will not compute the
- correct expression, since the value of DST that is used on
- the right hand side will be its final value, not its value
- just before this expression is evaluated. */
-
-static bfd_boolean
-scan_for_self_assignment (const char * dst, etree_type * rhs)
-{
- if (rhs == NULL || dst == NULL)
- return FALSE;
-
- switch (rhs->type.node_class)
- {
- case etree_binary:
- return scan_for_self_assignment (dst, rhs->binary.lhs)
- || scan_for_self_assignment (dst, rhs->binary.rhs);
-
- case etree_trinary:
- return scan_for_self_assignment (dst, rhs->trinary.lhs)
- || scan_for_self_assignment (dst, rhs->trinary.rhs);
-
- case etree_assign:
- case etree_provided:
- case etree_provide:
- if (strcmp (dst, rhs->assign.dst) == 0)
- return TRUE;
- return scan_for_self_assignment (dst, rhs->assign.src);
-
- case etree_unary:
- return scan_for_self_assignment (dst, rhs->unary.child);
-
- case etree_value:
- if (rhs->value.str)
- return strcmp (dst, rhs->value.str) == 0;
- return FALSE;
-
- case etree_name:
- if (rhs->name.name)
- return strcmp (dst, rhs->name.name) == 0;
- return FALSE;
-
- default:
- break;
- }
-
- return FALSE;
-}
-
-
static void
print_assignment (lang_assignment_statement_type *assignment,
lang_output_section_statement_type *output_section)
{
unsigned int i;
bfd_boolean is_dot;
- bfd_boolean computation_is_valid = TRUE;
etree_type *tree;
+ asection *osec;
for (i = 0; i < SECTION_NAME_MAP_LENGTH; i++)
print_space ();
{
is_dot = FALSE;
tree = assignment->exp->assert_s.child;
- computation_is_valid = TRUE;
}
else
{
const char *dst = assignment->exp->assign.dst;
is_dot = (dst[0] == '.' && dst[1] == 0);
+ if (!is_dot)
+ expld.assign_name = dst;
tree = assignment->exp->assign.src;
- computation_is_valid = is_dot || (scan_for_self_assignment (dst, tree) == FALSE);
}
- exp_fold_tree (tree, output_section->bfd_section, &print_dot);
+ osec = output_section->bfd_section;
+ if (osec == NULL)
+ osec = bfd_abs_section_ptr;
+
+ if (assignment->exp->type.node_class != etree_provide)
+ exp_fold_tree (tree, osec, &print_dot);
+ else
+ expld.result.valid_p = FALSE;
+
if (expld.result.valid_p)
{
bfd_vma value;
- if (computation_is_valid)
+ if (assignment->exp->type.node_class == etree_assert
+ || is_dot
+ || expld.assign_name != NULL)
{
value = expld.result.value;
- if (expld.result.section)
+ if (expld.result.section != NULL)
value += expld.result.section->vma;
minfo ("0x%V", value);
if (h)
{
value = h->u.def.value;
-
- if (expld.result.section)
- value += expld.result.section->vma;
+ value += h->u.def.section->output_section->vma;
+ value += h->u.def.section->output_offset;
minfo ("[0x%V]", value);
}
}
else
{
- minfo ("*undef* ");
+ if (assignment->exp->type.node_class == etree_provide)
+ minfo ("[!provide]");
+ else
+ minfo ("*undef* ");
#ifdef BFD64
minfo (" ");
#endif
}
+ expld.assign_name = NULL;
minfo (" ");
exp_print_tree (assignment->exp);
static bfd_boolean
print_one_symbol (struct bfd_link_hash_entry *hash_entry, void *ptr)
{
- asection *sec = ptr;
+ asection *sec = (asection *) ptr;
if ((hash_entry->type == bfd_link_hash_defined
|| hash_entry->type == bfd_link_hash_defweak)
return TRUE;
}
+static int
+hash_entry_addr_cmp (const void *a, const void *b)
+{
+ const struct bfd_link_hash_entry *l = *(const struct bfd_link_hash_entry **)a;
+ const struct bfd_link_hash_entry *r = *(const struct bfd_link_hash_entry **)b;
+
+ if (l->u.def.value < r->u.def.value)
+ return -1;
+ else if (l->u.def.value > r->u.def.value)
+ return 1;
+ else
+ return 0;
+}
+
static void
print_all_symbols (asection *sec)
{
- struct fat_user_section_struct *ud = get_userdata (sec);
+ input_section_userdata_type *ud
+ = (input_section_userdata_type *) get_userdata (sec);
struct map_symbol_def *def;
+ struct bfd_link_hash_entry **entries;
+ unsigned int i;
if (!ud)
return;
*ud->map_symbol_def_tail = 0;
- for (def = ud->map_symbol_def_head; def; def = def->next)
- print_one_symbol (def->entry, sec);
+
+ /* Sort the symbols by address. */
+ entries = (struct bfd_link_hash_entry **)
+ obstack_alloc (&map_obstack,
+ ud->map_symbol_def_count * sizeof (*entries));
+
+ for (i = 0, def = ud->map_symbol_def_head; def; def = def->next, i++)
+ entries[i] = def->entry;
+
+ qsort (entries, ud->map_symbol_def_count, sizeof (*entries),
+ hash_entry_addr_cmp);
+
+ /* Print the symbols. */
+ for (i = 0; i < ud->map_symbol_def_count; i++)
+ print_one_symbol (entries[i], sec);
+
+ obstack_free (&map_obstack, entries);
}
/* Print information about an input section to the map file. */
static void
-print_input_section (asection *i)
+print_input_section (asection *i, bfd_boolean is_discarded)
{
bfd_size_type size = i->size;
int len;
else
{
addr = print_dot;
- size = 0;
+ if (!is_discarded)
+ size = 0;
}
- minfo ("0x%V %W %B\n", addr, TO_ADDR (size), i->owner);
+ minfo ("0x%V %W %B\n", addr, size, i->owner);
if (size != i->rawsize && i->rawsize != 0)
{
else
print_all_symbols (i);
- print_dot = addr + TO_ADDR (size);
+ /* Update print_dot, but make sure that we do not move it
+ backwards - this could happen if we have overlays and a
+ later overlay is shorter than an earier one. */
+ if (addr + TO_ADDR (size) > print_dot)
+ print_dot = addr + TO_ADDR (size);
}
}
break;
}
- minfo ("0x%V %W %s 0x%v", addr, size, name, data->value);
+ if (size < TO_SIZE ((unsigned) 1))
+ size = TO_SIZE ((unsigned) 1);
+ minfo ("0x%V %W %s 0x%v", addr, TO_ADDR (size), name, data->value);
if (data->exp->type.node_class != etree_value)
{
size = bfd_get_reloc_size (reloc->howto);
- minfo ("0x%V %W RELOC %s ", addr, size, reloc->howto->name);
+ minfo ("0x%V %W RELOC %s ", addr, TO_ADDR (size), reloc->howto->name);
if (reloc->name != NULL)
minfo ("%s+", reloc->name);
addr = s->output_offset;
if (s->output_section != NULL)
addr += s->output_section->vma;
- minfo ("0x%V %W ", addr, (bfd_vma) s->size);
+ minfo ("0x%V %W ", addr, TO_ADDR (s->size));
if (s->fill->size != 0)
{
print_reloc_statement (&s->reloc_statement);
break;
case lang_input_section_enum:
- print_input_section (s->input_section.section);
+ print_input_section (s->input_section.section, FALSE);
break;
case lang_padding_statement_enum:
print_padding_statement (&s->padding_statement);
static void
insert_pad (lang_statement_union_type **ptr,
fill_type *fill,
- unsigned int alignment_needed,
+ bfd_size_type alignment_needed,
asection *output_section,
bfd_vma dot)
{
- static fill_type zero_fill = { 1, { 0 } };
+ static fill_type zero_fill;
lang_statement_union_type *pad = NULL;
if (ptr != &statement_list.head)
else
{
/* Make a new padding statement, linked into existing chain. */
- pad = stat_alloc (sizeof (lang_padding_statement_type));
+ pad = (lang_statement_union_type *)
+ stat_alloc (sizeof (lang_padding_statement_type));
pad->header.next = *ptr;
*ptr = pad;
pad->header.type = lang_padding_statement_enum;
}
pad->padding_statement.output_offset = dot - output_section->vma;
pad->padding_statement.size = alignment_needed;
- output_section->size += alignment_needed;
+ output_section->size = TO_SIZE (dot + TO_ADDR (alignment_needed)
+ - output_section->vma);
}
/* Work out how much this section will move the dot point. */
{
lang_input_section_type *is = &((*this_ptr)->input_section);
asection *i = is->section;
+ asection *o = output_section_statement->bfd_section;
- if (!((lang_input_statement_type *) i->owner->usrdata)->just_syms_flag
- && (i->flags & SEC_EXCLUDE) == 0)
+ if (i->sec_info_type == SEC_INFO_TYPE_JUST_SYMS)
+ i->output_offset = i->vma - o->vma;
+ else if ((i->flags & SEC_EXCLUDE) != 0)
+ i->output_offset = dot - o->vma;
+ else
{
- unsigned int alignment_needed;
- asection *o;
+ bfd_size_type alignment_needed;
/* Align this section first to the input sections requirement,
then to the output section's requirement. If this alignment
if (output_section_statement->subsection_alignment != -1)
i->alignment_power = output_section_statement->subsection_alignment;
- o = output_section_statement->bfd_section;
if (o->alignment_power < i->alignment_power)
o->alignment_power = i->alignment_power;
}
/* Remember where in the output section this input section goes. */
-
i->output_offset = dot - o->vma;
/* Mark how big the output section must be to contain this now. */
dot += TO_ADDR (i->size);
o->size = TO_SIZE (dot - o->vma);
}
- else
- {
- i->output_offset = i->vma - output_section_statement->bfd_section->vma;
- }
return dot;
}
+struct check_sec
+{
+ asection *sec;
+ bfd_boolean warned;
+};
+
static int
sort_sections_by_lma (const void *arg1, const void *arg2)
{
- const asection *sec1 = *(const asection **) arg1;
- const asection *sec2 = *(const asection **) arg2;
+ const asection *sec1 = ((const struct check_sec *) arg1)->sec;
+ const asection *sec2 = ((const struct check_sec *) arg2)->sec;
+
+ if (sec1->lma < sec2->lma)
+ return -1;
+ else if (sec1->lma > sec2->lma)
+ return 1;
+ else if (sec1->id < sec2->id)
+ return -1;
+ else if (sec1->id > sec2->id)
+ return 1;
+
+ return 0;
+}
+
+static int
+sort_sections_by_vma (const void *arg1, const void *arg2)
+{
+ const asection *sec1 = ((const struct check_sec *) arg1)->sec;
+ const asection *sec2 = ((const struct check_sec *) arg2)->sec;
- if (bfd_section_lma (sec1->owner, sec1)
- < bfd_section_lma (sec2->owner, sec2))
+ if (sec1->vma < sec2->vma)
return -1;
- else if (bfd_section_lma (sec1->owner, sec1)
- > bfd_section_lma (sec2->owner, sec2))
+ else if (sec1->vma > sec2->vma)
return 1;
else if (sec1->id < sec2->id)
return -1;
return 0;
}
+#define IS_TBSS(s) \
+ ((s->flags & (SEC_LOAD | SEC_THREAD_LOCAL)) == SEC_THREAD_LOCAL)
+
#define IGNORE_SECTION(s) \
- ((s->flags & SEC_NEVER_LOAD) != 0 \
- || (s->flags & SEC_ALLOC) == 0 \
- || ((s->flags & SEC_THREAD_LOCAL) != 0 \
- && (s->flags & SEC_LOAD) == 0))
+ ((s->flags & SEC_ALLOC) == 0 || IS_TBSS (s))
/* Check to see if any allocated sections overlap with other allocated
sections. This can happen if a linker script specifies the output
static void
lang_check_section_addresses (void)
{
- asection *s, *os;
- asection **sections, **spp;
- unsigned int count;
+ asection *s, *p;
+ struct check_sec *sections;
+ size_t i, count;
bfd_vma s_start;
bfd_vma s_end;
- bfd_vma os_start;
- bfd_vma os_end;
- bfd_size_type amt;
+ bfd_vma p_start = 0;
+ bfd_vma p_end = 0;
lang_memory_region_type *m;
+ bfd_boolean overlays;
if (bfd_count_sections (link_info.output_bfd) <= 1)
return;
- amt = bfd_count_sections (link_info.output_bfd) * sizeof (asection *);
- sections = xmalloc (amt);
+ count = bfd_count_sections (link_info.output_bfd);
+ sections = XNEWVEC (struct check_sec, count);
/* Scan all sections in the output list. */
count = 0;
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)
+ if (IGNORE_SECTION (s)
+ || s->size == 0)
continue;
- sections[count] = s;
+ sections[count].sec = s;
+ sections[count].warned = FALSE;
count++;
}
if (count <= 1)
- return;
+ {
+ free (sections);
+ return;
+ }
- qsort (sections, (size_t) count, sizeof (asection *),
- sort_sections_by_lma);
+ qsort (sections, count, sizeof (*sections), sort_sections_by_lma);
- spp = sections;
- s = *spp++;
- s_start = bfd_section_lma (link_info.output_bfd, s);
- s_end = s_start + TO_ADDR (s->size) - 1;
- for (count--; count; count--)
+ /* First check section LMAs. There should be no overlap of LMAs on
+ loadable sections, even with overlays. */
+ for (p = NULL, i = 0; i < count; i++)
{
- /* We must check the sections' LMA addresses not their VMA
- addresses because overlay sections can have overlapping VMAs
- but they must have distinct LMAs. */
- os = s;
- os_start = s_start;
- os_end = s_end;
- s = *spp++;
- s_start = bfd_section_lma (link_info.output_bfd, s);
- s_end = s_start + TO_ADDR (s->size) - 1;
+ s = sections[i].sec;
+ if ((s->flags & SEC_LOAD) != 0)
+ {
+ s_start = s->lma;
+ s_end = s_start + TO_ADDR (s->size) - 1;
+
+ /* Look for an overlap. We have sorted sections by lma, so
+ we know that s_start >= p_start. Besides the obvious
+ case of overlap when the current section starts before
+ the previous one ends, we also must have overlap if the
+ previous section wraps around the address space. */
+ if (p != NULL
+ && (s_start <= p_end
+ || p_end < p_start))
+ {
+ einfo (_("%X%P: section %s LMA [%V,%V]"
+ " overlaps section %s LMA [%V,%V]\n"),
+ s->name, s_start, s_end, p->name, p_start, p_end);
+ sections[i].warned = TRUE;
+ }
+ p = s;
+ p_start = s_start;
+ p_end = s_end;
+ }
+ }
+
+ /* If any non-zero size allocated section (excluding tbss) starts at
+ exactly the same VMA as another such section, then we have
+ overlays. Overlays generated by the OVERLAY keyword will have
+ this property. It is possible to intentionally generate overlays
+ that fail this test, but it would be unusual. */
+ qsort (sections, count, sizeof (*sections), sort_sections_by_vma);
+ overlays = FALSE;
+ p_start = sections[0].sec->vma;
+ for (i = 1; i < count; i++)
+ {
+ s_start = sections[i].sec->vma;
+ if (p_start == s_start)
+ {
+ overlays = TRUE;
+ break;
+ }
+ p_start = s_start;
+ }
- /* Look for an overlap. */
- if (s_end >= os_start && s_start <= os_end)
- einfo (_("%X%P: section %s [%V -> %V] overlaps section %s [%V -> %V]\n"),
- s->name, s_start, s_end, os->name, os_start, os_end);
+ /* Now check section VMAs if no overlays were detected. */
+ if (!overlays)
+ {
+ for (p = NULL, i = 0; i < count; i++)
+ {
+ s = sections[i].sec;
+ s_start = s->vma;
+ s_end = s_start + TO_ADDR (s->size) - 1;
+
+ if (p != NULL
+ && !sections[i].warned
+ && (s_start <= p_end
+ || p_end < p_start))
+ einfo (_("%X%P: section %s VMA [%V,%V]"
+ " overlaps section %s VMA [%V,%V]\n"),
+ s->name, s_start, s_end, p->name, p_start, p_end);
+ p = s;
+ p_start = s_start;
+ p_end = s_end;
+ }
}
free (sections);
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)));
-
+ einfo (_("%X%P: region `%s' overflowed by %ld bytes\n"),
+ m->name_list.name, (long)(m->current - (m->origin + m->length)));
}
/* Make sure the new address is within the region. We explicitly permit the
os_region_check (lang_output_section_statement_type *os,
lang_memory_region_type *region,
etree_type *tree,
- bfd_vma base)
+ bfd_vma rbase)
{
if ((region->current < region->origin
|| (region->current - region->origin > region->length))
&& ((region->current != region->origin + region->length)
- || base == 0))
+ || rbase == 0))
{
if (tree != NULL)
{
- einfo (_("%X%P: address 0x%v of %B section %s"
- " is not within region %s\n"),
+ einfo (_("%X%P: address 0x%v of %B section `%s'"
+ " is not within region `%s'\n"),
region->current,
os->bfd_section->owner,
os->bfd_section->name,
- region->name);
+ region->name_list.name);
}
else if (!region->had_full_message)
{
region->had_full_message = TRUE;
- einfo (_("%X%P: %B section %s will not fit in region %s\n"),
+ einfo (_("%X%P: %B section `%s' will not fit in region `%s'\n"),
os->bfd_section->owner,
os->bfd_section->name,
- region->name);
+ region->name_list.name);
}
}
}
static bfd_vma
lang_size_sections_1
- (lang_statement_union_type *s,
+ (lang_statement_union_type **prev,
lang_output_section_statement_type *output_section_statement,
- lang_statement_union_type **prev,
fill_type *fill,
bfd_vma dot,
bfd_boolean *relax,
bfd_boolean check_regions)
{
+ lang_statement_union_type *s;
+
/* Size up the sections from their constituent parts. */
- for (; s != NULL; s = s->header.next)
+ for (s = *prev; s != NULL; s = s->header.next)
{
switch (s->header.type)
{
case lang_output_section_statement_enum:
{
- bfd_vma newdot, after;
+ bfd_vma newdot, after, dotdelta;
lang_output_section_statement_type *os;
lang_memory_region_type *r;
+ int section_alignment = 0;
os = &s->output_section_statement;
+ if (os->constraint == -1)
+ break;
+
+ /* FIXME: We shouldn't need to zero section vmas for ld -r
+ here, in lang_insert_orphan, or in the default linker scripts.
+ This is covering for coff backend linker bugs. See PR6945. */
+ if (os->addr_tree == NULL
+ && bfd_link_relocatable (&link_info)
+ && (bfd_get_flavour (link_info.output_bfd)
+ == bfd_target_coff_flavour))
+ os->addr_tree = exp_intop (0);
if (os->addr_tree != NULL)
{
os->processed_vma = FALSE;
exp_fold_tree (os->addr_tree, bfd_abs_section_ptr, &dot);
if (expld.result.valid_p)
- dot = expld.result.value + expld.result.section->vma;
+ {
+ dot = expld.result.value;
+ if (expld.result.section != NULL)
+ dot += 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);
+ os->addr_tree, os->name);
}
if (os->bfd_section == NULL)
}
newdot = dot;
+ dotdelta = 0;
if (bfd_is_abs_section (os->bfd_section))
{
/* No matter what happens, an abs section starts at zero. */
}
else
{
- int align;
-
if (os->addr_tree == NULL)
{
/* No address specified for this section, get one
from the region specification. */
if (os->region == NULL
|| ((os->bfd_section->flags & (SEC_ALLOC | SEC_LOAD))
- && os->region->name[0] == '*'
- && strcmp (os->region->name,
+ && os->region->name_list.name[0] == '*'
+ && strcmp (os->region->name_list.name,
DEFAULT_MEMORY_REGION) == 0))
{
os->region = lang_memory_default (os->bfd_section);
defined, issue an error message. */
if (!os->ignored
&& !IGNORE_SECTION (os->bfd_section)
- && ! link_info.relocatable
+ && !bfd_link_relocatable (&link_info)
&& check_regions
- && strcmp (os->region->name,
+ && strcmp (os->region->name_list.name,
DEFAULT_MEMORY_REGION) == 0
&& lang_memory_region_list != NULL
- && (strcmp (lang_memory_region_list->name,
+ && (strcmp (lang_memory_region_list->name_list.name,
DEFAULT_MEMORY_REGION) != 0
|| lang_memory_region_list->next != NULL)
&& expld.phase != lang_mark_phase_enum)
}
newdot = os->region->current;
- align = os->bfd_section->alignment_power;
+ section_alignment = os->bfd_section->alignment_power;
}
else
- align = os->section_alignment;
+ section_alignment = os->section_alignment;
/* Align to what the section needs. */
- if (align > 0)
+ if (section_alignment > 0)
{
bfd_vma savedot = newdot;
- newdot = align_power (newdot, align);
+ newdot = align_power (newdot, section_alignment);
- if (newdot != savedot
+ dotdelta = newdot - savedot;
+ if (dotdelta != 0
&& (config.warn_section_align
|| os->addr_tree != NULL)
&& expld.phase != lang_mark_phase_enum)
einfo (_("%P: warning: changing start of section"
" %s by %lu bytes\n"),
- os->name, (unsigned long) (newdot - savedot));
+ os->name, (unsigned long) dotdelta);
}
bfd_set_section_vma (0, os->bfd_section, newdot);
os->bfd_section->output_offset = 0;
}
- lang_size_sections_1 (os->children.head, os, &os->children.head,
+ lang_size_sections_1 (&os->children.head, os,
os->fill, newdot, relax, check_regions);
os->processed_vma = TRUE;
{
bfd_vma lma = os->lma_region->current;
- if (os->section_alignment != -1)
- lma = align_power (lma, os->section_alignment);
+ if (os->align_lma_with_input)
+ lma += dotdelta;
+ else
+ {
+ /* When LMA_REGION is the same as REGION, align the LMA
+ as we did for the VMA, possibly including alignment
+ from the bfd section. If a different region, then
+ only align according to the value in the output
+ statement. */
+ if (os->lma_region != os->region)
+ section_alignment = os->section_alignment;
+ if (section_alignment > 0)
+ lma = align_power (lma, section_alignment);
+ }
os->bfd_section->lma = lma;
}
else if (r->last_os != NULL
create overlapping LMAs. */
if (dot < last->vma
&& os->bfd_section->size != 0
- && dot + os->bfd_section->size <= last->vma)
+ && dot + TO_ADDR (os->bfd_section->size) <= last->vma)
{
/* If dot moved backwards then leave lma equal to
vma. This is the old default lma, which might
so people can fix their linker scripts. */
if (last->vma != last->lma)
- einfo (_("%P: warning: dot moved backwards before `%s'\n"),
- os->name);
+ einfo (_("%P: warning: dot moved backwards "
+ "before `%s'\n"), os->name);
}
else
{
/* If this is an overlay, set the current lma to that
at the end of the previous section. */
if (os->sectype == overlay_section)
- lma = last->lma + last->size;
+ lma = last->lma + TO_ADDR (last->size);
/* Otherwise, keep the same lma to vma relationship
as the previous section. */
else
lma = dot + last->lma - last->vma;
- if (os->section_alignment != -1)
- lma = align_power (lma, os->section_alignment);
+ if (section_alignment > 0)
+ lma = align_power (lma, section_alignment);
os->bfd_section->lma = lma;
}
}
To avoid warnings about dot moving backwards when using
-Ttext, don't start tracking sections until we find one
of non-zero size or with lma set differently to vma. */
- if (((os->bfd_section->flags & SEC_HAS_CONTENTS) != 0
- || (os->bfd_section->flags & SEC_THREAD_LOCAL) == 0)
- && (os->bfd_section->flags & SEC_ALLOC) != 0
+ if (!IGNORE_SECTION (os->bfd_section)
&& (os->bfd_section->size != 0
|| (r->last_os == NULL
&& os->bfd_section->vma != os->bfd_section->lma)
&& dot >= (r->last_os->output_section_statement
.bfd_section->vma)))
&& os->lma_region == NULL
- && !link_info.relocatable)
+ && !bfd_link_relocatable (&link_info))
r->last_os = s;
/* .tbss sections effectively have zero size. */
- if ((os->bfd_section->flags & SEC_HAS_CONTENTS) != 0
- || (os->bfd_section->flags & SEC_THREAD_LOCAL) == 0
- || link_info.relocatable)
- dot += TO_ADDR (os->bfd_section->size);
+ if (!IS_TBSS (os->bfd_section)
+ || bfd_link_relocatable (&link_info))
+ dotdelta = TO_ADDR (os->bfd_section->size);
+ else
+ dotdelta = 0;
+ dot += dotdelta;
if (os->update_dot_tree != 0)
exp_fold_tree (os->update_dot_tree, bfd_abs_section_ptr, &dot);
/* Update dot in the region ?
We only do this if the section is going to be allocated,
since unallocated sections do not contribute to the region's
- overall size in memory.
-
- If the SEC_NEVER_LOAD bit is not set, it will affect the
- addresses of sections after it. We have to update
- dot. */
+ overall size in memory. */
if (os->region != NULL
- && ((os->bfd_section->flags & SEC_NEVER_LOAD) == 0
- || (os->bfd_section->flags & (SEC_ALLOC | SEC_LOAD))))
+ && (os->bfd_section->flags & (SEC_ALLOC | SEC_LOAD)))
{
os->region->current = dot;
os->bfd_section->vma);
if (os->lma_region != NULL && os->lma_region != os->region
- && (os->bfd_section->flags & SEC_LOAD))
+ && ((os->bfd_section->flags & SEC_LOAD)
+ || os->align_lma_with_input))
{
- os->lma_region->current
- = os->bfd_section->lma + TO_ADDR (os->bfd_section->size);
+ os->lma_region->current = os->bfd_section->lma + dotdelta;
if (check_regions)
os_region_check (os, os->lma_region, NULL,
break;
case lang_constructors_statement_enum:
- dot = lang_size_sections_1 (constructor_list.head,
+ dot = lang_size_sections_1 (&constructor_list.head,
output_section_statement,
- &s->wild_statement.children.head,
fill, dot, relax, check_regions);
break;
if (size < TO_SIZE ((unsigned) 1))
size = TO_SIZE ((unsigned) 1);
dot += TO_ADDR (size);
- output_section_statement->bfd_section->size += size;
+ output_section_statement->bfd_section->size
+ = TO_SIZE (dot - output_section_statement->bfd_section->vma);
+
}
break;
output_section_statement->bfd_section;
size = bfd_get_reloc_size (s->reloc_statement.howto);
dot += TO_ADDR (size);
- output_section_statement->bfd_section->size += size;
+ output_section_statement->bfd_section->size
+ = TO_SIZE (dot - output_section_statement->bfd_section->vma);
}
break;
case lang_wild_statement_enum:
- dot = lang_size_sections_1 (s->wild_statement.children.head,
+ dot = lang_size_sections_1 (&s->wild_statement.children.head,
output_section_statement,
- &s->wild_statement.children.head,
fill, dot, relax, check_regions);
break;
{
asection *i;
- i = (*prev)->input_section.section;
+ i = s->input_section.section;
if (relax)
{
bfd_boolean again;
- if (! bfd_relax_section (i->owner, i, &link_info, &again))
+ if (!bfd_relax_section (i->owner, i, &link_info, &again))
einfo (_("%P%F: can't relax section: %E\n"));
if (again)
*relax = TRUE;
}
dot = size_input_section (prev, output_section_statement,
- output_section_statement->fill, dot);
+ fill, dot);
}
break;
}
expld.dataseg.relro = exp_dataseg_relro_none;
- /* This symbol is relative to this section. */
+ /* This symbol may be relative to this section. */
if ((tree->type.node_class == etree_provided
|| tree->type.node_class == etree_assign)
&& (tree->assign.dst [0] != '.'
|| tree->assign.dst [1] != '\0'))
- output_section_statement->section_relative_symbol = 1;
+ output_section_statement->update_dot = 1;
if (!output_section_statement->ignored)
{
/* If dot is advanced, this implies that the section
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))
+ should not be allocated. */
+ if (output_section_statement->sectype != noalloc_section
+ && (output_section_statement->sectype != noload_section
+ || (bfd_get_flavour (link_info.output_bfd)
+ == bfd_target_elf_flavour)))
output_section_statement->bfd_section->flags |= SEC_ALLOC;
}
dot = newdot;
break;
case lang_group_statement_enum:
- dot = lang_size_sections_1 (s->group_statement.children.head,
+ dot = lang_size_sections_1 (&s->group_statement.children.head,
output_section_statement,
- &s->group_statement.children.head,
fill, dot, relax, check_regions);
break;
segments. We are allowed an opportunity to override this decision. */
bfd_boolean
-ldlang_override_segment_assignment (struct bfd_link_info * info ATTRIBUTE_UNUSED,
- bfd * abfd ATTRIBUTE_UNUSED,
- asection * current_section,
- asection * previous_section,
+ldlang_override_segment_assignment (struct bfd_link_info *info ATTRIBUTE_UNUSED,
+ bfd *abfd ATTRIBUTE_UNUSED,
+ asection *current_section,
+ asection *previous_section,
bfd_boolean new_segment)
{
- lang_output_section_statement_type * cur;
- lang_output_section_statement_type * prev;
+ lang_output_section_statement_type *cur;
+ lang_output_section_statement_type *prev;
/* The checks below are only necessary when the BFD library has decided
that the two sections ought to be placed into the same segment. */
if (current_section == NULL || previous_section == NULL)
return new_segment;
+ /* If this flag is set, the target never wants code and non-code
+ sections comingled in the same segment. */
+ if (config.separate_code
+ && ((current_section->flags ^ previous_section->flags) & SEC_CODE))
+ return TRUE;
+
/* Find the memory regions associated with the two sections.
We call lang_output_section_find() here rather than scanning the list
of output sections looking for a matching section pointer because if
one_lang_size_sections_pass (bfd_boolean *relax, bfd_boolean check_regions)
{
lang_statement_iteration++;
- lang_size_sections_1 (statement_list.head, abs_output_section,
- &statement_list.head, 0, 0, relax, check_regions);
+ lang_size_sections_1 (&statement_list.head, abs_output_section,
+ 0, 0, relax, check_regions);
}
void
if (expld.dataseg.phase == exp_dataseg_end_seen
&& link_info.relro && expld.dataseg.relro_end)
{
- /* If DATA_SEGMENT_ALIGN DATA_SEGMENT_RELRO_END pair was seen, try
- to put expld.dataseg.relro on a (common) page boundary. */
- bfd_vma old_min_base, relro_end, maxpage;
+ bfd_vma initial_base, relro_end, desired_end;
+ asection *sec;
- expld.dataseg.phase = exp_dataseg_relro_adjust;
- old_min_base = expld.dataseg.min_base;
- maxpage = expld.dataseg.maxpagesize;
- expld.dataseg.base += (-expld.dataseg.relro_end
- & (expld.dataseg.pagesize - 1));
/* Compute the expected PT_GNU_RELRO segment end. */
relro_end = ((expld.dataseg.relro_end + expld.dataseg.pagesize - 1)
& ~(expld.dataseg.pagesize - 1));
- if (old_min_base + maxpage < expld.dataseg.base)
- {
- expld.dataseg.base -= maxpage;
- relro_end -= maxpage;
- }
+
+ /* Adjust by the offset arg of DATA_SEGMENT_RELRO_END. */
+ desired_end = relro_end - expld.dataseg.relro_offset;
+
+ /* For sections in the relro segment.. */
+ for (sec = link_info.output_bfd->section_last; sec; sec = sec->prev)
+ if ((sec->flags & SEC_ALLOC) != 0
+ && sec->vma >= expld.dataseg.base
+ && sec->vma < expld.dataseg.relro_end - expld.dataseg.relro_offset)
+ {
+ /* Where do we want to put this section so that it ends as
+ desired? */
+ bfd_vma start, end, bump;
+
+ end = start = sec->vma;
+ if (!IS_TBSS (sec))
+ end += TO_ADDR (sec->size);
+ bump = desired_end - end;
+ /* We'd like to increase START by BUMP, but we must heed
+ alignment so the increase might be less than optimum. */
+ start += bump;
+ start &= ~(((bfd_vma) 1 << sec->alignment_power) - 1);
+ /* This is now the desired end for the previous section. */
+ desired_end = start;
+ }
+
+ expld.dataseg.phase = exp_dataseg_relro_adjust;
+ ASSERT (desired_end >= expld.dataseg.base);
+ initial_base = expld.dataseg.base;
+ expld.dataseg.base = desired_end;
lang_reset_memory_regions ();
one_lang_size_sections_pass (relax, check_regions);
+
if (expld.dataseg.relro_end > relro_end)
{
- /* The alignment of sections between DATA_SEGMENT_ALIGN
- and DATA_SEGMENT_RELRO_END caused huge padding to be
- inserted at DATA_SEGMENT_RELRO_END. Try some other base. */
- asection *sec;
- unsigned int max_alignment_power = 0;
-
- /* Find maximum alignment power of sections between
- DATA_SEGMENT_ALIGN and DATA_SEGMENT_RELRO_END. */
- 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)
- max_alignment_power = sec->alignment_power;
-
- if (((bfd_vma) 1 << max_alignment_power) < expld.dataseg.pagesize)
- {
- if (expld.dataseg.base - (1 << max_alignment_power)
- < old_min_base)
- expld.dataseg.base += expld.dataseg.pagesize;
- expld.dataseg.base -= (1 << max_alignment_power);
- lang_reset_memory_regions ();
- one_lang_size_sections_pass (relax, check_regions);
- }
+ /* Assignments to dot, or to output section address in a
+ user script have increased padding over the original.
+ Revert. */
+ expld.dataseg.base = initial_base;
+ lang_reset_memory_regions ();
+ one_lang_size_sections_pass (relax, check_regions);
}
+
link_info.relro_start = expld.dataseg.base;
link_info.relro_end = expld.dataseg.relro_end;
}
lang_reset_memory_regions ();
one_lang_size_sections_pass (relax, check_regions);
}
+ else
+ expld.dataseg.phase = exp_dataseg_done;
}
-
- expld.phase = lang_final_phase_enum;
+ else
+ expld.dataseg.phase = exp_dataseg_done;
}
+static lang_output_section_statement_type *current_section;
+static lang_assignment_statement_type *current_assign;
+static bfd_boolean prefer_next_section;
+
/* Worker function for lang_do_assignments. Recursiveness goes here. */
static bfd_vma
lang_do_assignments_1 (lang_statement_union_type *s,
lang_output_section_statement_type *current_os,
fill_type *fill,
- bfd_vma dot)
+ bfd_vma dot,
+ bfd_boolean *found_end)
{
for (; s != NULL; s = s->header.next)
{
{
case lang_constructors_statement_enum:
dot = lang_do_assignments_1 (constructor_list.head,
- current_os, fill, dot);
+ current_os, fill, dot, found_end);
break;
case lang_output_section_statement_enum:
lang_output_section_statement_type *os;
os = &(s->output_section_statement);
+ os->after_end = *found_end;
if (os->bfd_section != NULL && !os->ignored)
{
+ if ((os->bfd_section->flags & SEC_ALLOC) != 0)
+ {
+ current_section = os;
+ prefer_next_section = FALSE;
+ }
dot = os->bfd_section->vma;
- lang_do_assignments_1 (os->children.head, os, os->fill, dot);
+ lang_do_assignments_1 (os->children.head,
+ os, os->fill, dot, found_end);
/* .tbss sections effectively have zero size. */
- if ((os->bfd_section->flags & SEC_HAS_CONTENTS) != 0
- || (os->bfd_section->flags & SEC_THREAD_LOCAL) == 0
- || link_info.relocatable)
+ if (!IS_TBSS (os->bfd_section)
+ || bfd_link_relocatable (&link_info))
dot += TO_ADDR (os->bfd_section->size);
+
+ if (os->update_dot_tree != NULL)
+ exp_fold_tree (os->update_dot_tree, bfd_abs_section_ptr,
+ &dot);
}
}
break;
case lang_wild_statement_enum:
dot = lang_do_assignments_1 (s->wild_statement.children.head,
- current_os, fill, dot);
+ current_os, fill, dot, found_end);
break;
case lang_object_symbols_statement_enum:
case lang_data_statement_enum:
exp_fold_tree (s->data_statement.exp, bfd_abs_section_ptr, &dot);
if (expld.result.valid_p)
- s->data_statement.value = (expld.result.value
- + expld.result.section->vma);
+ {
+ s->data_statement.value = expld.result.value;
+ if (expld.result.section != NULL)
+ s->data_statement.value += expld.result.section->vma;
+ }
else
einfo (_("%F%P: invalid data statement\n"));
{
break;
case lang_assignment_statement_enum:
+ current_assign = &s->assignment_statement;
+ if (current_assign->exp->type.node_class != etree_assert)
+ {
+ const char *p = current_assign->exp->assign.dst;
+
+ if (current_os == abs_output_section && p[0] == '.' && p[1] == 0)
+ prefer_next_section = TRUE;
+
+ while (*p == '_')
+ ++p;
+ if (strcmp (p, "end") == 0)
+ *found_end = TRUE;
+ }
exp_fold_tree (s->assignment_statement.exp,
current_os->bfd_section,
&dot);
case lang_group_statement_enum:
dot = lang_do_assignments_1 (s->group_statement.children.head,
- current_os, fill, dot);
+ current_os, fill, dot, found_end);
break;
case lang_insert_statement_enum:
}
void
-lang_do_assignments (void)
+lang_do_assignments (lang_phase_type phase)
{
+ bfd_boolean found_end = FALSE;
+
+ current_section = NULL;
+ prefer_next_section = FALSE;
+ expld.phase = phase;
lang_statement_iteration++;
- lang_do_assignments_1 (statement_list.head, abs_output_section, NULL, 0);
+ lang_do_assignments_1 (statement_list.head,
+ abs_output_section, NULL, 0, &found_end);
+}
+
+/* For an assignment statement outside of an output section statement,
+ choose the best of neighbouring output sections to use for values
+ of "dot". */
+
+asection *
+section_for_dot (void)
+{
+ asection *s;
+
+ /* Assignments belong to the previous output section, unless there
+ has been an assignment to "dot", in which case following
+ assignments belong to the next output section. (The assumption
+ is that an assignment to "dot" is setting up the address for the
+ next output section.) Except that past the assignment to "_end"
+ we always associate with the previous section. This exception is
+ for targets like SH that define an alloc .stack or other
+ weirdness after non-alloc sections. */
+ if (current_section == NULL || prefer_next_section)
+ {
+ lang_statement_union_type *stmt;
+ lang_output_section_statement_type *os;
+
+ for (stmt = (lang_statement_union_type *) current_assign;
+ stmt != NULL;
+ stmt = stmt->header.next)
+ if (stmt->header.type == lang_output_section_statement_enum)
+ break;
+
+ os = &stmt->output_section_statement;
+ while (os != NULL
+ && !os->after_end
+ && (os->bfd_section == NULL
+ || (os->bfd_section->flags & SEC_EXCLUDE) != 0
+ || bfd_section_removed_from_list (link_info.output_bfd,
+ os->bfd_section)))
+ os = os->next;
+
+ if (current_section == NULL || os == NULL || !os->after_end)
+ {
+ if (os != NULL)
+ s = os->bfd_section;
+ else
+ s = link_info.output_bfd->section_last;
+ while (s != NULL
+ && ((s->flags & SEC_ALLOC) == 0
+ || (s->flags & SEC_THREAD_LOCAL) != 0))
+ s = s->prev;
+ if (s != NULL)
+ return s;
+
+ return bfd_abs_section_ptr;
+ }
+ }
+
+ s = current_section->bfd_section;
+
+ /* The section may have been stripped. */
+ while (s != NULL
+ && ((s->flags & SEC_EXCLUDE) != 0
+ || (s->flags & SEC_ALLOC) == 0
+ || (s->flags & SEC_THREAD_LOCAL) != 0
+ || bfd_section_removed_from_list (link_info.output_bfd, s)))
+ s = s->prev;
+ if (s == NULL)
+ s = link_info.output_bfd->sections;
+ while (s != NULL
+ && ((s->flags & SEC_ALLOC) == 0
+ || (s->flags & SEC_THREAD_LOCAL) != 0))
+ s = s->next;
+ if (s != NULL)
+ return s;
+
+ return bfd_abs_section_ptr;
}
/* Fix any .startof. or .sizeof. symbols. When the assemblers see the
{
asection *s;
- if (link_info.relocatable)
+ if (bfd_link_relocatable (&link_info))
return;
for (s = link_info.output_bfd->sections; s != NULL; s = s->next)
struct bfd_link_hash_entry *h;
secname = bfd_get_section_name (link_info.output_bfd, s);
- buf = xmalloc (10 + strlen (secname));
+ buf = (char *) xmalloc (10 + strlen (secname));
sprintf (buf, ".startof.%s", secname);
h = bfd_link_hash_lookup (link_info.hash, buf, FALSE, FALSE, TRUE);
if (h != NULL && h->type == bfd_link_hash_undefined)
{
h->type = bfd_link_hash_defined;
- h->u.def.value = bfd_get_section_vma (link_info.output_bfd, s);
- h->u.def.section = bfd_abs_section_ptr;
+ h->u.def.value = 0;
+ h->u.def.section = s;
}
sprintf (buf, ".sizeof.%s", secname);
struct bfd_link_hash_entry *h;
bfd_boolean warn;
- if ((link_info.relocatable && !link_info.gc_sections)
- || link_info.shared)
+ if ((bfd_link_relocatable (&link_info) && !link_info.gc_sections)
+ || bfd_link_dll (&link_info))
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))
+ if (link_info.gc_sections && bfd_link_relocatable (&link_info)
+ && !(entry_from_cmdline || undef_from_cmdline))
einfo (_("%P%F: gc-sections requires either an entry or "
"an undefined symbol\n"));
+ 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 (link_info.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 (link_info.output_bfd, val))
+ if (!bfd_set_start_address (link_info.output_bfd, val))
einfo (_("%P%F: can't set start address\n"));
}
else
}
}
}
-
- /* Don't bfd_hash_table_free (&lang_definedness_table);
- map file output may result in a call of lang_track_definedness. */
}
/* This is a small function used when we want to ignore errors from
for (file = file_chain.head; file != NULL; file = file->input_statement.next)
{
+#ifdef ENABLE_PLUGINS
+ /* Don't check format of files claimed by plugin. */
+ if (file->input_statement.flags.claimed)
+ continue;
+#endif /* ENABLE_PLUGINS */
input_bfd = file->input_statement.the_bfd;
compatible
= bfd_arch_get_compatible (input_bfd, link_info.output_bfd,
input format may not have equivalent representations in
the output format (and besides BFD does not translate
relocs for other link purposes than a final link). */
- if ((link_info.relocatable || link_info.emitrelocations)
+ if ((bfd_link_relocatable (&link_info)
+ || link_info.emitrelocations)
&& (compatible == NULL
|| (bfd_get_flavour (input_bfd)
!= bfd_get_flavour (link_info.output_bfd)))
function which will do nothing. We still want to call
bfd_merge_private_bfd_data, since it may set up
information which is needed in the output file. */
- if (! command_line.warn_mismatch)
+ if (!command_line.warn_mismatch)
pfn = bfd_set_error_handler (ignore_bfd_errors);
- if (! bfd_merge_private_bfd_data (input_bfd, link_info.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"
" of file %B\n"), input_bfd);
}
- if (! command_line.warn_mismatch)
+ if (!command_line.warn_mismatch)
bfd_set_error_handler (pfn);
}
}
{
if (command_line.inhibit_common_definition)
return;
- if (link_info.relocatable
- && ! command_line.force_common_definition)
+ if (bfd_link_relocatable (&link_info)
+ && !command_line.force_common_definition)
return;
- if (! config.sort_common)
+ if (!config.sort_common)
bfd_link_hash_traverse (link_info.hash, lang_one_common, NULL);
else
{
for (power = 0; power <= 4; power++)
bfd_link_hash_traverse (link_info.hash, lang_one_common, &power);
- power = UINT_MAX;
+ power = (unsigned int) -1;
bfd_link_hash_traverse (link_info.hash, lang_one_common, &power);
}
}
return TRUE;
section = h->u.c.p->section;
-
- /* Increase the size of the section to align the common sym. */
- section->size += ((bfd_vma) 1 << (power_of_two + opb_shift)) - 1;
- section->size &= (- (bfd_vma) 1 << (power_of_two + opb_shift));
-
- /* Adjust the alignment if necessary. */
- if (power_of_two > section->alignment_power)
- section->alignment_power = power_of_two;
-
- /* Change the symbol from common to defined. */
- h->type = bfd_link_hash_defined;
- h->u.def.section = section;
- h->u.def.value = section->size;
-
- /* Increase the size of the section. */
- section->size += size;
-
- /* Make sure the section is allocated in memory, and make sure that
- it is no longer a common section. */
- section->flags |= SEC_ALLOC;
- section->flags &= ~SEC_IS_COMMON;
+ if (!bfd_define_common_symbol (link_info.output_bfd, &link_info, h))
+ einfo (_("%P%F: Could not define common symbol `%T': %E\n"),
+ h->root.string);
if (config.map_file != NULL)
{
char *name;
char buf[50];
- if (! header_printed)
+ if (!header_printed)
{
minfo (_("\nAllocating common symbols\n"));
minfo (_("Common symbol size file\n\n"));
return TRUE;
}
+/* Handle a single orphan section S, placing the orphan into an appropriate
+ output section. The effects of the --orphan-handling command line
+ option are handled here. */
+
+static void
+ldlang_place_orphan (asection *s)
+{
+ if (config.orphan_handling == orphan_handling_discard)
+ {
+ lang_output_section_statement_type *os;
+ os = lang_output_section_statement_lookup (DISCARD_SECTION_NAME, 0,
+ TRUE);
+ if (os->addr_tree == NULL
+ && (bfd_link_relocatable (&link_info)
+ || (s->flags & (SEC_LOAD | SEC_ALLOC)) == 0))
+ os->addr_tree = exp_intop (0);
+ lang_add_section (&os->children, s, NULL, os);
+ }
+ else
+ {
+ lang_output_section_statement_type *os;
+ const char *name = s->name;
+ int constraint = 0;
+
+ if (config.orphan_handling == orphan_handling_error)
+ einfo ("%X%P: error: unplaced orphan section `%A' from `%B'.\n",
+ s, s->owner);
+
+ if (config.unique_orphan_sections || unique_section_p (s, NULL))
+ constraint = SPECIAL;
+
+ os = ldemul_place_orphan (s, name, constraint);
+ if (os == NULL)
+ {
+ os = lang_output_section_statement_lookup (name, constraint, TRUE);
+ if (os->addr_tree == NULL
+ && (bfd_link_relocatable (&link_info)
+ || (s->flags & (SEC_LOAD | SEC_ALLOC)) == 0))
+ os->addr_tree = exp_intop (0);
+ lang_add_section (&os->children, s, NULL, os);
+ }
+
+ if (config.orphan_handling == orphan_handling_warn)
+ einfo ("%P: warning: orphan section `%A' from `%B' being "
+ "placed in section `%s'.\n",
+ s, s->owner, os->name);
+ }
+}
+
/* Run through the input files and ensure that every input section has
somewhere to go. If one is found without a destination then create
an input request and place it into the statement tree. */
/* This section of the file is not attached, root
around for a sensible place for it to go. */
- if (file->just_syms_flag)
+ if (file->flags.just_syms)
bfd_link_just_syms (file->the_bfd, s, &link_info);
else if ((s->flags & SEC_EXCLUDE) != 0)
s->output_section = bfd_abs_section_ptr;
/* This is a lonely common section which must have
come from an archive. We attach to the section
with the wildcard. */
- if (! link_info.relocatable
+ if (!bfd_link_relocatable (&link_info)
|| command_line.force_common_definition)
{
if (default_common_section == NULL)
- {
- default_common_section =
- lang_output_section_statement_lookup (".bss");
-
- }
+ default_common_section
+ = lang_output_section_statement_lookup (".bss", 0,
+ TRUE);
lang_add_section (&default_common_section->children, s,
- default_common_section);
+ NULL, default_common_section);
}
}
- else if (ldemul_place_orphan (s))
- ;
else
- {
- lang_output_section_statement_type *os;
-
- os = lang_output_section_statement_lookup (s->name);
- lang_add_section (&os->children, s, os);
- }
+ ldlang_place_orphan (s);
}
}
}
flagword *ptr_flags;
ptr_flags = invert ? &ptr->not_flags : &ptr->flags;
+
while (*flags)
{
switch (*flags)
{
+ /* PR 17900: An exclamation mark in the attributes reverses
+ the sense of any of the attributes that follow. */
+ case '!':
+ invert = !invert;
+ ptr_flags = invert ? &ptr->not_flags : &ptr->flags;
+ break;
+
case 'A': case 'a':
*ptr_flags |= SEC_ALLOC;
break;
break;
default:
- einfo (_("%P%F: invalid syntax in flags\n"));
+ einfo (_("%P%F: invalid character %c (%d) in flags\n"),
+ *flags, *flags);
break;
}
flags++;
/* 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->link.next == NULL);
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;
+ 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);
}
}
-static lang_output_section_statement_type *current_section;
-
static int
topower (int x)
{
etree_type *align,
etree_type *subalign,
etree_type *ebase,
- int constraint)
+ int constraint,
+ int align_with_input)
{
lang_output_section_statement_type *os;
- os = lang_output_section_statement_lookup_1 (output_section_statement_name,
- constraint);
+ os = lang_output_section_statement_lookup (output_section_statement_name,
+ constraint, TRUE);
current_section = os;
- /* Make next things chain into subchain of this. */
-
if (os->addr_tree == NULL)
{
os->addr_tree = address_exp;
else
os->flags = SEC_NEVER_LOAD;
os->block_value = 1;
- stat_ptr = &os->children;
+
+ /* Make next things chain into subchain of this. */
+ push_stat_ptr (&os->children);
+
+ os->align_lma_with_input = align_with_input == ALIGN_WITH_INPUT;
+ if (os->align_lma_with_input && align != NULL)
+ einfo (_("%F%P:%S: error: align with input and explicit align specified\n"),
+ NULL);
os->subsection_alignment =
topower (exp_get_value_int (subalign, -1, "subsection alignment"));
void
lang_final (void)
{
- lang_output_statement_type *new;
+ lang_output_statement_type *new_stmt;
- new = new_stat (lang_output_statement, stat_ptr);
- new->name = output_filename;
+ new_stmt = new_stat (lang_output_statement, stat_ptr);
+ new_stmt->name = output_filename;
}
/* Reset the current counters in the regions. */
gc_section_callback (lang_wild_statement_type *ptr,
struct wildcard_list *sec ATTRIBUTE_UNUSED,
asection *section,
+ struct flag_info *sflag_info ATTRIBUTE_UNUSED,
lang_input_statement_type *file ATTRIBUTE_UNUSED,
void *data ATTRIBUTE_UNUSED)
{
lang_gc_sections (void)
{
/* Keep all sections so marked in the link script. */
-
lang_gc_sections_1 (statement_list.head);
/* 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.relocatable)
+ if (bfd_link_relocatable (&link_info))
{
LANG_FOR_EACH_INPUT_STATEMENT (f)
{
asection *sec;
+#ifdef ENABLE_PLUGINS
+ if (f->flags.claimed)
+ continue;
+#endif
for (sec = f->the_bfd->sections; sec != NULL; sec = sec->next)
if ((sec->flags & SEC_DEBUGGING) == 0)
sec->flags &= ~SEC_EXCLUDE;
find_relro_section_callback (lang_wild_statement_type *ptr ATTRIBUTE_UNUSED,
struct wildcard_list *sec ATTRIBUTE_UNUSED,
asection *section,
+ struct flag_info *sflag_info ATTRIBUTE_UNUSED,
lang_input_statement_type *file ATTRIBUTE_UNUSED,
void *data)
{
/* Relax all sections until bfd_relax_section gives up. */
-static void
-relax_sections (void)
+void
+lang_relax_sections (bfd_boolean need_layout)
{
- /* Keep relaxing until bfd_relax_section gives up. */
- bfd_boolean relax_again;
-
- link_info.relax_trip = -1;
- do
+ if (RELAXATION_ENABLED)
{
- relax_again = FALSE;
- link_info.relax_trip++;
+ /* We may need more than one relaxation pass. */
+ int i = link_info.relax_pass;
- /* Note: pe-dll.c does something like this also. If you find
- you need to change this code, you probably need to change
- pe-dll.c also. DJ */
+ /* The backend can use it to determine the current pass. */
+ link_info.relax_pass = 0;
+
+ while (i--)
+ {
+ /* Keep relaxing until bfd_relax_section gives up. */
+ bfd_boolean relax_again;
+
+ link_info.relax_trip = -1;
+ do
+ {
+ link_info.relax_trip++;
+
+ /* Note: pe-dll.c does something like this also. If you find
+ you need to change this code, you probably need to change
+ pe-dll.c also. DJ */
- /* Do all the assignments with our current guesses as to
- section sizes. */
- lang_do_assignments ();
+ /* Do all the assignments with our current guesses as to
+ section sizes. */
+ lang_do_assignments (lang_assigning_phase_enum);
+
+ /* We must do this after lang_do_assignments, because it uses
+ size. */
+ lang_reset_memory_regions ();
+
+ /* Perform another relax pass - this time we know where the
+ globals are, so can make a better guess. */
+ relax_again = FALSE;
+ lang_size_sections (&relax_again, FALSE);
+ }
+ while (relax_again);
+
+ link_info.relax_pass++;
+ }
+ need_layout = TRUE;
+ }
- /* We must do this after lang_do_assignments, because it uses
- size. */
+ if (need_layout)
+ {
+ /* Final extra sizing to report errors. */
+ lang_do_assignments (lang_assigning_phase_enum);
lang_reset_memory_regions ();
+ lang_size_sections (NULL, TRUE);
+ }
+}
+
+#ifdef ENABLE_PLUGINS
+/* Find the insert point for the plugin's replacement files. We
+ place them after the first claimed real object file, or if the
+ first claimed object is an archive member, after the last real
+ object file immediately preceding the archive. In the event
+ no objects have been claimed at all, we return the first dummy
+ object file on the list as the insert point; that works, but
+ the callee must be careful when relinking the file_chain as it
+ is not actually on that chain, only the statement_list and the
+ input_file list; in that case, the replacement files must be
+ inserted at the head of the file_chain. */
+
+static lang_input_statement_type *
+find_replacements_insert_point (void)
+{
+ lang_input_statement_type *claim1, *lastobject;
+ lastobject = &input_file_chain.head->input_statement;
+ for (claim1 = &file_chain.head->input_statement;
+ claim1 != NULL;
+ claim1 = &claim1->next->input_statement)
+ {
+ if (claim1->flags.claimed)
+ return claim1->flags.claim_archive ? lastobject : claim1;
+ /* Update lastobject if this is a real object file. */
+ if (claim1->the_bfd != NULL && claim1->the_bfd->my_archive == NULL)
+ lastobject = claim1;
+ }
+ /* No files were claimed by the plugin. Choose the last object
+ file found on the list (maybe the first, dummy entry) as the
+ insert point. */
+ return lastobject;
+}
+
+/* Insert SRCLIST into DESTLIST after given element by chaining
+ on FIELD as the next-pointer. (Counterintuitively does not need
+ a pointer to the actual after-node itself, just its chain field.) */
+
+static void
+lang_list_insert_after (lang_statement_list_type *destlist,
+ lang_statement_list_type *srclist,
+ lang_statement_union_type **field)
+{
+ *(srclist->tail) = *field;
+ *field = srclist->head;
+ if (destlist->tail == field)
+ destlist->tail = srclist->tail;
+}
+
+/* Detach new nodes added to DESTLIST since the time ORIGLIST
+ was taken as a copy of it and leave them in ORIGLIST. */
+
+static void
+lang_list_remove_tail (lang_statement_list_type *destlist,
+ lang_statement_list_type *origlist)
+{
+ union lang_statement_union **savetail;
+ /* Check that ORIGLIST really is an earlier state of DESTLIST. */
+ ASSERT (origlist->head == destlist->head);
+ savetail = origlist->tail;
+ origlist->head = *(savetail);
+ origlist->tail = destlist->tail;
+ destlist->tail = savetail;
+ *savetail = NULL;
+}
+#endif /* ENABLE_PLUGINS */
+
+/* Add NAME to the list of garbage collection entry points. */
+
+void
+lang_add_gc_name (const char *name)
+{
+ struct bfd_sym_chain *sym;
+
+ if (name == NULL)
+ return;
+
+ sym = (struct bfd_sym_chain *) stat_alloc (sizeof (*sym));
+
+ sym->next = link_info.gc_sym_list;
+ sym->name = name;
+ link_info.gc_sym_list = sym;
+}
- /* Perform another relax pass - this time we know where the
- globals are, so can make a better guess. */
- lang_size_sections (&relax_again, FALSE);
+/* Check relocations. */
+
+static void
+lang_check_relocs (void)
+{
+ if (link_info.check_relocs_after_open_input)
+ {
+ bfd *abfd;
+
+ for (abfd = link_info.input_bfds;
+ abfd != (bfd *) NULL; abfd = abfd->link.next)
+ if (!bfd_link_check_relocs (abfd, &link_info))
+ {
+ /* No object output, fail return. */
+ config.make_executable = FALSE;
+ /* Note: we do not abort the loop, but rather
+ continue the scan in case there are other
+ bad relocations to report. */
+ }
}
- while (relax_again);
}
void
/* Create a bfd for each input file. */
current_target = default_target;
- open_input_bfds (statement_list.head, FALSE);
+ open_input_bfds (statement_list.head, OPEN_BFD_NORMAL);
+
+#ifdef ENABLE_PLUGINS
+ if (link_info.lto_plugin_active)
+ {
+ lang_statement_list_type added;
+ lang_statement_list_type files, inputfiles;
+
+ /* Now all files are read, let the plugin(s) decide if there
+ are any more to be added to the link before we call the
+ emulation's after_open hook. We create a private list of
+ input statements for this purpose, which we will eventually
+ insert into the global statment list after the first claimed
+ file. */
+ added = *stat_ptr;
+ /* We need to manipulate all three chains in synchrony. */
+ files = file_chain;
+ inputfiles = input_file_chain;
+ if (plugin_call_all_symbols_read ())
+ einfo (_("%P%F: %s: plugin reported error after all symbols read\n"),
+ plugin_error_plugin ());
+ /* Open any newly added files, updating the file chains. */
+ open_input_bfds (*added.tail, OPEN_BFD_NORMAL);
+ /* Restore the global list pointer now they have all been added. */
+ lang_list_remove_tail (stat_ptr, &added);
+ /* And detach the fresh ends of the file lists. */
+ lang_list_remove_tail (&file_chain, &files);
+ lang_list_remove_tail (&input_file_chain, &inputfiles);
+ /* Were any new files added? */
+ if (added.head != NULL)
+ {
+ /* If so, we will insert them into the statement list immediately
+ after the first input file that was claimed by the plugin. */
+ plugin_insert = find_replacements_insert_point ();
+ /* If a plugin adds input files without having claimed any, we
+ don't really have a good idea where to place them. Just putting
+ them at the start or end of the list is liable to leave them
+ outside the crtbegin...crtend range. */
+ ASSERT (plugin_insert != NULL);
+ /* Splice the new statement list into the old one. */
+ lang_list_insert_after (stat_ptr, &added,
+ &plugin_insert->header.next);
+ /* Likewise for the file chains. */
+ lang_list_insert_after (&input_file_chain, &inputfiles,
+ &plugin_insert->next_real_file);
+ /* We must be careful when relinking file_chain; we may need to
+ insert the new files at the head of the list if the insert
+ point chosen is the dummy first input file. */
+ if (plugin_insert->filename)
+ lang_list_insert_after (&file_chain, &files, &plugin_insert->next);
+ else
+ lang_list_insert_after (&file_chain, &files, &file_chain.head);
+
+ /* Rescan archives in case new undefined symbols have appeared. */
+ open_input_bfds (statement_list.head, OPEN_BFD_RESCAN);
+ }
+ }
+#endif /* ENABLE_PLUGINS */
+
+ /* Make sure that nobody has tried to add a symbol to this list
+ before now. */
+ ASSERT (link_info.gc_sym_list == NULL);
link_info.gc_sym_list = &entry_symbol;
+
if (entry_symbol.name == NULL)
- link_info.gc_sym_list = ldlang_undef_chain_list_head;
+ {
+ link_info.gc_sym_list = ldlang_undef_chain_list_head;
+
+ /* entry_symbol is normally initialied by a ENTRY definition in the
+ linker script or the -e command line option. But if neither of
+ these have been used, the target specific backend may still have
+ provided an entry symbol via a call to lang_default_entry().
+ Unfortunately this value will not be processed until lang_end()
+ is called, long after this function has finished. So detect this
+ case here and add the target's entry symbol to the list of starting
+ points for garbage collection resolution. */
+ lang_add_gc_name (entry_symbol_default);
+ }
+
+ lang_add_gc_name (link_info.init_function);
+ lang_add_gc_name (link_info.fini_function);
ldemul_after_open ();
+ if (config.map_file != NULL)
+ lang_print_asneeded ();
bfd_section_already_linked_table_free ();
files. */
ldctor_build_sets ();
- /* Remove unreferenced sections if asked to. */
- lang_gc_sections ();
+ /* PR 13683: We must rerun the assignments prior to running garbage
+ collection in order to make sure that all symbol aliases are resolved. */
+ lang_do_assignments (lang_mark_phase_enum);
+
+ lang_do_memory_regions();
+ expld.phase = lang_first_phase_enum;
/* Size up the common data. */
lang_common ();
+ /* Remove unreferenced sections if asked to. */
+ lang_gc_sections ();
+
+ /* Check relocations. */
+ lang_check_relocs ();
+
/* Update wild statements. */
update_wild_statements (statement_list.head);
/* Run through the contours of the script and attach input sections
to the correct output sections. */
+ lang_statement_iteration++;
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 ();
- if (! link_info.relocatable)
+ if (!bfd_link_relocatable (&link_info))
{
asection *found;
lang_record_phdrs ();
/* Check relro sections. */
- if (link_info.relro && ! link_info.relocatable)
+ if (link_info.relro && !bfd_link_relocatable (&link_info))
lang_find_relro_sections ();
/* Size up the sections. */
- lang_size_sections (NULL, !command_line.relax);
-
- /* Now run around and relax if we can. */
- if (command_line.relax)
- {
- /* We may need more than one relaxation pass. */
- int i = link_info.relax_pass;
-
- /* The backend can use it to determine the current pass. */
- link_info.relax_pass = 0;
-
- while (i--)
- {
- relax_sections ();
- link_info.relax_pass++;
- }
-
- /* Final extra sizing to report errors. */
- lang_do_assignments ();
- lang_reset_memory_regions ();
- lang_size_sections (NULL, TRUE);
- }
+ lang_size_sections (NULL, !RELAXATION_ENABLED);
/* See if anything special should be done now we know how big
- everything is. */
+ everything is. This is where relaxation is done. */
ldemul_after_allocation ();
/* Fix any .startof. or .sizeof. symbols. */
/* Do all the assignments, now that we know the final resting places
of all the symbols. */
-
- lang_do_assignments ();
+ lang_do_assignments (lang_final_phase_enum);
ldemul_finish ();
+ /* Convert absolute symbols to section relative. */
+ ldexp_finalize_syms ();
+
/* Make sure that the section addresses make sense. */
- if (! link_info.relocatable
- && command_line.check_section_addresses)
+ if (command_line.check_section_addresses)
lang_check_section_addresses ();
+ /* Check any required symbols are known. */
+ ldlang_check_require_defined_symbols ();
+
lang_end ();
}
bfd_boolean keep_sections)
{
struct wildcard_list *curr, *next;
- lang_wild_statement_type *new;
+ lang_wild_statement_type *new_stmt;
/* Reverse the list as the parser puts it back to front. */
for (curr = section_list, section_list = NULL;
{
if (strcmp (filespec->name, "*") == 0)
filespec->name = NULL;
- else if (! wildcardp (filespec->name))
+ else if (!wildcardp (filespec->name))
lang_has_input_file = TRUE;
}
- new = new_stat (lang_wild_statement, stat_ptr);
- new->filename = NULL;
- new->filenames_sorted = FALSE;
+ new_stmt = new_stat (lang_wild_statement, stat_ptr);
+ new_stmt->filename = NULL;
+ new_stmt->filenames_sorted = FALSE;
+ new_stmt->section_flag_list = NULL;
if (filespec != NULL)
{
- new->filename = filespec->name;
- new->filenames_sorted = filespec->sorted == by_name;
+ new_stmt->filename = filespec->name;
+ new_stmt->filenames_sorted = filespec->sorted == by_name;
+ new_stmt->section_flag_list = filespec->section_flag_list;
}
- new->section_list = section_list;
- new->keep_sections = keep_sections;
- lang_list_init (&new->children);
- analyze_walk_wild_section_handler (new);
+ new_stmt->section_list = section_list;
+ new_stmt->keep_sections = keep_sections;
+ lang_list_init (&new_stmt->children);
+ analyze_walk_wild_section_handler (new_stmt);
}
void
{
if (entry_symbol.name == NULL
|| cmdline
- || ! entry_from_cmdline)
+ || !entry_from_cmdline)
{
entry_symbol.name = name;
entry_from_cmdline = cmdline;
void
lang_add_target (const char *name)
{
- lang_target_statement_type *new;
+ lang_target_statement_type *new_stmt;
- new = new_stat (lang_target_statement, stat_ptr);
- new->target = name;
+ new_stmt = new_stat (lang_target_statement, stat_ptr);
+ new_stmt->target = name;
}
void
void
lang_add_fill (fill_type *fill)
{
- lang_fill_statement_type *new;
+ lang_fill_statement_type *new_stmt;
- new = new_stat (lang_fill_statement, stat_ptr);
- new->fill = fill;
+ new_stmt = new_stat (lang_fill_statement, stat_ptr);
+ new_stmt->fill = fill;
}
void
lang_add_data (int type, union etree_union *exp)
{
- lang_data_statement_type *new;
+ lang_data_statement_type *new_stmt;
- new = new_stat (lang_data_statement, stat_ptr);
- new->exp = exp;
- new->type = type;
+ new_stmt = new_stat (lang_data_statement, stat_ptr);
+ new_stmt->exp = exp;
+ new_stmt->type = type;
}
/* Create a new reloc statement. RELOC is the BFD relocation type to
lang_assignment_statement_type *
lang_add_assignment (etree_type *exp)
{
- lang_assignment_statement_type *new;
+ lang_assignment_statement_type *new_stmt;
- new = new_stat (lang_assignment_statement, stat_ptr);
- new->exp = exp;
- return new;
+ new_stmt = new_stat (lang_assignment_statement, stat_ptr);
+ new_stmt->exp = exp;
+ return new_stmt;
}
void
void
lang_startup (const char *name)
{
- if (startup_file != NULL)
+ if (first_file->filename != NULL)
{
einfo (_("%P%F: multiple STARTUP files\n"));
}
first_file->filename = name;
first_file->local_sym_name = name;
- first_file->real = TRUE;
-
- startup_file = name;
+ first_file->flags.real = TRUE;
}
void
has been specified, then use the load region for the runtime region
as well. */
if (lma_memspec != NULL
- && ! have_vma
+ && !have_vma
&& strcmp (memspec, DEFAULT_MEMORY_REGION) == 0)
*region = *lma_region;
else
*region = lang_memory_region_lookup (memspec, FALSE);
if (have_lma && lma_memspec != 0)
- einfo (_("%X%P:%S: section has both a load address and a load region\n"));
+ einfo (_("%X%P:%S: section has both a load address and a load region\n"),
+ NULL);
}
void
memspec, lma_memspec,
current_section->load_base != NULL,
current_section->addr_tree != NULL);
- current_section->fill = fill;
- current_section->phdrs = phdrs;
- stat_ptr = &statement_list;
-}
-
-/* Create an absolute symbol with the given name with the value of the
- address of first byte of the section named.
-
- If the symbol already exists, then do nothing. */
-
-void
-lang_abs_symbol_at_beginning_of (const char *secname, const char *name)
-{
- struct bfd_link_hash_entry *h;
-
- h = bfd_link_hash_lookup (link_info.hash, name, TRUE, TRUE, TRUE);
- if (h == NULL)
- einfo (_("%P%F: bfd_link_hash_lookup failed: %E\n"));
-
- if (h->type == bfd_link_hash_new
- || h->type == bfd_link_hash_undefined)
- {
- asection *sec;
- h->type = bfd_link_hash_defined;
+ /* If this section has no load region or base, but uses the same
+ region as the previous section, then propagate the previous
+ section's load region. */
- 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 (link_info.output_bfd, sec);
-
- h->u.def.section = bfd_abs_section_ptr;
- }
-}
-
-/* Create an absolute symbol with the given name with the value of the
- address of the first byte after the end of the section named.
-
- If the symbol already exists, then do nothing. */
-
-void
-lang_abs_symbol_at_end_of (const char *secname, const char *name)
-{
- struct bfd_link_hash_entry *h;
-
- h = bfd_link_hash_lookup (link_info.hash, name, TRUE, TRUE, TRUE);
- if (h == NULL)
- einfo (_("%P%F: bfd_link_hash_lookup failed: %E\n"));
-
- if (h->type == bfd_link_hash_new
- || h->type == bfd_link_hash_undefined)
- {
- asection *sec;
-
- h->type = bfd_link_hash_defined;
-
- 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 (link_info.output_bfd, sec)
- + TO_ADDR (sec->size));
+ if (current_section->lma_region == NULL
+ && current_section->load_base == NULL
+ && current_section->addr_tree == NULL
+ && current_section->region == current_section->prev->region)
+ current_section->lma_region = current_section->prev->lma_region;
- h->u.def.section = bfd_abs_section_ptr;
- }
+ current_section->fill = fill;
+ current_section->phdrs = phdrs;
+ pop_stat_ptr ();
}
void
void
lang_add_insert (const char *where, int is_before)
{
- lang_insert_statement_type *new;
+ lang_insert_statement_type *new_stmt;
- new = new_stat (lang_insert_statement, stat_ptr);
- new->where = where;
- new->is_before = is_before;
+ new_stmt = new_stat (lang_insert_statement, stat_ptr);
+ new_stmt->where = where;
+ new_stmt->is_before = is_before;
saved_script_handle = previous_script_handle;
}
g = new_stat (lang_group_statement, stat_ptr);
lang_list_init (&g->children);
- stat_ptr = &g->children;
+ push_stat_ptr (&g->children);
}
/* Leave a group. This just resets stat_ptr to start writing to the
void
lang_leave_group (void)
{
- stat_ptr = &statement_list;
+ pop_stat_ptr ();
}
/* Add a new program header. This is called for each entry in a PHDRS
etree_type *flags)
{
struct lang_phdr *n, **pp;
+ bfd_boolean hdrs;
- n = stat_alloc (sizeof (struct lang_phdr));
+ n = (struct lang_phdr *) stat_alloc (sizeof (struct lang_phdr));
n->next = NULL;
n->name = name;
n->type = exp_get_value_int (type, 0, "program header type");
n->at = at;
n->flags = flags;
+ hdrs = n->type == 1 && (phdrs || filehdr);
+
for (pp = &lang_phdr_list; *pp != NULL; pp = &(*pp)->next)
- ;
+ if (hdrs
+ && (*pp)->type == 1
+ && !((*pp)->filehdr || (*pp)->phdrs))
+ {
+ einfo (_("%X%P:%S: PHDRS and FILEHDR are not supported"
+ " when prior PT_LOAD headers lack them\n"), NULL);
+ hdrs = FALSE;
+ }
+
*pp = n;
}
lang_output_section_statement_type *os;
alc = 10;
- secs = xmalloc (alc * sizeof (asection *));
+ secs = (asection **) xmalloc (alc * sizeof (asection *));
last = NULL;
for (l = lang_phdr_list; l != NULL; l = l->next)
{
lang_output_section_phdr_list *pl;
- if (os->constraint == -1)
+ if (os->constraint < 0)
continue;
pl = os->phdrs;
|| (os->bfd_section->flags & SEC_ALLOC) == 0)
continue;
+ /* Don't add orphans to PT_INTERP header. */
+ if (l->type == 3)
+ continue;
+
if (last == NULL)
{
- lang_output_section_statement_type * tmp_os;
+ lang_output_section_statement_type *tmp_os;
/* If we have not run across a section with a program
header assigned to it yet, then scan forwards to find
if (c >= alc)
{
alc *= 2;
- secs = xrealloc (secs, alc * sizeof (asection *));
+ secs = (asection **) xrealloc (secs,
+ alc * sizeof (asection *));
}
secs[c] = os->bfd_section;
++c;
else
at = exp_get_vma (l->at, 0, "phdr load address");
- if (! bfd_record_phdr (link_info.output_bfd, l->type,
- l->flags != NULL, flags, l->at != NULL,
- at, l->filehdr, l->phdrs, c, secs))
+ 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"));
}
{
lang_output_section_phdr_list *pl;
- if (os->constraint == -1
+ if (os->constraint < 0
|| os->bfd_section == NULL)
continue;
for (pl = os->phdrs;
pl != NULL;
pl = pl->next)
- if (! pl->used && strcmp (pl->name, "NONE") != 0)
+ if (!pl->used && strcmp (pl->name, "NONE") != 0)
einfo (_("%X%P: section `%s' assigned to non-existent phdr `%s'\n"),
os->name, pl->name);
}
{
struct lang_nocrossrefs *n;
- n = xmalloc (sizeof *n);
+ n = (struct lang_nocrossrefs *) xmalloc (sizeof *n);
n->next = nocrossref_list;
n->list = l;
+ n->onlyfirst = FALSE;
nocrossref_list = n;
/* Set notice_all so that we get informed about all symbols. */
link_info.notice_all = TRUE;
}
+
+/* Record a section that cannot be referenced from a list of sections. */
+
+void
+lang_add_nocrossref_to (lang_nocrossref_type *l)
+{
+ lang_add_nocrossref (l);
+ nocrossref_list->onlyfirst = TRUE;
+}
\f
/* Overlay handling. We handle overlays with some static variables. */
etree_type *size;
lang_enter_output_section_statement (name, overlay_vma, overlay_section,
- 0, overlay_subalign, 0, 0);
+ 0, overlay_subalign, 0, 0, 0);
/* If this is the first section, then base the VMA of future
sections on this one. This will work correctly even if `.' is
overlay_vma = exp_nameop (ADDR, name);
/* Remember the section. */
- n = xmalloc (sizeof *n);
+ n = (struct overlay_list *) xmalloc (sizeof *n);
n->os = current_section;
n->next = overlay_list;
overlay_list = n;
/* Define the magic symbols. */
- clean = xmalloc (strlen (name) + 1);
+ clean = (char *) xmalloc (strlen (name) + 1);
s2 = clean;
for (s1 = name; *s1 != '\0'; s1++)
if (ISALNUM (*s1) || *s1 == '_')
*s2++ = *s1;
*s2 = '\0';
- buf = xmalloc (strlen (clean) + sizeof "__load_start_");
+ buf = (char *) xmalloc (strlen (clean) + sizeof "__load_start_");
sprintf (buf, "__load_start_%s", clean);
lang_add_assignment (exp_provide (buf,
exp_nameop (LOADADDR, name),
FALSE));
- buf = xmalloc (strlen (clean) + sizeof "__load_stop_");
+ buf = (char *) xmalloc (strlen (clean) + sizeof "__load_stop_");
sprintf (buf, "__load_stop_%s", clean);
lang_add_assignment (exp_provide (buf,
exp_binop ('+',
/* After setting the size of the last section, set '.' to end of the
overlay region. */
if (overlay_list != NULL)
- overlay_list->os->update_dot_tree
- = exp_assop ('=', ".", exp_binop ('+', overlay_vma, overlay_max));
+ {
+ overlay_list->os->update_dot = 1;
+ overlay_list->os->update_dot_tree
+ = exp_assign (".", exp_binop ('+', overlay_vma, overlay_max), FALSE);
+ }
l = overlay_list;
while (l != NULL)
{
lang_nocrossref_type *nc;
- nc = xmalloc (sizeof *nc);
+ nc = (lang_nocrossref_type *) xmalloc (sizeof *nc);
nc->name = l->os->name;
nc->next = nocrossref;
nocrossref = nc;
\f
/* Version handling. This is only useful for ELF. */
-/* This global variable holds the version tree that we build. */
-
-struct bfd_elf_version_tree *lang_elf_version_info;
-
/* If PREV is NULL, return first version pattern matching particular symbol.
If PREV is non-NULL, return first version pattern matching particular
symbol after PREV (previously returned by lang_vers_match). */
struct bfd_elf_version_expr *prev,
const char *sym)
{
+ const char *c_sym;
const char *cxx_sym = sym;
const char *java_sym = sym;
struct bfd_elf_version_expr *expr = NULL;
+ enum demangling_styles curr_style;
+
+ curr_style = CURRENT_DEMANGLING_STYLE;
+ cplus_demangle_set_style (no_demangling);
+ c_sym = bfd_demangle (link_info.output_bfd, sym, DMGL_NO_OPTS);
+ if (!c_sym)
+ c_sym = sym;
+ cplus_demangle_set_style (curr_style);
if (head->mask & BFD_ELF_VERSION_CXX_TYPE)
{
- cxx_sym = cplus_demangle (sym, DMGL_PARAMS | DMGL_ANSI);
+ cxx_sym = bfd_demangle (link_info.output_bfd, sym,
+ DMGL_PARAMS | DMGL_ANSI);
if (!cxx_sym)
cxx_sym = sym;
}
if (head->mask & BFD_ELF_VERSION_JAVA_TYPE)
{
- java_sym = cplus_demangle (sym, DMGL_JAVA);
+ java_sym = bfd_demangle (link_info.output_bfd, sym, DMGL_JAVA);
if (!java_sym)
java_sym = sym;
}
- if (head->htab && (prev == NULL || prev->symbol))
+ if (head->htab && (prev == NULL || prev->literal))
{
struct bfd_elf_version_expr e;
case 0:
if (head->mask & BFD_ELF_VERSION_C_TYPE)
{
- e.symbol = sym;
- expr = htab_find (head->htab, &e);
- while (expr && strcmp (expr->symbol, sym) == 0)
+ e.pattern = c_sym;
+ expr = (struct bfd_elf_version_expr *)
+ htab_find ((htab_t) head->htab, &e);
+ while (expr && strcmp (expr->pattern, c_sym) == 0)
if (expr->mask == BFD_ELF_VERSION_C_TYPE)
goto out_ret;
else
case BFD_ELF_VERSION_C_TYPE:
if (head->mask & BFD_ELF_VERSION_CXX_TYPE)
{
- e.symbol = cxx_sym;
- expr = htab_find (head->htab, &e);
- while (expr && strcmp (expr->symbol, cxx_sym) == 0)
+ e.pattern = cxx_sym;
+ expr = (struct bfd_elf_version_expr *)
+ htab_find ((htab_t) head->htab, &e);
+ while (expr && strcmp (expr->pattern, cxx_sym) == 0)
if (expr->mask == BFD_ELF_VERSION_CXX_TYPE)
goto out_ret;
else
case BFD_ELF_VERSION_CXX_TYPE:
if (head->mask & BFD_ELF_VERSION_JAVA_TYPE)
{
- e.symbol = java_sym;
- expr = htab_find (head->htab, &e);
- while (expr && strcmp (expr->symbol, java_sym) == 0)
+ e.pattern = java_sym;
+ expr = (struct bfd_elf_version_expr *)
+ htab_find ((htab_t) head->htab, &e);
+ while (expr && strcmp (expr->pattern, java_sym) == 0)
if (expr->mask == BFD_ELF_VERSION_JAVA_TYPE)
goto out_ret;
else
}
/* Finally, try the wildcards. */
- if (prev == NULL || prev->symbol)
+ if (prev == NULL || prev->literal)
expr = head->remaining;
else
expr = prev->next;
else if (expr->mask == BFD_ELF_VERSION_CXX_TYPE)
s = cxx_sym;
else
- s = sym;
+ s = c_sym;
if (fnmatch (expr->pattern, s, 0) == 0)
break;
}
out_ret:
+ if (c_sym != sym)
+ free ((char *) c_sym);
if (cxx_sym != sym)
free ((char *) cxx_sym);
if (java_sym != sym)
}
/* Return NULL if the PATTERN argument is a glob pattern, otherwise,
- return a string pointing to the symbol name. */
+ return a pointer to the symbol name with any backslash quotes removed. */
static const char *
realsymbol (const char *pattern)
{
const char *p;
bfd_boolean changed = FALSE, backslash = FALSE;
- char *s, *symbol = xmalloc (strlen (pattern) + 1);
+ char *s, *symbol = (char *) xmalloc (strlen (pattern) + 1);
for (p = pattern, s = symbol; *p != '\0'; ++p)
{
/* It is a glob pattern only if there is no preceding
backslash. */
- if (! backslash && (*p == '?' || *p == '*' || *p == '['))
- {
- free (symbol);
- return NULL;
- }
-
if (backslash)
{
/* Remove the preceding backslash. */
*(s - 1) = *p;
+ backslash = FALSE;
changed = TRUE;
}
else
- *s++ = *p;
+ {
+ if (*p == '?' || *p == '*' || *p == '[')
+ {
+ free (symbol);
+ return NULL;
+ }
- backslash = *p == '\\';
+ *s++ = *p;
+ backslash = *p == '\\';
+ }
}
if (changed)
}
}
-/* This is called for each variable name or match expression. NEW is
+/* This is called for each variable name or match expression. NEW_NAME is
the name of the symbol to match, or, if LITERAL_P is FALSE, a glob
pattern to be matched against symbol names. */
struct bfd_elf_version_expr *
lang_new_vers_pattern (struct bfd_elf_version_expr *orig,
- const char *new,
+ const char *new_name,
const char *lang,
bfd_boolean literal_p)
{
struct bfd_elf_version_expr *ret;
- ret = xmalloc (sizeof *ret);
+ ret = (struct bfd_elf_version_expr *) xmalloc (sizeof *ret);
ret->next = orig;
- ret->pattern = literal_p ? NULL : new;
ret->symver = 0;
ret->script = 0;
- ret->symbol = literal_p ? new : realsymbol (new);
+ ret->literal = TRUE;
+ ret->pattern = literal_p ? new_name : realsymbol (new_name);
+ if (ret->pattern == NULL)
+ {
+ ret->pattern = new_name;
+ ret->literal = FALSE;
+ }
if (lang == NULL || strcasecmp (lang, "C") == 0)
ret->mask = BFD_ELF_VERSION_C_TYPE;
{
struct bfd_elf_version_tree *ret;
- ret = xcalloc (1, sizeof *ret);
+ ret = (struct bfd_elf_version_tree *) xcalloc (1, sizeof *ret);
ret->globals.list = globals;
ret->locals.list = locals;
ret->match = lang_vers_match;
static hashval_t
version_expr_head_hash (const void *p)
{
- const struct bfd_elf_version_expr *e = p;
+ const struct bfd_elf_version_expr *e =
+ (const struct bfd_elf_version_expr *) p;
- return htab_hash_string (e->symbol);
+ return htab_hash_string (e->pattern);
}
static int
version_expr_head_eq (const void *p1, const void *p2)
{
- const struct bfd_elf_version_expr *e1 = p1;
- const struct bfd_elf_version_expr *e2 = p2;
+ const struct bfd_elf_version_expr *e1 =
+ (const struct bfd_elf_version_expr *) p1;
+ const struct bfd_elf_version_expr *e2 =
+ (const struct bfd_elf_version_expr *) p2;
- return strcmp (e1->symbol, e2->symbol) == 0;
+ return strcmp (e1->pattern, e2->pattern) == 0;
}
static void
for (e = head->list; e; e = e->next)
{
- if (e->symbol)
+ if (e->literal)
count++;
head->mask |= e->mask;
}
for (e = head->list; e; e = next)
{
next = e->next;
- if (!e->symbol)
+ if (!e->literal)
{
*remaining_loc = e;
remaining_loc = &e->next;
}
else
{
- void **loc = htab_find_slot (head->htab, e, INSERT);
+ void **loc = htab_find_slot ((htab_t) head->htab, e, INSERT);
if (*loc)
{
struct bfd_elf_version_expr *e1, *last;
- e1 = *loc;
+ e1 = (struct bfd_elf_version_expr *) *loc;
last = NULL;
do
{
last = e1;
e1 = e1->next;
}
- while (e1 && strcmp (e1->symbol, e->symbol) == 0);
+ while (e1 && strcmp (e1->pattern, e->pattern) == 0);
if (last == NULL)
{
/* This is a duplicate. */
/* FIXME: Memory leak. Sometimes pattern is not
xmalloced alone, but in larger chunk of memory. */
- /* free (e->symbol); */
+ /* free (e->pattern); */
free (e);
}
else
if (name == NULL)
name = "";
- if ((name[0] == '\0' && lang_elf_version_info != NULL)
- || (lang_elf_version_info && lang_elf_version_info->name[0] == '\0'))
+ if (link_info.version_info != NULL
+ && (name[0] == '\0' || link_info.version_info->name[0] == '\0'))
{
einfo (_("%X%P: anonymous version tag cannot be combined"
" with other version tags\n"));
}
/* Make sure this node has a unique name. */
- for (t = lang_elf_version_info; t != NULL; t = t->next)
+ for (t = link_info.version_info; t != NULL; t = t->next)
if (strcmp (t->name, name) == 0)
einfo (_("%X%P: duplicate version tag `%s'\n"), name);
for (e1 = version->globals.list; e1 != NULL; e1 = e1->next)
{
- for (t = lang_elf_version_info; t != NULL; t = t->next)
+ for (t = link_info.version_info; t != NULL; t = t->next)
{
struct bfd_elf_version_expr *e2;
- if (t->locals.htab && e1->symbol)
+ if (t->locals.htab && e1->literal)
{
- e2 = htab_find (t->locals.htab, e1);
- while (e2 && strcmp (e1->symbol, e2->symbol) == 0)
+ e2 = (struct bfd_elf_version_expr *)
+ htab_find ((htab_t) t->locals.htab, e1);
+ while (e2 && strcmp (e1->pattern, e2->pattern) == 0)
{
if (e1->mask == e2->mask)
einfo (_("%X%P: duplicate expression `%s'"
- " in version information\n"), e1->symbol);
+ " in version information\n"), e1->pattern);
e2 = e2->next;
}
}
- else if (!e1->symbol)
+ else if (!e1->literal)
for (e2 = t->locals.remaining; e2 != NULL; e2 = e2->next)
if (strcmp (e1->pattern, e2->pattern) == 0
&& e1->mask == e2->mask)
for (e1 = version->locals.list; e1 != NULL; e1 = e1->next)
{
- for (t = lang_elf_version_info; t != NULL; t = t->next)
+ for (t = link_info.version_info; t != NULL; t = t->next)
{
struct bfd_elf_version_expr *e2;
- if (t->globals.htab && e1->symbol)
+ if (t->globals.htab && e1->literal)
{
- e2 = htab_find (t->globals.htab, e1);
- while (e2 && strcmp (e1->symbol, e2->symbol) == 0)
+ e2 = (struct bfd_elf_version_expr *)
+ htab_find ((htab_t) t->globals.htab, e1);
+ while (e2 && strcmp (e1->pattern, e2->pattern) == 0)
{
if (e1->mask == e2->mask)
einfo (_("%X%P: duplicate expression `%s'"
" in version information\n"),
- e1->symbol);
+ e1->pattern);
e2 = e2->next;
}
}
- else if (!e1->symbol)
+ else if (!e1->literal)
for (e2 = t->globals.remaining; e2 != NULL; e2 = e2->next)
if (strcmp (e1->pattern, e2->pattern) == 0
&& e1->mask == e2->mask)
else
version->vernum = 0;
- for (pp = &lang_elf_version_info; *pp != NULL; pp = &(*pp)->next)
+ for (pp = &link_info.version_info; *pp != NULL; pp = &(*pp)->next)
;
*pp = version;
}
struct bfd_elf_version_deps *ret;
struct bfd_elf_version_tree *t;
- ret = xmalloc (sizeof *ret);
+ ret = (struct bfd_elf_version_deps *) xmalloc (sizeof *ret);
ret->next = list;
- for (t = lang_elf_version_info; t != NULL; t = t->next)
+ for (t = link_info.version_info; t != NULL; t = t->next)
{
if (strcmp (t->name, name) == 0)
{
einfo (_("%X%P: unable to find version dependency `%s'\n"), name);
+ ret->version_needed = NULL;
return ret;
}
continue;
len = sec->size;
- contents = xmalloc (len);
+ contents = (char *) xmalloc (len);
if (!bfd_get_section_contents (is->the_bfd, sec, contents, 0, len))
einfo (_("%X%P: unable to read .exports section contents\n"), sec);
lang_new_vers_node (greg, lreg), NULL);
}
+/* Evaluate LENGTH and ORIGIN parts of MEMORY spec */
+
+static void
+lang_do_memory_regions (void)
+{
+ lang_memory_region_type *r = lang_memory_region_list;
+
+ for (; r != NULL; r = r->next)
+ {
+ if (r->origin_exp)
+ {
+ exp_fold_tree_no_dot (r->origin_exp);
+ if (expld.result.valid_p)
+ {
+ r->origin = expld.result.value;
+ r->current = r->origin;
+ }
+ else
+ einfo (_("%F%P: invalid origin for memory region %s\n"),
+ r->name_list.name);
+ }
+ if (r->length_exp)
+ {
+ exp_fold_tree_no_dot (r->length_exp);
+ if (expld.result.valid_p)
+ r->length = expld.result.value;
+ else
+ einfo (_("%F%P: invalid length for memory region %s\n"),
+ r->name_list.name);
+ }
+ }
+}
+
void
lang_add_unique (const char *name)
{
if (strcmp (ent->name, name) == 0)
return;
- ent = xmalloc (sizeof *ent);
+ ent = (struct unique_sections *) xmalloc (sizeof *ent);
ent->name = xstrdup (name);
ent->next = unique_section_list;
unique_section_list = ent;
{
struct bfd_elf_dynamic_list *d;
- d = xcalloc (1, sizeof *d);
+ d = (struct bfd_elf_dynamic_list *) xcalloc (1, sizeof *d);
d->head.list = dynamic;
d->match = lang_vers_match;
link_info.dynamic_list = d;
void
lang_append_dynamic_list_cpp_typeinfo (void)
{
- const char * symbols [] =
+ const char *symbols[] =
{
"typeinfo name for*",
"typeinfo for*"
void
lang_append_dynamic_list_cpp_new (void)
{
- const char * symbols [] =
+ const char *symbols[] =
{
"operator new*",
"operator delete*"
lang_append_dynamic_list (dynamic);
}
+
+/* Scan a space and/or comma separated string of features. */
+
+void
+lang_ld_feature (char *str)
+{
+ char *p, *q;
+
+ p = str;
+ while (*p)
+ {
+ char sep;
+ while (*p == ',' || ISSPACE (*p))
+ ++p;
+ if (!*p)
+ break;
+ q = p + 1;
+ while (*q && *q != ',' && !ISSPACE (*q))
+ ++q;
+ sep = *q;
+ *q = 0;
+ if (strcasecmp (p, "SANE_EXPR") == 0)
+ config.sane_expr = TRUE;
+ else
+ einfo (_("%X%P: unknown feature `%s'\n"), p);
+ *q = sep;
+ p = q;
+ }
+}
+
+/* Pretty print memory amount. */
+
+static void
+lang_print_memory_size (bfd_vma sz)
+{
+ if ((sz & 0x3fffffff) == 0)
+ printf ("%10" BFD_VMA_FMT "u GB", sz >> 30);
+ else if ((sz & 0xfffff) == 0)
+ printf ("%10" BFD_VMA_FMT "u MB", sz >> 20);
+ else if ((sz & 0x3ff) == 0)
+ printf ("%10" BFD_VMA_FMT "u KB", sz >> 10);
+ else
+ printf (" %10" BFD_VMA_FMT "u B", sz);
+}
+
+/* Implement --print-memory-usage: disply per region memory usage. */
+
+void
+lang_print_memory_usage (void)
+{
+ lang_memory_region_type *r;
+
+ printf ("Memory region Used Size Region Size %%age Used\n");
+ for (r = lang_memory_region_list; r->next != NULL; r = r->next)
+ {
+ bfd_vma used_length = r->current - r->origin;
+ double percent;
+
+ printf ("%16s: ",r->name_list.name);
+ lang_print_memory_size (used_length);
+ lang_print_memory_size ((bfd_vma) r->length);
+
+ percent = used_length * 100.0 / r->length;
+
+ printf (" %6.2f%%\n", percent);
+ }
+}