OSDN Git Service

* config/tc-mips.c (prev_insn_reloc_type): Make it an array to hold a
authorths <ths>
Thu, 4 Oct 2001 20:07:00 +0000 (20:07 +0000)
committerths <ths>
Thu, 4 Oct 2001 20:07:00 +0000 (20:07 +0000)
relocation triple.
(prev_insn_fixp): Likewise.
(append_insn): Changed prototype to accept a relocation pointer.
(imm_reloc): Make it an array.
(offset_reloc): Likewise.
(md_assemble): Handle triple relocations.
(append_insn): Likewise. Add handling for some NewABI relocations.
(mips_no_prev_insn): Handle triple relocations.
(macro_build): Likewise. Add handling for some NewABI relocations.
Move handling for the 'u' case to append_insn().
(mips16_macro_build): Handle triple relocations.
(macro_build_lui): Likewise. Don't handle _gp_disp as special symbol
for NewABI.
(mips_ip): Handle triple relocations.
(mips16_ip): Likewise.
(mips_force_relocation): Force handling of triple relocations
without symbols for NewABI.
(md_apply_fix): Add handling for some NewABI relocations.

gas/ChangeLog
gas/config/tc-mips.c

index a74637a..c35a822 100644 (file)
@@ -1,3 +1,25 @@
+2001-10-04  Thiemo Seufer <seufer@csv.ica.uni-stuttgart.de>
+
+       * config/tc-mips.c (prev_insn_reloc_type): Make it an array to hold a
+       relocation triple.
+       (prev_insn_fixp): Likewise.
+       (append_insn): Changed prototype to accept a relocation pointer.
+       (imm_reloc): Make it an array.
+       (offset_reloc): Likewise.
+       (md_assemble): Handle triple relocations.
+       (append_insn): Likewise. Add handling for some NewABI relocations.
+       (mips_no_prev_insn): Handle triple relocations.
+       (macro_build): Likewise. Add handling for some NewABI relocations.
+       Move handling for the 'u' case to append_insn().
+       (mips16_macro_build): Handle triple relocations.
+       (macro_build_lui): Likewise. Don't handle _gp_disp as special symbol
+       for NewABI.
+       (mips_ip): Handle triple relocations.
+       (mips16_ip): Likewise.
+       (mips_force_relocation): Force handling of triple relocations
+       without symbols for NewABI.
+       (md_apply_fix): Add handling for some NewABI relocations.
+
 2001-10-05  Alan Modra  <amodra@bigpond.net.au>
 
        * config/tc-i386.c (parse_register): If not producing code for
index cb6dd62..9a5abb1 100644 (file)
@@ -430,10 +430,10 @@ static struct frag *prev_insn_frag;
 static long prev_insn_where;
 
 /* The reloc type for the previous instruction, if any.  */
-static bfd_reloc_code_real_type prev_insn_reloc_type;
+static bfd_reloc_code_real_type prev_insn_reloc_type[3];
 
 /* The reloc for the previous instruction, if any.  */
-static fixS *prev_insn_fixp;
+static fixS *prev_insn_fixp[3];
 
 /* Non-zero if the previous instruction was in a delay slot.  */
 static int prev_insn_is_delay_slot;
@@ -643,7 +643,7 @@ static void mips16_mark_labels PARAMS ((void));
 static void append_insn PARAMS ((char *place,
                                 struct mips_cl_insn * ip,
                                 expressionS * p,
-                                bfd_reloc_code_real_type r,
+                                bfd_reloc_code_real_type *r,
                                 boolean));
 static void mips_no_prev_insn PARAMS ((int));
 static void mips_emit_delays PARAMS ((boolean));
@@ -868,8 +868,10 @@ static expressionS offset_expr;
 
 /* Relocs associated with imm_expr and offset_expr.  */
 
-static bfd_reloc_code_real_type imm_reloc;
-static bfd_reloc_code_real_type offset_reloc;
+static bfd_reloc_code_real_type imm_reloc[3]
+  = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+static bfd_reloc_code_real_type offset_reloc[3]
+  = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
 
 /* This is set by mips_ip if imm_reloc is an unmatched HI16_S reloc.  */
 
@@ -1303,12 +1305,18 @@ md_assemble (str)
      char *str;
 {
   struct mips_cl_insn insn;
+  bfd_reloc_code_real_type unused_reloc[3]
+    = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
 
   imm_expr.X_op = O_absent;
-  imm_reloc = BFD_RELOC_UNUSED;
   imm_unmatched_hi = false;
   offset_expr.X_op = O_absent;
-  offset_reloc = BFD_RELOC_UNUSED;
+  imm_reloc[0] = BFD_RELOC_UNUSED;
+  imm_reloc[1] = BFD_RELOC_UNUSED;
+  imm_reloc[2] = BFD_RELOC_UNUSED;
+  offset_reloc[0] = BFD_RELOC_UNUSED;
+  offset_reloc[1] = BFD_RELOC_UNUSED;
+  offset_reloc[2] = BFD_RELOC_UNUSED;
 
   if (mips_opts.mips16)
     mips16_ip (str, &insn);
@@ -1340,7 +1348,7 @@ md_assemble (str)
       else if (offset_expr.X_op != O_absent)
        append_insn ((char *) NULL, &insn, &offset_expr, offset_reloc, false);
       else
-       append_insn ((char *) NULL, &insn, NULL, BFD_RELOC_UNUSED, false);
+       append_insn ((char *) NULL, &insn, NULL, unused_reloc, false);
     }
 }
 
@@ -1492,12 +1500,12 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
      char *place;
      struct mips_cl_insn *ip;
      expressionS *address_expr;
-     bfd_reloc_code_real_type reloc_type;
+     bfd_reloc_code_real_type *reloc_type;
      boolean unmatched_hi;
 {
   register unsigned long prev_pinfo, pinfo;
   char *f;
-  fixS *fixp;
+  fixS *fixp[3];
   int nops = 0;
 
   /* Mark instruction labels in mips16 mode.  */
@@ -1843,16 +1851,16 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
        }
     }
 
-  if (reloc_type > BFD_RELOC_UNUSED)
+  if (*reloc_type > BFD_RELOC_UNUSED)
     {
       /* We need to set up a variant frag.  */
       assert (mips_opts.mips16 && address_expr != NULL);
       f = frag_var (rs_machine_dependent, 4, 0,
-                   RELAX_MIPS16_ENCODE (reloc_type - BFD_RELOC_UNUSED,
+                   RELAX_MIPS16_ENCODE (*reloc_type - BFD_RELOC_UNUSED,
                                         mips16_small, mips16_ext,
                                         (prev_pinfo
                                          & INSN_UNCOND_BRANCH_DELAY),
-                                        (prev_insn_reloc_type
+                                        (*prev_insn_reloc_type
                                          == BFD_RELOC_MIPS16_JMP)),
                    make_expr_symbol (address_expr), (offsetT) 0,
                    (char *) NULL);
@@ -1861,7 +1869,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
     f = place;
   else if (mips_opts.mips16
           && ! ip->use_extend
-          && reloc_type != BFD_RELOC_MIPS16_JMP)
+          && *reloc_type != BFD_RELOC_MIPS16_JMP)
     {
       /* Make sure there is enough room to swap this instruction with
          a following jump instruction.  */
@@ -1878,17 +1886,39 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
       f = frag_more (4);
     }
 
-  fixp = NULL;
-  if (address_expr != NULL && reloc_type < BFD_RELOC_UNUSED)
+  fixp[0] = fixp[1] = fixp[2] = NULL;
+  if (address_expr != NULL && *reloc_type < BFD_RELOC_UNUSED)
     {
       if (address_expr->X_op == O_constant)
        {
-         switch (reloc_type)
+         unsigned long tmp;
+
+         switch (*reloc_type)
            {
            case BFD_RELOC_32:
              ip->insn_opcode |= address_expr->X_add_number;
              break;
 
+           case BFD_RELOC_MIPS_HIGHEST:
+             tmp = (address_expr->X_add_number + 0x800080008000) >> 16;
+             tmp >>= 16;
+             ip->insn_opcode |= (tmp >> 16) & 0xffff;
+             break;
+
+           case BFD_RELOC_MIPS_HIGHER:
+             tmp = (address_expr->X_add_number + 0x80008000) >> 16;
+             ip->insn_opcode |= (tmp >> 16) & 0xffff;
+             break;
+
+           case BFD_RELOC_HI16_S:
+             ip->insn_opcode |= ((address_expr->X_add_number + 0x8000)
+                                 >> 16) & 0xffff;
+             break;
+
+           case BFD_RELOC_HI16:
+             ip->insn_opcode |= (address_expr->X_add_number >> 16) & 0xffff;
+             break;
+
            case BFD_RELOC_LO16:
              ip->insn_opcode |= address_expr->X_add_number & 0xffff;
              break;
@@ -1924,34 +1954,122 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
       else
        {
        need_reloc:
-         /* Don't generate a reloc if we are writing into a variant
-            frag.  */
+         /* Don't generate a reloc if we are writing into a variant frag.  */
          if (place == NULL)
            {
-             fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 4,
-                                 address_expr,
-                                 (reloc_type == BFD_RELOC_16_PCREL
-                                  || reloc_type == BFD_RELOC_16_PCREL_S2),
-                                 reloc_type);
+             fixp[0] = fix_new_exp (frag_now, f - frag_now->fr_literal, 4,
+                                    address_expr,
+                                    (*reloc_type == BFD_RELOC_16_PCREL
+                                     || *reloc_type == BFD_RELOC_16_PCREL_S2),
+                                    reloc_type[0]);
+
+             /* These relocations can have a addend that won't fit in
+                4 octets for 64bit assembly.  */
+             if (HAVE_64BIT_GPRS &&
+                 (*reloc_type == BFD_RELOC_16
+                 || *reloc_type == BFD_RELOC_32
+                 || *reloc_type == BFD_RELOC_MIPS_JMP
+                 || *reloc_type == BFD_RELOC_HI16_S
+                 || *reloc_type == BFD_RELOC_LO16
+                 || *reloc_type == BFD_RELOC_GPREL16
+                 || *reloc_type == BFD_RELOC_MIPS_LITERAL
+                 || *reloc_type == BFD_RELOC_GPREL32
+                 || *reloc_type == BFD_RELOC_64
+                 || *reloc_type == BFD_RELOC_CTOR
+                 || *reloc_type == BFD_RELOC_MIPS_SUB
+                 || *reloc_type == BFD_RELOC_MIPS_HIGHEST
+                 || *reloc_type == BFD_RELOC_MIPS_HIGHER
+                 || *reloc_type == BFD_RELOC_MIPS_SCN_DISP
+                 || *reloc_type == BFD_RELOC_MIPS_REL16
+                 || *reloc_type == BFD_RELOC_MIPS_RELGOT))
+               fixp[0]->fx_no_overflow = 1;
+
              if (unmatched_hi)
                {
                  struct mips_hi_fixup *hi_fixup;
 
-                 assert (reloc_type == BFD_RELOC_HI16_S);
+                 assert (*reloc_type == BFD_RELOC_HI16_S);
                  hi_fixup = ((struct mips_hi_fixup *)
                              xmalloc (sizeof (struct mips_hi_fixup)));
-                 hi_fixup->fixp = fixp;
+                 hi_fixup->fixp = fixp[0];
                  hi_fixup->seg = now_seg;
                  hi_fixup->next = mips_hi_fixup_list;
                  mips_hi_fixup_list = hi_fixup;
                }
+
+             if (reloc_type[1] != BFD_RELOC_UNUSED)
+               {
+                 /* FIXME: This symbol can be one of
+                    RSS_UNDEF, RSS_GP, RSS_GP0, RSS_LOC.  */
+                 address_expr->X_op = O_absent;
+                 address_expr->X_add_symbol = 0;
+                 address_expr->X_add_number = 0;
+
+                 fixp[1] = fix_new_exp (frag_now, f - frag_now->fr_literal,
+                                        4, address_expr, false,
+                                        reloc_type[1]);
+
+                 /* These relocations can have a addend that won't fit in
+                    4 octets for 64bit assembly.  */
+                 if (HAVE_64BIT_GPRS &&
+                     (*reloc_type == BFD_RELOC_16
+                      || *reloc_type == BFD_RELOC_32
+                      || *reloc_type == BFD_RELOC_MIPS_JMP
+                      || *reloc_type == BFD_RELOC_HI16_S
+                      || *reloc_type == BFD_RELOC_LO16
+                      || *reloc_type == BFD_RELOC_GPREL16
+                      || *reloc_type == BFD_RELOC_MIPS_LITERAL
+                      || *reloc_type == BFD_RELOC_GPREL32
+                      || *reloc_type == BFD_RELOC_64
+                      || *reloc_type == BFD_RELOC_CTOR
+                      || *reloc_type == BFD_RELOC_MIPS_SUB
+                      || *reloc_type == BFD_RELOC_MIPS_HIGHEST
+                      || *reloc_type == BFD_RELOC_MIPS_HIGHER
+                      || *reloc_type == BFD_RELOC_MIPS_SCN_DISP
+                      || *reloc_type == BFD_RELOC_MIPS_REL16
+                      || *reloc_type == BFD_RELOC_MIPS_RELGOT))
+                    fixp[1]->fx_no_overflow = 1;
+
+                 if (reloc_type[2] != BFD_RELOC_UNUSED)
+                   {
+                     address_expr->X_op = O_absent;
+                     address_expr->X_add_symbol = 0;
+                     address_expr->X_add_number = 0;
+
+                     fixp[2] = fix_new_exp (frag_now,
+                                            f - frag_now->fr_literal, 4,
+                                            address_expr, false,
+                                            reloc_type[2]);
+
+                     /* These relocations can have a addend that won't fit in
+                        4 octets for 64bit assembly.  */
+                     if (HAVE_64BIT_GPRS &&
+                         (*reloc_type == BFD_RELOC_16
+                          || *reloc_type == BFD_RELOC_32
+                          || *reloc_type == BFD_RELOC_MIPS_JMP
+                          || *reloc_type == BFD_RELOC_HI16_S
+                          || *reloc_type == BFD_RELOC_LO16
+                          || *reloc_type == BFD_RELOC_GPREL16
+                          || *reloc_type == BFD_RELOC_MIPS_LITERAL
+                          || *reloc_type == BFD_RELOC_GPREL32
+                          || *reloc_type == BFD_RELOC_64
+                          || *reloc_type == BFD_RELOC_CTOR
+                          || *reloc_type == BFD_RELOC_MIPS_SUB
+                          || *reloc_type == BFD_RELOC_MIPS_HIGHEST
+                          || *reloc_type == BFD_RELOC_MIPS_HIGHER
+                          || *reloc_type == BFD_RELOC_MIPS_SCN_DISP
+                          || *reloc_type == BFD_RELOC_MIPS_REL16
+                          || *reloc_type == BFD_RELOC_MIPS_RELGOT))
+                      fixp[2]->fx_no_overflow = 1;
+                   }
+               }
            }
        }
     }
 
   if (! mips_opts.mips16)
     md_number_to_chars (f, ip->insn_opcode, 4);
-  else if (reloc_type == BFD_RELOC_MIPS16_JMP)
+  else if (*reloc_type == BFD_RELOC_MIPS16_JMP)
     {
       md_number_to_chars (f, ip->insn_opcode >> 16, 2);
       md_number_to_chars (f + 2, ip->insn_opcode & 0xffff, 2);
@@ -2235,7 +2353,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
              /* If the previous instruction had a fixup in mips16
                  mode, we can not swap.  This normally means that the
                  previous instruction was a 4 byte branch anyhow.  */
-             || (mips_opts.mips16 && prev_insn_fixp)
+             || (mips_opts.mips16 && prev_insn_fixp[0])
              /* If the previous instruction is a sync, sync.l, or
                 sync.p, we can not swap.  */
              || (prev_pinfo & INSN_SYNC))
@@ -2261,15 +2379,35 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                  memcpy (temp, prev_f, 4);
                  memcpy (prev_f, f, 4);
                  memcpy (f, temp, 4);
-                 if (prev_insn_fixp)
+                 if (prev_insn_fixp[0])
+                   {
+                     prev_insn_fixp[0]->fx_frag = frag_now;
+                     prev_insn_fixp[0]->fx_where = f - frag_now->fr_literal;
+                   }
+                 if (prev_insn_fixp[1])
+                   {
+                     prev_insn_fixp[1]->fx_frag = frag_now;
+                     prev_insn_fixp[1]->fx_where = f - frag_now->fr_literal;
+                   }
+                 if (prev_insn_fixp[2])
                    {
-                     prev_insn_fixp->fx_frag = frag_now;
-                     prev_insn_fixp->fx_where = f - frag_now->fr_literal;
+                     prev_insn_fixp[2]->fx_frag = frag_now;
+                     prev_insn_fixp[2]->fx_where = f - frag_now->fr_literal;
                    }
-                 if (fixp)
+                 if (fixp[0])
                    {
-                     fixp->fx_frag = prev_insn_frag;
-                     fixp->fx_where = prev_insn_where;
+                     fixp[0]->fx_frag = prev_insn_frag;
+                     fixp[0]->fx_where = prev_insn_where;
+                   }
+                 if (fixp[1])
+                   {
+                     fixp[1]->fx_frag = prev_insn_frag;
+                     fixp[1]->fx_where = prev_insn_where;
+                   }
+                 if (fixp[2])
+                   {
+                     fixp[2]->fx_frag = prev_insn_frag;
+                     fixp[2]->fx_where = prev_insn_where;
                    }
                }
              else
@@ -2277,13 +2415,15 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                  char *prev_f;
                  char temp[2];
 
-                 assert (prev_insn_fixp == NULL);
+                 assert (prev_insn_fixp[0] == NULL);
+                 assert (prev_insn_fixp[1] == NULL);
+                 assert (prev_insn_fixp[2] == NULL);
                  prev_f = prev_insn_frag->fr_literal + prev_insn_where;
                  memcpy (temp, prev_f, 2);
                  memcpy (prev_f, f, 2);
-                 if (reloc_type != BFD_RELOC_MIPS16_JMP)
+                 if (*reloc_type != BFD_RELOC_MIPS16_JMP)
                    {
-                     assert (reloc_type == BFD_RELOC_UNUSED);
+                     assert (*reloc_type == BFD_RELOC_UNUSED);
                      memcpy (f, temp, 2);
                    }
                  else
@@ -2291,10 +2431,20 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                      memcpy (f, f + 2, 2);
                      memcpy (f + 2, temp, 2);
                    }
-                 if (fixp)
+                 if (fixp[0])
+                   {
+                     fixp[0]->fx_frag = prev_insn_frag;
+                     fixp[0]->fx_where = prev_insn_where;
+                   }
+                 if (fixp[1])
+                   {
+                     fixp[1]->fx_frag = prev_insn_frag;
+                     fixp[1]->fx_where = prev_insn_where;
+                   }
+                 if (fixp[2])
                    {
-                     fixp->fx_frag = prev_insn_frag;
-                     fixp->fx_where = prev_insn_where;
+                     fixp[2]->fx_frag = prev_insn_frag;
+                     fixp[2]->fx_where = prev_insn_where;
                    }
                }
 
@@ -2312,8 +2462,12 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
              prev_insn.insn_mo = &dummy_opcode;
            }
 
-         prev_insn_fixp = NULL;
-         prev_insn_reloc_type = BFD_RELOC_UNUSED;
+         prev_insn_fixp[0] = NULL;
+         prev_insn_fixp[1] = NULL;
+         prev_insn_fixp[2] = NULL;
+         prev_insn_reloc_type[0] = BFD_RELOC_UNUSED;
+         prev_insn_reloc_type[1] = BFD_RELOC_UNUSED;
+         prev_insn_reloc_type[2] = BFD_RELOC_UNUSED;
          prev_insn_extended = 0;
        }
       else if (pinfo & INSN_COND_BRANCH_LIKELY)
@@ -2326,8 +2480,12 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
          /* Update the previous insn information.  */
          prev_prev_insn = *ip;
          prev_insn.insn_mo = &dummy_opcode;
-         prev_insn_fixp = NULL;
-         prev_insn_reloc_type = BFD_RELOC_UNUSED;
+         prev_insn_fixp[0] = NULL;
+         prev_insn_fixp[1] = NULL;
+         prev_insn_fixp[2] = NULL;
+         prev_insn_reloc_type[0] = BFD_RELOC_UNUSED;
+         prev_insn_reloc_type[1] = BFD_RELOC_UNUSED;
+         prev_insn_reloc_type[2] = BFD_RELOC_UNUSED;
          prev_insn_extended = 0;
        }
       else
@@ -2344,11 +2502,15 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
             is not in a delay slot.  */
          prev_insn_is_delay_slot = 0;
 
-         prev_insn_fixp = fixp;
-         prev_insn_reloc_type = reloc_type;
+         prev_insn_fixp[0] = fixp[0];
+         prev_insn_fixp[1] = fixp[1];
+         prev_insn_fixp[2] = fixp[2];
+         prev_insn_reloc_type[0] = reloc_type[0];
+         prev_insn_reloc_type[1] = reloc_type[1];
+         prev_insn_reloc_type[2] = reloc_type[2];
          if (mips_opts.mips16)
            prev_insn_extended = (ip->use_extend
-                                 || reloc_type > BFD_RELOC_UNUSED);
+                                 || *reloc_type > BFD_RELOC_UNUSED);
        }
 
       prev_prev_insn_unreordered = prev_insn_unreordered;
@@ -2364,7 +2526,9 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
          PC relative relocs.  */
       prev_prev_insn = prev_insn;
       prev_insn = *ip;
-      prev_insn_reloc_type = reloc_type;
+      prev_insn_reloc_type[0] = reloc_type[0];
+      prev_insn_reloc_type[1] = reloc_type[1];
+      prev_insn_reloc_type[2] = reloc_type[2];
       prev_prev_insn_unreordered = prev_insn_unreordered;
       prev_insn_unreordered = 1;
     }
@@ -2404,7 +2568,9 @@ mips_no_prev_insn (preserve)
   prev_insn_is_delay_slot = 0;
   prev_insn_unreordered = 0;
   prev_insn_extended = 0;
-  prev_insn_reloc_type = BFD_RELOC_UNUSED;
+  prev_insn_reloc_type[0] = BFD_RELOC_UNUSED;
+  prev_insn_reloc_type[1] = BFD_RELOC_UNUSED;
+  prev_insn_reloc_type[2] = BFD_RELOC_UNUSED;
   prev_prev_insn_unreordered = 0;
   mips_clear_insn_labels ();
 }
@@ -2544,7 +2710,7 @@ macro_build (place, counter, ep, name, fmt, va_alist)
 #endif
 {
   struct mips_cl_insn insn;
-  bfd_reloc_code_real_type r;
+  bfd_reloc_code_real_type r[3];
   va_list args;
 
 #ifdef USE_STDARG
@@ -2571,7 +2737,9 @@ macro_build (place, counter, ep, name, fmt, va_alist)
       return;
     }
 
-  r = BFD_RELOC_UNUSED;
+  r[0] = BFD_RELOC_UNUSED;
+  r[1] = BFD_RELOC_UNUSED;
+  r[2] = BFD_RELOC_UNUSED;
   insn.insn_mo = (struct mips_opcode *) hash_find (op_hash, name);
   assert (insn.insn_mo);
   assert (strcmp (name, insn.insn_mo->name) == 0);
@@ -2670,35 +2838,33 @@ macro_build (place, counter, ep, name, fmt, va_alist)
        case 'i':
        case 'j':
        case 'o':
-         r = (bfd_reloc_code_real_type) va_arg (args, int);
-         assert (r == BFD_RELOC_MIPS_GPREL
-                 || r == BFD_RELOC_MIPS_LITERAL
-                 || r == BFD_RELOC_LO16
-                 || r == BFD_RELOC_MIPS_GOT16
-                 || r == BFD_RELOC_MIPS_CALL16
-                 || r == BFD_RELOC_MIPS_GOT_LO16
-                 || r == BFD_RELOC_MIPS_CALL_LO16
+         *r = (bfd_reloc_code_real_type) va_arg (args, int);
+         assert (*r == BFD_RELOC_MIPS_GPREL
+                 || *r == BFD_RELOC_MIPS_LITERAL
+                 || *r == BFD_RELOC_MIPS_HIGHER
+                 || *r == BFD_RELOC_HI16_S
+                 || *r == BFD_RELOC_LO16
+                 || *r == BFD_RELOC_MIPS_GOT16
+                 || *r == BFD_RELOC_MIPS_CALL16
+                 || *r == BFD_RELOC_MIPS_GOT_LO16
+                 || *r == BFD_RELOC_MIPS_CALL_LO16
                  || (ep->X_op == O_subtract
-                     && r == BFD_RELOC_PCREL_LO16));
+                     && *r == BFD_RELOC_PCREL_LO16));
          continue;
 
        case 'u':
-         r = (bfd_reloc_code_real_type) va_arg (args, int);
+         *r = (bfd_reloc_code_real_type) va_arg (args, int);
          assert (ep != NULL
                  && (ep->X_op == O_constant
                      || (ep->X_op == O_symbol
-                         && (r == BFD_RELOC_HI16_S
-                             || r == BFD_RELOC_HI16
-                             || r == BFD_RELOC_MIPS_GOT_HI16
-                             || r == BFD_RELOC_MIPS_CALL_HI16))
+                         && (*r == BFD_RELOC_MIPS_HIGHEST
+                             || *r == BFD_RELOC_HI16_S
+                             || *r == BFD_RELOC_HI16
+                             || *r == BFD_RELOC_GPREL16
+                             || *r == BFD_RELOC_MIPS_GOT_HI16
+                             || *r == BFD_RELOC_MIPS_CALL_HI16))
                      || (ep->X_op == O_subtract
-                         && r == BFD_RELOC_PCREL_HI16_S)));
-         if (ep->X_op == O_constant)
-           {
-             insn.insn_opcode |= (ep->X_add_number >> 16) & 0xffff;
-             ep = NULL;
-             r = BFD_RELOC_UNUSED;
-           }
+                         && *r == BFD_RELOC_PCREL_HI16_S)));
          continue;
 
        case 'p':
@@ -2717,14 +2883,14 @@ macro_build (place, counter, ep, name, fmt, va_alist)
            }
          else
            if (mips_pic == EMBEDDED_PIC)
-             r = BFD_RELOC_16_PCREL_S2;
+             *r = BFD_RELOC_16_PCREL_S2;
            else
-             r = BFD_RELOC_16_PCREL;
+             *r = BFD_RELOC_16_PCREL;
          continue;
 
        case 'a':
          assert (ep != NULL);
-         r = BFD_RELOC_MIPS_JMP;
+         *r = BFD_RELOC_MIPS_JMP;
          continue;
 
        case 'C':
@@ -2737,7 +2903,7 @@ macro_build (place, counter, ep, name, fmt, va_alist)
       break;
     }
   va_end (args);
-  assert (r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
+  assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
 
   append_insn (place, &insn, ep, r, false);
 }
@@ -2752,9 +2918,9 @@ mips16_macro_build (place, counter, ep, name, fmt, args)
      va_list args;
 {
   struct mips_cl_insn insn;
-  bfd_reloc_code_real_type r;
+  bfd_reloc_code_real_type r[3]
+    = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
 
-  r = BFD_RELOC_UNUSED;
   insn.insn_mo = (struct mips_opcode *) hash_find (mips16_op_hash, name);
   assert (insn.insn_mo);
   assert (strcmp (name, insn.insn_mo->name) == 0);
@@ -2843,14 +3009,14 @@ mips16_macro_build (place, counter, ep, name, fmt, args)
            assert (ep != NULL);
 
            if (ep->X_op != O_constant)
-             r = BFD_RELOC_UNUSED + c;
+             *r = BFD_RELOC_UNUSED + c;
            else
              {
                mips16_immed ((char *) NULL, 0, c, ep->X_add_number, false,
                              false, false, &insn.insn_opcode,
                              &insn.use_extend, &insn.extend);
                ep = NULL;
-               r = BFD_RELOC_UNUSED;
+               *r = BFD_RELOC_UNUSED;
              }
          }
          continue;
@@ -2863,7 +3029,7 @@ mips16_macro_build (place, counter, ep, name, fmt, args)
       break;
     }
 
-  assert (r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
+  assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
 
   append_insn (place, &insn, ep, r, false);
 }
@@ -2880,7 +3046,8 @@ macro_build_lui (place, counter, ep, regnum)
 {
   expressionS high_expr;
   struct mips_cl_insn insn;
-  bfd_reloc_code_real_type r;
+  bfd_reloc_code_real_type r[3]
+    = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
   CONST char *name = "lui";
   CONST char *fmt = "t,u";
 
@@ -2899,15 +3066,15 @@ macro_build_lui (place, counter, ep, regnum)
       /* we can compute the instruction now without a relocation entry */
       high_expr.X_add_number = ((high_expr.X_add_number + 0x8000)
                                >> 16) & 0xffff;
-      r = BFD_RELOC_UNUSED;
+      *r = BFD_RELOC_UNUSED;
     }
-  else
+  else if (! HAVE_NEWABI)
     {
       assert (ep->X_op == O_symbol);
       /* _gp_disp is a special case, used from s_cpload.  */
       assert (mips_pic == NO_PIC
              || strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0);
-      r = BFD_RELOC_HI16_S;
+      *r = BFD_RELOC_HI16_S;
     }
 
   /*
@@ -2927,7 +3094,7 @@ macro_build_lui (place, counter, ep, regnum)
   assert (strcmp (fmt, insn.insn_mo->args) == 0);
 
   insn.insn_opcode = insn.insn_mo->match | (regnum << OP_SH_RT);
-  if (r == BFD_RELOC_UNUSED)
+  if (*r == BFD_RELOC_UNUSED)
     {
       insn.insn_opcode |= high_expr.X_add_number;
       append_insn (place, &insn, NULL, r, false);
@@ -7492,7 +7659,7 @@ mips_ip (str, ip)
 
            case 'A':
              my_getExpression (&offset_expr, s);
-             imm_reloc = BFD_RELOC_32;
+             *imm_reloc = BFD_RELOC_32;
              s = expr_end;
              continue;
 
@@ -7704,7 +7871,7 @@ mips_ip (str, ip)
 
            case 'i':           /* 16 bit unsigned immediate */
            case 'j':           /* 16 bit signed immediate */
-             imm_reloc = BFD_RELOC_LO16;
+             *imm_reloc = BFD_RELOC_LO16;
              c = my_getSmallExpression (&imm_expr, s);
              if (c != S_EX_NONE)
                {
@@ -7714,16 +7881,16 @@ mips_ip (str, ip)
                        imm_expr.X_add_number =
                          (imm_expr.X_add_number >> 16) & 0xffff;
                      else if (c == S_EX_HIGHEST)
-                         imm_reloc = BFD_RELOC_MIPS_HIGHEST;
+                         *imm_reloc = BFD_RELOC_MIPS_HIGHEST;
                      else if (c == S_EX_HIGHER)
-                         imm_reloc = BFD_RELOC_MIPS_HIGHER;
+                         *imm_reloc = BFD_RELOC_MIPS_HIGHER;
                      else if (c == S_EX_HI)
                        {
-                         imm_reloc = BFD_RELOC_HI16_S;
+                         *imm_reloc = BFD_RELOC_HI16_S;
                          imm_unmatched_hi = true;
                        }
                      else
-                       imm_reloc = BFD_RELOC_HI16;
+                       *imm_reloc = BFD_RELOC_HI16;
                    }
                  else if (imm_expr.X_op == O_constant)
                    imm_expr.X_add_number &= 0xffff;
@@ -7814,22 +7981,22 @@ mips_ip (str, ip)
                  offset_expr.X_add_number =
                    (offset_expr.X_add_number >> 16) & 0xffff;
                }
-             offset_reloc = BFD_RELOC_LO16;
+             *offset_reloc = BFD_RELOC_LO16;
              s = expr_end;
              continue;
 
            case 'p':           /* pc relative offset */
              if (mips_pic == EMBEDDED_PIC)
-               offset_reloc = BFD_RELOC_16_PCREL_S2;
+               *offset_reloc = BFD_RELOC_16_PCREL_S2;
              else
-               offset_reloc = BFD_RELOC_16_PCREL;
+               *offset_reloc = BFD_RELOC_16_PCREL;
              my_getExpression (&offset_expr, s);
              s = expr_end;
              continue;
 
            case 'u':           /* upper 16 bits */
              c = my_getSmallExpression (&imm_expr, s);
-             imm_reloc = BFD_RELOC_LO16;
+             *imm_reloc = BFD_RELOC_LO16;
              if (c != S_EX_NONE)
                {
                  if (c != S_EX_LO)
@@ -7838,14 +8005,14 @@ mips_ip (str, ip)
                        imm_expr.X_add_number =
                          (imm_expr.X_add_number >> 16) & 0xffff;
                      else if (c == S_EX_HIGHEST)
-                         imm_reloc = BFD_RELOC_MIPS_HIGHEST;
+                         *imm_reloc = BFD_RELOC_MIPS_HIGHEST;
                      else if (c == S_EX_HI)
                        {
-                         imm_reloc = BFD_RELOC_HI16_S;
+                         *imm_reloc = BFD_RELOC_HI16_S;
                          imm_unmatched_hi = true;
                        }
                      else
-                       imm_reloc = BFD_RELOC_HI16;
+                       *imm_reloc = BFD_RELOC_HI16;
                    }
                  else if (imm_expr.X_op == O_constant)
                    imm_expr.X_add_number &= 0xffff;
@@ -7860,7 +8027,7 @@ mips_ip (str, ip)
            case 'a':           /* 26 bit address */
              my_getExpression (&offset_expr, s);
              s = expr_end;
-             offset_reloc = BFD_RELOC_MIPS_JMP;
+             *offset_reloc = BFD_RELOC_MIPS_JMP;
              continue;
 
            case 'N':           /* 3 bit branch condition code */
@@ -8003,9 +8170,13 @@ mips16_ip (str, ip)
       ip->insn_opcode = insn->match;
       ip->use_extend = false;
       imm_expr.X_op = O_absent;
-      imm_reloc = BFD_RELOC_UNUSED;
+      imm_reloc[0] = BFD_RELOC_UNUSED;
+      imm_reloc[1] = BFD_RELOC_UNUSED;
+      imm_reloc[2] = BFD_RELOC_UNUSED;
       offset_expr.X_op = O_absent;
-      offset_reloc = BFD_RELOC_UNUSED;
+      offset_reloc[0] = BFD_RELOC_UNUSED;
+      offset_reloc[1] = BFD_RELOC_UNUSED;
+      offset_reloc[2] = BFD_RELOC_UNUSED;
       for (args = insn->args; 1; ++args)
        {
          int c;
@@ -8025,16 +8196,16 @@ mips16_ip (str, ip)
                {
                  /* Stuff the immediate value in now, if we can.  */
                  if (imm_expr.X_op == O_constant
-                     && imm_reloc > BFD_RELOC_UNUSED
+                     && *imm_reloc > BFD_RELOC_UNUSED
                      && insn->pinfo != INSN_MACRO)
                    {
                      mips16_immed ((char *) NULL, 0,
-                                   imm_reloc - BFD_RELOC_UNUSED,
+                                   *imm_reloc - BFD_RELOC_UNUSED,
                                    imm_expr.X_add_number, true, mips16_small,
                                    mips16_ext, &ip->insn_opcode,
                                    &ip->use_extend, &ip->extend);
                      imm_expr.X_op = O_absent;
-                     imm_reloc = BFD_RELOC_UNUSED;
+                     *imm_reloc = BFD_RELOC_UNUSED;
                    }
 
                  return;
@@ -8259,7 +8430,7 @@ mips16_ip (str, ip)
                  if (imm_expr.X_op == O_symbol)
                    {
                      mips16_ext = true;
-                     imm_reloc = BFD_RELOC_MIPS16_GPREL;
+                     *imm_reloc = BFD_RELOC_MIPS16_GPREL;
                      s = expr_end;
                      ip->use_extend = true;
                      ip->extend = 0;
@@ -8286,7 +8457,7 @@ mips16_ip (str, ip)
                         explicit extensions correctly.  */
                      imm_expr.X_op = O_constant;
                      imm_expr.X_add_number = 0;
-                     imm_reloc = (int) BFD_RELOC_UNUSED + c;
+                     *imm_reloc = (int) BFD_RELOC_UNUSED + c;
                      continue;
                    }
 
@@ -8294,7 +8465,7 @@ mips16_ip (str, ip)
                }
 
              /* We need to relax this instruction.  */
-             imm_reloc = (int) BFD_RELOC_UNUSED + c;
+             *imm_reloc = (int) BFD_RELOC_UNUSED + c;
              s = expr_end;
              continue;
 
@@ -8312,7 +8483,7 @@ mips16_ip (str, ip)
                break;
 
              /* We need to relax this instruction.  */
-             offset_reloc = (int) BFD_RELOC_UNUSED + c;
+             *offset_reloc = (int) BFD_RELOC_UNUSED + c;
              s = expr_end;
              continue;
 
@@ -8334,7 +8505,7 @@ mips16_ip (str, ip)
            case 'a':           /* 26 bit address */
              my_getExpression (&offset_expr, s);
              s = expr_end;
-             offset_reloc = BFD_RELOC_MIPS16_JMP;
+             *offset_reloc = BFD_RELOC_MIPS16_JMP;
              ip->insn_opcode <<= 16;
              continue;
 
@@ -9590,7 +9761,10 @@ mips_frob_file ()
 
 /* When generating embedded PIC code we must keep all PC relative
    relocations, in case the linker has to relax a call.  We also need
-   to keep relocations for switch table entries.  */
+   to keep relocations for switch table entries.
+
+   We may have combined relocations without symbols in the N32/N64 ABI.
+   We have to prevent gas from dropping them.  */
 
 int
 mips_force_relocation (fixp)
@@ -9600,6 +9774,13 @@ mips_force_relocation (fixp)
       || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
     return 1;
 
+  if (HAVE_NEWABI
+      && S_GET_SEGMENT (fixp->fx_addsy) == bfd_abs_section_ptr
+      && (fixp->fx_r_type == BFD_RELOC_MIPS_SUB
+         || fixp->fx_r_type == BFD_RELOC_HI16_S
+         || fixp->fx_r_type == BFD_RELOC_LO16))
+    return 1;
+
   return (mips_pic == EMBEDDED_PIC
          && (fixp->fx_pcrel
              || SWITCH_TABLE (fixp)
@@ -9620,7 +9801,21 @@ md_apply_fix (fixP, valueP)
 
   assert (fixP->fx_size == 4
          || fixP->fx_r_type == BFD_RELOC_16
+         || fixP->fx_r_type == BFD_RELOC_32
+         || fixP->fx_r_type == BFD_RELOC_MIPS_JMP
+         || fixP->fx_r_type == BFD_RELOC_HI16_S
+         || fixP->fx_r_type == BFD_RELOC_LO16
+         || fixP->fx_r_type == BFD_RELOC_GPREL16
+         || fixP->fx_r_type == BFD_RELOC_MIPS_LITERAL 
+         || fixP->fx_r_type == BFD_RELOC_GPREL32
          || fixP->fx_r_type == BFD_RELOC_64
+         || fixP->fx_r_type == BFD_RELOC_CTOR
+         || fixP->fx_r_type == BFD_RELOC_MIPS_SUB
+         || fixP->fx_r_type == BFD_RELOC_MIPS_HIGHEST
+         || fixP->fx_r_type == BFD_RELOC_MIPS_HIGHER
+         || fixP->fx_r_type == BFD_RELOC_MIPS_SCN_DISP
+         || fixP->fx_r_type == BFD_RELOC_MIPS_REL16
+         || fixP->fx_r_type == BFD_RELOC_MIPS_RELGOT
          || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
          || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY);