/* Linker command language support.
- Copyright (C) 1991-2019 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"
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)
{
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);
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;
FAIL ();
}
- lang_statement_append (&input_file_chain,
- (lang_statement_union_type *) p,
- &p->next_real_file);
+ lang_statement_append (&input_file_chain, p, &p->next_real_file);
return p;
}
within the sysroot subdirectory.) */
unsigned int outer_sysrooted = input_flags.sysrooted;
input_flags.sysrooted = 0;
- ret = new_afile (sysrooted_name, file_type, target, TRUE);
+ ret = new_afile (sysrooted_name, file_type, target);
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
address, so we store the pointer in a variable and cast that
instead. */
nextp = &ret->s.output_section_statement.next;
- lang_statement_append (&lang_os_list,
- &ret->s,
- (lang_statement_union_type **) nextp);
+ lang_statement_append (&lang_os_list, &ret->s, nextp);
return &ret->root;
}
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;
NULL, region_name, alias);
/* Add alias to region name list. */
- n = (lang_memory_region_name *) stat_alloc (sizeof (lang_memory_region_name));
+ n = stat_alloc (sizeof (lang_memory_region_name));
n->name = xstrdup (alias);
n->next = region->name_list.next;
region->name_list.next = n;
lang_output_section_statement_type *
lang_output_section_get (const asection *output_section)
{
- return get_userdata (output_section);
+ return bfd_section_userdata (output_section);
}
/* Find or create an output_section_statement with the given NAME.
/* We know the first statement on this list is *ABS*. May as well
skip it. */
- first = &lang_os_list.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_os_list.head->output_section_statement;
+ ignore_first = after == (void *) lang_os_list.head;
for (where = &after->header.next;
*where != NULL;
/* Shuffle the bfd section list to make the output file look
neater. This is really only cosmetic. */
if (place->section == NULL
- && after != &lang_os_list.head->output_section_statement)
+ && after != (void *) lang_os_list.head)
{
asection *bfd_section = after->bfd_section;
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;
}
/* 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. */
{
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. */
case bfd_archive:
check_excluded_libs (entry->the_bfd);
- entry->the_bfd->usrdata = entry;
+ bfd_set_usrdata (entry->the_bfd, entry);
if (entry->flags.whole_archive)
{
bfd *member = NULL;
/* 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)
{
}
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:
einfo ("%F");
}
+/* Open the CTF sections in the input files with libctf: if any were opened,
+ create a fake input file that we'll write the merged CTF data to later
+ on. */
+
+static void
+ldlang_open_ctf (void)
+{
+ int any_ctf = 0;
+ int err;
+
+ LANG_FOR_EACH_INPUT_STATEMENT (file)
+ {
+ asection *sect;
+
+ /* Incoming files from the compiler have a single ctf_file_t in them
+ (which is presented to us by the libctf API in a ctf_archive_t
+ wrapper): files derived from a previous relocatable link have a CTF
+ archive containing possibly many CTF files. */
+
+ if ((file->the_ctf = ctf_bfdopen (file->the_bfd, &err)) == NULL)
+ {
+ if (err != ECTF_NOCTFDATA)
+ einfo (_("%P: warning: CTF section in `%pI' not loaded: "
+ "its types will be discarded: `%s'\n"), file,
+ ctf_errmsg (err));
+ continue;
+ }
+
+ /* 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;
+
+ any_ctf = 1;
+ }
+
+ if (!any_ctf)
+ {
+ ctf_output = NULL;
+ return;
+ }
+
+ if ((ctf_output = ctf_create (&err)) != NULL)
+ return;
+
+ einfo (_("%P: warning: CTF output not created: `s'\n"),
+ ctf_errmsg (err));
+
+ LANG_FOR_EACH_INPUT_STATEMENT (errfile)
+ ctf_close (errfile->the_ctf);
+}
+
+/* Merge together CTF sections. After this, only the symtab-dependent
+ function and data object sections need adjustment. */
+
+static void
+lang_merge_ctf (void)
+{
+ asection *output_sect;
+
+ if (!ctf_output)
+ return;
+
+ output_sect = bfd_get_section_by_name (link_info.output_bfd, ".ctf");
+
+ /* If the section was discarded, don't waste time merging. */
+ if (output_sect == NULL)
+ {
+ ctf_file_close (ctf_output);
+ ctf_output = NULL;
+
+ LANG_FOR_EACH_INPUT_STATEMENT (file)
+ {
+ ctf_close (file->the_ctf);
+ file->the_ctf = NULL;
+ }
+ return;
+ }
+
+ LANG_FOR_EACH_INPUT_STATEMENT (file)
+ {
+ if (!file->the_ctf)
+ continue;
+
+ /* Takes ownership of file->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
ldlang_undef_chain_list_type *new_undef;
undef_from_cmdline = undef_from_cmdline || cmdline;
- new_undef = (ldlang_undef_chain_list_type *) stat_alloc (sizeof (*new_undef));
+ new_undef = stat_alloc (sizeof (*new_undef));
new_undef->next = ldlang_undef_chain_list_head;
ldlang_undef_chain_list_head = new_undef;
struct require_defined_symbol *ptr;
ldlang_add_undef (name, TRUE);
- ptr = (struct require_defined_symbol *) stat_alloc (sizeof (*ptr));
+ ptr = stat_alloc (sizeof (*ptr));
ptr->next = require_defined_symbol_list;
ptr->name = strdup (name);
require_defined_symbol_list = ptr;
(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)
{
lang_reset_memory_regions ();
}
- for (os = &lang_os_list.head->output_section_statement;
+ for (os = (void *) lang_os_list.head;
os != NULL;
os = os->next)
{
if (map_head_is_link_order)
return;
- for (os = &lang_os_list.head->output_section_statement;
+ for (os = (void *) lang_os_list.head;
os != NULL;
os = os->next)
{
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);
}
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;
int len;
bfd_vma addr;
- init_opb ();
+ init_opb (i);
print_space ();
minfo ("%s", i->name);
size = 0;
}
- minfo ("0x%V %W %pB\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;
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;
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;
int section_alignment = 0;
os = &s->output_section_statement;
+ init_opb (os->bfd_section);
if (os->constraint == -1)
break;
" 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));
+ 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;
if (command_line.check_section_addresses)
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;
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;
}
/* 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 = &(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)
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 (_("%F%P: %s: can't set start address\n"), entry_symbol.name);
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))))
+ bfd_section_vma (ts));
+ if (!bfd_set_start_address (link_info.output_bfd,
+ bfd_section_vma (ts)))
einfo (_("%F%P: can't set start address\n"));
}
else
static void
lang_check (void)
{
- lang_statement_union_type *file;
+ lang_input_statement_type *file;
bfd *input_bfd;
const bfd_arch_info_type *compatible;
- for (file = file_chain.head; file != NULL; file = file->input_statement.next)
+ for (file = (void *) file_chain.head;
+ file != NULL;
+ file = file->next)
{
#ifdef ENABLE_PLUGINS
/* 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);
}
}
-/* Call a function on each input file. This function will be called
- on an archive, but not on the elements. */
+/* Call a function on each real input file. This function will be
+ called on an archive, but not on the elements. */
void
lang_for_each_input_file (void (*func) (lang_input_statement_type *))
{
lang_input_statement_type *f;
- for (f = &input_file_chain.head->input_statement;
+ for (f = (void *) input_file_chain.head;
f != NULL;
- f = &f->next_real_file->input_statement)
- func (f);
+ f = f->next_real_file)
+ if (f->flags.real)
+ func (f);
}
-/* Call a function on each file. The function will be called on all
- the elements of an archive which are included in the link, but will
- not be called on the archive file itself. */
+/* Call a function on each real file. The function will be called on
+ all the elements of an archive which are included in the link, but
+ will not be called on the archive file itself. */
void
lang_for_each_file (void (*func) (lang_input_statement_type *))
{
LANG_FOR_EACH_INPUT_STATEMENT (f)
{
- func (f);
+ if (f->flags.real)
+ func (f);
}
}
void
ldlang_add_file (lang_input_statement_type *entry)
{
- lang_statement_append (&file_chain,
- (lang_statement_union_type *) entry,
- &entry->next);
+ lang_statement_append (&file_chain, entry, &entry->next);
/* The BFD linker needs to have a list of all input BFDs involved in
a link. */
*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
p->last_os = NULL;
}
- for (os = &lang_os_list.head->output_section_statement;
+ for (os = (void *) lang_os_list.head;
os != NULL;
os = os->next)
{
inserted at the head of the file_chain. */
static lang_input_statement_type *
-find_replacements_insert_point (void)
+find_replacements_insert_point (bfd_boolean *before)
{
lang_input_statement_type *claim1, *lastobject;
- lastobject = &input_file_chain.head->input_statement;
- for (claim1 = &file_chain.head->input_statement;
+ lastobject = (void *) input_file_chain.head;
+ for (claim1 = (void *) file_chain.head;
claim1 != NULL;
- claim1 = &claim1->next->input_statement)
+ claim1 = claim1->next)
{
if (claim1->flags.claimed)
- return claim1->flags.claim_archive ? lastobject : claim1;
+ {
+ *before = claim1->flags.claim_archive;
+ return claim1->flags.claim_archive ? lastobject : claim1;
+ }
/* Update lastobject if this is a real object file. */
if (claim1->the_bfd != NULL && claim1->the_bfd->my_archive == NULL)
lastobject = claim1;
/* No files were claimed by the plugin. Choose the last object
file found on the list (maybe the first, dummy entry) as the
insert point. */
+ *before = FALSE;
return lastobject;
}
/* Find where to insert ADD, an archive element or shared library
added during a rescan. */
-static lang_statement_union_type **
+static lang_input_statement_type **
find_rescan_insertion (lang_input_statement_type *add)
{
bfd *add_bfd = add->the_bfd;
lang_input_statement_type *f;
lang_input_statement_type *last_loaded = NULL;
lang_input_statement_type *before = NULL;
- lang_statement_union_type **iter = NULL;
+ lang_input_statement_type **iter = NULL;
if (add_bfd->my_archive != NULL)
add_bfd = add_bfd->my_archive;
file chain if it is full of archive elements. Archives don't
appear on the file chain, but if an element has been extracted
then their input_statement->next points at it. */
- for (f = &input_file_chain.head->input_statement;
+ for (f = (void *) input_file_chain.head;
f != NULL;
- f = &f->next_real_file->input_statement)
+ f = f->next_real_file)
{
if (f->the_bfd == add_bfd)
{
before = last_loaded;
if (f->next != NULL)
- return &f->next->input_statement.next;
+ return &f->next->next;
}
if (f->the_bfd != NULL && f->next != NULL)
last_loaded = f;
for (iter = before ? &before->next : &file_chain.head->input_statement.next;
*iter != NULL;
- iter = &(*iter)->input_statement.next)
- if (!(*iter)->input_statement.flags.claim_archive
- && (*iter)->input_statement.the_bfd->my_archive == NULL)
+ iter = &(*iter)->next)
+ if (!(*iter)->flags.claim_archive
+ && (*iter)->the_bfd->my_archive == NULL)
break;
return iter;
destlist->tail = savetail;
*savetail = NULL;
}
+
+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. */
if (name == NULL)
return;
- sym = (struct bfd_sym_chain *) stat_alloc (sizeof (*sym));
+ sym = stat_alloc (sizeof (*sym));
sym->next = link_info.gc_sym_list;
sym->name = name;
{
lang_output_section_statement_type *os;
- for (os = &lang_os_list.head->output_section_statement;
+ for (os = (void *) lang_os_list.head;
os != NULL;
os = os->next)
{
/* Open the output file. */
lang_for_each_statement (ldlang_open_output);
- init_opb ();
+ init_opb (NULL);
ldemul_create_output_section_statements ();
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);
lang_list_remove_tail (&file_chain, &files);
while (files.head != NULL)
{
- lang_statement_union_type **insert;
- lang_statement_union_type **iter, *temp;
+ lang_input_statement_type **insert;
+ lang_input_statement_type **iter, *temp;
bfd *my_arch;
insert = find_rescan_insertion (&files.head->input_statement);
iter = &files.head->input_statement.next;
my_arch = files.head->input_statement.the_bfd->my_archive;
if (my_arch != NULL)
- for (; *iter != NULL; iter = &(*iter)->input_statement.next)
- if ((*iter)->input_statement.the_bfd->my_archive != my_arch)
+ for (; *iter != NULL; iter = &(*iter)->next)
+ if ((*iter)->the_bfd->my_archive != my_arch)
break;
temp = *insert;
- *insert = files.head;
- files.head = *iter;
+ *insert = &files.head->input_statement;
+ files.head = (lang_statement_union_type *) *iter;
*iter = temp;
if (my_arch != NULL)
{
- lang_input_statement_type *parent = my_arch->usrdata;
+ lang_input_statement_type *parent = bfd_usrdata (my_arch);
if (parent != NULL)
- parent->next = (lang_statement_union_type *)
+ parent->next = (lang_input_statement_type *)
((char *) iter
- offsetof (lang_input_statement_type, next));
}
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
}
}
+ /* 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 ();
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_vma (type, 0, "program header type");
bfd_vma at;
c = 0;
- for (os = &lang_os_list.head->output_section_statement;
+ for (os = (void *) lang_os_list.head;
os != NULL;
os = os->next)
{
free (secs);
/* Make sure all the phdr assignments succeeded. */
- for (os = &lang_os_list.head->output_section_statement;
+ for (os = (void *) lang_os_list.head;
os != NULL;
os = os->next)
{
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");
}
}