/* Print mips instructions for GDB, the GNU debugger, or for objdump.
- Copyright (C) 1989-2015 Free Software Foundation, Inc.
+ Copyright (C) 1989-2019 Free Software Foundation, Inc.
Contributed by Nobuyuki Hikichi(hikichi@sra.co.jp).
This file is part of the GNU opcodes library.
MA 02110-1301, USA. */
#include "sysdep.h"
-#include "dis-asm.h"
+#include "disassemble.h"
#include "libiberty.h"
#include "opcode/mips.h"
#include "opintl.h"
+#include "elf-bfd.h"
+#include "elf/mips.h"
+#include "elfxx-mips.h"
/* FIXME: These are needed to figure out if the code is mips16 or
not. The low bit of the address is often a good indicator. No
#if !defined(EMBEDDED_ENV)
#define SYMTAB_AVAILABLE 1
-#include "elf-bfd.h"
-#include "elf/mips.h"
#endif
/* Mips instructions are at maximum this many bytes long. */
"c0_taglo", "c0_taghi", "c0_errorepc", "$31"
};
-static const struct mips_cp0sel_name mips_cp0sel_names_mipsr5900[] =
-{
- { 24, 2, "c0_iab" },
- { 24, 3, "c0_iabm" },
- { 24, 4, "c0_dab" },
- { 24, 5, "c0_dabm" },
- { 24, 6, "c0_dvb" },
- { 24, 7, "c0_dvbm" },
- { 25, 1, "c0_perfcnt,1" },
- { 25, 2, "c0_perfcnt,2" }
-};
-
static const char * const mips_cp0_names_mips3264[32] =
{
"c0_index", "c0_random", "c0_entrylo0", "c0_entrylo1",
{ "mips32r6", 1, bfd_mach_mipsisa32r6, CPU_MIPS32R6,
ISA_MIPS32R6,
(ASE_EVA | ASE_MSA | ASE_VIRT | ASE_XPA | ASE_MCU | ASE_MT | ASE_DSP
- | ASE_DSPR2),
+ | ASE_DSPR2 | ASE_DSPR3 | ASE_CRC | ASE_GINV),
mips_cp0_names_mips3264r2,
mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 },
{ "mips64r6", 1, bfd_mach_mipsisa64r6, CPU_MIPS64R6,
ISA_MIPS64R6,
(ASE_EVA | ASE_MSA | ASE_MSA64 | ASE_XPA | ASE_VIRT | ASE_VIRT64
- | ASE_MCU | ASE_MT | ASE_DSP | ASE_DSPR2),
+ | ASE_MCU | ASE_MT | ASE_DSP | ASE_DSPR2 | ASE_DSPR3 | ASE_CRC
+ | ASE_CRC64 | ASE_GINV),
+ mips_cp0_names_mips3264r2,
+ mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
+ mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 },
+
+ { "interaptiv-mr2", 1, bfd_mach_mips_interaptiv_mr2, CPU_INTERAPTIV_MR2,
+ ISA_MIPS32R3,
+ ASE_MT | ASE_EVA | ASE_DSP | ASE_DSPR2 | ASE_MIPS16E2 | ASE_MIPS16E2_MT,
mips_cp0_names_mips3264r2,
mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 },
NULL, 0, mips_cp1_names_numeric, mips_hwr_names_numeric },
{ "loongson2f", 1, bfd_mach_mips_loongson_2f, CPU_LOONGSON_2F,
- ISA_MIPS3 | INSN_LOONGSON_2F, 0, mips_cp0_names_numeric,
+ ISA_MIPS3 | INSN_LOONGSON_2F, ASE_LOONGSON_MMI, mips_cp0_names_numeric,
NULL, 0, mips_cp1_names_numeric, mips_hwr_names_numeric },
- { "loongson3a", 1, bfd_mach_mips_loongson_3a, CPU_LOONGSON_3A,
- ISA_MIPS64R2 | INSN_LOONGSON_3A, 0, mips_cp0_names_numeric,
- NULL, 0, mips_cp1_names_mips3264, mips_hwr_names_numeric },
+ /* The loongson3a is an alias of gs464 for compatibility */
+ { "loongson3a", 1, bfd_mach_mips_gs464, CPU_GS464,
+ ISA_MIPS64R2, ASE_LOONGSON_MMI | ASE_LOONGSON_CAM | ASE_LOONGSON_EXT,
+ mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips3264,
+ mips_hwr_names_numeric },
+
+ { "gs464", 1, bfd_mach_mips_gs464, CPU_GS464,
+ ISA_MIPS64R2, ASE_LOONGSON_MMI | ASE_LOONGSON_CAM | ASE_LOONGSON_EXT,
+ mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips3264,
+ mips_hwr_names_numeric },
+
+ { "gs464e", 1, bfd_mach_mips_gs464e, CPU_GS464E,
+ ISA_MIPS64R2, ASE_LOONGSON_MMI | ASE_LOONGSON_CAM | ASE_LOONGSON_EXT
+ | ASE_LOONGSON_EXT2, mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips3264,
+ mips_hwr_names_numeric },
+
+ { "gs264e", 1, bfd_mach_mips_gs464e, CPU_GS264E,
+ ISA_MIPS64R2, ASE_LOONGSON_MMI | ASE_LOONGSON_CAM | ASE_LOONGSON_EXT
+ | ASE_LOONGSON_EXT2 | ASE_MSA | ASE_MSA64, mips_cp0_names_numeric, NULL,
+ 0, mips_cp1_names_mips3264, mips_hwr_names_numeric },
{ "octeon", 1, bfd_mach_mips_octeon, CPU_OCTEON,
ISA_MIPS64R2 | INSN_OCTEON, 0, mips_cp0_names_numeric, NULL, 0,
/* This entry, mips16, is here only for ISA/processor selection; do
not print its name. */
- { "", 1, bfd_mach_mips16, CPU_MIPS16, ISA_MIPS3, 0,
+ { "", 1, bfd_mach_mips16, CPU_MIPS16, ISA_MIPS64,
+ ASE_MIPS16E2 | ASE_MIPS16E2_MT,
mips_cp0_names_numeric, NULL, 0, mips_cp1_names_numeric,
mips_hwr_names_numeric },
};
return 0;
}
+/* Convert ASE flags from .MIPS.abiflags to internal values. */
+
+static unsigned long
+mips_convert_abiflags_ases (unsigned long afl_ases)
+{
+ unsigned long opcode_ases = 0;
+
+ if (afl_ases & AFL_ASE_DSP)
+ opcode_ases |= ASE_DSP;
+ if (afl_ases & AFL_ASE_DSPR2)
+ opcode_ases |= ASE_DSPR2;
+ if (afl_ases & AFL_ASE_EVA)
+ opcode_ases |= ASE_EVA;
+ if (afl_ases & AFL_ASE_MCU)
+ opcode_ases |= ASE_MCU;
+ if (afl_ases & AFL_ASE_MDMX)
+ opcode_ases |= ASE_MDMX;
+ if (afl_ases & AFL_ASE_MIPS3D)
+ opcode_ases |= ASE_MIPS3D;
+ if (afl_ases & AFL_ASE_MT)
+ opcode_ases |= ASE_MT;
+ if (afl_ases & AFL_ASE_SMARTMIPS)
+ opcode_ases |= ASE_SMARTMIPS;
+ if (afl_ases & AFL_ASE_VIRT)
+ opcode_ases |= ASE_VIRT;
+ if (afl_ases & AFL_ASE_MSA)
+ opcode_ases |= ASE_MSA;
+ if (afl_ases & AFL_ASE_XPA)
+ opcode_ases |= ASE_XPA;
+ if (afl_ases & AFL_ASE_DSPR3)
+ opcode_ases |= ASE_DSPR3;
+ if (afl_ases & AFL_ASE_MIPS16E2)
+ opcode_ases |= ASE_MIPS16E2;
+ return opcode_ases;
+}
+
+/* Calculate combination ASE flags from regular ASE flags. */
+
+static unsigned long
+mips_calculate_combination_ases (int opcode_isa, unsigned long opcode_ases)
+{
+ unsigned long combination_ases = 0;
+
+ if ((opcode_ases & (ASE_XPA | ASE_VIRT)) == (ASE_XPA | ASE_VIRT))
+ combination_ases |= ASE_XPA_VIRT;
+ if ((opcode_ases & (ASE_MIPS16E2 | ASE_MT)) == (ASE_MIPS16E2 | ASE_MT))
+ combination_ases |= ASE_MIPS16E2_MT;
+ if ((opcode_ases & ASE_EVA)
+ && ((opcode_isa & INSN_ISA_MASK) == ISA_MIPS64R6
+ || (opcode_isa & INSN_ISA_MASK) == ISA_MIPS32R6))
+ combination_ases |= ASE_EVA_R6;
+ return combination_ases;
+}
+
static void
set_default_mips_dis_options (struct disassemble_info *info)
{
mips_hwr_names = mips_hwr_names_numeric;
no_aliases = 0;
- /* Update settings according to the ELF file header flags. */
- if (info->flavour == bfd_target_elf_flavour && info->section != NULL)
- {
- Elf_Internal_Ehdr *header;
-
- header = elf_elfheader (info->section->owner);
- /* If an ELF "newabi" binary, use the n32/(n)64 GPR names. */
- if (is_newabi (header))
- mips_gpr_names = mips_gpr_names_newabi;
- /* If a microMIPS binary, then don't use MIPS16 bindings. */
- micromips_ase = is_micromips (header);
- }
-
/* Set ISA, architecture, and cp0 register names as best we can. */
#if ! SYMTAB_AVAILABLE
/* This is running out on a target machine, not in a host tool.
mips_cp1_names = chosen_arch->cp1_names;
mips_hwr_names = chosen_arch->hwr_names;
}
-#endif
-}
-static void
-parse_mips_dis_option (const char *option, unsigned int len)
-{
- unsigned int i, optionlen, vallen;
- const char *val;
- const struct mips_abi_choice *chosen_abi;
- const struct mips_arch_choice *chosen_arch;
-
- /* Try to match options that are simple flags */
- if (CONST_STRNEQ (option, "no-aliases"))
+ /* Update settings according to the ELF file header flags. */
+ if (info->flavour == bfd_target_elf_flavour && info->section != NULL)
{
- no_aliases = 1;
- return;
+ struct bfd *abfd = info->section->owner;
+ Elf_Internal_Ehdr *header = elf_elfheader (abfd);
+ Elf_Internal_ABIFlags_v0 *abiflags = NULL;
+
+ /* We won't ever get here if !HAVE_BFD_MIPS_ELF_GET_ABIFLAGS,
+ because we won't then have a MIPS/ELF BFD, however we need
+ to guard against a link error in a `--enable-targets=...'
+ configuration with a 32-bit host where the MIPS target is
+ a secondary, or with MIPS/ECOFF configurations. */
+#ifdef HAVE_BFD_MIPS_ELF_GET_ABIFLAGS
+ abiflags = bfd_mips_elf_get_abiflags (abfd);
+#endif
+ /* If an ELF "newabi" binary, use the n32/(n)64 GPR names. */
+ if (is_newabi (header))
+ mips_gpr_names = mips_gpr_names_newabi;
+ /* If a microMIPS binary, then don't use MIPS16 bindings. */
+ micromips_ase = is_micromips (header);
+ /* OR in any extra ASE flags set in ELF file structures. */
+ if (abiflags)
+ mips_ase |= mips_convert_abiflags_ases (abiflags->ases);
+ else if (header->e_flags & EF_MIPS_ARCH_ASE_MDMX)
+ mips_ase |= ASE_MDMX;
}
+#endif
+ mips_ase |= mips_calculate_combination_ases (mips_isa, mips_ase);
+}
+/* Parse an ASE disassembler option and set the corresponding global
+ ASE flag(s). Return TRUE if successful, FALSE otherwise. */
+
+static bfd_boolean
+parse_mips_ase_option (const char *option)
+{
if (CONST_STRNEQ (option, "msa"))
{
mips_ase |= ASE_MSA;
|| (mips_isa & INSN_ISA_MASK) == ISA_MIPS64R5
|| (mips_isa & INSN_ISA_MASK) == ISA_MIPS64R6)
mips_ase |= ASE_MSA64;
- return;
+ return TRUE;
}
if (CONST_STRNEQ (option, "virt"))
|| mips_isa & ISA_MIPS64R5
|| mips_isa & ISA_MIPS64R6)
mips_ase |= ASE_VIRT64;
- return;
+ return TRUE;
}
if (CONST_STRNEQ (option, "xpa"))
{
mips_ase |= ASE_XPA;
+ return TRUE;
+ }
+
+ if (CONST_STRNEQ (option, "ginv"))
+ {
+ mips_ase |= ASE_GINV;
+ return TRUE;
+ }
+
+ if (CONST_STRNEQ (option, "loongson-mmi"))
+ {
+ mips_ase |= ASE_LOONGSON_MMI;
+ return TRUE;
+ }
+
+ if (CONST_STRNEQ (option, "loongson-cam"))
+ {
+ mips_ase |= ASE_LOONGSON_CAM;
+ return TRUE;
+ }
+
+ /* Put here for match ext2 frist */
+ if (CONST_STRNEQ (option, "loongson-ext2"))
+ {
+ mips_ase |= ASE_LOONGSON_EXT2;
+ return TRUE;
+ }
+
+ if (CONST_STRNEQ (option, "loongson-ext"))
+ {
+ mips_ase |= ASE_LOONGSON_EXT;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+parse_mips_dis_option (const char *option, unsigned int len)
+{
+ unsigned int i, optionlen, vallen;
+ const char *val;
+ const struct mips_abi_choice *chosen_abi;
+ const struct mips_arch_choice *chosen_arch;
+
+ /* Try to match options that are simple flags */
+ if (CONST_STRNEQ (option, "no-aliases"))
+ {
+ no_aliases = 1;
return;
}
+ if (parse_mips_ase_option (option))
+ {
+ mips_ase |= mips_calculate_combination_ases (mips_isa, mips_ase);
+ return;
+ }
/* Look for the = that delimits the end of the option name. */
for (i = 0; i < len; i++)
}
}
+/* Print SAVE/RESTORE instruction operands according to the argument
+ register mask AMASK, the number of static registers saved NSREG,
+ the $ra, $s0 and $s1 register specifiers RA, S0 and S1 respectively,
+ and the frame size FRAME_SIZE. */
+
+static void
+mips_print_save_restore (struct disassemble_info *info, unsigned int amask,
+ unsigned int nsreg, unsigned int ra,
+ unsigned int s0, unsigned int s1,
+ unsigned int frame_size)
+{
+ const fprintf_ftype infprintf = info->fprintf_func;
+ unsigned int nargs, nstatics, smask, i, j;
+ void *is = info->stream;
+ const char *sep;
+
+ if (amask == MIPS_SVRS_ALL_ARGS)
+ {
+ nargs = 4;
+ nstatics = 0;
+ }
+ else if (amask == MIPS_SVRS_ALL_STATICS)
+ {
+ nargs = 0;
+ nstatics = 4;
+ }
+ else
+ {
+ nargs = amask >> 2;
+ nstatics = amask & 3;
+ }
+
+ sep = "";
+ if (nargs > 0)
+ {
+ infprintf (is, "%s", mips_gpr_names[4]);
+ if (nargs > 1)
+ infprintf (is, "-%s", mips_gpr_names[4 + nargs - 1]);
+ sep = ",";
+ }
+
+ infprintf (is, "%s%d", sep, frame_size);
+
+ if (ra) /* $ra */
+ infprintf (is, ",%s", mips_gpr_names[31]);
+
+ smask = 0;
+ if (s0) /* $s0 */
+ smask |= 1 << 0;
+ if (s1) /* $s1 */
+ smask |= 1 << 1;
+ if (nsreg > 0) /* $s2-$s8 */
+ smask |= ((1 << nsreg) - 1) << 2;
+
+ for (i = 0; i < 9; i++)
+ if (smask & (1 << i))
+ {
+ infprintf (is, ",%s", mips_gpr_names[i == 8 ? 30 : (16 + i)]);
+ /* Skip over string of set bits. */
+ for (j = i; smask & (2 << j); j++)
+ continue;
+ if (j > i)
+ infprintf (is, "-%s", mips_gpr_names[j == 8 ? 30 : (16 + j)]);
+ i = j + 1;
+ }
+ /* Statics $ax - $a3. */
+ if (nstatics == 1)
+ infprintf (is, ",%s", mips_gpr_names[7]);
+ else if (nstatics > 0)
+ infprintf (is, ",%s-%s",
+ mips_gpr_names[7 - nstatics + 1],
+ mips_gpr_names[7]);
+}
+
+
/* Print operand OPERAND of OPCODE, using STATE to track inter-operand state.
UVAL is the encoding of the operand (shifted into bit 0) and BASE_PC is
the base address for OP_PCREL operands. */
pcrel_op = (const struct mips_pcrel_operand *) operand;
info->target = mips_decode_pcrel_operand (pcrel_op, base_pc, uval);
- /* Preserve the ISA bit for the GDB disassembler,
- otherwise clear it. */
- if (info->flavour != bfd_target_unknown_flavour)
+ /* For jumps and branches clear the ISA bit except for
+ the GDB disassembler. */
+ if (pcrel_op->include_isa_bit
+ && info->flavour != bfd_target_unknown_flavour)
info->target &= -2;
(*info->print_address_func) (info->target, info);
break;
case OP_SAVE_RESTORE_LIST:
- /* Should be handled by the caller due to extend behavior. */
+ /* Should be handled by the caller due to complex behavior. */
abort ();
case OP_MDMX_IMM_REG:
infprintf (is, "$pc");
break;
+ case OP_REG28:
+ print_reg (info, opcode, OP_REG_GP, 28);
+ break;
+
case OP_VU0_SUFFIX:
case OP_VU0_MATCH_SUFFIX:
print_vu0_channel (info, operand, uval);
case OP_REPEAT_PREV_REG:
case OP_REPEAT_DEST_REG:
case OP_PC:
+ case OP_REG28:
case OP_VU0_SUFFIX:
case OP_VU0_MATCH_SUFFIX:
case OP_IMM_INDEX:
case OP_REG_INDEX:
- break;
-
case OP_SAVE_RESTORE_LIST:
- /* Should be handled by the caller due to extend behavior. */
- abort ();
+ break;
}
}
if (*s == 'm' || *s == '+' || *s == '-')
opcode->name, opcode->args);
return;
}
- if (operand->type == OP_REG
- && s[1] == ','
- && s[2] == 'H'
- && opcode->name[strlen (opcode->name) - 1] == '0')
+
+ if (operand->type == OP_SAVE_RESTORE_LIST)
{
- /* Coprocessor register 0 with sel field (MT ASE). */
+ /* Handle this case here because of the complex behavior. */
+ unsigned int amask = (insn >> 15) & 0xf;
+ unsigned int nsreg = (insn >> 23) & 0x7;
+ unsigned int ra = insn & 0x1000; /* $ra */
+ unsigned int s0 = insn & 0x800; /* $s0 */
+ unsigned int s1 = insn & 0x400; /* $s1 */
+ unsigned int frame_size = (((insn >> 15) & 0xf0)
+ | ((insn >> 6) & 0x0f)) * 8;
+ mips_print_save_restore (info, amask, nsreg, ra, s0, s1,
+ frame_size);
+ }
+ else if (operand->type == OP_REG
+ && s[1] == ','
+ && s[2] == 'H'
+ && opcode->name[strlen (opcode->name) - 1] == '0')
+ {
+ /* Coprocessor register 0 with sel field. */
const struct mips_cp0sel_name *n;
unsigned int reg, sel;
const fprintf_ftype infprintf = info->fprintf_func;
void *is = info->stream;
const struct mips_operand *operand, *ext_operand;
+ unsigned short ext_size;
unsigned int uval;
bfd_vma baseaddr;
if (operand->type == OP_SAVE_RESTORE_LIST)
{
- /* Handle this case here because of the complex interation
+ /* Handle this case here because of the complex interaction
with the EXTEND opcode. */
- unsigned int amask, nargs, nstatics, nsreg, smask, frame_size, i, j;
- const char *sep;
-
- amask = extend & 0xf;
- if (amask == MIPS16_ALL_ARGS)
- {
- nargs = 4;
- nstatics = 0;
- }
- else if (amask == MIPS16_ALL_STATICS)
- {
- nargs = 0;
- nstatics = 4;
- }
- else
- {
- nargs = amask >> 2;
- nstatics = amask & 3;
- }
-
- sep = "";
- if (nargs > 0)
- {
- infprintf (is, "%s", mips_gpr_names[4]);
- if (nargs > 1)
- infprintf (is, "-%s", mips_gpr_names[4 + nargs - 1]);
- sep = ",";
- }
-
- frame_size = ((extend & 0xf0) | (insn & 0x0f)) * 8;
+ unsigned int amask = extend & 0xf;
+ unsigned int nsreg = (extend >> 8) & 0x7;
+ unsigned int ra = insn & 0x40; /* $ra */
+ unsigned int s0 = insn & 0x20; /* $s0 */
+ unsigned int s1 = insn & 0x10; /* $s1 */
+ unsigned int frame_size = ((extend & 0xf0) | (insn & 0x0f)) * 8;
if (frame_size == 0 && !use_extend)
frame_size = 128;
- infprintf (is, "%s%d", sep, frame_size);
-
- if (insn & 0x40) /* $ra */
- infprintf (is, ",%s", mips_gpr_names[31]);
-
- nsreg = (extend >> 8) & 0x7;
- smask = 0;
- if (insn & 0x20) /* $s0 */
- smask |= 1 << 0;
- if (insn & 0x10) /* $s1 */
- smask |= 1 << 1;
- if (nsreg > 0) /* $s2-$s8 */
- smask |= ((1 << nsreg) - 1) << 2;
-
- for (i = 0; i < 9; i++)
- if (smask & (1 << i))
- {
- infprintf (is, ",%s", mips_gpr_names[i == 8 ? 30 : (16 + i)]);
- /* Skip over string of set bits. */
- for (j = i; smask & (2 << j); j++)
- continue;
- if (j > i)
- infprintf (is, "-%s", mips_gpr_names[j == 8 ? 30 : (16 + j)]);
- i = j + 1;
- }
- /* Statics $ax - $a3. */
- if (nstatics == 1)
- infprintf (is, ",%s", mips_gpr_names[7]);
- else if (nstatics > 0)
- infprintf (is, ",%s-%s",
- mips_gpr_names[7 - nstatics + 1],
- mips_gpr_names[7]);
+ mips_print_save_restore (info, amask, nsreg, ra, s0, s1, frame_size);
break;
}
info->data_size = 1 << int_op->shift;
}
- if (operand->size == 26)
- /* In this case INSN is the first two bytes of the instruction
- and EXTEND is the second two bytes. */
- uval = ((insn & 0x1f) << 21) | ((insn & 0x3e0) << 11) | extend;
- else
+ ext_size = 0;
+ if (use_extend)
{
- /* Calculate the full field value. */
- uval = mips_extract_operand (operand, insn);
- if (use_extend)
+ ext_operand = decode_mips16_operand (type, TRUE);
+ if (ext_operand != operand
+ || (operand->type == OP_INT && operand->lsb == 0
+ && mips_opcode_32bit_p (opcode)))
{
- ext_operand = decode_mips16_operand (type, TRUE);
- if (ext_operand != operand)
- {
- operand = ext_operand;
- if (operand->size == 16)
- uval |= ((extend & 0x1f) << 11) | (extend & 0x7e0);
- else if (operand->size == 15)
- uval |= ((extend & 0xf) << 11) | (extend & 0x7f0);
- else
- uval = ((extend >> 6) & 0x1f) | (extend & 0x20);
- }
+ ext_size = ext_operand->size;
+ operand = ext_operand;
}
}
+ if (operand->size == 26)
+ uval = ((extend & 0x1f) << 21) | ((extend & 0x3e0) << 11) | insn;
+ else if (ext_size == 16 || ext_size == 9)
+ uval = ((extend & 0x1f) << 11) | (extend & 0x7e0) | (insn & 0x1f);
+ else if (ext_size == 15)
+ uval = ((extend & 0xf) << 11) | (extend & 0x7f0) | (insn & 0xf);
+ else if (ext_size == 6)
+ uval = ((extend >> 6) & 0x1f) | (extend & 0x20);
+ else
+ uval = mips_extract_operand (operand, (extend << 16) | insn);
+ if (ext_size == 9)
+ uval &= (1U << ext_size) - 1;
baseaddr = memaddr + 2;
if (operand->type == OP_PCREL)
if (!pcrel_op->include_isa_bit && use_extend)
baseaddr = memaddr - 2;
else if (!pcrel_op->include_isa_bit)
- {
- bfd_byte buffer[2];
-
- /* If this instruction is in the delay slot of a JR
- instruction, the base address is the address of the
- JR instruction. If it is in the delay slot of a JALR
- instruction, the base address is the address of the
- JALR instruction. This test is unreliable: we have
- no way of knowing whether the previous word is
- instruction or data. */
- if (info->read_memory_func (memaddr - 4, buffer, 2, info) == 0
- && (((info->endian == BFD_ENDIAN_BIG
- ? bfd_getb16 (buffer)
- : bfd_getl16 (buffer))
- & 0xf800) == 0x1800))
- baseaddr = memaddr - 4;
- else if (info->read_memory_func (memaddr - 2, buffer, 2,
- info) == 0
- && (((info->endian == BFD_ENDIAN_BIG
- ? bfd_getb16 (buffer)
- : bfd_getl16 (buffer))
- & 0xf81f) == 0xe800))
- baseaddr = memaddr - 2;
- else
- baseaddr = memaddr;
- }
+ {
+ bfd_byte buffer[2];
+
+ /* If this instruction is in the delay slot of a JAL/JALX
+ instruction, the base address is the address of the
+ JAL/JALX instruction. If it is in the delay slot of
+ a JR/JALR instruction, the base address is the address
+ of the JR/JALR instruction. This test is unreliable:
+ we have no way of knowing whether the previous word is
+ instruction or data. */
+ if (info->read_memory_func (memaddr - 4, buffer, 2, info) == 0
+ && (((info->endian == BFD_ENDIAN_BIG
+ ? bfd_getb16 (buffer)
+ : bfd_getl16 (buffer))
+ & 0xf800) == 0x1800))
+ baseaddr = memaddr - 4;
+ else if (info->read_memory_func (memaddr - 2, buffer, 2,
+ info) == 0
+ && (((info->endian == BFD_ENDIAN_BIG
+ ? bfd_getb16 (buffer)
+ : bfd_getl16 (buffer))
+ & 0xf89f) == 0xe800)
+ && (((info->endian == BFD_ENDIAN_BIG
+ ? bfd_getb16 (buffer)
+ : bfd_getl16 (buffer))
+ & 0x0060) != 0x0060))
+ baseaddr = memaddr - 2;
+ else
+ baseaddr = memaddr;
+ }
}
print_insn_arg (info, state, opcode, operand, baseaddr + 1, uval);
return FALSE;
}
+/* Whether none, a 32-bit or a 16-bit instruction match has been done. */
+
+enum match_kind
+{
+ MATCH_NONE,
+ MATCH_FULL,
+ MATCH_SHORT
+};
+
/* Disassemble mips16 instructions. */
static int
const fprintf_ftype infprintf = info->fprintf_func;
int status;
bfd_byte buffer[4];
- int length;
- int insn;
- bfd_boolean use_extend;
- int extend = 0;
const struct mips_opcode *op, *opend;
struct mips_print_arg_state state;
void *is = info->stream;
+ bfd_boolean have_second;
+ bfd_boolean extend_only;
+ unsigned int second;
+ unsigned int first;
+ unsigned int full;
info->bytes_per_chunk = 2;
info->display_endian = info->endian;
return -1;
}
- length = 2;
+ extend_only = FALSE;
if (info->endian == BFD_ENDIAN_BIG)
- insn = bfd_getb16 (buffer);
+ first = bfd_getb16 (buffer);
else
- insn = bfd_getl16 (buffer);
+ first = bfd_getl16 (buffer);
- /* Handle the extend opcode specially. */
- use_extend = FALSE;
- if ((insn & 0xf800) == 0xf000)
+ status = (*info->read_memory_func) (memaddr + 2, buffer, 2, info);
+ if (status == 0)
{
- use_extend = TRUE;
- extend = insn & 0x7ff;
-
- memaddr += 2;
-
- status = (*info->read_memory_func) (memaddr, buffer, 2, info);
- if (status != 0)
- {
- infprintf (is, "extend 0x%x", (unsigned int) extend);
- (*info->memory_error_func) (status, memaddr, info);
- return -1;
- }
-
+ have_second = TRUE;
if (info->endian == BFD_ENDIAN_BIG)
- insn = bfd_getb16 (buffer);
+ second = bfd_getb16 (buffer);
else
- insn = bfd_getl16 (buffer);
-
- /* Check for an extend opcode followed by an extend opcode. */
- if ((insn & 0xf800) == 0xf000)
- {
- infprintf (is, "extend 0x%x", (unsigned int) extend);
- info->insn_type = dis_noninsn;
- return length;
- }
-
- length += 2;
+ second = bfd_getl16 (buffer);
+ full = (first << 16) | second;
+ }
+ else
+ {
+ have_second = FALSE;
+ second = 0;
+ full = first;
}
/* FIXME: Should probably use a hash table on the major opcode here. */
opend = mips16_opcodes + bfd_mips16_num_opcodes;
for (op = mips16_opcodes; op < opend; op++)
{
- if (op->pinfo != INSN_MACRO
- && !(no_aliases && (op->pinfo2 & INSN2_ALIAS))
- && (insn & op->mask) == op->match)
- {
- const char *s;
-
- if (op->args[0] == 'a' || op->args[0] == 'i')
- {
- if (use_extend)
- {
- infprintf (is, "extend 0x%x", (unsigned int) extend);
- info->insn_type = dis_noninsn;
- return length - 2;
- }
-
- use_extend = FALSE;
+ enum match_kind match;
- memaddr += 2;
+ if (!opcode_is_member (op, mips_isa, mips_ase, mips_processor))
+ continue;
- status = (*info->read_memory_func) (memaddr, buffer, 2,
- info);
- if (status == 0)
- {
- use_extend = TRUE;
- if (info->endian == BFD_ENDIAN_BIG)
- extend = bfd_getb16 (buffer);
- else
- extend = bfd_getl16 (buffer);
- length += 2;
- }
+ if (op->pinfo == INSN_MACRO
+ || (no_aliases && (op->pinfo2 & INSN2_ALIAS)))
+ match = MATCH_NONE;
+ else if (mips_opcode_32bit_p (op))
+ {
+ if (have_second
+ && (full & op->mask) == op->match)
+ match = MATCH_FULL;
+ else
+ match = MATCH_NONE;
+ }
+ else if ((first & op->mask) == op->match)
+ {
+ match = MATCH_SHORT;
+ second = 0;
+ full = first;
+ }
+ else if ((first & 0xf800) == 0xf000
+ && have_second
+ && !extend_only
+ && (second & op->mask) == op->match)
+ {
+ if (op->pinfo2 & INSN2_SHORT_ONLY)
+ {
+ match = MATCH_NONE;
+ extend_only = TRUE;
}
+ else
+ match = MATCH_FULL;
+ }
+ else
+ match = MATCH_NONE;
+
+ if (match != MATCH_NONE)
+ {
+ const char *s;
infprintf (is, "%s", op->name);
if (op->args[0] != '\0')
{
if (*s == ','
&& s[1] == 'w'
- && GET_OP (insn, RX) == GET_OP (insn, RY))
+ && GET_OP (full, RX) == GET_OP (full, RY))
{
/* Skip the register and the comma. */
++s;
}
if (*s == ','
&& s[1] == 'v'
- && GET_OP (insn, RZ) == GET_OP (insn, RX))
+ && GET_OP (full, RZ) == GET_OP (full, RX))
{
/* Skip the register and the comma. */
++s;
continue;
}
- print_mips16_insn_arg (info, &state, op, *s, memaddr, insn,
- use_extend, extend, s[1] == '(');
+ if (s[0] == 'N'
+ && s[1] == ','
+ && s[2] == 'O'
+ && op->name[strlen (op->name) - 1] == '0')
+ {
+ /* Coprocessor register 0 with sel field. */
+ const struct mips_cp0sel_name *n;
+ const struct mips_operand *operand;
+ unsigned int reg, sel;
+
+ operand = decode_mips16_operand (*s, TRUE);
+ reg = mips_extract_operand (operand, (first << 16) | second);
+ s += 2;
+ operand = decode_mips16_operand (*s, TRUE);
+ sel = mips_extract_operand (operand, (first << 16) | second);
+
+ /* CP0 register including 'sel' code for mftc0, to be
+ printed textually if known. If not known, print both
+ CP0 register name and sel numerically since CP0 register
+ with sel 0 may have a name unrelated to register being
+ printed. */
+ n = lookup_mips_cp0sel_name (mips_cp0sel_names,
+ mips_cp0sel_names_len,
+ reg, sel);
+ if (n != NULL)
+ infprintf (is, "%s", n->name);
+ else
+ infprintf (is, "$%d,%d", reg, sel);
+ }
+ else
+ switch (match)
+ {
+ case MATCH_FULL:
+ print_mips16_insn_arg (info, &state, op, *s, memaddr + 2,
+ second, TRUE, first, s[1] == '(');
+ break;
+ case MATCH_SHORT:
+ print_mips16_insn_arg (info, &state, op, *s, memaddr,
+ first, FALSE, 0, s[1] == '(');
+ break;
+ case MATCH_NONE: /* Stop the compiler complaining. */
+ break;
+ }
}
/* Figure out branch instruction type and delay slot information. */
else if ((op->pinfo2 & INSN2_COND_BRANCH) != 0)
info->insn_type = dis_condbranch;
- return length;
+ return match == MATCH_FULL ? 4 : 2;
}
}
#undef GET_OP
- if (use_extend)
- infprintf (is, "0x%x", extend | 0xf000);
- infprintf (is, "0x%x", insn);
+ infprintf (is, "0x%x", first);
info->insn_type = dis_noninsn;
- return length;
+ return 2;
}
/* Disassemble microMIPS instructions. */
else
insn = bfd_getl16 (buffer);
- if ((insn & 0xfc00) == 0x7c00)
- {
- /* This is a 48-bit microMIPS instruction. */
- higher = insn;
-
- status = (*info->read_memory_func) (memaddr + 2, buffer, 2, info);
- if (status != 0)
- {
- infprintf (is, "micromips 0x%x", higher);
- (*info->memory_error_func) (status, memaddr + 2, info);
- return -1;
- }
- if (info->endian == BFD_ENDIAN_BIG)
- insn = bfd_getb16 (buffer);
- else
- insn = bfd_getl16 (buffer);
- higher = (higher << 16) | insn;
-
- status = (*info->read_memory_func) (memaddr + 4, buffer, 2, info);
- if (status != 0)
- {
- infprintf (is, "micromips 0x%x", higher);
- (*info->memory_error_func) (status, memaddr + 4, info);
- return -1;
- }
- if (info->endian == BFD_ENDIAN_BIG)
- insn = bfd_getb16 (buffer);
- else
- insn = bfd_getl16 (buffer);
- infprintf (is, "0x%x%04x (48-bit insn)", higher, insn);
-
- info->insn_type = dis_noninsn;
- return 6;
- }
- else if ((insn & 0x1c00) == 0x0000 || (insn & 0x1000) == 0x1000)
+ if ((insn & 0x1c00) == 0x0000 || (insn & 0x1000) == 0x1000)
{
/* This is a 32-bit microMIPS instruction. */
higher = insn;
}
/* Return 1 if a symbol associated with the location being disassembled
- indicates a compressed (MIPS16 or microMIPS) mode. We iterate over
- all the symbols at the address being considered assuming if at least
- one of them indicates code compression, then such code has been
- genuinely produced here (other symbols could have been derived from
- function symbols defined elsewhere or could define data). Otherwise,
- return 0. */
+ indicates a compressed mode, either MIPS16 or microMIPS, according to
+ MICROMIPS_P. We iterate over all the symbols at the address being
+ considered assuming if at least one of them indicates code compression,
+ then such code has been genuinely produced here (other symbols could
+ have been derived from function symbols defined elsewhere or could
+ define data). Otherwise, return 0. */
static bfd_boolean
-is_compressed_mode_p (struct disassemble_info *info)
+is_compressed_mode_p (struct disassemble_info *info, bfd_boolean micromips_p)
{
int i;
int l;
for (i = info->symtab_pos, l = i + info->num_symbols; i < l; i++)
if (((info->symtab[i])->flags & BSF_SYNTHETIC) != 0
- && ((!micromips_ase
+ && ((!micromips_p
&& ELF_ST_IS_MIPS16 ((*info->symbols)->udata.i))
- || (micromips_ase
+ || (micromips_p
&& ELF_ST_IS_MICROMIPS ((*info->symbols)->udata.i))))
return 1;
else if (bfd_asymbol_flavour (info->symtab[i]) == bfd_target_elf_flavour
&& info->symtab[i]->section == info->section)
{
elf_symbol_type *symbol = (elf_symbol_type *) info->symtab[i];
- if ((!micromips_ase
+ if ((!micromips_p
&& ELF_ST_IS_MIPS16 (symbol->internal_elf_sym.st_other))
- || (micromips_ase
+ || (micromips_p
&& ELF_ST_IS_MICROMIPS (symbol->internal_elf_sym.st_other)))
return 1;
}
struct disassemble_info *info,
enum bfd_endian endianness)
{
- int (*print_insn_compr) (bfd_vma, struct disassemble_info *);
bfd_byte buffer[INSNLEN];
int status;
if (info->mach == bfd_mach_mips_micromips)
return print_insn_micromips (memaddr, info);
- print_insn_compr = !micromips_ase ? print_insn_mips16 : print_insn_micromips;
-
#if 1
/* FIXME: If odd address, this is CLEARLY a compressed instruction. */
/* Only a few tools will work this way. */
if (memaddr & 0x01)
- return print_insn_compr (memaddr, info);
+ {
+ if (micromips_ase)
+ return print_insn_micromips (memaddr, info);
+ else
+ return print_insn_mips16 (memaddr, info);
+ }
#endif
#if SYMTAB_AVAILABLE
- if (is_compressed_mode_p (info))
- return print_insn_compr (memaddr, info);
+ if (is_compressed_mode_p (info, TRUE))
+ return print_insn_micromips (memaddr, info);
+ if (is_compressed_mode_p (info, FALSE))
+ return print_insn_mips16 (memaddr, info);
#endif
status = (*info->read_memory_func) (memaddr, buffer, INSNLEN, info);
return _print_insn_mips (memaddr, info, BFD_ENDIAN_LITTLE);
}
\f
-void
-print_mips_disassembler_options (FILE *stream)
+/* Indices into option argument vector for options accepting an argument.
+ Use MIPS_OPTION_ARG_NONE for options accepting no argument. */
+typedef enum
{
- unsigned int i;
+ MIPS_OPTION_ARG_NONE = -1,
+ MIPS_OPTION_ARG_ABI,
+ MIPS_OPTION_ARG_ARCH,
+ MIPS_OPTION_ARG_SIZE
+} mips_option_arg_t;
+
+/* Valid MIPS disassembler options. */
+static struct
+{
+ const char *name;
+ const char *description;
+ mips_option_arg_t arg;
+} mips_options[] =
+{
+ { "no-aliases", N_("Use canonical instruction forms.\n"),
+ MIPS_OPTION_ARG_NONE },
+ { "msa", N_("Recognize MSA instructions.\n"),
+ MIPS_OPTION_ARG_NONE },
+ { "virt", N_("Recognize the virtualization ASE instructions.\n"),
+ MIPS_OPTION_ARG_NONE },
+ { "xpa", N_("Recognize the eXtended Physical Address (XPA) ASE\n\
+ instructions.\n"),
+ MIPS_OPTION_ARG_NONE },
+ { "ginv", N_("Recognize the Global INValidate (GINV) ASE "
+ "instructions.\n"),
+ MIPS_OPTION_ARG_NONE },
+ { "loongson-mmi",
+ N_("Recognize the Loongson MultiMedia extensions "
+ "Instructions (MMI) ASE instructions.\n"),
+ MIPS_OPTION_ARG_NONE },
+ { "loongson-cam",
+ N_("Recognize the Loongson Content Address Memory (CAM) "
+ " instructions.\n"),
+ MIPS_OPTION_ARG_NONE },
+ { "loongson-ext",
+ N_("Recognize the Loongson EXTensions (EXT) "
+ " instructions.\n"),
+ MIPS_OPTION_ARG_NONE },
+ { "loongson-ext2",
+ N_("Recognize the Loongson EXTensions R2 (EXT2) "
+ " instructions.\n"),
+ MIPS_OPTION_ARG_NONE },
+ { "gpr-names=", N_("Print GPR names according to specified ABI.\n\
+ Default: based on binary being disassembled.\n"),
+ MIPS_OPTION_ARG_ABI },
+ { "fpr-names=", N_("Print FPR names according to specified ABI.\n\
+ Default: numeric.\n"),
+ MIPS_OPTION_ARG_ABI },
+ { "cp0-names=", N_("Print CP0 register names according to specified "
+ "architecture.\n\
+ Default: based on binary being disassembled.\n"),
+ MIPS_OPTION_ARG_ARCH },
+ { "hwr-names=", N_("Print HWR names according to specified architecture.\n\
+ Default: based on binary being disassembled.\n"),
+ MIPS_OPTION_ARG_ARCH },
+ { "reg-names=", N_("Print GPR and FPR names according to specified ABI.\n"),
+ MIPS_OPTION_ARG_ABI },
+ { "reg-names=", N_("Print CP0 register and HWR names according to "
+ "specified\n\
+ architecture."),
+ MIPS_OPTION_ARG_ARCH }
+};
- fprintf (stream, _("\n\
-The following MIPS specific disassembler options are supported for use\n\
-with the -M switch (multiple options should be separated by commas):\n"));
+/* Build the structure representing valid MIPS disassembler options.
+ This is done dynamically for maintenance ease purpose; a static
+ initializer would be unreadable. */
- fprintf (stream, _("\n\
- msa Recognize MSA instructions.\n"));
+const disasm_options_and_args_t *
+disassembler_options_mips (void)
+{
+ static disasm_options_and_args_t *opts_and_args;
- fprintf (stream, _("\n\
- virt Recognize the virtualization ASE instructions.\n"));
+ if (opts_and_args == NULL)
+ {
+ size_t num_options = ARRAY_SIZE (mips_options);
+ size_t num_args = MIPS_OPTION_ARG_SIZE;
+ disasm_option_arg_t *args;
+ disasm_options_t *opts;
+ size_t i;
+ size_t j;
+
+ args = XNEWVEC (disasm_option_arg_t, num_args + 1);
+
+ args[MIPS_OPTION_ARG_ABI].name = "ABI";
+ args[MIPS_OPTION_ARG_ABI].values
+ = XNEWVEC (const char *, ARRAY_SIZE (mips_abi_choices) + 1);
+ for (i = 0; i < ARRAY_SIZE (mips_abi_choices); i++)
+ args[MIPS_OPTION_ARG_ABI].values[i] = mips_abi_choices[i].name;
+ /* The array we return must be NULL terminated. */
+ args[MIPS_OPTION_ARG_ABI].values[i] = NULL;
+
+ args[MIPS_OPTION_ARG_ARCH].name = "ARCH";
+ args[MIPS_OPTION_ARG_ARCH].values
+ = XNEWVEC (const char *, ARRAY_SIZE (mips_arch_choices) + 1);
+ for (i = 0, j = 0; i < ARRAY_SIZE (mips_arch_choices); i++)
+ if (*mips_arch_choices[i].name != '\0')
+ args[MIPS_OPTION_ARG_ARCH].values[j++] = mips_arch_choices[i].name;
+ /* The array we return must be NULL terminated. */
+ args[MIPS_OPTION_ARG_ARCH].values[j] = NULL;
+
+ /* The array we return must be NULL terminated. */
+ args[MIPS_OPTION_ARG_SIZE].name = NULL;
+ args[MIPS_OPTION_ARG_SIZE].values = NULL;
+
+ opts_and_args = XNEW (disasm_options_and_args_t);
+ opts_and_args->args = args;
+
+ opts = &opts_and_args->options;
+ opts->name = XNEWVEC (const char *, num_options + 1);
+ opts->description = XNEWVEC (const char *, num_options + 1);
+ opts->arg = XNEWVEC (const disasm_option_arg_t *, num_options + 1);
+ for (i = 0; i < num_options; i++)
+ {
+ opts->name[i] = mips_options[i].name;
+ opts->description[i] = _(mips_options[i].description);
+ if (mips_options[i].arg != MIPS_OPTION_ARG_NONE)
+ opts->arg[i] = &args[mips_options[i].arg];
+ else
+ opts->arg[i] = NULL;
+ }
+ /* The array we return must be NULL terminated. */
+ opts->name[i] = NULL;
+ opts->description[i] = NULL;
+ opts->arg[i] = NULL;
+ }
- fprintf (stream, _("\n\
- xpa Recognize the eXtended Physical Address (XPA) ASE instructions.\n"));
+ return opts_and_args;
+}
- fprintf (stream, _("\n\
- gpr-names=ABI Print GPR names according to specified ABI.\n\
- Default: based on binary being disassembled.\n"));
+void
+print_mips_disassembler_options (FILE *stream)
+{
+ const disasm_options_and_args_t *opts_and_args;
+ const disasm_option_arg_t *args;
+ const disasm_options_t *opts;
+ size_t max_len = 0;
+ size_t i;
+ size_t j;
- fprintf (stream, _("\n\
- fpr-names=ABI Print FPR names according to specified ABI.\n\
- Default: numeric.\n"));
+ opts_and_args = disassembler_options_mips ();
+ opts = &opts_and_args->options;
+ args = opts_and_args->args;
fprintf (stream, _("\n\
- cp0-names=ARCH Print CP0 register names according to\n\
- specified architecture.\n\
- Default: based on binary being disassembled.\n"));
+The following MIPS specific disassembler options are supported for use\n\
+with the -M switch (multiple options should be separated by commas):\n\n"));
- fprintf (stream, _("\n\
- hwr-names=ARCH Print HWR names according to specified \n\
- architecture.\n\
- Default: based on binary being disassembled.\n"));
+ /* Compute the length of the longest option name. */
+ for (i = 0; opts->name[i] != NULL; i++)
+ {
+ size_t len = strlen (opts->name[i]);
- fprintf (stream, _("\n\
- reg-names=ABI Print GPR and FPR names according to\n\
- specified ABI.\n"));
+ if (opts->arg[i] != NULL)
+ len += strlen (opts->arg[i]->name);
+ if (max_len < len)
+ max_len = len;
+ }
- fprintf (stream, _("\n\
- reg-names=ARCH Print CP0 register and HWR names according to\n\
- specified architecture.\n"));
+ for (i = 0, max_len++; opts->name[i] != NULL; i++)
+ {
+ fprintf (stream, " %s", opts->name[i]);
+ if (opts->arg[i] != NULL)
+ fprintf (stream, "%s", opts->arg[i]->name);
+ if (opts->description[i] != NULL)
+ {
+ size_t len = strlen (opts->name[i]);
- fprintf (stream, _("\n\
- For the options above, the following values are supported for \"ABI\":\n\
- "));
- for (i = 0; i < ARRAY_SIZE (mips_abi_choices); i++)
- fprintf (stream, " %s", mips_abi_choices[i].name);
- fprintf (stream, _("\n"));
+ if (opts->arg[i] != NULL)
+ len += strlen (opts->arg[i]->name);
+ fprintf (stream,
+ "%*c %s", (int) (max_len - len), ' ', opts->description[i]);
+ }
+ fprintf (stream, _("\n"));
+ }
- fprintf (stream, _("\n\
- For the options above, The following values are supported for \"ARCH\":\n\
- "));
- for (i = 0; i < ARRAY_SIZE (mips_arch_choices); i++)
- if (*mips_arch_choices[i].name != '\0')
- fprintf (stream, " %s", mips_arch_choices[i].name);
- fprintf (stream, _("\n"));
+ for (i = 0; args[i].name != NULL; i++)
+ {
+ fprintf (stream, _("\n\
+ For the options above, the following values are supported for \"%s\":\n "),
+ args[i].name);
+ for (j = 0; args[i].values[j] != NULL; j++)
+ fprintf (stream, " %s", args[i].values[j]);
+ fprintf (stream, _("\n"));
+ }
fprintf (stream, _("\n"));
}