MIPS: Verify the ISA mode and alignment of branch and jump targets
[deliverable/binutils-gdb.git] / gas / config / tc-mips.c
index cca545052dc67fb862e0cb34716bad32642fd4ec..57c2fe994bb0c214b25944418b65f9a836a60094 100644 (file)
@@ -14800,6 +14800,19 @@ mips_force_relocation (fixS *fixp)
       || fixp->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1)
     return 1;
 
+  /* We want to keep R_MIPS_PC26_S2, R_MIPS_PC21_S2, BFD_RELOC_16_PCREL_S2
+     BFD_RELOC_MIPS_21_PCREL_S2 and BFD_RELOC_MIPS_26_PCREL_S2 relocations
+     against MIPS16 and microMIPS symbols so that we do cross-mode branch
+     diagnostics.  */
+  if ((fixp->fx_r_type == R_MIPS_PC26_S2
+       || fixp->fx_r_type == R_MIPS_PC21_S2
+       || fixp->fx_r_type == BFD_RELOC_16_PCREL_S2
+       || fixp->fx_r_type == BFD_RELOC_MIPS_21_PCREL_S2
+       || fixp->fx_r_type == BFD_RELOC_MIPS_26_PCREL_S2)
+      && fixp->fx_addsy
+      && ELF_ST_IS_COMPRESSED (S_GET_OTHER (fixp->fx_addsy)))
+    return 1;
+
   /* We want all PC-relative relocations to be kept for R6 relaxation.  */
   if (ISA_IS_R6 (file_mips_opts.isa)
       && (fixp->fx_r_type == BFD_RELOC_16_PCREL_S2
@@ -14854,6 +14867,186 @@ write_reloc_insn (char *buf, bfd_reloc_code_real_type reloc,
     write_insn (buf, insn);
 }
 
+/* Return TRUE if the instruction pointed to by FIXP is an invalid jump
+   to a symbol in another ISA mode, which cannot be converted to JALX.  */
+
+static bfd_boolean
+fix_bad_cross_mode_jump_p (fixS *fixP)
+{
+  unsigned long opcode;
+  int other;
+  char *buf;
+
+  if (!fixP->fx_addsy || S_FORCE_RELOC (fixP->fx_addsy, TRUE))
+    return FALSE;
+
+  other = S_GET_OTHER (fixP->fx_addsy);
+  buf = fixP->fx_frag->fr_literal + fixP->fx_where;
+  opcode = read_reloc_insn (buf, fixP->fx_r_type) >> 26;
+  switch (fixP->fx_r_type)
+    {
+    case BFD_RELOC_MIPS_JMP:
+      return opcode != 0x1d && opcode != 0x03 && ELF_ST_IS_COMPRESSED (other);
+    case BFD_RELOC_MICROMIPS_JMP:
+      return opcode != 0x3c && opcode != 0x3d && !ELF_ST_IS_MICROMIPS (other);
+    default:
+      return FALSE;
+    }
+}
+
+/* Return TRUE if the instruction pointed to by FIXP is an invalid JALX
+   jump to a symbol in the same ISA mode.  */
+
+static bfd_boolean
+fix_bad_same_mode_jalx_p (fixS *fixP)
+{
+  unsigned long opcode;
+  int other;
+  char *buf;
+
+  if (!fixP->fx_addsy || S_FORCE_RELOC (fixP->fx_addsy, TRUE))
+    return FALSE;
+
+  other = S_GET_OTHER (fixP->fx_addsy);
+  buf = fixP->fx_frag->fr_literal + fixP->fx_where;
+  opcode = read_reloc_insn (buf, fixP->fx_r_type) >> 26;
+  switch (fixP->fx_r_type)
+    {
+    case BFD_RELOC_MIPS_JMP:
+      return opcode == 0x1d && !ELF_ST_IS_COMPRESSED (other);
+    case BFD_RELOC_MIPS16_JMP:
+      return opcode == 0x07 && ELF_ST_IS_COMPRESSED (other);
+    case BFD_RELOC_MICROMIPS_JMP:
+      return opcode == 0x3c && ELF_ST_IS_COMPRESSED (other);
+    default:
+      return FALSE;
+    }
+}
+
+/* Return TRUE if the instruction pointed to by FIXP is an invalid jump
+   to a symbol whose value plus addend is not aligned according to the
+   ultimate (after linker relaxation) jump instruction's immediate field
+   requirement, either to (1 << SHIFT), or, for jumps from microMIPS to
+   regular MIPS code, to (1 << 2).  */
+
+static bfd_boolean
+fix_bad_misaligned_jump_p (fixS *fixP, int shift)
+{
+  bfd_boolean micro_to_mips_p;
+  valueT val;
+  int other;
+
+  if (!fixP->fx_addsy || S_FORCE_RELOC (fixP->fx_addsy, TRUE))
+    return FALSE;
+
+  other = S_GET_OTHER (fixP->fx_addsy);
+  val = S_GET_VALUE (fixP->fx_addsy) | ELF_ST_IS_COMPRESSED (other);
+  val += fixP->fx_offset;
+  micro_to_mips_p = (fixP->fx_r_type == BFD_RELOC_MICROMIPS_JMP
+                    && !ELF_ST_IS_MICROMIPS (other));
+  return ((val & ((1 << (micro_to_mips_p ? 2 : shift)) - 1))
+         != ELF_ST_IS_COMPRESSED (other));
+}
+
+/* Return TRUE if the instruction pointed to by FIXP is an invalid branch
+   to a symbol whose annotation indicates another ISA mode.  For absolute
+   symbols check the ISA bit instead.  */
+
+static bfd_boolean
+fix_bad_cross_mode_branch_p (fixS *fixP)
+{
+  bfd_boolean absolute_p;
+  unsigned long opcode;
+  asection *symsec;
+  valueT val;
+  int other;
+  char *buf;
+
+  if (!fixP->fx_addsy || S_FORCE_RELOC (fixP->fx_addsy, TRUE))
+    return FALSE;
+
+  symsec = S_GET_SEGMENT (fixP->fx_addsy);
+  absolute_p = bfd_is_abs_section (symsec);
+
+  val = S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset;
+  other = S_GET_OTHER (fixP->fx_addsy);
+
+  buf = fixP->fx_frag->fr_literal + fixP->fx_where;
+  opcode = read_reloc_insn (buf, fixP->fx_r_type) >> 16;
+  switch (fixP->fx_r_type)
+    {
+    case BFD_RELOC_16_PCREL_S2:
+    case BFD_RELOC_MIPS_21_PCREL_S2:
+    case BFD_RELOC_MIPS_26_PCREL_S2:
+      return absolute_p ? val & 1 : ELF_ST_IS_COMPRESSED (other);
+    case BFD_RELOC_MIPS16_16_PCREL_S1:
+      return absolute_p ? !(val & 1) : !ELF_ST_IS_MIPS16 (other);
+    case BFD_RELOC_MICROMIPS_7_PCREL_S1:
+    case BFD_RELOC_MICROMIPS_10_PCREL_S1:
+    case BFD_RELOC_MICROMIPS_16_PCREL_S1:
+      return absolute_p ? !(val & 1) : !ELF_ST_IS_MICROMIPS (other);
+    default:
+      abort ();
+    }
+}
+
+/* Return TRUE if the symbol plus addend associated with a regular MIPS
+   branch instruction pointed to by FIXP is not aligned according to the
+   branch instruction's immediate field requirement.  We need the addend
+   to preserve the ISA bit and also the sum must not have bit 2 set.  We
+   must explicitly OR in the ISA bit from symbol annotation as the bit
+   won't be set in the symbol's value then.  */
+
+static bfd_boolean
+fix_bad_misaligned_branch_p (fixS *fixP)
+{
+  bfd_boolean absolute_p;
+  asection *symsec;
+  valueT isa_bit;
+  valueT val;
+  valueT off;
+  int other;
+
+  if (!fixP->fx_addsy || S_FORCE_RELOC (fixP->fx_addsy, TRUE))
+    return FALSE;
+
+  symsec = S_GET_SEGMENT (fixP->fx_addsy);
+  absolute_p = bfd_is_abs_section (symsec);
+
+  val = S_GET_VALUE (fixP->fx_addsy);
+  other = S_GET_OTHER (fixP->fx_addsy);
+  off = fixP->fx_offset;
+
+  isa_bit = absolute_p ? (val + off) & 1 : ELF_ST_IS_COMPRESSED (other);
+  val |= ELF_ST_IS_COMPRESSED (other);
+  val += off;
+  return (val & 0x3) != isa_bit;
+}
+
+/* Make the necessary checks on a regular MIPS branch pointed to by FIXP
+   and its calculated value VAL.  */
+
+static void
+fix_validate_branch (fixS *fixP, valueT val)
+{
+  if (fixP->fx_done && (val & 0x3) != 0)
+    as_bad_where (fixP->fx_file, fixP->fx_line,
+                 _("branch to misaligned address (0x%lx)"),
+                 (long) (val + md_pcrel_from (fixP)));
+  else if (fix_bad_cross_mode_branch_p (fixP))
+    as_bad_where (fixP->fx_file, fixP->fx_line,
+                 _("branch to a symbol in another ISA mode"));
+  else if (fix_bad_misaligned_branch_p (fixP))
+    as_bad_where (fixP->fx_file, fixP->fx_line,
+                 _("branch to misaligned address (0x%lx)"),
+                 (long) (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset));
+  else if (HAVE_IN_PLACE_ADDENDS && (fixP->fx_offset & 0x3) != 0)
+    as_bad_where (fixP->fx_file, fixP->fx_line,
+                 _("cannot encode misaligned addend "
+                   "in the relocatable field (0x%lx)"),
+                 (long) fixP->fx_offset);
+}
+
 /* Apply a fixup to the object file.  */
 
 void
@@ -14962,6 +15155,40 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       break;
 
     case BFD_RELOC_MIPS_JMP:
+    case BFD_RELOC_MIPS16_JMP:
+    case BFD_RELOC_MICROMIPS_JMP:
+      {
+       int shift;
+
+       gas_assert (!fixP->fx_done);
+
+       /* Shift is 2, unusually, for microMIPS JALX.  */
+       if (fixP->fx_r_type == BFD_RELOC_MICROMIPS_JMP
+           && (read_compressed_insn (buf, 4) >> 26) != 0x3c)
+         shift = 1;
+       else
+         shift = 2;
+
+       if (fix_bad_cross_mode_jump_p (fixP))
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("jump to a symbol in another ISA mode"));
+       else if (fix_bad_same_mode_jalx_p (fixP))
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("JALX to a symbol in the same ISA mode"));
+       else if (fix_bad_misaligned_jump_p (fixP, shift))
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("jump to misaligned address (0x%lx)"),
+                       (long) (S_GET_VALUE (fixP->fx_addsy)
+                               + fixP->fx_offset));
+       else if (HAVE_IN_PLACE_ADDENDS
+                && (fixP->fx_offset & ((1 << shift) - 1)) != 0)
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("cannot encode misaligned addend "
+                         "in the relocatable field (0x%lx)"),
+                       (long) fixP->fx_offset);
+      }
+      /* Fall through.  */
+
     case BFD_RELOC_MIPS_SHIFT5:
     case BFD_RELOC_MIPS_SHIFT6:
     case BFD_RELOC_MIPS_GOT_DISP:
@@ -14997,8 +15224,6 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     case BFD_RELOC_MIPS16_HI16:
     case BFD_RELOC_MIPS16_HI16_S:
     case BFD_RELOC_MIPS16_LO16:
-    case BFD_RELOC_MIPS16_JMP:
-    case BFD_RELOC_MICROMIPS_JMP:
     case BFD_RELOC_MICROMIPS_GOT_DISP:
     case BFD_RELOC_MICROMIPS_GOT_PAGE:
     case BFD_RELOC_MICROMIPS_GOT_OFST:
@@ -15072,9 +15297,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       break;
 
     case BFD_RELOC_MIPS_21_PCREL_S2:
-      if ((*valP & 0x3) != 0)
-       as_bad_where (fixP->fx_file, fixP->fx_line,
-                     _("branch to misaligned address (%lx)"), (long) *valP);
+      fix_validate_branch (fixP, *valP);
       if (!fixP->fx_done)
        break;
 
@@ -15090,9 +15313,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       break;
 
     case BFD_RELOC_MIPS_26_PCREL_S2:
-      if ((*valP & 0x3) != 0)
-       as_bad_where (fixP->fx_file, fixP->fx_line,
-                     _("branch to misaligned address (%lx)"), (long) *valP);
+      fix_validate_branch (fixP, *valP);
       if (!fixP->fx_done)
        break;
 
@@ -15150,9 +15371,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       break;
 
     case BFD_RELOC_16_PCREL_S2:
-      if ((*valP & 0x3) != 0)
-       as_bad_where (fixP->fx_file, fixP->fx_line,
-                     _("branch to misaligned address (%lx)"), (long) *valP);
+      fix_validate_branch (fixP, *valP);
 
       /* We need to save the bits in the instruction since fixup_segment()
         might be deleting the relocation entry (i.e., a branch within
@@ -15205,6 +15424,21 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     case BFD_RELOC_MICROMIPS_10_PCREL_S1:
     case BFD_RELOC_MICROMIPS_16_PCREL_S1:
       gas_assert (!fixP->fx_done);
+      if (fix_bad_cross_mode_branch_p (fixP))
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("branch to a symbol in another ISA mode"));
+      else if (fixP->fx_addsy
+              && !S_FORCE_RELOC (fixP->fx_addsy, TRUE)
+              && !bfd_is_abs_section (S_GET_SEGMENT (fixP->fx_addsy))
+              && (fixP->fx_offset & 0x1) != 0)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("branch to misaligned address (0x%lx)"),
+                     (long) (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset));
+      else if (HAVE_IN_PLACE_ADDENDS && (fixP->fx_offset & 0x1) != 0)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("cannot encode misaligned addend "
+                       "in the relocatable field (0x%lx)"),
+                     (long) fixP->fx_offset);
       break;
 
     case BFD_RELOC_VTABLE_INHERIT:
@@ -17814,6 +18048,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
       offsetT val;
       char *buf;
       unsigned int user_length, length;
+      bfd_boolean need_reloc;
       unsigned long insn;
       bfd_boolean ext;
       segT symsec;
@@ -17823,6 +18058,13 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
 
       ext = RELAX_MIPS16_EXTENDED (fragp->fr_subtype);
       val = resolve_symbol_value (fragp->fr_symbol) + fragp->fr_offset;
+
+      symsec = S_GET_SEGMENT (fragp->fr_symbol);
+      need_reloc = (S_FORCE_RELOC (fragp->fr_symbol, TRUE)
+                   || (operand->root.type == OP_PCREL
+                       ? asec != symsec
+                       : !bfd_is_abs_section (symsec)));
+
       if (operand->root.type == OP_PCREL)
        {
          const struct mips_pcrel_operand *pcrel_op;
@@ -17835,6 +18077,16 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
              complicated; see mips16_extended_frag.  */
          if (pcrel_op->include_isa_bit)
            {
+             if (!need_reloc)
+               {
+                 if (!ELF_ST_IS_MIPS16 (S_GET_OTHER (fragp->fr_symbol)))
+                   as_bad_where (fragp->fr_file, fragp->fr_line,
+                                 _("branch to a symbol in another ISA mode"));
+                 else if ((fragp->fr_offset & 0x1) != 0)
+                   as_bad_where (fragp->fr_file, fragp->fr_line,
+                                 _("branch to misaligned address (0x%lx)"),
+                                 (long) val);
+               }
              addr += 2;
              if (ext)
                addr += 2;
@@ -17875,11 +18127,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
       else
        user_length = 0;
 
-      symsec = S_GET_SEGMENT (fragp->fr_symbol);
-      if (S_FORCE_RELOC (fragp->fr_symbol, TRUE)
-         || (operand->root.type == OP_PCREL
-             ? asec != symsec
-             : !bfd_is_abs_section (symsec)))
+      if (need_reloc)
        {
          bfd_reloc_code_real_type reloc = BFD_RELOC_NONE;
          expressionS exp;
This page took 0.032874 seconds and 4 git commands to generate.