/* Linker command language support.
Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
- 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+ 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
Free Software Foundation, Inc.
This file is part of the GNU Binutils.
#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"
+#ifdef ENABLE_PLUGINS
+#include "plugin.h"
+#endif /* ENABLE_PLUGINS */
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) & (((TYPE*) 0)->MEMBER))
#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 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 bfd_boolean ldlang_sysrooted_script = FALSE;
/* Forward declarations. */
static void exp_init_os (etree_type *);
lang_statement_list_type input_file_chain;
struct bfd_sym_chain entry_symbol = { NULL, NULL };
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;
-bfd_boolean missing_file = FALSE;
/* Functions that traverse the linker script and might evaluate
DEFINED() need to increment this. */
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)
{
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/.ctors.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)
{
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);
if (member->usrdata != NULL)
{
walk_wild_section (s,
- (lang_input_statement_type *) member->usrdata,
- callback, data);
+ (lang_input_statement_type *) member->usrdata,
+ callback, data);
}
member = bfd_openr_next_archived_file (f->the_bfd, member);
{
lang_input_statement_type *p;
+ lang_has_input_file = TRUE;
+
if (add_to_list)
p = (lang_input_statement_type *) new_stat (lang_input_statement, stat_ptr);
else
{
p = (lang_input_statement_type *)
- stat_alloc (sizeof (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;
+ 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;
if (file_type == lang_input_file_is_l_enum
&& name[0] == ':' && name[1] != '\0')
{
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;
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->next_real_file = NULL;
- p->next = NULL;
- p->dynamic = config.dynamic_link;
- p->add_DT_NEEDED_for_dynamic = add_DT_NEEDED_for_dynamic;
- p->add_DT_NEEDED_for_regular = add_DT_NEEDED_for_regular;
- p->whole_archive = whole_archive;
- p->loaded = FALSE;
- p->missing_file = FALSE;
lang_statement_append (&input_file_chain,
(lang_statement_union_type *) p,
if (entry == NULL)
{
entry = (struct bfd_hash_entry *) bfd_hash_allocate (table,
- sizeof (*ret));
+ sizeof (*ret));
if (entry == NULL)
return entry;
}
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"),
- name);
- return r;
- }
+ {
+ 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));
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"));
+ 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. */
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"),
- alias);
+ 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);
}
/* Check if the target region exists. */
if (region == NULL)
einfo (_("%F%P:%S: error: memory region `%s' "
- "for alias `%s' does not exist\n"),
- region_name,
- alias);
+ "for alias `%s' does not exist\n"),
+ NULL, region_name, alias);
/* Add alias to region name list. */
n = (lang_memory_region_name *) stat_alloc (sizeof (lang_memory_region_name));
}
flags ^= sec->flags;
if (!(flags & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
- | SEC_READONLY))
- && !(look->flags & (SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
+ | SEC_READONLY | SEC_SMALL_DATA))
+ || (!(flags & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
+ | SEC_READONLY))
+ && !(look->flags & SEC_SMALL_DATA))
+ || (!(flags & (SEC_THREAD_LOCAL | SEC_ALLOC))
+ && (look->flags & SEC_THREAD_LOCAL)
+ && (!(flags & SEC_LOAD)
+ || (look->flags & SEC_LOAD))))
found = look;
}
}
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);
+ NULL, NULL, NULL, constraint);
ps = NULL;
if (config.build_constructors && *os_tail == os)
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_unop (ABSOLUTE,
- exp_nameop (NAME, ".")),
+ exp_nameop (NAME, "."),
FALSE));
}
}
if (add_child == NULL)
add_child = &os->children;
- lang_add_section (add_child, s, os);
+ lang_add_section (add_child, s, NULL, os);
if (after && (s->flags & (SEC_LOAD | SEC_ALLOC)) != 0)
{
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)
struct map_symbol_def *def;
ud = (struct fat_user_section_struct *)
- get_userdata (hash_entry->u.def.section);
+ 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 = (struct fat_user_section_struct *)
- get_userdata (hash_entry->u.def.section);
+ get_userdata (hash_entry->u.def.section);
}
else if (!ud->map_symbol_def_tail)
ud->map_symbol_def_tail = &ud->map_symbol_def_head;
if (!link_info.reduce_memory_overheads)
{
fat_section_userdata_type *new_userdata = (fat_section_userdata_type *)
- stat_alloc (sizeof (fat_section_userdata_type));
+ stat_alloc (sizeof (fat_section_userdata_type));
memset (new_userdata, 0, sizeof (fat_section_userdata_type));
get_userdata (s->bfd_section) = new_userdata;
}
/* 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 (sflag_info)
+ {
+ bfd_boolean keep;
+
+ keep = bfd_lookup_section_flags (&link_info, sflag_info, section);
+ if (!keep)
+ return;
+ }
+
if (section->output_section != NULL)
return;
to see .text with SEC_LINK_ONCE set. */
if (!link_info.relocatable)
- flags &= ~ (SEC_LINK_ONCE | SEC_LINK_DUPLICATES);
+ flags &= ~(SEC_LINK_ONCE | SEC_LINK_DUPLICATES | SEC_RELOC);
switch (output->sectype)
{
case noload_section:
flags &= ~SEC_LOAD;
flags |= SEC_NEVER_LOAD;
- if (((bfd_get_flavour (section->owner)
- == bfd_target_ecoff_flavour)
- || (bfd_get_flavour (section->owner)
- == bfd_target_coff_flavour)))
- {
- if ((flags & (SEC_COFF_SHARED_LIBRARY | SEC_DEBUGGING)) == 0)
- flags &= ~SEC_HAS_CONTENTS;
- }
- else
+ /* 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;
}
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)
{
of the current list. */
if (before == NULL)
- lang_add_section (&ptr->children, section, os);
+ 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, os);
+ 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 *output)
{
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))
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);
/* Do not process further if the file was missing. */
- if (entry->missing_file)
+ 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;
- bfd_boolean save_ldlang_sysrooted_script;
- bfd_boolean save_add_DT_NEEDED_for_regular;
- bfd_boolean save_add_DT_NEEDED_for_dynamic;
- bfd_boolean save_whole_archive;
+ struct lang_input_statement_flags save_flags;
+ extern FILE *yyin;
err = bfd_get_error ();
entry->the_bfd = NULL;
/* Try to interpret the file as a linker script. */
+ save_flags = input_flags;
ldfile_open_command_file (entry->filename);
push_stat_ptr (place);
- save_ldlang_sysrooted_script = ldlang_sysrooted_script;
- ldlang_sysrooted_script = entry->sysrooted;
- save_add_DT_NEEDED_for_regular = add_DT_NEEDED_for_regular;
- add_DT_NEEDED_for_regular = entry->add_DT_NEEDED_for_regular;
- save_add_DT_NEEDED_for_dynamic = add_DT_NEEDED_for_dynamic;
- add_DT_NEEDED_for_dynamic = entry->add_DT_NEEDED_for_dynamic;
- save_whole_archive = whole_archive;
- whole_archive = entry->whole_archive;
+ 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;
- add_DT_NEEDED_for_regular = save_add_DT_NEEDED_for_regular;
- add_DT_NEEDED_for_dynamic = save_add_DT_NEEDED_for_dynamic;
- whole_archive = save_whole_archive;
+ /* 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 TRUE;
}
break;
case bfd_object:
- ldlang_add_file (entry);
- if (trace_files || trace_file_tries)
+#ifdef ENABLE_PLUGINS
+ if (!entry->flags.reload)
+#endif
+ 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)
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);
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);
- return entry->loaded;
+ return entry->flags.loaded;
}
/* Handle a wild statement. S->FILENAME or S->SECTION_LIST or both
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? */
/* 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;
/* 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
+ has been loaded already. Do the same for a rescan. */
+ if (mode != OPEN_BFD_NORMAL
+#ifdef ENABLE_PLUGINS
+ && ((mode & OPEN_BFD_RESCAN) == 0
+ || plugin_insert == NULL)
+#endif
+ && !s->input_statement.flags.whole_archive
+ && s->input_statement.flags.loaded
+ && s->input_statement.the_bfd != NULL
&& bfd_check_format (s->input_statement.the_bfd,
bfd_archive))
- s->input_statement.loaded = FALSE;
+ s->input_statement.flags.loaded = FALSE;
+#ifdef ENABLE_PLUGINS
+ /* When rescanning, reload --as-needed shared libs. */
+ else if ((mode & OPEN_BFD_RESCAN) != 0
+ && plugin_insert == NULL
+ && s->input_statement.flags.loaded
+ && s->input_statement.flags.add_DT_NEEDED_for_regular
+ && s->input_statement.the_bfd != NULL
+ && ((s->input_statement.the_bfd->flags) & DYNAMIC) != 0
+ && plugin_should_reload (s->input_statement.the_bfd))
+ {
+ s->input_statement.flags.loaded = FALSE;
+ s->input_statement.flags.reload = TRUE;
+ }
+#endif
os_tail = lang_output_section_statement.tail;
lang_list_init (&add);
}
}
}
+#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;
}
/* Exit if any of the files were missing. */
- if (missing_file)
+ if (input_flags.missing_file)
einfo ("%F");
}
#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_undef = (ldlang_undef_chain_list_type *)
- stat_alloc (sizeof (ldlang_undef_chain_list_type));
+ ldlang_undef_chain_list_type *new_undef;
+ 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;
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:
flags = SEC_HAS_CONTENTS;
break;
case noload_section:
- flags = SEC_NEVER_LOAD;
+ 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)
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;
{
/* 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);
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);
+ 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);
+ return (scan_for_self_assignment (dst, rhs->trinary.lhs)
+ || scan_for_self_assignment (dst, rhs->trinary.rhs));
case etree_assign:
case etree_provided:
is_dot = (dst[0] == '.' && dst[1] == 0);
tree = assignment->exp->assign.src;
- computation_is_valid = is_dot || (scan_for_self_assignment (dst, tree) == FALSE);
+ computation_is_valid = is_dot || !scan_for_self_assignment (dst, tree);
}
osec = output_section->bfd_section;
if (h)
{
value = h->u.def.value;
-
- if (expld.result.section != NULL)
- value += expld.result.section->vma;
+ value += h->u.def.section->output_section->vma;
+ value += h->u.def.section->output_offset;
minfo ("[0x%V]", value);
}
entries[i] = def->entry;
qsort (entries, ud->map_symbol_def_count, sizeof (*entries),
- hash_entry_addr_cmp);
+ hash_entry_addr_cmp);
/* Print the symbols. */
for (i = 0; i < ud->map_symbol_def_count; i++)
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)
{
/* Make a new padding statement, linked into existing chain. */
pad = (lang_statement_union_type *)
- stat_alloc (sizeof (lang_padding_statement_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;
- if (!((lang_input_statement_type *) i->owner->usrdata)->just_syms_flag
+ if (i->sec_info_type != SEC_INFO_TYPE_JUST_SYMS
&& (i->flags & SEC_EXCLUDE) == 0)
{
- unsigned int alignment_needed;
+ bfd_size_type alignment_needed;
asection *o;
/* Align this section first to the input sections requirement,
return 0;
}
-/* On ELF, a debugging section must never set SEC_NEVER_LOAD, as no output
- would be written for it. So the combination of debugging and never-load
- is something which can only happen for pe-coff and must not be ignored. */
#define IGNORE_SECTION(s) \
- ((s->flags & (SEC_NEVER_LOAD | SEC_DEBUGGING)) == SEC_NEVER_LOAD \
- || (s->flags & SEC_ALLOC) == 0 \
- || ((s->flags & SEC_THREAD_LOCAL) != 0 \
+ ((s->flags & SEC_ALLOC) == 0 \
+ || ((s->flags & SEC_THREAD_LOCAL) != 0 \
&& (s->flags & SEC_LOAD) == 0))
/* Check to see if any allocated sections overlap with other allocated
for (s = link_info.output_bfd->sections; s != NULL; s = s->next)
{
/* Only consider loadable sections with real contents. */
- if ((s->flags & (SEC_NEVER_LOAD | SEC_DEBUGGING)) == SEC_NEVER_LOAD
- || !(s->flags & SEC_LOAD)
+ if (!(s->flags & SEC_LOAD)
|| !(s->flags & SEC_ALLOC)
|| s->size == 0)
continue;
bfd_vma newdot, after;
lang_output_section_statement_type *os;
lang_memory_region_type *r;
+ int section_alignment = 0;
os = &s->output_section_statement;
if (os->constraint == -1)
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)
}
else
{
- int align;
-
if (os->addr_tree == NULL)
{
/* No address specified for this section, get one
}
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
&& (config.warn_section_align
{
bfd_vma lma = os->lma_region->current;
- 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;
}
else if (r->last_os != NULL
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;
}
}
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;
}
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)
{
should have space allocated to it, unless the
user has explicitly stated that the section
should not be allocated. */
- if (output_section_statement->sectype != noalloc_section)
+ 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;
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
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
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:
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
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);
/* 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))
+ && !(entry_from_cmdline || undef_from_cmdline))
einfo (_("%P%F: gc-sections requires either an entry or "
"an undefined symbol\n"));
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,
/* 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;
= lang_output_section_statement_lookup (".bss", 0,
TRUE);
lang_add_section (&default_common_section->children, s,
- default_common_section);
+ NULL, default_common_section);
}
}
else
os = lang_output_section_statement_lookup (name,
constraint,
TRUE);
- lang_add_section (&os->children, s, os);
+ if (os->addr_tree == NULL
+ && (link_info.relocatable
+ || (s->flags & (SEC_LOAD | SEC_ALLOC)) == 0))
+ os->addr_tree = exp_intop (0);
+ lang_add_section (&os->children, s, NULL, os);
}
}
}
}
}
-static lang_output_section_statement_type *current_section;
-
static int
topower (int x)
{
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_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)
{
/* Do all the assignments with our current guesses as to
section sizes. */
- lang_do_assignments ();
+ lang_do_assignments (lang_assigning_phase_enum);
/* 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_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 && (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 */
+
void
lang_process (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 (plugin_active_plugins_p ())
+ {
+ 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. */
+ link_info.loading_lto_outputs = TRUE;
+ 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 */
link_info.gc_sym_list = &entry_symbol;
if (entry_symbol.name == NULL)
files. */
ldctor_build_sets ();
+ /* 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);
+ expld.phase = lang_first_phase_enum;
+
/* Remove unreferenced sections if asked to. */
lang_gc_sections ();
/* 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 ();
/* 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 ();
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_stmt->filename = filespec->name;
new_stmt->filenames_sorted = filespec->sorted == by_name;
+ new_stmt->section_flag_list = filespec->section_flag_list;
}
new_stmt->section_list = section_list;
new_stmt->keep_sections = keep_sections;
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
*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
current_section->load_base != NULL,
current_section->addr_tree != NULL);
- /* If this section has no load region or base, but has the same
+ /* 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. */
- if (!current_section->lma_region && !current_section->load_base
+ 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;
pop_stat_ptr ();
}
-/* 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;
-
- 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));
-
- h->u.def.section = bfd_abs_section_ptr;
- }
-}
-
void
lang_statement_append (lang_statement_list_type *list,
lang_statement_union_type *element,
&& (*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"));
+ einfo (_("%X%P:%S: PHDRS and FILEHDR are not supported"
+ " when prior PT_LOAD headers lack them\n"), NULL);
hdrs = FALSE;
}
{
alc *= 2;
secs = (asection **) xrealloc (secs,
- alc * sizeof (asection *));
+ alc * sizeof (asection *));
}
secs[c] = os->bfd_section;
++c;
/* 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)
\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;
}
case 0:
if (head->mask & BFD_ELF_VERSION_C_TYPE)
{
- e.pattern = sym;
+ e.pattern = c_sym;
expr = (struct bfd_elf_version_expr *)
- htab_find ((htab_t) head->htab, &e);
- while (expr && strcmp (expr->pattern, sym) == 0)
+ 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
{
e.pattern = cxx_sym;
expr = (struct bfd_elf_version_expr *)
- htab_find ((htab_t) head->htab, &e);
+ 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;
{
e.pattern = java_sym;
expr = (struct bfd_elf_version_expr *)
- htab_find ((htab_t) head->htab, &e);
+ 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 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)
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->literal)
{
e2 = (struct bfd_elf_version_expr *)
- htab_find ((htab_t) t->locals.htab, e1);
+ htab_find ((htab_t) t->locals.htab, e1);
while (e2 && strcmp (e1->pattern, e2->pattern) == 0)
{
if (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->literal)
{
e2 = (struct bfd_elf_version_expr *)
- htab_find ((htab_t) t->globals.htab, e1);
+ htab_find ((htab_t) t->globals.htab, e1);
while (e2 && strcmp (e1->pattern, e2->pattern) == 0)
{
if (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;
}
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)
{
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;
+ }
+}