New file to hold ECOFF linking routines used by both ECOFF and MIPS ELF.
authorIan Lance Taylor <ian@airs.com>
Wed, 24 Nov 1993 07:18:44 +0000 (07:18 +0000)
committerIan Lance Taylor <ian@airs.com>
Wed, 24 Nov 1993 07:18:44 +0000 (07:18 +0000)
bfd/.Sanitize
bfd/ecofflink.c [new file with mode: 0644]

index 05520a9..2c17e64 100644 (file)
@@ -100,6 +100,7 @@ demo64.c
 dep-in.sed
 doc
 ecoff.c
+ecofflink.c
 ecoffswap.h
 elf.c
 elf32-gen.c
diff --git a/bfd/ecofflink.c b/bfd/ecofflink.c
new file mode 100644 (file)
index 0000000..8882bac
--- /dev/null
@@ -0,0 +1,829 @@
+/* Routines to link ECOFF debugging information.
+   Copyright 1993 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Cygnus Support, <ian@cygnus.com>.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+#include "coff/internal.h"
+#include "coff/sym.h"
+#include "coff/symconst.h"
+#include "coff/ecoff.h"
+\f
+static boolean ecoff_add_bytes PARAMS ((char **buf, char **bufend,
+                                       bfd_size_type 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,
+                                      struct ecoff_debug_info *debug,
+                                      const struct ecoff_debug_swap *swap));
+\f
+/* The minimum amount of data to allocate.  */
+#define ALLOC_SIZE (4064)
+
+/* Add bytes to a buffer.  Return success.  */
+
+static boolean
+ecoff_add_bytes (buf, bufend, need)
+     char **buf;
+     char **bufend;
+     bfd_size_type need;
+{
+  bfd_size_type have;
+  bfd_size_type want;
+  char *newbuf;
+
+  have = *bufend - *buf;
+  if (have > need)
+    want = ALLOC_SIZE;
+  else
+    {
+      want = need - have;
+      if (want < ALLOC_SIZE)
+       want = ALLOC_SIZE;
+    }
+  if (*buf == NULL)
+    newbuf = (char *) malloc (have + want);
+  else
+    newbuf = (char *) realloc (*buf, have + want);
+  if (newbuf == NULL)
+    {
+      bfd_error = no_memory;
+      return false;
+    }
+  *buf = newbuf;
+  *bufend = *buf + have + want;
+  return true;
+}
+
+/* Accumulate the debugging information from INPUT_BFD into
+   OUTPUT_BFD.  The INPUT_DEBUG argument points to some ECOFF
+   debugging information which we want to link into the information
+   pointed to by the OUTPUT_DEBUG argument.  OUTPUT_SWAP and
+   INPUT_SWAP point to the swapping information needed.  */
+
+boolean
+bfd_ecoff_debug_accumulate (output_bfd, output_debug, output_swap,
+                           input_bfd, input_debug, input_swap,
+                           relocateable)
+     bfd *output_bfd;
+     struct ecoff_debug_info *output_debug;
+     const struct ecoff_debug_swap *output_swap;
+     bfd *input_bfd;
+     struct ecoff_debug_info *input_debug;
+     const struct ecoff_debug_swap *input_swap;
+     boolean relocateable;
+{
+  void (* const swap_sym_in) PARAMS ((bfd *, PTR, SYMR *))
+    = input_swap->swap_sym_in;
+  void (* const swap_sym_out) PARAMS ((bfd *, const SYMR *, PTR))
+    = output_swap->swap_sym_out;
+  void (* const swap_fdr_out) PARAMS ((bfd *, const FDR *, PTR))
+    = output_swap->swap_fdr_out;
+  void (* const swap_rfd_out) PARAMS ((bfd *, const RFDT *, PTR))
+    = output_swap->swap_rfd_out;
+  HDRR *output_symhdr;
+  HDRR *input_symhdr;
+  bfd_vma section_adjust[scMax];
+  asection *sec;
+  char *sym_out;
+  char *lraw_src;
+  char *lraw_end;
+  char *fdr_ptr;
+  char *fdr_end;
+  bfd_size_type fdr_add;
+  char *fdr_out;
+
+  output_symhdr = &output_debug->symbolic_header;
+  input_symhdr = &input_debug->symbolic_header;
+
+  /* Make sure the buffers are large enough.  */
+#define CHECK(start, end, count, size)                         \
+  if ((char *) output_debug->end - (char *) output_debug->start        \
+      < (output_symhdr->count + input_symhdr->count) * size)   \
+    {                                                          \
+      if (! ecoff_add_bytes ((char **) &output_debug->start,   \
+                            (char **) &output_debug->end,      \
+                            ((output_symhdr->count             \
+                              + input_symhdr->count)           \
+                             * size)))                         \
+       return false;                                           \
+    }
+
+  CHECK (line, line_end, cbLine, sizeof (unsigned char));
+#if 0
+  /* I don't think dense number information is needed.  */
+  CHECK (external_dnr, external_dnr_end, idnMax,
+        output_swap->external_dnr_size);
+#endif
+  CHECK (external_pdr, external_pdr_end, ipdMax,
+        output_swap->external_pdr_size);
+  CHECK (external_sym, external_sym_end, isymMax,
+        output_swap->external_sym_size);
+  CHECK (external_opt, external_opt_end, ioptMax,
+        output_swap->external_opt_size);
+  CHECK (external_aux, external_aux_end, iauxMax,
+        sizeof (union aux_ext));
+  CHECK (ss, ss_end, issMax, sizeof (char));
+  CHECK (external_fdr, external_fdr_end, ifdMax,
+        output_swap->external_fdr_size);
+
+  /* The RFD's are special, since we create them if needed.  */
+  {
+    bfd_size_type crfd;
+
+    crfd = input_symhdr->crfd;
+    if (input_symhdr->crfd == 0)
+      input_symhdr->crfd = input_symhdr->ifdMax;
+    CHECK (external_rfd, external_rfd_end, crfd,
+          output_swap->external_rfd_size);
+    input_symhdr->crfd = crfd;
+  }
+
+#undef CHECK
+
+  /* Use section_adjust to hold the value to add to a symbol in a
+     particular section.  */
+  memset ((PTR) section_adjust, 0, sizeof section_adjust);
+
+#define SET(name, indx) \
+  sec = bfd_get_section_by_name (input_bfd, name); \
+  if (sec != NULL) \
+    section_adjust[indx] = (sec->output_section->vma \
+                           + sec->output_offset \
+                           - sec->vma);
+
+  SET (".text", scText);
+  SET (".data", scData);
+  SET (".bss", scBss);
+  SET (".sdata", scSData);
+  SET (".sbss", scSBss);
+  /* scRdata section may be either .rdata or .rodata.  */
+  SET (".rdata", scRData);
+  SET (".rodata", scRData);
+  SET (".init", scInit);
+  SET (".fini", scFini);
+
+#undef SET
+
+  /* Swap in the local symbols, adjust their values, and swap them out
+     again.  The external symbols are handled separately.  */
+  sym_out = ((char *) output_debug->external_sym
+            + output_symhdr->isymMax * output_swap->external_sym_size);
+  lraw_src = (char *) input_debug->external_sym;
+  lraw_end = lraw_src + input_symhdr->isymMax * input_swap->external_sym_size;
+  for (;  lraw_src < lraw_end;  lraw_src += input_swap->external_sym_size)
+    {
+      SYMR internal_sym;
+
+      (*swap_sym_in) (input_bfd, (PTR) lraw_src, &internal_sym);
+
+      BFD_ASSERT (internal_sym.sc != scCommon
+                 && internal_sym.sc != scSCommon);
+
+      /* Adjust the symbol value if appropriate.  */
+      switch (internal_sym.st)
+       {
+       case stNil:
+         if (ECOFF_IS_STAB (&internal_sym))
+           break;
+         /* Fall through.  */
+       case stGlobal:
+       case stStatic:
+       case stLabel:
+       case stProc:
+       case stStaticProc:
+         internal_sym.value += section_adjust[internal_sym.sc];
+         break;
+
+       default:
+         break;
+       }
+
+      (*swap_sym_out) (output_bfd, &internal_sym, sym_out);
+      sym_out += output_swap->external_sym_size;
+    }
+
+  /* Copy the information that does not need swapping.  */
+  memcpy (output_debug->line + output_symhdr->cbLine,
+         input_debug->line,
+         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));
+  memcpy (output_debug->ss + output_symhdr->issMax,
+         input_debug->ss,
+         input_symhdr->issMax * sizeof (char));
+
+  /* Some of the information may need to be swapped.  */
+  if (output_bfd->xvec->header_byteorder_big_p
+      == input_bfd->xvec->header_byteorder_big_p)
+    {
+      /* The two BFD's have the same endianness, so memcpy will
+        suffice.  */
+#if 0
+      /* I don't think dense number information is needed.  */
+      BFD_ASSERT (output_swap->external_dnr_size
+                 == input_swap->external_dnr_size);
+      if (input_symhdr->idnMax > 0)
+       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);
+#endif
+      BFD_ASSERT (output_swap->external_pdr_size
+                 == input_swap->external_pdr_size);
+      if (input_symhdr->ipdMax > 0)
+       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);
+      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);
+    }
+  else
+    {
+      bfd_size_type outsz, insz;
+      char *in;
+      char *end;
+      char *out;
+
+      /* The two BFD's have different endianness, so we must swap
+        everything in and out.  This code would always work, but it
+        would be slow in the normal case.  */
+#if 0
+      /* I don't think dense number information is needed.  */
+      outsz = output_swap->external_dnr_size;
+      insz = input_swap->external_dnr_size;
+      in = (char *) input_debug->external_dnr;
+      end = in + input_symhdr->idnMax * insz;
+      out = ((char *) output_debug->external_dnr
+            + output_symhdr->idnMax * outsz);
+      for (; in < end; in += insz, out += outsz)
+       {
+         DNR dnr;
+
+         (*input_swap->swap_dnr_in) (input_bfd, in, &dnr);
+         (*output_swap->swap_dnr_out) (output_bfd, &dnr, out);
+       }
+#endif
+
+      outsz = output_swap->external_pdr_size;
+      insz = input_swap->external_pdr_size;
+      in = (char *) input_debug->external_pdr;
+      end = in + input_symhdr->ipdMax * insz;
+      out = ((char *) output_debug->external_pdr
+            + output_symhdr->ipdMax * outsz);
+      for (; in < end; in += insz, out += outsz)
+       {
+         PDR pdr;
+
+         (*input_swap->swap_pdr_in) (input_bfd, in, &pdr);
+         (*output_swap->swap_pdr_out) (output_bfd, &pdr, out);
+       }
+
+      outsz = output_swap->external_opt_size;
+      insz = input_swap->external_opt_size;
+      in = (char *) input_debug->external_opt;
+      end = in + input_symhdr->ioptMax * insz;
+      out = ((char *) output_debug->external_opt
+            + output_symhdr->ioptMax * outsz);
+      for (; in < end; in += insz, out += outsz)
+       {
+         OPTR opt;
+
+         (*input_swap->swap_opt_in) (input_bfd, in, &opt);
+         (*output_swap->swap_opt_out) (output_bfd, &opt, out);
+       }
+    }
+
+  /* Set ifdbase so that the external symbols know how to adjust their
+     ifd values.  */
+  input_debug->ifdbase = output_symhdr->ifdMax;
+
+  /* We need to handle the FDR's whether they are swapped or not.  */
+  if (input_debug->fdr != (FDR *) NULL)
+    {
+      fdr_ptr = (char *) input_debug->fdr;
+      fdr_add = sizeof (FDR);
+    }
+  else
+    {
+      fdr_ptr = (char *) input_debug->external_fdr;
+      fdr_add = input_swap->external_fdr_size;
+    }
+  fdr_end = fdr_ptr + input_symhdr->ifdMax * fdr_add;
+  fdr_out = ((char *) output_debug->external_fdr
+            + output_symhdr->ifdMax * output_swap->external_fdr_size);
+  for (;
+       fdr_ptr < fdr_end;
+       fdr_ptr += fdr_add, fdr_out += output_swap->external_fdr_size)
+    {
+      FDR fdr;
+
+      if (input_debug->fdr != (FDR *) NULL)
+       fdr = *(FDR *) fdr_ptr;
+      else
+       (*input_swap->swap_fdr_in) (input_bfd, (PTR) fdr_ptr, &fdr);
+
+      /* FIXME: It is conceivable that this FDR points to the .init or
+        .fini section, in which case this will not do the right
+        thing.  */
+      fdr.adr += section_adjust[scText];
+
+      fdr.issBase += output_symhdr->issMax;
+      fdr.isymBase += output_symhdr->isymMax;
+      fdr.ilineBase += output_symhdr->ilineMax;
+      fdr.ioptBase += output_symhdr->ioptMax;
+      fdr.ipdFirst += output_symhdr->ipdMax;
+      fdr.iauxBase += output_symhdr->iauxMax;
+      fdr.rfdBase += output_symhdr->crfd;
+
+      /* If there are no RFD's, we are going to add some.  We don't
+        want to adjust crfd for this, so that all the FDR's can share
+        the RFD's.  */
+      if (input_symhdr->crfd == 0)
+       fdr.crfd = input_symhdr->ifdMax;
+
+      if (fdr.cbLine != 0)
+       fdr.cbLineOffset += output_symhdr->cbLine;
+
+      (*swap_fdr_out) (output_bfd, &fdr, fdr_out);
+    }
+
+  if (input_symhdr->crfd > 0)
+    {
+      void (* const swap_rfd_in) PARAMS ((bfd *, PTR, RFDT *))
+       = input_swap->swap_rfd_in;
+      bfd_size_type outsz, insz;
+      char *rfd_in;
+      char *rfd_end;
+      char *rfd_out;
+
+      /* Swap and adjust the RFD's.  RFD's are only created by the
+        linker, so this will only be necessary if one of the input
+        files is the result of a partial link.  Presumably all
+        necessary RFD's are present.  */
+      outsz = output_swap->external_rfd_size;
+      insz = input_swap->external_rfd_size;
+      rfd_in = (char *) input_debug->external_rfd;
+      rfd_end = rfd_in + input_symhdr->crfd * insz;
+      rfd_out = ((char *) output_debug->external_rfd
+                + output_symhdr->crfd * outsz);
+      for (;
+          rfd_in < rfd_end;
+          rfd_in += insz, rfd_out += outsz)
+       {
+         RFDT rfd;
+
+         (*swap_rfd_in) (input_bfd, rfd_in, &rfd);
+         rfd += output_symhdr->ifdMax;
+         (*swap_rfd_out) (output_bfd, &rfd, rfd_out);
+       }
+      output_symhdr->crfd += input_symhdr->crfd;
+    }
+  else
+    {
+      bfd_size_type outsz;
+      char *rfd_out;
+      char *rfd_end;
+      RFDT rfd;
+
+      /* Create RFD's.  Some of the debugging information includes
+        relative file indices.  These indices are taken as indices to
+        the RFD table if there is one, or to the global table if
+        there is not.  If we did not create RFD's, we would have to
+        parse and adjust all the debugging information which contains
+        file indices.  */
+      outsz = output_swap->external_rfd_size;
+      rfd = output_symhdr->ifdMax;
+      rfd_out = ((char *) output_debug->external_rfd
+                + output_symhdr->crfd * outsz);
+      rfd_end = (rfd_out + input_symhdr->ifdMax * outsz);
+      for (; rfd_out < rfd_end; rfd_out += outsz, rfd++)
+       (*swap_rfd_out) (output_bfd, &rfd, rfd_out);
+      output_symhdr->crfd += input_symhdr->ifdMax;
+    }
+
+  /* Update the counts.  */
+  output_symhdr->ilineMax += input_symhdr->ilineMax;
+  output_symhdr->cbLine += input_symhdr->cbLine;
+#if 0
+  /* I don't think dense number information is needed.  */
+  output_symhdr->idnMax += input_symhdr->idnMax;
+#endif
+  output_symhdr->ipdMax += input_symhdr->ipdMax;
+  output_symhdr->isymMax += input_symhdr->isymMax;
+  output_symhdr->ioptMax += input_symhdr->ioptMax;
+  output_symhdr->iauxMax += input_symhdr->iauxMax;
+  output_symhdr->issMax += input_symhdr->issMax;
+  output_symhdr->ifdMax += input_symhdr->ifdMax;
+
+  return true;
+}
+
+/* Add a string to the debugging information we are accumulating.
+   Return the offset from the fdr string base.  */
+
+static bfd_size_type
+ecoff_add_string (output, fdr, string)
+     struct ecoff_debug_info *output;
+     FDR *fdr;
+     const char *string;
+{
+  HDRR *symhdr;
+  size_t len;
+  bfd_size_type ret;
+
+  symhdr = &output->symbolic_header;
+  len = strlen (string);
+  if (output->ss_end - output->ss < symhdr->issMax + len + 1)
+    {
+      if (ecoff_add_bytes (&output->ss, &output->ss_end,
+                          symhdr->issMax + len + 1) == false)
+       return -1;
+    }
+  memcpy (output->ss + symhdr->issMax, string, len + 1);
+  ret = fdr->cbSs;
+  symhdr->issMax += len + 1;
+  fdr->cbSs += len + 1;
+  return ret;
+}
+
+/* Add debugging information from a non-ECOFF file.  */
+
+boolean
+bfd_ecoff_debug_link_other (output_bfd, output_debug, output_swap, input_bfd)
+     bfd *output_bfd;
+     struct ecoff_debug_info *output_debug;
+     const struct ecoff_debug_swap *output_swap;
+     bfd *input_bfd;
+{
+  void (* const swap_sym_out) PARAMS ((bfd *, const SYMR *, PTR))
+    = output_swap->swap_sym_out;
+  HDRR *output_symhdr = &output_debug->symbolic_header;
+  FDR fdr;
+  asection *sec;
+  asymbol **symbols;
+  asymbol **sym_ptr;
+  asymbol **sym_end;
+
+  memset (&fdr, 0, sizeof fdr);
+
+  sec = bfd_get_section_by_name (input_bfd, ".text");
+  if (sec != NULL)
+    fdr.adr = sec->output_section->vma + sec->output_offset;
+  else
+    {
+      /* FIXME: What about .init or .fini?  */
+      fdr.adr = 0;
+    }
+
+  fdr.issBase = output_symhdr->issMax;
+  fdr.cbSs = 0;
+  fdr.rss = ecoff_add_string (output_debug, &fdr,
+                             bfd_get_filename (input_bfd));
+  fdr.isymBase = output_symhdr->isymMax;
+
+  /* Get the local symbols from the input BFD.  */
+  symbols = (asymbol **) bfd_alloc (output_bfd,
+                                   get_symtab_upper_bound (input_bfd));
+  if (symbols == (asymbol **) NULL)
+    {
+      bfd_error = no_memory;
+      return false;
+    }
+  sym_end = symbols + bfd_canonicalize_symtab (input_bfd, symbols);
+
+  /* Handle the local symbols.  Any external symbols are handled
+     separately.  */
+  fdr.csym = 0;
+  for (sym_ptr = symbols; sym_ptr != sym_end; sym_ptr++)
+    {
+      SYMR internal_sym;
+
+      if (((*sym_ptr)->flags & BSF_EXPORT) != 0)
+       continue;
+      memset (&internal_sym, 0, sizeof internal_sym);
+      internal_sym.iss = ecoff_add_string (output_debug, &fdr,
+                                          (*sym_ptr)->name);
+
+      if (bfd_is_com_section ((*sym_ptr)->section)
+         || (*sym_ptr)->section == &bfd_und_section)
+       internal_sym.value = (*sym_ptr)->value;
+      else
+       internal_sym.value = ((*sym_ptr)->value
+                             + (*sym_ptr)->section->output_offset
+                             + (*sym_ptr)->section->output_section->vma);
+      internal_sym.st = stNil;
+      internal_sym.sc = scUndefined;
+      internal_sym.index = indexNil;
+
+      if (((char *) output_debug->external_sym_end
+          - (char *) output_debug->external_sym)
+         < (output_symhdr->isymMax + 1) * output_swap->external_sym_size)
+       {
+         if (! ecoff_add_bytes ((char **) &output_debug->external_sym,
+                                (char **) &output_debug->external_sym_end,
+                                ((output_symhdr->isymMax + 1)
+                                 * output_swap->external_sym_size)))
+           return false;
+       }
+
+      (*swap_sym_out) (output_bfd, &internal_sym,
+                      ((char *) output_debug->external_sym
+                       + (output_symhdr->isymMax
+                          * output_swap->external_sym_size)));
+      ++fdr.csym;
+      ++output_symhdr->isymMax;
+    }
+
+  bfd_release (output_bfd, (PTR) symbols);
+
+  if (((char *) output_debug->external_fdr_end
+       - (char *) output_debug->external_fdr)
+      < (output_symhdr->ifdMax + 1) * output_swap->external_fdr_size)
+    {
+      if (! ecoff_add_bytes ((char **) &output_debug->external_fdr,
+                            (char **) &output_debug->external_fdr_end,
+                            ((output_symhdr->ifdMax + 1)
+                             * output_swap->external_fdr_size)))
+       return false;
+    }
+
+  /* Leave everything else in the FDR zeroed out.  This will cause
+     the lang field to be langC.  The fBigendian field will
+     indicate little endian format, but it doesn't matter because
+     it only applies to aux fields and there are none.  */
+  (*output_swap->swap_fdr_out)
+    (output_bfd, &fdr,
+     ((char *) output_debug->external_fdr
+      + output_symhdr->ifdMax * output_swap->external_fdr_size));
+  ++output_symhdr->ifdMax;
+  return true;
+}
+
+/* Set up ECOFF debugging information for the external symbols.  */
+
+boolean
+bfd_ecoff_debug_externals (abfd, debug, swap, relocateable, get_extr,
+                          set_index)
+     bfd *abfd;
+     struct ecoff_debug_info *debug;
+     const struct ecoff_debug_swap *swap;
+     boolean relocateable;
+     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;
+
+  sym_ptr_ptr = bfd_get_outsymbols (abfd);
+  if (sym_ptr_ptr == NULL)
+    return true;
+
+  for (c = bfd_get_symcount (abfd); c > 0; c--, sym_ptr_ptr++)
+    {
+      asymbol *sym_ptr;
+      EXTR esym;
+
+      sym_ptr = *sym_ptr_ptr;
+
+      /* Get the external symbol information.  */
+      if ((*get_extr) (sym_ptr, &esym) == false)
+       continue;
+
+      /* If we're producing an executable, move common symbols into
+        bss.  */
+      if (relocateable == false)
+       {
+         if (esym.asym.sc == scCommon)
+           esym.asym.sc = scBss;
+         else if (esym.asym.sc == scSCommon)
+           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)
+       esym.asym.value = sym_ptr->value;
+      else
+       esym.asym.value = (sym_ptr->value
+                          + 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);
+
+      ++symhdr->iextMax;
+
+      strcpy (debug->ssext + symhdr->issExtMax, sym_ptr->name);
+      symhdr->issExtMax += strlen (sym_ptr->name) + 1;
+    }
+
+  return true;
+}
+
+/* Align the ECOFF debugging information.  */
+
+static void
+ecoff_align_debug (abfd, debug, swap)
+     bfd *abfd;
+     struct ecoff_debug_info *debug;
+     const struct ecoff_debug_swap *swap;
+{
+  HDRR * const symhdr = &debug->symbolic_header;
+  bfd_size_type debug_align, aux_align, add;
+
+  /* Adjust the counts so that structures are aligned.  The alignment
+     of ALLOC_SIZE ensures that we do not have to worry about running
+     off the end of the memory block when doing the memset.  */
+  debug_align = swap->debug_align;
+  aux_align = debug_align / sizeof (union aux_ext);
+
+  add = debug_align - (symhdr->cbLine & (debug_align - 1));
+  if (add != debug_align)
+    {
+      memset (debug->line + symhdr->cbLine, 0, add);
+      symhdr->cbLine += add;
+    }
+
+  add = debug_align - (symhdr->issMax & (debug_align - 1));
+  if (add != debug_align)
+    {
+      memset (debug->ss + symhdr->issMax, 0, add);
+      symhdr->issMax += add;
+    }
+
+  add = debug_align - (symhdr->issExtMax & (debug_align - 1));
+  if (add != debug_align)
+    {
+      memset (debug->ssext + symhdr->issExtMax, 0, add);
+      symhdr->issExtMax += add;
+    }
+
+  add = aux_align - (symhdr->iauxMax & (aux_align - 1));
+  if (add != aux_align)
+    {
+      memset (debug->external_aux + symhdr->iauxMax, 0,
+             add * sizeof (union aux_ext));
+      symhdr->iauxMax += add;
+    }
+}
+
+/* Return the size required by the ECOFF debugging information.  */
+
+bfd_size_type
+bfd_ecoff_debug_size (abfd, debug, swap)
+     bfd *abfd;
+     struct ecoff_debug_info *debug;
+     const struct ecoff_debug_swap *swap;
+{
+  bfd_size_type tot;
+
+  ecoff_align_debug (abfd, debug, swap);
+  tot = swap->external_hdr_size;
+
+#define ADD(count, size) \
+  tot += debug->symbolic_header.count * size
+
+  ADD (cbLine, sizeof (unsigned char));
+  ADD (idnMax, swap->external_dnr_size);
+  ADD (ipdMax, swap->external_pdr_size);
+  ADD (isymMax, swap->external_sym_size);
+  ADD (ioptMax, swap->external_opt_size);
+  ADD (iauxMax, sizeof (union aux_ext));
+  ADD (issMax, sizeof (char));
+  ADD (issExtMax, sizeof (char));
+  ADD (ifdMax, swap->external_fdr_size);
+  ADD (crfd, swap->external_rfd_size);
+  ADD (iextMax, swap->external_ext_size);
+
+#undef ADD
+
+  return tot;
+}
+
+/* Write out the ECOFF debugging information.  This function assumes
+   that the information (the pointers and counts) in *DEBUG have been
+   set correctly.  WHERE is the position in the file to write the
+   information to.  This function fills in the file offsets in the
+   symbolic header.  */
+
+boolean
+bfd_ecoff_write_debug (abfd, debug, swap, where)
+     bfd *abfd;
+     struct ecoff_debug_info *debug;
+     const struct ecoff_debug_swap *swap;
+     file_ptr where;
+{
+  HDRR * const symhdr = &debug->symbolic_header;
+  char *buff;
+
+  ecoff_align_debug (abfd, debug, swap);
+
+  /* Go to the right location in the file.  */
+  if (bfd_seek (abfd, where, SEEK_SET) != 0)
+    return false;
+
+  where += swap->external_hdr_size;
+
+  /* Fill in the file offsets.  */
+#define SET(offset, count, size) \
+  if (symhdr->count == 0) \
+    symhdr->offset = 0; \
+  else \
+    { \
+      symhdr->offset = where; \
+      where += symhdr->count * size; \
+    }
+
+  SET (cbLineOffset, cbLine, sizeof (unsigned char));
+  SET (cbDnOffset, idnMax, swap->external_dnr_size);
+  SET (cbPdOffset, ipdMax, swap->external_pdr_size);
+  SET (cbSymOffset, isymMax, swap->external_sym_size);
+  SET (cbOptOffset, ioptMax, swap->external_opt_size);
+  SET (cbAuxOffset, iauxMax, sizeof (union aux_ext));
+  SET (cbSsOffset, issMax, sizeof (char));
+  SET (cbSsExtOffset, issExtMax, sizeof (char));
+  SET (cbFdOffset, ifdMax, swap->external_fdr_size);
+  SET (cbRfdOffset, crfd, swap->external_rfd_size);
+  SET (cbExtOffset, iextMax, swap->external_ext_size);
+#undef SET
+
+  buff = (PTR) alloca (swap->external_hdr_size);
+  (*swap->swap_hdr_out) (abfd, symhdr, buff);
+  if (bfd_write (buff, 1, swap->external_hdr_size, abfd)
+      != swap->external_hdr_size)
+    return false;
+
+#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) \
+      != size * symhdr->count) \
+    return false;
+
+  WRITE (line, cbLine, sizeof (unsigned char), cbLineOffset);
+  WRITE (external_dnr, idnMax, swap->external_dnr_size, cbDnOffset);
+  WRITE (external_pdr, ipdMax, swap->external_pdr_size, cbPdOffset);
+  WRITE (external_sym, isymMax, swap->external_sym_size, cbSymOffset);
+  WRITE (external_opt, ioptMax, swap->external_opt_size, cbOptOffset);
+  WRITE (external_aux, iauxMax, sizeof (union aux_ext), cbAuxOffset);
+  WRITE (ss, issMax, sizeof (char), cbSsOffset);
+  WRITE (ssext, issExtMax, sizeof (char), cbSsExtOffset);
+  WRITE (external_fdr, ifdMax, swap->external_fdr_size, cbFdOffset);
+  WRITE (external_rfd, crfd, swap->external_rfd_size, cbRfdOffset);
+  WRITE (external_ext, iextMax, swap->external_ext_size, cbExtOffset);
+#undef WRITE
+
+  return true;
+}