import gdb-1999-06-21 snapshot
[external/binutils.git] / bfd / cofflink.c
index 1e3d33e..cf56869 100644 (file)
@@ -1,5 +1,5 @@
 /* COFF specific linker code.
-   Copyright 1994, 1995 Free Software Foundation, Inc.
+   Copyright 1994, 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Cygnus Support.
 
 This file is part of BFD, the Binary File Descriptor library.
@@ -16,7 +16,7 @@ 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.  */
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 /* This file contains the COFF backend linker code.  */
 
@@ -27,125 +27,23 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "coff/internal.h"
 #include "libcoff.h"
 
-#define STRING_SIZE_SIZE (4)
-
-/* Information we keep for each section in the output file when doing
-   a relocateable link.  */
-
-struct coff_link_section_info
-{
-  /* The relocs to be output.  */
-  struct internal_reloc *relocs;
-  /* For each reloc against a global symbol whose index was not known
-     when the reloc was handled, the global hash table entry.  */
-  struct coff_link_hash_entry **rel_hashes;
-};
-
-/* Information that we pass around while doing the final link step.  */
-
-struct coff_final_link_info
-{
-  /* General link information.  */
-  struct bfd_link_info *info;
-  /* Output BFD.  */
-  bfd *output_bfd;
-  /* Used to indicate failure in traversal routine.  */
-  boolean failed;
-  /* Hash table for long symbol name.  */
-  struct bfd_strtab_hash *strtab;
-  /* When doing a relocateable link, an array of information kept for
-     each output section, indexed by the target_index field.  */
-  struct coff_link_section_info *section_info;
-  /* Symbol index of last C_FILE symbol (-1 if none).  */
-  long last_file_index;
-  /* Contents of last C_FILE symbol.  */
-  struct internal_syment last_file;
-  /* Buffer large enough to hold swapped symbols of any input file.  */
-  struct internal_syment *internal_syms;
-  /* Buffer large enough to hold sections of symbols of any input file.  */
-  asection **sec_ptrs;
-  /* Buffer large enough to hold output indices of symbols of any
-     input file.  */
-  long *sym_indices;
-  /* Buffer large enough to hold output symbols for any input file.  */
-  bfd_byte *outsyms;
-  /* Buffer large enough to hold external line numbers for any input
-     section.  */
-  bfd_byte *linenos;
-  /* Buffer large enough to hold any input section.  */
-  bfd_byte *contents;
-  /* Buffer large enough to hold external relocs of any input section.  */
-  bfd_byte *external_relocs;
-  /* Buffer large enough to hold swapped relocs of any input section.  */
-  struct internal_reloc *internal_relocs;
-
-enum   bfd_link_subsystem  subsystem;
-bfd_link_stack_heap stack_heap_parameters;
-};
-
-static struct bfd_hash_entry *coff_link_hash_newfunc
-  PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
 static boolean coff_link_add_object_symbols
   PARAMS ((bfd *, struct bfd_link_info *));
 static boolean coff_link_check_archive_element
   PARAMS ((bfd *, struct bfd_link_info *, boolean *));
-static boolean coff_link_get_symbols PARAMS ((bfd *));
-static const char *coff_read_string_table PARAMS ((bfd *));
-static boolean coff_link_free_symbols PARAMS ((bfd *));
 static boolean coff_link_check_ar_symbols
   PARAMS ((bfd *, struct bfd_link_info *, boolean *));
 static boolean coff_link_add_symbols PARAMS ((bfd *, struct bfd_link_info *));
-static boolean coff_link_input_bfd
-  PARAMS ((struct coff_final_link_info *, bfd *));
-static boolean coff_write_global_sym
-  PARAMS ((struct coff_link_hash_entry *, PTR));
-static boolean coff_reloc_link_order
-  PARAMS ((bfd *, struct coff_final_link_info *, asection *,
-          struct bfd_link_order *));
-
-
-/* These new data and data types are used to keep track of the .idata$4 and
-   .idata$5 relocations which are put into the .idata section for all of the
-   *.dll input libraries linked in.  This is not a great solution, and may
-   break in the future if MS changes the format of its libraries, but it
-   does work for the collection of mstools libraries we are currently working
-   with.  The main problem is that there are some new majic symbols defined
-   in the libraries which are non-standard coff and simply aren't handled 
-   completely by ld.  What has been included here will help finish up the job.
-     Basically, during the link, .idata$4 and .idata$5 pointers are correctly
-   relocated to the image.  At the very end of the link, the .idata$2
-   information is written.  This data appears at the beginning of the .idata
-   section and a 'set' of information appears for each *.dll passed in.
-   Each set of information consists of 3 addresses, a pointer to the .idata$4
-   start, a pointer to .idata$6 (which has the name of the dll), and a pointer
-   to .idata$5 start.  The idata$4 and 5 information is a list of pointers
-   which appear to point to the name of various functions found within the dll.
-   When invoked, the loader will write over these names with the correct
-   addresses to use for these functions.  
-     Without this 'fix', all information appears correctly except for the
-   addresses of the .idata$4 and 5 starts within the .idata$2 portion of the
-   .idata section.  What we will do is to keep track of the dll's processed
-   and the number of functions needed from each dll.  From this information
-   we can correctly compute the start of the idata$4 and 5 lists for each
-   dll in the idata section */
-static int num_DLLs_done = 0;
-static int num_DLLs      = 0;
-static int all_entries   = 0;
-struct DLL_struct {
-  const char * DLL_name;
-  int          num_entries;
-};
-struct DLL_struct MS_DLL[10];
-static bfd_vma idata_4_prev = 0;
-static bfd_vma idata_5_prev = 0;
-static bfd_vma add_to_val   = 0;
-
-
+static char *dores_com PARAMS ((char *, bfd *, int));
+static char *get_name PARAMS ((char *, char **));
+static int process_embedded_commands
+  PARAMS ((bfd *, struct bfd_link_info *, bfd *));
+static void mark_relocs PARAMS ((struct coff_final_link_info *, bfd *));
 
 /* Create an entry in a COFF linker hash table.  */
 
-static struct bfd_hash_entry *
-coff_link_hash_newfunc (entry, table, string)
+struct bfd_hash_entry *
+_bfd_coff_link_hash_newfunc (entry, table, string)
      struct bfd_hash_entry *entry;
      struct bfd_hash_table *table;
      const char *string;
@@ -158,10 +56,7 @@ coff_link_hash_newfunc (entry, table, string)
     ret = ((struct coff_link_hash_entry *)
           bfd_hash_allocate (table, sizeof (struct coff_link_hash_entry)));
   if (ret == (struct coff_link_hash_entry *) NULL)
-    {
-      bfd_set_error (bfd_error_no_memory);
-      return (struct bfd_hash_entry *) ret;
-    }
+    return (struct bfd_hash_entry *) ret;
 
   /* Call the allocation method of the superclass.  */
   ret = ((struct coff_link_hash_entry *)
@@ -181,6 +76,20 @@ coff_link_hash_newfunc (entry, table, string)
   return (struct bfd_hash_entry *) ret;
 }
 
+/* Initialize a COFF linker hash table.  */
+
+boolean
+_bfd_coff_link_hash_table_init (table, abfd, newfunc)
+     struct coff_link_hash_table *table;
+     bfd *abfd;
+     struct bfd_hash_entry *(*newfunc) PARAMS ((struct bfd_hash_entry *,
+                                               struct bfd_hash_table *,
+                                               const char *));
+{
+  table->stab_info = NULL;
+  return _bfd_link_hash_table_init (&table->root, abfd, newfunc);
+}
+
 /* Create a COFF linker hash table.  */
 
 struct bfd_link_hash_table *
@@ -190,21 +99,50 @@ _bfd_coff_link_hash_table_create (abfd)
   struct coff_link_hash_table *ret;
 
   ret = ((struct coff_link_hash_table *)
-        malloc (sizeof (struct coff_link_hash_table)));
+        bfd_alloc (abfd, sizeof (struct coff_link_hash_table)));
   if (ret == NULL)
+    return NULL;
+  if (! _bfd_coff_link_hash_table_init (ret, abfd,
+                                       _bfd_coff_link_hash_newfunc))
     {
-      bfd_set_error (bfd_error_no_memory);
-      return NULL;
-    }
-  if (! _bfd_link_hash_table_init (&ret->root, abfd,
-                                  coff_link_hash_newfunc))
-    {
-      free (ret);
+      bfd_release (abfd, ret);
       return (struct bfd_link_hash_table *) NULL;
     }
   return &ret->root;
 }
 
+/* Create an entry in a COFF debug merge hash table.  */
+
+struct bfd_hash_entry *
+_bfd_coff_debug_merge_hash_newfunc (entry, table, string)
+     struct bfd_hash_entry *entry;
+     struct bfd_hash_table *table;
+     const char *string;
+{
+  struct coff_debug_merge_hash_entry *ret =
+    (struct coff_debug_merge_hash_entry *) entry;
+
+  /* Allocate the structure if it has not already been allocated by a
+     subclass.  */
+  if (ret == (struct coff_debug_merge_hash_entry *) NULL)
+    ret = ((struct coff_debug_merge_hash_entry *)
+          bfd_hash_allocate (table,
+                             sizeof (struct coff_debug_merge_hash_entry)));
+  if (ret == (struct coff_debug_merge_hash_entry *) NULL)
+    return (struct bfd_hash_entry *) ret;
+
+  /* Call the allocation method of the superclass.  */
+  ret = ((struct coff_debug_merge_hash_entry *)
+        bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string));
+  if (ret != (struct coff_debug_merge_hash_entry *) NULL)
+    {
+      /* Set local fields.  */
+      ret->types = NULL;
+    }
+
+  return (struct bfd_hash_entry *) ret;
+}
+
 /* Given a COFF BFD, add symbols to the global hash table as
    appropriate.  */
 
@@ -233,13 +171,14 @@ coff_link_add_object_symbols (abfd, info)
      bfd *abfd;
      struct bfd_link_info *info;
 {
-  if (! coff_link_get_symbols (abfd))
+  if (! _bfd_coff_get_external_symbols (abfd))
     return false;
   if (! coff_link_add_symbols (abfd, info))
     return false;
+
   if (! info->keep_memory)
     {
-      if (! coff_link_free_symbols (abfd))
+      if (! _bfd_coff_free_symbols (abfd))
        return false;
     }
   return true;
@@ -256,7 +195,7 @@ coff_link_check_archive_element (abfd, info, pneeded)
      struct bfd_link_info *info;
      boolean *pneeded;
 {
-  if (! coff_link_get_symbols (abfd))
+  if (! _bfd_coff_get_external_symbols (abfd))
     return false;
 
   if (! coff_link_check_ar_symbols (abfd, info, pneeded))
@@ -270,127 +209,13 @@ coff_link_check_archive_element (abfd, info, pneeded)
 
   if (! info->keep_memory || ! *pneeded)
     {
-      if (! coff_link_free_symbols (abfd))
+      if (! _bfd_coff_free_symbols (abfd))
        return false;
     }
 
   return true;
 }
 
-/* Read in the external symbols.  */
-
-static boolean
-coff_link_get_symbols (abfd)
-     bfd *abfd;
-{
-  bfd_size_type symesz;
-  size_t size;
-  PTR syms;
-
-  if (obj_coff_external_syms (abfd) != NULL)
-    return true;
-
-  symesz = bfd_coff_symesz (abfd);
-
-  size = obj_raw_syment_count (abfd) * symesz;
-
-  syms = malloc (size);
-  if (syms == NULL && size != 0)
-    {
-      bfd_set_error (bfd_error_no_memory);
-      return false;
-    }
-
-  if (bfd_seek (abfd, obj_sym_filepos (abfd), SEEK_SET) != 0
-      || bfd_read (syms, size, 1, abfd) != size)
-    {
-      if (syms != NULL)
-       free (syms);
-      return false;
-    }
-
-  obj_coff_external_syms (abfd) = syms;
-
-  return true;
-}
-
-/* Read in the external strings.  The strings are not loaded until
-   they are needed.  This is because we have no simple way of
-   detecting a missing string table in an archive.  */
-
-static const char *
-coff_read_string_table (abfd)
-     bfd *abfd;
-{
-  char extstrsize[STRING_SIZE_SIZE];
-  size_t strsize;
-  char *strings;
-
-  if (obj_coff_strings (abfd) != NULL)
-    return obj_coff_strings (abfd);
-
-  if (bfd_seek (abfd,
-               (obj_sym_filepos (abfd)
-                + obj_raw_syment_count (abfd) * bfd_coff_symesz (abfd)),
-               SEEK_SET) != 0)
-    return NULL;
-    
-  if (bfd_read (extstrsize, sizeof extstrsize, 1, abfd) != sizeof extstrsize)
-    {
-      if (bfd_get_error () != bfd_error_file_truncated)
-       return NULL;
-
-      /* There is no string table.  */
-      strsize = STRING_SIZE_SIZE;
-    }
-  else
-    {
-#if STRING_SIZE_SIZE == 4
-      strsize = bfd_h_get_32 (abfd, (bfd_byte *) extstrsize);
-#else
- #error Change bfd_h_get_32
-#endif
-    }
-
-  strings = malloc (strsize);
-  if (strings == NULL)
-    {
-      bfd_set_error (bfd_error_no_memory);
-      return NULL;
-    }
-
-  if (bfd_read (strings + STRING_SIZE_SIZE,
-               strsize - STRING_SIZE_SIZE, 1, abfd)
-      != strsize - STRING_SIZE_SIZE)
-    {
-      free (strings);
-      return NULL;
-    }
-
-  obj_coff_strings (abfd) = strings;
-
-  return strings;
-}
-
-/* Free up the external symbols and strings read from a COFF file.  */
-
-static boolean
-coff_link_free_symbols (abfd)
-     bfd *abfd;
-{
-  if (obj_coff_external_syms (abfd) != NULL)
-    {
-      free (obj_coff_external_syms (abfd));
-      obj_coff_external_syms (abfd) = NULL;
-    }
-  if (obj_coff_strings (abfd) != NULL)
-    {
-      free (obj_coff_strings (abfd));
-      obj_coff_strings (abfd) = NULL;
-    }
-  return true;
-}
-
 /* Look through the symbols to see if this object file should be
    included in the link.  */
 
@@ -401,7 +226,6 @@ coff_link_check_ar_symbols (abfd, info, pneeded)
      boolean *pneeded;
 {
   boolean (*sym_is_global) PARAMS ((bfd *, struct internal_syment *));
-  const char *strings;
   bfd_size_type symesz;
   bfd_byte *esym;
   bfd_byte *esym_end;
@@ -409,7 +233,6 @@ coff_link_check_ar_symbols (abfd, info, pneeded)
   *pneeded = false;
 
   sym_is_global = coff_backend_info (abfd)->_bfd_coff_sym_is_global;
-  strings = NULL;
 
   symesz = bfd_coff_symesz (abfd);
   esym = (bfd_byte *) obj_coff_external_syms (abfd);
@@ -421,6 +244,11 @@ coff_link_check_ar_symbols (abfd, info, pneeded)
       bfd_coff_swap_sym_in (abfd, (PTR) esym, (PTR) &sym);
 
       if ((sym.n_sclass == C_EXT
+          || sym.n_sclass == C_WEAKEXT
+          || (obj_pe (abfd) && sym.n_sclass == C_NT_WEAK)
+#ifdef C_SYSTEM
+          || sym.n_sclass == C_SYSTEM
+#endif
           || (sym_is_global && (*sym_is_global) (abfd, &sym)))
          && (sym.n_scnum != 0 || sym.n_value != 0))
        {
@@ -431,27 +259,9 @@ coff_link_check_ar_symbols (abfd, info, pneeded)
          /* This symbol is externally visible, and is defined by this
              object file.  */
 
-         /* FIXME: It's not clear this will work correctly if sizeof
-             (_n_zeroes) != 4.  */
-         if (sym._n._n_n._n_zeroes != 0
-             || sym._n._n_n._n_offset == 0)
-           {
-             memcpy (buf, sym._n._n_name, SYMNMLEN);
-             buf[SYMNMLEN] = '\0';
-             name = buf;
-           }
-         else
-           {
-             BFD_ASSERT (sym._n._n_n._n_offset >= STRING_SIZE_SIZE);
-             if (strings == NULL)
-               {
-                 strings = coff_read_string_table (abfd);
-                 if (strings == NULL)
-                   return false;
-               }
-             name = strings + sym._n._n_n._n_offset;
-           }
-
+         name = _bfd_coff_internal_syment_name (abfd, &sym, buf);
+         if (name == NULL)
+           return false;
          h = bfd_link_hash_lookup (info->hash, name, false, false, true);
 
          /* We are only interested in symbols that are currently
@@ -483,7 +293,7 @@ coff_link_add_symbols (abfd, info)
      struct bfd_link_info *info;
 {
   boolean (*sym_is_global) PARAMS ((bfd *, struct internal_syment *));
-  const char *strings;
+  boolean keep_syms;
   boolean default_copy;
   bfd_size_type symcount;
   struct coff_link_hash_entry **sym_hash;
@@ -491,8 +301,12 @@ coff_link_add_symbols (abfd, info)
   bfd_byte *esym;
   bfd_byte *esym_end;
 
+  /* Keep the symbols during this function, in case the linker needs
+     to read the generic symbols in order to report an error message.  */
+  keep_syms = obj_coff_keep_syms (abfd);
+  obj_coff_keep_syms (abfd) = true;
+
   sym_is_global = coff_backend_info (abfd)->_bfd_coff_sym_is_global;
-  strings = NULL;
 
   if (info->keep_memory)
     default_copy = false;
@@ -508,10 +322,7 @@ coff_link_add_symbols (abfd, info)
                         ((size_t) symcount
                          * sizeof (struct coff_link_hash_entry *))));
   if (sym_hash == NULL && symcount != 0)
-    {
-      bfd_set_error (bfd_error_no_memory);
-      return false;
-    }
+    goto error_return;
   obj_coff_sym_hashes (abfd) = sym_hash;
   memset (sym_hash, 0,
          (size_t) symcount * sizeof (struct coff_link_hash_entry *));
@@ -528,6 +339,11 @@ coff_link_add_symbols (abfd, info)
       bfd_coff_swap_sym_in (abfd, (PTR) esym, (PTR) &sym);
 
       if (sym.n_sclass == C_EXT
+          || sym.n_sclass == C_WEAKEXT
+          || (obj_pe (abfd) && sym.n_sclass == C_NT_WEAK)
+#ifdef C_SYSTEM
+         || sym.n_sclass == C_SYSTEM
+#endif
          || (sym_is_global && (*sym_is_global) (abfd, &sym)))
        {
          const char *name;
@@ -538,28 +354,16 @@ coff_link_add_symbols (abfd, info)
 
          /* This symbol is externally visible.  */
 
-         /* FIXME: It's not clear this will work correctly if sizeof
-             (_n_zeroes) != 4.  */
+         name = _bfd_coff_internal_syment_name (abfd, &sym, buf);
+         if (name == NULL)
+           goto error_return;
+
+         /* We must copy the name into memory if we got it from the
+             syment itself, rather than the string table.  */
          copy = default_copy;
-         if (sym._n._n_n._n_zeroes == 0
-             && sym._n._n_n._n_offset != 0)
-           {
-             BFD_ASSERT (sym._n._n_n._n_offset >= STRING_SIZE_SIZE);
-             if (strings == NULL)
-               {
-                 strings = coff_read_string_table (abfd);
-                 if (strings == NULL)
-                   return false;
-               }
-             name = strings + sym._n._n_n._n_offset;
-           }
-         else
-           {
-             memcpy (buf, sym._n._n_name, SYMNMLEN);
-             buf[SYMNMLEN] = '\0';
-             name = buf;
-             copy = true;
-           }
+         if (sym._n._n_n._n_zeroes != 0
+             || sym._n._n_n._n_offset == 0)
+           copy = true;
 
          value = sym.n_value;
 
@@ -580,14 +384,26 @@ coff_link_add_symbols (abfd, info)
            {
              flags = BSF_EXPORT | BSF_GLOBAL;
              section = coff_section_from_bfd_index (abfd, sym.n_scnum);
-             value -= section->vma;
+             if (! obj_pe (abfd))
+               value -= section->vma;
            }
 
-         if (! (_bfd_generic_link_add_one_symbol
+         if (sym.n_sclass == C_WEAKEXT
+             || (obj_pe (abfd) && sym.n_sclass == C_NT_WEAK))
+           flags = BSF_WEAK;
+
+         if (! (bfd_coff_link_add_one_symbol
                 (info, abfd, name, flags, section, value,
                  (const char *) NULL, copy, false,
                  (struct bfd_link_hash_entry **) sym_hash)))
-           return false;
+           goto error_return;
+
+         if (section == bfd_com_section_ptr
+             && (*sym_hash)->root.type == bfd_link_hash_common
+             && ((*sym_hash)->root.u.c.p->alignment_power
+                 > bfd_coff_default_section_alignment_power (abfd)))
+           (*sym_hash)->root.u.c.p->alignment_power
+             = bfd_coff_default_section_alignment_power (abfd);
 
          if (info->hash->creator->flavour == bfd_get_flavour (abfd))
            {
@@ -595,11 +411,20 @@ coff_link_add_symbols (abfd, info)
                   && (*sym_hash)->type == T_NULL)
                  || sym.n_scnum != 0
                  || (sym.n_value != 0
-                     && (*sym_hash)->root.type != bfd_link_hash_defined))
+                     && (*sym_hash)->root.type != bfd_link_hash_defined
+                     && (*sym_hash)->root.type != bfd_link_hash_defweak))
                {
                  (*sym_hash)->class = sym.n_sclass;
-                 (*sym_hash)->type = sym.n_type;
-                 (*sym_hash)->numaux = sym.n_numaux;
+                 if (sym.n_type != T_NULL)
+                   {
+                     if ((*sym_hash)->type != T_NULL
+                         && (*sym_hash)->type != sym.n_type)
+                       (*_bfd_error_handler)
+                         (_("Warning: type of symbol `%s' changed from %d to %d in %s"),
+                          name, (*sym_hash)->type, sym.n_type,
+                          bfd_get_filename (abfd));
+                     (*sym_hash)->type = sym.n_type;
+                   }
                  (*sym_hash)->auxbfd = abfd;
                  if (sym.n_numaux != 0)
                    {
@@ -608,15 +433,13 @@ coff_link_add_symbols (abfd, info)
                      bfd_byte *eaux;
                      union internal_auxent *iaux;
 
+                     (*sym_hash)->numaux = sym.n_numaux;
                      alloc = ((union internal_auxent *)
                               bfd_hash_allocate (&info->hash->table,
                                                  (sym.n_numaux
                                                   * sizeof (*alloc))));
                      if (alloc == NULL)
-                       {
-                         bfd_set_error (bfd_error_no_memory);
-                         return false;
-                       }
+                       goto error_return;
                      for (i = 0, eaux = esym + symesz, iaux = alloc;
                           i < sym.n_numaux;
                           i++, eaux += symesz, iaux++)
@@ -633,129 +456,55 @@ coff_link_add_symbols (abfd, info)
       sym_hash += sym.n_numaux + 1;
     }
 
-  return true;
-}
+  /* If this is a non-traditional, non-relocateable link, try to
+     optimize the handling of any .stab/.stabstr sections.  */
+  if (! info->relocateable
+      && ! info->traditional_format
+      && info->hash->creator->flavour == bfd_get_flavour (abfd)
+      && (info->strip != strip_all && info->strip != strip_debugger))
+    {
+      asection *stab, *stabstr;
 
-/* parse out a -heap <reserved>,<commit> line */
+      stab = bfd_get_section_by_name (abfd, ".stab");
+      if (stab != NULL)
+       {
+         stabstr = bfd_get_section_by_name (abfd, ".stabstr");
 
-static char *
-dores_com (ptr, def,res, com)
-     char *ptr;
-     int *def;
-     int *res;
-     int *com;
-{
-  *def = 1;
-  *res = strtoul (ptr, &ptr, 0);
-  if (ptr[0] == ',')
-    *com = strtoul (ptr+1, &ptr, 0);
-  return ptr;
-}
+         if (stabstr != NULL)
+           {
+             struct coff_link_hash_table *table;
+             struct coff_section_tdata *secdata;
 
-static char *get_name(ptr, dst)
-char *ptr;
-char **dst;
-{
-  while (*ptr == ' ')
-    ptr++;
-  *dst = ptr;
-  while (*ptr && *ptr != ' ')
-    ptr++;
-  *ptr = 0;
-  return ptr+1;
-}
-/* Process any magic embedded commands in a section called .drectve */
-                       
-static int
-process_embedded_commands (abfd)
-     bfd *abfd;
-{
-  asection *sec = bfd_get_section_by_name (abfd, ".drectve");
-  char *s;
-  char *e;
-  char *copy;
-  if (!sec) 
-    return 1;
-  
-  copy = malloc (sec->_raw_size);
-  if (!copy) 
-    {
-      bfd_set_error (bfd_error_no_memory);
-      return 0;
-    }
-  if (! bfd_get_section_contents(abfd, sec, copy, 0, sec->_raw_size)) 
-    {
-      free (copy);
-      return 0;
-    }
-  e = copy + sec->_raw_size;
-  for (s = copy;  s < e ; ) 
-    {
-      if (s[0]!= '-') {
-       s++;
-       continue;
-      }
-      if (strncmp (s,"-attr", 5) == 0)
-       {
-         char *name;
-         char *attribs;
-         asection *asec;
+             secdata = coff_section_data (abfd, stab);
+             if (secdata == NULL)
+               {
+                 stab->used_by_bfd =
+                   (PTR) bfd_zalloc (abfd,
+                                     sizeof (struct coff_section_tdata));
+                 if (stab->used_by_bfd == NULL)
+                   goto error_return;
+                 secdata = coff_section_data (abfd, stab);
+               }
 
-         int loop = 1;
-         int had_write = 0;
-         int had_read = 0;
-         int had_exec= 0;
-         int had_shared= 0;
-         s += 5;
-         s = get_name(s, &name);
-         s = get_name(s, &attribs);
-         while (loop) {
-           switch (*attribs++) 
-             {
-             case 'W':
-               had_write = 1;
-               break;
-             case 'R':
-               had_read = 1;
-               break;
-             case 'S':
-               had_shared = 1;
-               break;
-             case 'X':
-               had_exec = 1;
-               break;
-             default:
-               loop = 0;
-             }
-         }
-         asec = bfd_get_section_by_name (abfd, name);
-         if (asec) {
-           if (had_exec)
-             asec->flags |= SEC_CODE;
-           if (!had_write)
-             asec->flags |= SEC_READONLY;
-         }
-       }
-      else if (strncmp (s,"-heap", 5) == 0)
-       {
-         s = dores_com (s+5, 
-                        &NT_stack_heap.heap_defined,
-                        &NT_stack_heap.heap_reserve,
-                        &NT_stack_heap.heap_commit);
-       }
-      else if (strncmp (s,"-stack", 6) == 0)
-       {
-         s = dores_com (s+6,
-                        &NT_stack_heap.heap_defined,
-                        &NT_stack_heap.heap_reserve,
-                        &NT_stack_heap.heap_commit);
+             table = coff_hash_table (info);
+
+             if (! _bfd_link_section_stabs (abfd, &table->stab_info,
+                                            stab, stabstr,
+                                            &secdata->stab_info))
+               goto error_return;
+           }
        }
-      else 
-       s++;
     }
-  free (copy);
-  return 1;
+
+  obj_coff_keep_syms (abfd) = keep_syms;
+
+  return true;
+
+ error_return:
+  obj_coff_keep_syms (abfd) = keep_syms;
+  return false;
 }
+\f
 /* Do the final link step.  */
 
 boolean
@@ -765,13 +514,15 @@ _bfd_coff_final_link (abfd, info)
 {
   bfd_size_type symesz;
   struct coff_final_link_info finfo;
+  boolean debug_merge_allocated;
+  boolean long_section_names;
   asection *o;
   struct bfd_link_order *p;
-  size_t max_contents_size;
   size_t max_sym_count;
   size_t max_lineno_count;
   size_t max_reloc_count;
   size_t max_output_reloc_count;
+  size_t max_contents_size;
   file_ptr rel_filepos;
   unsigned int relsz;
   file_ptr line_filepos;
@@ -787,6 +538,7 @@ _bfd_coff_final_link (abfd, info)
   finfo.strtab = NULL;
   finfo.section_info = NULL;
   finfo.last_file_index = -1;
+  finfo.last_bf_index = -1;
   finfo.internal_syms = NULL;
   finfo.sec_ptrs = NULL;
   finfo.sym_indices = NULL;
@@ -795,23 +547,25 @@ _bfd_coff_final_link (abfd, info)
   finfo.contents = NULL;
   finfo.external_relocs = NULL;
   finfo.internal_relocs = NULL;
+  finfo.global_to_static = false;
+  debug_merge_allocated = false;
 
-  if (obj_pe(abfd))
-    {
-      /* store the subsystem, stack and heap parameters in variables defined
-        in internal.h so that when they are needed to write the NT optional
-        file header (coffcode.h), they will be available */
-      NT_subsystem  = info->subsystem;
-      NT_stack_heap = info->stack_heap_parameters;
-    }
+  coff_data (abfd)->link_info = info;
 
   finfo.strtab = _bfd_stringtab_init ();
   if (finfo.strtab == NULL)
     goto error_return;
 
+  if (! coff_debug_merge_hash_table_init (&finfo.debug_merge))
+    goto error_return;
+  debug_merge_allocated = true;
+
   /* Compute the file positions for all the sections.  */
   if (! abfd->output_has_begun)
-    bfd_coff_compute_section_file_positions (abfd);
+    {
+      if (! bfd_coff_compute_section_file_positions (abfd))
+       goto error_return;
+    }
 
   /* Count the line numbers and relocation entries required for the
      output file.  Set the file positions for the relocs.  */
@@ -820,6 +574,8 @@ _bfd_coff_final_link (abfd, info)
   max_contents_size = 0;
   max_lineno_count = 0;
   max_reloc_count = 0;
+
+  long_section_names = false;
   for (o = abfd->sections; o != NULL; o = o->next)
     {
       o->reloc_count = 0;
@@ -832,6 +588,12 @@ _bfd_coff_final_link (abfd, info)
 
              sec = p->u.indirect.section;
 
+             /* Mark all sections which are to be included in the
+                link.  This will normally be every section.  We need
+                to do this so that we can identify any sections which
+                the linker has decided to not include.  */
+             sec->linker_mark = true;
+
              if (info->strip == strip_none
                  || info->strip == strip_some)
                o->lineno_count += sec->lineno_count;
@@ -859,6 +621,20 @@ _bfd_coff_final_link (abfd, info)
          o->rel_filepos = rel_filepos;
          rel_filepos += o->reloc_count * relsz;
        }
+
+      if (bfd_coff_long_section_names (abfd)
+         && strlen (o->name) > SCNNMLEN)
+       {
+         /* This section has a long name which must go in the string
+             table.  This must correspond to the code in
+             coff_write_object_contents which puts the string index
+             into the s_name field of the section header.  That is why
+             we pass hash as false.  */
+         if (_bfd_stringtab_add (finfo.strtab, o->name, false, false)
+             == (bfd_size_type) -1)
+           goto error_return;
+         long_section_names = true;
+       }
     }
 
   /* If doing a relocateable link, allocate space for the pointers we
@@ -869,14 +645,12 @@ _bfd_coff_final_link (abfd, info)
 
       /* We use section_count + 1, rather than section_count, because
          the target_index fields are 1 based.  */
-      finfo.section_info = ((struct coff_link_section_info *)
-                           malloc ((abfd->section_count + 1)
-                                   * sizeof (struct coff_link_section_info)));
+      finfo.section_info =
+       ((struct coff_link_section_info *)
+        bfd_malloc ((abfd->section_count + 1)
+                    * sizeof (struct coff_link_section_info)));
       if (finfo.section_info == NULL)
-       {
-         bfd_set_error (bfd_error_no_memory);
-         goto error_return;
-       }
+       goto error_return;
       for (i = 0; i <= abfd->section_count; i++)
        {
          finfo.section_info[i].relocs = NULL;
@@ -917,17 +691,14 @@ _bfd_coff_final_link (abfd, info)
          BFD_ASSERT (info->relocateable);
          finfo.section_info[o->target_index].relocs =
            ((struct internal_reloc *)
-            malloc (o->reloc_count * sizeof (struct internal_reloc)));
+            bfd_malloc (o->reloc_count * sizeof (struct internal_reloc)));
          finfo.section_info[o->target_index].rel_hashes =
            ((struct coff_link_hash_entry **)
-            malloc (o->reloc_count
+            bfd_malloc (o->reloc_count
                     * sizeof (struct coff_link_hash_entry *)));
          if (finfo.section_info[o->target_index].relocs == NULL
              || finfo.section_info[o->target_index].rel_hashes == NULL)
-           {
-             bfd_set_error (bfd_error_no_memory);
-             goto error_return;
-           }
+           goto error_return;
 
          if (o->reloc_count > max_output_reloc_count)
            max_output_reloc_count = o->reloc_count;
@@ -957,19 +728,21 @@ _bfd_coff_final_link (abfd, info)
 
   /* Allocate some buffers used while linking.  */
   finfo.internal_syms = ((struct internal_syment *)
-                        malloc (max_sym_count
-                                * sizeof (struct internal_syment)));
-  finfo.sec_ptrs = (asection **) malloc (max_sym_count * sizeof (asection *));
-  finfo.sym_indices = (long *) malloc (max_sym_count * sizeof (long));
-  finfo.outsyms = (bfd_byte *) malloc ((max_sym_count + 1) * symesz);
-  finfo.linenos = (bfd_byte *) malloc (max_lineno_count
+                        bfd_malloc (max_sym_count
+                                    * sizeof (struct internal_syment)));
+  finfo.sec_ptrs = (asection **) bfd_malloc (max_sym_count
+                                            * sizeof (asection *));
+  finfo.sym_indices = (long *) bfd_malloc (max_sym_count * sizeof (long));
+  finfo.outsyms = ((bfd_byte *)
+                  bfd_malloc ((size_t) ((max_sym_count + 1) * symesz)));
+  finfo.linenos = (bfd_byte *) bfd_malloc (max_lineno_count
                                       * bfd_coff_linesz (abfd));
-  finfo.contents = (bfd_byte *) malloc (max_contents_size);
-  finfo.external_relocs = (bfd_byte *) malloc (max_reloc_count * relsz);
+  finfo.contents = (bfd_byte *) bfd_malloc (max_contents_size);
+  finfo.external_relocs = (bfd_byte *) bfd_malloc (max_reloc_count * relsz);
   if (! info->relocateable)
     finfo.internal_relocs = ((struct internal_reloc *)
-                            malloc (max_reloc_count
-                                    * sizeof (struct internal_reloc)));
+                            bfd_malloc (max_reloc_count
+                                        * sizeof (struct internal_reloc)));
   if ((finfo.internal_syms == NULL && max_sym_count > 0)
       || (finfo.sec_ptrs == NULL && max_sym_count > 0)
       || (finfo.sym_indices == NULL && max_sym_count > 0)
@@ -980,10 +753,7 @@ _bfd_coff_final_link (abfd, info)
       || (! info->relocateable
          && finfo.internal_relocs == NULL
          && max_reloc_count > 0))
-    {
-      bfd_set_error (bfd_error_no_memory);
-      goto error_return;
-    }
+    goto error_return;
 
   /* We now know the position of everything in the file, except that
      we don't know the size of the symbol table and therefore we don't
@@ -991,6 +761,13 @@ _bfd_coff_final_link (abfd, info)
      table in memory as we go along.  We process all the relocations
      for a single input file at once.  */
   obj_raw_syment_count (abfd) = 0;
+
+  if (coff_backend_info (abfd)->_bfd_coff_start_final_link)
+    {
+      if (! bfd_coff_start_final_link (abfd, info))
+       goto error_return;
+    }
+
   for (o = abfd->sections; o != NULL; o = o->next)
     {
       for (p = o->link_order_head; p != NULL; p = p->next)
@@ -1000,9 +777,9 @@ _bfd_coff_final_link (abfd, info)
                  == bfd_target_coff_flavour))
            {
              sub = p->u.indirect.section->owner;
-             if (! sub->output_has_begun)
+             if (! bfd_coff_link_output_has_begun (sub, & finfo))
                {
-                 if (! coff_link_input_bfd (&finfo, sub))
+                 if (! _bfd_coff_link_input_bfd (&finfo, sub))
                    goto error_return;
                  sub->output_has_begun = true;
                }
@@ -1010,7 +787,7 @@ _bfd_coff_final_link (abfd, info)
          else if (p->type == bfd_section_reloc_link_order
                   || p->type == bfd_symbol_reloc_link_order)
            {
-             if (! coff_reloc_link_order (abfd, &finfo, o, p))
+             if (! _bfd_coff_reloc_link_order (abfd, &finfo, o, p))
                goto error_return;
            }
          else
@@ -1021,7 +798,14 @@ _bfd_coff_final_link (abfd, info)
        }
     }
 
-  /* Free up the buffers used by coff_link_input_bfd.  */
+  if (! bfd_coff_final_link_postscript (abfd, & finfo))
+    goto error_return;
+  
+  /* Free up the buffers used by _bfd_coff_link_input_bfd.  */
+
+  coff_debug_merge_hash_table_free (&finfo.debug_merge);
+  debug_merge_allocated = false;
+
   if (finfo.internal_syms != NULL)
     {
       free (finfo.internal_syms);
@@ -1062,7 +846,7 @@ _bfd_coff_final_link (abfd, info)
      index of the first external symbol.  Write it out again if
      necessary.  */
   if (finfo.last_file_index != -1
-      && finfo.last_file.n_value != obj_raw_syment_count (abfd))
+      && (unsigned int) finfo.last_file.n_value != obj_raw_syment_count (abfd))
     {
       finfo.last_file.n_value = obj_raw_syment_count (abfd);
       bfd_coff_swap_sym_out (abfd, (PTR) &finfo.last_file,
@@ -1075,31 +859,41 @@ _bfd_coff_final_link (abfd, info)
        return false;
     }
 
+  /* If doing task linking (ld --task-link) then make a pass through the
+     global symbols, writing out any that are defined, and making them
+     static. */
+  if (info->task_link)
+    {
+      finfo.failed = false;
+      coff_link_hash_traverse (coff_hash_table (info), _bfd_coff_write_task_globals,
+                              (PTR) &finfo);
+      if (finfo.failed)
+       goto error_return;
+    }
+
   /* Write out the global symbols.  */
   finfo.failed = false;
-  coff_link_hash_traverse (coff_hash_table (info), coff_write_global_sym,
+  coff_link_hash_traverse (coff_hash_table (info), _bfd_coff_write_global_sym,
                           (PTR) &finfo);
   if (finfo.failed)
     goto error_return;
 
-  /* The outsyms buffer is used by coff_write_global_sym.  */
+  /* The outsyms buffer is used by _bfd_coff_write_global_sym.  */
   if (finfo.outsyms != NULL)
     {
       free (finfo.outsyms);
       finfo.outsyms = NULL;
     }
 
-  if (info->relocateable)
+  if (info->relocateable && max_output_reloc_count > 0)
     {
       /* Now that we have written out all the global symbols, we know
         the symbol indices to use for relocs against them, and we can
         finally write out the relocs.  */
-      external_relocs = (bfd_byte *) malloc (max_output_reloc_count * relsz);
+      external_relocs = ((bfd_byte *)
+                        bfd_malloc (max_output_reloc_count * relsz));
       if (external_relocs == NULL)
-       {
-         bfd_set_error (bfd_error_no_memory);
-         goto error_return;
-       }
+       goto error_return;
 
       for (o = abfd->sections; o != NULL; o = o->next)
        {
@@ -1142,86 +936,279 @@ _bfd_coff_final_link (abfd, info)
 
       for (i = 0; i < abfd->section_count; i++)
        {
-         if (finfo.section_info[i].relocs != NULL)
-           free (finfo.section_info[i].relocs);
-         if (finfo.section_info[i].rel_hashes != NULL)
-           free (finfo.section_info[i].rel_hashes);
+         if (finfo.section_info[i].relocs != NULL)
+           free (finfo.section_info[i].relocs);
+         if (finfo.section_info[i].rel_hashes != NULL)
+           free (finfo.section_info[i].rel_hashes);
+       }
+      free (finfo.section_info);
+      finfo.section_info = NULL;
+    }
+
+  /* If we have optimized stabs strings, output them.  */
+  if (coff_hash_table (info)->stab_info != NULL)
+    {
+      if (! _bfd_write_stab_strings (abfd, &coff_hash_table (info)->stab_info))
+       return false;
+    }
+
+  /* Write out the string table.  */
+  if (obj_raw_syment_count (abfd) != 0 || long_section_names)
+    {
+      if (bfd_seek (abfd,
+                   (obj_sym_filepos (abfd)
+                    + obj_raw_syment_count (abfd) * symesz),
+                   SEEK_SET) != 0)
+       return false;
+
+#if STRING_SIZE_SIZE == 4
+      bfd_h_put_32 (abfd,
+                   _bfd_stringtab_size (finfo.strtab) + STRING_SIZE_SIZE,
+                   (bfd_byte *) strbuf);
+#else
+ #error Change bfd_h_put_32
+#endif
+
+      if (bfd_write (strbuf, 1, STRING_SIZE_SIZE, abfd) != STRING_SIZE_SIZE)
+       return false;
+
+      if (! _bfd_stringtab_emit (abfd, finfo.strtab))
+       return false;
+    }
+
+  _bfd_stringtab_free (finfo.strtab);
+
+  /* Setting bfd_get_symcount to 0 will cause write_object_contents to
+     not try to write out the symbols.  */
+  bfd_get_symcount (abfd) = 0;
+
+  return true;
+
+ error_return:
+  if (debug_merge_allocated)
+    coff_debug_merge_hash_table_free (&finfo.debug_merge);
+  if (finfo.strtab != NULL)
+    _bfd_stringtab_free (finfo.strtab);
+  if (finfo.section_info != NULL)
+    {
+      unsigned int i;
+
+      for (i = 0; i < abfd->section_count; i++)
+       {
+         if (finfo.section_info[i].relocs != NULL)
+           free (finfo.section_info[i].relocs);
+         if (finfo.section_info[i].rel_hashes != NULL)
+           free (finfo.section_info[i].rel_hashes);
+       }
+      free (finfo.section_info);
+    }
+  if (finfo.internal_syms != NULL)
+    free (finfo.internal_syms);
+  if (finfo.sec_ptrs != NULL)
+    free (finfo.sec_ptrs);
+  if (finfo.sym_indices != NULL)
+    free (finfo.sym_indices);
+  if (finfo.outsyms != NULL)
+    free (finfo.outsyms);
+  if (finfo.linenos != NULL)
+    free (finfo.linenos);
+  if (finfo.contents != NULL)
+    free (finfo.contents);
+  if (finfo.external_relocs != NULL)
+    free (finfo.external_relocs);
+  if (finfo.internal_relocs != NULL)
+    free (finfo.internal_relocs);
+  if (external_relocs != NULL)
+    free (external_relocs);
+  return false;
+}
+
+/* parse out a -heap <reserved>,<commit> line */
+
+static char *
+dores_com (ptr, output_bfd, heap)
+     char *ptr;
+     bfd *output_bfd;
+     int heap;
+{
+  if (coff_data(output_bfd)->pe) 
+    {
+      int val = strtoul (ptr, &ptr, 0);
+      if (heap)
+       pe_data(output_bfd)->pe_opthdr.SizeOfHeapReserve =val;
+      else
+       pe_data(output_bfd)->pe_opthdr.SizeOfStackReserve =val;
+
+      if (ptr[0] == ',') 
+       {
+         int val = strtoul (ptr+1, &ptr, 0);
+         if (heap)
+           pe_data(output_bfd)->pe_opthdr.SizeOfHeapCommit =val;
+         else
+           pe_data(output_bfd)->pe_opthdr.SizeOfStackCommit =val;
+       }
+    }
+  return ptr;
+}
+
+static char *get_name(ptr, dst)
+char *ptr;
+char **dst;
+{
+  while (*ptr == ' ')
+    ptr++;
+  *dst = ptr;
+  while (*ptr && *ptr != ' ')
+    ptr++;
+  *ptr = 0;
+  return ptr+1;
+}
+
+/* Process any magic embedded commands in a section called .drectve */
+                       
+static int
+process_embedded_commands (output_bfd, info,  abfd)
+     bfd *output_bfd;
+     struct bfd_link_info *info;
+     bfd *abfd;
+{
+  asection *sec = bfd_get_section_by_name (abfd, ".drectve");
+  char *s;
+  char *e;
+  char *copy;
+  if (!sec) 
+    return 1;
+  
+  copy = bfd_malloc ((size_t) sec->_raw_size);
+  if (!copy) 
+    return 0;
+  if (! bfd_get_section_contents(abfd, sec, copy, 0, sec->_raw_size)) 
+    {
+      free (copy);
+      return 0;
+    }
+  e = copy + sec->_raw_size;
+  for (s = copy;  s < e ; ) 
+    {
+      if (s[0]!= '-') {
+       s++;
+       continue;
+      }
+      if (strncmp (s,"-attr", 5) == 0)
+       {
+         char *name;
+         char *attribs;
+         asection *asec;
+
+         int loop = 1;
+         int had_write = 0;
+         int had_read = 0;
+         int had_exec= 0;
+         int had_shared= 0;
+         s += 5;
+         s = get_name(s, &name);
+         s = get_name(s, &attribs);
+         while (loop) {
+           switch (*attribs++) 
+             {
+             case 'W':
+               had_write = 1;
+               break;
+             case 'R':
+               had_read = 1;
+               break;
+             case 'S':
+               had_shared = 1;
+               break;
+             case 'X':
+               had_exec = 1;
+               break;
+             default:
+               loop = 0;
+             }
+         }
+         asec = bfd_get_section_by_name (abfd, name);
+         if (asec) {
+           if (had_exec)
+             asec->flags |= SEC_CODE;
+           if (!had_write)
+             asec->flags |= SEC_READONLY;
+         }
+       }
+      else if (strncmp (s,"-heap", 5) == 0)
+       {
+         s = dores_com (s+5, output_bfd, 1);
+       }
+      else if (strncmp (s,"-stack", 6) == 0)
+       {
+         s = dores_com (s+6, output_bfd, 0);
        }
-      free (finfo.section_info);
-      finfo.section_info = NULL;
+      else 
+       s++;
     }
+  free (copy);
+  return 1;
+}
 
-  /* Write out the string table.  */
-  if (bfd_seek (abfd,
-               (obj_sym_filepos (abfd)
-                + obj_raw_syment_count (abfd) * symesz),
-               SEEK_SET) != 0)
-    return false;
-
-#if STRING_SIZE_SIZE == 4
-  bfd_h_put_32 (abfd,
-               _bfd_stringtab_size (finfo.strtab) + STRING_SIZE_SIZE,
-               (bfd_byte *) strbuf);
-#else
- #error Change bfd_h_put_32
-#endif
-
-  if (bfd_write (strbuf, 1, STRING_SIZE_SIZE, abfd) != STRING_SIZE_SIZE)
-    return false;
+/* Place a marker against all symbols which are used by relocations.
+   This marker can be picked up by the 'do we skip this symbol ?'
+   loop in _bfd_coff_link_input_bfd() and used to prevent skipping
+   that symbol. 
+   */
 
-  if (! _bfd_stringtab_emit (abfd, finfo.strtab))
-    return false;
+static void
+mark_relocs (finfo, input_bfd)
+     struct coff_final_link_info *     finfo;
+     bfd *                             input_bfd;
+{
+  asection * a;
 
-  _bfd_stringtab_free (finfo.strtab);
+  if ((bfd_get_file_flags (input_bfd) & HAS_SYMS) == 0)
+    return;
+  
+  for (a = input_bfd->sections; a != (asection *) NULL; a = a->next)
+    {
+      struct internal_reloc *  internal_relocs;
+      struct internal_reloc *  irel;
+      struct internal_reloc *  irelend;
 
-  /* Setting bfd_get_symcount to 0 will cause write_object_contents to
-     not try to write out the symbols.  */
-  bfd_get_symcount (abfd) = 0;
+      
+      if ((a->flags & SEC_RELOC) == 0 || a->reloc_count  < 1)
+       continue;
 
-  return true;
+      /* Read in the relocs.  */
+      internal_relocs = _bfd_coff_read_internal_relocs
+       (input_bfd, a, false,
+        finfo->external_relocs,
+        finfo->info->relocateable,
+        (finfo->info->relocateable
+         ? (finfo->section_info[ a->output_section->target_index ].relocs + a->output_section->reloc_count)
+         : finfo->internal_relocs)
+       );
+      
+      if (internal_relocs == NULL)
+       continue;
 
- error_return:
-  if (finfo.strtab != NULL)
-    _bfd_stringtab_free (finfo.strtab);
-  if (finfo.section_info != NULL)
-    {
-      unsigned int i;
+      irel     = internal_relocs;
+      irelend  = irel + a->reloc_count;
 
-      for (i = 0; i < abfd->section_count; i++)
+      /* Place a mark in the sym_indices array (whose entries have
+        been initialised to 0) for all of the symbols that are used
+        in the relocation table.  This will then be picked up in the
+        skip/don't pass */
+      
+      for (; irel < irelend; irel++)
        {
-         if (finfo.section_info[i].relocs != NULL)
-           free (finfo.section_info[i].relocs);
-         if (finfo.section_info[i].rel_hashes != NULL)
-           free (finfo.section_info[i].rel_hashes);
+         finfo->sym_indices[ irel->r_symndx ] = -1;
        }
-      free (finfo.section_info);
     }
-  if (finfo.internal_syms != NULL)
-    free (finfo.internal_syms);
-  if (finfo.sec_ptrs != NULL)
-    free (finfo.sec_ptrs);
-  if (finfo.sym_indices != NULL)
-    free (finfo.sym_indices);
-  if (finfo.outsyms != NULL)
-    free (finfo.outsyms);
-  if (finfo.linenos != NULL)
-    free (finfo.linenos);
-  if (finfo.contents != NULL)
-    free (finfo.contents);
-  if (finfo.external_relocs != NULL)
-    free (finfo.external_relocs);
-  if (finfo.internal_relocs != NULL)
-    free (finfo.internal_relocs);
-  if (external_relocs != NULL)
-    free (external_relocs);
-  return false;
 }
 
 /* Link an input file into the linker output file.  This function
    handles all the sections and relocations of the input file at once.  */
 
-static boolean
-coff_link_input_bfd (finfo, input_bfd)
+boolean
+_bfd_coff_link_input_bfd (finfo, input_bfd)
      struct coff_final_link_info *finfo;
      bfd *input_bfd;
 {
@@ -1243,10 +1230,9 @@ coff_link_input_bfd (finfo, input_bfd)
   struct internal_syment *isymp;
   asection **secpp;
   long *indexp;
-  long output_index;
+  unsigned long output_index;
   bfd_byte *outsym;
   struct coff_link_hash_entry **sym_hash;
-  bfd_size_type relsz;
   asection *o;
 
   /* Move all the symbols to the output file.  */
@@ -1274,7 +1260,7 @@ coff_link_input_bfd (finfo, input_bfd)
   if ((output_bfd->flags & BFD_TRADITIONAL_FORMAT) != 0)
     hash = false;
 
-  if (! coff_link_get_symbols (input_bfd))
+  if (! _bfd_coff_get_external_symbols (input_bfd))
     return false;
 
   esym = (bfd_byte *) obj_coff_external_syms (input_bfd);
@@ -1285,17 +1271,31 @@ coff_link_input_bfd (finfo, input_bfd)
   output_index = syment_base;
   outsym = finfo->outsyms;
 
-  if (obj_pe (output_bfd))
-      {
-       if (!process_embedded_commands (input_bfd))
-         return false;
-      }
+  if (coff_data (output_bfd)->pe)
+    {
+      if (! process_embedded_commands (output_bfd, finfo->info, input_bfd))
+       return false;
+    }
+
+  /* If we are going to perform relocations and also strip/discard some symbols
+     then we must make sure that we do not strip/discard those symbols that are
+     going to be involved in the relocations */
+  if ((   finfo->info->strip   != strip_none
+       || finfo->info->discard != discard_none)
+      && finfo->info->relocateable)
+    {
+      /* mark the symbol array as 'not-used' */
+      memset (indexp, 0, obj_raw_syment_count (input_bfd) * sizeof * indexp); 
+       
+      mark_relocs (finfo, input_bfd);
+    }
 
   while (esym < esym_end)
     {
       struct internal_syment isym;
       boolean skip;
       boolean global;
+      boolean dont_skip_symbol;
       int add;
 
       bfd_coff_swap_sym_in (input_bfd, (PTR) esym, (PTR) isymp);
@@ -1316,6 +1316,15 @@ coff_link_input_bfd (finfo, input_bfd)
            *secpp = bfd_com_section_ptr;
        }
 
+      /* Extract the flag indicating if this symbol is used by a
+         relocation.  */
+      if ((finfo->info->strip != strip_none
+          || finfo->info->discard != discard_none)
+         && finfo->info->relocateable)
+       dont_skip_symbol = *indexp;
+      else
+       dont_skip_symbol = false;
+      
       *indexp = -1;
 
       skip = false;
@@ -1323,36 +1332,54 @@ coff_link_input_bfd (finfo, input_bfd)
       add = 1 + isym.n_numaux;
 
       /* If we are stripping all symbols, we want to skip this one.  */
-      if (finfo->info->strip == strip_all)
+      if (finfo->info->strip == strip_all && ! dont_skip_symbol)
        skip = true;
 
       if (! skip)
        {
          if (isym.n_sclass == C_EXT
+             || isym.n_sclass == C_WEAKEXT
+             || (obj_pe (input_bfd) && isym.n_sclass == C_NT_WEAK)
+#ifdef C_SYSTEM
+             || isym.n_sclass == C_SYSTEM
+#endif
              || (sym_is_global && (*sym_is_global) (input_bfd, &isym)))
            {
              /* This is a global symbol.  Global symbols come at the
                 end of the symbol table, so skip them for now.
-                Function symbols, however, are an exception, and are
-                not moved to the end.  */
+                Locally defined function symbols, however, are an
+                exception, and are not moved to the end.  */
              global = true;
-             if (! ISFCN (isym.n_type))
+             if (! ISFCN (isym.n_type) || isym.n_scnum == 0)
                skip = true;
            }
          else
            {
              /* This is a local symbol.  Skip it if we are discarding
                  local symbols.  */
-             if (finfo->info->discard == discard_all)
+             if (finfo->info->discard == discard_all && ! dont_skip_symbol)
                skip = true;
            }
        }
 
       /* If we stripping debugging symbols, and this is a debugging
-         symbol, then skip it.  */
+         symbol, then skip it.  FIXME: gas sets the section to N_ABS
+         for some types of debugging symbols; I don't know if this is
+         a bug or not.  In any case, we handle it here.  */
       if (! skip
          && finfo->info->strip == strip_debugger
-         && isym.n_scnum == N_DEBUG)
+         && ! dont_skip_symbol
+         && (isym.n_scnum == N_DEBUG
+             || (isym.n_scnum == N_ABS
+                 && (isym.n_sclass == C_AUTO
+                     || isym.n_sclass == C_REG
+                     || isym.n_sclass == C_MOS
+                     || isym.n_sclass == C_MOE
+                     || isym.n_sclass == C_MOU
+                     || isym.n_sclass == C_ARG
+                     || isym.n_sclass == C_REGPARM
+                     || isym.n_sclass == C_FIELD
+                     || isym.n_sclass == C_EOS))))
        skip = true;
 
       /* If some symbols are stripped based on the name, work out the
@@ -1364,32 +1391,185 @@ coff_link_input_bfd (finfo, input_bfd)
          const char *name;
          char buf[SYMNMLEN + 1];
 
-         if (isym._n._n_n._n_zeroes == 0
-             && isym._n._n_n._n_offset != 0)
+         name = _bfd_coff_internal_syment_name (input_bfd, &isym, buf);
+         if (name == NULL)
+           return false;
+
+         if (! dont_skip_symbol
+             && ((finfo->info->strip == strip_some
+                  && (bfd_hash_lookup (finfo->info->keep_hash, name, false,
+                                   false) == NULL))
+                  || (! global
+                      && finfo->info->discard == discard_l
+                      && bfd_is_local_label_name (input_bfd, name))))
+           skip = true;
+       }
+
+      /* If this is an enum, struct, or union tag, see if we have
+         already output an identical type.  */
+      if (! skip
+         && (finfo->output_bfd->flags & BFD_TRADITIONAL_FORMAT) == 0
+         && (isym.n_sclass == C_ENTAG
+             || isym.n_sclass == C_STRTAG
+             || isym.n_sclass == C_UNTAG)
+         && isym.n_numaux == 1)
+       {
+         const char *name;
+         char buf[SYMNMLEN + 1];
+         struct coff_debug_merge_hash_entry *mh;
+         struct coff_debug_merge_type *mt;
+         union internal_auxent aux;
+         struct coff_debug_merge_element **epp;
+         bfd_byte *esl, *eslend;
+         struct internal_syment *islp;
+
+         name = _bfd_coff_internal_syment_name (input_bfd, &isym, buf);
+         if (name == NULL)
+           return false;
+
+         /* Ignore fake names invented by compiler; treat them all as
+             the same name.  */
+         if (*name == '~' || *name == '.' || *name == '$'
+             || (*name == bfd_get_symbol_leading_char (input_bfd)
+                 && (name[1] == '~' || name[1] == '.' || name[1] == '$')))
+           name = "";
+
+         mh = coff_debug_merge_hash_lookup (&finfo->debug_merge, name,
+                                            true, true);
+         if (mh == NULL)
+           return false;
+
+         /* Allocate memory to hold type information.  If this turns
+             out to be a duplicate, we pass this address to
+             bfd_release.  */
+         mt = ((struct coff_debug_merge_type *)
+               bfd_alloc (input_bfd,
+                          sizeof (struct coff_debug_merge_type)));
+         if (mt == NULL)
+           return false;
+         mt->class = isym.n_sclass;
+
+         /* Pick up the aux entry, which points to the end of the tag
+             entries.  */
+         bfd_coff_swap_aux_in (input_bfd, (PTR) (esym + isymesz),
+                               isym.n_type, isym.n_sclass, 0, isym.n_numaux,
+                               (PTR) &aux);
+
+         /* Gather the elements.  */
+         epp = &mt->elements;
+         mt->elements = NULL;
+         islp = isymp + 2;
+         esl = esym + 2 * isymesz;
+         eslend = ((bfd_byte *) obj_coff_external_syms (input_bfd)
+                   + aux.x_sym.x_fcnary.x_fcn.x_endndx.l * isymesz);
+         while (esl < eslend)
            {
-             if (strings == NULL)
+             const char *elename;
+             char elebuf[SYMNMLEN + 1];
+             char *copy;
+
+             bfd_coff_swap_sym_in (input_bfd, (PTR) esl, (PTR) islp);
+
+             *epp = ((struct coff_debug_merge_element *)
+                     bfd_alloc (input_bfd,
+                                sizeof (struct coff_debug_merge_element)));
+             if (*epp == NULL)
+               return false;
+
+             elename = _bfd_coff_internal_syment_name (input_bfd, islp,
+                                                       elebuf);
+             if (elename == NULL)
+               return false;
+
+             copy = (char *) bfd_alloc (input_bfd, strlen (elename) + 1);
+             if (copy == NULL)
+               return false;
+             strcpy (copy, elename);
+
+             (*epp)->name = copy;
+             (*epp)->type = islp->n_type;
+             (*epp)->tagndx = 0;
+             if (islp->n_numaux >= 1
+                 && islp->n_type != T_NULL
+                 && islp->n_sclass != C_EOS)
                {
-                 strings = coff_read_string_table (input_bfd);
-                 if (strings == NULL)
-                   return false;
+                 union internal_auxent eleaux;
+                 long indx;
+
+                 bfd_coff_swap_aux_in (input_bfd, (PTR) (esl + isymesz),
+                                       islp->n_type, islp->n_sclass, 0,
+                                       islp->n_numaux, (PTR) &eleaux);
+                 indx = eleaux.x_sym.x_tagndx.l;
+
+                 /* FIXME: If this tagndx entry refers to a symbol
+                    defined later in this file, we just ignore it.
+                    Handling this correctly would be tedious, and may
+                    not be required.  */
+
+                 if (indx > 0
+                     && (indx
+                         < ((esym -
+                             (bfd_byte *) obj_coff_external_syms (input_bfd))
+                            / (long) isymesz)))
+                   {
+                     (*epp)->tagndx = finfo->sym_indices[indx];
+                     if ((*epp)->tagndx < 0)
+                       (*epp)->tagndx = 0;
+                   }
                }
-             name = strings + isym._n._n_n._n_offset;
+             epp = &(*epp)->next;
+             *epp = NULL;
+
+             esl += (islp->n_numaux + 1) * isymesz;
+             islp += islp->n_numaux + 1;
            }
+
+         /* See if we already have a definition which matches this
+             type.  We always output the type if it has no elements,
+             for simplicity.  */
+         if (mt->elements == NULL)
+           bfd_release (input_bfd, (PTR) mt);
          else
            {
-             memcpy (buf, isym._n._n_name, SYMNMLEN);
-             buf[SYMNMLEN] = '\0';
-             name = buf;
-           }
+             struct coff_debug_merge_type *mtl;
 
-         if ((finfo->info->strip == strip_some
-              && (bfd_hash_lookup (finfo->info->keep_hash, name, false,
-                                   false) == NULL))
-             || (! global
-                 && finfo->info->discard == discard_l
-                 && strncmp (name, finfo->info->lprefix,
-                             finfo->info->lprefix_len) == 0))
-           skip = true;
+             for (mtl = mh->types; mtl != NULL; mtl = mtl->next)
+               {
+                 struct coff_debug_merge_element *me, *mel;
+
+                 if (mtl->class != mt->class)
+                   continue;
+
+                 for (me = mt->elements, mel = mtl->elements;
+                      me != NULL && mel != NULL;
+                      me = me->next, mel = mel->next)
+                   {
+                     if (strcmp (me->name, mel->name) != 0
+                         || me->type != mel->type
+                         || me->tagndx != mel->tagndx)
+                       break;
+                   }
+
+                 if (me == NULL && mel == NULL)
+                   break;
+               }
+
+             if (mtl == NULL || (bfd_size_type) mtl->indx >= syment_base)
+               {
+                 /* This is the first definition of this type.  */
+                 mt->indx = output_index;
+                 mt->next = mh->types;
+                 mh->types = mt;
+               }
+             else
+               {
+                 /* This is a redefinition which can be merged.  */
+                 bfd_release (input_bfd, (PTR) mt);
+                 *indexp = mtl->indx;
+                 add = (eslend - esym) / isymesz;
+                 skip = true;
+               }
+           }
        }
 
       /* We now know whether we are to skip this symbol or not.  */
@@ -1408,14 +1588,10 @@ coff_link_input_bfd (finfo, input_bfd)
                 bfd_coff_symname_in_debug.  That is only true for
                 XCOFF, and XCOFF requires different linking code
                 anyhow.  */
-             BFD_ASSERT (isym._n._n_n._n_offset >= STRING_SIZE_SIZE);
-             if (strings == NULL)
-               {
-                 strings = coff_read_string_table (input_bfd);
-                 if (strings == NULL)
-                   return false;
-               }
-             name = strings + isym._n._n_n._n_offset;
+             name = _bfd_coff_internal_syment_name (input_bfd, &isym,
+                                                    (char *) NULL);
+             if (name == NULL)
+               return false;
              indx = _bfd_stringtab_add (finfo->strtab, name, hash, copy);
              if (indx == (bfd_size_type) -1)
                return false;
@@ -1425,9 +1601,11 @@ coff_link_input_bfd (finfo, input_bfd)
          if (isym.n_scnum > 0)
            {
              isym.n_scnum = (*secpp)->output_section->target_index;
-             isym.n_value += ((*secpp)->output_section->vma
-                              + (*secpp)->output_offset
-                              - (*secpp)->vma);
+             isym.n_value += (*secpp)->output_offset;
+             if (! obj_pe (input_bfd))
+               isym.n_value -= (*secpp)->vma;
+             if (! obj_pe (finfo->output_bfd))
+               isym.n_value += (*secpp)->output_section->vma;
            }
 
          /* The value of a C_FILE symbol is the symbol index of the
@@ -1442,11 +1620,11 @@ coff_link_input_bfd (finfo, input_bfd)
          if (isym.n_sclass == C_FILE)
            {
              if (finfo->last_file_index != -1
-                 && finfo->last_file.n_value != output_index)
+                 && finfo->last_file.n_value != (long) output_index)
                {
                  /* We must correct the value of the last C_FILE entry.  */
                  finfo->last_file.n_value = output_index;
-                 if (finfo->last_file_index >= syment_base)
+                 if ((bfd_size_type) finfo->last_file_index >= syment_base)
                    {
                      /* The last C_FILE symbol is in this input file.  */
                      bfd_coff_swap_sym_out (output_bfd,
@@ -1478,6 +1656,15 @@ coff_link_input_bfd (finfo, input_bfd)
              finfo->last_file = isym;
            }
 
+         /* If doing task linking, convert normal global function symbols to
+            static functions. */
+
+         if (finfo->info->task_link
+             && (isym.n_sclass == C_EXT
+                 || isym.n_sclass == C_WEAKEXT
+                 || (obj_pe (input_bfd) && isym.n_sclass == C_NT_WEAK)))
+           isym.n_sclass = C_STAT;
+
          /* Output the symbol.  */
 
          bfd_coff_swap_sym_out (output_bfd, (PTR) &isym, (PTR) outsym);
@@ -1492,7 +1679,13 @@ coff_link_input_bfd (finfo, input_bfd)
              indx = ((esym - (bfd_byte *) obj_coff_external_syms (input_bfd))
                      / isymesz);
              h = obj_coff_sym_hashes (input_bfd)[indx];
-             BFD_ASSERT (h != NULL);
+             if (h == NULL)
+               {
+                 /* This can happen if there were errors earlier in
+                     the link.  */
+                 bfd_set_error (bfd_error_bad_value);
+                 return false;
+               }
              h->indx = output_index;
            }
 
@@ -1527,7 +1720,8 @@ coff_link_input_bfd (finfo, input_bfd)
 
       add = 1 + isymp->n_numaux;
 
-      if (*indexp < 0
+      if ((*indexp < 0
+          || (bfd_size_type) *indexp < syment_base)
          && (*sym_hash == NULL
              || (*sym_hash)->auxbfd != input_bfd))
        esym += add * isymesz;
@@ -1540,7 +1734,12 @@ coff_link_input_bfd (finfo, input_bfd)
          if (*indexp < 0)
            {
              h = *sym_hash;
-             BFD_ASSERT (h->numaux == isymp->n_numaux);
+
+             /* The m68k-motorola-sysv assembler will sometimes
+                 generate two symbols with the same name, but only one
+                 will have aux entries.  */
+             BFD_ASSERT (isymp->n_numaux == 0
+                         || h->numaux == isymp->n_numaux);
            }
 
          esym += isymesz;
@@ -1579,7 +1778,7 @@ coff_link_input_bfd (finfo, input_bfd)
                                  >= STRING_SIZE_SIZE);
                      if (strings == NULL)
                        {
-                         strings = coff_read_string_table (input_bfd);
+                         strings = _bfd_coff_read_string_table (input_bfd);
                          if (strings == NULL)
                            return false;
                        }
@@ -1593,11 +1792,12 @@ coff_link_input_bfd (finfo, input_bfd)
                }
              else if (isymp->n_sclass != C_STAT || isymp->n_type != T_NULL)
                {
-                 long indx;
+                 unsigned long indx;
 
                  if (ISFCN (isymp->n_type)
                      || ISTAG (isymp->n_sclass)
-                     || isymp->n_sclass == C_BLOCK)
+                     || isymp->n_sclass == C_BLOCK
+                     || isymp->n_sclass == C_FCN)
                    {
                      indx = auxp->x_sym.x_fcnary.x_fcn.x_endndx.l;
                      if (indx > 0
@@ -1607,7 +1807,9 @@ coff_link_input_bfd (finfo, input_bfd)
                              the index of the next symbol we are going
                              to include.  I don't know if this is
                              entirely right.  */
-                         while (finfo->sym_indices[indx] < 0
+                         while ((finfo->sym_indices[indx] < 0
+                                 || ((bfd_size_type) finfo->sym_indices[indx]
+                                     < syment_base))
                                 && indx < obj_raw_syment_count (input_bfd))
                            ++indx;
                          if (indx >= obj_raw_syment_count (input_bfd))
@@ -1621,11 +1823,88 @@ coff_link_input_bfd (finfo, input_bfd)
                  indx = auxp->x_sym.x_tagndx.l;
                  if (indx > 0 && indx < obj_raw_syment_count (input_bfd))
                    {
-                     indx = finfo->sym_indices[indx];
-                     if (indx < 0)
+                     long symindx;
+
+                     symindx = finfo->sym_indices[indx];
+                     if (symindx < 0)
                        auxp->x_sym.x_tagndx.l = 0;
                      else
-                       auxp->x_sym.x_tagndx.l = indx;
+                       auxp->x_sym.x_tagndx.l = symindx;
+                   }
+
+                 /* The .bf symbols are supposed to be linked through
+                    the endndx field.  We need to carry this list
+                    across object files.  */
+                 if (i == 0
+                     && h == NULL
+                     && isymp->n_sclass == C_FCN
+                     && (isymp->_n._n_n._n_zeroes != 0
+                         || isymp->_n._n_n._n_offset == 0)
+                     && isymp->_n._n_name[0] == '.'
+                     && isymp->_n._n_name[1] == 'b'
+                     && isymp->_n._n_name[2] == 'f'
+                     && isymp->_n._n_name[3] == '\0')
+                   {
+                     if (finfo->last_bf_index != -1)
+                       {
+                         finfo->last_bf.x_sym.x_fcnary.x_fcn.x_endndx.l =
+                           *indexp;
+
+                         if ((bfd_size_type) finfo->last_bf_index
+                             >= syment_base)
+                           {
+                             PTR auxout;
+
+                             /* The last .bf symbol is in this input
+                                file.  This will only happen if the
+                                assembler did not set up the .bf
+                                endndx symbols correctly.  */
+                             auxout = (PTR) (finfo->outsyms
+                                             + ((finfo->last_bf_index
+                                                 - syment_base)
+                                                * osymesz));
+                             bfd_coff_swap_aux_out (output_bfd,
+                                                    (PTR) &finfo->last_bf,
+                                                    isymp->n_type,
+                                                    isymp->n_sclass,
+                                                    0, isymp->n_numaux,
+                                                    auxout);
+                           }
+                         else
+                           {
+                             /* We have already written out the last
+                                 .bf aux entry.  We need to write it
+                                 out again.  We borrow *outsym
+                                 temporarily.  FIXME: This case should
+                                 be made faster.  */
+                             bfd_coff_swap_aux_out (output_bfd,
+                                                    (PTR) &finfo->last_bf,
+                                                    isymp->n_type,
+                                                    isymp->n_sclass,
+                                                    0, isymp->n_numaux,
+                                                    (PTR) outsym);
+                             if (bfd_seek (output_bfd,
+                                           (obj_sym_filepos (output_bfd)
+                                            + finfo->last_bf_index * osymesz),
+                                           SEEK_SET) != 0
+                                 || bfd_write (outsym, osymesz, 1,
+                                               output_bfd) != osymesz)
+                               return false;
+                           }
+                       }
+
+                     if (auxp->x_sym.x_fcnary.x_fcn.x_endndx.l != 0)
+                       finfo->last_bf_index = -1;
+                     else
+                       {
+                         /* The endndx field of this aux entry must
+                             be updated with the symbol number of the
+                             next .bf symbol.  */
+                         finfo->last_bf = *auxp;
+                         finfo->last_bf_index = (((outsym - finfo->outsyms)
+                                                  / osymesz)
+                                                 + syment_base);
+                       }
                    }
                }
 
@@ -1656,7 +1935,17 @@ coff_link_input_bfd (finfo, input_bfd)
          bfd_byte *eline;
          bfd_byte *elineend;
 
-         if (o->lineno_count == 0)
+         /* FIXME: If SEC_HAS_CONTENTS is not for the section, then
+            build_link_order in ldwrite.c will not have created a
+            link order, which means that we will not have seen this
+            input section in _bfd_coff_final_link, which means that
+            we will not have allocated space for the line numbers of
+            this section.  I don't think line numbers can be
+            meaningful for a section which does not have
+            SEC_HAS_CONTENTS set, but, if they do, this must be
+            changed.  */
+         if (o->lineno_count == 0
+             || (o->output_section->flags & SEC_HAS_CONTENTS) == 0)
            continue;
 
          if (bfd_seek (input_bfd, o->line_filepos, SEEK_SET) != 0
@@ -1676,7 +1965,7 @@ coff_link_input_bfd (finfo, input_bfd)
              if (iline.l_lnno != 0)
                iline.l_addr.l_paddr += offset;
              else if (iline.l_addr.l_symndx >= 0
-                      && (iline.l_addr.l_symndx
+                      && ((unsigned long) iline.l_addr.l_symndx
                           < obj_raw_syment_count (input_bfd)))
                {
                  long indx;
@@ -1754,7 +2043,7 @@ coff_link_input_bfd (finfo, input_bfd)
      normal case, this will save us from writing out the C_FILE symbol
      again.  */
   if (finfo->last_file_index != -1
-      && finfo->last_file_index >= syment_base)
+      && (bfd_size_type) finfo->last_file_index >= syment_base)
     {
       finfo->last_file.n_value = output_index;
       bfd_coff_swap_sym_out (output_bfd, (PTR) &finfo->last_file,
@@ -1769,8 +2058,9 @@ coff_link_input_bfd (finfo, input_bfd)
       if (bfd_seek (output_bfd,
                    obj_sym_filepos (output_bfd) + syment_base * osymesz,
                    SEEK_SET) != 0
-         || bfd_write (finfo->outsyms, outsym - finfo->outsyms, 1,
-                       output_bfd) != outsym - finfo->outsyms)
+         || (bfd_write (finfo->outsyms, outsym - finfo->outsyms, 1,
+                       output_bfd)
+             != (bfd_size_type) (outsym - finfo->outsyms)))
        return false;
 
       BFD_ASSERT ((obj_raw_syment_count (output_bfd)
@@ -1781,53 +2071,69 @@ coff_link_input_bfd (finfo, input_bfd)
     }
 
   /* Relocate the contents of each section.  */
-  relsz = bfd_coff_relsz (input_bfd);
   adjust_symndx = coff_backend_info (input_bfd)->_bfd_coff_adjust_symndx;
   for (o = input_bfd->sections; o != NULL; o = o->next)
     {
-      if ((o->flags & SEC_HAS_CONTENTS) == 0)
-       continue;
+      bfd_byte *contents;
+      struct coff_section_tdata *secdata;
+
+      if (! o->linker_mark)
+       {
+         /* This section was omitted from the link.  */
+         continue;
+       }
+
+      if ((o->flags & SEC_HAS_CONTENTS) == 0
+         || (o->_raw_size == 0 && (o->flags & SEC_RELOC) == 0))
+       {
+         if ((o->flags & SEC_RELOC) != 0
+             && o->reloc_count != 0)
+           {
+             ((*_bfd_error_handler)
+              (_("%s: relocs in section `%s', but it has no contents"),
+               bfd_get_filename (input_bfd),
+               bfd_get_section_name (input_bfd, o)));
+             bfd_set_error (bfd_error_no_contents);
+             return false;
+           }
+
+         continue;
+       }
 
-      if (! bfd_get_section_contents (input_bfd, o, finfo->contents,
-                                     (file_ptr) 0, o->_raw_size))
-       return false;
+      secdata = coff_section_data (input_bfd, o);
+      if (secdata != NULL && secdata->contents != NULL)
+       contents = secdata->contents;
+      else
+       {
+         if (! bfd_get_section_contents (input_bfd, o, finfo->contents,
+                                         (file_ptr) 0, o->_raw_size))
+           return false;
+         contents = finfo->contents;
+       }
 
       if ((o->flags & SEC_RELOC) != 0)
        {
          int target_index;
          struct internal_reloc *internal_relocs;
-         bfd_byte *erel;
-         bfd_byte *erel_end;
          struct internal_reloc *irel;
 
          /* Read in the relocs.  */
-         if (bfd_seek (input_bfd, o->rel_filepos, SEEK_SET) != 0
-             || (bfd_read (finfo->external_relocs, relsz, o->reloc_count,
-                           input_bfd) != relsz * o->reloc_count))
-           return false;
-
-         /* If we are doing a relocateable link, we keep the swapped
-            in relocs in memory, and don't write them out until the
-            end of the link.  */
          target_index = o->output_section->target_index;
-         if (! finfo->info->relocateable)
-           internal_relocs = finfo->internal_relocs;
-         else
-           internal_relocs = (finfo->section_info[target_index].relocs
-                              + o->output_section->reloc_count);
-
-         /* Swap in the relocs.  */
-         erel = finfo->external_relocs;
-         erel_end = erel + relsz * o->reloc_count;
-         irel = internal_relocs;
-         for (; erel < erel_end; erel += relsz, irel++)
-           bfd_coff_swap_reloc_in (input_bfd, (PTR) erel, (PTR) irel);
+         internal_relocs = (_bfd_coff_read_internal_relocs
+                            (input_bfd, o, false, finfo->external_relocs,
+                             finfo->info->relocateable,
+                             (finfo->info->relocateable
+                              ? (finfo->section_info[target_index].relocs
+                                 + o->output_section->reloc_count)
+                              : finfo->internal_relocs)));
+         if (internal_relocs == NULL)
+           return false;
 
          /* Call processor specific code to relocate the section
              contents.  */
          if (! bfd_coff_relocate_section (output_bfd, finfo->info,
                                           input_bfd, o,
-                                          finfo->contents,
+                                          contents,
                                           internal_relocs,
                                           finfo->internal_syms,
                                           finfo->sec_ptrs))
@@ -1900,28 +2206,16 @@ coff_link_input_bfd (finfo, input_bfd)
                          char buf[SYMNMLEN + 1];
 
                          /* This reloc is against a symbol we are
-                             stripping.  It would be possible to
-                             handle this case, but I don't think it's
-                             worth it.  */
+                             stripping.  This should have been handled
+                            by the 'dont_skip_symbol' code in the while
+                            loop at the top of this function. */
+                         
                          is = finfo->internal_syms + irel->r_symndx;
 
-                         if (is->_n._n_n._n_zeroes == 0
-                             && is->_n._n_n._n_offset != 0)
-                           {
-                             if (strings == NULL)
-                               {
-                                 strings = coff_read_string_table (input_bfd);
-                                 if (strings == NULL)
-                                   return false;
-                               }
-                             name = strings + is->_n._n_n._n_offset;
-                           }
-                         else
-                           {
-                             memcpy (buf, is->_n._n_name, SYMNMLEN);
-                             buf[SYMNMLEN] = '\0';
-                             name = buf;
-                           }
+                         name = (_bfd_coff_internal_syment_name
+                                 (input_bfd, is, buf));
+                         if (name == NULL)
+                           return false;
 
                          if (! ((*finfo->info->callbacks->unattached_reloc)
                                 (finfo->info, name, input_bfd, o,
@@ -1936,17 +2230,27 @@ coff_link_input_bfd (finfo, input_bfd)
        }
 
       /* Write out the modified section contents.  */
-      if (! bfd_set_section_contents (output_bfd, o->output_section,
-                                     finfo->contents, o->output_offset,
-                                     (o->_cooked_size != 0
-                                      ? o->_cooked_size
-                                      : o->_raw_size)))
-       return false;
+      if (secdata == NULL || secdata->stab_info == NULL)
+       {
+         if (! bfd_set_section_contents (output_bfd, o->output_section,
+                                         contents, o->output_offset,
+                                         (o->_cooked_size != 0
+                                          ? o->_cooked_size
+                                          : o->_raw_size)))
+           return false;
+       }
+      else
+       {
+         if (! (_bfd_write_section_stabs
+                (output_bfd, &coff_hash_table (finfo->info)->stab_info,
+                 o, &secdata->stab_info, contents)))
+           return false;
+       }
     }
 
   if (! finfo->info->keep_memory)
     {
-      if (! coff_link_free_symbols (input_bfd))
+      if (! _bfd_coff_free_symbols (input_bfd))
        return false;
     }
 
@@ -1955,8 +2259,8 @@ coff_link_input_bfd (finfo, input_bfd)
 
 /* Write out a global symbol.  Called via coff_link_hash_traverse.  */
 
-static boolean
-coff_write_global_sym (h, data)
+boolean
+_bfd_coff_write_global_sym (h, data)
      struct coff_link_hash_entry *h;
      PTR data;
 {
@@ -2003,8 +2307,9 @@ coff_write_global_sym (h, data)
        else
          isym.n_scnum = sec->target_index;
        isym.n_value = (h->root.u.def.value
-                       + sec->vma
                        + h->root.u.def.section->output_offset);
+       if (! obj_pe (finfo->output_bfd))
+         isym.n_value += sec->vma;
       }
       break;
 
@@ -2046,6 +2351,21 @@ coff_write_global_sym (h, data)
   if (isym.n_sclass == C_NULL)
     isym.n_sclass = C_EXT;
 
+  /* If doing task linking and this is the pass where we convert
+     defined globals to statics, then do that conversion now.  If the
+     symbol is not being converted, just ignore it and it will be
+     output during a later pass. */
+  if (finfo->global_to_static)
+    {
+      if (isym.n_sclass != C_EXT
+         && isym.n_sclass != C_WEAKEXT
+         && (! obj_pe (output_bfd) || isym.n_sclass != C_NT_WEAK))
+       {
+         return true;
+       }
+      isym.n_sclass = C_STAT;
+    }
+
   isym.n_numaux = h->numaux;
   
   bfd_coff_swap_sym_out (output_bfd, (PTR) &isym, (PTR) finfo->outsyms);
@@ -2084,10 +2404,41 @@ coff_write_global_sym (h, data)
   return true;
 }
 
+/* Write out task global symbols, converting them to statics.  Called
+   via coff_link_hash_traverse.  Calls bfd_coff_write_global_sym to do
+   the dirty work, if the symbol we are processing needs conversion. */
+
+boolean
+_bfd_coff_write_task_globals (h, data)
+     struct coff_link_hash_entry *h;
+     PTR data;
+{
+  struct coff_final_link_info *finfo = (struct coff_final_link_info *) data;
+  boolean rtnval = true;
+  boolean save_global_to_static;
+
+  if (h->indx < 0)
+    {
+      switch (h->root.type)
+       {
+       case bfd_link_hash_defined:
+       case bfd_link_hash_defweak:
+         save_global_to_static = finfo->global_to_static;
+         finfo->global_to_static = true;
+         rtnval = _bfd_coff_write_global_sym (h, data);
+         finfo->global_to_static = save_global_to_static;
+         break;
+       default:
+         break;
+       }
+    }
+  return (rtnval);
+}
+
 /* Handle a link order which is supposed to generate a reloc.  */
 
-static boolean
-coff_reloc_link_order (output_bfd, finfo, output_section, link_order)
+boolean
+_bfd_coff_reloc_link_order (output_bfd, finfo, output_section, link_order)
      bfd *output_bfd;
      struct coff_final_link_info *finfo;
      asection *output_section;
@@ -2114,10 +2465,7 @@ coff_reloc_link_order (output_bfd, finfo, output_section, link_order)
       size = bfd_get_reloc_size (howto);
       buf = (bfd_byte *) bfd_zmalloc (size);
       if (buf == NULL)
-       {
-         bfd_set_error (bfd_error_no_memory);
-         return false;
-       }
+       return false;
 
       rstat = _bfd_relocate_contents (howto, output_bfd,
                                      link_order->u.reloc.p->addend, buf);
@@ -2177,9 +2525,10 @@ coff_reloc_link_order (output_bfd, finfo, output_section, link_order)
     {
       struct coff_link_hash_entry *h;
 
-      h = coff_link_hash_lookup (coff_hash_table (finfo->info),
-                                link_order->u.reloc.p->u.name,
-                                false, false, true);
+      h = ((struct coff_link_hash_entry *)
+          bfd_wrapped_link_hash_lookup (output_bfd, finfo->info,
+                                        link_order->u.reloc.p->u.name,
+                                        false, false, true));
       if (h != NULL)
        {
          if (h->indx >= 0)
@@ -2235,7 +2584,6 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
   struct internal_reloc *rel;
   struct internal_reloc *relend;
 
-
   rel = relocs;
   relend = rel + input_section->reloc_count;
   for (; rel < relend; rel++)
@@ -2277,21 +2625,23 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
       if (howto == NULL)
        return false;
 
-      /* WINDOWS_NT; in this next section, the value of 'val' will be computed.
-         With respect to the .idata and .rsrc sections, the NT_IMAGE_BASE
-         must be removed from the value that is to be relocated (NT_IMAGE_BASE
-         is currently defined in internal.h and has value 400000).  Now this
-         value should only be removed from addresses being relocated in the
-         .idata and .rsrc sections, not the .text section which should have
-         the 'real' address.  In addition, the .rsrc val's must also be
-         adjusted by the input_section->vma.  */
+      /* If we are doing a relocateable link, then we can just ignore
+         a PC relative reloc that is pcrel_offset.  It will already
+         have the correct value.  If this is not a relocateable link,
+         then we should ignore the symbol value.  */
+      if (howto->pc_relative && howto->pcrel_offset)
+       {
+         if (info->relocateable)
+           continue;
+         if (sym != NULL && sym->n_scnum != 0)
+           addend += sym->n_value;
+       }
 
       val = 0;
 
       if (h == NULL)
        {
          asection *sec;
-          int i;
 
          if (symndx == -1)
            {
@@ -2303,26 +2653,9 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
              sec = sections[symndx];
               val = (sec->output_section->vma
                     + sec->output_offset
-                    + sym->n_value
-                    - sec->vma);
-             if (obj_pe(output_bfd)) {
-               /* Make a correction here to val if the sec is either .rsrc
-                  or .idata */
-               if (strcmp (input_section->name, ".text") != 0)
-                 {
-                   if (strncmp (sec->name, ".idata$", 7) == 0)
-                     val -= NT_IMAGE_BASE;
-                   if (strncmp (sec->name, ".reloc", 6) == 0)
-                     val -= NT_IMAGE_BASE;
-                   else if (strncmp (sec->name, ".edata", 5) == 0)
-                     val -= NT_IMAGE_BASE;
-                   else if (strncmp (sec->name, ".rsrc$", 6) == 0) 
-                     {
-                       val -= NT_IMAGE_BASE;
-                       val += sec->vma;
-                     }
-                 }
-             }
+                    + sym->n_value);
+             if (! obj_pe (input_bfd))
+               val -= sec->vma;
            }
        }
       else
@@ -2336,25 +2669,8 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
              val = (h->root.u.def.value
                     + sec->output_section->vma
                     + sec->output_offset);
-             if (obj_pe (output_bfd)) {
-               /* Make a correction here to val if the sec is either .rsrc
-                  or .idata */
-               if (strcmp (input_section->name, ".text") != 0)
-                 {
-                   if (strncmp (sec->name, ".idata$", 7) == 0)
-                     val -= NT_IMAGE_BASE;
-                   else if (strncmp (sec->name, ".reloc", 5) == 0)
-                     val -= NT_IMAGE_BASE;
-                   else if (strncmp (sec->name, ".edata", 5) == 0)
-                     val -= NT_IMAGE_BASE;
-                   else if (strncmp (sec->name, ".rsrc$", 6) == 0) 
-                     {
-                       val -= NT_IMAGE_BASE;
-                       val += sec->vma;
-                     }
-                 }
              }
-           }
+
          else if (! info->relocateable)
            {
              if (! ((*info->callbacks->undefined_symbol)
@@ -2364,117 +2680,29 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
            }
        }
 
-      if (obj_pe (output_bfd)) {
-
-       /* Here's where we will collect the information about the dll .idata$4 
-          and 5 entries and fix up the vals for .idata$2 information.  When
-          we encounter processing for .idata$5 (this could also be done for
-          .idata$4) we will keep track of the number of entries made for a
-          particular dll.  Now if we are processing .idata$2 input_section,
-          then we know how many entries have been made from each dll and we
-          have to fix up the .idata$2 start addresses for .idata$4 and 
-          .idata$5. */
-       add_to_val = 0;
-       if (strncmp (input_section->name, ".idata$5", 8) == 0)
-         {
-           if (num_DLLs == 0)  /* this is the first one */
-             {
-               num_DLLs += 1;
-               MS_DLL[num_DLLs].DLL_name = input_bfd->filename;
-               MS_DLL[num_DLLs].num_entries += 1;
-             }
-           else if (!strcmp (input_bfd->filename, MS_DLL[num_DLLs].DLL_name))
-             {
-               /* this is just another entry */
-               MS_DLL[num_DLLs].num_entries += 1;
-             }
-           else
-             {
-               /* This is a new DLL */
-               num_DLLs += 1;
-               MS_DLL[num_DLLs].DLL_name = input_bfd->filename;
-               MS_DLL[num_DLLs].num_entries += 1; 
-             }
-           all_entries += 1;
-         }
-
-       else if (strncmp (input_section->name, ".idata$2", 8) == 0)
-         {
-           /* All information about the number of entries needed from each
-              DLL has been collected at this point.  Now we actually want to
-              make and adjustment to the val's for .idata$4 and .idata$5
-              which are part of the .idata$2 section. */
-           /* first we have to get the symbol name from sym.  This will be
-              either .idata$4, .idata$5 or .idata$6.  A 'fixup' is computed for
-              .idata$4 and .idata$5 but not for .idata$6 (this value is handled
-              correctly already and doesn't have to be fixed) */
-           const char *name;
-           char buf[SYMNMLEN + 1];
-
-           if (sym->_n._n_n._n_zeroes == 0 && sym->_n._n_n._n_offset != 0)
-             name = obj_coff_strings (input_bfd) + sym->_n._n_n._n_offset;
-           else
-             {
-               strncpy (buf, sym->_n._n_name, SYMNMLEN);
-               buf[SYMNMLEN] = '\0';
-               name = buf;
-             }
-
-           if (num_DLLs_done)
-             {
-               /* we have done at least one.  The val fixups are based on the
-                  previous fixups */
-               if (strncmp (name, ".idata$4", 8) == 0)
-                 {
-                   add_to_val = idata_4_prev +
-                     ((MS_DLL[num_DLLs_done].num_entries + 1) * 4);
-                   idata_4_prev = add_to_val;
-                 }
-               else if (strncmp (name, ".idata$5", 8) == 0)
-                 {
-                   add_to_val = idata_5_prev +
-                     ((MS_DLL[num_DLLs_done].num_entries + 1) * 4);
-                   idata_5_prev = add_to_val;
-                   num_DLLs_done += 1; /* assuming that idata$5 is done after $4*/
-                 }
-             }
-           else
-             {
-               /* This is the first one.  The other idata$4 and 5 entries will be
-                  computed from these */
-               if (strncmp (name, ".idata$4", 8) == 0)
-                 {
-                   add_to_val = ((num_DLLs - 1) * 0x14) + 0x28;
-                   idata_4_prev = add_to_val;
-                 }
-               else if (strncmp (name, ".idata$5", 8) == 0)
-                 {
-                   add_to_val = idata_4_prev + (all_entries + num_DLLs) * 4;
-                   idata_5_prev = add_to_val;
-                   num_DLLs_done += 1; /* assuming that idata$5 is done after $4*/
-                 }
-            
-             }
-         }
-       val = val + add_to_val;
-       
-      }
-
       if (info->base_file)
        {
-         /* So if this is non pcrelative, and is referenced
-            to a section or a common symbol, then it needs a reloc */
-         if (!howto->pc_relative
-             && (sym->n_scnum
-                 || sym->n_value))
+         /* Emit a reloc if the backend thinks it needs it. */
+         if (sym && pe_data (output_bfd)->in_reloc_p (output_bfd, howto))
            {
-             /* relocation to a symbol in a section which
-                isn't absolute - we output the address here 
-                to a file */
-             bfd_vma addr = rel->r_vaddr +
-               + input_section->output_offset 
-                 + input_section->output_section->vma;
-             fwrite (&addr, 1,4, info->base_file);
+             /* Relocation to a symbol in a section which isn't
+                absolute.  We output the address here to a file.
+                This file is then read by dlltool when generating the
+                reloc section.  Note that the base file is not
+                portable between systems.  We write out a long here,
+                and dlltool reads in a long.  */
+             long addr = (rel->r_vaddr 
+                          - input_section->vma 
+                          + input_section->output_offset 
+                          + input_section->output_section->vma);
+             if (coff_data (output_bfd)->pe)
+               addr -= pe_data(output_bfd)->pe_opthdr.ImageBase;
+             if (fwrite (&addr, 1, sizeof (long), (FILE *) info->base_file)
+                 != sizeof (long))
+               {
+                 bfd_set_error (bfd_error_system_call);
+                 return false;
+               }
            }
        }
   
@@ -2489,6 +2717,13 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
          abort ();
        case bfd_reloc_ok:
          break;
+       case bfd_reloc_outofrange:
+         (*_bfd_error_handler)
+           (_("%s: bad reloc address 0x%lx in section `%s'"),
+            bfd_get_filename (input_bfd),
+            (unsigned long) rel->r_vaddr,
+            bfd_get_section_name (input_bfd, input_section));
+         return false;
        case bfd_reloc_overflow:
          {
            const char *name;
@@ -2498,14 +2733,11 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
              name = "*ABS*";
            else if (h != NULL)
              name = h->root.root.string;
-           else if (sym->_n._n_n._n_zeroes == 0
-                    && sym->_n._n_n._n_offset != 0)
-             name = obj_coff_strings (input_bfd) + sym->_n._n_n._n_offset;
            else
              {
-               strncpy (buf, sym->_n._n_name, SYMNMLEN);
-               buf[SYMNMLEN] = '\0';
-               name = buf;
+               name = _bfd_coff_internal_syment_name (input_bfd, sym, buf);
+               if (name == NULL)
+                 return false;
              }
 
            if (! ((*info->callbacks->reloc_overflow)
@@ -2515,6 +2747,6 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
          }
        }
     }
-
   return true;
 }
+