* ecoff.c (_bfd_ecoff_new_section_hook): Handle .rconst section.
authorIan Lance Taylor <ian@airs.com>
Wed, 22 Nov 1995 19:01:43 +0000 (19:01 +0000)
committerIan Lance Taylor <ian@airs.com>
Wed, 22 Nov 1995 19:01:43 +0000 (19:01 +0000)
(ecoff_sec_to_styp_flags): Likewise.
(_bfd_ecoff_styp_to_sec_flags): Handle STYP_RCONST.
(ecoff_set_symbol_info): Handle scRConst.
(ecoff_slurp_reloc_table): Handle RELOC_SECTION_RCONST.
(ecoff_compute_section_file_positions): Handle .rconst section.
(_bfd_ecoff_write_object_contents): Likewise.
(ecoff_link_check_archive_element): Handle scRConst.
(ecoff_link_add_externals): Likewise.
(ecoff_link_write_external): Handle .rconst section.
(ecoff_reloc_link_order): Likewise.
* ecofflink.c (bfd_ecoff_debug_accumulate): Handle scRConst.
* coff-alpha.c (alpha_convert_external_reloc): Handle .rconst
section.
(alpha_relocate_section): Handle RELOC_SECTION_RCONST.

bfd/ChangeLog
bfd/coff-alpha.c
bfd/ecoff.c
bfd/ecofflink.c

index 1aed409..823a31e 100644 (file)
@@ -1,5 +1,21 @@
 Wed Nov 22 12:02:09 1995  Ian Lance Taylor  <ian@cygnus.com>
 
+       * ecoff.c (_bfd_ecoff_new_section_hook): Handle .rconst section.
+       (ecoff_sec_to_styp_flags): Likewise.
+       (_bfd_ecoff_styp_to_sec_flags): Handle STYP_RCONST.
+       (ecoff_set_symbol_info): Handle scRConst.
+       (ecoff_slurp_reloc_table): Handle RELOC_SECTION_RCONST.
+       (ecoff_compute_section_file_positions): Handle .rconst section.
+       (_bfd_ecoff_write_object_contents): Likewise.
+       (ecoff_link_check_archive_element): Handle scRConst.
+       (ecoff_link_add_externals): Likewise.
+       (ecoff_link_write_external): Handle .rconst section.
+       (ecoff_reloc_link_order): Likewise.
+       * ecofflink.c (bfd_ecoff_debug_accumulate): Handle scRConst.
+       * coff-alpha.c (alpha_convert_external_reloc): Handle .rconst
+       section.
+       (alpha_relocate_section): Handle RELOC_SECTION_RCONST.
+
        * sunos.c (sunos_scan_dynamic_symbol): Only set written if the
        DEF_DYNAMIC flag is set.
 
index bc140a1..7675c56 100644 (file)
@@ -1309,6 +1309,8 @@ alpha_convert_external_reloc (output_bfd, info, input_bfd, ext_rel, h)
        case 'r':
          if (strcmp (name, ".rdata") == 0)
            r_symndx = RELOC_SECTION_RDATA;
+         else if (strcmp (name, ".rconst") == 0)
+           r_symndx = RELOC_SECTION_RCONST;
          break;
        case 's':
          if (strcmp (name, ".sdata") == 0)
@@ -1421,6 +1423,8 @@ alpha_relocate_section (output_bfd, info, input_bfd, input_section,
       symndx_to_section[RELOC_SECTION_LITA] =
        bfd_get_section_by_name (input_bfd, ".lita");
       symndx_to_section[RELOC_SECTION_ABS] = bfd_abs_section_ptr;
+      symndx_to_section[RELOC_SECTION_RCONST] =
+       bfd_get_section_by_name (input_bfd, ".rconst");
 
       ecoff_data (input_bfd)->symndx_to_section = symndx_to_section;
     }
@@ -1976,7 +1980,7 @@ static const struct ecoff_backend_data alpha_ecoff_backend_data =
     alpha_ecoff_bad_format_hook, _bfd_ecoff_set_arch_mach_hook,
     alpha_ecoff_mkobject_hook, _bfd_ecoff_styp_to_sec_flags,
     _bfd_ecoff_set_alignment_hook, _bfd_ecoff_slurp_symbol_table,
-    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
   },
   /* Supported architecture.  */
   bfd_arch_alpha,
@@ -2053,6 +2057,10 @@ static const struct ecoff_backend_data alpha_ecoff_backend_data =
 #define _bfd_ecoff_bfd_get_relocated_section_contents \
   alpha_ecoff_get_relocated_section_contents
 
+/* Handling file windows is generic.  */
+#define _bfd_ecoff_get_section_contents_in_window \
+  _bfd_generic_get_section_contents_in_window
+
 /* Relaxing sections is generic.  */
 #define _bfd_ecoff_bfd_relax_section bfd_generic_relax_section
 
index 80291f5..c741efe 100644 (file)
@@ -163,7 +163,8 @@ _bfd_ecoff_new_section_hook (abfd, section)
     section->flags |= SEC_DATA | SEC_LOAD | SEC_ALLOC;
   else if (strcmp (section->name, _RDATA) == 0
           || strcmp (section->name, _LIT8) == 0
-          || strcmp (section->name, _LIT4) == 0)
+          || strcmp (section->name, _LIT4) == 0
+          || strcmp (section->name, _RCONST) == 0)
     section->flags |= SEC_DATA | SEC_LOAD | SEC_ALLOC | SEC_READONLY;
   else if (strcmp (section->name, _BSS) == 0
           || strcmp (section->name, _SBSS) == 0)
@@ -335,6 +336,8 @@ ecoff_sec_to_styp_flags (name, flags)
       styp = STYP_COMMENT;
       flags &=~ SEC_NEVER_LOAD;
     }
+  else if (strcmp (name, _RCONST) == 0)
+    styp = STYP_RCONST;
   else if (flags & SEC_CODE) 
     styp = STYP_TEXT;
   else if (flags & SEC_DATA) 
@@ -391,14 +394,16 @@ _bfd_ecoff_styp_to_sec_flags (abfd, hdr, name)
           || (styp_flags & STYP_SDATA)
           || styp_flags == STYP_PDATA
           || styp_flags == STYP_XDATA
-          || (styp_flags & STYP_GOT))
+          || (styp_flags & STYP_GOT)
+          || styp_flags == STYP_RCONST)
     {
       if (sec_flags & SEC_NEVER_LOAD)
        sec_flags |= SEC_DATA | SEC_COFF_SHARED_LIBRARY;
       else
        sec_flags |= SEC_DATA | SEC_LOAD | SEC_ALLOC;
       if ((styp_flags & STYP_RDATA)
-         || styp_flags == STYP_PDATA)
+         || styp_flags == STYP_PDATA
+         || styp_flags == STYP_RCONST)
        sec_flags |= SEC_READONLY;
     }
   else if ((styp_flags & STYP_BSS)
@@ -829,6 +834,10 @@ ecoff_set_symbol_info (abfd, ecoff_sym, asym, ext, weak)
       asym->section = bfd_make_section_old_way (abfd, ".fini");
       asym->value -= asym->section->vma;
       break;
+    case scRConst:
+      asym->section = bfd_make_section_old_way (abfd, ".rconst");
+      asym->value -= asym->section->vma;
+      break;
     default:
       break;
     }
@@ -1743,6 +1752,7 @@ ecoff_slurp_reloc_table (abfd, section, symbols)
            case RELOC_SECTION_PDATA: sec_name = ".pdata"; break;
            case RELOC_SECTION_FINI:  sec_name = ".fini"; break;
            case RELOC_SECTION_LITA:  sec_name = ".lita";  break;
+           case RELOC_SECTION_RCONST: sec_name = ".rconst"; break;
            default: abort ();
            }
 
@@ -2128,11 +2138,12 @@ ecoff_compute_section_file_positions (abfd)
         the data.  */
       if ((abfd->flags & EXEC_P) != 0
          && (abfd->flags & D_PAGED) != 0
-         && first_data != false
+         && ! first_data
          && (current->flags & SEC_CODE) == 0
          && (! ecoff_backend (abfd)->rdata_in_text
              || strcmp (current->name, _RDATA) != 0)
-         && strcmp (current->name, _PDATA) != 0)
+         && strcmp (current->name, _PDATA) != 0
+         && strcmp (current->name, _RCONST) != 0)
        {
          sofar = (sofar + round - 1) &~ (round - 1);
          first_data = false;
@@ -2573,7 +2584,8 @@ _bfd_ecoff_write_object_contents (abfd)
          || (section.s_flags & STYP_DYNSYM) != 0
          || (section.s_flags & STYP_HASH) != 0
          || (section.s_flags & STYP_ECOFF_INIT) != 0
-         || (section.s_flags & STYP_ECOFF_FINI) != 0)
+         || (section.s_flags & STYP_ECOFF_FINI) != 0
+         || section.s_flags == STYP_RCONST)
        {
          text_size += bfd_get_section_size_before_reloc (current);
          if (! set_text_start || text_start > vma)
@@ -2810,6 +2822,8 @@ _bfd_ecoff_write_object_contents (abfd)
                    in.r_symndx = RELOC_SECTION_LITA;
                  else if (strcmp (name, "*ABS*") == 0)
                    in.r_symndx = RELOC_SECTION_ABS;
+                 else if (strcmp (name, ".rconst") == 0)
+                   in.r_symndx = RELOC_SECTION_RCONST;
                  else
                    abort ();
                  in.r_extern = 0;
@@ -3005,7 +3019,7 @@ _bfd_ecoff_slurp_armap (abfd)
 
   /* Read in the armap.  */
   ardata = bfd_ardata (abfd);
-  mapdata = _bfd_read_ar_hdr (abfd);
+  mapdata = (struct areltdata *) _bfd_read_ar_hdr (abfd);
   if (mapdata == (struct areltdata *) NULL)
     return false;
   parsed_size = mapdata->parsed_size;
@@ -3687,6 +3701,7 @@ ecoff_link_check_archive_element (abfd, info, pneeded)
        case scSCommon:
        case scInit:
        case scFini:
+       case scRConst:
          def = true;
          break;
        default:
@@ -3948,6 +3963,10 @@ ecoff_link_add_externals (abfd, info, external_ext, ssext)
          section = bfd_make_section_old_way (abfd, ".fini");
          value -= section->vma;
          break;
+       case scRConst:
+         section = bfd_make_section_old_way (abfd, ".rconst");
+         value -= section->vma;
+         break;
        }
 
       if (section == (asection *) NULL)
@@ -4377,6 +4396,8 @@ ecoff_link_write_external (h, data)
            h->esym.asym.sc = scPData;
          else if (strcmp (name, _XDATA) == 0)
            h->esym.asym.sc = scXData;
+         else if (strcmp (name, _RCONST) == 0)
+           h->esym.asym.sc = scRConst;
          else
            h->esym.asym.sc = scAbs;
        }
@@ -4585,12 +4606,19 @@ ecoff_reloc_link_order (output_bfd, info, output_section, link_order)
      asection *output_section;
      struct bfd_link_order *link_order;
 {
+  enum bfd_link_order_type type;
+  asection *section;
+  bfd_vma addend;
   arelent rel;
   struct internal_reloc in;
   bfd_size_type external_reloc_size;
   bfd_byte *rbuf;
   boolean ok;
 
+  type = link_order->type;
+  section = NULL;
+  addend = link_order->u.reloc.p->addend;
+
   /* We set up an arelent to pass to the backend adjust_reloc_out
      routine.  */
   rel.address = link_order->offset;
@@ -4602,21 +4630,44 @@ ecoff_reloc_link_order (output_bfd, info, output_section, link_order)
       return false;
     }
 
-  if (link_order->type == bfd_section_reloc_link_order)
-    rel.sym_ptr_ptr = link_order->u.reloc.p->u.section->symbol_ptr_ptr;
+  if (type == bfd_section_reloc_link_order)
+    {
+      section = link_order->u.reloc.p->u.section;
+      rel.sym_ptr_ptr = section->symbol_ptr_ptr;
+    }
   else
     {
-      /* We can't set up a reloc against a symbol correctly, because
-        we have no asymbol structure.  Currently no adjust_reloc_out
-        routine cases.  */
-      rel.sym_ptr_ptr = (asymbol **) NULL;
+      struct bfd_link_hash_entry *h;
+
+      /* Treat a reloc against a defined symbol as though it were
+         actually against the section.  */
+      h = bfd_link_hash_lookup (info->hash, link_order->u.reloc.p->u.name,
+                               false, false, false);
+      if (h != NULL
+         && (h->type == bfd_link_hash_defined
+             || h->type == bfd_link_hash_defweak))
+       {
+         type = bfd_section_reloc_link_order;
+         section = h->u.def.section->output_section;
+         /* It seems that we ought to add the symbol value to the
+             addend here, but in practice it has already been added
+             because it was passed to constructor_callback.  */
+         addend += section->vma + h->u.def.section->output_offset;
+       }
+      else
+       {
+         /* We can't set up a reloc against a symbol correctly,
+            because we have no asymbol structure.  Currently no
+            adjust_reloc_out routine cares.  */
+         rel.sym_ptr_ptr = (asymbol **) NULL;
+       }
     }
 
   /* All ECOFF relocs are in-place.  Put the addend into the object
      file.  */
 
   BFD_ASSERT (rel.howto->partial_inplace);
-  if (link_order->u.reloc.p->addend != 0)
+  if (addend != 0)
     {
       bfd_size_type size;
       bfd_reloc_status_type rstat;
@@ -4630,8 +4681,7 @@ ecoff_reloc_link_order (output_bfd, info, output_section, link_order)
          bfd_set_error (bfd_error_no_memory);
          return false;
        }
-      rstat = _bfd_relocate_contents (rel.howto, output_bfd,
-                                     link_order->u.reloc.p->addend, buf);
+      rstat = _bfd_relocate_contents (rel.howto, output_bfd, addend, buf);
       switch (rstat)
        {
        case bfd_reloc_ok:
@@ -4643,11 +4693,10 @@ ecoff_reloc_link_order (output_bfd, info, output_section, link_order)
          if (! ((*info->callbacks->reloc_overflow)
                 (info,
                  (link_order->type == bfd_section_reloc_link_order
-                  ? bfd_section_name (output_bfd,
-                                      link_order->u.reloc.p->u.section)
+                  ? bfd_section_name (output_bfd, section)
                   : link_order->u.reloc.p->u.name),
-                 rel.howto->name, link_order->u.reloc.p->addend,
-                 (bfd *) NULL, (asection *) NULL, (bfd_vma) 0)))
+                 rel.howto->name, addend, (bfd *) NULL,
+                 (asection *) NULL, (bfd_vma) 0)))
            {
              free (buf);
              return false;
@@ -4668,7 +4717,7 @@ ecoff_reloc_link_order (output_bfd, info, output_section, link_order)
                + bfd_get_section_vma (output_bfd, output_section));
   in.r_type = rel.howto->type;
 
-  if (link_order->type == bfd_symbol_reloc_link_order)
+  if (type == bfd_symbol_reloc_link_order)
     {
       struct ecoff_link_hash_entry *h;
 
@@ -4692,8 +4741,7 @@ ecoff_reloc_link_order (output_bfd, info, output_section, link_order)
     {
       CONST char *name;
 
-      name = bfd_get_section_name (output_bfd,
-                                  link_order->u.reloc.p->u.section);
+      name = bfd_get_section_name (output_bfd, section);
       if (strcmp (name, ".text") == 0)
        in.r_symndx = RELOC_SECTION_TEXT;
       else if (strcmp (name, ".rdata") == 0)
@@ -4722,6 +4770,8 @@ ecoff_reloc_link_order (output_bfd, info, output_section, link_order)
        in.r_symndx = RELOC_SECTION_LITA;
       else if (strcmp (name, "*ABS*") == 0)
        in.r_symndx = RELOC_SECTION_ABS;
+      else if (strcmp (name, ".rconst") == 0)
+       in.r_symndx = RELOC_SECTION_RCONST;
       else
        abort ();
       in.r_extern = 0;
index 2d1f99b..d527f93 100644 (file)
@@ -16,13 +16,14 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include "bfd.h"
 #include "sysdep.h"
 #include "bfdlink.h"
 #include "libbfd.h"
 #include "obstack.h"
+#include "aout/stab_gnu.h"
 #include "coff/internal.h"
 #include "coff/sym.h"
 #include "coff/symconst.h"
@@ -39,11 +40,212 @@ static void ecoff_align_debug PARAMS ((bfd *abfd,
 static boolean ecoff_write_symhdr PARAMS ((bfd *, struct ecoff_debug_info *,
                                           const struct ecoff_debug_swap *,
                                           file_ptr where));
+static int cmp_fdrtab_entry PARAMS ((const PTR, const PTR));
+static boolean mk_fdrtab PARAMS ((bfd *,
+                                 struct ecoff_debug_info * const,
+                                 const struct ecoff_debug_swap * const,
+                                 struct ecoff_find_line *));
+static long fdrtab_lookup PARAMS ((struct ecoff_find_line *, bfd_vma));
 
 /* Obstack allocation and deallocation routines.  */
 #define obstack_chunk_alloc malloc
 #define obstack_chunk_free free
 \f
+/* Routines to swap auxiliary information in and out.  I am assuming
+   that the auxiliary information format is always going to be target
+   independent.  */
+
+/* Swap in a type information record.
+   BIGEND says whether AUX symbols are big-endian or little-endian; this
+   info comes from the file header record (fh-fBigendian).  */
+
+void
+_bfd_ecoff_swap_tir_in (bigend, ext_copy, intern)
+     int bigend;
+     const struct tir_ext *ext_copy;
+     TIR *intern;
+{
+  struct tir_ext ext[1];
+
+  *ext = *ext_copy;            /* Make it reasonable to do in-place.  */
+  
+  /* now the fun stuff... */
+  if (bigend) {
+    intern->fBitfield   = 0 != (ext->t_bits1[0] & TIR_BITS1_FBITFIELD_BIG);
+    intern->continued   = 0 != (ext->t_bits1[0] & TIR_BITS1_CONTINUED_BIG);
+    intern->bt          = (ext->t_bits1[0] & TIR_BITS1_BT_BIG)
+                       >>                  TIR_BITS1_BT_SH_BIG;
+    intern->tq4         = (ext->t_tq45[0] & TIR_BITS_TQ4_BIG)
+                       >>                  TIR_BITS_TQ4_SH_BIG;
+    intern->tq5         = (ext->t_tq45[0] & TIR_BITS_TQ5_BIG)
+                       >>                  TIR_BITS_TQ5_SH_BIG;
+    intern->tq0         = (ext->t_tq01[0] & TIR_BITS_TQ0_BIG)
+                       >>                  TIR_BITS_TQ0_SH_BIG;
+    intern->tq1         = (ext->t_tq01[0] & TIR_BITS_TQ1_BIG)
+                       >>                  TIR_BITS_TQ1_SH_BIG;
+    intern->tq2         = (ext->t_tq23[0] & TIR_BITS_TQ2_BIG)
+                       >>                  TIR_BITS_TQ2_SH_BIG;
+    intern->tq3         = (ext->t_tq23[0] & TIR_BITS_TQ3_BIG)
+                       >>                  TIR_BITS_TQ3_SH_BIG;
+  } else {
+    intern->fBitfield   = 0 != (ext->t_bits1[0] & TIR_BITS1_FBITFIELD_LITTLE);
+    intern->continued   = 0 != (ext->t_bits1[0] & TIR_BITS1_CONTINUED_LITTLE);
+    intern->bt          = (ext->t_bits1[0] & TIR_BITS1_BT_LITTLE)
+                       >>                  TIR_BITS1_BT_SH_LITTLE;
+    intern->tq4         = (ext->t_tq45[0] & TIR_BITS_TQ4_LITTLE)
+                       >>                  TIR_BITS_TQ4_SH_LITTLE;
+    intern->tq5         = (ext->t_tq45[0] & TIR_BITS_TQ5_LITTLE)
+                       >>                  TIR_BITS_TQ5_SH_LITTLE;
+    intern->tq0         = (ext->t_tq01[0] & TIR_BITS_TQ0_LITTLE)
+                       >>                  TIR_BITS_TQ0_SH_LITTLE;
+    intern->tq1         = (ext->t_tq01[0] & TIR_BITS_TQ1_LITTLE)
+                       >>                  TIR_BITS_TQ1_SH_LITTLE;
+    intern->tq2         = (ext->t_tq23[0] & TIR_BITS_TQ2_LITTLE)
+                       >>                  TIR_BITS_TQ2_SH_LITTLE;
+    intern->tq3         = (ext->t_tq23[0] & TIR_BITS_TQ3_LITTLE)
+                       >>                  TIR_BITS_TQ3_SH_LITTLE;
+  }
+
+#ifdef TEST
+  if (memcmp ((char *)ext, (char *)intern, sizeof (*intern)) != 0)
+    abort();
+#endif
+}
+
+/* Swap out a type information record.
+   BIGEND says whether AUX symbols are big-endian or little-endian; this
+   info comes from the file header record (fh-fBigendian).  */
+
+void
+_bfd_ecoff_swap_tir_out (bigend, intern_copy, ext)
+     int bigend;
+     const TIR *intern_copy;
+     struct tir_ext *ext;
+{
+  TIR intern[1];
+
+  *intern = *intern_copy;      /* Make it reasonable to do in-place.  */
+  
+  /* now the fun stuff... */
+  if (bigend) {
+    ext->t_bits1[0] = ((intern->fBitfield ? TIR_BITS1_FBITFIELD_BIG : 0)
+                      | (intern->continued ? TIR_BITS1_CONTINUED_BIG : 0)
+                      | ((intern->bt << TIR_BITS1_BT_SH_BIG)
+                         & TIR_BITS1_BT_BIG));
+    ext->t_tq45[0] = (((intern->tq4 << TIR_BITS_TQ4_SH_BIG)
+                      & TIR_BITS_TQ4_BIG)
+                     | ((intern->tq5 << TIR_BITS_TQ5_SH_BIG)
+                        & TIR_BITS_TQ5_BIG));
+    ext->t_tq01[0] = (((intern->tq0 << TIR_BITS_TQ0_SH_BIG)
+                      & TIR_BITS_TQ0_BIG)
+                     | ((intern->tq1 << TIR_BITS_TQ1_SH_BIG)
+                        & TIR_BITS_TQ1_BIG));
+    ext->t_tq23[0] = (((intern->tq2 << TIR_BITS_TQ2_SH_BIG)
+                      & TIR_BITS_TQ2_BIG)
+                     | ((intern->tq3 << TIR_BITS_TQ3_SH_BIG)
+                        & TIR_BITS_TQ3_BIG));
+  } else {
+    ext->t_bits1[0] = ((intern->fBitfield ? TIR_BITS1_FBITFIELD_LITTLE : 0)
+                      | (intern->continued ? TIR_BITS1_CONTINUED_LITTLE : 0)
+                      | ((intern->bt << TIR_BITS1_BT_SH_LITTLE)
+                         & TIR_BITS1_BT_LITTLE));
+    ext->t_tq45[0] = (((intern->tq4 << TIR_BITS_TQ4_SH_LITTLE)
+                      & TIR_BITS_TQ4_LITTLE)
+                     | ((intern->tq5 << TIR_BITS_TQ5_SH_LITTLE)
+                        & TIR_BITS_TQ5_LITTLE));
+    ext->t_tq01[0] = (((intern->tq0 << TIR_BITS_TQ0_SH_LITTLE)
+                      & TIR_BITS_TQ0_LITTLE)
+                     | ((intern->tq1 << TIR_BITS_TQ1_SH_LITTLE)
+                        & TIR_BITS_TQ1_LITTLE));
+    ext->t_tq23[0] = (((intern->tq2 << TIR_BITS_TQ2_SH_LITTLE)
+                      & TIR_BITS_TQ2_LITTLE)
+                     | ((intern->tq3 << TIR_BITS_TQ3_SH_LITTLE)
+                        & TIR_BITS_TQ3_LITTLE));
+  }
+
+#ifdef TEST
+  if (memcmp ((char *)ext, (char *)intern, sizeof (*intern)) != 0)
+    abort();
+#endif
+}
+
+/* Swap in a relative symbol record.  BIGEND says whether it is in
+   big-endian or little-endian format.*/
+
+void
+_bfd_ecoff_swap_rndx_in (bigend, ext_copy, intern)
+     int bigend;
+     const struct rndx_ext *ext_copy;
+     RNDXR *intern;
+{
+  struct rndx_ext ext[1];
+
+  *ext = *ext_copy;            /* Make it reasonable to do in-place.  */
+  
+  /* now the fun stuff... */
+  if (bigend) {
+    intern->rfd   = (ext->r_bits[0] << RNDX_BITS0_RFD_SH_LEFT_BIG)
+                 | ((ext->r_bits[1] & RNDX_BITS1_RFD_BIG)
+                                   >> RNDX_BITS1_RFD_SH_BIG);
+    intern->index = ((ext->r_bits[1] & RNDX_BITS1_INDEX_BIG)
+                                   << RNDX_BITS1_INDEX_SH_LEFT_BIG)
+                 | (ext->r_bits[2] << RNDX_BITS2_INDEX_SH_LEFT_BIG)
+                 | (ext->r_bits[3] << RNDX_BITS3_INDEX_SH_LEFT_BIG);
+  } else {
+    intern->rfd   = (ext->r_bits[0] << RNDX_BITS0_RFD_SH_LEFT_LITTLE)
+                 | ((ext->r_bits[1] & RNDX_BITS1_RFD_LITTLE)
+                                   << RNDX_BITS1_RFD_SH_LEFT_LITTLE);
+    intern->index = ((ext->r_bits[1] & RNDX_BITS1_INDEX_LITTLE)
+                                   >> RNDX_BITS1_INDEX_SH_LITTLE)
+                 | (ext->r_bits[2] << RNDX_BITS2_INDEX_SH_LEFT_LITTLE)
+                 | ((unsigned int) ext->r_bits[3]
+                    << RNDX_BITS3_INDEX_SH_LEFT_LITTLE);
+  }
+
+#ifdef TEST
+  if (memcmp ((char *)ext, (char *)intern, sizeof (*intern)) != 0)
+    abort();
+#endif
+}
+
+/* Swap out a relative symbol record.  BIGEND says whether it is in
+   big-endian or little-endian format.*/
+
+void
+_bfd_ecoff_swap_rndx_out (bigend, intern_copy, ext)
+     int bigend;
+     const RNDXR *intern_copy;
+     struct rndx_ext *ext;
+{
+  RNDXR intern[1];
+
+  *intern = *intern_copy;      /* Make it reasonable to do in-place.  */
+  
+  /* now the fun stuff... */
+  if (bigend) {
+    ext->r_bits[0] = intern->rfd >> RNDX_BITS0_RFD_SH_LEFT_BIG;
+    ext->r_bits[1] = (((intern->rfd << RNDX_BITS1_RFD_SH_BIG)
+                      & RNDX_BITS1_RFD_BIG)
+                     | ((intern->index >> RNDX_BITS1_INDEX_SH_LEFT_BIG)
+                        & RNDX_BITS1_INDEX_BIG));
+    ext->r_bits[2] = intern->index >> RNDX_BITS2_INDEX_SH_LEFT_BIG;
+    ext->r_bits[3] = intern->index >> RNDX_BITS3_INDEX_SH_LEFT_BIG;
+  } else {
+    ext->r_bits[0] = intern->rfd >> RNDX_BITS0_RFD_SH_LEFT_LITTLE;
+    ext->r_bits[1] = (((intern->rfd >> RNDX_BITS1_RFD_SH_LEFT_LITTLE)
+                      & RNDX_BITS1_RFD_LITTLE)
+                     | ((intern->index << RNDX_BITS1_INDEX_SH_LITTLE)
+                        & RNDX_BITS1_INDEX_LITTLE));
+    ext->r_bits[2] = intern->index >> RNDX_BITS2_INDEX_SH_LEFT_LITTLE;
+    ext->r_bits[3] = intern->index >> RNDX_BITS3_INDEX_SH_LEFT_LITTLE;
+  }
+
+#ifdef TEST
+  if (memcmp ((char *)ext, (char *)intern, sizeof (*intern)) != 0)
+    abort();
+#endif
+}
+\f
 /* The minimum amount of data to allocate.  */
 #define ALLOC_SIZE (4064)
 
@@ -224,7 +426,7 @@ add_file_shuffle (ainfo, head, tail, input_bfd, offset, size)
   if (*tail != (struct shuffle *) NULL
       && (*tail)->filep
       && (*tail)->u.file.input_bfd == input_bfd
-      && (*tail)->u.file.offset + (*tail)->size == offset)
+      && (*tail)->u.file.offset + (*tail)->size == (unsigned long) offset)
     {
       /* Just merge this entry onto the existing one.  */
       (*tail)->size += size;
@@ -453,6 +655,7 @@ bfd_ecoff_debug_accumulate (handle, output_bfd, output_debug, output_swap,
   SET (".rodata", scRData);
   SET (".init", scInit);
   SET (".fini", scFini);
+  SET (".rconst", scRConst);
 
 #undef SET
 
@@ -1049,7 +1252,7 @@ bfd_ecoff_debug_accumulate_other (handle, output_bfd, output_debug,
       if (internal_sym.iss == -1)
        return false;
       if (bfd_is_com_section ((*sym_ptr)->section)
-         || (*sym_ptr)->section == &bfd_und_section)
+         || bfd_is_und_section ((*sym_ptr)->section))
        internal_sym.value = (*sym_ptr)->value;
       else
        internal_sym.value = ((*sym_ptr)->value
@@ -1140,7 +1343,8 @@ bfd_ecoff_debug_externals (abfd, debug, swap, relocateable, get_extr,
        }
 
       if (bfd_is_com_section (sym_ptr->section)
-         || sym_ptr->section == &bfd_und_section)
+         || bfd_is_und_section (sym_ptr->section)
+         || sym_ptr->section->output_section == (asection *) NULL)
        {
          /* FIXME: gas does not keep the value of a small undefined
             symbol in the symbol itself, because of relocation
@@ -1184,7 +1388,7 @@ bfd_ecoff_debug_one_external (abfd, debug, swap, name, esym)
 
   namelen = strlen (name);
 
-  if (debug->ssext_end - debug->ssext
+  if ((size_t) (debug->ssext_end - debug->ssext)
       < symhdr->issExtMax + namelen + 1)
     {
       if (ecoff_add_bytes ((char **) &debug->ssext,
@@ -1193,7 +1397,8 @@ bfd_ecoff_debug_one_external (abfd, debug, swap, name, esym)
          == false)
        return false;
     }
-  if ((char *) debug->external_ext_end - (char *) debug->external_ext
+  if ((size_t) ((char *) debug->external_ext_end
+               - (char *) debug->external_ext)
       < (symhdr->iextMax + 1) * external_ext_size)
     {
       if (ecoff_add_bytes ((char **) &debug->external_ext,
@@ -1274,7 +1479,7 @@ ecoff_align_debug (abfd, debug, swap)
       if (debug->external_rfd != (PTR) NULL)
        memset ((PTR) ((char *) debug->external_rfd
                       + symhdr->crfd * swap->external_rfd_size),
-               0, add * swap->external_rfd_size);
+               0, (size_t) (add * swap->external_rfd_size));
       symhdr->crfd += add;
     }
 }
@@ -1359,7 +1564,7 @@ ecoff_write_symhdr (abfd, debug, swap, where)
   SET (cbExtOffset, iextMax, swap->external_ext_size);
 #undef SET
 
-  buff = (PTR) malloc (swap->external_hdr_size);
+  buff = (PTR) malloc ((size_t) swap->external_hdr_size);
   if (buff == NULL && swap->external_hdr_size != 0)
     {
       bfd_set_error (bfd_error_no_memory);
@@ -1399,7 +1604,8 @@ bfd_ecoff_write_debug (abfd, debug, swap, where)
     return false;
 
 #define WRITE(ptr, count, size, offset) \
-  BFD_ASSERT (symhdr->offset == 0 || bfd_tell (abfd) == symhdr->offset); \
+  BFD_ASSERT (symhdr->offset == 0 \
+             || (bfd_vma) bfd_tell (abfd) == symhdr->offset); \
   if (bfd_write ((PTR) debug->ptr, size, symhdr->count, abfd) \
       != size * symhdr->count) \
     return false;
@@ -1456,7 +1662,7 @@ ecoff_write_shuffle (abfd, swap, shuffle, space)
 
   if ((total & (swap->debug_align - 1)) != 0)
     {
-      int i;
+      unsigned int i;
       bfd_byte *s;
 
       i = swap->debug_align - (total & (swap->debug_align - 1));
@@ -1545,7 +1751,7 @@ bfd_ecoff_write_accumulated_debug (handle, abfd, debug, swap, info, where)
 
       if ((total & (swap->debug_align - 1)) != 0)
        {
-         int i;
+         unsigned int i;
          bfd_byte *s;
 
          i = swap->debug_align - (total & (swap->debug_align - 1));
@@ -1568,11 +1774,11 @@ bfd_ecoff_write_accumulated_debug (handle, abfd, debug, swap, info, where)
   /* The external strings and symbol are not converted over to using
      shuffles.  FIXME: They probably should be.  */
   if (bfd_write (debug->ssext, 1, debug->symbolic_header.issExtMax, abfd)
-      != debug->symbolic_header.issExtMax)
+      != (bfd_size_type) debug->symbolic_header.issExtMax)
     goto error_return;
   if ((debug->symbolic_header.issExtMax & (swap->debug_align - 1)) != 0)
     {
-      int i;
+      unsigned int i;
       bfd_byte *s;
 
       i = (swap->debug_align
@@ -1597,7 +1803,8 @@ bfd_ecoff_write_accumulated_debug (handle, abfd, debug, swap, info, where)
     goto error_return;
 
   BFD_ASSERT (debug->symbolic_header.cbExtOffset == 0
-             || debug->symbolic_header.cbExtOffset == bfd_tell (abfd));
+             || (debug->symbolic_header.cbExtOffset
+                 == (bfd_vma) bfd_tell (abfd)));
 
   if (bfd_write (debug->external_ext, swap->external_ext_size,
                 debug->symbolic_header.iextMax, abfd)
@@ -1613,3 +1820,672 @@ bfd_ecoff_write_accumulated_debug (handle, abfd, debug, swap, info, where)
     free (space);
   return false;
 }
+\f
+/* Handle the find_nearest_line function for both ECOFF and MIPS ELF
+   files.  */
+
+/* Compare FDR entries.  This is called via qsort.  */
+
+static int
+cmp_fdrtab_entry (leftp, rightp)
+     const PTR leftp;
+     const PTR rightp;
+{
+  const struct ecoff_fdrtab_entry *lp =
+    (const struct ecoff_fdrtab_entry *) leftp;
+  const struct ecoff_fdrtab_entry *rp =
+    (const struct ecoff_fdrtab_entry *) rightp;
+
+  if (lp->base_addr < rp->base_addr)
+    return -1;
+  if (lp->base_addr > rp->base_addr)
+    return 1;
+  return 0;
+}
+
+/* Each file descriptor (FDR) has a memory address, to simplify
+   looking up an FDR by address, we build a table covering all FDRs
+   that have a least one procedure descriptor in them.  The final
+   table will be sorted by address so we can look it up via binary
+   search.  */
+
+static boolean
+mk_fdrtab (abfd, debug_info, debug_swap, line_info)
+     bfd *abfd;
+     struct ecoff_debug_info * const debug_info;
+     const struct ecoff_debug_swap * const debug_swap;
+     struct ecoff_find_line *line_info;
+{
+  struct ecoff_fdrtab_entry *tab;
+  FDR *fdr_ptr;
+  FDR *fdr_start;
+  FDR *fdr_end;
+  boolean stabs;
+  long len;
+
+  fdr_start = debug_info->fdr;
+  fdr_end = fdr_start + debug_info->symbolic_header.ifdMax;
+
+  /* First, let's see how long the table needs to be: */
+  for (len = 0, fdr_ptr = fdr_start; fdr_ptr < fdr_end; fdr_ptr++)
+    {
+      if (fdr_ptr->cpd == 0)   /* skip FDRs that have no PDRs */
+       continue;
+      ++len;
+    }
+
+  /* Now, create and fill in the table: */
+
+  line_info->fdrtab = ((struct ecoff_fdrtab_entry*)
+                      bfd_zalloc (abfd,
+                                  len * sizeof (struct ecoff_fdrtab_entry)));
+  if (line_info->fdrtab == NULL)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      return false;
+    }
+  line_info->fdrtab_len = len;
+
+  tab = line_info->fdrtab;
+  for (fdr_ptr = fdr_start; fdr_ptr < fdr_end; fdr_ptr++)
+    {
+      if (fdr_ptr->cpd == 0)
+       continue;
+
+      /* Check whether this file has stabs debugging information.  In
+        a file with stabs debugging information, the second local
+        symbol is named @stabs.  */
+      stabs = false;
+      if (fdr_ptr->csym >= 2)
+       {
+         char *sym_ptr;
+         SYMR sym;
+
+         sym_ptr = ((char *) debug_info->external_sym
+                    + (fdr_ptr->isymBase + 1)*debug_swap->external_sym_size);
+         (*debug_swap->swap_sym_in) (abfd, sym_ptr, &sym);
+         if (strcmp (debug_info->ss + fdr_ptr->issBase + sym.iss,
+                     STABS_SYMBOL) == 0)
+           stabs = true;
+       }
+
+      if (!stabs)
+       {
+         bfd_size_type external_pdr_size;
+         char *pdr_ptr;
+         PDR pdr;
+
+         external_pdr_size = debug_swap->external_pdr_size;
+
+         pdr_ptr = ((char *) debug_info->external_pdr
+                    + fdr_ptr->ipdFirst * external_pdr_size);
+         (*debug_swap->swap_pdr_in) (abfd, (PTR) pdr_ptr, &pdr);
+         /* The address of the first PDR is the offset of that
+            procedure relative to the beginning of file FDR.  */
+         tab->base_addr = fdr_ptr->adr - pdr.adr;
+       }
+      else
+       {
+         /* XXX I don't know about stabs, so this is a guess
+            (davidm@cs.arizona.edu): */
+         tab->base_addr = fdr_ptr->adr;
+       }
+      tab->fdr = fdr_ptr;
+      ++tab;
+    }
+
+  /* Finally, the table is sorted in increasing memory-address order.
+     The table is mostly sorted already, but there are cases (e.g.,
+     static functions in include files), where this does not hold.
+     Use "odump -PFv" to verify...  */
+  qsort ((PTR) line_info->fdrtab, len,
+        sizeof (struct ecoff_fdrtab_entry), cmp_fdrtab_entry);
+
+  return true;
+}
+
+/* Return index of first FDR that covers to OFFSET.  */
+
+static long
+fdrtab_lookup (line_info, offset)
+     struct ecoff_find_line *line_info;
+     bfd_vma offset;
+{
+  long low, high, len;
+  long mid = -1;
+  struct ecoff_fdrtab_entry *tab;
+
+  len = line_info->fdrtab_len;
+  if (len == 0)
+    return -1;
+
+  tab = line_info->fdrtab;
+  for (low = 0, high = len - 1 ; low != high ;)
+    {
+      mid = (high + low) / 2;
+      if (offset >= tab[mid].base_addr && offset < tab[mid + 1].base_addr)
+       goto find_min;
+
+      if (tab[mid].base_addr > offset)
+       high = mid;
+      else
+       low = mid + 1;
+    }
+  ++mid;
+
+  /* last entry is catch-all for all higher addresses: */
+  if (offset < tab[mid].base_addr)
+    return -1;
+
+ find_min:
+
+  while (mid > 0 && tab[mid - 1].base_addr == tab[mid].base_addr)
+    --mid;
+
+  return mid;
+}
+
+/* Do the work of find_nearest_line.  */
+
+boolean
+_bfd_ecoff_locate_line (abfd, section, offset, debug_info, debug_swap,
+                       line_info, filename_ptr, functionname_ptr, retline_ptr)
+     bfd *abfd;
+     asection *section;
+     bfd_vma offset;
+     struct ecoff_debug_info * const debug_info;
+     const struct ecoff_debug_swap * const debug_swap;
+     struct ecoff_find_line *line_info;
+     const char **filename_ptr;
+     const char **functionname_ptr;
+     unsigned int *retline_ptr;
+{
+  struct ecoff_fdrtab_entry *tab;
+  boolean stabs;
+  FDR *fdr_ptr;
+  int i;
+  
+  offset += section->vma;
+     
+  /* Build FDR table (sorted by object file's base-address) if we
+     don't have it already.  */
+  if (line_info->fdrtab == NULL
+      && !mk_fdrtab (abfd, debug_info, debug_swap, line_info))
+    return false;
+
+  tab = line_info->fdrtab;
+
+  /* find first FDR for address OFFSET */
+  i = fdrtab_lookup (line_info, offset);
+  if (i < 0)
+    return false;              /* no FDR, no fun... */
+  fdr_ptr = tab[i].fdr;
+
+  /* Check whether this file has stabs debugging information.  In a
+     file with stabs debugging information, the second local symbol is
+     named @stabs.  */
+  stabs = false;
+  if (fdr_ptr->csym >= 2)
+    {
+      char *sym_ptr;
+      SYMR sym;
+
+      sym_ptr = ((char *) debug_info->external_sym
+                + (fdr_ptr->isymBase + 1) * debug_swap->external_sym_size);
+      (*debug_swap->swap_sym_in) (abfd, sym_ptr, &sym);
+      if (strcmp (debug_info->ss + fdr_ptr->issBase + sym.iss,
+                 STABS_SYMBOL) == 0)
+       stabs = true;
+    }
+
+  if (!stabs)
+    {
+      bfd_size_type external_pdr_size;
+      char *pdr_ptr;
+      char *best_pdr = NULL;
+      FDR *best_fdr;
+      bfd_vma best_dist = ~0;
+      PDR pdr;
+      unsigned char *line_ptr;
+      unsigned char *line_end;
+      int lineno;
+      /* This file uses ECOFF debugging information.  Each FDR has a
+         list of procedure descriptors (PDR).  The address in the FDR
+         is the absolute address of the first procedure.  The address
+         in the first PDR gives the offset of that procedure relative
+         to the object file's base-address.  The addresses in
+         subsequent PDRs specify each procedure's address relative to
+         the object file's base-address.  To make things more juicy,
+         whenever the PROF bit in the PDR is set, the real entry point
+         of the procedure may be 16 bytes below what would normally be
+         the procedure's entry point.  Instead, DEC came up with a
+         wicked scheme to create profiled libraries "on the fly":
+         instead of shipping a regular and a profiled version of each
+         library, they insert 16 bytes of unused space in front of
+         each procedure and set the "prof" bit in the PDR to indicate
+         that there is a gap there (this is done automagically by "as"
+         when option "-pg" is specified).  Thus, normally, you link
+         against such a library and, except for lots of 16 byte gaps
+         between functions, things will behave as usual.  However,
+         when invoking "ld" with option "-pg", it will fill those gaps
+         with code that calls mcount().  It then moves the function's
+         entry point down by 16 bytes, and out pops a binary that has
+         all functions profiled.
+
+         NOTE: Neither FDRs nor PDRs are strictly sorted in memory
+               order.  For example, when including header-files that
+               define functions, the FDRs follow behind the including
+               file, even though their code may have been generated at
+               a lower address.  File coff-alpha.c from libbfd
+               illustrates this (use "odump -PFv" to look at a file's
+               FDR/PDR).  Similarly, PDRs are sometimes out of order
+               as well.  An example of this is OSF/1 v3.0 libc's
+               malloc.c.  I'm not sure why this happens, but it could
+               be due to optimizations that reorder a function's
+               position within an object-file.
+        
+         Strategy:
+         
+         On the first call to this function, we build a table of FDRs
+         that is sorted by the base-address of the object-file the FDR
+         is referring to.  Notice that each object-file may contain
+         code from multiple source files (e.g., due to code defined in
+         include files).  Thus, for any given base-address, there may
+         be multiple FDRs (but this case is, fortunately, uncommon).
+         lookup(addr) guarantees to return the first FDR that applies
+         to address ADDR.  Thus, after invoking lookup(), we have a
+         list of FDRs that may contain the PDR for ADDR.  Next, we
+         walk through the PDRs of these FDRs and locate the one that
+         is closest to ADDR (i.e., for which the difference between
+         ADDR and the PDR's entry point is positive and minimal).
+         Once, the right FDR and PDR are located, we simply walk
+         through the line-number table to lookup the line-number that
+         best matches ADDR.  Obviously, things could be sped up by
+         keeping a sorted list of PDRs instead of a sorted list of
+         FDRs.  However, this would increase space requirements
+         considerably, which is undesirable.  */
+      external_pdr_size = debug_swap->external_pdr_size;
+
+      /* Make offset relative to object file's start-address: */
+      offset -= tab[i].base_addr;
+      /* Search FDR list starting at tab[i] for the PDR that best matches
+         OFFSET.  Normally, the FDR list is only one entry long.  */
+      best_fdr = NULL;
+      do
+       {
+         bfd_vma dist, min_dist = 0;
+         char *pdr_hold;
+         char *pdr_end;
+         
+         fdr_ptr = tab[i].fdr;
+         
+         pdr_ptr = ((char *) debug_info->external_pdr
+                    + fdr_ptr->ipdFirst * external_pdr_size);
+         pdr_end = pdr_ptr + fdr_ptr->cpd * external_pdr_size;
+         (*debug_swap->swap_pdr_in) (abfd, (PTR) pdr_ptr, &pdr);
+         /* Find PDR that is closest to OFFSET.  If pdr.prof is set,
+            the procedure entry-point *may* be 0x10 below pdr.adr.  We
+            simply pretend that pdr.prof *implies* a lower entry-point.
+            This is safe because it just means that may identify 4 NOPs
+            in front of the function as belonging to the function.  */
+         for (pdr_hold = NULL;
+              pdr_ptr < pdr_end;
+              (pdr_ptr += external_pdr_size,
+               (*debug_swap->swap_pdr_in) (abfd, (PTR) pdr_ptr, &pdr)))
+           {
+             if (offset >= (pdr.adr - 0x10 * pdr.prof))
+               {
+                 dist = offset - (pdr.adr - 0x10 * pdr.prof);
+                 if (!pdr_hold || dist < min_dist)
+                   {
+                     min_dist = dist;
+                     pdr_hold = pdr_ptr;
+                   }
+               }
+           }
+         
+         if (!best_pdr || min_dist < best_dist)
+           {
+             best_dist = min_dist;
+             best_fdr = fdr_ptr;
+             best_pdr = pdr_hold;
+           }
+         /* continue looping until base_addr of next entry is different: */
+       }
+      while (++i < line_info->fdrtab_len
+            && tab[i].base_addr == tab[i - 1].base_addr);
+
+      if (!best_fdr || !best_pdr)
+       return false;                   /* shouldn't happen... */
+
+      /* phew, finally we got something that we can hold onto: */
+      fdr_ptr = best_fdr;
+      pdr_ptr = best_pdr;
+      (*debug_swap->swap_pdr_in) (abfd, (PTR) pdr_ptr, &pdr);
+      /* Now we can look for the actual line number.  The line numbers
+         are stored in a very funky format, which I won't try to
+         describe.  The search is bounded by the end of the FDRs line
+         number entries.  */
+      line_end = debug_info->line + fdr_ptr->cbLineOffset + fdr_ptr->cbLine;
+
+      /* Make offset relative to procedure entry: */
+      offset -= pdr.adr - 0x10 * pdr.prof;
+      lineno = pdr.lnLow;
+      line_ptr = debug_info->line + fdr_ptr->cbLineOffset + pdr.cbLineOffset;
+      while (line_ptr < line_end)
+       {
+         int delta;
+         unsigned int count;
+
+         delta = *line_ptr >> 4;
+         if (delta >= 0x8)
+           delta -= 0x10;
+         count = (*line_ptr & 0xf) + 1;
+         ++line_ptr;
+         if (delta == -8)
+           {
+             delta = (((line_ptr[0]) & 0xff) << 8) + ((line_ptr[1]) & 0xff);
+             if (delta >= 0x8000)
+               delta -= 0x10000;
+             line_ptr += 2;
+           }
+         lineno += delta;
+         if (offset < count * 4)
+           break;
+         offset -= count * 4;
+       }
+
+      /* If fdr_ptr->rss is -1, then this file does not have full
+         symbols, at least according to gdb/mipsread.c.  */
+      if (fdr_ptr->rss == -1)
+       {
+         *filename_ptr = NULL;
+         if (pdr.isym == -1)
+           *functionname_ptr = NULL;
+         else
+           {
+             EXTR proc_ext;
+
+             (*debug_swap->swap_ext_in)
+               (abfd,
+                ((char *) debug_info->external_ext
+                 + pdr.isym * debug_swap->external_ext_size),
+                &proc_ext);
+             *functionname_ptr = debug_info->ssext + proc_ext.asym.iss;
+           }
+       }
+      else
+       {
+         SYMR proc_sym;
+
+         *filename_ptr = debug_info->ss + fdr_ptr->issBase + fdr_ptr->rss;
+         (*debug_swap->swap_sym_in)
+           (abfd,
+            ((char *) debug_info->external_sym
+             + (fdr_ptr->isymBase + pdr.isym) * debug_swap->external_sym_size),
+            &proc_sym);
+         *functionname_ptr = debug_info->ss + fdr_ptr->issBase + proc_sym.iss;
+       }
+      if (lineno == ilineNil)
+       lineno = 0;
+      *retline_ptr = lineno;
+    }
+  else
+    {
+      bfd_size_type external_sym_size;
+      const char *directory_name;
+      const char *main_file_name;
+      const char *current_file_name;
+      const char *function_name;
+      const char *line_file_name;
+      bfd_vma low_func_vma;
+      bfd_vma low_line_vma;
+      boolean past_line;
+      boolean past_fn;
+      char *sym_ptr, *sym_ptr_end;
+      size_t len, funclen;
+      char *buffer = NULL;
+
+      /* This file uses stabs debugging information.  When gcc is not
+        optimizing, it will put the line number information before
+        the function name stabs entry.  When gcc is optimizing, it
+        will put the stabs entry for all the function first, followed
+        by the line number information.  (This appears to happen
+        because of the two output files used by the -mgpopt switch,
+        which is implied by -O).  This means that we must keep
+        looking through the symbols until we find both a line number
+        and a function name which are beyond the address we want.  */
+
+      *filename_ptr = NULL;
+      *functionname_ptr = NULL;
+      *retline_ptr = 0;
+
+      directory_name = NULL;
+      main_file_name = NULL;
+      current_file_name = NULL;
+      function_name = NULL;
+      line_file_name = NULL;
+      low_func_vma = 0;
+      low_line_vma = 0;
+      past_line = false;
+      past_fn = false;
+
+      external_sym_size = debug_swap->external_sym_size;
+
+      sym_ptr = ((char *) debug_info->external_sym
+                + (fdr_ptr->isymBase + 2) * external_sym_size);
+      sym_ptr_end = sym_ptr + (fdr_ptr->csym - 2) * external_sym_size;
+      for (;
+          sym_ptr < sym_ptr_end && (! past_line || ! past_fn);
+          sym_ptr += external_sym_size)
+       {
+         SYMR sym;
+
+         (*debug_swap->swap_sym_in) (abfd, sym_ptr, &sym);
+
+         if (ECOFF_IS_STAB (&sym))
+           {
+             switch (ECOFF_UNMARK_STAB (sym.index))
+               {
+               case N_SO:
+                 main_file_name = current_file_name =
+                   debug_info->ss + fdr_ptr->issBase + sym.iss;
+
+                 /* Check the next symbol to see if it is also an
+                     N_SO symbol.  */
+                 if (sym_ptr + external_sym_size < sym_ptr_end)
+                   {
+                     SYMR nextsym;
+
+                     (*debug_swap->swap_sym_in) (abfd,
+                                                 sym_ptr + external_sym_size,
+                                                 &nextsym);
+                     if (ECOFF_IS_STAB (&nextsym)
+                         && ECOFF_UNMARK_STAB (nextsym.index) == N_SO)
+                       {
+                         directory_name = current_file_name;
+                         main_file_name = current_file_name =
+                           debug_info->ss + fdr_ptr->issBase + nextsym.iss;
+                         sym_ptr += external_sym_size;
+                       }
+                   }
+                 break;
+
+               case N_SOL:
+                 current_file_name =
+                   debug_info->ss + fdr_ptr->issBase + sym.iss;
+                 break;
+
+               case N_FUN:
+                 if (sym.value > offset)
+                   past_fn = true;
+                 else if (sym.value >= low_func_vma)
+                   {
+                     low_func_vma = sym.value;
+                     function_name =
+                       debug_info->ss + fdr_ptr->issBase + sym.iss;
+                   }
+                 break;
+               }
+           }
+         else if (sym.st == stLabel && sym.index != indexNil)
+           {
+             if (sym.value > offset)
+               past_line = true;
+             else if (sym.value >= low_line_vma)
+               {
+                 low_line_vma = sym.value;
+                 line_file_name = current_file_name;
+                 *retline_ptr = sym.index;
+               }
+           }
+       }
+
+      if (*retline_ptr != 0)
+       main_file_name = line_file_name;
+
+      /* We need to remove the stuff after the colon in the function
+         name.  We also need to put the directory name and the file
+         name together.  */
+      if (function_name == NULL)
+       len = funclen = 0;
+      else
+       len = funclen = strlen (function_name) + 1;
+
+      if (main_file_name != NULL
+         && directory_name != NULL
+         && main_file_name[0] != '/')
+       len += strlen (directory_name) + strlen (main_file_name) + 1;
+
+      if (len != 0)
+       {
+         if (line_info->find_buffer != NULL)
+           free (line_info->find_buffer);
+         buffer = (char *) malloc (len);
+         if (buffer == NULL)
+           {
+             bfd_set_error (bfd_error_no_memory);
+             return false;
+           }
+         line_info->find_buffer = buffer;
+       }
+
+      if (function_name != NULL)
+       {
+         char *colon;
+
+         strcpy (buffer, function_name);
+         colon = strchr (buffer, ':');
+         if (colon != NULL)
+           *colon = '\0';
+         *functionname_ptr = buffer;
+       }
+
+      if (main_file_name != NULL)
+       {
+         if (directory_name == NULL || main_file_name[0] == '/')
+           *filename_ptr = main_file_name;
+         else
+           {
+             sprintf (buffer + funclen, "%s%s", directory_name,
+                      main_file_name);
+             *filename_ptr = buffer + funclen;
+           }
+       }
+    }
+
+  return true;
+}
+\f
+/* These routines copy symbolic information into a memory buffer.
+
+   FIXME: The whole point of the shuffle code is to avoid storing
+   everything in memory, since the linker is such a memory hog.  This
+   code makes that effort useless.  It is only called by the MIPS ELF
+   code when generating a shared library, so it is not that big a
+   deal, but it should be fixed eventually.  */
+
+/* Collect a shuffle into a memory buffer.  */
+
+static boolean ecoff_collect_shuffle PARAMS ((struct shuffle *, bfd_byte *));
+
+static boolean
+ecoff_collect_shuffle (l, buff)
+     struct shuffle *l;
+     bfd_byte *buff;
+{
+  unsigned long total;
+
+  total = 0;
+  for (; l != (struct shuffle *) NULL; l = l->next)
+    {
+      if (! l->filep)
+       memcpy (buff, l->u.memory, l->size);
+      else
+       {
+         if (bfd_seek (l->u.file.input_bfd, l->u.file.offset, SEEK_SET) != 0
+             || bfd_read (buff, 1, l->size, l->u.file.input_bfd) != l->size)
+           return false;
+       }
+      total += l->size;
+      buff += l->size;
+    }
+
+  return true;
+}
+
+/* Copy PDR information into a memory buffer.  */
+
+boolean
+_bfd_ecoff_get_accumulated_pdr (handle, buff)
+     PTR handle;
+     bfd_byte *buff;
+{
+  struct accumulate *ainfo = (struct accumulate *) handle;
+
+  return ecoff_collect_shuffle (ainfo->pdr, buff);
+}
+
+/* Copy symbol information into a memory buffer.  */
+
+boolean
+_bfd_ecoff_get_accumulated_sym (handle, buff)
+     PTR handle;
+     bfd_byte *buff;
+{
+  struct accumulate *ainfo = (struct accumulate *) handle;
+
+  return ecoff_collect_shuffle (ainfo->sym, buff);
+}
+
+/* Copy the string table into a memory buffer.  */
+
+boolean
+_bfd_ecoff_get_accumulated_ss (handle, buff)
+     PTR handle;
+     bfd_byte *buff;
+{
+  struct accumulate *ainfo = (struct accumulate *) handle;
+  struct string_hash_entry *sh;
+  unsigned long total;
+
+  /* The string table is written out from the hash table if this is a
+     final link.  */
+  BFD_ASSERT (ainfo->ss == (struct shuffle *) NULL);
+  *buff++ = '\0';
+  total = 1;
+  BFD_ASSERT (ainfo->ss_hash == NULL || ainfo->ss_hash->val == 1);
+  for (sh = ainfo->ss_hash;
+       sh != (struct string_hash_entry *) NULL;
+       sh = sh->next)
+    {
+      size_t len;
+
+      len = strlen (sh->root.string);
+      memcpy (buff, (PTR) sh->root.string, len + 1);
+      total += len + 1;
+      buff += len + 1;
+    }
+
+  return true;
+}