* configure: Ignore new autoconf configure options.
[binutils-gdb.git] / bfd / elf32-mips.c
index 08d2d7f2cabb12f5dba9cb46c46be1026c9aaee5..118ebb07b39a3df3f8c5b8c7cee4e485628f6b9c 100644 (file)
@@ -1,5 +1,5 @@
 /* MIPS-specific support for 32-bit ELF
-   Copyright 1993, 1994 Free Software Foundation, Inc.
+   Copyright 1993, 1994, 1995 Free Software Foundation, Inc.
 
    Most of the information added by Ian Lance Taylor, Cygnus Support,
    <ian@cygnus.com>.
@@ -18,14 +18,14 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
 #include "bfd.h"
 #include "sysdep.h"
 #include "libbfd.h"
 #include "bfdlink.h"
 #include "genlink.h"
-#include "libelf.h"
+#include "elf-bfd.h"
 #include "elf/mips.h"
 
 /* Get the ECOFF swapping routines.  */
@@ -65,7 +65,7 @@ static bfd_reloc_status_type mips_elf_gprel16_reloc PARAMS ((bfd *abfd,
                                                             asection *section,
                                                             bfd *output_bfd,
                                                             char **error));
-static const struct reloc_howto_struct *bfd_elf32_bfd_reloc_type_lookup
+static reloc_howto_type *bfd_elf32_bfd_reloc_type_lookup
   PARAMS ((bfd *, bfd_reloc_code_real_type));
 static void mips_info_to_howto_rel
   PARAMS ((bfd *, arelent *, Elf32_Internal_Rel *));
@@ -84,6 +84,11 @@ static boolean mips_elf_section_processing
 static void mips_elf_symbol_processing PARAMS ((bfd *, asymbol *));
 static boolean mips_elf_read_ecoff_info
   PARAMS ((bfd *, asection *, struct ecoff_debug_info *));
+static boolean mips_elf_is_local_label
+  PARAMS ((bfd *, asymbol *));
+static boolean mips_elf_find_nearest_line
+  PARAMS ((bfd *, asection *, asymbol **, bfd_vma, const char **,
+          const char **, unsigned int *));
 static struct bfd_hash_entry *mips_elf_link_hash_newfunc
   PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
 static struct bfd_link_hash_table *mips_elf_link_hash_table_create
@@ -96,7 +101,7 @@ static void mips_elf_relocate_hi16
           bfd_vma));
 static boolean mips_elf_relocate_section
   PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
-          Elf_Internal_Rela *, Elf_Internal_Sym *, asection **, char *));
+          Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
 static boolean mips_elf_add_symbol_hook
   PARAMS ((bfd *, struct bfd_link_info *, const Elf_Internal_Sym *,
           const char **, flagword *, asection **, bfd_vma *));
@@ -112,6 +117,18 @@ enum reloc_type
   R_MIPS_GPREL16,      R_MIPS_LITERAL,
   R_MIPS_GOT16,                R_MIPS_PC16,
   R_MIPS_CALL16,       R_MIPS_GPREL32,
+  /* The remaining relocs are defined on Irix, although they are not
+     in the MIPS ELF ABI.  */
+  R_MIPS_UNUSED1,      R_MIPS_UNUSED2,
+  R_MIPS_UNUSED3,
+  R_MIPS_SHIFT5,       R_MIPS_SHIFT6,
+  R_MIPS_64,           R_MIPS_GOT_DISP,
+  R_MIPS_GOT_PAGE,     R_MIPS_GOT_OFST,
+  R_MIPS_GOT_HI16,     R_MIPS_GOT_LO16,
+  R_MIPS_SUB,          R_MIPS_INSERT_A,
+  R_MIPS_INSERT_B,     R_MIPS_DELETE,
+  R_MIPS_HIGHER,       R_MIPS_HIGHEST,
+  R_MIPS_CALL_HI16,    R_MIPS_CALL_LO16,
   R_MIPS_max
 };
 
@@ -316,6 +333,172 @@ static reloc_howto_type elf_mips_howto_table[] =
         true,                  /* partial_inplace */
         0xffffffff,            /* src_mask */
         0xffffffff,            /* dst_mask */
+        false),                /* pcrel_offset */
+
+    /* The remaining relocs are defined on Irix 5, although they are
+       not defined by the ABI.  */
+    { 13 },
+    { 14 },
+    { 15 },
+
+  /* A 5 bit shift field.  */
+  HOWTO (R_MIPS_SHIFT5,                /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        5,                     /* bitsize */
+        false,                 /* pc_relative */
+        6,                     /* bitpos */
+        complain_overflow_bitfield, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_MIPS_SHIFT5",       /* name */
+        true,                  /* partial_inplace */
+        0x000007c0,            /* src_mask */
+        0x000007c0,            /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* A 6 bit shift field.  */
+  /* FIXME: This is not handled correctly; a special function is
+     needed to put the most significant bit in the right place.  */
+  HOWTO (R_MIPS_SHIFT6,                /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        6,                     /* bitsize */
+        false,                 /* pc_relative */
+        6,                     /* bitpos */
+        complain_overflow_bitfield, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_MIPS_SHIFT6",       /* name */
+        true,                  /* partial_inplace */
+        0x000007c4,            /* src_mask */
+        0x000007c4,            /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* A 64 bit relocation.  Presumably not used in 32 bit ELF.  */
+  { R_MIPS_64 },
+
+  /* Displacement in the global offset table.  */
+  /* FIXME: Not handled correctly.  */
+  HOWTO (R_MIPS_GOT_DISP,      /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_MIPS_GOT_DISP",     /* name */
+        true,                  /* partial_inplace */
+        0x0000ffff,            /* src_mask */
+        0x0000ffff,            /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* Displacement to page pointer in the global offset table.  */
+  /* FIXME: Not handled correctly.  */
+  HOWTO (R_MIPS_GOT_PAGE,      /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_MIPS_GOT_PAGE",     /* name */
+        true,                  /* partial_inplace */
+        0x0000ffff,            /* src_mask */
+        0x0000ffff,            /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* Offset from page pointer in the global offset table.  */
+  /* FIXME: Not handled correctly.  */
+  HOWTO (R_MIPS_GOT_OFST,      /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_MIPS_GOT_OFST",     /* name */
+        true,                  /* partial_inplace */
+        0x0000ffff,            /* src_mask */
+        0x0000ffff,            /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* High 16 bits of displacement in global offset table.  */
+  /* FIXME: Not handled correctly.  */
+  HOWTO (R_MIPS_GOT_HI16,      /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_MIPS_GOT_HI16",     /* name */
+        true,                  /* partial_inplace */
+        0x0000ffff,            /* src_mask */
+        0x0000ffff,            /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* Low 16 bits of displacement in global offset table.  */
+  /* FIXME: Not handled correctly.  */
+  HOWTO (R_MIPS_GOT_LO16,      /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_MIPS_GOT_LO16",     /* name */
+        true,                  /* partial_inplace */
+        0x0000ffff,            /* src_mask */
+        0x0000ffff,            /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* 64 bit subtraction.  Presumably not used in 32 bit ELF.  */
+  { R_MIPS_SUB },
+
+  /* Used to cause the linker to insert and delete instructions?  */
+  { R_MIPS_INSERT_A },
+  { R_MIPS_INSERT_B },
+  { R_MIPS_DELETE },
+
+  /* Get the higher values of a 64 bit addend.  Presumably not used in
+     32 bit ELF.  */
+  { R_MIPS_HIGHER },
+  { R_MIPS_HIGHEST },
+
+  /* High 16 bits of displacement in global offset table.  */
+  /* FIXME: Not handled correctly.  */
+  HOWTO (R_MIPS_CALL_HI16,     /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_MIPS_CALL_HI16",    /* name */
+        true,                  /* partial_inplace */
+        0x0000ffff,            /* src_mask */
+        0x0000ffff,            /* dst_mask */
+        false),                /* pcrel_offset */
+
+  /* Low 16 bits of displacement in global offset table.  */
+  /* FIXME: Not handled correctly.  */
+  HOWTO (R_MIPS_CALL_LO16,     /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        false,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_MIPS_CALL_LO16",    /* name */
+        true,                  /* partial_inplace */
+        0x0000ffff,            /* src_mask */
+        0x0000ffff,            /* dst_mask */
         false)                 /* pcrel_offset */
 };
 
@@ -508,6 +691,10 @@ mips_elf_got16_reloc (abfd,
    cleverly because the entries in the .lit8 and .lit4 sections can be
    merged.  */
 
+static bfd_reloc_status_type gprel16_with_gp PARAMS ((bfd *, asymbol *,
+                                                     arelent *, asection *,
+                                                     boolean, PTR, bfd_vma));
+
 static bfd_reloc_status_type
 mips_elf_gprel16_reloc (abfd,
                        reloc_entry,
@@ -525,9 +712,6 @@ mips_elf_gprel16_reloc (abfd,
      char **error_message;
 {
   boolean relocateable;
-  bfd_vma relocation;
-  unsigned long val;
-  unsigned long insn;
 
   /* If we're relocating, and this is an external symbol with no
      addend, we don't want to change anything.  We will only have an
@@ -553,6 +737,10 @@ mips_elf_gprel16_reloc (abfd,
       && relocateable == false)
     return bfd_reloc_undefined;
 
+  /* Some of the code below assumes the output bfd is ELF too.  */
+  if (output_bfd->xvec->flavour != bfd_target_elf_flavour)
+    abort ();
+
   /* We have to figure out the gp value, so that we can adjust the
      symbol value correctly.  We look up the symbol _gp in the output
      BFD.  If we can't find it, we're stuck.  We cache it in the ELF
@@ -605,6 +793,25 @@ mips_elf_gprel16_reloc (abfd,
        }
     }
 
+  return gprel16_with_gp (abfd, symbol, reloc_entry, input_section,
+                         relocateable, data, elf_gp (output_bfd));
+}
+
+static bfd_reloc_status_type
+gprel16_with_gp (abfd, symbol, reloc_entry, input_section, relocateable, data,
+                gp)
+     bfd *abfd;
+     asymbol *symbol;
+     arelent *reloc_entry;
+     asection *input_section;
+     boolean relocateable;
+     PTR data;
+     bfd_vma gp;
+{
+  bfd_vma relocation;
+  unsigned long insn;
+  unsigned long val;
+
   if (bfd_is_com_section (symbol->section))
     relocation = 0;
   else
@@ -628,7 +835,7 @@ mips_elf_gprel16_reloc (abfd,
      an external symbol.  */
   if (relocateable == false
       || (symbol->flags & BSF_SECTION_SYM) != 0)
-    val += relocation - elf_gp (output_bfd);
+    val += relocation - gp;
 
   insn = (insn &~ 0xffff) | (val & 0xffff);
   bfd_put_32 (abfd, insn, (bfd_byte *) data + reloc_entry->address);
@@ -670,12 +877,12 @@ static CONST struct elf_reloc_map mips_reloc_map[] =
 
 /* Given a BFD reloc type, return a howto structure.  */
 
-static const struct reloc_howto_struct *
+static reloc_howto_type *
 bfd_elf32_bfd_reloc_type_lookup (abfd, code)
      bfd *abfd;
      bfd_reloc_code_real_type code;
 {
-  int i;
+  unsigned int i;
 
   for (i = 0; i < sizeof (mips_reloc_map) / sizeof (struct elf_reloc_map); i++)
     {
@@ -811,27 +1018,8 @@ mips_elf_object_p (abfd)
 
   /* Irix 5 is broken.  Object file symbol tables are not always
      sorted correctly such that local symbols precede global symbols,
-     and the sh_info field in the symbol table is not always right.
-     We try to quickly check whether the symbol table is broken for
-     this BFD, and, if it is, we set elf_bad_symtab in tdata.  */
-  if (elf_onesymtab (abfd) != 0)
-    {
-      Elf_Internal_Shdr *symtab_hdr;
-      Elf32_External_Sym esym;
-
-      symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
-      if (bfd_seek (abfd,
-                   (symtab_hdr->sh_offset
-                    + symtab_hdr->sh_size
-                    - sizeof (Elf32_External_Sym)),
-                   SEEK_SET) != 0
-         || (bfd_read ((PTR) &esym, 1, sizeof (Elf32_External_Sym), abfd)
-             != sizeof (Elf32_External_Sym)))
-       return false;
-      if (ELF_ST_BIND (bfd_h_get_8 (abfd, (bfd_byte *) esym.st_info))
-         == STB_LOCAL)
-       elf_bad_symtab (abfd) = true;
-    }
+     and the sh_info field in the symbol table is not always right.  */
+  elf_bad_symtab (abfd) = true;
 
   return true;
 }
@@ -916,6 +1104,10 @@ mips_elf_section_from_shdr (abfd, hdr, name)
       if (strcmp (name, ".liblist") != 0)
        return false;
       break;
+    case SHT_MIPS_MSYM:
+      if (strcmp (name, ".msym") != 0)
+       return false;
+      break;
     case SHT_MIPS_CONFLICT:
       if (strcmp (name, ".conflict") != 0)
        return false;
@@ -941,6 +1133,14 @@ mips_elf_section_from_shdr (abfd, hdr, name)
       if (strcmp (name, ".options") != 0)
        return false;
       break;
+    case SHT_MIPS_DWARF:
+      if (strncmp (name, ".debug_", sizeof ".debug_" - 1) != 0)
+       return false;
+      break;
+    case SHT_MIPS_EVENTS:
+      if (strncmp (name, ".MIPS.events.", sizeof ".MIPS.events." - 1) != 0)
+       return false;
+      break;
     default:
       return false;
     }
@@ -996,6 +1196,12 @@ mips_elf_fake_sections (abfd, hdr, sec)
       hdr->sh_info = sec->_raw_size / sizeof (Elf32_Lib);
       /* FIXME: Set the sh_link field.  */
     }
+  else if (strcmp (name, ".msym") == 0)
+    {
+      hdr->sh_type = SHT_MIPS_MSYM;
+      hdr->sh_entsize = 8;
+      /* FIXME: Set the sh_info field.  */
+    }
   else if (strcmp (name, ".conflict") == 0)
     hdr->sh_type = SHT_MIPS_CONFLICT;
   else if (strncmp (name, ".gptab.", sizeof ".gptab." - 1) == 0)
@@ -1026,6 +1232,10 @@ mips_elf_fake_sections (abfd, hdr, sec)
       hdr->sh_type = SHT_MIPS_OPTIONS;
       hdr->sh_entsize = 1;
     }
+  else if (strncmp (name, ".debug_", sizeof ".debug_" - 1) == 0)
+    hdr->sh_type = SHT_MIPS_DWARF;
+  else if (strncmp (name, ".MIPS.events.", sizeof ".MIPS.events." - 1) == 0)
+    hdr->sh_type = SHT_MIPS_EVENTS;
 
   return true;
 }
@@ -1045,6 +1255,11 @@ mips_elf_section_from_bfd_section (abfd, hdr, sec, retval)
       *retval = SHN_MIPS_SCOMMON;
       return true;
     }
+  if (strcmp (bfd_get_section_name (abfd, sec), ".acommon") == 0)
+    {
+      *retval = SHN_MIPS_ACOMMON;
+      return true;
+    }
   return false;
 }
 
@@ -1137,7 +1352,7 @@ mips_elf_symbol_processing (abfd, asym)
        {
          /* Initialize the acommon section.  */
          mips_elf_acom_section.name = ".acommon";
-         mips_elf_acom_section.flags = SEC_NO_FLAGS;
+         mips_elf_acom_section.flags = SEC_ALLOC;
          mips_elf_acom_section.output_section = &mips_elf_acom_section;
          mips_elf_acom_section.symbol = &mips_elf_acom_symbol;
          mips_elf_acom_section.symbol_ptr_ptr = &mips_elf_acom_symbol_ptr;
@@ -1194,7 +1409,7 @@ mips_elf_read_ecoff_info (abfd, section, debug)
 
   swap = get_elf_backend_data (abfd)->elf_backend_ecoff_debug_swap;
 
-  ext_hdr = (char *) malloc (swap->external_hdr_size);
+  ext_hdr = (char *) malloc ((size_t) swap->external_hdr_size);
   if (ext_hdr == NULL && swap->external_hdr_size != 0)
     {
       bfd_set_error (bfd_error_no_memory);
@@ -1216,7 +1431,7 @@ mips_elf_read_ecoff_info (abfd, section, debug)
     debug->ptr = NULL;                                                 \
   else                                                                 \
     {                                                                  \
-      debug->ptr = (type) malloc (size * symhdr->count);               \
+      debug->ptr = (type) malloc ((size_t) (size * symhdr->count));    \
       if (debug->ptr == NULL)                                          \
        {                                                               \
          bfd_set_error (bfd_error_no_memory);                          \
@@ -1274,7 +1489,125 @@ mips_elf_read_ecoff_info (abfd, section, debug)
     free (debug->external_ext);
   return false;
 }
+\f
+/* MIPS ELF local labels start with '$', not 'L'.  */
+
+/*ARGSUSED*/
+static boolean
+mips_elf_is_local_label (abfd, symbol)
+     bfd *abfd;
+     asymbol *symbol;
+{
+  return symbol->name[0] == '$';
+}
+
+/* MIPS ELF uses a special find_nearest_line routine in order the
+   handle the ECOFF debugging information.  */
+
+struct mips_elf_find_line
+{
+  struct ecoff_debug_info d;
+  struct ecoff_find_line i;
+};
+
+static boolean
+mips_elf_find_nearest_line (abfd, section, symbols, offset, filename_ptr,
+                           functionname_ptr, line_ptr)
+     bfd *abfd;
+     asection *section;
+     asymbol **symbols;
+     bfd_vma offset;
+     const char **filename_ptr;
+     const char **functionname_ptr;
+     unsigned int *line_ptr;
+{
+  asection *msec;
 
+  msec = bfd_get_section_by_name (abfd, ".mdebug");
+  if (msec != NULL)
+    {
+      flagword origflags;
+      struct mips_elf_find_line *fi;
+      const struct ecoff_debug_swap * const swap =
+       get_elf_backend_data (abfd)->elf_backend_ecoff_debug_swap;
+
+      /* If we are called during a link, mips_elf_final_link may have
+        cleared the SEC_HAS_CONTENTS field.  We force it back on here
+        if appropriate (which it normally will be).  */
+      origflags = msec->flags;
+      if (elf_section_data (msec)->this_hdr.sh_type != SHT_NOBITS)
+       msec->flags |= SEC_HAS_CONTENTS;
+
+      fi = elf_tdata (abfd)->find_line_info;
+      if (fi == NULL)
+       {
+         bfd_size_type external_fdr_size;
+         char *fraw_src;
+         char *fraw_end;
+         struct fdr *fdr_ptr;
+
+         fi = ((struct mips_elf_find_line *)
+               bfd_alloc (abfd, sizeof (struct mips_elf_find_line)));
+         if (fi == NULL)
+           {
+             msec->flags = origflags;
+             return false;
+           }
+
+         memset (fi, 0, sizeof (struct mips_elf_find_line));
+
+         if (! mips_elf_read_ecoff_info (abfd, msec, &fi->d))
+           {
+             msec->flags = origflags;
+             return false;
+           }
+
+         /* Swap in the FDR information.  */
+         fi->d.fdr = ((struct fdr *)
+                      bfd_alloc (abfd,
+                                 (fi->d.symbolic_header.ifdMax *
+                                  sizeof (struct fdr))));
+         if (fi->d.fdr == NULL)
+           {
+             msec->flags = origflags;
+             return false;
+           }
+         external_fdr_size = swap->external_fdr_size;
+         fdr_ptr = fi->d.fdr;
+         fraw_src = (char *) fi->d.external_fdr;
+         fraw_end = (fraw_src
+                     + fi->d.symbolic_header.ifdMax * external_fdr_size);
+         for (; fraw_src < fraw_end; fraw_src += external_fdr_size, fdr_ptr++)
+           (*swap->swap_fdr_in) (abfd, (PTR) fraw_src, fdr_ptr);
+
+         elf_tdata (abfd)->find_line_info = fi;
+
+         /* Note that we don't bother to ever free this information.
+             find_nearest_line is either called all the time, as in
+             objdump -l, so the information should be saved, or it is
+             rarely called, as in ld error messages, so the memory
+             wasted is unimportant.  Still, it would probably be a
+             good idea for free_cached_info to throw it away.  */
+       }
+
+      if (_bfd_ecoff_locate_line (abfd, section, offset, &fi->d, swap,
+                                 &fi->i, filename_ptr, functionname_ptr,
+                                 line_ptr))
+       {
+         msec->flags = origflags;
+         return true;
+       }
+
+      msec->flags = origflags;
+    }
+
+  /* Fall back on the generic ELF find_nearest_line routine.  */
+
+  return _bfd_elf_find_nearest_line (abfd, section, symbols, offset,
+                                    filename_ptr, functionname_ptr,
+                                    line_ptr);
+}
+\f
 /* The MIPS ELF linker needs additional information for each symbol in
    the global hash table.  */
 
@@ -1334,10 +1667,7 @@ mips_elf_link_hash_newfunc (entry, table, string)
           bfd_hash_allocate (table,
                              sizeof (struct mips_elf_link_hash_entry)));
   if (ret == (struct mips_elf_link_hash_entry *) NULL)
-    {
-      bfd_set_error (bfd_error_no_memory);
-      return (struct bfd_hash_entry *) ret;
-    }
+    return (struct bfd_hash_entry *) ret;
 
   /* Call the allocation method of the superclass.  */
   ret = ((struct mips_elf_link_hash_entry *)
@@ -1366,10 +1696,7 @@ mips_elf_link_hash_table_create (abfd)
   ret = ((struct mips_elf_link_hash_table *)
         bfd_alloc (abfd, sizeof (struct mips_elf_link_hash_table)));
   if (ret == (struct mips_elf_link_hash_table *) NULL)
-    {
-      bfd_set_error (bfd_error_no_memory);
-      return NULL;
-    }
+    return NULL;
 
   if (! _bfd_elf_link_hash_table_init (&ret->root, abfd,
                                       mips_elf_link_hash_newfunc))
@@ -1479,7 +1806,8 @@ mips_elf_output_extsym (h, data)
       h->esym.asym.value = 0;
       h->esym.asym.st = stGlobal;
 
-      if (h->root.root.type != bfd_link_hash_defined)
+      if (h->root.root.type != bfd_link_hash_defined
+         && h->root.root.type != bfd_link_hash_defweak)
        h->esym.asym.sc = scAbs;
       else
        {
@@ -1514,6 +1842,23 @@ mips_elf_output_extsym (h, data)
       h->esym.asym.index = indexNil;
     }
 
+  if (h->root.root.type == bfd_link_hash_common)
+    h->esym.asym.value = h->root.root.u.c.size;
+  else if (h->root.root.type == bfd_link_hash_defined
+          || h->root.root.type == bfd_link_hash_defweak)
+    {
+      asection *sec;
+
+      if (h->esym.asym.sc == scCommon)
+       h->esym.asym.sc = scBss;
+      else if (h->esym.asym.sc == scSCommon)
+       h->esym.asym.sc = scSBss;
+
+      sec = h->root.root.u.def.section;
+      h->esym.asym.value = (h->root.root.u.def.value
+                           + sec->output_offset
+                           + sec->output_section->vma);
+    }
 
   if (! bfd_ecoff_debug_one_external (einfo->abfd, einfo->debug, einfo->swap,
                                      h->root.root.root.string,
@@ -1608,8 +1953,12 @@ mips_elf_final_link (abfd, info)
 
              input_section = p->u.indirect.section;
              input_bfd = input_section->owner;
-             BFD_ASSERT (input_section->_raw_size
-                         == sizeof (Elf32_External_RegInfo));
+
+             /* The linker emulation code has probably clobbered the
+                 size to be zero bytes.  */
+             if (input_section->_raw_size == 0)
+               input_section->_raw_size = sizeof (Elf32_External_RegInfo);
+
              if (! bfd_get_section_contents (input_bfd, input_section,
                                              (PTR) &ext,
                                              (file_ptr) 0,
@@ -1869,6 +2218,9 @@ mips_elf_final_link (abfd, info)
            gptab_bss_sec = o;
          else
            {
+             (*_bfd_error_handler)
+               ("%s: illegal section name `%s'",
+                bfd_get_filename (abfd), o->name);
              bfd_set_error (bfd_error_nonrepresentable_section);
              return false;
            }
@@ -2015,7 +2367,6 @@ mips_elf_final_link (abfd, info)
                     bfd_alloc (abfd, c * sizeof (Elf32_External_gptab)));
          if (ext_tab == NULL)
            {
-             bfd_set_error (bfd_error_no_memory);
              free (tab);
              return false;
            }
@@ -2151,8 +2502,7 @@ mips_elf_relocate_hi16 (input_bfd, relhi, rello, contents, addend)
 
 static boolean
 mips_elf_relocate_section (output_bfd, info, input_bfd, input_section,
-                          contents, relocs, local_syms, local_sections,
-                          output_names)
+                          contents, relocs, local_syms, local_sections)
      bfd *output_bfd;
      struct bfd_link_info *info;
      bfd *input_bfd;
@@ -2161,7 +2511,6 @@ mips_elf_relocate_section (output_bfd, info, input_bfd, input_section,
      Elf_Internal_Rela *relocs;
      Elf_Internal_Sym *local_syms;
      asection **local_sections;
-     char *output_names;
 {
   Elf_Internal_Shdr *symtab_hdr;
   size_t locsymcount;
@@ -2187,8 +2536,8 @@ mips_elf_relocate_section (output_bfd, info, input_bfd, input_section,
   for (; rel < relend; rel++)
     {
       int r_type;
-      const reloc_howto_type *howto;
-      long r_symndx;
+      reloc_howto_type *howto;
+      unsigned long r_symndx;
       bfd_vma addend;
       struct elf_link_hash_entry *h;
       asection *sec;
@@ -2327,14 +2676,15 @@ mips_elf_relocate_section (output_bfd, info, input_bfd, input_section,
 
              indx = r_symndx - extsymoff;
              h = elf_sym_hashes (input_bfd)[indx];
-             if (h->root.type == bfd_link_hash_defined)
+             if (h->root.type == bfd_link_hash_defined
+                 || h->root.type == bfd_link_hash_defweak)
                {
                  sec = h->root.u.def.section;
                  relocation = (h->root.u.def.value
                                + sec->output_section->vma
                                + sec->output_offset);
                }
-             else if (h->root.type == bfd_link_hash_weak)
+             else if (h->root.type == bfd_link_hash_undefweak)
                relocation = 0;
              else
                {
@@ -2375,7 +2725,9 @@ mips_elf_relocate_section (output_bfd, info, input_bfd, input_section,
                  name = h->root.root.string;
                else
                  {
-                   name = output_names + sym->st_name;
+                   name = bfd_elf_string_from_elf_section (input_bfd,
+                                                           symtab_hdr->sh_link,
+                                                           sym->st_name);
                    if (name == NULL)
                      return false;
                    if (*name == '\0')
@@ -2394,6 +2746,191 @@ mips_elf_relocate_section (output_bfd, info, input_bfd, input_section,
   return true;
 }
 \f
+/* This is almost identical to bfd_generic_get_... except that some
+   MIPS relocations need to be handled specially.  Sigh.  */
+static bfd_byte *
+elf32_mips_get_relocated_section_contents (abfd, link_info, link_order, data,
+                                          relocateable, symbols)
+     bfd *abfd;
+     struct bfd_link_info *link_info;
+     struct bfd_link_order *link_order;
+     bfd_byte *data;
+     boolean relocateable;
+     asymbol **symbols;
+{
+  /* Get enough memory to hold the stuff */
+  bfd *input_bfd = link_order->u.indirect.section->owner;
+  asection *input_section = link_order->u.indirect.section;
+
+  long reloc_size = bfd_get_reloc_upper_bound (input_bfd, input_section);
+  arelent **reloc_vector = NULL;
+  long reloc_count;
+
+  if (reloc_size < 0)
+    goto error_return;
+
+  reloc_vector = (arelent **) malloc (reloc_size);
+  if (reloc_vector == NULL && reloc_size != 0)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      goto error_return;
+    }
+
+  /* read in the section */
+  if (!bfd_get_section_contents (input_bfd,
+                                input_section,
+                                (PTR) data,
+                                0,
+                                input_section->_raw_size))
+    goto error_return;
+
+  /* We're not relaxing the section, so just copy the size info */
+  input_section->_cooked_size = input_section->_raw_size;
+  input_section->reloc_done = true;
+
+  reloc_count = bfd_canonicalize_reloc (input_bfd,
+                                       input_section,
+                                       reloc_vector,
+                                       symbols);
+  if (reloc_count < 0)
+    goto error_return;
+
+  if (reloc_count > 0)
+    {
+      arelent **parent;
+      /* for mips */
+      int gp_found;
+      bfd_vma gp = 0x12345678; /* initialize just to shut gcc up */
+
+      {
+       struct bfd_hash_entry *h;
+       struct bfd_link_hash_entry *lh;
+       /* Skip all this stuff if we aren't mixing formats.  */
+       if (abfd && input_bfd
+           && abfd->xvec == input_bfd->xvec)
+         lh = 0;
+       else
+         {
+           h = bfd_hash_lookup (&link_info->hash->table, "_gp", false, false);
+           lh = (struct bfd_link_hash_entry *) h;
+         }
+      lookup:
+       if (lh)
+         {
+           switch (lh->type)
+             {
+             case bfd_link_hash_undefined:
+             case bfd_link_hash_undefweak:
+             case bfd_link_hash_common:
+               gp_found = 0;
+               break;
+             case bfd_link_hash_defined:
+             case bfd_link_hash_defweak:
+               gp_found = 1;
+               gp = lh->u.def.value;
+               break;
+             case bfd_link_hash_indirect:
+             case bfd_link_hash_warning:
+               lh = lh->u.i.link;
+               /* @@FIXME  ignoring warning for now */
+               goto lookup;
+             case bfd_link_hash_new:
+             default:
+               abort ();
+             }
+         }
+       else
+         gp_found = 0;
+      }
+      /* end mips */
+      for (parent = reloc_vector; *parent != (arelent *) NULL;
+          parent++)
+       {
+         char *error_message = (char *) NULL;
+         bfd_reloc_status_type r;
+
+         /* Specific to MIPS: Deal with relocation types that require
+            knowing the gp of the output bfd.  */
+         asymbol *sym = *(*parent)->sym_ptr_ptr;
+         if (bfd_is_abs_section (sym->section) && abfd)
+           {
+             /* The special_function wouldn't get called anyways.  */
+           }
+         else if (!gp_found)
+           {
+             /* The gp isn't there; let the special function code
+                fall over on its own.  */
+           }
+         else if ((*parent)->howto->special_function == mips_elf_gprel16_reloc)
+           {
+             /* bypass special_function call */
+             r = gprel16_with_gp (input_bfd, sym, *parent, input_section,
+                                  relocateable, (PTR) data, gp);
+             goto skip_bfd_perform_relocation;
+           }
+         /* end mips specific stuff */
+
+         r = bfd_perform_relocation (input_bfd,
+                                     *parent,
+                                     (PTR) data,
+                                     input_section,
+                                     relocateable ? abfd : (bfd *) NULL,
+                                     &error_message);
+       skip_bfd_perform_relocation:
+
+         if (relocateable)
+           {
+             asection *os = input_section->output_section;
+
+             /* A partial link, so keep the relocs */
+             os->orelocation[os->reloc_count] = *parent;
+             os->reloc_count++;
+           }
+
+         if (r != bfd_reloc_ok)
+           {
+             switch (r)
+               {
+               case bfd_reloc_undefined:
+                 if (!((*link_info->callbacks->undefined_symbol)
+                       (link_info, bfd_asymbol_name (*(*parent)->sym_ptr_ptr),
+                        input_bfd, input_section, (*parent)->address)))
+                   goto error_return;
+                 break;
+               case bfd_reloc_dangerous:
+                 BFD_ASSERT (error_message != (char *) NULL);
+                 if (!((*link_info->callbacks->reloc_dangerous)
+                       (link_info, error_message, input_bfd, input_section,
+                        (*parent)->address)))
+                   goto error_return;
+                 break;
+               case bfd_reloc_overflow:
+                 if (!((*link_info->callbacks->reloc_overflow)
+                       (link_info, bfd_asymbol_name (*(*parent)->sym_ptr_ptr),
+                        (*parent)->howto->name, (*parent)->addend,
+                        input_bfd, input_section, (*parent)->address)))
+                   goto error_return;
+                 break;
+               case bfd_reloc_outofrange:
+               default:
+                 abort ();
+                 break;
+               }
+
+           }
+       }
+    }
+  if (reloc_vector != NULL)
+    free (reloc_vector);
+  return data;
+
+error_return:
+  if (reloc_vector != NULL)
+    free (reloc_vector);
+  return NULL;
+}
+#define bfd_elf32_bfd_get_relocated_section_contents elf32_mips_get_relocated_section_contents
+\f
 /* ECOFF swapping routines.  These are used when dealing with the
    .mdebug section, which is in the ECOFF debugging format.  */
 static const struct ecoff_debug_swap mips_elf_ecoff_debug_swap =
@@ -2459,6 +2996,9 @@ static const struct ecoff_debug_swap mips_elf_ecoff_debug_swap =
                                        mips_elf_final_write_processing
 #define elf_backend_ecoff_debug_swap   &mips_elf_ecoff_debug_swap
 
+#define bfd_elf32_bfd_is_local_label   mips_elf_is_local_label
+#define bfd_elf32_find_nearest_line    mips_elf_find_nearest_line
+
 #define bfd_elf32_bfd_link_hash_table_create \
                                        mips_elf_link_hash_table_create
 #define bfd_elf32_bfd_final_link       mips_elf_final_link