/* Linker command language support.
- Copyright (C) 1991-2016 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 "fnmatch.h"
#include "demangle.h"
#include "hashtab.h"
-#include "libbfd.h"
#include "elf-bfd.h"
#ifdef ENABLE_PLUGINS
#include "plugin.h"
#define obstack_chunk_alloc xmalloc
#define obstack_chunk_free free
static const char *entry_symbol_default = "start";
-static bfd_boolean placed_commons = FALSE;
static bfd_boolean map_head_is_link_order = FALSE;
static lang_output_section_statement_type *default_common_section;
static bfd_boolean map_option_f;
static bfd_vma print_dot;
static lang_input_statement_type *first_file;
static const char *current_target;
+/* 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;
struct bfd_sym_chain entry_symbol = { NULL, NULL };
const char *entry_section = ".text";
struct lang_phdr *lang_phdr_list;
struct lang_nocrossrefs *nocrossref_list;
struct asneeded_minfo **asneeded_list_tail;
+static ctf_file_t *ctf_output;
/* Functions that traverse the linker script and might evaluate
DEFINED() need to increment this at the start of the traversal. */
#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)
{
struct unique_sections *unam;
const char *secnam;
- if (bfd_link_relocatable (&link_info)
+ if (!link_info.resolve_section_groups
&& sec->owner != NULL
&& bfd_is_group_section (sec->owner, sec))
return !(os != NULL
/* Generic traversal routines for finding matching sections. */
-/* Try processing a section against a wildcard. This just calls
- the callback unless the filename exclusion list is present
- and excludes the file. It's hardly ever present so this
- function is very fast. */
+/* Return true if FILE matches a pattern in EXCLUDE_LIST, otherwise return
+ false. */
-static void
-walk_wild_consider_section (lang_wild_statement_type *ptr,
- lang_input_statement_type *file,
- asection *s,
- struct wildcard_list *sec,
- callback_t callback,
- void *data)
+static bfd_boolean
+walk_wild_file_in_exclude_list (struct name_list *exclude_list,
+ lang_input_statement_type *file)
{
struct name_list *list_tmp;
- /* Don't process sections from files which were excluded. */
- for (list_tmp = sec->spec.exclude_name_list;
+ for (list_tmp = exclude_list;
list_tmp;
list_tmp = list_tmp->next)
{
if (p != NULL)
{
if (input_statement_is_archive_path (list_tmp->name, p, file))
- return;
+ return TRUE;
}
else if (name_match (list_tmp->name, file->filename) == 0)
- return;
+ return TRUE;
/* FIXME: Perhaps remove the following at some stage? Matching
unadorned archives like this was never documented and has
&& file->the_bfd->my_archive != NULL
&& name_match (list_tmp->name,
file->the_bfd->my_archive->filename) == 0)
- return;
+ return TRUE;
}
+ return FALSE;
+}
+
+/* Try processing a section against a wildcard. This just calls
+ the callback unless the filename exclusion list is present
+ and excludes the file. It's hardly ever present so this
+ function is very fast. */
+
+static void
+walk_wild_consider_section (lang_wild_statement_type *ptr,
+ lang_input_statement_type *file,
+ asection *s,
+ struct wildcard_list *sec,
+ callback_t callback,
+ void *data)
+{
+ /* Don't process sections from files which were excluded. */
+ if (walk_wild_file_in_exclude_list (sec->spec.exclude_name_list, file))
+ return;
+
(*callback) (ptr, sec, s, ptr->section_flag_list, file, data);
}
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));
+ 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);
callback_t callback,
void *data)
{
+ if (walk_wild_file_in_exclude_list (s->exclude_name_list, f))
+ return;
+
if (f->the_bfd == NULL
- || ! bfd_check_format (f->the_bfd, bfd_archive))
+ || !bfd_check_format (f->the_bfd, bfd_archive))
walk_wild_section (s, f, callback, data);
else
{
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);
static lang_input_statement_type *
new_afile (const char *name,
lang_input_file_enum_type file_type,
- const char *target,
- bfd_boolean add_to_list)
+ const char *target)
{
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->target = target;
break;
case lang_input_file_is_l_enum:
if (name[0] == ':' && name[1] != '\0')
- {
- p->filename = name + 1;
- p->flags.full_name_provided = TRUE;
- }
+ {
+ p->filename = name + 1;
+ p->flags.full_name_provided = TRUE;
+ }
else
- p->filename = name;
+ p->filename = name;
p->local_sym_name = concat ("-l", name, (const char *) NULL);
p->flags.maybe_archive = TRUE;
p->flags.real = TRUE;
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;
}
lang_input_file_enum_type file_type,
const char *target)
{
- if (name != NULL && *name == '=')
+ if (name != NULL
+ && (*name == '=' || CONST_STRNEQ (name, "$SYSROOT")))
{
lang_input_statement_type *ret;
char *sysrooted_name
- = concat (ld_sysroot, name + 1, (const char *) NULL);
+ = concat (ld_sysroot,
+ name + (*name == '=' ? 1 : strlen ("$SYSROOT")),
+ (const char *) NULL);
/* We've now forcibly prepended the sysroot, making the input
file independent of the context. Therefore, temporarily
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);
input_flags.sysrooted = outer_sysrooted;
return ret;
}
- return new_afile (name, file_type, target, TRUE);
+ return new_afile (name, file_type, target);
}
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;
}
void
-lang_memory_region_alias (const char * alias, const char * region_name)
+lang_memory_region_alias (const char *alias, const char *region_name)
{
- lang_memory_region_name * n;
- lang_memory_region_type * r;
- lang_memory_region_type * region;
+ lang_memory_region_name *n;
+ lang_memory_region_type *r;
+ lang_memory_region_type *region;
/* The default region must be unique. This ensures that it is not necessary
to iterate through the name list if someone wants the check if a region is
the default memory region. */
if (strcmp (region_name, DEFAULT_MEMORY_REGION) == 0
|| strcmp (alias, DEFAULT_MEMORY_REGION) == 0)
- einfo (_("%F%P:%S: error: alias for default memory region\n"), NULL);
+ 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;
}
static lang_memory_region_type *
-lang_memory_default (asection * section)
+lang_memory_default (asection *section)
{
lang_memory_region_type *p;
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)
lang_statement_list_type *add_child)
{
lang_statement_list_type add;
- const char *ps;
- lang_assignment_statement_type *start_assign;
lang_output_section_statement_type *os;
lang_output_section_statement_type **os_tail;
|| (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);
- ps = NULL;
- start_assign = NULL;
- if (config.build_constructors && *os_tail == os)
- {
- /* If the name of the section is representable in C, then create
- symbols to mark the start and the end of the section. */
- for (ps = secname; *ps != '\0'; ps++)
- if (! ISALNUM ((unsigned char) *ps) && *ps != '_')
- break;
- if (*ps == '\0')
- {
- char *symname;
-
- symname = (char *) xmalloc (ps - secname + sizeof "__start_" + 1);
- symname[0] = bfd_get_symbol_leading_char (link_info.output_bfd);
- sprintf (symname + (symname[0] != 0), "__start_%s", secname);
- start_assign
- = lang_add_assignment (exp_provide (symname,
- exp_nameop (NAME, "."),
- FALSE));
- }
- }
-
if (add_child == NULL)
add_child = &os->children;
lang_add_section (add_child, s, NULL, os);
lang_leave_output_section_statement (NULL, DEFAULT_MEMORY_REGION, NULL,
NULL);
- if (start_assign != NULL)
- {
- char *symname;
- lang_assignment_statement_type *stop_assign;
- bfd_vma dot;
-
- symname = (char *) xmalloc (ps - secname + sizeof "__stop_" + 1);
- symname[0] = bfd_get_symbol_leading_char (link_info.output_bfd);
- sprintf (symname + (symname[0] != 0), "__stop_%s", secname);
- stop_assign
- = lang_add_assignment (exp_provide (symname,
- exp_nameop (NAME, "."),
- FALSE));
- /* Evaluate the expression to define the symbol if referenced,
- before sizing dynamic sections. */
- dot = os->bfd_section->vma;
- exp_fold_tree (start_assign->exp, os->bfd_section, &dot);
- dot += TO_ADDR (s->size);
- exp_fold_tree (stop_assign->exp, os->bfd_section, &dot);
- }
-
/* Restore the global list pointer. */
if (after != NULL)
pop_stat_ptr ();
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;
lang_print_asneeded (void)
{
struct asneeded_minfo *m;
- char buf[100];
if (asneeded_list_head == NULL)
return;
- sprintf (buf, _("\nAs-needed library included "
- "to satisfy reference by file (symbol)\n\n"));
- minfo ("%s", buf);
+ minfo (_("\nAs-needed library included to satisfy reference by file (symbol)\n\n"));
for (m = asneeded_list_head; m != NULL; m = m->next)
{
}
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"));
fprintf (config.map_file, _("\nLinker script and memory map\n\n"));
- if (! link_info.reduce_memory_overheads)
+ if (!link_info.reduce_memory_overheads)
{
obstack_begin (&map_obstack, 1000);
bfd_link_hash_traverse (link_info.hash, sort_def_symbol, 0);
}
+ expld.phase = lang_fixed_phase_enum;
lang_statement_iteration++;
print_statements ();
- ldemul_extra_map_file_text (link_info.output_bfd, &link_info, config.map_file);
+ ldemul_extra_map_file_text (link_info.output_bfd, &link_info,
+ config.map_file);
}
static bfd_boolean
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 called %s\n"),
+ einfo (_("%F%P: output format %s cannot represent section"
+ " called %s: %E\n"),
link_info.output_bfd->xvec->name, s->name);
}
s->bfd_section->output_section = s->bfd_section;
/* 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;
return;
}
+ /* Deal with SHF_EXCLUDE ELF sections. */
+ if (!bfd_link_relocatable (&link_info)
+ && (abfd->flags & BFD_PLUGIN) == 0
+ && (sec->flags & (SEC_GROUP | SEC_KEEP | SEC_EXCLUDE)) == SEC_EXCLUDE)
+ sec->output_section = bfd_abs_section_ptr;
+
if (!(abfd->flags & DYNAMIC))
bfd_section_already_linked (abfd, sec, &link_info);
}
\f
+
+/* Returns true if SECTION is one we know will be discarded based on its
+ section flags, otherwise returns false. */
+
+static bfd_boolean
+lang_discard_section_p (asection *section)
+{
+ bfd_boolean discard;
+ flagword flags = section->flags;
+
+ /* Discard sections marked with SEC_EXCLUDE. */
+ discard = (flags & SEC_EXCLUDE) != 0;
+
+ /* Discard the group descriptor sections when we're finally placing the
+ sections from within the group. */
+ if ((flags & SEC_GROUP) != 0
+ && link_info.resolve_section_groups)
+ discard = TRUE;
+
+ /* Discard debugging sections if we are stripping debugging
+ information. */
+ if ((link_info.strip == strip_debugger || link_info.strip == strip_all)
+ && (flags & SEC_DEBUGGING) != 0)
+ discard = TRUE;
+
+ return discard;
+}
+
/* The wild routines.
These expand statements like *(.text) and foo.o to a list of
lang_input_section_type *new_section;
bfd *abfd = link_info.output_bfd;
- /* Discard sections marked with SEC_EXCLUDE. */
- discard = (flags & SEC_EXCLUDE) != 0;
+ /* Is this section one we know should be discarded? */
+ discard = lang_discard_section_p (section);
/* Discard input sections which are assigned to a section named
DISCARD_SECTION_NAME. */
if (strcmp (output->name, DISCARD_SECTION_NAME) == 0)
discard = TRUE;
- /* Discard debugging sections if we are stripping debugging
- information. */
- if ((link_info.strip == strip_debugger || link_info.strip == strip_all)
- && (flags & SEC_DEBUGGING) != 0)
- discard = TRUE;
-
if (discard)
{
if (section->output_section == NULL)
already been processed. One reason to do this is that on pe
format targets, .text$foo sections go into .text and it's odd
to see .text with SEC_LINK_ONCE set. */
-
- if (!bfd_link_relocatable (&link_info))
+ if ((flags & (SEC_LINK_ONCE | SEC_GROUP)) == (SEC_LINK_ONCE | SEC_GROUP))
+ {
+ if (link_info.resolve_section_groups)
+ flags &= ~(SEC_LINK_ONCE | SEC_LINK_DUPLICATES | SEC_RELOC);
+ else
+ flags &= ~(SEC_LINK_DUPLICATES | SEC_RELOC);
+ }
+ else if (!bfd_link_relocatable (&link_info))
flags &= ~(SEC_LINK_ONCE | SEC_LINK_DUPLICATES | SEC_RELOC);
switch (output->sectype)
{
case normal_section:
case overlay_section:
+ case first_overlay_section:
break;
case noalloc_section:
flags &= ~SEC_ALLOC;
archive. */
if (file->the_bfd != NULL
- && bfd_my_archive (file->the_bfd) != NULL)
+ && file->the_bfd->my_archive != NULL)
{
- fn = bfd_get_filename (bfd_my_archive (file->the_bfd));
+ fn = bfd_get_filename (file->the_bfd->my_archive);
fa = TRUE;
}
else
fa = FALSE;
}
- if (bfd_my_archive (ls->section->owner) != NULL)
+ if (ls->section->owner->my_archive != NULL)
{
- ln = bfd_get_filename (bfd_my_archive (ls->section->owner));
+ ln = bfd_get_filename (ls->section->owner->my_archive);
la = TRUE;
}
else
{
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);
+ *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. */
if (search->flags.loaded || !search->flags.real)
return search;
- if (! load_symbols (search, NULL))
+ if (!load_symbols (search, NULL))
return NULL;
return search;
if (entry->flags.missing_file)
return TRUE;
- if (! bfd_check_format (entry->the_bfd, bfd_archive)
- && ! bfd_check_format_matches (entry->the_bfd, bfd_object, &matching))
+ 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))
{
bfd_error_type err;
struct lang_input_statement_flags save_flags;
{
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;
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);
+ bfd_set_usrdata (entry->the_bfd, entry);
if (entry->flags.whole_archive)
{
bfd *member = NULL;
if (member == NULL)
break;
- if (! bfd_check_format (member, bfd_object))
+ if (!bfd_check_format (member, bfd_object))
{
- einfo (_("%F%B: member %B in archive is not an object\n"),
+ 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)
{
line? */
if (command_line.endian != ENDIAN_UNSET)
{
- const bfd_target *target;
- enum bfd_endian desired_endian;
-
/* Get the chosen target. */
- target = bfd_search_for_target (get_target, (void *) output_target);
+ const bfd_target *target
+ = bfd_iterate_over_targets (get_target, (void *) output_target);
/* If the target is not supported, we cannot do anything. */
if (target != NULL)
{
+ enum bfd_endian desired_endian;
+
if (command_line.endian == ENDIAN_BIG)
desired_endian = BFD_ENDIAN_BIG;
else
/* Try to find a target as similar as possible to
the default target, but which has the desired
endian characteristic. */
- bfd_search_for_target (closest_target_match,
- (void *) target);
+ bfd_iterate_over_targets (closest_target_match,
+ (void *) target);
/* Oh dear - we could not find any targets that
satisfy our requirements. */
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);
- if (! bfd_set_arch_mach (link_info.output_bfd,
+ if (!bfd_set_format (link_info.output_bfd, bfd_object))
+ 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)
{
};
#ifdef ENABLE_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;
+#ifdef ENABLE_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
{
+#ifdef ENABLE_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
+#ifdef ENABLE_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:
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))
+ if (!load_symbols (&s->input_statement, &add))
config.make_executable = FALSE;
if (add.head != NULL)
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"),
#endif
break;
case lang_assignment_statement_enum:
- if (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");
}
-/* 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
- on a list, then, once the output file has been opened, transfer the
- name to the symbol table. */
-
-typedef struct bfd_sym_chain ldlang_undef_chain_list_type;
-
-#define ldlang_undef_chain_list_head entry_symbol.next
+/* 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. */
-void
-ldlang_add_undef (const char *const name, bfd_boolean cmdline)
+static void
+ldlang_open_ctf (void)
{
- ldlang_undef_chain_list_type *new_undef;
+ int any_ctf = 0;
+ int err;
- undef_from_cmdline = undef_from_cmdline || cmdline;
- new_undef = (ldlang_undef_chain_list_type *) stat_alloc (sizeof (*new_undef));
- new_undef->next = ldlang_undef_chain_list_head;
- ldlang_undef_chain_list_head = new_undef;
+ LANG_FOR_EACH_INPUT_STATEMENT (file)
+ {
+ asection *sect;
- new_undef->name = xstrdup (name);
+ /* 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 (link_info.output_bfd != NULL)
- insert_undefined (new_undef->name);
-}
+ if ((file->the_ctf = ctf_bfdopen (file->the_bfd, &err)) == NULL)
+ {
+ if (err != ECTF_NOCTFDATA)
+ einfo (_("%P: warning: CTF section in `%pI' not loaded: "
+ "its types will be discarded: `%s'\n"), file,
+ ctf_errmsg (err));
+ continue;
+ }
-/* Insert NAME as undefined in the symbol table. */
+ /* Prevent the contents of this section from being written, while
+ requiring the section itself to be duplicated in the output. */
+ /* 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;
-static void
-insert_undefined (const char *name)
-{
- struct bfd_link_hash_entry *h;
+ any_ctf = 1;
+ }
- 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"));
- if (h->type == bfd_link_hash_new)
+ if (!any_ctf)
{
- h->type = bfd_link_hash_undefined;
- h->u.undef.abfd = NULL;
- bfd_link_add_undef (link_info.hash, h);
+ ctf_output = NULL;
+ return;
}
-}
-/* Run through the list of undefineds created above and place them
- into the linker hash table as undefined symbols belonging to the
- script file. */
+ if ((ctf_output = ctf_create (&err)) != NULL)
+ return;
-static void
-lang_place_undefineds (void)
-{
- ldlang_undef_chain_list_type *ptr;
+ einfo (_("%P: warning: CTF output not created: `s'\n"),
+ ctf_errmsg (err));
- for (ptr = ldlang_undef_chain_list_head; ptr != NULL; ptr = ptr->next)
- insert_undefined (ptr->name);
+ LANG_FOR_EACH_INPUT_STATEMENT (errfile)
+ ctf_close (errfile->the_ctf);
}
-/* Structure used to build the list of symbols that the user has required
- be defined. */
+/* Merge together CTF sections. After this, only the symtab-dependent
+ function and data object sections need adjustment. */
-struct require_defined_symbol
+static void
+lang_merge_ctf (void)
{
- const char *name;
- struct require_defined_symbol *next;
-};
+ asection *output_sect;
-/* The list of symbols that the user has required be defined. */
+ if (!ctf_output)
+ return;
-static struct require_defined_symbol *require_defined_symbol_list;
+ output_sect = bfd_get_section_by_name (link_info.output_bfd, ".ctf");
-/* Add a new symbol NAME to the list of symbols that are required to be
- defined. */
+ /* If the section was discarded, don't waste time merging. */
+ if (output_sect == NULL)
+ {
+ ctf_file_close (ctf_output);
+ ctf_output = NULL;
-void
-ldlang_add_require_defined (const char * const name)
-{
- struct require_defined_symbol *ptr;
+ LANG_FOR_EACH_INPUT_STATEMENT (file)
+ {
+ ctf_close (file->the_ctf);
+ file->the_ctf = NULL;
+ }
+ return;
+ }
- ldlang_add_undef (name, TRUE);
- ptr = (struct require_defined_symbol *) stat_alloc (sizeof (*ptr));
+ LANG_FOR_EACH_INPUT_STATEMENT (file)
+ {
+ if (!file->the_ctf)
+ continue;
+
+ /* Takes ownership of file->u.the_ctfa. */
+ if (ctf_link_add_ctf (ctf_output, file->the_ctf, file->filename) < 0)
+ {
+ einfo (_("%F%P: cannot link with CTF in %pB: %s\n"), file->the_bfd,
+ ctf_errmsg (ctf_errno (ctf_output)));
+ ctf_close (file->the_ctf);
+ file->the_ctf = NULL;
+ continue;
+ }
+ }
+
+ if (ctf_link (ctf_output, CTF_LINK_SHARE_UNCONFLICTED) < 0)
+ {
+ einfo (_("%F%P: 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;
+ }
+ }
+}
+
+/* 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;
+
+ if (!output_sect->contents)
+ {
+ einfo (_("%F%P: 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);
+}
+
+/* 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
+ on a list, then, once the output file has been opened, transfer the
+ name to the symbol table. */
+
+typedef struct bfd_sym_chain ldlang_undef_chain_list_type;
+
+#define ldlang_undef_chain_list_head entry_symbol.next
+
+void
+ldlang_add_undef (const char *const name, bfd_boolean cmdline)
+{
+ ldlang_undef_chain_list_type *new_undef;
+
+ undef_from_cmdline = undef_from_cmdline || cmdline;
+ new_undef = stat_alloc (sizeof (*new_undef));
+ new_undef->next = ldlang_undef_chain_list_head;
+ ldlang_undef_chain_list_head = new_undef;
+
+ new_undef->name = xstrdup (name);
+
+ if (link_info.output_bfd != NULL)
+ insert_undefined (new_undef->name);
+}
+
+/* Insert NAME as undefined in the symbol table. */
+
+static void
+insert_undefined (const char *name)
+{
+ struct bfd_link_hash_entry *h;
+
+ h = bfd_link_hash_lookup (link_info.hash, name, TRUE, FALSE, TRUE);
+ if (h == NULL)
+ 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);
+ }
+}
+
+/* Run through the list of undefineds created above and place them
+ into the linker hash table as undefined symbols belonging to the
+ script file. */
+
+static void
+lang_place_undefineds (void)
+{
+ ldlang_undef_chain_list_type *ptr;
+
+ for (ptr = ldlang_undef_chain_list_head; ptr != NULL; ptr = ptr->next)
+ insert_undefined (ptr->name);
+}
+
+/* Structure used to build the list of symbols that the user has required
+ be defined. */
+
+struct require_defined_symbol
+{
+ const char *name;
+ struct require_defined_symbol *next;
+};
+
+/* The list of symbols that the user has required be defined. */
+
+static struct require_defined_symbol *require_defined_symbol_list;
+
+/* Add a new symbol NAME to the list of symbols that are required to be
+ defined. */
+
+void
+ldlang_add_require_defined (const char *const name)
+{
+ struct require_defined_symbol *ptr;
+
+ ldlang_add_undef (name, TRUE);
+ ptr = stat_alloc (sizeof (*ptr));
ptr->next = require_defined_symbol_list;
ptr->name = strdup (name);
require_defined_symbol_list = ptr;
struct bfd_link_hash_entry *h;
h = bfd_link_hash_lookup (link_info.hash, ptr->name,
- FALSE, FALSE, TRUE);
+ FALSE, FALSE, TRUE);
if (h == NULL
- || (h->type != bfd_link_hash_defined
- && h->type != bfd_link_hash_defweak))
- einfo(_("%P%X: required symbol `%s' not defined\n"), ptr->name);
+ || (h->type != bfd_link_hash_defined
+ && h->type != bfd_link_hash_defweak))
+ 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:
walk_wild (&s->wild_statement, check_section_callback,
output_section_statement);
- if (! output_section_statement->all_input_readonly)
+ if (!output_section_statement->all_input_readonly)
return;
break;
case lang_constructors_statement_enum:
check_input_sections (constructor_list.head,
output_section_statement);
- if (! output_section_statement->all_input_readonly)
+ if (!output_section_statement->all_input_readonly)
return;
break;
case lang_group_statement_enum:
check_input_sections (s->group_statement.children.head,
output_section_statement);
- if (! output_section_statement->all_input_readonly)
+ if (!output_section_statement->all_input_readonly)
return;
break;
default:
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;
processed the segment marker. Originally, the linker
treated segment directives (like -Ttext on the
command-line) as section directives. We honor the
- section directive semantics for backwards compatibilty;
+ section directive semantics for backwards compatibility;
linker scripts that do not specifically check for
SEGMENT_START automatically get the old semantics. */
if (!s->address_statement.segment
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;
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;
else
{
if (assignment->exp->type.node_class == etree_provide)
- minfo ("[!provide]");
+ minfo ("[!provide]");
else
- minfo ("*undef* ");
+ minfo ("*undef* ");
#ifdef BFD64
minfo (" ");
#endif
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);
}
+ 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;
/* Sort the symbols by address. */
entries = (struct bfd_link_hash_entry **)
- obstack_alloc (&map_obstack, ud->map_symbol_def_count * sizeof (*entries));
+ obstack_alloc (&map_obstack,
+ ud->map_symbol_def_count * sizeof (*entries));
for (i = 0, def = ud->map_symbol_def_head; def; def = def->next, i++)
entries[i] = def->entry;
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
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;
print_space ();
+ if (w->exclude_name_list)
+ {
+ name_list *tmp;
+ minfo ("EXCLUDE_FILE(%s", w->exclude_name_list->name);
+ for (tmp = w->exclude_name_list->next; tmp; tmp = tmp->next)
+ minfo (" %s", tmp->name);
+ minfo (") ");
+ }
+
if (w->filenames_sorted)
- minfo ("SORT(");
+ minfo ("SORT_BY_NAME(");
if (w->filename != NULL)
minfo ("%s", w->filename);
else
minfo ("(");
for (sec = w->section_list; sec; sec = sec->next)
{
- if (sec->spec.sorted)
- minfo ("SORT(");
+ int closing_paren = 0;
+
+ switch (sec->spec.sorted)
+ {
+ case none:
+ break;
+
+ case by_name:
+ minfo ("SORT_BY_NAME(");
+ closing_paren = 1;
+ break;
+
+ case by_alignment:
+ minfo ("SORT_BY_ALIGNMENT(");
+ closing_paren = 1;
+ break;
+
+ case by_name_alignment:
+ minfo ("SORT_BY_NAME(SORT_BY_ALIGNMENT(");
+ closing_paren = 2;
+ break;
+
+ case by_alignment_name:
+ minfo ("SORT_BY_ALIGNMENT(SORT_BY_NAME(");
+ closing_paren = 2;
+ break;
+
+ case by_none:
+ minfo ("SORT_NONE(");
+ closing_paren = 1;
+ break;
+
+ case by_init_priority:
+ minfo ("SORT_BY_INIT_PRIORITY(");
+ closing_paren = 1;
+ break;
+ }
+
if (sec->spec.exclude_name_list != NULL)
{
name_list *tmp;
minfo ("%s", sec->spec.name);
else
minfo ("*");
- if (sec->spec.sorted)
+ for (;closing_paren > 0; closing_paren--)
minfo (")");
if (sec->next)
minfo (" ");
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. */
if (i->sec_info_type == SEC_INFO_TYPE_JUST_SYMS)
i->output_offset = i->vma - o->vma;
- else if ((i->flags & SEC_EXCLUDE) != 0)
+ else if (((i->flags & SEC_EXCLUDE) != 0)
+ || output_section_statement->ignored)
i->output_offset = dot - o->vma;
else
{
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;
/* 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);
}
return dot;
asection *s, *p;
struct check_sec *sections;
size_t i, count;
+ bfd_vma addr_mask;
bfd_vma s_start;
bfd_vma s_end;
bfd_vma p_start = 0;
lang_memory_region_type *m;
bfd_boolean overlays;
+ /* Detect address space overflow on allocated sections. */
+ addr_mask = ((bfd_vma) 1 <<
+ (bfd_arch_bits_per_address (link_info.output_bfd) - 1)) - 1;
+ addr_mask = (addr_mask << 1) + 1;
+ for (s = link_info.output_bfd->sections; s != NULL; s = s->next)
+ if ((s->flags & SEC_ALLOC) != 0)
+ {
+ s_end = (s->vma + s->size) & addr_mask;
+ if (s_end != 0 && s_end < (s->vma & addr_mask))
+ einfo (_("%X%P: section %s VMA wraps around address space\n"),
+ s->name);
+ else
+ {
+ s_end = (s->lma + s->size) & addr_mask;
+ if (s_end != 0 && s_end < (s->lma & addr_mask))
+ einfo (_("%X%P: section %s LMA wraps around address space\n"),
+ s->name);
+ }
+ }
+
if (bfd_count_sections (link_info.output_bfd) <= 1)
return;
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;
a bfd_vma quantity in decimal. */
for (m = lang_memory_region_list; m; m = m->next)
if (m->had_full_message)
- einfo (_("%X%P: region `%s' overflowed by %ld bytes\n"),
- m->name_list.name, (long)(m->current - (m->origin + m->length)));
+ {
+ unsigned long over = m->current - (m->origin + m->length);
+ einfo (ngettext ("%X%P: region `%s' overflowed by %lu byte\n",
+ "%X%P: region `%s' overflowed by %lu bytes\n",
+ over),
+ m->name_list.name, over);
+ }
}
/* Make sure the new address is within the region. We explicitly permit the
{
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
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;
}
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)
&& (config.warn_section_align
|| os->addr_tree != NULL)
&& expld.phase != lang_mark_phase_enum)
- einfo (_("%P: warning: changing start of section"
- " %s by %lu bytes\n"),
+ 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);
}
- 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);
}
so people can fix their linker scripts. */
if (last->vma != last->lma)
- einfo (_("%P: warning: dot moved backwards before `%s'\n"),
- os->name);
+ einfo (_("%P: warning: dot moved backwards "
+ "before `%s'\n"), os->name);
}
else
{
/* 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);
}
os->processed_lma = TRUE;
- if (bfd_is_abs_section (os->bfd_section) || os->ignored)
- break;
-
/* Keep track of normal sections using the default
lma region. We use this to set the lma for
following sections. Overlays or other linker
default lma == vma is incorrect.
To avoid warnings about dot moving backwards when using
-Ttext, don't start tracking sections until we find one
- of non-zero size or with lma set differently to vma. */
- if (!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)))
+ of non-zero size or with lma set differently to vma.
+ Do this tracking before we short-cut the loop so that we
+ track changes for the case where the section size is zero,
+ but the lma is set differently to the vma. This is
+ 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))))
+ || os->sectype == first_overlay_section)
&& os->lma_region == NULL
&& !bfd_link_relocatable (&link_info))
r->last_os = s;
+ if (bfd_is_abs_section (os->bfd_section) || os->ignored)
+ break;
+
/* .tbss sections effectively have zero size. */
if (!IS_TBSS (os->bfd_section)
|| bfd_link_relocatable (&link_info))
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"));
+ if (!bfd_relax_section (i->owner, i, &link_info, &again))
+ einfo (_("%F%P: can't relax section: %E\n"));
if (again)
*relax = TRUE;
}
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
segments. We are allowed an opportunity to override this decision. */
bfd_boolean
-ldlang_override_segment_assignment (struct bfd_link_info * info ATTRIBUTE_UNUSED,
- bfd * abfd ATTRIBUTE_UNUSED,
- asection * current_section,
- asection * previous_section,
+ldlang_override_segment_assignment (struct bfd_link_info *info ATTRIBUTE_UNUSED,
+ bfd *abfd ATTRIBUTE_UNUSED,
+ asection *current_section,
+ asection *previous_section,
bfd_boolean new_segment)
{
- lang_output_section_statement_type * cur;
- lang_output_section_statement_type * prev;
+ lang_output_section_statement_type *cur;
+ lang_output_section_statement_type *prev;
/* The checks below are only necessary when the BFD library has decided
that the two sections ought to be placed into the same segment. */
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;
+ }
+
+ seg->phase = exp_seg_done;
+ return FALSE;
+}
- /* Compute the expected PT_GNU_RELRO segment end. */
- relro_end = ((expld.dataseg.relro_end + expld.dataseg.pagesize - 1)
- & ~(expld.dataseg.pagesize - 1));
+static bfd_vma
+lang_size_relro_segment_1 (seg_align_type *seg)
+{
+ bfd_vma relro_end, desired_end;
+ asection *sec;
- /* Adjust by the offset arg of DATA_SEGMENT_RELRO_END. */
- desired_end = relro_end - expld.dataseg.relro_offset;
+ /* Compute the expected PT_GNU_RELRO/PT_LOAD segment end. */
+ relro_end = ((seg->relro_end + seg->pagesize - 1)
+ & ~(seg->pagesize - 1));
- /* 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;
- }
+ /* 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;
+ }
- expld.dataseg.phase = exp_dataseg_relro_adjust;
- ASSERT (desired_end >= expld.dataseg.base);
- initial_base = expld.dataseg.base;
- expld.dataseg.base = desired_end;
+ 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;
case lang_output_section_statement_enum:
{
lang_output_section_statement_type *os;
+ bfd_vma newdot;
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)
prefer_next_section = FALSE;
}
dot = os->bfd_section->vma;
-
- lang_do_assignments_1 (os->children.head,
- os, os->fill, dot, found_end);
-
- /* .tbss sections effectively have zero size. */
- if (!IS_TBSS (os->bfd_section)
- || bfd_link_relocatable (&link_info))
- dot += TO_ADDR (os->bfd_section->size);
-
- if (os->update_dot_tree != NULL)
- exp_fold_tree (os->update_dot_tree, bfd_abs_section_ptr, &dot);
+ }
+ newdot = lang_do_assignments_1 (os->children.head,
+ os, os->fill, dot, found_end);
+ if (!os->ignored)
+ {
+ if (os->bfd_section != NULL)
+ {
+ /* .tbss sections effectively have zero size. */
+ if (!IS_TBSS (os->bfd_section)
+ || bfd_link_relocatable (&link_info))
+ dot += TO_ADDR (os->bfd_section->size);
+
+ if (os->update_dot_tree != NULL)
+ exp_fold_tree (os->update_dot_tree,
+ bfd_abs_section_ptr, &dot);
+ }
+ else
+ dot = newdot;
}
}
break;
if (expld.result.section != NULL)
s->data_statement.value += expld.result.section->vma;
}
- else
+ else if (expld.phase == lang_final_phase_enum)
einfo (_("%F%P: invalid data statement\n"));
{
unsigned int size;
bfd_abs_section_ptr, &dot);
if (expld.result.valid_p)
s->reloc_statement.addend_value = expld.result.value;
- else
+ else if (expld.phase == lang_final_phase_enum)
einfo (_("%F%P: invalid reloc statement\n"));
dot += TO_ADDR (bfd_get_reloc_size (s->reloc_statement.howto));
break;
*found_end = TRUE;
}
exp_fold_tree (s->assignment_statement.exp,
- current_os->bfd_section,
+ (current_os->bfd_section != NULL
+ ? current_os->bfd_section : bfd_und_section_ptr),
&dot);
break;
return bfd_abs_section_ptr;
}
-/* Fix any .startof. or .sizeof. symbols. When the assemblers see the
- operator .startof. (section_name), it produces an undefined symbol
- .startof.section_name. Similarly, when it sees
- .sizeof. (section_name), it produces an undefined symbol
- .sizeof.section_name. For all the output sections, we look for
- such symbols, and set them to the correct value. */
+/* Array of __start/__stop/.startof./.sizeof/ symbols. */
+
+static struct bfd_link_hash_entry **start_stop_syms;
+static size_t start_stop_count = 0;
+static size_t start_stop_alloc = 0;
+
+/* Give start/stop SYMBOL for SEC a preliminary definition, and add it
+ to start_stop_syms. */
+
+static void
+lang_define_start_stop (const char *symbol, asection *sec)
+{
+ struct bfd_link_hash_entry *h;
+
+ h = bfd_define_start_stop (link_info.output_bfd, &link_info, symbol, sec);
+ if (h != NULL)
+ {
+ if (start_stop_count == start_stop_alloc)
+ {
+ start_stop_alloc = 2 * start_stop_alloc + 10;
+ start_stop_syms
+ = xrealloc (start_stop_syms,
+ start_stop_alloc * sizeof (*start_stop_syms));
+ }
+ start_stop_syms[start_stop_count++] = h;
+ }
+}
+
+/* Check for input sections whose names match references to
+ __start_SECNAME or __stop_SECNAME symbols. Give the symbols
+ preliminary definitions. */
static void
-lang_set_startof (void)
+lang_init_start_stop (void)
{
+ bfd *abfd;
asection *s;
+ char leading_char = bfd_get_symbol_leading_char (link_info.output_bfd);
- if (bfd_link_relocatable (&link_info))
+ for (abfd = link_info.input_bfds; abfd != NULL; abfd = abfd->link.next)
+ for (s = abfd->sections; s != NULL; s = s->next)
+ {
+ const char *ps;
+ const char *secname = s->name;
+
+ for (ps = secname; *ps != '\0'; ps++)
+ if (!ISALNUM ((unsigned char) *ps) && *ps != '_')
+ break;
+ if (*ps == '\0')
+ {
+ char *symbol = (char *) xmalloc (10 + strlen (secname));
+
+ symbol[0] = leading_char;
+ sprintf (symbol + (leading_char != 0), "__start_%s", secname);
+ lang_define_start_stop (symbol, s);
+
+ symbol[1] = leading_char;
+ memcpy (symbol + 1 + (leading_char != 0), "__stop", 6);
+ lang_define_start_stop (symbol + 1, s);
+
+ free (symbol);
+ }
+ }
+}
+
+/* Iterate over start_stop_syms. */
+
+static void
+foreach_start_stop (void (*func) (struct bfd_link_hash_entry *))
+{
+ size_t i;
+
+ for (i = 0; i < start_stop_count; ++i)
+ func (start_stop_syms[i]);
+}
+
+/* __start and __stop symbols are only supposed to be defined by the
+ linker for orphan sections, but we now extend that to sections that
+ map to an output section of the same name. The symbols were
+ defined early for --gc-sections, before we mapped input to output
+ sections, so undo those that don't satisfy this rule. */
+
+static void
+undef_start_stop (struct bfd_link_hash_entry *h)
+{
+ if (h->ldscript_def)
return;
+ if (h->u.def.section->output_section == NULL
+ || h->u.def.section->output_section->owner != link_info.output_bfd
+ || 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;
+ }
+}
+
+static void
+lang_undef_start_stop (void)
+{
+ foreach_start_stop (undef_start_stop);
+}
+
+/* Check for output sections whose names match references to
+ .startof.SECNAME or .sizeof.SECNAME symbols. Give the symbols
+ preliminary definitions. */
+
+static void
+lang_init_startof_sizeof (void)
+{
+ asection *s;
+
for (s = link_info.output_bfd->sections; s != NULL; s = s->next)
{
- const char *secname;
- char *buf;
- struct bfd_link_hash_entry *h;
+ const char *secname = s->name;
+ char *symbol = (char *) xmalloc (10 + strlen (secname));
- secname = bfd_get_section_name (link_info.output_bfd, s);
- buf = (char *) xmalloc (10 + strlen (secname));
+ sprintf (symbol, ".startof.%s", secname);
+ lang_define_start_stop (symbol, s);
- sprintf (buf, ".startof.%s", secname);
- h = bfd_link_hash_lookup (link_info.hash, buf, FALSE, FALSE, TRUE);
- if (h != NULL && h->type == bfd_link_hash_undefined)
- {
- h->type = bfd_link_hash_defined;
- h->u.def.value = 0;
- h->u.def.section = s;
- }
+ memcpy (symbol + 1, ".size", 5);
+ lang_define_start_stop (symbol + 1, s);
+ free (symbol);
+ }
+}
+
+/* Set .startof., .sizeof., __start and __stop symbols final values. */
+
+static void
+set_start_stop (struct bfd_link_hash_entry *h)
+{
+ if (h->ldscript_def
+ || h->type != bfd_link_hash_defined)
+ return;
- sprintf (buf, ".sizeof.%s", secname);
- h = bfd_link_hash_lookup (link_info.hash, buf, FALSE, FALSE, TRUE);
- if (h != NULL && h->type == bfd_link_hash_undefined)
+ if (h->root.string[0] == '.')
+ {
+ /* .startof. or .sizeof. symbol.
+ .startof. already has final value. */
+ if (h->root.string[2] == 'i')
{
- h->type = bfd_link_hash_defined;
- h->u.def.value = TO_ADDR (s->size);
+ /* .sizeof. */
+ h->u.def.value = TO_ADDR (h->u.def.section->size);
h->u.def.section = bfd_abs_section_ptr;
}
+ }
+ else
+ {
+ /* __start or __stop symbol. */
+ int has_lead = bfd_get_symbol_leading_char (link_info.output_bfd) != 0;
- free (buf);
+ h->u.def.section = h->u.def.section->output_section;
+ if (h->root.string[4 + has_lead] == 'o')
+ {
+ /* __stop_ */
+ h->u.def.value = TO_ADDR (h->u.def.section->size);
+ }
}
}
+static void
+lang_finalize_start_stop (void)
+{
+ foreach_start_stop (set_start_stop);
+}
+
static void
lang_end (void)
{
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)
+ --gc-sections, unless --gc-keep-exported was also given. */
+ if (bfd_link_relocatable (&link_info)
+ && link_info.gc_sections
+ && !link_info.gc_keep_exported
&& !(entry_from_cmdline || undef_from_cmdline))
- einfo (_("%P%F: gc-sections requires either an entry or "
+ einfo (_("%F%P: gc-sections requires either an entry or "
"an undefined symbol\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);
+ if (!bfd_set_start_address (link_info.output_bfd, val))
+ einfo (_("%F%P: %s: can't set start address\n"), entry_symbol.name);
}
else
{
val = bfd_scan_vma (entry_symbol.name, &send, 0);
if (*send == '\0')
{
- if (! bfd_set_start_address (link_info.output_bfd, val))
- einfo (_("%P%F: can't set start address\n"));
+ if (!bfd_set_start_address (link_info.output_bfd, val))
+ 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
{
BFD. */
static void
-ignore_bfd_errors (const char *s ATTRIBUTE_UNUSED, ...)
+ignore_bfd_errors (const char *fmt ATTRIBUTE_UNUSED,
+ va_list ap ATTRIBUTE_UNUSED)
{
/* Don't do anything. */
}
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
/* 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;
+ input_bfd = file->the_bfd;
compatible
= bfd_arch_get_compatible (input_bfd, link_info.output_bfd,
command_line.accept_unknown_input_arch);
!= 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));
function which will do nothing. We still want to call
bfd_merge_private_bfd_data, since it may set up
information which is needed in the output file. */
- if (! command_line.warn_mismatch)
+ if (!command_line.warn_mismatch)
pfn = bfd_set_error_handler (ignore_bfd_errors);
- if (! bfd_merge_private_bfd_data (input_bfd, link_info.output_bfd))
+ if (!bfd_merge_private_bfd_data (input_bfd, &link_info))
{
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)
+ if (!command_line.warn_mismatch)
bfd_set_error_handler (pfn);
}
}
static void
lang_common (void)
{
- if (command_line.inhibit_common_definition)
+ if (link_info.inhibit_common_definition)
return;
if (bfd_link_relocatable (&link_info)
- && ! command_line.force_common_definition)
+ && !command_line.force_common_definition)
return;
- if (! config.sort_common)
+ if (!config.sort_common)
bfd_link_hash_traverse (link_info.hash, lang_one_common, NULL);
else
{
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)
char *name;
char buf[50];
- if (! header_printed)
+ if (!header_printed)
{
minfo (_("\nAllocating common symbols\n"));
minfo (_("Common symbol size file\n\n"));
++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);
}
}
if (file->flags.just_syms)
bfd_link_just_syms (file->the_bfd, s, &link_info);
- else if ((s->flags & SEC_EXCLUDE) != 0)
+ else if (lang_discard_section_p (s))
s->output_section = bfd_abs_section_ptr;
else if (strcmp (s->name, "COMMON") == 0)
{
/* PR 17900: An exclamation mark in the attributes reverses
the sense of any of the attributes that follow. */
case '!':
- invert = ! invert;
+ invert = !invert;
ptr_flags = invert ? &ptr->not_flags : &ptr->flags;
break;
break;
default:
- einfo (_("%P%F: invalid character %c (%d) in flags\n"), * flags, * flags);
+ einfo (_("%F%P: invalid character %c (%d) in flags\n"),
+ *flags, *flags);
break;
}
flags++;
}
}
-/* 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 = (lang_input_statement_type *) input_file_chain.head;
+ for (f = (void *) input_file_chain.head;
f != NULL;
- f = (lang_input_statement_type *) f->next_real_file)
- 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. */
*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"), NULL);
+ 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;
}
}
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;
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 && (claim1->the_bfd->my_archive == NULL))
+ 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_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_input_statement_type **iter = NULL;
+
+ if (add_bfd->my_archive != NULL)
+ add_bfd = add_bfd->my_archive;
+
+ /* First look through the input file chain, to find an object file
+ before the one we've rescanned. Normal object files always
+ appear on both the input file chain and the file chain, so this
+ lets us get quickly to somewhere near the correct place on the
+ 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 = (void *) input_file_chain.head;
+ f != NULL;
+ f = f->next_real_file)
+ {
+ if (f->the_bfd == add_bfd)
+ {
+ before = last_loaded;
+ if (f->next != NULL)
+ 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)->next)
+ if (!(*iter)->flags.claim_archive
+ && (*iter)->the_bfd->my_archive == NULL)
+ break;
+
+ return iter;
+}
+
/* Insert SRCLIST into DESTLIST after given element by chaining
on FIELD as the next-pointer. (Counterintuitively does not need
a pointer to the actual after-node itself, just its chain field.) */
destlist->tail = savetail;
*savetail = NULL;
}
+
+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 /* ENABLE_PLUGINS */
/* Add NAME to the list of garbage collection entry points. */
void
-lang_add_gc_name (const char * name)
+lang_add_gc_name (const char *name)
{
struct bfd_sym_chain *sym;
if (name == NULL)
return;
- sym = (struct bfd_sym_chain *) stat_alloc (sizeof (*sym));
+ sym = stat_alloc (sizeof (*sym));
sym->next = link_info.gc_sym_list;
sym->name = name;
link_info.gc_sym_list = sym;
}
+/* Check relocations. */
+
+static void
+lang_check_relocs (void)
+{
+ if (link_info.check_relocs_after_open_input)
+ {
+ bfd *abfd;
+
+ for (abfd = link_info.input_bfds;
+ abfd != (bfd *) NULL; abfd = abfd->link.next)
+ if (!bfd_link_check_relocs (abfd, &link_info))
+ {
+ /* No object output, fail return. */
+ config.make_executable = FALSE;
+ /* Note: we do not abort the loop, but rather
+ continue the scan in case there are other
+ bad relocations to report. */
+ }
+ }
+}
+
+/* Look through all output sections looking for places where we can
+ propagate forward the lma region. */
+
+static void
+lang_propagate_lma_regions (void)
+{
+ lang_output_section_statement_type *os;
+
+ for (os = (void *) lang_os_list.head;
+ os != NULL;
+ os = os->next)
+ {
+ if (os->prev != NULL
+ && os->lma_region == NULL
+ && os->load_base == NULL
+ && os->addr_tree == NULL
+ && os->region == os->prev->region)
+ os->lma_region = os->prev->lma_region;
+ }
+}
+
void
lang_process (void)
{
/* 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 (link_info.lto_plugin_active)
are any more to be added to the link before we call the
emulation's after_open hook. We create a private list of
input statements for this purpose, which we will eventually
- insert into the global statment list after the first claimed
+ insert into the global statement list after the first claimed
file. */
added = *stat_ptr;
/* We need to manipulate all three chains in synchrony. */
files = file_chain;
inputfiles = input_file_chain;
if (plugin_call_all_symbols_read ())
- einfo (_("%P%F: %s: plugin reported error after all symbols read\n"),
+ einfo (_("%F%P: %s: plugin reported error after all symbols read\n"),
plugin_error_plugin ());
/* 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_input_statement_type **insert;
+ lang_input_statement_type **iter, *temp;
+ bfd *my_arch;
+
+ insert = find_rescan_insertion (&files.head->input_statement);
+ /* All elements from an archive can be added at once. */
+ 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)->next)
+ if ((*iter)->the_bfd->my_archive != my_arch)
+ break;
+ temp = *insert;
+ *insert = &files.head->input_statement;
+ files.head = (lang_statement_union_type *) *iter;
+ *iter = temp;
+ if (my_arch != NULL)
+ {
+ lang_input_statement_type *parent = bfd_usrdata (my_arch);
+ if (parent != NULL)
+ parent->next = (lang_input_statement_type *)
+ ((char *) iter
+ - offsetof (lang_input_statement_type, next));
+ }
+ }
}
}
#endif /* ENABLE_PLUGINS */
- /* Make sure that nobody has tried to add a symbol to this list before now. */
+ /* Make sure that nobody has tried to add a symbol to this list
+ before now. */
ASSERT (link_info.gc_sym_list == NULL);
link_info.gc_sym_list = &entry_symbol;
if (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
files. */
ldctor_build_sets ();
+ /* Give initial values for __start and __stop symbols, so that ELF
+ gc_sections will keep sections referenced by these symbols. Must
+ be done before lang_do_assignments below. */
+ if (config.build_constructors)
+ lang_init_start_stop ();
+
/* 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. */
/* Remove unreferenced sections if asked to. */
lang_gc_sections ();
+ /* Check relocations. */
+ lang_check_relocs ();
+
+ ldemul_after_check_relocs ();
+
/* Update wild statements. */
update_wild_statements (statement_list.head);
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);
/* 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 ();
+
+ /* Defining __start/__stop symbols early for --gc-sections to work
+ around a glibc build problem can result in these symbols being
+ defined when they should not be. Fix them now. */
+ if (config.build_constructors)
+ lang_undef_start_stop ();
+
+ /* Define .startof./.sizeof. symbols with preliminary values before
+ dynamic symbols are created. */
+ if (!bfd_link_relocatable (&link_info))
+ lang_init_startof_sizeof ();
+
/* Do anything special before sizing sections. This is where ELF
and other back-ends size dynamic sections. */
ldemul_before_allocation ();
lang_find_relro_sections ();
/* Size up the sections. */
- lang_size_sections (NULL, ! RELAXATION_ENABLED);
+ lang_size_sections (NULL, !RELAXATION_ENABLED);
/* See if anything special should be done now we know how big
everything is. This is where relaxation is done. */
ldemul_after_allocation ();
- /* Fix any .startof. or .sizeof. symbols. */
- lang_set_startof ();
+ /* 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 ();
curr != NULL;
section_list = curr, curr = next)
{
- if (curr->spec.name != NULL && strcmp (curr->spec.name, "COMMON") == 0)
- placed_commons = TRUE;
-
next = curr->next;
curr->next = section_list;
}
{
if (strcmp (filespec->name, "*") == 0)
filespec->name = NULL;
- else if (! wildcardp (filespec->name))
+ else if (!wildcardp (filespec->name))
lang_has_input_file = TRUE;
}
new_stmt->filename = NULL;
new_stmt->filenames_sorted = FALSE;
new_stmt->section_flag_list = NULL;
+ new_stmt->exclude_name_list = NULL;
if (filespec != NULL)
{
new_stmt->filename = filespec->name;
new_stmt->filenames_sorted = filespec->sorted == by_name;
new_stmt->section_flag_list = filespec->section_flag_list;
+ new_stmt->exclude_name_list = filespec->exclude_name_list;
}
new_stmt->section_list = section_list;
new_stmt->keep_sections = keep_sections;
{
if (entry_symbol.name == NULL
|| cmdline
- || ! entry_from_cmdline)
+ || !entry_from_cmdline)
{
entry_symbol.name = name;
entry_from_cmdline = cmdline;
{
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;
has been specified, then use the load region for the runtime region
as well. */
if (lma_memspec != NULL
- && ! have_vma
+ && !have_vma
&& strcmp (memspec, DEFAULT_MEMORY_REGION) == 0)
*region = *lma_region;
else
*region = lang_memory_region_lookup (memspec, FALSE);
if (have_lma && lma_memspec != 0)
- einfo (_("%X%P:%S: section has both a load address and a load region\n"),
+ einfo (_("%X%P:%pS: section has both a load address and a load region\n"),
NULL);
}
current_section->load_base != NULL,
current_section->addr_tree != NULL);
- /* If this section has no load region or base, but uses the same
- region as the previous section, then propagate the previous
- section's load region. */
-
- if (current_section->lma_region == NULL
- && current_section->load_base == NULL
- && current_section->addr_tree == NULL
- && current_section->region == current_section->prev->region)
- current_section->lma_region = current_section->prev->lma_region;
-
current_section->fill = fill;
current_section->phdrs = phdrs;
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)
{
if (last == NULL)
{
- lang_output_section_statement_type * tmp_os;
+ lang_output_section_statement_type *tmp_os;
/* If we have not run across a section with a program
header assigned to it yet, then scan forwards to find
else
at = exp_get_vma (l->at, 0, "phdr load address");
- if (! bfd_record_phdr (link_info.output_bfd, l->type,
- l->flags != NULL, flags, l->at != NULL,
- at, l->filehdr, l->phdrs, c, secs))
+ if (!bfd_record_phdr (link_info.output_bfd, l->type,
+ l->flags != NULL, flags, l->at != NULL,
+ at, l->filehdr, l->phdrs, c, secs))
einfo (_("%F%P: bfd_record_phdr failed: %E\n"));
}
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)
{
for (pl = os->phdrs;
pl != NULL;
pl = pl->next)
- if (! pl->used && strcmp (pl->name, "NONE") != 0)
+ if (!pl->used && strcmp (pl->name, "NONE") != 0)
einfo (_("%X%P: section `%s' assigned to non-existent phdr `%s'\n"),
os->name, pl->name);
}
n = (struct lang_nocrossrefs *) xmalloc (sizeof *n);
n->next = nocrossref_list;
n->list = l;
+ n->onlyfirst = FALSE;
nocrossref_list = n;
/* Set notice_all so that we get informed about all symbols. */
link_info.notice_all = TRUE;
}
+
+/* Record a section that cannot be referenced from a list of sections. */
+
+void
+lang_add_nocrossref_to (lang_nocrossref_type *l)
+{
+ lang_add_nocrossref (l);
+ nocrossref_list->onlyfirst = TRUE;
+}
\f
/* Overlay handling. We handle overlays with some static variables. */
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;
overlay_vma = NULL;
overlay_list = NULL;
overlay_max = NULL;
+ overlay_subalign = NULL;
}
\f
/* Version handling. This is only useful for ELF. */
for (; r != NULL; r = r->next)
{
if (r->origin_exp)
- {
- exp_fold_tree_no_dot (r->origin_exp);
- if (expld.result.valid_p)
- {
- r->origin = expld.result.value;
- r->current = r->origin;
- }
- else
- einfo (_("%F%P: invalid origin for memory region %s\n"), r->name_list.name);
- }
+ {
+ exp_fold_tree_no_dot (r->origin_exp);
+ if (expld.result.valid_p)
+ {
+ r->origin = expld.result.value;
+ r->current = r->origin;
+ }
+ else
+ einfo (_("%F%P: invalid origin for memory region %s\n"),
+ r->name_list.name);
+ }
if (r->length_exp)
- {
- exp_fold_tree_no_dot (r->length_exp);
- if (expld.result.valid_p)
- r->length = expld.result.value;
- else
- einfo (_("%F%P: invalid length for memory region %s\n"), r->name_list.name);
- }
+ {
+ exp_fold_tree_no_dot (r->length_exp);
+ if (expld.result.valid_p)
+ r->length = expld.result.value;
+ else
+ einfo (_("%F%P: invalid length for memory region %s\n"),
+ r->name_list.name);
+ }
}
}
void
lang_append_dynamic_list_cpp_typeinfo (void)
{
- const char * symbols [] =
+ const char *symbols[] =
{
"typeinfo name for*",
"typeinfo for*"
void
lang_append_dynamic_list_cpp_new (void)
{
- const char * symbols [] =
+ const char *symbols[] =
{
"operator new*",
"operator delete*"
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");
}
}