/* aarch64-dis.c -- AArch64 disassembler.
- Copyright (C) 2009-2018 Free Software Foundation, Inc.
+ Copyright (C) 2009-2019 Free Software Foundation, Inc.
Contributed by ARM Ltd.
This file is part of the GNU opcodes library.
#include "aarch64-dis.h"
#include "elf-bfd.h"
-#define ERR_OK 0
-#define ERR_UND -1
-#define ERR_UNP -3
-#define ERR_NYI -5
-
#define INSNLEN 4
/* Cached mapping symbol state. */
/* Other options */
static int no_aliases = 0; /* If set disassemble as most general inst. */
-\f
+\fstatic int no_notes = 1; /* If set do not print disassemble notes in the
+ output as comments. */
+
+/* Currently active instruction sequence. */
+static aarch64_instr_sequence insn_sequence;
static void
set_default_aarch64_dis_options (struct disassemble_info *info ATTRIBUTE_UNUSED)
return;
}
+ if (CONST_STRNEQ (option, "no-notes"))
+ {
+ no_notes = 1;
+ return;
+ }
+
+ if (CONST_STRNEQ (option, "notes"))
+ {
+ no_notes = 0;
+ return;
+ }
+
#ifdef DEBUG_AARCH64
if (CONST_STRNEQ (option, "debug_dump"))
{
switch (info->qualifier)
{
case AARCH64_OPND_QLF_S_H:
- /* h:l:m */
- info->reglane.index = extract_fields (code, 0, 3, FLD_H, FLD_L,
- FLD_M);
- info->reglane.regno &= 0xf;
+ if (info->type == AARCH64_OPND_Em16)
+ {
+ /* h:l:m */
+ info->reglane.index = extract_fields (code, 0, 3, FLD_H, FLD_L,
+ FLD_M);
+ info->reglane.regno &= 0xf;
+ }
+ else
+ {
+ /* h:l */
+ info->reglane.index = extract_fields (code, 0, 2, FLD_H, FLD_L);
+ }
break;
case AARCH64_OPND_QLF_S_S:
/* h:l */
return FALSE;
}
- if (inst->opcode->op == OP_FCMLA_ELEM)
+ if (inst->opcode->op == OP_FCMLA_ELEM
+ && info->qualifier != AARCH64_OPND_QLF_S_H)
{
/* Complex operand takes two elements. */
if (info->reglane.index & 1)
if (operand_need_shift_by_two (self))
imm <<= 2;
+ else if (operand_need_shift_by_four (self))
+ imm <<= 4;
if (info->type == AARCH64_OPND_ADDR_ADRP)
imm <<= 12;
/* simm (imm9 or imm7) */
imm = extract_field (self->fields[0], code, 0);
info->addr.offset.imm = sign_extend (imm, fields[self->fields[0]].width - 1);
- if (self->fields[0] == FLD_imm7)
+ if (self->fields[0] == FLD_imm7
+ || info->qualifier == AARCH64_OPND_QLF_imm_tag)
/* scaled immediate in ld/st pair instructions. */
info->addr.offset.imm *= aarch64_get_qualifier_esize (info->qualifier);
/* qualifier */
/* op0:op1:CRn:CRm:op2 */
info->sysreg.value = extract_fields (code, 0, 5, FLD_op0, FLD_op1, FLD_CRn,
FLD_CRm, FLD_op2);
- return 1;
+ info->sysreg.flags = 0;
+
+ /* If a system instruction, check which restrictions should be on the register
+ value during decoding, these will be enforced then. */
+ if (inst->opcode->iclass == ic_system)
+ {
+ /* Check to see if it's read-only, else check if it's write only.
+ if it's both or unspecified don't care. */
+ if ((inst->opcode->flags & (F_SYS_READ | F_SYS_WRITE)) == F_SYS_READ)
+ info->sysreg.flags = F_REG_READ;
+ else if ((inst->opcode->flags & (F_SYS_READ | F_SYS_WRITE))
+ == F_SYS_WRITE)
+ info->sysreg.flags = F_REG_WRITE;
+ }
+
+ return TRUE;
}
/* Decode the PSTATE field operand for e.g. MSR <pstatefield>, #<imm>. */
case AARCH64_OPND_SYSREG_DC: sysins_ops = aarch64_sys_regs_dc; break;
case AARCH64_OPND_SYSREG_IC: sysins_ops = aarch64_sys_regs_ic; break;
case AARCH64_OPND_SYSREG_TLBI: sysins_ops = aarch64_sys_regs_tlbi; break;
+ case AARCH64_OPND_SYSREG_SR:
+ sysins_ops = aarch64_sys_regs_sr;
+ /* Let's remove op2 for rctx. Refer to comments in the definition of
+ aarch64_sys_regs_sr[]. */
+ value = value & ~(0x7);
+ break;
default: assert (0); return FALSE;
}
for (i = 0; aarch64_hint_options[i].name != NULL; i++)
{
- if (hint_number == aarch64_hint_options[i].value)
+ if (hint_number == HINT_VAL (aarch64_hint_options[i].value))
{
info->hint_option = &(aarch64_hint_options[i]);
return TRUE;
}
/* If the opcode has a verifier, then check it now. */
- if (opcode->verifier && ! opcode->verifier (opcode, code))
+ if (opcode->verifier
+ && opcode->verifier (inst, code, 0, FALSE, errors, NULL) != ERR_OK)
{
DEBUG_TRACE ("operand verifier FAIL");
goto decode_fail;
opcode may be filled in *INSN if NOALIASES_P is FALSE. Return zero on
success. */
-int
+enum err_type
aarch64_decode_insn (aarch64_insn insn, aarch64_inst *inst,
bfd_boolean noaliases_p,
aarch64_operand_error *errors)
static void
print_operands (bfd_vma pc, const aarch64_opcode *opcode,
- const aarch64_opnd_info *opnds, struct disassemble_info *info)
+ const aarch64_opnd_info *opnds, struct disassemble_info *info,
+ bfd_boolean *has_notes)
{
+ char *notes = NULL;
int i, pcrel_p, num_printed;
for (i = 0, num_printed = 0; i < AARCH64_MAX_OPND_NUM; ++i)
{
/* Generate the operand string in STR. */
aarch64_print_operand (str, sizeof (str), pc, opcode, opnds, i, &pcrel_p,
- &info->target);
+ &info->target, ¬es);
/* Print the delimiter (taking account of omitted operand(s)). */
if (str[0] != '\0')
else
(*info->fprintf_func) (info->stream, "%s", str);
}
+
+ if (notes && !no_notes)
+ {
+ *has_notes = TRUE;
+ (*info->fprintf_func) (info->stream, " // note: %s", notes);
+ }
}
/* Set NAME to a copy of INST's mnemonic with the "." suffix removed. */
}
}
+/* Build notes from verifiers into a string for printing. */
+
+static void
+print_verifier_notes (aarch64_operand_error *detail,
+ struct disassemble_info *info)
+{
+ if (no_notes)
+ return;
+
+ /* The output of the verifier cannot be a fatal error, otherwise the assembly
+ would not have succeeded. We can safely ignore these. */
+ assert (detail->non_fatal);
+ assert (detail->error);
+
+ /* If there are multiple verifier messages, concat them up to 1k. */
+ (*info->fprintf_func) (info->stream, " // note: %s", detail->error);
+ if (detail->index >= 0)
+ (*info->fprintf_func) (info->stream, " at operand %d", detail->index + 1);
+}
+
/* Print the instruction according to *INST. */
static void
print_aarch64_insn (bfd_vma pc, const aarch64_inst *inst,
- struct disassemble_info *info)
+ const aarch64_insn code,
+ struct disassemble_info *info,
+ aarch64_operand_error *mismatch_details)
{
+ bfd_boolean has_notes = FALSE;
+
print_mnemonic_name (inst, info);
- print_operands (pc, inst->opcode, inst->operands, info);
+ print_operands (pc, inst->opcode, inst->operands, info, &has_notes);
print_comment (inst, info);
+
+ /* We've already printed a note, not enough space to print more so exit.
+ Usually notes shouldn't overlap so it shouldn't happen that we have a note
+ from a register and instruction at the same time. */
+ if (has_notes)
+ return;
+
+ /* Always run constraint verifiers, this is needed because constraints need to
+ maintain a global state regardless of whether the instruction has the flag
+ set or not. */
+ enum err_type result = verify_constraints (inst, code, pc, FALSE,
+ mismatch_details, &insn_sequence);
+ switch (result)
+ {
+ case ERR_UND:
+ case ERR_UNP:
+ case ERR_NYI:
+ assert (0);
+ case ERR_VFI:
+ print_verifier_notes (mismatch_details, info);
+ break;
+ default:
+ break;
+ }
}
/* Entry-point of the instruction disassembler and printer. */
struct disassemble_info *info,
aarch64_operand_error *errors)
{
- static const char *err_msg[6] =
+ static const char *err_msg[ERR_NR_ENTRIES+1] =
{
- [ERR_OK] = "_",
- [-ERR_UND] = "undefined",
- [-ERR_UNP] = "unpredictable",
- [-ERR_NYI] = "NYI"
+ [ERR_OK] = "_",
+ [ERR_UND] = "undefined",
+ [ERR_UNP] = "unpredictable",
+ [ERR_NYI] = "NYI"
};
- int ret;
+ enum err_type ret;
aarch64_inst inst;
info->insn_info_valid = 1;
/* Handle undefined instructions. */
info->insn_type = dis_noninsn;
(*info->fprintf_func) (info->stream,".inst\t0x%08x ; %s",
- word, err_msg[-ret]);
+ word, err_msg[ret]);
break;
case ERR_OK:
user_friendly_fixup (&inst);
- print_aarch64_insn (pc, &inst, info);
+ print_aarch64_insn (pc, &inst, word, info, errors);
break;
default:
abort ();
fprintf (stream, _("\n\
aliases Do print instruction aliases.\n"));
+ fprintf (stream, _("\n\
+ no-notes Don't print instruction notes.\n"));
+
+ fprintf (stream, _("\n\
+ notes Do print instruction notes.\n"));
+
#ifdef DEBUG_AARCH64
fprintf (stream, _("\n\
debug_dump Temp switch for debug trace.\n"));