* config/tc-mips.c (md_section_align): Do align if OBJ_ELF, but
[binutils-gdb.git] / gas / config / tc-mips.c
index 61f38dc729ec97e50fea0d5a4cfd89edca26c165..38bcebc5dc467f66c9d8d9e87c013d778e180b3d 100644 (file)
@@ -70,7 +70,9 @@ static int mips_output_flavor () { return OUTPUT_FLAVOR; }
 
 #include "ecoff.h"
 
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
 static char *mips_regmask_frag;
+#endif
 
 #define AT  1
 #define PIC_CALL_REG 25
@@ -83,6 +85,10 @@ static char *mips_regmask_frag;
 
 extern int target_big_endian;
 
+/* 1 is we should use the 64 bit MIPS ELF ABI, 0 if we should use the
+   32 bit ABI.  This has no meaning for ECOFF.  */
+static int mips_64;
+
 /* The default target format to use.  */
 const char *
 mips_target_format ()
@@ -94,7 +100,9 @@ mips_target_format ()
     case bfd_target_ecoff_flavour:
       return target_big_endian ? "ecoff-bigmips" : "ecoff-littlemips";
     case bfd_target_elf_flavour:
-      return target_big_endian ? "elf32-bigmips" : "elf32-littlemips";
+      return (target_big_endian
+             ? (mips_64 ? "elf64-bigmips" : "elf32-bigmips")
+             : (mips_64 ? "elf64-littlemips" : "elf32-littlemips"));
     default:
       abort ();
     }
@@ -138,6 +146,10 @@ static int mips_4100 = -1;
    require nops to be inserted.  */
 static int interlocks = -1;
 
+/* As with "interlocks" this is used by hardware that has FP
+   (co-processor) interlocks.  */
+static int cop_interlocks = -1;
+
 /* MIPS PIC level.  */
 
 enum mips_pic_level
@@ -682,11 +694,16 @@ md_begin ()
   if (mips_4100 < 0)
     mips_4100 = 0;
 
-  if (mips_4650 || mips_4010 || mips_4100)
+  if (mips_4650 || mips_4010 || mips_4100 || mips_cpu == 4300)
     interlocks = 1;
   else
     interlocks = 0;
 
+  if (mips_cpu == 4300)
+    cop_interlocks = 1;
+  else
+    cop_interlocks = 0;
+
   if (mips_isa < 2 && mips_trap)
     as_bad ("trap exception not supported at ISA 1");
 
@@ -768,18 +785,51 @@ md_begin ()
 
        seg = now_seg;
        subseg = now_subseg;
-       sec = subseg_new (".reginfo", (subsegT) 0);
 
-       /* The ABI says this section should be loaded so that the
-          running program can access it.  */
-       (void) bfd_set_section_flags (stdoutput, sec,
-                                     (SEC_ALLOC | SEC_LOAD
-                                      | SEC_READONLY | SEC_DATA));
-       (void) bfd_set_section_alignment (stdoutput, sec, 2);
+       if (! mips_64)
+         {
+           sec = subseg_new (".reginfo", (subsegT) 0);
+
+           /* The ABI says this section should be loaded so that the
+              running program can access it.  */
+           (void) bfd_set_section_flags (stdoutput, sec,
+                                         (SEC_ALLOC | SEC_LOAD
+                                          | SEC_READONLY | SEC_DATA));
+           (void) bfd_set_section_alignment (stdoutput, sec, 2);
+       
+#ifdef OBJ_ELF
+           mips_regmask_frag = frag_more (sizeof (Elf32_External_RegInfo));
+#endif
+         }
+       else
+         {
+           /* The 64-bit ABI uses a .MIPS.options section rather than
+               .reginfo section.  */
+           sec = subseg_new (".MIPS.options", (subsegT) 0);
+           (void) bfd_set_section_flags (stdoutput, sec,
+                                         (SEC_ALLOC | SEC_LOAD
+                                          | SEC_READONLY | SEC_DATA));
+           (void) bfd_set_section_alignment (stdoutput, sec, 3);
 
 #ifdef OBJ_ELF
-       mips_regmask_frag = frag_more (sizeof (Elf32_External_RegInfo));
+           /* Set up the option header.  */
+           {
+             Elf_Internal_Options opthdr;
+             char *f;
+
+             opthdr.kind = ODK_REGINFO;
+             opthdr.size = (sizeof (Elf_External_Options)
+                            + sizeof (Elf64_External_RegInfo));
+             opthdr.section = 0;
+             opthdr.info = 0;
+             f = frag_more (sizeof (Elf_External_Options));
+             bfd_mips_elf_swap_options_out (stdoutput, &opthdr,
+                                            (Elf_External_Options *) f);
+
+             mips_regmask_frag = frag_more (sizeof (Elf64_External_RegInfo));
+           }
 #endif
+         }
 
        if (ECOFF_DEBUGGING)
          {
@@ -959,7 +1009,8 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
       /* The previous insn might require a delay slot, depending upon
         the contents of the current insn.  */
       if (mips_isa < 4
-         && ((prev_pinfo & INSN_LOAD_COPROC_DELAY)
+         && (((prev_pinfo & INSN_LOAD_COPROC_DELAY)
+               && ! cop_interlocks)
              || (mips_isa < 2
                  && (prev_pinfo & INSN_LOAD_MEMORY_DELAY))))
        {
@@ -976,7 +1027,8 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
            ++nops;
        }
       else if (mips_isa < 4
-              && ((prev_pinfo & INSN_COPROC_MOVE_DELAY)
+              && (((prev_pinfo & INSN_COPROC_MOVE_DELAY)
+                    && ! cop_interlocks)
                   || (mips_isa < 2
                       && (prev_pinfo & INSN_COPROC_MEMORY_DELAY))))
        {
@@ -1029,7 +1081,8 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
            }
        }
       else if (mips_isa < 4
-              && (prev_pinfo & INSN_WRITE_COND_CODE))
+              && (prev_pinfo & INSN_WRITE_COND_CODE)
+               && ! cop_interlocks)
        {
          /* The previous instruction sets the coprocessor condition
             codes, but does not require a general coprocessor delay
@@ -1044,7 +1097,8 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
        {
          /* The previous instruction reads the LO register; if the
             current instruction writes to the LO register, we must
-            insert two NOPS.  The R4650 and VR4100 have interlocks.  */
+            insert two NOPS.  The R4650, VR4100 and VR4300 have
+            interlocks.  */
          if (! interlocks
              && (mips_optimize == 0
                  || (pinfo & INSN_WRITE_LO)))
@@ -1054,7 +1108,8 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
        {
          /* The previous instruction reads the HI register; if the
             current instruction writes to the HI register, we must
-            insert a NOP.  The R4650 and VR4100 have interlocks.  */
+            insert a NOP.  The R4650, VR4100 and VR4300 have
+            interlocks.  */
          if (! interlocks
              && (mips_optimize == 0
                  || (pinfo & INSN_WRITE_HI)))
@@ -1066,15 +1121,16 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
         coprocessor instruction which requires a general coprocessor
         delay and then reading the condition codes 2) reading the HI
         or LO register and then writing to it (except on the R4650,
-        and VR4100 which have interlocks).  If we are not already
-        emitting a NOP instruction, we must check for these cases
-        compared to the instruction previous to the previous
+        VR4100, and VR4300 which have interlocks).  If we are not
+        already emitting a NOP instruction, we must check for these
+        cases compared to the instruction previous to the previous
         instruction.  */
       if (nops == 0
          && ((mips_isa < 4
               && (prev_prev_insn.insn_mo->pinfo & INSN_COPROC_MOVE_DELAY)
               && (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
-              && (pinfo & INSN_READ_COND_CODE))
+              && (pinfo & INSN_READ_COND_CODE)
+               && ! cop_interlocks)
              || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)
                  && (pinfo & INSN_WRITE_LO)
                  && ! interlocks)
@@ -1093,10 +1149,16 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
       /* Now emit the right number of NOP instructions.  */
       if (nops > 0)
        {
+         fragS *old_frag;
+         unsigned long old_frag_offset;
          int i;
 
+         old_frag = frag_now;
+         old_frag_offset = frag_now_fix ();
+
          for (i = 0; i < nops; i++)
            emit_nop ();
+
          if (listing)
            {
              listing_prev_line ();
@@ -1110,12 +1172,18 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                  all needed nop instructions themselves.  */
              frag_grow (40);
            }
+
          if (insn_label != NULL)
            {
              assert (S_GET_SEGMENT (insn_label) == now_seg);
              insn_label->sy_frag = frag_now;
              S_SET_VALUE (insn_label, (valueT) frag_now_fix ());
            }
+
+#ifndef NO_ECOFF_DEBUGGING
+         if (ECOFF_DEBUGGING)
+           ecoff_fix_loc (old_frag, old_frag_offset);
+#endif
        }
     }
   
@@ -1453,10 +1521,11 @@ mips_emit_delays ()
 
       nop = 0;
       if ((mips_isa < 4
-          && (prev_insn.insn_mo->pinfo
-              & (INSN_LOAD_COPROC_DELAY
-                 | INSN_COPROC_MOVE_DELAY
-                 | INSN_WRITE_COND_CODE)))
+          && (! cop_interlocks
+               && (prev_insn.insn_mo->pinfo
+                   & (INSN_LOAD_COPROC_DELAY
+                      | INSN_COPROC_MOVE_DELAY
+                      | INSN_WRITE_COND_CODE))))
          || (! interlocks
              && (prev_insn.insn_mo->pinfo
                  & (INSN_READ_LO
@@ -1468,14 +1537,16 @@ mips_emit_delays ()
        {
          nop = 1;
          if ((mips_isa < 4
-              && (prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE))
+              && (! cop_interlocks
+                   && prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE))
              || (! interlocks
                  && ((prev_insn.insn_mo->pinfo & INSN_READ_HI)
                      || (prev_insn.insn_mo->pinfo & INSN_READ_LO))))
            emit_nop ();
        }
       else if ((mips_isa < 4
-               && (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE))
+               && (! cop_interlocks
+                    && prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE))
               || (! interlocks
                   && ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
                       || (prev_prev_insn.insn_mo->pinfo & INSN_READ_LO))))
@@ -1832,13 +1903,15 @@ load_register (counter, reg, ep, dbl)
                       (int) BFD_RELOC_LO16);
          return;
        }
-      else if (((ep->X_add_number &~ (offsetT) 0x7fffffff) == 0
-               || ((ep->X_add_number &~ (offsetT) 0x7fffffff)
-                   == ~ (offsetT) 0x7fffffff))
-              && (! dbl
-                  || ! ep->X_unsigned
-                  || sizeof (ep->X_add_number) > 4
-                  || (ep->X_add_number & 0x80000000) == 0))
+      else if ((((ep->X_add_number &~ (offsetT) 0x7fffffff) == 0
+                || ((ep->X_add_number &~ (offsetT) 0x7fffffff)
+                    == ~ (offsetT) 0x7fffffff))
+               && (! dbl
+                   || ! ep->X_unsigned
+                   || sizeof (ep->X_add_number) > 4
+                   || (ep->X_add_number & 0x80000000) == 0))
+              || (mips_isa < 3
+                  && (ep->X_add_number &~ 0xffffffff) == 0))
        {
          /* 32 bit values require an lui.  */
          macro_build ((char *) NULL, counter, ep, "lui", "t,u", reg,
@@ -4417,9 +4490,9 @@ macro2 (ip)
 
     case M_ROL_I:
       macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", AT, sreg,
-                  imm_expr.X_add_number & 0x1f);
+                  (int) (imm_expr.X_add_number & 0x1f));
       macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", dreg, sreg,
-                  (0 - imm_expr.X_add_number) & 0x1f);
+                  (int) ((0 - imm_expr.X_add_number) & 0x1f));
       macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
       break;
 
@@ -4433,9 +4506,9 @@ macro2 (ip)
 
     case M_ROR_I:
       macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", AT, sreg,
-                  imm_expr.X_add_number & 0x1f);
+                  (int) (imm_expr.X_add_number & 0x1f));
       macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", dreg, sreg,
-                  (0 - imm_expr.X_add_number) & 0x1f);
+                  (int) ((0 - imm_expr.X_add_number) & 0x1f));
       macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
       break;
 
@@ -4998,8 +5071,7 @@ mips_ip (str, ip)
     }
   if ((insn = (struct mips_opcode *) hash_find (op_hash, str)) == NULL)
     {
-      as_warn ("`%s' not in hash table.", str);
-      insn_error = "ERROR: Unrecognized opcode";
+      insn_error = "unrecognized opcode";
       return;
     }
   argsStart = s;
@@ -5034,7 +5106,8 @@ mips_ip (str, ip)
              ++insn;
              continue;
            }
-         as_warn ("Instruction not supported on this processor");
+         insn_error = "opcode not supported on this processor";
+         return;
        }
 
       ip->insn_mo = insn;
@@ -5238,7 +5311,10 @@ mips_ip (str, ip)
                      else
                        goto notreg;
                    }
-                 if (regno == AT && ! mips_noat)
+                 if (regno == AT
+                     && ! mips_noat
+                     && *args != 'E'
+                     && *args != 'G')
                    as_warn ("Used $at without \".set noat\"");
                  c = *args;
                  if (*s == ' ')
@@ -5380,8 +5456,9 @@ mips_ip (str, ip)
 
            case 'I':
              my_getExpression (&imm_expr, s);
-             if (imm_expr.X_op != O_big)
-               check_absolute_expr (ip, &imm_expr);
+             if (imm_expr.X_op != O_big
+                 && imm_expr.X_op != O_constant)
+               insn_error = "absolute expression required";
              s = expr_end;
              continue;
 
@@ -5496,6 +5573,12 @@ mips_ip (str, ip)
                        break;
                      }
                    new_seg = subseg_new (newname, (subsegT) 0);
+                   if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+                     bfd_set_section_flags (stdoutput, new_seg,
+                                            (SEC_ALLOC
+                                             | SEC_LOAD
+                                             | SEC_READONLY
+                                             | SEC_DATA));
                    frag_align (*args == 'l' ? 2 : 3, 0);
                    if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
                      record_alignment (new_seg, 4);
@@ -5526,7 +5609,7 @@ mips_ip (str, ip)
            case 'j':           /* 16 bit signed immediate */
              imm_reloc = BFD_RELOC_LO16;
              c = my_getSmallExpression (&imm_expr, s);
-             if (c)
+             if (c != '\0')
                {
                  if (c != 'l')
                    {
@@ -5542,18 +5625,21 @@ mips_ip (str, ip)
                        imm_reloc = BFD_RELOC_HI16;
                    }
                }
-             else if (imm_expr.X_op != O_big)
-               check_absolute_expr (ip, &imm_expr);
              if (*args == 'i')
                {
-                 if (imm_expr.X_op == O_big
-                     || imm_expr.X_add_number < 0
-                     || imm_expr.X_add_number >= 0x10000)
+                 if ((c == '\0' && imm_expr.X_op != O_constant)
+                     || ((imm_expr.X_add_number < 0
+                           || imm_expr.X_add_number >= 0x10000)
+                          && imm_expr.X_op == O_constant))
                    {
                      if (insn + 1 < &mips_opcodes[NUMOPCODES] &&
                          !strcmp (insn->name, insn[1].name))
                        break;
-                     as_bad ("16 bit expression not in range 0..65535");
+                     if (imm_expr.X_op != O_constant
+                         && imm_expr.X_op != O_big)
+                       insn_error = "absolute expression required";
+                     else
+                       as_bad ("16 bit expression not in range 0..65535");
                    }
                }
              else
@@ -5576,9 +5662,10 @@ mips_ip (str, ip)
                    max = 0x8000;
                  else
                    max = 0x10000;
-                 if (imm_expr.X_op == O_big
-                     || imm_expr.X_add_number < -0x8000
-                     || imm_expr.X_add_number >= max
+                 if ((c == '\0' && imm_expr.X_op != O_constant)
+                     || ((imm_expr.X_add_number < -0x8000
+                           || imm_expr.X_add_number >= max)
+                          && imm_expr.X_op == O_constant)
                      || (more
                          && imm_expr.X_add_number < 0
                          && mips_isa >= 3
@@ -5587,7 +5674,11 @@ mips_ip (str, ip)
                    {
                      if (more)
                        break;
-                     as_bad ("16 bit expression not in range -32768..32767");
+                     if (imm_expr.X_op != O_constant
+                         && imm_expr.X_op != O_big)
+                       insn_error = "absolute expression required";
+                     else
+                       as_bad ("16 bit expression not in range -32768..32767");
                    }
                }
              s = expr_end;
@@ -5698,7 +5789,7 @@ mips_ip (str, ip)
          s = argsStart;
          continue;
        }
-      insn_error = "ERROR: Illegal operands";
+      insn_error = "illegal operands";
       return;
     }
 }
@@ -5927,11 +6018,15 @@ struct option md_longopts[] = {
 #define OPTION_CALL_SHARED (OPTION_MD_BASE + 7)
 #define OPTION_NON_SHARED (OPTION_MD_BASE + 8)
 #define OPTION_XGOT (OPTION_MD_BASE + 19)
+#define OPTION_32 (OPTION_MD_BASE + 20)
+#define OPTION_64 (OPTION_MD_BASE + 21)
 #ifdef OBJ_ELF
   {"KPIC", no_argument, NULL, OPTION_CALL_SHARED},
   {"xgot", no_argument, NULL, OPTION_XGOT},
   {"call_shared", no_argument, NULL, OPTION_CALL_SHARED},
   {"non_shared", no_argument, NULL, OPTION_NON_SHARED},
+  {"32", no_argument, NULL, OPTION_32},
+  {"64", no_argument, NULL, OPTION_64},
 #endif
 
   {NULL, no_argument, NULL, 0}
@@ -6202,6 +6297,28 @@ md_parse_option (c, arg)
       g_switch_seen = 1;
       break;
 
+      /* The -32 and -64 options tell the assembler to output the 32
+         bit or the 64 bit MIPS ELF format.  */
+    case OPTION_32:
+      mips_64 = 0;
+      break;
+
+    case OPTION_64:
+      {
+       const char **list, **l;
+
+       list = bfd_target_list ();
+       for (l = list; *l != NULL; l++)
+         if (strcmp (*l, "elf64-bigmips") == 0
+             || strcmp (*l, "elf64-littlemips") == 0)
+           break;
+       if (*l == NULL)
+         as_fatal ("No compiled in support for 64 bit object file format");
+       free (list);
+       mips_64 = 1;
+      }
+      break;
+
     default:
       return 0;
     }
@@ -6243,7 +6360,9 @@ MIPS options:\n\
   fprintf(stream, "\
 -KPIC, -call_shared    generate SVR4 position independent code\n\
 -non_shared            do not generate position independent code\n\
--xgot                  assume a 32 bit GOT\n");
+-xgot                  assume a 32 bit GOT\n\
+-32                    create 32 bit object file (default)\n\
+-64                    create 64 bit object file\n");
 #endif
 }
 
@@ -6285,21 +6404,21 @@ cons_fix_new_mips (frag, where, nbytes, exp)
      expressionS *exp;
 {
   /* If we are assembling in 32 bit mode, turn an 8 byte reloc into a
-     4 byte reloc.  
-     FIXME: There is no way to select anything but 32 bit mode right
-     now.  */
-  if (nbytes == 8)
+     4 byte reloc.  */
+  if (nbytes == 8 && ! mips_64)
     {
       if (byte_order == BIG_ENDIAN)
        where += 4;
       nbytes = 4;
     }
 
-  if (nbytes != 2 && nbytes != 4)
+  if (nbytes != 2 && nbytes != 4 && nbytes != 8)
     as_bad ("Unsupported reloc size %d", nbytes);
 
   fix_new_exp (frag_now, where, (int) nbytes, exp, 0,
-              nbytes == 2 ? BFD_RELOC_16 : BFD_RELOC_32);
+              (nbytes == 2
+               ? BFD_RELOC_16
+               : (nbytes == 4 ? BFD_RELOC_32 : BFD_RELOC_64)));
 }
 
 /* Sort any unmatched HI16_S relocs so that they immediately precede
@@ -6315,7 +6434,7 @@ mips_frob_file ()
   for (l = mips_hi_fixup_list; l != NULL; l = l->next)
     {
       segment_info_type *seginfo;
-      fixS *f, *prev;
+      int pass;
 
       assert (l->fixp->fx_r_type == BFD_RELOC_HI16_S);
 
@@ -6328,46 +6447,57 @@ mips_frob_file ()
        continue;
 
       /* Look through the fixups for this segment for a matching %lo.
-         When we find one, move the %hi just in front of it.  */
+         When we find one, move the %hi just in front of it.  We do
+         this in two passes.  In the first pass, we try to find a
+         unique %lo.  In the second pass, we permit multiple %hi
+         relocs for a single %lo (this is a GNU extension).  */
       seginfo = seg_info (l->seg);
-      prev = NULL;
-      for (f = seginfo->fix_root; f != NULL; f = f->fx_next)
-       {
-         /* Check whether this is a %lo fixup which matches l->fixp;
-             we can't use it if the %lo is already matching a %hi.  */
-         if (f->fx_r_type == BFD_RELOC_LO16
-             && f->fx_addsy == l->fixp->fx_addsy
-             && f->fx_offset == l->fixp->fx_offset
-             && (prev == NULL
-                 || prev->fx_r_type != BFD_RELOC_HI16_S
-                 || prev->fx_addsy != f->fx_addsy
-                 || prev->fx_offset !=  f->fx_offset))
+      for (pass = 0; pass < 2; pass++)
+       {
+         fixS *f, *prev;
+
+         prev = NULL;
+         for (f = seginfo->fix_root; f != NULL; f = f->fx_next)
            {
-             fixS **pf;
+             /* Check whether this is a %lo fixup which matches l->fixp.  */
+             if (f->fx_r_type == BFD_RELOC_LO16
+                 && f->fx_addsy == l->fixp->fx_addsy
+                 && f->fx_offset == l->fixp->fx_offset
+                 && (pass == 1
+                     || prev == NULL
+                     || prev->fx_r_type != BFD_RELOC_HI16_S
+                     || prev->fx_addsy != f->fx_addsy
+                     || prev->fx_offset !=  f->fx_offset))
+               {
+                 fixS **pf;
 
-             /* Move l->fixp before f.  */
-             for (pf = &seginfo->fix_root;
-                  *pf != l->fixp;
-                  pf = &(*pf)->fx_next)
-               assert (*pf != NULL);
+                 /* Move l->fixp before f.  */
+                 for (pf = &seginfo->fix_root;
+                      *pf != l->fixp;
+                      pf = &(*pf)->fx_next)
+                   assert (*pf != NULL);
 
-             *pf = l->fixp->fx_next;
+                 *pf = l->fixp->fx_next;
 
-             l->fixp->fx_next = f;
-             if (prev == NULL)
-               seginfo->fix_root = l->fixp;
-             else
-               prev->fx_next = l->fixp;
+                 l->fixp->fx_next = f;
+                 if (prev == NULL)
+                   seginfo->fix_root = l->fixp;
+                 else
+                   prev->fx_next = l->fixp;
 
-             break;
+                 break;
+               }
+
+             prev = f;
            }
 
-         prev = f;
-       }
+         if (f != NULL)
+           break;
 
-      if (f == NULL)
-       as_warn_where (l->fixp->fx_file, l->fixp->fx_line,
-                      "Unmatched %%hi reloc");
+         if (pass == 1)
+           as_warn_where (l->fixp->fx_file, l->fixp->fx_line,
+                          "Unmatched %%hi reloc");
+       }
     }
 }
 
@@ -7265,6 +7395,14 @@ md_section_align (seg, addr)
 {
   int align = bfd_get_section_alignment (stdoutput, seg);
 
+#ifdef OBJ_ELF
+  /* We don't need to align ELF sections to the full alignment.
+     However, Irix 5 may prefer that we align them at least to a 16
+     byte boundary.  */
+  if (align > 16)
+    align = 16;
+#endif
+
   return ((addr + (1 << align) - 1) & (-1 << align));
 }
 
@@ -7386,6 +7524,7 @@ tc_gen_reloc (section, fixp)
 {
   static arelent *retval[4];
   arelent *reloc;
+  bfd_reloc_code_real_type code;
 
   reloc = retval[0] = (arelent *) xmalloc (sizeof (arelent));
   retval[1] = NULL;
@@ -7518,20 +7657,53 @@ tc_gen_reloc (section, fixp)
        abort ();
     }
 
+  /* Since DIFF_EXPR_OK is defined in tc-mips.h, it is possible that
+     fixup_segment converted a non-PC relative reloc into a PC
+     relative reloc.  In such a case, we need to convert the reloc
+     code.  */
+  code = fixp->fx_r_type;
+  if (fixp->fx_pcrel)
+    {
+      switch (code)
+       {
+       case BFD_RELOC_8:
+         code = BFD_RELOC_8_PCREL;
+         break;
+       case BFD_RELOC_16:
+         code = BFD_RELOC_16_PCREL;
+         break;
+       case BFD_RELOC_32:
+         code = BFD_RELOC_32_PCREL;
+         break;
+       case BFD_RELOC_8_PCREL:
+       case BFD_RELOC_16_PCREL:
+       case BFD_RELOC_32_PCREL:
+       case BFD_RELOC_16_PCREL_S2:
+       case BFD_RELOC_PCREL_HI16_S:
+       case BFD_RELOC_PCREL_LO16:
+         break;
+       default:
+         as_bad_where (fixp->fx_file, fixp->fx_line,
+                       "Cannot make %s relocation PC relative",
+                       bfd_get_reloc_code_name (code));
+       }
+    }
+
   /* To support a PC relative reloc when generating embedded PIC code
      for ECOFF, we use a Cygnus extension.  We check for that here to
      make sure that we don't let such a reloc escape normally.  */
   if (OUTPUT_FLAVOR == bfd_target_ecoff_flavour
-      && fixp->fx_r_type == BFD_RELOC_16_PCREL_S2
+      && code == BFD_RELOC_16_PCREL_S2
       && mips_pic != EMBEDDED_PIC)
     reloc->howto = NULL;
   else
-    reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+    reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
 
   if (reloc->howto == NULL)
     {
       as_bad_where (fixp->fx_file, fixp->fx_line,
-                   "Can not represent relocation in this object file format");
+                   "Can not represent %s relocation in this object file format",
+                   bfd_get_reloc_code_name (code));
       retval[0] = NULL;
     }
 
@@ -7607,19 +7779,38 @@ mips_local_label (name)
 void
 mips_elf_final_processing ()
 {
-  Elf32_RegInfo s;
-
-  /* Write out the .reginfo section.  */
-  s.ri_gprmask = mips_gprmask;
-  s.ri_cprmask[0] = mips_cprmask[0];
-  s.ri_cprmask[1] = mips_cprmask[1];
-  s.ri_cprmask[2] = mips_cprmask[2];
-  s.ri_cprmask[3] = mips_cprmask[3];
-  /* The gp_value field is set by the MIPS ELF backend.  */
-
-  bfd_mips_elf32_swap_reginfo_out (stdoutput, &s,
-                                  ((Elf32_External_RegInfo *)
-                                   mips_regmask_frag));
+  /* Write out the register information.  */
+  if (! mips_64)
+    {
+      Elf32_RegInfo s;
+
+      s.ri_gprmask = mips_gprmask;
+      s.ri_cprmask[0] = mips_cprmask[0];
+      s.ri_cprmask[1] = mips_cprmask[1];
+      s.ri_cprmask[2] = mips_cprmask[2];
+      s.ri_cprmask[3] = mips_cprmask[3];
+      /* The gp_value field is set by the MIPS ELF backend.  */
+
+      bfd_mips_elf32_swap_reginfo_out (stdoutput, &s,
+                                      ((Elf32_External_RegInfo *)
+                                       mips_regmask_frag));
+    }
+  else
+    {
+      Elf64_Internal_RegInfo s;
+
+      s.ri_gprmask = mips_gprmask;
+      s.ri_pad = 0;
+      s.ri_cprmask[0] = mips_cprmask[0];
+      s.ri_cprmask[1] = mips_cprmask[1];
+      s.ri_cprmask[2] = mips_cprmask[2];
+      s.ri_cprmask[3] = mips_cprmask[3];
+      /* The gp_value field is set by the MIPS ELF backend.  */
+
+      bfd_mips_elf64_swap_reginfo_out (stdoutput, &s,
+                                      ((Elf64_External_RegInfo *)
+                                       mips_regmask_frag));
+    }
 
   /* Set the MIPS ELF flag bits.  FIXME: There should probably be some
      sort of BFD interface for this.  */