/* Linker command language support.
- Copyright (C) 1991-2017 Free Software Foundation, Inc.
+ Copyright (C) 1991-2020 Free Software Foundation, Inc.
This file is part of the GNU Binutils.
MA 02110-1301, USA. */
#include "sysdep.h"
+#include <limits.h>
#include "bfd.h"
#include "libiberty.h"
#include "filenames.h"
#include "safe-ctype.h"
#include "obstack.h"
#include "bfdlink.h"
+#include "ctf-api.h"
#include "ld.h"
#include "ldmain.h"
#include "demangle.h"
#include "hashtab.h"
#include "elf-bfd.h"
-#ifdef ENABLE_PLUGINS
+#if BFD_SUPPORTS_PLUGINS
#include "plugin.h"
-#endif /* ENABLE_PLUGINS */
+#endif /* BFD_SUPPORTS_PLUGINS */
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) & (((TYPE*) 0)->MEMBER))
static bfd_vma print_dot;
static lang_input_statement_type *first_file;
static const char *current_target;
+/* Header for list of statements corresponding to any files involved in the
+ link, either specified from the command-line or added implicitely (eg.
+ archive member used to resolved undefined symbol, wildcard statement from
+ linker script, etc.). Next pointer is in next field of a
+ lang_statement_header_type (reached via header field in a
+ lang_statement_union). */
static lang_statement_list_type statement_list;
static lang_statement_list_type *stat_save[10];
static lang_statement_list_type **stat_save_ptr = &stat_save[0];
/* 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 lang_os_list;
lang_statement_list_type *stat_ptr = &statement_list;
+/* Header for list of statements corresponding to files used in the final
+ executable. This can be either object file specified on the command-line
+ or library member resolving an undefined reference. Next pointer is in next
+ field of a lang_input_statement_type (reached via input_statement field in a
+ lang_statement_union). */
lang_statement_list_type file_chain = { NULL, NULL };
+/* Header for list of statements corresponding to files specified on the
+ command-line for linking. It thus contains real object files and archive
+ but not archive members. Next pointer is in next_real_file field of a
+ lang_input_statement_type statement (reached via input_statement field in a
+ lang_statement_union). */
lang_statement_list_type input_file_chain;
+static const char *current_input_file;
+struct bfd_elf_dynamic_list **current_dynamic_list_p;
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;
struct lang_phdr *lang_phdr_list;
struct lang_nocrossrefs *nocrossref_list;
struct asneeded_minfo **asneeded_list_tail;
+#ifdef ENABLE_LIBCTF
+static ctf_file_t *ctf_output;
+#endif
- /* Functions that traverse the linker script and might evaluate
- DEFINED() need to increment this at the start of the traversal. */
+/* Functions that traverse the linker script and might evaluate
+ DEFINED() need to increment this at the start of the traversal. */
int lang_statement_iteration = 0;
+/* Count times through one_lang_size_sections_pass after mark phase. */
+static int lang_sizing_iteration = 0;
+
/* 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
#define outside_symbol_address(q) \
((q)->value + outside_section_address (q->section))
-#define SECTION_NAME_MAP_LENGTH (16)
+/* CTF sections smaller than this are not compressed: compression of
+ dictionaries this small doesn't gain much, and this lets consumers mmap the
+ sections directly out of the ELF file and use them with no decompression
+ overhead if they want to. */
+#define CTF_COMPRESSION_THRESHOLD 4096
void *
stat_alloc (size_t size)
return strcmp (pattern, name);
}
+static char *
+ldirname (const char *name)
+{
+ const char *base = lbasename (name);
+ char *dirname;
+
+ while (base > name && IS_DIR_SEPARATOR (base[-1]))
+ --base;
+ if (base == name)
+ return strdup (".");
+ dirname = strdup (name);
+ dirname[base - name] = '\0';
+ return dirname;
+}
+
/* If PATTERN is of the form archive:file, return a pointer to the
separator. If not, return NULL. */
if (sep != file_spec)
{
- const char *aname = f->the_bfd->my_archive->filename;
+ const char *aname = bfd_get_filename (f->the_bfd->my_archive);
*sep = 0;
match = name_match (file_spec, aname) == 0;
*sep = link_info.path_separator;
else if (file->the_bfd != NULL
&& file->the_bfd->my_archive != NULL
&& name_match (list_tmp->name,
- file->the_bfd->my_archive->filename) == 0)
+ bfd_get_filename (file->the_bfd->my_archive)) == 0)
return TRUE;
}
if (sec->spec.name != NULL)
{
- const char *sname = bfd_get_section_name (file->the_bfd, s);
+ const char *sname = bfd_section_name (s);
skip = name_match (sec->spec.name, sname) != 0;
}
/* Return the numerical value of the init_priority attribute from
section name NAME. */
-static unsigned long
-get_init_priority (const char *name)
+static int
+get_init_priority (const asection *sec)
{
- char *end;
- unsigned long init_priority;
+ const char *name = bfd_section_name (sec);
+ const char *dot;
/* GCC uses the following section names for the init_priority
- attribute with numerical values 101 and 65535 inclusive. A
+ attribute with numerical values 101 to 65535 inclusive. A
lower value means a higher priority.
- 1: .init_array.NNNN/.fini_array.NNNN: Where NNNN is the
+ 1: .init_array.NNNNN/.fini_array.NNNNN: Where NNNNN 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
+ 2: .ctors.NNNNN/.dtors.NNNNN: Where NNNNN 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;
+ .init_array.NNNNN sections would normally be placed in an output
+ .init_array section, .fini_array.NNNNN in .fini_array,
+ .ctors.NNNNN in .ctors, and .dtors.NNNNN in .dtors. This means
+ we should sort by increasing number (and could just use
+ SORT_BY_NAME in scripts). However if .ctors.NNNNN sections are
+ being placed in .init_array (which may also contain
+ .init_array.NNNNN sections) or .dtors.NNNNN sections are being
+ placed in .fini_array then we need to extract the init_priority
+ attribute and sort on that. */
+ dot = strrchr (name, '.');
+ if (dot != NULL && ISDIGIT (dot[1]))
+ {
+ char *end;
+ unsigned long init_priority = strtoul (dot + 1, &end, 10);
+ if (*end == 0)
+ {
+ if (dot == name + 6
+ && (strncmp (name, ".ctors", 6) == 0
+ || strncmp (name, ".dtors", 6) == 0))
+ init_priority = 65535 - init_priority;
+ if (init_priority <= INT_MAX)
+ return init_priority;
+ }
+ }
+ return -1;
}
/* Compare sections ASEC and BSEC according to SORT. */
compare_section (sort_type sort, asection *asec, asection *bsec)
{
int ret;
- unsigned long ainit_priority, binit_priority;
+ int a_priority, b_priority;
switch (sort)
{
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)
+ a_priority = get_init_priority (asec);
+ b_priority = get_init_priority (bsec);
+ if (a_priority < 0 || b_priority < 0)
goto sort_by_name;
- ret = ainit_priority - binit_priority;
+ ret = a_priority - b_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));
+ ret = bfd_section_alignment (bsec) - bfd_section_alignment (asec);
if (ret)
break;
/* Fall through. */
case by_name:
-sort_by_name:
- ret = strcmp (bfd_get_section_name (asec->owner, asec),
- bfd_get_section_name (bsec->owner, bsec));
+ sort_by_name:
+ ret = strcmp (bfd_section_name (asec), bfd_section_name (bsec));
break;
case by_name_alignment:
- ret = strcmp (bfd_get_section_name (asec->owner, asec),
- bfd_get_section_name (bsec->owner, bsec));
+ ret = strcmp (bfd_section_name (asec), bfd_section_name (bsec));
if (ret)
break;
/* Fall through. */
case by_alignment:
- ret = (bfd_section_alignment (bsec->owner, bsec)
- - bfd_section_alignment (asec->owner, asec));
+ ret = bfd_section_alignment (bsec) - bfd_section_alignment (asec);
break;
}
for (s = file->the_bfd->sections; s != NULL; s = s->next)
{
- const char *sname = bfd_get_section_name (file->the_bfd, s);
+ const char *sname = bfd_section_name (s);
bfd_boolean skip = !match_simple_wild (wildsec0->spec.name, sname);
if (!skip)
walk_wild_consider_section (ptr, file, s, sec0, callback, data);
else
{
- const char *sname = bfd_get_section_name (file->the_bfd, s);
+ const char *sname = bfd_section_name (s);
bfd_boolean skip = !match_simple_wild (wildsec1->spec.name, sname);
if (!skip)
walk_wild_consider_section (ptr, file, s, sec0, callback, data);
else
{
- const char *sname = bfd_get_section_name (file->the_bfd, s);
+ const char *sname = bfd_section_name (s);
bfd_boolean skip = !match_simple_wild (wildsec1->spec.name, sname);
if (!skip)
walk_wild_consider_section (ptr, file, s, sec1, callback, data);
else
{
- const char *sname = bfd_get_section_name (file->the_bfd, s);
+ const char *sname = bfd_section_name (s);
bfd_boolean skip = !match_simple_wild (wildsec2->spec.name,
sname);
archive which is included, BFD will call ldlang_add_file,
which will set the usrdata field of the member to the
lang_input_statement. */
- if (member->usrdata != NULL)
- {
- walk_wild_section (s,
- (lang_input_statement_type *) member->usrdata,
- callback, data);
- }
+ if (bfd_usrdata (member) != NULL)
+ walk_wild_section (s, bfd_usrdata (member), callback, data);
member = bfd_openr_next_archived_file (f->the_bfd, member);
}
list->tail = &list->head;
}
+static void
+lang_statement_append (lang_statement_list_type *list,
+ void *element,
+ void *field)
+{
+ *(list->tail) = element;
+ list->tail = field;
+}
+
void
push_stat_ptr (lang_statement_list_type *new_ptr)
{
{
lang_statement_union_type *new_stmt;
- new_stmt = (lang_statement_union_type *) stat_alloc (size);
+ new_stmt = stat_alloc (size);
new_stmt->header.type = type;
new_stmt->header.next = NULL;
lang_statement_append (list, new_stmt, &new_stmt->header.next);
new_afile (const char *name,
lang_input_file_enum_type file_type,
const char *target,
- bfd_boolean add_to_list)
+ const char *from_filename)
{
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));
- p->header.type = lang_input_statement_enum;
- p->header.next = NULL;
- }
-
+ p = new_stat (lang_input_statement, stat_ptr);
memset (&p->the_bfd, 0,
sizeof (*p) - offsetof (lang_input_statement_type, the_bfd));
+ p->extra_search_path = NULL;
p->target = target;
p->flags.dynamic = input_flags.dynamic;
p->flags.add_DT_NEEDED_for_dynamic = input_flags.add_DT_NEEDED_for_dynamic;
case lang_input_file_is_search_file_enum:
p->filename = name;
p->local_sym_name = name;
+ /* If name is a relative path, search the directory of the current linker
+ script first. */
+ if (from_filename && !IS_ABSOLUTE_PATH (name))
+ p->extra_search_path = ldirname (from_filename);
p->flags.real = TRUE;
p->flags.search_dirs = TRUE;
break;
FAIL ();
}
- lang_statement_append (&input_file_chain,
- (lang_statement_union_type *) p,
- &p->next_real_file);
+ lang_statement_append (&input_file_chain, p, &p->next_real_file);
return p;
}
within the sysroot subdirectory.) */
unsigned int outer_sysrooted = input_flags.sysrooted;
input_flags.sysrooted = 0;
- ret = new_afile (sysrooted_name, file_type, target, TRUE);
+ ret = new_afile (sysrooted_name, file_type, target, NULL);
input_flags.sysrooted = outer_sysrooted;
return ret;
}
- return new_afile (name, file_type, target, TRUE);
+ return new_afile (name, file_type, target, current_input_file);
}
struct out_section_hash_entry
ret = (struct out_section_hash_entry *) entry;
memset (&ret->s, 0, sizeof (ret->s));
ret->s.header.type = lang_output_section_statement_enum;
- ret->s.output_section_statement.subsection_alignment = -1;
- ret->s.output_section_statement.section_alignment = -1;
+ ret->s.output_section_statement.subsection_alignment = NULL;
+ ret->s.output_section_statement.section_alignment = NULL;
ret->s.output_section_statement.block_value = 1;
lang_list_init (&ret->s.output_section_statement.children);
lang_statement_append (stat_ptr, &ret->s, &ret->s.header.next);
/* For every output section statement added to the list, except the
- first one, lang_output_section_statement.tail points to the "next"
+ first one, lang_os_list.tail points to the "next"
field of the last element of the list. */
- if (lang_output_section_statement.head != NULL)
+ if (lang_os_list.head != NULL)
ret->s.output_section_statement.prev
= ((lang_output_section_statement_type *)
- ((char *) lang_output_section_statement.tail
+ ((char *) lang_os_list.tail
- offsetof (lang_output_section_statement_type, next)));
/* GCC's strict aliasing rules prevent us from just casting the
address, so we store the pointer in a variable and cast that
instead. */
nextp = &ret->s.output_section_statement.next;
- lang_statement_append (&lang_output_section_statement,
- &ret->s,
- (lang_statement_union_type **) nextp);
+ lang_statement_append (&lang_os_list, &ret->s, nextp);
return &ret->root;
}
output_section_statement_newfunc,
sizeof (struct out_section_hash_entry),
61))
- einfo (_("%P%F: can not create hash table: %E\n"));
+ einfo (_("%F%P: can not create hash table: %E\n"));
}
static void
lang_list_init (stat_ptr);
lang_list_init (&input_file_chain);
- lang_list_init (&lang_output_section_statement);
+ lang_list_init (&lang_os_list);
lang_list_init (&file_chain);
first_file = lang_add_input_file (NULL, lang_input_file_is_marker_enum,
NULL);
if (strcmp (n->name, name) == 0)
{
if (create)
- einfo (_("%P:%S: warning: redeclaration of memory region `%s'\n"),
+ einfo (_("%P:%pS: 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"),
+ einfo (_("%P:%pS: warning: memory region `%s' not declared\n"),
NULL, name);
- new_region = (lang_memory_region_type *)
- stat_alloc (sizeof (lang_memory_region_type));
+ new_region = stat_alloc (sizeof (lang_memory_region_type));
new_region->name_list.name = xstrdup (name);
new_region->name_list.next = NULL;
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);
+ einfo (_("%F%P:%pS: error: alias for default memory region\n"), NULL);
/* Look for the target region and check if the alias is not already
in use. */
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 "
+ einfo (_("%F%P:%pS: 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' "
+ einfo (_("%F%P:%pS: error: memory region `%s' "
"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));
+ n = stat_alloc (sizeof (lang_memory_region_name));
n->name = xstrdup (alias);
n->next = region->name_list.next;
region->name_list.next = n;
lang_output_section_statement_type *
lang_output_section_get (const asection *output_section)
{
- return get_userdata (output_section);
+ return bfd_section_userdata (output_section);
}
/* Find or create an output_section_statement with the given NAME.
if (entry == NULL)
{
if (create)
- einfo (_("%P%F: failed creating section `%s': %E\n"), name);
+ einfo (_("%F%P: failed creating section `%s': %E\n"), name);
return NULL;
}
name));
if (entry == NULL)
{
- einfo (_("%P%F: failed creating section `%s': %E\n"), name);
+ einfo (_("%F%P: failed creating section `%s': %E\n"), name);
return NULL;
}
entry->root = last_ent->root;
/* We know the first statement on this list is *ABS*. May as well
skip it. */
- first = &lang_output_section_statement.head->output_section_statement;
+ first = (void *) lang_os_list.head;
first = first->next;
/* First try for an exact match. */
lang_statement_union_type **assign = NULL;
bfd_boolean ignore_first;
- ignore_first
- = after == &lang_output_section_statement.head->output_section_statement;
+ ignore_first = after == (void *) lang_os_list.head;
for (where = &after->header.next;
*where != NULL;
ass = &(*where)->assignment_statement;
if (ass->exp->type.node_class != etree_assert
&& ass->exp->assign.dst[0] == '.'
- && ass->exp->assign.dst[1] == 0
- && !ignore_first)
- assign = where;
+ && ass->exp->assign.dst[1] == 0)
+ {
+ if (!ignore_first)
+ assign = where;
+ ignore_first = FALSE;
+ }
}
- ignore_first = FALSE;
continue;
case lang_wild_statement_enum:
case lang_input_section_enum:
case lang_padding_statement_enum:
case lang_constructors_statement_enum:
assign = NULL;
+ ignore_first = FALSE;
continue;
case lang_output_section_statement_enum:
if (assign != NULL)
|| (s->flags & (SEC_LOAD | SEC_ALLOC)) == 0)
address = exp_intop (0);
- os_tail = ((lang_output_section_statement_type **)
- lang_output_section_statement.tail);
+ os_tail = (lang_output_section_statement_type **) lang_os_list.tail;
os = lang_enter_output_section_statement (secname, address, normal_section,
NULL, NULL, NULL, constraint, 0);
if (after != NULL && os->bfd_section != NULL)
{
asection *snew, *as;
+ bfd_boolean place_after = place->stmt == NULL;
+ bfd_boolean insert_after = TRUE;
snew = os->bfd_section;
/* Shuffle the bfd section list to make the output file look
neater. This is really only cosmetic. */
if (place->section == NULL
- && after != (&lang_output_section_statement.head
- ->output_section_statement))
+ && after != (void *) lang_os_list.head)
{
asection *bfd_section = after->bfd_section;
/* Now tack it back on in the right place. */
bfd_section_list_append (link_info.output_bfd, snew);
}
+ else if ((bfd_get_flavour (link_info.output_bfd)
+ == bfd_target_elf_flavour)
+ && (bfd_get_flavour (s->owner)
+ == bfd_target_elf_flavour)
+ && ((elf_section_type (s) == SHT_NOTE
+ && (s->flags & SEC_LOAD) != 0)
+ || (elf_section_type (as) == SHT_NOTE
+ && (as->flags & SEC_LOAD) != 0)))
+ {
+ /* Make sure that output note sections are grouped and sorted
+ by alignments when inserting a note section or insert a
+ section after a note section, */
+ asection *sec;
+ /* A specific section after which the output note section
+ should be placed. */
+ asection *after_sec;
+ /* True if we need to insert the orphan section after a
+ specific section to maintain output note section order. */
+ bfd_boolean after_sec_note = FALSE;
+
+ static asection *first_orphan_note = NULL;
+
+ /* Group and sort output note section by alignments in
+ ascending order. */
+ after_sec = NULL;
+ if (elf_section_type (s) == SHT_NOTE
+ && (s->flags & SEC_LOAD) != 0)
+ {
+ /* Search from the beginning for the last output note
+ section with equal or larger alignments. NB: Don't
+ place orphan note section after non-note sections. */
+
+ first_orphan_note = NULL;
+ for (sec = link_info.output_bfd->sections;
+ (sec != NULL
+ && !bfd_is_abs_section (sec));
+ sec = sec->next)
+ if (sec != snew
+ && elf_section_type (sec) == SHT_NOTE
+ && (sec->flags & SEC_LOAD) != 0)
+ {
+ if (!first_orphan_note)
+ first_orphan_note = sec;
+ if (sec->alignment_power >= s->alignment_power)
+ after_sec = sec;
+ }
+ else if (first_orphan_note)
+ {
+ /* Stop if there is non-note section after the first
+ orphan note section. */
+ break;
+ }
+
+ /* If this will be the first orphan note section, it can
+ be placed at the default location. */
+ after_sec_note = first_orphan_note != NULL;
+ if (after_sec == NULL && after_sec_note)
+ {
+ /* If all output note sections have smaller
+ alignments, place the section before all
+ output orphan note sections. */
+ after_sec = first_orphan_note;
+ insert_after = FALSE;
+ }
+ }
+ else if (first_orphan_note)
+ {
+ /* Don't place non-note sections in the middle of orphan
+ note sections. */
+ after_sec_note = TRUE;
+ after_sec = as;
+ for (sec = as->next;
+ (sec != NULL
+ && !bfd_is_abs_section (sec));
+ sec = sec->next)
+ if (elf_section_type (sec) == SHT_NOTE
+ && (sec->flags & SEC_LOAD) != 0)
+ after_sec = sec;
+ }
+
+ if (after_sec_note)
+ {
+ if (after_sec)
+ {
+ /* Search forward to insert OS after AFTER_SEC output
+ statement. */
+ lang_output_section_statement_type *stmt, *next;
+ bfd_boolean found = FALSE;
+ for (stmt = after; stmt != NULL; stmt = next)
+ {
+ next = stmt->next;
+ if (insert_after)
+ {
+ if (stmt->bfd_section == after_sec)
+ {
+ place_after = TRUE;
+ found = TRUE;
+ after = stmt;
+ break;
+ }
+ }
+ else
+ {
+ /* If INSERT_AFTER is FALSE, place OS before
+ AFTER_SEC output statement. */
+ if (next && next->bfd_section == after_sec)
+ {
+ place_after = TRUE;
+ found = TRUE;
+ after = stmt;
+ break;
+ }
+ }
+ }
+
+ /* Search backward to insert OS after AFTER_SEC output
+ statement. */
+ if (!found)
+ for (stmt = after; stmt != NULL; stmt = stmt->prev)
+ {
+ if (insert_after)
+ {
+ if (stmt->bfd_section == after_sec)
+ {
+ place_after = TRUE;
+ after = stmt;
+ break;
+ }
+ }
+ else
+ {
+ /* If INSERT_AFTER is FALSE, place OS before
+ AFTER_SEC output statement. */
+ if (stmt->next->bfd_section == after_sec)
+ {
+ place_after = TRUE;
+ after = stmt;
+ break;
+ }
+ }
+ }
+ }
+
+ if (after_sec == NULL
+ || (insert_after && after_sec->next != snew)
+ || (!insert_after && after_sec->prev != snew))
+ {
+ /* Unlink the section. */
+ bfd_section_list_remove (link_info.output_bfd, snew);
+
+ /* Place SNEW after AFTER_SEC. If AFTER_SEC is NULL,
+ prepend SNEW. */
+ if (after_sec)
+ {
+ if (insert_after)
+ bfd_section_list_insert_after (link_info.output_bfd,
+ after_sec, snew);
+ else
+ bfd_section_list_insert_before (link_info.output_bfd,
+ after_sec, snew);
+ }
+ else
+ bfd_section_list_prepend (link_info.output_bfd, snew);
+ }
+ }
+ else if (as != snew && as->prev != snew)
+ {
+ /* Unlink the section. */
+ bfd_section_list_remove (link_info.output_bfd, snew);
+
+ /* Now tack it back on in the right place. */
+ bfd_section_list_insert_before (link_info.output_bfd,
+ as, snew);
+ }
+ }
else if (as != snew && as->prev != snew)
{
/* Unlink the section. */
{
lang_output_section_statement_type *newly_added_os;
- if (place->stmt == NULL)
+ /* Place OS after AFTER if AFTER_NOTE is TRUE. */
+ if (place_after)
{
lang_statement_union_type **where = insert_os_after (after);
assigning *os_tail = NULL, but possibly added it back in
the same place when assigning *place->os_tail. */
if (*os_tail == NULL)
- lang_output_section_statement.tail
- = (lang_statement_union_type **) os_tail;
+ lang_os_list.tail = (lang_statement_union_type **) os_tail;
}
}
return os;
}
if (m->ref != NULL)
- minfo ("%B ", m->ref);
- minfo ("(%T)\n", m->name);
+ minfo ("%pB ", m->ref);
+ minfo ("(%pT)\n", m->name);
}
}
|| file->flags.just_syms)
continue;
- for (s = file->the_bfd->sections; s != NULL; s = s->next)
- if ((s->output_section == NULL
- || s->output_section->owner != link_info.output_bfd)
- && (s->flags & (SEC_LINKER_CREATED | SEC_KEEP)) == 0)
- {
- if (!dis_header_printed)
- {
- fprintf (config.map_file, _("\nDiscarded input sections\n\n"));
- dis_header_printed = TRUE;
- }
+ if (config.print_map_discarded)
+ for (s = file->the_bfd->sections; s != NULL; s = s->next)
+ if ((s->output_section == NULL
+ || s->output_section->owner != link_info.output_bfd)
+ && (s->flags & (SEC_LINKER_CREATED | SEC_KEEP)) == 0)
+ {
+ if (! dis_header_printed)
+ {
+ fprintf (config.map_file, _("\nDiscarded input sections\n\n"));
+ dis_header_printed = TRUE;
+ }
- print_input_section (s, TRUE);
- }
+ print_input_section (s, TRUE);
+ }
}
minfo (_("\nMemory Configuration\n\n"));
obstack_begin (&map_obstack, 1000);
bfd_link_hash_traverse (link_info.hash, sort_def_symbol, 0);
}
+ expld.phase = lang_fixed_phase_enum;
lang_statement_iteration++;
print_statements ();
input_section_userdata_type *ud;
struct map_symbol_def *def;
- ud = ((input_section_userdata_type *)
- get_userdata (hash_entry->u.def.section));
+ ud = bfd_section_userdata (hash_entry->u.def.section);
if (!ud)
{
- ud = (input_section_userdata_type *) stat_alloc (sizeof (*ud));
- get_userdata (hash_entry->u.def.section) = ud;
+ ud = stat_alloc (sizeof (*ud));
+ bfd_set_section_userdata (hash_entry->u.def.section, ud);
ud->map_symbol_def_tail = &ud->map_symbol_def_head;
ud->map_symbol_def_count = 0;
}
init_os (lang_output_section_statement_type *s, flagword flags)
{
if (strcmp (s->name, DISCARD_SECTION_NAME) == 0)
- einfo (_("%P%F: Illegal use of `%s' section\n"), DISCARD_SECTION_NAME);
+ einfo (_("%F%P: illegal use of `%s' section\n"), DISCARD_SECTION_NAME);
if (s->constraint != SPECIAL)
s->bfd_section = bfd_get_section_by_name (link_info.output_bfd, s->name);
s->name, flags);
if (s->bfd_section == NULL)
{
- einfo (_("%P%F: output format %s cannot represent section"
+ einfo (_("%F%P: output format %s cannot represent section"
" called %s: %E\n"),
link_info.output_bfd->xvec->name, s->name);
}
/* Set the userdata of the output section to the output section
statement to avoid lookup. */
- get_userdata (s->bfd_section) = s;
+ bfd_set_section_userdata (s->bfd_section, s);
/* If there is a base address, make sure that any sections it might
mention are initialized. */
exp_init_os (s->load_base);
/* If supplied an alignment, set it. */
- if (s->section_alignment != -1)
- s->bfd_section->alignment_power = s->section_alignment;
+ if (s->section_alignment != NULL)
+ s->bfd_section->alignment_power = exp_get_power (s->section_alignment,
+ "section alignment");
}
/* Make sure that all output sections mentioned in an expression are
{
case etree_assign:
case etree_provide:
+ case etree_provided:
exp_init_os (exp->assign.src);
break;
/* This prevents future calls from assigning this section. */
section->output_section = bfd_abs_section_ptr;
}
+ else if (link_info.non_contiguous_regions_warnings)
+ einfo (_("%P:%pS: warning: --enable-non-contiguous-regions makes "
+ "section `%pA' from '%pB' match /DISCARD/ clause.\n"),
+ NULL, section, section->owner);
+
return;
}
}
if (section->output_section != NULL)
- return;
+ {
+ if (!link_info.non_contiguous_regions)
+ return;
+
+ /* SECTION has already been handled in a special way
+ (eg. LINK_ONCE): skip it. */
+ if (bfd_is_abs_section (section->output_section))
+ return;
+
+ /* Already assigned to the same output section, do not process
+ it again, to avoid creating loops between duplicate sections
+ later. */
+ if (section->output_section == output->bfd_section)
+ return;
+
+ if (link_info.non_contiguous_regions_warnings && output->bfd_section)
+ einfo (_("%P:%pS: warning: --enable-non-contiguous-regions may "
+ "change behaviour for section `%pA' from '%pB' (assigned to "
+ "%pA, but additional match: %pA)\n"),
+ NULL, section, section->owner, section->output_section,
+ output->bfd_section);
+
+ /* SECTION has already been assigned to an output section, but
+ the user allows it to be mapped to another one in case it
+ overflows. We'll later update the actual output section in
+ size_input_section as appropriate. */
+ }
/* 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
{
case normal_section:
case overlay_section:
+ case first_overlay_section:
break;
case noalloc_section:
flags &= ~SEC_ALLOC;
}
else
{
- ln = ls->section->owner->filename;
+ ln = bfd_get_filename (ls->section->owner);
la = FALSE;
}
if (fa)
fn = file->filename;
if (la)
- ln = ls->section->owner->filename;
+ ln = bfd_get_filename (ls->section->owner);
i = filename_cmp (fn, ln);
if (i > 0)
{
lang_input_statement_type *search;
- for (search = (lang_input_statement_type *) input_file_chain.head;
+ for (search = (void *) input_file_chain.head;
search != NULL;
- search = (lang_input_statement_type *) search->next_real_file)
+ search = search->next_real_file)
{
/* Use the local_sym_name as the name of the file that has
already been loaded as filename might have been transformed
}
if (search == NULL)
- search = new_afile (name, lang_input_file_is_search_file_enum,
- default_target, FALSE);
+ {
+ /* Arrange to splice the input statement added by new_afile into
+ statement_list after the current input_file_chain tail.
+ We know input_file_chain is not an empty list, and that
+ lookup_name was called via open_input_bfds. Later calls to
+ lookup_name should always match an existing input_statement. */
+ lang_statement_union_type **tail = stat_ptr->tail;
+ lang_statement_union_type **after
+ = (void *) ((char *) input_file_chain.tail
+ - offsetof (lang_input_statement_type, next_real_file)
+ + offsetof (lang_input_statement_type, header.next));
+ lang_statement_union_type *rest = *after;
+ stat_ptr->tail = after;
+ search = new_afile (name, lang_input_file_is_search_file_enum,
+ default_target, NULL);
+ *stat_ptr->tail = rest;
+ if (*tail == NULL)
+ stat_ptr->tail = tail;
+ }
/* If we have already added this file, or this file is not real
don't add this file. */
while (lib)
{
int len = strlen (lib->name);
- const char *filename = lbasename (abfd->filename);
+ const char *filename = lbasename (bfd_get_filename (abfd));
if (strcmp (lib->name, "ALL") == 0)
{
if (entry->flags.missing_file)
return TRUE;
+ if (trace_files || verbose)
+ info_msg ("%pI\n", entry);
+
if (!bfd_check_format (entry->the_bfd, bfd_archive)
&& !bfd_check_format_matches (entry->the_bfd, bfd_object, &matching))
{
{
char **p;
- einfo (_("%B: file not recognized: %E\n"), entry->the_bfd);
- einfo (_("%B: matching formats:"), entry->the_bfd);
+ einfo (_("%P: %pB: file not recognized: %E;"
+ " matching formats:"), entry->the_bfd);
for (p = matching; *p != NULL; p++)
einfo (" %s", *p);
einfo ("%F\n");
}
else if (err != bfd_error_file_not_recognized
|| place == NULL)
- einfo (_("%F%B: file not recognized: %E\n"), entry->the_bfd);
+ einfo (_("%F%P: %pB: file not recognized: %E\n"), entry->the_bfd);
bfd_close (entry->the_bfd);
entry->the_bfd = NULL;
ldfile_assumed_script = TRUE;
parser_input = input_script;
+ current_input_file = entry->filename;
yyparse ();
+ current_input_file = NULL;
ldfile_assumed_script = FALSE;
/* missing_file is sticky. sysrooted will already have been
case bfd_object:
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);
- entry->the_bfd->usrdata = entry;
+ bfd_set_usrdata (entry->the_bfd, entry);
if (entry->flags.whole_archive)
{
bfd *member = NULL;
if (!bfd_check_format (member, bfd_object))
{
- einfo (_("%F%B: member %B in archive is not an object\n"),
+ einfo (_("%F%P: %pB: member %pB in archive is not an object\n"),
entry->the_bfd, member);
loaded = FALSE;
}
substitute BFD for us. */
if (!bfd_link_add_symbols (subsbfd, &link_info))
{
- einfo (_("%F%B: error adding symbols: %E\n"), member);
+ einfo (_("%F%P: %pB: error adding symbols: %E\n"), member);
loaded = FALSE;
}
}
if (bfd_link_add_symbols (entry->the_bfd, &link_info))
entry->flags.loaded = TRUE;
else
- einfo (_("%F%B: error adding symbols: %E\n"), entry->the_bfd);
+ einfo (_("%F%P: %pB: error adding symbols: %E\n"), entry->the_bfd);
return entry->flags.loaded;
}
/* Like strcpy() but convert to lower case as well. */
static void
-stricpy (char *dest, char *src)
+stricpy (char *dest, const char *src)
{
char c;
from haystack. */
static void
-strcut (char *haystack, char *needle)
+strcut (char *haystack, const char *needle)
{
haystack = strstr (haystack, needle);
Return a value indicating how "similar" they are. */
static int
-name_compare (char *first, char *second)
+name_compare (const char *first, const char *second)
{
char *copy1;
char *copy2;
/* Return the BFD target format of the first input file. */
-static char *
+static const char *
get_first_input_target (void)
{
- char *target = NULL;
+ const char *target = NULL;
LANG_FOR_EACH_INPUT_STATEMENT (s)
{
if (link_info.output_bfd == NULL)
{
if (bfd_get_error () == bfd_error_invalid_target)
- einfo (_("%P%F: target %s not found\n"), output_target);
+ einfo (_("%F%P: target %s not found\n"), output_target);
- einfo (_("%P%F: cannot open output file %s: %E\n"), name);
+ einfo (_("%F%P: cannot open output file %s: %E\n"), name);
}
delete_output_file_on_failure = TRUE;
if (!bfd_set_format (link_info.output_bfd, bfd_object))
- einfo (_("%P%F:%s: can not make object file: %E\n"), name);
+ einfo (_("%F%P: %s: can not make object file: %E\n"), name);
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);
+ einfo (_("%F%P: %s: can not set architecture: %E\n"), name);
link_info.hash = bfd_link_hash_table_create (link_info.output_bfd);
if (link_info.hash == NULL)
- einfo (_("%P%F: can not create hash table: %E\n"));
+ einfo (_("%F%P: can not create hash table: %E\n"));
bfd_set_gp_size (link_info.output_bfd, g_switch_value);
}
}
static void
-init_opb (void)
+init_opb (asection *s)
{
- unsigned x = bfd_arch_mach_octets_per_byte (ldfile_output_architecture,
- ldfile_output_machine);
+ unsigned int x;
+
opb_shift = 0;
+ if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour
+ && s != NULL
+ && (s->flags & SEC_ELF_OCTETS) != 0)
+ return;
+
+ x = bfd_arch_mach_octets_per_byte (ldfile_output_architecture,
+ ldfile_output_machine);
if (x > 1)
while ((x & 1) == 0)
{
OPEN_BFD_FORCE = 1,
OPEN_BFD_RESCAN = 2
};
-#ifdef ENABLE_PLUGINS
+#if BFD_SUPPORTS_PLUGINS
static lang_input_statement_type *plugin_insert = NULL;
+static struct bfd_link_hash_entry *plugin_undefs = NULL;
#endif
static void
case lang_group_statement_enum:
{
struct bfd_link_hash_entry *undefs;
+#if BFD_SUPPORTS_PLUGINS
+ lang_input_statement_type *plugin_insert_save;
+#endif
/* We must continually search the entries in the group
until no new symbols are added to the list of undefined
do
{
+#if BFD_SUPPORTS_PLUGINS
+ plugin_insert_save = plugin_insert;
+#endif
undefs = link_info.hash->undefs_tail;
open_input_bfds (s->group_statement.children.head,
mode | OPEN_BFD_FORCE);
}
- while (undefs != link_info.hash->undefs_tail);
+ while (undefs != link_info.hash->undefs_tail
+#if BFD_SUPPORTS_PLUGINS
+ /* Objects inserted by a plugin, which are loaded
+ before we hit this loop, may have added new
+ undefs. */
+ || (plugin_insert != plugin_insert_save && plugin_undefs)
+#endif
+ );
}
break;
case lang_target_statement_enum:
has been loaded already. Do the same for a rescan.
Likewise reload --as-needed shared libs. */
if (mode != OPEN_BFD_NORMAL
-#ifdef ENABLE_PLUGINS
+#if BFD_SUPPORTS_PLUGINS
&& ((mode & OPEN_BFD_RESCAN) == 0
|| plugin_insert == NULL)
#endif
s->input_statement.flags.reload = TRUE;
}
- os_tail = lang_output_section_statement.tail;
+ os_tail = lang_os_list.tail;
lang_list_init (&add);
if (!load_symbols (&s->input_statement, &add))
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)
+ if (os_tail != lang_os_list.tail)
{
einfo (_("%P: warning: %s contains output sections;"
" did you forget -T?\n"),
}
}
}
-#ifdef ENABLE_PLUGINS
+#if BFD_SUPPORTS_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)
#endif
break;
case lang_assignment_statement_enum:
- if (s->assignment_statement.exp->type.node_class != etree_assert
- && s->assignment_statement.exp->assign.defsym)
- /* This is from a --defsym on the command line. */
+ if (s->assignment_statement.exp->type.node_class != etree_assert)
exp_fold_tree_no_dot (s->assignment_statement.exp);
break;
default:
einfo ("%F");
}
+#ifdef ENABLE_LIBCTF
+/* Emit CTF errors and warnings. fp can be NULL to report errors/warnings
+ that happened specifically at CTF open time. */
+static void
+lang_ctf_errs_warnings (ctf_file_t *fp)
+{
+ ctf_next_t *i = NULL;
+ char *text;
+ int is_warning;
+ int err;
+
+ while ((text = ctf_errwarning_next (fp, &i, &is_warning, &err)) != NULL)
+ {
+ einfo (_("%s: %s\n"), is_warning ? _("CTF warning"): _("CTF error"),
+ text);
+ free (text);
+ }
+ if (err != ECTF_NEXT_END)
+ {
+ einfo (_("CTF error: cannot get CTF errors: `%s'\n"),
+ ctf_errmsg (err));
+ }
+
+ /* `err' returns errors from the error/warning iterator in particular.
+ These never assert. But if we have an fp, that could have recorded
+ an assertion failure: assert if it has done so. */
+ ASSERT (!fp || ctf_errno (fp) != ECTF_INTERNAL);
+}
+
+/* Open the CTF sections in the input files with libctf: if any were opened,
+ create a fake input file that we'll write the merged CTF data to later
+ on. */
+
+static void
+ldlang_open_ctf (void)
+{
+ int any_ctf = 0;
+ int err;
+
+ LANG_FOR_EACH_INPUT_STATEMENT (file)
+ {
+ asection *sect;
+
+ /* Incoming files from the compiler have a single ctf_file_t in them
+ (which is presented to us by the libctf API in a ctf_archive_t
+ wrapper): files derived from a previous relocatable link have a CTF
+ archive containing possibly many CTF files. */
+
+ if ((file->the_ctf = ctf_bfdopen (file->the_bfd, &err)) == NULL)
+ {
+ if (err != ECTF_NOCTFDATA)
+ {
+ lang_ctf_errs_warnings (NULL);
+ einfo (_("%P: warning: CTF section in %pB not loaded; "
+ "its types will be discarded: %s\n"), file->the_bfd,
+ ctf_errmsg (err));
+ }
+ continue;
+ }
+
+ /* Prevent the contents of this section from being written, while
+ requiring the section itself to be duplicated in the output, but only
+ once. */
+ /* This section must exist if ctf_bfdopen() succeeded. */
+ sect = bfd_get_section_by_name (file->the_bfd, ".ctf");
+ sect->size = 0;
+ sect->flags |= SEC_NEVER_LOAD | SEC_HAS_CONTENTS | SEC_LINKER_CREATED;
+
+ if (any_ctf)
+ sect->flags |= SEC_EXCLUDE;
+ any_ctf = 1;
+ }
+
+ if (!any_ctf)
+ {
+ ctf_output = NULL;
+ return;
+ }
+
+ if ((ctf_output = ctf_create (&err)) != NULL)
+ return;
+
+ einfo (_("%P: warning: CTF output not created: `%s'\n"),
+ ctf_errmsg (err));
+
+ LANG_FOR_EACH_INPUT_STATEMENT (errfile)
+ ctf_close (errfile->the_ctf);
+}
+
+/* Merge together CTF sections. After this, only the symtab-dependent
+ function and data object sections need adjustment. */
+
+static void
+lang_merge_ctf (void)
+{
+ asection *output_sect;
+ int flags = 0;
+
+ if (!ctf_output)
+ return;
+
+ output_sect = bfd_get_section_by_name (link_info.output_bfd, ".ctf");
+
+ /* If the section was discarded, don't waste time merging. */
+ if (output_sect == NULL)
+ {
+ ctf_file_close (ctf_output);
+ ctf_output = NULL;
+
+ LANG_FOR_EACH_INPUT_STATEMENT (file)
+ {
+ ctf_close (file->the_ctf);
+ file->the_ctf = NULL;
+ }
+ return;
+ }
+
+ LANG_FOR_EACH_INPUT_STATEMENT (file)
+ {
+ if (!file->the_ctf)
+ continue;
+
+ /* Takes ownership of file->the_ctf. */
+ if (ctf_link_add_ctf (ctf_output, file->the_ctf, file->filename) < 0)
+ {
+ einfo (_("%P: warning: CTF section in %pB cannot be linked: `%s'\n"),
+ file->the_bfd, ctf_errmsg (ctf_errno (ctf_output)));
+ ctf_close (file->the_ctf);
+ file->the_ctf = NULL;
+ continue;
+ }
+ }
+
+ if (!config.ctf_share_duplicated)
+ flags = CTF_LINK_SHARE_UNCONFLICTED;
+ else
+ flags = CTF_LINK_SHARE_DUPLICATED;
+ if (!config.ctf_variables)
+ flags |= CTF_LINK_OMIT_VARIABLES_SECTION;
+
+ if (ctf_link (ctf_output, flags) < 0)
+ {
+ lang_ctf_errs_warnings (ctf_output);
+ einfo (_("%P: warning: CTF linking failed; "
+ "output will have no CTF section: %s\n"),
+ ctf_errmsg (ctf_errno (ctf_output)));
+ if (output_sect)
+ {
+ output_sect->size = 0;
+ output_sect->flags |= SEC_EXCLUDE;
+ }
+ }
+ /* Output any lingering errors that didn't come from ctf_link. */
+ lang_ctf_errs_warnings (ctf_output);
+}
+
+/* Let the emulation examine the symbol table and strtab to help it optimize the
+ CTF, if supported. */
+
+void
+ldlang_ctf_apply_strsym (struct elf_sym_strtab *syms, bfd_size_type symcount,
+ struct elf_strtab_hash *symstrtab)
+{
+ ldemul_examine_strtab_for_ctf (ctf_output, syms, symcount, symstrtab);
+}
+
+/* Write out the CTF section. Called early, if the emulation isn't going to
+ need to dedup against the strtab and symtab, then possibly called from the
+ target linker code if the dedup has happened. */
+static void
+lang_write_ctf (int late)
+{
+ size_t output_size;
+ asection *output_sect;
+
+ if (!ctf_output)
+ return;
+
+ if (late)
+ {
+ /* Emit CTF late if this emulation says it can do so. */
+ if (ldemul_emit_ctf_early ())
+ return;
+ }
+ else
+ {
+ if (!ldemul_emit_ctf_early ())
+ return;
+ }
+
+ /* Emit CTF. */
+
+ output_sect = bfd_get_section_by_name (link_info.output_bfd, ".ctf");
+ if (output_sect)
+ {
+ output_sect->contents = ctf_link_write (ctf_output, &output_size,
+ CTF_COMPRESSION_THRESHOLD);
+ output_sect->size = output_size;
+ output_sect->flags |= SEC_IN_MEMORY | SEC_KEEP;
+
+ lang_ctf_errs_warnings (ctf_output);
+ if (!output_sect->contents)
+ {
+ einfo (_("%P: warning: CTF section emission failed; "
+ "output will have no CTF section: %s\n"),
+ ctf_errmsg (ctf_errno (ctf_output)));
+ output_sect->size = 0;
+ output_sect->flags |= SEC_EXCLUDE;
+ }
+ }
+
+ /* This also closes every CTF input file used in the link. */
+ ctf_file_close (ctf_output);
+ ctf_output = NULL;
+
+ LANG_FOR_EACH_INPUT_STATEMENT (file)
+ file->the_ctf = NULL;
+}
+
+/* Write out the CTF section late, if the emulation needs that. */
+
+void
+ldlang_write_ctf_late (void)
+{
+ /* Trigger a "late call", if the emulation needs one. */
+
+ lang_write_ctf (1);
+}
+#else
+static void
+ldlang_open_ctf (void)
+{
+ LANG_FOR_EACH_INPUT_STATEMENT (file)
+ {
+ asection *sect;
+
+ /* If built without CTF, warn and delete all CTF sections from the output.
+ (The alternative would be to simply concatenate them, which does not
+ yield a valid CTF section.) */
+
+ if ((sect = bfd_get_section_by_name (file->the_bfd, ".ctf")) != NULL)
+ {
+ einfo (_("%P: warning: CTF section in %pB not linkable: "
+ "%P was built without support for CTF\n"), file->the_bfd);
+ sect->size = 0;
+ sect->flags |= SEC_EXCLUDE;
+ }
+ }
+}
+
+static void lang_merge_ctf (void) {}
+void
+ldlang_ctf_apply_strsym (struct elf_sym_strtab *syms ATTRIBUTE_UNUSED,
+ bfd_size_type symcount ATTRIBUTE_UNUSED,
+ struct elf_strtab_hash *symstrtab ATTRIBUTE_UNUSED)
+{
+}
+static void lang_write_ctf (int late ATTRIBUTE_UNUSED) {}
+void ldlang_write_ctf_late (void) {}
+#endif
+
/* Add the supplied name to the symbol table as an undefined reference.
This is a two step process as the symbol table doesn't even exist at
the time the ld command line is processed. First we put the name
#define ldlang_undef_chain_list_head entry_symbol.next
void
-ldlang_add_undef (const char *const name, bfd_boolean cmdline)
+ldlang_add_undef (const char *const name, bfd_boolean cmdline ATTRIBUTE_UNUSED)
{
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 = stat_alloc (sizeof (*new_undef));
new_undef->next = ldlang_undef_chain_list_head;
ldlang_undef_chain_list_head = new_undef;
h = bfd_link_hash_lookup (link_info.hash, name, TRUE, FALSE, TRUE);
if (h == NULL)
- einfo (_("%P%F: bfd_link_hash_lookup failed: %E\n"));
+ einfo (_("%F%P: bfd_link_hash_lookup failed: %E\n"));
if (h->type == bfd_link_hash_new)
{
h->type = bfd_link_hash_undefined;
h->u.undef.abfd = NULL;
+ h->non_ir_ref_regular = TRUE;
if (is_elf_hash_table (link_info.hash))
((struct elf_link_hash_entry *) h)->mark = 1;
bfd_link_add_undef (link_info.hash, h);
struct require_defined_symbol *ptr;
ldlang_add_undef (name, TRUE);
- ptr = (struct require_defined_symbol *) stat_alloc (sizeof (*ptr));
+ ptr = stat_alloc (sizeof (*ptr));
ptr->next = require_defined_symbol_list;
ptr->name = strdup (name);
require_defined_symbol_list = ptr;
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);
+ einfo(_("%X%P: required symbol `%s' not defined\n"), ptr->name);
}
}
(lang_statement_union_type *s,
lang_output_section_statement_type *output_section_statement)
{
- for (; s != (lang_statement_union_type *) NULL; s = s->header.next)
+ for (; s != NULL; s = s->header.next)
{
switch (s->header.type)
{
case lang_wild_statement_enum:
for (sec = s->wild_statement.section_list; sec != NULL;
sec = sec->next)
- {
+ /* Don't sort .init/.fini sections. */
+ if (strcmp (sec->spec.name, ".init") != 0
+ && strcmp (sec->spec.name, ".fini") != 0)
switch (sec->spec.sorted)
{
case none:
default:
break;
}
- }
break;
case lang_constructors_statement_enum:
break;
case lang_output_section_statement_enum:
- /* 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);
+ update_wild_statements
+ (s->output_section_statement.children.head);
break;
case lang_group_statement_enum:
{
case normal_section:
case overlay_section:
+ case first_overlay_section:
break;
case noalloc_section:
flags = SEC_HAS_CONTENTS;
start of the list and places them after the output section
statement specified by the insert. This operation is complicated
by the fact that we keep a doubly linked list of output section
- statements as well as the singly linked list of all statements. */
+ statements as well as the singly linked list of all statements.
+ FIXME someday: Twiddling with the list not only moves statements
+ from the user's script but also input and group statements that are
+ built from command line object files and --start-group. We only
+ get away with this because the list pointers used by file_chain
+ and input_file_chain are not reordered, and processing via
+ statement_list after this point mostly ignores input statements.
+ One exception is the map file, where LOAD and START GROUP/END GROUP
+ can end up looking odd. */
static void
-process_insert_statements (void)
+process_insert_statements (lang_statement_union_type **start)
{
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
- reordered. */
- s = &lang_output_section_statement.head;
- while (*(s = &(*s)->header.next) != NULL)
+ s = start;
+ while (*s != NULL)
{
if ((*s)->header.type == lang_output_section_statement_enum)
{
if (first_os == NULL)
first_os = last_os;
}
+ else if ((*s)->header.type == lang_group_statement_enum)
+ {
+ /* A user might put -T between --start-group and
+ --end-group. One way this odd construct might arise is
+ from a wrapper around ld to change library search
+ behaviour. For example:
+ #! /bin/sh
+ exec real_ld --start-group "$@" --end-group
+ This isn't completely unreasonable so go looking inside a
+ group statement for insert statements. */
+ process_insert_statements (&(*s)->group_statement.children.head);
+ }
else if ((*s)->header.type == lang_insert_statement_enum)
{
lang_insert_statement_type *i = &(*s)->insert_statement;
lang_statement_union_type **ptr;
lang_statement_union_type *first;
+ if (link_info.non_contiguous_regions)
+ {
+ einfo (_("warning: INSERT statement in linker script is "
+ "incompatible with --enable-non-contiguous-regions.\n"));
+ }
+
where = lang_output_section_find (i->where);
if (where != NULL && i->is_before)
{
if (last_os->next == NULL)
{
next = &first_os->prev->next;
- lang_output_section_statement.tail
- = (lang_statement_union_type **) next;
+ lang_os_list.tail = (lang_statement_union_type **) next;
}
else
last_os->next->prev = first_os->prev;
if (where->next == NULL)
{
next = &last_os->next;
- lang_output_section_statement.tail
- = (lang_statement_union_type **) next;
+ lang_os_list.tail = (lang_statement_union_type **) next;
}
else
where->next->prev = last_os;
}
ptr = insert_os_after (where);
- /* Snip everything after the abs_section output statement we
- know is at the start of the list, up to and including
- the insert statement we are currently processing. */
- first = lang_output_section_statement.head->header.next;
- lang_output_section_statement.head->header.next = (*s)->header.next;
- /* Add them back where they belong. */
+ /* Snip everything from the start of the list, up to and
+ including the insert statement we are currently processing. */
+ first = *start;
+ *start = (*s)->header.next;
+ /* Add them back where they belong, minus the insert. */
*s = *ptr;
if (*s == NULL)
statement_list.tail = s;
*ptr = first;
- s = &lang_output_section_statement.head;
+ s = start;
+ continue;
}
+ s = &(*s)->header.next;
}
/* Undo constraint twiddling. */
if (expld.phase != lang_mark_phase_enum)
{
expld.phase = lang_mark_phase_enum;
- expld.dataseg.phase = exp_dataseg_none;
+ expld.dataseg.phase = exp_seg_none;
one_lang_size_sections_pass (NULL, FALSE);
lang_reset_memory_regions ();
}
- for (os = &lang_output_section_statement.head->output_section_statement;
+ for (os = (void *) lang_os_list.head;
os != NULL;
os = os->next)
{
}
/* 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. */
+ asection.map_tail for use as link_orders in ldwrite. */
void
lang_clear_os_map (void)
if (map_head_is_link_order)
return;
- for (os = &lang_output_section_statement.head->output_section_statement;
+ for (os = (void *) lang_os_list.head;
os != NULL;
os = os->next)
{
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;
+ tree = assignment->exp;
}
osec = output_section->bfd_section;
h = bfd_link_hash_lookup (link_info.hash, assignment->exp->assign.dst,
FALSE, FALSE, TRUE);
- if (h)
+ if (h != NULL
+ && (h->type == bfd_link_hash_defined
+ || h->type == bfd_link_hash_defweak))
{
value = h->u.def.value;
value += h->u.def.section->output_section->vma;
static void
print_input_statement (lang_input_statement_type *statm)
{
- if (statm->filename != NULL
- && (statm->the_bfd == NULL
- || (statm->the_bfd->flags & BFD_LINKER_CREATED) == 0))
+ if (statm->filename != NULL)
fprintf (config.map_file, "LOAD %s\n", statm->filename);
}
/* Print all symbols defined in a particular section. This is called
via bfd_link_hash_traverse, or by print_all_symbols. */
-static bfd_boolean
+bfd_boolean
print_one_symbol (struct bfd_link_hash_entry *hash_entry, void *ptr)
{
asection *sec = (asection *) ptr;
+ hash_entry->u.def.section->output_offset
+ hash_entry->u.def.section->output_section->vma));
- minfo (" %T\n", hash_entry->root.string);
+ minfo (" %pT\n", hash_entry->root.string);
}
return TRUE;
static void
print_all_symbols (asection *sec)
{
- input_section_userdata_type *ud
- = (input_section_userdata_type *) get_userdata (sec);
+ input_section_userdata_type *ud = bfd_section_userdata (sec);
struct map_symbol_def *def;
struct bfd_link_hash_entry **entries;
unsigned int i;
/* Print the symbols. */
for (i = 0; i < ud->map_symbol_def_count; i++)
- print_one_symbol (entries[i], sec);
+ ldemul_print_symbol (entries[i], sec);
obstack_free (&map_obstack, entries);
}
int len;
bfd_vma addr;
- init_opb ();
+ init_opb (i);
print_space ();
minfo ("%s", i->name);
size = 0;
}
- minfo ("0x%V %W %B\n", addr, size, i->owner);
+ minfo ("0x%V %W %pB\n", addr, TO_ADDR (size), i->owner);
if (size != i->rawsize && i->rawsize != 0)
{
--len;
}
- minfo (_("%W (size before relaxing)\n"), i->rawsize);
+ minfo (_("%W (size before relaxing)\n"), TO_ADDR (i->rawsize));
}
if (i->output_section != NULL
&& i->output_section->owner == link_info.output_bfd)
{
if (link_info.reduce_memory_overheads)
- bfd_link_hash_traverse (link_info.hash, print_one_symbol, i);
+ bfd_link_hash_traverse (link_info.hash, ldemul_print_symbol, i);
else
print_all_symbols (i);
bfd_size_type size;
const char *name;
- init_opb ();
+ init_opb (data->output_section);
for (i = 0; i < SECTION_NAME_MAP_LENGTH; i++)
print_space ();
bfd_vma addr;
bfd_size_type size;
- init_opb ();
+ init_opb (reloc->output_section);
for (i = 0; i < SECTION_NAME_MAP_LENGTH; i++)
print_space ();
int len;
bfd_vma addr;
- init_opb ();
+ init_opb (s->output_section);
minfo (" *fill*");
len = sizeof " *fill*" - 1;
else
{
/* Make a new padding statement, linked into existing chain. */
- pad = (lang_statement_union_type *)
- stat_alloc (sizeof (lang_padding_statement_type));
+ pad = 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 = TO_SIZE (dot + TO_ADDR (alignment_needed)
- - output_section->vma);
+ if (!(output_section->flags & SEC_FIXED_SIZE))
+ 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_statement_union_type **this_ptr,
lang_output_section_statement_type *output_section_statement,
fill_type *fill,
+ bfd_boolean *removed,
bfd_vma dot)
{
lang_input_section_type *is = &((*this_ptr)->input_section);
asection *i = is->section;
asection *o = output_section_statement->bfd_section;
+ *removed = 0;
+
+ if (link_info.non_contiguous_regions)
+ {
+ /* If the input section I has already been successfully assigned
+ to an output section other than O, don't bother with it and
+ let the caller remove it from the list. Keep processing in
+ case we have already handled O, because the repeated passes
+ have reinitialized its size. */
+ if (i->already_assigned && i->already_assigned != o)
+ {
+ *removed = 1;
+ return dot;
+ }
+ }
if (i->sec_info_type == SEC_INFO_TYPE_JUST_SYMS)
i->output_offset = i->vma - o->vma;
is greater than any seen before, then record it too. Perform
the alignment by inserting a magic 'padding' statement. */
- if (output_section_statement->subsection_alignment != -1)
- i->alignment_power = output_section_statement->subsection_alignment;
+ if (output_section_statement->subsection_alignment != NULL)
+ i->alignment_power
+ = exp_get_power (output_section_statement->subsection_alignment,
+ "subsection alignment");
if (o->alignment_power < i->alignment_power)
o->alignment_power = i->alignment_power;
dot += alignment_needed;
}
+ if (link_info.non_contiguous_regions)
+ {
+ /* If I would overflow O, let the caller remove I from the
+ list. */
+ if (output_section_statement->region)
+ {
+ bfd_vma end = output_section_statement->region->origin
+ + output_section_statement->region->length;
+
+ if (dot + TO_ADDR (i->size) > end)
+ {
+ if (i->flags & SEC_LINKER_CREATED)
+ einfo (_("%F%P: Output section '%s' not large enough for the "
+ "linker-created stubs section '%s'.\n"),
+ i->output_section->name, i->name);
+
+ if (i->rawsize && i->rawsize != i->size)
+ einfo (_("%F%P: Relaxation not supported with "
+ "--enable-non-contiguous-regions (section '%s' "
+ "would overflow '%s' after it changed size).\n"),
+ i->name, i->output_section->name);
+
+ *removed = 1;
+ dot = end;
+ i->output_section = NULL;
+ return dot;
+ }
+ }
+ }
+
/* 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);
+ if (!(o->flags & SEC_FIXED_SIZE))
+ o->size = TO_SIZE (dot - o->vma);
+
+ if (link_info.non_contiguous_regions)
+ {
+ /* Record that I was successfully assigned to O, and update
+ its actual output section too. */
+ i->already_assigned = o;
+ i->output_section = o;
+ }
}
return dot;
for (p = NULL, i = 0; i < count; i++)
{
s = sections[i].sec;
+ init_opb (s);
if ((s->flags & SEC_LOAD) != 0)
{
s_start = s->lma;
for (p = NULL, i = 0; i < count; i++)
{
s = sections[i].sec;
+ init_opb (s);
s_start = s->vma;
s_end = s_start + TO_ADDR (s->size) - 1;
{
if (tree != NULL)
{
- einfo (_("%X%P: address 0x%v of %B section `%s'"
+ einfo (_("%X%P: address 0x%v of %pB section `%s'"
" is not within region `%s'\n"),
region->current,
os->bfd_section->owner,
{
region->had_full_message = TRUE;
- einfo (_("%X%P: %B section `%s' will not fit in region `%s'\n"),
+ einfo (_("%X%P: %pB section `%s' will not fit in region `%s'\n"),
os->bfd_section->owner,
os->bfd_section->name,
region->name_list.name);
}
}
+static void
+ldlang_check_relro_region (lang_statement_union_type *s,
+ seg_align_type *seg)
+{
+ if (seg->relro == exp_seg_relro_start)
+ {
+ if (!seg->relro_start_stat)
+ seg->relro_start_stat = s;
+ else
+ {
+ ASSERT (seg->relro_start_stat == s);
+ }
+ }
+ else if (seg->relro == exp_seg_relro_end)
+ {
+ if (!seg->relro_end_stat)
+ seg->relro_end_stat = s;
+ else
+ {
+ ASSERT (seg->relro_end_stat == s);
+ }
+ }
+}
+
/* Set the sizes for all the output sections. */
static bfd_vma
bfd_boolean check_regions)
{
lang_statement_union_type *s;
+ lang_statement_union_type *prev_s = NULL;
+ bfd_boolean removed_prev_s = FALSE;
/* Size up the sections from their constituent parts. */
- for (s = *prev; s != NULL; s = s->header.next)
+ for (s = *prev; s != NULL; prev_s = s, s = s->header.next)
{
+ bfd_boolean removed=FALSE;
+
switch (s->header.type)
{
case lang_output_section_statement_enum:
int section_alignment = 0;
os = &s->output_section_statement;
+ init_opb (os->bfd_section);
if (os->constraint == -1)
break;
dot += expld.result.section->vma;
}
else if (expld.phase != lang_mark_phase_enum)
- einfo (_("%F%S: non constant or forward reference"
+ einfo (_("%F%P:%pS: non constant or forward reference"
" address expression for section %s\n"),
os->addr_tree, os->name);
}
|| os->children.head->header.next != NULL
|| (os->children.head->header.type
!= lang_input_section_enum))
- einfo (_("%P%X: Internal error on COFF shared library"
+ einfo (_("%X%P: internal error on COFF shared library"
" section %s\n"), os->name);
input = os->children.head->input_section.section;
- bfd_set_section_vma (os->bfd_section->owner,
- os->bfd_section,
- bfd_section_vma (input->owner, input));
- os->bfd_section->size = input->size;
+ bfd_set_section_vma (os->bfd_section,
+ bfd_section_vma (input));
+ if (!(os->bfd_section->flags & SEC_FIXED_SIZE))
+ os->bfd_section->size = input->size;
break;
}
&& (strcmp (lang_memory_region_list->name_list.name,
DEFAULT_MEMORY_REGION) != 0
|| lang_memory_region_list->next != NULL)
- && expld.phase != lang_mark_phase_enum)
+ && lang_sizing_iteration == 1)
{
/* By default this is an error rather than just a
warning because if we allocate the section to the
overridden by the using the --no-check-sections
switch. */
if (command_line.check_section_addresses)
- einfo (_("%P%F: error: no memory region specified"
+ einfo (_("%F%P: error: no memory region specified"
" for loadable section `%s'\n"),
- bfd_get_section_name (link_info.output_bfd,
- os->bfd_section));
+ bfd_section_name (os->bfd_section));
else
einfo (_("%P: warning: no memory region specified"
" for loadable section `%s'\n"),
- bfd_get_section_name (link_info.output_bfd,
- os->bfd_section));
+ bfd_section_name (os->bfd_section));
}
newdot = os->region->current;
section_alignment = os->bfd_section->alignment_power;
}
else
- section_alignment = os->section_alignment;
+ section_alignment = exp_get_power (os->section_alignment,
+ "section alignment");
/* Align to what the section needs. */
if (section_alignment > 0)
{
bfd_vma savedot = newdot;
- newdot = align_power (newdot, section_alignment);
+ bfd_vma diff = 0;
+ newdot = align_power (newdot, section_alignment);
dotdelta = newdot - savedot;
- if (dotdelta != 0
+
+ if (lang_sizing_iteration == 1)
+ diff = dotdelta;
+ else if (lang_sizing_iteration > 1)
+ {
+ /* Only report adjustments that would change
+ alignment from what we have already reported. */
+ diff = newdot - os->bfd_section->vma;
+ if (!(diff & (((bfd_vma) 1 << section_alignment) - 1)))
+ diff = 0;
+ }
+ if (diff != 0
&& (config.warn_section_align
- || os->addr_tree != NULL)
- && expld.phase != lang_mark_phase_enum)
- einfo (ngettext ("%P: warning: changing start of "
- "section %s by %lu byte\n",
- "%P: warning: changing start of "
- "section %s by %lu bytes\n",
- (unsigned long) dotdelta),
- os->name, (unsigned long) dotdelta);
+ || os->addr_tree != NULL))
+ einfo (_("%P: warning: "
+ "start of section %s changed by %ld\n"),
+ os->name, (long) diff);
}
- bfd_set_section_vma (0, os->bfd_section, newdot);
+ bfd_set_section_vma (os->bfd_section, newdot);
os->bfd_section->output_offset = 0;
}
+ os->block_value - 1)
& - (bfd_vma) os->block_value);
- os->bfd_section->size = TO_SIZE (after - os->bfd_section->vma);
+ if (!(os->bfd_section->flags & SEC_FIXED_SIZE))
+ os->bfd_section->size = TO_SIZE (after
+ - os->bfd_section->vma);
}
/* Set section lma. */
only align according to the value in the output
statement. */
if (os->lma_region != os->region)
- section_alignment = os->section_alignment;
+ section_alignment = exp_get_power (os->section_alignment,
+ "section alignment");
if (section_alignment > 0)
lma = align_power (lma, section_alignment);
}
/* Otherwise, keep the same lma to vma relationship
as the previous section. */
else
- lma = dot + last->lma - last->vma;
+ lma = os->bfd_section->vma + last->lma - last->vma;
if (section_alignment > 0)
lma = align_power (lma, section_alignment);
important, if an orphan section is placed after an
otherwise empty output section that has an explicit lma
set, we want that lma reflected in the orphans lma. */
- if (!IGNORE_SECTION (os->bfd_section)
- && (os->bfd_section->size != 0
- || (r->last_os == NULL
- && os->bfd_section->vma != os->bfd_section->lma)
- || (r->last_os != NULL
- && dot >= (r->last_os->output_section_statement
- .bfd_section->vma)))
+ if (((!IGNORE_SECTION (os->bfd_section)
+ && (os->bfd_section->size != 0
+ || (r->last_os == NULL
+ && os->bfd_section->vma != os->bfd_section->lma)
+ || (r->last_os != NULL
+ && dot >= (r->last_os->output_section_statement
+ .bfd_section->vma))))
+ || os->sectype == first_overlay_section)
&& os->lma_region == NULL
&& !bfd_link_relocatable (&link_info))
r->last_os = s;
if (size < TO_SIZE ((unsigned) 1))
size = TO_SIZE ((unsigned) 1);
dot += TO_ADDR (size);
- output_section_statement->bfd_section->size
- = TO_SIZE (dot - output_section_statement->bfd_section->vma);
+ if (!(output_section_statement->bfd_section->flags
+ & SEC_FIXED_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
- = TO_SIZE (dot - output_section_statement->bfd_section->vma);
+ if (!(output_section_statement->bfd_section->flags
+ & SEC_FIXED_SIZE))
+ output_section_statement->bfd_section->size
+ = TO_SIZE (dot - output_section_statement->bfd_section->vma);
}
break;
break;
case lang_object_symbols_statement_enum:
- link_info.create_object_symbols_section =
- output_section_statement->bfd_section;
+ link_info.create_object_symbols_section
+ = output_section_statement->bfd_section;
+ output_section_statement->bfd_section->flags |= SEC_KEEP;
break;
case lang_output_statement_enum:
bfd_boolean again;
if (!bfd_relax_section (i->owner, i, &link_info, &again))
- einfo (_("%P%F: can't relax section: %E\n"));
+ einfo (_("%F%P: can't relax section: %E\n"));
if (again)
*relax = TRUE;
}
dot = size_input_section (prev, output_section_statement,
- fill, dot);
+ fill, &removed, dot);
}
break;
bfd_vma newdot = dot;
etree_type *tree = s->assignment_statement.exp;
- expld.dataseg.relro = exp_dataseg_relro_none;
+ expld.dataseg.relro = exp_seg_relro_none;
exp_fold_tree (tree,
output_section_statement->bfd_section,
&newdot);
- if (expld.dataseg.relro == exp_dataseg_relro_start)
- {
- if (!expld.dataseg.relro_start_stat)
- expld.dataseg.relro_start_stat = s;
- else
- {
- ASSERT (expld.dataseg.relro_start_stat == s);
- }
- }
- else if (expld.dataseg.relro == exp_dataseg_relro_end)
- {
- if (!expld.dataseg.relro_end_stat)
- expld.dataseg.relro_end_stat = s;
- else
- {
- ASSERT (expld.dataseg.relro_end_stat == s);
- }
- }
- expld.dataseg.relro = exp_dataseg_relro_none;
+ ldlang_check_relro_region (s, &expld.dataseg);
+
+ expld.dataseg.relro = exp_seg_relro_none;
/* This symbol may be relative to this section. */
if ((tree->type.node_class == etree_provided
FAIL ();
break;
}
- prev = &s->header.next;
+
+ /* If an input section doesn't fit in the current output
+ section, remove it from the list. Handle the case where we
+ have to remove an input_section statement here: there is a
+ special case to remove the first element of the list. */
+ if (link_info.non_contiguous_regions && removed)
+ {
+ /* If we removed the first element during the previous
+ iteration, override the loop assignment of prev_s. */
+ if (removed_prev_s)
+ prev_s = NULL;
+
+ if (prev_s)
+ {
+ /* If there was a real previous input section, just skip
+ the current one. */
+ prev_s->header.next=s->header.next;
+ s = prev_s;
+ removed_prev_s = FALSE;
+ }
+ else
+ {
+ /* Remove the first input section of the list. */
+ *prev = s->header.next;
+ removed_prev_s = TRUE;
+ }
+
+ /* Move to next element, unless we removed the head of the
+ list. */
+ if (!removed_prev_s)
+ prev = &s->header.next;
+ }
+ else
+ {
+ prev = &s->header.next;
+ removed_prev_s = FALSE;
+ }
}
return dot;
}
one_lang_size_sections_pass (bfd_boolean *relax, bfd_boolean check_regions)
{
lang_statement_iteration++;
+ if (expld.phase != lang_mark_phase_enum)
+ lang_sizing_iteration++;
lang_size_sections_1 (&statement_list.head, abs_output_section,
0, 0, relax, check_regions);
}
-void
-lang_size_sections (bfd_boolean *relax, bfd_boolean check_regions)
+static bfd_boolean
+lang_size_segment (seg_align_type *seg)
{
- expld.phase = lang_allocating_phase_enum;
- expld.dataseg.phase = exp_dataseg_none;
+ /* If XXX_SEGMENT_ALIGN XXX_SEGMENT_END pair was seen, check whether
+ a page could be saved in the data segment. */
+ bfd_vma first, last;
- one_lang_size_sections_pass (relax, check_regions);
- if (expld.dataseg.phase == exp_dataseg_end_seen
- && link_info.relro && expld.dataseg.relro_end)
+ first = -seg->base & (seg->pagesize - 1);
+ last = seg->end & (seg->pagesize - 1);
+ if (first && last
+ && ((seg->base & ~(seg->pagesize - 1))
+ != (seg->end & ~(seg->pagesize - 1)))
+ && first + last <= seg->pagesize)
{
- bfd_vma initial_base, relro_end, desired_end;
- asection *sec;
+ seg->phase = exp_seg_adjust;
+ return TRUE;
+ }
- /* Compute the expected PT_GNU_RELRO segment end. */
- relro_end = ((expld.dataseg.relro_end + expld.dataseg.pagesize - 1)
- & ~(expld.dataseg.pagesize - 1));
+ seg->phase = exp_seg_done;
+ return FALSE;
+}
- /* Adjust by the offset arg of DATA_SEGMENT_RELRO_END. */
- desired_end = relro_end - expld.dataseg.relro_offset;
+static bfd_vma
+lang_size_relro_segment_1 (seg_align_type *seg)
+{
+ bfd_vma relro_end, desired_end;
+ asection *sec;
- /* 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;
- }
+ /* Compute the expected PT_GNU_RELRO/PT_LOAD segment end. */
+ relro_end = ((seg->relro_end + seg->pagesize - 1)
+ & ~(seg->pagesize - 1));
- expld.dataseg.phase = exp_dataseg_relro_adjust;
- ASSERT (desired_end >= expld.dataseg.base);
- initial_base = expld.dataseg.base;
- expld.dataseg.base = desired_end;
+ /* Adjust by the offset arg of XXX_SEGMENT_RELRO_END. */
+ desired_end = relro_end - seg->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 >= seg->base
+ && sec->vma < seg->relro_end - seg->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;
+ }
+
+ seg->phase = exp_seg_relro_adjust;
+ ASSERT (desired_end >= seg->base);
+ seg->base = desired_end;
+ return relro_end;
+}
+
+static bfd_boolean
+lang_size_relro_segment (bfd_boolean *relax, bfd_boolean check_regions)
+{
+ bfd_boolean do_reset = FALSE;
+ bfd_boolean do_data_relro;
+ bfd_vma data_initial_base, data_relro_end;
+
+ if (link_info.relro && expld.dataseg.relro_end)
+ {
+ do_data_relro = TRUE;
+ data_initial_base = expld.dataseg.base;
+ data_relro_end = lang_size_relro_segment_1 (&expld.dataseg);
+ }
+ else
+ {
+ do_data_relro = FALSE;
+ data_initial_base = data_relro_end = 0;
+ }
+
+ if (do_data_relro)
+ {
lang_reset_memory_regions ();
one_lang_size_sections_pass (relax, check_regions);
- if (expld.dataseg.relro_end > relro_end)
+ /* Assignments to dot, or to output section address in a user
+ script have increased padding over the original. Revert. */
+ if (do_data_relro && expld.dataseg.relro_end > data_relro_end)
{
- /* 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);
+ expld.dataseg.base = data_initial_base;;
+ do_reset = TRUE;
}
-
- link_info.relro_start = expld.dataseg.base;
- link_info.relro_end = expld.dataseg.relro_end;
}
- else if (expld.dataseg.phase == exp_dataseg_end_seen)
+
+ if (!do_data_relro && lang_size_segment (&expld.dataseg))
+ do_reset = TRUE;
+
+ return do_reset;
+}
+
+void
+lang_size_sections (bfd_boolean *relax, bfd_boolean check_regions)
+{
+ expld.phase = lang_allocating_phase_enum;
+ expld.dataseg.phase = exp_seg_none;
+
+ one_lang_size_sections_pass (relax, check_regions);
+
+ if (expld.dataseg.phase != exp_seg_end_seen)
+ expld.dataseg.phase = exp_seg_done;
+
+ if (expld.dataseg.phase == exp_seg_end_seen)
{
- /* If DATA_SEGMENT_ALIGN DATA_SEGMENT_END pair was seen, check whether
- a page could be saved in the data segment. */
- bfd_vma first, last;
+ bfd_boolean do_reset
+ = lang_size_relro_segment (relax, check_regions);
- first = -expld.dataseg.base & (expld.dataseg.pagesize - 1);
- last = expld.dataseg.end & (expld.dataseg.pagesize - 1);
- if (first && last
- && ((expld.dataseg.base & ~(expld.dataseg.pagesize - 1))
- != (expld.dataseg.end & ~(expld.dataseg.pagesize - 1)))
- && first + last <= expld.dataseg.pagesize)
+ if (do_reset)
{
- expld.dataseg.phase = exp_dataseg_adjust;
lang_reset_memory_regions ();
one_lang_size_sections_pass (relax, check_regions);
}
- else
- expld.dataseg.phase = exp_dataseg_done;
+
+ if (link_info.relro && expld.dataseg.relro_end)
+ {
+ link_info.relro_start = expld.dataseg.base;
+ link_info.relro_end = expld.dataseg.relro_end;
+ }
}
- else
- expld.dataseg.phase = exp_dataseg_done;
}
static lang_output_section_statement_type *current_section;
os = &(s->output_section_statement);
os->after_end = *found_end;
+ init_opb (os->bfd_section);
if (os->bfd_section != NULL && !os->ignored)
{
if ((os->bfd_section->flags & SEC_ALLOC) != 0)
|| strcmp (h->u.def.section->name,
h->u.def.section->output_section->name) != 0)
{
+ asection *sec = bfd_get_section_by_name (link_info.output_bfd,
+ h->u.def.section->name);
+ if (sec != NULL)
+ {
+ /* When there are more than one input sections with the same
+ section name, SECNAME, linker picks the first one to define
+ __start_SECNAME and __stop_SECNAME symbols. When the first
+ input section is removed by comdat group, we need to check
+ if there is still an output section with section name
+ SECNAME. */
+ asection *i;
+ for (i = sec->map_head.s; i != NULL; i = i->map_head.s)
+ if (strcmp (h->u.def.section->name, i->name) == 0)
+ {
+ h->u.def.section = i;
+ return;
+ }
+ }
h->type = bfd_link_hash_undefined;
h->u.undef.abfd = NULL;
}
warn = TRUE;
/* Force the user to specify a root when generating a relocatable with
- --gc-sections. */
- 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"));
+ --gc-sections, unless --gc-keep-exported was also given. */
+ if (bfd_link_relocatable (&link_info)
+ && link_info.gc_sections
+ && !link_info.gc_keep_exported)
+ {
+ struct bfd_sym_chain *sym;
+
+ for (sym = link_info.gc_sym_list; sym != NULL; sym = sym->next)
+ {
+ h = bfd_link_hash_lookup (link_info.hash, sym->name,
+ FALSE, FALSE, FALSE);
+ if (h != NULL
+ && (h->type == bfd_link_hash_defined
+ || h->type == bfd_link_hash_defweak)
+ && !bfd_is_const_section (h->u.def.section))
+ break;
+ }
+ if (!sym)
+ einfo (_("%F%P: --gc-sections requires a defined symbol root "
+ "specified by -e or -u\n"));
+ }
if (entry_symbol.name == NULL)
{
bfd_vma val;
val = (h->u.def.value
- + bfd_get_section_vma (link_info.output_bfd,
- h->u.def.section->output_section)
+ + bfd_section_vma (h->u.def.section->output_section)
+ h->u.def.section->output_offset);
if (!bfd_set_start_address (link_info.output_bfd, val))
- einfo (_("%P%F:%s: can't set start address\n"), entry_symbol.name);
+ einfo (_("%F%P: %s: can't set start address\n"), entry_symbol.name);
}
else
{
if (*send == '\0')
{
if (!bfd_set_start_address (link_info.output_bfd, val))
- einfo (_("%P%F: can't set start address\n"));
+ einfo (_("%F%P: can't set start address\n"));
}
else
{
einfo (_("%P: warning: cannot find entry symbol %s;"
" defaulting to %V\n"),
entry_symbol.name,
- bfd_get_section_vma (link_info.output_bfd, ts));
- if (!(bfd_set_start_address
- (link_info.output_bfd,
- bfd_get_section_vma (link_info.output_bfd, ts))))
- einfo (_("%P%F: can't set start address\n"));
+ bfd_section_vma (ts));
+ if (!bfd_set_start_address (link_info.output_bfd,
+ bfd_section_vma (ts)))
+ einfo (_("%F%P: can't set start address\n"));
}
else
{
static void
lang_check (void)
{
- lang_statement_union_type *file;
+ lang_input_statement_type *file;
bfd *input_bfd;
const bfd_arch_info_type *compatible;
- for (file = file_chain.head; file != NULL; file = file->input_statement.next)
+ for (file = (void *) file_chain.head;
+ file != NULL;
+ file = file->next)
{
-#ifdef ENABLE_PLUGINS
+#if BFD_SUPPORTS_PLUGINS
/* Don't check format of files claimed by plugin. */
- if (file->input_statement.flags.claimed)
+ if (file->flags.claimed)
continue;
-#endif /* ENABLE_PLUGINS */
- input_bfd = file->input_statement.the_bfd;
+#endif /* BFD_SUPPORTS_PLUGINS */
+ input_bfd = file->the_bfd;
compatible
= bfd_arch_get_compatible (input_bfd, link_info.output_bfd,
command_line.accept_unknown_input_arch);
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 ((bfd_link_relocatable (&link_info)
- || link_info.emitrelocations)
+ if (!file->flags.just_syms
+ && (bfd_link_relocatable (&link_info)
+ || link_info.emitrelocations)
&& (compatible == NULL
|| (bfd_get_flavour (input_bfd)
!= bfd_get_flavour (link_info.output_bfd)))
&& (bfd_get_file_flags (input_bfd) & HAS_RELOC) != 0)
{
- einfo (_("%P%F: Relocatable linking with relocations from"
- " format %s (%B) to format %s (%B) is not supported\n"),
+ einfo (_("%F%P: relocatable linking with relocations from"
+ " format %s (%pB) to format %s (%pB) is not supported\n"),
bfd_get_target (input_bfd), input_bfd,
bfd_get_target (link_info.output_bfd), link_info.output_bfd);
/* einfo with %F exits. */
if (compatible == NULL)
{
if (command_line.warn_mismatch)
- einfo (_("%P%X: %s architecture of input file `%B'"
+ einfo (_("%X%P: %s architecture of input file `%pB'"
" is incompatible with %s output\n"),
bfd_printable_name (input_bfd), input_bfd,
bfd_printable_name (link_info.output_bfd));
}
- else if (bfd_count_sections (input_bfd))
- {
- /* If the input bfd has no contents, it shouldn't set the
- private data of the output bfd. */
+ /* If the input bfd has no contents, it shouldn't set the
+ private data of the output bfd. */
+ else if (!file->flags.just_syms
+ && ((input_bfd->flags & DYNAMIC) != 0
+ || bfd_count_sections (input_bfd) != 0))
+ {
bfd_error_handler_type pfn = NULL;
/* If we aren't supposed to warn about mismatched input
if (!bfd_merge_private_bfd_data (input_bfd, &link_info))
{
if (command_line.warn_mismatch)
- einfo (_("%P%X: failed to merge target specific data"
- " of file %B\n"), input_bfd);
+ einfo (_("%X%P: failed to merge target specific data"
+ " of file %pB\n"), input_bfd);
}
if (!command_line.warn_mismatch)
bfd_set_error_handler (pfn);
section = h->u.c.p->section;
if (!bfd_define_common_symbol (link_info.output_bfd, &link_info, h))
- einfo (_("%P%F: Could not define common symbol `%T': %E\n"),
+ einfo (_("%F%P: could not define common symbol `%pT': %E\n"),
h->root.string);
if (config.map_file != NULL)
++len;
}
- minfo ("%B\n", section->owner);
+ minfo ("%pB\n", section->owner);
}
return TRUE;
int constraint = 0;
if (config.orphan_handling == orphan_handling_error)
- einfo (_("%X%P: error: unplaced orphan section `%A' from `%B'.\n"),
+ einfo (_("%X%P: error: unplaced orphan section `%pA' from `%pB'\n"),
s, s->owner);
if (config.unique_orphan_sections || unique_section_p (s, NULL))
}
if (config.orphan_handling == orphan_handling_warn)
- einfo (_("%P: warning: orphan section `%A' from `%B' being "
- "placed in section `%s'.\n"),
+ einfo (_("%P: warning: orphan section `%pA' from `%pB' being "
+ "placed in section `%s'\n"),
s, s->owner, os->name);
}
}
break;
default:
- einfo (_("%P%F: invalid character %c (%d) in flags\n"),
+ einfo (_("%F%P: invalid character %c (%d) in flags\n"),
*flags, *flags);
break;
}
}
}
-/* Call a function on each input file. This function will be called
- on an archive, but not on the elements. */
+/* Call a function on each real input file. This function will be
+ called on an archive, but not on the elements. */
void
lang_for_each_input_file (void (*func) (lang_input_statement_type *))
{
lang_input_statement_type *f;
- for (f = &input_file_chain.head->input_statement;
+ for (f = (void *) input_file_chain.head;
f != NULL;
- f = &f->next_real_file->input_statement)
- func (f);
+ f = f->next_real_file)
+ if (f->flags.real)
+ func (f);
}
-/* Call a function on each file. The function will be called on all
- the elements of an archive which are included in the link, but will
- not be called on the archive file itself. */
+/* Call a function on each real file. The function will be called on
+ all the elements of an archive which are included in the link, but
+ will not be called on the archive file itself. */
void
lang_for_each_file (void (*func) (lang_input_statement_type *))
{
LANG_FOR_EACH_INPUT_STATEMENT (f)
{
- func (f);
+ if (f->flags.real)
+ func (f);
}
}
void
ldlang_add_file (lang_input_statement_type *entry)
{
- lang_statement_append (&file_chain,
- (lang_statement_union_type *) entry,
- &entry->next);
+ lang_statement_append (&file_chain, entry, &entry->next);
/* The BFD linker needs to have a list of all input BFDs involved in
a link. */
- ASSERT (entry->the_bfd->link.next == NULL);
+ ASSERT (link_info.input_bfds_tail != &entry->the_bfd->link.next
+ && 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;
- entry->the_bfd->usrdata = entry;
+ bfd_set_usrdata (entry->the_bfd, entry);
bfd_set_gp_size (entry->the_bfd, g_switch_value);
/* Look through the sections and check for any which should not be
}
}
-static int
-topower (int x)
-{
- unsigned int i = 1;
- int l;
-
- if (x < 0)
- return -1;
-
- for (l = 0; l < 32; l++)
- {
- if (i >= (unsigned int) x)
- return l;
- i <<= 1;
- }
-
- return 0;
-}
-
lang_output_section_statement_type *
lang_enter_output_section_statement (const char *output_section_statement_name,
etree_type *address_exp,
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"),
+ einfo (_("%F%P:%pS: error: align with input and explicit align specified\n"),
NULL);
- os->subsection_alignment =
- topower (exp_get_value_int (subalign, -1, "subsection alignment"));
- os->section_alignment =
- topower (exp_get_value_int (align, -1, "section alignment"));
+ os->subsection_alignment = subalign;
+ os->section_alignment = align;
os->load_base = ebase;
return os;
p->last_os = NULL;
}
- for (os = &lang_output_section_statement.head->output_section_statement;
+ for (os = (void *) lang_os_list.head;
os != NULL;
os = os->next)
{
{
/* Save the last size for possible use by bfd_relax_section. */
o->rawsize = o->size;
- o->size = 0;
+ if (!(o->flags & SEC_FIXED_SIZE))
+ o->size = 0;
}
}
LANG_FOR_EACH_INPUT_STATEMENT (f)
{
asection *sec;
-#ifdef ENABLE_PLUGINS
+#if BFD_SUPPORTS_PLUGINS
if (f->flags.claimed)
continue;
#endif
static void
lang_find_relro_sections_1 (lang_statement_union_type *s,
+ seg_align_type *seg,
bfd_boolean *has_relro_section)
{
if (*has_relro_section)
for (; s != NULL; s = s->header.next)
{
- if (s == expld.dataseg.relro_end_stat)
+ if (s == seg->relro_end_stat)
break;
switch (s->header.type)
break;
case lang_constructors_statement_enum:
lang_find_relro_sections_1 (constructor_list.head,
- has_relro_section);
+ seg, has_relro_section);
break;
case lang_output_section_statement_enum:
lang_find_relro_sections_1 (s->output_section_statement.children.head,
- has_relro_section);
+ seg, has_relro_section);
break;
case lang_group_statement_enum:
lang_find_relro_sections_1 (s->group_statement.children.head,
- has_relro_section);
+ seg, has_relro_section);
break;
default:
break;
/* Check all sections in the link script. */
lang_find_relro_sections_1 (expld.dataseg.relro_start_stat,
- &has_relro_section);
+ &expld.dataseg, &has_relro_section);
if (!has_relro_section)
link_info.relro = FALSE;
}
}
-#ifdef ENABLE_PLUGINS
+#if BFD_SUPPORTS_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
inserted at the head of the file_chain. */
static lang_input_statement_type *
-find_replacements_insert_point (void)
+find_replacements_insert_point (bfd_boolean *before)
{
lang_input_statement_type *claim1, *lastobject;
- lastobject = &input_file_chain.head->input_statement;
- for (claim1 = &file_chain.head->input_statement;
+ lastobject = (void *) input_file_chain.head;
+ for (claim1 = (void *) file_chain.head;
claim1 != NULL;
- claim1 = &claim1->next->input_statement)
+ claim1 = claim1->next)
{
if (claim1->flags.claimed)
- return claim1->flags.claim_archive ? lastobject : claim1;
+ {
+ *before = claim1->flags.claim_archive;
+ 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. */
+ *before = FALSE;
return lastobject;
}
/* Find where to insert ADD, an archive element or shared library
added during a rescan. */
-static lang_statement_union_type **
+static lang_input_statement_type **
find_rescan_insertion (lang_input_statement_type *add)
{
bfd *add_bfd = add->the_bfd;
lang_input_statement_type *f;
lang_input_statement_type *last_loaded = NULL;
lang_input_statement_type *before = NULL;
- lang_statement_union_type **iter = NULL;
+ lang_input_statement_type **iter = NULL;
if (add_bfd->my_archive != NULL)
add_bfd = add_bfd->my_archive;
file chain if it is full of archive elements. Archives don't
appear on the file chain, but if an element has been extracted
then their input_statement->next points at it. */
- for (f = &input_file_chain.head->input_statement;
+ for (f = (void *) input_file_chain.head;
f != NULL;
- f = &f->next_real_file->input_statement)
+ f = f->next_real_file)
{
if (f->the_bfd == add_bfd)
{
before = last_loaded;
if (f->next != NULL)
- return &f->next->input_statement.next;
+ return &f->next->next;
}
if (f->the_bfd != NULL && f->next != NULL)
last_loaded = f;
for (iter = before ? &before->next : &file_chain.head->input_statement.next;
*iter != NULL;
- iter = &(*iter)->input_statement.next)
- if ((*iter)->input_statement.the_bfd->my_archive == NULL)
+ iter = &(*iter)->next)
+ if (!(*iter)->flags.claim_archive
+ && (*iter)->the_bfd->my_archive == NULL)
break;
return iter;
destlist->tail = savetail;
*savetail = NULL;
}
-#endif /* ENABLE_PLUGINS */
+
+static lang_statement_union_type **
+find_next_input_statement (lang_statement_union_type **s)
+{
+ for ( ; *s; s = &(*s)->header.next)
+ {
+ lang_statement_union_type **t;
+ switch ((*s)->header.type)
+ {
+ case lang_input_statement_enum:
+ return s;
+ case lang_wild_statement_enum:
+ t = &(*s)->wild_statement.children.head;
+ break;
+ case lang_group_statement_enum:
+ t = &(*s)->group_statement.children.head;
+ break;
+ case lang_output_section_statement_enum:
+ t = &(*s)->output_section_statement.children.head;
+ break;
+ default:
+ continue;
+ }
+ t = find_next_input_statement (t);
+ if (*t)
+ return t;
+ }
+ return s;
+}
+#endif /* BFD_SUPPORTS_PLUGINS */
/* Add NAME to the list of garbage collection entry points. */
if (name == NULL)
return;
- sym = (struct bfd_sym_chain *) stat_alloc (sizeof (*sym));
+ sym = stat_alloc (sizeof (*sym));
sym->next = link_info.gc_sym_list;
sym->name = name;
{
lang_output_section_statement_type *os;
- for (os = &lang_output_section_statement.head->output_section_statement;
+ for (os = (void *) lang_os_list.head;
os != NULL;
os = os->next)
{
/* Open the output file. */
lang_for_each_statement (ldlang_open_output);
- init_opb ();
+ init_opb (NULL);
ldemul_create_output_section_statements ();
lang_place_undefineds ();
if (!bfd_section_already_linked_table_init ())
- einfo (_("%P%F: Failed to create hash table\n"));
+ einfo (_("%F%P: can not create hash table: %E\n"));
/* Create a bfd for each input file. */
current_target = default_target;
+ lang_statement_iteration++;
open_input_bfds (statement_list.head, OPEN_BFD_NORMAL);
+ /* open_input_bfds also handles assignments, so we can give values
+ to symbolic origin/length now. */
+ lang_do_memory_regions ();
-#ifdef ENABLE_PLUGINS
+#if BFD_SUPPORTS_PLUGINS
if (link_info.lto_plugin_active)
{
lang_statement_list_type added;
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"),
+ einfo (_("%F%P: %s: plugin reported error after all symbols read\n"),
plugin_error_plugin ());
+ link_info.lto_all_symbols_read = TRUE;
/* Open any newly added files, updating the file chains. */
+ plugin_undefs = link_info.hash->undefs_tail;
open_input_bfds (*added.tail, OPEN_BFD_NORMAL);
+ if (plugin_undefs == link_info.hash->undefs_tail)
+ plugin_undefs = NULL;
/* 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. */
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 ();
+ after the first input file that was claimed by the plugin,
+ unless that file was an archive in which case it is inserted
+ immediately before. */
+ bfd_boolean before;
+ lang_statement_union_type **prev;
+ plugin_insert = find_replacements_insert_point (&before);
/* 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);
+ prev = &plugin_insert->header.next;
+ if (before)
+ {
+ prev = find_next_input_statement (prev);
+ if (*prev != (void *) plugin_insert->next_real_file)
+ {
+ /* We didn't find the expected input statement.
+ Fall back to adding after plugin_insert. */
+ prev = &plugin_insert->header.next;
+ }
+ }
+ lang_list_insert_after (stat_ptr, &added, prev);
/* Likewise for the file chains. */
lang_list_insert_after (&input_file_chain, &inputfiles,
- &plugin_insert->next_real_file);
+ (void *) &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);
+ lang_list_insert_after (&file_chain, &files,
+ (void *) &plugin_insert->next);
else
lang_list_insert_after (&file_chain, &files, &file_chain.head);
/* Rescan archives in case new undefined symbols have appeared. */
files = file_chain;
+ lang_statement_iteration++;
open_input_bfds (statement_list.head, OPEN_BFD_RESCAN);
lang_list_remove_tail (&file_chain, &files);
while (files.head != NULL)
{
- lang_statement_union_type **insert;
- lang_statement_union_type **iter, *temp;
+ lang_input_statement_type **insert;
+ lang_input_statement_type **iter, *temp;
bfd *my_arch;
insert = find_rescan_insertion (&files.head->input_statement);
iter = &files.head->input_statement.next;
my_arch = files.head->input_statement.the_bfd->my_archive;
if (my_arch != NULL)
- for (; *iter != NULL; iter = &(*iter)->input_statement.next)
- if ((*iter)->input_statement.the_bfd->my_archive != my_arch)
+ for (; *iter != NULL; iter = &(*iter)->next)
+ if ((*iter)->the_bfd->my_archive != my_arch)
break;
temp = *insert;
- *insert = files.head;
- files.head = *iter;
+ *insert = &files.head->input_statement;
+ files.head = (lang_statement_union_type *) *iter;
*iter = temp;
if (my_arch != NULL)
{
- lang_input_statement_type *parent = my_arch->usrdata;
+ lang_input_statement_type *parent = bfd_usrdata (my_arch);
if (parent != NULL)
- parent->next = (lang_statement_union_type *)
+ parent->next = (lang_input_statement_type *)
((char *) iter
- offsetof (lang_input_statement_type, next));
}
}
}
}
-#endif /* ENABLE_PLUGINS */
+#endif /* BFD_SUPPORTS_PLUGINS */
/* Make sure that nobody has tried to add a symbol to this list
before now. */
if (config.map_file != NULL)
lang_print_asneeded ();
+ ldlang_open_ctf ();
+
bfd_section_already_linked_table_free ();
/* Make sure that we're not mixing architectures. We call this
/* 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_statement_iteration++;
map_input_to_output_sections (statement_list.head, NULL, NULL);
- process_insert_statements ();
+ /* Start at the statement immediately after the special abs_section
+ output statement, so that it isn't reordered. */
+ process_insert_statements (&lang_os_list.head->header.next);
+
+ ldemul_before_place_orphans ();
/* Find any sections not attached explicitly and handle them. */
lang_place_orphans ();
}
}
+ /* Merge together CTF sections. After this, only the symtab-dependent
+ function and data object sections need adjustment. */
+ lang_merge_ctf ();
+
+ /* Emit the CTF, iff the emulation doesn't need to do late emission after
+ examining things laid out late, like the strtab. */
+ lang_write_ctf (0);
+
/* Copy forward lma regions for output sections in same lma region. */
lang_propagate_lma_regions ();
/* Fix any __start, __stop, .startof. or .sizeof. symbols. */
lang_finalize_start_stop ();
- /* Do all the assignments, now that we know the final resting places
- of all the symbols. */
+ /* Do all the assignments again, to report errors. Assignment
+ statements are processed multiple times, updating symbols; In
+ open_input_bfds, lang_do_assignments, and lang_size_sections.
+ Since lang_relax_sections calls lang_do_assignments, symbols are
+ also updated in ldemul_after_allocation. */
lang_do_assignments (lang_final_phase_enum);
ldemul_finish ();
{
if (first_file->filename != NULL)
{
- einfo (_("%P%F: multiple STARTUP files\n"));
+ einfo (_("%F%P: multiple STARTUP files\n"));
}
first_file->filename = name;
first_file->local_sym_name = name;
*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:%pS: section has both a load address and a load region\n"),
NULL);
}
pop_stat_ptr ();
}
-void
-lang_statement_append (lang_statement_list_type *list,
- lang_statement_union_type *element,
- lang_statement_union_type **field)
-{
- *(list->tail) = element;
- list->tail = field;
-}
-
/* Set the output format type. -oformat overrides scripts. */
void
struct lang_phdr *n, **pp;
bfd_boolean hdrs;
- n = (struct lang_phdr *) stat_alloc (sizeof (struct lang_phdr));
+ n = stat_alloc (sizeof (struct lang_phdr));
n->next = NULL;
n->name = name;
- n->type = exp_get_value_int (type, 0, "program header type");
+ n->type = exp_get_vma (type, 0, "program header type");
n->filehdr = filehdr;
n->phdrs = phdrs;
n->at = at;
&& (*pp)->type == 1
&& !((*pp)->filehdr || (*pp)->phdrs))
{
- einfo (_("%X%P:%S: PHDRS and FILEHDR are not supported"
+ einfo (_("%X%P:%pS: PHDRS and FILEHDR are not supported"
" when prior PT_LOAD headers lack them\n"), NULL);
hdrs = FALSE;
}
bfd_vma at;
c = 0;
- for (os = &lang_output_section_statement.head->output_section_statement;
+ for (os = (void *) lang_os_list.head;
os != NULL;
os = os->next)
{
free (secs);
/* Make sure all the phdr assignments succeeded. */
- for (os = &lang_output_section_statement.head->output_section_statement;
+ for (os = (void *) lang_os_list.head;
os != NULL;
os = os->next)
{
if (l->next == 0)
{
l->os->load_base = lma_expr;
- l->os->sectype = normal_section;
+ l->os->sectype = first_overlay_section;
}
if (phdrs != NULL && l->os->phdrs == NULL)
l->os->phdrs = phdrs;
/* Append the list of dynamic symbols to the existing one. */
void
-lang_append_dynamic_list (struct bfd_elf_version_expr *dynamic)
+lang_append_dynamic_list (struct bfd_elf_dynamic_list **list_p,
+ struct bfd_elf_version_expr *dynamic)
{
- if (link_info.dynamic_list)
+ if (*list_p)
{
struct bfd_elf_version_expr *tail;
for (tail = dynamic; tail->next != NULL; tail = tail->next)
;
- tail->next = link_info.dynamic_list->head.list;
- link_info.dynamic_list->head.list = dynamic;
+ tail->next = (*list_p)->head.list;
+ (*list_p)->head.list = dynamic;
}
else
{
d = (struct bfd_elf_dynamic_list *) xcalloc (1, sizeof *d);
d->head.list = dynamic;
d->match = lang_vers_match;
- link_info.dynamic_list = d;
+ *list_p = d;
}
}
dynamic = lang_new_vers_pattern (dynamic, symbols [i], "C++",
FALSE);
- lang_append_dynamic_list (dynamic);
+ lang_append_dynamic_list (&link_info.dynamic_list, dynamic);
}
/* Append the list of C++ operator new and delete dynamic symbols to the
dynamic = lang_new_vers_pattern (dynamic, symbols [i], "C++",
FALSE);
- lang_append_dynamic_list (dynamic);
+ lang_append_dynamic_list (&link_info.dynamic_list, dynamic);
}
/* Scan a space and/or comma separated string of features. */
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);
+ if (r->length != 0)
+ {
+ double percent = used_length * 100.0 / r->length;
+ printf (" %6.2f%%", percent);
+ }
+ printf ("\n");
}
}