Fixed xgettext invocation in .Sanitize files
[binutils-gdb.git] / bfd / coff-mips.c
index f9cc8f451cb5b72cbe1c011b529c4024b60ea7c9..d2a34de697f78c13852a8548c8bb9219375d4ff0 100644 (file)
@@ -1,5 +1,5 @@
 /* BFD back-end for MIPS Extended-Coff files.
-   Copyright 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+   Copyright 1990, 91, 92, 93, 94, 95, 96, 1997 Free Software Foundation, Inc.
    Original version by Per Bothner.
    Full support added by Ian Lance Taylor, ian@cygnus.com.
 
@@ -17,1406 +17,394 @@ 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 "seclet.h"
-#include "aout/ar.h"
-#include "aout/ranlib.h"
-#include "coff/mips.h"
 #include "coff/internal.h"
 #include "coff/sym.h"
 #include "coff/symconst.h"
-#include "coff/ecoff-ext.h"
+#include "coff/ecoff.h"
+#include "coff/mips.h"
 #include "libcoff.h"
 #include "libecoff.h"
-
-/* Each canonical asymbol really looks like this.  */
-
-typedef struct ecoff_symbol_struct
-{
-  /* The actual symbol which the rest of BFD works with */
-  asymbol symbol;
-
-  /* The fdr for this symbol.  */
-  FDR *fdr;
-
-  /* true if this is a local symbol rather than an external one.  */
-  boolean local;
-
-  /* A pointer to the unswapped hidden information for this symbol */
-  union
-    {
-      struct sym_ext *lnative;
-      struct ext_ext *enative;
-    }
-  native;
-} ecoff_symbol_type;
-
-/* We take the address of the first element of a asymbol to ensure that the
-   macro is only ever applied to an asymbol.  */
-#define ecoffsymbol(asymbol) ((ecoff_symbol_type *) (&((asymbol)->the_bfd)))
-
-/* The page boundary used to align sections in the executable file.  */
-#define ROUND_SIZE 0x1000
-
-/* The linker needs a section to hold small common variables while
-   linking.  There is no convenient way to create it when the linker
-   needs it, so we always create one for each BFD.  We then avoid
-   writing it out.  */
-#define SCOMMON ".scommon"
-
-/* MIPS ECOFF has COFF sections, but the debugging information is
-   stored in a completely different format.  This files uses the some
-   of the swapping routines from coffswap.h, and some of the generic
-   COFF routines in coffgen.c, but, unlike the real COFF targets, does
-   not use coffcode.h itself.  */
 \f
 /* Prototypes for static functions.  */
 
-static boolean ecoff_bad_format_hook PARAMS ((bfd *abfd, PTR filehdr));
-static asection *ecoff_make_section_hook PARAMS ((bfd *abfd, char *name));
-static boolean ecoff_new_section_hook PARAMS ((bfd *abfd, asection *section));
-static boolean ecoff_mkobject PARAMS ((bfd *abfd));
-static PTR ecoff_mkobject_hook PARAMS ((bfd *abfd, PTR filehdr, PTR aouthdr));
-static boolean ecoff_set_arch_mach_hook PARAMS ((bfd *abfd, PTR filehdr));
-static long ecoff_sec_to_styp_flags PARAMS ((CONST char *name,
-                                            flagword flags));
-static flagword ecoff_styp_to_sec_flags PARAMS ((bfd *abfd, PTR hdr));
-static asymbol *ecoff_make_empty_symbol PARAMS ((bfd *abfd));
-static void ecoff_set_symbol_info PARAMS ((bfd *abfd, SYMR *ecoff_sym,
-                                          asymbol *asym, int ext));
-static boolean ecoff_slurp_symbol_table PARAMS ((bfd *abfd));
-static unsigned int ecoff_get_symtab_upper_bound PARAMS ((bfd *abfd));
-static unsigned int ecoff_get_symtab PARAMS ((bfd *abfd,
-                                             asymbol **alocation));
-static void ecoff_emit_aggregate PARAMS ((bfd *abfd, char *string,
-                                         RNDXR *rndx, long isym,
-                                         CONST char *which));
-static char *ecoff_type_to_string PARAMS ((bfd *abfd, union aux_ext *aux_ptr,
-                                          int indx, int bigendian));
-static void ecoff_print_symbol PARAMS ((bfd *abfd, PTR filep,
-                                       asymbol *symbol,
-                                       bfd_print_symbol_type how));
-static void ecoff_swap_reloc_in PARAMS ((bfd *abfd, RELOC *ext,
-                                        struct internal_reloc *intern));
-static unsigned int ecoff_swap_reloc_out PARAMS ((bfd *abfd, PTR src,
-                                                 PTR dst));
-static bfd_reloc_status_type ecoff_generic_reloc PARAMS ((bfd *abfd,
-                                                         arelent *reloc,
-                                                         asymbol *symbol,
-                                                         PTR data,
-                                                         asection *section,
-                                                         bfd *output_bfd));
-static bfd_reloc_status_type ecoff_refhi_reloc PARAMS ((bfd *abfd,
+static boolean mips_ecoff_bad_format_hook PARAMS ((bfd *abfd, PTR filehdr));
+static void mips_ecoff_swap_reloc_in PARAMS ((bfd *, PTR,
+                                             struct internal_reloc *));
+static void mips_ecoff_swap_reloc_out PARAMS ((bfd *,
+                                              const struct internal_reloc *,
+                                              PTR));
+static void mips_adjust_reloc_in PARAMS ((bfd *,
+                                         const struct internal_reloc *,
+                                         arelent *));
+static void mips_adjust_reloc_out PARAMS ((bfd *, const arelent *,
+                                          struct internal_reloc *));
+static bfd_reloc_status_type mips_generic_reloc PARAMS ((bfd *abfd,
+                                                        arelent *reloc,
+                                                        asymbol *symbol,
+                                                        PTR data,
+                                                        asection *section,
+                                                        bfd *output_bfd,
+                                                        char **error));
+static bfd_reloc_status_type mips_refhi_reloc PARAMS ((bfd *abfd,
+                                                      arelent *reloc,
+                                                      asymbol *symbol,
+                                                      PTR data,
+                                                      asection *section,
+                                                      bfd *output_bfd,
+                                                      char **error));
+static bfd_reloc_status_type mips_reflo_reloc PARAMS ((bfd *abfd,
+                                                      arelent *reloc,
+                                                      asymbol *symbol,
+                                                      PTR data,
+                                                      asection *section,
+                                                      bfd *output_bfd,
+                                                      char **error));
+static bfd_reloc_status_type mips_gprel_reloc PARAMS ((bfd *abfd,
+                                                      arelent *reloc,
+                                                      asymbol *symbol,
+                                                      PTR data,
+                                                      asection *section,
+                                                      bfd *output_bfd,
+                                                      char **error));
+static bfd_reloc_status_type mips_relhi_reloc PARAMS ((bfd *abfd,
+                                                      arelent *reloc,
+                                                      asymbol *symbol,
+                                                      PTR data,
+                                                      asection *section,
+                                                      bfd *output_bfd,
+                                                      char **error));
+static bfd_reloc_status_type mips_rello_reloc PARAMS ((bfd *abfd,
+                                                      arelent *reloc,
+                                                      asymbol *symbol,
+                                                      PTR data,
+                                                      asection *section,
+                                                      bfd *output_bfd,
+                                                      char **error));
+static bfd_reloc_status_type mips_switch_reloc PARAMS ((bfd *abfd,
                                                        arelent *reloc,
                                                        asymbol *symbol,
                                                        PTR data,
                                                        asection *section,
-                                                       bfd *output_bfd));
-static bfd_reloc_status_type ecoff_reflo_reloc PARAMS ((bfd *abfd,
-                                                       arelent *reloc,
-                                                       asymbol *symbol,
-                                                       PTR data,
-                                                       asection *section,
-                                                       bfd *output_bfd));
-static bfd_reloc_status_type ecoff_gprel_reloc PARAMS ((bfd *abfd,
-                                                       arelent *reloc,
-                                                       asymbol *symbol,
-                                                       PTR data,
-                                                       asection *section,
-                                                       bfd *output_bfd));
-static boolean ecoff_slurp_reloc_table PARAMS ((bfd *abfd, asection *section,
-                                               asymbol **symbols));
-static unsigned int ecoff_canonicalize_reloc PARAMS ((bfd *abfd,
-                                                     asection *section,
-                                                     arelent **relptr,
-                                                     asymbol **symbols));
-static CONST struct reloc_howto_struct *ecoff_bfd_reloc_type_lookup
-  PARAMS ((bfd *abfd, bfd_reloc_code_real_type code));
-static boolean ecoff_find_nearest_line PARAMS ((bfd *abfd,
-                                               asection *section,
-                                               asymbol **symbols,
-                                               bfd_vma offset,
-                                               CONST char **filename_ptr,
-                                               CONST char **fnname_ptr,
-                                               unsigned int *retline_ptr));
-static void ecoff_clear_output_flags PARAMS ((bfd *abfd));
-static boolean ecoff_rel PARAMS ((bfd *output_bfd, bfd_seclet_type *seclet,
-                                 asection *output_section, PTR data,
-                                 boolean relocateable));
-static boolean ecoff_dump_seclet PARAMS ((bfd *abfd, bfd_seclet_type *seclet,
-                                         asection *section, PTR data,
-                                         boolean relocateable));
-static long ecoff_add_string PARAMS ((bfd *output_bfd, FDR *fdr,
-                                     CONST char *string, boolean external));
-static boolean ecoff_get_debug PARAMS ((bfd *output_bfd,
-                                       bfd_seclet_type *seclet,
-                                       asection *section,
-                                       boolean relocateable));
-static boolean ecoff_bfd_seclet_link PARAMS ((bfd *abfd, PTR data,
-                                             boolean relocateable));
-static boolean ecoff_set_arch_mach PARAMS ((bfd *abfd,
-                                           enum bfd_architecture arch,
-                                           unsigned long machine));
-static int ecoff_sizeof_headers PARAMS ((bfd *abfd, boolean reloc));
-static void ecoff_compute_section_file_positions PARAMS ((bfd *abfd));
-static boolean ecoff_set_section_contents PARAMS ((bfd *abfd,
-                                                  asection *section,
-                                                  PTR location,
-                                                  file_ptr offset,
-                                                  bfd_size_type count));
-static boolean ecoff_write_object_contents PARAMS ((bfd *abfd));
-static unsigned int ecoff_armap_hash PARAMS ((CONST char *s,
-                                             unsigned int *rehash,
-                                             unsigned int size,
-                                             unsigned int hlog));
-static boolean ecoff_slurp_armap PARAMS ((bfd *abfd));
-static boolean ecoff_write_armap PARAMS ((bfd *abfd, unsigned int elength,
-                                         struct orl *map,
-                                         unsigned int orl_count,
-                                         int stridx));
-static bfd_target *ecoff_archive_p PARAMS ((bfd *abfd));
+                                                       bfd *output_bfd,
+                                                       char **error));
+static void mips_relocate_hi PARAMS ((struct internal_reloc *refhi,
+                                     struct internal_reloc *reflo,
+                                     bfd *input_bfd,
+                                     asection *input_section,
+                                     bfd_byte *contents,
+                                     size_t adjust,
+                                     bfd_vma relocation,
+                                     boolean pcrel));
+static boolean mips_relocate_section PARAMS ((bfd *, struct bfd_link_info *,
+                                             bfd *, asection *,
+                                             bfd_byte *, PTR));
+static boolean mips_read_relocs PARAMS ((bfd *, asection *));
+static boolean mips_relax_section PARAMS ((bfd *, asection *,
+                                          struct bfd_link_info *,
+                                          boolean *));
+static boolean mips_relax_pcrel16 PARAMS ((struct bfd_link_info *, bfd *,
+                                          asection *,
+                                          struct ecoff_link_hash_entry *,
+                                          bfd_byte *, bfd_vma));
+static reloc_howto_type *mips_bfd_reloc_type_lookup
+  PARAMS ((bfd *, bfd_reloc_code_real_type));
+
 \f
-/* Get the generic COFF swapping routines, except for the reloc,
-   symbol, and lineno ones.  Give them ecoff names.  */
+/* ECOFF has COFF sections, but the debugging information is stored in
+   a completely different format.  ECOFF targets use some of the
+   swapping routines from coffswap.h, and some of the generic COFF
+   routines in coffgen.c, but, unlike the real COFF targets, do not
+   use coffcode.h itself.
+
+   Get the generic COFF swapping routines, except for the reloc,
+   symbol, and lineno ones.  Give them ECOFF names.  */
 #define MIPSECOFF
 #define NO_COFF_RELOCS
 #define NO_COFF_SYMBOLS
 #define NO_COFF_LINENOS
-#define coff_swap_filehdr_in ecoff_swap_filehdr_in
-#define coff_swap_filehdr_out ecoff_swap_filehdr_out
-#define coff_swap_aouthdr_in ecoff_swap_aouthdr_in
-#define coff_swap_aouthdr_out ecoff_swap_aouthdr_out
-#define coff_swap_scnhdr_in ecoff_swap_scnhdr_in
-#define coff_swap_scnhdr_out ecoff_swap_scnhdr_out
+#define coff_swap_filehdr_in mips_ecoff_swap_filehdr_in
+#define coff_swap_filehdr_out mips_ecoff_swap_filehdr_out
+#define coff_swap_aouthdr_in mips_ecoff_swap_aouthdr_in
+#define coff_swap_aouthdr_out mips_ecoff_swap_aouthdr_out
+#define coff_swap_scnhdr_in mips_ecoff_swap_scnhdr_in
+#define coff_swap_scnhdr_out mips_ecoff_swap_scnhdr_out
 #include "coffswap.h"
-\f
-/* This stuff is somewhat copied from coffcode.h.  */
-
-static asection bfd_debug_section = { "*DEBUG*" };
-
-/* See whether the magic number matches.  */
-
-static boolean
-ecoff_bad_format_hook (abfd, filehdr)
-     bfd *abfd;
-     PTR filehdr;
-{
-  struct internal_filehdr *internal_f = (struct internal_filehdr *) filehdr;
-
-  if (ECOFFBADMAG (*internal_f))
-    return false;
-
-  return true;
-}
-
-/* This is a hook needed by SCO COFF, but we have nothing to do.  */
-
-static asection *
-ecoff_make_section_hook (abfd, name)
-     bfd *abfd;
-     char *name;
-{
-  return (asection *) NULL;
-}
-
-/* Initialize a new section.  */
-
-static boolean
-ecoff_new_section_hook (abfd, section)
-     bfd *abfd;
-     asection *section;
-{
-  section->alignment_power = abfd->xvec->align_power_min;
-
-  if (strcmp (section->name, _TEXT) == 0)
-    section->flags |= SEC_CODE | SEC_LOAD | SEC_ALLOC;
-  else if (strcmp (section->name, _DATA) == 0
-          || strcmp (section->name, _SDATA) == 0)
-    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)
-    section->flags |= SEC_DATA | SEC_LOAD | SEC_ALLOC | SEC_READONLY;
-  else if (strcmp (section->name, _BSS) == 0
-          || strcmp (section->name, _SBSS) == 0)
-    section->flags |= SEC_ALLOC;
-
-  /* Probably any other section name is SEC_NEVER_LOAD, but I'm
-     uncertain about .init on some systems and I don't know how shared
-     libraries work.  */
-
-  return true;
-}
-
-/* Set the alignment of a section; we have nothing to do.  */
-
-#define ecoff_set_alignment_hook \
-  ((void (*) PARAMS ((bfd *, asection *, PTR))) bfd_void)
-
-/* Create an ECOFF object.  */
-
-static boolean
-ecoff_mkobject (abfd)
-     bfd *abfd;
-{
-  abfd->tdata.ecoff_obj_data = ((struct ecoff_tdata *)
-                               bfd_zalloc (abfd, sizeof (ecoff_data_type)));
-  if (abfd->tdata.ecoff_obj_data == NULL)
-    {
-      bfd_error = no_memory;
-      return false;
-    }
 
-  /* Always create a .scommon section for every BFD.  This is a hack so
-     that the linker has something to attach scSCommon symbols to.  */
-  bfd_make_section (abfd, SCOMMON);
-
-  return true;
-}
-
-/* Create the ECOFF backend specific information.  */
-
-static PTR
-ecoff_mkobject_hook (abfd, filehdr, aouthdr)
-     bfd *abfd;
-     PTR filehdr;
-     PTR aouthdr;
-{
-  struct internal_filehdr *internal_f = (struct internal_filehdr *) filehdr;
-  struct internal_aouthdr *internal_a = (struct internal_aouthdr *) aouthdr;
-  ecoff_data_type *ecoff;
-
-  if (ecoff_mkobject (abfd) == false)
-    return NULL;
-
-  ecoff = ecoff_data (abfd);
-  ecoff->gp_size = 8;
-  ecoff->sym_filepos = internal_f->f_symptr;
-
-  if (internal_a != (struct internal_aouthdr *) NULL)
-    {
-      int i;
-
-      ecoff->text_start = internal_a->text_start;
-      ecoff->text_end = internal_a->text_start + internal_a->tsize;
-      ecoff->gp = internal_a->gp_value;
-      ecoff->gprmask = internal_a->gprmask;
-      for (i = 0; i < 4; i++)
-       ecoff->cprmask[i] = internal_a->cprmask[i];
-    }
-
-  return (PTR) ecoff;
-}
-
-/* Determine the machine architecture and type.  */
-
-static boolean
-ecoff_set_arch_mach_hook (abfd, filehdr)
-     bfd *abfd;
-     PTR filehdr;
-{
-  struct internal_filehdr *internal_f = (struct internal_filehdr *) filehdr;
-  enum bfd_architecture arch;
-
-  switch (internal_f->f_magic)
-    {
-    case MIPS_MAGIC_1:
-    case MIPS_MAGIC_LITTLE:
-    case MIPS_MAGIC_BIG:
-      arch = bfd_arch_mips;
-      break;
-
-    default:
-      arch = bfd_arch_obscure;
-      break;
-    }
-
-  bfd_default_set_arch_mach (abfd, arch, (unsigned long) 0);
-
-  return true;
-}
-
-/* Get the section s_flags to use for a section.  */
-
-static long
-ecoff_sec_to_styp_flags (name, flags)
-     CONST char *name;
-     flagword flags;
-{
-  long styp;
-
-  styp = 0;
-
-  if (strcmp (name, _TEXT) == 0)
-    styp = STYP_TEXT;
-  else if (strcmp (name, _DATA) == 0)
-    styp = STYP_DATA;
-  else if (strcmp (name, _SDATA) == 0)
-    styp = STYP_SDATA;
-  else if (strcmp (name, _RDATA) == 0)
-    styp = STYP_RDATA;
-  else if (strcmp (name, _LIT8) == 0)
-    styp = STYP_LIT8;
-  else if (strcmp (name, _LIT4) == 0)
-    styp = STYP_LIT4;
-  else if (strcmp (name, _BSS) == 0)
-    styp = STYP_BSS;
-  else if (strcmp (name, _SBSS) == 0)
-    styp = STYP_SBSS;
-  else if (flags & SEC_CODE) 
-    styp = STYP_TEXT;
-  else if (flags & SEC_DATA) 
-    styp = STYP_DATA;
-  else if (flags & SEC_READONLY)
-    styp = STYP_RDATA;
-  else if (flags & SEC_LOAD)
-    styp = STYP_TEXT;
-  else
-    styp = STYP_BSS;
-
-  if (flags & SEC_NEVER_LOAD)
-    styp |= STYP_NOLOAD;
-
-  return styp;
-}
-
-/* Get the BFD flags to use for a section.  */
-
-static flagword
-ecoff_styp_to_sec_flags (abfd, hdr)
-     bfd *abfd;
-     PTR hdr;
-{
-  struct internal_scnhdr *internal_s = (struct internal_scnhdr *) hdr;
-  long styp_flags = internal_s->s_flags;
-  flagword sec_flags=0;
-
-  if (styp_flags & STYP_NOLOAD)
-    sec_flags |= SEC_NEVER_LOAD;
-
-  /* For 386 COFF, at least, an unloadable text or data section is
-     actually a shared library section.  */
-  if (styp_flags & STYP_TEXT)
-    {
-      if (sec_flags & SEC_NEVER_LOAD)
-       sec_flags |= SEC_CODE | SEC_SHARED_LIBRARY;
-      else
-       sec_flags |= SEC_CODE | SEC_LOAD | SEC_ALLOC;
-    }
-  else if ((styp_flags & STYP_DATA)
-          || (styp_flags & STYP_RDATA)
-          || (styp_flags & STYP_SDATA))
-    {
-      if (sec_flags & SEC_NEVER_LOAD)
-       sec_flags |= SEC_DATA | SEC_SHARED_LIBRARY;
-      else
-       sec_flags |= SEC_DATA | SEC_LOAD | SEC_ALLOC;
-      if (styp_flags & STYP_RDATA)
-       sec_flags |= SEC_READONLY;
-    }
-  else if ((styp_flags & STYP_BSS)
-          || (styp_flags & STYP_SBSS))
-    {
-      sec_flags |= SEC_ALLOC;
-    }
-  else if (styp_flags & STYP_INFO) 
-    {
-      sec_flags |= SEC_NEVER_LOAD;
-    }
-  else if ((styp_flags & STYP_LIT8)
-          || (styp_flags & STYP_LIT4))
-    {
-      sec_flags |= SEC_DATA | SEC_LOAD | SEC_ALLOC | SEC_READONLY;
-    }
-  else
-    {
-      sec_flags |= SEC_ALLOC | SEC_LOAD;
-    }
-
-  return sec_flags;
-}
-\f
-/* Read in and swap the important symbolic information for an ECOFF
-   object file.  FIXME: This is called by gdb.  If there is ever
-   another ECOFF target, it should be moved into some sort of target
-   specific structure.  */
-
-boolean
-ecoff_slurp_symbolic_info (abfd)
-     bfd *abfd;
-{
-  struct hdr_ext external_symhdr;
-  HDRR *internal_symhdr;
-  bfd_size_type raw_base;
-  bfd_size_type raw_size;
-  PTR raw;
-  struct fdr_ext *fraw_src;
-  struct fdr_ext *fraw_end;
-  struct fdr *fdr_ptr;
-
-  /* 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;
-    }
-
-  /* At this point bfd_get_symcount (abfd) holds the number of symbols
-     as read from the file header, but on ECOFF this is always the
-     size of the symbolic information header.  It would be cleaner to
-     handle this when we first read the file in coffgen.c.  */
-  if (bfd_get_symcount (abfd) != sizeof (external_symhdr))
-    {
-      bfd_error = bad_value;
-      return false;
-    }
-
-  /* Read the symbolic information header.  */
-  if (bfd_seek (abfd, ecoff_data (abfd)->sym_filepos, SEEK_SET) == -1
-      || (bfd_read ((PTR) &external_symhdr, sizeof (external_symhdr), 1, abfd)
-         != sizeof (external_symhdr)))
-    {
-      bfd_error = system_call_error;
-      return false;
-    }
-  internal_symhdr = &ecoff_data (abfd)->symbolic_header;
-  ecoff_swap_hdr_in (abfd, &external_symhdr, internal_symhdr);
-
-  if (internal_symhdr->magic != magicSym)
-    {
-      bfd_error = bad_value;
-      return false;
-    }
-
-  /* Now we can get the correct number of symbols.  */
-  bfd_get_symcount (abfd) = (internal_symhdr->isymMax
-                            + internal_symhdr->iextMax);
-
-  /* Read all the symbolic information at once.  */
-  raw_base = ecoff_data (abfd)->sym_filepos + sizeof (struct hdr_ext);
-
-  if (internal_symhdr->cbExtOffset != 0)
-    raw_size = (internal_symhdr->cbExtOffset
-               - raw_base
-               + internal_symhdr->iextMax * sizeof (struct ext_ext));
-  else
-    {
-      long cbline, issmax, issextmax;
-
-      cbline = (internal_symhdr->cbLine + 3) &~ 3;
-      issmax = (internal_symhdr->issMax + 3) &~ 3;
-      issextmax = (internal_symhdr->issExtMax + 3) &~ 3;
-      raw_size = (cbline * sizeof (unsigned char)
-                 + internal_symhdr->idnMax * sizeof (struct dnr_ext)
-                 + internal_symhdr->ipdMax * sizeof (struct pdr_ext)
-                 + internal_symhdr->isymMax * sizeof (struct sym_ext)
-                 + internal_symhdr->ioptMax * sizeof (struct opt_ext)
-                 + internal_symhdr->iauxMax * sizeof (union aux_ext)
-                 + issmax * sizeof (char)
-                 + issextmax * sizeof (char)
-                 + internal_symhdr->ifdMax * sizeof (struct fdr_ext)
-                 + internal_symhdr->crfd * sizeof (struct rfd_ext)
-                 + internal_symhdr->iextMax * sizeof (struct ext_ext));
-    }
-
-  if (raw_size == 0)
-    {
-      ecoff_data (abfd)->sym_filepos = 0;
-      return true;
-    }
-  raw = (PTR) bfd_alloc (abfd, raw_size);
-  if (raw == NULL)
-    {
-      bfd_error = no_memory;
-      return false;
-    }
-  if (bfd_read (raw, raw_size, 1, abfd) != raw_size)
-    {
-      bfd_error = system_call_error;
-      bfd_release (abfd, raw);
-      return false;
-    }
-
-  ecoff_data (abfd)->raw_size = raw_size;
-  ecoff_data (abfd)->raw_syments = raw;
-
-  /* Get pointers for the numeric offsets in the HDRR structure.  */
-#define FIX(off1, off2, type) \
-  if (internal_symhdr->off1 == 0) \
-    ecoff_data (abfd)->off2 = (type *) NULL; \
-  else \
-    ecoff_data (abfd)->off2 = (type *) ((char *) raw \
-                                       + internal_symhdr->off1 \
-                                       - raw_base)
-  FIX (cbLineOffset, line, unsigned char);
-  FIX (cbDnOffset, external_dnr, struct dnr_ext);
-  FIX (cbPdOffset, external_pdr, struct pdr_ext);
-  FIX (cbSymOffset, external_sym, struct sym_ext);
-  FIX (cbOptOffset, external_opt, struct opt_ext);
-  FIX (cbAuxOffset, external_aux, union aux_ext);
-  FIX (cbSsOffset, ss, char);
-  FIX (cbSsExtOffset, ssext, char);
-  FIX (cbFdOffset, external_fdr, struct fdr_ext);
-  FIX (cbRfdOffset, external_rfd, struct rfd_ext);
-  FIX (cbExtOffset, external_ext, struct ext_ext);
-#undef FIX
-
-  /* I don't want to always swap all the data, because it will just
-     waste time and most programs will never look at it.  The only
-     time the linker needs most of the debugging information swapped
-     is when linking big-endian and little-endian MIPS object files
-     together, which is not a common occurrence.
-
-     We need to look at the fdr to deal with a lot of information in
-     the symbols, so we swap them here.  */
-  ecoff_data (abfd)->fdr = (struct fdr *) bfd_alloc (abfd,
-                                                    (internal_symhdr->ifdMax *
-                                                     sizeof (struct fdr)));
-  if (ecoff_data (abfd)->fdr == NULL)
-    {
-      bfd_error = no_memory;
-      return false;
-    }
-  fdr_ptr = ecoff_data (abfd)->fdr;
-  fraw_src = ecoff_data (abfd)->external_fdr;
-  fraw_end = fraw_src + internal_symhdr->ifdMax;
-  for (; fraw_src < fraw_end; fraw_src++, fdr_ptr++)
-    ecoff_swap_fdr_in (abfd, fraw_src, fdr_ptr);
-
-  return true;
-}
+/* Get the ECOFF swapping routines.  */
+#define ECOFF_32
+#include "ecoffswap.h"
 \f
-/* ECOFF symbol table routines.  The ECOFF symbol table is described
-   in gcc/mips-tfile.c.  */
-
-/* ECOFF uses two common sections.  One is the usual one, and the
-   other is for small objects.  All the small objects are kept
-   together, and then referenced via the gp pointer, which yields
-   faster assembler code.  This is what we use for the small common
-   section.  */
-static asection ecoff_scom_section;
-static asymbol ecoff_scom_symbol;
-static asymbol *ecoff_scom_symbol_ptr;
-
-/* Create an empty symbol.  */
-
-static asymbol *
-ecoff_make_empty_symbol (abfd)
-     bfd *abfd;
-{
-  ecoff_symbol_type *new;
-
-  new = (ecoff_symbol_type *) bfd_alloc (abfd, sizeof (ecoff_symbol_type));
-  if (new == (ecoff_symbol_type *) NULL)
-    {
-      bfd_error = no_memory;
-      return (asymbol *) NULL;
-    }
-  memset (new, 0, sizeof *new);
-  new->symbol.section = (asection *) NULL;
-  new->fdr = (FDR *) NULL;
-  new->local = false;
-  new->native.lnative = (struct sym_ext *) NULL;
-  new->symbol.the_bfd = abfd;
-  return &new->symbol;
-}
-
-/* Set the BFD flags and section for an ECOFF symbol.  */
-
-static void
-ecoff_set_symbol_info (abfd, ecoff_sym, asym, ext)
-     bfd *abfd;
-     SYMR *ecoff_sym;
-     asymbol *asym;
-     int ext;
-{
-  asym->the_bfd = abfd;
-  asym->value = ecoff_sym->value;
-  asym->section = &bfd_debug_section;
-  asym->udata = NULL;
-
-  /* Most symbol types are just for debugging.  */
-  switch (ecoff_sym->st)
-    {
-    case stGlobal:
-    case stStatic:
-    case stLabel:
-    case stProc:
-    case stStaticProc:
-      break;
-    case stNil:
-      if (MIPS_IS_STAB (ecoff_sym))
-       {
-         asym->flags = BSF_DEBUGGING;
-         return;
-       }
-      break;
-    default:
-      asym->flags = BSF_DEBUGGING;
-      return;
-    }
-
-  if (ext)
-    asym->flags = BSF_EXPORT | BSF_GLOBAL;
-  else
-    asym->flags = BSF_LOCAL;
-  switch (ecoff_sym->sc)
-    {
-    case scNil:
-      /* Used for compiler generated labels.  Leave them in the
-        debugging section, and mark them as local.  If BSF_DEBUGGING
-        is set, then nm does not display them for some reason.  If no
-        flags are set then the linker whines about them.  */
-      asym->flags = BSF_LOCAL;
-      break;
-    case scText:
-      asym->section = bfd_make_section_old_way (abfd, ".text");
-      asym->value -= asym->section->vma;
-      break;
-    case scData:
-      asym->section = bfd_make_section_old_way (abfd, ".data");
-      asym->value -= asym->section->vma;
-      break;
-    case scBss:
-      if (ext)
-       {
-         asym->section = &bfd_com_section;
-         asym->flags = 0;
-       }
-      else
-       {
-         asym->section = bfd_make_section_old_way (abfd, ".bss");
-         asym->value -= asym->section->vma;
-       }
-      break;
-    case scRegister:
-      asym->flags = BSF_DEBUGGING;
-      break;
-    case scAbs:
-      asym->section = &bfd_abs_section;
-      break;
-    case scUndefined:
-      asym->section = &bfd_und_section;
-      asym->flags = 0;
-      asym->value = 0;
-      break;
-    case scCdbLocal:
-    case scBits:
-    case scCdbSystem:
-    case scRegImage:
-    case scInfo:
-    case scUserStruct:
-      asym->flags = BSF_DEBUGGING;
-      break;
-    case scSData:
-      asym->section = bfd_make_section_old_way (abfd, ".sdata");
-      asym->value -= asym->section->vma;
-      break;
-    case scSBss:
-      asym->section = bfd_make_section_old_way (abfd, ".sbss");
-      if (! ext)
-       asym->value -= asym->section->vma;
-      break;
-    case scRData:
-      asym->section = bfd_make_section_old_way (abfd, ".rdata");
-      asym->value -= asym->section->vma;
-      break;
-    case scVar:
-      asym->flags = BSF_DEBUGGING;
-      break;
-    case scCommon:
-      if (asym->value > ecoff_data (abfd)->gp_size)
-       {
-         asym->section = &bfd_com_section;
-         asym->flags = 0;
-         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;
-       }
-      asym->section = &ecoff_scom_section;
-      asym->flags = 0;
-      break;
-    case scVarRegister:
-    case scVariant:
-      asym->flags = BSF_DEBUGGING;
-      break;
-    case scSUndefined:
-      asym->section = &bfd_und_section;
-      asym->flags = 0;
-      asym->value = 0;
-      break;
-    case scInit:
-      asym->section = bfd_make_section_old_way (abfd, ".init");
-      asym->value -= asym->section->vma;
-      break;
-    case scBasedVar:
-    case scXData:
-    case scPData:
-      asym->flags = BSF_DEBUGGING;
-      break;
-    case scFini:
-      asym->section = bfd_make_section_old_way (abfd, ".fini");
-      asym->value -= asym->section->vma;
-      break;
-    default:
-      break;
-    }
-}
-
-/* Read an ECOFF symbol table.  */
-
-static boolean
-ecoff_slurp_symbol_table (abfd)
-     bfd *abfd;
-{
-  bfd_size_type internal_size;
-  ecoff_symbol_type *internal;
-  ecoff_symbol_type *internal_ptr;
-  struct ext_ext *eraw_src;
-  struct ext_ext *eraw_end;
-  FDR *fdr_ptr;
-  FDR *fdr_end;
-
-  /* If we've already read in the symbol table, do nothing.  */
-  if (ecoff_data (abfd)->canonical_symbols != NULL)
-    return true;
-
-  /* Get the symbolic information.  */
-  if (ecoff_slurp_symbolic_info (abfd) == false)
-    return false;
-  if (bfd_get_symcount (abfd) == 0)
-    return true;
-
-  internal_size = bfd_get_symcount (abfd) * sizeof (ecoff_symbol_type);
-  internal = (ecoff_symbol_type *) bfd_alloc (abfd, internal_size);
-  if (internal == NULL)
-    {
-      bfd_error = no_memory;
-      return false;
-    }
-
-  internal_ptr = internal;
-  eraw_src = ecoff_data (abfd)->external_ext;
-  eraw_end = eraw_src + ecoff_data (abfd)->symbolic_header.iextMax;
-  for (; eraw_src < eraw_end; eraw_src++, internal_ptr++)
-    {
-      EXTR internal_esym;
-
-      ecoff_swap_ext_in (abfd, eraw_src, &internal_esym);
-      internal_ptr->symbol.name = (ecoff_data (abfd)->ssext
-                                  + internal_esym.asym.iss);
-      ecoff_set_symbol_info (abfd, &internal_esym.asym,
-                            &internal_ptr->symbol, 1);
-      internal_ptr->fdr = ecoff_data (abfd)->fdr + internal_esym.ifd;
-      internal_ptr->local = false;
-      internal_ptr->native.enative = eraw_src;
-    }
-
-  /* The local symbols must be accessed via the fdr's, because the
-     string and aux indices are relative to the fdr information.  */
-  fdr_ptr = ecoff_data (abfd)->fdr;
-  fdr_end = fdr_ptr + ecoff_data (abfd)->symbolic_header.ifdMax;
-  for (; fdr_ptr < fdr_end; fdr_ptr++)
-    {
-      struct sym_ext *lraw_src;
-      struct sym_ext *lraw_end;
-
-      lraw_src = ecoff_data (abfd)->external_sym + fdr_ptr->isymBase;
-      lraw_end = lraw_src + fdr_ptr->csym;
-      for (; lraw_src < lraw_end; lraw_src++, internal_ptr++)
-       {
-         SYMR internal_sym;
-
-         ecoff_swap_sym_in (abfd, lraw_src, &internal_sym);
-         internal_ptr->symbol.name = (ecoff_data (abfd)->ss
-                                      + fdr_ptr->issBase
-                                      + internal_sym.iss);
-         ecoff_set_symbol_info (abfd, &internal_sym,
-                                &internal_ptr->symbol, 0);
-         internal_ptr->fdr = fdr_ptr;
-         internal_ptr->local = true;
-         internal_ptr->native.lnative = lraw_src;
-       }
-    }
-
-  ecoff_data (abfd)->canonical_symbols = internal;
-
-  return true;
-}
-
-static unsigned int
-ecoff_get_symtab_upper_bound (abfd)
-     bfd *abfd;
-{
-  if (ecoff_slurp_symbolic_info (abfd) == false
-      || bfd_get_symcount (abfd) == 0)
-    return 0;
-
-  return (bfd_get_symcount (abfd) + 1) * (sizeof (ecoff_symbol_type *));
-}
-
-static unsigned int
-ecoff_get_symtab (abfd, alocation)
-     bfd *abfd;
-     asymbol **alocation;
-{
-  unsigned int counter = 0;
-  ecoff_symbol_type *symbase;
-  ecoff_symbol_type **location = (ecoff_symbol_type **) alocation;
-
-  if (ecoff_slurp_symbol_table (abfd) == false
-      || bfd_get_symcount (abfd) == 0)
-    return 0;
-
-  symbase = ecoff_data (abfd)->canonical_symbols;
-  while (counter < bfd_get_symcount (abfd))
-    {
-      *(location++) = symbase++;
-      counter++;
-    }
-  *location++ = (ecoff_symbol_type *) NULL;
-  return bfd_get_symcount (abfd);
-}
-
-/* Turn ECOFF type information into a printable string.
-   ecoff_emit_aggregate and ecoff_type_to_string are from
-   gcc/mips-tdump.c, with swapping added and used_ptr removed.  */
-
-/* Write aggregate information to a string.  */
-
-static void
-ecoff_emit_aggregate (abfd, string, rndx, isym, which)
-     bfd *abfd;
-     char *string;
-     RNDXR *rndx;
-     long isym;
-     CONST char *which;
-{
-  int ifd = rndx->rfd;
-  int indx = rndx->index;
-  int sym_base, ss_base;
-  CONST char *name;
-  
-  if (ifd == 0xfff)
-    ifd = isym;
-
-  sym_base = ecoff_data (abfd)->fdr[ifd].isymBase;
-  ss_base  = ecoff_data (abfd)->fdr[ifd].issBase;
-  
-  if (indx == indexNil)
-    name = "/* no name */";
-  else
-    {
-      SYMR sym;
-
-      indx += sym_base;
-      ecoff_swap_sym_in (abfd,
-                        ecoff_data (abfd)->external_sym + indx,
-                        &sym);
-      name = ecoff_data (abfd)->ss + ss_base + sym.iss;
-    }
-
-  sprintf (string,
-          "%s %s { ifd = %d, index = %d }",
-          which, name, ifd,
-          indx + ecoff_data (abfd)->symbolic_header.iextMax);
-}
-
-/* Convert the type information to string format.  */
+/* How to process the various relocs types.  */
 
-static char *
-ecoff_type_to_string (abfd, aux_ptr, indx, bigendian)
-     bfd *abfd;
-     union aux_ext *aux_ptr;
-     int indx;
-     int bigendian;
+static reloc_howto_type mips_howto_table[] =
 {
-  AUXU u;
-  struct qual {
-    unsigned int  type;
-    int  low_bound;
-    int  high_bound;
-    int  stride;
-  } qualifiers[7];
-
-  unsigned int basic_type;
-  int i;
-  static char buffer1[1024];
-  static char buffer2[1024];
-  char *p1 = buffer1;
-  char *p2 = buffer2;
-  RNDXR rndx;
-
-  for (i = 0; i < 7; i++)
-    {
-      qualifiers[i].low_bound = 0;
-      qualifiers[i].high_bound = 0;
-      qualifiers[i].stride = 0;
-    }
-
-  if (AUX_GET_ISYM (bigendian, &aux_ptr[indx]) == -1)
-    return "-1 (no type)";
-  ecoff_swap_tir_in (bigendian, &aux_ptr[indx++].a_ti, &u.ti);
-
-  basic_type = u.ti.bt;
-  qualifiers[0].type = u.ti.tq0;
-  qualifiers[1].type = u.ti.tq1;
-  qualifiers[2].type = u.ti.tq2;
-  qualifiers[3].type = u.ti.tq3;
-  qualifiers[4].type = u.ti.tq4;
-  qualifiers[5].type = u.ti.tq5;
-  qualifiers[6].type = tqNil;
-
-  /*
-   * Go get the basic type.
-   */
-  switch (basic_type)
-    {
-    case btNil:                        /* undefined */
-      strcpy (p1, "nil");
-      break;
-
-    case btAdr:                        /* address - integer same size as pointer */
-      strcpy (p1, "address");
-      break;
-
-    case btChar:               /* character */
-      strcpy (p1, "char");
-      break;
-
-    case btUChar:              /* unsigned character */
-      strcpy (p1, "unsigned char");
-      break;
-
-    case btShort:              /* short */
-      strcpy (p1, "short");
-      break;
-
-    case btUShort:             /* unsigned short */
-      strcpy (p1, "unsigned short");
-      break;
-
-    case btInt:                        /* int */
-      strcpy (p1, "int");
-      break;
-
-    case btUInt:               /* unsigned int */
-      strcpy (p1, "unsigned int");
-      break;
-
-    case btLong:               /* long */
-      strcpy (p1, "long");
-      break;
-
-    case btULong:              /* unsigned long */
-      strcpy (p1, "unsigned long");
-      break;
-
-    case btFloat:              /* float (real) */
-      strcpy (p1, "float");
-      break;
-
-    case btDouble:             /* Double (real) */
-      strcpy (p1, "double");
-      break;
-
-      /* Structures add 1-2 aux words:
-        1st word is [ST_RFDESCAPE, offset] pointer to struct def;
-        2nd word is file index if 1st word rfd is ST_RFDESCAPE.  */
-
-    case btStruct:             /* Structure (Record) */
-      ecoff_swap_rndx_in (bigendian, &aux_ptr[indx].a_rndx, &rndx);
-      ecoff_emit_aggregate (abfd, p1, &rndx,
-                           AUX_GET_ISYM (bigendian, &aux_ptr[indx+1]),
-                           "struct");
-      indx++;                  /* skip aux words */
-      break;
-
-      /* Unions add 1-2 aux words:
-        1st word is [ST_RFDESCAPE, offset] pointer to union def;
-        2nd word is file index if 1st word rfd is ST_RFDESCAPE.  */
-
-    case btUnion:              /* Union */
-      ecoff_swap_rndx_in (bigendian, &aux_ptr[indx].a_rndx, &rndx);
-      ecoff_emit_aggregate (abfd, p1, &rndx,
-                           AUX_GET_ISYM (bigendian, &aux_ptr[indx+1]),
-                           "union");
-      indx++;                  /* skip aux words */
-      break;
-
-      /* Enumerations add 1-2 aux words:
-        1st word is [ST_RFDESCAPE, offset] pointer to enum def;
-        2nd word is file index if 1st word rfd is ST_RFDESCAPE.  */
-
-    case btEnum:               /* Enumeration */
-      ecoff_swap_rndx_in (bigendian, &aux_ptr[indx].a_rndx, &rndx);
-      ecoff_emit_aggregate (abfd, p1, &rndx,
-                           AUX_GET_ISYM (bigendian, &aux_ptr[indx+1]),
-                           "enum");
-      indx++;                  /* skip aux words */
-      break;
-
-    case btTypedef:            /* defined via a typedef, isymRef points */
-      strcpy (p1, "typedef");
-      break;
-
-    case btRange:              /* subrange of int */
-      strcpy (p1, "subrange");
-      break;
-
-    case btSet:                        /* pascal sets */
-      strcpy (p1, "set");
-      break;
-
-    case btComplex:            /* fortran complex */
-      strcpy (p1, "complex");
-      break;
-
-    case btDComplex:           /* fortran double complex */
-      strcpy (p1, "double complex");
-      break;
-
-    case btIndirect:           /* forward or unnamed typedef */
-      strcpy (p1, "forward/unamed typedef");
-      break;
-
-    case btFixedDec:           /* Fixed Decimal */
-      strcpy (p1, "fixed decimal");
-      break;
-
-    case btFloatDec:           /* Float Decimal */
-      strcpy (p1, "float decimal");
-      break;
-
-    case btString:             /* Varying Length Character String */
-      strcpy (p1, "string");
-      break;
-
-    case btBit:                        /* Aligned Bit String */
-      strcpy (p1, "bit");
-      break;
-
-    case btPicture:            /* Picture */
-      strcpy (p1, "picture");
-      break;
-
-    case btVoid:               /* Void */
-      strcpy (p1, "void");
-      break;
-
-    default:
-      sprintf (p1, "Unknown basic type %d", (int) basic_type);
-      break;
-    }
-
-  p1 += strlen (buffer1);
-
-  /*
-   * If this is a bitfield, get the bitsize.
-   */
-  if (u.ti.fBitfield)
-    {
-      int bitsize;
+  /* Reloc type 0 is ignored.  The reloc reading code ensures that
+     this is a reference to the .abs section, which will cause
+     bfd_perform_relocation to do nothing.  */
+  HOWTO (MIPS_R_IGNORE,        /* type */
+        0,                     /* rightshift */
+        0,                     /* size (0 = byte, 1 = short, 2 = long) */
+        8,                     /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        0,                     /* special_function */
+        "IGNORE",              /* name */
+        false,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0,                     /* dst_mask */
+        false),                /* pcrel_offset */
 
-      bitsize = AUX_GET_WIDTH (bigendian, &aux_ptr[indx++]);
-      sprintf (p1, " : %d", bitsize);
-      p1 += strlen (buffer1);
-    }
+  /* A 16 bit reference to a symbol, normally from a data section.  */
+  HOWTO (MIPS_R_REFHALF,       /* type */
+        0,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield, /* complain_on_overflow */
+        mips_generic_reloc,    /* special_function */
+        "REFHALF",             /* name */
+        true,                  /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
 
+  /* A 32 bit reference to a symbol, normally from a data section.  */
+  HOWTO (MIPS_R_REFWORD,       /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        32,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield, /* complain_on_overflow */
+        mips_generic_reloc,    /* special_function */
+        "REFWORD",             /* name */
+        true,                  /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        false),                /* pcrel_offset */
 
-  /*
-   * Deal with any qualifiers.
-   */
-  if (qualifiers[0].type != tqNil)
-    {
-      /*
-       * Snarf up any array bounds in the correct order.  Arrays
-       * store 5 successive words in the aux. table:
-       *       word 0  RNDXR to type of the bounds (ie, int)
-       *       word 1  Current file descriptor index
-       *       word 2  low bound
-       *       word 3  high bound (or -1 if [])
-       *       word 4  stride size in bits
-       */
-      for (i = 0; i < 7; i++)
-       {
-         if (qualifiers[i].type == tqArray)
-           {
-             qualifiers[i].low_bound =
-               AUX_GET_DNLOW (bigendian, &aux_ptr[indx+2]);
-             qualifiers[i].high_bound =
-               AUX_GET_DNHIGH (bigendian, &aux_ptr[indx+3]);
-             qualifiers[i].stride =
-               AUX_GET_WIDTH (bigendian, &aux_ptr[indx+4]);
-             indx += 5;
-           }
-       }
+  /* A 26 bit absolute jump address.  */
+  HOWTO (MIPS_R_JMPADDR,       /* type */
+        2,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        26,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+                               /* This needs complex overflow
+                                  detection, because the upper four
+                                  bits must match the PC.  */
+        mips_generic_reloc,    /* special_function */
+        "JMPADDR",             /* name */
+        true,                  /* partial_inplace */
+        0x3ffffff,             /* src_mask */
+        0x3ffffff,             /* dst_mask */
+        false),                /* pcrel_offset */
 
-      /*
-       * Now print out the qualifiers.
-       */
-      for (i = 0; i < 6; i++)
-       {
-         switch (qualifiers[i].type)
-           {
-           case tqNil:
-           case tqMax:
-             break;
+  /* The high 16 bits of a symbol value.  Handled by the function
+     mips_refhi_reloc.  */
+  HOWTO (MIPS_R_REFHI,         /* type */
+        16,                    /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield, /* complain_on_overflow */
+        mips_refhi_reloc,      /* special_function */
+        "REFHI",               /* name */
+        true,                  /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
 
-           case tqPtr:
-             strcpy (p2, "ptr to ");
-             p2 += sizeof ("ptr to ")-1;
-             break;
+  /* The low 16 bits of a symbol value.  */
+  HOWTO (MIPS_R_REFLO,         /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        mips_reflo_reloc,      /* special_function */
+        "REFLO",               /* name */
+        true,                  /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
 
-           case tqVol:
-             strcpy (p2, "volatile ");
-             p2 += sizeof ("volatile ")-1;
-             break;
+  /* A reference to an offset from the gp register.  Handled by the
+     function mips_gprel_reloc.  */
+  HOWTO (MIPS_R_GPREL,         /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        mips_gprel_reloc,      /* special_function */
+        "GPREL",               /* name */
+        true,                  /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
 
-           case tqFar:
-             strcpy (p2, "far ");
-             p2 += sizeof ("far ")-1;
-             break;
+  /* A reference to a literal using an offset from the gp register.
+     Handled by the function mips_gprel_reloc.  */
+  HOWTO (MIPS_R_LITERAL,       /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        mips_gprel_reloc,      /* special_function */
+        "LITERAL",             /* name */
+        true,                  /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        false),                /* pcrel_offset */
 
-           case tqProc:
-             strcpy (p2, "func. ret. ");
-             p2 += sizeof ("func. ret. ");
-             break;
+  { 8 },
+  { 9 },
+  { 10 },
+  { 11 },
 
-           case tqArray:
-             {
-               int first_array = i;
-               int j;
-
-               /* Print array bounds reversed (ie, in the order the C
-                  programmer writes them).  C is such a fun language.... */
-
-               while (i < 5 && qualifiers[i+1].type == tqArray)
-                 i++;
-
-               for (j = i; j >= first_array; j--)
-                 {
-                   strcpy (p2, "array [");
-                   p2 += sizeof ("array [")-1;
-                   if (qualifiers[j].low_bound != 0)
-                     sprintf (p2,
-                              "%ld:%ld {%ld bits}",
-                              (long) qualifiers[j].low_bound,
-                              (long) qualifiers[j].high_bound,
-                              (long) qualifiers[j].stride);
-
-                   else if (qualifiers[j].high_bound != -1)
-                     sprintf (p2,
-                              "%ld {%ld bits}",
-                              (long) (qualifiers[j].high_bound + 1),
-                              (long) (qualifiers[j].stride));
-
-                   else
-                     sprintf (p2, " {%ld bits}", (long) (qualifiers[j].stride));
-
-                   p2 += strlen (p2);
-                   strcpy (p2, "] of ");
-                   p2 += sizeof ("] of ")-1;
-                 }
-             }
-             break;
-           }
-       }
-    }
+  /* This reloc is a Cygnus extension used when generating position
+     independent code for embedded systems.  It represents a 16 bit PC
+     relative reloc rightshifted twice as used in the MIPS branch
+     instructions.  */
+  HOWTO (MIPS_R_PCREL16,       /* type */
+        2,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        true,                  /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        mips_generic_reloc,    /* special_function */
+        "PCREL16",             /* name */
+        true,                  /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        true),                 /* pcrel_offset */
+
+  /* This reloc is a Cygnus extension used when generating position
+     independent code for embedded systems.  It represents the high 16
+     bits of a PC relative reloc.  The next reloc must be
+     MIPS_R_RELLO, and the addend is formed from the addends of the
+     two instructions, just as in MIPS_R_REFHI and MIPS_R_REFLO.  The
+     final value is actually PC relative to the location of the
+     MIPS_R_RELLO reloc, not the MIPS_R_RELHI reloc.  */
+  HOWTO (MIPS_R_RELHI,         /* type */
+        16,                    /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        true,                  /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield, /* complain_on_overflow */
+        mips_relhi_reloc,      /* special_function */
+        "RELHI",               /* name */
+        true,                  /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        true),                 /* pcrel_offset */
 
-  strcpy (p2, buffer1);
-  return buffer2;
-}
+  /* This reloc is a Cygnus extension used when generating position
+     independent code for embedded systems.  It represents the low 16
+     bits of a PC relative reloc.  */
+  HOWTO (MIPS_R_RELLO,         /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        true,                  /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        mips_rello_reloc,      /* special_function */
+        "RELLO",               /* name */
+        true,                  /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        true),                 /* pcrel_offset */
+
+  { 15 },
+  { 16 },
+  { 17 },
+  { 18 },
+  { 19 },
+  { 20 },
+  { 21 },
+
+  /* This reloc is a Cygnus extension used when generating position
+     independent code for embedded systems.  It represents an entry in
+     a switch table, which is the difference between two symbols in
+     the .text section.  The symndx is actually the offset from the
+     reloc address to the subtrahend.  See include/coff/mips.h for
+     more details.  */
+  HOWTO (MIPS_R_SWITCH,                /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        32,                    /* bitsize */
+        true,                  /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        mips_switch_reloc,     /* special_function */
+        "SWITCH",              /* name */
+        true,                  /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        true)                  /* pcrel_offset */
+};
 
-/* Print information about an ECOFF symbol.  */
+/* When the linker is doing relaxing, it may change a external PCREL16
+   reloc.  This typically represents an instruction like
+       bal foo
+   We change it to
+       .set  noreorder
+       bal   $L1
+       lui   $at,%hi(foo - $L1)
+     $L1:
+       addiu $at,%lo(foo - $L1)
+       addu  $at,$at,$31
+       jalr  $at
+   PCREL16_EXPANSION_ADJUSTMENT is the number of bytes this changes the
+   instruction by.  */
+
+#define PCREL16_EXPANSION_ADJUSTMENT (4 * 4)
+\f
+/* See whether the magic number matches.  */
 
-static void
-ecoff_print_symbol (abfd, filep, symbol, how)
+static boolean
+mips_ecoff_bad_format_hook (abfd, filehdr)
      bfd *abfd;
-     PTR filep;
-     asymbol *symbol;
-     bfd_print_symbol_type how;
+     PTR filehdr;
 {
-  FILE *file = (FILE *)filep;
+  struct internal_filehdr *internal_f = (struct internal_filehdr *) filehdr;
 
-  switch (how)
+  switch (internal_f->f_magic)
     {
-    case bfd_print_symbol_name:
-      fprintf (file, "%s", symbol->name);
-      break;
-    case bfd_print_symbol_more:
-      if (ecoffsymbol (symbol)->local)
-       {
-         SYMR ecoff_sym;
-       
-         ecoff_swap_sym_in (abfd, ecoffsymbol (symbol)->native.lnative,
-                            &ecoff_sym);
-         fprintf (file, "ecoff local %lx %x %x",
-                  (unsigned long) ecoff_sym.value,
-                  (unsigned) ecoff_sym.st, (unsigned) ecoff_sym.sc);
-       }
-      else
-       {
-         EXTR ecoff_ext;
-
-         ecoff_swap_ext_in (abfd, ecoffsymbol (symbol)->native.enative,
-                            &ecoff_ext);
-         fprintf (file, "ecoff extern %lx %x %x",
-                  (unsigned long) ecoff_ext.asym.value,
-                  (unsigned) ecoff_ext.asym.st,
-                  (unsigned) ecoff_ext.asym.sc);
-       }
-      break;
-    case bfd_print_symbol_nm:
-      {
-       CONST char *section_name = symbol->section->name;
-
-       bfd_print_symbol_vandf ((PTR) file, symbol);
-       fprintf (file, " %-5s %s %s",
-                section_name,
-                ecoffsymbol (symbol)->local ? "l" : "e",
-                symbol->name);
-      }
-      break;
-    case bfd_print_symbol_all:
-      /* Print out the symbols in a reasonable way */
-      {
-       char type;
-       int pos;
-       EXTR ecoff_ext;
-       char jmptbl;
-       char cobol_main;
-       char weakext;
-
-       if (ecoffsymbol (symbol)->local)
-         {
-           ecoff_swap_sym_in (abfd, ecoffsymbol (symbol)->native.lnative,
-                              &ecoff_ext.asym);
-           type = 'l';
-           pos = (ecoffsymbol (symbol)->native.lnative
-                  - ecoff_data (abfd)->external_sym
-                  + ecoff_data (abfd)->symbolic_header.iextMax);
-           jmptbl = ' ';
-           cobol_main = ' ';
-           weakext = ' ';
-         }
-       else
-         {
-           ecoff_swap_ext_in (abfd, ecoffsymbol (symbol)->native.enative,
-                              &ecoff_ext);
-           type = 'e';
-           pos = (ecoffsymbol (symbol)->native.enative
-                  - ecoff_data (abfd)->external_ext);
-           jmptbl = ecoff_ext.jmptbl ? 'j' : ' ';
-           cobol_main = ecoff_ext.cobol_main ? 'c' : ' ';
-           weakext = ecoff_ext.weakext ? 'w' : ' ';
-         }
-
-       fprintf (file, "[%3d] %c %lx st %x sc %x indx %x %c%c%c %s",
-                pos, type, (unsigned long) ecoff_ext.asym.value,
-                (unsigned) ecoff_ext.asym.st,
-                (unsigned) ecoff_ext.asym.sc,
-                (unsigned) ecoff_ext.asym.index,
-                jmptbl, cobol_main, weakext,
-                symbol->name);
-
-       if (ecoffsymbol (symbol)->fdr != NULL
-           && ecoff_ext.asym.index != indexNil)
-         {
-           unsigned indx;
-           int bigendian;
-           long sym_base;
-           union aux_ext *aux_base;
-
-           indx = ecoff_ext.asym.index;
-
-           /* sym_base is used to map the fdr relative indices which
-              appear in the file to the position number which we are
-              using.  */
-           sym_base = ecoffsymbol (symbol)->fdr->isymBase;
-           if (ecoffsymbol (symbol)->local)
-             sym_base += ecoff_data (abfd)->symbolic_header.iextMax;
-
-           /* aux_base is the start of the aux entries for this file;
-              asym.index is an offset from this.  */
-           aux_base = (ecoff_data (abfd)->external_aux
-                       + ecoffsymbol (symbol)->fdr->iauxBase);
-
-           /* The aux entries are stored in host byte order; the
-              order is indicated by a bit in the fdr.  */
-           bigendian = ecoffsymbol (symbol)->fdr->fBigendian;
-
-           /* This switch is basically from gcc/mips-tdump.c  */
-           switch (ecoff_ext.asym.st)
-             {
-             case stNil:
-             case stLabel:
-               break;
-
-             case stFile:
-             case stBlock:
-               printf ("\n      End+1 symbol: %ld", indx + sym_base);
-               break;
+    case MIPS_MAGIC_1:
+      /* I don't know what endianness this implies.  */
+      return true;
 
-             case stEnd:
-               if (ecoff_ext.asym.sc == scText
-                   || ecoff_ext.asym.sc == scInfo)
-                 printf ("\n      First symbol: %ld", indx + sym_base);
-               else
-                 printf ("\n      First symbol: %ld", 
-                         (AUX_GET_ISYM (bigendian,
-                                        &aux_base[ecoff_ext.asym.index])
-                          + sym_base));
-               break;
+    case MIPS_MAGIC_BIG:
+    case MIPS_MAGIC_BIG2:
+    case MIPS_MAGIC_BIG3:
+      return bfd_big_endian (abfd);
 
-             case stProc:
-             case stStaticProc:
-               if (MIPS_IS_STAB (&ecoff_ext.asym))
-                 ;
-               else if (ecoffsymbol (symbol)->local)
-                 printf ("\n      End+1 symbol: %-7ld   Type:  %s",
-                         (AUX_GET_ISYM (bigendian,
-                                        &aux_base[ecoff_ext.asym.index])
-                          + sym_base),
-                         ecoff_type_to_string (abfd, aux_base, indx + 1,
-                                               bigendian));
-               else
-                 printf ("\n      Local symbol: %d",
-                         (indx
-                          + sym_base
-                          + ecoff_data (abfd)->symbolic_header.iextMax));
-               break;
+    case MIPS_MAGIC_LITTLE:
+    case MIPS_MAGIC_LITTLE2:
+    case MIPS_MAGIC_LITTLE3:
+      return bfd_little_endian (abfd);
 
-             default:
-               if (!MIPS_IS_STAB (&ecoff_ext.asym))
-                 printf ("\n      Type: %s",
-                         ecoff_type_to_string (abfd, aux_base, indx,
-                                               bigendian));
-               break;
-             }
-         }
-      }
-      break;
+    default:
+      return false;
     }
 }
 \f
@@ -1427,13 +415,15 @@ ecoff_print_symbol (abfd, filep, symbol, how)
 /* Swap a reloc in.  */
 
 static void
-ecoff_swap_reloc_in (abfd, ext, intern)
+mips_ecoff_swap_reloc_in (abfd, ext_ptr, intern)
      bfd *abfd;
-     RELOC *ext;
+     PTR ext_ptr;
      struct internal_reloc *intern;
 {
+  const RELOC *ext = (RELOC *) ext_ptr;
+
   intern->r_vaddr = bfd_h_get_32 (abfd, (bfd_byte *) ext->r_vaddr);
-  if (abfd->xvec->header_byteorder_big_p != false)
+  if (bfd_header_big_endian (abfd))
     {
       intern->r_symndx = (((int) ext->r_bits[0]
                           << RELOC_BITS0_SYMNDX_SH_LEFT_BIG)
@@ -1453,44 +443,142 @@ ecoff_swap_reloc_in (abfd, ext, intern)
                             << RELOC_BITS1_SYMNDX_SH_LEFT_LITTLE)
                          | ((int) ext->r_bits[2]
                             << RELOC_BITS2_SYMNDX_SH_LEFT_LITTLE));
-      intern->r_type = ((ext->r_bits[3] & RELOC_BITS3_TYPE_LITTLE)
-                       >> RELOC_BITS3_TYPE_SH_LITTLE);
+      intern->r_type = (((ext->r_bits[3] & RELOC_BITS3_TYPE_LITTLE)
+                        >> RELOC_BITS3_TYPE_SH_LITTLE)
+                       | ((ext->r_bits[3] & RELOC_BITS3_TYPEHI_LITTLE)
+                          << RELOC_BITS3_TYPEHI_SH_LITTLE));
       intern->r_extern = (ext->r_bits[3] & RELOC_BITS3_EXTERN_LITTLE) != 0;
     }
+
+  /* If this is a MIPS_R_SWITCH reloc, or an internal MIPS_R_RELHI or
+     MIPS_R_RELLO reloc, r_symndx is actually the offset from the
+     reloc address to the base of the difference (see
+     include/coff/mips.h for more details).  We copy symndx into the
+     r_offset field so as not to confuse ecoff_slurp_reloc_table in
+     ecoff.c.  In adjust_reloc_in we then copy r_offset into the reloc
+     addend.  */
+  if (intern->r_type == MIPS_R_SWITCH
+      || (! intern->r_extern
+         && (intern->r_type == MIPS_R_RELLO
+             || intern->r_type == MIPS_R_RELHI)))
+    {
+      BFD_ASSERT (! intern->r_extern);
+      intern->r_offset = intern->r_symndx;
+      if (intern->r_offset & 0x800000)
+       intern->r_offset -= 0x1000000;
+      intern->r_symndx = RELOC_SECTION_TEXT;
+    }
 }
 
 /* Swap a reloc out.  */
 
-static unsigned int
-ecoff_swap_reloc_out (abfd, src, dst)
+static void
+mips_ecoff_swap_reloc_out (abfd, intern, dst)
      bfd *abfd;
-     PTR src;
+     const struct internal_reloc *intern;
      PTR dst;
 {
-  struct internal_reloc *intern = (struct internal_reloc *) src;
   RELOC *ext = (RELOC *) dst;
+  long r_symndx;
+
+  BFD_ASSERT (intern->r_extern
+             || (intern->r_symndx >= 0 && intern->r_symndx <= 12));
+
+  /* If this is a MIPS_R_SWITCH reloc, or an internal MIPS_R_RELLO or
+     MIPS_R_RELHI reloc, we actually want to write the contents of
+     r_offset out as the symbol index.  This undoes the change made by
+     mips_ecoff_swap_reloc_in.  */
+  if (intern->r_type != MIPS_R_SWITCH
+      && (intern->r_extern
+         || (intern->r_type != MIPS_R_RELHI
+             && intern->r_type != MIPS_R_RELLO)))
+    r_symndx = intern->r_symndx;
+  else
+    {
+      BFD_ASSERT (intern->r_symndx == RELOC_SECTION_TEXT);
+      r_symndx = intern->r_offset & 0xffffff;
+    }
 
   bfd_h_put_32 (abfd, intern->r_vaddr, (bfd_byte *) ext->r_vaddr);
-  if (abfd->xvec->header_byteorder_big_p != false)
+  if (bfd_header_big_endian (abfd))
     {
-      ext->r_bits[0] = intern->r_symndx >> RELOC_BITS0_SYMNDX_SH_LEFT_BIG;
-      ext->r_bits[1] = intern->r_symndx >> RELOC_BITS1_SYMNDX_SH_LEFT_BIG;
-      ext->r_bits[2] = intern->r_symndx >> RELOC_BITS2_SYMNDX_SH_LEFT_BIG;
+      ext->r_bits[0] = r_symndx >> RELOC_BITS0_SYMNDX_SH_LEFT_BIG;
+      ext->r_bits[1] = r_symndx >> RELOC_BITS1_SYMNDX_SH_LEFT_BIG;
+      ext->r_bits[2] = r_symndx >> RELOC_BITS2_SYMNDX_SH_LEFT_BIG;
       ext->r_bits[3] = (((intern->r_type << RELOC_BITS3_TYPE_SH_BIG)
                         & RELOC_BITS3_TYPE_BIG)
                        | (intern->r_extern ? RELOC_BITS3_EXTERN_BIG : 0));
     }
   else
     {
-      ext->r_bits[0] = intern->r_symndx >> RELOC_BITS0_SYMNDX_SH_LEFT_LITTLE;
-      ext->r_bits[1] = intern->r_symndx >> RELOC_BITS1_SYMNDX_SH_LEFT_LITTLE;
-      ext->r_bits[2] = intern->r_symndx >> RELOC_BITS2_SYMNDX_SH_LEFT_LITTLE;
+      ext->r_bits[0] = r_symndx >> RELOC_BITS0_SYMNDX_SH_LEFT_LITTLE;
+      ext->r_bits[1] = r_symndx >> RELOC_BITS1_SYMNDX_SH_LEFT_LITTLE;
+      ext->r_bits[2] = r_symndx >> RELOC_BITS2_SYMNDX_SH_LEFT_LITTLE;
       ext->r_bits[3] = (((intern->r_type << RELOC_BITS3_TYPE_SH_LITTLE)
                         & RELOC_BITS3_TYPE_LITTLE)
+                       | ((intern->r_type >> RELOC_BITS3_TYPEHI_SH_LITTLE
+                           & RELOC_BITS3_TYPEHI_LITTLE))
                        | (intern->r_extern ? RELOC_BITS3_EXTERN_LITTLE : 0));
     }
+}
+
+/* Finish canonicalizing a reloc.  Part of this is generic to all
+   ECOFF targets, and that part is in ecoff.c.  The rest is done in
+   this backend routine.  It must fill in the howto field.  */
 
-  return RELSZ;
+static void
+mips_adjust_reloc_in (abfd, intern, rptr)
+     bfd *abfd;
+     const struct internal_reloc *intern;
+     arelent *rptr;
+{
+  if (intern->r_type > MIPS_R_SWITCH)
+    abort ();
+
+  if (! intern->r_extern
+      && (intern->r_type == MIPS_R_GPREL
+         || intern->r_type == MIPS_R_LITERAL))
+    rptr->addend += ecoff_data (abfd)->gp;
+
+  /* If the type is MIPS_R_IGNORE, make sure this is a reference to
+     the absolute section so that the reloc is ignored.  */
+  if (intern->r_type == MIPS_R_IGNORE)
+    rptr->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
+
+  /* If this is a MIPS_R_SWITCH reloc, or an internal MIPS_R_RELHI or
+     MIPS_R_RELLO reloc, we want the addend field of the BFD relocto
+     hold the value which was originally in the symndx field of the
+     internal MIPS ECOFF reloc.  This value was copied into
+     intern->r_offset by mips_swap_reloc_in, and here we copy it into
+     the addend field.  */
+  if (intern->r_type == MIPS_R_SWITCH
+      || (! intern->r_extern
+         && (intern->r_type == MIPS_R_RELHI
+             || intern->r_type == MIPS_R_RELLO)))
+    rptr->addend = intern->r_offset;
+
+  rptr->howto = &ecoff_backend (abfd)->howto_table[intern->r_type];
+}
+
+/* Make any adjustments needed to a reloc before writing it out.  None
+   are needed for MIPS.  */
+
+static void
+mips_adjust_reloc_out (abfd, rel, intern)
+     bfd *abfd;
+     const arelent *rel;
+     struct internal_reloc *intern;
+{
+  /* For a MIPS_R_SWITCH reloc, or an internal MIPS_R_RELHI or
+     MIPS_R_RELLO reloc, we must copy rel->addend into
+     intern->r_offset.  This will then be written out as the symbol
+     index by mips_ecoff_swap_reloc_out.  This operation parallels the
+     action of mips_adjust_reloc_in.  */
+  if (intern->r_type == MIPS_R_SWITCH
+      || (! intern->r_extern
+         && (intern->r_type == MIPS_R_RELHI
+             || intern->r_type == MIPS_R_RELLO)))
+    intern->r_offset = rel->addend;
 }
 
 /* ECOFF relocs are either against external symbols, or against
@@ -1505,18 +593,20 @@ ecoff_swap_reloc_out (abfd, src, dst)
    relocateable output against an external symbol.  */
 
 static bfd_reloc_status_type
-ecoff_generic_reloc (abfd,
-                    reloc_entry,
-                    symbol,
-                    data,
-                    input_section,
-                    output_bfd)
+mips_generic_reloc (abfd,
+                   reloc_entry,
+                   symbol,
+                   data,
+                   input_section,
+                   output_bfd,
+                   error_message)
      bfd *abfd;
      arelent *reloc_entry;
      asymbol *symbol;
      PTR data;
      asection *input_section;
      bfd *output_bfd;
+     char **error_message;
 {
   if (output_bfd != (bfd *) NULL
       && (symbol->flags & BSF_SECTION_SYM) == 0
@@ -1532,28 +622,42 @@ ecoff_generic_reloc (abfd,
 /* Do a REFHI relocation.  This has to be done in combination with a
    REFLO reloc, because there is a carry from the REFLO to the REFHI.
    Here we just save the information we need; we do the actual
-   relocation when we see the REFLO.  ECOFF requires that the REFLO
-   immediately follow the REFHI, so this ought to work.  */
+   relocation when we see the REFLO.  MIPS ECOFF requires that the
+   REFLO immediately follow the REFHI.  As a GNU extension, we permit
+   an arbitrary number of HI relocs to be associated with a single LO
+   reloc.  This extension permits gcc to output the HI and LO relocs
+   itself.  */
+
+struct mips_hi
+{
+  struct mips_hi *next;
+  bfd_byte *addr;
+  bfd_vma addend;
+};
+
+/* FIXME: This should not be a static variable.  */
 
-static bfd_byte *ecoff_refhi_addr;
-static bfd_vma ecoff_refhi_addend;
+static struct mips_hi *mips_refhi_list;
 
 static bfd_reloc_status_type
-ecoff_refhi_reloc (abfd,
-                  reloc_entry,
-                  symbol,
-                  data,
-                  input_section,
-                  output_bfd)
+mips_refhi_reloc (abfd,
+                 reloc_entry,
+                 symbol,
+                 data,
+                 input_section,
+                 output_bfd,
+                 error_message)
      bfd *abfd;
      arelent *reloc_entry;
      asymbol *symbol;
      PTR data;
      asection *input_section;
      bfd *output_bfd;
+     char **error_message;
 {
   bfd_reloc_status_type ret;
   bfd_vma relocation;
+  struct mips_hi *n;
 
   /* If we're relocating, and this an external symbol, we don't want
      to change anything.  */
@@ -1566,7 +670,7 @@ ecoff_refhi_reloc (abfd,
     }
 
   ret = bfd_reloc_ok;
-  if (symbol->section == &bfd_und_section
+  if (bfd_is_und_section (symbol->section)
       && output_bfd == (bfd *) NULL)
     ret = bfd_reloc_undefined;
 
@@ -1583,8 +687,13 @@ ecoff_refhi_reloc (abfd,
     return bfd_reloc_outofrange;
 
   /* Save the information, and let REFLO do the actual relocation.  */
-  ecoff_refhi_addr = (bfd_byte *) data + reloc_entry->address;
-  ecoff_refhi_addend = relocation;
+  n = (struct mips_hi *) bfd_malloc (sizeof *n);
+  if (n == NULL)
+    return bfd_reloc_outofrange;
+  n->addr = (bfd_byte *) data + reloc_entry->address;
+  n->addend = relocation;
+  n->next = mips_refhi_list;
+  mips_refhi_list = n;
 
   if (output_bfd != (bfd *) NULL)
     reloc_entry->address += input_section->output_offset;
@@ -1597,74 +706,91 @@ ecoff_refhi_reloc (abfd,
    relocation described above.  */
 
 static bfd_reloc_status_type
-ecoff_reflo_reloc (abfd,
-                  reloc_entry,
-                  symbol,
-                  data,
-                  input_section,
-                  output_bfd)
+mips_reflo_reloc (abfd,
+                 reloc_entry,
+                 symbol,
+                 data,
+                 input_section,
+                 output_bfd,
+                 error_message)
      bfd *abfd;
      arelent *reloc_entry;
      asymbol *symbol;
      PTR data;
      asection *input_section;
      bfd *output_bfd;
+     char **error_message;
 {
-  if (ecoff_refhi_addr != (bfd_byte *) NULL)
+  if (mips_refhi_list != NULL)
     {
-      unsigned long insn;
-      unsigned long val;
-      unsigned long vallo;
-
-      /* Do the REFHI relocation.  Note that we actually don't need to
-        know anything about the REFLO itself, except where to find
-        the low 16 bits of the addend needed by the REFHI.  */
-      insn = bfd_get_32 (abfd, ecoff_refhi_addr);
-      vallo = (bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address)
-              & 0xffff);
-      val = ((insn & 0xffff) << 16) + vallo;
-      val += ecoff_refhi_addend;
-
-      /* The low order 16 bits are always treated as a signed value.
-        Therefore, a negative value in the low order bits requires an
-        adjustment in the high order bits.  We need to make this
-        adjustment in two ways: once for the bits we took from the
-        data, and once for the bits we are putting back in to the
-        data.  */
-      if ((vallo & 0x8000) != 0)
-       val -= 0x10000;
-      if ((val & 0x8000) != 0)
-       val += 0x10000;
-
-      insn = (insn &~ 0xffff) | ((val >> 16) & 0xffff);
-      bfd_put_32 (abfd, insn, ecoff_refhi_addr);
-
-      ecoff_refhi_addr = (bfd_byte *) NULL;
+      struct mips_hi *l;
+
+      l = mips_refhi_list;
+      while (l != NULL)
+       {
+         unsigned long insn;
+         unsigned long val;
+         unsigned long vallo;
+         struct mips_hi *next;
+
+         /* Do the REFHI relocation.  Note that we actually don't
+            need to know anything about the REFLO itself, except
+            where to find the low 16 bits of the addend needed by the
+            REFHI.  */
+         insn = bfd_get_32 (abfd, l->addr);
+         vallo = (bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address)
+                  & 0xffff);
+         val = ((insn & 0xffff) << 16) + vallo;
+         val += l->addend;
+
+         /* The low order 16 bits are always treated as a signed
+            value.  Therefore, a negative value in the low order bits
+            requires an adjustment in the high order bits.  We need
+            to make this adjustment in two ways: once for the bits we
+            took from the data, and once for the bits we are putting
+            back in to the data.  */
+         if ((vallo & 0x8000) != 0)
+           val -= 0x10000;
+         if ((val & 0x8000) != 0)
+           val += 0x10000;
+
+         insn = (insn &~ 0xffff) | ((val >> 16) & 0xffff);
+         bfd_put_32 (abfd, insn, l->addr);
+
+         next = l->next;
+         free (l);
+         l = next;
+       }
+
+      mips_refhi_list = NULL;
     }
 
   /* Now do the REFLO reloc in the usual way.  */
-  return ecoff_generic_reloc (abfd, reloc_entry, symbol, data,
-                             input_section, output_bfd);
+  return mips_generic_reloc (abfd, reloc_entry, symbol, data,
+                             input_section, output_bfd, error_message);
 }
 
 /* Do a GPREL relocation.  This is a 16 bit value which must become
    the offset from the gp register.  */
 
 static bfd_reloc_status_type
-ecoff_gprel_reloc (abfd,
-                  reloc_entry,
-                  symbol,
-                  data,
-                  input_section,
-                  output_bfd)
+mips_gprel_reloc (abfd,
+                 reloc_entry,
+                 symbol,
+                 data,
+                 input_section,
+                 output_bfd,
+                 error_message)
      bfd *abfd;
      arelent *reloc_entry;
      asymbol *symbol;
      PTR data;
      asection *input_section;
      bfd *output_bfd;
+     char **error_message;
 {
   boolean relocateable;
+  bfd_vma gp;
   bfd_vma relocation;
   unsigned long val;
   unsigned long insn;
@@ -1689,7 +815,7 @@ ecoff_gprel_reloc (abfd,
       output_bfd = symbol->section->output_section->owner;
     }
 
-  if (symbol->section == &bfd_und_section
+  if (bfd_is_und_section (symbol->section)
       && relocateable == false)
     return bfd_reloc_undefined;
 
@@ -1698,15 +824,16 @@ ecoff_gprel_reloc (abfd,
      BFD.  If we can't find it, we're stuck.  We cache it in the ECOFF
      target data.  We don't need to adjust the symbol value for an
      external symbol if we are producing relocateable output.  */
-  if (ecoff_data (output_bfd)->gp == 0
+  gp = _bfd_get_gp_value (output_bfd);
+  if (gp == 0
       && (relocateable == false
          || (symbol->flags & BSF_SECTION_SYM) != 0))
     {
       if (relocateable != false)
        {
          /* Make up a value.  */
-         ecoff_data (output_bfd)->gp =
-           symbol->section->output_section->vma + 0x4000;
+         gp = symbol->section->output_section->vma + 0x4000;
+         _bfd_set_gp_value (output_bfd, gp);
        }
       else
        {
@@ -1717,27 +844,33 @@ ecoff_gprel_reloc (abfd,
          count = bfd_get_symcount (output_bfd);
          sym = bfd_get_outsymbols (output_bfd);
 
-         /* We should do something more friendly here, but we don't
-            have a good reloc status to return.  */
          if (sym == (asymbol **) NULL)
-           abort ();
-
-         for (i = 0; i < count; i++, sym++)
+           i = count;
+         else
            {
-             register CONST char *name;
-
-             name = bfd_asymbol_name (*sym);
-             if (*name == '_' && strcmp (name, "_gp") == 0)
+             for (i = 0; i < count; i++, sym++)
                {
-                 ecoff_data (output_bfd)->gp = bfd_asymbol_value (*sym);
-                 break;
+                 register CONST char *name;
+
+                 name = bfd_asymbol_name (*sym);
+                 if (*name == '_' && strcmp (name, "_gp") == 0)
+                   {
+                     gp = bfd_asymbol_value (*sym);
+                     _bfd_set_gp_value (output_bfd, gp);
+                     break;
+                   }
                }
            }
 
-         /* We should do something more friendly here, but we don't have
-            a good reloc status to return.  */
          if (i >= count)
-           abort ();
+           {
+             /* Only get the error once.  */
+             gp = 4;
+             _bfd_set_gp_value (output_bfd, gp);
+             *error_message =
+               (char *) "GP relative relocation when _gp not defined";
+             return bfd_reloc_dangerous;
+           }
        }
     }
 
@@ -1764,7 +897,7 @@ ecoff_gprel_reloc (abfd,
      an external symbol.  */
   if (relocateable == false
       || (symbol->flags & BSF_SECTION_SYM) != 0)
-    val += relocation - ecoff_data (output_bfd)->gp;
+    val += relocation - gp;
 
   insn = (insn &~ 0xffff) | (val & 0xffff);
   bfd_put_32 (abfd, insn, (bfd_byte *) data + reloc_entry->address);
@@ -1774,2406 +907,1811 @@ ecoff_gprel_reloc (abfd,
 
   /* Make sure it fit in 16 bits.  */
   if (val >= 0x8000 && val < 0xffff8000)
-    return bfd_reloc_outofrange;
+    return bfd_reloc_overflow;
 
   return bfd_reloc_ok;
 }
 
-/* How to process the various relocs types.  */
-
-static reloc_howto_type ecoff_howto_table[] =
-{
-  /* Reloc type 0 is ignored.  The reloc reading code ensures that
-     this is a reference to the .abs section, which will cause
-     bfd_perform_relocation to do nothing.  */
-  HOWTO (ECOFF_R_IGNORE,       /* type */
-        0,                     /* rightshift */
-        0,                     /* size (0 = byte, 1 = short, 2 = long) */
-        8,                     /* bitsize (obsolete) */
-        false,                 /* pc_relative */
-        0,                     /* bitpos */
-        false,                 /* absolute (obsolete) */
-        false,                 /* complain_on_overflow */
-        0,                     /* special_function */
-        "IGNORE",              /* name */
-        false,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0,                     /* dst_mask */
-        false),                /* pcrel_offset */
-
-  /* A 16 bit reference to a symbol, normally from a data section.  */
-  HOWTO (ECOFF_R_REFHALF,      /* type */
-        0,                     /* rightshift */
-        1,                     /* size (0 = byte, 1 = short, 2 = long) */
-        16,                    /* bitsize (obsolete) */
-        false,                 /* pc_relative */
-        0,                     /* bitpos */
-        false,                 /* absolute (obsolete) */
-        true,                  /* complain_on_overflow */
-        ecoff_generic_reloc,   /* special_function */
-        "REFHALF",             /* name */
-        true,                  /* partial_inplace */
-        0xffff,                /* src_mask */
-        0xffff,                /* dst_mask */
-        false),                /* pcrel_offset */
-
-  /* A 32 bit reference to a symbol, normally from a data section.  */
-  HOWTO (ECOFF_R_REFWORD,      /* type */
-        0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize (obsolete) */
-        false,                 /* pc_relative */
-        0,                     /* bitpos */
-        false,                 /* absolute (obsolete) */
-        true,                  /* complain_on_overflow */
-        ecoff_generic_reloc,   /* special_function */
-        "REFWORD",             /* name */
-        true,                  /* partial_inplace */
-        0xffffffff,            /* src_mask */
-        0xffffffff,            /* dst_mask */
-        false),                /* pcrel_offset */
-
-  /* A 26 bit absolute jump address.  */
-  HOWTO (ECOFF_R_JMPADDR,      /* type */
-        2,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize (obsolete) */
-        false,                 /* pc_relative */
-        0,                     /* bitpos */
-        false,                 /* absolute (obsolete) */
-        true,                  /* complain_on_overflow */
-        ecoff_generic_reloc,   /* special_function */
-        "JMPADDR",             /* name */
-        true,                  /* partial_inplace */
-        0x3ffffff,             /* src_mask */
-        0x3ffffff,             /* dst_mask */
-        false),                /* pcrel_offset */
-
-  /* The high 16 bits of a symbol value.  Handled by the function
-     ecoff_refhi_reloc.  */
-  HOWTO (ECOFF_R_REFHI,                /* type */
-        16,                    /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize (obsolete) */
-        false,                 /* pc_relative */
-        0,                     /* bitpos */
-        false,                 /* absolute (obsolete) */
-        true,                  /* complain_on_overflow */
-        ecoff_refhi_reloc,     /* special_function */
-        "REFHI",               /* name */
-        true,                  /* partial_inplace */
-        0xffff,                /* src_mask */
-        0xffff,                /* dst_mask */
-        false),                /* pcrel_offset */
-
-  /* The low 16 bits of a symbol value.  */
-  HOWTO (ECOFF_R_REFLO,                /* type */
-        0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize (obsolete) */
-        false,                 /* pc_relative */
-        0,                     /* bitpos */
-        false,                 /* absolute (obsolete) */
-        true,                  /* complain_on_overflow */
-        ecoff_reflo_reloc,     /* special_function */
-        "REFLO",               /* name */
-        true,                  /* partial_inplace */
-        0xffff,                /* src_mask */
-        0xffff,                /* dst_mask */
-        false),                /* pcrel_offset */
-
-  /* A reference to an offset from the gp register.  Handled by the
-     function ecoff_gprel_reloc.  */
-  HOWTO (ECOFF_R_GPREL,                /* type */
-        0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize (obsolete) */
-        false,                 /* pc_relative */
-        0,                     /* bitpos */
-        false,                 /* absolute (obsolete) */
-        true,                  /* complain_on_overflow */
-        ecoff_gprel_reloc,     /* special_function */
-        "GPREL",               /* name */
-        true,                  /* partial_inplace */
-        0xffff,                /* src_mask */
-        0xffff,                /* dst_mask */
-        false),                /* pcrel_offset */
-
-  /* A reference to a literal using an offset from the gp register.
-     Handled by the function ecoff_gprel_reloc.  */
-  HOWTO (ECOFF_R_LITERAL,      /* type */
-        0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize (obsolete) */
-        false,                 /* pc_relative */
-        0,                     /* bitpos */
-        false,                 /* absolute (obsolete) */
-        true,                  /* complain_on_overflow */
-        ecoff_gprel_reloc,     /* special_function */
-        "LITERAL",             /* name */
-        true,                  /* partial_inplace */
-        0xffff,                /* src_mask */
-        0xffff,                /* dst_mask */
-        false)                 /* pcrel_offset */
-};
+/* Do a RELHI relocation.  We do this in conjunction with a RELLO
+   reloc, just as REFHI and REFLO are done together.  RELHI and RELLO
+   are Cygnus extensions used when generating position independent
+   code for embedded systems.  */
 
-#define ECOFF_HOWTO_COUNT \
-  (sizeof ecoff_howto_table / sizeof ecoff_howto_table[0])
+/* FIXME: This should not be a static variable.  */
 
-/* Read in the relocs for a section.  */
+static struct mips_hi *mips_relhi_list;
 
-static boolean
-ecoff_slurp_reloc_table (abfd, section, symbols)
+static bfd_reloc_status_type
+mips_relhi_reloc (abfd,
+                 reloc_entry,
+                 symbol,
+                 data,
+                 input_section,
+                 output_bfd,
+                 error_message)
      bfd *abfd;
-     asection *section;
-     asymbol **symbols;
+     arelent *reloc_entry;
+     asymbol *symbol;
+     PTR data;
+     asection *input_section;
+     bfd *output_bfd;
+     char **error_message;
 {
-  RELOC *external_relocs;
-  arelent *internal_relocs;
-  arelent *rptr;
-  unsigned int i;
-
-  if (section->relocation != (arelent *) NULL
-      || section->reloc_count == 0
-      || (section->flags & SEC_CONSTRUCTOR) != 0)
-    return true;
+  bfd_reloc_status_type ret;
+  bfd_vma relocation;
+  struct mips_hi *n;
 
-  if (ecoff_slurp_symbol_table (abfd) == false)
-    return false;
-  
-  internal_relocs = (arelent *) bfd_alloc (abfd,
-                                          (sizeof (arelent)
-                                           * section->reloc_count));
-  external_relocs = (RELOC *) bfd_alloc (abfd, RELSZ * section->reloc_count);
-  if (internal_relocs == (arelent *) NULL
-      || external_relocs == (RELOC *) NULL)
+  /* If this is a reloc against a section symbol, then it is correct
+     in the object file.  The only time we want to change this case is
+     when we are relaxing, and that is handled entirely by
+     mips_relocate_section and never calls this function.  */
+  if ((symbol->flags & BSF_SECTION_SYM) != 0)
     {
-      bfd_error = no_memory;
-      return false;
+      if (output_bfd != (bfd *) NULL)
+       reloc_entry->address += input_section->output_offset;
+      return bfd_reloc_ok;
     }
-  if (bfd_seek (abfd, section->rel_filepos, SEEK_SET) != 0)
-    return false;
-  if (bfd_read (external_relocs, 1, RELSZ * section->reloc_count, abfd)
-      != RELSZ * section->reloc_count)
+
+  /* This is an external symbol.  If we're relocating, we don't want
+     to change anything.  */
+  if (output_bfd != (bfd *) NULL)
     {
-      bfd_error = system_call_error;
-      return false;
+      reloc_entry->address += input_section->output_offset;
+      return bfd_reloc_ok;
     }
 
-  for (i = 0, rptr = internal_relocs; i < section->reloc_count; i++, rptr++)
-    {
-      struct internal_reloc intern;
+  ret = bfd_reloc_ok;
+  if (bfd_is_und_section (symbol->section)
+      && output_bfd == (bfd *) NULL)
+    ret = bfd_reloc_undefined;
+
+  if (bfd_is_com_section (symbol->section))
+    relocation = 0;
+  else
+    relocation = symbol->value;
 
-      ecoff_swap_reloc_in (abfd, external_relocs + i, &intern);
+  relocation += symbol->section->output_section->vma;
+  relocation += symbol->section->output_offset;
+  relocation += reloc_entry->addend;
 
-      if (intern.r_type > ECOFF_R_LITERAL)
-       abort ();
+  if (reloc_entry->address > input_section->_cooked_size)
+    return bfd_reloc_outofrange;
 
-      if (intern.r_extern)
-       {
-         /* r_symndx is an index into the external symbols.  */
-         BFD_ASSERT (intern.r_symndx >= 0
-                     && (intern.r_symndx
-                         < ecoff_data (abfd)->symbolic_header.iextMax));
-         rptr->sym_ptr_ptr = symbols + intern.r_symndx;
-         rptr->addend = 0;
-       }
-      else
-       {
-         CONST char *sec_name;
-         asection *sec;
+  /* Save the information, and let RELLO do the actual relocation.  */
+  n = (struct mips_hi *) bfd_malloc (sizeof *n);
+  if (n == NULL)
+    return bfd_reloc_outofrange;
+  n->addr = (bfd_byte *) data + reloc_entry->address;
+  n->addend = relocation;
+  n->next = mips_relhi_list;
+  mips_relhi_list = n;
 
-         /* r_symndx is a section key.  */
-         switch (intern.r_symndx)
-           {
-           case RELOC_SECTION_TEXT:  sec_name = ".text";  break;
-           case RELOC_SECTION_RDATA: sec_name = ".rdata"; break;
-           case RELOC_SECTION_DATA:  sec_name = ".data";  break;
-           case RELOC_SECTION_SDATA: sec_name = ".sdata"; break;
-           case RELOC_SECTION_SBSS:  sec_name = ".sbss";  break;
-           case RELOC_SECTION_BSS:   sec_name = ".bss";   break;
-           case RELOC_SECTION_INIT:  sec_name = ".init";  break;
-           case RELOC_SECTION_LIT8:  sec_name = ".lit8";  break;
-           case RELOC_SECTION_LIT4:  sec_name = ".lit4";  break;
-           default: abort ();
-           }
+  if (output_bfd != (bfd *) NULL)
+    reloc_entry->address += input_section->output_offset;
 
-         sec = bfd_get_section_by_name (abfd, sec_name);
-         if (sec == (asection *) NULL)
-           abort ();
-         rptr->sym_ptr_ptr = sec->symbol_ptr_ptr;
+  return ret;
+}
+
+/* Do a RELLO relocation.  This is a straightforward 16 bit PC
+   relative relocation; this function exists in order to do the RELHI
+   relocation described above.  */
+
+static bfd_reloc_status_type
+mips_rello_reloc (abfd,
+                 reloc_entry,
+                 symbol,
+                 data,
+                 input_section,
+                 output_bfd,
+                 error_message)
+     bfd *abfd;
+     arelent *reloc_entry;
+     asymbol *symbol;
+     PTR data;
+     asection *input_section;
+     bfd *output_bfd;
+     char **error_message;
+{
+  if (mips_relhi_list != NULL)
+    {
+      struct mips_hi *l;
 
-         rptr->addend = - bfd_get_section_vma (abfd, sec);
-         if (intern.r_type == ECOFF_R_GPREL
-             || intern.r_type == ECOFF_R_LITERAL)
-           rptr->addend += ecoff_data (abfd)->gp;
+      l = mips_relhi_list;
+      while (l != NULL)
+       {
+         unsigned long insn;
+         unsigned long val;
+         unsigned long vallo;
+         struct mips_hi *next;
+
+         /* Do the RELHI relocation.  Note that we actually don't
+            need to know anything about the RELLO itself, except
+            where to find the low 16 bits of the addend needed by the
+            RELHI.  */
+         insn = bfd_get_32 (abfd, l->addr);
+         vallo = (bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address)
+                  & 0xffff);
+         val = ((insn & 0xffff) << 16) + vallo;
+         val += l->addend;
+
+         /* If the symbol is defined, make val PC relative.  If the
+            symbol is not defined we don't want to do this, because
+            we don't want the value in the object file to incorporate
+            the address of the reloc.  */
+         if (! bfd_is_und_section (bfd_get_section (symbol))
+             && ! bfd_is_com_section (bfd_get_section (symbol)))
+           val -= (input_section->output_section->vma
+                   + input_section->output_offset
+                   + reloc_entry->address);
+
+         /* The low order 16 bits are always treated as a signed
+            value.  Therefore, a negative value in the low order bits
+            requires an adjustment in the high order bits.  We need
+            to make this adjustment in two ways: once for the bits we
+            took from the data, and once for the bits we are putting
+            back in to the data.  */
+         if ((vallo & 0x8000) != 0)
+           val -= 0x10000;
+         if ((val & 0x8000) != 0)
+           val += 0x10000;
+
+         insn = (insn &~ 0xffff) | ((val >> 16) & 0xffff);
+         bfd_put_32 (abfd, insn, l->addr);
+
+         next = l->next;
+         free (l);
+         l = next;
        }
 
-      rptr->address = intern.r_vaddr - bfd_get_section_vma (abfd, section);
-      rptr->howto = &ecoff_howto_table[intern.r_type];
-
-      /* If the type is ECOFF_R_IGNORE, make sure this is a reference
-        to the absolute section so that the reloc is ignored.  */
-      if (intern.r_type == ECOFF_R_IGNORE)
-       rptr->sym_ptr_ptr = bfd_abs_section.symbol_ptr_ptr;
+      mips_relhi_list = NULL;
     }
 
-  bfd_release (abfd, external_relocs);
+  /* If this is a reloc against a section symbol, then it is correct
+     in the object file.  The only time we want to change this case is
+     when we are relaxing, and that is handled entirely by
+     mips_relocate_section and never calls this function.  */
+  if ((symbol->flags & BSF_SECTION_SYM) != 0)
+    {
+      if (output_bfd != (bfd *) NULL)
+       reloc_entry->address += input_section->output_offset;
+      return bfd_reloc_ok;
+    }
 
-  section->relocation = internal_relocs;
+  /* bfd_perform_relocation does not handle pcrel_offset relocations
+     correctly when generating a relocateable file, so handle them
+     directly here.  */
+  if (output_bfd != (bfd *) NULL)
+    {
+      reloc_entry->address += input_section->output_offset;
+      return bfd_reloc_ok;
+    }
 
-  return true;
+  /* Now do the RELLO reloc in the usual way.  */
+  return mips_generic_reloc (abfd, reloc_entry, symbol, data,
+                             input_section, output_bfd, error_message);
 }
 
-/* Get a canonical list of relocs.  */
+/* This is the special function for the MIPS_R_SWITCH reloc.  This
+   special reloc is normally correct in the object file, and only
+   requires special handling when relaxing.  We don't want
+   bfd_perform_relocation to tamper with it at all.  */
 
-static unsigned int
-ecoff_canonicalize_reloc (abfd, section, relptr, symbols)
+/*ARGSUSED*/
+static bfd_reloc_status_type
+mips_switch_reloc (abfd,
+                  reloc_entry,
+                  symbol,
+                  data,
+                  input_section,
+                  output_bfd,
+                  error_message)
      bfd *abfd;
-     asection *section;
-     arelent **relptr;
-     asymbol **symbols;
+     arelent *reloc_entry;
+     asymbol *symbol;
+     PTR data;
+     asection *input_section;
+     bfd *output_bfd;
+     char **error_message;
 {
-  unsigned int count;
-
-  if (section->flags & SEC_CONSTRUCTOR) 
-    {
-      arelent_chain *chain;
-
-      /* This section has relocs made up by us, not the file, so take
-        them out of their chain and place them into the data area
-        provided.  */
-      for (count = 0, chain = section->constructor_chain;
-          count < section->reloc_count;
-          count++, chain = chain->next)
-       *relptr++ = &chain->relent;
-    }
-  else
-    { 
-      arelent *tblptr;
-
-      if (ecoff_slurp_reloc_table (abfd, section, symbols) == false)
-       return 0;
-
-      tblptr = section->relocation;
-      if (tblptr == (arelent *) NULL)
-       return 0;
-
-      for (count = 0; count < section->reloc_count; count++)
-       *relptr++ = tblptr++;
-    }
-
-  *relptr = (arelent *) NULL;
-
-  return section->reloc_count;
+  return bfd_reloc_ok;
 }
 
 /* Get the howto structure for a generic reloc type.  */
 
-static CONST struct reloc_howto_struct *
-ecoff_bfd_reloc_type_lookup (abfd, code)
+static reloc_howto_type *
+mips_bfd_reloc_type_lookup (abfd, code)
      bfd *abfd;
      bfd_reloc_code_real_type code;
 {
-  int ecoff_type;
+  int mips_type;
 
   switch (code)
     {
     case BFD_RELOC_16:
-      ecoff_type = ECOFF_R_REFHALF;
+      mips_type = MIPS_R_REFHALF;
       break;
     case BFD_RELOC_32:
-      ecoff_type = ECOFF_R_REFWORD;
+    case BFD_RELOC_CTOR:
+      mips_type = MIPS_R_REFWORD;
       break;
     case BFD_RELOC_MIPS_JMP:
-      ecoff_type = ECOFF_R_JMPADDR;
+      mips_type = MIPS_R_JMPADDR;
       break;
     case BFD_RELOC_HI16_S:
-      ecoff_type = ECOFF_R_REFHI;
+      mips_type = MIPS_R_REFHI;
       break;
     case BFD_RELOC_LO16:
-      ecoff_type = ECOFF_R_REFLO;
+      mips_type = MIPS_R_REFLO;
       break;
     case BFD_RELOC_MIPS_GPREL:
-      ecoff_type = ECOFF_R_GPREL;
+      mips_type = MIPS_R_GPREL;
+      break;
+    case BFD_RELOC_MIPS_LITERAL:
+      mips_type = MIPS_R_LITERAL;
+      break;
+    case BFD_RELOC_16_PCREL_S2:
+      mips_type = MIPS_R_PCREL16;
+      break;
+    case BFD_RELOC_PCREL_HI16_S:
+      mips_type = MIPS_R_RELHI;
+      break;
+    case BFD_RELOC_PCREL_LO16:
+      mips_type = MIPS_R_RELLO;
+      break;
+    case BFD_RELOC_GPREL32:
+      mips_type = MIPS_R_SWITCH;
       break;
     default:
-      return (CONST struct reloc_howto_struct *) NULL;
+      return (reloc_howto_type *) NULL;
     }
 
-  return &ecoff_howto_table[ecoff_type];
+  return &ecoff_backend (abfd)->howto_table[mips_type];
 }
 \f
-/* Provided a BFD, a section and an offset into the section, calculate
-   and return the name of the source file and the line nearest to the
-   wanted location.  */
+/* A helper routine for mips_relocate_section which handles the REFHI
+   and RELHI relocations.  The REFHI relocation must be followed by a
+   REFLO relocation (and RELHI by a RELLO), and the addend used is
+   formed from the addends of both instructions.  */
 
-static boolean
-ecoff_find_nearest_line (abfd,
-                        section,
-                        ignore_symbols,
-                        offset,
-                        filename_ptr,
-                        functionname_ptr,
-                        retline_ptr)
-     bfd *abfd;
-     asection *section;
-     asymbol **ignore_symbols;
-     bfd_vma offset;
-     CONST char **filename_ptr;
-     CONST char **functionname_ptr;
-     unsigned int *retline_ptr;
+static void
+mips_relocate_hi (refhi, reflo, input_bfd, input_section, contents, adjust,
+                 relocation, pcrel)
+     struct internal_reloc *refhi;
+     struct internal_reloc *reflo;
+     bfd *input_bfd;
+     asection *input_section;
+     bfd_byte *contents;
+     size_t adjust;
+     bfd_vma relocation;
+     boolean pcrel;
 {
-  FDR *fdr_ptr;
-  FDR *fdr_start;
-  FDR *fdr_end;
-  FDR *fdr_hold;
-  struct pdr_ext *pdr_ptr;
-  struct pdr_ext *pdr_end;
-  PDR pdr;
-  unsigned char *line_ptr;
-  unsigned char *line_end;
-  int lineno;
-
-  /* If we're not in the .text section, we don't have any line
-     numbers.  */
-  if (strcmp (section->name, _TEXT) != 0
-      || offset < ecoff_data (abfd)->text_start
-      || offset >= ecoff_data (abfd)->text_end)
-    return false;
-
-  /* Make sure we have the FDR's.  */
-  if (ecoff_slurp_symbolic_info (abfd) == false
-      || bfd_get_symcount (abfd) == 0)
-    return false;
-
-  /* Each file descriptor (FDR) has a memory address.  Here we track
-     down which FDR we want.  The FDR's are stored in increasing
-     memory order.  If speed is ever important, this can become a
-     binary search.  We must ignore FDR's with no PDR entries; they
-     will have the adr of the FDR before or after them.  */
-  fdr_start = ecoff_data (abfd)->fdr;
-  fdr_end = fdr_start + ecoff_data (abfd)->symbolic_header.ifdMax;
-  fdr_hold = (FDR *) NULL;
-  for (fdr_ptr = fdr_start; fdr_ptr < fdr_end; fdr_ptr++)
-    {
-      if (fdr_ptr->cpd == 0)
-       continue;
-      if (offset < fdr_ptr->adr)
-       break;
-      fdr_hold = fdr_ptr;
-    }
-  if (fdr_hold == (FDR *) NULL)
-    return false;
-  fdr_ptr = fdr_hold;
-
-  /* Each FDR has a list of procedure descriptors (PDR).  PDR's also
-     have an address, which is relative to the FDR address, and are
-     also stored in increasing memory order.  */
-  offset -= fdr_ptr->adr;
-  pdr_ptr = ecoff_data (abfd)->external_pdr + fdr_ptr->ipdFirst;
-  pdr_end = pdr_ptr + fdr_ptr->cpd;
-  ecoff_swap_pdr_in (abfd, pdr_ptr, &pdr);
-
-  /* The address of the first PDR is an offset which applies to the
-     addresses of all the PDR's.  */
-  offset += pdr.adr;
-
-  for (pdr_ptr++; pdr_ptr < pdr_end; pdr_ptr++)
-    {
-      ecoff_swap_pdr_in (abfd, pdr_ptr, &pdr);
-      if (offset < pdr.adr)
-       break;
-    }
-
-  /* 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.
-     Note that right here pdr_ptr and pdr hold the PDR *after* the one
-     we want; we need this to compute line_end.  */
-  line_end = ecoff_data (abfd)->line;
-  if (pdr_ptr == pdr_end)
-    line_end += fdr_ptr->cbLineOffset + fdr_ptr->cbLine;
-  else
-    line_end += fdr_ptr->cbLineOffset + pdr.cbLineOffset;
-
-  /* Now change pdr and pdr_ptr to the one we want.  */
-  pdr_ptr--;
-  ecoff_swap_pdr_in (abfd, pdr_ptr, &pdr);
-
-  offset -= pdr.adr;
-  lineno = pdr.lnLow;
-  line_ptr = (ecoff_data (abfd)->line
-             + fdr_ptr->cbLineOffset
-             + pdr.cbLineOffset);
-  while (line_ptr < line_end)
-    {
-      int delta;
-      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;
-    }
+  unsigned long insn;
+  unsigned long val;
+  unsigned long vallo;
+
+  insn = bfd_get_32 (input_bfd,
+                    contents + adjust + refhi->r_vaddr - input_section->vma);
+  vallo = (bfd_get_32 (input_bfd,
+                      contents + adjust + reflo->r_vaddr - input_section->vma)
+          & 0xffff);
+  val = ((insn & 0xffff) << 16) + vallo;
+  val += relocation;
+
+  /* The low order 16 bits are always treated as a signed value.
+     Therefore, a negative value in the low order bits requires an
+     adjustment in the high order bits.  We need to make this
+     adjustment in two ways: once for the bits we took from the data,
+     and once for the bits we are putting back in to the data.  */
+  if ((vallo & 0x8000) != 0)
+    val -= 0x10000;
 
-  /* 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;
+  if (pcrel)
+    val -= (input_section->output_section->vma
+           + input_section->output_offset
+           + (reflo->r_vaddr - input_section->vma + adjust));
 
-         ecoff_swap_ext_in (abfd,
-                            (ecoff_data (abfd)->external_ext
-                             + pdr.isym),
-                            &proc_ext);
-         *functionname_ptr = ecoff_data (abfd)->ssext + proc_ext.asym.iss;
-       }
-    }
-  else
-    {
-      SYMR proc_sym;
-
-      *filename_ptr = ecoff_data (abfd)->ss + fdr_ptr->issBase + fdr_ptr->rss;
-      ecoff_swap_sym_in (abfd,
-                        (ecoff_data (abfd)->external_sym
-                         + fdr_ptr->isymBase
-                         + pdr.isym),
-                        &proc_sym);
-      *functionname_ptr = (ecoff_data (abfd)->ss
-                          + fdr_ptr->issBase
-                          + proc_sym.iss);
-    }
-  *retline_ptr = lineno;
-  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 link by looping over all the seclets.  We make two passes.  On
-   the first we set the actual section contents and determine the size
-   of the debugging information.  On the second we accumulate the
-   debugging information and write it out.
-
-   This currently always accumulates the debugging information, which
-   is incorrect, because it ignores the -s and -S options of the
-   linker.  The linker needs to be modified to give us that
-   information in a more useful format (currently it just provides a
-   list of symbols which should appear in the output file).  */
-
-/* Clear the output_has_begun flag for all the input BFD's.  We use it
-   to avoid linking in the debugging information for a BFD more than
-   once.  */
+  if ((val & 0x8000) != 0)
+    val += 0x10000;
 
-static void
-ecoff_clear_output_flags (abfd)
-     bfd *abfd;
-{
-  register asection *o;
-  register bfd_seclet_type *p;
-
-  for (o = abfd->sections; o != (asection *) NULL; o = o->next)
-    for (p = o->seclets_head;
-        p != (bfd_seclet_type *) NULL;
-        p = p->next)
-      if (p->type == bfd_indirect_seclet)
-       p->u.indirect.section->owner->output_has_begun = false;
+  insn = (insn &~ 0xffff) | ((val >> 16) & 0xffff);
+  bfd_put_32 (input_bfd, (bfd_vma) insn,
+             contents + adjust + refhi->r_vaddr - input_section->vma);
 }
 
-/* Handle an indirect seclet on the first pass.  Set the contents of
-   the output section, and accumulate the debugging information if
-   any.  */
+/* Relocate a section while linking a MIPS ECOFF file.  */
 
 static boolean
-ecoff_rel (output_bfd, seclet, output_section, data, relocateable)
+mips_relocate_section (output_bfd, info, input_bfd, input_section,
+                      contents, external_relocs)
      bfd *output_bfd;
-     bfd_seclet_type *seclet;
-     asection *output_section;
-     PTR data;
-     boolean relocateable;
+     struct bfd_link_info *info;
+     bfd *input_bfd;
+     asection *input_section;
+     bfd_byte *contents;
+     PTR external_relocs;
 {
-  bfd *input_bfd;
-  HDRR *output_symhdr;
-  HDRR *input_symhdr;
-
-  if ((output_section->flags & SEC_HAS_CONTENTS)
-      && !(output_section->flags & SEC_NEVER_LOAD)
-      && (output_section->flags & SEC_LOAD)
-      && seclet->size)
-    {
-      data = (PTR) bfd_get_relocated_section_contents (output_bfd,
-                                                      seclet,
-                                                      data,
-                                                      relocateable);
-      if (bfd_set_section_contents (output_bfd,
-                                   output_section,
-                                   data,
-                                   seclet->offset,
-                                   seclet->size)
-         == false)
-       {
-         abort();
-       }
-    }
-
-  input_bfd = seclet->u.indirect.section->owner;
-
-  /* We want to figure out how much space will be required to
-     incorporate all the debugging information from input_bfd.  We use
-     the output_has_begun field to avoid adding it in more than once.
-     The actual incorporation is done in the second pass, in
-     ecoff_get_debug.  The code has to parallel that code in its
-     manipulations of output_symhdr.  */
-
-  if (input_bfd->output_has_begun)
-    return true;
-  input_bfd->output_has_begun = true;
+  asection **symndx_to_section;
+  struct ecoff_link_hash_entry **sym_hashes;
+  bfd_vma gp;
+  boolean gp_undefined;
+  size_t adjust;
+  long *offsets;
+  struct external_reloc *ext_rel;
+  struct external_reloc *ext_rel_end;
+  unsigned int i;
+  boolean got_lo;
+  struct internal_reloc lo_int_rel;
+
+  BFD_ASSERT (input_bfd->xvec->byteorder
+             == output_bfd->xvec->byteorder);
+
+  /* We keep a table mapping the symndx found in an internal reloc to
+     the appropriate section.  This is faster than looking up the
+     section by name each time.  */
+  symndx_to_section = ecoff_data (input_bfd)->symndx_to_section;
+  if (symndx_to_section == (asection **) NULL)
+    {
+      symndx_to_section = ((asection **)
+                          bfd_alloc (input_bfd,
+                                     (NUM_RELOC_SECTIONS
+                                      * sizeof (asection *))));
+      if (!symndx_to_section)
+       return false;
 
-  output_symhdr = &ecoff_data (output_bfd)->symbolic_header;
+      symndx_to_section[RELOC_SECTION_NONE] = NULL;
+      symndx_to_section[RELOC_SECTION_TEXT] =
+       bfd_get_section_by_name (input_bfd, ".text");
+      symndx_to_section[RELOC_SECTION_RDATA] =
+       bfd_get_section_by_name (input_bfd, ".rdata");
+      symndx_to_section[RELOC_SECTION_DATA] =
+       bfd_get_section_by_name (input_bfd, ".data");
+      symndx_to_section[RELOC_SECTION_SDATA] =
+       bfd_get_section_by_name (input_bfd, ".sdata");
+      symndx_to_section[RELOC_SECTION_SBSS] =
+       bfd_get_section_by_name (input_bfd, ".sbss");
+      symndx_to_section[RELOC_SECTION_BSS] =
+       bfd_get_section_by_name (input_bfd, ".bss");
+      symndx_to_section[RELOC_SECTION_INIT] =
+       bfd_get_section_by_name (input_bfd, ".init");
+      symndx_to_section[RELOC_SECTION_LIT8] =
+       bfd_get_section_by_name (input_bfd, ".lit8");
+      symndx_to_section[RELOC_SECTION_LIT4] =
+       bfd_get_section_by_name (input_bfd, ".lit4");
+      symndx_to_section[RELOC_SECTION_XDATA] = NULL;
+      symndx_to_section[RELOC_SECTION_PDATA] = NULL;
+      symndx_to_section[RELOC_SECTION_FINI] =
+       bfd_get_section_by_name (input_bfd, ".fini");
+      symndx_to_section[RELOC_SECTION_LITA] = NULL;
+      symndx_to_section[RELOC_SECTION_ABS] = NULL;
+
+      ecoff_data (input_bfd)->symndx_to_section = symndx_to_section;
+    }
+
+  sym_hashes = ecoff_data (input_bfd)->sym_hashes;
+
+  gp = _bfd_get_gp_value (output_bfd);
+  if (gp == 0)
+    gp_undefined = true;
+  else
+    gp_undefined = false;
 
-  if (input_bfd->xvec->flavour != bfd_target_ecoff_flavour)
-    {
-      asymbol **symbols;
-      asymbol **sym_ptr;
-      asymbol **sym_end;
+  got_lo = false;
 
-      /* We just accumulate local symbols from a non-ECOFF BFD.  The
-        external symbols are handled separately.  */
+  adjust = 0;
 
-      symbols = (asymbol **) bfd_alloc (output_bfd,
-                                       get_symtab_upper_bound (input_bfd));
-      if (symbols == (asymbol **) NULL)
+  if (ecoff_section_data (input_bfd, input_section) == NULL)
+    offsets = NULL;
+  else
+    offsets = ecoff_section_data (input_bfd, input_section)->offsets;
+
+  ext_rel = (struct external_reloc *) external_relocs;
+  ext_rel_end = ext_rel + input_section->reloc_count;
+  for (i = 0; ext_rel < ext_rel_end; ext_rel++, i++)
+    {
+      struct internal_reloc int_rel;
+      boolean use_lo;
+      bfd_vma addend;
+      reloc_howto_type *howto;
+      struct ecoff_link_hash_entry *h = NULL;
+      asection *s = NULL;
+      bfd_vma relocation;
+      bfd_reloc_status_type r;
+
+      if (! got_lo)
+       mips_ecoff_swap_reloc_in (input_bfd, (PTR) ext_rel, &int_rel);
+      else
        {
-         bfd_error = no_memory;
-         return false;
+         int_rel = lo_int_rel;
+         got_lo = false;
        }
-      sym_end = symbols + bfd_canonicalize_symtab (input_bfd, symbols);
 
-      for (sym_ptr = symbols; sym_ptr < sym_end; sym_ptr++)
+      BFD_ASSERT (int_rel.r_type < ecoff_backend (abfd)->howto_table_size);
+
+      /* The REFHI and RELHI relocs requires special handling.  they
+        must be followed by a REFLO or RELLO reloc, respectively, and
+        the addend is formed from both relocs.  */
+      if (int_rel.r_type == MIPS_R_REFHI
+         || int_rel.r_type == MIPS_R_RELHI)
        {
-         size_t len;
+         struct external_reloc *lo_ext_rel;
+
+         /* As a GNU extension, permit an arbitrary number of REFHI
+             or RELHI relocs before the REFLO or RELLO reloc.  This
+             permits gcc to emit the HI and LO relocs itself.  */
+         for (lo_ext_rel = ext_rel + 1;
+              lo_ext_rel < ext_rel_end;
+              lo_ext_rel++)
+           {
+             mips_ecoff_swap_reloc_in (input_bfd, (PTR) lo_ext_rel,
+                                       &lo_int_rel);
+             if (lo_int_rel.r_type != int_rel.r_type)
+               break;
+           }
 
-         len = strlen ((*sym_ptr)->name);
-         if (((*sym_ptr)->flags & BSF_EXPORT) == 0)
+         if (lo_ext_rel < ext_rel_end
+             && (lo_int_rel.r_type
+                 == (int_rel.r_type == MIPS_R_REFHI
+                     ? MIPS_R_REFLO
+                     : MIPS_R_RELLO))
+             && int_rel.r_extern == lo_int_rel.r_extern
+             && int_rel.r_symndx == lo_int_rel.r_symndx)
            {
-             ++output_symhdr->isymMax;
-             output_symhdr->issMax += len + 1;
+             use_lo = true;
+             if (lo_ext_rel == ext_rel + 1)
+               got_lo = true;
            }
        }
 
-      bfd_release (output_bfd, (PTR) symbols);
-
-      ++output_symhdr->ifdMax;
-
-      return true;
-    }
-
-  /* We simply add in the information from another ECOFF BFD.  First
-     we make sure we have the symbolic information.  */
-  if (ecoff_slurp_symbol_table (input_bfd) == false)
-    return false;
-  if (bfd_get_symcount (input_bfd) == 0)
-    return true;
-
-  input_symhdr = &ecoff_data (input_bfd)->symbolic_header;
-
-  /* Figure out how much information we are going to be putting in.
-     The external symbols are handled separately.  */
-  output_symhdr->ilineMax += input_symhdr->ilineMax;
-  output_symhdr->cbLine += input_symhdr->cbLine;
-  output_symhdr->idnMax += input_symhdr->idnMax;
-  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;
-
-  /* The RFD's are special, since we create them if needed.  */
-  if (input_symhdr->crfd > 0)
-    output_symhdr->crfd += input_symhdr->crfd;
-  else
-    output_symhdr->crfd += input_symhdr->ifdMax;
-
-  return true;
-}
+      howto = &ecoff_backend (abfd)->howto_table[int_rel.r_type];
+
+      /* The SWITCH reloc must be handled specially.  This reloc is
+        marks the location of a difference between two portions of an
+        object file.  The symbol index does not reference a symbol,
+        but is actually the offset from the reloc to the subtrahend
+        of the difference.  This reloc is correct in the object file,
+        and needs no further adjustment, unless we are relaxing.  If
+        we are relaxing, we may have to add in an offset.  Since no
+        symbols are involved in this reloc, we handle it completely
+        here.  */
+      if (int_rel.r_type == MIPS_R_SWITCH)
+       {
+         if (offsets != NULL
+             && offsets[i] != 0)
+           {
+             r = _bfd_relocate_contents (howto, input_bfd,
+                                         (bfd_vma) offsets[i],
+                                         (contents
+                                          + adjust
+                                          + int_rel.r_vaddr
+                                          - input_section->vma));
+             BFD_ASSERT (r == bfd_reloc_ok);
+           }
 
-/* Handle an arbitrary seclet on the first pass.  */
+         continue;
+       }
 
-static boolean
-ecoff_dump_seclet (abfd, seclet, section, data, relocateable)
-     bfd *abfd;
-     bfd_seclet_type *seclet;
-     asection *section;
-     PTR data;
-     boolean relocateable;
-{
-  switch (seclet->type) 
-    {
-    case bfd_indirect_seclet:
-      /* The contents of this section come from another one somewhere
-        else.  */
-      return ecoff_rel (abfd, seclet, section, data, relocateable);
-
-    case bfd_fill_seclet:
-      /* Fill in the section with fill.value.  This is used to pad out
-        sections, but we must avoid padding the .bss section.  */
-      if ((section->flags & SEC_HAS_CONTENTS) == 0)
+      if (int_rel.r_extern)
        {
-         if (seclet->u.fill.value != 0)
+         h = sym_hashes[int_rel.r_symndx];
+         /* If h is NULL, that means that there is a reloc against an
+            external symbol which we thought was just a debugging
+            symbol.  This should not happen.  */
+         if (h == (struct ecoff_link_hash_entry *) NULL)
            abort ();
        }
       else
        {
-         char *d = (char *) bfd_alloc (abfd, seclet->size);
-         unsigned int i;
-         boolean ret;
-
-         for (i = 0; i < seclet->size; i+=2)
-           d[i] = seclet->u.fill.value >> 8;
-         for (i = 1; i < seclet->size; i+=2)
-           d[i] = seclet->u.fill.value;
-         ret = bfd_set_section_contents (abfd, section, d, seclet->offset,
-                                         seclet->size);
-         bfd_release (abfd, (PTR) d);
-         return ret;
-       }
-      break;
-
-    default:
-      abort();
-    }
-
-  return true;
-}
-
-/* Add a string to the debugging information we are accumulating for a
-   file.  Return the offset from the fdr string base or from the
-   external string base.  */
-
-static long
-ecoff_add_string (output_bfd, fdr, string, external)
-     bfd *output_bfd;
-     FDR *fdr;
-     CONST char *string;
-     boolean external;
-{
-  HDRR *symhdr;
-  size_t len;
-  long ret;
-
-  symhdr = &ecoff_data (output_bfd)->symbolic_header;
-  len = strlen (string);
-  if (external)
-    {
-      strcpy (ecoff_data (output_bfd)->ssext + symhdr->issExtMax, string);
-      ret = symhdr->issExtMax;
-      symhdr->issExtMax += len + 1;
-    }
-  else
-    {
-      strcpy (ecoff_data (output_bfd)->ss + symhdr->issMax, string);
-      ret = fdr->cbSs;
-      symhdr->issMax += len + 1;
-      fdr->cbSs += len + 1;
-    }
-  return ret;
-}
-
-/* Accumulate the debugging information from an input section.  */
-
-static boolean
-ecoff_get_debug (output_bfd, seclet, section, relocateable)
-     bfd *output_bfd;
-     bfd_seclet_type *seclet;
-     asection *section;
-     boolean relocateable;
-{
-  bfd *input_bfd;
-  HDRR *output_symhdr;
-  HDRR *input_symhdr;
-  ecoff_data_type *output_ecoff;
-  ecoff_data_type *input_ecoff;
-  unsigned int count;
-  struct sym_ext *sym_out;
-  ecoff_symbol_type *esym_ptr;
-  ecoff_symbol_type *esym_end;
-  unsigned long pdr_off;
-  FDR *fdr_ptr;
-  FDR *fdr_end;
-  struct fdr_ext *fdr_out;
-
-  input_bfd = seclet->u.indirect.section->owner;
-
-  /* Don't get the information more than once. */
-  if (input_bfd->output_has_begun)
-    return true;
-  input_bfd->output_has_begun = true;
-
-  output_ecoff = ecoff_data (output_bfd);
-  output_symhdr = &output_ecoff->symbolic_header;
+         if (int_rel.r_symndx < 0 || int_rel.r_symndx >= NUM_RELOC_SECTIONS)
+           s = NULL;
+         else
+           s = symndx_to_section[int_rel.r_symndx];
 
-  if (input_bfd->xvec->flavour != bfd_target_ecoff_flavour)
-    {
-      FDR fdr;
-      asymbol **symbols;
-      asymbol **sym_ptr;
-      asymbol **sym_end;
-
-      /* This is not an ECOFF BFD.  Just gather the symbols.  */
-
-      memset (&fdr, 0, sizeof fdr);
-
-      fdr.adr = bfd_get_section_vma (output_bfd, section) + seclet->offset;
-      fdr.issBase = output_symhdr->issMax;
-      fdr.cbSs = 0;
-      fdr.rss = ecoff_add_string (output_bfd,
-                                 &fdr,
-                                 bfd_get_filename (input_bfd),
-                                 false);
-      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;
+         if (s == (asection *) NULL)
+           abort ();
        }
-      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++)
+      /* The GPREL reloc uses an addend: the difference in the GP
+        values.  */
+      if (int_rel.r_type != MIPS_R_GPREL
+         && int_rel.r_type != MIPS_R_LITERAL)
+       addend = 0;
+      else
        {
-         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_bfd,
-                                              &fdr,
-                                              (*sym_ptr)->name,
-                                              false);
-
-         if (bfd_is_com_section ((*sym_ptr)->section)
-             || (*sym_ptr)->section == &bfd_und_section)
-           internal_sym.value = (*sym_ptr)->value;
+         if (gp_undefined)
+           {
+             if (! ((*info->callbacks->reloc_dangerous)
+                    (info, "GP relative relocation when GP not defined",
+                     input_bfd, input_section,
+                     int_rel.r_vaddr - input_section->vma)))
+               return false;
+             /* Only give the error once per link.  */
+             gp = 4;
+             _bfd_set_gp_value (output_bfd, gp);
+             gp_undefined = false;
+           }
+         if (! int_rel.r_extern)
+           {
+             /* This is a relocation against a section.  The current
+                addend in the instruction is the difference between
+                INPUT_SECTION->vma and the GP value of INPUT_BFD.  We
+                must change this to be the difference between the
+                final definition (which will end up in RELOCATION)
+                and the GP value of OUTPUT_BFD (which is in GP).  */
+             addend = ecoff_data (input_bfd)->gp - gp;
+           }
+         else if (! info->relocateable
+                  || h->root.type == bfd_link_hash_defined
+                  || h->root.type == bfd_link_hash_defweak)
+           {
+             /* This is a relocation against a defined symbol.  The
+                current addend in the instruction is simply the
+                desired offset into the symbol (normally zero).  We
+                are going to change this into a relocation against a
+                defined symbol, so we want the instruction to hold
+                the difference between the final definition of the
+                symbol (which will end up in RELOCATION) and the GP
+                value of OUTPUT_BFD (which is in GP).  */
+             addend = - gp;
+           }
          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;
-         ecoff_swap_sym_out (output_bfd, &internal_sym,
-                             (output_ecoff->external_sym
-                              + output_symhdr->isymMax));
-         ++fdr.csym;
-         ++output_symhdr->isymMax;
-       }
-
-      bfd_release (output_bfd, (PTR) symbols);
-
-      /* 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.  */
-
-      ecoff_swap_fdr_out (output_bfd, &fdr,
-                         (output_ecoff->external_fdr
-                          + output_symhdr->ifdMax));
-      ++output_symhdr->ifdMax;
-      return true;
-    }
-
-  /* This is an ECOFF BFD.  We want to grab the information from
-     input_bfd and attach it to output_bfd.  */
-  count = bfd_get_symcount (input_bfd);
-  if (count == 0)
-    return true;
-  input_ecoff = ecoff_data (input_bfd);
-  input_symhdr = &input_ecoff->symbolic_header;
-
-  /* I think that it is more efficient to simply copy the debugging
-     information from the input BFD to the output BFD.  Because ECOFF
-     uses relative pointers for most of the debugging information,
-     only a little of it has to be changed at all.  */
-
-  /* Swap in the local symbols, adjust their values, and swap them out
-     again.  The external symbols are handled separately.  */
-  sym_out = output_ecoff->external_sym + output_symhdr->isymMax;
-
-  esym_ptr = ecoff_data (input_bfd)->canonical_symbols;
-  esym_end = esym_ptr + count;
-  for (; esym_ptr < esym_end; esym_ptr++)
-    {
-      if (esym_ptr->local)
-       {
-         SYMR sym;
-
-         ecoff_swap_sym_in (input_bfd, esym_ptr->native.lnative, &sym);
-
-         /* If we're producing an executable, move common symbols
-            into bss.  */
-         if (relocateable == false)
            {
-             if (sym.sc == scCommon)
-               sym.sc = scBss;
-             else if (sym.sc == scSCommon)
-               sym.sc = scSBss;
+             /* This is a relocation against an undefined or common
+                symbol.  The current addend in the instruction is
+                simply the desired offset into the symbol (normally
+                zero).  We are generating relocateable output, and we
+                aren't going to define this symbol, so we just leave
+                the instruction alone.  */
+             addend = 0;
            }
-
-         if (! bfd_is_com_section (esym_ptr->symbol.section)
-             && (esym_ptr->symbol.flags & BSF_DEBUGGING) == 0
-             && esym_ptr->symbol.section != &bfd_und_section)
-           sym.value = (esym_ptr->symbol.value
-                        + esym_ptr->symbol.section->output_offset
-                        + esym_ptr->symbol.section->output_section->vma);
-         ecoff_swap_sym_out (output_bfd, &sym, sym_out);
-         ++sym_out;
-       }
-    }
-
-  /* That should have accounted for all the local symbols in
-     input_bfd.  */
-  BFD_ASSERT ((sym_out - output_ecoff->external_sym) - output_symhdr->isymMax
-             == input_symhdr->isymMax);
-
-  /* Copy the information that does not need swapping.  */
-  memcpy (output_ecoff->line + output_symhdr->cbLine,
-         input_ecoff->line,
-         input_symhdr->cbLine * sizeof (unsigned char));
-  memcpy (output_ecoff->external_aux + output_symhdr->iauxMax,
-         input_ecoff->external_aux,
-         input_symhdr->iauxMax * sizeof (union aux_ext));
-  memcpy (output_ecoff->ss + output_symhdr->issMax,
-         input_ecoff->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.  */
-      memcpy (output_ecoff->external_dnr + output_symhdr->idnMax,
-             input_ecoff->external_dnr,
-             input_symhdr->idnMax * sizeof (struct dnr_ext));
-      memcpy (output_ecoff->external_pdr + output_symhdr->ipdMax,
-             input_ecoff->external_pdr,
-             input_symhdr->ipdMax * sizeof (struct pdr_ext));
-      if (input_symhdr->ipdMax == 0)
-       pdr_off = 0;
-      else
-       {
-         PDR pdr;
-
-         ecoff_swap_pdr_in (input_bfd, input_ecoff->external_pdr, &pdr);
-         pdr_off = pdr.adr;
        }
-      memcpy (output_ecoff->external_opt + output_symhdr->ioptMax,
-             input_ecoff->external_opt,
-             input_symhdr->ioptMax * sizeof (struct opt_ext));
-    }
-  else
-    {
-      struct dnr_ext *dnr_in;
-      struct dnr_ext *dnr_end;
-      struct dnr_ext *dnr_out;
-      struct pdr_ext *pdr_in;
-      struct pdr_ext *pdr_end;
-      struct pdr_ext *pdr_out;
-      int first_pdr;
-      struct opt_ext *opt_in;
-      struct opt_ext *opt_end;
-      struct opt_ext *opt_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.  */
-      dnr_in = input_ecoff->external_dnr;
-      dnr_end = dnr_in + input_symhdr->idnMax;
-      dnr_out = output_ecoff->external_dnr + output_symhdr->idnMax;
-      for (; dnr_in < dnr_end; dnr_in++, dnr_out++)
-       {
-         DNR dnr;
 
-         ecoff_swap_dnr_in (input_bfd, dnr_in, &dnr);
-         ecoff_swap_dnr_out (output_bfd, &dnr, dnr_out);
-       }
-      pdr_in = input_ecoff->external_pdr;
-      pdr_end = pdr_in + input_symhdr->ipdMax;
-      pdr_out = output_ecoff->external_pdr + output_symhdr->ipdMax;
-      first_pdr = 1;
-      pdr_off = 0;
-      for (; pdr_in < pdr_end; pdr_in++, pdr_out++)
+      /* If we are relaxing, mips_relax_section may have set
+        offsets[i] to some value.  A value of 1 means we must expand
+        a PC relative branch into a multi-instruction of sequence,
+        and any other value is an addend.  */
+      if (offsets != NULL
+         && offsets[i] != 0)
        {
-         PDR pdr;
-
-         ecoff_swap_pdr_in (input_bfd, pdr_in, &pdr);
-         ecoff_swap_pdr_out (output_bfd, &pdr, pdr_out);
-         if (first_pdr)
+         BFD_ASSERT (! info->relocateable);
+         BFD_ASSERT (int_rel.r_type == MIPS_R_PCREL16
+                     || int_rel.r_type == MIPS_R_RELHI
+                     || int_rel.r_type == MIPS_R_RELLO);
+         if (offsets[i] != 1)
+           addend += offsets[i];
+         else
            {
-             pdr_off = pdr.adr;
-             first_pdr = 0;
-           }
-       }
-      opt_in = input_ecoff->external_opt;
-      opt_end = opt_in + input_symhdr->ioptMax;
-      opt_out = output_ecoff->external_opt + output_symhdr->ioptMax;
-      for (; opt_in < opt_end; opt_in++, opt_out++)
-       {
-         OPTR opt;
-
-         ecoff_swap_opt_in (input_bfd, opt_in, &opt);
-         ecoff_swap_opt_out (output_bfd, &opt, opt_out);
-       }
-    }
-
-  /* Set ifdbase so that the external symbols know how to adjust their
-     ifd values.  */
-  input_ecoff->ifdbase = output_symhdr->ifdMax;
-
-  fdr_ptr = input_ecoff->fdr;
-  fdr_end = fdr_ptr + input_symhdr->ifdMax;
-  fdr_out = output_ecoff->external_fdr + output_symhdr->ifdMax;
-  for (; fdr_ptr < fdr_end; fdr_ptr++, fdr_out++)
-    {
-      FDR fdr;
-
-      fdr = *fdr_ptr;
-
-      /* The memory address for this fdr is the address for the seclet
-        plus the offset to this fdr within input_bfd.  For some
-        reason the offset of the first procedure pointer is also
-        added in.  */
-      fdr.adr = (bfd_get_section_vma (output_bfd, section)
-                + seclet->offset
-                + (fdr_ptr->adr - input_ecoff->fdr->adr)
-                + pdr_off);
-
-      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 irfd 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;
-
-      ecoff_swap_fdr_out (output_bfd, &fdr, fdr_out);
-    }
+             bfd_byte *here;
+
+             BFD_ASSERT (int_rel.r_extern
+                         && int_rel.r_type == MIPS_R_PCREL16);
+
+             /* Move the rest of the instructions up.  */
+             here = (contents
+                     + adjust
+                     + int_rel.r_vaddr
+                     - input_section->vma);
+             memmove (here + PCREL16_EXPANSION_ADJUSTMENT, here,
+                      (size_t) (input_section->_raw_size
+                                - (int_rel.r_vaddr - input_section->vma)));
+                      
+             /* Generate the new instructions.  */
+             if (! mips_relax_pcrel16 (info, input_bfd, input_section,
+                                       h, here,
+                                       (input_section->output_section->vma
+                                        + input_section->output_offset
+                                        + (int_rel.r_vaddr
+                                           - input_section->vma)
+                                        + adjust)))
+               return false;
 
-  if (input_symhdr->crfd > 0)
-    {
-      struct rfd_ext *rfd_in;
-      struct rfd_ext *rfd_end;
-      struct rfd_ext *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.  */
-      rfd_in = input_ecoff->external_rfd;
-      rfd_end = rfd_in + input_symhdr->crfd;
-      rfd_out = output_ecoff->external_rfd + output_symhdr->crfd;
-      for (; rfd_in < rfd_end; rfd_in++, rfd_out++)
-       {
-         RFDT rfd;
+             /* We must adjust everything else up a notch.  */
+             adjust += PCREL16_EXPANSION_ADJUSTMENT;
 
-         ecoff_swap_rfd_in (input_bfd, rfd_in, &rfd);
-         rfd += output_symhdr->ifdMax;
-         ecoff_swap_rfd_out (output_bfd, &rfd, rfd_out);
+             /* mips_relax_pcrel16 handles all the details of this
+                relocation.  */
+             continue;
+           }
        }
-      output_symhdr->crfd += input_symhdr->crfd;
-    }
-  else
-    {
-      struct rfd_ext *rfd_out;
-      struct rfd_ext *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.  */
-      rfd = output_symhdr->ifdMax;
-      rfd_out = output_ecoff->external_rfd + output_symhdr->crfd;
-      rfd_end = rfd_out + input_symhdr->ifdMax;
-      for (; rfd_out < rfd_end; rfd_out++, rfd++)
-       ecoff_swap_rfd_out (output_bfd, &rfd, rfd_out);
-      output_symhdr->crfd += input_symhdr->ifdMax;
-    }
-
-  /* Combine the register masks.  */
-  {
-    int i;
-
-    output_ecoff->gprmask |= input_ecoff->gprmask;
-    for (i = 0; i < 4; i++)
-      output_ecoff->cprmask[i] |= input_ecoff->cprmask[i];
-  }
-
-  /* Update the counts.  */
-  output_symhdr->ilineMax += input_symhdr->ilineMax;
-  output_symhdr->cbLine += input_symhdr->cbLine;
-  output_symhdr->idnMax += input_symhdr->idnMax;
-  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;
-}
 
-/* This is the actual link routine.  It makes two passes over all the
-   seclets.  */
-
-static boolean
-ecoff_bfd_seclet_link (abfd, data, relocateable)
-     bfd *abfd;
-     PTR data;
-     boolean relocateable;
-{
-  HDRR *symhdr;
-  int ipass;
-  register asection *o;
-  register bfd_seclet_type *p;
-  asymbol **sym_ptr_ptr;
-  bfd_size_type size;
-  char *raw;
-
-  /* We accumulate the debugging information counts in the symbolic
-     header.  */
-  symhdr = &ecoff_data (abfd)->symbolic_header;
-  symhdr->magic = magicSym;
-  /* 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 need to copy over the debugging symbols from each input BFD.
-     When we do this copying, we have to adjust the text address in
-     the FDR structures, so we have to know the text address used for
-     the input BFD.  Since we only want to copy the symbols once per
-     input BFD, but we are going to look at each input BFD multiple
-     times (once for each section it provides), we arrange to always
-     look at the text section first.  That means that when we copy the
-     debugging information, we always know the text address.  So we
-     actually do each pass in two sub passes; first the text sections,
-     then the non-text sections.  We use the output_has_begun flag to
-     determine whether we have copied over the debugging information
-     yet.  */
-
-  /* Do the first pass: set the output section contents and count the
-     debugging information.  */
-  ecoff_clear_output_flags (abfd);
-  for (ipass = 0; ipass < 2; ipass++)
-    {
-      for (o = abfd->sections; o != (asection *) NULL; o = o->next)
+      /* If we are relaxing, and this is a reloc against the .text
+        segment, we may need to adjust it if some branches have been
+        expanded.  The reloc types which are likely to occur in the
+        .text section are handled efficiently by mips_relax_section,
+        and thus do not need to be handled here.  */
+      if (ecoff_data (input_bfd)->debug_info.adjust != NULL
+         && ! int_rel.r_extern
+         && int_rel.r_symndx == RELOC_SECTION_TEXT
+         && (strcmp (bfd_get_section_name (input_bfd, input_section),
+                     ".text") != 0
+             || (int_rel.r_type != MIPS_R_PCREL16
+                 && int_rel.r_type != MIPS_R_SWITCH
+                 && int_rel.r_type != MIPS_R_RELHI
+                 && int_rel.r_type != MIPS_R_RELLO)))
        {
-         /* For SEC_CODE sections, (flags & SEC_CODE) == 0 is false,
-            so they are done on pass 0.  For other sections the
-            expression is true, so they are done on pass 1.  */
-         if (((o->flags & SEC_CODE) == 0) != ipass)
-           continue;
-
-         for (p = o->seclets_head;
-              p != (bfd_seclet_type *) NULL;
-              p = p->next)
+         bfd_vma adr;
+         struct ecoff_value_adjust *a;
+
+         /* We need to get the addend so that we know whether we need
+            to adjust the address.  */
+         BFD_ASSERT (int_rel.r_type == MIPS_R_REFWORD);
+
+         adr = bfd_get_32 (input_bfd,
+                           (contents
+                            + adjust
+                            + int_rel.r_vaddr
+                            - input_section->vma));
+
+         for (a = ecoff_data (input_bfd)->debug_info.adjust;
+              a != (struct ecoff_value_adjust *) NULL;
+              a = a->next)
            {
-             if (ecoff_dump_seclet (abfd, p, o, data, relocateable)
-                 == false)
-               return false;
+             if (adr >= a->start && adr < a->end)
+               addend += a->adjust;
            }
        }
-    }
 
-  /* We handle the external symbols differently.  We use the ones
-     attached to the output_bfd.  The linker will have already
-     determined which symbols are to be attached.  Here we just
-     determine how much space we will need for them.  */
-  sym_ptr_ptr = bfd_get_outsymbols (abfd);
-  if (sym_ptr_ptr != NULL)
-    {
-      asymbol **sym_end;
-
-      sym_end = sym_ptr_ptr + bfd_get_symcount (abfd);
-      for (; sym_ptr_ptr < sym_end; sym_ptr_ptr++)
+      if (info->relocateable)
        {
-         if (((*sym_ptr_ptr)->flags & BSF_DEBUGGING) == 0
-             && ((*sym_ptr_ptr)->flags & BSF_LOCAL) == 0)
+         /* We are generating relocateable output, and must convert
+            the existing reloc.  */
+         if (int_rel.r_extern)
+           {
+             if ((h->root.type == bfd_link_hash_defined
+                  || h->root.type == bfd_link_hash_defweak)
+                 && ! bfd_is_abs_section (h->root.u.def.section))
+               {
+                 const char *name;
+
+                 /* This symbol is defined in the output.  Convert
+                    the reloc from being against the symbol to being
+                    against the section.  */
+
+                 /* Clear the r_extern bit.  */
+                 int_rel.r_extern = 0;
+
+                 /* Compute a new r_symndx value.  */
+                 s = h->root.u.def.section;
+                 name = bfd_get_section_name (output_bfd,
+                                              s->output_section);
+
+                 int_rel.r_symndx = -1;
+                 switch (name[1])
+                   {
+                   case 'b':
+                     if (strcmp (name, ".bss") == 0)
+                       int_rel.r_symndx = RELOC_SECTION_BSS;
+                     break;
+                   case 'd':
+                     if (strcmp (name, ".data") == 0)
+                       int_rel.r_symndx = RELOC_SECTION_DATA;
+                     break;
+                   case 'f':
+                     if (strcmp (name, ".fini") == 0)
+                       int_rel.r_symndx = RELOC_SECTION_FINI;
+                     break;
+                   case 'i':
+                     if (strcmp (name, ".init") == 0)
+                       int_rel.r_symndx = RELOC_SECTION_INIT;
+                     break;
+                   case 'l':
+                     if (strcmp (name, ".lit8") == 0)
+                       int_rel.r_symndx = RELOC_SECTION_LIT8;
+                     else if (strcmp (name, ".lit4") == 0)
+                       int_rel.r_symndx = RELOC_SECTION_LIT4;
+                     break;
+                   case 'r':
+                     if (strcmp (name, ".rdata") == 0)
+                       int_rel.r_symndx = RELOC_SECTION_RDATA;
+                     break;
+                   case 's':
+                     if (strcmp (name, ".sdata") == 0)
+                       int_rel.r_symndx = RELOC_SECTION_SDATA;
+                     else if (strcmp (name, ".sbss") == 0)
+                       int_rel.r_symndx = RELOC_SECTION_SBSS;
+                     break;
+                   case 't':
+                     if (strcmp (name, ".text") == 0)
+                       int_rel.r_symndx = RELOC_SECTION_TEXT;
+                     break;
+                   }
+                     
+                 if (int_rel.r_symndx == -1)
+                   abort ();
+
+                 /* Add the section VMA and the symbol value.  */
+                 relocation = (h->root.u.def.value
+                               + s->output_section->vma
+                               + s->output_offset);
+
+                 /* For a PC relative relocation, the object file
+                    currently holds just the addend.  We must adjust
+                    by the address to get the right value.  */
+                 if (howto->pc_relative)
+                   {
+                     relocation -= int_rel.r_vaddr - input_section->vma;
+
+                     /* If we are converting a RELHI or RELLO reloc
+                        from being against an external symbol to
+                        being against a section, we must put a
+                        special value into the r_offset field.  This
+                        value is the old addend.  The r_offset for
+                        both the RELHI and RELLO relocs are the same,
+                        and we set both when we see RELHI.  */
+                     if (int_rel.r_type == MIPS_R_RELHI)
+                       {
+                         long addhi, addlo;
+
+                         addhi = bfd_get_32 (input_bfd,
+                                             (contents
+                                              + adjust
+                                              + int_rel.r_vaddr
+                                              - input_section->vma));
+                         addhi &= 0xffff;
+                         if (addhi & 0x8000)
+                           addhi -= 0x10000;
+                         addhi <<= 16;
+
+                         if (! use_lo)
+                           addlo = 0;
+                         else
+                           {
+                             addlo = bfd_get_32 (input_bfd,
+                                                 (contents
+                                                  + adjust
+                                                  + lo_int_rel.r_vaddr
+                                                  - input_section->vma));
+                             addlo &= 0xffff;
+                             if (addlo & 0x8000)
+                               addlo -= 0x10000;
+
+                             lo_int_rel.r_offset = addhi + addlo;
+                           }
+
+                         int_rel.r_offset = addhi + addlo;
+                       }
+                   }
+
+                 h = NULL;
+               }
+             else
+               {
+                 /* Change the symndx value to the right one for the
+                    output BFD.  */
+                 int_rel.r_symndx = h->indx;
+                 if (int_rel.r_symndx == -1)
+                   {
+                     /* This symbol is not being written out.  */
+                     if (! ((*info->callbacks->unattached_reloc)
+                            (info, h->root.root.string, input_bfd,
+                             input_section,
+                             int_rel.r_vaddr - input_section->vma)))
+                       return false;
+                     int_rel.r_symndx = 0;
+                   }
+                 relocation = 0;
+               }
+           }
+         else
            {
-             ++symhdr->iextMax;
-             symhdr->issExtMax += strlen ((*sym_ptr_ptr)->name) + 1;
+             /* This is a relocation against a section.  Adjust the
+                value by the amount the section moved.  */
+             relocation = (s->output_section->vma
+                           + s->output_offset
+                           - s->vma);
            }
-       }
-    }
 
-  /* Adjust the counts so that structures are longword aligned.  */
-  symhdr->cbLine = (symhdr->cbLine + 3) &~ 3;
-  symhdr->issMax = (symhdr->issMax + 3) &~ 3;
-  symhdr->issExtMax = (symhdr->issExtMax + 3) &~ 3;
-
-  /* Now the counts in symhdr are the correct size for the debugging
-     information.  We allocate the right amount of space, and reset
-     the counts so that the second pass can use them as indices.  It
-     would be possible to output the debugging information directly to
-     the file in pass 2, rather than to build it in memory and then
-     write it out.  Outputting to the file would require a lot of
-     seeks and small writes, though, and I think this approach is
-     faster.  */
-  size = (symhdr->cbLine * sizeof (unsigned char)
-         + symhdr->idnMax * sizeof (struct dnr_ext)
-         + symhdr->ipdMax * sizeof (struct pdr_ext)
-         + symhdr->isymMax * sizeof (struct sym_ext)
-         + symhdr->ioptMax * sizeof (struct opt_ext)
-         + symhdr->iauxMax * sizeof (union aux_ext)
-         + symhdr->issMax * sizeof (char)
-         + symhdr->issExtMax * sizeof (char)
-         + symhdr->ifdMax * sizeof (struct fdr_ext)
-         + symhdr->crfd * sizeof (struct rfd_ext)
-         + symhdr->iextMax * sizeof (struct ext_ext));
-  raw = (char *) bfd_alloc (abfd, size);
-  if (raw == (char *) NULL)
-    {
-      bfd_error = no_memory;
-      return false;
-    }
-  ecoff_data (abfd)->raw_size = size;
-  ecoff_data (abfd)->raw_syments = (PTR) raw;
-
-  /* Initialize the raw pointers.  */
-#define SET(field, count, type) \
-  ecoff_data (abfd)->field = (type *) raw; \
-  raw += symhdr->count * sizeof (type)
-
-  SET (line, cbLine, unsigned char);
-  SET (external_dnr, idnMax, struct dnr_ext);
-  SET (external_pdr, ipdMax, struct pdr_ext);
-  SET (external_sym, isymMax, struct sym_ext);
-  SET (external_opt, ioptMax, struct opt_ext);
-  SET (external_aux, iauxMax, union aux_ext);
-  SET (ss, issMax, char);
-  SET (ssext, issExtMax, char);
-  SET (external_fdr, ifdMax, struct fdr_ext);
-  SET (external_rfd, crfd, struct rfd_ext);
-  SET (external_ext, iextMax, struct ext_ext);
-#undef SET
-
-  /* Reset the counts so the second pass can use them to know how far
-     it has gotten.  */
-  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;
-
-  /* Do the second pass: accumulate the debugging information.  */
-  ecoff_clear_output_flags (abfd);
-  for (ipass = 0; ipass < 2; ipass++)
-    {
-      for (o = abfd->sections; o != (asection *) NULL; o = o->next)
-       {
-         if (((o->flags & SEC_CODE) == 0) != ipass)
-           continue;
-         for (p = o->seclets_head;
-              p != (bfd_seclet_type *) NULL;
-              p = p->next)
+         relocation += addend;
+         addend = 0;
+
+         /* Adjust a PC relative relocation by removing the reference
+            to the original address in the section and including the
+            reference to the new address.  However, external RELHI
+            and RELLO relocs are PC relative, but don't include any
+            reference to the address.  The addend is merely an
+            addend.  */
+         if (howto->pc_relative
+             && (! int_rel.r_extern
+                 || (int_rel.r_type != MIPS_R_RELHI
+                     && int_rel.r_type != MIPS_R_RELLO)))
+           relocation -= (input_section->output_section->vma
+                          + input_section->output_offset
+                          - input_section->vma);
+
+         /* Adjust the contents.  */
+         if (relocation == 0)
+           r = bfd_reloc_ok;
+         else
            {
-             if (p->type == bfd_indirect_seclet)
+             if (int_rel.r_type != MIPS_R_REFHI
+                 && int_rel.r_type != MIPS_R_RELHI)
+               r = _bfd_relocate_contents (howto, input_bfd, relocation,
+                                           (contents
+                                            + adjust
+                                            + int_rel.r_vaddr
+                                            - input_section->vma));
+             else
                {
-                 if (ecoff_get_debug (abfd, p, o, relocateable) == false)
-                   return false;
+                 mips_relocate_hi (&int_rel,
+                                   use_lo ? &lo_int_rel : NULL,
+                                   input_bfd, input_section, contents,
+                                   adjust, relocation,
+                                   int_rel.r_type == MIPS_R_RELHI);
+                 r = bfd_reloc_ok;
                }
            }
-       }
-    }
-
-  /* Put in the external symbols.  */
-  sym_ptr_ptr = bfd_get_outsymbols (abfd);
-  if (sym_ptr_ptr != NULL)
-    {
-      char *ssext;
-      struct ext_ext *external_ext;
-
-      ssext = ecoff_data (abfd)->ssext;
-      external_ext = ecoff_data (abfd)->external_ext;
-      for (; *sym_ptr_ptr != NULL; sym_ptr_ptr++)
-       {
-         asymbol *sym_ptr;
-         EXTR esym;
-
-         sym_ptr = *sym_ptr_ptr;
 
-         if ((sym_ptr->flags & BSF_DEBUGGING) != 0
-             || (sym_ptr->flags & BSF_LOCAL) != 0)
-           continue;
+         /* Adjust the reloc address.  */
+         int_rel.r_vaddr += (input_section->output_section->vma
+                             + input_section->output_offset
+                             - input_section->vma);
 
-         /* The enative pointer can be NULL for a symbol created by
-            the linker via ecoff_make_empty_symbol.  */
-         if (bfd_asymbol_flavour (sym_ptr) != bfd_target_ecoff_flavour
-             || (((ecoff_symbol_type *) sym_ptr)->native.enative
-                 == (struct ext_ext *) NULL))
+         /* Save the changed reloc information.  */
+         mips_ecoff_swap_reloc_out (input_bfd, &int_rel, (PTR) ext_rel);
+       }
+      else
+       {
+         /* We are producing a final executable.  */
+         if (int_rel.r_extern)
            {
-             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;
+             /* This is a reloc against a symbol.  */
+             if (h->root.type == bfd_link_hash_defined
+                 || h->root.type == bfd_link_hash_defweak)
+               {
+                 asection *hsec;
+
+                 hsec = h->root.u.def.section;
+                 relocation = (h->root.u.def.value
+                               + hsec->output_section->vma
+                               + hsec->output_offset);
+               }
+             else
+               {
+                 if (! ((*info->callbacks->undefined_symbol)
+                        (info, h->root.root.string, input_bfd,
+                         input_section,
+                         int_rel.r_vaddr - input_section->vma)))
+                   return false;
+                 relocation = 0;
+               }
            }
          else
            {
-             ecoff_symbol_type *ecoff_sym_ptr;
-
-             ecoff_sym_ptr = (ecoff_symbol_type *) sym_ptr;
-             if (ecoff_sym_ptr->local)
-               abort ();
-             ecoff_swap_ext_in (abfd, ecoff_sym_ptr->native.enative, &esym);
-
-             /* If we're producing an executable, move common symbols
-                into bss.  */
-             if (relocateable == false)
+             /* This is a reloc against a section.  */
+             relocation = (s->output_section->vma
+                           + s->output_offset
+                           - s->vma);
+
+             /* A PC relative reloc is already correct in the object
+                file.  Make it look like a pcrel_offset relocation by
+                adding in the start address.  */
+             if (howto->pc_relative)
                {
-                 if (esym.asym.sc == scCommon)
-                   esym.asym.sc = scBss;
-                 else if (esym.asym.sc == scSCommon)
-                   esym.asym.sc = scSBss;
+                 if (int_rel.r_type != MIPS_R_RELHI || ! use_lo)
+                   relocation += int_rel.r_vaddr + adjust;
+                 else
+                   relocation += lo_int_rel.r_vaddr + adjust;
                }
-
-             /* Adjust the FDR index for the symbol by that used for
-                the input BFD.  */
-             esym.ifd += ecoff_data (bfd_asymbol_bfd (sym_ptr))->ifdbase;
            }
 
-         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;
+         if (int_rel.r_type != MIPS_R_REFHI
+             && int_rel.r_type != MIPS_R_RELHI)
+           r = _bfd_final_link_relocate (howto,
+                                         input_bfd,
+                                         input_section,
+                                         contents,
+                                         (int_rel.r_vaddr
+                                          - input_section->vma
+                                          + adjust),
+                                         relocation,
+                                         addend);
          else
-           esym.asym.value = (sym_ptr->value
-                              + sym_ptr->section->output_offset
-                              + sym_ptr->section->output_section->vma);
-
-         ecoff_swap_ext_out (abfd, &esym, external_ext + symhdr->iextMax);
-
-         ecoff_set_sym_index (sym_ptr, symhdr->iextMax);
+           {
+             mips_relocate_hi (&int_rel,
+                               use_lo ? &lo_int_rel : NULL,
+                               input_bfd, input_section, contents, adjust,
+                               relocation,
+                               int_rel.r_type == MIPS_R_RELHI);
+             r = bfd_reloc_ok;
+           }
+       }
 
-         ++symhdr->iextMax;
+      /* MIPS_R_JMPADDR requires peculiar overflow detection.  The
+        instruction provides a 28 bit address (the two lower bits are
+        implicit zeroes) which is combined with the upper four bits
+        of the instruction address.  */
+      if (r == bfd_reloc_ok
+         && int_rel.r_type == MIPS_R_JMPADDR
+         && (((relocation
+               + addend
+               + (int_rel.r_extern ? 0 : s->vma))
+              & 0xf0000000)
+             != ((input_section->output_section->vma
+                  + input_section->output_offset
+                  + (int_rel.r_vaddr - input_section->vma)
+                  + adjust)
+                 & 0xf0000000)))
+       r = bfd_reloc_overflow;
+
+      if (r != bfd_reloc_ok)
+       {
+         switch (r)
+           {
+           default:
+           case bfd_reloc_outofrange:
+             abort ();
+           case bfd_reloc_overflow:
+             {
+               const char *name;
 
-         strcpy (ssext + symhdr->issExtMax, sym_ptr->name);
-         symhdr->issExtMax += strlen (sym_ptr->name) + 1;
+               if (int_rel.r_extern)
+                 name = h->root.root.string;
+               else
+                 name = bfd_section_name (input_bfd, s);
+               if (! ((*info->callbacks->reloc_overflow)
+                      (info, name, howto->name, (bfd_vma) 0,
+                       input_bfd, input_section,
+                       int_rel.r_vaddr - input_section->vma)))
+                 return false;
+             }
+             break;
+           }
        }
     }
 
-  /* Adjust the counts so that structures are longword aligned.  */
-  symhdr->cbLine = (symhdr->cbLine + 3) &~ 3;
-  symhdr->issMax = (symhdr->issMax + 3) &~ 3;
-  symhdr->issExtMax = (symhdr->issExtMax + 3) &~ 3;
-
   return true;
 }
 \f
-/* Set the architecture.  The only architecture we support here is
-   mips.  We set the architecture anyhow, since many callers ignore
-   the return value.  */
+/* Read in the relocs for a section.  */
 
 static boolean
-ecoff_set_arch_mach (abfd, arch, machine)
-     bfd *abfd;
-     enum bfd_architecture arch;
-     unsigned long machine;
-{
-  bfd_default_set_arch_mach (abfd, arch, machine);
-  return arch == bfd_arch_mips;
-}
-
-/* Get the size of the section headers.  We do not output the .scommon
-   section which we created in ecoff_mkobject.  */
-
-static int
-ecoff_sizeof_headers (abfd, reloc)
-     bfd *abfd;
-     boolean reloc;
-{
-  return FILHSZ + AOUTSZ + (abfd->section_count - 1) * SCNHSZ;
-}
-
-/* Calculate the file position for each section, and set
-   reloc_filepos.  */
-
-static void
-ecoff_compute_section_file_positions (abfd)
+mips_read_relocs (abfd, sec)
      bfd *abfd;
+     asection *sec;
 {
-  asection *current;
-  file_ptr sofar;
-  file_ptr old_sofar;
-  boolean first_data;
+  struct ecoff_section_tdata *section_tdata;
 
-  if (bfd_get_start_address (abfd)) 
-    abfd->flags |= EXEC_P;
-
-  sofar = ecoff_sizeof_headers (abfd, false);
-
-  first_data = true;
-  for (current = abfd->sections;
-       current != (asection *) NULL;
-       current = current->next)
+  section_tdata = ecoff_section_data (abfd, sec);
+  if (section_tdata == (struct ecoff_section_tdata *) NULL)
     {
-      /* Only deal with sections which have contents */
-      if (! (current->flags & SEC_HAS_CONTENTS)
-         || strcmp (current->name, SCOMMON) == 0)
-       continue;
+      sec->used_by_bfd =
+       (PTR) bfd_alloc (abfd, sizeof (struct ecoff_section_tdata));
+      if (sec->used_by_bfd == NULL)
+       return false;
 
-      /* On Ultrix, the data sections in an executable file must be
-        aligned to a page boundary within the file.  This does not
-        affect the section size, though.  FIXME: Does this work for
-        other platforms?  */
-      if ((abfd->flags & EXEC_P) != 0
-         && first_data != false
-         && (current->flags & SEC_CODE) == 0)
-       {
-         sofar = (sofar + ROUND_SIZE - 1) &~ (ROUND_SIZE - 1);
-         first_data = false;
-       }
+      section_tdata = ecoff_section_data (abfd, sec);
+      section_tdata->external_relocs = NULL;
+      section_tdata->contents = NULL;
+      section_tdata->offsets = NULL;
+    }
 
-      /* Align the sections in the file to the same boundary on
-        which they are aligned in virtual memory.  */
-      old_sofar = sofar;
-      sofar = BFD_ALIGN (sofar, 1 << current->alignment_power);
+  if (section_tdata->external_relocs == NULL)
+    {
+      bfd_size_type external_relocs_size;
 
-      current->filepos = sofar;
+      external_relocs_size = (ecoff_backend (abfd)->external_reloc_size
+                             * sec->reloc_count);
 
-      sofar += current->_raw_size;
+      section_tdata->external_relocs =
+       (PTR) bfd_alloc (abfd, external_relocs_size);
+      if (section_tdata->external_relocs == NULL && external_relocs_size != 0)
+       return false;
 
-      /* make sure that this section is of the right size too */
-      old_sofar = sofar;
-      sofar = BFD_ALIGN (sofar, 1 << current->alignment_power);
-      current->_raw_size += sofar - old_sofar;
+      if (bfd_seek (abfd, sec->rel_filepos, SEEK_SET) != 0
+         || (bfd_read (section_tdata->external_relocs, 1,
+                       external_relocs_size, abfd)
+             != external_relocs_size))
+       return false;
     }
 
-  ecoff_data (abfd)->reloc_filepos = sofar;
+  return true;
 }
 
-/* Set the contents of a section.  */
+/* Relax a section when linking a MIPS ECOFF file.  This is used for
+   embedded PIC code, which always uses PC relative branches which
+   only have an 18 bit range on MIPS.  If a branch is not in range, we
+   generate a long instruction sequence to compensate.  Each time we
+   find a branch to expand, we have to check all the others again to
+   make sure they are still in range.  This is slow, but it only has
+   to be done when -relax is passed to the linker.
+
+   This routine figures out which branches need to expand; the actual
+   expansion is done in mips_relocate_section when the section
+   contents are relocated.  The information is stored in the offsets
+   field of the ecoff_section_tdata structure.  An offset of 1 means
+   that the branch must be expanded into a multi-instruction PC
+   relative branch (such an offset will only occur for a PC relative
+   branch to an external symbol).  Any other offset must be a multiple
+   of four, and is the amount to change the branch by (such an offset
+   will only occur for a PC relative branch within the same section).
+
+   We do not modify the section relocs or contents themselves so that
+   if memory usage becomes an issue we can discard them and read them
+   again.  The only information we must save in memory between this
+   routine and the mips_relocate_section routine is the table of
+   offsets.  */
 
 static boolean
-ecoff_set_section_contents (abfd, section, location, offset, count)
+mips_relax_section (abfd, sec, info, again)
      bfd *abfd;
-     asection *section;
-     PTR location;
-     file_ptr offset;
-     bfd_size_type count;
+     asection *sec;
+     struct bfd_link_info *info;
+     boolean *again;
 {
-  if (abfd->output_has_begun == false)
-    ecoff_compute_section_file_positions (abfd);
+  struct ecoff_section_tdata *section_tdata;
+  bfd_byte *contents = NULL;
+  long *offsets;
+  struct external_reloc *ext_rel;
+  struct external_reloc *ext_rel_end;
+  unsigned int i;
 
-  bfd_seek (abfd, (file_ptr) (section->filepos + offset), SEEK_SET);
+  /* Assume we are not going to need another pass.  */
+  *again = false;
 
-  if (count != 0)
-    return (bfd_write (location, 1, count, abfd) == count) ? true : false;
+  /* If we are not generating an ECOFF file, this is much too
+     confusing to deal with.  */
+  if (info->hash->creator->flavour != bfd_get_flavour (abfd))
+    return true;
 
-  return true;
-}
+  /* If there are no relocs, there is nothing to do.  */
+  if (sec->reloc_count == 0)
+    return true;
 
-/* Write out an ECOFF file.  */
+  /* We are only interested in PC relative relocs, and why would there
+     ever be one from anything but the .text section?  */
+  if (strcmp (bfd_get_section_name (abfd, sec), ".text") != 0)
+    return true;
 
-static boolean
-ecoff_write_object_contents (abfd)
-     bfd *abfd;
-{
-  asection *current;
-  unsigned int count;
-  file_ptr scn_base;
-  file_ptr reloc_base;
-  file_ptr sym_base;
-  unsigned long reloc_size;
-  unsigned long text_size;
-  unsigned long text_start;
-  unsigned long data_size;
-  unsigned long data_start;
-  unsigned long bss_size;
-  struct internal_filehdr internal_f;
-  struct internal_aouthdr internal_a;
-  int i;
-
-  bfd_error = system_call_error;
-
-  if(abfd->output_has_begun == false)
-    ecoff_compute_section_file_positions(abfd);
-
-  if (abfd->sections != (asection *) NULL)
-    scn_base = abfd->sections->filepos;
-  else
-    scn_base = 0;
-  reloc_base = ecoff_data (abfd)->reloc_filepos;
-
-  count = 1;
-  reloc_size = 0;
-  for (current = abfd->sections;
-       current != (asection *)NULL; 
-       current = current->next) 
+  /* Read in the relocs, if we haven't already got them.  */
+  section_tdata = ecoff_section_data (abfd, sec);
+  if (section_tdata == (struct ecoff_section_tdata *) NULL
+      || section_tdata->external_relocs == NULL)
     {
-      if (strcmp (current->name, SCOMMON) == 0)
-       continue;
-      current->target_index = count;
-      ++count;
-      if (current->reloc_count != 0)
-       {
-         bfd_size_type relsize;
-
-         current->rel_filepos = reloc_base;
-         relsize = current->reloc_count * RELSZ;
-         reloc_size += relsize;
-         reloc_base += relsize;
-       }
-      else
-       current->rel_filepos = 0;
+      if (! mips_read_relocs (abfd, sec))
+       goto error_return;
+      section_tdata = ecoff_section_data (abfd, sec);
     }
 
-  sym_base = reloc_base + 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
-     platforms?  */
-  if ((abfd->flags & EXEC_P) != 0)
-    sym_base = (sym_base + ROUND_SIZE - 1) &~ (ROUND_SIZE - 1);
-
-  ecoff_data (abfd)->sym_filepos = sym_base;
-
-  text_size = ecoff_sizeof_headers (abfd, false);
-  text_start = 0;
-  data_size = 0;
-  data_start = 0;
-  bss_size = 0;
+  if (sec->_cooked_size == 0)
+    {
+      /* We must initialize _cooked_size only the first time we are
+        called.  */
+      sec->_cooked_size = sec->_raw_size;
+    }
 
-  /* Write section headers to the file.  */
+  contents = section_tdata->contents;
+  offsets = section_tdata->offsets;
 
-  internal_f.f_nscns = 0;
-  if (bfd_seek (abfd, (file_ptr) (FILHSZ + AOUTSZ), SEEK_SET) != 0)
-    return false;
-  for (current = abfd->sections;
-       current != (asection *) NULL;
-       current = current->next)
+  /* Look for any external PC relative relocs.  Internal PC relative
+     relocs are already correct in the object file, so they certainly
+     can not overflow.  */
+  ext_rel = (struct external_reloc *) section_tdata->external_relocs;
+  ext_rel_end = ext_rel + sec->reloc_count;
+  for (i = 0; ext_rel < ext_rel_end; ext_rel++, i++)
     {
-      struct internal_scnhdr section;
-      bfd_vma vma;
+      struct internal_reloc int_rel;
+      struct ecoff_link_hash_entry *h;
+      asection *hsec;
+      bfd_signed_vma relocation;
+      struct external_reloc *adj_ext_rel;
+      unsigned int adj_i;
+      unsigned long ext_count;
+      struct ecoff_link_hash_entry **adj_h_ptr;
+      struct ecoff_link_hash_entry **adj_h_ptr_end;
+      struct ecoff_value_adjust *adjust;
 
-      if (strcmp (current->name, SCOMMON) == 0)
+      /* If we have already expanded this reloc, we certainly don't
+        need to do it again.  */
+      if (offsets != (long *) NULL && offsets[i] == 1)
+       continue;
+
+      /* Quickly check that this reloc is external PCREL16.  */
+      if (bfd_header_big_endian (abfd))
        {
-         BFD_ASSERT (bfd_get_section_size_before_reloc (current) == 0
-                     && current->reloc_count == 0);
-         continue;
+         if ((ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_BIG) == 0
+             || (((ext_rel->r_bits[3] & RELOC_BITS3_TYPE_BIG)
+                  >> RELOC_BITS3_TYPE_SH_BIG)
+                 != MIPS_R_PCREL16))
+           continue;
        }
-
-      ++internal_f.f_nscns;
-
-      strncpy (section.s_name, current->name, sizeof section.s_name);
-
-      /* FIXME: is this correct for shared libraries?  I think it is
-        but I have no platform to check.  Ian Lance Taylor.  */
-      vma = bfd_get_section_vma (abfd, current);
-      if (strcmp (current->name, _LIB) == 0)
-       section.s_vaddr = 0;
       else
-       section.s_vaddr = vma;
+       {
+         if ((ext_rel->r_bits[3] & RELOC_BITS3_EXTERN_LITTLE) == 0
+             || (((ext_rel->r_bits[3] & RELOC_BITS3_TYPE_LITTLE)
+                  >> RELOC_BITS3_TYPE_SH_LITTLE)
+                 != MIPS_R_PCREL16))
+           continue;
+       }
 
-      section.s_paddr = vma;
-      section.s_size = bfd_get_section_size_before_reloc (current);
+      mips_ecoff_swap_reloc_in (abfd, (PTR) ext_rel, &int_rel);
 
-      /* If this section has no size or is unloadable then the scnptr
-        will be 0 too.  */
-      if (current->_raw_size == 0
-         || (current->flags & (SEC_LOAD | SEC_HAS_CONTENTS)) == 0)
-       section.s_scnptr = 0;
-      else
-       section.s_scnptr = current->filepos;
-      section.s_relptr = current->rel_filepos;
-
-      /* FIXME: the lnnoptr of the .sbss or .sdata section of an
-        object file produced by the assembler is supposed to point to
-        information about how much room is required by objects of
-        various different sizes.  I think this only matters if we
-        want the linker to compute the best size to use, or
-        something.  I don't know what happens if the information is
-        not present.  */
-      section.s_lnnoptr = 0;
-
-      section.s_nreloc = current->reloc_count;
-      section.s_nlnno = 0;
-      section.s_flags = ecoff_sec_to_styp_flags (current->name,
-                                                current->flags);
-
-      {
-       SCNHDR buff;
-
-       ecoff_swap_scnhdr_out (abfd, (PTR) &section, (PTR) &buff);
-       if (bfd_write ((PTR) &buff, 1, SCNHSZ, abfd) != SCNHSZ)
-         return false;
-      }
+      h = ecoff_data (abfd)->sym_hashes[int_rel.r_symndx];
+      if (h == (struct ecoff_link_hash_entry *) NULL)
+       abort ();
 
-      if ((section.s_flags & STYP_TEXT) != 0)
+      if (h->root.type != bfd_link_hash_defined
+         && h->root.type != bfd_link_hash_defweak)
        {
-         text_size += bfd_get_section_size_before_reloc (current);
-         if (text_start == 0 || text_start > vma)
-           text_start = vma;
+         /* Just ignore undefined symbols.  These will presumably
+            generate an error later in the link.  */
+         continue;
        }
-      else if ((section.s_flags & STYP_RDATA) != 0
-              || (section.s_flags & STYP_DATA) != 0
-              || (section.s_flags & STYP_LIT8) != 0
-              || (section.s_flags & STYP_LIT4) != 0
-              || (section.s_flags & STYP_SDATA) != 0)
+
+      /* Get the value of the symbol.  */
+      hsec = h->root.u.def.section;
+      relocation = (h->root.u.def.value
+                   + hsec->output_section->vma
+                   + hsec->output_offset);
+
+      /* Subtract out the current address.  */
+      relocation -= (sec->output_section->vma
+                    + sec->output_offset
+                    + (int_rel.r_vaddr - sec->vma));
+
+      /* The addend is stored in the object file.  In the normal case
+        of ``bal symbol'', the addend will be -4.  It will only be
+        different in the case of ``bal symbol+constant''.  To avoid
+        always reading in the section contents, we don't check the
+        addend in the object file (we could easily check the contents
+        if we happen to have already read them in, but I fear that
+        this could be confusing).  This means we will screw up if
+        there is a branch to a symbol that is in range, but added to
+        a constant which puts it out of range; in such a case the
+        link will fail with a reloc overflow error.  Since the
+        compiler will never generate such code, it should be easy
+        enough to work around it by changing the assembly code in the
+        source file.  */
+      relocation -= 4;
+
+      /* Now RELOCATION is the number we want to put in the object
+        file.  See whether it fits.  */
+      if (relocation >= -0x20000 && relocation < 0x20000)
+       continue;
+
+      /* Now that we know this reloc needs work, which will rarely
+        happen, go ahead and grab the section contents.  */
+      if (contents == (bfd_byte *) NULL)
        {
-         data_size += bfd_get_section_size_before_reloc (current);
-         if (data_start == 0 || data_start > vma)
-           data_start = vma;
+         if (info->keep_memory)
+           contents = (bfd_byte *) bfd_alloc (abfd, sec->_raw_size);
+         else
+           contents = (bfd_byte *) bfd_malloc ((size_t) sec->_raw_size);
+         if (contents == (bfd_byte *) NULL)
+           goto error_return;
+         if (! bfd_get_section_contents (abfd, sec, (PTR) contents,
+                                         (file_ptr) 0, sec->_raw_size))
+           goto error_return;
+         if (info->keep_memory)
+           section_tdata->contents = contents;
        }
-      else if ((section.s_flags & STYP_BSS) != 0
-              || (section.s_flags & STYP_SBSS) != 0)
-       bss_size += bfd_get_section_size_before_reloc (current);
-    }  
 
-  /* Set up the file header.  */
-
-  if (abfd->xvec->header_byteorder_big_p != false)
-    internal_f.f_magic = MIPS_MAGIC_BIG;
-  else
-    internal_f.f_magic = MIPS_MAGIC_LITTLE;
+      /* We only support changing the bal instruction.  It would be
+        possible to handle other PC relative branches, but some of
+        them (the conditional branches) would require a different
+        length instruction sequence which would complicate both this
+        routine and mips_relax_pcrel16.  It could be written if
+        somebody felt it were important.  Ignoring this reloc will
+        presumably cause a reloc overflow error later on.  */
+      if (bfd_get_32 (abfd, contents + int_rel.r_vaddr - sec->vma)
+         != 0x0411ffff) /* bgezal $0,. == bal . */
+       continue;
 
-  /*
-    We will NOT put a fucking timestamp in the header here. Every time you
-    put it back, I will come in and take it out again.  I'm sorry.  This
-    field does not belong here.  We fill it with a 0 so it compares the
-    same but is not a reasonable time. -- gnu@cygnus.com
-    */
-  internal_f.f_timdat = 0;
+      /* Bother.  We need to expand this reloc, and we will need to
+        make another relaxation pass since this change may put other
+        relocs out of range.  We need to examine the local branches
+        and we need to allocate memory to hold the offsets we must
+        add to them.  We also need to adjust the values of all
+        symbols in the object file following this location.  */
 
-  if (bfd_get_symcount (abfd) != 0)
-    {
-      /* The ECOFF f_nsyms field is not actually the number of
-        symbols, it's the size of symbolic information header.  */
-      internal_f.f_nsyms = sizeof (struct hdr_ext);
-      internal_f.f_symptr = sym_base;
-    }
-  else
-    {
-      internal_f.f_nsyms = 0;
-      internal_f.f_symptr = 0;
-    }
+      sec->_cooked_size += PCREL16_EXPANSION_ADJUSTMENT;
+      *again = true;
 
-  internal_f.f_opthdr = AOUTSZ;
+      if (offsets == (long *) NULL)
+       {
+         size_t size;
+
+         size = sec->reloc_count * sizeof (long);
+         offsets = (long *) bfd_alloc (abfd, size);
+         if (offsets == (long *) NULL)
+           goto error_return;
+         memset (offsets, 0, size);
+         section_tdata->offsets = offsets;
+       }
 
-  internal_f.f_flags = F_LNNO;
-  if (reloc_size == 0)
-    internal_f.f_flags |= F_RELFLG;
-  if (bfd_get_symcount (abfd) == 0)
-    internal_f.f_flags |= F_LSYMS;
-  if (abfd->flags & EXEC_P)
-    internal_f.f_flags |= F_EXEC;
+      offsets[i] = 1;
 
-  if (! abfd->xvec->byteorder_big_p)
-    internal_f.f_flags |= F_AR32WR;
-  else
-    internal_f.f_flags |= F_AR32W;
-
-  /* Set up the ``optional'' header.  */
-  internal_a.magic = ZMAGIC;
-
-  /* FIXME: This is what Ultrix puts in, and it makes the Ultrix
-     linker happy.  But, is it right?  */
-  internal_a.vstamp = 0x20a;
-
-  /* At least on Ultrix, these have to be rounded to page boundaries.
-     FIXME: Is this true on other platforms?  */
-  internal_a.tsize = (text_size + ROUND_SIZE - 1) &~ (ROUND_SIZE - 1);
-  internal_a.text_start = text_start &~ (ROUND_SIZE - 1);
-  internal_a.dsize = (data_size + ROUND_SIZE - 1) &~ (ROUND_SIZE - 1);
-  internal_a.data_start = data_start &~ (ROUND_SIZE - 1);
-
-  /* On Ultrix, the initial portions of the .sbss and .bss segments
-     are at the end of the data section.  The bsize field in the
-     optional header records how many bss bytes are required beyond
-     those in the data section.  The value is not rounded to a page
-     boundary.  */
-  if (bss_size < internal_a.dsize - data_size)
-    bss_size = 0;
-  else
-    bss_size -= internal_a.dsize - data_size;
-  internal_a.bsize = bss_size;
-  internal_a.bss_start = internal_a.data_start + internal_a.dsize;
+      /* Now look for all PC relative references that cross this reloc
+        and adjust their offsets.  */
+      adj_ext_rel = (struct external_reloc *) section_tdata->external_relocs;
+      for (adj_i = 0; adj_ext_rel < ext_rel_end; adj_ext_rel++, adj_i++)
+       {
+         struct internal_reloc adj_int_rel;
+         bfd_vma start, stop;
+         int change;
 
-  internal_a.entry = bfd_get_start_address (abfd);
+         mips_ecoff_swap_reloc_in (abfd, (PTR) adj_ext_rel, &adj_int_rel);
 
-  internal_a.gp_value = ecoff_data (abfd)->gp;
+         if (adj_int_rel.r_type == MIPS_R_PCREL16)
+           {
+             unsigned long insn;
 
-  internal_a.gprmask = ecoff_data (abfd)->gprmask;
-  for (i = 0; i < 4; i++)
-    internal_a.cprmask[i] = ecoff_data (abfd)->cprmask[i];
+             /* We only care about local references.  External ones
+                will be relocated correctly anyhow.  */
+             if (adj_int_rel.r_extern)
+               continue;
 
-  /* Write out the file header and the optional header.  */
+             /* We are only interested in a PC relative reloc within
+                this section.  FIXME: Cross section PC relative
+                relocs may not be handled correctly; does anybody
+                care?  */
+             if (adj_int_rel.r_symndx != RELOC_SECTION_TEXT)
+               continue;
 
-  if (bfd_seek (abfd, (file_ptr) 0, SEEK_SET) != 0)
-    return false;
+             start = adj_int_rel.r_vaddr;
 
-  {
-    FILHDR buff;
-    ecoff_swap_filehdr_out (abfd, (PTR) &internal_f, (PTR) &buff);
-    if (bfd_write ((PTR) &buff, 1, FILHSZ, abfd) != FILHSZ)
-      return false;
-  }
+             insn = bfd_get_32 (abfd,
+                                contents + adj_int_rel.r_vaddr - sec->vma);
 
-  {
-    AOUTHDR buff;
+             stop = (insn & 0xffff) << 2;
+             if ((stop & 0x20000) != 0)
+               stop -= 0x40000;
+             stop += adj_int_rel.r_vaddr + 4;
+           }
+         else if (adj_int_rel.r_type == MIPS_R_RELHI)
+           {
+             struct internal_reloc rello;
+             long addhi, addlo;
+
+             /* The next reloc must be MIPS_R_RELLO, and we handle
+                them together.  */
+             BFD_ASSERT (adj_ext_rel + 1 < ext_rel_end);
+
+             mips_ecoff_swap_reloc_in (abfd, (PTR) (adj_ext_rel + 1), &rello);
+
+             BFD_ASSERT (rello.r_type == MIPS_R_RELLO);
+             
+             addhi = bfd_get_32 (abfd,
+                                  contents + adj_int_rel.r_vaddr - sec->vma);
+             addhi &= 0xffff;
+             if (addhi & 0x8000)
+               addhi -= 0x10000;
+             addhi <<= 16;
+
+             addlo = bfd_get_32 (abfd, contents + rello.r_vaddr - sec->vma);
+             addlo &= 0xffff;
+             if (addlo & 0x8000)
+               addlo -= 0x10000;
+
+             if (adj_int_rel.r_extern)
+               {
+                 /* The value we want here is
+                      sym - RELLOaddr + addend
+                    which we can express as
+                      sym - (RELLOaddr - addend)
+                    Therefore if we are expanding the area between
+                    RELLOaddr and RELLOaddr - addend we must adjust
+                    the addend.  This is admittedly ambiguous, since
+                    we might mean (sym + addend) - RELLOaddr, but in
+                    practice we don't, and there is no way to handle
+                    that case correctly since at this point we have
+                    no idea whether any reloc is being expanded
+                    between sym and sym + addend.  */
+                 start = rello.r_vaddr - (addhi + addlo);
+                 stop = rello.r_vaddr;
+               }
+             else
+               {
+                 /* An internal RELHI/RELLO pair represents the
+                    difference between two addresses, $LC0 - foo.
+                    The symndx value is actually the difference
+                    between the reloc address and $LC0.  This lets us
+                    compute $LC0, and, by considering the addend,
+                    foo.  If the reloc we are expanding falls between
+                    those two relocs, we must adjust the addend.  At
+                    this point, the symndx value is actually in the
+                    r_offset field, where it was put by
+                    mips_ecoff_swap_reloc_in.  */
+                 start = rello.r_vaddr - adj_int_rel.r_offset;
+                 stop = start + addhi + addlo;
+               }
+           }
+         else if (adj_int_rel.r_type == MIPS_R_SWITCH)
+           {
+             /* A MIPS_R_SWITCH reloc represents a word of the form
+                  .word $L3-$LS12
+                The value in the object file is correct, assuming the
+                original value of $L3.  The symndx value is actually
+                the difference between the reloc address and $LS12.
+                This lets us compute the original value of $LS12 as
+                  vaddr - symndx
+                and the original value of $L3 as
+                  vaddr - symndx + addend
+                where addend is the value from the object file.  At
+                this point, the symndx value is actually found in the
+                r_offset field, since it was moved by
+                mips_ecoff_swap_reloc_in.  */
+             start = adj_int_rel.r_vaddr - adj_int_rel.r_offset;
+             stop = start + bfd_get_32 (abfd,
+                                        (contents
+                                         + adj_int_rel.r_vaddr
+                                         - sec->vma));
+           }
+         else
+           continue;
 
-    ecoff_swap_aouthdr_out (abfd, (PTR) &internal_a, (PTR) &buff);
-    if (bfd_write ((PTR) &buff, 1, AOUTSZ, abfd) != AOUTSZ)
-      return false;
-  }
+         /* If the range expressed by this reloc, which is the
+            distance between START and STOP crosses the reloc we are
+            expanding, we must adjust the offset.  The sign of the
+            adjustment depends upon the direction in which the range
+            crosses the reloc being expanded.  */
+         if (start <= int_rel.r_vaddr && stop > int_rel.r_vaddr)
+           change = PCREL16_EXPANSION_ADJUSTMENT;
+         else if (start > int_rel.r_vaddr && stop <= int_rel.r_vaddr)
+           change = - PCREL16_EXPANSION_ADJUSTMENT;
+         else
+           change = 0;
 
-  /* Write out the relocs.  */
-  for (current = abfd->sections;
-       current != (asection *) NULL;
-       current = current->next)
-    {
-      RELOC *buff;
-      arelent **reloc_ptr_ptr;
-      arelent **reloc_end;
-      RELOC *out_ptr;
+         offsets[adj_i] += change;
 
-      if (current->reloc_count == 0)
-       continue;
+         if (adj_int_rel.r_type == MIPS_R_RELHI)
+           {
+             adj_ext_rel++;
+             adj_i++;
+             offsets[adj_i] += change;
+           }
+       }
 
-      buff = (RELOC *) bfd_alloc (abfd, current->reloc_count * RELSZ);
-      if (buff == (RELOC *) NULL)
+      /* Find all symbols in this section defined by this object file
+        and adjust their values.  Note that we decide whether to
+        adjust the value based on the value stored in the ECOFF EXTR
+        structure, because the value stored in the hash table may
+        have been changed by an earlier expanded reloc and thus may
+        no longer correctly indicate whether the symbol is before or
+        after the expanded reloc.  */
+      ext_count = ecoff_data (abfd)->debug_info.symbolic_header.iextMax;
+      adj_h_ptr = ecoff_data (abfd)->sym_hashes;
+      adj_h_ptr_end = adj_h_ptr + ext_count;
+      for (; adj_h_ptr < adj_h_ptr_end; adj_h_ptr++)
        {
-         bfd_error = no_memory;
-         return false;
+         struct ecoff_link_hash_entry *adj_h;
+
+         adj_h = *adj_h_ptr;
+         if (adj_h != (struct ecoff_link_hash_entry *) NULL
+             && (adj_h->root.type == bfd_link_hash_defined
+                 || adj_h->root.type == bfd_link_hash_defweak)
+             && adj_h->root.u.def.section == sec
+             && adj_h->esym.asym.value > int_rel.r_vaddr)
+           adj_h->root.u.def.value += PCREL16_EXPANSION_ADJUSTMENT;
        }
 
-      reloc_ptr_ptr = current->orelocation;
-      reloc_end = reloc_ptr_ptr + current->reloc_count;
-      out_ptr = buff;
-      for (; reloc_ptr_ptr < reloc_end; reloc_ptr_ptr++, out_ptr++)
-       {
-         arelent *reloc;
-         asymbol *sym;
-         struct internal_reloc in;
-         
-         memset (&in, 0, sizeof in);
-
-         reloc = *reloc_ptr_ptr;
-         sym = *reloc->sym_ptr_ptr;
-
-         /* This must be an ECOFF reloc.  */
-         BFD_ASSERT (reloc->howto != (reloc_howto_type *) NULL
-                     && reloc->howto >= ecoff_howto_table
-                     && (reloc->howto
-                         < (ecoff_howto_table + ECOFF_HOWTO_COUNT)));
-
-         in.r_vaddr = reloc->address + bfd_get_section_vma (abfd, current);
-         in.r_type = reloc->howto->type;
-
-         /* If this is a REFHI reloc, the next one must be a REFLO
-            reloc for the same symbol.  */
-         BFD_ASSERT (in.r_type != ECOFF_R_REFHI
-                     || (reloc_ptr_ptr < reloc_end
-                         && (reloc_ptr_ptr[1]->howto
-                             != (reloc_howto_type *) NULL)
-                         && (reloc_ptr_ptr[1]->howto->type
-                             == ECOFF_R_REFLO)
-                         && (sym == *reloc_ptr_ptr[1]->sym_ptr_ptr)));
-
-         if ((sym->flags & BSF_SECTION_SYM) == 0)
-           {
-             in.r_symndx = ecoff_get_sym_index (*reloc->sym_ptr_ptr);
-             in.r_extern = 1;
-           }
-         else
-           {
-             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
-               abort ();
-             in.r_extern = 0;
-           }
+      /* Add an entry to the symbol value adjust list.  This is used
+        by bfd_ecoff_debug_accumulate to adjust the values of
+        internal symbols and FDR's.  */
+      adjust = ((struct ecoff_value_adjust *)
+               bfd_alloc (abfd, sizeof (struct ecoff_value_adjust)));
+      if (adjust == (struct ecoff_value_adjust *) NULL)
+       goto error_return;
 
-         ecoff_swap_reloc_out (abfd, (PTR) &in, (PTR) out_ptr);
-       }
+      adjust->start = int_rel.r_vaddr;
+      adjust->end = sec->vma + sec->_raw_size;
+      adjust->adjust = PCREL16_EXPANSION_ADJUSTMENT;
 
-      if (bfd_seek (abfd, current->rel_filepos, SEEK_SET) != 0)
-       return false;
-      if (bfd_write ((PTR) buff, RELSZ, current->reloc_count, abfd)
-         != RELSZ * current->reloc_count)
-       return false;
-      bfd_release (abfd, (PTR) buff);
+      adjust->next = ecoff_data (abfd)->debug_info.adjust;
+      ecoff_data (abfd)->debug_info.adjust = adjust;
     }
 
-  /* Write out the symbolic debugging information.  */
-  if (bfd_get_symcount (abfd) > 0)
-    {
-      HDRR *symhdr;
-      unsigned long sym_offset;
-      struct hdr_ext buff;
-
-      /* Set up the offsets in the symbolic header.  */
-      symhdr = &ecoff_data (abfd)->symbolic_header;
-      sym_offset = ecoff_data (abfd)->sym_filepos + sizeof (struct hdr_ext);
-
-#define SET(offset, size, ptr) \
-  if (symhdr->size == 0) \
-    symhdr->offset = 0; \
-  else \
-    symhdr->offset = (((char *) ecoff_data (abfd)->ptr \
-                      - (char *) ecoff_data (abfd)->raw_syments) \
-                     + sym_offset);
-
-      SET (cbLineOffset, cbLine, line);
-      SET (cbDnOffset, idnMax, external_dnr);
-      SET (cbPdOffset, ipdMax, external_pdr);
-      SET (cbSymOffset, isymMax, external_sym);
-      SET (cbOptOffset, ioptMax, external_opt);
-      SET (cbAuxOffset, iauxMax, external_aux);
-      SET (cbSsOffset, issMax, ss);
-      SET (cbSsExtOffset, issExtMax, ssext);
-      SET (cbFdOffset, ifdMax, external_fdr);
-      SET (cbRfdOffset, crfd, external_rfd);
-      SET (cbExtOffset, iextMax, external_ext);
-#undef SET
-
-      if (bfd_seek (abfd, (file_ptr) ecoff_data (abfd)->sym_filepos,
-                   SEEK_SET) != 0)
-       return false;
-      ecoff_swap_hdr_out (abfd, &ecoff_data (abfd)->symbolic_header, &buff);
-      if (bfd_write ((PTR) &buff, 1, sizeof buff, abfd) != sizeof buff)
-       return false;
-      if (bfd_write ((PTR) ecoff_data (abfd)->raw_syments, 1,
-                    ecoff_data (abfd)->raw_size, abfd)
-         != ecoff_data (abfd)->raw_size)
-       return false;
-    }
+  if (contents != (bfd_byte *) NULL && ! info->keep_memory)
+    free (contents);
 
   return true;
-}
-\f
-/* Archive handling.  ECOFF uses what appears to be a unique type of
-   archive header (which I call an armap).  The byte ordering of the
-   armap and the contents are encoded in the name of the armap itself.
-   At least for now, we only support archives with the same byte
-   ordering in the armap and the contents.
-
-   The first four bytes in the armap are the number of symbol
-   definitions.  This is always a power of two.
-
-   This is followed by the symbol definitions.  Each symbol definition
-   occupies 8 bytes.  The first four bytes are the offset from the
-   start of the armap strings to the null-terminated string naming
-   this symbol.  The second four bytes are the file offset to the
-   archive member which defines this symbol.  If the second four bytes
-   are 0, then this is not actually a symbol definition, and it should
-   be ignored.
-
-   The symbols are hashed into the armap with a closed hashing scheme.
-   See the functions below for the details of the algorithm.
-
-   We could use the hash table when looking up symbols in a library.
-   This would require a new BFD target entry point to replace the
-   bfd_get_next_mapent function used by the linker.
-
-   After the symbol definitions comes four bytes holding the size of
-   the string table, followed by the string table itself.  */
-
-/* The name of an archive headers looks like this:
-   __________E[BL]E[BL]_ (with a trailing space).
-   The trailing space is changed to an X if the archive is changed to
-   indicate that the armap is out of date.  */
-
-#define ARMAP_BIG_ENDIAN 'B'
-#define ARMAP_LITTLE_ENDIAN 'L'
-#define ARMAP_MARKER 'E'
-#define ARMAP_START "__________"
-#define ARMAP_HEADER_MARKER_INDEX 10
-#define ARMAP_HEADER_ENDIAN_INDEX 11
-#define ARMAP_OBJECT_MARKER_INDEX 12
-#define ARMAP_OBJECT_ENDIAN_INDEX 13
-#define ARMAP_END_INDEX 14
-#define ARMAP_END "_ "
-
-/* This is a magic number used in the hashing algorithm.  */
-#define ARMAP_HASH_MAGIC 0x9dd68ab5
-
-/* This returns the hash value to use for a string.  It also sets
-   *REHASH to the rehash adjustment if the first slot is taken.  SIZE
-   is the number of entries in the hash table, and HLOG is the log
-   base 2 of SIZE.  */
-
-static unsigned int
-ecoff_armap_hash (s, rehash, size, hlog)
-     CONST char *s;
-     unsigned int *rehash;
-     unsigned int size;
-     unsigned int hlog;
-{
-  unsigned int hash;
-
-  hash = *s++;
-  while (*s != '\0')
-    hash = ((hash >> 27) | (hash << 5)) + *s++;
-  hash *= ARMAP_HASH_MAGIC;
-  *rehash = (hash & (size - 1)) | 1;
-  return hash >> (32 - hlog);
+
+ error_return:
+  if (contents != (bfd_byte *) NULL && ! info->keep_memory)
+    free (contents);
+  return false;
 }
 
-/* Read in the armap.  */
+/* This routine is called from mips_relocate_section when a PC
+   relative reloc must be expanded into the five instruction sequence.
+   It handles all the details of the expansion, including resolving
+   the reloc.  */
 
 static boolean
-ecoff_slurp_armap (abfd)
-     bfd *abfd;
+mips_relax_pcrel16 (info, input_bfd, input_section, h, location, address)
+     struct bfd_link_info *info;
+     bfd *input_bfd;
+     asection *input_section;
+     struct ecoff_link_hash_entry *h;
+     bfd_byte *location;
+     bfd_vma address;
 {
-  char nextname[17];
-  unsigned int i;
-  struct areltdata *mapdata;
-  bfd_size_type parsed_size;
-  char *raw_armap;
-  struct artdata *ardata;
-  unsigned int count;
-  char *raw_ptr;
-  struct symdef *symdef_ptr;
-  char *stringbase;
-  
-  /* Get the name of the first element.  */
-  i = bfd_read ((PTR) nextname, 1, 16, abfd);
-  if (i == 0)
-      return true;
-  if (i != 16)
-      return false;
-
-  bfd_seek (abfd, (file_ptr) -16, SEEK_CUR);
-
-  /* See if the first element is an armap.  */
-  if (strncmp (nextname, ARMAP_START, sizeof ARMAP_START - 1) != 0
-      || nextname[ARMAP_HEADER_MARKER_INDEX] != ARMAP_MARKER
-      || (nextname[ARMAP_HEADER_ENDIAN_INDEX] != ARMAP_BIG_ENDIAN
-         && nextname[ARMAP_HEADER_ENDIAN_INDEX] != ARMAP_LITTLE_ENDIAN)
-      || nextname[ARMAP_OBJECT_MARKER_INDEX] != ARMAP_MARKER
-      || (nextname[ARMAP_OBJECT_ENDIAN_INDEX] != ARMAP_BIG_ENDIAN
-         && nextname[ARMAP_OBJECT_ENDIAN_INDEX] != ARMAP_LITTLE_ENDIAN)
-      || strncmp (nextname + ARMAP_END_INDEX,
-                 ARMAP_END, sizeof ARMAP_END - 1) != 0)
-    {
-      bfd_has_map (abfd) = false;
-      return true;
-    }
-
-  /* Make sure we have the right byte ordering.  */
-  if (((nextname[ARMAP_HEADER_ENDIAN_INDEX] == ARMAP_BIG_ENDIAN)
-       ^ (abfd->xvec->header_byteorder_big_p != false))
-      || ((nextname[ARMAP_OBJECT_ENDIAN_INDEX] == ARMAP_BIG_ENDIAN)
-         ^ (abfd->xvec->byteorder_big_p != false)))
-    {
-      bfd_error = wrong_format;
-      return false;
-    }
-
-  /* Read in the armap.  */
-  ardata = bfd_ardata (abfd);
-  mapdata = snarf_ar_hdr (abfd);
-  if (mapdata == (struct areltdata *) NULL)
-    return false;
-  parsed_size = mapdata->parsed_size;
-  bfd_release (abfd, (PTR) mapdata);
-    
-  raw_armap = (char *) bfd_alloc (abfd, parsed_size);
-  if (raw_armap == (char *) NULL)
-    {
-      bfd_error = no_memory;
-      return false;
-    }
-    
-  if (bfd_read ((PTR) raw_armap, 1, parsed_size, abfd) != parsed_size)
-    {
-      bfd_error = malformed_archive;
-      bfd_release (abfd, (PTR) raw_armap);
-      return false;
-    }
-    
-  count = bfd_h_get_32 (abfd, (PTR) raw_armap);
-
-  ardata->symdef_count = 0;
-  ardata->cache = (struct ar_cache *) NULL;
+  bfd_vma relocation;
 
-  /* Hack: overlay the symdefs on top of the raw archive data.  This
-     is the way do_slurp_bsd_armap works.  */
-  raw_ptr = raw_armap + LONG_SIZE;
-  symdef_ptr = (struct symdef *) raw_ptr;
-  ardata->symdefs = (carsym *) symdef_ptr;
-  stringbase = raw_ptr + count * (2 * LONG_SIZE) + LONG_SIZE;
+  /* 0x0411ffff is bgezal $0,. == bal .  */
+  BFD_ASSERT (bfd_get_32 (input_bfd, location) == 0x0411ffff);
+
+  /* We need to compute the distance between the symbol and the
+     current address plus eight.  */
+  relocation = (h->root.u.def.value
+               + h->root.u.def.section->output_section->vma
+               + h->root.u.def.section->output_offset);
+  relocation -= address + 8;
+
+  /* If the lower half is negative, increment the upper 16 half.  */
+  if ((relocation & 0x8000) != 0)
+    relocation += 0x10000;
+
+  bfd_put_32 (input_bfd, 0x04110001, location); /* bal .+8 */
+  bfd_put_32 (input_bfd,
+             0x3c010000 | ((relocation >> 16) & 0xffff), /* lui $at,XX */
+             location + 4);
+  bfd_put_32 (input_bfd,
+             0x24210000 | (relocation & 0xffff), /* addiu $at,$at,XX */
+             location + 8);
+  bfd_put_32 (input_bfd, 0x003f0821, location + 12); /* addu $at,$at,$ra */
+  bfd_put_32 (input_bfd, 0x0020f809, location + 16); /* jalr $at */
 
-#ifdef CHECK_ARMAP_HASH
-  {
-    unsigned int hlog;
-
-    /* Double check that I have the hashing algorithm right by making
-       sure that every symbol can be looked up successfully.  */
-    hlog = 0;
-    for (i = 1; i < count; i <<= 1)
-      hlog++;
-    BFD_ASSERT (i == count);
-
-    for (i = 0; i < count; i++, raw_ptr += 2 * LONG_SIZE)
-      {
-       unsigned int name_offset, file_offset;
-       unsigned int hash, rehash, srch;
-      
-       name_offset = bfd_h_get_32 (abfd, (PTR) raw_ptr);
-       file_offset = bfd_h_get_32 (abfd, (PTR) (raw_ptr + LONG_SIZE));
-       if (file_offset == 0)
-         continue;
-       hash = ecoff_armap_hash (stringbase + name_offset, &rehash, count,
-                                hlog);
-       if (hash == i)
-         continue;
+  return true;
+}
 
-       /* See if we can rehash to this location.  */
-       for (srch = (hash + rehash) & (count - 1);
-            srch != hash && srch != i;
-            srch = (srch + rehash) & (count - 1))
-         BFD_ASSERT (bfd_h_get_32 (abfd,
-                                   (PTR) (raw_armap
-                                          + LONG_SIZE
-                                          + (srch * 2 * LONG_SIZE)
-                                          + LONG_SIZE))
-                     != 0);
-       BFD_ASSERT (srch == i);
-      }
-  }
-
-  raw_ptr = raw_armap + LONG_SIZE;
-#endif /* CHECK_ARMAP_HASH */
-
-  for (i = 0; i < count; i++, raw_ptr += 2 * LONG_SIZE)
-    {
-      unsigned int name_offset, file_offset;
+/* Given a .sdata section and a .rel.sdata in-memory section, store
+   relocation information into the .rel.sdata section which can be
+   used at runtime to relocate the section.  This is called by the
+   linker when the --embedded-relocs switch is used.  This is called
+   after the add_symbols entry point has been called for all the
+   objects, and before the final_link entry point is called.  This
+   function presumes that the object was compiled using
+   -membedded-pic.  */
 
-      name_offset = bfd_h_get_32 (abfd, (PTR) raw_ptr);
-      file_offset = bfd_h_get_32 (abfd, (PTR) (raw_ptr + LONG_SIZE));
-      if (file_offset == 0)
-       continue;
-      symdef_ptr->s.name = stringbase + name_offset;
-      symdef_ptr->file_offset = file_offset;
-      ++symdef_ptr;
-      ++ardata->symdef_count;
-    }
+boolean
+bfd_mips_ecoff_create_embedded_relocs (abfd, info, datasec, relsec, errmsg)
+     bfd *abfd;
+     struct bfd_link_info *info;
+     asection *datasec;
+     asection *relsec;
+     char **errmsg;
+{
+  struct ecoff_link_hash_entry **sym_hashes;
+  struct ecoff_section_tdata *section_tdata;
+  struct external_reloc *ext_rel;
+  struct external_reloc *ext_rel_end;
+  bfd_byte *p;
 
-  ardata->first_file_filepos = bfd_tell (abfd);
-  /* Pad to an even boundary.  */
-  ardata->first_file_filepos += ardata->first_file_filepos % 2;
+  BFD_ASSERT (! info->relocateable);
 
-  bfd_has_map (abfd) = true;
+  *errmsg = NULL;
 
-  return true;
-}
+  if (datasec->reloc_count == 0)
+    return true;
 
-/* Write out an armap.  */
+  sym_hashes = ecoff_data (abfd)->sym_hashes;
 
-static boolean
-ecoff_write_armap (abfd, elength, map, orl_count, stridx)
-     bfd *abfd;
-     unsigned int elength;
-     struct orl *map;
-     unsigned int orl_count;
-     int stridx;
-{
-  unsigned int hashsize, hashlog;
-  unsigned int symdefsize;
-  int padit;
-  unsigned int stringsize;
-  unsigned int mapsize;
-  file_ptr firstreal;
-  struct ar_hdr hdr;
-  struct stat statbuf;
-  unsigned int i;
-  bfd_byte temp[LONG_SIZE];
-  bfd_byte *hashtable;
-  bfd *current;
-  bfd *last_elt;
-
-  /* Ultrix appears to use as a hash table size the least power of two
-     greater than twice the number of entries.  */
-  for (hashlog = 0; (1 << hashlog) <= 2 * orl_count; hashlog++)
-    ;
-  hashsize = 1 << hashlog;
-
-  symdefsize = hashsize * 2 * LONG_SIZE;
-  padit = stridx % 2;
-  stringsize = stridx + padit;
-
-  /* Include 8 bytes to store symdefsize and stringsize in output. */
-  mapsize = LONG_SIZE + symdefsize + stringsize + LONG_SIZE;
-
-  firstreal = SARMAG + sizeof (struct ar_hdr) + mapsize + elength;
-
-  memset ((PTR) &hdr, 0, sizeof hdr);
-
-  /* Work out the ECOFF armap name.  */
-  strcpy (hdr.ar_name, ARMAP_START);
-  hdr.ar_name[ARMAP_HEADER_MARKER_INDEX] = ARMAP_MARKER;
-  hdr.ar_name[ARMAP_HEADER_ENDIAN_INDEX] =
-    (abfd->xvec->header_byteorder_big_p
-     ? ARMAP_BIG_ENDIAN
-     : ARMAP_LITTLE_ENDIAN);
-  hdr.ar_name[ARMAP_OBJECT_MARKER_INDEX] = ARMAP_MARKER;
-  hdr.ar_name[ARMAP_OBJECT_ENDIAN_INDEX] =
-    abfd->xvec->byteorder_big_p ? ARMAP_BIG_ENDIAN : ARMAP_LITTLE_ENDIAN;
-  memcpy (hdr.ar_name + ARMAP_END_INDEX, ARMAP_END, sizeof ARMAP_END - 1);
-
-  /* Write the timestamp of the archive header to be just a little bit
-     later than the timestamp of the file, otherwise the linker will
-     complain that the index is out of date.  Actually, the Ultrix
-     linker just checks the archive name; the GNU linker may check the
-     date.  */
-  stat (abfd->filename, &statbuf);
-  sprintf (hdr.ar_date, "%ld", (long) (statbuf.st_mtime + 60));
-
-  /* The DECstation uses zeroes for the uid, gid and mode of the
-     armap.  */
-  hdr.ar_uid[0] = '0';
-  hdr.ar_gid[0] = '0';
-  hdr.ar_mode[0] = '0';
-
-  sprintf (hdr.ar_size, "%-10d", (int) mapsize);
-
-  hdr.ar_fmag[0] = '`';
-  hdr.ar_fmag[1] = '\n';
-
-  /* Turn all null bytes in the header into spaces.  */
-  for (i = 0; i < sizeof (struct ar_hdr); i++)
-   if (((char *)(&hdr))[i] == '\0')
-     (((char *)(&hdr))[i]) = ' ';
-
-  if (bfd_write ((PTR) &hdr, 1, sizeof (struct ar_hdr), abfd)
-      != sizeof (struct ar_hdr))
+  if (! mips_read_relocs (abfd, datasec))
     return false;
 
-  bfd_h_put_32 (abfd, hashsize, temp);
-  if (bfd_write (temp, 1, LONG_SIZE, abfd) != LONG_SIZE)
+  relsec->contents = (bfd_byte *) bfd_alloc (abfd, datasec->reloc_count * 4);
+  if (relsec->contents == NULL)
     return false;
-  
-  hashtable = (bfd_byte *) bfd_zalloc (abfd, symdefsize);
 
-  current = abfd->archive_head;
-  last_elt = current;
-  for (i = 0; i < orl_count; i++)
+  p = relsec->contents;
+
+  section_tdata = ecoff_section_data (abfd, datasec);
+  ext_rel = (struct external_reloc *) section_tdata->external_relocs;
+  ext_rel_end = ext_rel + datasec->reloc_count;
+  for (; ext_rel < ext_rel_end; ext_rel++, p += 4)
     {
-      unsigned int hash, rehash;
+      struct internal_reloc int_rel;
+      boolean text_relative;
+
+      mips_ecoff_swap_reloc_in (abfd, (PTR) ext_rel, &int_rel);
 
-      /* Advance firstreal to the file position of this archive
-        element.  */
-      if (((bfd *) map[i].pos) != last_elt)
+      /* We are going to write a four byte word into the runtime reloc
+        section.  The word will be the address in the data section
+        which must be relocated.  This must be on a word boundary,
+        which means the lower two bits must be zero.  We use the
+        least significant bit to indicate how the value in the data
+        section must be relocated.  A 0 means that the value is
+        relative to the text section, while a 1 indicates that the
+        value is relative to the data section.  Given that we are
+        assuming the code was compiled using -membedded-pic, there
+        should not be any other possibilities.  */
+
+      /* We can only relocate REFWORD relocs at run time.  */
+      if (int_rel.r_type != MIPS_R_REFWORD)
        {
-         do
-           {
-             firstreal += arelt_size (current) + sizeof (struct ar_hdr);
-             firstreal += firstreal % 2;
-             current = current->next;
-           }
-         while (current != (bfd *) map[i].pos);
+         *errmsg = "unsupported reloc type";
+         bfd_set_error (bfd_error_bad_value);
+         return false;
        }
 
-      last_elt = current;
+      if (int_rel.r_extern)
+       {
+         struct ecoff_link_hash_entry *h;
 
-      hash = ecoff_armap_hash (*map[i].name, &rehash, hashsize, hashlog);
-      if (bfd_h_get_32 (abfd, (PTR) (hashtable
-                                    + (hash * 2 * LONG_SIZE)
-                                    + LONG_SIZE))
-         != 0)
+         h = sym_hashes[int_rel.r_symndx];
+         /* If h is NULL, that means that there is a reloc against an
+            external symbol which we thought was just a debugging
+            symbol.  This should not happen.  */
+         if (h == (struct ecoff_link_hash_entry *) NULL)
+           abort ();
+         if ((h->root.type == bfd_link_hash_defined
+              || h->root.type == bfd_link_hash_defweak)
+             && (h->root.u.def.section->flags & SEC_CODE) != 0)
+           text_relative = true;
+         else
+           text_relative = false;
+       }
+      else
        {
-         unsigned int srch;
-
-         /* The desired slot is already taken.  */
-         for (srch = (hash + rehash) & (hashsize - 1);
-              srch != hash;
-              srch = (srch + rehash) & (hashsize - 1))
-           if (bfd_h_get_32 (abfd, (PTR) (hashtable
-                                          + (srch * 2 * LONG_SIZE)
-                                          + LONG_SIZE))
-               == 0)
+         switch (int_rel.r_symndx)
+           {
+           case RELOC_SECTION_TEXT:
+             text_relative = true;
              break;
-
-         BFD_ASSERT (srch != hash);
-
-         hash = srch;
+           case RELOC_SECTION_SDATA:
+           case RELOC_SECTION_SBSS:
+           case RELOC_SECTION_LIT8:
+             text_relative = false;
+             break;
+           default:
+             /* No other sections should appear in -membedded-pic
+                 code.  */
+             *errmsg = "reloc against unsupported section";
+             bfd_set_error (bfd_error_bad_value);
+             return false;
+           }
        }
-       
-      bfd_h_put_32 (abfd, map[i].namidx,
-                   (PTR) (hashtable + hash * 2 * LONG_SIZE));
-      bfd_h_put_32 (abfd, firstreal,
-                   (PTR) (hashtable + hash * 2 * LONG_SIZE + LONG_SIZE));
-    }
-
-  if (bfd_write (hashtable, 1, symdefsize, abfd) != symdefsize)
-    return false;
-
-  bfd_release (abfd, hashtable);
 
-  /* Now write the strings.  */
-  bfd_h_put_32 (abfd, stringsize, temp);
-  if (bfd_write (temp, 1, LONG_SIZE, abfd) != LONG_SIZE)
-    return false;
-  for (i = 0; i < orl_count; i++)
-    {
-      bfd_size_type len;
-
-      len = strlen (*map[i].name) + 1;
-      if (bfd_write ((PTR) (*map[i].name), 1, len, abfd) != len)
-       return false;
-    }
+      if ((int_rel.r_offset & 3) != 0)
+       {
+         *errmsg = "reloc not properly aligned";
+         bfd_set_error (bfd_error_bad_value);
+         return false;
+       }
 
-  /* The spec sez this should be a newline.  But in order to be
-     bug-compatible for DECstation ar we use a null.  */
-  if (padit)
-    {
-      if (bfd_write ("\0", 1, 1, abfd) != 1)
-       return false;
+      bfd_put_32 (abfd,
+                 (int_rel.r_vaddr - datasec->vma + datasec->output_offset
+                  + (text_relative ? 0 : 1)),
+                 p);
     }
 
   return true;
 }
+\f
+/* This is the ECOFF backend structure.  The backend field of the
+   target vector points to this.  */
 
-/* We just use the generic extended name support.  This is a GNU
-   extension.  */
-#define ecoff_slurp_extended_name_table        _bfd_slurp_extended_name_table
-
-/* See whether this BFD is an archive.  If it is, read in the armap
-   and the extended name table.  */
-
-static bfd_target *
-ecoff_archive_p (abfd)
-     bfd *abfd;
+static const struct ecoff_backend_data mips_ecoff_backend_data =
 {
-  char armag[SARMAG + 1];
-
-  if (bfd_read ((PTR) armag, 1, SARMAG, abfd) != SARMAG
-      || strncmp (armag, ARMAG, SARMAG) != 0)
-    {
-      bfd_error = wrong_format;
-      return (bfd_target *) NULL;
-    }
+  /* COFF backend structure.  */
+  {
+    (void (*) PARAMS ((bfd *,PTR,int,int,int,int,PTR))) bfd_void, /* aux_in */
+    (void (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* sym_in */
+    (void (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* lineno_in */
+    (unsigned (*) PARAMS ((bfd *,PTR,int,int,int,int,PTR)))bfd_void,/*aux_out*/
+    (unsigned (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* sym_out */
+    (unsigned (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* lineno_out */
+    (unsigned (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* reloc_out */
+    mips_ecoff_swap_filehdr_out, mips_ecoff_swap_aouthdr_out,
+    mips_ecoff_swap_scnhdr_out,
+    FILHSZ, AOUTSZ, SCNHSZ, 0, 0, 0, 0, true, false, 4,
+    mips_ecoff_swap_filehdr_in, mips_ecoff_swap_aouthdr_in,
+    mips_ecoff_swap_scnhdr_in, NULL,
+    mips_ecoff_bad_format_hook, _bfd_ecoff_set_arch_mach_hook,
+    _bfd_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
+  },
+  /* Supported architecture.  */
+  bfd_arch_mips,
+  /* Initial portion of armap string.  */
+  "__________",
+  /* The page boundary used to align sections in a demand-paged
+     executable file.  E.g., 0x1000.  */
+  0x1000,
+  /* True if the .rdata section is part of the text segment, as on the
+     Alpha.  False if .rdata is part of the data segment, as on the
+     MIPS.  */
+  false,
+  /* Bitsize of constructor entries.  */
+  32,
+  /* Reloc to use for constructor entries.  */
+  &mips_howto_table[MIPS_R_REFWORD],
+  {
+    /* Symbol table magic number.  */
+    magicSym,
+    /* Alignment of debugging information.  E.g., 4.  */
+    4,
+    /* Sizes of external symbolic information.  */
+    sizeof (struct hdr_ext),
+    sizeof (struct dnr_ext),
+    sizeof (struct pdr_ext),
+    sizeof (struct sym_ext),
+    sizeof (struct opt_ext),
+    sizeof (struct fdr_ext),
+    sizeof (struct rfd_ext),
+    sizeof (struct ext_ext),
+    /* Functions to swap in external symbolic data.  */
+    ecoff_swap_hdr_in,
+    ecoff_swap_dnr_in,
+    ecoff_swap_pdr_in,
+    ecoff_swap_sym_in,
+    ecoff_swap_opt_in,
+    ecoff_swap_fdr_in,
+    ecoff_swap_rfd_in,
+    ecoff_swap_ext_in,
+    _bfd_ecoff_swap_tir_in,
+    _bfd_ecoff_swap_rndx_in,
+    /* Functions to swap out external symbolic data.  */
+    ecoff_swap_hdr_out,
+    ecoff_swap_dnr_out,
+    ecoff_swap_pdr_out,
+    ecoff_swap_sym_out,
+    ecoff_swap_opt_out,
+    ecoff_swap_fdr_out,
+    ecoff_swap_rfd_out,
+    ecoff_swap_ext_out,
+    _bfd_ecoff_swap_tir_out,
+    _bfd_ecoff_swap_rndx_out,
+    /* Function to read in symbolic data.  */
+    _bfd_ecoff_slurp_symbolic_info
+  },
+  /* External reloc size.  */
+  RELSZ,
+  /* Howto reloc table.  */
+  mips_howto_table,
+  /* Howto reloc table size.  */
+  sizeof mips_howto_table / sizeof mips_howto_table[0],
+  /* Does this backend implement the LITERALSLEAZY reloc?  */
+  false,
+  /* Reloc swapping functions.  */
+  mips_ecoff_swap_reloc_in,
+  mips_ecoff_swap_reloc_out,
+  /* Backend reloc tweaking.  */
+  mips_adjust_reloc_in,
+  mips_adjust_reloc_out,
+  /* Relocate section contents while linking.  */
+  mips_relocate_section,
+  /* Do final adjustments to filehdr and aouthdr.  */
+  NULL,
+  /* Read an element from an archive at a given file position.  */
+  _bfd_get_elt_at_filepos
+};
 
-  /* We are setting bfd_ardata(abfd) here, but since bfd_ardata
-     involves a cast, we can't do it as the left operand of
-     assignment.  */
-  abfd->tdata.aout_ar_data =
-    (struct artdata *) bfd_zalloc (abfd, sizeof (struct artdata));
+/* Looking up a reloc type is MIPS specific.  */
+#define _bfd_ecoff_bfd_reloc_type_lookup mips_bfd_reloc_type_lookup
 
-  if (bfd_ardata (abfd) == (struct artdata *) NULL)
-    {
-      bfd_error = no_memory;
-      return (bfd_target *) NULL;
-    }
+/* Getting relocated section contents is generic.  */
+#define _bfd_ecoff_bfd_get_relocated_section_contents \
+  bfd_generic_get_relocated_section_contents
 
-  bfd_ardata (abfd)->first_file_filepos = SARMAG;
-  
-  if (ecoff_slurp_armap (abfd) == false
-      || ecoff_slurp_extended_name_table (abfd) == false)
-    {
-      bfd_release (abfd, bfd_ardata (abfd));
-      abfd->tdata.aout_ar_data = (struct artdata *) NULL;
-      return (bfd_target *) NULL;
-    }
-  
-  return abfd->xvec;
-}
-\f
-/* This is the COFF backend structure.  The backend_data field of the
-   bfd_target structure is set to this.  The section reading code in
-   coffgen.c uses this structure.  */
-
-static CONST bfd_coff_backend_data bfd_ecoff_std_swap_table = {
-  (void (*) PARAMS ((bfd *,PTR,int,int,PTR))) bfd_void, /* aux_in */
-  (void (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* sym_in */
-  (void (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* lineno_in */
-  (unsigned (*) PARAMS ((bfd *,PTR,int,int,PTR))) bfd_void, /* aux_out */
-  (unsigned (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* sym_out */
-  (unsigned (*) PARAMS ((bfd *,PTR,PTR))) bfd_void, /* lineno_out */
-  ecoff_swap_reloc_out, ecoff_swap_filehdr_out, ecoff_swap_aouthdr_out,
-  ecoff_swap_scnhdr_out,
-  FILHSZ, AOUTSZ, SCNHSZ, 0, 0, 0, true,
-  ecoff_swap_filehdr_in, ecoff_swap_aouthdr_in, ecoff_swap_scnhdr_in,
-  ecoff_bad_format_hook, ecoff_set_arch_mach_hook, ecoff_mkobject_hook,
-  ecoff_styp_to_sec_flags, ecoff_make_section_hook, ecoff_set_alignment_hook,
-  ecoff_slurp_symbol_table
-};
+/* Handling file windows is generic.  */
+#define _bfd_ecoff_get_section_contents_in_window \
+  _bfd_generic_get_section_contents_in_window
 
-/* get_lineno could be written for ECOFF, but it would currently only
-   be useful for linking ECOFF and COFF files together, which doesn't
-   seem too likely.  */
-#define ecoff_get_lineno \
-  ((alent *(*) PARAMS ((bfd *, asymbol *))) bfd_nullvoidptr)
-
-/* These bfd_target functions are defined in other files.  */
-
-#define ecoff_core_file_failing_command        _bfd_dummy_core_file_failing_command
-#define ecoff_core_file_failing_signal _bfd_dummy_core_file_failing_signal
-#define ecoff_core_file_matches_executable_p \
-  _bfd_dummy_core_file_matches_executable_p
-#define ecoff_truncate_arname          bfd_dont_truncate_arname
-#define ecoff_openr_next_archived_file bfd_generic_openr_next_archived_file
-#define ecoff_generic_stat_arch_elt    bfd_generic_stat_arch_elt
-#define        ecoff_get_section_contents      bfd_generic_get_section_contents
-#define ecoff_get_reloc_upper_bound    coff_get_reloc_upper_bound
-#define        ecoff_close_and_cleanup         bfd_generic_close_and_cleanup
-#define ecoff_bfd_debug_info_start     bfd_void
-#define ecoff_bfd_debug_info_end       bfd_void
-#define ecoff_bfd_debug_info_accumulate        \
-  ((void (*) PARAMS ((bfd *, struct sec *))) bfd_void)
-#define ecoff_bfd_get_relocated_section_contents \
-  bfd_generic_get_relocated_section_contents
-#define ecoff_bfd_relax_section                bfd_generic_relax_section
-#define ecoff_bfd_make_debug_symbol \
-  ((asymbol *(*) PARAMS ((bfd *, void *, unsigned long))) bfd_nullvoidptr)
+/* Relaxing sections is MIPS specific.  */
+#define _bfd_ecoff_bfd_relax_section mips_relax_section
 
-bfd_target ecoff_little_vec =
+const bfd_target ecoff_little_vec =
 {
   "ecoff-littlemips",          /* name */
   bfd_target_ecoff_flavour,
-  false,                       /* data byte order is little */
-  false,                       /* header byte order is little */
+  BFD_ENDIAN_LITTLE,           /* data byte order is little */
+  BFD_ENDIAN_LITTLE,           /* header byte order is little */
 
   (HAS_RELOC | EXEC_P |                /* object flags */
    HAS_LINENO | HAS_DEBUG |
-   HAS_SYMS | HAS_LOCALS | DYNAMIC | WP_TEXT),
+   HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED),
 
-  (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC), /* sect
-                                                           flags */
+  (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_CODE | SEC_DATA),
   0,                           /* leading underscore */
-  '/',                         /* ar_pad_char */
+  ' ',                         /* ar_pad_char */
   15,                          /* ar_max_namelen */
-  3,                           /* minimum alignment power */
-  _do_getl64, _do_getl_signed_64, _do_putl64,
-     _do_getl32, _do_getl_signed_32, _do_putl32,
-     _do_getl16, _do_getl_signed_16, _do_putl16, /* data */
-  _do_getl64, _do_getl_signed_64, _do_putl64,
-     _do_getl32, _do_getl_signed_32, _do_putl32,
-     _do_getl16, _do_getl_signed_16, _do_putl16, /* hdrs */
+  bfd_getl64, bfd_getl_signed_64, bfd_putl64,
+     bfd_getl32, bfd_getl_signed_32, bfd_putl32,
+     bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* data */
+  bfd_getl64, bfd_getl_signed_64, bfd_putl64,
+     bfd_getl32, bfd_getl_signed_32, bfd_putl32,
+     bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* hdrs */
 
   {_bfd_dummy_target, coff_object_p, /* bfd_check_format */
-     ecoff_archive_p, _bfd_dummy_target},
-  {bfd_false, ecoff_mkobject, _bfd_generic_mkarchive, /* bfd_set_format */
-     bfd_false},
-  {bfd_false, ecoff_write_object_contents, /* bfd_write_contents */
+     _bfd_ecoff_archive_p, _bfd_dummy_target},
+  {bfd_false, _bfd_ecoff_mkobject,  /* bfd_set_format */
+     _bfd_generic_mkarchive, bfd_false},
+  {bfd_false, _bfd_ecoff_write_object_contents, /* bfd_write_contents */
      _bfd_write_archive_contents, bfd_false},
-  JUMP_TABLE (ecoff),
-  (PTR) &bfd_ecoff_std_swap_table
+
+     BFD_JUMP_TABLE_GENERIC (_bfd_ecoff),
+     BFD_JUMP_TABLE_COPY (_bfd_ecoff),
+     BFD_JUMP_TABLE_CORE (_bfd_nocore),
+     BFD_JUMP_TABLE_ARCHIVE (_bfd_ecoff),
+     BFD_JUMP_TABLE_SYMBOLS (_bfd_ecoff),
+     BFD_JUMP_TABLE_RELOCS (_bfd_ecoff),
+     BFD_JUMP_TABLE_WRITE (_bfd_ecoff),
+     BFD_JUMP_TABLE_LINK (_bfd_ecoff),
+     BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
+
+  (PTR) &mips_ecoff_backend_data
 };
 
-bfd_target ecoff_big_vec =
+const bfd_target ecoff_big_vec =
 {
   "ecoff-bigmips",             /* name */
   bfd_target_ecoff_flavour,
-  true,                                /* data byte order is big */
-  true,                                /* header byte order is big */
+  BFD_ENDIAN_BIG,              /* data byte order is big */
+  BFD_ENDIAN_BIG,              /* header byte order is big */
 
   (HAS_RELOC | EXEC_P |                /* object flags */
    HAS_LINENO | HAS_DEBUG |
-   HAS_SYMS | HAS_LOCALS | DYNAMIC | WP_TEXT),
+   HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED),
 
-  (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC), /* sect flags */
+  (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_CODE | SEC_DATA),
   0,                           /* leading underscore */
   ' ',                         /* ar_pad_char */
-  16,                          /* ar_max_namelen */
-  3,                           /* minimum alignment power */
-  _do_getb64, _do_getb_signed_64, _do_putb64,
-     _do_getb32, _do_getb_signed_32, _do_putb32,
-     _do_getb16, _do_getb_signed_16, _do_putb16,
-  _do_getb64, _do_getb_signed_64, _do_putb64,
-     _do_getb32, _do_getb_signed_32, _do_putb32,
-     _do_getb16, _do_getb_signed_16, _do_putb16,
+  15,                          /* ar_max_namelen */
+  bfd_getb64, bfd_getb_signed_64, bfd_putb64,
+     bfd_getb32, bfd_getb_signed_32, bfd_putb32,
+     bfd_getb16, bfd_getb_signed_16, bfd_putb16,
+  bfd_getb64, bfd_getb_signed_64, bfd_putb64,
+     bfd_getb32, bfd_getb_signed_32, bfd_putb32,
+     bfd_getb16, bfd_getb_signed_16, bfd_putb16,
  {_bfd_dummy_target, coff_object_p, /* bfd_check_format */
-    ecoff_archive_p, _bfd_dummy_target},
- {bfd_false, ecoff_mkobject, _bfd_generic_mkarchive, /* bfd_set_format */
-    bfd_false},
- {bfd_false, ecoff_write_object_contents, /* bfd_write_contents */
+    _bfd_ecoff_archive_p, _bfd_dummy_target},
+ {bfd_false, _bfd_ecoff_mkobject, /* bfd_set_format */
+    _bfd_generic_mkarchive, bfd_false},
+ {bfd_false, _bfd_ecoff_write_object_contents, /* bfd_write_contents */
     _bfd_write_archive_contents, bfd_false},
-  JUMP_TABLE(ecoff),
-  (PTR) &bfd_ecoff_std_swap_table
-  /* Note that there is another bfd_target just above this one.  If
-     you are adding initializers here, you should be adding them there
-     as well.  */
+
+     BFD_JUMP_TABLE_GENERIC (_bfd_ecoff),
+     BFD_JUMP_TABLE_COPY (_bfd_ecoff),
+     BFD_JUMP_TABLE_CORE (_bfd_nocore),
+     BFD_JUMP_TABLE_ARCHIVE (_bfd_ecoff),
+     BFD_JUMP_TABLE_SYMBOLS (_bfd_ecoff),
+     BFD_JUMP_TABLE_RELOCS (_bfd_ecoff),
+     BFD_JUMP_TABLE_WRITE (_bfd_ecoff),
+     BFD_JUMP_TABLE_LINK (_bfd_ecoff),
+     BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
+
+  (PTR) &mips_ecoff_backend_data
+};
+
+const bfd_target ecoff_biglittle_vec =
+{
+  "ecoff-biglittlemips",               /* name */
+  bfd_target_ecoff_flavour,
+  BFD_ENDIAN_LITTLE,           /* data byte order is little */
+  BFD_ENDIAN_BIG,              /* header byte order is big */
+
+  (HAS_RELOC | EXEC_P |                /* object flags */
+   HAS_LINENO | HAS_DEBUG |
+   HAS_SYMS | HAS_LOCALS | WP_TEXT | D_PAGED),
+
+  (SEC_HAS_CONTENTS | SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_CODE | SEC_DATA),
+  0,                           /* leading underscore */
+  ' ',                         /* ar_pad_char */
+  15,                          /* ar_max_namelen */
+  bfd_getl64, bfd_getl_signed_64, bfd_putl64,
+     bfd_getl32, bfd_getl_signed_32, bfd_putl32,
+     bfd_getl16, bfd_getl_signed_16, bfd_putl16, /* data */
+  bfd_getb64, bfd_getb_signed_64, bfd_putb64,
+     bfd_getb32, bfd_getb_signed_32, bfd_putb32,
+     bfd_getb16, bfd_getb_signed_16, bfd_putb16, /* hdrs */
+
+  {_bfd_dummy_target, coff_object_p, /* bfd_check_format */
+     _bfd_ecoff_archive_p, _bfd_dummy_target},
+  {bfd_false, _bfd_ecoff_mkobject,  /* bfd_set_format */
+     _bfd_generic_mkarchive, bfd_false},
+  {bfd_false, _bfd_ecoff_write_object_contents, /* bfd_write_contents */
+     _bfd_write_archive_contents, bfd_false},
+
+     BFD_JUMP_TABLE_GENERIC (_bfd_ecoff),
+     BFD_JUMP_TABLE_COPY (_bfd_ecoff),
+     BFD_JUMP_TABLE_CORE (_bfd_nocore),
+     BFD_JUMP_TABLE_ARCHIVE (_bfd_ecoff),
+     BFD_JUMP_TABLE_SYMBOLS (_bfd_ecoff),
+     BFD_JUMP_TABLE_RELOCS (_bfd_ecoff),
+     BFD_JUMP_TABLE_WRITE (_bfd_ecoff),
+     BFD_JUMP_TABLE_LINK (_bfd_ecoff),
+     BFD_JUMP_TABLE_DYNAMIC (_bfd_nodynamic),
+
+  (PTR) &mips_ecoff_backend_data
 };