diff options
Diffstat (limited to 'opcodes/loongarch-dis.c')
-rw-r--r-- | opcodes/loongarch-dis.c | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/opcodes/loongarch-dis.c b/opcodes/loongarch-dis.c new file mode 100644 index 0000000..8e2361d --- /dev/null +++ b/opcodes/loongarch-dis.c @@ -0,0 +1,342 @@ +/* LoongArch opcode support. + Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Loongson Ltd. + + 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" +#include "disassemble.h" +#include "opintl.h" +#include "opcode/loongarch.h" +#include "libiberty.h" +#include <stdlib.h> + +static const struct loongarch_opcode * +get_loongarch_opcode_by_binfmt (insn_t insn) +{ + const struct loongarch_opcode *it; + struct loongarch_ase *ase; + size_t i; + for (ase = loongarch_ASEs; ase->enabled; ase++) + { + if (!*ase->enabled || (ase->include && !*ase->include) + || (ase->exclude && *ase->exclude)) + continue; + + if (!ase->opc_htab_inited) + { + for (it = ase->opcodes; it->mask; it++) + if (!ase->opc_htab[LARCH_INSN_OPC (it->match)] + && it->macro == NULL) + ase->opc_htab[LARCH_INSN_OPC (it->match)] = it; + for (i = 0; i < 16; i++) + if (!ase->opc_htab[i]) + ase->opc_htab[i] = it; + ase->opc_htab_inited = 1; + } + + it = ase->opc_htab[LARCH_INSN_OPC (insn)]; + for (; it->name; it++) + if ((insn & it->mask) == it->match && it->mask + && !(it->include && !*it->include) + && !(it->exclude && *it->exclude)) + return it; + } + return NULL; +} + +static const char *const *loongarch_r_disname = NULL; +static const char *const *loongarch_f_disname = NULL; +static const char *const *loongarch_c_disname = NULL; +static const char *const *loongarch_cr_disname = NULL; +static const char *const *loongarch_v_disname = NULL; +static const char *const *loongarch_x_disname = NULL; + +static void +set_default_loongarch_dis_options (void) +{ + LARCH_opts.ase_ilp32 = 1; + LARCH_opts.ase_lp64 = 1; + LARCH_opts.ase_sf = 1; + LARCH_opts.ase_df = 1; + LARCH_opts.ase_lsx = 1; + LARCH_opts.ase_lasx = 1; + + loongarch_r_disname = loongarch_r_lp64_name; + loongarch_f_disname = loongarch_f_lp64_name; + loongarch_c_disname = loongarch_c_normal_name; + loongarch_cr_disname = loongarch_cr_normal_name; + loongarch_v_disname = loongarch_v_normal_name; + loongarch_x_disname = loongarch_x_normal_name; +} + +static int +parse_loongarch_dis_option (const char *option) +{ + if (strcmp (option, "numeric") == 0) + { + loongarch_r_disname = loongarch_r_normal_name; + loongarch_f_disname = loongarch_f_normal_name; + } + return -1; +} + +static int +parse_loongarch_dis_options (const char *opts_in) +{ + set_default_loongarch_dis_options (); + + if (opts_in == NULL) + return 0; + + char *opts, *opt, *opt_end; + opts = xmalloc (strlen (opts_in) + 1); + strcpy (opts, opts_in); + + for (opt = opt_end = opts; opt_end != NULL; opt = opt_end + 1) + { + if ((opt_end = strchr (opt, ',')) != NULL) + *opt_end = 0; + if (parse_loongarch_dis_option (opt) != 0) + return -1; + } + free (opts); + return 0; +} + +static int32_t +dis_one_arg (char esc1, char esc2, const char *bit_field, + const char *arg ATTRIBUTE_UNUSED, void *context) +{ + static int need_comma = 0; + struct disassemble_info *info = context; + insn_t insn = *(insn_t *) info->private_data; + int32_t imm, u_imm; + + if (esc1) + { + if (need_comma) + info->fprintf_func (info->stream, ", "); + need_comma = 1; + imm = loongarch_decode_imm (bit_field, insn, 1); + u_imm = loongarch_decode_imm (bit_field, insn, 0); + } + + switch (esc1) + { + case 'r': + info->fprintf_func (info->stream, "%s", loongarch_r_disname[u_imm]); + break; + case 'f': + info->fprintf_func (info->stream, "%s", loongarch_f_disname[u_imm]); + break; + case 'c': + switch (esc2) + { + case 'r': + info->fprintf_func (info->stream, "%s", loongarch_cr_disname[u_imm]); + break; + default: + info->fprintf_func (info->stream, "%s", loongarch_c_disname[u_imm]); + } + break; + case 'v': + info->fprintf_func (info->stream, "%s", loongarch_v_disname[u_imm]); + break; + case 'x': + info->fprintf_func (info->stream, "%s", loongarch_x_disname[u_imm]); + break; + case 'u': + info->fprintf_func (info->stream, "0x%x", u_imm); + break; + case 's': + if (imm == 0) + info->fprintf_func (info->stream, "%d", imm); + else + info->fprintf_func (info->stream, "%d(0x%x)", imm, u_imm); + switch (esc2) + { + case 'b': + info->insn_type = dis_branch; + info->target += imm; + } + break; + case '\0': + need_comma = 0; + } + return 0; +} + +static void +disassemble_one (insn_t insn, struct disassemble_info *info) +{ + const struct loongarch_opcode *opc = get_loongarch_opcode_by_binfmt (insn); + +#ifdef LOONGARCH_DEBUG + char have_space[32] = { 0 }; + insn_t t; + int i; + const char *t_f = opc ? opc->format : NULL; + if (t_f) + while (*t_f) + { + while (('a' <= t_f[0] && t_f[0] <= 'z') + || ('A' <= t_f[0] && t_f[0] <= 'Z') + || t_f[0] == ',') + t_f++; + while (1) + { + i = strtol (t_f, &t_f, 10); + have_space[i] = 1; + t_f++; /* ':' */ + i += strtol (t_f, &t_f, 10); + have_space[i] = 1; + if (t_f[0] == '|') + t_f++; + else + break; + } + if (t_f[0] == '<') + t_f += 2; /* '<' '<' */ + strtol (t_f, &t_f, 10); + } + + have_space[28] = 1; + have_space[0] = 0; + t = ~((insn_t) -1 >> 1); + for (i = 31; 0 <= i; i--) + { + if (t & insn) + info->fprintf_func (info->stream, "1"); + else + info->fprintf_func (info->stream, "0"); + if (have_space[i]) + info->fprintf_func (info->stream, " "); + t = t >> 1; + } + info->fprintf_func (info->stream, "\t"); +#endif + + if (!opc) + { + info->insn_type = dis_noninsn; + info->fprintf_func (info->stream, "0x%08x", insn); + return; + } + + info->insn_type = dis_nonbranch; + info->fprintf_func (info->stream, "%-12s", opc->name); + + { + char *fake_args = xmalloc (strlen (opc->format) + 1); + const char *fake_arg_strs[MAX_ARG_NUM_PLUS_2]; + strcpy (fake_args, opc->format); + if (0 < loongarch_split_args_by_comma (fake_args, fake_arg_strs)) + info->fprintf_func (info->stream, "\t"); + info->private_data = &insn; + loongarch_foreach_args (opc->format, fake_arg_strs, dis_one_arg, info); + free (fake_args); + } + + if (info->insn_type == dis_branch || info->insn_type == dis_condbranch + /* Someother if we have extra info to print. */) + info->fprintf_func (info->stream, "\t#"); + + if (info->insn_type == dis_branch || info->insn_type == dis_condbranch) + { + info->fprintf_func (info->stream, " "); + info->print_address_func (info->target, info); + } +} + +int +print_insn_loongarch (bfd_vma memaddr, struct disassemble_info *info) +{ + insn_t insn; + int status; + + static int not_init_yet = 1; + if (not_init_yet) + { + parse_loongarch_dis_options (info->disassembler_options); + not_init_yet = 0; + } + + info->bytes_per_chunk = 4; + info->bytes_per_line = 4; + info->display_endian = BFD_ENDIAN_LITTLE; + info->insn_info_valid = 1; + info->target = memaddr; + + if ((status = info->read_memory_func (memaddr, (bfd_byte *) &insn, + sizeof (insn), info)) != 0) + { + info->memory_error_func (status, memaddr, info); + return -1; /* loongarch_insn_length (0); */ + } + + disassemble_one (insn, info); + + return loongarch_insn_length (insn); +} + +void +print_loongarch_disassembler_options (FILE *stream) +{ + fprintf (stream, _("\n\ +The following LoongArch disassembler options are supported for use\n\ +with the -M switch (multiple options should be separated by commas):\n")); + + fprintf (stream, _("\n\ + numeric Print numeric register names, rather than ABI names.\n")); + fprintf (stream, _("\n")); +} + +int +loongarch_parse_dis_options (const char *opts_in) +{ + return parse_loongarch_dis_options (opts_in); +} + +static void +my_print_address_func (bfd_vma addr, struct disassemble_info *dinfo) +{ + dinfo->fprintf_func (dinfo->stream, "0x%llx", (long long) addr); +} + +void +loongarch_disassemble_one (int64_t pc, insn_t insn, + int (*fprintf_func) (void *stream, + const char *format, ...), + void *stream) +{ + static struct disassemble_info my_disinfo = + { + .print_address_func = my_print_address_func, + }; + static int not_init_yet = 1; + if (not_init_yet) + { + loongarch_parse_dis_options (NULL); + not_init_yet = 0; + } + + my_disinfo.fprintf_func = fprintf_func; + my_disinfo.stream = stream; + my_disinfo.target = pc; + disassemble_one (insn, &my_disinfo); +} |