2016-11-02 00:45:57 +08:00
|
|
|
/* RISC-V disassembler
|
2019-01-01 18:31:27 +08:00
|
|
|
Copyright (C) 2011-2019 Free Software Foundation, Inc.
|
2016-11-02 00:45:57 +08:00
|
|
|
|
|
|
|
Contributed by Andrew Waterman (andrew@sifive.com).
|
|
|
|
Based on MIPS target.
|
|
|
|
|
|
|
|
This file is part of the GNU opcodes library.
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
|
|
any later version.
|
|
|
|
|
|
|
|
It is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
|
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
|
|
|
License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; see the file COPYING3. If not,
|
|
|
|
see <http://www.gnu.org/licenses/>. */
|
|
|
|
|
|
|
|
#include "sysdep.h"
|
Move print_insn_XXX to an opcodes internal header
With the changes done in previous patches, print_insn_XXX functions
don't have to be external visible out of opcodes, because both gdb
and objdump select disassemblers through a single interface.
This patch moves these print_insn_XXX declarations from
include/dis-asm.h to opcodes/disassemble.h, which is a new header
added by this patch.
include:
2017-05-24 Yao Qi <yao.qi@linaro.org>
* dis-asm.h: Move some function declarations to
opcodes/disassemble.h.
opcodes:
2017-05-24 Yao Qi <yao.qi@linaro.org>
* alpha-dis.c: Include disassemble.h, don't include
dis-asm.h.
* avr-dis.c, bfin-dis.c, cr16-dis.c: Likewise.
* crx-dis.c, d10v-dis.c, d30v-dis.c: Likewise.
* disassemble.c, dlx-dis.c, epiphany-dis.c: Likewise.
* fr30-dis.c, ft32-dis.c, h8300-dis.c, h8500-dis.c: Likewise.
* hppa-dis.c, i370-dis.c, i386-dis.c: Likewise.
* i860-dis.c, i960-dis.c, ip2k-dis.c: Likewise.
* iq2000-dis.c, lm32-dis.c, m10200-dis.c: Likewise.
* m10300-dis.c, m32r-dis.c, m68hc11-dis.c: Likewise.
* m68k-dis.c, m88k-dis.c, mcore-dis.c: Likewise.
* metag-dis.c, microblaze-dis.c, mmix-dis.c: Likewise.
* moxie-dis.c, msp430-dis.c, mt-dis.c:
* nds32-dis.c, nios2-dis.c, ns32k-dis.c: Likewise.
* or1k-dis.c, pdp11-dis.c, pj-dis.c: Likewise.
* ppc-dis.c, pru-dis.c, riscv-dis.c: Likewise.
* rl78-dis.c, s390-dis.c, score-dis.c: Likewise.
* sh-dis.c, sh64-dis.c, tic30-dis.c: Likewise.
* tic4x-dis.c, tic54x-dis.c, tic6x-dis.c: Likewise.
* tic80-dis.c, tilegx-dis.c, tilepro-dis.c: Likewise.
* v850-dis.c, vax-dis.c, visium-dis.c: Likewise.
* w65-dis.c, wasm32-dis.c, xc16x-dis.c: Likewise.
* xgate-dis.c, xstormy16-dis.c, xtensa-dis.c: Likewise.
* z80-dis.c, z8k-dis.c: Likewise.
* disassemble.h: New file.
2017-05-25 00:23:52 +08:00
|
|
|
#include "disassemble.h"
|
2016-11-02 00:45:57 +08:00
|
|
|
#include "libiberty.h"
|
|
|
|
#include "opcode/riscv.h"
|
|
|
|
#include "opintl.h"
|
|
|
|
#include "elf-bfd.h"
|
|
|
|
#include "elf/riscv.h"
|
|
|
|
|
2018-12-18 16:33:51 +08:00
|
|
|
#include "bfd_stdint.h"
|
2016-11-02 00:45:57 +08:00
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
struct riscv_private_data
|
|
|
|
{
|
|
|
|
bfd_vma gp;
|
|
|
|
bfd_vma print_addr;
|
|
|
|
bfd_vma hi_addr[OP_MASK_RD + 1];
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char * const *riscv_gpr_names;
|
|
|
|
static const char * const *riscv_fpr_names;
|
|
|
|
|
|
|
|
/* Other options. */
|
|
|
|
static int no_aliases; /* If set disassemble as most general inst. */
|
|
|
|
|
|
|
|
static void
|
|
|
|
set_default_riscv_dis_options (void)
|
|
|
|
{
|
|
|
|
riscv_gpr_names = riscv_gpr_names_abi;
|
|
|
|
riscv_fpr_names = riscv_fpr_names_abi;
|
|
|
|
no_aliases = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
parse_riscv_dis_option (const char *option)
|
|
|
|
{
|
|
|
|
if (strcmp (option, "no-aliases") == 0)
|
|
|
|
no_aliases = 1;
|
|
|
|
else if (strcmp (option, "numeric") == 0)
|
|
|
|
{
|
|
|
|
riscv_gpr_names = riscv_gpr_names_numeric;
|
|
|
|
riscv_fpr_names = riscv_fpr_names_numeric;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
opcodes error messages
Another patch aimed at making binutils comply with the GNU coding
standard. The generated files require
https://sourceware.org/ml/cgen/2018-q1/msg00004.html
cpu/
* frv.opc: Include opintl.h.
(add_next_to_vliw): Use opcodes_error_handler to print error.
Standardize error message.
(fr500_check_insn_major_constraints, frv_vliw_add_insn): Likewise.
opcodes/
* sysdep.h (opcodes_error_handler): Define.
(_bfd_error_handler): Declare.
* Makefile.am: Remove stray #.
* opc2c.c (main): Remove bogus -l arg handling. Print "DO NOT
EDIT" comment.
* aarch64-dis.c, * arc-dis.c, * arm-dis.c, * avr-dis.c,
* d30v-dis.c, * h8300-dis.c, * mmix-dis.c, * ppc-dis.c,
* riscv-dis.c, * s390-dis.c, * sparc-dis.c, * v850-dis.c: Use
opcodes_error_handler to print errors. Standardize error messages.
* msp430-decode.opc, * nios2-dis.c, * rl78-decode.opc: Likewise,
and include opintl.h.
* nds32-asm.c: Likewise, and include sysdep.h and opintl.h.
* i386-gen.c: Standardize error messages.
* msp430-decode.c, * rl78-decode.c, rx-decode.c: Regenerate.
* Makefile.in: Regenerate.
* epiphany-asm.c, * epiphany-desc.c, * epiphany-dis.c,
* epiphany-ibld.c, * fr30-asm.c, * fr30-desc.c, * fr30-dis.c,
* fr30-ibld.c, * frv-asm.c, * frv-desc.c, * frv-dis.c, * frv-ibld.c,
* frv-opc.c, * ip2k-asm.c, * ip2k-desc.c, * ip2k-dis.c, * ip2k-ibld.c,
* iq2000-asm.c, * iq2000-desc.c, * iq2000-dis.c, * iq2000-ibld.c,
* lm32-asm.c, * lm32-desc.c, * lm32-dis.c, * lm32-ibld.c,
* m32c-asm.c, * m32c-desc.c, * m32c-dis.c, * m32c-ibld.c,
* m32r-asm.c, * m32r-desc.c, * m32r-dis.c, * m32r-ibld.c,
* mep-asm.c, * mep-desc.c, * mep-dis.c, * mep-ibld.c, * mt-asm.c,
* mt-desc.c, * mt-dis.c, * mt-ibld.c, * or1k-asm.c, * or1k-desc.c,
* or1k-dis.c, * or1k-ibld.c, * xc16x-asm.c, * xc16x-desc.c,
* xc16x-dis.c, * xc16x-ibld.c, * xstormy16-asm.c, * xstormy16-desc.c,
* xstormy16-dis.c, * xstormy16-ibld.c: Regenerate.
2018-03-02 05:53:50 +08:00
|
|
|
/* xgettext:c-format */
|
|
|
|
opcodes_error_handler (_("unrecognized disassembler option: %s"), option);
|
2016-11-02 00:45:57 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
parse_riscv_dis_options (const char *opts_in)
|
|
|
|
{
|
|
|
|
char *opts = xstrdup (opts_in), *opt = opts, *opt_end = opts;
|
|
|
|
|
|
|
|
set_default_riscv_dis_options ();
|
|
|
|
|
|
|
|
for ( ; opt_end != NULL; opt = opt_end + 1)
|
|
|
|
{
|
|
|
|
if ((opt_end = strchr (opt, ',')) != NULL)
|
|
|
|
*opt_end = 0;
|
|
|
|
parse_riscv_dis_option (opt);
|
|
|
|
}
|
|
|
|
|
|
|
|
free (opts);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Print one argument from an array. */
|
|
|
|
|
|
|
|
static void
|
|
|
|
arg_print (struct disassemble_info *info, unsigned long val,
|
|
|
|
const char* const* array, size_t size)
|
|
|
|
{
|
|
|
|
const char *s = val >= size || array[val] == NULL ? "unknown" : array[val];
|
|
|
|
(*info->fprintf_func) (info->stream, "%s", s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
maybe_print_address (struct riscv_private_data *pd, int base_reg, int offset)
|
|
|
|
{
|
|
|
|
if (pd->hi_addr[base_reg] != (bfd_vma)-1)
|
|
|
|
{
|
2018-01-10 08:40:06 +08:00
|
|
|
pd->print_addr = (base_reg != 0 ? pd->hi_addr[base_reg] : 0) + offset;
|
2016-11-02 00:45:57 +08:00
|
|
|
pd->hi_addr[base_reg] = -1;
|
|
|
|
}
|
|
|
|
else if (base_reg == X_GP && pd->gp != (bfd_vma)-1)
|
|
|
|
pd->print_addr = pd->gp + offset;
|
|
|
|
else if (base_reg == X_TP || base_reg == 0)
|
|
|
|
pd->print_addr = offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Print insn arguments for 32/64-bit code. */
|
|
|
|
|
|
|
|
static void
|
|
|
|
print_insn_args (const char *d, insn_t l, bfd_vma pc, disassemble_info *info)
|
|
|
|
{
|
|
|
|
struct riscv_private_data *pd = info->private_data;
|
|
|
|
int rs1 = (l >> OP_SH_RS1) & OP_MASK_RS1;
|
|
|
|
int rd = (l >> OP_SH_RD) & OP_MASK_RD;
|
|
|
|
fprintf_ftype print = info->fprintf_func;
|
|
|
|
|
|
|
|
if (*d != '\0')
|
|
|
|
print (info->stream, "\t");
|
|
|
|
|
|
|
|
for (; *d != '\0'; d++)
|
|
|
|
{
|
|
|
|
switch (*d)
|
|
|
|
{
|
|
|
|
case 'C': /* RVC */
|
|
|
|
switch (*++d)
|
|
|
|
{
|
|
|
|
case 's': /* RS1 x8-x15 */
|
|
|
|
case 'w': /* RS1 x8-x15 */
|
|
|
|
print (info->stream, "%s",
|
|
|
|
riscv_gpr_names[EXTRACT_OPERAND (CRS1S, l) + 8]);
|
|
|
|
break;
|
|
|
|
case 't': /* RS2 x8-x15 */
|
|
|
|
case 'x': /* RS2 x8-x15 */
|
|
|
|
print (info->stream, "%s",
|
|
|
|
riscv_gpr_names[EXTRACT_OPERAND (CRS2S, l) + 8]);
|
|
|
|
break;
|
|
|
|
case 'U': /* RS1, constrained to equal RD */
|
|
|
|
print (info->stream, "%s", riscv_gpr_names[rd]);
|
|
|
|
break;
|
|
|
|
case 'c': /* RS1, constrained to equal sp */
|
|
|
|
print (info->stream, "%s", riscv_gpr_names[X_SP]);
|
|
|
|
break;
|
|
|
|
case 'V': /* RS2 */
|
|
|
|
print (info->stream, "%s",
|
|
|
|
riscv_gpr_names[EXTRACT_OPERAND (CRS2, l)]);
|
|
|
|
break;
|
|
|
|
case 'i':
|
|
|
|
print (info->stream, "%d", (int)EXTRACT_RVC_SIMM3 (l));
|
|
|
|
break;
|
2017-04-05 20:58:28 +08:00
|
|
|
case 'o':
|
2016-11-02 00:45:57 +08:00
|
|
|
case 'j':
|
|
|
|
print (info->stream, "%d", (int)EXTRACT_RVC_IMM (l));
|
|
|
|
break;
|
|
|
|
case 'k':
|
|
|
|
print (info->stream, "%d", (int)EXTRACT_RVC_LW_IMM (l));
|
|
|
|
break;
|
|
|
|
case 'l':
|
|
|
|
print (info->stream, "%d", (int)EXTRACT_RVC_LD_IMM (l));
|
|
|
|
break;
|
|
|
|
case 'm':
|
|
|
|
print (info->stream, "%d", (int)EXTRACT_RVC_LWSP_IMM (l));
|
|
|
|
break;
|
|
|
|
case 'n':
|
|
|
|
print (info->stream, "%d", (int)EXTRACT_RVC_LDSP_IMM (l));
|
|
|
|
break;
|
|
|
|
case 'K':
|
|
|
|
print (info->stream, "%d", (int)EXTRACT_RVC_ADDI4SPN_IMM (l));
|
|
|
|
break;
|
|
|
|
case 'L':
|
|
|
|
print (info->stream, "%d", (int)EXTRACT_RVC_ADDI16SP_IMM (l));
|
|
|
|
break;
|
|
|
|
case 'M':
|
|
|
|
print (info->stream, "%d", (int)EXTRACT_RVC_SWSP_IMM (l));
|
|
|
|
break;
|
|
|
|
case 'N':
|
|
|
|
print (info->stream, "%d", (int)EXTRACT_RVC_SDSP_IMM (l));
|
|
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
info->target = EXTRACT_RVC_B_IMM (l) + pc;
|
|
|
|
(*info->print_address_func) (info->target, info);
|
|
|
|
break;
|
|
|
|
case 'a':
|
|
|
|
info->target = EXTRACT_RVC_J_IMM (l) + pc;
|
|
|
|
(*info->print_address_func) (info->target, info);
|
|
|
|
break;
|
|
|
|
case 'u':
|
|
|
|
print (info->stream, "0x%x",
|
|
|
|
(int)(EXTRACT_RVC_IMM (l) & (RISCV_BIGIMM_REACH-1)));
|
|
|
|
break;
|
|
|
|
case '>':
|
|
|
|
print (info->stream, "0x%x", (int)EXTRACT_RVC_IMM (l) & 0x3f);
|
|
|
|
break;
|
|
|
|
case '<':
|
|
|
|
print (info->stream, "0x%x", (int)EXTRACT_RVC_IMM (l) & 0x1f);
|
|
|
|
break;
|
|
|
|
case 'T': /* floating-point RS2 */
|
|
|
|
print (info->stream, "%s",
|
|
|
|
riscv_fpr_names[EXTRACT_OPERAND (CRS2, l)]);
|
|
|
|
break;
|
|
|
|
case 'D': /* floating-point RS2 x8-x15 */
|
|
|
|
print (info->stream, "%s",
|
|
|
|
riscv_fpr_names[EXTRACT_OPERAND (CRS2S, l) + 8]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ',':
|
|
|
|
case '(':
|
|
|
|
case ')':
|
|
|
|
case '[':
|
|
|
|
case ']':
|
|
|
|
print (info->stream, "%c", *d);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '0':
|
|
|
|
/* Only print constant 0 if it is the last argument */
|
|
|
|
if (!d[1])
|
|
|
|
print (info->stream, "0");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'b':
|
|
|
|
case 's':
|
2018-01-06 09:51:23 +08:00
|
|
|
if ((l & MASK_JALR) == MATCH_JALR)
|
|
|
|
maybe_print_address (pd, rs1, 0);
|
2016-11-02 00:45:57 +08:00
|
|
|
print (info->stream, "%s", riscv_gpr_names[rs1]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 't':
|
|
|
|
print (info->stream, "%s",
|
|
|
|
riscv_gpr_names[EXTRACT_OPERAND (RS2, l)]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'u':
|
|
|
|
print (info->stream, "0x%x",
|
|
|
|
(unsigned)EXTRACT_UTYPE_IMM (l) >> RISCV_IMM_BITS);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'm':
|
|
|
|
arg_print (info, EXTRACT_OPERAND (RM, l),
|
|
|
|
riscv_rm, ARRAY_SIZE (riscv_rm));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'P':
|
|
|
|
arg_print (info, EXTRACT_OPERAND (PRED, l),
|
|
|
|
riscv_pred_succ, ARRAY_SIZE (riscv_pred_succ));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'Q':
|
|
|
|
arg_print (info, EXTRACT_OPERAND (SUCC, l),
|
|
|
|
riscv_pred_succ, ARRAY_SIZE (riscv_pred_succ));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'o':
|
|
|
|
maybe_print_address (pd, rs1, EXTRACT_ITYPE_IMM (l));
|
2017-01-04 00:02:36 +08:00
|
|
|
/* Fall through. */
|
2016-11-02 00:45:57 +08:00
|
|
|
case 'j':
|
|
|
|
if (((l & MASK_ADDI) == MATCH_ADDI && rs1 != 0)
|
|
|
|
|| (l & MASK_JALR) == MATCH_JALR)
|
|
|
|
maybe_print_address (pd, rs1, EXTRACT_ITYPE_IMM (l));
|
|
|
|
print (info->stream, "%d", (int)EXTRACT_ITYPE_IMM (l));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'q':
|
|
|
|
maybe_print_address (pd, rs1, EXTRACT_STYPE_IMM (l));
|
|
|
|
print (info->stream, "%d", (int)EXTRACT_STYPE_IMM (l));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'a':
|
|
|
|
info->target = EXTRACT_UJTYPE_IMM (l) + pc;
|
|
|
|
(*info->print_address_func) (info->target, info);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'p':
|
|
|
|
info->target = EXTRACT_SBTYPE_IMM (l) + pc;
|
|
|
|
(*info->print_address_func) (info->target, info);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'd':
|
|
|
|
if ((l & MASK_AUIPC) == MATCH_AUIPC)
|
|
|
|
pd->hi_addr[rd] = pc + EXTRACT_UTYPE_IMM (l);
|
|
|
|
else if ((l & MASK_LUI) == MATCH_LUI)
|
|
|
|
pd->hi_addr[rd] = EXTRACT_UTYPE_IMM (l);
|
|
|
|
else if ((l & MASK_C_LUI) == MATCH_C_LUI)
|
|
|
|
pd->hi_addr[rd] = EXTRACT_RVC_LUI_IMM (l);
|
|
|
|
print (info->stream, "%s", riscv_gpr_names[rd]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'z':
|
|
|
|
print (info->stream, "%s", riscv_gpr_names[0]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '>':
|
|
|
|
print (info->stream, "0x%x", (int)EXTRACT_OPERAND (SHAMT, l));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '<':
|
|
|
|
print (info->stream, "0x%x", (int)EXTRACT_OPERAND (SHAMTW, l));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'S':
|
|
|
|
case 'U':
|
|
|
|
print (info->stream, "%s", riscv_fpr_names[rs1]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'T':
|
|
|
|
print (info->stream, "%s", riscv_fpr_names[EXTRACT_OPERAND (RS2, l)]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'D':
|
|
|
|
print (info->stream, "%s", riscv_fpr_names[rd]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'R':
|
|
|
|
print (info->stream, "%s", riscv_fpr_names[EXTRACT_OPERAND (RS3, l)]);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'E':
|
|
|
|
{
|
|
|
|
const char* csr_name = NULL;
|
|
|
|
unsigned int csr = EXTRACT_OPERAND (CSR, l);
|
|
|
|
switch (csr)
|
|
|
|
{
|
|
|
|
#define DECLARE_CSR(name, num) case num: csr_name = #name; break;
|
|
|
|
#include "opcode/riscv-opc.h"
|
|
|
|
#undef DECLARE_CSR
|
|
|
|
}
|
|
|
|
if (csr_name)
|
|
|
|
print (info->stream, "%s", csr_name);
|
|
|
|
else
|
|
|
|
print (info->stream, "0x%x", csr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'Z':
|
|
|
|
print (info->stream, "%d", rs1);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* xgettext:c-format */
|
|
|
|
print (info->stream, _("# internal error, undefined modifier (%c)"),
|
|
|
|
*d);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Print the RISC-V instruction at address MEMADDR in debugged memory,
|
|
|
|
on using INFO. Returns length of the instruction, in bytes.
|
|
|
|
BIGENDIAN must be 1 if this is big-endian code, 0 if
|
|
|
|
this is little-endian code. */
|
|
|
|
|
|
|
|
static int
|
|
|
|
riscv_disassemble_insn (bfd_vma memaddr, insn_t word, disassemble_info *info)
|
|
|
|
{
|
|
|
|
const struct riscv_opcode *op;
|
|
|
|
static bfd_boolean init = 0;
|
|
|
|
static const struct riscv_opcode *riscv_hash[OP_MASK_OP + 1];
|
|
|
|
struct riscv_private_data *pd;
|
|
|
|
int insnlen;
|
|
|
|
|
|
|
|
#define OP_HASH_IDX(i) ((i) & (riscv_insn_length (i) == 2 ? 0x3 : OP_MASK_OP))
|
|
|
|
|
|
|
|
/* Build a hash table to shorten the search time. */
|
|
|
|
if (! init)
|
|
|
|
{
|
|
|
|
for (op = riscv_opcodes; op->name; op++)
|
|
|
|
if (!riscv_hash[OP_HASH_IDX (op->match)])
|
|
|
|
riscv_hash[OP_HASH_IDX (op->match)] = op;
|
|
|
|
|
|
|
|
init = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (info->private_data == NULL)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
pd = info->private_data = xcalloc (1, sizeof (struct riscv_private_data));
|
|
|
|
pd->gp = -1;
|
|
|
|
pd->print_addr = -1;
|
|
|
|
for (i = 0; i < (int)ARRAY_SIZE (pd->hi_addr); i++)
|
|
|
|
pd->hi_addr[i] = -1;
|
|
|
|
|
|
|
|
for (i = 0; i < info->symtab_size; i++)
|
2017-04-04 01:08:29 +08:00
|
|
|
if (strcmp (bfd_asymbol_name (info->symtab[i]), RISCV_GP_SYMBOL) == 0)
|
2016-11-02 00:45:57 +08:00
|
|
|
pd->gp = bfd_asymbol_value (info->symtab[i]);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
pd = info->private_data;
|
|
|
|
|
|
|
|
insnlen = riscv_insn_length (word);
|
|
|
|
|
|
|
|
info->bytes_per_chunk = insnlen % 4 == 0 ? 4 : 2;
|
|
|
|
info->bytes_per_line = 8;
|
|
|
|
info->display_endian = info->endian;
|
|
|
|
info->insn_info_valid = 1;
|
|
|
|
info->branch_delay_insns = 0;
|
|
|
|
info->data_size = 0;
|
|
|
|
info->insn_type = dis_nonbranch;
|
|
|
|
info->target = 0;
|
|
|
|
info->target2 = 0;
|
|
|
|
|
|
|
|
op = riscv_hash[OP_HASH_IDX (word)];
|
|
|
|
if (op != NULL)
|
|
|
|
{
|
2018-12-04 05:59:44 +08:00
|
|
|
unsigned xlen = 0;
|
2016-11-02 00:45:57 +08:00
|
|
|
|
Re-work RISC-V gas flags: now we just support -mabi and -march
We've decided to standardize on two flags for RISC-V: "-march" sets the
target architecture (which determines which instructions can be
generated), and "-mabi" sets the target ABI. We needed to rework this
because the old flag set didn't support soft-float or single-float ABIs,
and didn't support an x32-style ABI on RISC-V.
Additionally, we've changed the behavior of the -march flag: it's now a
lot stricter and only parses things we can actually understand.
Additionally, it's now lowercase-only: the rationale is that while the
RISC-V ISA manual specifies that ISA strings are case-insensitive, in
Linux-land things are usually case-sensitive. Since this flag can be
used to determine library paths, we didn't want to bake some
case-insensitivity in there that would case trouble later.
This patch implements these two new flags and removes the old flags that
could conflict with these. There wasn't a RISC-V release before, so we
want to just support a clean flag set.
include/
* elf/riscv.h (EF_RISCV_SOFT_FLOAT): Don't define.
(EF_RISCV_FLOAT_ABI, EF_RISCV_FLOAT_ABI_SOFT): Define.
(EF_RISCV_FLOAT_ABI_SINGLE, EF_RISCV_FLOAT_ABI_DOUBLE): Define.
(EF_RISCV_FLOAT_ABI_QUAD): Define.
bfd/
* elfnn-riscv.c (_bfd_riscv_elf_merge_private_bfd_data): Use
EF_RISCV_FLOAT_ABI_SOFT instead of EF_RISCV_SOFT_FLOAT.
binutils/
* readelf.c (get_machine_flags): Use
EF_RISCV_FLOAT_ABI_{SOFT,SINGLE,DOBULE,QUAD) instead of
EF_RISCV_{SOFT,HARD}_FLOAT.
gas/
* config/tc-riscv.h (xlen): Delete.
* config/tc-riscv.c (xlen): Make static.
(abi_xlen): New variable.
(options): Replace OPTION_{M32,M64,MSOFT_FLOAT,MHARD_FLOAT,MRVC}
with OPTION_MABI.
(md_longopts): Likewise.
(md_parse_option): Likewise.
(riscv_elf_final_processing): Likewise.
* doc/as.texinfo (Target RISC-V options): Likewise.
* doc/c-riscv.texi (OPTIONS): Likewise.
* config/tc-riscv.c (float_mode): Removed.
(float_abi): New type, specifies the floating-point ABI.
(riscv_set_abi): New function.
(riscv_add_subset): Only allow lower-case ISA names and require
them to start with "rv".
(riscv_after_parse_args): Likewise.
opcodes/
* riscv-dis.c (riscv_disassemble_insn): Default to the ELF's
XLEN when none is provided.
2016-12-19 14:53:50 +08:00
|
|
|
/* If XLEN is not known, get its value from the ELF class. */
|
|
|
|
if (info->mach == bfd_mach_riscv64)
|
|
|
|
xlen = 64;
|
|
|
|
else if (info->mach == bfd_mach_riscv32)
|
|
|
|
xlen = 32;
|
|
|
|
else if (info->section != NULL)
|
2016-11-02 00:45:57 +08:00
|
|
|
{
|
|
|
|
Elf_Internal_Ehdr *ehdr = elf_elfheader (info->section->owner);
|
|
|
|
xlen = ehdr->e_ident[EI_CLASS] == ELFCLASS64 ? 64 : 32;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; op->name; op++)
|
|
|
|
{
|
|
|
|
/* Does the opcode match? */
|
|
|
|
if (! (op->match_func) (op, word))
|
|
|
|
continue;
|
|
|
|
/* Is this a pseudo-instruction and may we print it as such? */
|
|
|
|
if (no_aliases && (op->pinfo & INSN_ALIAS))
|
|
|
|
continue;
|
|
|
|
/* Is this instruction restricted to a certain value of XLEN? */
|
2018-08-31 04:23:12 +08:00
|
|
|
if ((op->xlen_requirement != 0) && (op->xlen_requirement != xlen))
|
2016-11-02 00:45:57 +08:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* It's a match. */
|
|
|
|
(*info->fprintf_func) (info->stream, "%s", op->name);
|
|
|
|
print_insn_args (op->args, word, memaddr, info);
|
|
|
|
|
|
|
|
/* Try to disassemble multi-instruction addressing sequences. */
|
|
|
|
if (pd->print_addr != (bfd_vma)-1)
|
|
|
|
{
|
|
|
|
info->target = pd->print_addr;
|
|
|
|
(*info->fprintf_func) (info->stream, " # ");
|
|
|
|
(*info->print_address_func) (info->target, info);
|
|
|
|
pd->print_addr = -1;
|
|
|
|
}
|
|
|
|
|
2018-07-31 04:55:41 +08:00
|
|
|
/* Finish filling out insn_info fields. */
|
|
|
|
switch (op->pinfo & INSN_TYPE)
|
|
|
|
{
|
|
|
|
case INSN_BRANCH:
|
|
|
|
info->insn_type = dis_branch;
|
|
|
|
break;
|
|
|
|
case INSN_CONDBRANCH:
|
|
|
|
info->insn_type = dis_condbranch;
|
|
|
|
break;
|
|
|
|
case INSN_JSR:
|
|
|
|
info->insn_type = dis_jsr;
|
|
|
|
break;
|
|
|
|
case INSN_DREF:
|
|
|
|
info->insn_type = dis_dref;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (op->pinfo & INSN_DATA_SIZE)
|
|
|
|
{
|
|
|
|
int size = ((op->pinfo & INSN_DATA_SIZE)
|
|
|
|
>> INSN_DATA_SIZE_SHIFT);
|
|
|
|
info->data_size = 1 << (size - 1);
|
|
|
|
}
|
|
|
|
|
2016-11-02 00:45:57 +08:00
|
|
|
return insnlen;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We did not find a match, so just print the instruction bits. */
|
|
|
|
info->insn_type = dis_noninsn;
|
|
|
|
(*info->fprintf_func) (info->stream, "0x%llx", (unsigned long long)word);
|
|
|
|
return insnlen;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
print_insn_riscv (bfd_vma memaddr, struct disassemble_info *info)
|
|
|
|
{
|
|
|
|
bfd_byte packet[2];
|
|
|
|
insn_t insn = 0;
|
|
|
|
bfd_vma n;
|
|
|
|
int status;
|
|
|
|
|
|
|
|
if (info->disassembler_options != NULL)
|
|
|
|
{
|
|
|
|
parse_riscv_dis_options (info->disassembler_options);
|
|
|
|
/* Avoid repeatedly parsing the options. */
|
|
|
|
info->disassembler_options = NULL;
|
|
|
|
}
|
|
|
|
else if (riscv_gpr_names == NULL)
|
|
|
|
set_default_riscv_dis_options ();
|
|
|
|
|
|
|
|
/* Instructions are a sequence of 2-byte packets in little-endian order. */
|
|
|
|
for (n = 0; n < sizeof (insn) && n < riscv_insn_length (insn); n += 2)
|
|
|
|
{
|
|
|
|
status = (*info->read_memory_func) (memaddr + n, packet, 2, info);
|
|
|
|
if (status != 0)
|
|
|
|
{
|
|
|
|
/* Don't fail just because we fell off the end. */
|
|
|
|
if (n > 0)
|
|
|
|
break;
|
|
|
|
(*info->memory_error_func) (status, memaddr, info);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
insn |= ((insn_t) bfd_getl16 (packet)) << (8 * n);
|
|
|
|
}
|
|
|
|
|
|
|
|
return riscv_disassemble_insn (memaddr, insn, info);
|
|
|
|
}
|
|
|
|
|
opcodes/riscv: Hide '.L0 ' fake symbols
The RISC-V assembler generates fake labels with the name '.L0 ' as
part of the debug information (see
gas/config/tc-riscv.h:FAKE_LABEL_NAME).
The problem is that currently, when disassembling an object file, the
output looks like this (this is an example from the GDB testsuite, but
is pretty representative of anything with debug information):
000000000000001e <main>:
1e: 7179 addi sp,sp,-48
20: f406 sd ra,40(sp)
22: f022 sd s0,32(sp)
24: 1800 addi s0,sp,48
0000000000000026 <.L0 >:
26: 87aa mv a5,a0
28: feb43023 sd a1,-32(s0)
2c: fcc43c23 sd a2,-40(s0)
30: fef42623 sw a5,-20(s0)
0000000000000034 <.L0 >:
34: fec42783 lw a5,-20(s0)
38: 0007871b sext.w a4,a5
3c: 678d lui a5,0x3
3e: 03978793 addi a5,a5,57 # 3039 <.LASF30+0x2a9d>
42: 02f71463 bne a4,a5,6a <.L0 >
0000000000000046 <.L0 >:
46: 000007b7 lui a5,0x0
4a: 0007b783 ld a5,0(a5) # 0 <need_malloc>
4e: 6f9c ld a5,24(a5)
0000000000000050 <.L0 >:
50: 86be mv a3,a5
52: 466d li a2,27
54: 4585 li a1,1
56: 000007b7 lui a5,0x0
5a: 00078513 mv a0,a5
5e: 00000097 auipc ra,0x0
62: 000080e7 jalr ra # 5e <.L0 +0xe>
0000000000000066 <.L0 >:
66: 4785 li a5,1
68: a869 j 102 <.L0 >
000000000000006a <.L0 >:
6a: 000007b7 lui a5,0x0
6e: 00078513 mv a0,a5
72: 00000097 auipc ra,0x0
76: 000080e7 jalr ra # 72 <.L0 +0x8>
The frequent repeated '.L0 ' labels are pointless, as they are
non-unique there's no way to match a use of '.L0 ' to its appearence
in the output, so we'd be better off just not printing it at all.
That's what this patch does by defining a 'symbol_is_valid' method for
RISC-V. With this commit, the same disassembly now looks like this:
000000000000001e <main>:
1e: 7179 addi sp,sp,-48
20: f406 sd ra,40(sp)
22: f022 sd s0,32(sp)
24: 1800 addi s0,sp,48
26: 87aa mv a5,a0
28: feb43023 sd a1,-32(s0)
2c: fcc43c23 sd a2,-40(s0)
30: fef42623 sw a5,-20(s0)
34: fec42783 lw a5,-20(s0)
38: 0007871b sext.w a4,a5
3c: 678d lui a5,0x3
3e: 03978793 addi a5,a5,57 # 3039 <.LASF30+0x2a9d>
42: 02f71463 bne a4,a5,6a <.L4>
46: 000007b7 lui a5,0x0
4a: 0007b783 ld a5,0(a5) # 0 <need_malloc>
4e: 6f9c ld a5,24(a5)
50: 86be mv a3,a5
52: 466d li a2,27
54: 4585 li a1,1
56: 000007b7 lui a5,0x0
5a: 00078513 mv a0,a5
5e: 00000097 auipc ra,0x0
62: 000080e7 jalr ra # 5e <main+0x40>
66: 4785 li a5,1
68: a869 j 102 <.L5>
000000000000006a <.L4>:
6a: 000007b7 lui a5,0x0
6e: 00078513 mv a0,a5
72: 00000097 auipc ra,0x0
76: 000080e7 jalr ra # 72 <.L4+0x8>
In order to share the fake label between the assembler and the
libopcodes library, I've added some new defines RISCV_FAKE_LABEL_NAME
and RISCV_FAKE_LABEL_CHAR in include/opcode/riscv.h. I could have
just moved FAKE_LABEL_NAME to the include file, however, I thnk this
would be confusing, someone working on the assembler would likely not
expect to find FAKE_LABEL_NAME defined outside of the assembler source
tree. By introducing the RISCV_FAKE_LABEL_* defines I can leave the
assembler standard FAKE_LABEL_ defines in the assembler source, but
still share the RISCV_FAKE_LABEL_* with libopcodes.
gas/ChangeLog:
* config/tc-riscv.h (FAKE_LABEL_NAME): Define as
RISCV_FAKE_LABEL_NAME.
(FAKE_LABEL_CHAR): Define as RISCV_FAKE_LABEL_CHAR.
include/ChangeLog:
* dis-asm.h (riscv_symbol_is_valid): Declare.
* opcode/riscv.h (RISCV_FAKE_LABEL_NAME): Define.
(RISCV_FAKE_LABEL_CHAR): Define.
opcodes/ChangeLog:
* disassembler.c (disassemble_init_for_target): Add RISC-V
initialisation.
* riscv-dis.c (riscv_symbol_is_valid): New function.
2018-12-03 22:46:18 +08:00
|
|
|
/* Prevent use of the fake labels that are generated as part of the DWARF
|
|
|
|
and for relaxable relocations in the assembler. */
|
|
|
|
|
|
|
|
bfd_boolean
|
|
|
|
riscv_symbol_is_valid (asymbol * sym,
|
|
|
|
struct disassemble_info * info ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
const char * name;
|
|
|
|
|
|
|
|
if (sym == NULL)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
name = bfd_asymbol_name (sym);
|
|
|
|
|
|
|
|
return (strcmp (name, RISCV_FAKE_LABEL_NAME) != 0);
|
|
|
|
}
|
|
|
|
|
2016-11-02 00:45:57 +08:00
|
|
|
void
|
|
|
|
print_riscv_disassembler_options (FILE *stream)
|
|
|
|
{
|
|
|
|
fprintf (stream, _("\n\
|
|
|
|
The following RISC-V-specific disassembler options are supported for use\n\
|
|
|
|
with the -M switch (multiple options should be separated by commas):\n"));
|
|
|
|
|
|
|
|
fprintf (stream, _("\n\
|
2017-07-25 19:12:16 +08:00
|
|
|
numeric Print numeric register names, rather than ABI names.\n"));
|
2016-11-02 00:45:57 +08:00
|
|
|
|
|
|
|
fprintf (stream, _("\n\
|
|
|
|
no-aliases Disassemble only into canonical instructions, rather\n\
|
|
|
|
than into pseudoinstructions.\n"));
|
|
|
|
|
|
|
|
fprintf (stream, _("\n"));
|
|
|
|
}
|