Added new files for linker support, and removed old seclet files.
authorIan Lance Taylor <ian@airs.com>
Thu, 30 Dec 1993 20:03:34 +0000 (20:03 +0000)
committerIan Lance Taylor <ian@airs.com>
Thu, 30 Dec 1993 20:03:34 +0000 (20:03 +0000)
bfd/.Sanitize
bfd/genlink.h [new file with mode: 0644]
bfd/hash.c [new file with mode: 0644]
bfd/linker.c [new file with mode: 0644]
bfd/seclet.c [deleted file]
bfd/seclet.h [deleted file]

index b2b21611a94496e786dc55a6dc31af5a45ccdcaf..0e844a7fc543139232e305c072c611fdecdcf758 100644 (file)
@@ -121,6 +121,8 @@ elfcode.h
 filemode.c
 format.c
 gen-aout.c
+genlink.h
+hash.c
 host-aout.c
 hosts
 hp300bsd.c
@@ -148,6 +150,7 @@ libhppa.h
 libieee.h
 libnlm.h
 liboasys.h
+linker.c
 lynx-core.c
 m68klynx.c
 mipsbsd.c
@@ -171,8 +174,6 @@ ptrace-core.c
 reloc.c
 reloc16.c
 rs6000-core.c
-seclet.c
-seclet.h
 section.c
 som.c
 som.h
diff --git a/bfd/genlink.h b/bfd/genlink.h
new file mode 100644 (file)
index 0000000..fed0849
--- /dev/null
@@ -0,0 +1,103 @@
+/* genlink.h -- interface to the BFD generic linker
+   Copyright 1993 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#ifndef GENLINK_H
+#define GENLINK_H
+
+/* This header file is internal to BFD.  It describes the internal
+   structures and functions used by the BFD generic linker, in case
+   any of the more specific linkers want to use or call them.  Note
+   that some functions, such as _bfd_generic_link_hash_table_create,
+   are declared in libbfd.h, because they are expected to be widely
+   used.  The functions and structures in this file will probably only
+   be used by a few files besides linker.c itself.  In fact, this file
+   is not particularly complete; I have only put in the interfaces I
+   actually needed.  */
+
+/* The generic linker uses a hash table which is a derived class of
+   the standard linker hash table, just as the other backend specific
+   linkers do.  Do not confuse the generic linker hash table with the
+   standard BFD linker hash table it is built upon.  The generic
+   linker hash table is onl referred to in this file.  */
+
+/* Generic linker hash table entries.  */
+
+struct generic_link_hash_entry
+{
+  struct bfd_link_hash_entry root;
+  /* Symbol from input BFD.  */
+  asymbol *sym;
+};
+
+/* Generic linker hash table.  */
+
+struct generic_link_hash_table
+{
+  struct bfd_link_hash_table root;
+};
+
+/* Look up an entry in an generic link hash table.  */
+
+#define _bfd_generic_link_hash_lookup(table, string, create, copy, follow) \
+  ((struct generic_link_hash_entry *) \
+   bfd_link_hash_lookup (&(table)->root, (string), (create), (copy), (follow)))
+
+/* Traverse an generic link hash table.  */
+
+#define _bfd_generic_link_hash_traverse(table, func, info)             \
+  (bfd_link_hash_traverse                                              \
+   (&(table)->root,                                                    \
+    (boolean (*) PARAMS ((struct bfd_link_hash_entry *, PTR))) (func), \
+    (info)))
+
+/* Get the generic link hash table from the info structure.  This is
+   just a cast.  */
+
+#define _bfd_generic_hash_table(p) \
+  ((struct generic_link_hash_table *) ((p)->hash))
+
+/* Add the symbols of input_bfd to the symbols being built for
+   output_bfd.  */
+extern boolean _bfd_generic_link_output_symbols
+  PARAMS ((bfd *output_bfd, bfd *input_bfd, struct bfd_link_info *,
+          size_t *psymalloc));
+
+/* This structure is used to pass information to
+   _bfd_generic_link_write_global_symbol, which may be called via
+   _bfd_generic_link_hash_traverse.  */
+
+struct generic_write_global_symbol_info
+{
+  bfd *output_bfd;
+  size_t *psymalloc;
+};
+
+/* Write out a single global symbol.  This is expected to be called
+   via _bfd_generic_link_hash_traverse.  The second argument must
+   actually be a struct generic_write_global_symbol_info *.  */
+extern boolean _bfd_generic_link_write_global_symbol
+  PARAMS ((struct generic_link_hash_entry *, PTR));
+
+/* Handle a bfd_indirect_link_order.  */
+extern boolean _bfd_generic_indirect_link_order
+  PARAMS ((bfd *, struct bfd_link_info *, asection *,
+          struct bfd_link_order *));
+
+#endif
diff --git a/bfd/hash.c b/bfd/hash.c
new file mode 100644 (file)
index 0000000..991b2c3
--- /dev/null
@@ -0,0 +1,181 @@
+/* hash.c -- hash table routines for BFD
+   Copyright 1993 Free Software Foundation, Inc.
+   Written by Steve Chamberlain <sac@cygnus.com>
+
+This file is part of GLD, the Gnu Linker.
+
+GLD is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GLD is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+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, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+#include "obstack.h"
+
+/* Obstack allocation and deallocation routines.  */
+#define obstack_chunk_alloc bfd_xmalloc_by_size_t
+#define obstack_chunk_free free
+
+/* The default number of entries to use when creating a hash table.  */
+#define DEFAULT_SIZE (4051)
+
+/* Create a new hash table, given a number of entries.  */
+
+boolean
+bfd_hash_table_init_n (table, newfunc, size)
+     struct bfd_hash_table *table;
+     struct bfd_hash_entry *(*newfunc) PARAMS ((struct bfd_hash_entry *,
+                                               struct bfd_hash_table *,
+                                               const char *));
+     unsigned int size;
+{
+  unsigned int alloc;
+
+  alloc = size * sizeof (struct bfd_hash_entry *);
+  obstack_begin (&table->memory, alloc);
+  table->table = ((struct bfd_hash_entry **)
+                 obstack_alloc (&table->memory, alloc));
+  memset ((PTR) table->table, 0, alloc);
+  table->size = size;
+  table->newfunc = newfunc;
+  return true;
+}
+
+/* Create a new hash table with the default number of entries.  */
+
+boolean
+bfd_hash_table_init (table, newfunc)
+     struct bfd_hash_table *table;
+     struct bfd_hash_entry *(*newfunc) PARAMS ((struct bfd_hash_entry *,
+                                               struct bfd_hash_table *,
+                                               const char *));
+{
+  return bfd_hash_table_init_n (table, newfunc, DEFAULT_SIZE);
+}
+
+/* Free a hash table.  */
+
+void
+bfd_hash_table_free (table)
+     struct bfd_hash_table *table;
+{
+  obstack_free (&table->memory, (PTR) NULL);
+}
+
+/* Look up a string in a hash table.  */
+
+struct bfd_hash_entry *
+bfd_hash_lookup (table, string, create, copy)
+     struct bfd_hash_table *table;
+     const char *string;
+     boolean create;
+     boolean copy;
+{
+  register const unsigned char *s;
+  register unsigned long hash;
+  register unsigned int c;
+  struct bfd_hash_entry *hashp;
+  unsigned int len;
+  unsigned int index;
+  
+  hash = 0;
+  len = 0;
+  s = (const unsigned char *) string;
+  while ((c = *s++) != '\0')
+    {
+      hash += c + (c << 17);
+      hash ^= hash >> 2;
+      ++len;
+    }
+  hash += len + (len << 17);
+  hash ^= hash >> 2;
+
+  index = hash % table->size;
+  for (hashp = table->table[index];
+       hashp != (struct bfd_hash_entry *) NULL;
+       hashp = hashp->next)
+    {
+      if (hashp->hash == hash
+         && strcmp (hashp->string, string) == 0)
+       return hashp;
+    }
+
+  if (! create)
+    return (struct bfd_hash_entry *) NULL;
+
+  hashp = (*table->newfunc) ((struct bfd_hash_entry *) NULL, table, string);
+  if (hashp == (struct bfd_hash_entry *) NULL)
+    return (struct bfd_hash_entry *) NULL;
+  if (copy)
+    {
+      char *new;
+
+      new = (char *) obstack_alloc (&table->memory, len + 1);
+      strcpy (new, string);
+      string = new;
+    }
+  hashp->string = string;
+  hashp->hash = hash;
+  hashp->next = table->table[index];
+  table->table[index] = hashp;
+
+  return hashp;
+}
+
+/* Base method for creating a new hash table entry.  */
+
+/*ARGSUSED*/
+struct bfd_hash_entry *
+bfd_hash_newfunc (entry, table, string)
+     struct bfd_hash_entry *entry;
+     struct bfd_hash_table *table;
+     const char *string;
+{
+  if (entry == (struct bfd_hash_entry *) NULL)
+    entry = ((struct bfd_hash_entry *)
+            bfd_hash_allocate (table, sizeof (struct bfd_hash_entry)));
+  return entry;
+}
+
+/* Allocate space in a hash table.  */
+
+PTR
+bfd_hash_allocate (table, size)
+     struct bfd_hash_table *table;
+     size_t size;
+{
+  return obstack_alloc (&table->memory, size);
+}
+
+/* Traverse a hash table.  */
+
+void
+bfd_hash_traverse (table, func, info)
+     struct bfd_hash_table *table;
+     boolean (*func) PARAMS ((struct bfd_hash_entry *, PTR));
+     PTR info;
+{
+  unsigned int i;
+
+  for (i = 0; i < table->size; i++)
+    {
+      struct bfd_hash_entry *p;
+
+      for (p = table->table[i]; p != NULL; p = p->next)
+       {
+         if (! (*func) (p, info))
+           return;
+       }
+    }
+}
diff --git a/bfd/linker.c b/bfd/linker.c
new file mode 100644 (file)
index 0000000..0e8c9f4
--- /dev/null
@@ -0,0 +1,1484 @@
+/* linker.c -- BFD linker routines
+   Copyright 1993 Free Software Foundation, Inc.
+   Written by Steve Chamberlain and Ian Lance Taylor, Cygnus Support
+
+This file is part of BFD
+
+GLD is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GLD is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+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, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+#include "bfdlink.h"
+#include "genlink.h"
+
+static struct bfd_hash_entry *generic_link_hash_newfunc
+  PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *,
+          const char *));
+static boolean generic_link_add_object_symbols
+  PARAMS ((bfd *, struct bfd_link_info *));
+static boolean generic_link_check_archive_element
+  PARAMS ((bfd *, struct bfd_link_info *, boolean *pneeded));
+static boolean generic_link_add_symbol_list
+  PARAMS ((bfd *, struct bfd_link_info *, bfd_size_type count, asymbol **));
+static boolean generic_add_output_symbol
+  PARAMS ((bfd *, size_t *psymalloc, asymbol *));
+static boolean default_fill_link_order
+  PARAMS ((bfd *, struct bfd_link_info *, asection *,
+          struct bfd_link_order *));
+
+/* The link hash table structure is defined in bfdlink.h.  It provides
+   a base hash table which the backend specific hash tables are built
+   upon.  */
+
+/* Routine to create an entry in the link hash table.  */
+
+struct bfd_hash_entry *
+_bfd_link_hash_newfunc (entry, table, string)
+     struct bfd_hash_entry *entry;
+     struct bfd_hash_table *table;
+     const char *string;
+{
+  struct bfd_link_hash_entry *ret = (struct bfd_link_hash_entry *) entry;
+
+  /* Allocate the structure if it has not already been allocated by a
+     subclass.  */
+  if (ret == (struct bfd_link_hash_entry *) NULL)
+    ret = ((struct bfd_link_hash_entry *)
+          bfd_hash_allocate (table, sizeof (struct bfd_link_hash_entry)));
+
+  /* Call the allocation method of the superclass.  */
+  ret = ((struct bfd_link_hash_entry *)
+        bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string));
+
+  /* Initialize the local fields.  */
+  ret->type = bfd_link_hash_new;
+  ret->written = false;
+  ret->next = NULL;
+
+  return (struct bfd_hash_entry *) ret;
+}
+
+/* Initialize a link hash table.  The BFD argument is the one
+   responsible for creating this table.  */
+
+boolean
+_bfd_link_hash_table_init (table, abfd, newfunc)
+     struct bfd_link_hash_table *table;
+     bfd *abfd;
+     struct bfd_hash_entry *(*newfunc) PARAMS ((struct bfd_hash_entry *,
+                                               struct bfd_hash_table *,
+                                               const char *));
+{
+  table->creator = abfd->xvec;
+  table->undefs = NULL;
+  table->undefs_tail = NULL;
+  return bfd_hash_table_init (&table->table, newfunc);
+}
+
+/* Look up a symbol in a link hash table.  If follow is true, we
+   follow bfd_link_hash_indirect and bfd_link_hash_warning links to
+   the real symbol.  */
+
+struct bfd_link_hash_entry *
+bfd_link_hash_lookup (table, string, create, copy, follow)
+     struct bfd_link_hash_table *table;
+     const char *string;
+     boolean create;
+     boolean copy;
+     boolean follow;
+{
+  struct bfd_link_hash_entry *ret;
+
+  ret = ((struct bfd_link_hash_entry *)
+        bfd_hash_lookup (&table->table, string, create, copy));
+
+  if (follow && ret != (struct bfd_link_hash_entry *) NULL)
+    {
+      while (ret->type == bfd_link_hash_indirect
+            || ret->type == bfd_link_hash_warning)
+       ret = ret->u.i.link;
+    }
+
+  return ret;
+}
+
+/* Traverse a generic link hash table.  The only reason this is not a
+   macro is to do better type checking.  This code presumes that an
+   argument passed as a struct bfd_hash_entry * may be cause as a
+   struct bfd_link_hash_entry * with no explicit cast required on the
+   call.  */
+
+void 
+bfd_link_hash_traverse (table, func, info)
+     struct bfd_link_hash_table *table;
+     boolean (*func) PARAMS ((struct bfd_link_hash_entry *, PTR));
+     PTR info;
+{
+  bfd_hash_traverse (&table->table,
+                    ((boolean (*) PARAMS ((struct bfd_hash_entry *, PTR)))
+                     func),
+                    info);
+}
+
+/* Add a symbol to the linker hash table undefs list.  */
+
+INLINE void
+bfd_link_add_undef (table, h)
+     struct bfd_link_hash_table *table;
+     struct bfd_link_hash_entry *h;
+{
+  BFD_ASSERT (h->next == NULL);
+  if (table->undefs_tail != (struct bfd_link_hash_entry *) NULL)
+    table->undefs_tail->next = h;
+  if (table->undefs == (struct bfd_link_hash_entry *) NULL)
+    table->undefs = h;
+  table->undefs_tail = h;
+}
+\f
+/* Routine to create an entry in an generic link hash table.  */
+
+static struct bfd_hash_entry *
+generic_link_hash_newfunc (entry, table, string)
+     struct bfd_hash_entry *entry;
+     struct bfd_hash_table *table;
+     const char *string;
+{
+  struct generic_link_hash_entry *ret =
+    (struct generic_link_hash_entry *) entry;
+
+  /* Allocate the structure if it has not already been allocated by a
+     subclass.  */
+  if (ret == (struct generic_link_hash_entry *) NULL)
+    ret = ((struct generic_link_hash_entry *)
+          bfd_hash_allocate (table, sizeof (struct generic_link_hash_entry)));
+
+  /* Call the allocation method of the superclass.  */
+  ret = ((struct generic_link_hash_entry *)
+        _bfd_link_hash_newfunc ((struct bfd_hash_entry *) ret,
+                                table, string));
+
+  /* Set local fields.  */
+  ret->sym = NULL;
+
+  return (struct bfd_hash_entry *) ret;
+}
+
+/* Create an generic link hash table.  */
+
+struct bfd_link_hash_table *
+_bfd_generic_link_hash_table_create (abfd)
+     bfd *abfd;
+{
+  struct generic_link_hash_table *ret;
+
+  ret = ((struct generic_link_hash_table *)
+        bfd_xmalloc (sizeof (struct generic_link_hash_table)));
+  if (! _bfd_link_hash_table_init (&ret->root, abfd,
+                                  generic_link_hash_newfunc))
+    {
+      free (ret);
+      return (struct bfd_link_hash_table *) NULL;
+    }
+  return &ret->root;
+}
+\f
+/* Generic function to add symbols from an object file to the global
+   hash table.  */
+
+boolean
+_bfd_generic_link_add_symbols (abfd, info)
+     bfd *abfd;
+     struct bfd_link_info *info;
+{
+  boolean ret;
+
+  switch (bfd_get_format (abfd))
+    {
+    case bfd_object:
+      ret = generic_link_add_object_symbols (abfd, info);
+      break;
+    case bfd_archive:
+      ret = _bfd_generic_link_add_archive_symbols
+       (abfd, info, generic_link_check_archive_element);
+      break;
+    default:
+      bfd_error = wrong_format;
+      ret = false;
+    }
+
+  /* If we might be using the C based alloca function, make sure we
+     have dumped the symbol tables we just allocated.  */
+#ifndef __GNUC__
+#ifndef alloca
+  alloca (0);
+#endif
+#endif
+
+  return ret;
+}
+
+/* Add symbols from an object file to the global hash table.  */
+
+static boolean
+generic_link_add_object_symbols (abfd, info)
+     bfd *abfd;
+     struct bfd_link_info *info;
+{
+  size_t symsize;
+  asymbol **symbols;
+  bfd_size_type symbol_count;
+
+  symsize = get_symtab_upper_bound (abfd);
+  symbols = (asymbol **) alloca (symsize);
+  symbol_count = bfd_canonicalize_symtab (abfd, symbols);
+
+  return generic_link_add_symbol_list (abfd, info, symbol_count, symbols);
+}
+\f
+/* We build a hash table of all symbols defined in an archive.  */
+
+/* An archive symbol may be defined by multiple archive elements.
+   This linked list is used to hold the elements.  */
+
+struct archive_list
+{
+  struct archive_list *next;
+  int indx;
+};
+
+/* An entry in an archive hash table.  */
+
+struct archive_hash_entry
+{
+  struct bfd_hash_entry root;
+  /* Where the symbol is defined.  */
+  struct archive_list *defs;
+};
+
+/* An archive hash table itself.  */
+
+struct archive_hash_table
+{
+  struct bfd_hash_table table;
+};
+
+static struct bfd_hash_entry *archive_hash_newfunc
+  PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
+static boolean archive_hash_table_init
+  PARAMS ((struct archive_hash_table *,
+          struct bfd_hash_entry *(*) (struct bfd_hash_entry *,
+                                      struct bfd_hash_table *,
+                                      const char *)));
+
+/* Create a new entry for an archive hash table.  */
+
+static struct bfd_hash_entry *
+archive_hash_newfunc (entry, table, string)
+     struct bfd_hash_entry *entry;
+     struct bfd_hash_table *table;
+     const char *string;
+{
+  struct archive_hash_entry *ret = (struct archive_hash_entry *) entry;
+
+  /* Allocate the structure if it has not already been allocated by a
+     subclass.  */
+  if (ret == (struct archive_hash_entry *) NULL)
+    ret = ((struct archive_hash_entry *)
+          bfd_hash_allocate (table, sizeof (struct archive_hash_entry)));
+
+  /* Call the allocation method of the superclass.  */
+  ret = ((struct archive_hash_entry *)
+        bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string));
+
+  /* Initialize the local fields.  */
+  ret->defs = (struct archive_list *) NULL;
+
+  return (struct bfd_hash_entry *) ret;
+}
+
+/* Initialize an archive hash table.  */
+
+static boolean
+archive_hash_table_init (table, newfunc)
+     struct archive_hash_table *table;
+     struct bfd_hash_entry *(*newfunc) PARAMS ((struct bfd_hash_entry *,
+                                               struct bfd_hash_table *,
+                                               const char *));
+{
+  return bfd_hash_table_init (&table->table, newfunc);
+}
+
+/* Look up an entry in an archive hash table.  */
+
+#define archive_hash_lookup(t, string, create, copy) \
+  ((struct archive_hash_entry *) \
+   bfd_hash_lookup (&(t)->table, (string), (create), (copy)))
+
+/* Free an archive hash table.  */
+
+#define archive_hash_table_free(t) bfd_hash_table_free (&(t)->table)
+
+/* Generic function to add symbols from an archive file to the global
+   hash file.  This function presumes that the archive symbol table
+   has already been read in (this is normally done by the
+   bfd_check_format entry point).  It looks through the undefined and
+   common symbols and searches the archive symbol table for them.  If
+   it finds an entry, it includes the associated object file in the
+   link.
+
+   The old linker looked through the archive symbol table for
+   undefined symbols.  We do it the other way around, looking through
+   undefined symbols for symbols defined in the archive.  The
+   advantage of the newer scheme is that we only have to look through
+   the list of undefined symbols once, whereas the old method had to
+   re-search the symbol table each time a new object file was added.
+
+   The CHECKFN argument is used to see if an object file should be
+   included.  CHECKFN should set *PNEEDED to true if the object file
+   should be included, and must also call the bfd_link_info
+   add_archive_element callback function and handle adding the symbols
+   to the global hash table.  CHECKFN should only return false if some
+   sort of error occurs.
+
+   For some formats, such as a.out, it is possible to look through an
+   object file but not actually include it in the link.  The
+   archive_pass field in a BFD is used to avoid checking the symbols
+   of an object files too many times.  When an object is included in
+   the link, archive_pass is set to -1.  If an object is scanned but
+   not included, archive_pass is set to the pass number.  The pass
+   number is incremented each time a new object file is included.  The
+   pass number is used because when a new object file is included it
+   may create new undefined symbols which cause a previously examined
+   object file to be included.  */
+
+boolean
+_bfd_generic_link_add_archive_symbols (abfd, info, checkfn)
+     bfd *abfd;
+     struct bfd_link_info *info;
+     boolean (*checkfn) PARAMS ((bfd *, struct bfd_link_info *,
+                                boolean *pneeded));
+{
+  carsym *arsyms;
+  carsym *arsym_end;
+  register carsym *arsym;
+  int pass;
+  struct archive_hash_table arsym_hash;
+  int indx;
+  struct bfd_link_hash_entry **pundef;
+
+  if (! bfd_has_map (abfd))
+    {
+      bfd_error = no_symbols;
+      return false;
+    }
+
+  arsyms = bfd_ardata (abfd)->symdefs;
+  arsym_end = arsyms + bfd_ardata (abfd)->symdef_count;
+
+  /* In order to quickly determine whether an symbol is defined in
+     this archive, we build a hash table of the symbols.  */
+  if (! archive_hash_table_init (&arsym_hash, archive_hash_newfunc))
+    return false;
+  for (arsym = arsyms, indx = 0; arsym < arsym_end; arsym++, indx++)
+    {
+      struct archive_hash_entry *arh;
+      struct archive_list *l;
+
+      arh = archive_hash_lookup (&arsym_hash, arsym->name, true, false);
+      if (arh == (struct archive_hash_entry *) NULL)
+       return false;
+      l = (struct archive_list *) alloca (sizeof (struct archive_list));
+      l->next = arh->defs;
+      arh->defs = l;
+      l->indx = indx;
+    }
+
+  pass = 1;
+
+  /* New undefined symbols are added to the end of the list, so we
+     only need to look through it once.  */
+  pundef = &info->hash->undefs;
+  while (*pundef != (struct bfd_link_hash_entry *) NULL)
+    {
+      struct bfd_link_hash_entry *h;
+      struct archive_hash_entry *arh;
+      struct archive_list *l;
+
+      h = *pundef;
+
+      /* When a symbol is defined, it is not necessarily removed from
+        the list.  */
+      if (h->type != bfd_link_hash_undefined
+         && h->type != bfd_link_hash_common)
+       {
+         /* Remove this entry from the list, for general cleanliness
+            and because we are going to look through the list again
+            if we search any more libraries.  We can't remove the
+            entry if it is the tail, because that would lose any
+            entries we add to the list later on.  */
+         if (*pundef != info->hash->undefs_tail)
+           *pundef = (*pundef)->next;
+         else
+           pundef = &(*pundef)->next;
+         continue;
+       }
+
+      /* Look for this symbol in the archive symbol map.  */
+      arh = archive_hash_lookup (&arsym_hash, h->root.string, false, false);
+      if (arh == (struct archive_hash_entry *) NULL)
+       {
+         pundef = &(*pundef)->next;
+         continue;
+       }
+
+      /* Look at all the objects which define this symbol.  */
+      for (l = arh->defs; l != (struct archive_list *) NULL; l = l->next)
+       {
+         bfd *element;
+         boolean needed;
+
+         /* If the symbol has gotten defined along the way, quit.  */
+         if (h->type != bfd_link_hash_undefined
+             && h->type != bfd_link_hash_common)
+           break;
+
+         element = bfd_get_elt_at_index (abfd, l->indx);
+         if (element == (bfd *) NULL)
+           return false;
+
+         /* If we've already included this element, or if we've
+            already checked it on this pass, continue.  */
+         if (element->archive_pass == -1
+             || element->archive_pass == pass)
+           continue;
+
+         /* If we can't figure this element out, just ignore it.  */
+         if (! bfd_check_format (element, bfd_object))
+           {
+             element->archive_pass = -1;
+             continue;
+           }
+
+         /* CHECKFN will see if this element should be included, and
+            go ahead and include it if appropriate.  */
+         if (! (*checkfn) (element, info, &needed))
+           return false;
+
+         if (! needed)
+           element->archive_pass = pass;
+         else
+           {
+             element->archive_pass = -1;
+
+             /* Increment the pass count to show that we may need to
+                recheck object files which were already checked.  */
+             ++pass;
+           }
+       }
+
+      pundef = &(*pundef)->next;
+    }
+
+  archive_hash_table_free (&arsym_hash);
+
+  return true;
+}
+\f
+/* See if we should include an archive element.  */
+
+static boolean
+generic_link_check_archive_element (abfd, info, pneeded)
+     bfd *abfd;
+     struct bfd_link_info *info;
+     boolean *pneeded;
+{
+  size_t symsize;
+  asymbol **symbols;
+  bfd_size_type symbol_count;
+  asymbol **pp, **ppend;
+
+  *pneeded = false;
+
+  symsize = get_symtab_upper_bound (abfd);
+  symbols = (asymbol **) alloca (symsize);
+  symbol_count = bfd_canonicalize_symtab (abfd, symbols);
+
+  pp = symbols;
+  ppend = symbols + symbol_count;
+  for (; pp < ppend; pp++)
+    {
+      asymbol *p;
+      struct bfd_link_hash_entry *h;
+
+      p = *pp;
+
+      /* We are only interested in globally visible symbols.  */
+      if (! bfd_is_com_section (p->section)
+         && (p->flags & (BSF_GLOBAL | BSF_INDIRECT | BSF_WEAK)) == 0)
+       continue;
+
+      /* We are only interested if we know something about this
+        symbol, and it is undefined or common.  An undefined weak
+        symbol (type bfd_link_hash_weak) is not considered to be a
+        reference when pulling files out of an archive.  See the SVR4
+        ABI, p. 4-27.  */
+      h = bfd_link_hash_lookup (info->hash, bfd_asymbol_name (p), false,
+                               false, true);
+      if (h == (struct bfd_link_hash_entry *) NULL
+         || (h->type != bfd_link_hash_undefined
+             && h->type != bfd_link_hash_common))
+       continue;
+
+      /* P is a symbol we are looking for.  */
+
+      if (! bfd_is_com_section (p->section))
+       {
+         /* This object file defines this symbol, so pull it in.  */
+         if (! (*info->callbacks->add_archive_element) (info, abfd,
+                                                        bfd_asymbol_name (p)))
+           return false;
+         if (! generic_link_add_symbol_list (abfd, info, symbol_count,
+                                             symbols))
+           return false;
+         *pneeded = true;
+         return true;
+       }
+
+      /* P is a common symbol.  */
+
+      if (h->type == bfd_link_hash_undefined)
+       {
+         bfd *symbfd;
+
+         symbfd = h->u.undef.abfd;
+         if (symbfd == (bfd *) NULL)
+           {
+             /* This symbol was created as undefined from outside
+                BFD.  We assume that we should link in the object
+                file.  This is for the -u option in the linker.  */
+             if (! (*info->callbacks->add_archive_element)
+                 (info, abfd, bfd_asymbol_name (p)))
+               return false;
+             *pneeded = true;
+             return true;
+           }
+
+         /* Turn the symbol into a common symbol but do not link in
+            the object file.  This is how a.out works.  Object
+            formats that require different semantics must implement
+            this function differently.  This symbol is already on the
+            undefs list.  */
+         h->type = bfd_link_hash_common;
+         h->u.c.size = bfd_asymbol_value (p);
+         h->u.c.section = bfd_make_section_old_way (symbfd,
+                                                    "COMMON");
+       }
+      else
+       {
+         /* Adjust the size of the common symbol if necessary.  This
+            is how a.out works.  Object formats that require
+            different semantics must implement this function
+            differently.  */
+         if (bfd_asymbol_value (p) > h->u.c.size)
+           h->u.c.size = bfd_asymbol_value (p);
+       }
+    }
+
+  /* This archive element is not needed.  */
+  return true;
+}
+
+/* Add the symbol from an object file to the global hash table.  */
+
+static boolean
+generic_link_add_symbol_list (abfd, info, symbol_count, symbols)
+     bfd *abfd;
+     struct bfd_link_info *info;
+     bfd_size_type symbol_count;
+     asymbol **symbols;
+{
+  asymbol **pp, **ppend;
+
+  pp = symbols;
+  ppend = symbols + symbol_count;
+  for (; pp < ppend; pp++)
+    {
+      asymbol *p;
+
+      p = *pp;
+
+      if ((p->flags & (BSF_INDIRECT
+                      | BSF_WARNING
+                      | BSF_GLOBAL
+                      | BSF_CONSTRUCTOR
+                      | BSF_WEAK)) != 0
+         || bfd_get_section (p) == &bfd_und_section
+         || bfd_is_com_section (bfd_get_section (p))
+         || bfd_get_section (p) == &bfd_ind_section)
+       {
+         const char *name;
+         const char *string;
+         struct generic_link_hash_entry *h;
+
+         name = bfd_asymbol_name (p);
+         if ((p->flags & BSF_INDIRECT) != 0
+             || p->section == &bfd_ind_section)
+           string = bfd_asymbol_name ((asymbol *) p->value);
+         else if ((p->flags & BSF_WARNING) != 0)
+           {
+             /* The name of P is actually the warning string, and the
+                value is actually a pointer to the symbol to warn
+                about.  */
+             string = name;
+             name = bfd_asymbol_name ((asymbol *) p->value);
+           }
+         else
+           string = NULL;
+         if (! (_bfd_generic_link_add_one_symbol
+                (info, abfd, name, p->flags, bfd_get_section (p),
+                 p->value, string, false,
+                 (struct bfd_link_hash_entry **) &h)))
+           return false;
+
+         /* Save the BFD symbol so that we don't lose any backend
+            specific information that may be attached to it.  We only
+            want this one if it gives more information than the
+            existing one; we don't want to replace a defined symbol
+            with an undefined one.  This routine may be called with a
+            hash table other than the generic hash table, so we only
+            do this if we are certain that the hash table is a
+            generic one.  */
+         if (info->hash->creator == abfd->xvec)
+           {
+             if (h->sym == (asymbol *) NULL
+                 || (bfd_get_section (p) != &bfd_und_section
+                     && (! bfd_is_com_section (bfd_get_section (p))
+                         || (bfd_get_section (h->sym) == &bfd_und_section))))
+               h->sym = p;
+           }
+       }
+    }
+
+  return true;
+}
+\f
+/* We use a state table to deal with adding symbols from an object
+   file.  The first index into the state table describes the symbol
+   from the object file.  The second index into the state table is the
+   type of the symbol in the hash table.  */
+
+/* The symbol from the object file is turned into one of these row
+   values.  */
+
+enum link_row
+{
+  UNDEF_ROW,           /* Undefined.  */
+  UNDEFW_ROW,          /* Weak undefined.  */
+  DEF_ROW,             /* Defined.  */
+  DEFW_ROW,            /* Weak defined.  */
+  COMMON_ROW,          /* Common.  */
+  INDR_ROW,            /* Indirect.  */
+  WARN_ROW,            /* Warning.  */
+  SET_ROW              /* Member of set.  */
+};
+
+/* The actions to take in the state table.  */
+
+enum link_action
+{
+  FAIL,                /* Abort. */
+  UND,         /* Mark symbol undefined.  */
+  WEAK,                /* Mark symbol weak undefined.  */
+  DEF,         /* Mark symbol defined.  */
+  COM,         /* Mark symbol common.  */
+  CREF,                /* Possibly warn about common reference to defined symbol.  */
+  CDEF,                /* Define existing common symbol.  */
+  NOACT,       /* No action.  */
+  BIG,         /* Mark symbol common using largest size.  */
+  MDEF,                /* Multiple definition error.  */
+  IND,         /* Make indirect symbol.  */
+  SET,         /* Add value to set.  */
+  MWARN,       /* Make warning symbol.  */
+  WARN,                /* Issue warning.  */
+  CYCLE,       /* Repeat with symbol pointed to.  */
+  WARNC                /* Issue warning and then CYCLE.  */
+};
+
+/* The state table itself.  The first index is a link_row and the
+   second index is a bfd_link_hash_type.  */
+
+static const enum link_action link_action[8][7] =
+{
+  /* current\prev    new    undef  weak   def    com    indr   warn  */
+  /* UNDEF_ROW         */  {UND,   NOACT, NOACT, NOACT, NOACT, CYCLE, WARNC },
+  /* UNDEFW_ROW        */  {WEAK,  WEAK,  NOACT, NOACT, NOACT, CYCLE, WARNC },
+  /* DEF_ROW   */  {DEF,   DEF,   DEF,   MDEF,  CDEF,  CYCLE, CYCLE },
+  /* DEFW_ROW  */  {DEF,   DEF,   DEF,   NOACT, NOACT, CYCLE, CYCLE },
+  /* COMMON_ROW        */  {COM,   COM,   COM,   CREF,  BIG,   CYCLE, WARNC },
+  /* INDR_ROW  */  {IND,   IND,   IND,   MDEF,  MDEF,  MDEF,  WARNC },
+  /* WARN_ROW   */  {MWARN, WARN,  WARN,  MWARN, MWARN, MWARN, NOACT },
+  /* SET_ROW   */  {SET,   SET,   SET,   SET,   SET,   CYCLE, WARNC }
+};
+
+/* Add a symbol to the global hash table.
+   ABFD is the BFD the symbol comes from.
+   NAME is the name of the symbol.
+   FLAGS is the BSF_* bits associated with the symbol.
+   SECTION is the section in which the symbol is defined; this may be
+     bfd_und_section or bfd_com_section.
+   VALUE is the value of the symbol, relative to the section.
+   STRING is used for either an indirect symbol, in which case it is
+     the name of the symbol to indirect to, or a warning symbol, in
+     which case it is the warning string.
+   COPY is true if NAME or STRING must be copied into locally
+     allocated memory if they need to be saved.
+   HASHP, if not NULL, is a place to store the created hash table
+     entry.  */
+
+boolean
+_bfd_generic_link_add_one_symbol (info, abfd, name, flags, section, value,
+                                 string, copy, hashp)
+     struct bfd_link_info *info;
+     bfd *abfd;
+     const char *name;
+     flagword flags;
+     asection *section;
+     bfd_vma value;
+     const char *string;
+     boolean copy;
+     struct bfd_link_hash_entry **hashp;
+{
+  enum link_row row;
+  struct bfd_link_hash_entry *h;
+  boolean cycle;
+
+  if (section == &bfd_ind_section
+      || (flags & BSF_INDIRECT) != 0)
+    row = INDR_ROW;
+  else if ((flags & BSF_WARNING) != 0)
+    row = WARN_ROW;
+  else if ((flags & BSF_CONSTRUCTOR) != 0)
+    row = SET_ROW;
+  else if (section == &bfd_und_section)
+    {
+      if ((flags & BSF_WEAK) != 0)
+       row = UNDEFW_ROW;
+      else
+       row = UNDEF_ROW;
+    }
+  else if ((flags & BSF_WEAK) != 0)
+    row = DEFW_ROW;
+  else if (bfd_is_com_section (section))
+    row = COMMON_ROW;
+  else
+    row = DEF_ROW;
+
+  h = bfd_link_hash_lookup (info->hash, name, true, copy, false);
+  if (h == (struct bfd_link_hash_entry *) NULL)
+    {
+      if (hashp != (struct bfd_link_hash_entry **) NULL)
+       *hashp = NULL;
+      return false;
+    }
+
+  if (info->notice_hash != (struct bfd_hash_table *) NULL
+      && (bfd_hash_lookup (info->notice_hash, name, false, false)
+         != (struct bfd_hash_entry *) NULL))
+    {
+      if (! (*info->callbacks->notice) (info, name, abfd, section, value))
+       return false;
+    }
+
+  if (hashp != (struct bfd_link_hash_entry **) NULL)
+    *hashp = h;
+
+  do
+    {
+      enum link_action action;
+
+      cycle = false;
+      action = link_action[(int) row][(int) h->type];
+      switch (action)
+       {
+       case FAIL:
+         abort ();
+       case UND:
+         h->type = bfd_link_hash_undefined;
+         h->u.undef.abfd = abfd;
+         bfd_link_add_undef (info->hash, h);
+         break;
+       case WEAK:
+         h->type = bfd_link_hash_weak;
+         h->u.undef.abfd = abfd;
+         break;
+       case CDEF:
+         BFD_ASSERT (h->type == bfd_link_hash_common);
+         if (! ((*info->callbacks->multiple_common)
+                (info, name,
+                 h->u.c.section->owner, bfd_link_hash_common, h->u.c.size,
+                 abfd, bfd_link_hash_defined, (bfd_vma) 0)))
+           return false;
+         /* Fall through.  */
+       case DEF:
+         h->type = bfd_link_hash_defined;
+         h->u.def.section = section;
+         h->u.def.value = value;
+         break;
+       case COM:
+         if (h->type == bfd_link_hash_new)
+           bfd_link_add_undef (info->hash, h);
+         h->type = bfd_link_hash_common;
+         h->u.c.size = value;
+         if (section == &bfd_com_section)
+           h->u.c.section = bfd_make_section_old_way (abfd, "COMMON");
+         else if (section->owner != abfd)
+           h->u.c.section = bfd_make_section_old_way (abfd, section->name);
+         else
+           h->u.c.section = section;
+         break;
+       case NOACT:
+         break;
+       case BIG:
+         BFD_ASSERT (h->type == bfd_link_hash_common);
+         if (! ((*info->callbacks->multiple_common)
+                (info, name,
+                 h->u.c.section->owner, bfd_link_hash_common, h->u.c.size,
+                 abfd, bfd_link_hash_common, value)))
+           return false;
+         if (value > h->u.c.size)
+           h->u.c.size = value;
+         if (h->u.c.section == (asection *) NULL)
+           h->u.c.section = bfd_make_section_old_way (abfd, "COMMON");
+         break;
+       case CREF:
+         BFD_ASSERT (h->type == bfd_link_hash_defined);
+         if (! ((*info->callbacks->multiple_common)
+                (info, name,
+                 h->u.def.section->owner, bfd_link_hash_defined, (bfd_vma) 0,
+                 abfd, bfd_link_hash_common, value)))
+           return false;
+         break;
+       case MDEF:
+         {
+           asection *msec;
+           bfd_vma mval;
+
+           switch (h->type)
+             {
+             case bfd_link_hash_defined:
+               msec = h->u.def.section;
+               mval = h->u.def.value;
+               break;
+             case bfd_link_hash_common:
+               msec = &bfd_com_section;
+               mval = h->u.c.size;
+               break;
+             case bfd_link_hash_indirect:
+               msec = &bfd_ind_section;
+               mval = 0;
+               break;
+             default:
+               abort ();
+             }
+             
+           if (! ((*info->callbacks->multiple_definition)
+                  (info, name, msec->owner, msec, mval, abfd, section,
+                   value)))
+             return false;
+         }
+         break;
+       case IND:
+         {
+           struct bfd_link_hash_entry *inh;
+
+           /* STRING is the name of the symbol we want to indirect
+              to.  */
+           inh = bfd_link_hash_lookup (info->hash, string, true, copy,
+                                       false);
+           if (inh == (struct bfd_link_hash_entry *) NULL)
+             return false;
+           if (inh->type == bfd_link_hash_new)
+             {
+               inh->type = bfd_link_hash_undefined;
+               inh->u.undef.abfd = abfd;
+               bfd_link_add_undef (info->hash, inh);
+             }
+           h->type = bfd_link_hash_indirect;
+           h->u.i.link = inh;
+         }
+         break;
+       case SET:
+         if (! (*info->callbacks->add_to_set) (info, h, abfd, section, value))
+           return false;
+         break;
+       case WARN:
+       case WARNC:
+         if (h->u.i.warning != NULL)
+           {
+             if (! (*info->callbacks->warning) (info, h->u.i.warning))
+               return false;
+             /* Only issue a warning once.  */
+             h->u.i.warning = NULL;
+           }
+         if (action == WARN)
+           break;
+         /* Fall through.  */
+       case CYCLE:
+         h = h->u.i.link;
+         cycle = true;
+         break;
+       case MWARN:
+         {
+           struct bfd_link_hash_entry *sub;
+
+           /* STRING is the warning to give.  */
+           sub = ((struct bfd_link_hash_entry *)
+                  bfd_hash_allocate (&info->hash->table,
+                                     sizeof (struct bfd_link_hash_entry)));
+           *sub = *h;
+           h->type = bfd_link_hash_warning;
+           h->u.i.link = sub;
+           if (! copy)
+             h->u.i.warning = string;
+           else
+             {
+               char *w;
+
+               w = bfd_hash_allocate (&info->hash->table,
+                                      strlen (string) + 1);
+               strcpy (w, string);
+               h->u.i.warning = w;
+             }
+         }
+         break;
+       }
+    }
+  while (cycle);
+
+  return true;
+}
+\f
+/* Generic final link routine.  */
+
+boolean
+_bfd_generic_final_link (abfd, info)
+     bfd *abfd;
+     struct bfd_link_info *info;
+{
+  bfd *sub;
+  asection *o;
+  struct bfd_link_order *p;
+  size_t outsymalloc;
+  struct generic_write_global_symbol_info wginfo;
+
+  abfd->outsymbols = (asymbol **) NULL;
+  abfd->symcount = 0;
+  outsymalloc = 0;
+
+  /* Build the output symbol table.  This also reads in the symbols
+     for all the input BFDs, keeping them in the outsymbols field.  */
+  for (sub = info->input_bfds; sub != (bfd *) NULL; sub = sub->link_next)
+    if (! _bfd_generic_link_output_symbols (abfd, sub, info, &outsymalloc))
+      return false;
+
+  /* Accumulate the global symbols.  */
+  wginfo.output_bfd = abfd;
+  wginfo.psymalloc = &outsymalloc;
+  _bfd_generic_link_hash_traverse (_bfd_generic_hash_table (info),
+                                  _bfd_generic_link_write_global_symbol,
+                                  (PTR) &wginfo);
+
+  if (info->relocateable)
+    {
+      /* Allocate space for the output relocs for each section.  */
+      for (o = abfd->sections;
+          o != (asection *) NULL;
+          o = o->next)
+       {
+         o->reloc_count = 0;
+         for (p = o->link_order_head;
+              p != (struct bfd_link_order *) NULL;
+              p = p->next)
+           {
+             if (p->type == bfd_indirect_link_order)
+               {
+                 asection *input_section;
+                 bfd *input_bfd;
+                 bfd_size_type relsize;
+                 arelent **relocs;
+                 bfd_size_type reloc_count;
+
+                 input_section = p->u.indirect.section;
+                 input_bfd = input_section->owner;
+                 relsize = bfd_get_reloc_upper_bound (input_bfd,
+                                                      input_section);
+                 relocs = (arelent **) bfd_xmalloc (relsize);
+                 reloc_count =
+                   bfd_canonicalize_reloc (input_bfd, input_section,
+                                           relocs,
+                                           bfd_get_outsymbols (input_bfd));
+                 BFD_ASSERT (reloc_count == input_section->reloc_count);
+                 o->reloc_count += reloc_count;
+                 free (relocs);
+               }
+           }
+         if (o->reloc_count > 0)
+           {
+             o->orelocation = ((arelent **)
+                               bfd_alloc (abfd,
+                                          (o->reloc_count
+                                           * sizeof (arelent *))));
+             /* Reset the count so that it can be used as an index
+                when putting in the output relocs.  */
+             o->reloc_count = 0;
+           }
+       }
+    }
+
+  /* Handle all the link order information for the sections.  */
+  for (o = abfd->sections;
+       o != (asection *) NULL;
+       o = o->next)
+    {
+      for (p = o->link_order_head;
+          p != (struct bfd_link_order *) NULL;
+          p = p->next)
+       {
+         switch (p->type)
+           {
+           case bfd_indirect_link_order:
+             if (! _bfd_generic_indirect_link_order (abfd, info, o, p))
+               return false;
+             break;
+           default:
+             if (! _bfd_default_link_order (abfd, info, o, p))
+               return false;
+             break;
+           }
+       }
+    }
+
+  return true;
+}
+
+/* Add an output symbol to the output BFD.  */
+
+static boolean
+generic_add_output_symbol (output_bfd, psymalloc, sym)
+     bfd *output_bfd;
+     size_t *psymalloc;
+     asymbol *sym;
+{
+  if (output_bfd->symcount >= *psymalloc)
+    {
+      asymbol **newsyms;
+
+      if (*psymalloc == 0)
+       *psymalloc = 124;
+      else
+       *psymalloc *= 2;
+      if (output_bfd->outsymbols == (asymbol **) NULL)
+       newsyms = (asymbol **) malloc (*psymalloc * sizeof (asymbol *));
+      else
+       newsyms = (asymbol **) realloc (output_bfd->outsymbols,
+                                       *psymalloc * sizeof (asymbol *));
+      if (newsyms == (asymbol **) NULL)
+       {
+         bfd_error = no_memory;
+         return false;
+       }
+      output_bfd->outsymbols = newsyms;
+    }
+
+  output_bfd->outsymbols[output_bfd->symcount] = sym;
+  ++output_bfd->symcount;
+
+  return true;
+}
+
+/* Handle the symbols for an input BFD.  */
+
+boolean
+_bfd_generic_link_output_symbols (output_bfd, input_bfd, info, psymalloc)
+     bfd *output_bfd;
+     bfd *input_bfd;
+     struct bfd_link_info *info;
+     size_t *psymalloc;
+{
+  size_t symsize;
+  asymbol **sym_ptr;
+  asymbol **sym_end;
+
+  symsize = get_symtab_upper_bound (input_bfd);
+  input_bfd->outsymbols = (asymbol **) bfd_alloc (input_bfd, symsize);
+  input_bfd->symcount = bfd_canonicalize_symtab (input_bfd,
+                                                input_bfd->outsymbols);
+
+  /* Create a filename symbol if we are supposed to.  */
+  if (info->create_object_symbols_section != (asection *) NULL)
+    {
+      asection *sec;
+
+      for (sec = input_bfd->sections;
+          sec != (asection *) NULL;
+          sec = sec->next)
+       {
+         if (sec->output_section == info->create_object_symbols_section)
+           {
+             asymbol *newsym;
+
+             newsym = bfd_make_empty_symbol (input_bfd);
+             newsym->name = input_bfd->filename;
+             newsym->value = 0;
+             newsym->flags = BSF_LOCAL | BSF_FILE;
+             newsym->section = sec;
+
+             if (! generic_add_output_symbol (output_bfd, psymalloc,
+                                              newsym))
+               return false;
+
+             break;
+           }
+       }
+    }
+
+  /* Adjust the values of the globally visible symbols, and write out
+     local symbols.  */
+  sym_ptr = bfd_get_outsymbols (input_bfd);
+  sym_end = sym_ptr + bfd_get_symcount (input_bfd);
+  for (; sym_ptr < sym_end; sym_ptr++)
+    {
+      asymbol *sym;
+      struct generic_link_hash_entry *h;
+      boolean output;
+
+      h = (struct generic_link_hash_entry *) NULL;
+      sym = *sym_ptr;
+      if ((sym->flags & (BSF_INDIRECT
+                        | BSF_WARNING
+                        | BSF_GLOBAL
+                        | BSF_CONSTRUCTOR
+                        | BSF_WEAK)) != 0
+         || bfd_get_section (sym) == &bfd_und_section
+         || bfd_is_com_section (bfd_get_section (sym))
+         || bfd_get_section (sym) == &bfd_ind_section)
+       {
+         h = _bfd_generic_link_hash_lookup (_bfd_generic_hash_table (info),
+                                            bfd_asymbol_name (sym),
+                                            false, false, true);
+         if (h != (struct generic_link_hash_entry *) NULL)
+           {
+             /* Force all references to this symbol to point to
+                the same area in memory.  It is possible that
+                this routine will be called with a hash table
+                other than a generic hash table, so we double
+                check that.  */
+             if (info->hash->creator == input_bfd->xvec)
+               {
+                 if (h->sym != (asymbol *) NULL)
+                   *sym_ptr = sym = h->sym;
+               }
+
+             switch (h->root.type)
+               {
+               default:
+               case bfd_link_hash_new:
+                 abort ();
+               case bfd_link_hash_undefined:
+               case bfd_link_hash_weak:
+                 break;
+               case bfd_link_hash_defined:
+                 sym->value = h->root.u.def.value;
+                 sym->section = h->root.u.def.section;
+                 sym->flags |= BSF_GLOBAL;
+                 break;
+               case bfd_link_hash_common:
+                 sym->value = h->root.u.c.size;
+                 sym->flags |= BSF_GLOBAL;
+                 /* We do not set the section of the symbol to
+                    c.section.  c.section is saved so that we know
+                    where to allocate the symbol if we define it.  In
+                    this case the type is still bfd_link_hash_common,
+                    so we did not define it, so we do not want to use
+                    that section.  */
+                 BFD_ASSERT (bfd_is_com_section (sym->section));
+                 break;
+               }
+           }
+       }
+
+      /* This switch is straight from the old code in
+        write_file_locals in ldsym.c.  */
+      if (info->strip == strip_some
+         && (bfd_hash_lookup (info->keep_hash, bfd_asymbol_name (sym),
+                              false, false)
+             == (struct bfd_hash_entry *) NULL))
+       output = false;
+      else if ((sym->flags & (BSF_GLOBAL | BSF_WEAK)) != 0)
+       {
+         /* If this symbol is marked as occurring now, rather
+            than at the end, output it now.  This is used for
+            COFF C_EXT FCN symbols.  FIXME: There must be a
+            better way.  */
+         if (bfd_asymbol_bfd (sym) == input_bfd
+             && (sym->flags & BSF_NOT_AT_END) != 0)
+           output = true;
+         else
+           output = false;
+       }
+      else if (sym->section == &bfd_ind_section)
+       output = false;
+      else if ((sym->flags & BSF_DEBUGGING) != 0)
+       {
+         if (info->strip == strip_none)
+           output = true;
+         else
+           output = false;
+       }
+      else if (sym->section == &bfd_und_section
+              || bfd_is_com_section (sym->section))
+       output = false;
+      else if ((sym->flags & BSF_LOCAL) != 0)
+       {
+         if ((sym->flags & BSF_WARNING) != 0)
+           output = false;
+         else
+           {
+             switch (info->discard)
+               {
+               default:
+               case discard_all:
+                 output = false;
+                 break;
+               case discard_l:
+                 if (bfd_asymbol_name (sym)[0] == info->lprefix[0]
+                     && (info->lprefix_len == 1
+                         || strncmp (bfd_asymbol_name (sym), info->lprefix,
+                                     info->lprefix_len) == 0))
+                   output = false;
+                 else
+                   output = true;
+                 break;
+               case discard_none:
+                 output = true;
+                 break;
+               }
+           }
+       }
+      else if ((sym->flags & BSF_CONSTRUCTOR))
+       {
+         if (info->strip != strip_all)
+           output = true;
+         else
+           output = false;
+       }
+      else
+       abort ();
+
+      if (output)
+       {
+         if (! generic_add_output_symbol (output_bfd, psymalloc, sym))
+           return false;
+         if (h != (struct generic_link_hash_entry *) NULL)
+           h->root.written = true;
+       }
+    }
+
+  return true;
+}
+
+/* Write out a global symbol, if it hasn't already been written out.
+   This is called for each symbol in the hash table.  */
+
+boolean
+_bfd_generic_link_write_global_symbol (h, data)
+     struct generic_link_hash_entry *h;
+     PTR data;
+{
+  struct generic_write_global_symbol_info *wginfo =
+    (struct generic_write_global_symbol_info *) data;
+  asymbol *sym;
+
+  if (h->root.written)
+    return true;
+
+  if (h->sym != (asymbol *) NULL)
+    {
+      sym = h->sym;
+      BFD_ASSERT (strcmp (bfd_asymbol_name (sym), h->root.root.string) == 0);
+    }
+  else
+    {
+      sym = bfd_make_empty_symbol (wginfo->output_bfd);
+      sym->name = h->root.root.string;
+      sym->flags = 0;
+    }
+
+  switch (h->root.type)
+    {
+    default:
+    case bfd_link_hash_new:
+      abort ();
+    case bfd_link_hash_undefined:
+      sym->section = &bfd_und_section;
+      sym->value = 0;
+      break;
+    case bfd_link_hash_weak:
+      sym->section = &bfd_und_section;
+      sym->value = 0;
+      sym->flags |= BSF_WEAK;
+    case bfd_link_hash_defined:
+      sym->section = h->root.u.def.section;
+      sym->value = h->root.u.def.value;
+      break;
+    case bfd_link_hash_common:
+      sym->value = h->root.u.c.size;
+      /* Do not set the section; see _bfd_generic_link_output_symbols.  */
+      BFD_ASSERT (bfd_is_com_section (sym->section));
+      break;
+    case bfd_link_hash_indirect:
+    case bfd_link_hash_warning:
+      /* FIXME: What should we do here?  */
+      break;
+    }
+
+  sym->flags |= BSF_GLOBAL;
+
+  if (! generic_add_output_symbol (wginfo->output_bfd, wginfo->psymalloc,
+                                  sym))
+    {
+      /* FIXME: No way to return failure.  */
+      abort ();
+    }
+
+  h->root.written = true;
+
+  return true;
+}
+
+/* Handle an indirect section when doing a generic link.  */
+
+boolean
+_bfd_generic_indirect_link_order (output_bfd, info, output_section, link_order)
+     bfd *output_bfd;
+     struct bfd_link_info *info;
+     asection *output_section;
+     struct bfd_link_order *link_order;
+{
+  asection *input_section;
+  bfd *input_bfd;
+  bfd_byte *contents;
+
+  BFD_ASSERT ((output_section->flags & SEC_HAS_CONTENTS) != 0);
+
+  if (link_order->size == 0)
+    return true;
+
+  input_section = link_order->u.indirect.section;
+  input_bfd = input_section->owner;
+
+  BFD_ASSERT (input_section->output_section == output_section);
+  BFD_ASSERT (input_section->output_offset == link_order->offset);
+  BFD_ASSERT (bfd_section_size (input_bfd, input_section) == link_order->size);
+
+  /* Get and relocate the section contents.  */
+  contents = (bfd_byte *) alloca (bfd_section_size (input_bfd, input_section));
+  contents = (bfd_get_relocated_section_contents
+             (output_bfd, info, link_order, contents, info->relocateable,
+              bfd_get_outsymbols (input_bfd)));
+
+  /* Output the section contents.  */
+  if (! bfd_set_section_contents (output_bfd, output_section, contents,
+                                 link_order->offset, link_order->size))
+    return false;
+
+  return true;
+}
+\f
+/* Allocate a new link_order for a section.  */
+
+struct bfd_link_order *
+bfd_new_link_order (abfd, section)
+     bfd *abfd;
+     asection *section;
+{
+  struct bfd_link_order *new;
+
+  new = ((struct bfd_link_order *)
+        bfd_alloc_by_size_t (abfd, sizeof (struct bfd_link_order)));
+
+  new->type = bfd_undefined_link_order;
+  new->offset = 0;
+  new->size = 0;
+  new->next = (struct bfd_link_order *) NULL;
+
+  if (section->link_order_tail != (struct bfd_link_order *) NULL)
+    section->link_order_tail->next = new;
+  else
+    section->link_order_head = new;
+  section->link_order_tail = new;
+
+  return new;
+}
+
+/* Default link order processing routine.  */
+
+boolean
+_bfd_default_link_order (abfd, info, sec, link_order)
+     bfd *abfd;
+     struct bfd_link_info *info;
+     asection *sec;
+     struct bfd_link_order *link_order;
+{
+  switch (link_order->type)
+    {
+    case bfd_undefined_link_order:
+    default:
+      abort ();
+    case bfd_indirect_link_order:
+      abort ();
+    case bfd_fill_link_order:
+      return default_fill_link_order (abfd, info, sec, link_order);
+    }
+}
+
+/* Default routine to handle a bfd_fill_link_order.  */
+
+static boolean
+default_fill_link_order (abfd, info, sec, link_order)
+     bfd *abfd;
+     struct bfd_link_info *info;
+     asection *sec;
+     struct bfd_link_order *link_order;
+{
+  size_t size;
+  char *space;
+  size_t i;
+  int fill;
+
+  BFD_ASSERT ((sec->flags & SEC_HAS_CONTENTS) != 0);
+
+  size = (size_t) link_order->size;
+  space = (char *) alloca (size);
+  fill = link_order->u.fill.value;
+  for (i = 0; i < size; i += 2)
+    space[i] = fill >> 8;
+  for (i = 1; i < size; i += 2)
+    space[i] = fill;
+  return bfd_set_section_contents (abfd, sec, space,
+                                  (file_ptr) link_order->offset,
+                                  link_order->size);
+}
diff --git a/bfd/seclet.c b/bfd/seclet.c
deleted file mode 100644 (file)
index 5a6c37d..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-/* seclet.c
-   Copyright (C) 1992, 1993 Free Software Foundation, Inc.
-   Written by Cygnus Support.
-
-This file is part of BFD, the Binary File Descriptor library.
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
-
-/* This module is part of BFD */
-
-
-/* The intention is that one day, all the code which uses sections
-   will change and use seclets instead - maybe seglet would have been
-   a better name..
-
-   Anyway, a seclet contains enough info to be able to describe an
-   area of output memory in one go.
-
-   The only description so far catered for is that of the
-   <<bfd_indirect_seclet>>, which is a select which points to a
-   <<section>> and the <<asymbols>> associated with the section, so
-   that relocation can be done when needed.
-
-   One day there will be more types - they will at least migrate from
-   the linker's data structures - also there could be extra stuff,
-   like a bss seclet, which descibes a lump of memory as containing
-   zeros compactly, without the horrible SEC_* flag cruft.
-
-
-*/
-
-#include "bfd.h"
-#include "sysdep.h"
-#include "libbfd.h"
-#include "seclet.h"
-#include "coff/internal.h"
-
-/* Create a new seclet and attach it to a section.  */
-
-bfd_seclet_type *
-DEFUN(bfd_new_seclet,(abfd, section),
-      bfd *abfd AND
-      asection *section)
-{
-  bfd_seclet_type *n = (bfd_seclet_type *)bfd_alloc(abfd, sizeof(bfd_seclet_type));
-  if (section->seclets_tail != (bfd_seclet_type *)NULL) {
-      section->seclets_tail->next = n;
-    }
-  else
-  {
-    section->seclets_head = n;
-  }
-  section->seclets_tail = n;
-
-  return n;
-}
-
-/* Given an indirect seclet which points to an input section, relocate
-   the contents of the seclet and put the data in its final
-   destination.  */
-
-static boolean
-DEFUN(rel,(abfd, seclet, output_section, data, relocateable),
-      bfd *abfd AND
-      bfd_seclet_type *seclet AND
-      asection *output_section AND
-      PTR data AND
-      boolean relocateable)
-{
-  if ((output_section->flags & SEC_HAS_CONTENTS) != 0
-      && seclet->size)
-  {
-    data = (PTR) bfd_get_relocated_section_contents(abfd, seclet, data,
-                                                   relocateable);
-    if(bfd_set_section_contents(abfd,
-                               output_section,
-                               data,
-                               seclet->offset,
-                               seclet->size) == false)
-    {
-      return false;
-    }
-  }
-  return true;
-}
-
-/* Put the contents of a seclet in its final destination.  */
-
-static boolean
-DEFUN(seclet_dump_seclet,(abfd, seclet, section, data, relocateable),
-      bfd *abfd AND
-      bfd_seclet_type *seclet AND
-      asection *section AND
-      PTR data AND
-      boolean relocateable)
-{
-  switch (seclet->type) 
-    {
-    case bfd_indirect_seclet:
-      /* The contents of this section come from another one somewhere
-        else */
-      return rel(abfd, seclet, section, data, relocateable);
-
-    case bfd_fill_seclet:
-      /* Fill in the section with us */
-      {
-       char *d = bfd_xmalloc(seclet->size);
-       unsigned int i;
-       for (i =0;  i < seclet->size; i+=2) {
-         d[i] = seclet->u.fill.value >> 8;
-       }
-       for (i = 1; i < seclet->size; i+=2) {
-         d[i] = seclet->u.fill.value ;
-       }
-       /* Don't bother to fill in empty sections */
-       if (!(bfd_get_section_flags(abfd, section) & SEC_HAS_CONTENTS))
-         {
-           return true;
-         }
-       return bfd_set_section_contents(abfd, section, d, seclet->offset,
-                                       seclet->size);
-      }
-
-    default:
-      abort();
-    }
-
-  return true;
-}
-
-/*
-INTERNAL_FUNCTION
-       bfd_generic_seclet_link
-
-SYNOPSIS
-       boolean bfd_generic_seclet_link
-        (bfd *abfd,
-         PTR data,
-         boolean relocateable);
-
-DESCRIPTION
-
-       The generic seclet linking routine.  The caller should have
-       set up seclets for all the output sections.  The DATA argument
-       should point to a memory area large enough to hold the largest
-       section.  This function looks through the seclets and moves
-       the contents into the output sections.  If RELOCATEABLE is
-       true, the orelocation fields of the output sections must
-       already be initialized.
-
-*/
-
-boolean
-DEFUN(bfd_generic_seclet_link,(abfd, data, relocateable),
-      bfd *abfd AND
-      PTR data AND
-      boolean relocateable)
-{
-  asection *o = abfd->sections;
-  while (o != (asection *)NULL) 
-  {
-    bfd_seclet_type *p = o->seclets_head;
-    while (p != (bfd_seclet_type *)NULL) 
-    {
-      if (seclet_dump_seclet(abfd, p, o, data, relocateable) == false)
-       return false;
-      p = p ->next;
-    }
-    o = o->next;
-  }
-
-  return true;
-}
diff --git a/bfd/seclet.h b/bfd/seclet.h
deleted file mode 100644 (file)
index de5fdff..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Definitions of little sections (seclets) for BFD.
-   Copyright 1992 Free Software Foundation, Inc.
-   Hacked by Steve Chamberlain of Cygnus Support.
-
-This file is part of BFD, the Binary File Descriptor library.
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
-
-#ifndef _SECLET_H
-#define _SECLET_H
-
-enum bfd_seclet_enum
-{
-  bfd_indirect_seclet,
-  bfd_fill_seclet
-};
-
-struct bfd_seclet 
-{
-  struct bfd_seclet *next;
-  enum bfd_seclet_enum type;
-  unsigned int offset;  
-  unsigned int size;
-  union 
-  {
-    struct 
-    {
-      asection *section;
-      asymbol **symbols;
-    } indirect;
-    struct {
-      int value;
-    } fill;
-  }
-  u;
-};
-
-typedef struct bfd_seclet bfd_seclet_type;
-
-bfd_seclet_type *
-bfd_new_seclet PARAMS ((bfd *, asection *));
-
-#endif