/* Linker command language support.
- Copyright (C) 1991, 92, 93, 94, 95, 1996 Free Software Foundation, Inc.
+ Copyright (C) 1991, 92, 93, 94, 95, 96, 1997 Free Software Foundation, Inc.
This file is part of GLD, the Gnu Linker.
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with GLD; see the file COPYING. If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+along with GLD; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
#include "bfd.h"
#include "sysdep.h"
#include "libiberty.h"
+#include "obstack.h"
#include "bfdlink.h"
#include "ld.h"
#include "ldmisc.h"
#include "ldctor.h"
#include "ldfile.h"
+#include "fnmatch.h"
+
+#include <ctype.h>
/* FORWARDS */
static lang_statement_union_type *new_statement PARAMS ((enum statement_enum,
static lang_statement_list_type lang_output_section_statement;
static CONST char *current_target;
static CONST char *output_target;
-static int longest_section_name = 8;
static lang_statement_list_type statement_list;
static struct lang_phdr *lang_phdr_list;
-static void print_size PARAMS ((size_t value));
-static void print_alignment PARAMS ((unsigned int value));
-static void print_fill PARAMS ((fill_type value));
-static void print_section PARAMS ((const char *name));
static void lang_for_each_statement_worker
PARAMS ((void (*func) (lang_statement_union_type *),
lang_statement_union_type *s));
static lang_input_statement_type *new_afile
PARAMS ((const char *name, lang_input_file_enum_type file_type,
const char *target, boolean add_to_list));
-static void print_flags PARAMS ((int *ignore_flags));
static void init_os PARAMS ((lang_output_section_statement_type *s));
+static void exp_init_os PARAMS ((etree_type *));
static void section_already_linked PARAMS ((bfd *, asection *, PTR));
+static boolean wildcardp PARAMS ((const char *));
static void wild_section PARAMS ((lang_wild_statement_type *ptr,
const char *section,
lang_input_statement_type *file,
static lang_input_statement_type *lookup_name PARAMS ((const char *name));
static void load_symbols PARAMS ((lang_input_statement_type *entry,
lang_statement_list_type *));
+static void wild_file PARAMS ((lang_wild_statement_type *, const char *,
+ lang_input_statement_type *,
+ lang_output_section_statement_type *));
static void wild PARAMS ((lang_wild_statement_type *s,
const char *section, const char *file,
const char *target,
PARAMS ((lang_assignment_statement_type *assignment,
lang_output_section_statement_type *output_section));
static void print_input_statement PARAMS ((lang_input_statement_type *statm));
+static boolean print_one_symbol PARAMS ((struct bfd_link_hash_entry *, PTR));
static void print_input_section PARAMS ((lang_input_section_type *in));
static void print_fill_statement PARAMS ((lang_fill_statement_type *fill));
static void print_data_statement PARAMS ((lang_data_statement_type *data));
+static void print_address_statement PARAMS ((lang_address_statement_type *));
static void print_reloc_statement PARAMS ((lang_reloc_statement_type *reloc));
static void print_padding_statement PARAMS ((lang_padding_statement_type *s));
static void print_wild_statement
boolean had_output_filename = false;
boolean lang_float_flag = false;
boolean delete_output_file_on_failure = false;
+struct lang_nocrossrefs *nocrossref_list;
etree_type *base; /* Relocation base - or null */
#define outside_symbol_address(q) ((q)->value + outside_section_address(q->section))
+#define SECTION_NAME_MAP_LENGTH (16)
+
PTR
stat_alloc (size)
size_t size;
return obstack_alloc (&stat_obstack, size);
}
-static void
-print_size (value)
- size_t value;
-{
- fprintf (config.map_file, "%5x", (unsigned) value);
-}
-
-static void
-print_alignment (value)
- unsigned int value;
-{
- fprintf (config.map_file, "2**%1u", value);
-}
-
-static void
-print_fill (value)
- fill_type value;
-{
- fprintf (config.map_file, "%04x", (unsigned) value);
-}
-
-static void
-print_section (name)
- CONST char *name;
-{
- fprintf (config.map_file, "%*s", -longest_section_name, name);
-}
-
/*----------------------------------------------------------------------
lang_for_each_statement walks the parse tree and calls the provided
function for each node
case lang_assignment_statement_enum:
case lang_padding_statement_enum:
case lang_address_statement_enum:
+ case lang_fill_statement_enum:
break;
default:
FAIL ();
lang_has_input_file = true;
p->target = target;
- p->complained = false;
switch (file_type)
{
case lang_input_file_is_symbols_only_enum:
p->next_real_file = (lang_statement_union_type *) NULL;
p->next = (lang_statement_union_type *) NULL;
p->symbol_count = 0;
- p->common_output_section = (asection *) NULL;
p->dynamic = config.dynamic_link;
p->whole_archive = whole_archive;
p->loaded = false;
CONST char *CONST name;
{
- lang_memory_region_type *p = lang_memory_region_list;
+ lang_memory_region_type *p;
for (p = lang_memory_region_list;
p != (lang_memory_region_type *) NULL;
lookup->next = (lang_statement_union_type *) NULL;
lookup->bfd_section = (asection *) NULL;
lookup->processed = false;
- lookup->loadable = 1;
+ lookup->sectype = normal_section;
lookup->addr_tree = (etree_type *) NULL;
lang_list_init (&lookup->children);
return lookup;
}
-/*ARGSUSED*/
-static void
-print_flags (ignore_flags)
- int *ignore_flags;
-{
- fprintf (config.map_file, "(");
-#if 0
- if (flags->flag_read)
- fprintf (outfile, "R");
- if (flags->flag_write)
- fprintf (outfile, "W");
- if (flags->flag_executable)
- fprintf (outfile, "X");
- if (flags->flag_loadable)
- fprintf (outfile, "L");
-#endif
- fprintf (config.map_file, ")");
-}
-
void
lang_map ()
{
lang_memory_region_type *m;
- fprintf (config.map_file, "**MEMORY CONFIGURATION**\n\n");
-#ifdef BFD64
- fprintf (config.map_file, "name\t\torigin\t\tlength\t\tattributes\n");
-#else
- fprintf (config.map_file,
- "name\t\torigin length r_size c_size is attributes\n");
+ minfo ("\nMemory Configuration\n\n");
+ fprintf (config.map_file, "%-16s %-18s %-18s\n",
+ "Name", "Origin", "Length");
-#endif
for (m = lang_memory_region_list;
m != (lang_memory_region_type *) NULL;
m = m->next)
{
- fprintf (config.map_file, "%-16s", m->name);
- print_address (m->origin);
- print_space ();
- print_address ((bfd_vma)m->length);
- print_space ();
- print_address ((bfd_vma)m->old_length);
- print_space();
- print_address (m->current - m->origin);
- print_space();
- if (m->old_length)
- fprintf (config.map_file, " %2d%% ",
- (int) ((m->current - m->origin) * 100 / m->old_length));
- print_flags (&m->flags);
- fprintf (config.map_file, "\n");
- }
- fprintf (config.map_file, "\n\n**LINK EDITOR MEMORY MAP**\n\n");
- fprintf (config.map_file, "output input virtual\n");
- fprintf (config.map_file, "section section address tsize\n\n");
+ char buf[100];
+ int len;
- print_statements ();
+ 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\n", m->length);
+ }
+
+ fprintf (config.map_file, "\nLinker script and memory map\n\n");
+ print_statements ();
}
-/*
- *
- */
+/* Initialize an output section. */
+
static void
init_os (s)
- lang_output_section_statement_type * s;
+ lang_output_section_statement_type *s;
{
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", DISCARD_SECTION_NAME);
/* vma to allow us to output a section through itself */
s->bfd_section->output_offset = 0;
get_userdata (s->bfd_section) = (PTR) new;
+
+ /* If there is a base address, make sure that any sections it might
+ mention are initialized. */
+ if (s->addr_tree != NULL)
+ exp_init_os (s->addr_tree);
+}
+
+/* Make sure that all output sections mentioned in an expression are
+ initialized. */
+
+static void
+exp_init_os (exp)
+ 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_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;
+ }
}
/* Sections marked with the SEC_LINK_ONCE flag should only be linked
/*ARGSUSED*/
static void
-section_already_linked (abfd, sec, ignore)
+section_already_linked (abfd, sec, data)
bfd *abfd;
asection *sec;
- PTR ignore;
+ PTR data;
{
+ lang_input_statement_type *entry = (lang_input_statement_type *) data;
struct sec_link_once
{
struct sec_link_once *next;
const char *name;
struct sec_link_once *l;
+ /* If we are only reading symbols from this object, then we want to
+ discard all sections. */
+ if (entry->just_syms_flag)
+ {
+ sec->output_section = bfd_abs_section_ptr;
+ sec->output_offset = sec->vma;
+ return;
+ }
+
flags = bfd_get_section_flags (abfd, sec);
if ((flags & SEC_LINK_ONCE) == 0)
break;
case SEC_LINK_DUPLICATES_ONE_ONLY:
- einfo ("%P: %B: warning: ignoring duplicate section `%s'",
+ einfo ("%P: %B: warning: ignoring duplicate section `%s'\n",
abfd, name);
break;
case SEC_LINK_DUPLICATES_SAME_SIZE:
if (bfd_section_size (abfd, sec)
!= bfd_section_size (l->sec->owner, l->sec))
- einfo ("%P: %B: warning: duplicate section `%s' has different size",
- abfd, sec);
+ einfo ("%P: %B: warning: duplicate section `%s' has different size\n",
+ abfd, name);
break;
}
explicit actions, like foo.o(.text), bar.o(.text) and
foo.o(.text, .data). */
+/* Return true if the PATTERN argument is a wildcard pattern.
+ Although backslashes are treated specially if a pattern contains
+ wildcards, we do not consider the mere presence of a backslash to
+ be enough to cause the the pattern to be treated as a wildcard.
+ That lets us handle DOS filenames more naturally. */
+
+static boolean
+wildcardp (pattern)
+ const char *pattern;
+{
+ const char *s;
+
+ for (s = pattern; *s != '\0'; ++s)
+ if (*s == '?'
+ || *s == '*'
+ || *s == '[')
+ return true;
+ return false;
+}
+
/* Add SECTION to the output section OUTPUT. Do this by creating a
lang_input_section statement which is placed at PTR. FILE is the
input file which holds SECTION. */
if (section->output_section == NULL)
{
+ boolean first;
lang_input_section_type *new;
+ flagword flags;
if (output->bfd_section == NULL)
- init_os (output);
+ {
+ init_os (output);
+ first = true;
+ }
+ else
+ first = false;
/* Add a section reference to the list */
new = new_stat (lang_input_section, ptr);
new->ifile = file;
section->output_section = output->bfd_section;
+ flags = section->flags;
+
/* We don't copy the SEC_NEVER_LOAD flag from an input section
to an output section, because we want to be able to include a
SEC_NEVER_LOAD section in the middle of an otherwise loaded
section (I don't know why we want to do this, but we do).
build_link_order in ldwrite.c handles this case by turning
the embedded SEC_NEVER_LOAD section into a fill. */
- section->output_section->flags |=
- section->flags & (flagword) (~ SEC_NEVER_LOAD);
- if (! output->loadable)
+ flags &= ~ SEC_NEVER_LOAD;
+
+ /* If final link, don't copy the SEC_LINK_ONCE flags, they've
+ already been processed. One reason to do this is that on pe
+ format targets, .text$foo sections go into .text and it's odd
+ to see .text with SEC_LINK_ONCE set. */
+
+ if (! link_info.relocateable)
+ flags &= ~ (SEC_LINK_ONCE | SEC_LINK_DUPLICATES);
+
+ /* If this is not the first input section, and the SEC_READONLY
+ flag is not currently set, then don't set it just because the
+ input section has it set. */
+
+ if (! first && (section->output_section->flags & SEC_READONLY) == 0)
+ flags &= ~ SEC_READONLY;
+
+ section->output_section->flags |= flags;
+
+ /* If SEC_READONLY is not set in the input section, then clear
+ it from the output section. */
+ if ((section->flags & SEC_READONLY) == 0)
+ section->output_section->flags &= ~SEC_READONLY;
+
+ switch (output->sectype)
{
- /* Turn off load flag */
+ 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->alignment_power > output->bfd_section->alignment_power)
if (file->just_syms_flag == false)
{
register asection *s;
+ boolean wildcard;
+
+ if (section == NULL)
+ wildcard = false;
+ else
+ wildcard = wildcardp (section);
for (s = file->the_bfd->sections; s != NULL; s = s->next)
{
+ boolean match;
+
/* Attach all sections named SECTION. If SECTION is NULL,
then attach all sections.
section. I did not understand that, and I took it out.
--ian@cygnus.com. */
- if (section == NULL
- || strcmp (bfd_get_section_name (file->the_bfd, s),
- section) == 0)
+ if (section == NULL)
+ match = true;
+ else
+ {
+ const char *name;
+
+ name = bfd_get_section_name (file->the_bfd, s);
+ if (wildcard)
+ match = fnmatch (section, name, 0) == 0 ? true : false;
+ else
+ match = strcmp (section, name) == 0 ? true : false;
+ }
+ if (match)
wild_doit (&ptr->children, s, output, file);
}
}
einfo ("%F%B: object %B in archive is not object\n",
entry->the_bfd, member);
if (! ((*link_info.callbacks->add_archive_element)
- (&link_info, member, "-whole-archive")))
+ (&link_info, member, "--whole-archive")))
abort ();
if (! bfd_link_add_symbols (member, &link_info))
einfo ("%F%B: could not read symbols: %E\n", member);
entry->loaded = true;
}
+/* Handle a wild statement for a single file F. */
+
+static void
+wild_file (s, section, f, output)
+ lang_wild_statement_type *s;
+ const char *section;
+ lang_input_statement_type *f;
+ lang_output_section_statement_type *output;
+{
+ if (f->the_bfd == NULL
+ || ! bfd_check_format (f->the_bfd, bfd_archive))
+ wild_section (s, section, f, output);
+ else
+ {
+ bfd *member;
+
+ /* This is an archive file. We must map each member of the
+ archive separately. */
+ member = bfd_openr_next_archived_file (f->the_bfd, (bfd *) NULL);
+ while (member != NULL)
+ {
+ /* When lookup_name is called, it will call the add_symbols
+ entry point for the archive. For each element of the
+ archive which is included, BFD will call ldlang_add_file,
+ which will set the usrdata field of the member to the
+ lang_input_statement. */
+ if (member->usrdata != NULL)
+ {
+ wild_section (s, section,
+ (lang_input_statement_type *) member->usrdata,
+ output);
+ }
+
+ member = bfd_openr_next_archived_file (f->the_bfd, member);
+ }
+ }
+}
+
/* Handle a wild statement. SECTION or FILE or both may be NULL,
indicating that it is a wildcard. Separate lang_input_section
statements are created for each part of the expansion; they are
f != (lang_input_statement_type *) NULL;
f = (lang_input_statement_type *) f->next)
{
- wild_section (s, section, f, output);
+ wild_file (s, section, f, output);
+ }
+ }
+ else if (wildcardp (file))
+ {
+ for (f = (lang_input_statement_type *) file_chain.head;
+ f != (lang_input_statement_type *) NULL;
+ f = (lang_input_statement_type *) f->next)
+ {
+ if (fnmatch (file, f->filename, FNM_FILE_NAME) == 0)
+ wild_file (s, section, f, output);
}
}
else
{
/* Perform the iteration over a single file */
f = lookup_name (file);
- if (f->the_bfd == NULL
- || ! bfd_check_format (f->the_bfd, bfd_archive))
- wild_section (s, section, f, output);
- else
- {
- bfd *member;
-
- /* This is an archive file. We must map each member of the
- archive separately. */
- member = bfd_openr_next_archived_file (f->the_bfd, (bfd *) NULL);
- while (member != NULL)
- {
- /* When lookup_name is called, it will call the
- add_symbols entry point for the archive. For each
- element of the archive which is included, BFD will
- call ldlang_add_file, which will set the usrdata
- field of the member to the lang_input_statement. */
- if (member->usrdata != NULL)
- {
- wild_section (s, section,
- (lang_input_statement_type *) member->usrdata,
- output);
- }
-
- member = bfd_openr_next_archived_file (f->the_bfd, member);
- }
- }
+ wild_file (s, section, f, output);
}
if (section != (char *) NULL
einfo ("%P%F: cannot open output file %s: %E\n", name);
}
- delete_output_file_on_failure = 1;
+ delete_output_file_on_failure = true;
/* output->flags |= D_PAGED;*/
break;
case lang_wild_statement_enum:
/* Maybe we should load the file's symbols */
- if (s->wild_statement.filename)
+ if (s->wild_statement.filename
+ && ! wildcardp (s->wild_statement.filename))
(void) lookup_name (s->wild_statement.filename);
open_input_bfds (s->wild_statement.children.head, force);
break;
h = bfd_link_hash_lookup (link_info.hash, ptr->name, true, false, true);
if (h == (struct bfd_link_hash_entry *) NULL)
- einfo ("%P%F: bfd_link_hash_lookup failed: %E");
+ einfo ("%P%F: bfd_link_hash_lookup failed: %E\n");
if (h->type == bfd_link_hash_new)
{
h->type = bfd_link_hash_undefined;
case lang_object_symbols_statement_enum:
case lang_data_statement_enum:
case lang_reloc_statement_enum:
- case lang_assignment_statement_enum:
case lang_padding_statement_enum:
case lang_input_statement_enum:
if (output_section_statement != NULL
&& output_section_statement->bfd_section == NULL)
init_os (output_section_statement);
break;
+ case lang_assignment_statement_enum:
+ if (output_section_statement != NULL
+ && output_section_statement->bfd_section == NULL)
+ init_os (output_section_statement);
+
+ /* Make sure that any sections mentioned in the assignment
+ are initialized. */
+ exp_init_os (s->assignment_statement.exp);
+ break;
case lang_afile_asection_pair_statement_enum:
FAIL ();
break;
lang_output_section_statement_type * output_section_statement;
{
asection *section = output_section_statement->bfd_section;
+ int len;
- print_nl ();
- print_section (output_section_statement->name);
+ if (output_section_statement != abs_output_section)
+ {
+ minfo ("\n%s", output_section_statement->name);
+ if (section != NULL)
+ {
+ print_dot = section->vma;
- if (section)
- {
- print_dot = section->vma;
- print_space ();
- print_section ("");
- print_space ();
- print_address (section->vma);
- print_space ();
- print_size (section->_raw_size);
- print_space ();
- print_size(section->_cooked_size);
- print_space ();
- print_alignment (section->alignment_power);
- print_space ();
-#if 0
- fprintf (config.map_file, "%s flags", output_section_statement->region->name);
- print_flags (stdout, &output_section_statement->flags);
-#endif
- if (section->flags & SEC_LOAD)
- fprintf (config.map_file, "load ");
- if (section->flags & SEC_ALLOC)
- fprintf (config.map_file, "alloc ");
- if (section->flags & SEC_RELOC)
- fprintf (config.map_file, "reloc ");
- if (section->flags & SEC_HAS_CONTENTS)
- fprintf (config.map_file, "contents ");
+ 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;
+ }
- }
- else
- {
- fprintf (config.map_file, " (no attached output section)");
- }
- print_nl ();
- if (output_section_statement->load_base)
- {
- bfd_vma b = exp_get_abs_int(output_section_statement->load_base,
- 0, "output base", lang_final_phase_enum);
- fprintf (config.map_file, "Output address ");
- fprintf_vma (config.map_file, b);
- fprintf (config.map_file, "\n");
- }
- if (output_section_statement->section_alignment >= 0
- || output_section_statement->subsection_alignment >= 0)
- {
- fprintf (config.map_file, "\t\t\t\t\tforced alignment ");
- if (output_section_statement->section_alignment >= 0)
- {
- fprintf (config.map_file, "section 2**%d ",output_section_statement->section_alignment );
- }
- if ( output_section_statement->subsection_alignment >= 0)
- {
- fprintf (config.map_file, "subsection 2**%d ",output_section_statement->subsection_alignment );
+ minfo ("0x%V %W", section->vma, section->_raw_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_nl ();
- }
+
print_statement_list (output_section_statement->children.head,
output_section_statement);
-
}
static void
lang_assignment_statement_type * assignment;
lang_output_section_statement_type * output_section;
{
+ int i;
etree_value_type result;
- print_section ("");
- print_space ();
- print_section ("");
- print_space ();
- print_address (print_dot);
- print_space ();
- result = exp_fold_tree (assignment->exp->assign.src,
- output_section,
- lang_final_phase_enum,
- print_dot,
- &print_dot);
+ for (i = 0; i < SECTION_NAME_MAP_LENGTH; i++)
+ print_space ();
+ result = exp_fold_tree (assignment->exp->assign.src, output_section,
+ lang_final_phase_enum, print_dot, &print_dot);
if (result.valid)
- {
- print_address (result.value);
- }
+ minfo ("0x%V", result.value + result.section->bfd_section->vma);
else
{
- fprintf (config.map_file, "*undefined*");
+ minfo ("*undef* ");
+#ifdef BFD64
+ minfo (" ");
+#endif
}
- print_space ();
+
+ minfo (" ");
+
exp_print_tree (assignment->exp);
- fprintf (config.map_file, "\n");
+ print_nl ();
}
static void
}
}
-/* Print all the defined symbols for the abfd provided by in the supplied
- section.
-*/
+/* Print all symbols defined in a particular section. This is called
+ via bfd_link_hash_traverse. */
static boolean
print_one_symbol (hash_entry, ptr)
-struct bfd_link_hash_entry *hash_entry;
-PTR ptr;
-{
- asection * sec = (asection *)ptr;
-
- if (hash_entry->type == bfd_link_hash_defined
- || hash_entry->type == bfd_link_hash_defweak)
- {
- if (sec == hash_entry->u.def.section) {
- print_section ("");
- fprintf (config.map_file, " ");
- print_section ("");
- fprintf (config.map_file, " ");
- print_address (hash_entry->u.def.value + outside_section_address (sec));
- fprintf (config.map_file, " %s", hash_entry->root.string);
- print_nl ();
- }
+ struct bfd_link_hash_entry *hash_entry;
+ PTR ptr;
+{
+ asection *sec = (asection *) 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;
}
+/* Print information about an input section to the map file. */
+
static void
print_input_section (in)
lang_input_section_type * in;
if (size != 0)
{
- print_section ("");
- fprintf (config.map_file, " ");
- print_section (i->name);
- fprintf (config.map_file, " ");
- if (i->output_section)
+ print_space ();
+
+ minfo ("%s", i->name);
+
+ if (i->output_section != NULL)
{
- print_address (i->output_section->vma + i->output_offset);
- fprintf (config.map_file, " ");
- print_size (i->_raw_size);
- fprintf (config.map_file, " ");
- print_size(i->_cooked_size);
- fprintf (config.map_file, " ");
- print_alignment (i->alignment_power);
- fprintf (config.map_file, " ");
- if (in->ifile)
- {
+ int len;
- bfd *abfd = in->ifile->the_bfd;
+ 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 (in->ifile->just_syms_flag == true)
- {
- fprintf (config.map_file, "symbols only ");
- }
+ minfo ("0x%V %W %B\n",
+ i->output_section->vma + i->output_offset, size,
+ i->owner);
- fprintf (config.map_file, " %s ", abfd->xvec->name);
- if (abfd->my_archive != (bfd *) NULL)
- {
- fprintf (config.map_file, "[%s]%s", abfd->my_archive->filename,
- abfd->filename);
- }
- else
+ if (i->_cooked_size != 0 && i->_cooked_size != i->_raw_size)
+ {
+ len = SECTION_NAME_MAP_LENGTH + 3;
+#ifdef BFD64
+ len += 16;
+#else
+ len += 8;
+#endif
+ while (len > 0)
{
- fprintf (config.map_file, "%s", abfd->filename);
+ print_space ();
+ --len;
}
- fprintf (config.map_file, "(overhead %d bytes)", (int) bfd_alloc_size (abfd));
- print_nl ();
- /* Print all the symbols */
- bfd_link_hash_traverse (link_info.hash, print_one_symbol, (PTR) i);
- }
- else
- {
- print_nl ();
+ minfo ("%W (size before relaxing)\n", i->_raw_size);
}
+ bfd_link_hash_traverse (link_info.hash, print_one_symbol, (PTR) i);
- print_dot = outside_section_address (i) + size;
- }
- else
- {
- fprintf (config.map_file, "No output section allocated\n");
+ print_dot = i->output_section->vma + i->output_offset + size;
}
}
}
print_fill_statement (fill)
lang_fill_statement_type * fill;
{
- fprintf (config.map_file, "FILL mask ");
- print_fill (fill->fill);
+ fprintf (config.map_file, " FILL mask 0x%x\n", fill->fill);
}
static void
print_data_statement (data)
lang_data_statement_type * data;
{
-/* bfd_vma value; */
- print_section ("");
- print_space ();
- print_section ("");
- print_space ();
-/* ASSERT(print_dot == data->output_vma);*/
+ int i;
+ bfd_vma addr;
+ bfd_size_type size;
+ const char *name;
+
+ 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;
- /* data->output_section may be NULL if called from gdb. */
- if (data->output_section)
- print_address (data->output_vma + data->output_section->vma);
- else
- print_address (data->output_vma);
- print_space ();
- print_address (data->value);
- print_space ();
switch (data->type)
{
+ default:
+ abort ();
case BYTE:
- fprintf (config.map_file, "BYTE ");
- print_dot += BYTE_SIZE;
+ size = BYTE_SIZE;
+ name = "BYTE";
break;
case SHORT:
- fprintf (config.map_file, "SHORT ");
- print_dot += SHORT_SIZE;
+ size = SHORT_SIZE;
+ name = "SHORT";
break;
case LONG:
- fprintf (config.map_file, "LONG ");
- print_dot += LONG_SIZE;
+ size = LONG_SIZE;
+ name = "LONG";
break;
case QUAD:
- fprintf (config.map_file, "QUAD ");
- print_dot += QUAD_SIZE;
+ 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);
}
- exp_print_tree (data->exp);
+ print_nl ();
+
+ print_dot = addr + size;
+}
+
+/* Print an address statement. These are generated by options like
+ -Ttext. */
- fprintf (config.map_file, "\n");
+static void
+print_address_statement (address)
+ lang_address_statement_type *address;
+{
+ minfo ("Address of section %s set to ", address->section_name);
+ exp_print_tree (address->address);
+ print_nl ();
}
/* Print a reloc statement. */
print_reloc_statement (reloc)
lang_reloc_statement_type *reloc;
{
- print_section ("");
- print_space ();
- print_section ("");
- print_space ();
+ int i;
+ bfd_vma addr;
+ bfd_size_type size;
-/* ASSERT(print_dot == data->output_vma);*/
+ for (i = 0; i < SECTION_NAME_MAP_LENGTH; i++)
+ print_space ();
- /* reloc->output_section may be NULL if called from gdb. */
- if (reloc->output_section)
- print_address (reloc->output_vma + reloc->output_section->vma);
- else
- print_address (reloc->output_vma);
- print_space ();
- print_address (reloc->addend_value);
- print_space ();
+ addr = reloc->output_vma;
+ if (reloc->output_section != NULL)
+ addr += reloc->output_section->vma;
- fprintf (config.map_file, "RELOC %s ", reloc->howto->name);
+ size = bfd_get_reloc_size (reloc->howto);
- print_dot += 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);
- fprintf (config.map_file, "\n");
+ print_nl ();
+
+ print_dot = addr + size;
}
static void
print_padding_statement (s)
- lang_padding_statement_type * s;
+ lang_padding_statement_type *s;
{
- print_section ("");
- print_space ();
- print_section ("*fill*");
- print_space ();
- /* s->output_section may be NULL if called from gdb. */
- if (s->output_section)
- print_address (s->output_offset + s->output_section->vma);
- else
- print_address (s->output_offset);
- print_space ();
- print_size (s->size);
- print_space ();
- print_fill (s->fill);
+ int len;
+ bfd_vma addr;
+
+ 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, s->size);
+
+ if (s->fill != 0)
+ minfo (" %u", s->fill);
+
print_nl ();
- print_dot = s->output_offset + s->size;
- /* s->output_section may be NULL if called from gdb. */
- if (s->output_section)
- print_dot += s->output_section->vma;
+ print_dot = addr + s->size;
}
static void
lang_wild_statement_type * w;
lang_output_section_statement_type * os;
{
- fprintf (config.map_file, " from ");
- if (w->filename != (char *) NULL)
- {
- fprintf (config.map_file, "%s", w->filename);
- }
+ print_space ();
+
+ if (w->filename != NULL)
+ minfo ("%s", w->filename);
else
- {
- fprintf (config.map_file, "*");
- }
- if (w->section_name != (char *) NULL)
- {
- fprintf (config.map_file, "(%s)", w->section_name);
- }
+ minfo ("*");
+
+ if (w->section_name != NULL)
+ minfo ("(%s)", w->section_name);
else
- {
- fprintf (config.map_file, "(*)");
- }
+ minfo ("(*)");
+
print_nl ();
- print_statement_list (w->children.head, os);
+ print_statement_list (w->children.head, os);
}
/* Print a group statement. */
static void
print_statement_list (s, os)
- lang_statement_union_type * s;
- lang_output_section_statement_type * os;
+ lang_statement_union_type *s;
+ lang_output_section_statement_type *os;
{
- while (s)
+ while (s != NULL)
{
print_statement (s, os);
s = s->next;
static void
print_statement (s, os)
- lang_statement_union_type * s;
- lang_output_section_statement_type * os;
+ 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:
- fprintf (config.map_file, "constructors:\n");
- print_statement_list (constructor_list.head, os);
+ if (constructor_list.head != NULL)
+ {
+ minfo (" CONSTRUCTORS\n");
+ print_statement_list (constructor_list.head, os);
+ }
break;
case lang_wild_statement_enum:
print_wild_statement (&s->wild_statement, os);
break;
- default:
- fprintf (config.map_file, "Fail with %d\n", s->header.type);
- FAIL ();
- break;
case lang_address_statement_enum:
- fprintf (config.map_file, "address\n");
+ print_address_statement (&s->address_statement);
break;
case lang_object_symbols_statement_enum:
- fprintf (config.map_file, "object symbols\n");
+ minfo (" CREATE_OBJECT_SYMBOLS\n");
break;
case lang_fill_statement_enum:
print_fill_statement (&s->fill_statement);
print_output_section_statement (&s->output_section_statement);
break;
case lang_assignment_statement_enum:
- print_assignment (&s->assignment_statement,
- os);
+ 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:
- fprintf (config.map_file, "OUTPUT(%s %s)\n",
- s->output_statement.name,
- output_target ? output_target : "");
+ 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);
}
}
-
static void
print_statements ()
{
- print_statement_list (statement_list.head,
- abs_output_section);
-
+ print_statement_list (statement_list.head, abs_output_section);
}
/* Print the first N statements in statement list S to STDERR.
print_statement_list (s, abs_output_section);
else
{
- while (--n >= 0)
+ while (s && --n >= 0)
{
print_statement (s, abs_output_section);
s = s->next;
if (os->children.head == NULL
|| os->children.head->next != NULL
|| os->children.head->header.type != lang_input_section_enum)
- einfo ("%P%X: Internal error on COFF shared library section %s",
+ einfo ("%P%X: Internal error on COFF shared library section %s\n",
os->name);
input = os->children.head->input_section.section;
}
dot = os->region->current;
if (os->section_alignment == -1)
- dot = align_power (dot, os->bfd_section->alignment_power);
+ {
+ 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
{
einfo ("%F%S: non constant address expression for section %s\n",
os->name);
}
- dot = r.value;
+ dot = r.value + r.section->bfd_section->vma;
}
/* The section starts here */
/* First, align to what the section needs */
output_section_statement->bfd_section;
switch (s->data_statement.type)
- {
- case QUAD:
- size = QUAD_SIZE;
- break;
- case LONG:
- size = LONG_SIZE;
- break;
- case SHORT:
- size = SHORT_SIZE;
- break;
- case BYTE:
- size = BYTE_SIZE;
- break;
+ {
+ 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;
+ }
- }
dot += size;
output_section_statement->bfd_section->_raw_size += size;
/* The output section gets contents, and then we inspect for
switch (s->data_statement.type)
{
case QUAD:
+ case SQUAD:
dot += QUAD_SIZE;
break;
case LONG:
bfd_printable_name (input_bfd), input_bfd,
bfd_printable_name (output_bfd));
- else
- bfd_merge_private_bfd_data (input_bfd, output_bfd);
+ else if (! bfd_merge_private_bfd_data (input_bfd, output_bfd))
+ {
+ einfo ("%E%X: failed to merge target specific data of file %B\n", input_bfd);
+ }
}
}
/* Increase the size of the section. */
section->_raw_size += size;
- /* Make sure the section is allocated in memory. */
+ /* Make sure the section is allocated in memory, and make sure that
+ it is no longer a common section. */
section->flags |= SEC_ALLOC;
+ section->flags &= ~ SEC_IS_COMMON;
if (config.map_file != NULL)
{
s->output_section = bfd_abs_section_ptr;
s->output_offset = s->vma;
}
- else if (file->common_section == s)
+ else if (strcmp (s->name, "COMMON") == 0)
{
- /* This is a lonely common section which must
- have come from an archive. We attatch to the
- section with the wildcard */
+ /* This is a lonely common section which must have
+ come from an archive. We attach to the section
+ with the wildcard. */
if (! link_info.relocateable
- && ! command_line.force_common_definition)
+ || command_line.force_common_definition)
{
- if (default_common_section ==
- (lang_output_section_statement_type *) NULL)
+ if (default_common_section == NULL)
{
+#if 0
+ /* This message happens when using the
+ svr3.ifile linker script, so I have
+ disabled it. */
info_msg ("%P: no [COMMON] command, defaulting to .bss\n");
-
+#endif
default_common_section =
lang_output_section_statement_lookup (".bss");
each backend which might set the SEC_LINK_ONCE flag. If we do
this, we should probably handle SEC_EXCLUDE in the same way. */
- bfd_map_over_sections (entry->the_bfd, section_already_linked, (PTR) NULL);
+ bfd_map_over_sections (entry->the_bfd, section_already_linked, (PTR) entry);
}
void
return 0;
}
+
void
lang_enter_output_section_statement (output_section_statement_name,
- address_exp, flags, block_value,
+ address_exp, sectype, block_value,
align, subalign, ebase)
const char *output_section_statement_name;
etree_type * address_exp;
- int flags;
+ enum section_type sectype;
bfd_vma block_value;
etree_type *align;
etree_type *subalign;
os->addr_tree =
address_exp;
}
- os->flags = flags;
- if (flags & SEC_NEVER_LOAD)
- os->loadable = 0;
+ os->sectype = sectype;
+ if (sectype != noload_section)
+ os->flags = SEC_NO_FLAGS;
else
- os->loadable = 1;
+ os->flags = SEC_NEVER_LOAD;
os->block_value = block_value ? block_value : 1;
stat_ptr = &os->children;
ldemul_after_open ();
+ /* Make sure that we're not mixing architectures. We call this
+ after all the input files have been opened, but before we do any
+ other processing, so that any operations merge_private_bfd_data
+ does on the output file will be known during the rest of the
+ link. */
+ lang_check ();
+
/* Build all sets based on the information gathered from the input
files. */
ldctor_build_sets ();
/* Size up the common data */
lang_common ();
- /* Run through the contours of the script and attatch input sections
+ /* Run through the contours of the script and attach input sections
to the correct output sections
*/
map_input_to_output_sections (statement_list.head, (char *) NULL,
(lang_output_section_statement_type *) NULL);
- /* Find any sections not attatched explicitly and handle them */
+ /* Find any sections not attached explicitly and handle them */
lang_place_orphans ();
ldemul_before_allocation ();
abs_output_section,
(fill_type) 0, (bfd_vma) 0);
- /* Make sure that we're not mixing architectures */
-
- lang_check ();
-
/* Final stuffs */
ldemul_finish ();
}
void
-lang_leave_output_section_statement (fill, memspec)
+lang_leave_output_section_statement (fill, memspec, phdrs)
bfd_vma fill;
- CONST char *memspec;
+ const char *memspec;
+ struct lang_output_section_phdr_list *phdrs;
{
current_section->fill = fill;
current_section->region = lang_memory_region_lookup (memspec);
+ current_section->phdrs = phdrs;
stat_ptr = &statement_list;
}
*pp = n;
}
-/* Record that a section should be placed in a phdr. */
-
-void
-lang_section_in_phdr (name)
- const char *name;
-{
- struct lang_output_section_phdr_list *n;
-
- n = ((struct lang_output_section_phdr_list *)
- stat_alloc (sizeof (struct lang_output_section_phdr_list)));
- n->name = name;
- n->used = false;
- n->next = current_section->phdrs;
- current_section->phdrs = n;
-}
-
/* Record the program header information in the output BFD. FIXME: We
should not be calling an ELF specific function here. */
lang_statement_union_type *u;
alc = 10;
- secs = xmalloc (alc * sizeof (asection *));
+ secs = (asection **) xmalloc (alc * sizeof (asection *));
last = NULL;
for (l = lang_phdr_list; l != NULL; l = l->next)
{
last = pl;
else
{
- if (! os->loadable
+ if (os->sectype == noload_section
|| os->bfd_section == NULL
|| (os->bfd_section->flags & SEC_ALLOC) == 0)
continue;
if (c >= alc)
{
alc *= 2;
- secs = xrealloc (secs, alc * sizeof (asection *));
+ secs = ((asection **)
+ xrealloc (secs, alc * sizeof (asection *)));
}
secs[c] = os->bfd_section;
++c;
u->output_section_statement.name, pl->name);
}
}
+
+/* Record a list of sections which may not be cross referenced. */
+
+void
+lang_add_nocrossref (l)
+ struct lang_nocrossref *l;
+{
+ struct lang_nocrossrefs *n;
+
+ n = (struct lang_nocrossrefs *) xmalloc (sizeof *n);
+ n->next = nocrossref_list;
+ n->list = l;
+ nocrossref_list = n;
+
+ /* Set notice_all so that we get informed about all symbols. */
+ link_info.notice_all = true;
+}
+\f
+/* Overlay handling. We handle overlays with some static variables. */
+
+/* The overlay virtual address. */
+static etree_type *overlay_vma;
+
+/* The overlay load address. */
+static etree_type *overlay_lma;
+
+/* Whether nocrossrefs is set for this overlay. */
+static int overlay_nocrossrefs;
+
+/* An expression for the maximum section size seen so far. */
+static etree_type *overlay_max;
+
+/* A list of all the sections in this overlay. */
+
+struct overlay_list
+{
+ struct overlay_list *next;
+ lang_output_section_statement_type *os;
+};
+
+static struct overlay_list *overlay_list;
+
+/* Start handling an overlay. */
+
+void
+lang_enter_overlay (vma_expr, lma_expr, nocrossrefs)
+ etree_type *vma_expr;
+ etree_type *lma_expr;
+ int nocrossrefs;
+{
+ /* The grammar should prevent nested overlays from occurring. */
+ ASSERT (overlay_vma == NULL
+ && overlay_lma == NULL
+ && overlay_list == NULL
+ && overlay_max == NULL);
+
+ overlay_vma = vma_expr;
+ overlay_lma = lma_expr;
+ overlay_nocrossrefs = nocrossrefs;
+}
+
+/* Start a section in an overlay. We handle this by calling
+ lang_enter_output_section_statement with the correct VMA and LMA. */
+
+void
+lang_enter_overlay_section (name)
+ const char *name;
+{
+ struct overlay_list *n;
+ etree_type *size;
+
+ lang_enter_output_section_statement (name, overlay_vma, normal_section,
+ 0, 0, 0, overlay_lma);
+
+ /* If this is the first section, then base the VMA and LMA of future
+ sections on this one. This will work correctly even if `.' is
+ used in the addresses. */
+ if (overlay_list == NULL)
+ {
+ overlay_vma = exp_nameop (ADDR, name);
+ overlay_lma = exp_nameop (LOADADDR, name);
+ }
+
+ /* Remember the section. */
+ n = (struct overlay_list *) xmalloc (sizeof *n);
+ n->os = current_section;
+ n->next = overlay_list;
+ overlay_list = n;
+
+ size = exp_nameop (SIZEOF, name);
+
+ /* Adjust the LMA for the next section. */
+ overlay_lma = exp_binop ('+', overlay_lma, size);
+
+ /* Arrange to work out the maximum section end address. */
+ if (overlay_max == NULL)
+ overlay_max = size;
+ else
+ overlay_max = exp_binop (MAX, overlay_max, size);
+}
+
+/* Finish a section in an overlay. There isn't any special to do
+ here. */
+
+void
+lang_leave_overlay_section (fill, phdrs)
+ bfd_vma fill;
+ struct 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*", phdrs);
+
+ /* Define the magic symbols. */
+
+ 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);
+}
+
+/* Finish an overlay. If there are any overlay wide settings, this
+ looks through all the sections in the overlay and sets them. */
+
+void
+lang_leave_overlay (fill, memspec, phdrs)
+ bfd_vma fill;
+ const char *memspec;
+ struct lang_output_section_phdr_list *phdrs;
+{
+ lang_memory_region_type *region;
+ struct overlay_list *l;
+ struct lang_nocrossref *nocrossref;
+
+ if (memspec == NULL)
+ region = NULL;
+ else
+ region = lang_memory_region_lookup (memspec);
+
+ nocrossref = NULL;
+
+ l = overlay_list;
+ while (l != NULL)
+ {
+ struct overlay_list *next;
+
+ if (fill != 0 && l->os->fill == 0)
+ l->os->fill = fill;
+ if (region != NULL && l->os->region == NULL)
+ l->os->region = region;
+ if (phdrs != NULL && l->os->phdrs == NULL)
+ l->os->phdrs = phdrs;
+
+ if (overlay_nocrossrefs)
+ {
+ struct lang_nocrossref *nc;
+
+ nc = (struct lang_nocrossref *) 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);
+
+ /* Update . for the end of the overlay. */
+ lang_add_assignment (exp_assop ('=', ".",
+ exp_binop ('+', overlay_vma, overlay_max)));
+
+ overlay_vma = NULL;
+ overlay_lma = NULL;
+ overlay_nocrossrefs = 0;
+ overlay_list = NULL;
+ overlay_max = NULL;
+}
+\f
+/* Version handling. This is only useful for ELF. */
+
+/* This global variable holds the version tree that we build. */
+
+struct bfd_elf_version_tree *lang_elf_version_info;
+
+/* This is called for each variable name or match expression. */
+
+struct bfd_elf_version_expr *
+lang_new_vers_regex (orig, new)
+ struct bfd_elf_version_expr *orig;
+ const char *new;
+{
+ struct bfd_elf_version_expr *ret;
+
+ ret = (struct bfd_elf_version_expr *) xmalloc (sizeof *ret);
+ ret->next = orig;
+ ret->match = new;
+ return ret;
+}
+
+/* This is called for each set of variable names and match
+ expressions. */
+
+struct bfd_elf_version_tree *
+lang_new_vers_node (globals, locals)
+ struct bfd_elf_version_expr *globals;
+ struct bfd_elf_version_expr *locals;
+{
+ struct bfd_elf_version_tree *ret;
+
+ ret = (struct bfd_elf_version_tree *) xmalloc (sizeof *ret);
+ ret->next = NULL;
+ ret->name = NULL;
+ ret->vernum = 0;
+ ret->globals = globals;
+ ret->locals = locals;
+ ret->deps = NULL;
+ ret->name_indx = (unsigned int) -1;
+ ret->used = 0;
+ return ret;
+}
+
+/* This static variable keeps track of version indices. */
+
+static int version_index;
+
+/* This is called when we know the name and dependencies of the
+ version. */
+
+void
+lang_register_vers_node (name, version, deps)
+ 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;
+
+ /* Make sure this node has a unique name. */
+ 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);
+
+ /* Check the global and local match names, and make sure there
+ aren't any duplicates. */
+
+ for (e1 = version->globals; e1 != NULL; e1 = e1->next)
+ {
+ for (t = lang_elf_version_info; t != NULL; t = t->next)
+ {
+ struct bfd_elf_version_expr *e2;
+
+ for (e2 = t->locals; e2 != NULL; e2 = e2->next)
+ if (strcmp (e1->match, e2->match) == 0)
+ einfo ("%X%P: duplicate expression `%s' in version information\n",
+ e1->match);
+ }
+ }
+
+ for (e1 = version->locals; e1 != NULL; e1 = e1->next)
+ {
+ for (t = lang_elf_version_info; t != NULL; t = t->next)
+ {
+ struct bfd_elf_version_expr *e2;
+
+ for (e2 = t->globals; e2 != NULL; e2 = e2->next)
+ if (strcmp (e1->match, e2->match) == 0)
+ einfo ("%X%P: duplicate expression `%s' in version information\n",
+ e1->match);
+ }
+ }
+
+ version->deps = deps;
+ version->name = name;
+ ++version_index;
+ version->vernum = version_index;
+
+ for (pp = &lang_elf_version_info; *pp != NULL; pp = &(*pp)->next)
+ ;
+ *pp = version;
+}
+
+/* This is called when we see a version dependency. */
+
+struct bfd_elf_version_deps *
+lang_add_vers_depend (list, name)
+ struct bfd_elf_version_deps *list;
+ const char *name;
+{
+ struct bfd_elf_version_deps *ret;
+ struct bfd_elf_version_tree *t;
+
+ ret = (struct bfd_elf_version_deps *) 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;
+}