* ecoff.c: First cut at new style of linker backend for
authorIan Lance Taylor <ian@airs.com>
Thu, 6 Jan 1994 20:04:43 +0000 (20:04 +0000)
committerIan Lance Taylor <ian@airs.com>
Thu, 6 Jan 1994 20:04:43 +0000 (20:04 +0000)
ECOFF--added a bunch of functions.  Also:
(ecoff_sec_to_styp_flags): Set flags for .pdata and .xdata.
(ecoff_slurp_symbolic_header): New function.
(ecoff_slurp_symbolic_info): Call ecoff_slurp_symbolic_header.
(ecoff_compute_reloc_file_positions): New function.
(ecoff_set_section_contents): Get out quickly if count is zero.
Check errors better.
(ecoff_write_object_contents): Put .xdata section in data segment.
Call ecoff_compute_reloc_file_positions.  Don't output relocs or
external symbols if outsymbols is NULL.
(ecoff_bfd_final_link): Completely rewritten.
* libecoff.h: Include bfdlink.h.
(struct ecoff_backend_data): Add relocate_section field.
(ecoff_data_type): Add sym_hashes and symndx_to_section fields.
(struct ecoff_link_hash_entry): Define.
(struct ecoff_link_hash_table): Define.
(ecoff_bfd_link_add_symbols): Declare as function, not macro.
(ecoff_bfd_link_hash_table_create): Likewise.
* ecofflink.c (bfd_ecoff_debug_one_external): New function.
(bfd_ecoff_debug_externals): Call bfd_ecoff_debug_one_external.
* bfd-in.h (bfd_ecoff_debug_one_external): Declare.
* bfd-in2.h: Rebuilt.
* coff-alpha.c (alpha_howto_table): Mark BRADDR as
partial_inplace, and set the src_mask to 0x1fffff.
(alpha_ecoff_get_relocated_section_contents): Remove unused
variable gp_warned.
(alpha_convert_external_reloc): New static function.
(alpha_relocate_section): New static function.
(alpha_ecoff_backend_data): Initialize relocate_section field.
* coff-mips.c (mips_relocate_refhi): New static function.
(mips_relocate_section): New static function.
(mips_ecoff_backend_data): Initialize relocate_section field.

bfd/bfd-in.h
bfd/bfd-in2.h
bfd/ecoff.c
bfd/ecofflink.c

index 343944b..5de9115 100644 (file)
@@ -407,7 +407,8 @@ extern struct bfd_hash_entry *bfd_hash_newfunc
           const char *));
 
 /* Grab some space for a hash table entry.  */
-extern PTR bfd_hash_allocate PARAMS ((struct bfd_hash_table *, size_t));
+extern PTR bfd_hash_allocate PARAMS ((struct bfd_hash_table *,
+                                     unsigned int));
 
 /* Traverse a hash table in a random order, calling a function on each
    element.  If the function returns false, the traversal stops.  The
@@ -558,6 +559,10 @@ extern boolean bfd_ecoff_debug_externals
                                struct ecoff_extr *),
           void (*set_index) (struct symbol_cache_entry *,
                              bfd_size_type)));
+extern boolean bfd_ecoff_debug_one_external
+  PARAMS ((bfd *abfd, struct ecoff_debug_info *debug,
+          const struct ecoff_debug_swap *swap,
+          const char *name, struct ecoff_extr *esym));
 extern bfd_size_type bfd_ecoff_debug_size
   PARAMS ((bfd *abfd, struct ecoff_debug_info *debug,
           const struct ecoff_debug_swap *swap));
index 34d49b4..ef7e748 100644 (file)
@@ -407,7 +407,8 @@ extern struct bfd_hash_entry *bfd_hash_newfunc
           const char *));
 
 /* Grab some space for a hash table entry.  */
-extern PTR bfd_hash_allocate PARAMS ((struct bfd_hash_table *, size_t));
+extern PTR bfd_hash_allocate PARAMS ((struct bfd_hash_table *,
+                                     unsigned int));
 
 /* Traverse a hash table in a random order, calling a function on each
    element.  If the function returns false, the traversal stops.  The
@@ -558,6 +559,10 @@ extern boolean bfd_ecoff_debug_externals
                                struct ecoff_extr *),
           void (*set_index) (struct symbol_cache_entry *,
                              bfd_size_type)));
+extern boolean bfd_ecoff_debug_one_external
+  PARAMS ((bfd *abfd, struct ecoff_debug_info *debug,
+          const struct ecoff_debug_swap *swap,
+          const char *name, struct ecoff_extr *esym));
 extern bfd_size_type bfd_ecoff_debug_size
   PARAMS ((bfd *abfd, struct ecoff_debug_info *debug,
           const struct ecoff_debug_swap *swap));
index fb4960a..e2f2319 100644 (file)
@@ -45,6 +45,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 /* Prototypes for static functions.  */
 
 static int ecoff_get_magic PARAMS ((bfd *abfd));
+static boolean ecoff_slurp_symbolic_header PARAMS ((bfd *abfd));
 static void ecoff_set_symbol_info PARAMS ((bfd *abfd, SYMR *ecoff_sym,
                                           asymbol *asym, int ext,
                                           asymbol **indirect_ptr_ptr));
@@ -56,6 +57,7 @@ static char *ecoff_type_to_string PARAMS ((bfd *abfd, union aux_ext *aux_ptr,
 static boolean ecoff_slurp_reloc_table PARAMS ((bfd *abfd, asection *section,
                                                asymbol **symbols));
 static void ecoff_compute_section_file_positions PARAMS ((bfd *abfd));
+static bfd_size_type ecoff_compute_reloc_file_positions PARAMS ((bfd *abfd));
 static boolean ecoff_get_extr PARAMS ((asymbol *, EXTR *));
 static void ecoff_set_index PARAMS ((asymbol *, bfd_size_type));
 static unsigned int ecoff_armap_hash PARAMS ((CONST char *s,
@@ -310,6 +312,10 @@ ecoff_sec_to_styp_flags (name, flags)
     styp = STYP_ECOFF_INIT;
   else if (strcmp (name, _FINI) == 0)
     styp = STYP_ECOFF_FINI;
+  else if (strcmp (name, _PDATA) == 0)
+    styp = STYP_PDATA;
+  else if (strcmp (name, _XDATA) == 0)
+    styp = STYP_XDATA;
   else if (flags & SEC_CODE) 
     styp = STYP_TEXT;
   else if (flags & SEC_DATA) 
@@ -581,30 +587,23 @@ ecoff_swap_rndx_out (bigend, intern_copy, ext)
 #endif
 }
 \f
-/* Read in and swap the important symbolic information for an ECOFF
-   object file.  This is called by gdb.  */
+/* Read in the symbolic header for an ECOFF object file.  */
 
-boolean
-ecoff_slurp_symbolic_info (abfd)
+static boolean
+ecoff_slurp_symbolic_header (abfd)
      bfd *abfd;
 {
   const struct ecoff_backend_data * const backend = ecoff_backend (abfd);
   bfd_size_type external_hdr_size;
-  HDRR *internal_symhdr;
-  bfd_size_type raw_base;
-  bfd_size_type raw_size;
   PTR raw;
-  bfd_size_type external_fdr_size;
-  char *fraw_src;
-  char *fraw_end;
-  struct fdr *fdr_ptr;
-  bfd_size_type raw_end;
-  bfd_size_type cb_end;
+  HDRR *internal_symhdr;
 
-  /* Check whether we've already gotten it, and whether there's any to
-     get.  */
-  if (ecoff_data (abfd)->raw_syments != (PTR) NULL)
+  /* See if we've already read it in.  */
+  if (ecoff_data (abfd)->debug_info.symbolic_header.magic == 
+      backend->debug_swap.sym_magic)
     return true;
+
+  /* See whether there is a symbolic header.  */
   if (ecoff_data (abfd)->sym_filepos == 0)
     {
       bfd_get_symcount (abfd) = 0;
@@ -623,7 +622,7 @@ ecoff_slurp_symbolic_info (abfd)
     }
 
   /* Read the symbolic information header.  */
-  raw = (PTR) alloca (external_hdr_size);
+  raw = (PTR) alloca ((size_t) external_hdr_size);
   if (bfd_seek (abfd, ecoff_data (abfd)->sym_filepos, SEEK_SET) == -1
       || (bfd_read (raw, external_hdr_size, 1, abfd)
          != external_hdr_size))
@@ -644,8 +643,46 @@ ecoff_slurp_symbolic_info (abfd)
   bfd_get_symcount (abfd) = (internal_symhdr->isymMax
                             + internal_symhdr->iextMax);
 
+  return true;
+}
+
+/* Read in and swap the important symbolic information for an ECOFF
+   object file.  This is called by gdb.  */
+
+boolean
+ecoff_slurp_symbolic_info (abfd)
+     bfd *abfd;
+{
+  const struct ecoff_backend_data * const backend = ecoff_backend (abfd);
+  HDRR *internal_symhdr;
+  bfd_size_type raw_base;
+  bfd_size_type raw_size;
+  PTR raw;
+  bfd_size_type external_fdr_size;
+  char *fraw_src;
+  char *fraw_end;
+  struct fdr *fdr_ptr;
+  bfd_size_type raw_end;
+  bfd_size_type cb_end;
+
+  /* Check whether we've already gotten it, and whether there's any to
+     get.  */
+  if (ecoff_data (abfd)->raw_syments != (PTR) NULL)
+    return true;
+  if (ecoff_data (abfd)->sym_filepos == 0)
+    {
+      bfd_get_symcount (abfd) = 0;
+      return true;
+    }
+
+  if (! ecoff_slurp_symbolic_header (abfd))
+    return false;
+
+  internal_symhdr = &ecoff_data (abfd)->debug_info.symbolic_header;
+
   /* Read all the symbolic information at once.  */
-  raw_base = ecoff_data (abfd)->sym_filepos + external_hdr_size;
+  raw_base = (ecoff_data (abfd)->sym_filepos
+             + backend->debug_swap.external_hdr_size);
 
   /* Alpha ecoff makes the determination of raw_size difficult. It has
      an undocumented debug data section between the symhdr and the first
@@ -686,7 +723,11 @@ ecoff_slurp_symbolic_info (abfd)
       bfd_error = no_memory;
       return false;
     }
-  if (bfd_read (raw, raw_size, 1, abfd) != raw_size)
+  if (bfd_seek (abfd,
+               (ecoff_data (abfd)->sym_filepos
+                + backend->debug_swap.external_hdr_size),
+               SEEK_SET) != 0
+      || bfd_read (raw, raw_size, 1, abfd) != raw_size)
     {
       bfd_error = system_call_error;
       bfd_release (abfd, raw);
@@ -2043,169 +2084,6 @@ ecoff_find_nearest_line (abfd,
   return true;
 }
 \f
-/* We can't use the generic linking routines for ECOFF, because we
-   have to handle all the debugging information.  The generic link
-   routine just works out the section contents and attaches a list of
-   symbols.  We find each input BFD by looping over all the link_order
-   information.  We accumulate the debugging information for each
-   input BFD.  */
-
-/* Get ECOFF EXTR information for an external symbol.  This function
-   is passed to bfd_ecoff_debug_externals.  */
-
-static boolean
-ecoff_get_extr (sym, esym)
-     asymbol *sym;
-     EXTR *esym;
-{
-  ecoff_symbol_type *ecoff_sym_ptr;
-  bfd *input_bfd;
-
-  /* Don't include debugging or local symbols.  */
-  if ((sym->flags & BSF_DEBUGGING) != 0
-      || (sym->flags & BSF_LOCAL) != 0)
-    return false;
-
-  if (bfd_asymbol_flavour (sym) != bfd_target_ecoff_flavour
-      || ecoffsymbol (sym)->native == NULL)
-    {
-      esym->jmptbl = 0;
-      esym->cobol_main = 0;
-      esym->weakext = 0;
-      esym->reserved = 0;
-      esym->ifd = ifdNil;
-      /* FIXME: we can do better than this for st and sc.  */
-      esym->asym.st = stGlobal;
-      esym->asym.sc = scAbs;
-      esym->asym.reserved = 0;
-      esym->asym.index = indexNil;
-      return true;
-    }
-
-  ecoff_sym_ptr = ecoffsymbol (sym);
-
-  if (ecoff_sym_ptr->local)
-    abort ();
-
-  input_bfd = bfd_asymbol_bfd (sym);
-  (*(ecoff_backend (input_bfd)->debug_swap.swap_ext_in))
-    (input_bfd, ecoff_sym_ptr->native, esym);
-
-  /* If the symbol was defined by the linker, then esym will be
-     undefined but sym will not be.  Get a better class for such a
-     symbol.  */
-  if ((esym->asym.sc == scUndefined
-       || esym->asym.sc == scSUndefined)
-      && bfd_get_section (sym) != &bfd_und_section)
-    esym->asym.sc = scAbs;
-
-  /* Adjust the FDR index for the symbol by that used for the input
-     BFD.  */
-  esym->ifd += ecoff_data (input_bfd)->debug_info.ifdbase;
-
-  return true;
-}
-
-/* Set the external symbol index.  This routine is passed to
-   bfd_ecoff_debug_externals.  */
-
-static void
-ecoff_set_index (sym, indx)
-     asymbol *sym;
-     bfd_size_type indx;
-{
-  ecoff_set_sym_index (sym, indx);
-}
-
-/* This is the actual link routine.  It builds the debugging
-   information, and then lets the generic linking routine complete the
-   link.  */
-
-boolean
-ecoff_bfd_final_link (abfd, info)
-     bfd *abfd;
-     struct bfd_link_info *info;
-{
-  const struct ecoff_backend_data * const backend = ecoff_backend (abfd);
-  struct ecoff_debug_info * const debug = &ecoff_data (abfd)->debug_info;
-  HDRR *symhdr;
-  register bfd *input_bfd;
-  asection *o;
-
-  /* We accumulate the debugging information counts in the symbolic
-     header.  */
-  symhdr = &debug->symbolic_header;
-  symhdr->magic = backend->debug_swap.sym_magic;
-  /* FIXME: What should the version stamp be?  */
-  symhdr->vstamp = 0;
-  symhdr->ilineMax = 0;
-  symhdr->cbLine = 0;
-  symhdr->idnMax = 0;
-  symhdr->ipdMax = 0;
-  symhdr->isymMax = 0;
-  symhdr->ioptMax = 0;
-  symhdr->iauxMax = 0;
-  symhdr->issMax = 0;
-  symhdr->ifdMax = 0;
-  symhdr->crfd = 0;
-
-  /* We accumulate the debugging information itself in the debug_info
-     structure.  */
-  debug->line = debug->line_end = NULL;
-  debug->external_dnr = debug->external_dnr_end = NULL;
-  debug->external_pdr = debug->external_pdr_end = NULL;
-  debug->external_sym = debug->external_sym_end = NULL;
-  debug->external_opt = debug->external_opt_end = NULL;
-  debug->external_aux = debug->external_aux_end = NULL;
-  debug->ss = debug->ss_end = NULL;
-  debug->external_fdr = debug->external_fdr_end = NULL;
-  debug->external_rfd = debug->external_rfd_end = NULL;
-
-  /* We accumulate the debugging symbols from each input BFD.  */
-  for (input_bfd = info->input_bfds;
-       input_bfd != (bfd *) NULL;
-       input_bfd = input_bfd->link_next)
-    {
-      boolean ret;
-
-      if (bfd_get_flavour (input_bfd) == bfd_target_ecoff_flavour)
-       ret = (bfd_ecoff_debug_accumulate
-              (abfd, debug, &backend->debug_swap,
-               input_bfd, &ecoff_data (input_bfd)->debug_info,
-               &ecoff_backend (input_bfd)->debug_swap, info->relocateable));
-      else
-       ret = bfd_ecoff_debug_link_other (abfd,
-                                         debug,
-                                         &backend->debug_swap,
-                                         input_bfd);
-
-      if (! ret)
-       return false;
-
-      /* Combine the register masks.  */
-      ecoff_data (abfd)->gprmask |= ecoff_data (input_bfd)->gprmask;
-      ecoff_data (abfd)->fprmask |= ecoff_data (input_bfd)->fprmask;
-      ecoff_data (abfd)->cprmask[0] |= ecoff_data (input_bfd)->cprmask[0];
-      ecoff_data (abfd)->cprmask[1] |= ecoff_data (input_bfd)->cprmask[1];
-      ecoff_data (abfd)->cprmask[2] |= ecoff_data (input_bfd)->cprmask[2];
-      ecoff_data (abfd)->cprmask[3] |= ecoff_data (input_bfd)->cprmask[3];
-    }
-
-  /* Don't let the generic routine link the .reginfo sections.  */
-  for (o = abfd->sections; o != (asection *) NULL; o = o->next)
-    {
-      if (strcmp (o->name, REGINFO) == 0)
-       {
-         o->link_order_head = (struct bfd_link_order *) NULL;
-         break;
-       }
-    }
-
-  /* Let the generic link routine handle writing out the section
-     contents.  */
-  return _bfd_generic_final_link (abfd, info);
-}
-\f
 /* Set the architecture.  The supported architecture is stored in the
    backend pointer.  We always set the architecture anyhow, since many
    callers ignore the return value.  */
@@ -2340,6 +2218,47 @@ ecoff_compute_section_file_positions (abfd)
   ecoff_data (abfd)->reloc_filepos = sofar;
 }
 
+/* Determine the location of the relocs for all the sections in the
+   output file.  */
+
+static bfd_size_type
+ecoff_compute_reloc_file_positions (abfd)
+     bfd *abfd;
+{
+  const bfd_size_type external_reloc_size =
+    ecoff_backend (abfd)->external_reloc_size;
+  file_ptr reloc_base;
+  bfd_size_type reloc_size;
+  asection *current;
+
+  if (! abfd->output_has_begun)
+    ecoff_compute_section_file_positions (abfd);
+  
+  reloc_base = ecoff_data (abfd)->reloc_filepos;
+
+  reloc_size = 0;
+  for (current = abfd->sections;
+       current != (asection *)NULL; 
+       current = current->next) 
+    {
+      if (strcmp (current->name, REGINFO) == 0)
+       continue;
+      if (current->reloc_count == 0)
+       current->rel_filepos = 0;
+      else
+       {
+         bfd_size_type relsize;
+
+         current->rel_filepos = reloc_base;
+         relsize = current->reloc_count * external_reloc_size;
+         reloc_size += relsize;
+         reloc_base += relsize;
+       }
+    }
+
+  return reloc_size;
+}
+
 /* Set the contents of a section.  This is where we handle setting the
    contents of the .reginfo section, which implicitly holds a
    ecoff_reginfo structure.  */
@@ -2352,9 +2271,14 @@ ecoff_set_section_contents (abfd, section, location, offset, count)
      file_ptr offset;
      bfd_size_type count;
 {
+  /* This must be done first, because bfd_set_section_contents is
+     going to set output_has_begun to true.  */
   if (abfd->output_has_begun == false)
     ecoff_compute_section_file_positions (abfd);
 
+  if (count == 0)
+    return true;
+
   if (strcmp (section->name, REGINFO) == 0)
     {
       ecoff_data_type *tdata = ecoff_data (abfd);
@@ -2386,34 +2310,100 @@ ecoff_set_section_contents (abfd, section, location, offset, count)
       tdata->fprmask = s.fprmask;
 
       return true;
-
     }
 
-  bfd_seek (abfd, (file_ptr) (section->filepos + offset), SEEK_SET);
-
-  if (count != 0)
-    return (bfd_write (location, 1, count, abfd) == count) ? true : false;
+  if (bfd_seek (abfd, (file_ptr) (section->filepos + offset), SEEK_SET) != 0
+      || bfd_write (location, 1, count, abfd) != count)
+    return false;
 
   return true;
 }
 
-/* Write out an ECOFF file.  */
+/* Get ECOFF EXTR information for an external symbol.  This function
+   is passed to bfd_ecoff_debug_externals.  */
 
-boolean
-ecoff_write_object_contents (abfd)
-     bfd *abfd;
+static boolean
+ecoff_get_extr (sym, esym)
+     asymbol *sym;
+     EXTR *esym;
 {
-  const struct ecoff_backend_data * const backend = ecoff_backend (abfd);
-  const bfd_vma round = backend->round;
-  const bfd_size_type filhsz = bfd_coff_filhsz (abfd);
-  const bfd_size_type aoutsz = bfd_coff_aoutsz (abfd);
-  const bfd_size_type scnhsz = bfd_coff_scnhsz (abfd);
-  const bfd_size_type external_hdr_size
-    = backend->debug_swap.external_hdr_size;
-  const bfd_size_type external_reloc_size = backend->external_reloc_size;
-  void (* const adjust_reloc_out) PARAMS ((bfd *,
-                                          const arelent *,
-                                          struct internal_reloc *))
+  ecoff_symbol_type *ecoff_sym_ptr;
+  bfd *input_bfd;
+
+  /* Don't include debugging, local or section symbols.  */
+  if ((sym->flags & BSF_DEBUGGING) != 0
+      || (sym->flags & BSF_LOCAL) != 0
+      || (sym->flags & BSF_SECTION_SYM) != 0)
+    return false;
+
+  if (bfd_asymbol_flavour (sym) != bfd_target_ecoff_flavour
+      || ecoffsymbol (sym)->native == NULL)
+    {
+      esym->jmptbl = 0;
+      esym->cobol_main = 0;
+      esym->weakext = 0;
+      esym->reserved = 0;
+      esym->ifd = ifdNil;
+      /* FIXME: we can do better than this for st and sc.  */
+      esym->asym.st = stGlobal;
+      esym->asym.sc = scAbs;
+      esym->asym.reserved = 0;
+      esym->asym.index = indexNil;
+      return true;
+    }
+
+  ecoff_sym_ptr = ecoffsymbol (sym);
+
+  if (ecoff_sym_ptr->local)
+    abort ();
+
+  input_bfd = bfd_asymbol_bfd (sym);
+  (*(ecoff_backend (input_bfd)->debug_swap.swap_ext_in))
+    (input_bfd, ecoff_sym_ptr->native, esym);
+
+  /* If the symbol was defined by the linker, then esym will be
+     undefined but sym will not be.  Get a better class for such a
+     symbol.  */
+  if ((esym->asym.sc == scUndefined
+       || esym->asym.sc == scSUndefined)
+      && bfd_get_section (sym) != &bfd_und_section)
+    esym->asym.sc = scAbs;
+
+  /* Adjust the FDR index for the symbol by that used for the input
+     BFD.  */
+  esym->ifd += ecoff_data (input_bfd)->debug_info.ifdbase;
+
+  return true;
+}
+
+/* Set the external symbol index.  This routine is passed to
+   bfd_ecoff_debug_externals.  */
+
+static void
+ecoff_set_index (sym, indx)
+     asymbol *sym;
+     bfd_size_type indx;
+{
+  ecoff_set_sym_index (sym, indx);
+}
+
+/* Write out an ECOFF file.  */
+
+boolean
+ecoff_write_object_contents (abfd)
+     bfd *abfd;
+{
+  const struct ecoff_backend_data * const backend = ecoff_backend (abfd);
+  const bfd_vma round = backend->round;
+  const bfd_size_type filhsz = bfd_coff_filhsz (abfd);
+  const bfd_size_type aoutsz = bfd_coff_aoutsz (abfd);
+  const bfd_size_type scnhsz = bfd_coff_scnhsz (abfd);
+  const bfd_size_type external_hdr_size
+    = backend->debug_swap.external_hdr_size;
+  const bfd_size_type external_reloc_size = backend->external_reloc_size;
+  void (* const adjust_reloc_out) PARAMS ((bfd *,
+                                          const arelent *,
+                                          struct internal_reloc *))
     = backend->adjust_reloc_out;
   void (* const swap_reloc_out) PARAMS ((bfd *,
                                         const struct internal_reloc *,
@@ -2423,9 +2413,8 @@ ecoff_write_object_contents (abfd)
   HDRR * const symhdr = &debug->symbolic_header;
   asection *current;
   unsigned int count;
-  file_ptr reloc_base;
   file_ptr sym_base;
-  unsigned long reloc_size;
+  bfd_size_type reloc_size;
   unsigned long text_size;
   unsigned long text_start;
   unsigned long data_size;
@@ -2438,13 +2427,11 @@ ecoff_write_object_contents (abfd)
 
   bfd_error = system_call_error;
 
-  if(abfd->output_has_begun == false)
-    ecoff_compute_section_file_positions(abfd);
-
-  reloc_base = ecoff_data (abfd)->reloc_filepos;
+  /* Determine where the sections and relocs will go in the output
+     file.  */
+  reloc_size = ecoff_compute_reloc_file_positions (abfd);
 
   count = 1;
-  reloc_size = 0;
   for (current = abfd->sections;
        current != (asection *)NULL; 
        current = current->next) 
@@ -2453,20 +2440,9 @@ ecoff_write_object_contents (abfd)
        continue;
       current->target_index = count;
       ++count;
-      if (current->reloc_count != 0)
-       {
-         bfd_size_type relsize;
-
-         current->rel_filepos = reloc_base;
-         relsize = current->reloc_count * external_reloc_size;
-         reloc_size += relsize;
-         reloc_base += relsize;
-       }
-      else
-       current->rel_filepos = 0;
     }
 
-  sym_base = reloc_base + reloc_size;
+  sym_base = ecoff_data (abfd)->reloc_filepos + reloc_size;
 
   /* At least on Ultrix, the symbol table of an executable file must
      be aligned to a page boundary.  FIXME: Is this true on other
@@ -2559,7 +2535,8 @@ ecoff_write_object_contents (abfd)
               || (section.s_flags & STYP_LITA) != 0
               || (section.s_flags & STYP_LIT8) != 0
               || (section.s_flags & STYP_LIT4) != 0
-              || (section.s_flags & STYP_SDATA) != 0)
+              || (section.s_flags & STYP_SDATA) != 0
+              || strcmp (current->name, _XDATA) == 0)
        {
          data_size += bfd_get_section_size_before_reloc (current);
          if (data_start == 0 || data_start > vma)
@@ -2568,6 +2545,8 @@ ecoff_write_object_contents (abfd)
       else if ((section.s_flags & STYP_BSS) != 0
               || (section.s_flags & STYP_SBSS) != 0)
        bss_size += bfd_get_section_size_before_reloc (current);
+      else
+       abort ();
     }  
 
   /* Set up the file header.  */
@@ -2673,110 +2652,118 @@ ecoff_write_object_contents (abfd)
     return false;
 
   /* Build the external symbol information.  This must be done before
-     writing out the relocs so that we know the symbol indices.  */
-  symhdr->iextMax = 0;
-  symhdr->issExtMax = 0;
-  debug->external_ext = debug->external_ext_end = NULL;
-  debug->ssext = debug->ssext_end = NULL;
-  if (bfd_ecoff_debug_externals (abfd, debug, &backend->debug_swap,
-                                (((abfd->flags & EXEC_P) == 0)
-                                 ? true : false),
-                                ecoff_get_extr, ecoff_set_index)
-      == false)
-    return false;
-
-  /* Write out the relocs.  */
-  for (current = abfd->sections;
-       current != (asection *) NULL;
-       current = current->next)
+     writing out the relocs so that we know the symbol indices.  The
+     condition checks makes sure this object was not created by
+     ecoff_bfd_final_link, since if it was we do not want to tamper
+     with the external symbols.  */
+  if (bfd_get_outsymbols (abfd) != (asymbol **) NULL
+      || bfd_get_symcount (abfd) == 0)
     {
-      arelent **reloc_ptr_ptr;
-      arelent **reloc_end;
-      char *out_ptr;
-
-      if (current->reloc_count == 0)
-       continue;
-
-      buff = bfd_alloc (abfd, current->reloc_count * external_reloc_size);
-      if (buff == NULL)
-       {
-         bfd_error = no_memory;
-         return false;
-       }
+      symhdr->iextMax = 0;
+      symhdr->issExtMax = 0;
+      debug->external_ext = debug->external_ext_end = NULL;
+      debug->ssext = debug->ssext_end = NULL;
+      if (bfd_ecoff_debug_externals (abfd, debug, &backend->debug_swap,
+                                    (((abfd->flags & EXEC_P) == 0)
+                                     ? true : false),
+                                    ecoff_get_extr, ecoff_set_index)
+         == false)
+       return false;
 
-      reloc_ptr_ptr = current->orelocation;
-      reloc_end = reloc_ptr_ptr + current->reloc_count;
-      out_ptr = (char *) buff;
-      for (;
-          reloc_ptr_ptr < reloc_end;
-          reloc_ptr_ptr++, out_ptr += external_reloc_size)
+      /* Write out the relocs.  */
+      for (current = abfd->sections;
+          current != (asection *) NULL;
+          current = current->next)
        {
-         arelent *reloc;
-         asymbol *sym;
-         struct internal_reloc in;
-         
-         memset (&in, 0, sizeof in);
+         arelent **reloc_ptr_ptr;
+         arelent **reloc_end;
+         char *out_ptr;
 
-         reloc = *reloc_ptr_ptr;
-         sym = *reloc->sym_ptr_ptr;
+         if (current->reloc_count == 0)
+           continue;
 
-         in.r_vaddr = reloc->address + bfd_get_section_vma (abfd, current);
-         in.r_type = reloc->howto->type;
-
-         if ((sym->flags & BSF_SECTION_SYM) == 0)
+         buff = bfd_alloc (abfd, current->reloc_count * external_reloc_size);
+         if (buff == NULL)
            {
-             in.r_symndx = ecoff_get_sym_index (*reloc->sym_ptr_ptr);
-             in.r_extern = 1;
+             bfd_error = no_memory;
+             return false;
            }
-         else
+
+         reloc_ptr_ptr = current->orelocation;
+         reloc_end = reloc_ptr_ptr + current->reloc_count;
+         out_ptr = (char *) buff;
+         for (;
+              reloc_ptr_ptr < reloc_end;
+              reloc_ptr_ptr++, out_ptr += external_reloc_size)
            {
-             CONST char *name;
-
-             name = bfd_get_section_name (abfd, bfd_get_section (sym));
-             if (strcmp (name, ".text") == 0)
-               in.r_symndx = RELOC_SECTION_TEXT;
-             else if (strcmp (name, ".rdata") == 0)
-               in.r_symndx = RELOC_SECTION_RDATA;
-             else if (strcmp (name, ".data") == 0)
-               in.r_symndx = RELOC_SECTION_DATA;
-             else if (strcmp (name, ".sdata") == 0)
-               in.r_symndx = RELOC_SECTION_SDATA;
-             else if (strcmp (name, ".sbss") == 0)
-               in.r_symndx = RELOC_SECTION_SBSS;
-             else if (strcmp (name, ".bss") == 0)
-               in.r_symndx = RELOC_SECTION_BSS;
-             else if (strcmp (name, ".init") == 0)
-               in.r_symndx = RELOC_SECTION_INIT;
-             else if (strcmp (name, ".lit8") == 0)
-               in.r_symndx = RELOC_SECTION_LIT8;
-             else if (strcmp (name, ".lit4") == 0)
-               in.r_symndx = RELOC_SECTION_LIT4;
-             else if (strcmp (name, ".xdata") == 0)
-               in.r_symndx = RELOC_SECTION_XDATA;
-             else if (strcmp (name, ".pdata") == 0)
-               in.r_symndx = RELOC_SECTION_PDATA;
-             else if (strcmp (name, ".fini") == 0)
-               in.r_symndx = RELOC_SECTION_FINI;
-             else if (strcmp (name, ".lita") == 0)
-               in.r_symndx = RELOC_SECTION_LITA;
-             else if (strcmp (name, "*ABS*") == 0)
-               in.r_symndx = RELOC_SECTION_ABS;
+             arelent *reloc;
+             asymbol *sym;
+             struct internal_reloc in;
+         
+             memset (&in, 0, sizeof in);
+
+             reloc = *reloc_ptr_ptr;
+             sym = *reloc->sym_ptr_ptr;
+
+             in.r_vaddr = (reloc->address
+                           + bfd_get_section_vma (abfd, current));
+             in.r_type = reloc->howto->type;
+
+             if ((sym->flags & BSF_SECTION_SYM) == 0)
+               {
+                 in.r_symndx = ecoff_get_sym_index (*reloc->sym_ptr_ptr);
+                 in.r_extern = 1;
+               }
              else
-               abort ();
-             in.r_extern = 0;
+               {
+                 CONST char *name;
+
+                 name = bfd_get_section_name (abfd, bfd_get_section (sym));
+                 if (strcmp (name, ".text") == 0)
+                   in.r_symndx = RELOC_SECTION_TEXT;
+                 else if (strcmp (name, ".rdata") == 0)
+                   in.r_symndx = RELOC_SECTION_RDATA;
+                 else if (strcmp (name, ".data") == 0)
+                   in.r_symndx = RELOC_SECTION_DATA;
+                 else if (strcmp (name, ".sdata") == 0)
+                   in.r_symndx = RELOC_SECTION_SDATA;
+                 else if (strcmp (name, ".sbss") == 0)
+                   in.r_symndx = RELOC_SECTION_SBSS;
+                 else if (strcmp (name, ".bss") == 0)
+                   in.r_symndx = RELOC_SECTION_BSS;
+                 else if (strcmp (name, ".init") == 0)
+                   in.r_symndx = RELOC_SECTION_INIT;
+                 else if (strcmp (name, ".lit8") == 0)
+                   in.r_symndx = RELOC_SECTION_LIT8;
+                 else if (strcmp (name, ".lit4") == 0)
+                   in.r_symndx = RELOC_SECTION_LIT4;
+                 else if (strcmp (name, ".xdata") == 0)
+                   in.r_symndx = RELOC_SECTION_XDATA;
+                 else if (strcmp (name, ".pdata") == 0)
+                   in.r_symndx = RELOC_SECTION_PDATA;
+                 else if (strcmp (name, ".fini") == 0)
+                   in.r_symndx = RELOC_SECTION_FINI;
+                 else if (strcmp (name, ".lita") == 0)
+                   in.r_symndx = RELOC_SECTION_LITA;
+                 else if (strcmp (name, "*ABS*") == 0)
+                   in.r_symndx = RELOC_SECTION_ABS;
+                 else
+                   abort ();
+                 in.r_extern = 0;
+               }
+
+             (*adjust_reloc_out) (abfd, reloc, &in);
+
+             (*swap_reloc_out) (abfd, &in, (PTR) out_ptr);
            }
 
-         (*adjust_reloc_out) (abfd, reloc, &in);
-
-         (*swap_reloc_out) (abfd, &in, (PTR) out_ptr);
+         if (bfd_seek (abfd, current->rel_filepos, SEEK_SET) != 0)
+           return false;
+         if (bfd_write (buff, external_reloc_size, current->reloc_count, abfd)
+             != external_reloc_size * current->reloc_count)
+           return false;
+         bfd_release (abfd, buff);
        }
-
-      if (bfd_seek (abfd, current->rel_filepos, SEEK_SET) != 0)
-       return false;
-      if (bfd_write (buff, external_reloc_size, current->reloc_count, abfd)
-         != external_reloc_size * current->reloc_count)
-       return false;
-      bfd_release (abfd, buff);
     }
 
   /* Write out the symbolic debugging information.  */
@@ -3246,3 +3233,1006 @@ ecoff_archive_p (abfd)
   
   return abfd->xvec;
 }
+\f
+/* ECOFF linker code.  */
+
+static struct bfd_hash_entry *ecoff_link_hash_newfunc
+  PARAMS ((struct bfd_hash_entry *entry,
+          struct bfd_hash_table *table,
+          const char *string));
+static boolean ecoff_link_add_archive_symbols
+  PARAMS ((bfd *, struct bfd_link_info *));
+static boolean ecoff_link_check_archive_element
+  PARAMS ((bfd *, struct bfd_link_info *, boolean *pneeded));
+static boolean ecoff_link_add_object_symbols
+  PARAMS ((bfd *, struct bfd_link_info *));
+static boolean ecoff_link_add_externals
+  PARAMS ((bfd *, struct bfd_link_info *, PTR, char *));
+
+/* Routine to create an entry in an ECOFF link hash table.  */
+
+static struct bfd_hash_entry *
+ecoff_link_hash_newfunc (entry, table, string)
+     struct bfd_hash_entry *entry;
+     struct bfd_hash_table *table;
+     const char *string;
+{
+  struct ecoff_link_hash_entry *ret = (struct ecoff_link_hash_entry *) entry;
+
+  /* Allocate the structure if it has not already been allocated by a
+     subclass.  */
+  if (ret == (struct ecoff_link_hash_entry *) NULL)
+    ret = ((struct ecoff_link_hash_entry *)
+          bfd_hash_allocate (table, sizeof (struct ecoff_link_hash_entry)));
+
+  /* Call the allocation method of the superclass.  */
+  ret = ((struct ecoff_link_hash_entry *)
+        _bfd_link_hash_newfunc ((struct bfd_hash_entry *) ret,
+                                table, string));
+
+  /* Set local fields.  */
+  ret->indx = -1;
+  ret->abfd = NULL;
+  memset (&ret->esym, 0, sizeof ret->esym);
+
+  return (struct bfd_hash_entry *) ret;
+}
+
+/* Create an ECOFF link hash table.  */
+
+struct bfd_link_hash_table *
+ecoff_bfd_link_hash_table_create (abfd)
+     bfd *abfd;
+{
+  struct ecoff_link_hash_table *ret;
+
+  ret = ((struct ecoff_link_hash_table *)
+        bfd_xmalloc (sizeof (struct ecoff_link_hash_table)));
+  if (! _bfd_link_hash_table_init (&ret->root, abfd,
+                                  ecoff_link_hash_newfunc))
+    {
+      free (ret);
+      return (struct bfd_link_hash_table *) NULL;
+    }
+  return &ret->root;
+}
+
+/* Look up an entry in an ECOFF link hash table.  */
+
+#define ecoff_link_hash_lookup(table, string, create, copy, follow) \
+  ((struct ecoff_link_hash_entry *) \
+   bfd_link_hash_lookup (&(table)->root, (string), (create), (copy), (follow)))
+
+/* Traverse an ECOFF link hash table.  */
+
+#define ecoff_link_hash_traverse(table, func, info)                    \
+  (bfd_link_hash_traverse                                              \
+   (&(table)->root,                                                    \
+    (boolean (*) PARAMS ((struct bfd_link_hash_entry *, PTR))) (func), \
+    (info)))
+
+/* Get the ECOFF link hash table from the info structure.  This is
+   just a cast.  */
+
+#define ecoff_hash_table(p) ((struct ecoff_link_hash_table *) ((p)->hash))
+
+/* Given an ECOFF BFD, add symbols to the global hash table as
+   appropriate.  */
+
+boolean
+ecoff_bfd_link_add_symbols (abfd, info)
+     bfd *abfd;
+     struct bfd_link_info *info;
+{
+  switch (bfd_get_format (abfd))
+    {
+    case bfd_object:
+      return ecoff_link_add_object_symbols (abfd, info);
+    case bfd_archive:
+      return ecoff_link_add_archive_symbols (abfd, info);
+    default:
+      bfd_error = wrong_format;
+      return false;
+    }
+}
+
+/* Add the symbols from an archive file to the global hash table.
+   This looks through the undefined symbols, looks each one up in the
+   archive hash table, and adds any associated object file.  We do not
+   use _bfd_generic_link_add_archive_symbols because ECOFF archives
+   already have a hash table, so there is no reason to construct
+   another one.  */
+
+static boolean
+ecoff_link_add_archive_symbols (abfd, info)
+     bfd *abfd;
+     struct bfd_link_info *info;
+{
+  const bfd_byte *raw_armap;
+  struct bfd_link_hash_entry **pundef;
+  unsigned int armap_count;
+  unsigned int armap_log;
+  unsigned int i;
+  const bfd_byte *hashtable;
+  const char *stringbase;
+
+  if (! bfd_has_map (abfd))
+    {
+      bfd_error = no_symbols;
+      return false;
+    }
+
+  /* If we don't have any raw data for this archive, as can happen on
+     Irix 4.0.5F, we call the generic routine.
+     FIXME: We should be more clever about this, since someday tdata
+     may get to something for a generic archive.  */
+  raw_armap = (const bfd_byte *) bfd_ardata (abfd)->tdata;
+  if (raw_armap == (bfd_byte *) NULL)
+    return (_bfd_generic_link_add_archive_symbols
+           (abfd, info, ecoff_link_check_archive_element));
+
+  armap_count = bfd_h_get_32 (abfd, raw_armap);
+
+  armap_log = 0;
+  for (i = 1; i < armap_count; i <<= 1)
+    armap_log++;
+  BFD_ASSERT (i == armap_count);
+
+  hashtable = raw_armap + 4;
+  stringbase = (const char *) raw_armap + armap_count * 8 + 8;
+
+  /* Look through the list of undefined symbols.  */
+  pundef = &info->hash->undefs;
+  while (*pundef != (struct bfd_link_hash_entry *) NULL)
+    {
+      struct bfd_link_hash_entry *h;
+      unsigned int hash, rehash;
+      unsigned int file_offset;
+      const char *name;
+      bfd *element;
+
+      h = *pundef;
+
+      /* When a symbol is defined, it is not necessarily removed from
+        the list.  */
+      if (h->type != bfd_link_hash_undefined
+         && h->type != bfd_link_hash_common)
+       {
+         /* Remove this entry from the list, for general cleanliness
+            and because we are going to look through the list again
+            if we search any more libraries.  We can't remove the
+            entry if it is the tail, because that would lose any
+            entries we add to the list later on.  */
+         if (*pundef != info->hash->undefs_tail)
+           *pundef = (*pundef)->next;
+         else
+           pundef = &(*pundef)->next;
+         continue;
+       }
+
+      /* Native ECOFF linkers do not pull in archive elements merely
+        to satisfy common definitions, so neither do we.  We leave
+        them on the list, though, in case we are linking against some
+        other object format.  */
+      if (h->type != bfd_link_hash_undefined)
+       {
+         pundef = &(*pundef)->next;
+         continue;
+       }
+
+      /* Look for this symbol in the archive hash table.  */
+      hash = ecoff_armap_hash (h->root.string, &rehash, armap_count,
+                              armap_log);
+
+      file_offset = bfd_h_get_32 (abfd, hashtable + (hash * 8) + 4);
+      if (file_offset == 0)
+       {
+         /* Nothing in this slot.  */
+         pundef = &(*pundef)->next;
+         continue;
+       }
+
+      name = stringbase + bfd_h_get_32 (abfd, hashtable + (hash * 8));
+      if (name[0] != h->root.string[0]
+         || strcmp (name, h->root.string) != 0)
+       {
+         unsigned int srch;
+         boolean found;
+
+         /* That was the wrong symbol.  Try rehashing.  */
+         found = false;
+         for (srch = (hash + rehash) & (armap_count - 1);
+              srch != hash;
+              srch = (srch + rehash) & (armap_count - 1))
+           {
+             file_offset = bfd_h_get_32 (abfd, hashtable + (srch * 8) + 4);
+             if (file_offset == 0)
+               break;
+             name = stringbase + bfd_h_get_32 (abfd, hashtable + (srch * 8));
+             if (name[0] == h->root.string[0]
+                 && strcmp (name, h->root.string) == 0)
+               {
+                 found = true;
+                 break;
+               }
+           }
+
+         if (! found)
+           {
+             pundef = &(*pundef)->next;
+             continue;
+           }
+
+         hash = srch;
+       }
+
+      element = _bfd_get_elt_at_filepos (abfd, file_offset);
+      if (element == (bfd *) NULL)
+       return false;
+
+      if (! bfd_check_format (element, bfd_object))
+       return false;
+
+      /* Unlike the generic linker, we know that this element provides
+        a definition for an undefined symbol and we know that we want
+        to include it.  We don't need to check anything.  */
+      if (! (*info->callbacks->add_archive_element) (info, element, name))
+       return false;
+      if (! ecoff_link_add_object_symbols (element, info))
+       return false;
+
+      pundef = &(*pundef)->next;
+    }
+
+  return true;
+}
+
+/* This is called if we used _bfd_generic_link_add_archive_symbols
+   because we were not dealing with an ECOFF archive.  */
+
+static boolean
+ecoff_link_check_archive_element (abfd, info, pneeded)
+     bfd *abfd;
+     struct bfd_link_info *info;
+     boolean *pneeded;
+{
+  const struct ecoff_backend_data * const backend = ecoff_backend (abfd);
+  void (* const swap_ext_in) PARAMS ((bfd *, PTR, EXTR *))
+    = backend->debug_swap.swap_ext_in;
+  HDRR *symhdr;
+  bfd_size_type external_ext_size;
+  PTR external_ext;
+  size_t esize;
+  char *ssext;
+  char *ext_ptr;
+  char *ext_end;
+
+  *pneeded = false;
+
+  if (! ecoff_slurp_symbolic_header (abfd))
+    return false;
+
+  /* If there are no symbols, we don't want it.  */
+  if (bfd_get_symcount (abfd) == 0)
+    return true;
+
+  symhdr = &ecoff_data (abfd)->debug_info.symbolic_header;
+
+  /* Read in the external symbols and external strings.  */
+  external_ext_size = backend->debug_swap.external_ext_size;
+  esize = symhdr->iextMax * external_ext_size;
+  external_ext = (PTR) alloca (esize);
+  if (bfd_seek (abfd, symhdr->cbExtOffset, SEEK_SET) != 0
+      || bfd_read (external_ext, 1, esize, abfd) != esize)
+    return false;
+
+  ssext = (char *) alloca (symhdr->issExtMax);
+  if (bfd_seek (abfd, symhdr->cbSsExtOffset, SEEK_SET) != 0
+      || bfd_read (ssext, 1, symhdr->issExtMax, abfd) != symhdr->issExtMax)
+    return false;
+
+  /* Look through the external symbols to see if they define some
+     symbol that is currently undefined.  */
+  ext_ptr = (char *) external_ext;
+  ext_end = ext_ptr + esize;
+  for (; ext_ptr < ext_end; ext_ptr += external_ext_size)
+    {
+      EXTR esym;
+      boolean def;
+      const char *name;
+      struct bfd_link_hash_entry *h;
+
+      (*swap_ext_in) (abfd, (PTR) ext_ptr, &esym);
+
+      /* See if this symbol defines something.  */
+      if (esym.asym.st != stGlobal
+         && esym.asym.st != stLabel
+         && esym.asym.st != stProc)
+       continue;
+
+      switch (esym.asym.sc)
+       {
+       case scText:
+       case scData:
+       case scBss:
+       case scAbs:
+       case scSData:
+       case scSBss:
+       case scRData:
+       case scCommon:
+       case scSCommon:
+       case scInit:
+       case scFini:
+         def = true;
+         break;
+       default:
+         def = false;
+         break;
+       }
+
+      if (! def)
+       continue;
+
+      name = ssext + esym.asym.iss;
+      h = bfd_link_hash_lookup (info->hash, name, false, false, true);
+
+      /* Unlike the generic linker, we do not pull in elements because
+        of common symbols.  */
+      if (h == (struct bfd_link_hash_entry *) NULL
+         || h->type != bfd_link_hash_undefined)
+       continue;
+
+      /* Include this element.  */
+      if (! (*info->callbacks->add_archive_element) (info, abfd, name))
+       return false;
+      if (! ecoff_link_add_externals (abfd, info, external_ext, ssext))
+       return false;
+
+      *pneeded = true;
+      return true;
+    }
+
+  return true;
+}
+
+/* Add symbols from an ECOFF object file to the global linker hash
+   table.  */
+
+static boolean
+ecoff_link_add_object_symbols (abfd, info)
+     bfd *abfd;
+     struct bfd_link_info *info;
+{
+  HDRR *symhdr;
+  bfd_size_type external_ext_size;
+  PTR external_ext;
+  size_t esize;
+  char *ssext;
+
+  if (! ecoff_slurp_symbolic_header (abfd))
+    return false;
+
+  /* If there are no symbols, we don't want it.  */
+  if (bfd_get_symcount (abfd) == 0)
+    return true;
+
+  symhdr = &ecoff_data (abfd)->debug_info.symbolic_header;
+
+  /* Read in the external symbols and external strings.  */
+  external_ext_size = ecoff_backend (abfd)->debug_swap.external_ext_size;
+  esize = symhdr->iextMax * external_ext_size;
+  external_ext = (PTR) alloca (esize);
+  if (bfd_seek (abfd, symhdr->cbExtOffset, SEEK_SET) != 0
+      || bfd_read (external_ext, 1, esize, abfd) != esize)
+    return false;
+
+  ssext = (char *) alloca (symhdr->issExtMax);
+  if (bfd_seek (abfd, symhdr->cbSsExtOffset, SEEK_SET) != 0
+      || bfd_read (ssext, 1, symhdr->issExtMax, abfd) != symhdr->issExtMax)
+    return false;
+
+  return ecoff_link_add_externals (abfd, info, external_ext, ssext);
+}
+
+/* Add the external symbols of an object file to the global linker
+   hash table.  The external symbols and strings we are passed are
+   just allocated on the stack, and will be discarded.  We must
+   explicitly save any information we may need later on in the link.
+   We do not want to read the external symbol information again.  */
+
+static boolean
+ecoff_link_add_externals (abfd, info, external_ext, ssext)
+     bfd *abfd;
+     struct bfd_link_info *info;
+     PTR external_ext;
+     char *ssext;
+{
+  const struct ecoff_backend_data * const backend = ecoff_backend (abfd);
+  void (* const swap_ext_in) PARAMS ((bfd *, PTR, EXTR *))
+    = backend->debug_swap.swap_ext_in;
+  bfd_size_type external_ext_size = backend->debug_swap.external_ext_size;
+  unsigned long ext_count;
+  struct ecoff_link_hash_entry **sym_hash;
+  char *ext_ptr;
+  char *ext_end;
+
+  ext_count = ecoff_data (abfd)->debug_info.symbolic_header.iextMax;
+
+  sym_hash = ((struct ecoff_link_hash_entry **)
+             bfd_alloc (abfd,
+                        ext_count * sizeof (struct bfd_link_hash_entry *)));
+  ecoff_data (abfd)->sym_hashes = sym_hash;
+
+  ext_ptr = (char *) external_ext;
+  ext_end = ext_ptr + ext_count * external_ext_size;
+  for (; ext_ptr < ext_end; ext_ptr += external_ext_size, sym_hash++)
+    {
+      EXTR esym;
+      boolean skip;
+      bfd_vma value;
+      asection *section;
+      const char *name;
+      struct ecoff_link_hash_entry *h;
+
+      *sym_hash = NULL;
+
+      (*swap_ext_in) (abfd, (PTR) ext_ptr, &esym);
+
+      /* Skip debugging symbols.  */
+      skip = false;
+      switch (esym.asym.st)
+       {
+       case stGlobal:
+       case stStatic:
+       case stLabel:
+       case stProc:
+       case stStaticProc:
+         break;
+       default:
+         skip = true;
+         break;
+       }
+
+      if (skip)
+       continue;
+
+      /* Get the information for this symbol.  */
+      value = esym.asym.value;
+      switch (esym.asym.sc)
+       {
+       default:
+       case scNil:
+       case scRegister:
+       case scCdbLocal:
+       case scBits:
+       case scCdbSystem:
+       case scRegImage:
+       case scInfo:
+       case scUserStruct:
+       case scVar:
+       case scVarRegister:
+       case scVariant:
+       case scBasedVar:
+       case scXData:
+       case scPData:
+         section = NULL;
+         break;
+       case scText:
+         section = bfd_make_section_old_way (abfd, ".text");
+         value -= section->vma;
+         break;
+       case scData:
+         section = bfd_make_section_old_way (abfd, ".data");
+         value -= section->vma;
+         break;
+       case scBss:
+         section = bfd_make_section_old_way (abfd, ".bss");
+         value -= section->vma;
+         break;
+       case scAbs:
+         section = &bfd_abs_section;
+         break;
+       case scUndefined:
+         section = &bfd_und_section;
+         break;
+       case scSData:
+         section = bfd_make_section_old_way (abfd, ".sdata");
+         value -= section->vma;
+         break;
+       case scSBss:
+         section = bfd_make_section_old_way (abfd, ".sbss");
+         value -= section->vma;
+         break;
+       case scRData:
+         section = bfd_make_section_old_way (abfd, ".rdata");
+         value -= section->vma;
+         break;
+       case scCommon:
+         if (value > ecoff_data (abfd)->gp_size)
+           {
+             section = &bfd_com_section;
+             break;
+           }
+         /* Fall through.  */
+       case scSCommon:
+         if (ecoff_scom_section.name == NULL)
+           {
+             /* Initialize the small common section.  */
+             ecoff_scom_section.name = SCOMMON;
+             ecoff_scom_section.flags = SEC_IS_COMMON;
+             ecoff_scom_section.output_section = &ecoff_scom_section;
+             ecoff_scom_section.symbol = &ecoff_scom_symbol;
+             ecoff_scom_section.symbol_ptr_ptr = &ecoff_scom_symbol_ptr;
+             ecoff_scom_symbol.name = SCOMMON;
+             ecoff_scom_symbol.flags = BSF_SECTION_SYM;
+             ecoff_scom_symbol.section = &ecoff_scom_section;
+             ecoff_scom_symbol_ptr = &ecoff_scom_symbol;
+           }
+         section = &ecoff_scom_section;
+         break;
+       case scSUndefined:
+         section = &bfd_und_section;
+         break;
+       case scInit:
+         section = bfd_make_section_old_way (abfd, ".init");
+         value -= section->vma;
+         break;
+       case scFini:
+         section = bfd_make_section_old_way (abfd, ".fini");
+         value -= section->vma;
+         break;
+       }
+
+      if (section == (asection *) NULL)
+       continue;
+
+      name = ssext + esym.asym.iss;
+
+      if (! (_bfd_generic_link_add_one_symbol
+            (info, abfd, name, BSF_GLOBAL, section, value,
+             (const char *) NULL, true, true, backend->constructor_bitsize,
+             (struct bfd_link_hash_entry **) &h)))
+       return false;
+
+      *sym_hash = h;
+
+      /* If we are building an ECOFF hash table, save the external
+        symbol information.  */
+      if (info->hash->creator->flavour == bfd_get_flavour (abfd))
+       {
+         if (h->abfd == (bfd *) NULL
+             || (section != &bfd_und_section
+                 && (! bfd_is_com_section (section)
+                     || h->root.type != bfd_link_hash_defined)))
+           {
+             h->abfd = abfd;
+             h->esym = esym;
+           }
+       }
+    }
+
+  return true;
+}
+\f
+/* ECOFF final link routines.  */
+
+static boolean ecoff_final_link_debug_accumulate
+  PARAMS ((bfd *output_bfd, bfd *input_bfd, struct bfd_link_info *));
+static boolean ecoff_link_write_external
+  PARAMS ((struct ecoff_link_hash_entry *, PTR));
+static boolean ecoff_indirect_link_order
+  PARAMS ((bfd *, struct bfd_link_info *, asection *,
+          struct bfd_link_order *));
+
+/* ECOFF final link routine.  This looks through all the input BFDs
+   and gathers together all the debugging information, and then
+   processes all the link order information.  This may cause it to
+   close and reopen some input BFDs; I'll see how bad this is.  */
+
+boolean
+ecoff_bfd_final_link (abfd, info)
+     bfd *abfd;
+     struct bfd_link_info *info;
+{
+  const struct ecoff_backend_data * const backend = ecoff_backend (abfd);
+  struct ecoff_debug_info * const debug = &ecoff_data (abfd)->debug_info;
+  HDRR *symhdr;
+  register bfd *input_bfd;
+  asection *o;
+  struct bfd_link_order *p;
+
+  /* We accumulate the debugging information counts in the symbolic
+     header.  */
+  symhdr = &debug->symbolic_header;
+  symhdr->magic = backend->debug_swap.sym_magic;
+  /* FIXME: What should the version stamp be?  */
+  symhdr->vstamp = 0;
+  symhdr->ilineMax = 0;
+  symhdr->cbLine = 0;
+  symhdr->idnMax = 0;
+  symhdr->ipdMax = 0;
+  symhdr->isymMax = 0;
+  symhdr->ioptMax = 0;
+  symhdr->iauxMax = 0;
+  symhdr->issMax = 0;
+  symhdr->issExtMax = 0;
+  symhdr->ifdMax = 0;
+  symhdr->crfd = 0;
+  symhdr->iextMax = 0;
+
+  /* We accumulate the debugging information itself in the debug_info
+     structure.  */
+  debug->line = debug->line_end = NULL;
+  debug->external_dnr = debug->external_dnr_end = NULL;
+  debug->external_pdr = debug->external_pdr_end = NULL;
+  debug->external_sym = debug->external_sym_end = NULL;
+  debug->external_opt = debug->external_opt_end = NULL;
+  debug->external_aux = debug->external_aux_end = NULL;
+  debug->ss = debug->ss_end = NULL;
+  debug->ssext = debug->ssext_end = NULL;
+  debug->external_fdr = debug->external_fdr_end = NULL;
+  debug->external_rfd = debug->external_rfd_end = NULL;
+  debug->external_ext = debug->external_ext_end = NULL;
+
+  /* Accumulate the debugging symbols from each input BFD.  */
+  for (input_bfd = info->input_bfds;
+       input_bfd != (bfd *) NULL;
+       input_bfd = input_bfd->link_next)
+    {
+      boolean ret;
+
+      /* If we might be using the C based alloca function, dump memory
+        allocated by ecoff_final_link_debug_accumulate.  */
+#ifndef __GNUC__
+#ifndef alloca
+      (void) alloca (0);
+#endif
+#endif
+
+      if (bfd_get_flavour (input_bfd) == bfd_target_ecoff_flavour)
+       ret = ecoff_final_link_debug_accumulate (abfd, input_bfd, info);
+      else
+       ret = bfd_ecoff_debug_link_other (abfd,
+                                         debug,
+                                         &backend->debug_swap,
+                                         input_bfd);
+      if (! ret)
+       return false;
+
+      /* Combine the register masks.  */
+      ecoff_data (abfd)->gprmask |= ecoff_data (input_bfd)->gprmask;
+      ecoff_data (abfd)->fprmask |= ecoff_data (input_bfd)->fprmask;
+      ecoff_data (abfd)->cprmask[0] |= ecoff_data (input_bfd)->cprmask[0];
+      ecoff_data (abfd)->cprmask[1] |= ecoff_data (input_bfd)->cprmask[1];
+      ecoff_data (abfd)->cprmask[2] |= ecoff_data (input_bfd)->cprmask[2];
+      ecoff_data (abfd)->cprmask[3] |= ecoff_data (input_bfd)->cprmask[3];
+    }
+
+  /* Write out the external symbols.  */
+  ecoff_link_hash_traverse (ecoff_hash_table (info),
+                           ecoff_link_write_external,
+                           (PTR) abfd);
+
+  if (info->relocateable)
+    {
+      /* We need to make a pass over the link_orders to count up the
+        number of relocations we will need to output, so that we know
+        how much space they will take up.  */
+      for (o = abfd->sections; o != (asection *) NULL; o = o->next)
+       {
+         o->reloc_count = 0;
+         for (p = o->link_order_head;
+              p != (struct bfd_link_order *) NULL;
+              p = p->next)
+           if (p->type == bfd_indirect_link_order)
+             o->reloc_count += p->u.indirect.section->reloc_count;
+       }
+
+      ecoff_compute_reloc_file_positions (abfd);
+
+      /* Now reset the reloc_count field of the sections in the output
+        BFD to 0, so that we can use them to keep track of how many
+        relocs we have output thus far.  */
+      for (o = abfd->sections; o != (asection *) NULL; o = o->next)
+       o->reloc_count = 0;
+    }
+
+  /* Get a value for the GP register.  */
+  if (ecoff_data (abfd)->gp == 0)
+    {
+      struct bfd_link_hash_entry *h;
+
+      h = bfd_link_hash_lookup (info->hash, "_gp", false, false, true);
+      if (h != (struct bfd_link_hash_entry *) NULL
+         && h->type == bfd_link_hash_defined)
+       ecoff_data (abfd)->gp = (h->u.def.value
+                                + h->u.def.section->output_section->vma
+                                + h->u.def.section->output_offset);
+      else if (info->relocateable)
+       {
+         bfd_vma lo;
+
+         /* Make up a value.  */
+         lo = (bfd_vma) -1;
+         for (o = abfd->sections; o != (asection *) NULL; o = o->next)
+           {
+             if (o->vma < lo
+                 && (strcmp (o->name, _SBSS) == 0
+                     || strcmp (o->name, _SDATA) == 0
+                     || strcmp (o->name, _LIT4) == 0
+                     || strcmp (o->name, _LIT8) == 0
+                     || strcmp (o->name, _LITA) == 0))
+               lo = o->vma;
+           }
+         ecoff_data (abfd)->gp = lo + 0x8000;
+       }
+      else
+       {
+         /* If the relocate_section function needs to do a reloc
+            involving the GP value, it should make a reloc_dangerous
+            callback to warn that GP is not defined.  */
+       }
+    }
+
+  for (o = abfd->sections; o != (asection *) NULL; o = o->next)
+    {
+      /* Ignore any link_orders for the .reginfo section, which does
+        not really exist.  */
+      if (strcmp (o->name, REGINFO) == 0)
+       continue;
+      
+      for (p = o->link_order_head;
+          p != (struct bfd_link_order *) NULL;
+          p = p->next)
+       {
+         /* If we might be using the C based alloca function, we need
+            to dump the memory allocated by the function
+            ecoff_indirect_link_order.  */
+#ifndef __GNUC__
+#ifndef alloca
+         (void) alloca (0);
+#endif
+#endif
+         if (p->type == bfd_indirect_link_order
+             && (bfd_get_flavour (p->u.indirect.section->owner)
+                 == bfd_target_ecoff_flavour))
+           {
+             if (! ecoff_indirect_link_order (abfd, info, o, p))
+               return false;
+           }
+         else
+           {
+             if (! _bfd_default_link_order (abfd, info, o, p))
+               return false;
+           }
+       }
+    }
+
+  bfd_get_symcount (abfd) = symhdr->iextMax + symhdr->isymMax;
+
+  return true;
+}
+
+/* Accumulate the debugging information for an input BFD into the
+   output BFD.  This must read in the symbolic information of the
+   input BFD.  */
+
+static boolean
+ecoff_final_link_debug_accumulate (output_bfd, input_bfd, info)
+     bfd *output_bfd;
+     bfd *input_bfd;
+     struct bfd_link_info *info;
+{
+  struct ecoff_debug_info * const debug = &ecoff_data (input_bfd)->debug_info;
+  const struct ecoff_debug_swap * const swap =
+    &ecoff_backend (input_bfd)->debug_swap;
+  HDRR *symhdr = &debug->symbolic_header;
+  boolean ret;
+
+#define READ(ptr, offset, count, size, type)                           \
+  if (symhdr->count == 0)                                              \
+    debug->ptr = NULL;                                                 \
+  else                                                                 \
+    {                                                                  \
+      debug->ptr = (type) alloca (size * symhdr->count);               \
+      if ((bfd_seek (input_bfd, (file_ptr) symhdr->offset, SEEK_SET)   \
+          != 0)                                                        \
+         || (bfd_read (debug->ptr, size, symhdr->count,                \
+                       input_bfd) != size * symhdr->count))            \
+       return false;                                                   \
+    }
+
+  READ (line, cbLineOffset, cbLine, sizeof (unsigned char), unsigned char *);
+  READ (external_dnr, cbDnOffset, idnMax, swap->external_dnr_size, PTR);
+  READ (external_pdr, cbPdOffset, ipdMax, swap->external_pdr_size, PTR);
+  READ (external_sym, cbSymOffset, isymMax, swap->external_sym_size, PTR);
+  READ (external_opt, cbOptOffset, ioptMax, swap->external_opt_size, PTR);
+  READ (external_aux, cbAuxOffset, iauxMax, sizeof (union aux_ext),
+       union aux_ext *);
+  READ (ss, cbSsOffset, issMax, sizeof (char), char *);
+  READ (external_fdr, cbFdOffset, ifdMax, swap->external_fdr_size, PTR);
+  READ (external_rfd, cbRfdOffset, crfd, swap->external_rfd_size, PTR);
+#undef READ
+
+  /* We do not read the external strings or the external symbols.  */
+
+  ret = (bfd_ecoff_debug_accumulate
+        (output_bfd, &ecoff_data (output_bfd)->debug_info,
+         &ecoff_backend (output_bfd)->debug_swap,
+         input_bfd, debug, swap, info->relocateable));
+
+  /* Make sure we don't accidentally follow one of these pointers on
+     to the stack.  */
+  debug->line = NULL;
+  debug->external_dnr = NULL;
+  debug->external_pdr = NULL;
+  debug->external_sym = NULL;
+  debug->external_opt = NULL;
+  debug->external_aux = NULL;
+  debug->ss = NULL;
+  debug->external_fdr = NULL;
+  debug->external_rfd = NULL;
+
+  return ret;
+}
+
+/* Put out information for an external symbol.  These come only from
+   the hash table.  */
+
+static boolean
+ecoff_link_write_external (h, data)
+     struct ecoff_link_hash_entry *h;
+     PTR data;
+{
+  bfd *output_bfd = (bfd *) data;
+
+  /* FIXME: We should check if this symbol is being stripped.  */
+
+  if (h->root.written)
+    return true;
+
+  if (h->abfd == (bfd *) NULL)
+    {
+      h->esym.jmptbl = 0;
+      h->esym.cobol_main = 0;
+      h->esym.weakext = 0;
+      h->esym.reserved = 0;
+      h->esym.ifd = ifdNil;
+      h->esym.asym.value = 0;
+      /* FIXME: we can do better than this for st and sc.  */
+      h->esym.asym.st = stGlobal;
+      h->esym.asym.sc = scAbs;
+      h->esym.asym.reserved = 0;
+      h->esym.asym.index = indexNil;
+    }
+  else
+    {
+      /* Adjust the FDR index for the symbol by that used for the
+        input BFD.  */
+      h->esym.ifd += ecoff_data (h->abfd)->debug_info.ifdbase;
+    }
+
+  switch (h->root.type)
+    {
+    default:
+    case bfd_link_hash_new:
+      abort ();
+    case bfd_link_hash_undefined:
+    case bfd_link_hash_weak:
+      if (h->esym.asym.st != scUndefined
+         && h->esym.asym.st != scSUndefined)
+       h->esym.asym.st = scUndefined;
+      break;
+    case bfd_link_hash_defined:
+      if (h->esym.asym.sc == scUndefined
+         || h->esym.asym.sc == scSUndefined)
+       h->esym.asym.sc = scAbs;
+      else if (h->esym.asym.sc == scCommon)
+       h->esym.asym.sc = scBss;
+      else if (h->esym.asym.sc == scSCommon)
+       h->esym.asym.sc = scSBss;
+      h->esym.asym.value = (h->root.u.def.value
+                           + h->root.u.def.section->output_section->vma
+                           + h->root.u.def.section->output_offset);
+      break;
+    case bfd_link_hash_common:
+      if (h->esym.asym.sc != scCommon
+         && h->esym.asym.sc != scSCommon)
+       h->esym.asym.sc = scCommon;
+      h->esym.asym.value = h->root.u.c.size;
+      break;
+    case bfd_link_hash_indirect:
+    case bfd_link_hash_warning:
+      /* FIXME: Ignore these for now.  The circumstances under which
+        they should be written out are not clear to me.  */
+      return true;
+    }
+
+  /* bfd_ecoff_debug_one_external uses iextMax to keep track of the
+     symbol number.  */
+  h->indx = ecoff_data (output_bfd)->debug_info.symbolic_header.iextMax;
+  h->root.written = true;
+
+  return (bfd_ecoff_debug_one_external
+         (output_bfd, &ecoff_data (output_bfd)->debug_info,
+          &ecoff_backend (output_bfd)->debug_swap, h->root.root.string,
+          &h->esym));
+}
+
+/* Relocate and write an ECOFF section into an ECOFF output file.  */
+
+static boolean
+ecoff_indirect_link_order (output_bfd, info, output_section, link_order)
+     bfd *output_bfd;
+     struct bfd_link_info *info;
+     asection *output_section;
+     struct bfd_link_order *link_order;
+{
+  asection *input_section;
+  bfd *input_bfd;
+  bfd_size_type input_size;
+  bfd_byte *contents;
+  bfd_size_type external_reloc_size;
+  bfd_size_type external_relocs_size;
+  PTR external_relocs;
+
+  BFD_ASSERT ((output_section->flags & SEC_HAS_CONTENTS) != 0);
+
+  if (link_order->size == 0)
+    return true;
+
+  input_section = link_order->u.indirect.section;
+  input_bfd = input_section->owner;
+
+  BFD_ASSERT (input_section->output_section == output_section);
+  BFD_ASSERT (input_section->output_offset == link_order->offset);
+  BFD_ASSERT (bfd_section_size (input_bfd, input_section) == link_order->size);
+
+  /* Get the section contents.  */
+  input_size = bfd_section_size (input_bfd, input_section);
+  contents = (bfd_byte *) alloca (input_size);
+  if (! bfd_get_section_contents (input_bfd, input_section, (PTR) contents,
+                                 (file_ptr) 0, input_size))
+    return false;
+
+  /* Get the relocs.  */
+  external_reloc_size = ecoff_backend (input_bfd)->external_reloc_size;
+  external_relocs_size = external_reloc_size * input_section->reloc_count;
+  external_relocs = (PTR) alloca (external_relocs_size);
+  if (bfd_seek (input_bfd, input_section->rel_filepos, SEEK_SET) != 0
+      || (bfd_read (external_relocs, 1, external_relocs_size, input_bfd)
+         != external_relocs_size))
+    return false;
+
+  /* Relocate the section contents.  */
+  if (! ((*ecoff_backend (input_bfd)->relocate_section)
+        (output_bfd, info, input_bfd, input_section, contents,
+         external_relocs)))
+    return false;
+
+  /* Write out the relocated section.  */
+  if (! bfd_set_section_contents (output_bfd,
+                                 output_section,
+                                 (PTR) contents,
+                                 input_section->output_offset,
+                                 input_size))
+    return false;
+
+  /* If we are producing relocateable output, the relocs were
+     modified, and we write them out now.  We use the reloc_count
+     field of output_section to keep track of the number of relocs we
+     have output so far.  */
+  if (info->relocateable)
+    {
+      if (bfd_seek (output_bfd,
+                   (output_section->rel_filepos +
+                    output_section->reloc_count * external_reloc_size),
+                   SEEK_SET) != 0
+         || (bfd_write (external_relocs, 1, external_relocs_size, output_bfd)
+             != external_relocs_size))
+       return false;
+      output_section->reloc_count += input_section->reloc_count;
+    }
+
+  return true;
+}
index 8882bac..ac9de9f 100644 (file)
@@ -27,7 +27,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "coff/ecoff.h"
 \f
 static boolean ecoff_add_bytes PARAMS ((char **buf, char **bufend,
-                                       bfd_size_type need));
+                                       size_t need));
 static bfd_size_type ecoff_add_string PARAMS ((struct ecoff_debug_info *,
                                               FDR *fdr, const char *string));
 static void ecoff_align_debug PARAMS ((bfd *abfd,
@@ -43,10 +43,10 @@ static boolean
 ecoff_add_bytes (buf, bufend, need)
      char **buf;
      char **bufend;
-     bfd_size_type need;
+     size_t need;
 {
-  bfd_size_type have;
-  bfd_size_type want;
+  size_t have;
+  size_t want;
   char *newbuf;
 
   have = *bufend - *buf;
@@ -78,6 +78,7 @@ ecoff_add_bytes (buf, bufend, need)
    pointed to by the OUTPUT_DEBUG argument.  OUTPUT_SWAP and
    INPUT_SWAP point to the swapping information needed.  */
 
+/*ARGSUSED*/
 boolean
 bfd_ecoff_debug_accumulate (output_bfd, output_debug, output_swap,
                            input_bfd, input_debug, input_swap,
@@ -223,13 +224,13 @@ bfd_ecoff_debug_accumulate (output_bfd, output_debug, output_swap,
   /* Copy the information that does not need swapping.  */
   memcpy (output_debug->line + output_symhdr->cbLine,
          input_debug->line,
-         input_symhdr->cbLine * sizeof (unsigned char));
+         (size_t) (input_symhdr->cbLine * sizeof (unsigned char)));
   memcpy (output_debug->external_aux + output_symhdr->iauxMax,
          input_debug->external_aux,
-         input_symhdr->iauxMax * sizeof (union aux_ext));
+         (size_t) (input_symhdr->iauxMax * sizeof (union aux_ext)));
   memcpy (output_debug->ss + output_symhdr->issMax,
          input_debug->ss,
-         input_symhdr->issMax * sizeof (char));
+         (size_t) (input_symhdr->issMax * sizeof (char)));
 
   /* Some of the information may need to be swapped.  */
   if (output_bfd->xvec->header_byteorder_big_p
@@ -245,7 +246,8 @@ bfd_ecoff_debug_accumulate (output_bfd, output_debug, output_swap,
        memcpy (((char *) output_debug->external_dnr
                 + output_symhdr->idnMax * output_swap->external_dnr_size),
                input_debug->external_dnr,
-               input_symhdr->idnMax * output_swap->external_dnr_size);
+               ((size_t)
+                (input_symhdr->idnMax * output_swap->external_dnr_size)));
 #endif
       BFD_ASSERT (output_swap->external_pdr_size
                  == input_swap->external_pdr_size);
@@ -253,14 +255,16 @@ bfd_ecoff_debug_accumulate (output_bfd, output_debug, output_swap,
        memcpy (((char *) output_debug->external_pdr
                 + output_symhdr->ipdMax * output_swap->external_pdr_size),
                input_debug->external_pdr,
-               input_symhdr->ipdMax * output_swap->external_pdr_size);
+               ((size_t)
+                (input_symhdr->ipdMax * output_swap->external_pdr_size)));
       BFD_ASSERT (output_swap->external_opt_size
                  == input_swap->external_opt_size);
       if (input_symhdr->ioptMax > 0)
        memcpy (((char *) output_debug->external_opt
                 + output_symhdr->ioptMax * output_swap->external_opt_size),
                input_debug->external_opt,
-               input_symhdr->ioptMax * output_swap->external_opt_size);
+               ((size_t)
+                (input_symhdr->ioptMax * output_swap->external_opt_size)));
     }
   else
     {
@@ -462,7 +466,7 @@ ecoff_add_string (output, fdr, string)
     {
       if (ecoff_add_bytes (&output->ss, &output->ss_end,
                           symhdr->issMax + len + 1) == false)
-       return -1;
+       return (bfd_size_type) -1;
     }
   memcpy (output->ss + symhdr->issMax, string, len + 1);
   ret = fdr->cbSs;
@@ -504,6 +508,8 @@ bfd_ecoff_debug_link_other (output_bfd, output_debug, output_swap, input_bfd)
   fdr.cbSs = 0;
   fdr.rss = ecoff_add_string (output_debug, &fdr,
                              bfd_get_filename (input_bfd));
+  if (fdr.rss == -1)
+    return false;
   fdr.isymBase = output_symhdr->isymMax;
 
   /* Get the local symbols from the input BFD.  */
@@ -529,6 +535,8 @@ bfd_ecoff_debug_link_other (output_bfd, output_debug, output_swap, input_bfd)
       internal_sym.iss = ecoff_add_string (output_debug, &fdr,
                                           (*sym_ptr)->name);
 
+      if (internal_sym.iss == -1)
+       return false;
       if (bfd_is_com_section ((*sym_ptr)->section)
          || (*sym_ptr)->section == &bfd_und_section)
        internal_sym.value = (*sym_ptr)->value;
@@ -596,9 +604,6 @@ bfd_ecoff_debug_externals (abfd, debug, swap, relocateable, get_extr,
      boolean (*get_extr) PARAMS ((asymbol *, EXTR *));
      void (*set_index) PARAMS ((asymbol *, bfd_size_type));
 {
-  const bfd_size_type external_ext_size = swap->external_ext_size;
-  void (* const swap_ext_out) PARAMS ((bfd *, const EXTR *, PTR))
-    = swap->swap_ext_out;
   HDRR * const symhdr = &debug->symbolic_header;
   asymbol **sym_ptr_ptr;
   size_t c;
@@ -628,26 +633,6 @@ bfd_ecoff_debug_externals (abfd, debug, swap, relocateable, get_extr,
            esym.asym.sc = scSBss;
        }
 
-      if (debug->ssext_end - debug->ssext
-         < symhdr->issExtMax + strlen (sym_ptr->name) + 1)
-       {
-         if (ecoff_add_bytes ((char **) &debug->ssext,
-                              (char **) &debug->ssext_end,
-                              symhdr->issExtMax + strlen (sym_ptr->name) + 1)
-             == false)
-           return false;
-       }
-      if ((char *) debug->external_ext_end - (char *) debug->external_ext
-         < (symhdr->iextMax + 1) * external_ext_size)
-       {
-         if (ecoff_add_bytes ((char **) &debug->external_ext,
-                              (char **) &debug->external_ext_end,
-                              (symhdr->iextMax + 1) * external_ext_size)
-             == false)
-           return false;
-       }
-
-      esym.asym.iss = symhdr->issExtMax;
 
       if (bfd_is_com_section (sym_ptr->section)
          || sym_ptr->section == &bfd_und_section)
@@ -657,24 +642,71 @@ bfd_ecoff_debug_externals (abfd, debug, swap, relocateable, get_extr,
                           + sym_ptr->section->output_offset
                           + sym_ptr->section->output_section->vma);
 
-      (*swap_ext_out) (abfd, &esym,
-                      ((char *) debug->external_ext
-                       + symhdr->iextMax * swap->external_ext_size));
-
       if (set_index)
-       (*set_index) (sym_ptr, symhdr->iextMax);
+       (*set_index) (sym_ptr, (bfd_size_type) symhdr->iextMax);
+
+      if (! bfd_ecoff_debug_one_external (abfd, debug, swap,
+                                         sym_ptr->name, &esym))
+       return false;
+    }
 
-      ++symhdr->iextMax;
+  return true;
+}
 
-      strcpy (debug->ssext + symhdr->issExtMax, sym_ptr->name);
-      symhdr->issExtMax += strlen (sym_ptr->name) + 1;
+/* Add a single external symbol to the debugging information.  */
+
+boolean
+bfd_ecoff_debug_one_external (abfd, debug, swap, name, esym)
+     bfd *abfd;
+     struct ecoff_debug_info *debug;
+     const struct ecoff_debug_swap *swap;
+     const char *name;
+     EXTR *esym;
+{
+  const bfd_size_type external_ext_size = swap->external_ext_size;
+  void (* const swap_ext_out) PARAMS ((bfd *, const EXTR *, PTR))
+    = swap->swap_ext_out;
+  HDRR * const symhdr = &debug->symbolic_header;
+  size_t namelen;
+
+  namelen = strlen (name);
+
+  if (debug->ssext_end - debug->ssext
+      < symhdr->issExtMax + namelen + 1)
+    {
+      if (ecoff_add_bytes ((char **) &debug->ssext,
+                          (char **) &debug->ssext_end,
+                          symhdr->issExtMax + namelen + 1)
+         == false)
+       return false;
+    }
+  if ((char *) debug->external_ext_end - (char *) debug->external_ext
+      < (symhdr->iextMax + 1) * external_ext_size)
+    {
+      if (ecoff_add_bytes ((char **) &debug->external_ext,
+                          (char **) &debug->external_ext_end,
+                          (symhdr->iextMax + 1) * external_ext_size)
+         == false)
+       return false;
     }
 
+  esym->asym.iss = symhdr->issExtMax;
+
+  (*swap_ext_out) (abfd, esym,
+                  ((char *) debug->external_ext
+                   + symhdr->iextMax * swap->external_ext_size));
+
+  ++symhdr->iextMax;
+
+  strcpy (debug->ssext + symhdr->issExtMax, name);
+  symhdr->issExtMax += namelen + 1;
+
   return true;
 }
 
 /* Align the ECOFF debugging information.  */
 
+/*ARGSUSED*/
 static void
 ecoff_align_debug (abfd, debug, swap)
      bfd *abfd;
@@ -682,7 +714,8 @@ ecoff_align_debug (abfd, debug, swap)
      const struct ecoff_debug_swap *swap;
 {
   HDRR * const symhdr = &debug->symbolic_header;
-  bfd_size_type debug_align, aux_align, add;
+  bfd_size_type debug_align, aux_align;
+  size_t add;
 
   /* Adjust the counts so that structures are aligned.  The alignment
      of ALLOC_SIZE ensures that we do not have to worry about running
@@ -808,7 +841,7 @@ bfd_ecoff_write_debug (abfd, debug, swap, where)
 
 #define WRITE(ptr, count, size, offset) \
   BFD_ASSERT (symhdr->offset == 0 || bfd_tell (abfd) == symhdr->offset); \
-  if (bfd_write (debug->ptr, size, symhdr->count, abfd) \
+  if (bfd_write ((PTR) debug->ptr, size, symhdr->count, abfd) \
       != size * symhdr->count) \
     return false;