* elf32-i386qnx.c (TARGET_LITTLE_NAME): Define.
[deliverable/binutils-gdb.git] / bfd / cofflink.c
index 003e3b998983d81dbd24f36ed3395a586c73e1c4..c1302ee878a28d0dc9dfbe6845ee04c47815a1c1 100644 (file)
@@ -1,5 +1,6 @@
 /* COFF specific linker code.
-   Copyright 1994, 1995 Free Software Foundation, Inc.
+   Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
+   Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Cygnus Support.
 
 This file is part of BFD, the Binary File Descriptor library.
@@ -27,124 +28,42 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #include "coff/internal.h"
 #include "libcoff.h"
 
-#define STRING_SIZE_SIZE (4)
-
-/* Information we keep for each section in the output file when doing
-   a relocateable link.  */
-
-struct coff_link_section_info
-{
-  /* The relocs to be output.  */
-  struct internal_reloc *relocs;
-  /* For each reloc against a global symbol whose index was not known
-     when the reloc was handled, the global hash table entry.  */
-  struct coff_link_hash_entry **rel_hashes;
-};
-
-/* Information that we pass around while doing the final link step.  */
-
-struct coff_final_link_info
-{
-  /* General link information.  */
-  struct bfd_link_info *info;
-  /* Output BFD.  */
-  bfd *output_bfd;
-  /* Used to indicate failure in traversal routine.  */
-  boolean failed;
-  /* Hash table for long symbol name.  */
-  struct bfd_strtab_hash *strtab;
-  /* When doing a relocateable link, an array of information kept for
-     each output section, indexed by the target_index field.  */
-  struct coff_link_section_info *section_info;
-  /* Symbol index of last C_FILE symbol (-1 if none).  */
-  long last_file_index;
-  /* Contents of last C_FILE symbol.  */
-  struct internal_syment last_file;
-  /* Buffer large enough to hold swapped symbols of any input file.  */
-  struct internal_syment *internal_syms;
-  /* Buffer large enough to hold sections of symbols of any input file.  */
-  asection **sec_ptrs;
-  /* Buffer large enough to hold output indices of symbols of any
-     input file.  */
-  long *sym_indices;
-  /* Buffer large enough to hold output symbols for any input file.  */
-  bfd_byte *outsyms;
-  /* Buffer large enough to hold external line numbers for any input
-     section.  */
-  bfd_byte *linenos;
-  /* Buffer large enough to hold any input section.  */
-  bfd_byte *contents;
-  /* Buffer large enough to hold external relocs of any input section.  */
-  bfd_byte *external_relocs;
-  /* Buffer large enough to hold swapped relocs of any input section.  */
-  struct internal_reloc *internal_relocs;
-
-enum   bfd_link_subsystem  subsystem;
-bfd_link_stack_heap stack_heap_parameters;
-};
-
-static struct bfd_hash_entry *coff_link_hash_newfunc
-  PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
 static boolean coff_link_add_object_symbols
   PARAMS ((bfd *, struct bfd_link_info *));
 static boolean coff_link_check_archive_element
   PARAMS ((bfd *, struct bfd_link_info *, boolean *));
-static INLINE const char *_bfd_coff_internal_syment_name
-  PARAMS ((bfd *, const struct internal_syment *, char *));
 static boolean coff_link_check_ar_symbols
   PARAMS ((bfd *, struct bfd_link_info *, boolean *));
 static boolean coff_link_add_symbols PARAMS ((bfd *, struct bfd_link_info *));
-static boolean coff_link_input_bfd
-  PARAMS ((struct coff_final_link_info *, bfd *));
-static boolean coff_write_global_sym
-  PARAMS ((struct coff_link_hash_entry *, PTR));
-static boolean coff_reloc_link_order
-  PARAMS ((bfd *, struct coff_final_link_info *, asection *,
-          struct bfd_link_order *));
-
-
-/* These new data and data types are used to keep track of the .idata$4 and
-   .idata$5 relocations which are put into the .idata section for all of the
-   *.dll input libraries linked in.  This is not a great solution, and may
-   break in the future if MS changes the format of its libraries, but it
-   does work for the collection of mstools libraries we are currently working
-   with.  The main problem is that there are some new majic symbols defined
-   in the libraries which are non-standard coff and simply aren't handled 
-   completely by ld.  What has been included here will help finish up the job.
-     Basically, during the link, .idata$4 and .idata$5 pointers are correctly
-   relocated to the image.  At the very end of the link, the .idata$2
-   information is written.  This data appears at the beginning of the .idata
-   section and a 'set' of information appears for each *.dll passed in.
-   Each set of information consists of 3 addresses, a pointer to the .idata$4
-   start, a pointer to .idata$6 (which has the name of the dll), and a pointer
-   to .idata$5 start.  The idata$4 and 5 information is a list of pointers
-   which appear to point to the name of various functions found within the dll.
-   When invoked, the loader will write over these names with the correct
-   addresses to use for these functions.  
-     Without this 'fix', all information appears correctly except for the
-   addresses of the .idata$4 and 5 starts within the .idata$2 portion of the
-   .idata section.  What we will do is to keep track of the dll's processed
-   and the number of functions needed from each dll.  From this information
-   we can correctly compute the start of the idata$4 and 5 lists for each
-   dll in the idata section */
-static int num_DLLs_done = 0;
-static int num_DLLs      = 0;
-static int all_entries   = 0;
-struct DLL_struct {
-  const char * DLL_name;
-  int          num_entries;
-};
-struct DLL_struct MS_DLL[10];
-static bfd_vma idata_4_prev = 0;
-static bfd_vma idata_5_prev = 0;
-static bfd_vma add_to_val   = 0;
-
+static char *dores_com PARAMS ((char *, bfd *, int));
+static char *get_name PARAMS ((char *, char **));
+static int process_embedded_commands
+  PARAMS ((bfd *, struct bfd_link_info *, bfd *));
+static void mark_relocs PARAMS ((struct coff_final_link_info *, bfd *));
+
+/* 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))
+
+/* 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 *
+_bfd_coff_link_hash_newfunc (entry, table, string)
      struct bfd_hash_entry *entry;
      struct bfd_hash_table *table;
      const char *string;
@@ -157,10 +76,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 *)
@@ -180,6 +96,20 @@ coff_link_hash_newfunc (entry, table, string)
   return (struct bfd_hash_entry *) ret;
 }
 
+/* Initialize a COFF linker hash table.  */
+
+boolean
+_bfd_coff_link_hash_table_init (table, abfd, newfunc)
+     struct coff_link_hash_table *table;
+     bfd *abfd;
+     struct bfd_hash_entry *(*newfunc) PARAMS ((struct bfd_hash_entry *,
+                                               struct bfd_hash_table *,
+                                               const char *));
+{
+  table->stab_info = NULL;
+  return _bfd_link_hash_table_init (&table->root, abfd, newfunc);
+}
+
 /* Create a COFF linker hash table.  */
 
 struct bfd_link_hash_table *
@@ -187,16 +117,13 @@ _bfd_coff_link_hash_table_create (abfd)
      bfd *abfd;
 {
   struct coff_link_hash_table *ret;
+  bfd_size_type amt = sizeof (struct coff_link_hash_table);
 
-  ret = ((struct coff_link_hash_table *)
-        bfd_alloc (abfd, 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))
     {
       free (ret);
       return (struct bfd_link_hash_table *) NULL;
@@ -204,6 +131,38 @@ _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 (entry, table, string)
+     struct bfd_hash_entry *entry;
+     struct bfd_hash_table *table;
+     const char *string;
+{
+  struct coff_debug_merge_hash_entry *ret =
+    (struct coff_debug_merge_hash_entry *) entry;
+
+  /* Allocate the structure if it has not already been allocated by a
+     subclass.  */
+  if (ret == (struct coff_debug_merge_hash_entry *) NULL)
+    ret = ((struct coff_debug_merge_hash_entry *)
+          bfd_hash_allocate (table,
+                             sizeof (struct coff_debug_merge_hash_entry)));
+  if (ret == (struct coff_debug_merge_hash_entry *) NULL)
+    return (struct bfd_hash_entry *) ret;
+
+  /* Call the allocation method of the superclass.  */
+  ret = ((struct coff_debug_merge_hash_entry *)
+        bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string));
+  if (ret != (struct coff_debug_merge_hash_entry *) NULL)
+    {
+      /* Set local fields.  */
+      ret->types = NULL;
+    }
+
+  return (struct bfd_hash_entry *) ret;
+}
+
 /* Given a COFF BFD, add symbols to the global hash table as
    appropriate.  */
 
@@ -236,6 +195,7 @@ coff_link_add_object_symbols (abfd, info)
     return false;
   if (! coff_link_add_symbols (abfd, info))
     return false;
+
   if (! info->keep_memory)
     {
       if (! _bfd_coff_free_symbols (abfd))
@@ -276,40 +236,6 @@ coff_link_check_archive_element (abfd, info, pneeded)
   return true;
 }
 
-/* Get the name of a symbol.  The caller must pass in a buffer of size
-   >= SYMNMLEN + 1.  */
-
-static INLINE const char *
-_bfd_coff_internal_syment_name (abfd, sym, buf)
-     bfd *abfd;
-     const struct internal_syment *sym;
-     char *buf;
-{
-  /* 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';
-      return buf;
-    }
-  else
-    {
-      const char *strings;
-
-      BFD_ASSERT (sym->_n._n_n._n_offset >= STRING_SIZE_SIZE);
-      strings = obj_coff_strings (abfd);
-      if (strings == NULL)
-       {
-         strings = _bfd_coff_read_string_table (abfd);
-         if (strings == NULL)
-           return NULL;
-       }
-      return strings + sym->_n._n_n._n_offset;
-    }
-}
-
 /* Look through the symbols to see if this object file should be
    included in the link.  */
 
@@ -319,27 +245,25 @@ coff_link_check_ar_symbols (abfd, info, pneeded)
      struct bfd_link_info *info;
      boolean *pneeded;
 {
-  boolean (*sym_is_global) PARAMS ((bfd *, struct internal_syment *));
   bfd_size_type symesz;
   bfd_byte *esym;
   bfd_byte *esym_end;
 
   *pneeded = false;
 
-  sym_is_global = coff_backend_info (abfd)->_bfd_coff_sym_is_global;
-
   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;
+      enum coff_symbol_classification classification;
 
       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))
+      classification = bfd_coff_classify_symbol (abfd, &sym);
+      if (classification == COFF_SYMBOL_GLOBAL
+         || classification == COFF_SYMBOL_COMMON)
        {
          const char *name;
          char buf[SYMNMLEN + 1];
@@ -353,6 +277,16 @@ coff_link_check_ar_symbols (abfd, info, pneeded)
            return false;
          h = bfd_link_hash_lookup (info->hash, name, false, false, true);
 
+         /* auto import */
+         if (!h && info->pei386_auto_import)
+           {
+             if (!strncmp (name,"__imp_", 6))
+               {
+                 h =
+                    bfd_link_hash_lookup (info->hash, name + 6, false, false,
+                                          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
@@ -381,15 +315,22 @@ coff_link_add_symbols (abfd, info)
      bfd *abfd;
      struct bfd_link_info *info;
 {
-  boolean (*sym_is_global) PARAMS ((bfd *, struct internal_syment *));
+  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;
+  boolean keep_syms;
   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;
+  /* 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;
@@ -400,18 +341,11 @@ coff_link_add_symbols (abfd, info)
 
   /* 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 *))));
+  amt = symcount * sizeof (struct coff_link_hash_entry *);
+  sym_hash = (struct coff_link_hash_entry **) bfd_zalloc (abfd, amt);
   if (sym_hash == NULL && symcount != 0)
-    {
-      bfd_set_error (bfd_error_no_memory);
-      return false;
-    }
+    goto error_return;
   obj_coff_sym_hashes (abfd) = sym_hash;
-  memset (sym_hash, 0,
-         (size_t) symcount * sizeof (struct coff_link_hash_entry *));
 
   symesz = bfd_coff_symesz (abfd);
   BFD_ASSERT (symesz == bfd_coff_auxesz (abfd));
@@ -420,24 +354,26 @@ coff_link_add_symbols (abfd, info)
   while (esym < esym_end)
     {
       struct internal_syment sym;
+      enum coff_symbol_classification classification;
       boolean copy;
 
       bfd_coff_swap_sym_in (abfd, (PTR) esym, (PTR) &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;
+         boolean addit;
 
          /* This symbol is externally visible.  */
 
          name = _bfd_coff_internal_syment_name (abfd, &sym, buf);
          if (name == NULL)
-           return false;
+           goto error_return;
 
          /* We must copy the name into memory if we got it from the
              syment itself, rather than the string table.  */
@@ -448,44 +384,165 @@ coff_link_add_symbols (abfd, info)
 
          value = sym.n_value;
 
-         if (sym.n_scnum == 0)
+         switch (classification)
            {
-             if (value == 0)
-               {
-                 flags = 0;
-                 section = bfd_und_section_ptr;
-               }
-             else
+           default:
+             abort ();
+
+           case COFF_SYMBOL_GLOBAL:
+             flags = BSF_EXPORT | BSF_GLOBAL;
+             section = coff_section_from_bfd_index (abfd, sym.n_scnum);
+             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);
+             break;
+           }
+
+         if (IS_WEAK_EXTERNAL (abfd, sym))
+           flags = BSF_WEAK;
+
+         addit = true;
+
+         /* 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)
+           {
+             *sym_hash = coff_link_hash_lookup (coff_hash_table (info),
+                                                name, false, copy, false);
+             if (*sym_hash != NULL)
                {
-                 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)
+             && section->comdat != NULL
+             && strncmp (name, "??_", 3) == 0
+             && strcmp (name, 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
+                 && (*sym_hash)->root.u.def.section->comdat != NULL
+                 && strcmp ((*sym_hash)->root.u.def.section->comdat->name,
+                            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 (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 (info->hash->creator->flavour == bfd_get_flavour (abfd))
            {
-             if (((*sym_hash)->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)->class = sym.n_sclass;
-                 (*sym_hash)->type = sym.n_type;
-                 (*sym_hash)->numaux = sym.n_numaux;
-                 (*sym_hash)->auxbfd = abfd;
+             /* 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)->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_defweak))
+               {
+                 (*sym_hash)->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)
+                         (_("Warning: type of symbol `%s' changed from %d to %d in %s"),
+                          name, (*sym_hash)->type, sym.n_type,
+                          bfd_archive_filename (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)
                    {
                      union internal_auxent *alloc;
@@ -493,154 +550,97 @@ 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);
+                                             sym.n_sclass, (int) i,
+                                             sym.n_numaux, (PTR) 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->_raw_size == 0)
+               section->_raw_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;
-}
+  /* If this is a non-traditional, non-relocateable link, try to
+     optimize the handling of any .stab/.stabstr sections.  */
+  if (! info->relocateable
+      && ! info->traditional_format
+      && info->hash->creator->flavour == bfd_get_flavour (abfd)
+      && (info->strip != strip_all && info->strip != strip_debugger))
+    {
+      asection *stab, *stabstr;
 
-/* parse out a -heap <reserved>,<commit> line */
+      stab = bfd_get_section_by_name (abfd, ".stab");
+      if (stab != NULL)
+       {
+         stabstr = bfd_get_section_by_name (abfd, ".stabstr");
 
-static char *
-dores_com (ptr, def,res, com)
-     char *ptr;
-     int *def;
-     int *res;
-     int *com;
-{
-  *def = 1;
-  *res = strtoul (ptr, &ptr, 0);
-  if (ptr[0] == ',')
-    *com = strtoul (ptr+1, &ptr, 0);
-  return ptr;
-}
+         if (stabstr != NULL)
+           {
+             struct coff_link_hash_table *table;
+             struct coff_section_tdata *secdata;
 
-static char *get_name(ptr, dst)
-char *ptr;
-char **dst;
-{
-  while (*ptr == ' ')
-    ptr++;
-  *dst = ptr;
-  while (*ptr && *ptr != ' ')
-    ptr++;
-  *ptr = 0;
-  return ptr+1;
-}
-/* Process any magic embedded commands in a section called .drectve */
-                       
-static int
-process_embedded_commands (abfd)
-     bfd *abfd;
-{
-  asection *sec = bfd_get_section_by_name (abfd, ".drectve");
-  char *s;
-  char *e;
-  char *copy;
-  if (!sec) 
-    return 1;
-  
-  copy = malloc ((size_t) sec->_raw_size);
-  if (!copy) 
-    {
-      bfd_set_error (bfd_error_no_memory);
-      return 0;
-    }
-  if (! bfd_get_section_contents(abfd, sec, copy, 0, sec->_raw_size)) 
-    {
-      free (copy);
-      return 0;
-    }
-  e = copy + sec->_raw_size;
-  for (s = copy;  s < e ; ) 
-    {
-      if (s[0]!= '-') {
-       s++;
-       continue;
-      }
-      if (strncmp (s,"-attr", 5) == 0)
-       {
-         char *name;
-         char *attribs;
-         asection *asec;
+             secdata = coff_section_data (abfd, stab);
+             if (secdata == NULL)
+               {
+                 amt = sizeof (struct coff_section_tdata);
+                 stab->used_by_bfd = (PTR) bfd_zalloc (abfd, amt);
+                 if (stab->used_by_bfd == NULL)
+                   goto error_return;
+                 secdata = coff_section_data (abfd, stab);
+               }
 
-         int loop = 1;
-         int had_write = 0;
-         int had_read = 0;
-         int had_exec= 0;
-         int had_shared= 0;
-         s += 5;
-         s = get_name(s, &name);
-         s = get_name(s, &attribs);
-         while (loop) {
-           switch (*attribs++) 
-             {
-             case 'W':
-               had_write = 1;
-               break;
-             case 'R':
-               had_read = 1;
-               break;
-             case 'S':
-               had_shared = 1;
-               break;
-             case 'X':
-               had_exec = 1;
-               break;
-             default:
-               loop = 0;
-             }
-         }
-         asec = bfd_get_section_by_name (abfd, name);
-         if (asec) {
-           if (had_exec)
-             asec->flags |= SEC_CODE;
-           if (!had_write)
-             asec->flags |= SEC_READONLY;
-         }
-       }
-      else if (strncmp (s,"-heap", 5) == 0)
-       {
-         s = dores_com (s+5, 
-                        &NT_stack_heap.heap_defined,
-                        &NT_stack_heap.heap_reserve,
-                        &NT_stack_heap.heap_commit);
-       }
-      else if (strncmp (s,"-stack", 6) == 0)
-       {
-         s = dores_com (s+6,
-                        &NT_stack_heap.heap_defined,
-                        &NT_stack_heap.heap_reserve,
-                        &NT_stack_heap.heap_commit);
+             table = coff_hash_table (info);
+
+             if (! _bfd_link_section_stabs (abfd, &table->stab_info,
+                                            stab, stabstr,
+                                            &secdata->stab_info))
+               goto error_return;
+           }
        }
-      else 
-       s++;
     }
-  free (copy);
-  return 1;
+
+  obj_coff_keep_syms (abfd) = keep_syms;
+
+  return true;
+
+ error_return:
+  obj_coff_keep_syms (abfd) = keep_syms;
+  return false;
 }
+\f
 /* Do the final link step.  */
 
 boolean
@@ -650,13 +650,15 @@ _bfd_coff_final_link (abfd, info)
 {
   bfd_size_type symesz;
   struct coff_final_link_info finfo;
+  boolean debug_merge_allocated;
+  boolean long_section_names;
   asection *o;
   struct bfd_link_order *p;
-  size_t max_contents_size;
-  size_t max_sym_count;
-  size_t max_lineno_count;
-  size_t max_reloc_count;
-  size_t max_output_reloc_count;
+  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;
@@ -664,6 +666,7 @@ _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);
 
@@ -672,6 +675,7 @@ _bfd_coff_final_link (abfd, info)
   finfo.strtab = NULL;
   finfo.section_info = NULL;
   finfo.last_file_index = -1;
+  finfo.last_bf_index = -1;
   finfo.internal_syms = NULL;
   finfo.sec_ptrs = NULL;
   finfo.sym_indices = NULL;
@@ -680,23 +684,25 @@ _bfd_coff_final_link (abfd, info)
   finfo.contents = NULL;
   finfo.external_relocs = NULL;
   finfo.internal_relocs = NULL;
+  finfo.global_to_static = false;
+  debug_merge_allocated = false;
 
-  if (obj_pe(abfd))
-    {
-      /* store the subsystem, stack and heap parameters in variables defined
-        in internal.h so that when they are needed to write the NT optional
-        file header (coffcode.h), they will be available */
-      NT_subsystem  = info->subsystem;
-      NT_stack_heap = info->stack_heap_parameters;
-    }
+  coff_data (abfd)->link_info = info;
 
   finfo.strtab = _bfd_stringtab_init ();
   if (finfo.strtab == NULL)
     goto error_return;
 
+  if (! coff_debug_merge_hash_table_init (&finfo.debug_merge))
+    goto error_return;
+  debug_merge_allocated = true;
+
   /* Compute the file positions for all the sections.  */
   if (! abfd->output_has_begun)
-    bfd_coff_compute_section_file_positions (abfd);
+    {
+      if (! bfd_coff_compute_section_file_positions (abfd))
+       goto error_return;
+    }
 
   /* Count the line numbers and relocation entries required for the
      output file.  Set the file positions for the relocs.  */
@@ -705,6 +711,8 @@ _bfd_coff_final_link (abfd, info)
   max_contents_size = 0;
   max_lineno_count = 0;
   max_reloc_count = 0;
+
+  long_section_names = false;
   for (o = abfd->sections; o != NULL; o = o->next)
     {
       o->reloc_count = 0;
@@ -717,6 +725,12 @@ _bfd_coff_final_link (abfd, info)
 
              sec = p->u.indirect.section;
 
+             /* Mark all sections which are to be included in the
+                link.  This will normally be every section.  We need
+                to do this so that we can identify any sections which
+                the linker has decided to not include.  */
+             sec->linker_mark = true;
+
              if (info->strip == strip_none
                  || info->strip == strip_some)
                o->lineno_count += sec->lineno_count;
@@ -743,6 +757,24 @@ _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 (finfo.strtab, o->name, false, false)
+             == (bfd_size_type) -1)
+           goto error_return;
+         long_section_names = true;
        }
     }
 
@@ -754,14 +786,11 @@ _bfd_coff_final_link (abfd, info)
 
       /* We use section_count + 1, rather than section_count, because
          the target_index fields are 1 based.  */
-      finfo.section_info = ((struct coff_link_section_info *)
-                           malloc ((abfd->section_count + 1)
-                                   * sizeof (struct coff_link_section_info)));
+      amt = abfd->section_count + 1;
+      amt *= sizeof (struct coff_link_section_info);
+      finfo.section_info = (struct coff_link_section_info *) bfd_malloc (amt);
       if (finfo.section_info == NULL)
-       {
-         bfd_set_error (bfd_error_no_memory);
-         goto error_return;
-       }
+       goto error_return;
       for (i = 0; i <= abfd->section_count; i++)
        {
          finfo.section_info[i].relocs = NULL;
@@ -800,19 +829,17 @@ _bfd_coff_final_link (abfd, info)
             but only when doing a relocateable link, which is not the
             common case.  */
          BFD_ASSERT (info->relocateable);
+         amt = o->reloc_count;
+         amt *= sizeof (struct internal_reloc);
          finfo.section_info[o->target_index].relocs =
-           ((struct internal_reloc *)
-            malloc (o->reloc_count * sizeof (struct internal_reloc)));
+           (struct internal_reloc *) bfd_malloc (amt);
+         amt = o->reloc_count;
+         amt *= sizeof (struct coff_link_hash_entry *);
          finfo.section_info[o->target_index].rel_hashes =
-           ((struct coff_link_hash_entry **)
-            malloc (o->reloc_count
-                    * sizeof (struct coff_link_hash_entry *)));
+           (struct coff_link_hash_entry **) bfd_malloc (amt);
          if (finfo.section_info[o->target_index].relocs == NULL
              || finfo.section_info[o->target_index].rel_hashes == NULL)
-           {
-             bfd_set_error (bfd_error_no_memory);
-             goto error_return;
-           }
+           goto error_return;
 
          if (o->reloc_count > max_output_reloc_count)
            max_output_reloc_count = o->reloc_count;
@@ -841,21 +868,23 @@ _bfd_coff_final_link (abfd, info)
     }
 
   /* Allocate some buffers used while linking.  */
-  finfo.internal_syms = ((struct internal_syment *)
-                        malloc (max_sym_count
-                                * sizeof (struct internal_syment)));
-  finfo.sec_ptrs = (asection **) malloc (max_sym_count * sizeof (asection *));
-  finfo.sym_indices = (long *) malloc (max_sym_count * sizeof (long));
-  finfo.outsyms = ((bfd_byte *)
-                  malloc ((size_t) ((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);
+  amt = max_sym_count * sizeof (struct internal_syment);
+  finfo.internal_syms = (struct internal_syment *) bfd_malloc (amt);
+  amt = max_sym_count * sizeof (asection *);
+  finfo.sec_ptrs = (asection **) bfd_malloc (amt);
+  amt = max_sym_count * sizeof (long);
+  finfo.sym_indices = (long *) bfd_malloc (amt);
+  finfo.outsyms = (bfd_byte *) bfd_malloc ((max_sym_count + 1) * symesz);
+  amt = max_lineno_count * bfd_coff_linesz (abfd);
+  finfo.linenos = (bfd_byte *) bfd_malloc (amt);
+  finfo.contents = (bfd_byte *) bfd_malloc (max_contents_size);
+  amt = max_reloc_count * relsz;
+  finfo.external_relocs = (bfd_byte *) bfd_malloc (amt);
   if (! info->relocateable)
-    finfo.internal_relocs = ((struct internal_reloc *)
-                            malloc (max_reloc_count
-                                    * sizeof (struct internal_reloc)));
+    {
+      amt = max_reloc_count * sizeof (struct internal_reloc);
+      finfo.internal_relocs = (struct internal_reloc *) bfd_malloc (amt);
+    }
   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)
@@ -866,10 +895,7 @@ _bfd_coff_final_link (abfd, info)
       || (! info->relocateable
          && finfo.internal_relocs == NULL
          && max_reloc_count > 0))
-    {
-      bfd_set_error (bfd_error_no_memory);
-      goto error_return;
-    }
+    goto error_return;
 
   /* We now know the position of everything in the file, except that
      we don't know the size of the symbol table and therefore we don't
@@ -877,18 +903,24 @@ _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)
        {
          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, & finfo))
                {
-                 if (! coff_link_input_bfd (&finfo, sub))
+                 if (! _bfd_coff_link_input_bfd (&finfo, sub))
                    goto error_return;
                  sub->output_has_begun = true;
                }
@@ -896,7 +928,7 @@ _bfd_coff_final_link (abfd, info)
          else if (p->type == bfd_section_reloc_link_order
                   || p->type == bfd_symbol_reloc_link_order)
            {
-             if (! coff_reloc_link_order (abfd, &finfo, o, p))
+             if (! _bfd_coff_reloc_link_order (abfd, &finfo, o, p))
                goto error_return;
            }
          else
@@ -907,7 +939,14 @@ _bfd_coff_final_link (abfd, info)
        }
     }
 
-  /* Free up the buffers used by coff_link_input_bfd.  */
+  if (! bfd_coff_final_link_postscript (abfd, & finfo))
+    goto error_return;
+
+  /* Free up the buffers used by _bfd_coff_link_input_bfd.  */
+
+  coff_debug_merge_hash_table_free (&finfo.debug_merge);
+  debug_merge_allocated = false;
+
   if (finfo.internal_syms != NULL)
     {
       free (finfo.internal_syms);
@@ -948,44 +987,57 @@ _bfd_coff_final_link (abfd, info)
      index of the first external symbol.  Write it out again if
      necessary.  */
   if (finfo.last_file_index != -1
-      && finfo.last_file.n_value != obj_raw_syment_count (abfd))
+      && (unsigned int) finfo.last_file.n_value != obj_raw_syment_count (abfd))
     {
+      file_ptr pos;
+
       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)
+
+      pos = obj_sym_filepos (abfd) + finfo.last_file_index * symesz;
+      if (bfd_seek (abfd, pos, SEEK_SET) != 0
+         || bfd_bwrite (finfo.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.failed = false;
+      coff_link_hash_traverse (coff_hash_table (info),
+                              _bfd_coff_write_task_globals,
+                              (PTR) &finfo);
+      if (finfo.failed)
+       goto error_return;
+    }
+
   /* Write out the global symbols.  */
   finfo.failed = false;
-  coff_link_hash_traverse (coff_hash_table (info), coff_write_global_sym,
+  coff_link_hash_traverse (coff_hash_table (info),
+                          _bfd_coff_write_global_sym,
                           (PTR) &finfo);
   if (finfo.failed)
     goto error_return;
 
-  /* The outsyms buffer is used by coff_write_global_sym.  */
+  /* The outsyms buffer is used by _bfd_coff_write_global_sym.  */
   if (finfo.outsyms != NULL)
     {
       free (finfo.outsyms);
       finfo.outsyms = NULL;
     }
 
-  if (info->relocateable)
+  if (info->relocateable && max_output_reloc_count > 0)
     {
       /* Now that we have written out all the global symbols, we know
         the symbol indices to use for relocs against them, and we can
         finally write out the relocs.  */
-      external_relocs = (bfd_byte *) malloc (max_output_reloc_count * relsz);
+      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)
        {
@@ -1012,8 +1064,9 @@ _bfd_coff_final_link (abfd, info)
            }
 
          if (bfd_seek (abfd, o->rel_filepos, SEEK_SET) != 0
-             || bfd_write ((PTR) external_relocs, relsz, o->reloc_count,
-                           abfd) != relsz * o->reloc_count)
+             || (bfd_bwrite ((PTR) external_relocs,
+                            (bfd_size_type) relsz * o->reloc_count, abfd)
+                 != (bfd_size_type) relsz * o->reloc_count))
            goto error_return;
        }
 
@@ -1037,26 +1090,39 @@ _bfd_coff_final_link (abfd, info)
       finfo.section_info = NULL;
     }
 
+  /* If we have optimized stabs strings, output them.  */
+  if (coff_hash_table (info)->stab_info != NULL)
+    {
+      if (! _bfd_write_stab_strings (abfd, &coff_hash_table (info)->stab_info))
+       return false;
+    }
+
   /* Write out the string table.  */
-  if (bfd_seek (abfd,
-               (obj_sym_filepos (abfd)
-                + obj_raw_syment_count (abfd) * symesz),
-               SEEK_SET) != 0)
-    return false;
+  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
-  bfd_h_put_32 (abfd,
+      H_PUT_32 (abfd,
                _bfd_stringtab_size (finfo.strtab) + STRING_SIZE_SIZE,
-               (bfd_byte *) strbuf);
+               strbuf);
 #else
- #error Change bfd_h_put_32
+ #error Change H_PUT_32 above
 #endif
 
-  if (bfd_write (strbuf, 1, STRING_SIZE_SIZE, abfd) != STRING_SIZE_SIZE)
-    return false;
+      if (bfd_bwrite (strbuf, (bfd_size_type) STRING_SIZE_SIZE, abfd)
+         != STRING_SIZE_SIZE)
+       return false;
 
-  if (! _bfd_stringtab_emit (abfd, finfo.strtab))
-    return false;
+      if (! _bfd_stringtab_emit (abfd, finfo.strtab))
+       return false;
+
+      obj_coff_strings_written (abfd) = true;
+    }
 
   _bfd_stringtab_free (finfo.strtab);
 
@@ -1067,6 +1133,8 @@ _bfd_coff_final_link (abfd, info)
   return true;
 
  error_return:
+  if (debug_merge_allocated)
+    coff_debug_merge_hash_table_free (&finfo.debug_merge);
   if (finfo.strtab != NULL)
     _bfd_stringtab_free (finfo.strtab);
   if (finfo.section_info != NULL)
@@ -1075,157 +1143,236 @@ _bfd_coff_final_link (abfd, info)
 
       for (i = 0; i < abfd->section_count; i++)
        {
-         if (finfo.section_info[i].relocs != NULL)
-           free (finfo.section_info[i].relocs);
-         if (finfo.section_info[i].rel_hashes != NULL)
-           free (finfo.section_info[i].rel_hashes);
+         if (finfo.section_info[i].relocs != NULL)
+           free (finfo.section_info[i].relocs);
+         if (finfo.section_info[i].rel_hashes != NULL)
+           free (finfo.section_info[i].rel_hashes);
+       }
+      free (finfo.section_info);
+    }
+  if (finfo.internal_syms != NULL)
+    free (finfo.internal_syms);
+  if (finfo.sec_ptrs != NULL)
+    free (finfo.sec_ptrs);
+  if (finfo.sym_indices != NULL)
+    free (finfo.sym_indices);
+  if (finfo.outsyms != NULL)
+    free (finfo.outsyms);
+  if (finfo.linenos != NULL)
+    free (finfo.linenos);
+  if (finfo.contents != NULL)
+    free (finfo.contents);
+  if (finfo.external_relocs != NULL)
+    free (finfo.external_relocs);
+  if (finfo.internal_relocs != NULL)
+    free (finfo.internal_relocs);
+  if (external_relocs != NULL)
+    free (external_relocs);
+  return false;
+}
+
+/* parse out a -heap <reserved>,<commit> line */
+
+static char *
+dores_com (ptr, output_bfd, heap)
+     char *ptr;
+     bfd *output_bfd;
+     int heap;
+{
+  if (coff_data(output_bfd)->pe)
+    {
+      int val = strtoul (ptr, &ptr, 0);
+      if (heap)
+       pe_data(output_bfd)->pe_opthdr.SizeOfHeapReserve = val;
+      else
+       pe_data(output_bfd)->pe_opthdr.SizeOfStackReserve = val;
+
+      if (ptr[0] == ',')
+       {
+         val = strtoul (ptr+1, &ptr, 0);
+         if (heap)
+           pe_data(output_bfd)->pe_opthdr.SizeOfHeapCommit = val;
+         else
+           pe_data(output_bfd)->pe_opthdr.SizeOfStackCommit = val;
+       }
+    }
+  return ptr;
+}
+
+static char *get_name(ptr, dst)
+char *ptr;
+char **dst;
+{
+  while (*ptr == ' ')
+    ptr++;
+  *dst = ptr;
+  while (*ptr && *ptr != ' ')
+    ptr++;
+  *ptr = 0;
+  return ptr+1;
+}
+
+/* Process any magic embedded commands in a section called .drectve */
+
+static int
+process_embedded_commands (output_bfd, info,  abfd)
+     bfd *output_bfd;
+     struct bfd_link_info *info ATTRIBUTE_UNUSED;
+     bfd *abfd;
+{
+  asection *sec = bfd_get_section_by_name (abfd, ".drectve");
+  char *s;
+  char *e;
+  char *copy;
+  if (!sec)
+    return 1;
+
+  copy = bfd_malloc (sec->_raw_size);
+  if (!copy)
+    return 0;
+  if (! bfd_get_section_contents(abfd, sec, copy, (bfd_vma) 0, sec->_raw_size))
+    {
+      free (copy);
+      return 0;
+    }
+  e = copy + sec->_raw_size;
+  for (s = copy;  s < e ; )
+    {
+      if (s[0]!= '-') {
+       s++;
+       continue;
+      }
+      if (strncmp (s,"-attr", 5) == 0)
+       {
+         char *name;
+         char *attribs;
+         asection *asec;
+
+         int loop = 1;
+         int had_write = 0;
+         int had_read = 0;
+         int had_exec= 0;
+         int had_shared= 0;
+         s += 5;
+         s = get_name(s, &name);
+         s = get_name(s, &attribs);
+         while (loop) {
+           switch (*attribs++)
+             {
+             case 'W':
+               had_write = 1;
+               break;
+             case 'R':
+               had_read = 1;
+               break;
+             case 'S':
+               had_shared = 1;
+               break;
+             case 'X':
+               had_exec = 1;
+               break;
+             default:
+               loop = 0;
+             }
+         }
+         asec = bfd_get_section_by_name (abfd, name);
+         if (asec) {
+           if (had_exec)
+             asec->flags |= SEC_CODE;
+           if (!had_write)
+             asec->flags |= SEC_READONLY;
+         }
+       }
+      else if (strncmp (s,"-heap", 5) == 0)
+       {
+         s = dores_com (s+5, output_bfd, 1);
        }
-      free (finfo.section_info);
+      else if (strncmp (s,"-stack", 6) == 0)
+       {
+         s = dores_com (s+6, output_bfd, 0);
+       }
+      else
+       s++;
     }
-  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;
+  free (copy);
+  return 1;
 }
 
-/* Read in and swap the relocs.  This returns a buffer holding the
-   relocs for section SEC in file ABFD.  If CACHE is true and
-   INTERNAL_RELOCS is NULL, the relocs read in wil be saved in case
-   the function is called again.  If EXTERNAL_RELOCS is not NULL, it
-   is a buffer large enough to hold the unswapped relocs.  If
-   INTERNAL_RELOCS is not NULL, it is a buffer large enough to hold
-   the swapped relocs.  If REQUIRE_INTERNAL is true, then the return
-   value must be INTERNAL_RELOCS.  The function returns NULL on error.  */
-
-struct internal_reloc *
-_bfd_coff_read_internal_relocs (abfd, sec, cache, external_relocs,
-                               require_internal, internal_relocs)
-     bfd *abfd;
-     asection *sec;
-     boolean cache;
-     bfd_byte *external_relocs;
-     boolean require_internal;
-     struct internal_reloc *internal_relocs;
-{
-  bfd_size_type relsz;
-  bfd_byte *free_external = NULL;
-  struct internal_reloc *free_internal = NULL;
-  bfd_byte *erel;
-  bfd_byte *erel_end;
-  struct internal_reloc *irel;
+/* 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 (coff_section_data (abfd, sec) != NULL
-      && coff_section_data (abfd, sec)->relocs != NULL)
-    {
-      if (! require_internal)
-       return coff_section_data (abfd, sec)->relocs;
-      memcpy (internal_relocs, coff_section_data (abfd, sec)->relocs,
-             sec->reloc_count * sizeof (struct internal_reloc));
-      return internal_relocs;
-    }
+static void
+mark_relocs (finfo, input_bfd)
+     struct coff_final_link_info *     finfo;
+     bfd *                             input_bfd;
+{
+  asection * a;
 
-  relsz = bfd_coff_relsz (abfd);
+  if ((bfd_get_file_flags (input_bfd) & HAS_SYMS) == 0)
+    return;
 
-  if (external_relocs == NULL)
+  for (a = input_bfd->sections; a != (asection *) NULL; a = a->next)
     {
-      free_external = (bfd_byte *) malloc (sec->reloc_count * relsz);
-      if (free_external == NULL && sec->reloc_count > 0)
-       {
-         bfd_set_error (bfd_error_no_memory);
-         goto error_return;
-       }
-      external_relocs = free_external;
-    }
+      struct internal_reloc *  internal_relocs;
+      struct internal_reloc *  irel;
+      struct internal_reloc *  irelend;
 
-  if (bfd_seek (abfd, sec->rel_filepos, SEEK_SET) != 0
-      || (bfd_read (external_relocs, relsz, sec->reloc_count, abfd)
-         != relsz * sec->reloc_count))
-    goto error_return;
+      if ((a->flags & SEC_RELOC) == 0 || a->reloc_count  < 1)
+       continue;
+      /* Don't mark relocs in excluded sections.  */
+      if (a->output_section == bfd_abs_section_ptr)
+       continue;
 
-  if (internal_relocs == NULL)
-    {
-      free_internal = ((struct internal_reloc *)
-                      malloc (sec->reloc_count
-                              * sizeof (struct internal_reloc)));
-      if (free_internal == NULL && sec->reloc_count > 0)
-       {
-         bfd_set_error (bfd_error_no_memory);
-         goto error_return;
-       }
-      internal_relocs = free_internal;
-    }
+      /* Read in the relocs.  */
+      internal_relocs = _bfd_coff_read_internal_relocs
+       (input_bfd, a, false,
+        finfo->external_relocs,
+        finfo->info->relocateable,
+        (finfo->info->relocateable
+         ? (finfo->section_info[ a->output_section->target_index ].relocs + a->output_section->reloc_count)
+         : finfo->internal_relocs)
+       );
+
+      if (internal_relocs == NULL)
+       continue;
 
-  /* Swap in the relocs.  */
-  erel = external_relocs;
-  erel_end = erel + relsz * sec->reloc_count;
-  irel = internal_relocs;
-  for (; erel < erel_end; erel += relsz, irel++)
-    bfd_coff_swap_reloc_in (abfd, (PTR) erel, (PTR) irel);
+      irel     = internal_relocs;
+      irelend  = irel + a->reloc_count;
 
-  if (free_external != NULL)
-    {
-      free (free_external);
-      free_external = NULL;
-    }
+      /* Place a mark in the sym_indices array (whose entries have
+        been initialised to 0) for all of the symbols that are used
+        in the relocation table.  This will then be picked up in the
+        skip/don't pass */
 
-  if (cache && free_internal != NULL)
-    {
-      if (coff_section_data (abfd, sec) == NULL)
+      for (; irel < irelend; irel++)
        {
-         sec->used_by_bfd = ((PTR) bfd_zalloc (abfd,
-                                               sizeof (struct coff_section_tdata)));
-         if (sec->used_by_bfd == NULL)
-           {
-             bfd_set_error (bfd_error_no_memory);
-             goto error_return;
-           }
-         coff_section_data (abfd, sec)->contents = NULL;
+         finfo->sym_indices[ irel->r_symndx ] = -1;
        }
-      coff_section_data (abfd, sec)->relocs = free_internal;
     }
-
-  return internal_relocs;
-
- error_return:
-  if (free_external != NULL)
-    free (free_external);
-  if (free_internal != NULL)
-    free (free_internal);
-  return NULL;
 }
 
 /* Link an input file into the linker output file.  This function
    handles all the sections and relocations of the input file at once.  */
 
-static boolean
-coff_link_input_bfd (finfo, input_bfd)
+boolean
+_bfd_coff_link_input_bfd (finfo, input_bfd)
      struct coff_final_link_info *finfo;
      bfd *input_bfd;
 {
-  boolean (*sym_is_global) PARAMS ((bfd *, struct internal_syment *));
+  unsigned int n_tmask = coff_data (input_bfd)->local_n_tmask;
+  unsigned int n_btshft = coff_data (input_bfd)->local_n_btshft;
+#if 0
+  unsigned int n_btmask = coff_data (input_bfd)->local_n_btmask;
+#endif
   boolean (*adjust_symndx) PARAMS ((bfd *, struct bfd_link_info *, bfd *,
                                    asection *, struct internal_reloc *,
                                    boolean *));
   bfd *output_bfd;
   const char *strings;
   bfd_size_type syment_base;
-  unsigned int n_tmask;
-  unsigned int n_btshft;
   boolean copy, hash;
   bfd_size_type isymesz;
   bfd_size_type osymesz;
@@ -1235,7 +1382,7 @@ 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;
   asection *o;
@@ -1243,7 +1390,6 @@ coff_link_input_bfd (finfo, input_bfd)
   /* 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;
   strings = NULL;
   syment_base = obj_raw_syment_count (output_bfd);
   isymesz = bfd_coff_symesz (input_bfd);
@@ -1251,13 +1397,6 @@ 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;
@@ -1276,17 +1415,32 @@ coff_link_input_bfd (finfo, input_bfd)
   output_index = syment_base;
   outsym = finfo->outsyms;
 
-  if (obj_pe (output_bfd))
-      {
-       if (!process_embedded_commands (input_bfd))
-         return false;
-      }
+  if (coff_data (output_bfd)->pe)
+    {
+      if (! process_embedded_commands (output_bfd, finfo->info, input_bfd))
+       return false;
+    }
+
+  /* If we are going to perform relocations and also strip/discard some symbols
+     then we must make sure that we do not strip/discard those symbols that are
+     going to be involved in the relocations */
+  if ((   finfo->info->strip   != strip_none
+       || finfo->info->discard != discard_none)
+      && finfo->info->relocateable)
+    {
+      /* mark the symbol array as 'not-used' */
+      memset (indexp, 0, obj_raw_syment_count (input_bfd) * sizeof * indexp);
+
+      mark_relocs (finfo, input_bfd);
+    }
 
   while (esym < esym_end)
     {
       struct internal_syment isym;
+      enum coff_symbol_classification classification;
       boolean skip;
       boolean global;
+      boolean dont_skip_symbol;
       int add;
 
       bfd_coff_swap_sym_in (input_bfd, (PTR) esym, (PTR) isymp);
@@ -1297,16 +1451,33 @@ 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 ((finfo->info->strip != strip_none
+          || finfo->info->discard != discard_none)
+         && finfo->info->relocateable)
+       dont_skip_symbol = *indexp;
+      else
+       dont_skip_symbol = false;
+
       *indexp = -1;
 
       skip = false;
@@ -1314,36 +1485,73 @@ coff_link_input_bfd (finfo, input_bfd)
       add = 1 + isym.n_numaux;
 
       /* If we are stripping all symbols, we want to skip this one.  */
-      if (finfo->info->strip == strip_all)
+      if (finfo->info->strip == strip_all && ! dont_skip_symbol)
        skip = true;
 
       if (! skip)
        {
-         if (isym.n_sclass == C_EXT
-             || (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.  */
+                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
-           {
+             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)
+             if (finfo->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
+         && isym.n_sclass == C_STAT
+         && isym.n_type == T_NULL
+          && isym.n_numaux > 0)
+        {
+          if ((*secpp)->output_section == bfd_abs_section_ptr)
+            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)
+         && ! 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
@@ -1359,16 +1567,184 @@ coff_link_input_bfd (finfo, input_bfd)
          if (name == NULL)
            return false;
 
-         if ((finfo->info->strip == strip_some
-              && (bfd_hash_lookup (finfo->info->keep_hash, name, false,
+         if (! dont_skip_symbol
+             && ((finfo->info->strip == strip_some
+                  && (bfd_hash_lookup (finfo->info->keep_hash, name, false,
                                    false) == NULL))
-             || (! global
-                 && finfo->info->discard == discard_l
-                 && strncmp (name, finfo->info->lprefix,
-                             finfo->info->lprefix_len) == 0))
+                  || (! global
+                      && finfo->info->discard == discard_l
+                      && bfd_is_local_label_name (input_bfd, name))))
            skip = true;
        }
 
+      /* If this is an enum, struct, or union tag, see if we have
+         already output an identical type.  */
+      if (! skip
+         && (finfo->output_bfd->flags & BFD_TRADITIONAL_FORMAT) == 0
+         && (isym.n_sclass == C_ENTAG
+             || isym.n_sclass == C_STRTAG
+             || isym.n_sclass == C_UNTAG)
+         && isym.n_numaux == 1)
+       {
+         const char *name;
+         char buf[SYMNMLEN + 1];
+         struct coff_debug_merge_hash_entry *mh;
+         struct coff_debug_merge_type *mt;
+         union internal_auxent aux;
+         struct coff_debug_merge_element **epp;
+         bfd_byte *esl, *eslend;
+         struct internal_syment *islp;
+         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 (&finfo->debug_merge, name,
+                                            true, true);
+         if (mh == NULL)
+           return false;
+
+         /* Allocate memory to hold type information.  If this turns
+             out to be a duplicate, we pass this address to
+             bfd_release.  */
+         amt = sizeof (struct coff_debug_merge_type);
+         mt = (struct coff_debug_merge_type *) bfd_alloc (input_bfd, amt);
+         if (mt == NULL)
+           return false;
+         mt->class = isym.n_sclass;
+
+         /* Pick up the aux entry, which points to the end of the tag
+             entries.  */
+         bfd_coff_swap_aux_in (input_bfd, (PTR) (esym + isymesz),
+                               isym.n_type, isym.n_sclass, 0, isym.n_numaux,
+                               (PTR) &aux);
+
+         /* Gather the elements.  */
+         epp = &mt->elements;
+         mt->elements = NULL;
+         islp = isymp + 2;
+         esl = esym + 2 * isymesz;
+         eslend = ((bfd_byte *) obj_coff_external_syms (input_bfd)
+                   + aux.x_sym.x_fcnary.x_fcn.x_endndx.l * isymesz);
+         while (esl < eslend)
+           {
+             const char *elename;
+             char elebuf[SYMNMLEN + 1];
+             char *name_copy;
+
+             bfd_coff_swap_sym_in (input_bfd, (PTR) esl, (PTR) 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)
+               {
+                 union internal_auxent eleaux;
+                 long indx;
+
+                 bfd_coff_swap_aux_in (input_bfd, (PTR) (esl + isymesz),
+                                       islp->n_type, islp->n_sclass, 0,
+                                       islp->n_numaux, (PTR) &eleaux);
+                 indx = eleaux.x_sym.x_tagndx.l;
+
+                 /* FIXME: If this tagndx entry refers to a symbol
+                    defined later in this file, we just ignore it.
+                    Handling this correctly would be tedious, and may
+                    not be required.  */
+
+                 if (indx > 0
+                     && (indx
+                         < ((esym -
+                             (bfd_byte *) obj_coff_external_syms (input_bfd))
+                            / (long) isymesz)))
+                   {
+                     (*epp)->tagndx = finfo->sym_indices[indx];
+                     if ((*epp)->tagndx < 0)
+                       (*epp)->tagndx = 0;
+                   }
+               }
+             epp = &(*epp)->next;
+             *epp = NULL;
+
+             esl += (islp->n_numaux + 1) * isymesz;
+             islp += islp->n_numaux + 1;
+           }
+
+         /* See if we already have a definition which matches this
+             type.  We always output the type if it has no elements,
+             for simplicity.  */
+         if (mt->elements == NULL)
+           bfd_release (input_bfd, (PTR) mt);
+         else
+           {
+             struct coff_debug_merge_type *mtl;
+
+             for (mtl = mh->types; mtl != NULL; mtl = mtl->next)
+               {
+                 struct coff_debug_merge_element *me, *mel;
+
+                 if (mtl->class != mt->class)
+                   continue;
+
+                 for (me = mt->elements, mel = mtl->elements;
+                      me != NULL && mel != NULL;
+                      me = me->next, mel = mel->next)
+                   {
+                     if (strcmp (me->name, mel->name) != 0
+                         || me->type != mel->type
+                         || me->tagndx != mel->tagndx)
+                       break;
+                   }
+
+                 if (me == NULL && mel == NULL)
+                   break;
+               }
+
+             if (mtl == NULL || (bfd_size_type) mtl->indx >= syment_base)
+               {
+                 /* This is the first definition of this type.  */
+                 mt->indx = output_index;
+                 mt->next = mh->types;
+                 mh->types = mt;
+               }
+             else
+               {
+                 /* This is a redefinition which can be merged.  */
+                 bfd_release (input_bfd, (PTR) mt);
+                 *indexp = mtl->indx;
+                 add = (eslend - esym) / isymesz;
+                 skip = true;
+               }
+           }
+       }
+
       /* We now know whether we are to skip this symbol or not.  */
       if (! skip)
        {
@@ -1395,31 +1771,78 @@ coff_link_input_bfd (finfo, input_bfd)
              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);
-           }
+           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)
+                 && strcmp (isym.n_name, ".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 (finfo->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.  */
 
-         /* 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)
+                 && finfo->last_file.n_value != (bfd_vma) output_index)
                {
-                 /* We must correct the value of the last C_FILE entry.  */
+                 /* We must correct the value of the last C_FILE
+                     entry.  */
                  finfo->last_file.n_value = output_index;
-                 if (finfo->last_file_index >= syment_base)
+                 if ((bfd_size_type) finfo->last_file_index >= syment_base)
                    {
                      /* The last C_FILE symbol is in this input file.  */
                      bfd_coff_swap_sym_out (output_bfd,
@@ -1431,26 +1854,32 @@ coff_link_input_bfd (finfo, input_bfd)
                    }
                  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))
+                     pos = obj_sym_filepos (output_bfd);
+                     pos += finfo->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;
+             break;
            }
 
+         /* If doing task linking, convert normal global function symbols to
+            static functions.  */
+         if (finfo->info->task_link && IS_EXTERNAL (input_bfd, isym))
+           isym.n_sclass = C_STAT;
+
          /* Output the symbol.  */
 
          bfd_coff_swap_sym_out (output_bfd, (PTR) &isym, (PTR) outsym);
@@ -1465,7 +1894,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;
            }
 
@@ -1500,7 +1935,8 @@ coff_link_input_bfd (finfo, input_bfd)
 
       add = 1 + isymp->n_numaux;
 
-      if (*indexp < 0
+      if ((*indexp < 0
+          || (bfd_size_type) *indexp < syment_base)
          && (*sym_hash == NULL
              || (*sym_hash)->auxbfd != input_bfd))
        esym += add * isymesz;
@@ -1513,7 +1949,12 @@ coff_link_input_bfd (finfo, input_bfd)
          if (*indexp < 0)
            {
              h = *sym_hash;
-             BFD_ASSERT (h->numaux == isymp->n_numaux);
+
+             /* The m68k-motorola-sysv assembler will sometimes
+                 generate two symbols with the same name, but only one
+                 will have aux entries.  */
+             BFD_ASSERT (isymp->n_numaux == 0
+                         || h->numaux == isymp->n_numaux);
            }
 
          esym += isymesz;
@@ -1566,11 +2007,12 @@ coff_link_input_bfd (finfo, input_bfd)
                }
              else if (isymp->n_sclass != C_STAT || isymp->n_type != T_NULL)
                {
-                 long indx;
+                 unsigned long indx;
 
                  if (ISFCN (isymp->n_type)
                      || ISTAG (isymp->n_sclass)
-                     || isymp->n_sclass == C_BLOCK)
+                     || isymp->n_sclass == C_BLOCK
+                     || isymp->n_sclass == C_FCN)
                    {
                      indx = auxp->x_sym.x_fcnary.x_fcn.x_endndx.l;
                      if (indx > 0
@@ -1580,7 +2022,9 @@ coff_link_input_bfd (finfo, input_bfd)
                              the index of the next symbol we are going
                              to include.  I don't know if this is
                              entirely right.  */
-                         while (finfo->sym_indices[indx] < 0
+                         while ((finfo->sym_indices[indx] < 0
+                                 || ((bfd_size_type) finfo->sym_indices[indx]
+                                     < syment_base))
                                 && indx < obj_raw_syment_count (input_bfd))
                            ++indx;
                          if (indx >= obj_raw_syment_count (input_bfd))
@@ -1594,11 +2038,89 @@ coff_link_input_bfd (finfo, input_bfd)
                  indx = auxp->x_sym.x_tagndx.l;
                  if (indx > 0 && indx < obj_raw_syment_count (input_bfd))
                    {
-                     indx = finfo->sym_indices[indx];
-                     if (indx < 0)
+                     long symindx;
+
+                     symindx = finfo->sym_indices[indx];
+                     if (symindx < 0)
                        auxp->x_sym.x_tagndx.l = 0;
                      else
-                       auxp->x_sym.x_tagndx.l = indx;
+                       auxp->x_sym.x_tagndx.l = symindx;
+                   }
+
+                 /* The .bf symbols are supposed to be linked through
+                    the endndx field.  We need to carry this list
+                    across object files.  */
+                 if (i == 0
+                     && h == NULL
+                     && isymp->n_sclass == C_FCN
+                     && (isymp->_n._n_n._n_zeroes != 0
+                         || isymp->_n._n_n._n_offset == 0)
+                     && isymp->_n._n_name[0] == '.'
+                     && isymp->_n._n_name[1] == 'b'
+                     && isymp->_n._n_name[2] == 'f'
+                     && isymp->_n._n_name[3] == '\0')
+                   {
+                     if (finfo->last_bf_index != -1)
+                       {
+                         finfo->last_bf.x_sym.x_fcnary.x_fcn.x_endndx.l =
+                           *indexp;
+
+                         if ((bfd_size_type) finfo->last_bf_index
+                             >= syment_base)
+                           {
+                             PTR auxout;
+
+                             /* The last .bf symbol is in this input
+                                file.  This will only happen if the
+                                assembler did not set up the .bf
+                                endndx symbols correctly.  */
+                             auxout = (PTR) (finfo->outsyms
+                                             + ((finfo->last_bf_index
+                                                 - syment_base)
+                                                * osymesz));
+                             bfd_coff_swap_aux_out (output_bfd,
+                                                    (PTR) &finfo->last_bf,
+                                                    isymp->n_type,
+                                                    isymp->n_sclass,
+                                                    0, isymp->n_numaux,
+                                                    auxout);
+                           }
+                         else
+                           {
+                             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,
+                                                    (PTR) &finfo->last_bf,
+                                                    isymp->n_type,
+                                                    isymp->n_sclass,
+                                                    0, isymp->n_numaux,
+                                                    (PTR) outsym);
+                             pos = obj_sym_filepos (output_bfd);
+                             pos += finfo->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)
+                       finfo->last_bf_index = -1;
+                     else
+                       {
+                         /* The endndx field of this aux entry must
+                             be updated with the symbol number of the
+                             next .bf symbol.  */
+                         finfo->last_bf = *auxp;
+                         finfo->last_bf_index = (((outsym - finfo->outsyms)
+                                                  / osymesz)
+                                                 + syment_base);
+                       }
                    }
                }
 
@@ -1628,18 +2150,34 @@ coff_link_input_bfd (finfo, input_bfd)
          bfd_vma offset;
          bfd_byte *eline;
          bfd_byte *elineend;
-
-         if (o->lineno_count == 0)
+         bfd_byte *oeline;
+         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 (finfo->linenos, linesz * o->lineno_count,
                           input_bfd) != linesz * o->lineno_count)
            return false;
 
          offset = o->output_section->vma + o->output_offset - o->vma;
          eline = finfo->linenos;
+         oeline = finfo->linenos;
          elineend = eline + linesz * o->lineno_count;
+         skipping = false;
          for (; eline < elineend; eline += linesz)
            {
              struct internal_lineno iline;
@@ -1649,7 +2187,7 @@ coff_link_input_bfd (finfo, input_bfd)
              if (iline.l_lnno != 0)
                iline.l_addr.l_paddr += offset;
              else if (iline.l_addr.l_symndx >= 0
-                      && (iline.l_addr.l_symndx
+                      && ((unsigned long) iline.l_addr.l_symndx
                           < obj_raw_syment_count (input_bfd)))
                {
                  long indx;
@@ -1659,11 +2197,13 @@ coff_link_input_bfd (finfo, input_bfd)
                  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
                    {
@@ -1702,23 +2242,29 @@ coff_link_input_bfd (finfo, input_bfd)
                                                 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, (PTR) &iline,
+                                           (PTR) 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)
+         pos = o->output_section->line_filepos;
+         pos += o->output_section->lineno_count * linesz;
+         amt = oeline - finfo->linenos;
+         if (bfd_seek (output_bfd, pos, SEEK_SET) != 0
+             || bfd_bwrite (finfo->linenos, amt, output_bfd) != amt)
            return false;
 
-         o->output_section->lineno_count += o->lineno_count;
+         o->output_section->lineno_count += amt / linesz;
        }
     }
 
@@ -1727,7 +2273,7 @@ coff_link_input_bfd (finfo, input_bfd)
      normal case, this will save us from writing out the C_FILE symbol
      again.  */
   if (finfo->last_file_index != -1
-      && finfo->last_file_index >= syment_base)
+      && (bfd_size_type) finfo->last_file_index >= syment_base)
     {
       finfo->last_file.n_value = output_index;
       bfd_coff_swap_sym_out (output_bfd, (PTR) &finfo->last_file,
@@ -1739,11 +2285,13 @@ coff_link_input_bfd (finfo, input_bfd)
   /* Write the modified symbols to the output file.  */
   if (outsym > finfo->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)
+      file_ptr pos;
+      bfd_size_type amt;
+
+      pos = obj_sym_filepos (output_bfd) + syment_base * osymesz;
+      amt = outsym - finfo->outsyms;
+      if (bfd_seek (output_bfd, pos, SEEK_SET) != 0
+         || bfd_bwrite (finfo->outsyms, amt, output_bfd) != amt)
        return false;
 
       BFD_ASSERT ((obj_raw_syment_count (output_bfd)
@@ -1758,13 +2306,34 @@ coff_link_input_bfd (finfo, input_bfd)
   for (o = input_bfd->sections; o != NULL; o = o->next)
     {
       bfd_byte *contents;
+      struct coff_section_tdata *secdata;
 
-      if ((o->flags & SEC_HAS_CONTENTS) == 0)
-       continue;
+      if (! o->linker_mark)
+       {
+         /* This section was omitted from the link.  */
+         continue;
+       }
+
+      if ((o->flags & SEC_HAS_CONTENTS) == 0
+         || (o->_raw_size == 0 && (o->flags & SEC_RELOC) == 0))
+       {
+         if ((o->flags & SEC_RELOC) != 0
+             && o->reloc_count != 0)
+           {
+             ((*_bfd_error_handler)
+              (_("%s: relocs in section `%s', but it has no contents"),
+               bfd_archive_filename (input_bfd),
+               bfd_get_section_name (input_bfd, o)));
+             bfd_set_error (bfd_error_no_contents);
+             return false;
+           }
 
-      if (coff_section_data (input_bfd, o) != NULL
-         && coff_section_data (input_bfd, o)->contents != NULL)
-       contents = coff_section_data (input_bfd, o)->contents;
+         continue;
+       }
+
+      secdata = coff_section_data (input_bfd, o);
+      if (secdata != NULL && secdata->contents != NULL)
+       contents = secdata->contents;
       else
        {
          if (! bfd_get_section_contents (input_bfd, o, finfo->contents,
@@ -1868,9 +2437,10 @@ coff_link_input_bfd (finfo, input_bfd)
                          char buf[SYMNMLEN + 1];
 
                          /* This reloc is against a symbol we are
-                             stripping.  It would be possible to
-                             handle this case, but I don't think it's
-                             worth it.  */
+                             stripping.  This should have been handled
+                            by the 'dont_skip_symbol' code in the while
+                            loop at the top of this function.  */
+
                          is = finfo->internal_syms + irel->r_symndx;
 
                          name = (_bfd_coff_internal_syment_name
@@ -1891,12 +2461,22 @@ coff_link_input_bfd (finfo, input_bfd)
        }
 
       /* Write out the modified section contents.  */
-      if (! bfd_set_section_contents (output_bfd, o->output_section,
-                                     contents, o->output_offset,
-                                     (o->_cooked_size != 0
-                                      ? o->_cooked_size
-                                      : o->_raw_size)))
-       return false;
+      if (secdata == NULL || secdata->stab_info == NULL)
+       {
+         file_ptr loc = o->output_offset * bfd_octets_per_byte (output_bfd);
+         bfd_size_type amt = (o->_cooked_size != 0
+                              ? o->_cooked_size : o->_raw_size);
+         if (! bfd_set_section_contents (output_bfd, o->output_section,
+                                         contents, loc, amt))
+           return false;
+       }
+      else
+       {
+         if (! (_bfd_write_section_stabs
+                (output_bfd, &coff_hash_table (finfo->info)->stab_info,
+                 o, &secdata->stab_info, contents)))
+           return false;
+       }
     }
 
   if (! finfo->info->keep_memory)
@@ -1910,8 +2490,8 @@ coff_link_input_bfd (finfo, input_bfd)
 
 /* Write out a global symbol.  Called via coff_link_hash_traverse.  */
 
-static boolean
-coff_write_global_sym (h, data)
+boolean
+_bfd_coff_write_global_sym (h, data)
      struct coff_link_hash_entry *h;
      PTR data;
 {
@@ -1920,9 +2500,17 @@ coff_write_global_sym (h, data)
   struct internal_syment isym;
   bfd_size_type symesz;
   unsigned int i;
+  file_ptr pos;
 
   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;
 
@@ -1938,6 +2526,7 @@ coff_write_global_sym (h, data)
     {
     default:
     case bfd_link_hash_new:
+    case bfd_link_hash_warning:
       abort ();
       return false;
 
@@ -1958,8 +2547,9 @@ coff_write_global_sym (h, data)
        else
          isym.n_scnum = sec->target_index;
        isym.n_value = (h->root.u.def.value
-                       + sec->vma
                        + h->root.u.def.section->output_offset);
+       if (! obj_pe (finfo->output_bfd))
+         isym.n_value += sec->vma;
       }
       break;
 
@@ -1969,7 +2559,6 @@ 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;
     }
@@ -2001,17 +2590,36 @@ coff_write_global_sym (h, data)
   if (isym.n_sclass == C_NULL)
     isym.n_sclass = C_EXT;
 
+  /* If doing task linking and this is the pass where we convert
+     defined globals to statics, then do that conversion now.  If the
+     symbol is not being converted, just ignore it and it will be
+     output during a later pass.  */
+  if (finfo->global_to_static)
+    {
+      if (! IS_EXTERNAL (output_bfd, isym))
+       return true;
+
+      isym.n_sclass = C_STAT;
+    }
+
+  /* When a weak symbol is not overriden by a strong one,
+     turn it into an external symbol when not building a
+     shared or relocateable object.  */
+  if (! finfo->info->shared
+      && ! finfo->info->relocateable
+      && IS_WEAK_EXTERNAL (finfo->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);
 
   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 (finfo->outsyms, symesz, output_bfd) != symesz)
     {
       finfo->failed = true;
       return false;
@@ -2021,14 +2629,67 @@ coff_write_global_sym (h, data)
 
   ++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,
+      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->_cooked_size != 0
+                                     ? sec->_cooked_size
+                                     : sec->_raw_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)
+                     || finfo->info->relocateable))
+               (*_bfd_error_handler)
+                 (_("%s: %s: reloc overflow: 0x%lx > 0xffff"),
+                  bfd_get_filename (output_bfd),
+                  bfd_get_section_name (output_bfd, sec),
+                  sec->reloc_count);
+
+             if (sec->lineno_count > 0xffff
+                 && (! obj_pe (output_bfd)
+                     || finfo->info->relocateable))
+               (*_bfd_error_handler)
+                 (_("%s: warning: %s: line number overflow: 0x%lx > 0xffff"),
+                  bfd_get_filename (output_bfd),
+                  bfd_get_section_name (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, (PTR) auxp, isym.n_type,
+                            isym.n_sclass, (int) i, isym.n_numaux,
                             (PTR) finfo->outsyms);
-      if (bfd_write (finfo->outsyms, symesz, 1, output_bfd) != symesz)
+      if (bfd_bwrite (finfo->outsyms, symesz, output_bfd) != symesz)
        {
          finfo->failed = true;
          return false;
@@ -2039,10 +2700,44 @@ coff_write_global_sym (h, data)
   return true;
 }
 
+/* Write out task global symbols, converting them to statics.  Called
+   via coff_link_hash_traverse.  Calls bfd_coff_write_global_sym to do
+   the dirty work, if the symbol we are processing needs conversion.  */
+
+boolean
+_bfd_coff_write_task_globals (h, data)
+     struct coff_link_hash_entry *h;
+     PTR data;
+{
+  struct coff_final_link_info *finfo = (struct coff_final_link_info *) data;
+  boolean rtnval = true;
+  boolean save_global_to_static;
+
+  if (h->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 = finfo->global_to_static;
+         finfo->global_to_static = true;
+         rtnval = _bfd_coff_write_global_sym (h, data);
+         finfo->global_to_static = save_global_to_static;
+         break;
+       default:
+         break;
+       }
+    }
+  return (rtnval);
+}
+
 /* Handle a link order which is supposed to generate a reloc.  */
 
-static boolean
-coff_reloc_link_order (output_bfd, finfo, output_section, link_order)
+boolean
+_bfd_coff_reloc_link_order (output_bfd, finfo, output_section, link_order)
      bfd *output_bfd;
      struct coff_final_link_info *finfo;
      asection *output_section;
@@ -2065,17 +2760,16 @@ coff_reloc_link_order (output_bfd, finfo, output_section, link_order)
       bfd_byte *buf;
       bfd_reloc_status_type rstat;
       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;
-       }
+       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:
@@ -2098,8 +2792,9 @@ coff_reloc_link_order (output_bfd, finfo, output_section, link_order)
            }
          break;
        }
+      loc = link_order->offset * bfd_octets_per_byte (output_bfd);
       ok = bfd_set_section_contents (output_bfd, output_section, (PTR) buf,
-                                    (file_ptr) link_order->offset, size);
+                                     loc, size);
       free (buf);
       if (! ok)
        return false;
@@ -2132,9 +2827,10 @@ coff_reloc_link_order (output_bfd, finfo, output_section, link_order)
     {
       struct coff_link_hash_entry *h;
 
-      h = coff_link_hash_lookup (coff_hash_table (finfo->info),
-                                link_order->u.reloc.p->u.name,
-                                false, false, true);
+      h = ((struct coff_link_hash_entry *)
+          bfd_wrapped_link_hash_lookup (output_bfd, finfo->info,
+                                        link_order->u.reloc.p->u.name,
+                                        false, false, true));
       if (h != NULL)
        {
          if (h->indx >= 0)
@@ -2190,7 +2886,6 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
   struct internal_reloc *rel;
   struct internal_reloc *relend;
 
-
   rel = relocs;
   relend = rel + input_section->reloc_count;
   for (; rel < relend; rel++)
@@ -2210,8 +2905,16 @@ _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)
+           ("%s: illegal symbol index %ld in relocs",
+            bfd_archive_filename (input_bfd), symndx);
+         return false;
+       }
       else
-       {    
+       {
          h = obj_coff_sym_hashes (input_bfd)[symndx];
          sym = syms + symndx;
        }
@@ -2226,20 +2929,22 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
       else
        addend = 0;
 
-
       howto = bfd_coff_rtype_to_howto (input_bfd, input_section, rel, h,
                                       sym, &addend);
       if (howto == NULL)
        return false;
 
-      /* WINDOWS_NT; in this next section, the value of 'val' will be computed.
-         With respect to the .idata and .rsrc sections, the NT_IMAGE_BASE
-         must be removed from the value that is to be relocated (NT_IMAGE_BASE
-         is currently defined in internal.h and has value 400000).  Now this
-         value should only be removed from addresses being relocated in the
-         .idata and .rsrc sections, not the .text section which should have
-         the 'real' address.  In addition, the .rsrc val's must also be
-         adjusted by the input_section->vma.  */
+      /* If we are doing a relocateable link, then we can just ignore
+         a PC relative reloc that is pcrel_offset.  It will already
+         have the correct value.  If this is not a relocateable link,
+         then we should ignore the symbol value.  */
+      if (howto->pc_relative && howto->pcrel_offset)
+       {
+         if (info->relocateable)
+           continue;
+         if (sym != NULL && sym->n_scnum != 0)
+           addend += sym->n_value;
+       }
 
       val = 0;
 
@@ -2257,8 +2962,9 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
              sec = sections[symndx];
               val = (sec->output_section->vma
                     + sec->output_offset
-                    + sym->n_value
-                    - sec->vma);
+                    + sym->n_value);
+             if (! obj_pe (input_bfd))
+               val -= sec->vma;
            }
        }
       else
@@ -2274,33 +2980,44 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
                     + sec->output_offset);
              }
 
+         else if (h->root.type == bfd_link_hash_undefweak)
+           val = 0;
+
          else if (! info->relocateable)
            {
              if (! ((*info->callbacks->undefined_symbol)
                     (info, h->root.root.string, input_bfd, input_section,
-                     rel->r_vaddr - input_section->vma)))
+                     rel->r_vaddr - input_section->vma, true)))
                return false;
            }
        }
 
       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 long here,
+                and dlltool reads in a long.  */
+             long addr = (rel->r_vaddr
+                          - input_section->vma
+                          + input_section->output_offset
+                          + input_section->output_section->vma);
+             if (coff_data (output_bfd)->pe)
+               addr -= pe_data(output_bfd)->pe_opthdr.ImageBase;
+             if (fwrite (&addr, 1, sizeof (long), (FILE *) info->base_file)
+                 != sizeof (long))
+               {
+                 bfd_set_error (bfd_error_system_call);
+                 return false;
+               }
            }
        }
-  
+
       rstat = _bfd_final_link_relocate (howto, input_bfd, input_section,
                                        contents,
                                        rel->r_vaddr - input_section->vma,
@@ -2312,6 +3029,13 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
          abort ();
        case bfd_reloc_ok:
          break;
+       case bfd_reloc_outofrange:
+         (*_bfd_error_handler)
+           (_("%s: bad reloc address 0x%lx in section `%s'"),
+            bfd_archive_filename (input_bfd),
+            (unsigned long) rel->r_vaddr,
+            bfd_get_section_name (input_bfd, input_section));
+         return false;
        case bfd_reloc_overflow:
          {
            const char *name;
@@ -2335,6 +3059,5 @@ _bfd_coff_generic_relocate_section (output_bfd, info, input_bfd,
          }
        }
     }
-
   return true;
 }
This page took 0.053338 seconds and 4 git commands to generate.