+/* 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.
+
+ We accept BFD_RELOC_16_PCREL_S2 relocations against MIPS16 and microMIPS
+ symbols or BFD_RELOC_MICROMIPS_16_PCREL_S1 relocations against regular
+ MIPS symbols and associated with BAL instructions as these instructions
+ may be be converted to JALX by the linker. */
+
+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:
+ return ((absolute_p ? val & 1 : ELF_ST_IS_COMPRESSED (other))
+ && opcode != 0x0411);
+ case BFD_RELOC_MICROMIPS_16_PCREL_S1:
+ return ((absolute_p ? !(val & 1) : !ELF_ST_IS_MICROMIPS (other))
+ && opcode != 0x4060);
+ 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:
+ 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);
+}
+