/* 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 "ldfile.h"
#include "fnmatch.h"
+#include <ctype.h>
+
/* FORWARDS */
static lang_statement_union_type *new_statement PARAMS ((enum statement_enum,
size_t,
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));
lang_output_section_statement_type *output_section_statement,
fill_type fill, bfd_vma dot, boolean relax));
static void lang_finish PARAMS ((void));
+static void ignore_bfd_errors PARAMS ((const char *, ...));
static void lang_check PARAMS ((void));
static void lang_common PARAMS ((void));
static boolean lang_one_common PARAMS ((struct bfd_link_hash_entry *, PTR));
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 */
lang_memory_region_lookup (name)
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;
*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;
}
+lang_memory_region_type *
+lang_memory_default (section)
+ asection *section;
+{
+ lang_memory_region_type *p;
+
+ flagword sec_flags = section->flags;
+
+ /* Override SEC_DATA to mean a writable section. */
+ if (sec_flags & (SEC_ALLOC | SEC_READONLY | SEC_CODE) == SEC_ALLOC)
+ sec_flags |= SEC_DATA;
+
+ for (p = lang_memory_region_list;
+ p != (lang_memory_region_type *) NULL;
+ p = p->next)
+ {
+ if ((p->flags & sec_flags) != 0
+ && (p->not_flags & sec_flags) == 0)
+ {
+ return p;
+ }
+ }
+ return lang_memory_region_lookup ("*default*");
+}
+
lang_output_section_statement_type *
lang_output_section_find (name)
CONST char *CONST name;
return lookup;
}
+static void
+lang_map_flags (flag)
+ 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 ()
{
lang_memory_region_type *m;
minfo ("\nMemory Configuration\n\n");
- fprintf (config.map_file, "%-16s %-18s %-18s\n",
- "Name", "Origin", "Length");
+ fprintf (config.map_file, "%-16s %-18s %-18s %s\n",
+ "Name", "Origin", "Length", "Attributes");
for (m = lang_memory_region_list;
m != (lang_memory_region_type *) NULL;
++len;
}
- minfo ("0x%V\n", m->length);
+ 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");
switch (exp->type.node_code)
{
case ADDR:
+ case LOADADDR:
case SIZEOF:
{
lang_output_section_statement_type *os;
/*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)
explicit actions, like foo.o(.text), bar.o(.text) and
foo.o(.text, .data). */
-/* Return true if the PATTERN argument is a wildcard pattern. */
+/* 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)
for (s = pattern; *s != '\0'; ++s)
if (*s == '?'
- || *s == '\\'
|| *s == '*'
|| *s == '[')
return true;
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.
+ the embedded SEC_NEVER_LOAD section into a fill. */
+
+ 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 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 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. */
- section->output_section->flags |=
- section->flags & (flagword) (~ (SEC_NEVER_LOAD
- | (! link_info.relocateable
- ? SEC_LINK_ONCE | SEC_LINK_DUPLICATES
- : 0)));
+ 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)
{
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;
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 (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;
*/
if (os->region == (lang_memory_region_type *) NULL)
{
- os->region = lang_memory_region_lookup ("*default*");
+ os->region = lang_memory_default (os->bfd_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
{
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:
}
}
+/* This is a small function used when we want to ignore errors from
+ BFD. */
+
+static void
+#ifdef ANSI_PROTOTYPES
+ignore_bfd_errors (const char *s, ...)
+#else
+ignore_bfd_errors (s)
+ const char *s;
+#endif
+{
+ /* Don't do anything. */
+}
+
/* Check that the architecture of all the input files is compatible
with the output file. Also call the backend to let it do any
other checking that is needed. */
compatible = bfd_arch_get_compatible (input_bfd,
output_bfd);
if (compatible == NULL)
- 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));
-
+ {
+ 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
- bfd_merge_private_bfd_data (input_bfd, output_bfd);
+ {
+ bfd_error_handler_type pfn;
+
+ /* If we aren't supposed to warn about mismatched input
+ files, temporarily set the BFD error handler to a
+ function which will do nothing. We still want to call
+ bfd_merge_private_bfd_data, since it may set up
+ information which is needed in the output file. */
+ if (! command_line.warn_mismatch)
+ pfn = bfd_set_error_handler (ignore_bfd_errors);
+ if (! bfd_merge_private_bfd_data (input_bfd, output_bfd))
+ {
+ if (command_line.warn_mismatch)
+ einfo ("%E%X: failed to merge target specific data of file %B\n",
+ input_bfd);
+ }
+ if (! command_line.warn_mismatch)
+ bfd_set_error_handler (pfn);
+ }
}
}
/* 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)
{
void
lang_set_flags (ptr, flags)
- int *ptr;
+ lang_memory_region_type *ptr;
CONST char *flags;
{
- boolean state = false;
+ flagword *ptr_flags = &ptr->flags;
- *ptr = 0;
+ ptr->flags = ptr->not_flags = 0;
while (*flags)
{
- if (*flags == '!')
- {
- state = false;
- flags++;
- }
- else
- state = true;
switch (*flags)
{
- case 'R':
- /* ptr->flag_read = state; */
+ case '!':
+ ptr_flags = (ptr_flags == &ptr->flags) ? &ptr->not_flags : &ptr->flags;
+ break;
+
+ case 'A': case 'a':
+ *ptr_flags |= SEC_ALLOC;
+ break;
+
+ case 'R': case 'r':
+ *ptr_flags |= SEC_READONLY;
break;
- case 'W':
- /* ptr->flag_write = state; */
+
+ case 'W': case 'w':
+ *ptr_flags |= SEC_DATA;
break;
- case 'X':
- /* ptr->flag_executable= state;*/
+
+ case 'X': case 'x':
+ *ptr_flags |= SEC_CODE;
break;
- case 'L':
- case 'I':
- /* ptr->flag_loadable= state;*/
+
+ case 'L': case 'l':
+ case 'I': case 'i':
+ *ptr_flags |= SEC_LOAD;
break;
+
default:
einfo ("%P%F: invalid syntax in flags\n");
break;
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
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)
{
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;
+}