gdbarch-selftests.c: No longer error out if debugging something
[deliverable/binutils-gdb.git] / bfd / cofflink.c
index 4913b512a79a2c4a7f096bf1ae72f365f004e48b..b2b64db95002451e9582ba09174fbfd36412f4d2 100644 (file)
 /* COFF specific linker code.
-   Copyright 1994, 1995 Free Software Foundation, Inc.
+   Copyright (C) 1994-2020 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Cygnus Support.
 
-This file is part of BFD, the Binary File Descriptor library.
+   This file is part of BFD, the Binary File Descriptor library.
 
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
 
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+   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., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
 
 /* This file contains the COFF backend linker code.  */
 
-#include "bfd.h"
 #include "sysdep.h"
+#include "bfd.h"
 #include "bfdlink.h"
 #include "libbfd.h"
 #include "coff/internal.h"
 #include "libcoff.h"
+#include "safe-ctype.h"
 
-#define STRING_SIZE_SIZE (4)
+static bfd_boolean coff_link_add_object_symbols (bfd *, struct bfd_link_info *);
+static bfd_boolean coff_link_check_archive_element
+  (bfd *, struct bfd_link_info *, struct bfd_link_hash_entry *, const char *,
+   bfd_boolean *);
+static bfd_boolean coff_link_add_symbols (bfd *, struct bfd_link_info *);
 
-/* 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;
-};
+/* Return TRUE if SYM is a weak, external symbol.  */
+#define IS_WEAK_EXTERNAL(abfd, sym)                    \
+  ((sym).n_sclass == C_WEAKEXT                         \
+   || (obj_pe (abfd) && (sym).n_sclass == C_NT_WEAK))
 
-/* 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;
+/* Return TRUE if SYM is an external symbol.  */
+#define IS_EXTERNAL(abfd, sym)                         \
+  ((sym).n_sclass == C_EXT || IS_WEAK_EXTERNAL (abfd, sym))
 
+/* Define macros so that the ISFCN, et. al., macros work correctly.
+   These macros are defined in include/coff/internal.h in terms of
+   N_TMASK, etc.  These definitions require a user to define local
+   variables with the appropriate names, and with values from the
+   coff_data (abfd) structure.  */
 
+#define N_TMASK n_tmask
+#define N_BTSHFT n_btshft
+#define N_BTMASK n_btmask
 
 /* 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 *entry;
-     struct bfd_hash_table *table;
-     const char *string;
+struct bfd_hash_entry *
+_bfd_coff_link_hash_newfunc (struct bfd_hash_entry *entry,
+                            struct bfd_hash_table *table,
+                            const char *string)
 {
   struct coff_link_hash_entry *ret = (struct coff_link_hash_entry *) entry;
 
@@ -158,10 +69,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 *)
@@ -172,7 +80,7 @@ coff_link_hash_newfunc (entry, table, string)
       /* Set local fields.  */
       ret->indx = -1;
       ret->type = T_NULL;
-      ret->class = C_NULL;
+      ret->symbol_class = C_NULL;
       ret->numaux = 0;
       ret->auxbfd = NULL;
       ret->aux = NULL;
@@ -181,23 +89,35 @@ coff_link_hash_newfunc (entry, table, string)
   return (struct bfd_hash_entry *) ret;
 }
 
+/* Initialize a COFF linker hash table.  */
+
+bfd_boolean
+_bfd_coff_link_hash_table_init (struct coff_link_hash_table *table,
+                               bfd *abfd,
+                               struct bfd_hash_entry *(*newfunc) (struct bfd_hash_entry *,
+                                                                  struct bfd_hash_table *,
+                                                                  const char *),
+                               unsigned int entsize)
+{
+  memset (&table->stab_info, 0, sizeof (table->stab_info));
+  return _bfd_link_hash_table_init (&table->root, abfd, newfunc, entsize);
+}
+
 /* Create a COFF linker hash table.  */
 
 struct bfd_link_hash_table *
-_bfd_coff_link_hash_table_create (abfd)
-     bfd *abfd;
+_bfd_coff_link_hash_table_create (bfd *abfd)
 {
   struct coff_link_hash_table *ret;
+  bfd_size_type amt = sizeof (struct coff_link_hash_table);
 
-  ret = ((struct coff_link_hash_table *)
-        malloc (sizeof (struct coff_link_hash_table)));
+  ret = (struct coff_link_hash_table *) bfd_malloc (amt);
   if (ret == NULL)
-    {
-      bfd_set_error (bfd_error_no_memory);
-      return NULL;
-    }
-  if (! _bfd_link_hash_table_init (&ret->root, abfd,
-                                  coff_link_hash_newfunc))
+    return NULL;
+
+  if (! _bfd_coff_link_hash_table_init (ret, abfd,
+                                       _bfd_coff_link_hash_newfunc,
+                                       sizeof (struct coff_link_hash_entry)))
     {
       free (ret);
       return (struct bfd_link_hash_table *) NULL;
@@ -205,44 +125,71 @@ _bfd_coff_link_hash_table_create (abfd)
   return &ret->root;
 }
 
+/* Create an entry in a COFF debug merge hash table.  */
+
+struct bfd_hash_entry *
+_bfd_coff_debug_merge_hash_newfunc (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.  */
 
-boolean
-_bfd_coff_link_add_symbols (abfd, info)
-     bfd *abfd;
-     struct bfd_link_info *info;
+bfd_boolean
+_bfd_coff_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
 {
   switch (bfd_get_format (abfd))
     {
     case bfd_object:
       return coff_link_add_object_symbols (abfd, info);
     case bfd_archive:
-      return (_bfd_generic_link_add_archive_symbols
-             (abfd, info, coff_link_check_archive_element));
+      return _bfd_generic_link_add_archive_symbols
+       (abfd, info, coff_link_check_archive_element);
     default:
       bfd_set_error (bfd_error_wrong_format);
-      return false;
+      return FALSE;
     }
 }
 
 /* Add symbols from a COFF object file.  */
 
-static boolean
-coff_link_add_object_symbols (abfd, info)
-     bfd *abfd;
-     struct bfd_link_info *info;
+static bfd_boolean
+coff_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
 {
-  if (! coff_link_get_symbols (abfd))
-    return false;
+  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))
-       return false;
-    }
-  return true;
+    return FALSE;
+
+  if (! info->keep_memory
+      && ! _bfd_coff_free_symbols (abfd))
+    return FALSE;
+
+  return TRUE;
 }
 
 /* Check a single archive element to see if we need to include it in
@@ -250,271 +197,73 @@ coff_link_add_object_symbols (abfd, info)
    needed in the link or not.  This is called via
    _bfd_generic_link_add_archive_symbols.  */
 
-static boolean
-coff_link_check_archive_element (abfd, info, pneeded)
-     bfd *abfd;
-     struct bfd_link_info *info;
-     boolean *pneeded;
-{
-  if (! coff_link_get_symbols (abfd))
-    return false;
-
-  if (! coff_link_check_ar_symbols (abfd, info, pneeded))
-    return false;
-
-  if (*pneeded)
-    {
-      if (! coff_link_add_symbols (abfd, info))
-       return false;
-    }
-
-  if (! info->keep_memory || ! *pneeded)
-    {
-      if (! coff_link_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;
+static bfd_boolean
+coff_link_check_archive_element (bfd *abfd,
+                                struct bfd_link_info *info,
+                                struct bfd_link_hash_entry *h,
+                                const char *name,
+                                bfd_boolean *pneeded)
 {
-  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.  */
-
-static boolean
-coff_link_check_ar_symbols (abfd, info, pneeded)
-     bfd *abfd;
-     struct bfd_link_info *info;
-     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;
+  *pneeded = FALSE;
 
-  *pneeded = false;
+  /* We are only interested in symbols that are currently undefined.
+     If a symbol is currently known to be common, COFF linkers do not
+     bring in an object file which defines it.  */
+  if (h->type != bfd_link_hash_undefined)
+    return TRUE;
 
-  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);
-  esym_end = esym + obj_raw_syment_count (abfd) * symesz;
-  while (esym < esym_end)
-    {
-      struct internal_syment sym;
-
-      bfd_coff_swap_sym_in (abfd, (PTR) esym, (PTR) &sym);
-
-      if ((sym.n_sclass == C_EXT
-          || (sym_is_global && (*sym_is_global) (abfd, &sym)))
-         && (sym.n_scnum != 0 || sym.n_value != 0))
-       {
-         const char *name;
-         char buf[SYMNMLEN + 1];
-         struct bfd_link_hash_entry *h;
-
-         /* This symbol is externally visible, and is defined by this
-             object file.  */
+  /* PR 22369 - Skip non COFF objects in the archive.  */
+  if (! bfd_family_coff (abfd))
+    return TRUE;
 
-         /* 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;
-           }
-
-         h = bfd_link_hash_lookup (info->hash, name, false, false, true);
+  /* Include this element?  */
+  if (!(*info->callbacks->add_archive_element) (info, abfd, name, &abfd))
+    return TRUE;
+  *pneeded = TRUE;
 
-         /* We are only interested in symbols that are currently
-            undefined.  If a symbol is currently known to be common,
-            COFF linkers do not bring in an object file which defines
-            it.  */
-         if (h != (struct bfd_link_hash_entry *) NULL
-             && h->type == bfd_link_hash_undefined)
-           {
-             if (! (*info->callbacks->add_archive_element) (info, abfd, name))
-               return false;
-             *pneeded = true;
-             return true;
-           }
-       }
-
-      esym += (sym.n_numaux + 1) * symesz;
-    }
-
-  /* We do not need this object file.  */
-  return true;
+  return bfd_link_add_symbols (abfd, info);
 }
 
 /* Add all the symbols from an object file to the hash table.  */
 
-static boolean
-coff_link_add_symbols (abfd, info)
-     bfd *abfd;
-     struct bfd_link_info *info;
+static bfd_boolean
+coff_link_add_symbols (bfd *abfd,
+                      struct bfd_link_info *info)
 {
-  boolean (*sym_is_global) PARAMS ((bfd *, struct internal_syment *));
-  const char *strings;
-  boolean default_copy;
+  unsigned int n_tmask = coff_data (abfd)->local_n_tmask;
+  unsigned int n_btshft = coff_data (abfd)->local_n_btshft;
+  unsigned int n_btmask = coff_data (abfd)->local_n_btmask;
+  bfd_boolean keep_syms;
+  bfd_boolean default_copy;
   bfd_size_type symcount;
   struct coff_link_hash_entry **sym_hash;
   bfd_size_type symesz;
   bfd_byte *esym;
   bfd_byte *esym_end;
+  bfd_size_type amt;
 
-  sym_is_global = coff_backend_info (abfd)->_bfd_coff_sym_is_global;
-  strings = NULL;
+  symcount = obj_raw_syment_count (abfd);
+
+  if (symcount == 0)
+    return TRUE;               /* Nothing to do.  */
+
+  /* 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;
 
   if (info->keep_memory)
-    default_copy = false;
+    default_copy = FALSE;
   else
-    default_copy = true;
-
-  symcount = obj_raw_syment_count (abfd);
+    default_copy = TRUE;
 
   /* We keep a list of the linker hash table entries that correspond
      to particular symbols.  */
-  sym_hash = ((struct coff_link_hash_entry **)
-             bfd_alloc (abfd,
-                        ((size_t) symcount
-                         * sizeof (struct coff_link_hash_entry *))));
-  if (sym_hash == NULL && symcount != 0)
-    {
-      bfd_set_error (bfd_error_no_memory);
-      return false;
-    }
+  amt = symcount * sizeof (struct coff_link_hash_entry *);
+  sym_hash = (struct coff_link_hash_entry **) bfd_zalloc (abfd, amt);
+  if (sym_hash == NULL)
+    goto error_return;
   obj_coff_sym_hashes (abfd) = sym_hash;
-  memset (sym_hash, 0,
-         (size_t) symcount * sizeof (struct coff_link_hash_entry *));
 
   symesz = bfd_coff_symesz (abfd);
   BFD_ASSERT (symesz == bfd_coff_auxesz (abfd));
@@ -523,83 +272,200 @@ coff_link_add_symbols (abfd, info)
   while (esym < esym_end)
     {
       struct internal_syment sym;
-      boolean copy;
+      enum coff_symbol_classification classification;
+      bfd_boolean copy;
 
-      bfd_coff_swap_sym_in (abfd, (PTR) esym, (PTR) &sym);
+      bfd_coff_swap_sym_in (abfd, esym, &sym);
 
-      if (sym.n_sclass == C_EXT
-         || (sym_is_global && (*sym_is_global) (abfd, &sym)))
+      classification = bfd_coff_classify_symbol (abfd, &sym);
+      if (classification != COFF_SYMBOL_LOCAL)
        {
          const char *name;
          char buf[SYMNMLEN + 1];
          flagword flags;
          asection *section;
          bfd_vma value;
+         bfd_boolean addit;
 
          /* 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
+         if (sym._n._n_n._n_zeroes != 0
+             || sym._n._n_n._n_offset == 0)
+           copy = TRUE;
+
+         value = sym.n_value;
+
+         switch (classification)
            {
-             memcpy (buf, sym._n._n_name, SYMNMLEN);
-             buf[SYMNMLEN] = '\0';
-             name = buf;
-             copy = true;
+           default:
+             abort ();
+
+           case COFF_SYMBOL_GLOBAL:
+             flags = BSF_EXPORT | BSF_GLOBAL;
+             section = coff_section_from_bfd_index (abfd, sym.n_scnum);
+             if (discarded_section (section))
+               section = bfd_und_section_ptr;
+             else if (! obj_pe (abfd))
+               value -= section->vma;
+             break;
+
+           case COFF_SYMBOL_UNDEFINED:
+             flags = 0;
+             section = bfd_und_section_ptr;
+             break;
+
+           case COFF_SYMBOL_COMMON:
+             flags = BSF_GLOBAL;
+             section = bfd_com_section_ptr;
+             break;
+
+           case COFF_SYMBOL_PE_SECTION:
+             flags = BSF_SECTION_SYM | BSF_GLOBAL;
+             section = coff_section_from_bfd_index (abfd, sym.n_scnum);
+             if (discarded_section (section))
+               section = bfd_und_section_ptr;
+             break;
            }
 
-         value = sym.n_value;
+         if (IS_WEAK_EXTERNAL (abfd, sym))
+           flags = BSF_WEAK;
+
+         addit = TRUE;
 
-         if (sym.n_scnum == 0)
+         /* In the PE format, section symbols actually refer to the
+            start of the output section.  We handle them specially
+            here.  */
+         if (obj_pe (abfd) && (flags & BSF_SECTION_SYM) != 0)
            {
-             if (value == 0)
+             *sym_hash = coff_link_hash_lookup (coff_hash_table (info),
+                                                name, FALSE, copy, FALSE);
+             if (*sym_hash != NULL)
                {
-                 flags = 0;
-                 section = bfd_und_section_ptr;
-               }
-             else
-               {
-                 flags = BSF_GLOBAL;
-                 section = bfd_com_section_ptr;
+                 if (((*sym_hash)->coff_link_hash_flags
+                      & COFF_LINK_HASH_PE_SECTION_SYMBOL) == 0
+                     && (*sym_hash)->root.type != bfd_link_hash_undefined
+                     && (*sym_hash)->root.type != bfd_link_hash_undefweak)
+                   _bfd_error_handler
+                     (_("warning: symbol `%s' is both section and non-section"),
+                      name);
+
+                 addit = FALSE;
                }
            }
-         else
+
+         /* The Microsoft Visual C compiler does string pooling by
+            hashing the constants to an internal symbol name, and
+            relying on the linker comdat support to discard
+            duplicate names.  However, if one string is a literal and
+            one is a data initializer, one will end up in the .data
+            section and one will end up in the .rdata section.  The
+            Microsoft linker will combine them into the .data
+            section, which seems to be wrong since it might cause the
+            literal to change.
+
+            As long as there are no external references to the
+            symbols, which there shouldn't be, we can treat the .data
+            and .rdata instances as separate symbols.  The comdat
+            code in the linker will do the appropriate merging.  Here
+            we avoid getting a multiple definition error for one of
+            these special symbols.
+
+            FIXME: I don't think this will work in the case where
+            there are two object files which use the constants as a
+            literal and two object files which use it as a data
+            initializer.  One or the other of the second object files
+            is going to wind up with an inappropriate reference.  */
+         if (obj_pe (abfd)
+             && (classification == COFF_SYMBOL_GLOBAL
+                 || classification == COFF_SYMBOL_PE_SECTION)
+             && coff_section_data (abfd, section) != NULL
+             && coff_section_data (abfd, section)->comdat != NULL
+             && CONST_STRNEQ (name, "??_")
+             && strcmp (name, coff_section_data (abfd, section)->comdat->name) == 0)
            {
-             flags = BSF_EXPORT | BSF_GLOBAL;
-             section = coff_section_from_bfd_index (abfd, sym.n_scnum);
-             value -= section->vma;
+             if (*sym_hash == NULL)
+               *sym_hash = coff_link_hash_lookup (coff_hash_table (info),
+                                                  name, FALSE, copy, FALSE);
+             if (*sym_hash != NULL
+                 && (*sym_hash)->root.type == bfd_link_hash_defined
+                 && coff_section_data (abfd, (*sym_hash)->root.u.def.section)->comdat != NULL
+                 && strcmp (coff_section_data (abfd, (*sym_hash)->root.u.def.section)->comdat->name,
+                            coff_section_data (abfd, section)->comdat->name) == 0)
+               addit = FALSE;
            }
 
-         if (! (_bfd_generic_link_add_one_symbol
-                (info, abfd, name, flags, section, value,
-                 (const char *) NULL, copy, false,
-                 (struct bfd_link_hash_entry **) sym_hash)))
-           return false;
+         if (addit)
+           {
+             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)))
+               goto error_return;
+           }
 
-         if (info->hash->creator->flavour == bfd_get_flavour (abfd))
+         if (obj_pe (abfd) && (flags & BSF_SECTION_SYM) != 0)
+           (*sym_hash)->coff_link_hash_flags |=
+             COFF_LINK_HASH_PE_SECTION_SYMBOL;
+
+         /* Limit the alignment of a common symbol to the possible
+            alignment of a section.  There is no point to permitting
+            a higher alignment for a common symbol: we can not
+            guarantee it, and it may cause us to allocate extra space
+            in the common section.  */
+         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 (bfd_get_flavour (info->output_bfd) == bfd_get_flavour (abfd))
            {
-             if (((*sym_hash)->class == C_NULL
+             /* If we don't have any symbol information currently in
+                the hash table, or if we are looking at a symbol
+                definition, then update the symbol class and type in
+                the hash table.  */
+             if (((*sym_hash)->symbol_class == C_NULL
                   && (*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;
+                 (*sym_hash)->symbol_class = sym.n_sclass;
+                 if (sym.n_type != T_NULL)
+                   {
+                     /* We want to warn if the type changed, but not
+                        if it changed from an unspecified type.
+                        Testing the whole type byte may work, but the
+                        change from (e.g.) a function of unspecified
+                        type to function of known type also wants to
+                        skip the warning.  */
+                     if ((*sym_hash)->type != T_NULL
+                         && (*sym_hash)->type != sym.n_type
+                         && !(DTYPE ((*sym_hash)->type) == DTYPE (sym.n_type)
+                              && (BTYPE ((*sym_hash)->type) == T_NULL
+                                  || BTYPE (sym.n_type) == T_NULL)))
+                       _bfd_error_handler
+                         /* xgettext: c-format */
+                         (_("warning: type of symbol `%s' changed"
+                            " from %d to %d in %pB"),
+                          name, (*sym_hash)->type, sym.n_type, abfd);
+
+                     /* We don't want to change from a meaningful
+                        base type to a null one, but if we know
+                        nothing, take what little we might now know.  */
+                     if (BTYPE (sym.n_type) != T_NULL
+                         || (*sym_hash)->type == T_NULL)
+                       (*sym_hash)->type = sym.n_type;
+                   }
                  (*sym_hash)->auxbfd = abfd;
                  if (sym.n_numaux != 0)
                    {
@@ -608,170 +474,119 @@ 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++)
-                       bfd_coff_swap_aux_in (abfd, (PTR) eaux, sym.n_type,
-                                             sym.n_sclass, i, sym.n_numaux,
-                                             (PTR) iaux);
+                       bfd_coff_swap_aux_in (abfd, eaux, sym.n_type,
+                                             sym.n_sclass, (int) i,
+                                             sym.n_numaux, iaux);
                      (*sym_hash)->aux = alloc;
                    }
                }
            }
+
+         if (classification == COFF_SYMBOL_PE_SECTION
+             && (*sym_hash)->numaux != 0)
+           {
+             /* Some PE sections (such as .bss) have a zero size in
+                the section header, but a non-zero size in the AUX
+                record.  Correct that here.
+
+                FIXME: This is not at all the right place to do this.
+                For example, it won't help objdump.  This needs to be
+                done when we swap in the section header.  */
+             BFD_ASSERT ((*sym_hash)->numaux == 1);
+             if (section->size == 0)
+               section->size = (*sym_hash)->aux[0].x_scn.x_scnlen;
+
+             /* FIXME: We could test whether the section sizes
+                matches the size in the aux entry, but apparently
+                that sometimes fails unexpectedly.  */
+           }
        }
 
       esym += (sym.n_numaux + 1) * symesz;
       sym_hash += sym.n_numaux + 1;
     }
 
-  return true;
-}
-
-/* parse out a -heap <reserved>,<commit> line */
+  /* If this is a non-traditional, non-relocatable link, try to
+     optimize the handling of any .stab/.stabstr sections.  */
+  if (! bfd_link_relocatable (info)
+      && ! info->traditional_format
+      && bfd_get_flavour (info->output_bfd) == bfd_get_flavour (abfd)
+      && (info->strip != strip_all && info->strip != strip_debugger))
+    {
+      asection *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;
-}
+      stabstr = bfd_get_section_by_name (abfd, ".stabstr");
 
-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)
+      if (stabstr != NULL)
        {
-         char *name;
-         char *attribs;
-         asection *asec;
+         bfd_size_type string_offset = 0;
+         asection *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);
+         for (stab = abfd->sections; stab; stab = stab->next)
+           if (CONST_STRNEQ (stab->name, ".stab")
+               && (!stab->name[5]
+                   || (stab->name[5] == '.' && ISDIGIT (stab->name[6]))))
+           {
+             struct coff_link_hash_table *table;
+             struct coff_section_tdata *secdata
+               = coff_section_data (abfd, stab);
+
+             if (secdata == NULL)
+               {
+                 amt = sizeof (struct coff_section_tdata);
+                 stab->used_by_bfd = bfd_zalloc (abfd, amt);
+                 if (stab->used_by_bfd == NULL)
+                   goto error_return;
+                 secdata = coff_section_data (abfd, stab);
+               }
+
+             table = coff_hash_table (info);
+
+             if (! _bfd_link_section_stabs (abfd, &table->stab_info,
+                                            stab, stabstr,
+                                            &secdata->stab_info,
+                                            &string_offset))
+               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
-_bfd_coff_final_link (abfd, info)
-     bfd *abfd;
-     struct bfd_link_info *info;
+bfd_boolean
+_bfd_coff_final_link (bfd *abfd,
+                     struct bfd_link_info *info)
 {
   bfd_size_type symesz;
-  struct coff_final_link_info finfo;
+  struct coff_final_link_info flaginfo;
+  bfd_boolean debug_merge_allocated;
+  bfd_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;
+  bfd_size_type max_sym_count;
+  bfd_size_type max_lineno_count;
+  bfd_size_type max_reloc_count;
+  bfd_size_type max_output_reloc_count;
+  bfd_size_type max_contents_size;
   file_ptr rel_filepos;
   unsigned int relsz;
   file_ptr line_filepos;
@@ -779,39 +594,43 @@ _bfd_coff_final_link (abfd, info)
   bfd *sub;
   bfd_byte *external_relocs = NULL;
   char strbuf[STRING_SIZE_SIZE];
+  bfd_size_type amt;
 
   symesz = bfd_coff_symesz (abfd);
 
-  finfo.info = info;
-  finfo.output_bfd = abfd;
-  finfo.strtab = NULL;
-  finfo.section_info = NULL;
-  finfo.last_file_index = -1;
-  finfo.internal_syms = NULL;
-  finfo.sec_ptrs = NULL;
-  finfo.sym_indices = NULL;
-  finfo.outsyms = NULL;
-  finfo.linenos = NULL;
-  finfo.contents = NULL;
-  finfo.external_relocs = NULL;
-  finfo.internal_relocs = NULL;
-
-  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;
-    }
+  flaginfo.info = info;
+  flaginfo.output_bfd = abfd;
+  flaginfo.strtab = NULL;
+  flaginfo.section_info = NULL;
+  flaginfo.last_file_index = -1;
+  flaginfo.last_bf_index = -1;
+  flaginfo.internal_syms = NULL;
+  flaginfo.sec_ptrs = NULL;
+  flaginfo.sym_indices = NULL;
+  flaginfo.outsyms = NULL;
+  flaginfo.linenos = NULL;
+  flaginfo.contents = NULL;
+  flaginfo.external_relocs = NULL;
+  flaginfo.internal_relocs = NULL;
+  flaginfo.global_to_static = FALSE;
+  debug_merge_allocated = FALSE;
+
+  coff_data (abfd)->link_info = info;
+
+  flaginfo.strtab = _bfd_stringtab_init ();
+  if (flaginfo.strtab == NULL)
+    goto error_return;
 
-  finfo.strtab = _bfd_stringtab_init ();
-  if (finfo.strtab == NULL)
+  if (! coff_debug_merge_hash_table_init (&flaginfo.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,11 +639,13 @@ _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;
       o->lineno_count = 0;
-      for (p = o->link_order_head; p != NULL; p = p->next)
+      for (p = o->map_head.link_order; p != NULL; p = p->next)
        {
          if (p->type == bfd_indirect_link_order)
            {
@@ -832,21 +653,29 @@ _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;
 
-             if (info->relocateable)
+             if (bfd_link_relocatable (info))
                o->reloc_count += sec->reloc_count;
 
-             if (sec->_raw_size > max_contents_size)
-               max_contents_size = sec->_raw_size;
+             if (sec->rawsize > max_contents_size)
+               max_contents_size = sec->rawsize;
+             if (sec->size > max_contents_size)
+               max_contents_size = sec->size;
              if (sec->lineno_count > max_lineno_count)
                max_lineno_count = sec->lineno_count;
              if (sec->reloc_count > max_reloc_count)
                max_reloc_count = sec->reloc_count;
            }
-         else if (info->relocateable
+         else if (bfd_link_relocatable (info)
                   && (p->type == bfd_section_reloc_link_order
                       || p->type == bfd_symbol_reloc_link_order))
            ++o->reloc_count;
@@ -858,29 +687,44 @@ _bfd_coff_final_link (abfd, info)
          o->flags |= SEC_RELOC;
          o->rel_filepos = rel_filepos;
          rel_filepos += o->reloc_count * relsz;
+         /* In PE COFF, if there are at least 0xffff relocations an
+            extra relocation will be written out to encode the count.  */
+         if (obj_pe (abfd) && o->reloc_count >= 0xffff)
+           rel_filepos += 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 (flaginfo.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
+  /* If doing a relocatable link, allocate space for the pointers we
      need to keep.  */
-  if (info->relocateable)
+  if (bfd_link_relocatable (info))
     {
       unsigned int i;
 
       /* 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)));
-      if (finfo.section_info == NULL)
-       {
-         bfd_set_error (bfd_error_no_memory);
-         goto error_return;
-       }
+        the target_index fields are 1 based.  */
+      amt = abfd->section_count + 1;
+      amt *= sizeof (struct coff_link_section_info);
+      flaginfo.section_info = (struct coff_link_section_info *) bfd_malloc (amt);
+      if (flaginfo.section_info == NULL)
+       goto error_return;
       for (i = 0; i <= abfd->section_count; i++)
        {
-         finfo.section_info[i].relocs = NULL;
-         finfo.section_info[i].rel_hashes = NULL;
+         flaginfo.section_info[i].relocs = NULL;
+         flaginfo.section_info[i].rel_hashes = NULL;
        }
     }
 
@@ -902,32 +746,30 @@ _bfd_coff_final_link (abfd, info)
       if (o->reloc_count != 0)
        {
          /* We don't know the indices of global symbols until we have
-             written out all the local symbols.  For each section in
-             the output file, we keep an array of pointers to hash
-             table entries.  Each entry in the array corresponds to a
-             reloc.  When we find a reloc against a global symbol, we
-             set the corresponding entry in this array so that we can
-             fix up the symbol index after we have written out all the
-             local symbols.
+            written out all the local symbols.  For each section in
+            the output file, we keep an array of pointers to hash
+            table entries.  Each entry in the array corresponds to a
+            reloc.  When we find a reloc against a global symbol, we
+            set the corresponding entry in this array so that we can
+            fix up the symbol index after we have written out all the
+            local symbols.
 
             Because of this problem, we also keep the relocs in
             memory until the end of the link.  This wastes memory,
-            but only when doing a relocateable link, which is not the
+            but only when doing a relocatable link, which is not the
             common case.  */
-         BFD_ASSERT (info->relocateable);
-         finfo.section_info[o->target_index].relocs =
-           ((struct internal_reloc *)
-            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
-                    * 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;
-           }
+         BFD_ASSERT (bfd_link_relocatable (info));
+         amt = o->reloc_count;
+         amt *= sizeof (struct internal_reloc);
+         flaginfo.section_info[o->target_index].relocs =
+             (struct internal_reloc *) bfd_malloc (amt);
+         amt = o->reloc_count;
+         amt *= sizeof (struct coff_link_hash_entry *);
+         flaginfo.section_info[o->target_index].rel_hashes =
+             (struct coff_link_hash_entry **) bfd_malloc (amt);
+         if (flaginfo.section_info[o->target_index].relocs == NULL
+             || flaginfo.section_info[o->target_index].rel_hashes == NULL)
+           goto error_return;
 
          if (o->reloc_count > max_output_reloc_count)
            max_output_reloc_count = o->reloc_count;
@@ -945,45 +787,45 @@ _bfd_coff_final_link (abfd, info)
      the opportunity to clear the output_has_begun fields of all the
      input BFD's.  */
   max_sym_count = 0;
-  for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
+  for (sub = info->input_bfds; sub != NULL; sub = sub->link.next)
     {
       size_t sz;
 
-      sub->output_has_begun = false;
-      sz = obj_raw_syment_count (sub);
+      sub->output_has_begun = FALSE;
+      sz = bfd_family_coff (sub) ? obj_raw_syment_count (sub) : 2;
       if (sz > max_sym_count)
        max_sym_count = sz;
     }
 
   /* 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_coff_linesz (abfd));
-  finfo.contents = (bfd_byte *) malloc (max_contents_size);
-  finfo.external_relocs = (bfd_byte *) malloc (max_reloc_count * relsz);
-  if (! info->relocateable)
-    finfo.internal_relocs = ((struct internal_reloc *)
-                            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)
-      || finfo.outsyms == NULL
-      || (finfo.linenos == NULL && max_lineno_count > 0)
-      || (finfo.contents == NULL && max_contents_size > 0)
-      || (finfo.external_relocs == NULL && max_reloc_count > 0)
-      || (! info->relocateable
-         && finfo.internal_relocs == NULL
-         && max_reloc_count > 0))
+  amt = max_sym_count * sizeof (struct internal_syment);
+  flaginfo.internal_syms = (struct internal_syment *) bfd_malloc (amt);
+  amt = max_sym_count * sizeof (asection *);
+  flaginfo.sec_ptrs = (asection **) bfd_malloc (amt);
+  amt = max_sym_count * sizeof (long);
+  flaginfo.sym_indices = (long int *) bfd_malloc (amt);
+  flaginfo.outsyms = (bfd_byte *) bfd_malloc ((max_sym_count + 1) * symesz);
+  amt = max_lineno_count * bfd_coff_linesz (abfd);
+  flaginfo.linenos = (bfd_byte *) bfd_malloc (amt);
+  flaginfo.contents = (bfd_byte *) bfd_malloc (max_contents_size);
+  amt = max_reloc_count * relsz;
+  flaginfo.external_relocs = (bfd_byte *) bfd_malloc (amt);
+  if (! bfd_link_relocatable (info))
     {
-      bfd_set_error (bfd_error_no_memory);
-      goto error_return;
+      amt = max_reloc_count * sizeof (struct internal_reloc);
+      flaginfo.internal_relocs = (struct internal_reloc *) bfd_malloc (amt);
     }
+  if ((flaginfo.internal_syms == NULL && max_sym_count > 0)
+      || (flaginfo.sec_ptrs == NULL && max_sym_count > 0)
+      || (flaginfo.sym_indices == NULL && max_sym_count > 0)
+      || flaginfo.outsyms == NULL
+      || (flaginfo.linenos == NULL && max_lineno_count > 0)
+      || (flaginfo.contents == NULL && max_contents_size > 0)
+      || (flaginfo.external_relocs == NULL && max_reloc_count > 0)
+      || (! bfd_link_relocatable (info)
+         && flaginfo.internal_relocs == NULL
+         && max_reloc_count > 0))
+    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,26 +833,32 @@ _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)
+      for (p = o->map_head.link_order; p != NULL; p = p->next)
        {
          if (p->type == bfd_indirect_link_order
-             && (bfd_get_flavour (p->u.indirect.section->owner)
-                 == bfd_target_coff_flavour))
+             && bfd_family_coff (p->u.indirect.section->owner))
            {
              sub = p->u.indirect.section->owner;
-             if (! sub->output_has_begun)
+             if (! bfd_coff_link_output_has_begun (sub, & flaginfo))
                {
-                 if (! coff_link_input_bfd (&finfo, sub))
+                 if (! _bfd_coff_link_input_bfd (&flaginfo, sub))
                    goto error_return;
-                 sub->output_has_begun = true;
+                 sub->output_has_begun = TRUE;
                }
            }
          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, &flaginfo, o, p))
                goto error_return;
            }
          else
@@ -1021,85 +869,218 @@ _bfd_coff_final_link (abfd, info)
        }
     }
 
-  /* Free up the buffers used by coff_link_input_bfd.  */
-  if (finfo.internal_syms != NULL)
+  if (flaginfo.info->strip != strip_all && flaginfo.info->discard != discard_all)
     {
-      free (finfo.internal_syms);
-      finfo.internal_syms = NULL;
+      /* Add local symbols from foreign inputs.  */
+      for (sub = info->input_bfds; sub != NULL; sub = sub->link.next)
+       {
+         unsigned int i;
+
+         if (bfd_family_coff (sub) || ! bfd_get_outsymbols (sub))
+           continue;
+         for (i = 0; i < bfd_get_symcount (sub); ++i)
+           {
+             asymbol *sym = bfd_get_outsymbols (sub) [i];
+             file_ptr pos;
+             struct internal_syment isym;
+             union internal_auxent iaux;
+             bfd_size_type string_size = 0, indx;
+             bfd_vma written = 0;
+             bfd_boolean rewrite = FALSE, hash;
+
+             if (! (sym->flags & BSF_LOCAL)
+                 || (sym->flags & (BSF_SECTION_SYM | BSF_DEBUGGING_RELOC
+                                   | BSF_THREAD_LOCAL | BSF_RELC | BSF_SRELC
+                                   | BSF_SYNTHETIC))
+                 || ((sym->flags & BSF_DEBUGGING)
+                     && ! (sym->flags & BSF_FILE)))
+               continue;
+
+             /* See if we are discarding symbols with this name.  */
+             if ((flaginfo.info->strip == strip_some
+                  && (bfd_hash_lookup (flaginfo.info->keep_hash,
+                                       bfd_asymbol_name(sym), FALSE, FALSE)
+                      == NULL))
+                 || (((flaginfo.info->discard == discard_sec_merge
+                       && (bfd_asymbol_section (sym)->flags & SEC_MERGE)
+                       && ! bfd_link_relocatable (flaginfo.info))
+                      || flaginfo.info->discard == discard_l)
+                     && bfd_is_local_label_name (sub, bfd_asymbol_name(sym))))
+               continue;
+
+             pos = obj_sym_filepos (abfd) + obj_raw_syment_count (abfd)
+                                            * symesz;
+             if (bfd_seek (abfd, pos, SEEK_SET) != 0)
+               goto error_return;
+             if (! coff_write_alien_symbol(abfd, sym, &isym, &iaux, &written,
+                                           &string_size, NULL, NULL))
+               goto error_return;
+
+             hash = !flaginfo.info->traditional_format;
+
+             if (string_size >= 6 && isym.n_sclass == C_FILE
+                 && ! isym._n._n_n._n_zeroes && isym.n_numaux)
+               {
+                 indx = _bfd_stringtab_add (flaginfo.strtab, ".file", hash,
+                                            FALSE);
+                 if (indx == (bfd_size_type) -1)
+                   goto error_return;
+                 isym._n._n_n._n_offset = STRING_SIZE_SIZE + indx;
+                 bfd_coff_swap_sym_out (abfd, &isym, flaginfo.outsyms);
+                 if (bfd_seek (abfd, pos, SEEK_SET) != 0
+                     || bfd_bwrite (flaginfo.outsyms, symesz,
+                                    abfd) != symesz)
+                   goto error_return;
+                 string_size -= 6;
+               }
+
+             if (string_size)
+               {
+                 indx = _bfd_stringtab_add (flaginfo.strtab,
+                                            bfd_asymbol_name (sym), hash,
+                                            FALSE);
+                 if (indx == (bfd_size_type) -1)
+                   goto error_return;
+                 if (isym.n_sclass != C_FILE)
+                   {
+                     isym._n._n_n._n_offset = STRING_SIZE_SIZE + indx;
+                     bfd_coff_swap_sym_out (abfd, &isym, flaginfo.outsyms);
+                     rewrite = TRUE;
+                   }
+                 else
+                   {
+                     BFD_ASSERT (isym.n_numaux == 1);
+                     iaux.x_file.x_n.x_offset = STRING_SIZE_SIZE + indx;
+                     bfd_coff_swap_aux_out (abfd, &iaux, isym.n_type, C_FILE,
+                                            0, 1, flaginfo.outsyms + symesz);
+                     if (bfd_seek (abfd, pos + symesz, SEEK_SET) != 0
+                         || bfd_bwrite (flaginfo.outsyms + symesz, symesz,
+                                        abfd) != symesz)
+                       goto error_return;
+                   }
+               }
+
+             if (isym.n_sclass == C_FILE)
+               {
+                 if (flaginfo.last_file_index != -1)
+                   {
+                     flaginfo.last_file.n_value = obj_raw_syment_count (abfd);
+                     bfd_coff_swap_sym_out (abfd, &flaginfo.last_file,
+                                            flaginfo.outsyms);
+                     pos = obj_sym_filepos (abfd) + flaginfo.last_file_index
+                                                    * symesz;
+                     rewrite = TRUE;
+                   }
+                 flaginfo.last_file_index = obj_raw_syment_count (abfd);
+                 flaginfo.last_file = isym;
+               }
+
+             if (rewrite
+                 && (bfd_seek (abfd, pos, SEEK_SET) != 0
+                     || bfd_bwrite (flaginfo.outsyms, symesz, abfd) != symesz))
+               goto error_return;
+
+             obj_raw_syment_count (abfd) += written;
+           }
+       }
+    }
+
+  if (! bfd_coff_final_link_postscript (abfd, & flaginfo))
+    goto error_return;
+
+  /* Free up the buffers used by _bfd_coff_link_input_bfd.  */
+
+  coff_debug_merge_hash_table_free (&flaginfo.debug_merge);
+  debug_merge_allocated = FALSE;
+
+  if (flaginfo.internal_syms != NULL)
+    {
+      free (flaginfo.internal_syms);
+      flaginfo.internal_syms = NULL;
     }
-  if (finfo.sec_ptrs != NULL)
+  if (flaginfo.sec_ptrs != NULL)
     {
-      free (finfo.sec_ptrs);
-      finfo.sec_ptrs = NULL;
+      free (flaginfo.sec_ptrs);
+      flaginfo.sec_ptrs = NULL;
     }
-  if (finfo.sym_indices != NULL)
+  if (flaginfo.sym_indices != NULL)
     {
-      free (finfo.sym_indices);
-      finfo.sym_indices = NULL;
+      free (flaginfo.sym_indices);
+      flaginfo.sym_indices = NULL;
     }
-  if (finfo.linenos != NULL)
+  if (flaginfo.linenos != NULL)
     {
-      free (finfo.linenos);
-      finfo.linenos = NULL;
+      free (flaginfo.linenos);
+      flaginfo.linenos = NULL;
     }
-  if (finfo.contents != NULL)
+  if (flaginfo.contents != NULL)
     {
-      free (finfo.contents);
-      finfo.contents = NULL;
+      free (flaginfo.contents);
+      flaginfo.contents = NULL;
     }
-  if (finfo.external_relocs != NULL)
+  if (flaginfo.external_relocs != NULL)
     {
-      free (finfo.external_relocs);
-      finfo.external_relocs = NULL;
+      free (flaginfo.external_relocs);
+      flaginfo.external_relocs = NULL;
     }
-  if (finfo.internal_relocs != NULL)
+  if (flaginfo.internal_relocs != NULL)
     {
-      free (finfo.internal_relocs);
-      finfo.internal_relocs = NULL;
+      free (flaginfo.internal_relocs);
+      flaginfo.internal_relocs = NULL;
     }
 
   /* The value of the last C_FILE symbol is supposed to be the symbol
      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))
+  if (flaginfo.last_file_index != -1
+      && (unsigned int) flaginfo.last_file.n_value != obj_raw_syment_count (abfd))
+    {
+      file_ptr pos;
+
+      flaginfo.last_file.n_value = obj_raw_syment_count (abfd);
+      bfd_coff_swap_sym_out (abfd, &flaginfo.last_file,
+                            flaginfo.outsyms);
+
+      pos = obj_sym_filepos (abfd) + flaginfo.last_file_index * symesz;
+      if (bfd_seek (abfd, pos, SEEK_SET) != 0
+         || bfd_bwrite (flaginfo.outsyms, symesz, abfd) != symesz)
+       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.last_file.n_value = obj_raw_syment_count (abfd);
-      bfd_coff_swap_sym_out (abfd, (PTR) &finfo.last_file,
-                            (PTR) finfo.outsyms);
-      if (bfd_seek (abfd,
-                   (obj_sym_filepos (abfd)
-                    + finfo.last_file_index * symesz),
-                   SEEK_SET) != 0
-         || bfd_write (finfo.outsyms, symesz, 1, abfd) != symesz)
-       return false;
+      flaginfo.failed = FALSE;
+      coff_link_hash_traverse (coff_hash_table (info),
+                              _bfd_coff_write_task_globals, &flaginfo);
+      if (flaginfo.failed)
+       goto error_return;
     }
 
   /* Write out the global symbols.  */
-  finfo.failed = false;
-  coff_link_hash_traverse (coff_hash_table (info), coff_write_global_sym,
-                          (PTR) &finfo);
-  if (finfo.failed)
+  flaginfo.failed = FALSE;
+  bfd_hash_traverse (&info->hash->table, _bfd_coff_write_global_sym, &flaginfo);
+  if (flaginfo.failed)
     goto error_return;
 
-  /* The outsyms buffer is used by coff_write_global_sym.  */
-  if (finfo.outsyms != NULL)
+  /* The outsyms buffer is used by _bfd_coff_write_global_sym.  */
+  if (flaginfo.outsyms != NULL)
     {
-      free (finfo.outsyms);
-      finfo.outsyms = NULL;
+      free (flaginfo.outsyms);
+      flaginfo.outsyms = NULL;
     }
 
-  if (info->relocateable)
+  if (bfd_link_relocatable (info) && 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);
+      amt = max_output_reloc_count * relsz;
+      external_relocs = (bfd_byte *) bfd_malloc (amt);
       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)
        {
@@ -1111,9 +1092,9 @@ _bfd_coff_final_link (abfd, info)
          if (o->reloc_count == 0)
            continue;
 
-         irel = finfo.section_info[o->target_index].relocs;
+         irel = flaginfo.section_info[o->target_index].relocs;
          irelend = irel + o->reloc_count;
-         rel_hash = finfo.section_info[o->target_index].rel_hashes;
+         rel_hash = flaginfo.section_info[o->target_index].rel_hashes;
          erel = external_relocs;
          for (; irel < irelend; irel++, rel_hash++, erel += relsz)
            {
@@ -1122,12 +1103,30 @@ _bfd_coff_final_link (abfd, info)
                  BFD_ASSERT ((*rel_hash)->indx >= 0);
                  irel->r_symndx = (*rel_hash)->indx;
                }
-             bfd_coff_swap_reloc_out (abfd, (PTR) irel, (PTR) erel);
+             bfd_coff_swap_reloc_out (abfd, irel, erel);
            }
 
-         if (bfd_seek (abfd, o->rel_filepos, SEEK_SET) != 0
-             || bfd_write ((PTR) external_relocs, relsz, o->reloc_count,
-                           abfd) != relsz * o->reloc_count)
+         if (bfd_seek (abfd, o->rel_filepos, SEEK_SET) != 0)
+           goto error_return;
+         if (obj_pe (abfd) && o->reloc_count >= 0xffff)
+           {
+             /* In PE COFF, write the count of relocs as the first
+                reloc.  The header overflow bit will be set
+                elsewhere. */
+             struct internal_reloc incount;
+             bfd_byte *excount = (bfd_byte *)bfd_malloc (relsz);
+
+             memset (&incount, 0, sizeof (incount));
+             incount.r_vaddr = o->reloc_count + 1;
+             bfd_coff_swap_reloc_out (abfd, &incount, excount);
+             if (bfd_bwrite (excount, relsz, abfd) != relsz)
+               /* We'll leak, but it's an error anyway. */
+               goto error_return;
+             free (excount);
+           }
+         if (bfd_bwrite (external_relocs,
+                         (bfd_size_type) relsz * o->reloc_count, abfd)
+             != (bfd_size_type) relsz * o->reloc_count)
            goto error_return;
        }
 
@@ -1136,105 +1135,299 @@ _bfd_coff_final_link (abfd, info)
     }
 
   /* Free up the section information.  */
-  if (finfo.section_info != NULL)
+  if (flaginfo.section_info != NULL)
     {
       unsigned int i;
 
-      for (i = 0; i < abfd->section_count; i++)
+      for (i = 0; i < abfd->section_count; i++)
+       {
+         if (flaginfo.section_info[i].relocs != NULL)
+           free (flaginfo.section_info[i].relocs);
+         if (flaginfo.section_info[i].rel_hashes != NULL)
+           free (flaginfo.section_info[i].rel_hashes);
+       }
+      free (flaginfo.section_info);
+      flaginfo.section_info = NULL;
+    }
+
+  /* If we have optimized stabs strings, output them.  */
+  if (coff_hash_table (info)->stab_info.stabstr != 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)
+    {
+      file_ptr pos;
+
+      pos = obj_sym_filepos (abfd) + obj_raw_syment_count (abfd) * symesz;
+      if (bfd_seek (abfd, pos, SEEK_SET) != 0)
+       return FALSE;
+
+#if STRING_SIZE_SIZE == 4
+      H_PUT_32 (abfd,
+               _bfd_stringtab_size (flaginfo.strtab) + STRING_SIZE_SIZE,
+               strbuf);
+#else
+ #error Change H_PUT_32 above
+#endif
+
+      if (bfd_bwrite (strbuf, (bfd_size_type) STRING_SIZE_SIZE, abfd)
+         != STRING_SIZE_SIZE)
+       return FALSE;
+
+      if (! _bfd_stringtab_emit (abfd, flaginfo.strtab))
+       return FALSE;
+
+      obj_coff_strings_written (abfd) = TRUE;
+    }
+
+  _bfd_stringtab_free (flaginfo.strtab);
+
+  /* Setting symcount to 0 will cause write_object_contents to
+     not try to write out the symbols.  */
+  abfd->symcount = 0;
+
+  return TRUE;
+
+ error_return:
+  if (debug_merge_allocated)
+    coff_debug_merge_hash_table_free (&flaginfo.debug_merge);
+  if (flaginfo.strtab != NULL)
+    _bfd_stringtab_free (flaginfo.strtab);
+  if (flaginfo.section_info != NULL)
+    {
+      unsigned int i;
+
+      for (i = 0; i < abfd->section_count; i++)
+       {
+         if (flaginfo.section_info[i].relocs != NULL)
+           free (flaginfo.section_info[i].relocs);
+         if (flaginfo.section_info[i].rel_hashes != NULL)
+           free (flaginfo.section_info[i].rel_hashes);
+       }
+      free (flaginfo.section_info);
+    }
+  if (flaginfo.internal_syms != NULL)
+    free (flaginfo.internal_syms);
+  if (flaginfo.sec_ptrs != NULL)
+    free (flaginfo.sec_ptrs);
+  if (flaginfo.sym_indices != NULL)
+    free (flaginfo.sym_indices);
+  if (flaginfo.outsyms != NULL)
+    free (flaginfo.outsyms);
+  if (flaginfo.linenos != NULL)
+    free (flaginfo.linenos);
+  if (flaginfo.contents != NULL)
+    free (flaginfo.contents);
+  if (flaginfo.external_relocs != NULL)
+    free (flaginfo.external_relocs);
+  if (flaginfo.internal_relocs != NULL)
+    free (flaginfo.internal_relocs);
+  if (external_relocs != NULL)
+    free (external_relocs);
+  return FALSE;
+}
+
+/* Parse out a -heap <reserved>,<commit> line.  */
+
+static char *
+dores_com (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] == ',')
+       {
+         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 (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 (bfd *output_bfd,
+                          struct bfd_link_info *info ATTRIBUTE_UNUSED,
+                          bfd *abfd)
+{
+  asection *sec = bfd_get_section_by_name (abfd, ".drectve");
+  char *s;
+  char *e;
+  bfd_byte *copy;
+
+  if (!sec)
+    return 1;
+
+  if (!bfd_malloc_and_get_section (abfd, sec, &copy))
+    {
+      if (copy != NULL)
+       free (copy);
+      return 0;
+    }
+  e = (char *) copy + sec->size;
+
+  for (s = (char *) copy; s < e ; )
+    {
+      if (s[0] != '-')
+       {
+         s++;
+         continue;
+       }
+      if (CONST_STRNEQ (s, "-attr"))
+       {
+         char *name;
+         char *attribs;
+         asection *asec;
+         int loop = 1;
+         int had_write = 0;
+         int had_exec= 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':
+                 break;
+               case 'S':
+                 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 (CONST_STRNEQ (s, "-heap"))
+       s = dores_com (s + 5, output_bfd, 1);
+
+      else if (CONST_STRNEQ (s, "-stack"))
+       s = dores_com (s + 6, output_bfd, 0);
+
+      /* GNU extension for aligned commons.  */
+      else if (CONST_STRNEQ (s, "-aligncomm:"))
        {
-         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);
+         /* Common symbols must be aligned on reading, as it
+         is too late to do anything here, after they have
+         already been allocated, so just skip the directive.  */
+         s += 11;
        }
-      free (finfo.section_info);
-      finfo.section_info = NULL;
-    }
 
-  /* Write out the string table.  */
-  if (bfd_seek (abfd,
-               (obj_sym_filepos (abfd)
-                + obj_raw_syment_count (abfd) * symesz),
-               SEEK_SET) != 0)
-    return false;
+      else
+       s++;
+    }
+  free (copy);
+  return 1;
+}
 
-#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
+/* 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_write (strbuf, 1, STRING_SIZE_SIZE, abfd) != STRING_SIZE_SIZE)
-    return false;
+static void
+mark_relocs (struct coff_final_link_info *flaginfo, bfd *input_bfd)
+{
+  asection * a;
 
-  if (! _bfd_stringtab_emit (abfd, finfo.strtab))
-    return false;
+  if ((bfd_get_file_flags (input_bfd) & HAS_SYMS) == 0)
+    return;
 
-  _bfd_stringtab_free (finfo.strtab);
+  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
+         || a->linker_mark == 0)
+       continue;
+      /* Don't mark relocs in excluded sections.  */
+      if (a->output_section == bfd_abs_section_ptr)
+       continue;
 
-  return true;
+      /* Read in the relocs.  */
+      internal_relocs = _bfd_coff_read_internal_relocs
+       (input_bfd, a, FALSE,
+        flaginfo->external_relocs,
+        bfd_link_relocatable (flaginfo->info),
+        (bfd_link_relocatable (flaginfo->info)
+         ? (flaginfo->section_info[ a->output_section->target_index ].relocs + a->output_section->reloc_count)
+         : flaginfo->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++)
-       {
-         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);
+      /* 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-skip pass.  */
+      for (; irel < irelend; irel++)
+       if ((unsigned long) irel->r_symndx < obj_raw_syment_count (input_bfd))
+         flaginfo->sym_indices[irel->r_symndx] = -1;
     }
-  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)
-     struct coff_final_link_info *finfo;
-     bfd *input_bfd;
+bfd_boolean
+_bfd_coff_link_input_bfd (struct coff_final_link_info *flaginfo, bfd *input_bfd)
 {
-  boolean (*sym_is_global) PARAMS ((bfd *, struct internal_syment *));
-  boolean (*adjust_symndx) PARAMS ((bfd *, struct bfd_link_info *, bfd *,
-                                   asection *, struct internal_reloc *,
-                                   boolean *));
+  unsigned int n_tmask = coff_data (input_bfd)->local_n_tmask;
+  unsigned int n_btshft = coff_data (input_bfd)->local_n_btshft;
+  bfd_boolean (*adjust_symndx)
+    (bfd *, struct bfd_link_info *, bfd *, asection *,
+     struct internal_reloc *, bfd_boolean *);
   bfd *output_bfd;
   const char *strings;
   bfd_size_type syment_base;
-  unsigned int n_tmask;
-  unsigned int n_btshft;
-  boolean copy, hash;
+  bfd_boolean copy, hash;
   bfd_size_type isymesz;
   bfd_size_type osymesz;
   bfd_size_type linesz;
@@ -1243,16 +1436,14 @@ 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.  */
 
-  output_bfd = finfo->output_bfd;
-  sym_is_global = coff_backend_info (input_bfd)->_bfd_coff_sym_is_global;
+  output_bfd = flaginfo->output_bfd;
   strings = NULL;
   syment_base = obj_raw_syment_count (output_bfd);
   isymesz = bfd_coff_symesz (input_bfd);
@@ -1260,45 +1451,51 @@ coff_link_input_bfd (finfo, input_bfd)
   linesz = bfd_coff_linesz (input_bfd);
   BFD_ASSERT (linesz == bfd_coff_linesz (output_bfd));
 
-  n_tmask = coff_data (input_bfd)->local_n_tmask;
-  n_btshft = coff_data (input_bfd)->local_n_btshft;
-
-  /* Define macros so that ISFCN, et. al., macros work correctly.  */
-#define N_TMASK n_tmask
-#define N_BTSHFT n_btshft
-
-  copy = false;
-  if (! finfo->info->keep_memory)
-    copy = true;
-  hash = true;
-  if ((output_bfd->flags & BFD_TRADITIONAL_FORMAT) != 0)
-    hash = false;
+  copy = FALSE;
+  if (! flaginfo->info->keep_memory)
+    copy = TRUE;
+  hash = TRUE;
+  if (flaginfo->info->traditional_format)
+    hash = FALSE;
 
-  if (! coff_link_get_symbols (input_bfd))
-    return false;
+  if (! _bfd_coff_get_external_symbols (input_bfd))
+    return FALSE;
 
   esym = (bfd_byte *) obj_coff_external_syms (input_bfd);
   esym_end = esym + obj_raw_syment_count (input_bfd) * isymesz;
-  isymp = finfo->internal_syms;
-  secpp = finfo->sec_ptrs;
-  indexp = finfo->sym_indices;
+  isymp = flaginfo->internal_syms;
+  secpp = flaginfo->sec_ptrs;
+  indexp = flaginfo->sym_indices;
   output_index = syment_base;
-  outsym = finfo->outsyms;
+  outsym = flaginfo->outsyms;
+
+  if (coff_data (output_bfd)->pe
+      && ! process_embedded_commands (output_bfd, flaginfo->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 ((   flaginfo->info->strip   != strip_none
+       || flaginfo->info->discard != discard_none)
+      && bfd_link_relocatable (flaginfo->info))
+    {
+      /* Mark the symbol array as 'not-used'.  */
+      memset (indexp, 0, obj_raw_syment_count (input_bfd) * sizeof * indexp);
 
-  if (obj_pe (output_bfd))
-      {
-       if (!process_embedded_commands (input_bfd))
-         return false;
-      }
+      mark_relocs (flaginfo, input_bfd);
+    }
 
   while (esym < esym_end)
     {
       struct internal_syment isym;
-      boolean skip;
-      boolean global;
+      enum coff_symbol_classification classification;
+      bfd_boolean skip;
+      bfd_boolean global;
+      bfd_boolean dont_skip_symbol;
       int add;
 
-      bfd_coff_swap_sym_in (input_bfd, (PTR) esym, (PTR) isymp);
+      bfd_coff_swap_sym_in (input_bfd, esym, isymp);
 
       /* Make a copy of *isymp so that the relocate_section function
         always sees the original values.  This is more reliable than
@@ -1306,90 +1503,298 @@ coff_link_input_bfd (finfo, input_bfd)
         the symbol.  */
       isym = *isymp;
 
-      if (isym.n_scnum != 0)
-       *secpp = coff_section_from_bfd_index (input_bfd, isym.n_scnum);
-      else
+      classification = bfd_coff_classify_symbol (input_bfd, &isym);
+      switch (classification)
        {
-         if (isym.n_value == 0)
-           *secpp = bfd_und_section_ptr;
-         else
-           *secpp = bfd_com_section_ptr;
+       default:
+         abort ();
+       case COFF_SYMBOL_GLOBAL:
+       case COFF_SYMBOL_PE_SECTION:
+       case COFF_SYMBOL_LOCAL:
+         *secpp = coff_section_from_bfd_index (input_bfd, isym.n_scnum);
+         break;
+       case COFF_SYMBOL_COMMON:
+         *secpp = bfd_com_section_ptr;
+         break;
+       case COFF_SYMBOL_UNDEFINED:
+         *secpp = bfd_und_section_ptr;
+         break;
        }
 
+      /* Extract the flag indicating if this symbol is used by a
+        relocation.  */
+      if ((flaginfo->info->strip != strip_none
+          || flaginfo->info->discard != discard_none)
+         && bfd_link_relocatable (flaginfo->info))
+       dont_skip_symbol = *indexp;
+      else
+       dont_skip_symbol = FALSE;
+
       *indexp = -1;
 
-      skip = false;
-      global = false;
+      skip = FALSE;
+      global = FALSE;
       add = 1 + isym.n_numaux;
 
       /* If we are stripping all symbols, we want to skip this one.  */
-      if (finfo->info->strip == strip_all)
-       skip = true;
+      if (flaginfo->info->strip == strip_all && ! dont_skip_symbol)
+       skip = TRUE;
 
       if (! skip)
        {
-         if (isym.n_sclass == C_EXT
-             || (sym_is_global && (*sym_is_global) (input_bfd, &isym)))
+         switch (classification)
            {
+           default:
+             abort ();
+           case COFF_SYMBOL_GLOBAL:
+           case COFF_SYMBOL_COMMON:
+           case COFF_SYMBOL_PE_SECTION:
              /* 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.  */
-             global = true;
+                Locally defined function symbols, however, are an
+                exception, and are not moved to the end.  */
+             global = TRUE;
              if (! ISFCN (isym.n_type))
-               skip = true;
-           }
-         else
-           {
+               skip = TRUE;
+             break;
+
+           case COFF_SYMBOL_UNDEFINED:
+             /* Undefined symbols are left for the end.  */
+             global = TRUE;
+             skip = TRUE;
+             break;
+
+           case COFF_SYMBOL_LOCAL:
              /* This is a local symbol.  Skip it if we are discarding
-                 local symbols.  */
-             if (finfo->info->discard == discard_all)
-               skip = true;
+                local symbols.  */
+             if (flaginfo->info->discard == discard_all && ! dont_skip_symbol)
+               skip = TRUE;
+             break;
            }
        }
 
+#ifndef COFF_WITH_PE
+      /* Skip section symbols for sections which are not going to be
+        emitted.  */
+      if (!skip
+         && !dont_skip_symbol
+         && isym.n_sclass == C_STAT
+         && isym.n_type == T_NULL
+         && isym.n_numaux > 0
+         && ((*secpp)->output_section == bfd_abs_section_ptr
+             || bfd_section_removed_from_list (output_bfd,
+                                               (*secpp)->output_section)))
+       skip = TRUE;
+#endif
+
       /* 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)
-       skip = true;
+         && flaginfo->info->strip == strip_debugger
+         && ! 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
         name and decide whether to skip this symbol.  */
       if (! skip
-         && (finfo->info->strip == strip_some
-             || finfo->info->discard == discard_l))
+         && (flaginfo->info->strip == strip_some
+             || flaginfo->info->discard == discard_l))
        {
          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
+             && ((flaginfo->info->strip == strip_some
+                  && (bfd_hash_lookup (flaginfo->info->keep_hash, name, FALSE,
+                                   FALSE) == NULL))
+                  || (! global
+                      && flaginfo->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
+         && !flaginfo->info->traditional_format
+         && (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;
+         bfd_size_type amt;
+
+         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 (&flaginfo->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.  */
+         amt = sizeof (struct coff_debug_merge_type);
+         mt = (struct coff_debug_merge_type *) bfd_alloc (input_bfd, amt);
+         if (mt == NULL)
+           return FALSE;
+         mt->type_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, (esym + isymesz),
+                               isym.n_type, isym.n_sclass, 0, isym.n_numaux,
+                               &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 *name_copy;
+
+             bfd_coff_swap_sym_in (input_bfd, esl, islp);
+
+             amt = sizeof (struct coff_debug_merge_element);
+             *epp = (struct coff_debug_merge_element *)
+                 bfd_alloc (input_bfd, amt);
+             if (*epp == NULL)
+               return FALSE;
+
+             elename = _bfd_coff_internal_syment_name (input_bfd, islp,
+                                                       elebuf);
+             if (elename == NULL)
+               return FALSE;
+
+             amt = strlen (elename) + 1;
+             name_copy = (char *) bfd_alloc (input_bfd, amt);
+             if (name_copy == NULL)
+               return FALSE;
+             strcpy (name_copy, elename);
+
+             (*epp)->name = 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, (esl + isymesz),
+                                       islp->n_type, islp->n_sclass, 0,
+                                       islp->n_numaux, &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 = flaginfo->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, mt);
          else
            {
-             memcpy (buf, isym._n._n_name, SYMNMLEN);
-             buf[SYMNMLEN] = '\0';
-             name = buf;
-           }
+             struct coff_debug_merge_type *mtl;
+
+             for (mtl = mh->types; mtl != NULL; mtl = mtl->next)
+               {
+                 struct coff_debug_merge_element *me, *mel;
+
+                 if (mtl->type_class != mt->type_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 ((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;
+             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, mt);
+                 *indexp = mtl->indx;
+                 add = (eslend - esym) / isymesz;
+                 skip = TRUE;
+               }
+           }
        }
 
       /* We now know whether we are to skip this symbol or not.  */
@@ -1408,79 +1813,124 @@ 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;
-             indx = _bfd_stringtab_add (finfo->strtab, name, hash, copy);
+             name = _bfd_coff_internal_syment_name (input_bfd, &isym, NULL);
+             if (name == NULL)
+               return FALSE;
+             indx = _bfd_stringtab_add (flaginfo->strtab, name, hash, copy);
              if (indx == (bfd_size_type) -1)
-               return false;
+               return FALSE;
              isym._n._n_n._n_offset = STRING_SIZE_SIZE + indx;
            }
 
-         if (isym.n_scnum > 0)
+         switch (isym.n_sclass)
            {
-             isym.n_scnum = (*secpp)->output_section->target_index;
-             isym.n_value += ((*secpp)->output_section->vma
-                              + (*secpp)->output_offset
-                              - (*secpp)->vma);
-           }
-
-         /* The value of a C_FILE symbol is the symbol index of the
-            next C_FILE symbol.  The value of the last C_FILE symbol
-            is the symbol index to the first external symbol
-            (actually, coff_renumber_symbols does not get this
-            right--it just sets the value of the last C_FILE symbol
-            to zero--and nobody has ever complained about it).  We
-            try to get this right, below, just before we write the
-            symbols out, but in the general case we may have to write
-            the symbol out twice.  */
-         if (isym.n_sclass == C_FILE)
-           {
-             if (finfo->last_file_index != -1
-                 && finfo->last_file.n_value != output_index)
+           case C_AUTO:
+           case C_MOS:
+           case C_EOS:
+           case C_MOE:
+           case C_MOU:
+           case C_UNTAG:
+           case C_STRTAG:
+           case C_ENTAG:
+           case C_TPDEF:
+           case C_ARG:
+           case C_USTATIC:
+           case C_REG:
+           case C_REGPARM:
+           case C_FIELD:
+             /* The symbol value should not be modified.  */
+             break;
+
+           case C_FCN:
+             if (obj_pe (input_bfd)
+                 && memcmp (isym.n_name, ".bf", sizeof ".bf") != 0
+                 && isym.n_scnum > 0)
+               {
+                 /* For PE, .lf and .ef get their value left alone,
+                    while .bf gets relocated.  However, they all have
+                    "real" section numbers, and need to be moved into
+                    the new section.  */
+                 isym.n_scnum = (*secpp)->output_section->target_index;
+                 break;
+               }
+             /* Fall through.  */
+           default:
+           case C_LABEL:  /* Not completely sure about these 2 */
+           case C_EXTDEF:
+           case C_BLOCK:
+           case C_EFCN:
+           case C_NULL:
+           case C_EXT:
+           case C_STAT:
+           case C_SECTION:
+           case C_NT_WEAK:
+             /* Compute new symbol location.  */
+           if (isym.n_scnum > 0)
+             {
+               isym.n_scnum = (*secpp)->output_section->target_index;
+               isym.n_value += (*secpp)->output_offset;
+               if (! obj_pe (input_bfd))
+                 isym.n_value -= (*secpp)->vma;
+               if (! obj_pe (flaginfo->output_bfd))
+                 isym.n_value += (*secpp)->output_section->vma;
+             }
+           break;
+
+           case C_FILE:
+             /* The value of a C_FILE symbol is the symbol index of
+                the next C_FILE symbol.  The value of the last C_FILE
+                symbol is the symbol index to the first external
+                symbol (actually, coff_renumber_symbols does not get
+                this right--it just sets the value of the last C_FILE
+                symbol to zero--and nobody has ever complained about
+                it).  We try to get this right, below, just before we
+                write the symbols out, but in the general case we may
+                have to write the symbol out twice.  */
+             if (flaginfo->last_file_index != -1
+                 && flaginfo->last_file.n_value != (bfd_vma) 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)
+                 /* We must correct the value of the last C_FILE
+                    entry.  */
+                 flaginfo->last_file.n_value = output_index;
+                 if ((bfd_size_type) flaginfo->last_file_index >= syment_base)
                    {
                      /* The last C_FILE symbol is in this input file.  */
                      bfd_coff_swap_sym_out (output_bfd,
-                                            (PTR) &finfo->last_file,
-                                            (PTR) (finfo->outsyms
-                                                   + ((finfo->last_file_index
-                                                       - syment_base)
-                                                      * osymesz)));
+                                            &flaginfo->last_file,
+                                            (flaginfo->outsyms
+                                             + ((flaginfo->last_file_index
+                                                 - syment_base)
+                                                * osymesz)));
                    }
                  else
                    {
+                     file_ptr pos;
+
                      /* We have already written out the last C_FILE
                         symbol.  We need to write it out again.  We
                         borrow *outsym temporarily.  */
                      bfd_coff_swap_sym_out (output_bfd,
-                                            (PTR) &finfo->last_file,
-                                            (PTR) outsym);
-                     if (bfd_seek (output_bfd,
-                                   (obj_sym_filepos (output_bfd)
-                                    + finfo->last_file_index * osymesz),
-                                   SEEK_SET) != 0
-                         || (bfd_write (outsym, osymesz, 1, output_bfd)
-                             != osymesz))
-                       return false;
+                                            &flaginfo->last_file, outsym);
+                     pos = obj_sym_filepos (output_bfd);
+                     pos += flaginfo->last_file_index * osymesz;
+                     if (bfd_seek (output_bfd, pos, SEEK_SET) != 0
+                         || bfd_bwrite (outsym, osymesz, output_bfd) != osymesz)
+                       return FALSE;
                    }
                }
 
-             finfo->last_file_index = output_index;
-             finfo->last_file = isym;
+             flaginfo->last_file_index = output_index;
+             flaginfo->last_file = isym;
+             break;
            }
 
-         /* Output the symbol.  */
+         /* If doing task linking, convert normal global function symbols to
+            static functions.  */
+         if (flaginfo->info->task_link && IS_EXTERNAL (input_bfd, isym))
+           isym.n_sclass = C_STAT;
 
-         bfd_coff_swap_sym_out (output_bfd, (PTR) &isym, (PTR) outsym);
+         /* Output the symbol.  */
+         bfd_coff_swap_sym_out (output_bfd, &isym, outsym);
 
          *indexp = output_index;
 
@@ -1492,7 +1942,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;
            }
 
@@ -1514,20 +1970,21 @@ coff_link_input_bfd (finfo, input_bfd)
   /* Fix up the aux entries.  This must be done in a separate pass,
      because we don't know the correct symbol indices until we have
      already decided which symbols we are going to keep.  */
-
   esym = (bfd_byte *) obj_coff_external_syms (input_bfd);
   esym_end = esym + obj_raw_syment_count (input_bfd) * isymesz;
-  isymp = finfo->internal_syms;
-  indexp = finfo->sym_indices;
+  isymp = flaginfo->internal_syms;
+  indexp = flaginfo->sym_indices;
   sym_hash = obj_coff_sym_hashes (input_bfd);
-  outsym = finfo->outsyms;
+  outsym = flaginfo->outsyms;
+
   while (esym < esym_end)
     {
       int add;
 
       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 +1997,13 @@ 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 == 0
+                         || h->numaux == isymp->n_numaux);
            }
 
          esym += isymesz;
@@ -1555,13 +2018,12 @@ coff_link_input_bfd (finfo, input_bfd)
              union internal_auxent aux;
              union internal_auxent *auxp;
 
-             if (h != NULL)
+             if (h != NULL && h->aux != NULL && (h->numaux > i))
                auxp = h->aux + i;
              else
                {
-                 bfd_coff_swap_aux_in (input_bfd, (PTR) esym, isymp->n_type,
-                                       isymp->n_sclass, i, isymp->n_numaux,
-                                       (PTR) &aux);
+                 bfd_coff_swap_aux_in (input_bfd, esym, isymp->n_type,
+                                       isymp->n_sclass, i, isymp->n_numaux, &aux);
                  auxp = &aux;
                }
 
@@ -1579,41 +2041,48 @@ 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;
+                           return FALSE;
                        }
-                     filename = strings + auxp->x_file.x_n.x_offset;
-                     indx = _bfd_stringtab_add (finfo->strtab, filename,
+                     if ((bfd_size_type) auxp->x_file.x_n.x_offset >= obj_coff_strings_len (input_bfd))
+                       filename = _("<corrupt>");
+                     else
+                       filename = strings + auxp->x_file.x_n.x_offset;
+                     indx = _bfd_stringtab_add (flaginfo->strtab, filename,
                                                 hash, copy);
                      if (indx == (bfd_size_type) -1)
-                       return false;
+                       return FALSE;
                      auxp->x_file.x_n.x_offset = STRING_SIZE_SIZE + indx;
                    }
                }
-             else if (isymp->n_sclass != C_STAT || isymp->n_type != T_NULL)
+             else if ((isymp->n_sclass != C_STAT || isymp->n_type != T_NULL)
+                      && isymp->n_sclass != C_NT_WEAK)
                {
-                 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
                          && indx < obj_raw_syment_count (input_bfd))
                        {
                          /* We look forward through the symbol for
-                             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
+                            the index of the next symbol we are going
+                            to include.  I don't know if this is
+                            entirely right.  */
+                         while ((flaginfo->sym_indices[indx] < 0
+                                 || ((bfd_size_type) flaginfo->sym_indices[indx]
+                                     < syment_base))
                                 && indx < obj_raw_syment_count (input_bfd))
                            ++indx;
                          if (indx >= obj_raw_syment_count (input_bfd))
                            indx = output_index;
                          else
-                           indx = finfo->sym_indices[indx];
+                           indx = flaginfo->sym_indices[indx];
                          auxp->x_sym.x_fcnary.x_fcn.x_endndx.l = indx;
                        }
                    }
@@ -1621,19 +2090,98 @@ 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 = flaginfo->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 (flaginfo->last_bf_index != -1)
+                       {
+                         flaginfo->last_bf.x_sym.x_fcnary.x_fcn.x_endndx.l =
+                           *indexp;
+
+                         if ((bfd_size_type) flaginfo->last_bf_index
+                             >= syment_base)
+                           {
+                             void *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 = (flaginfo->outsyms
+                                       + ((flaginfo->last_bf_index
+                                           - syment_base)
+                                          * osymesz));
+
+                             bfd_coff_swap_aux_out (output_bfd,
+                                                    &flaginfo->last_bf,
+                                                    isymp->n_type,
+                                                    isymp->n_sclass,
+                                                    0, isymp->n_numaux,
+                                                    auxout);
+                           }
+                         else
+                           {
+                             file_ptr pos;
+
+                             /* 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,
+                                                    &flaginfo->last_bf,
+                                                    isymp->n_type,
+                                                    isymp->n_sclass,
+                                                    0, isymp->n_numaux,
+                                                    outsym);
+                             pos = obj_sym_filepos (output_bfd);
+                             pos += flaginfo->last_bf_index * osymesz;
+                             if (bfd_seek (output_bfd, pos, SEEK_SET) != 0
+                                 || (bfd_bwrite (outsym, osymesz, output_bfd)
+                                     != osymesz))
+                               return FALSE;
+                           }
+                       }
+
+                     if (auxp->x_sym.x_fcnary.x_fcn.x_endndx.l != 0)
+                       flaginfo->last_bf_index = -1;
+                     else
+                       {
+                         /* The endndx field of this aux entry must
+                            be updated with the symbol number of the
+                            next .bf symbol.  */
+                         flaginfo->last_bf = *auxp;
+                         flaginfo->last_bf_index = (((outsym - flaginfo->outsyms)
+                                                  / osymesz)
+                                                 + syment_base);
+                       }
                    }
                }
 
              if (h == NULL)
                {
-                 bfd_coff_swap_aux_out (output_bfd, (PTR) auxp, isymp->n_type,
+                 bfd_coff_swap_aux_out (output_bfd, auxp, isymp->n_type,
                                         isymp->n_sclass, i, isymp->n_numaux,
-                                        (PTR) outsym);
+                                        outsym);
                  outsym += osymesz;
                }
 
@@ -1647,50 +2195,68 @@ coff_link_input_bfd (finfo, input_bfd)
     }
 
   /* Relocate the line numbers, unless we are stripping them.  */
-  if (finfo->info->strip == strip_none
-      || finfo->info->strip == strip_some)
+  if (flaginfo->info->strip == strip_none
+      || flaginfo->info->strip == strip_some)
     {
       for (o = input_bfd->sections; o != NULL; o = o->next)
        {
          bfd_vma offset;
          bfd_byte *eline;
          bfd_byte *elineend;
-
-         if (o->lineno_count == 0)
+         bfd_byte *oeline;
+         bfd_boolean skipping;
+         file_ptr pos;
+         bfd_size_type amt;
+
+         /* 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
-             || bfd_read (finfo->linenos, linesz, o->lineno_count,
+             || bfd_bread (flaginfo->linenos, linesz * o->lineno_count,
                           input_bfd) != linesz * o->lineno_count)
-           return false;
+           return FALSE;
 
          offset = o->output_section->vma + o->output_offset - o->vma;
-         eline = finfo->linenos;
+         eline = flaginfo->linenos;
+         oeline = flaginfo->linenos;
          elineend = eline + linesz * o->lineno_count;
+         skipping = FALSE;
          for (; eline < elineend; eline += linesz)
            {
              struct internal_lineno iline;
 
-             bfd_coff_swap_lineno_in (input_bfd, (PTR) eline, (PTR) &iline);
+             bfd_coff_swap_lineno_in (input_bfd, eline, &iline);
 
              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;
 
-                 indx = finfo->sym_indices[iline.l_addr.l_symndx];
+                 indx = flaginfo->sym_indices[iline.l_addr.l_symndx];
 
                  if (indx < 0)
                    {
                      /* These line numbers are attached to a symbol
-                        which we are stripping.  We should really
-                        just discard the line numbers, but that would
-                        be a pain because we have already counted
-                        them.  */
-                     indx = 0;
+                        which we are stripping.  We must discard the
+                        line numbers because reading them back with
+                        no associated symbol (or associating them all
+                        with symbol #0) will fail.  We can't regain
+                        the space in the output file, but at least
+                        they're dense.  */
+                     skipping = TRUE;
                    }
                  else
                    {
@@ -1705,47 +2271,51 @@ coff_link_input_bfd (finfo, input_bfd)
                         of the line numbers rather than an absolute
                         file index.  */
                      bfd_coff_swap_sym_in (output_bfd,
-                                           (PTR) (finfo->outsyms
-                                                  + ((indx - syment_base)
-                                                     * osymesz)),
-                                           (PTR) &is);
+                                           (flaginfo->outsyms
+                                            + ((indx - syment_base)
+                                               * osymesz)), &is);
                      if ((ISFCN (is.n_type)
                           || is.n_sclass == C_BLOCK)
                          && is.n_numaux >= 1)
                        {
-                         PTR auxptr;
+                         void *auxptr;
 
-                         auxptr = (PTR) (finfo->outsyms
-                                         + ((indx - syment_base + 1)
-                                            * osymesz));
+                         auxptr = (flaginfo->outsyms
+                                   + ((indx - syment_base + 1)
+                                      * osymesz));
                          bfd_coff_swap_aux_in (output_bfd, auxptr,
                                                is.n_type, is.n_sclass,
-                                               0, is.n_numaux, (PTR) &ia);
+                                               0, is.n_numaux, &ia);
                          ia.x_sym.x_fcnary.x_fcn.x_lnnoptr =
                            (o->output_section->line_filepos
                             + o->output_section->lineno_count * linesz
-                            + eline - finfo->linenos);
-                         bfd_coff_swap_aux_out (output_bfd, (PTR) &ia,
+                            + eline - flaginfo->linenos);
+                         bfd_coff_swap_aux_out (output_bfd, &ia,
                                                 is.n_type, is.n_sclass, 0,
                                                 is.n_numaux, auxptr);
                        }
+
+                     skipping = FALSE;
                    }
 
                  iline.l_addr.l_symndx = indx;
                }
 
-             bfd_coff_swap_lineno_out (output_bfd, (PTR) &iline, (PTR) eline);
+             if (!skipping)
+               {
+                 bfd_coff_swap_lineno_out (output_bfd, &iline, oeline);
+                 oeline += linesz;
+               }
            }
 
-         if (bfd_seek (output_bfd,
-                       (o->output_section->line_filepos
-                        + o->output_section->lineno_count * linesz),
-                       SEEK_SET) != 0
-             || bfd_write (finfo->linenos, linesz, o->lineno_count,
-                           output_bfd) != linesz * o->lineno_count)
-           return false;
+         pos = o->output_section->line_filepos;
+         pos += o->output_section->lineno_count * linesz;
+         amt = oeline - flaginfo->linenos;
+         if (bfd_seek (output_bfd, pos, SEEK_SET) != 0
+             || bfd_bwrite (flaginfo->linenos, amt, output_bfd) != amt)
+           return FALSE;
 
-         o->output_section->lineno_count += o->lineno_count;
+         o->output_section->lineno_count += amt / linesz;
        }
     }
 
@@ -1753,87 +2323,136 @@ coff_link_input_bfd (finfo, input_bfd)
      symbol will be the first symbol in the next input file.  In the
      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)
+  if (flaginfo->last_file_index != -1
+      && (bfd_size_type) flaginfo->last_file_index >= syment_base)
     {
-      finfo->last_file.n_value = output_index;
-      bfd_coff_swap_sym_out (output_bfd, (PTR) &finfo->last_file,
-                            (PTR) (finfo->outsyms
-                                   + ((finfo->last_file_index - syment_base)
-                                      * osymesz)));
+      flaginfo->last_file.n_value = output_index;
+      bfd_coff_swap_sym_out (output_bfd, &flaginfo->last_file,
+                            (flaginfo->outsyms
+                             + ((flaginfo->last_file_index - syment_base)
+                                * osymesz)));
     }
 
   /* Write the modified symbols to the output file.  */
-  if (outsym > finfo->outsyms)
+  if (outsym > flaginfo->outsyms)
     {
-      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)
-       return false;
+      file_ptr pos;
+      bfd_size_type amt;
+
+      pos = obj_sym_filepos (output_bfd) + syment_base * osymesz;
+      amt = outsym - flaginfo->outsyms;
+      if (bfd_seek (output_bfd, pos, SEEK_SET) != 0
+         || bfd_bwrite (flaginfo->outsyms, amt, output_bfd) != amt)
+       return FALSE;
 
       BFD_ASSERT ((obj_raw_syment_count (output_bfd)
-                  + (outsym - finfo->outsyms) / osymesz)
+                  + (outsym - flaginfo->outsyms) / osymesz)
                  == output_index);
 
       obj_raw_syment_count (output_bfd) = output_index;
     }
 
   /* 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)
+      bfd_byte *contents;
+      struct coff_section_tdata *secdata;
+
+      if (! o->linker_mark)
+       /* This section was omitted from the link.  */
        continue;
 
-      if (! bfd_get_section_contents (input_bfd, o, finfo->contents,
-                                     (file_ptr) 0, o->_raw_size))
-       return false;
+      if ((o->flags & SEC_LINKER_CREATED) != 0)
+       continue;
+
+      if ((o->flags & SEC_HAS_CONTENTS) == 0
+         || (o->size == 0 && (o->flags & SEC_RELOC) == 0))
+       {
+         if ((o->flags & SEC_RELOC) != 0
+             && o->reloc_count != 0)
+           {
+             _bfd_error_handler
+               /* xgettext: c-format */
+               (_("%pB: relocs in section `%pA', but it has no contents"),
+                input_bfd, o);
+             bfd_set_error (bfd_error_no_contents);
+             return FALSE;
+           }
+
+         continue;
+       }
+
+      secdata = coff_section_data (input_bfd, o);
+      if (secdata != NULL && secdata->contents != NULL)
+       contents = secdata->contents;
+      else
+       {
+         contents = flaginfo->contents;
+         if (! bfd_get_full_section_contents (input_bfd, o, &contents))
+           return FALSE;
+       }
 
       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;
+         internal_relocs = (_bfd_coff_read_internal_relocs
+                            (input_bfd, o, FALSE, flaginfo->external_relocs,
+                             bfd_link_relocatable (flaginfo->info),
+                             (bfd_link_relocatable (flaginfo->info)
+                              ? (flaginfo->section_info[target_index].relocs
+                                 + o->output_section->reloc_count)
+                              : flaginfo->internal_relocs)));
+         if (internal_relocs == NULL
+             && o->reloc_count > 0)
+           return FALSE;
+
+         /* Run through the relocs looking for relocs against symbols
+            coming from discarded sections and complain about them.  */
          irel = internal_relocs;
-         for (; erel < erel_end; erel += relsz, irel++)
-           bfd_coff_swap_reloc_in (input_bfd, (PTR) erel, (PTR) irel);
+         for (; irel < &internal_relocs[o->reloc_count]; irel++)
+           {
+             struct coff_link_hash_entry *h;
+             asection *ps = NULL;
+             long symndx = irel->r_symndx;
+             if (symndx < 0)
+               continue;
+             h = obj_coff_sym_hashes (input_bfd)[symndx];
+             if (h == NULL)
+               continue;
+             while (h->root.type == bfd_link_hash_indirect
+                    || h->root.type == bfd_link_hash_warning)
+               h = (struct coff_link_hash_entry *) h->root.u.i.link;
+             if (h->root.type == bfd_link_hash_defined
+                 || h->root.type == bfd_link_hash_defweak)
+               ps = h->root.u.def.section;
+             if (ps == NULL)
+               continue;
+             /* Complain if definition comes from an excluded section.  */
+             if (ps->flags & SEC_EXCLUDE)
+               (*flaginfo->info->callbacks->einfo)
+                 /* xgettext: c-format */
+                 (_("%X`%s' referenced in section `%pA' of %pB: "
+                    "defined in discarded section `%pA' of %pB\n"),
+                  h->root.root.string, o, input_bfd, ps, ps->owner);
+           }
 
          /* Call processor specific code to relocate the section
-             contents.  */
-         if (! bfd_coff_relocate_section (output_bfd, finfo->info,
+            contents.  */
+         if (! bfd_coff_relocate_section (output_bfd, flaginfo->info,
                                           input_bfd, o,
-                                          finfo->contents,
+                                          contents,
                                           internal_relocs,
-                                          finfo->internal_syms,
-                                          finfo->sec_ptrs))
-           return false;
+                                          flaginfo->internal_syms,
+                                          flaginfo->sec_ptrs))
+           return FALSE;
 
-         if (finfo->info->relocateable)
+         if (bfd_link_relocatable (flaginfo->info))
            {
              bfd_vma offset;
              struct internal_reloc *irelend;
@@ -1842,17 +2461,16 @@ coff_link_input_bfd (finfo, input_bfd)
              offset = o->output_section->vma + o->output_offset - o->vma;
              irel = internal_relocs;
              irelend = irel + o->reloc_count;
-             rel_hash = (finfo->section_info[target_index].rel_hashes
+             rel_hash = (flaginfo->section_info[target_index].rel_hashes
                          + o->output_section->reloc_count);
              for (; irel < irelend; irel++, rel_hash++)
                {
                  struct coff_link_hash_entry *h;
-                 boolean adjusted;
+                 bfd_boolean adjusted;
 
                  *rel_hash = NULL;
 
                  /* Adjust the reloc address and symbol index.  */
-
                  irel->r_vaddr += offset;
 
                  if (irel->r_symndx == -1)
@@ -1860,10 +2478,10 @@ coff_link_input_bfd (finfo, input_bfd)
 
                  if (adjust_symndx)
                    {
-                     if (! (*adjust_symndx) (output_bfd, finfo->info,
+                     if (! (*adjust_symndx) (output_bfd, flaginfo->info,
                                              input_bfd, o, irel,
                                              &adjusted))
-                       return false;
+                       return FALSE;
                      if (adjusted)
                        continue;
                    }
@@ -1890,7 +2508,7 @@ coff_link_input_bfd (finfo, input_bfd)
                    {
                      long indx;
 
-                     indx = finfo->sym_indices[irel->r_symndx];
+                     indx = flaginfo->sym_indices[irel->r_symndx];
                      if (indx != -1)
                        irel->r_symndx = indx;
                      else
@@ -1900,33 +2518,18 @@ 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.  */
-                         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;
-                           }
-
-                         if (! ((*finfo->info->callbacks->unattached_reloc)
-                                (finfo->info, name, input_bfd, o,
-                                 irel->r_vaddr)))
-                           return false;
+                            stripping.  This should have been handled
+                            by the 'dont_skip_symbol' code in the while
+                            loop at the top of this function.  */
+                         is = flaginfo->internal_syms + irel->r_symndx;
+
+                         name = (_bfd_coff_internal_syment_name
+                                 (input_bfd, is, buf));
+                         if (name == NULL)
+                           return FALSE;
+
+                         (*flaginfo->info->callbacks->unattached_reloc)
+                           (flaginfo->info, name, input_bfd, o, irel->r_vaddr);
                        }
                    }
                }
@@ -1936,55 +2539,70 @@ 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)
+       {
+         file_ptr loc = (o->output_offset
+                         * bfd_octets_per_byte (output_bfd, o));
+         if (! bfd_set_section_contents (output_bfd, o->output_section,
+                                         contents, loc, o->size))
+           return FALSE;
+       }
+      else
+       {
+         if (! (_bfd_write_section_stabs
+                (output_bfd, &coff_hash_table (flaginfo->info)->stab_info,
+                 o, &secdata->stab_info, contents)))
+           return FALSE;
+       }
     }
 
-  if (! finfo->info->keep_memory)
-    {
-      if (! coff_link_free_symbols (input_bfd))
-       return false;
-    }
+  if (! flaginfo->info->keep_memory
+      && ! _bfd_coff_free_symbols (input_bfd))
+    return FALSE;
 
-  return true;
+  return TRUE;
 }
 
-/* Write out a global symbol.  Called via coff_link_hash_traverse.  */
+/* Write out a global symbol.  Called via bfd_hash_traverse.  */
 
-static boolean
-coff_write_global_sym (h, data)
-     struct coff_link_hash_entry *h;
-     PTR data;
+bfd_boolean
+_bfd_coff_write_global_sym (struct bfd_hash_entry *bh, void *data)
 {
-  struct coff_final_link_info *finfo = (struct coff_final_link_info *) data;
+  struct coff_link_hash_entry *h = (struct coff_link_hash_entry *) bh;
+  struct coff_final_link_info *flaginfo = (struct coff_final_link_info *) data;
   bfd *output_bfd;
   struct internal_syment isym;
   bfd_size_type symesz;
   unsigned int i;
+  file_ptr pos;
+
+  output_bfd = flaginfo->output_bfd;
 
-  output_bfd = finfo->output_bfd;
+  if (h->root.type == bfd_link_hash_warning)
+    {
+      h = (struct coff_link_hash_entry *) h->root.u.i.link;
+      if (h->root.type == bfd_link_hash_new)
+       return TRUE;
+    }
 
   if (h->indx >= 0)
-    return true;
+    return TRUE;
 
   if (h->indx != -2
-      && (finfo->info->strip == strip_all
-         || (finfo->info->strip == strip_some
-             && (bfd_hash_lookup (finfo->info->keep_hash,
-                                  h->root.root.string, false, false)
+      && (flaginfo->info->strip == strip_all
+         || (flaginfo->info->strip == strip_some
+             && (bfd_hash_lookup (flaginfo->info->keep_hash,
+                                  h->root.root.string, FALSE, FALSE)
                  == NULL))))
-    return true;
+    return TRUE;
 
   switch (h->root.type)
     {
     default:
     case bfd_link_hash_new:
+    case bfd_link_hash_warning:
       abort ();
-      return false;
+      return FALSE;
 
     case bfd_link_hash_undefined:
     case bfd_link_hash_undefweak:
@@ -2003,8 +2621,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 (flaginfo->output_bfd))
+         isym.n_value += sec->vma;
       }
       break;
 
@@ -2014,84 +2633,181 @@ coff_write_global_sym (h, data)
       break;
 
     case bfd_link_hash_indirect:
-    case bfd_link_hash_warning:
       /* Just ignore these.  They can't be handled anyhow.  */
-      return true;
+      return TRUE;
     }
 
   if (strlen (h->root.root.string) <= SYMNMLEN)
     strncpy (isym._n._n_name, h->root.root.string, SYMNMLEN);
   else
     {
-      boolean hash;
+      bfd_boolean hash;
       bfd_size_type indx;
 
-      hash = true;
-      if ((output_bfd->flags & BFD_TRADITIONAL_FORMAT) != 0)
-       hash = false;
-      indx = _bfd_stringtab_add (finfo->strtab, h->root.root.string, hash,
-                                false);
+      hash = TRUE;
+      if (flaginfo->info->traditional_format)
+       hash = FALSE;
+      indx = _bfd_stringtab_add (flaginfo->strtab, h->root.root.string, hash,
+                                FALSE);
       if (indx == (bfd_size_type) -1)
        {
-         finfo->failed = true;
-         return false;
+         flaginfo->failed = TRUE;
+         return FALSE;
        }
       isym._n._n_n._n_zeroes = 0;
       isym._n._n_n._n_offset = STRING_SIZE_SIZE + indx;
     }
 
-  isym.n_sclass = h->class;
+  isym.n_sclass = h->symbol_class;
   isym.n_type = h->type;
 
   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 (flaginfo->global_to_static)
+    {
+      if (! IS_EXTERNAL (output_bfd, isym))
+       return TRUE;
+
+      isym.n_sclass = C_STAT;
+    }
+
+  /* When a weak symbol is not overridden by a strong one,
+     turn it into an external symbol when not building a
+     shared or relocatable object.  */
+  if (! bfd_link_pic (flaginfo->info)
+      && ! bfd_link_relocatable (flaginfo->info)
+      && IS_WEAK_EXTERNAL (flaginfo->output_bfd, isym))
+    isym.n_sclass = C_EXT;
+
   isym.n_numaux = h->numaux;
-  
-  bfd_coff_swap_sym_out (output_bfd, (PTR) &isym, (PTR) finfo->outsyms);
+
+  bfd_coff_swap_sym_out (output_bfd, &isym, flaginfo->outsyms);
 
   symesz = bfd_coff_symesz (output_bfd);
 
-  if (bfd_seek (output_bfd,
-               (obj_sym_filepos (output_bfd)
-                + obj_raw_syment_count (output_bfd) * symesz),
-               SEEK_SET) != 0
-      || bfd_write (finfo->outsyms, symesz, 1, output_bfd) != symesz)
+  pos = obj_sym_filepos (output_bfd);
+  pos += obj_raw_syment_count (output_bfd) * symesz;
+  if (bfd_seek (output_bfd, pos, SEEK_SET) != 0
+      || bfd_bwrite (flaginfo->outsyms, symesz, output_bfd) != symesz)
     {
-      finfo->failed = true;
-      return false;
+      flaginfo->failed = TRUE;
+      return FALSE;
     }
 
   h->indx = obj_raw_syment_count (output_bfd);
 
   ++obj_raw_syment_count (output_bfd);
 
-  /* Write out any associated aux entries.  There normally will be
-     none.  If there are any, I have no idea how to modify them.  */
+  /* Write out any associated aux entries.  Most of the aux entries
+     will have been modified in _bfd_coff_link_input_bfd.  We have to
+     handle section aux entries here, now that we have the final
+     relocation and line number counts.  */
   for (i = 0; i < isym.n_numaux; i++)
     {
-      bfd_coff_swap_aux_out (output_bfd, (PTR) (h->aux + i), isym.n_type,
-                            isym.n_sclass, i, isym.n_numaux,
-                            (PTR) finfo->outsyms);
-      if (bfd_write (finfo->outsyms, symesz, 1, output_bfd) != symesz)
+      union internal_auxent *auxp;
+
+      auxp = h->aux + i;
+
+      /* Look for a section aux entry here using the same tests that
+        coff_swap_aux_out uses.  */
+      if (i == 0
+         && (isym.n_sclass == C_STAT
+             || isym.n_sclass == C_HIDDEN)
+         && isym.n_type == T_NULL
+         && (h->root.type == bfd_link_hash_defined
+             || h->root.type == bfd_link_hash_defweak))
+       {
+         asection *sec;
+
+         sec = h->root.u.def.section->output_section;
+         if (sec != NULL)
+           {
+             auxp->x_scn.x_scnlen = sec->size;
+
+             /* For PE, an overflow on the final link reportedly does
+                not matter.  FIXME: Why not?  */
+             if (sec->reloc_count > 0xffff
+                 && (! obj_pe (output_bfd)
+                     || bfd_link_relocatable (flaginfo->info)))
+               _bfd_error_handler
+                 /* xgettext: c-format */
+                 (_("%pB: %pA: reloc overflow: %#x > 0xffff"),
+                  output_bfd, sec, sec->reloc_count);
+
+             if (sec->lineno_count > 0xffff
+                 && (! obj_pe (output_bfd)
+                     || bfd_link_relocatable (flaginfo->info)))
+               _bfd_error_handler
+                 /* xgettext: c-format */
+                 (_("%pB: warning: %pA: line number overflow: %#x > 0xffff"),
+                  output_bfd, sec, sec->lineno_count);
+
+             auxp->x_scn.x_nreloc = sec->reloc_count;
+             auxp->x_scn.x_nlinno = sec->lineno_count;
+             auxp->x_scn.x_checksum = 0;
+             auxp->x_scn.x_associated = 0;
+             auxp->x_scn.x_comdat = 0;
+           }
+       }
+
+      bfd_coff_swap_aux_out (output_bfd, auxp, isym.n_type,
+                            isym.n_sclass, (int) i, isym.n_numaux,
+                            flaginfo->outsyms);
+      if (bfd_bwrite (flaginfo->outsyms, symesz, output_bfd) != symesz)
        {
-         finfo->failed = true;
-         return false;
+         flaginfo->failed = TRUE;
+         return FALSE;
        }
       ++obj_raw_syment_count (output_bfd);
     }
 
-  return true;
+  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.  */
+
+bfd_boolean
+_bfd_coff_write_task_globals (struct coff_link_hash_entry *h, void *data)
+{
+  struct coff_final_link_info *flaginfo = (struct coff_final_link_info *) data;
+  bfd_boolean rtnval = TRUE;
+  bfd_boolean save_global_to_static;
+
+  if (h->root.type == bfd_link_hash_warning)
+    h = (struct coff_link_hash_entry *) h->root.u.i.link;
+
+  if (h->indx < 0)
+    {
+      switch (h->root.type)
+       {
+       case bfd_link_hash_defined:
+       case bfd_link_hash_defweak:
+         save_global_to_static = flaginfo->global_to_static;
+         flaginfo->global_to_static = TRUE;
+         rtnval = _bfd_coff_write_global_sym (&h->root.root, data);
+         flaginfo->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)
-     bfd *output_bfd;
-     struct coff_final_link_info *finfo;
-     asection *output_section;
-     struct bfd_link_order *link_order;
+bfd_boolean
+_bfd_coff_reloc_link_order (bfd *output_bfd,
+                           struct coff_final_link_info *flaginfo,
+                           asection *output_section,
+                           struct bfd_link_order *link_order)
 {
   reloc_howto_type *howto;
   struct internal_reloc *irel;
@@ -2101,7 +2817,7 @@ coff_reloc_link_order (output_bfd, finfo, output_section, link_order)
   if (howto == NULL)
     {
       bfd_set_error (bfd_error_bad_value);
-      return false;
+      return FALSE;
     }
 
   if (link_order->u.reloc.p->addend != 0)
@@ -2109,18 +2825,17 @@ coff_reloc_link_order (output_bfd, finfo, output_section, link_order)
       bfd_size_type size;
       bfd_byte *buf;
       bfd_reloc_status_type rstat;
-      boolean ok;
+      bfd_boolean ok;
+      file_ptr loc;
 
       size = bfd_get_reloc_size (howto);
       buf = (bfd_byte *) bfd_zmalloc (size);
-      if (buf == NULL)
-       {
-         bfd_set_error (bfd_error_no_memory);
-         return false;
-       }
+      if (buf == NULL && size != 0)
+       return FALSE;
 
       rstat = _bfd_relocate_contents (howto, output_bfd,
-                                     link_order->u.reloc.p->addend, buf);
+                                     (bfd_vma) link_order->u.reloc.p->addend,
+                                     buf);
       switch (rstat)
        {
        case bfd_reloc_ok:
@@ -2129,33 +2844,29 @@ coff_reloc_link_order (output_bfd, finfo, output_section, link_order)
        case bfd_reloc_outofrange:
          abort ();
        case bfd_reloc_overflow:
-         if (! ((*finfo->info->callbacks->reloc_overflow)
-                (finfo->info,
-                 (link_order->type == bfd_section_reloc_link_order
-                  ? bfd_section_name (output_bfd,
-                                      link_order->u.reloc.p->u.section)
-                  : link_order->u.reloc.p->u.name),
-                 howto->name, link_order->u.reloc.p->addend,
-                 (bfd *) NULL, (asection *) NULL, (bfd_vma) 0)))
-           {
-             free (buf);
-             return false;
-           }
+         (*flaginfo->info->callbacks->reloc_overflow)
+           (flaginfo->info, NULL,
+            (link_order->type == bfd_section_reloc_link_order
+             ? bfd_section_name (link_order->u.reloc.p->u.section)
+             : link_order->u.reloc.p->u.name),
+            howto->name, link_order->u.reloc.p->addend,
+            (bfd *) NULL, (asection *) NULL, (bfd_vma) 0);
          break;
        }
-      ok = bfd_set_section_contents (output_bfd, output_section, (PTR) buf,
-                                    (file_ptr) link_order->offset, size);
+      loc = link_order->offset * bfd_octets_per_byte (output_bfd,
+                                                     output_section);
+      ok = bfd_set_section_contents (output_bfd, output_section, buf,
+                                    loc, size);
       free (buf);
       if (! ok)
-       return false;
+       return FALSE;
     }
 
   /* Store the reloc information in the right place.  It will get
      swapped and written out at the end of the final_link routine.  */
-
-  irel = (finfo->section_info[output_section->target_index].relocs
+  irel = (flaginfo->section_info[output_section->target_index].relocs
          + output_section->reloc_count);
-  rel_hash_ptr = (finfo->section_info[output_section->target_index].rel_hashes
+  rel_hash_ptr = (flaginfo->section_info[output_section->target_index].rel_hashes
                  + output_section->reloc_count);
 
   memset (irel, 0, sizeof (struct internal_reloc));
@@ -2166,9 +2877,9 @@ coff_reloc_link_order (output_bfd, finfo, output_section, link_order)
   if (link_order->type == bfd_section_reloc_link_order)
     {
       /* We need to somehow locate a symbol in the right section.  The
-         symbol must either have a value of zero, or we must adjust
-         the addend by the value of the symbol.  FIXME: Write this
-         when we need it.  The old linker couldn't handle this anyhow.  */
+        symbol must either have a value of zero, or we must adjust
+        the addend by the value of the symbol.  FIXME: Write this
+        when we need it.  The old linker couldn't handle this anyhow.  */
       abort ();
       *rel_hash_ptr = NULL;
       irel->r_symndx = 0;
@@ -2177,9 +2888,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, flaginfo->info,
+                                        link_order->u.reloc.p->u.name,
+                                        FALSE, FALSE, TRUE));
       if (h != NULL)
        {
          if (h->indx >= 0)
@@ -2195,10 +2907,9 @@ coff_reloc_link_order (output_bfd, finfo, output_section, link_order)
        }
       else
        {
-         if (! ((*finfo->info->callbacks->unattached_reloc)
-                (finfo->info, link_order->u.reloc.p->u.name, (bfd *) NULL,
-                 (asection *) NULL, (bfd_vma) 0)))
-           return false;
+         (*flaginfo->info->callbacks->unattached_reloc)
+           (flaginfo->info, link_order->u.reloc.p->u.name,
+            (bfd *) NULL, (asection *) NULL, (bfd_vma) 0);
          irel->r_symndx = 0;
        }
     }
@@ -2210,32 +2921,27 @@ coff_reloc_link_order (output_bfd, finfo, output_section, link_order)
      routines anyhow.  r_extern is only used for ECOFF.  */
 
   /* FIXME: What is the right value for r_offset?  Is zero OK?  */
-
   ++output_section->reloc_count;
 
-  return true;
+  return TRUE;
 }
 
 /* A basic reloc handling routine which may be used by processors with
    simple relocs.  */
 
-boolean
-_bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
-                                   input_section, contents, relocs, syms,
-                                   sections)
-     bfd *output_bfd;
-     struct bfd_link_info *info;
-     bfd *input_bfd;
-     asection *input_section;
-     bfd_byte *contents;
-     struct internal_reloc *relocs;
-     struct internal_syment *syms;
-     asection **sections;
+bfd_boolean
+_bfd_coff_generic_relocate_section (bfd *output_bfd,
+                                   struct bfd_link_info *info,
+                                   bfd *input_bfd,
+                                   asection *input_section,
+                                   bfd_byte *contents,
+                                   struct internal_reloc *relocs,
+                                   struct internal_syment *syms,
+                                   asection **sections)
 {
   struct internal_reloc *rel;
   struct internal_reloc *relend;
 
-
   rel = relocs;
   relend = rel + input_section->reloc_count;
   for (; rel < relend; rel++)
@@ -2245,6 +2951,7 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
       struct internal_syment *sym;
       bfd_vma addend;
       bfd_vma val;
+      asection *sec;
       reloc_howto_type *howto;
       bfd_reloc_status_type rstat;
 
@@ -2255,44 +2962,50 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
          h = NULL;
          sym = NULL;
        }
+      else if (symndx < 0
+              || (unsigned long) symndx >= obj_raw_syment_count (input_bfd))
+       {
+         _bfd_error_handler
+           /* xgettext: c-format */
+           (_("%pB: illegal symbol index %ld in relocs"), input_bfd, symndx);
+         return FALSE;
+       }
       else
-       {    
+       {
          h = obj_coff_sym_hashes (input_bfd)[symndx];
          sym = syms + symndx;
        }
 
       /* COFF treats common symbols in one of two ways.  Either the
-         size of the symbol is included in the section contents, or it
-         is not.  We assume that the size is not included, and force
-         the rtype_to_howto function to adjust the addend as needed.  */
-
+        size of the symbol is included in the section contents, or it
+        is not.  We assume that the size is not included, and force
+        the rtype_to_howto function to adjust the addend as needed.  */
       if (sym != NULL && sym->n_scnum != 0)
        addend = - sym->n_value;
       else
        addend = 0;
 
-
       howto = bfd_coff_rtype_to_howto (input_bfd, input_section, rel, h,
                                       sym, &addend);
       if (howto == NULL)
-       return false;
+       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 relocatable 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 relocatable link,
+        then we should ignore the symbol value.  */
+      if (howto->pc_relative && howto->pcrel_offset)
+       {
+         if (bfd_link_relocatable (info))
+           continue;
+         if (sym != NULL && sym->n_scnum != 0)
+           addend += sym->n_value;
+       }
 
       val = 0;
-
+      sec = NULL;
       if (h == NULL)
        {
-         asection *sec;
-          int i;
-
          if (symndx == -1)
            {
              sec = bfd_abs_section_ptr;
@@ -2301,27 +3014,17 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
          else
            {
              sec = sections[symndx];
-              val = (sec->output_section->vma
+
+             /* PR 19623: Relocations against symbols in
+                the absolute sections should ignored.  */
+             if (bfd_is_abs_section (sec))
+               continue;
+
+             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$nnn
-                  or .idata$nnn or reloc or edata */
-               if (strcmp (input_section->name, ".text") != 0)
-                 {
-                   if (strncmp (sec->name, ".idata$", 7) == 0
-                       || strcmp (sec->name, ".reloc") == 0
-                       || strcmp (sec->name, ".edata") == 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
@@ -2329,152 +3032,89 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
          if (h->root.type == bfd_link_hash_defined
              || h->root.type == bfd_link_hash_defweak)
            {
-             asection *sec;
-
+             /* Defined weak symbols are a GNU extension. */
              sec = h->root.u.def.section;
              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$nnn
-                  or .idata$nnnn or reloc or edata. */
-               if (strcmp (input_section->name, ".text") != 0)
-                 {
-                   if (strncmp (sec->name, ".idata$", 7) == 0
-                       || strcmp (sec->name, ".reloc") == 0
-                       || strcmp (sec->name, ".edata") == 0)
-                     val -= NT_IMAGE_BASE;
-                   else if (strncmp (sec->name, ".rsrc$", 6) == 0) 
-                     {
-                       val -= NT_IMAGE_BASE;
-                       val += sec->vma;
-                     }
-                 }
-             }
            }
-         else if (! info->relocateable)
+
+         else if (h->root.type == bfd_link_hash_undefweak)
            {
-             if (! ((*info->callbacks->undefined_symbol)
-                    (info, h->root.root.string, input_bfd, input_section,
-                     rel->r_vaddr - input_section->vma)))
-               return false;
+             if (h->symbol_class == C_NT_WEAK && h->numaux == 1)
+               {
+                 /* See _Microsoft Portable Executable and Common Object
+                    File Format Specification_, section 5.5.3.
+                    Note that weak symbols without aux records are a GNU
+                    extension.
+                    FIXME: All weak externals are treated as having
+                    characteristic IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY (1).
+                    These behave as per SVR4 ABI:  A library member
+                    will resolve a weak external only if a normal
+                    external causes the library member to be linked.
+                    See also linker.c: generic_link_check_archive_element. */
+                 struct coff_link_hash_entry *h2 =
+                   h->auxbfd->tdata.coff_obj_data->sym_hashes[
+                   h->aux->x_sym.x_tagndx.l];
+
+                 if (!h2 || h2->root.type == bfd_link_hash_undefined)
+                   {
+                     sec = bfd_abs_section_ptr;
+                     val = 0;
+                   }
+                 else
+                   {
+                     sec = h2->root.u.def.section;
+                     val = h2->root.u.def.value
+                       + sec->output_section->vma + sec->output_offset;
+                   }
+               }
+             else
+               /* This is a GNU extension.  */
+               val = 0;
            }
-       }
 
-      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;
-             }
+         else if (! bfd_link_relocatable (info))
+           (*info->callbacks->undefined_symbol)
+             (info, h->root.root.string, input_bfd, input_section,
+              rel->r_vaddr - input_section->vma, TRUE);
+       }
 
-           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 the input section defining the symbol has been discarded
+        then zero this reloc field.  */
+      if (sec != NULL && discarded_section (sec))
+       {
+         _bfd_clear_contents (howto, input_bfd, input_section,
+                              contents, rel->r_vaddr - input_section->vma);
+         continue;
+       }
 
       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, (FILE *) 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 bfd_vma here,
+                and dlltool reads in a bfd_vma.  */
+             bfd_vma 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 (bfd_vma), (FILE *) info->base_file)
+                 != sizeof (bfd_vma))
+               {
+                 bfd_set_error (bfd_error_system_call);
+                 return FALSE;
+               }
            }
        }
-  
+
       rstat = _bfd_final_link_relocate (howto, input_bfd, input_section,
                                        contents,
                                        rel->r_vaddr - input_section->vma,
@@ -2486,6 +3126,12 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
          abort ();
        case bfd_reloc_ok:
          break;
+       case bfd_reloc_outofrange:
+         _bfd_error_handler
+           /* xgettext: c-format */
+           (_("%pB: bad reloc address %#" PRIx64 " in section `%pA'"),
+            input_bfd, (uint64_t) rel->r_vaddr, input_section);
+         return FALSE;
        case bfd_reloc_overflow:
          {
            const char *name;
@@ -2494,24 +3140,20 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
            if (symndx == -1)
              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;
+             name = NULL;
            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)
-                  (info, name, howto->name, (bfd_vma) 0, input_bfd,
-                   input_section, rel->r_vaddr - input_section->vma)))
-             return false;
+           (*info->callbacks->reloc_overflow)
+             (info, (h ? &h->root : NULL), name, howto->name,
+              (bfd_vma) 0, input_bfd, input_section,
+              rel->r_vaddr - input_section->vma);
          }
        }
     }
-
-  return true;
+  return TRUE;
 }
This page took 0.076183 seconds and 4 git commands to generate.