#include "bfd.h"
#include "sysdep.h"
#include "libiberty.h"
#include "safe-ctype.h"
#include "obstack.h"
#include "bfdlink.h"
#include "ld.h"
#include "ldmain.h"
#include "ldexp.h"
#include "ldlang.h"
#include <ldgram.h>
#include "ldlex.h"
#include "ldmisc.h"
#include "ldctor.h"
#include "ldfile.h"
#include "ldemul.h"
#include "fnmatch.h"
#include "demangle.h"
#include "hashtab.h"
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) & (((TYPE*) 0)->MEMBER))
#endif
static struct obstack stat_obstack;
static struct obstack map_obstack;
#define obstack_chunk_alloc xmalloc
#define obstack_chunk_free free
static const char *startup_file;
static lang_statement_list_type input_file_chain;
static bfd_boolean placed_commons = FALSE;
static lang_output_section_statement_type *default_common_section;
static bfd_boolean map_option_f;
static bfd_vma print_dot;
static lang_input_statement_type *first_file;
static const char *current_target;
static const char *output_target;
static lang_statement_list_type statement_list;
static struct lang_phdr *lang_phdr_list;
static struct bfd_hash_table lang_definedness_table;
static void exp_init_os (etree_type *);
static void init_map_userdata (bfd *, asection *, void *);
static lang_input_statement_type *lookup_name (const char *);
static bfd_boolean load_symbols (lang_input_statement_type *,
lang_statement_list_type *);
static struct bfd_hash_entry *lang_definedness_newfunc
(struct bfd_hash_entry *, struct bfd_hash_table *, const char *);
static void insert_undefined (const char *);
static void print_all_symbols (asection *);
static bfd_boolean sort_def_symbol (struct bfd_link_hash_entry *, void *);
static void print_statement (lang_statement_union_type *,
lang_output_section_statement_type *);
static void print_statement_list (lang_statement_union_type *,
lang_output_section_statement_type *);
static void print_statements (void);
static bfd_boolean lang_one_common (struct bfd_link_hash_entry *, void *);
static void lang_record_phdrs (void);
static void lang_do_version_exports_section (void);
typedef void (*callback_t) (lang_wild_statement_type *, struct wildcard_list *,
asection *, lang_input_statement_type *, void *);
lang_output_section_statement_type *abs_output_section;
lang_statement_list_type lang_output_section_statement;
lang_statement_list_type *stat_ptr = &statement_list;
lang_statement_list_type file_chain = { NULL, NULL };
struct bfd_sym_chain entry_symbol = { NULL, NULL };
const char *entry_section = ".text";
bfd_boolean entry_from_cmdline;
bfd_boolean lang_has_input_file = FALSE;
bfd_boolean had_output_filename = FALSE;
bfd_boolean lang_float_flag = FALSE;
bfd_boolean delete_output_file_on_failure = FALSE;
struct lang_nocrossrefs *nocrossref_list;
struct unique_sections *unique_section_list;
static bfd_boolean ldlang_sysrooted_script = FALSE;
int lang_statement_iteration = 0;
etree_type *base;
#define wildcardp(pattern) (strpbrk ((pattern), "?*[") != NULL)
#define new_stat(x, y) \
(x##_type *) new_statement (x##_enum, sizeof (x##_type), y)
#define outside_section_address(q) \
((q)->output_offset + (q)->output_section->vma)
#define outside_symbol_address(q) \
((q)->value + outside_section_address (q->section))
#define SECTION_NAME_MAP_LENGTH (16)
void *
stat_alloc (size_t size)
{
return obstack_alloc (&stat_obstack, size);
}
bfd_boolean
unique_section_p (const asection *sec)
{
struct unique_sections *unam;
const char *secnam;
if (link_info.relocatable
&& sec->owner != NULL
&& bfd_is_group_section (sec->owner, sec))
return TRUE;
secnam = sec->name;
for (unam = unique_section_list; unam; unam = unam->next)
if (wildcardp (unam->name)
? fnmatch (unam->name, secnam, 0) == 0
: strcmp (unam->name, secnam) == 0)
{
return TRUE;
}
return FALSE;
}
static void
walk_wild_section (lang_wild_statement_type *ptr,
lang_input_statement_type *file,
callback_t callback,
void *data)
{
asection *s;
if (file->just_syms_flag)
return;
for (s = file->the_bfd->sections; s != NULL; s = s->next)
{
struct wildcard_list *sec;
sec = ptr->section_list;
if (sec == NULL)
(*callback) (ptr, sec, s, file, data);
while (sec != NULL)
{
bfd_boolean skip = FALSE;
struct name_list *list_tmp;
for (list_tmp = sec->spec.exclude_name_list;
list_tmp;
list_tmp = list_tmp->next)
{
if (wildcardp (list_tmp->name))
skip = fnmatch (list_tmp->name, file->filename, 0) == 0;
else
skip = strcmp (list_tmp->name, file->filename) == 0;
if (! skip && file->the_bfd != NULL
&& file->the_bfd->my_archive != NULL
&& file->the_bfd->my_archive->filename != NULL)
{
if (wildcardp (list_tmp->name))
skip = fnmatch (list_tmp->name,
file->the_bfd->my_archive->filename,
0) == 0;
else
skip = strcmp (list_tmp->name,
file->the_bfd->my_archive->filename) == 0;
}
if (skip)
break;
}
if (!skip && sec->spec.name != NULL)
{
const char *sname = bfd_get_section_name (file->the_bfd, s);
if (wildcardp (sec->spec.name))
skip = fnmatch (sec->spec.name, sname, 0) != 0;
else
skip = strcmp (sec->spec.name, sname) != 0;
}
if (!skip)
(*callback) (ptr, sec, s, file, data);
sec = sec->next;
}
}
}
static void
walk_wild_file (lang_wild_statement_type *s,
lang_input_statement_type *f,
callback_t callback,
void *data)
{
if (f->the_bfd == NULL
|| ! bfd_check_format (f->the_bfd, bfd_archive))
walk_wild_section (s, f, callback, data);
else
{
bfd *member;
member = bfd_openr_next_archived_file (f->the_bfd, NULL);
while (member != NULL)
{
if (member->usrdata != NULL)
{
walk_wild_section (s, member->usrdata, callback, data);
}
member = bfd_openr_next_archived_file (f->the_bfd, member);
}
}
}
static void
walk_wild (lang_wild_statement_type *s, callback_t callback, void *data)
{
const char *file_spec = s->filename;
if (file_spec == NULL)
{
LANG_FOR_EACH_INPUT_STATEMENT (f)
{
walk_wild_file (s, f, callback, data);
}
}
else if (wildcardp (file_spec))
{
LANG_FOR_EACH_INPUT_STATEMENT (f)
{
if (fnmatch (file_spec, f->filename, FNM_FILE_NAME) == 0)
walk_wild_file (s, f, callback, data);
}
}
else
{
lang_input_statement_type *f;
f = lookup_name (file_spec);
if (f)
walk_wild_file (s, f, callback, data);
}
}
static void
lang_for_each_statement_worker (void (*func) (lang_statement_union_type *),
lang_statement_union_type *s)
{
for (; s != NULL; s = s->header.next)
{
func (s);
switch (s->header.type)
{
case lang_constructors_statement_enum:
lang_for_each_statement_worker (func, constructor_list.head);
break;
case lang_output_section_statement_enum:
lang_for_each_statement_worker
(func, s->output_section_statement.children.head);
break;
case lang_wild_statement_enum:
lang_for_each_statement_worker (func,
s->wild_statement.children.head);
break;
case lang_group_statement_enum:
lang_for_each_statement_worker (func,
s->group_statement.children.head);
break;
case lang_data_statement_enum:
case lang_reloc_statement_enum:
case lang_object_symbols_statement_enum:
case lang_output_statement_enum:
case lang_target_statement_enum:
case lang_input_section_enum:
case lang_input_statement_enum:
case lang_assignment_statement_enum:
case lang_padding_statement_enum:
case lang_address_statement_enum:
case lang_fill_statement_enum:
break;
default:
FAIL ();
break;
}
}
}
void
lang_for_each_statement (void (*func) (lang_statement_union_type *))
{
lang_for_each_statement_worker (func, statement_list.head);
}
void
lang_list_init (lang_statement_list_type *list)
{
list->head = NULL;
list->tail = &list->head;
}
static lang_statement_union_type *
new_statement (enum statement_enum type,
size_t size,
lang_statement_list_type *list)
{
lang_statement_union_type *new;
new = stat_alloc (size);
new->header.type = type;
new->header.next = NULL;
lang_statement_append (list, new, &new->header.next);
return new;
}
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)
{
lang_input_statement_type *p;
if (add_to_list)
p = new_stat (lang_input_statement, stat_ptr);
else
{
p = stat_alloc (sizeof (lang_input_statement_type));
p->header.next = NULL;
}
lang_has_input_file = TRUE;
p->target = target;
p->sysrooted = FALSE;
switch (file_type)
{
case lang_input_file_is_symbols_only_enum:
p->filename = name;
p->is_archive = FALSE;
p->real = TRUE;
p->local_sym_name = name;
p->just_syms_flag = TRUE;
p->search_dirs_flag = FALSE;
break;
case lang_input_file_is_fake_enum:
p->filename = name;
p->is_archive = FALSE;
p->real = FALSE;
p->local_sym_name = name;
p->just_syms_flag = FALSE;
p->search_dirs_flag = FALSE;
break;
case lang_input_file_is_l_enum:
p->is_archive = TRUE;
p->filename = name;
p->real = TRUE;
p->local_sym_name = concat ("-l", name, NULL);
p->just_syms_flag = FALSE;
p->search_dirs_flag = TRUE;
break;
case lang_input_file_is_marker_enum:
p->filename = name;
p->is_archive = FALSE;
p->real = FALSE;
p->local_sym_name = name;
p->just_syms_flag = FALSE;
p->search_dirs_flag = TRUE;
break;
case lang_input_file_is_search_file_enum:
p->sysrooted = ldlang_sysrooted_script;
p->filename = name;
p->is_archive = FALSE;
p->real = TRUE;
p->local_sym_name = name;
p->just_syms_flag = FALSE;
p->search_dirs_flag = TRUE;
break;
case lang_input_file_is_file_enum:
p->filename = name;
p->is_archive = FALSE;
p->real = TRUE;
p->local_sym_name = name;
p->just_syms_flag = FALSE;
p->search_dirs_flag = FALSE;
break;
default:
FAIL ();
}
p->the_bfd = NULL;
p->asymbols = NULL;
p->next_real_file = NULL;
p->next = NULL;
p->symbol_count = 0;
p->dynamic = config.dynamic_link;
p->add_needed = add_needed;
p->as_needed = as_needed;
p->whole_archive = whole_archive;
p->loaded = FALSE;
lang_statement_append (&input_file_chain,
(lang_statement_union_type *) p,
&p->next_real_file);
return p;
}
lang_input_statement_type *
lang_add_input_file (const char *name,
lang_input_file_enum_type file_type,
const char *target)
{
lang_has_input_file = TRUE;
return new_afile (name, file_type, target, TRUE);
}
void
lang_init (void)
{
obstack_begin (&stat_obstack, 1000);
stat_ptr = &statement_list;
lang_list_init (stat_ptr);
lang_list_init (&input_file_chain);
lang_list_init (&lang_output_section_statement);
lang_list_init (&file_chain);
first_file = lang_add_input_file (NULL, lang_input_file_is_marker_enum,
NULL);
abs_output_section =
lang_output_section_statement_lookup (BFD_ABS_SECTION_NAME);
abs_output_section->bfd_section = bfd_abs_section_ptr;
if (!bfd_hash_table_init_n (&lang_definedness_table,
lang_definedness_newfunc, 3))
einfo (_("%P%F: out of memory during initialization"));
lang_statement_iteration = 0;
}
static lang_memory_region_type *lang_memory_region_list;
static lang_memory_region_type **lang_memory_region_list_tail
= &lang_memory_region_list;
lang_memory_region_type *
lang_memory_region_lookup (const char *const name, bfd_boolean create)
{
lang_memory_region_type *p;
lang_memory_region_type *new;
if (name == NULL)
return NULL;
for (p = lang_memory_region_list; p != NULL; p = p->next)
if (strcmp (p->name, name) == 0)
{
if (create)
einfo (_("%P:%S: warning: redeclaration of memory region '%s'\n"),
name);
return p;
}
if (!create && strcmp (name, DEFAULT_MEMORY_REGION))
einfo (_("%P:%S: warning: memory region %s not declared\n"), name);
new = stat_alloc (sizeof (lang_memory_region_type));
new->name = xstrdup (name);
new->next = NULL;
*lang_memory_region_list_tail = new;
lang_memory_region_list_tail = &new->next;
new->origin = 0;
new->flags = 0;
new->not_flags = 0;
new->length = ~(bfd_size_type) 0;
new->current = 0;
new->had_full_message = FALSE;
return new;
}
static lang_memory_region_type *
lang_memory_default (asection *section)
{
lang_memory_region_type *p;
flagword sec_flags = section->flags;
if ((sec_flags & (SEC_ALLOC | SEC_READONLY | SEC_CODE)) == SEC_ALLOC)
sec_flags |= SEC_DATA;
for (p = lang_memory_region_list; p != NULL; p = p->next)
{
if ((p->flags & sec_flags) != 0
&& (p->not_flags & sec_flags) == 0)
{
return p;
}
}
return lang_memory_region_lookup (DEFAULT_MEMORY_REGION, FALSE);
}
static lang_output_section_statement_type *
lang_output_section_find_1 (const char *const name, int constraint)
{
lang_output_section_statement_type *lookup;
for (lookup = &lang_output_section_statement.head->output_section_statement;
lookup != NULL;
lookup = lookup->next)
{
if (strcmp (name, lookup->name) == 0
&& lookup->constraint != -1
&& (constraint == 0 || constraint == lookup->constraint))
return lookup;
}
return NULL;
}
lang_output_section_statement_type *
lang_output_section_find (const char *const name)
{
return lang_output_section_find_1 (name, 0);
}
static lang_output_section_statement_type *
lang_output_section_statement_lookup_1 (const char *const name, int constraint)
{
lang_output_section_statement_type *lookup;
lookup = lang_output_section_find_1 (name, constraint);
if (lookup == NULL)
{
lookup = new_stat (lang_output_section_statement, stat_ptr);
lookup->region = NULL;
lookup->lma_region = NULL;
lookup->fill = 0;
lookup->block_value = 1;
lookup->name = name;
lookup->next = NULL;
lookup->bfd_section = NULL;
lookup->processed = 0;
lookup->constraint = constraint;
lookup->sectype = normal_section;
lookup->addr_tree = NULL;
lang_list_init (&lookup->children);
lookup->memspec = NULL;
lookup->flags = 0;
lookup->subsection_alignment = -1;
lookup->section_alignment = -1;
lookup->load_base = NULL;
lookup->update_dot_tree = NULL;
lookup->phdrs = NULL;
lang_statement_append (&lang_output_section_statement,
(lang_statement_union_type *) lookup,
(lang_statement_union_type **) &lookup->next);
}
return lookup;
}
lang_output_section_statement_type *
lang_output_section_statement_lookup (const char *const name)
{
return lang_output_section_statement_lookup_1 (name, 0);
}
lang_output_section_statement_type *
lang_output_section_find_by_flags (const asection *sec,
lang_output_section_statement_type **exact)
{
lang_output_section_statement_type *first, *look, *found;
flagword flags;
first = &lang_output_section_statement.head->output_section_statement;
first = first->next;
found = NULL;
for (look = first; look; look = look->next)
{
flags = look->flags;
if (look->bfd_section != NULL)
flags = look->bfd_section->flags;
flags ^= sec->flags;
if (!(flags & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_READONLY
| SEC_CODE | SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
found = look;
}
if (found != NULL)
{
*exact = found;
return found;
}
if (sec->flags & SEC_CODE)
{
for (look = first; look; look = look->next)
{
flags = look->flags;
if (look->bfd_section != NULL)
flags = look->bfd_section->flags;
flags ^= sec->flags;
if (!(flags & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
| SEC_CODE | SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
found = look;
}
return found;
}
if (sec->flags & (SEC_READONLY | SEC_THREAD_LOCAL))
{
for (look = first; look; look = look->next)
{
flags = look->flags;
if (look->bfd_section != NULL)
flags = look->bfd_section->flags;
flags ^= sec->flags;
if (!(flags & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
| SEC_READONLY))
&& !(look->flags & (SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
found = look;
}
return found;
}
if (sec->flags & SEC_SMALL_DATA)
{
for (look = first; look; look = look->next)
{
flags = look->flags;
if (look->bfd_section != NULL)
flags = look->bfd_section->flags;
flags ^= sec->flags;
if (!(flags & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
| SEC_THREAD_LOCAL))
|| ((look->flags & SEC_SMALL_DATA)
&& !(sec->flags & SEC_HAS_CONTENTS)))
found = look;
}
return found;
}
if (sec->flags & SEC_HAS_CONTENTS)
{
for (look = first; look; look = look->next)
{
flags = look->flags;
if (look->bfd_section != NULL)
flags = look->bfd_section->flags;
flags ^= sec->flags;
if (!(flags & (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD
| SEC_SMALL_DATA | SEC_THREAD_LOCAL)))
found = look;
}
return found;
}
for (look = first; look; look = look->next)
{
flags = look->flags;
if (look->bfd_section != NULL)
flags = look->bfd_section->flags;
flags ^= sec->flags;
if (!(flags & SEC_ALLOC))
found = look;
}
return found;
}
static asection *
output_prev_sec_find (lang_output_section_statement_type *os)
{
asection *s = (asection *) NULL;
lang_output_section_statement_type *lookup;
for (lookup = &lang_output_section_statement.head->output_section_statement;
lookup != NULL;
lookup = lookup->next)
{
if (lookup->constraint == -1)
continue;
if (lookup == os)
return s;
if (lookup->bfd_section != NULL && lookup->bfd_section->owner != NULL)
s = lookup->bfd_section;
}
return NULL;
}
lang_output_section_statement_type *
lang_insert_orphan (lang_input_statement_type *file,
asection *s,
const char *secname,
lang_output_section_statement_type *after,
struct orphan_save *place,
etree_type *address,
lang_statement_list_type *add_child)
{
lang_statement_list_type *old;
lang_statement_list_type add;
const char *ps;
etree_type *load_base;
lang_output_section_statement_type *os;
lang_output_section_statement_type **os_tail;
old = stat_ptr;
if (after != NULL)
{
stat_ptr = &add;
lang_list_init (stat_ptr);
}
ps = NULL;
if (config.build_constructors)
{
for (ps = secname; *ps != '\0'; ps++)
if (! ISALNUM ((unsigned char) *ps) && *ps != '_')
break;
if (*ps == '\0')
{
char *symname;
etree_type *e_align;
symname = (char *) xmalloc (ps - secname + sizeof "__start_" + 1);
symname[0] = bfd_get_symbol_leading_char (output_bfd);
sprintf (symname + (symname[0] != 0), "__start_%s", secname);
e_align = exp_unop (ALIGN_K,
exp_intop ((bfd_vma) 1 << s->alignment_power));
lang_add_assignment (exp_assop ('=', ".", e_align));
lang_add_assignment (exp_assop ('=', symname,
exp_nameop (NAME, ".")));
}
}
if (link_info.relocatable || (s->flags & (SEC_LOAD | SEC_ALLOC)) == 0)
address = exp_intop (0);
load_base = NULL;
if (after != NULL && after->load_base != NULL)
{
etree_type *lma_from_vma;
lma_from_vma = exp_binop ('-', after->load_base,
exp_nameop (ADDR, after->name));
load_base = exp_binop ('+', lma_from_vma,
exp_nameop (ADDR, secname));
}
os_tail = ((lang_output_section_statement_type **)
lang_output_section_statement.tail);
os = lang_enter_output_section_statement (secname, address, 0, NULL, NULL,
load_base, 0);
if (add_child == NULL)
add_child = &os->children;
lang_add_section (add_child, s, os, file);
lang_leave_output_section_statement (0, "*default*", NULL, NULL);
if (config.build_constructors && *ps == '\0')
{
char *symname;
if (after != NULL)
stat_ptr = &add;
symname = (char *) xmalloc (ps - secname + sizeof "__stop_" + 1);
symname[0] = bfd_get_symbol_leading_char (output_bfd);
sprintf (symname + (symname[0] != 0), "__stop_%s", secname);
lang_add_assignment (exp_assop ('=', symname,
exp_nameop (NAME, ".")));
}
if (after != NULL)
stat_ptr = old;
if (after != NULL && os->bfd_section != NULL)
{
asection *snew, **pps;
snew = os->bfd_section;
if (place->section == NULL
&& after != (&lang_output_section_statement.head
->output_section_statement))
{
asection *bfd_section = after->bfd_section;
if (bfd_section == NULL)
bfd_section = output_prev_sec_find (after);
if (bfd_section != NULL && bfd_section != snew)
place->section = &bfd_section->next;
}
if (place->section == NULL)
place->section = &output_bfd->sections;
for (pps = &output_bfd->sections; *pps != snew; pps = &(*pps)->next)
continue;
bfd_section_list_remove (output_bfd, pps);
bfd_section_list_insert (output_bfd, place->section, snew);
place->section = &snew->next;
if (add.head != NULL)
{
lang_output_section_statement_type *newly_added_os;
if (place->stmt == NULL)
{
lang_statement_union_type **where;
lang_statement_union_type **assign = NULL;
for (where = &after->header.next;
*where != NULL;
where = &(*where)->header.next)
{
switch ((*where)->header.type)
{
case lang_assignment_statement_enum:
if (assign == NULL)
{
lang_assignment_statement_type *ass;
ass = &(*where)->assignment_statement;
if (ass->exp->type.node_class != etree_assert
&& ass->exp->assign.dst[0] == '.'
&& ass->exp->assign.dst[1] == 0)
assign = where;
}
continue;
case lang_wild_statement_enum:
case lang_input_section_enum:
case lang_object_symbols_statement_enum:
case lang_fill_statement_enum:
case lang_data_statement_enum:
case lang_reloc_statement_enum:
case lang_padding_statement_enum:
case lang_constructors_statement_enum:
assign = NULL;
continue;
case lang_output_section_statement_enum:
if (assign != NULL)
where = assign;
case lang_input_statement_enum:
case lang_address_statement_enum:
case lang_target_statement_enum:
case lang_output_statement_enum:
case lang_group_statement_enum:
case lang_afile_asection_pair_statement_enum:
break;
}
break;
}
*add.tail = *where;
*where = add.head;
place->os_tail = &after->next;
}
else
{
*add.tail = *place->stmt;
*place->stmt = add.head;
}
if (*old->tail == add.head)
old->tail = add.tail;
place->stmt = add.tail;
newly_added_os = *os_tail;
*os_tail = NULL;
newly_added_os->next = *place->os_tail;
*place->os_tail = newly_added_os;
place->os_tail = &newly_added_os->next;
if (*os_tail == NULL)
lang_output_section_statement.tail
= (lang_statement_union_type **) os_tail;
}
}
return os;
}
static void
lang_map_flags (flagword flag)
{
if (flag & SEC_ALLOC)
minfo ("a");
if (flag & SEC_CODE)
minfo ("x");
if (flag & SEC_READONLY)
minfo ("r");
if (flag & SEC_DATA)
minfo ("w");
if (flag & SEC_LOAD)
minfo ("l");
}
void
lang_map (void)
{
lang_memory_region_type *m;
bfd *p;
minfo (_("\nMemory Configuration\n\n"));
fprintf (config.map_file, "%-16s %-18s %-18s %s\n",
_("Name"), _("Origin"), _("Length"), _("Attributes"));
for (m = lang_memory_region_list; m != NULL; m = m->next)
{
char buf[100];
int len;
fprintf (config.map_file, "%-16s ", m->name);
sprintf_vma (buf, m->origin);
minfo ("0x%s ", buf);
len = strlen (buf);
while (len < 16)
{
print_space ();
++len;
}
minfo ("0x%V", m->length);
if (m->flags || m->not_flags)
{
#ifndef BFD64
minfo (" ");
#endif
if (m->flags)
{
print_space ();
lang_map_flags (m->flags);
}
if (m->not_flags)
{
minfo (" !");
lang_map_flags (m->not_flags);
}
}
print_nl ();
}
fprintf (config.map_file, _("\nLinker script and memory map\n\n"));
if (! command_line.reduce_memory_overheads)
{
obstack_begin (&map_obstack, 1000);
for (p = link_info.input_bfds; p != (bfd *) NULL; p = p->link_next)
bfd_map_over_sections (p, init_map_userdata, 0);
bfd_link_hash_traverse (link_info.hash, sort_def_symbol, 0);
}
print_statements ();
}
static void
init_map_userdata (abfd, sec, data)
bfd *abfd ATTRIBUTE_UNUSED;
asection *sec;
void *data ATTRIBUTE_UNUSED;
{
fat_section_userdata_type *new_data
= ((fat_section_userdata_type *) (stat_alloc
(sizeof (fat_section_userdata_type))));
ASSERT (get_userdata (sec) == NULL);
get_userdata (sec) = new_data;
new_data->map_symbol_def_tail = &new_data->map_symbol_def_head;
}
static bfd_boolean
sort_def_symbol (hash_entry, info)
struct bfd_link_hash_entry *hash_entry;
void *info ATTRIBUTE_UNUSED;
{
if (hash_entry->type == bfd_link_hash_defined
|| hash_entry->type == bfd_link_hash_defweak)
{
struct fat_user_section_struct *ud;
struct map_symbol_def *def;
ud = get_userdata (hash_entry->u.def.section);
if (! ud)
{
init_map_userdata (0, hash_entry->u.def.section, 0);
ud = get_userdata (hash_entry->u.def.section);
}
else if (!ud->map_symbol_def_tail)
ud->map_symbol_def_tail = &ud->map_symbol_def_head;
def = obstack_alloc (&map_obstack, sizeof *def);
def->entry = hash_entry;
*(ud->map_symbol_def_tail) = def;
ud->map_symbol_def_tail = &def->next;
}
return TRUE;
}
static void
init_os (lang_output_section_statement_type *s)
{
lean_section_userdata_type *new;
if (s->bfd_section != NULL)
return;
if (strcmp (s->name, DISCARD_SECTION_NAME) == 0)
einfo (_("%P%F: Illegal use of `%s' section\n"), DISCARD_SECTION_NAME);
new = stat_alloc (SECTION_USERDATA_SIZE);
memset (new, 0, SECTION_USERDATA_SIZE);
s->bfd_section = bfd_get_section_by_name (output_bfd, s->name);
if (s->bfd_section == NULL)
s->bfd_section = bfd_make_section (output_bfd, s->name);
if (s->bfd_section == NULL)
{
einfo (_("%P%F: output format %s cannot represent section called %s\n"),
output_bfd->xvec->name, s->name);
}
s->bfd_section->output_section = s->bfd_section;
s->bfd_section->output_offset = 0;
get_userdata (s->bfd_section) = new;
if (s->addr_tree != NULL)
exp_init_os (s->addr_tree);
if (s->load_base != NULL)
exp_init_os (s->load_base);
}
static void
exp_init_os (etree_type *exp)
{
switch (exp->type.node_class)
{
case etree_assign:
exp_init_os (exp->assign.src);
break;
case etree_binary:
exp_init_os (exp->binary.lhs);
exp_init_os (exp->binary.rhs);
break;
case etree_trinary:
exp_init_os (exp->trinary.cond);
exp_init_os (exp->trinary.lhs);
exp_init_os (exp->trinary.rhs);
break;
case etree_assert:
exp_init_os (exp->assert_s.child);
break;
case etree_unary:
exp_init_os (exp->unary.child);
break;
case etree_name:
switch (exp->type.node_code)
{
case ADDR:
case LOADADDR:
case SIZEOF:
{
lang_output_section_statement_type *os;
os = lang_output_section_find (exp->name.name);
if (os != NULL && os->bfd_section == NULL)
init_os (os);
}
}
break;
default:
break;
}
}
static void
section_already_linked (bfd *abfd, asection *sec, void *data)
{
lang_input_statement_type *entry = data;
if (entry->just_syms_flag)
{
bfd_link_just_syms (abfd, sec, &link_info);
return;
}
if (!(abfd->flags & DYNAMIC))
bfd_section_already_linked (abfd, sec);
}
void
lang_add_section (lang_statement_list_type *ptr,
asection *section,
lang_output_section_statement_type *output,
lang_input_statement_type *file)
{
flagword flags = section->flags;
bfd_boolean discard;
discard = (flags & SEC_EXCLUDE) != 0;
if (strcmp (output->name, DISCARD_SECTION_NAME) == 0)
discard = TRUE;
if ((link_info.strip == strip_debugger || link_info.strip == strip_all)
&& (flags & SEC_DEBUGGING) != 0)
discard = TRUE;
if (discard)
{
if (section->output_section == NULL)
{
section->output_section = bfd_abs_section_ptr;
}
return;
}
if (section->output_section == NULL)
{
bfd_boolean first;
lang_input_section_type *new;
flagword flags;
if (output->bfd_section == NULL)
init_os (output);
first = ! output->bfd_section->linker_has_input;
output->bfd_section->linker_has_input = 1;
new = new_stat (lang_input_section, ptr);
new->section = section;
new->ifile = file;
section->output_section = output->bfd_section;
flags = section->flags;
flags &= ~ SEC_NEVER_LOAD;
if (! link_info.relocatable)
flags &= ~ (SEC_LINK_ONCE | SEC_LINK_DUPLICATES);
if (! first && (output->bfd_section->flags & SEC_READONLY) == 0)
flags &= ~ SEC_READONLY;
if (! first
&& ((output->bfd_section->flags & (SEC_MERGE | SEC_STRINGS))
!= (flags & (SEC_MERGE | SEC_STRINGS))
|| ((flags & SEC_MERGE)
&& output->bfd_section->entsize != section->entsize)))
{
output->bfd_section->flags &= ~ (SEC_MERGE | SEC_STRINGS);
flags &= ~ (SEC_MERGE | SEC_STRINGS);
}
output->bfd_section->flags |= flags;
if (flags & SEC_MERGE)
output->bfd_section->entsize = section->entsize;
if ((section->flags & SEC_READONLY) == 0)
output->bfd_section->flags &= ~SEC_READONLY;
switch (output->sectype)
{
case normal_section:
break;
case dsect_section:
case copy_section:
case info_section:
case overlay_section:
output->bfd_section->flags &= ~SEC_ALLOC;
break;
case noload_section:
output->bfd_section->flags &= ~SEC_LOAD;
output->bfd_section->flags |= SEC_NEVER_LOAD;
break;
}
if (section->flags & SEC_SMALL_DATA)
output->bfd_section->flags |= SEC_SMALL_DATA;
if (section->alignment_power > output->bfd_section->alignment_power)
output->bfd_section->alignment_power = section->alignment_power;
if (output->section_alignment != -1)
output->bfd_section->alignment_power = output->section_alignment;
if (bfd_get_arch (section->owner) == bfd_arch_tic54x
&& (section->flags & SEC_TIC54X_BLOCK) != 0)
{
output->bfd_section->flags |= SEC_TIC54X_BLOCK;
output->block_value = 128;
}
}
}
static int
compare_section (sort_type sort, asection *asec, asection *bsec)
{
int ret;
switch (sort)
{
default:
abort ();
case by_alignment_name:
ret = (bfd_section_alignment (bsec->owner, bsec)
- bfd_section_alignment (asec->owner, asec));
if (ret)
break;
case by_name:
ret = strcmp (bfd_get_section_name (asec->owner, asec),
bfd_get_section_name (bsec->owner, bsec));
break;
case by_name_alignment:
ret = strcmp (bfd_get_section_name (asec->owner, asec),
bfd_get_section_name (bsec->owner, bsec));
if (ret)
break;
case by_alignment:
ret = (bfd_section_alignment (bsec->owner, bsec)
- bfd_section_alignment (asec->owner, asec));
break;
}
return ret;
}
static lang_statement_union_type *
wild_sort (lang_wild_statement_type *wild,
struct wildcard_list *sec,
lang_input_statement_type *file,
asection *section)
{
const char *section_name;
lang_statement_union_type *l;
if (!wild->filenames_sorted
&& (sec == NULL || sec->spec.sorted == none))
return NULL;
section_name = bfd_get_section_name (file->the_bfd, section);
for (l = wild->children.head; l != NULL; l = l->header.next)
{
lang_input_section_type *ls;
if (l->header.type != lang_input_section_enum)
continue;
ls = &l->input_section;
if (wild->filenames_sorted)
{
const char *fn, *ln;
bfd_boolean fa, la;
int i;
if (file->the_bfd != NULL
&& bfd_my_archive (file->the_bfd) != NULL)
{
fn = bfd_get_filename (bfd_my_archive (file->the_bfd));
fa = TRUE;
}
else
{
fn = file->filename;
fa = FALSE;
}
if (ls->ifile->the_bfd != NULL
&& bfd_my_archive (ls->ifile->the_bfd) != NULL)
{
ln = bfd_get_filename (bfd_my_archive (ls->ifile->the_bfd));
la = TRUE;
}
else
{
ln = ls->ifile->filename;
la = FALSE;
}
i = strcmp (fn, ln);
if (i > 0)
continue;
else if (i < 0)
break;
if (fa || la)
{
if (fa)
fn = file->filename;
if (la)
ln = ls->ifile->filename;
i = strcmp (fn, ln);
if (i > 0)
continue;
else if (i < 0)
break;
}
}
if (sec != NULL && sec->spec.sorted != none)
{
if (compare_section (sec->spec.sorted, section,
ls->section) < 0)
break;
}
}
return l;
}
static void
output_section_callback (lang_wild_statement_type *ptr,
struct wildcard_list *sec,
asection *section,
lang_input_statement_type *file,
void *output)
{
lang_statement_union_type *before;
if (unique_section_p (section))
return;
before = wild_sort (ptr, sec, file, section);
if (before == NULL)
lang_add_section (&ptr->children, section,
(lang_output_section_statement_type *) output,
file);
else
{
lang_statement_list_type list;
lang_statement_union_type **pp;
lang_list_init (&list);
lang_add_section (&list, section,
(lang_output_section_statement_type *) output,
file);
if (list.head != NULL)
{
ASSERT (list.head->header.next == NULL);
for (pp = &ptr->children.head;
*pp != before;
pp = &(*pp)->header.next)
ASSERT (*pp != NULL);
list.head->header.next = *pp;
*pp = list.head;
}
}
}
static void
check_section_callback (lang_wild_statement_type *ptr ATTRIBUTE_UNUSED,
struct wildcard_list *sec ATTRIBUTE_UNUSED,
asection *section,
lang_input_statement_type *file ATTRIBUTE_UNUSED,
void *data)
{
if (unique_section_p (section))
return;
if (section->output_section == NULL && (section->flags & SEC_READONLY) == 0)
((lang_output_section_statement_type *) data)->all_input_readonly = FALSE;
}
static lang_input_statement_type *
lookup_name (const char *name)
{
lang_input_statement_type *search;
for (search = (lang_input_statement_type *) input_file_chain.head;
search != NULL;
search = (lang_input_statement_type *) search->next_real_file)
{
const char * filename = search->local_sym_name;
if (filename == NULL && name == NULL)
return search;
if (filename != NULL
&& name != NULL
&& strcmp (filename, name) == 0)
break;
}
if (search == NULL)
search = new_afile (name, lang_input_file_is_search_file_enum,
default_target, FALSE);
if (search->loaded
|| ! search->real
|| search->filename == NULL)
return search;
if (! load_symbols (search, NULL))
return NULL;
return search;
}
struct excluded_lib
{
char *name;
struct excluded_lib *next;
};
static struct excluded_lib *excluded_libs;
void
add_excluded_libs (const char *list)
{
const char *p = list, *end;
while (*p != '\0')
{
struct excluded_lib *entry;
end = strpbrk (p, ",:");
if (end == NULL)
end = p + strlen (p);
entry = xmalloc (sizeof (*entry));
entry->next = excluded_libs;
entry->name = xmalloc (end - p + 1);
memcpy (entry->name, p, end - p);
entry->name[end - p] = '\0';
excluded_libs = entry;
if (*end == '\0')
break;
p = end + 1;
}
}
static void
check_excluded_libs (bfd *abfd)
{
struct excluded_lib *lib = excluded_libs;
while (lib)
{
int len = strlen (lib->name);
const char *filename = lbasename (abfd->filename);
if (strcmp (lib->name, "ALL") == 0)
{
abfd->no_export = TRUE;
return;
}
if (strncmp (lib->name, filename, len) == 0
&& (filename[len] == '\0'
|| (filename[len] == '.' && filename[len + 1] == 'a'
&& filename[len + 2] == '\0')))
{
abfd->no_export = TRUE;
return;
}
lib = lib->next;
}
}
static bfd_boolean
load_symbols (lang_input_statement_type *entry,
lang_statement_list_type *place)
{
char **matching;
if (entry->loaded)
return TRUE;
ldfile_open_file (entry);
if (! bfd_check_format (entry->the_bfd, bfd_archive)
&& ! bfd_check_format_matches (entry->the_bfd, bfd_object, &matching))
{
bfd_error_type err;
lang_statement_list_type *hold;
bfd_boolean bad_load = TRUE;
bfd_boolean save_ldlang_sysrooted_script;
err = bfd_get_error ();
if (ldemul_unrecognized_file (entry))
return TRUE;
if (err == bfd_error_file_ambiguously_recognized)
{
char **p;
einfo (_("%B: file not recognized: %E\n"), entry->the_bfd);
einfo (_("%B: 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);
else
bad_load = FALSE;
bfd_close (entry->the_bfd);
entry->the_bfd = NULL;
ldfile_open_command_file (entry->filename);
hold = stat_ptr;
stat_ptr = place;
save_ldlang_sysrooted_script = ldlang_sysrooted_script;
ldlang_sysrooted_script = entry->sysrooted;
ldfile_assumed_script = TRUE;
parser_input = input_script;
config.dynamic_link = entry->dynamic;
yyparse ();
ldfile_assumed_script = FALSE;
ldlang_sysrooted_script = save_ldlang_sysrooted_script;
stat_ptr = hold;
return ! bad_load;
}
if (ldemul_recognized_file (entry))
return TRUE;
switch (bfd_get_format (entry->the_bfd))
{
default:
break;
case bfd_object:
ldlang_add_file (entry);
if (trace_files || trace_file_tries)
info_msg ("%I\n", entry);
break;
case bfd_archive:
check_excluded_libs (entry->the_bfd);
if (entry->whole_archive)
{
bfd *member = NULL;
bfd_boolean loaded = TRUE;
for (;;)
{
member = bfd_openr_next_archived_file (entry->the_bfd, member);
if (member == NULL)
break;
if (! bfd_check_format (member, bfd_object))
{
einfo (_("%F%B: member %B in archive is not an object\n"),
entry->the_bfd, member);
loaded = FALSE;
}
if (! ((*link_info.callbacks->add_archive_element)
(&link_info, member, "--whole-archive")))
abort ();
if (! bfd_link_add_symbols (member, &link_info))
{
einfo (_("%F%B: could not read symbols: %E\n"), member);
loaded = FALSE;
}
}
entry->loaded = loaded;
return loaded;
}
break;
}
if (bfd_link_add_symbols (entry->the_bfd, &link_info))
entry->loaded = TRUE;
else
einfo (_("%F%B: could not read symbols: %E\n"), entry->the_bfd);
return entry->loaded;
}
static void
wild (lang_wild_statement_type *s,
const char *target ATTRIBUTE_UNUSED,
lang_output_section_statement_type *output)
{
struct wildcard_list *sec;
walk_wild (s, output_section_callback, output);
for (sec = s->section_list; sec != NULL; sec = sec->next)
{
if (default_common_section != NULL)
break;
if (sec->spec.name != NULL && strcmp (sec->spec.name, "COMMON") == 0)
{
default_common_section = output;
}
}
}
static int
get_target (const bfd_target *target, void *data)
{
const char *sought = data;
return strcmp (target->name, sought) == 0;
}
static void
stricpy (char *dest, char *src)
{
char c;
while ((c = *src++) != 0)
*dest++ = TOLOWER (c);
*dest = 0;
}
static void
strcut (char *haystack, char *needle)
{
haystack = strstr (haystack, needle);
if (haystack)
{
char *src;
for (src = haystack + strlen (needle); *src;)
*haystack++ = *src++;
*haystack = 0;
}
}
static int
name_compare (char *first, char *second)
{
char *copy1;
char *copy2;
int result;
copy1 = xmalloc (strlen (first) + 1);
copy2 = xmalloc (strlen (second) + 1);
stricpy (copy1, first);
stricpy (copy2, second);
strcut (copy1, "big");
strcut (copy1, "little");
strcut (copy2, "big");
strcut (copy2, "little");
for (result = 0; copy1[result] == copy2[result]; result++)
if (copy1[result] == 0)
{
result *= 10;
break;
}
free (copy1);
free (copy2);
return result;
}
static const bfd_target *winner;
static int
closest_target_match (const bfd_target *target, void *data)
{
const bfd_target *original = data;
if (command_line.endian == ENDIAN_BIG
&& target->byteorder != BFD_ENDIAN_BIG)
return 0;
if (command_line.endian == ENDIAN_LITTLE
&& target->byteorder != BFD_ENDIAN_LITTLE)
return 0;
if (target->flavour != original->flavour)
return 0;
if (winner == NULL)
{
winner = target;
return 0;
}
if (name_compare (target->name, original->name)
> name_compare (winner->name, original->name))
winner = target;
return 0;
}
static char *
get_first_input_target (void)
{
char *target = NULL;
LANG_FOR_EACH_INPUT_STATEMENT (s)
{
if (s->header.type == lang_input_statement_enum
&& s->real)
{
ldfile_open_file (s);
if (s->the_bfd != NULL
&& bfd_check_format (s->the_bfd, bfd_object))
{
target = bfd_get_target (s->the_bfd);
if (target != NULL)
break;
}
}
}
return target;
}
const char *
lang_get_output_target (void)
{
const char *target;
if (output_target != NULL)
return output_target;
if (current_target != default_target)
return current_target;
target = get_first_input_target ();
if (target != NULL)
return target;
return default_target;
}
static bfd *
open_output (const char *name)
{
bfd *output;
output_target = lang_get_output_target ();
if (command_line.endian != ENDIAN_UNSET)
{
const bfd_target *target;
enum bfd_endian desired_endian;
target = bfd_search_for_target (get_target, (void *) output_target);
if (target != NULL)
{
if (command_line.endian == ENDIAN_BIG)
desired_endian = BFD_ENDIAN_BIG;
else
desired_endian = BFD_ENDIAN_LITTLE;
if (target->byteorder != desired_endian)
{
if (target->alternative_target != NULL
&& (target->alternative_target->byteorder == desired_endian))
output_target = target->alternative_target->name;
else
{
bfd_search_for_target (closest_target_match,
(void *) target);
if (winner == NULL)
einfo (_("%P: warning: could not find any targets"
" that match endianness requirement\n"));
else
output_target = winner->name;
}
}
}
}
output = bfd_openw (name, output_target);
if (output == NULL)
{
if (bfd_get_error () == bfd_error_invalid_target)
einfo (_("%P%F: target %s not found\n"), output_target);
einfo (_("%P%F: cannot open output file %s: %E\n"), name);
}
delete_output_file_on_failure = TRUE;
if (! bfd_set_format (output, bfd_object))
einfo (_("%P%F:%s: can not make object file: %E\n"), name);
if (! bfd_set_arch_mach (output,
ldfile_output_architecture,
ldfile_output_machine))
einfo (_("%P%F:%s: can not set architecture: %E\n"), name);
link_info.hash = bfd_link_hash_table_create (output);
if (link_info.hash == NULL)
einfo (_("%P%F: can not create link hash table: %E\n"));
bfd_set_gp_size (output, g_switch_value);
return output;
}
static void
ldlang_open_output (lang_statement_union_type *statement)
{
switch (statement->header.type)
{
case lang_output_statement_enum:
ASSERT (output_bfd == NULL);
output_bfd = open_output (statement->output_statement.name);
ldemul_set_output_arch ();
if (config.magic_demand_paged && !link_info.relocatable)
output_bfd->flags |= D_PAGED;
else
output_bfd->flags &= ~D_PAGED;
if (config.text_read_only)
output_bfd->flags |= WP_TEXT;
else
output_bfd->flags &= ~WP_TEXT;
if (link_info.traditional_format)
output_bfd->flags |= BFD_TRADITIONAL_FORMAT;
else
output_bfd->flags &= ~BFD_TRADITIONAL_FORMAT;
break;
case lang_target_statement_enum:
current_target = statement->target_statement.target;
break;
default:
break;
}
}
#define TO_ADDR(X) ((X) >> opb_shift)
#define TO_SIZE(X) ((X) << opb_shift)
static unsigned int opb_shift = 0;
static void
init_opb (void)
{
unsigned x = bfd_arch_mach_octets_per_byte (ldfile_output_architecture,
ldfile_output_machine);
opb_shift = 0;
if (x > 1)
while ((x & 1) == 0)
{
x >>= 1;
++opb_shift;
}
ASSERT (x == 1);
}
static void
open_input_bfds (lang_statement_union_type *s, bfd_boolean force)
{
for (; s != NULL; s = s->header.next)
{
switch (s->header.type)
{
case lang_constructors_statement_enum:
open_input_bfds (constructor_list.head, force);
break;
case lang_output_section_statement_enum:
open_input_bfds (s->output_section_statement.children.head, force);
break;
case lang_wild_statement_enum:
if (s->wild_statement.filename
&& ! wildcardp (s->wild_statement.filename))
lookup_name (s->wild_statement.filename);
open_input_bfds (s->wild_statement.children.head, force);
break;
case lang_group_statement_enum:
{
struct bfd_link_hash_entry *undefs;
do
{
undefs = link_info.hash->undefs_tail;
open_input_bfds (s->group_statement.children.head, TRUE);
}
while (undefs != link_info.hash->undefs_tail);
}
break;
case lang_target_statement_enum:
current_target = s->target_statement.target;
break;
case lang_input_statement_enum:
if (s->input_statement.real)
{
lang_statement_list_type add;
s->input_statement.target = current_target;
if (force
&& !s->input_statement.whole_archive
&& s->input_statement.loaded
&& bfd_check_format (s->input_statement.the_bfd,
bfd_archive))
s->input_statement.loaded = FALSE;
lang_list_init (&add);
if (! load_symbols (&s->input_statement, &add))
config.make_executable = FALSE;
if (add.head != NULL)
{
*add.tail = s->header.next;
s->header.next = add.head;
}
}
break;
default:
break;
}
}
}
void
lang_track_definedness (const char *name)
{
if (bfd_hash_lookup (&lang_definedness_table, name, TRUE, FALSE) == NULL)
einfo (_("%P%F: bfd_hash_lookup failed creating symbol %s\n"), name);
}
static struct bfd_hash_entry *
lang_definedness_newfunc (struct bfd_hash_entry *entry,
struct bfd_hash_table *table ATTRIBUTE_UNUSED,
const char *name ATTRIBUTE_UNUSED)
{
struct lang_definedness_hash_entry *ret
= (struct lang_definedness_hash_entry *) entry;
if (ret == NULL)
ret = (struct lang_definedness_hash_entry *)
bfd_hash_allocate (table, sizeof (struct lang_definedness_hash_entry));
if (ret == NULL)
einfo (_("%P%F: bfd_hash_allocate failed creating symbol %s\n"), name);
ret->iteration = -1;
return &ret->root;
}
int
lang_symbol_definition_iteration (const char *name)
{
struct lang_definedness_hash_entry *defentry
= (struct lang_definedness_hash_entry *)
bfd_hash_lookup (&lang_definedness_table, name, FALSE, FALSE);
if (defentry == NULL)
FAIL ();
return defentry->iteration;
}
void
lang_update_definedness (const char *name, struct bfd_link_hash_entry *h)
{
struct lang_definedness_hash_entry *defentry
= (struct lang_definedness_hash_entry *)
bfd_hash_lookup (&lang_definedness_table, name, FALSE, FALSE);
if (defentry == NULL)
return;
if (h->type != bfd_link_hash_undefined
&& h->type != bfd_link_hash_common
&& h->type != bfd_link_hash_new
&& defentry->iteration == -1)
return;
defentry->iteration = lang_statement_iteration;
}
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)
{
ldlang_undef_chain_list_type *new =
stat_alloc (sizeof (ldlang_undef_chain_list_type));
new->next = ldlang_undef_chain_list_head;
ldlang_undef_chain_list_head = new;
new->name = xstrdup (name);
if (output_bfd != NULL)
insert_undefined (new->name);
}
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 (_("%P%F: 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;
bfd_link_add_undef (link_info.hash, h);
}
}
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);
}
static void
check_input_sections
(lang_statement_union_type *s,
lang_output_section_statement_type *output_section_statement)
{
for (; s != (lang_statement_union_type *) 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)
return;
break;
case lang_constructors_statement_enum:
check_input_sections (constructor_list.head,
output_section_statement);
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)
return;
break;
default:
break;
}
}
}
static void
update_wild_statements (lang_statement_union_type *s)
{
struct wildcard_list *sec;
switch (sort_section)
{
default:
FAIL ();
case none:
break;
case by_name:
case by_alignment:
for (; s != NULL; s = s->header.next)
{
switch (s->header.type)
{
default:
break;
case lang_wild_statement_enum:
sec = s->wild_statement.section_list;
if (sec != NULL)
{
switch (sec->spec.sorted)
{
case none:
sec->spec.sorted = sort_section;
break;
case by_name:
if (sort_section == by_alignment)
sec->spec.sorted = by_name_alignment;
break;
case by_alignment:
if (sort_section == by_name)
sec->spec.sorted = by_alignment_name;
break;
default:
break;
}
}
break;
case lang_constructors_statement_enum:
update_wild_statements (constructor_list.head);
break;
case lang_output_section_statement_enum:
update_wild_statements
(s->output_section_statement.children.head);
break;
case lang_group_statement_enum:
update_wild_statements (s->group_statement.children.head);
break;
}
}
break;
}
}
static void
map_input_to_output_sections
(lang_statement_union_type *s, const char *target,
lang_output_section_statement_type *os)
{
for (; s != NULL; s = s->header.next)
{
switch (s->header.type)
{
case lang_wild_statement_enum:
wild (&s->wild_statement, target, os);
break;
case lang_constructors_statement_enum:
map_input_to_output_sections (constructor_list.head,
target,
os);
break;
case lang_output_section_statement_enum:
if (s->output_section_statement.constraint)
{
if (s->output_section_statement.constraint == -1)
break;
s->output_section_statement.all_input_readonly = TRUE;
check_input_sections (s->output_section_statement.children.head,
&s->output_section_statement);
if ((s->output_section_statement.all_input_readonly
&& s->output_section_statement.constraint == ONLY_IF_RW)
|| (!s->output_section_statement.all_input_readonly
&& s->output_section_statement.constraint == ONLY_IF_RO))
{
s->output_section_statement.constraint = -1;
break;
}
}
map_input_to_output_sections (s->output_section_statement.children.head,
target,
&s->output_section_statement);
break;
case lang_output_statement_enum:
break;
case lang_target_statement_enum:
target = s->target_statement.target;
break;
case lang_group_statement_enum:
map_input_to_output_sections (s->group_statement.children.head,
target,
os);
break;
case lang_data_statement_enum:
exp_init_os (s->data_statement.exp);
if (os != NULL && os->bfd_section == NULL)
init_os (os);
os->bfd_section->flags |= SEC_HAS_CONTENTS;
if (!(os->flags & SEC_NEVER_LOAD))
os->bfd_section->flags |= SEC_ALLOC | SEC_LOAD;
break;
case lang_fill_statement_enum:
case lang_input_section_enum:
case lang_object_symbols_statement_enum:
case lang_reloc_statement_enum:
case lang_padding_statement_enum:
case lang_input_statement_enum:
if (os != NULL && os->bfd_section == NULL)
init_os (os);
break;
case lang_assignment_statement_enum:
if (os != NULL && os->bfd_section == NULL)
init_os (os);
exp_init_os (s->assignment_statement.exp);
break;
case lang_afile_asection_pair_statement_enum:
FAIL ();
break;
case lang_address_statement_enum:
if (!s->address_statement.segment
|| !s->address_statement.segment->used)
{
lang_output_section_statement_type *aos
= (lang_output_section_statement_lookup
(s->address_statement.section_name));
if (aos->bfd_section == NULL)
init_os (aos);
aos->addr_tree = s->address_statement.address;
}
break;
}
}
}
static void
strip_excluded_output_sections (void)
{
lang_output_section_statement_type *os;
for (os = &lang_output_section_statement.head->output_section_statement;
os != NULL;
os = os->next)
{
asection *s;
if (os->constraint == -1)
continue;
s = os->bfd_section;
if (s != NULL && (s->flags & SEC_EXCLUDE) != 0)
{
asection **p;
os->bfd_section = NULL;
for (p = &output_bfd->sections; *p; p = &(*p)->next)
if (*p == s)
{
bfd_section_list_remove (output_bfd, p);
output_bfd->section_count--;
break;
}
}
}
}
static void
print_output_section_statement
(lang_output_section_statement_type *output_section_statement)
{
asection *section = output_section_statement->bfd_section;
int len;
if (output_section_statement != abs_output_section)
{
minfo ("\n%s", output_section_statement->name);
if (section != NULL)
{
print_dot = section->vma;
len = strlen (output_section_statement->name);
if (len >= SECTION_NAME_MAP_LENGTH - 1)
{
print_nl ();
len = 0;
}
while (len < SECTION_NAME_MAP_LENGTH)
{
print_space ();
++len;
}
minfo ("0x%V %W", section->vma, section->size);
if (output_section_statement->load_base != NULL)
{
bfd_vma addr;
addr = exp_get_abs_int (output_section_statement->load_base, 0,
"load base", lang_final_phase_enum);
minfo (_(" load address 0x%V"), addr);
}
}
print_nl ();
}
print_statement_list (output_section_statement->children.head,
output_section_statement);
}
static void
print_assignment (lang_assignment_statement_type *assignment,
lang_output_section_statement_type *output_section)
{
int i;
int is_dot;
etree_type *tree;
etree_value_type result;
for (i = 0; i < SECTION_NAME_MAP_LENGTH; i++)
print_space ();
if (assignment->exp->type.node_class == etree_assert)
{
is_dot = 0;
tree = assignment->exp->assert_s.child;
}
else
{
const char *dst = assignment->exp->assign.dst;
is_dot = dst[0] == '.' && dst[1] == 0;
tree = assignment->exp->assign.src;
}
result = exp_fold_tree (tree, output_section, lang_final_phase_enum,
print_dot, &print_dot);
if (result.valid_p)
{
bfd_vma value;
value = result.value + result.section->bfd_section->vma;
minfo ("0x%V", value);
if (is_dot)
print_dot = value;
}
else
{
minfo ("*undef* ");
#ifdef BFD64
minfo (" ");
#endif
}
minfo (" ");
exp_print_tree (assignment->exp);
print_nl ();
}
static void
print_input_statement (lang_input_statement_type *statm)
{
if (statm->filename != NULL)
{
fprintf (config.map_file, "LOAD %s\n", statm->filename);
}
}
static bfd_boolean
print_one_symbol (struct bfd_link_hash_entry *hash_entry, void *ptr)
{
asection *sec = ptr;
if ((hash_entry->type == bfd_link_hash_defined
|| hash_entry->type == bfd_link_hash_defweak)
&& sec == hash_entry->u.def.section)
{
int i;
for (i = 0; i < SECTION_NAME_MAP_LENGTH; i++)
print_space ();
minfo ("0x%V ",
(hash_entry->u.def.value
+ hash_entry->u.def.section->output_offset
+ hash_entry->u.def.section->output_section->vma));
minfo (" %T\n", hash_entry->root.string);
}
return TRUE;
}
static void
print_all_symbols (sec)
asection *sec;
{
struct fat_user_section_struct *ud = get_userdata (sec);
struct map_symbol_def *def;
if (!ud)
return;
*ud->map_symbol_def_tail = 0;
for (def = ud->map_symbol_def_head; def; def = def->next)
print_one_symbol (def->entry, sec);
}
static void
print_input_section (lang_input_section_type *in)
{
asection *i = in->section;
bfd_size_type size = i->size;
init_opb ();
if (size != 0)
{
int len;
bfd_vma addr;
print_space ();
minfo ("%s", i->name);
len = 1 + strlen (i->name);
if (len >= SECTION_NAME_MAP_LENGTH - 1)
{
print_nl ();
len = 0;
}
while (len < SECTION_NAME_MAP_LENGTH)
{
print_space ();
++len;
}
if (i->output_section != NULL && (i->flags & SEC_EXCLUDE) == 0)
addr = i->output_section->vma + i->output_offset;
else
{
addr = print_dot;
size = 0;
}
minfo ("0x%V %W %B\n", addr, TO_ADDR (size), i->owner);
if (size != i->rawsize && i->rawsize != 0)
{
len = SECTION_NAME_MAP_LENGTH + 3;
#ifdef BFD64
len += 16;
#else
len += 8;
#endif
while (len > 0)
{
print_space ();
--len;
}
minfo (_("%W (size before relaxing)\n"), i->rawsize);
}
if (i->output_section != NULL && (i->flags & SEC_EXCLUDE) == 0)
{
if (command_line.reduce_memory_overheads)
bfd_link_hash_traverse (link_info.hash, print_one_symbol, i);
else
print_all_symbols (i);
print_dot = addr + TO_ADDR (size);
}
}
}
static void
print_fill_statement (lang_fill_statement_type *fill)
{
size_t size;
unsigned char *p;
fputs (" FILL mask 0x", config.map_file);
for (p = fill->fill->data, size = fill->fill->size; size != 0; p++, size--)
fprintf (config.map_file, "%02x", *p);
fputs ("\n", config.map_file);
}
static void
print_data_statement (lang_data_statement_type *data)
{
int i;
bfd_vma addr;
bfd_size_type size;
const char *name;
init_opb ();
for (i = 0; i < SECTION_NAME_MAP_LENGTH; i++)
print_space ();
addr = data->output_vma;
if (data->output_section != NULL)
addr += data->output_section->vma;
switch (data->type)
{
default:
abort ();
case BYTE:
size = BYTE_SIZE;
name = "BYTE";
break;
case SHORT:
size = SHORT_SIZE;
name = "SHORT";
break;
case LONG:
size = LONG_SIZE;
name = "LONG";
break;
case QUAD:
size = QUAD_SIZE;
name = "QUAD";
break;
case SQUAD:
size = QUAD_SIZE;
name = "SQUAD";
break;
}
minfo ("0x%V %W %s 0x%v", addr, size, name, data->value);
if (data->exp->type.node_class != etree_value)
{
print_space ();
exp_print_tree (data->exp);
}
print_nl ();
print_dot = addr + TO_ADDR (size);
}
static void
print_address_statement (lang_address_statement_type *address)
{
minfo (_("Address of section %s set to "), address->section_name);
exp_print_tree (address->address);
print_nl ();
}
static void
print_reloc_statement (lang_reloc_statement_type *reloc)
{
int i;
bfd_vma addr;
bfd_size_type size;
init_opb ();
for (i = 0; i < SECTION_NAME_MAP_LENGTH; i++)
print_space ();
addr = reloc->output_vma;
if (reloc->output_section != NULL)
addr += reloc->output_section->vma;
size = bfd_get_reloc_size (reloc->howto);
minfo ("0x%V %W RELOC %s ", addr, size, reloc->howto->name);
if (reloc->name != NULL)
minfo ("%s+", reloc->name);
else
minfo ("%s+", reloc->section->name);
exp_print_tree (reloc->addend_exp);
print_nl ();
print_dot = addr + TO_ADDR (size);
}
static void
print_padding_statement (lang_padding_statement_type *s)
{
int len;
bfd_vma addr;
init_opb ();
minfo (" *fill*");
len = sizeof " *fill*" - 1;
while (len < SECTION_NAME_MAP_LENGTH)
{
print_space ();
++len;
}
addr = s->output_offset;
if (s->output_section != NULL)
addr += s->output_section->vma;
minfo ("0x%V %W ", addr, (bfd_vma) s->size);
if (s->fill->size != 0)
{
size_t size;
unsigned char *p;
for (p = s->fill->data, size = s->fill->size; size != 0; p++, size--)
fprintf (config.map_file, "%02x", *p);
}
print_nl ();
print_dot = addr + TO_ADDR (s->size);
}
static void
print_wild_statement (lang_wild_statement_type *w,
lang_output_section_statement_type *os)
{
struct wildcard_list *sec;
print_space ();
if (w->filenames_sorted)
minfo ("SORT(");
if (w->filename != NULL)
minfo ("%s", w->filename);
else
minfo ("*");
if (w->filenames_sorted)
minfo (")");
minfo ("(");
for (sec = w->section_list; sec; sec = sec->next)
{
if (sec->spec.sorted)
minfo ("SORT(");
if (sec->spec.exclude_name_list != NULL)
{
name_list *tmp;
minfo ("EXCLUDE_FILE(%s", sec->spec.exclude_name_list->name);
for (tmp = sec->spec.exclude_name_list->next; tmp; tmp = tmp->next)
minfo (" %s", tmp->name);
minfo (") ");
}
if (sec->spec.name != NULL)
minfo ("%s", sec->spec.name);
else
minfo ("*");
if (sec->spec.sorted)
minfo (")");
if (sec->next)
minfo (" ");
}
minfo (")");
print_nl ();
print_statement_list (w->children.head, os);
}
static void
print_group (lang_group_statement_type *s,
lang_output_section_statement_type *os)
{
fprintf (config.map_file, "START GROUP\n");
print_statement_list (s->children.head, os);
fprintf (config.map_file, "END GROUP\n");
}
static void
print_statement_list (lang_statement_union_type *s,
lang_output_section_statement_type *os)
{
while (s != NULL)
{
print_statement (s, os);
s = s->header.next;
}
}
static void
print_statement (lang_statement_union_type *s,
lang_output_section_statement_type *os)
{
switch (s->header.type)
{
default:
fprintf (config.map_file, _("Fail with %d\n"), s->header.type);
FAIL ();
break;
case lang_constructors_statement_enum:
if (constructor_list.head != NULL)
{
if (constructors_sorted)
minfo (" SORT (CONSTRUCTORS)\n");
else
minfo (" CONSTRUCTORS\n");
print_statement_list (constructor_list.head, os);
}
break;
case lang_wild_statement_enum:
print_wild_statement (&s->wild_statement, os);
break;
case lang_address_statement_enum:
print_address_statement (&s->address_statement);
break;
case lang_object_symbols_statement_enum:
minfo (" CREATE_OBJECT_SYMBOLS\n");
break;
case lang_fill_statement_enum:
print_fill_statement (&s->fill_statement);
break;
case lang_data_statement_enum:
print_data_statement (&s->data_statement);
break;
case lang_reloc_statement_enum:
print_reloc_statement (&s->reloc_statement);
break;
case lang_input_section_enum:
print_input_section (&s->input_section);
break;
case lang_padding_statement_enum:
print_padding_statement (&s->padding_statement);
break;
case lang_output_section_statement_enum:
print_output_section_statement (&s->output_section_statement);
break;
case lang_assignment_statement_enum:
print_assignment (&s->assignment_statement, os);
break;
case lang_target_statement_enum:
fprintf (config.map_file, "TARGET(%s)\n", s->target_statement.target);
break;
case lang_output_statement_enum:
minfo ("OUTPUT(%s", s->output_statement.name);
if (output_target != NULL)
minfo (" %s", output_target);
minfo (")\n");
break;
case lang_input_statement_enum:
print_input_statement (&s->input_statement);
break;
case lang_group_statement_enum:
print_group (&s->group_statement, os);
break;
case lang_afile_asection_pair_statement_enum:
FAIL ();
break;
}
}
static void
print_statements (void)
{
print_statement_list (statement_list.head, abs_output_section);
}
void
dprint_statement (lang_statement_union_type *s, int n)
{
FILE *map_save = config.map_file;
config.map_file = stderr;
if (n < 0)
print_statement_list (s, abs_output_section);
else
{
while (s && --n >= 0)
{
print_statement (s, abs_output_section);
s = s->header.next;
}
}
config.map_file = map_save;
}
static void
insert_pad (lang_statement_union_type **ptr,
fill_type *fill,
unsigned int alignment_needed,
asection *output_section,
bfd_vma dot)
{
static fill_type zero_fill = { 1, { 0 } };
lang_statement_union_type *pad;
pad = ((lang_statement_union_type *)
((char *) ptr - offsetof (lang_statement_union_type, header.next)));
if (ptr != &statement_list.head
&& pad->header.type == lang_padding_statement_enum
&& pad->padding_statement.output_section == output_section)
{
}
else
{
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_section = output_section;
if (fill == NULL)
fill = &zero_fill;
pad->padding_statement.fill = fill;
}
pad->padding_statement.output_offset = dot - output_section->vma;
pad->padding_statement.size = alignment_needed;
output_section->size += alignment_needed;
}
static bfd_vma
size_input_section
(lang_statement_union_type **this_ptr,
lang_output_section_statement_type *output_section_statement,
fill_type *fill,
bfd_vma dot)
{
lang_input_section_type *is = &((*this_ptr)->input_section);
asection *i = is->section;
if (!is->ifile->just_syms_flag && (i->flags & SEC_EXCLUDE) == 0)
{
unsigned int alignment_needed;
asection *o;
if (output_section_statement->subsection_alignment != -1)
i->alignment_power = output_section_statement->subsection_alignment;
o = output_section_statement->bfd_section;
if (o->alignment_power < i->alignment_power)
o->alignment_power = i->alignment_power;
alignment_needed = align_power (dot, i->alignment_power) - dot;
if (alignment_needed != 0)
{
insert_pad (this_ptr, fill, TO_SIZE (alignment_needed), o, dot);
dot += alignment_needed;
}
i->output_offset = dot - o->vma;
dot += TO_ADDR (i->size);
o->size = TO_SIZE (dot - o->vma);
}
else
{
i->output_offset = i->vma - output_section_statement->bfd_section->vma;
}
return dot;
}
#define IGNORE_SECTION(s) \
((s->flags & SEC_NEVER_LOAD) != 0 \
|| (s->flags & SEC_ALLOC) == 0 \
|| ((s->flags & SEC_THREAD_LOCAL) != 0 \
&& (s->flags & SEC_LOAD) == 0))
static void
lang_check_section_addresses (void)
{
asection *s;
for (s = output_bfd->sections; s != NULL; s = s->next)
{
asection *os;
if (IGNORE_SECTION (s) || s->size == 0)
continue;
for (os = output_bfd->sections; os != s; os = os->next)
{
bfd_vma s_start;
bfd_vma s_end;
bfd_vma os_start;
bfd_vma os_end;
if (IGNORE_SECTION (os) || os->size == 0)
continue;
s_start = bfd_section_lma (output_bfd, s);
os_start = bfd_section_lma (output_bfd, os);
s_end = s_start + TO_ADDR (s->size) - 1;
os_end = os_start + TO_ADDR (os->size) - 1;
if ((s_end < os_start) || (s_start > os_end))
continue;
einfo (
_("%X%P: section %s [%V -> %V] overlaps section %s [%V -> %V]\n"),
s->name, s_start, s_end, os->name, os_start, os_end);
break;
}
}
}
static void
os_region_check (lang_output_section_statement_type *os,
lang_memory_region_type *region,
etree_type *tree,
bfd_vma base)
{
if ((region->current < region->origin
|| (region->current - region->origin > region->length))
&& ((region->current != region->origin + region->length)
|| base == 0))
{
if (tree != NULL)
{
einfo (_("%X%P: address 0x%v of %B section %s"
" is not within region %s\n"),
region->current,
os->bfd_section->owner,
os->bfd_section->name,
region->name);
}
else
{
einfo (_("%X%P: region %s is full (%B section %s)\n"),
region->name,
os->bfd_section->owner,
os->bfd_section->name);
}
region->current = region->origin;
}
}
static bfd_vma
lang_size_sections_1
(lang_statement_union_type *s,
lang_output_section_statement_type *output_section_statement,
lang_statement_union_type **prev,
fill_type *fill,
bfd_vma dot,
bfd_boolean *relax,
bfd_boolean check_regions)
{
for (; s != NULL; s = s->header.next)
{
switch (s->header.type)
{
case lang_output_section_statement_enum:
{
bfd_vma after;
lang_output_section_statement_type *os;
os = &s->output_section_statement;
if (os->bfd_section == NULL)
break;
if ((bfd_get_flavour (output_bfd) == bfd_target_ecoff_flavour
|| bfd_get_flavour (output_bfd) == bfd_target_coff_flavour)
&& (os->bfd_section->flags & SEC_COFF_SHARED_LIBRARY) != 0)
{
asection *input;
if (os->children.head == NULL
|| os->children.head->header.next != NULL
|| (os->children.head->header.type
!= lang_input_section_enum))
einfo (_("%P%X: 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;
break;
}
if (bfd_is_abs_section (os->bfd_section))
{
ASSERT (os->bfd_section->vma == 0);
}
else
{
if (os->addr_tree == NULL)
{
if (os->region == NULL
|| ((os->bfd_section->flags & (SEC_ALLOC | SEC_LOAD))
&& os->region->name[0] == '*'
&& strcmp (os->region->name,
DEFAULT_MEMORY_REGION) == 0))
{
os->region = lang_memory_default (os->bfd_section);
}
if (!IGNORE_SECTION (os->bfd_section)
&& ! link_info.relocatable
&& check_regions
&& strcmp (os->region->name,
DEFAULT_MEMORY_REGION) == 0
&& lang_memory_region_list != NULL
&& (strcmp (lang_memory_region_list->name,
DEFAULT_MEMORY_REGION) != 0
|| lang_memory_region_list->next != NULL))
{
if (command_line.check_section_addresses)
einfo (_("%P%F: error: no memory region specified"
" for loadable section `%s'\n"),
bfd_get_section_name (output_bfd,
os->bfd_section));
else
einfo (_("%P: warning: no memory region specified"
" for loadable section `%s'\n"),
bfd_get_section_name (output_bfd,
os->bfd_section));
}
dot = os->region->current;
if (os->section_alignment == -1)
{
bfd_vma olddot;
olddot = dot;
dot = align_power (dot,
os->bfd_section->alignment_power);
if (dot != olddot && config.warn_section_align)
einfo (_("%P: warning: changing start of section"
" %s by %u bytes\n"),
os->name, (unsigned int) (dot - olddot));
}
}
else
{
etree_value_type r;
os->processed = -1;
r = exp_fold_tree (os->addr_tree,
abs_output_section,
lang_allocating_phase_enum,
dot, &dot);
os->processed = 0;
if (!r.valid_p)
einfo (_("%F%S: non constant or forward reference"
" address expression for section %s\n"),
os->name);
dot = r.value + r.section->bfd_section->vma;
}
if (os->section_alignment != -1)
dot = align_power (dot, os->section_alignment);
bfd_set_section_vma (0, os->bfd_section, dot);
os->bfd_section->output_offset = 0;
}
lang_size_sections_1 (os->children.head, os, &os->children.head,
os->fill, dot, relax, check_regions);
after = ((os->bfd_section->vma
+ TO_ADDR (os->bfd_section->size)
+ os->block_value - 1)
& - (bfd_vma) os->block_value);
if (bfd_is_abs_section (os->bfd_section))
ASSERT (after == os->bfd_section->vma);
else
os->bfd_section->size
= TO_SIZE (after - os->bfd_section->vma);
dot = os->bfd_section->vma;
if ((os->bfd_section->flags & SEC_HAS_CONTENTS) != 0
|| (os->bfd_section->flags & SEC_THREAD_LOCAL) == 0
|| link_info.relocatable)
dot += TO_ADDR (os->bfd_section->size);
os->processed = 1;
if (os->update_dot_tree != 0)
exp_fold_tree (os->update_dot_tree, abs_output_section,
lang_allocating_phase_enum, dot, &dot);
if (os->region != NULL
&& ((os->bfd_section->flags & SEC_NEVER_LOAD) == 0
|| (os->bfd_section->flags & (SEC_ALLOC | SEC_LOAD))))
{
os->region->current = dot;
if (check_regions)
os_region_check (os, os->region, os->addr_tree,
os->bfd_section->vma);
if (os->lma_region == NULL && os->load_base == NULL)
os->lma_region = os->region;
if (os->lma_region != NULL && os->lma_region != os->region)
{
os->load_base = exp_intop (os->lma_region->current);
os->lma_region->current +=
TO_ADDR (os->bfd_section->size);
if (check_regions)
os_region_check (os, os->lma_region, NULL,
os->bfd_section->lma);
}
}
}
break;
case lang_constructors_statement_enum:
dot = lang_size_sections_1 (constructor_list.head,
output_section_statement,
&s->wild_statement.children.head,
fill, dot, relax, check_regions);
break;
case lang_data_statement_enum:
{
unsigned int size = 0;
s->data_statement.output_vma =
dot - output_section_statement->bfd_section->vma;
s->data_statement.output_section =
output_section_statement->bfd_section;
exp_fold_tree (s->data_statement.exp, abs_output_section,
lang_allocating_phase_enum, dot, &dot);
switch (s->data_statement.type)
{
default:
abort ();
case QUAD:
case SQUAD:
size = QUAD_SIZE;
break;
case LONG:
size = LONG_SIZE;
break;
case SHORT:
size = SHORT_SIZE;
break;
case BYTE:
size = BYTE_SIZE;
break;
}
if (size < TO_SIZE ((unsigned) 1))
size = TO_SIZE ((unsigned) 1);
dot += TO_ADDR (size);
output_section_statement->bfd_section->size += size;
}
break;
case lang_reloc_statement_enum:
{
int size;
s->reloc_statement.output_vma =
dot - output_section_statement->bfd_section->vma;
s->reloc_statement.output_section =
output_section_statement->bfd_section;
size = bfd_get_reloc_size (s->reloc_statement.howto);
dot += TO_ADDR (size);
output_section_statement->bfd_section->size += size;
}
break;
case lang_wild_statement_enum:
dot = lang_size_sections_1 (s->wild_statement.children.head,
output_section_statement,
&s->wild_statement.children.head,
fill, dot, relax, check_regions);
break;
case lang_object_symbols_statement_enum:
link_info.create_object_symbols_section =
output_section_statement->bfd_section;
break;
case lang_output_statement_enum:
case lang_target_statement_enum:
break;
case lang_input_section_enum:
{
asection *i;
i = (*prev)->input_section.section;
if (relax)
{
bfd_boolean again;
if (! bfd_relax_section (i->owner, i, &link_info, &again))
einfo (_("%P%F: can't relax section: %E\n"));
if (again)
*relax = TRUE;
}
dot = size_input_section (prev, output_section_statement,
output_section_statement->fill, dot);
}
break;
case lang_input_statement_enum:
break;
case lang_fill_statement_enum:
s->fill_statement.output_section =
output_section_statement->bfd_section;
fill = s->fill_statement.fill;
break;
case lang_assignment_statement_enum:
{
bfd_vma newdot = dot;
exp_fold_tree (s->assignment_statement.exp,
output_section_statement,
lang_allocating_phase_enum,
dot,
&newdot);
if (newdot != dot)
{
if (output_section_statement == abs_output_section)
{
lang_memory_region_lookup (DEFAULT_MEMORY_REGION,
FALSE)->current = newdot;
}
else
{
insert_pad (&s->header.next, fill, TO_SIZE (newdot - dot),
output_section_statement->bfd_section, dot);
s = s->header.next;
}
if (!(output_section_statement->flags
& (SEC_NEVER_LOAD | SEC_ALLOC)))
output_section_statement->bfd_section->flags |= SEC_ALLOC;
dot = newdot;
}
}
break;
case lang_padding_statement_enum:
s->padding_statement.size = 0;
s->padding_statement.output_offset
= dot - output_section_statement->bfd_section->vma;
break;
case lang_group_statement_enum:
dot = lang_size_sections_1 (s->group_statement.children.head,
output_section_statement,
&s->group_statement.children.head,
fill, dot, relax, check_regions);
break;
default:
FAIL ();
break;
case lang_address_statement_enum:
break;
}
prev = &s->header.next;
}
return dot;
}
bfd_vma
lang_size_sections
(lang_statement_union_type *s,
lang_output_section_statement_type *output_section_statement,
lang_statement_union_type **prev,
fill_type *fill,
bfd_vma dot,
bfd_boolean *relax,
bfd_boolean check_regions)
{
bfd_vma result;
lang_statement_iteration++;
exp_data_seg.phase = exp_dataseg_none;
result = lang_size_sections_1 (s, output_section_statement, prev, fill,
dot, relax, check_regions);
if (exp_data_seg.phase == exp_dataseg_end_seen
&& link_info.relro && exp_data_seg.relro_end)
{
bfd_vma old_min_base, relro_end, maxpage;
exp_data_seg.phase = exp_dataseg_relro_adjust;
old_min_base = exp_data_seg.min_base;
maxpage = exp_data_seg.maxpagesize;
exp_data_seg.base += (-exp_data_seg.relro_end
& (exp_data_seg.pagesize - 1));
relro_end = (exp_data_seg.relro_end + exp_data_seg.pagesize - 1)
& ~(exp_data_seg.pagesize - 1);
if (old_min_base + maxpage < exp_data_seg.base)
{
exp_data_seg.base -= maxpage;
relro_end -= maxpage;
}
result = lang_size_sections_1 (s, output_section_statement, prev, fill,
dot, relax, check_regions);
if (exp_data_seg.relro_end > relro_end)
{
asection *sec;
unsigned int max_alignment_power = 0;
for (sec = output_bfd->sections; sec; sec = sec->next)
if (sec->vma >= exp_data_seg.base
&& sec->vma < exp_data_seg.relro_end
&& sec->alignment_power > max_alignment_power)
max_alignment_power = sec->alignment_power;
if (((bfd_vma) 1 << max_alignment_power) < exp_data_seg.pagesize)
{
if (exp_data_seg.base - (1 << max_alignment_power)
< old_min_base)
exp_data_seg.base += exp_data_seg.pagesize;
exp_data_seg.base -= (1 << max_alignment_power);
result = lang_size_sections_1 (s, output_section_statement,
prev, fill, dot, relax,
check_regions);
}
}
link_info.relro_start = exp_data_seg.base;
link_info.relro_end = exp_data_seg.relro_end;
}
else if (exp_data_seg.phase == exp_dataseg_end_seen)
{
bfd_vma first, last;
first = -exp_data_seg.base & (exp_data_seg.pagesize - 1);
last = exp_data_seg.end & (exp_data_seg.pagesize - 1);
if (first && last
&& ((exp_data_seg.base & ~(exp_data_seg.pagesize - 1))
!= (exp_data_seg.end & ~(exp_data_seg.pagesize - 1)))
&& first + last <= exp_data_seg.pagesize)
{
exp_data_seg.phase = exp_dataseg_adjust;
lang_statement_iteration++;
result = lang_size_sections_1 (s, output_section_statement, prev,
fill, dot, relax, check_regions);
}
}
return result;
}
static bfd_vma
lang_do_assignments_1
(lang_statement_union_type *s,
lang_output_section_statement_type *output_section_statement,
fill_type *fill,
bfd_vma dot)
{
for (; s != NULL; s = s->header.next)
{
switch (s->header.type)
{
case lang_constructors_statement_enum:
dot = lang_do_assignments_1 (constructor_list.head,
output_section_statement,
fill,
dot);
break;
case lang_output_section_statement_enum:
{
lang_output_section_statement_type *os;
os = &(s->output_section_statement);
if (os->bfd_section != NULL)
{
dot = os->bfd_section->vma;
lang_do_assignments_1 (os->children.head, os, os->fill, dot);
if ((os->bfd_section->flags & SEC_HAS_CONTENTS) != 0
|| (os->bfd_section->flags & SEC_THREAD_LOCAL) == 0
|| link_info.relocatable)
dot += TO_ADDR (os->bfd_section->size);
}
if (os->load_base)
{
if (os->bfd_section)
{
os->bfd_section->lma
= exp_get_abs_int (os->load_base, 0, "load base",
lang_final_phase_enum);
}
}
}
break;
case lang_wild_statement_enum:
dot = lang_do_assignments_1 (s->wild_statement.children.head,
output_section_statement,
fill, dot);
break;
case lang_object_symbols_statement_enum:
case lang_output_statement_enum:
case lang_target_statement_enum:
break;
case lang_data_statement_enum:
{
etree_value_type value;
value = exp_fold_tree (s->data_statement.exp,
abs_output_section,
lang_final_phase_enum, dot, &dot);
if (!value.valid_p)
einfo (_("%F%P: invalid data statement\n"));
s->data_statement.value
= value.value + value.section->bfd_section->vma;
}
{
unsigned int size;
switch (s->data_statement.type)
{
default:
abort ();
case QUAD:
case SQUAD:
size = QUAD_SIZE;
break;
case LONG:
size = LONG_SIZE;
break;
case SHORT:
size = SHORT_SIZE;
break;
case BYTE:
size = BYTE_SIZE;
break;
}
if (size < TO_SIZE ((unsigned) 1))
size = TO_SIZE ((unsigned) 1);
dot += TO_ADDR (size);
}
break;
case lang_reloc_statement_enum:
{
etree_value_type value;
value = exp_fold_tree (s->reloc_statement.addend_exp,
abs_output_section,
lang_final_phase_enum, dot, &dot);
s->reloc_statement.addend_value = value.value;
if (!value.valid_p)
einfo (_("%F%P: invalid reloc statement\n"));
}
dot += TO_ADDR (bfd_get_reloc_size (s->reloc_statement.howto));
break;
case lang_input_section_enum:
{
asection *in = s->input_section.section;
if ((in->flags & SEC_EXCLUDE) == 0)
dot += TO_ADDR (in->size);
}
break;
case lang_input_statement_enum:
break;
case lang_fill_statement_enum:
fill = s->fill_statement.fill;
break;
case lang_assignment_statement_enum:
{
exp_fold_tree (s->assignment_statement.exp,
output_section_statement,
lang_final_phase_enum,
dot,
&dot);
}
break;
case lang_padding_statement_enum:
dot += TO_ADDR (s->padding_statement.size);
break;
case lang_group_statement_enum:
dot = lang_do_assignments_1 (s->group_statement.children.head,
output_section_statement,
fill, dot);
break;
default:
FAIL ();
break;
case lang_address_statement_enum:
break;
}
}
return dot;
}
void
lang_do_assignments
(lang_statement_union_type *s,
lang_output_section_statement_type *output_section_statement,
fill_type *fill,
bfd_vma dot)
{
lang_statement_iteration++;
lang_do_assignments_1 (s, output_section_statement, fill, dot);
}
static void
lang_set_startof (void)
{
asection *s;
if (link_info.relocatable)
return;
for (s = output_bfd->sections; s != NULL; s = s->next)
{
const char *secname;
char *buf;
struct bfd_link_hash_entry *h;
secname = bfd_get_section_name (output_bfd, s);
buf = xmalloc (10 + strlen (secname));
sprintf (buf, ".startof.%s", secname);
h = bfd_link_hash_lookup (link_info.hash, buf, FALSE, FALSE, TRUE);
if (h != NULL && h->type == bfd_link_hash_undefined)
{
h->type = bfd_link_hash_defined;
h->u.def.value = bfd_get_section_vma (output_bfd, s);
h->u.def.section = bfd_abs_section_ptr;
}
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)
{
h->type = bfd_link_hash_defined;
h->u.def.value = TO_ADDR (s->size);
h->u.def.section = bfd_abs_section_ptr;
}
free (buf);
}
}
static void
lang_finish (void)
{
struct bfd_link_hash_entry *h;
bfd_boolean warn;
if (link_info.relocatable || link_info.shared)
warn = FALSE;
else
warn = TRUE;
if (entry_symbol.name == NULL)
{
entry_symbol.name = "start";
warn = FALSE;
}
h = bfd_link_hash_lookup (link_info.hash, entry_symbol.name,
FALSE, FALSE, TRUE);
if (h != NULL
&& (h->type == bfd_link_hash_defined
|| h->type == bfd_link_hash_defweak)
&& h->u.def.section->output_section != NULL)
{
bfd_vma val;
val = (h->u.def.value
+ bfd_get_section_vma (output_bfd,
h->u.def.section->output_section)
+ h->u.def.section->output_offset);
if (! bfd_set_start_address (output_bfd, val))
einfo (_("%P%F:%s: can't set start address\n"), entry_symbol.name);
}
else
{
bfd_vma val;
const char *send;
val = bfd_scan_vma (entry_symbol.name, &send, 0);
if (*send == '\0')
{
if (! bfd_set_start_address (output_bfd, val))
einfo (_("%P%F: can't set start address\n"));
}
else
{
asection *ts;
ts = bfd_get_section_by_name (output_bfd, entry_section);
if (ts != NULL)
{
if (warn)
einfo (_("%P: warning: cannot find entry symbol %s;"
" defaulting to %V\n"),
entry_symbol.name,
bfd_get_section_vma (output_bfd, ts));
if (! bfd_set_start_address (output_bfd,
bfd_get_section_vma (output_bfd,
ts)))
einfo (_("%P%F: can't set start address\n"));
}
else
{
if (warn)
einfo (_("%P: warning: cannot find entry symbol %s;"
" not setting start address\n"),
entry_symbol.name);
}
}
}
}
static void
ignore_bfd_errors (const char *s ATTRIBUTE_UNUSED, ...)
{
}
static void
lang_check (void)
{
lang_statement_union_type *file;
bfd *input_bfd;
const bfd_arch_info_type *compatible;
for (file = file_chain.head; file != NULL; file = file->input_statement.next)
{
input_bfd = file->input_statement.the_bfd;
compatible
= bfd_arch_get_compatible (input_bfd, output_bfd,
command_line.accept_unknown_input_arch);
if ((link_info.relocatable || link_info.emitrelocations)
&& (compatible == NULL
|| bfd_get_flavour (input_bfd) != bfd_get_flavour (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"),
bfd_get_target (input_bfd), input_bfd,
bfd_get_target (output_bfd), output_bfd);
}
if (compatible == NULL)
{
if (command_line.warn_mismatch)
einfo (_("%P: warning: %s architecture of input file `%B'"
" is incompatible with %s output\n"),
bfd_printable_name (input_bfd), input_bfd,
bfd_printable_name (output_bfd));
}
else if (bfd_count_sections (input_bfd))
{
bfd_error_handler_type pfn = NULL;
if (! command_line.warn_mismatch)
pfn = bfd_set_error_handler (ignore_bfd_errors);
if (! bfd_merge_private_bfd_data (input_bfd, output_bfd))
{
if (command_line.warn_mismatch)
einfo (_("%P%X: failed to merge target specific data"
" of file %B\n"), input_bfd);
}
if (! command_line.warn_mismatch)
bfd_set_error_handler (pfn);
}
}
}
static void
lang_common (void)
{
if (command_line.inhibit_common_definition)
return;
if (link_info.relocatable
&& ! command_line.force_common_definition)
return;
if (! config.sort_common)
bfd_link_hash_traverse (link_info.hash, lang_one_common, NULL);
else
{
int power;
for (power = 4; power >= 0; power--)
bfd_link_hash_traverse (link_info.hash, lang_one_common, &power);
}
}
static bfd_boolean
lang_one_common (struct bfd_link_hash_entry *h, void *info)
{
unsigned int power_of_two;
bfd_vma size;
asection *section;
if (h->type != bfd_link_hash_common)
return TRUE;
size = h->u.c.size;
power_of_two = h->u.c.p->alignment_power;
if (config.sort_common
&& power_of_two < (unsigned int) *(int *) info)
return TRUE;
section = h->u.c.p->section;
section->size += ((bfd_vma) 1 << (power_of_two + opb_shift)) - 1;
section->size &= (- (bfd_vma) 1 << (power_of_two + opb_shift));
if (power_of_two > section->alignment_power)
section->alignment_power = power_of_two;
h->type = bfd_link_hash_defined;
h->u.def.section = section;
h->u.def.value = section->size;
section->size += size;
section->flags |= SEC_ALLOC;
section->flags &= ~SEC_IS_COMMON;
if (config.map_file != NULL)
{
static bfd_boolean header_printed;
int len;
char *name;
char buf[50];
if (! header_printed)
{
minfo (_("\nAllocating common symbols\n"));
minfo (_("Common symbol size file\n\n"));
header_printed = TRUE;
}
name = demangle (h->root.string);
minfo ("%s", name);
len = strlen (name);
free (name);
if (len >= 19)
{
print_nl ();
len = 0;
}
while (len < 20)
{
print_space ();
++len;
}
minfo ("0x");
if (size <= 0xffffffff)
sprintf (buf, "%lx", (unsigned long) size);
else
sprintf_vma (buf, size);
minfo ("%s", buf);
len = strlen (buf);
while (len < 16)
{
print_space ();
++len;
}
minfo ("%B\n", section->owner);
}
return TRUE;
}
static void
lang_place_orphans (void)
{
LANG_FOR_EACH_INPUT_STATEMENT (file)
{
asection *s;
for (s = file->the_bfd->sections; s != NULL; s = s->next)
{
if (s->output_section == NULL)
{
if (file->just_syms_flag)
bfd_link_just_syms (file->the_bfd, s, &link_info);
else if ((s->flags & SEC_EXCLUDE) != 0)
s->output_section = bfd_abs_section_ptr;
else if (strcmp (s->name, "COMMON") == 0)
{
if (! link_info.relocatable
|| command_line.force_common_definition)
{
if (default_common_section == NULL)
{
default_common_section =
lang_output_section_statement_lookup (".bss");
}
lang_add_section (&default_common_section->children, s,
default_common_section, file);
}
}
else if (ldemul_place_orphan (file, s))
;
else
{
lang_output_section_statement_type *os;
os = lang_output_section_statement_lookup (s->name);
lang_add_section (&os->children, s, os, file);
}
}
}
}
}
void
lang_set_flags (lang_memory_region_type *ptr, const char *flags, int invert)
{
flagword *ptr_flags;
ptr_flags = invert ? &ptr->not_flags : &ptr->flags;
while (*flags)
{
switch (*flags)
{
case 'A': case 'a':
*ptr_flags |= SEC_ALLOC;
break;
case 'R': case 'r':
*ptr_flags |= SEC_READONLY;
break;
case 'W': case 'w':
*ptr_flags |= SEC_DATA;
break;
case 'X': case 'x':
*ptr_flags |= SEC_CODE;
break;
case 'L': case 'l':
case 'I': case 'i':
*ptr_flags |= SEC_LOAD;
break;
default:
einfo (_("%P%F: invalid syntax in flags\n"));
break;
}
flags++;
}
}
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;
f != NULL;
f = (lang_input_statement_type *) f->next_real_file)
func (f);
}
void
lang_for_each_file (void (*func) (lang_input_statement_type *))
{
LANG_FOR_EACH_INPUT_STATEMENT (f)
{
func (f);
}
}
void
ldlang_add_file (lang_input_statement_type *entry)
{
bfd **pp;
lang_statement_append (&file_chain,
(lang_statement_union_type *) entry,
&entry->next);
ASSERT (entry->the_bfd->link_next == NULL);
ASSERT (entry->the_bfd != output_bfd);
for (pp = &link_info.input_bfds; *pp != NULL; pp = &(*pp)->link_next)
;
*pp = entry->the_bfd;
entry->the_bfd->usrdata = entry;
bfd_set_gp_size (entry->the_bfd, g_switch_value);
bfd_map_over_sections (entry->the_bfd, section_already_linked, entry);
}
void
lang_add_output (const char *name, int from_script)
{
if (!had_output_filename || !from_script)
{
output_filename = name;
had_output_filename = TRUE;
}
}
static lang_output_section_statement_type *current_section;
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,
enum section_type sectype,
etree_type *align,
etree_type *subalign,
etree_type *ebase,
int constraint)
{
lang_output_section_statement_type *os;
current_section =
os =
lang_output_section_statement_lookup_1 (output_section_statement_name,
constraint);
if (os->addr_tree == NULL)
{
os->addr_tree = address_exp;
}
os->sectype = sectype;
if (sectype != noload_section)
os->flags = SEC_NO_FLAGS;
else
os->flags = SEC_NEVER_LOAD;
os->block_value = 1;
stat_ptr = &os->children;
os->subsection_alignment =
topower (exp_get_value_int (subalign, -1, "subsection alignment", 0));
os->section_alignment =
topower (exp_get_value_int (align, -1, "section alignment", 0));
os->load_base = ebase;
return os;
}
void
lang_final (void)
{
lang_output_statement_type *new =
new_stat (lang_output_statement, stat_ptr);
new->name = output_filename;
}
void
lang_reset_memory_regions (void)
{
lang_memory_region_type *p = lang_memory_region_list;
asection *o;
for (p = lang_memory_region_list; p != NULL; p = p->next)
{
p->old_length = (bfd_size_type) (p->current - p->origin);
p->current = p->origin;
}
for (o = output_bfd->sections; o != NULL; o = o->next)
{
o->rawsize = o->size;
o->size = 0;
}
}
static void
gc_section_callback (lang_wild_statement_type *ptr,
struct wildcard_list *sec ATTRIBUTE_UNUSED,
asection *section,
lang_input_statement_type *file ATTRIBUTE_UNUSED,
void *data ATTRIBUTE_UNUSED)
{
if (ptr->keep_sections)
section->flags |= SEC_KEEP;
}
static void
lang_gc_sections_1 (lang_statement_union_type *s)
{
for (; s != NULL; s = s->header.next)
{
switch (s->header.type)
{
case lang_wild_statement_enum:
walk_wild (&s->wild_statement, gc_section_callback, NULL);
break;
case lang_constructors_statement_enum:
lang_gc_sections_1 (constructor_list.head);
break;
case lang_output_section_statement_enum:
lang_gc_sections_1 (s->output_section_statement.children.head);
break;
case lang_group_statement_enum:
lang_gc_sections_1 (s->group_statement.children.head);
break;
default:
break;
}
}
}
static void
lang_gc_sections (void)
{
struct bfd_link_hash_entry *h;
ldlang_undef_chain_list_type *ulist;
lang_gc_sections_1 (statement_list.head);
for (ulist = link_info.gc_sym_list; ulist; ulist = ulist->next)
{
h = bfd_link_hash_lookup (link_info.hash, ulist->name,
FALSE, FALSE, FALSE);
if (h != NULL
&& (h->type == bfd_link_hash_defined
|| h->type == bfd_link_hash_defweak)
&& ! bfd_is_abs_section (h->u.def.section))
{
h->u.def.section->flags |= SEC_KEEP;
}
}
if (link_info.relocatable)
{
LANG_FOR_EACH_INPUT_STATEMENT (f)
{
asection *sec;
for (sec = f->the_bfd->sections; sec != NULL; sec = sec->next)
if ((sec->flags & SEC_DEBUGGING) == 0)
sec->flags &= ~SEC_EXCLUDE;
}
}
if (command_line.gc_sections)
bfd_gc_sections (output_bfd, &link_info);
}
void
lang_process (void)
{
current_target = default_target;
lang_for_each_statement (ldlang_open_output);
init_opb ();
ldemul_create_output_section_statements ();
lang_place_undefineds ();
if (!bfd_section_already_linked_table_init ())
einfo (_("%P%F: Failed to create hash table\n"));
current_target = default_target;
open_input_bfds (statement_list.head, FALSE);
link_info.gc_sym_list = &entry_symbol;
if (entry_symbol.name == NULL)
link_info.gc_sym_list = ldlang_undef_chain_list_head;
ldemul_after_open ();
bfd_section_already_linked_table_free ();
lang_check ();
if (command_line.version_exports_section)
lang_do_version_exports_section ();
ldctor_build_sets ();
lang_gc_sections ();
lang_common ();
update_wild_statements (statement_list.head);
map_input_to_output_sections (statement_list.head, NULL, NULL);
lang_place_orphans ();
if (! link_info.relocatable)
{
asection *found;
bfd_merge_sections (output_bfd, &link_info);
found = bfd_get_section_by_name (output_bfd, ".text");
if (found != NULL)
{
if (config.text_read_only)
found->flags |= SEC_READONLY;
else
found->flags &= ~SEC_READONLY;
}
}
ldemul_before_allocation ();
if (!link_info.relocatable)
strip_excluded_output_sections ();
lang_record_phdrs ();
lang_size_sections (statement_list.head, abs_output_section,
&statement_list.head, 0, 0, NULL,
command_line.relax ? FALSE : TRUE);
if (command_line.relax)
{
bfd_boolean relax_again;
do
{
relax_again = FALSE;
lang_do_assignments (statement_list.head, abs_output_section,
NULL, 0);
lang_reset_memory_regions ();
lang_size_sections (statement_list.head, abs_output_section,
&statement_list.head, 0, 0, &relax_again, FALSE);
if (!relax_again && link_info.need_relax_finalize)
{
link_info.need_relax_finalize = FALSE;
relax_again = TRUE;
}
}
while (relax_again);
lang_do_assignments (statement_list.head, abs_output_section, NULL, 0);
lang_reset_memory_regions ();
lang_size_sections (statement_list.head, abs_output_section,
&statement_list.head, 0, 0, NULL, TRUE);
}
ldemul_after_allocation ();
lang_set_startof ();
lang_do_assignments (statement_list.head, abs_output_section, NULL, 0);
if (! link_info.relocatable
&& command_line.check_section_addresses)
lang_check_section_addresses ();
ldemul_finish ();
lang_finish ();
}
void
lang_add_wild (struct wildcard_spec *filespec,
struct wildcard_list *section_list,
bfd_boolean keep_sections)
{
struct wildcard_list *curr, *next;
lang_wild_statement_type *new;
for (curr = section_list, section_list = NULL;
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 (filespec != NULL && filespec->name != NULL)
{
if (strcmp (filespec->name, "*") == 0)
filespec->name = NULL;
else if (! wildcardp (filespec->name))
lang_has_input_file = TRUE;
}
new = new_stat (lang_wild_statement, stat_ptr);
new->filename = NULL;
new->filenames_sorted = FALSE;
if (filespec != NULL)
{
new->filename = filespec->name;
new->filenames_sorted = filespec->sorted == by_name;
}
new->section_list = section_list;
new->keep_sections = keep_sections;
lang_list_init (&new->children);
}
void
lang_section_start (const char *name, etree_type *address,
const segment_type *segment)
{
lang_address_statement_type *ad;
ad = new_stat (lang_address_statement, stat_ptr);
ad->section_name = name;
ad->address = address;
ad->segment = segment;
}
void
lang_add_entry (const char *name, bfd_boolean cmdline)
{
if (entry_symbol.name == NULL
|| cmdline
|| ! entry_from_cmdline)
{
entry_symbol.name = name;
entry_from_cmdline = cmdline;
}
}
void
lang_add_target (const char *name)
{
lang_target_statement_type *new = new_stat (lang_target_statement,
stat_ptr);
new->target = name;
}
void
lang_add_map (const char *name)
{
while (*name)
{
switch (*name)
{
case 'F':
map_option_f = TRUE;
break;
}
name++;
}
}
void
lang_add_fill (fill_type *fill)
{
lang_fill_statement_type *new = new_stat (lang_fill_statement,
stat_ptr);
new->fill = fill;
}
void
lang_add_data (int type, union etree_union *exp)
{
lang_data_statement_type *new = new_stat (lang_data_statement,
stat_ptr);
new->exp = exp;
new->type = type;
}
void
lang_add_reloc (bfd_reloc_code_real_type reloc,
reloc_howto_type *howto,
asection *section,
const char *name,
union etree_union *addend)
{
lang_reloc_statement_type *p = new_stat (lang_reloc_statement, stat_ptr);
p->reloc = reloc;
p->howto = howto;
p->section = section;
p->name = name;
p->addend_exp = addend;
p->addend_value = 0;
p->output_section = NULL;
p->output_vma = 0;
}
lang_assignment_statement_type *
lang_add_assignment (etree_type *exp)
{
lang_assignment_statement_type *new = new_stat (lang_assignment_statement,
stat_ptr);
new->exp = exp;
return new;
}
void
lang_add_attribute (enum statement_enum attribute)
{
new_statement (attribute, sizeof (lang_statement_union_type), stat_ptr);
}
void
lang_startup (const char *name)
{
if (startup_file != NULL)
{
einfo (_("%P%Fmultiple STARTUP files\n"));
}
first_file->filename = name;
first_file->local_sym_name = name;
first_file->real = TRUE;
startup_file = name;
}
void
lang_float (bfd_boolean maybe)
{
lang_float_flag = maybe;
}
static void
lang_get_regions (lang_memory_region_type **region,
lang_memory_region_type **lma_region,
const char *memspec,
const char *lma_memspec,
bfd_boolean have_lma,
bfd_boolean have_vma)
{
*lma_region = lang_memory_region_lookup (lma_memspec, FALSE);
if (lma_memspec != NULL
&& ! 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"));
}
void
lang_leave_output_section_statement (fill_type *fill, const char *memspec,
lang_output_section_phdr_list *phdrs,
const char *lma_memspec)
{
lang_get_regions (¤t_section->region,
¤t_section->lma_region,
memspec, lma_memspec,
current_section->load_base != NULL,
current_section->addr_tree != NULL);
current_section->fill = fill;
current_section->phdrs = phdrs;
stat_ptr = &statement_list;
}
void
lang_abs_symbol_at_beginning_of (const char *secname, const char *name)
{
struct bfd_link_hash_entry *h;
h = bfd_link_hash_lookup (link_info.hash, name, TRUE, TRUE, TRUE);
if (h == NULL)
einfo (_("%P%F: bfd_link_hash_lookup failed: %E\n"));
if (h->type == bfd_link_hash_new
|| h->type == bfd_link_hash_undefined)
{
asection *sec;
h->type = bfd_link_hash_defined;
sec = bfd_get_section_by_name (output_bfd, secname);
if (sec == NULL)
h->u.def.value = 0;
else
h->u.def.value = bfd_get_section_vma (output_bfd, sec);
h->u.def.section = bfd_abs_section_ptr;
}
}
void
lang_abs_symbol_at_end_of (const char *secname, const char *name)
{
struct bfd_link_hash_entry *h;
h = bfd_link_hash_lookup (link_info.hash, name, TRUE, TRUE, TRUE);
if (h == NULL)
einfo (_("%P%F: bfd_link_hash_lookup failed: %E\n"));
if (h->type == bfd_link_hash_new
|| h->type == bfd_link_hash_undefined)
{
asection *sec;
h->type = bfd_link_hash_defined;
sec = bfd_get_section_by_name (output_bfd, secname);
if (sec == NULL)
h->u.def.value = 0;
else
h->u.def.value = (bfd_get_section_vma (output_bfd, sec)
+ TO_ADDR (sec->size));
h->u.def.section = bfd_abs_section_ptr;
}
}
void
lang_statement_append (lang_statement_list_type *list,
lang_statement_union_type *element,
lang_statement_union_type **field)
{
*(list->tail) = element;
list->tail = field;
}
void
lang_add_output_format (const char *format,
const char *big,
const char *little,
int from_script)
{
if (output_target == NULL || !from_script)
{
if (command_line.endian == ENDIAN_BIG
&& big != NULL)
format = big;
else if (command_line.endian == ENDIAN_LITTLE
&& little != NULL)
format = little;
output_target = format;
}
}
void
lang_enter_group (void)
{
lang_group_statement_type *g;
g = new_stat (lang_group_statement, stat_ptr);
lang_list_init (&g->children);
stat_ptr = &g->children;
}
void
lang_leave_group (void)
{
stat_ptr = &statement_list;
}
void
lang_new_phdr (const char *name,
etree_type *type,
bfd_boolean filehdr,
bfd_boolean phdrs,
etree_type *at,
etree_type *flags)
{
struct lang_phdr *n, **pp;
n = stat_alloc (sizeof (struct lang_phdr));
n->next = NULL;
n->name = name;
n->type = exp_get_value_int (type, 0, "program header type",
lang_final_phase_enum);
n->filehdr = filehdr;
n->phdrs = phdrs;
n->at = at;
n->flags = flags;
for (pp = &lang_phdr_list; *pp != NULL; pp = &(*pp)->next)
;
*pp = n;
}
static void
lang_record_phdrs (void)
{
unsigned int alc;
asection **secs;
lang_output_section_phdr_list *last;
struct lang_phdr *l;
lang_output_section_statement_type *os;
alc = 10;
secs = xmalloc (alc * sizeof (asection *));
last = NULL;
for (l = lang_phdr_list; l != NULL; l = l->next)
{
unsigned int c;
flagword flags;
bfd_vma at;
c = 0;
for (os = &lang_output_section_statement.head->output_section_statement;
os != NULL;
os = os->next)
{
lang_output_section_phdr_list *pl;
if (os->constraint == -1)
continue;
pl = os->phdrs;
if (pl != NULL)
last = pl;
else
{
if (os->sectype == noload_section
|| os->bfd_section == NULL
|| (os->bfd_section->flags & SEC_ALLOC) == 0)
continue;
pl = last;
}
if (os->bfd_section == NULL)
continue;
for (; pl != NULL; pl = pl->next)
{
if (strcmp (pl->name, l->name) == 0)
{
if (c >= alc)
{
alc *= 2;
secs = xrealloc (secs, alc * sizeof (asection *));
}
secs[c] = os->bfd_section;
++c;
pl->used = TRUE;
}
}
}
if (l->flags == NULL)
flags = 0;
else
flags = exp_get_vma (l->flags, 0, "phdr flags",
lang_final_phase_enum);
if (l->at == NULL)
at = 0;
else
at = exp_get_vma (l->at, 0, "phdr load address",
lang_final_phase_enum);
if (! bfd_record_phdr (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);
for (os = &lang_output_section_statement.head->output_section_statement;
os != NULL;
os = os->next)
{
lang_output_section_phdr_list *pl;
if (os->constraint == -1
|| os->bfd_section == NULL)
continue;
for (pl = os->phdrs;
pl != NULL;
pl = pl->next)
if (! pl->used && strcmp (pl->name, "NONE") != 0)
einfo (_("%X%P: section `%s' assigned to non-existent phdr `%s'\n"),
os->name, pl->name);
}
}
void
lang_add_nocrossref (lang_nocrossref_type *l)
{
struct lang_nocrossrefs *n;
n = xmalloc (sizeof *n);
n->next = nocrossref_list;
n->list = l;
nocrossref_list = n;
link_info.notice_all = TRUE;
}
static etree_type *overlay_vma;
static etree_type *overlay_subalign;
static etree_type *overlay_max;
struct overlay_list {
struct overlay_list *next;
lang_output_section_statement_type *os;
};
static struct overlay_list *overlay_list;
void
lang_enter_overlay (etree_type *vma_expr, etree_type *subalign)
{
ASSERT (overlay_vma == NULL
&& overlay_subalign == NULL
&& overlay_max == NULL);
overlay_vma = vma_expr;
overlay_subalign = subalign;
}
void
lang_enter_overlay_section (const char *name)
{
struct overlay_list *n;
etree_type *size;
lang_enter_output_section_statement (name, overlay_vma, normal_section,
0, overlay_subalign, 0, 0);
if (overlay_list == NULL)
overlay_vma = exp_nameop (ADDR, name);
n = xmalloc (sizeof *n);
n->os = current_section;
n->next = overlay_list;
overlay_list = n;
size = exp_nameop (SIZEOF, name);
if (overlay_max == NULL)
overlay_max = size;
else
overlay_max = exp_binop (MAX_K, overlay_max, size);
}
void
lang_leave_overlay_section (fill_type *fill,
lang_output_section_phdr_list *phdrs)
{
const char *name;
char *clean, *s2;
const char *s1;
char *buf;
name = current_section->name;
lang_leave_output_section_statement (fill, DEFAULT_MEMORY_REGION, phdrs, 0);
clean = xmalloc (strlen (name) + 1);
s2 = clean;
for (s1 = name; *s1 != '\0'; s1++)
if (ISALNUM (*s1) || *s1 == '_')
*s2++ = *s1;
*s2 = '\0';
buf = xmalloc (strlen (clean) + sizeof "__load_start_");
sprintf (buf, "__load_start_%s", clean);
lang_add_assignment (exp_assop ('=', buf,
exp_nameop (LOADADDR, name)));
buf = xmalloc (strlen (clean) + sizeof "__load_stop_");
sprintf (buf, "__load_stop_%s", clean);
lang_add_assignment (exp_assop ('=', buf,
exp_binop ('+',
exp_nameop (LOADADDR, name),
exp_nameop (SIZEOF, name))));
free (clean);
}
void
lang_leave_overlay (etree_type *lma_expr,
int nocrossrefs,
fill_type *fill,
const char *memspec,
lang_output_section_phdr_list *phdrs,
const char *lma_memspec)
{
lang_memory_region_type *region;
lang_memory_region_type *lma_region;
struct overlay_list *l;
lang_nocrossref_type *nocrossref;
lang_get_regions (®ion, &lma_region,
memspec, lma_memspec,
lma_expr != NULL, FALSE);
nocrossref = NULL;
if (overlay_list != NULL)
overlay_list->os->update_dot_tree
= exp_assop ('=', ".", exp_binop ('+', overlay_vma, overlay_max));
l = overlay_list;
while (l != NULL)
{
struct overlay_list *next;
if (fill != NULL && l->os->fill == NULL)
l->os->fill = fill;
l->os->region = region;
l->os->lma_region = lma_region;
if (l->next == 0)
l->os->load_base = lma_expr;
else if (lma_region == 0)
l->os->load_base = exp_binop ('+',
exp_nameop (LOADADDR, l->next->os->name),
exp_nameop (SIZEOF, l->next->os->name));
if (phdrs != NULL && l->os->phdrs == NULL)
l->os->phdrs = phdrs;
if (nocrossrefs)
{
lang_nocrossref_type *nc;
nc = xmalloc (sizeof *nc);
nc->name = l->os->name;
nc->next = nocrossref;
nocrossref = nc;
}
next = l->next;
free (l);
l = next;
}
if (nocrossref != NULL)
lang_add_nocrossref (nocrossref);
overlay_vma = NULL;
overlay_list = NULL;
overlay_max = NULL;
}
struct bfd_elf_version_tree *lang_elf_version_info;
static struct bfd_elf_version_expr *
lang_vers_match (struct bfd_elf_version_expr_head *head,
struct bfd_elf_version_expr *prev,
const char *sym)
{
const char *cxx_sym = sym;
const char *java_sym = sym;
struct bfd_elf_version_expr *expr = NULL;
if (head->mask & BFD_ELF_VERSION_CXX_TYPE)
{
cxx_sym = cplus_demangle (sym, DMGL_PARAMS | DMGL_ANSI);
if (!cxx_sym)
cxx_sym = sym;
}
if (head->mask & BFD_ELF_VERSION_JAVA_TYPE)
{
java_sym = cplus_demangle (sym, DMGL_JAVA);
if (!java_sym)
java_sym = sym;
}
if (head->htab && (prev == NULL || prev->symbol))
{
struct bfd_elf_version_expr e;
switch (prev ? prev->mask : 0)
{
case 0:
if (head->mask & BFD_ELF_VERSION_C_TYPE)
{
e.symbol = sym;
expr = htab_find (head->htab, &e);
while (expr && strcmp (expr->symbol, sym) == 0)
if (expr->mask == BFD_ELF_VERSION_C_TYPE)
goto out_ret;
else
expr = expr->next;
}
case BFD_ELF_VERSION_C_TYPE:
if (head->mask & BFD_ELF_VERSION_CXX_TYPE)
{
e.symbol = cxx_sym;
expr = htab_find (head->htab, &e);
while (expr && strcmp (expr->symbol, cxx_sym) == 0)
if (expr->mask == BFD_ELF_VERSION_CXX_TYPE)
goto out_ret;
else
expr = expr->next;
}
case BFD_ELF_VERSION_CXX_TYPE:
if (head->mask & BFD_ELF_VERSION_JAVA_TYPE)
{
e.symbol = java_sym;
expr = htab_find (head->htab, &e);
while (expr && strcmp (expr->symbol, java_sym) == 0)
if (expr->mask == BFD_ELF_VERSION_JAVA_TYPE)
goto out_ret;
else
expr = expr->next;
}
default:
break;
}
}
if (prev == NULL || prev->symbol)
expr = head->remaining;
else
expr = prev->next;
while (expr)
{
const char *s;
if (expr->pattern[0] == '*' && expr->pattern[1] == '\0')
break;
if (expr->mask == BFD_ELF_VERSION_JAVA_TYPE)
s = java_sym;
else if (expr->mask == BFD_ELF_VERSION_CXX_TYPE)
s = cxx_sym;
else
s = sym;
if (fnmatch (expr->pattern, s, 0) == 0)
break;
expr = expr->next;
}
out_ret:
if (cxx_sym != sym)
free ((char *) cxx_sym);
if (java_sym != sym)
free ((char *) java_sym);
return expr;
}
static const char *
realsymbol (const char *pattern)
{
const char *p;
bfd_boolean changed = FALSE, backslash = FALSE;
char *s, *symbol = xmalloc (strlen (pattern) + 1);
for (p = pattern, s = symbol; *p != '\0'; ++p)
{
if (! backslash && (*p == '?' || *p == '*' || *p == '['))
{
free (symbol);
return NULL;
}
if (backslash)
{
*(s - 1) = *p;
changed = TRUE;
}
else
*s++ = *p;
backslash = *p == '\\';
}
if (changed)
{
*s = '\0';
return symbol;
}
else
{
free (symbol);
return pattern;
}
}
struct bfd_elf_version_expr *
lang_new_vers_pattern (struct bfd_elf_version_expr *orig,
const char *new,
const char *lang)
{
struct bfd_elf_version_expr *ret;
ret = xmalloc (sizeof *ret);
ret->next = orig;
ret->pattern = new;
ret->symver = 0;
ret->script = 0;
ret->symbol = realsymbol (new);
if (lang == NULL || strcasecmp (lang, "C") == 0)
ret->mask = BFD_ELF_VERSION_C_TYPE;
else if (strcasecmp (lang, "C++") == 0)
ret->mask = BFD_ELF_VERSION_CXX_TYPE;
else if (strcasecmp (lang, "Java") == 0)
ret->mask = BFD_ELF_VERSION_JAVA_TYPE;
else
{
einfo (_("%X%P: unknown language `%s' in version information\n"),
lang);
ret->mask = BFD_ELF_VERSION_C_TYPE;
}
return ldemul_new_vers_pattern (ret);
}
struct bfd_elf_version_tree *
lang_new_vers_node (struct bfd_elf_version_expr *globals,
struct bfd_elf_version_expr *locals)
{
struct bfd_elf_version_tree *ret;
ret = xcalloc (1, sizeof *ret);
ret->globals.list = globals;
ret->locals.list = locals;
ret->match = lang_vers_match;
ret->name_indx = (unsigned int) -1;
return ret;
}
static int version_index;
static hashval_t
version_expr_head_hash (const void *p)
{
const struct bfd_elf_version_expr *e = p;
return htab_hash_string (e->symbol);
}
static int
version_expr_head_eq (const void *p1, const void *p2)
{
const struct bfd_elf_version_expr *e1 = p1;
const struct bfd_elf_version_expr *e2 = p2;
return strcmp (e1->symbol, e2->symbol) == 0;
}
static void
lang_finalize_version_expr_head (struct bfd_elf_version_expr_head *head)
{
size_t count = 0;
struct bfd_elf_version_expr *e, *next;
struct bfd_elf_version_expr **list_loc, **remaining_loc;
for (e = head->list; e; e = e->next)
{
if (e->symbol)
count++;
head->mask |= e->mask;
}
if (count)
{
head->htab = htab_create (count * 2, version_expr_head_hash,
version_expr_head_eq, NULL);
list_loc = &head->list;
remaining_loc = &head->remaining;
for (e = head->list; e; e = next)
{
next = e->next;
if (!e->symbol)
{
*remaining_loc = e;
remaining_loc = &e->next;
}
else
{
void **loc = htab_find_slot (head->htab, e, INSERT);
if (*loc)
{
struct bfd_elf_version_expr *e1, *last;
e1 = *loc;
last = NULL;
do
{
if (e1->mask == e->mask)
{
last = NULL;
break;
}
last = e1;
e1 = e1->next;
}
while (e1 && strcmp (e1->symbol, e->symbol) == 0);
if (last == NULL)
{
free (e);
}
else
{
e->next = last->next;
last->next = e;
}
}
else
{
*loc = e;
*list_loc = e;
list_loc = &e->next;
}
}
}
*remaining_loc = NULL;
*list_loc = head->remaining;
}
else
head->remaining = head->list;
}
void
lang_register_vers_node (const char *name,
struct bfd_elf_version_tree *version,
struct bfd_elf_version_deps *deps)
{
struct bfd_elf_version_tree *t, **pp;
struct bfd_elf_version_expr *e1;
if (name == NULL)
name = "";
if ((name[0] == '\0' && lang_elf_version_info != NULL)
|| (lang_elf_version_info && lang_elf_version_info->name[0] == '\0'))
{
einfo (_("%X%P: anonymous version tag cannot be combined"
" with other version tags\n"));
free (version);
return;
}
for (t = lang_elf_version_info; t != NULL; t = t->next)
if (strcmp (t->name, name) == 0)
einfo (_("%X%P: duplicate version tag `%s'\n"), name);
lang_finalize_version_expr_head (&version->globals);
lang_finalize_version_expr_head (&version->locals);
for (e1 = version->globals.list; e1 != NULL; e1 = e1->next)
{
for (t = lang_elf_version_info; t != NULL; t = t->next)
{
struct bfd_elf_version_expr *e2;
if (t->locals.htab && e1->symbol)
{
e2 = htab_find (t->locals.htab, e1);
while (e2 && strcmp (e1->symbol, e2->symbol) == 0)
{
if (e1->mask == e2->mask)
einfo (_("%X%P: duplicate expression `%s'"
" in version information\n"), e1->symbol);
e2 = e2->next;
}
}
else if (!e1->symbol)
for (e2 = t->locals.remaining; e2 != NULL; e2 = e2->next)
if (strcmp (e1->pattern, e2->pattern) == 0
&& e1->mask == e2->mask)
einfo (_("%X%P: duplicate expression `%s'"
" in version information\n"), e1->pattern);
}
}
for (e1 = version->locals.list; e1 != NULL; e1 = e1->next)
{
for (t = lang_elf_version_info; t != NULL; t = t->next)
{
struct bfd_elf_version_expr *e2;
if (t->globals.htab && e1->symbol)
{
e2 = htab_find (t->globals.htab, e1);
while (e2 && strcmp (e1->symbol, e2->symbol) == 0)
{
if (e1->mask == e2->mask)
einfo (_("%X%P: duplicate expression `%s'"
" in version information\n"),
e1->symbol);
e2 = e2->next;
}
}
else if (!e1->symbol)
for (e2 = t->globals.remaining; e2 != NULL; e2 = e2->next)
if (strcmp (e1->pattern, e2->pattern) == 0
&& e1->mask == e2->mask)
einfo (_("%X%P: duplicate expression `%s'"
" in version information\n"), e1->pattern);
}
}
version->deps = deps;
version->name = name;
if (name[0] != '\0')
{
++version_index;
version->vernum = version_index;
}
else
version->vernum = 0;
for (pp = &lang_elf_version_info; *pp != NULL; pp = &(*pp)->next)
;
*pp = version;
}
struct bfd_elf_version_deps *
lang_add_vers_depend (struct bfd_elf_version_deps *list, const char *name)
{
struct bfd_elf_version_deps *ret;
struct bfd_elf_version_tree *t;
ret = xmalloc (sizeof *ret);
ret->next = list;
for (t = lang_elf_version_info; t != NULL; t = t->next)
{
if (strcmp (t->name, name) == 0)
{
ret->version_needed = t;
return ret;
}
}
einfo (_("%X%P: unable to find version dependency `%s'\n"), name);
return ret;
}
static void
lang_do_version_exports_section (void)
{
struct bfd_elf_version_expr *greg = NULL, *lreg;
LANG_FOR_EACH_INPUT_STATEMENT (is)
{
asection *sec = bfd_get_section_by_name (is->the_bfd, ".exports");
char *contents, *p;
bfd_size_type len;
if (sec == NULL)
continue;
len = sec->size;
contents = xmalloc (len);
if (!bfd_get_section_contents (is->the_bfd, sec, contents, 0, len))
einfo (_("%X%P: unable to read .exports section contents\n"), sec);
p = contents;
while (p < contents + len)
{
greg = lang_new_vers_pattern (greg, p, NULL);
p = strchr (p, '\0') + 1;
}
sec->flags |= SEC_EXCLUDE;
}
lreg = lang_new_vers_pattern (NULL, "*", NULL);
lang_register_vers_node (command_line.version_exports_section,
lang_new_vers_node (greg, lreg), NULL);
}
void
lang_add_unique (const char *name)
{
struct unique_sections *ent;
for (ent = unique_section_list; ent; ent = ent->next)
if (strcmp (ent->name, name) == 0)
return;
ent = xmalloc (sizeof *ent);
ent->name = xstrdup (name);
ent->next = unique_section_list;
unique_section_list = ent;
}