diff options
author | Ulrich Drepper <drepper@gmail.com> | 2019-09-06 23:49:54 +0200 |
---|---|---|
committer | Ulrich Drepper <drepper@gmail.com> | 2019-09-06 23:49:54 +0200 |
commit | fcab4f6587528db731da3676decb3bedcf44c08d (patch) | |
tree | 1cb27502bfef12cdaeb9f2fbfebf8eb01218f84e /libcpu | |
parent | c950e8a995dfee0b6094c9854581b103754c6bb6 (diff) | |
download | elfutils-fcab4f6587528db731da3676decb3bedcf44c08d.tar.gz |
Implement RISC-V disassembler
Diffstat (limited to 'libcpu')
-rw-r--r-- | libcpu/Makefile.am | 2 | ||||
-rw-r--r-- | libcpu/i386_disasm.c | 2 | ||||
-rw-r--r-- | libcpu/riscv_disasm.c | 1501 |
3 files changed, 1503 insertions, 2 deletions
diff --git a/libcpu/Makefile.am b/libcpu/Makefile.am index 88717361..03c71ea3 100644 --- a/libcpu/Makefile.am +++ b/libcpu/Makefile.am @@ -42,7 +42,7 @@ noinst_LIBRARIES = libcpu.a libcpu_pic.a noinst_HEADERS = i386_dis.h x86_64_dis.h -libcpu_a_SOURCES = i386_disasm.c x86_64_disasm.c bpf_disasm.c +libcpu_a_SOURCES = i386_disasm.c x86_64_disasm.c bpf_disasm.c riscv_disasm.c libcpu_pic_a_SOURCES = am_libcpu_pic_a_OBJECTS = $(libcpu_a_SOURCES:.c=.os) diff --git a/libcpu/i386_disasm.c b/libcpu/i386_disasm.c index a7e03f95..8a206398 100644 --- a/libcpu/i386_disasm.c +++ b/libcpu/i386_disasm.c @@ -1030,7 +1030,7 @@ i386_disasm (Ebl *ebl __attribute__((unused)), string_end_idx = bufcnt; } else - bufcnt = string_end_idx; + start_idx = bufcnt = string_end_idx; break; case 'e': diff --git a/libcpu/riscv_disasm.c b/libcpu/riscv_disasm.c new file mode 100644 index 00000000..796cdfa8 --- /dev/null +++ b/libcpu/riscv_disasm.c @@ -0,0 +1,1501 @@ +/* Disassembler for RISC-V. + Copyright (C) 2019 Red Hat, Inc. + This file is part of elfutils. + Written by Ulrich Drepper <drepper@redhat.com>, 2019. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils 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 copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "../libebl/libeblP.h" + +#define MACHINE_ENCODING __LITTLE_ENDIAN +#include "memory-access.h" + + +#define ADD_CHAR(ch) \ + do { \ + if (unlikely (bufcnt == bufsize)) \ + goto enomem; \ + buf[bufcnt++] = (ch); \ + } while (0) + +#define ADD_STRING(str) \ + do { \ + const char *_str0 = (str); \ + size_t _len0 = strlen (_str0); \ + ADD_NSTRING (_str0, _len0); \ + } while (0) + +#define ADD_NSTRING(str, len) \ + do { \ + const char *_str = (str); \ + size_t _len = (len); \ + if (unlikely (bufcnt + _len > bufsize)) \ + goto enomem; \ + memcpy (buf + bufcnt, _str, _len); \ + bufcnt += _len; \ + } while (0) + + +static const char *regnames[32] = + { + "zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", + "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5", + "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", + "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6" + }; +#define REG(nr) ((char *) regnames[nr]) +#define REGP(nr) REG (8 + (nr)) + + +static const char *fregnames[32] = + { + "ft0", "ft1", "ft2", "ft3", "ft4", "ft5", "ft6", "ft7", + "fs0", "fs1", "fa0", "fa1", "fa2", "fa3", "fa4", "fa5", + "fa6", "fa7", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7", + "fs8", "fs9", "fs10", "fs11", "ft8", "ft9", "ft10", "ft11" + }; +#define FREG(nr) ((char *) fregnames[nr]) +#define FREGP(nr) FREG (8 + (nr)) + + +struct known_csrs + { + uint16_t nr; + const char *name; + }; + +static int compare_csr (const void *a, const void *b) +{ + const struct known_csrs *ka = (const struct known_csrs *) a; + const struct known_csrs *kb = (const struct known_csrs *) b; + if (ka->nr < kb->nr) + return -1; + return ka->nr == kb->nr ? 0 : 1; +} + + +int +riscv_disasm (Ebl *ebl, + const uint8_t **startp, const uint8_t *end, GElf_Addr addr, + const char *fmt, DisasmOutputCB_t outcb, + DisasmGetSymCB_t symcb __attribute__((unused)), + void *outcbarg, void *symcbarg __attribute__((unused))) +{ + const char *const save_fmt = fmt; + +#define BUFSIZE 512 + char initbuf[BUFSIZE]; + size_t bufcnt; + size_t bufsize = BUFSIZE; + char *buf = initbuf; + + int retval = 0; + while (1) + { + const uint8_t *data = *startp; + assert (data <= end); + if (data + 2 > end) + { + if (data != end) + retval = -1; + break; + } + uint16_t first = read_2ubyte_unaligned (data); + + // Determine length. + size_t length; + if ((first & 0x3) != 0x3) + length = 2; + else if ((first & 0x1f) != 0x1f) + length = 4; + else if ((first & 0x3f) != 0x3f) + length = 6; + else if ((first & 0x7f) != 0x7f) + length = 8; + else + { + uint16_t nnn = (first >> 12) & 0x7; + if (nnn != 0x7) + length = 10 + 2 * nnn; + else + // This is invalid as of the RISC-V spec on 2019-06-21. + // The instruction is at least 192 bits in size so use + // this minimum size. + length = 24; + } + if (data + length > end) + { + retval = -1; + break; + } + + char *mne = NULL; + char mnebuf[32]; + char *op[5] = { NULL, NULL, NULL, NULL, NULL }; + char immbuf[32]; + size_t len; + char *strp = NULL; + char addrbuf[32]; + bufcnt = 0; + int64_t opaddr; + if (length == 2) + { + size_t idx = (first >> 13) * 3 + (first & 0x3); + switch (idx) + { + uint16_t rd; + uint16_t rs1; + uint16_t rs2; + + case 0: + if ((first & 0x1fe0) != 0) + { + mne = "addi"; + op[0] = REGP ((first & 0x1c) >> 2); + op[1] = REG (2); + opaddr = (((first >> 1) & 0x3c0) + | ((first >> 7) & 0x30) + | ((first >> 2) & 0x8) + | ((first >> 4) & 0x4)); + snprintf (addrbuf, sizeof (addrbuf), "%" PRIu64, opaddr); + op[2] = addrbuf; + } + else if (first == 0) + mne = "unimp"; + break; + case 1: + rs1 = (first >> 7) & 0x1f; + int16_t nzimm = ((0 - ((first >> 7) & 0x20)) + | ((first >> 2) & 0x1f)); + if (rs1 == 0) + mne = nzimm == 0 ? "nop" : "c.nop"; + else + { + mne = nzimm == 0 ? "c.addi" : "addi"; + op[0] = op[1] = REG (rs1); + snprintf (addrbuf, sizeof (addrbuf), "%" PRId16, nzimm); + op[2] = addrbuf; + } + break; + case 2: + rs1 = (first >> 7) & 0x1f; + op[0] = op[1] = REG (rs1); + opaddr = ((first >> 7) & 0x20) | ((first >> 2) & 0x1f); + snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr); + op[2] = addrbuf; + mne = rs1 == 0 ? "c.slli" : "slli"; + break; + case 3: + op[0] = FREGP ((first >> 2) & 0x7); + opaddr = ((first << 1) & 0xc0) | ((first >> 7) & 0x38); + snprintf (addrbuf, sizeof (addrbuf), "%" PRIu64 "(%s)", + opaddr, REGP ((first >> 7) & 0x7)); + op[1] = addrbuf; + mne = "fld"; + break; + case 4: + if (ebl->class == ELFCLASS32) + { + mne = "jal"; + opaddr = (((first << 3) & 0x20) | ((first >> 2) & 0xe) + | ((first << 1) & 0x80) | ((first >> 1) | 0x40) + | ((first << 2) & 0x400) | (first & 0xb00) + | ((first >> 6) & 0x10)); + snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr); + op[0] = addrbuf; + } + else + { + int32_t imm = (((UINT32_C (0) - ((first >> 12) & 0x1)) << 5) + | ((first >> 2) & 0x1f)); + uint16_t reg = (first >> 7) & 0x1f; + if (reg == 0) + { + // Reserved + len = snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx16, first); + strp = addrbuf; + } + else + { + if (imm == 0) + mne = "sext.w"; + else + { + mne = "addiw"; + snprintf (addrbuf, sizeof (addrbuf), "%" PRId32, imm); + op[2] = addrbuf; + } + op[0] = op[1] = REG (reg); + } + } + break; + case 5: + op[0] = FREG ((first >> 7) & 0x1f); + opaddr = ((first << 4) & 0x1c0) | ((first >> 7) & 0x20) | ((first >> 2) & 0x18); + snprintf (addrbuf, sizeof (addrbuf), "%" PRIu64 "(%s)", opaddr, REG (2)); + op[1] = addrbuf; + mne = "fld"; + break; + case 6: + case 18: + mne = idx == 6 ? "lw" : "sw"; + op[0] = REGP ((first >> 2) & 0x7); + opaddr = (((first >> 7) & 0x38) | ((first << 1) & 0x40) + | ((first >> 4) & 0x4)); + snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)", + opaddr, REGP ((first >> 7) & 0x7)); + op[1] = addrbuf; + break; + case 7: + mne = (first & 0xf80) == 0 ? "c.li" : "li"; + op[0] = REG((first >> 7) & 0x1f); + snprintf (addrbuf, sizeof (addrbuf), "%" PRId16, + (UINT16_C (0) - ((first >> 7) & 0x20)) | ((first >> 2) & 0x1f)); + op[1] = addrbuf; + break; + case 8: + rd = ((first >> 7) & 0x1f); + if (rd == 0) + { + len = snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx16, first); + strp = addrbuf; + } + else + { + uint16_t uimm = (((first << 4) & 0xc0) + | ((first >> 7) & 0x20) + | ((first >> 2) & 0x1c)); + mne = "lw"; + op[0] = REG (rd); + snprintf (addrbuf, sizeof (addrbuf), "%" PRIu16 "(%s)", uimm, REG (2)); + op[1] = addrbuf; + } + break; + case 9: + if (ebl->class == ELFCLASS32) + { + mne = "flw"; + op[0] = FREGP ((first >> 2) & 0x7); + opaddr = (((first << 1) & 0x40) + | ((first >> 7) & 0x38) + | ((first >> 4) & 0x4)); + } + else + { + mne = "ld"; + op[0] = REGP ((first >> 2) & 0x7); + opaddr = ((first >> 7) & 0x38) | ((first << 1) & 0xc0); + } + snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)", + opaddr, REGP ((first >> 7) & 0x7)); + op[1] = addrbuf; + break; + case 10: + if ((first & 0xf80) == (2 << 7)) + { + mne = "addi"; + op[0] = op[1] = REG (2); + opaddr = (((first >> 2) & 0x10) | ((first << 3) & 0x20) + | ((first << 1) & 0x40) | ((first << 4) & 0x180) + | ((UINT64_C (0) - ((first >> 12) & 0x1)) << 9)); + snprintf (addrbuf, sizeof (addrbuf), "%" PRId64, opaddr); + op[2] = addrbuf; + } + else + { + mne = "lui"; + op[0] = REG((first & 0xf80) >> 7); + opaddr = (((UINT64_C (0) - ((first >> 12) & 0x1)) & ~0x1f) + | ((first >> 2) & 0x1f)); + snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr & 0xfffff); + op[1] = addrbuf; + } + break; + case 11: + if (ebl->class == ELFCLASS32) + { + mne = "flw"; + op[0] = FREG ((first >> 7) & 0x1f); + opaddr = (((first << 4) & 0xc0) + | ((first >> 7) & 0x20) + | ((first >> 2) & 0x1c)); + } + else + { + mne = "ld"; + op[0] = REG ((first >> 7) & 0x1f); + opaddr = (((first << 4) & 0x1c0) + | ((first >> 7) & 0x20) + | ((first >> 2) & 0x18)); + } + snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)", opaddr, REG (2)); + op[1] = addrbuf; + break; + case 13: + if ((first & 0xc00) != 0xc00) + { + int16_t imm = ((first >> 7) & 0x20) | ((first >> 2) & 0x1f); + if ((first & 0xc00) == 0x800) + { + imm |= 0 - (imm & 0x20); + mne = "andi"; + snprintf (addrbuf, sizeof (addrbuf), "%" PRId16, imm); + } + else + { + if (ebl->class != ELFCLASS32 || imm < 32) + { + mne = (first & 0x400) ? "srai" : "srli"; + if (imm == 0) + { + strcpy (stpcpy (mnebuf, "c."), mne); + mne = mnebuf; + } + } + snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx16, imm); + } + op[2] = addrbuf; + } + else + { + op[2] = REGP ((first >> 2) & 0x7); + static const char *const arithmne[8] = + { + "sub", "xor", "or", "and", "subw", "addw", NULL, NULL + }; + mne = (char *) arithmne[((first >> 10) & 0x4) | ((first >> 5) & 0x3)]; + } + op[0] = op[1] = REGP ((first >> 7) & 0x7); + break; + case 14: + rs1 = (first >> 7) & 0x1f; + rs2 = (first >> 2) & 0x1f; + op[0] = REG (rs1); + if ((first & 0x1000) == 0) + { + if (rs2 == 0) + { + op[1] = NULL; + if (rs1 == 1) + { + mne = "ret"; + op[0] = NULL; + } + else + mne = "jr"; + } + else + { + mne = rs1 != 0 ? "mv" : "c.mv"; + op[1] = REG (rs2); + } + } + else + { + if (rs2 == 0) + { + if (rs1 == 0) + { + mne = "ebreak"; + op[0] = op[1] = NULL; + } + else + mne = "jalr"; + } + else + { + mne = rs1 != 0 ? "add" : "c.add"; + op[2] = REG (rs2); + op[1] = op[0]; + } + } + break; + case 15: + op[0] = FREGP ((first >> 2) & 0x7); + opaddr = ((first << 1) & 0xc0) | ((first >> 7) & 0x38); + snprintf (addrbuf, sizeof (addrbuf), "%" PRIu64 "(%s)", + opaddr, REGP ((first >> 7) & 0x7)); + op[1] = addrbuf; + mne = "fsd"; + break; + case 16: + opaddr = (((INT64_C (0) - ((first >> 12) & 0x1)) << 11) + | ((first << 2) & 0x400) + | ((first >> 1) & 0x300) + | ((first << 1) & 0x80) + | ((first >> 1) & 0x40) + | ((first << 3) & 0x20) + | ((first >> 7) & 0x10) + | ((first >> 2) & 0xe)); + mne = "j"; + // TODO translate address + snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, addr + opaddr); + op[0] = addrbuf; + break; + case 17: + op[0] = FREG ((first >> 2) & 0x1f); + opaddr = ((first >> 1) & 0x1c0) | ((first >> 7) & 0x38); + snprintf (addrbuf, sizeof (addrbuf), "%" PRIu64 "(%s)", opaddr, REG (2)); + op[1] = addrbuf; + mne = "fsd"; + break; + case 19: + case 22: + mne = idx == 19 ? "beqz" : "bnez"; + op[0] = REG (8 + ((first >> 7) & 0x7)); + opaddr = addr + (((UINT64_C (0) - ((first >> 12) & 0x1)) & ~0xff) + | ((first << 1) & 0xc0) | ((first << 3) & 0x20) + | ((first >> 7) & 0x18) | ((first >> 2) & 0x6)); + // TODO translate address + snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr); + op[1] = addrbuf; + break; + case 20: + op[0] = REG ((first >> 2) & 0x1f); + opaddr = ((first >> 1) & 0xc0) | ((first >> 7) & 0x3c); + snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)", opaddr, REG (2)); + op[1] = addrbuf; + mne = "sw"; + break; + case 21: + if (idx == 18 || ebl->class == ELFCLASS32) + { + mne = "fsw"; + op[0] = FREGP ((first >> 2) & 0x7); + opaddr = (((first >> 7) & 0x38) | ((first << 1) & 0x40) + | ((first >> 4) & 0x4)); + } + else + { + mne = "sd"; + op[0] = REGP ((first >> 2) & 0x7); + opaddr = ((first >> 7) & 0x38) | ((first << 1) & 0xc0); + } + snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)", + opaddr, REGP ((first >> 7) & 0x7)); + op[1] = addrbuf; + break; + case 23: + if (idx == 18 || ebl->class == ELFCLASS32) + { + mne = "fsw"; + op[0] = FREG ((first & 0x7c) >> 2); + opaddr = ((first & 0x1e00) >> 7) | ((first & 0x180) >> 1); + } + else + { + mne = "sd"; + op[0] = REG ((first & 0x7c) >> 2); + opaddr = ((first & 0x1c00) >> 7) | ((first & 0x380) >> 1); + } + snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)", opaddr, REG (2)); + op[1] = addrbuf; + break; + default: + break; + } + + if (strp == NULL && mne == NULL) + { + len = snprintf (immbuf, sizeof (immbuf), "0x%04" PRIx16, first); + strp = immbuf; + } + } + else if (length == 4) + { + uint32_t word = read_4ubyte_unaligned (data); + size_t idx = (word >> 2) & 0x1f; + + switch (idx) + { + static const char widthchar[4] = { 's', 'd', '\0', 'q' }; + static const char intwidthchar[4] = { 'w', 'd', '\0', 'q' }; + static const char *const rndmode[8] = { "rne", "rtz", "rdn", "rup", "rmm", "???", "???", "dyn" }; + uint32_t rd; + uint32_t rs1; + uint32_t rs2; + uint32_t rs3; + uint32_t func; + + case 0x00: + case 0x01: + // LOAD and LOAD-FP + rd = (word >> 7) & 0x1f; + op[0] = idx == 0x00 ? REG (rd) : FREG (rd); + opaddr = ((int32_t) word) >> 20; + snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)", + opaddr, REG ((word >> 15) & 0x1f)); + op[1] = addrbuf; + func = (word >> 12) & 0x7; + static const char *const loadmne[8] = + { + "lb", "lh", "lw", "ld", "lbu", "lhu", "lwu", NULL + }; + static const char *const floadmne[8] = + { + NULL, NULL, "flw", "fld", "flq", NULL, NULL, NULL + }; + mne = (char *) (idx == 0x00 ? loadmne[func] : floadmne[func]); + break; + case 0x03: + // MISC-MEM + rd = (word >> 7) & 0x1f; + rs1 = (word >> 15) & 0x1f; + func = (word >> 12) & 0x7; + + if (word == 0x8330000f) + mne = "fence.tso"; + else if (word == 0x0000100f) + mne = "fence.i"; + else if (func == 0 && rd == 0 && rs1 == 0 && (word & 0xf0000000) == 0) + { + static const char *const order[16] = + { + "unknown", "w", "r", "rw", "o", "ow", "or", "orw", + "i", "iw", "ir", "irw", "io", "iow", "ior", "iorw" + }; + uint32_t pred = (word >> 20) & 0xf; + uint32_t succ = (word >> 24) & 0xf; + if (pred != 0xf || succ != 0xf) + { + op[0] = (char *) order[succ]; + op[1] = (char *) order[pred]; + } + mne = "fence"; + } + break; + case 0x04: + case 0x06: + // OP-IMM and OP-IMM32 + rd = (word >> 7) & 0x1f; + op[0] = REG (rd); + rs1 = (word >> 15) & 0x1f; + op[1] = REG (rs1); + opaddr = ((int32_t) word) >> 20; + static const char *const opimmmne[8] = + { + "addi", NULL, "slti", "sltiu", "xori", NULL, "ori", "andi" + }; + func = (word >> 12) & 0x7; + mne = (char *) opimmmne[func]; + if (mne == NULL) + { + const uint64_t shiftmask = ebl->class == ELFCLASS32 ? 0x1f : 0x3f; + if (func == 0x1 && (opaddr & ~shiftmask) == 0) + mne = "slli"; + else if (func == 0x5 && (opaddr & ~shiftmask) == 0) + mne = "srli"; + else if (func == 0x5 && (opaddr & ~shiftmask) == 0x400) + mne = "srai"; + snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr & shiftmask); + op[2] = addrbuf; + } + else if (func == 0x0 && (rd != 0 || idx == 0x06) && rs1 == 0 && rd != 0) + { + mne = "li"; + snprintf (addrbuf, sizeof (addrbuf), "%" PRId64, opaddr); + op[1] = addrbuf; + } + else if (func == 0x00 && opaddr == 0) + { + if (idx == 0x06) + mne ="sext."; + else if (rd == 0) + { + mne = "nop"; + op[0] = op[1] = NULL; + } + else + mne = "mv"; + } + else if (func == 0x3 && opaddr == 1) + mne = "seqz"; + else if (func == 0x4 && opaddr == -1) + { + mne = "not"; + op[2] = NULL; + } + else + { + snprintf (addrbuf, sizeof (addrbuf), "%" PRId64, opaddr); + op[2] = addrbuf; + + if (func == 0x0 && rs1 == 0 && rd != 0) + { + op[1] = op[2]; + op[2] = NULL; + mne = "li"; + } + } + if (mne != NULL && idx == 0x06) + { + mne = strcpy (mnebuf, mne); + strcat (mnebuf, "w"); + } + break; + case 0x05: + case 0x0d: + // LUI and AUIPC + mne = idx == 0x05 ? "auipc" : "lui"; + op[0] = REG ((word >> 7) & 0x1f); + opaddr = word >> 12; + snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr); + op[1] = addrbuf; + break; + case 0x08: + case 0x09: + // STORE and STORE-FP + rs2 = (word >> 20) & 0x1f; + op[0] = idx == 0x08 ? REG (rs2) : FREG (rs2); + opaddr = ((((int64_t) ((int32_t) word) >> 20)) & ~0x1f) | ((word >> 7) & 0x1f); + snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)", + opaddr, REG ((word >> 15) & 0x1f)); + op[1] = addrbuf; + func = (word >> 12) & 0x7; + static const char *const storemne[8] = + { + "sb", "sh", "sw", "sd", NULL, NULL, NULL, NULL + }; + static const char *const fstoremne[8] = + { + NULL, NULL, "fsw", "fsd", "fsq", NULL, NULL, NULL + }; + mne = (char *) (idx == 0x08 ? storemne[func] : fstoremne[func]); + break; + case 0x0b: + // AMO + op[0] = REG ((word >> 7) & 0x1f); + rs1 = (word >> 15) & 0x1f; + rs2 = (word >> 20) & 0x1f; + snprintf (addrbuf, sizeof (addrbuf), "(%s)", REG (rs1)); + op[2] = addrbuf; + size_t width = (word >> 12) & 0x7; + func = word >> 27; + static const char *const amomne[32] = + { + "amoadd", "amoswap", "lr", "sc", "amoxor", NULL, NULL, NULL, + "amoor", NULL, NULL, NULL, "amoand", NULL, NULL, NULL, + "amomin", NULL, NULL, NULL, "amomax", NULL, NULL, NULL, + "amominu", NULL, NULL, NULL, "amomaxu", NULL, NULL, NULL + }; + if (amomne[func] != NULL && width >= 2 && width <= 3 + && (func != 0x02 || rs2 == 0)) + { + if (func == 0x02) + { + op[1] = op[2]; + op[2] = NULL; + } + else + op[1] = REG (rs2); + + char *cp = stpcpy (mnebuf, amomne[func]); + *cp++ = '.'; + *cp++ = " wd "[width]; + assert (cp[-1] != ' '); + static const char *const aqrlstr[4] = + { + "", ".rl", ".aq", ".aqrl" + }; + strcpy (cp, aqrlstr[(word >> 25) & 0x3]); + mne = mnebuf; + } + break; + case 0x0c: + case 0x0e: + // OP and OP-32 + if ((word & 0xbc000000) == 0) + { + rs1 = (word >> 15) & 0x1f; + rs2 = (word >> 20) & 0x1f; + op[0] = REG ((word >> 7) & 0x1f); + func = ((word >> 21) & 0x10) | ((word >> 27) & 0x8) | ((word >> 12) & 0x7); + static const char *const arithmne2[32] = + { + "add", "sll", "slt", "sltu", "xor", "srl", "or", "and", + "sub", NULL, NULL, NULL, NULL, "sra", NULL, NULL, + "mul", "mulh", "mulhsu", "mulhu", "div", "divu", "rem", "remu", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL + }; + static const char *const arithmne3[32] = + { + "addw", "sllw", NULL, NULL, NULL, "srlw", NULL, NULL, + "subw", NULL, NULL, NULL, NULL, "sraw", NULL, NULL, + "mulw", NULL, NULL, NULL, "divw", "divuw", "remw", "remuw", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL + }; + if (func == 8 && rs1 == 0) + { + mne = idx == 0x0c ? "neg" : "negw"; + op[1] = REG (rs2); + } + else if (idx == 0x0c && rs2 == 0 && func == 2) + { + op[1] = REG (rs1); + mne = "sltz"; + } + else if (idx == 0x0c && rs1 == 0 && (func == 2 || func == 3)) + { + op[1] = REG (rs2); + mne = func == 2 ? "sgtz" : "snez"; + } + else + { + mne = (char *) (idx == 0x0c ? arithmne2[func] : arithmne3[func]); + op[1] = REG (rs1); + op[2] = REG (rs2); + } + } + break; + case 0x10: + case 0x11: + case 0x12: + case 0x13: + // MADD, MSUB, NMSUB, NMADD + if ((word & 0x06000000) != 0x04000000) + { + rd = (word >> 7) & 0x1f; + rs1 = (word >> 15) & 0x1f; + rs2 = (word >> 20) & 0x1f; + rs3 = (word >> 27) & 0x1f; + uint32_t rm = (word >> 12) & 0x7; + width = (word >> 25) & 0x3; + + static const char *const fmamne[4] = + { + "fmadd.", "fmsub.", "fnmsub.", "fnmadd." + }; + char *cp = stpcpy (mnebuf, fmamne[idx & 0x3]); + *cp++ = widthchar[width]; + *cp = '\0'; + mne = mnebuf; + op[0] = FREG (rd); + op[1] = FREG (rs1); + op[2] = FREG (rs2); + op[3] = FREG (rs3); + if (rm != 0x7) + op[4] = (char *) rndmode[rm]; + } + break; + case 0x14: + // OP-FP + if ((word & 0x06000000) != 0x04000000) + { + width = (word >> 25) & 0x3; + rd = (word >> 7) & 0x1f; + rs1 = (word >> 15) & 0x1f; + rs2 = (word >> 20) & 0x1f; + func = word >> 27; + uint32_t rm = (word >> 12) & 0x7; + if (func < 4) + { + static const char *const fpop[4] = + { + "fadd", "fsub", "fmul", "fdiv" + }; + char *cp = stpcpy (mnebuf, fpop[func]); + *cp++ = '.'; + *cp++ = widthchar[width]; + *cp = '\0'; + mne = mnebuf; + op[0] = FREG (rd); + op[1] = FREG (rs1); + op[2] = FREG (rs2); + if (rm != 0x7) + op[3] = (char *) rndmode[rm]; + } + else if (func == 0x1c && width != 2 && rs2 == 0 && rm <= 1) + { + char *cp; + if (rm == 0) + { + cp = stpcpy (mnebuf, "fmv.x."); + *cp++ = intwidthchar[width]; + } + else + { + cp = stpcpy (mnebuf, "fclass."); + *cp++ = widthchar[width]; + } + *cp = '\0'; + mne = mnebuf; + op[0] = REG (rd); + op[1] = FREG (rs1); + } + else if (func == 0x1e && width != 2 && rs2 == 0 && rm == 0) + { + char *cp = stpcpy (mnebuf, "fmv."); + *cp++ = intwidthchar[width]; + strcpy (cp, ".x"); + mne = mnebuf; + op[0] = FREG (rd); + op[1] = REG (rs1); + } + else if (func == 0x14) + { + uint32_t cmpop = (word >> 12) & 0x7; + if (cmpop < 3) + { + static const char *const mnefpcmp[3] = + { + "fle", "flt", "feq" + }; + char *cp = stpcpy (mnebuf, mnefpcmp[cmpop]); + *cp++ = '.'; + *cp++ = widthchar[width]; + *cp = '\0'; + mne = mnebuf; + op[0] = REG (rd); + op[1] = FREG (rs1); + op[2] = FREG (rs2); + } + } + else if (func == 0x04) + { + uint32_t cmpop = (word >> 12) & 0x7; + if (cmpop < 3) + { + op[0] = FREG (rd); + op[1] = FREG (rs1); + + static const char *const mnefpcmp[3] = + { + "fsgnj.", "fsgnjn.", "fsgnjx." + }; + static const char *const altsignmne[3] = + { + "fmv.", "fneg.", "fabs." + }; + char *cp = stpcpy (mnebuf, rs1 == rs2 ? altsignmne[cmpop] : mnefpcmp[cmpop]); + *cp++ = widthchar[width]; + *cp = '\0'; + mne = mnebuf; + + if (rs1 != rs2) + op[2] = FREG (rs2); + } + } + else if (func == 0x08 && width != 2 && rs2 <= 3 && rs2 != 2 && rs2 != width) + { + op[0] = FREG (rd); + op[1] = FREG (rs1); + char *cp = stpcpy (mnebuf, "fcvt."); + *cp++ = widthchar[width]; + *cp++ = '.'; + *cp++ = widthchar[rs2]; + *cp = '\0'; + mne = mnebuf; + } + else if ((func & 0x1d) == 0x18 && width != 2 && rs2 < 4) + { + char *cp = stpcpy (mnebuf, "fcvt."); + if (func == 0x18) + { + *cp++ = rs2 >= 2 ? 'l' : 'w'; + if ((rs2 & 1) == 1) + *cp++ = 'u'; + *cp++ = '.'; + *cp++ = widthchar[width]; + *cp = '\0'; + op[0] = REG (rd); + op[1] = FREG (rs1); + } + else + { + *cp++ = widthchar[width]; + *cp++ = '.'; + *cp++ = rs2 >= 2 ? 'l' : 'w'; + if ((rs2 & 1) == 1) + *cp++ = 'u'; + *cp = '\0'; + op[0] = FREG (rd); + op[1] = REG (rs1); + } + mne = mnebuf; + if (rm != 0x7 && (func == 0x18 || width == 0 || rs2 >= 2)) + op[2] = (char *) rndmode[rm]; + } + else if (func == 0x0b && rs2 == 0) + { + op[0] = FREG (rd); + op[1] = FREG (rs1); + char *cp = stpcpy (mnebuf, "fsqrt."); + *cp++ = widthchar[width]; + *cp = '\0'; + mne = mnebuf; + if (rm != 0x7) + op[2] = (char *) rndmode[rm]; + } + else if (func == 0x05 && rm < 2) + { + op[0] = FREG (rd); + op[1] = FREG (rs1); + op[2] = FREG (rs2); + char *cp = stpcpy (mnebuf, rm == 0 ? "fmin." : "fmax."); + *cp++ = widthchar[width]; + *cp = '\0'; + mne = mnebuf; + } + else if (func == 0x14 && rm <= 0x2) + { + op[0] = REG (rd); + op[1] = FREG (rs1); + op[2] = FREG (rs2); + static const char *const fltcmpmne[3] = + { + "fle.", "flt.", "feq." + }; + char *cp = stpcpy (mnebuf, fltcmpmne[rm]); + *cp++ = widthchar[width]; + *cp = '\0'; + mne = mnebuf; + } + } + break; + case 0x18: + // BRANCH + rs1 = (word >> 15) & 0x1f; + op[0] = REG (rs1); + rs2 = (word >> 20) & 0x1f; + op[1] = REG (rs2); + opaddr = addr + (((UINT64_C (0) - (word >> 31)) << 12) + + ((word << 4) & 0x800) + + ((word >> 20) & 0x7e0) + + ((word >> 7) & 0x1e)); + // TODO translate address + snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr); + op[2] = addrbuf; + static const char *const branchmne[8] = + { + "beq", "bne", NULL, NULL, "blt", "bge", "bltu", "bgeu" + }; + func = (word >> 12) & 0x7; + mne = (char *) branchmne[func]; + if (rs1 == 0 && func == 5) + { + op[0] = op[1]; + op[1] = op[2]; + op[2] = NULL; + mne = "blez"; + } + else if (rs1 == 0 && func == 4) + { + op[0] = op[1]; + op[1] = op[2]; + op[2] = NULL; + mne = "bgtz"; + } + else if (rs2 == 0) + { + if (func == 0 || func == 1 || func == 4 || func == 5) + { + op[1] = op[2]; + op[2] = NULL; + strcpy (stpcpy (mnebuf, mne), "z"); + mne = mnebuf; + } + } + else if (func == 5 || func == 7) + { + // binutils use these opcodes and the reverse parameter order + char *tmp = op[0]; + op[0] = op[1]; + op[1] = tmp; + mne = func == 5 ? "ble" : "bleu"; + } + break; + case 0x19: + // JALR + if ((word & 0x7000) == 0) + { + rd = (word >> 7) & 0x1f; + rs1 = (word >> 15) & 0x1f; + opaddr = (int32_t) word >> 20; + size_t next = 0; + if (rd > 1) + op[next++] = REG (rd); + if (opaddr == 0) + { + if (rs1 != 0 || next == 0) + op[next] = REG (rs1); + } + else + { + snprintf (addrbuf, sizeof (addrbuf), "%" PRId64 "(%s)", opaddr, REG (rs1)); + op[next] = addrbuf; + } + mne = rd == 0 ? "jr" : "jalr"; + } + break; + case 0x1b: + // JAL + rd = (word >> 7) & 0x1f; + if (rd != 0) + op[0] = REG (rd); + opaddr = addr + ((UINT64_C (0) - ((word >> 11) & 0x100000)) + | (word & 0xff000) + | ((word >> 9) & 0x800) + | ((word >> 20) & 0x7fe)); + // TODO translate address + snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr); + op[rd != 0] = addrbuf; + mne = rd == 0 ? "j" : "jal"; + break; + case 0x1c: + // SYSTEM + rd = (word >> 7) & 0x1f; + rs1 = (word >> 15) & 0x1f; + if (word == 0x00000073) + mne = "ecall"; + else if (word == 0x00100073) + mne = "ebreak"; + else if (word == 0x00200073) + mne = "uret"; + else if (word == 0x10200073) + mne = "sret"; + else if (word == 0x30200073) + mne = "mret"; + else if (word == 0x10500073) + mne = "wfi"; + else if ((word & 0x3000) == 0x2000 && rs1 == 0) + { + uint32_t csr = word >> 20; + if (/* csr >= 0x000 && */ csr <= 0x007) + { + static const char *const unprivrw[4] = + { + NULL, "frflags", "frrm", "frsr", + }; + mne = (char *) unprivrw[csr - 0x000]; + } + else if (csr >= 0xc00 && csr <= 0xc03) + { + static const char *const unprivrolow[3] = + { + "rdcycle", "rdtime", "rdinstret" + }; + mne = (char *) unprivrolow[csr - 0xc00]; + } + op[0] = REG ((word >> 7) & 0x1f); + } + else if ((word & 0x3000) == 0x1000 && rd == 0) + { + uint32_t csr = word >> 20; + if (/* csr >= 0x000 && */ csr <= 0x003) + { + static const char *const unprivrs[4] = + { + NULL, "fsflags", "fsrm", "fssr", + }; + static const char *const unprivrsi[4] = + { + NULL, "fsflagsi", "fsrmi", NULL + }; + mne = (char *) ((word & 0x4000) == 0 ? unprivrs : unprivrsi)[csr - 0x000]; + + if ((word & 0x4000) == 0) + op[0] = REG ((word >> 15) & 0x1f); + else + { + snprintf (immbuf, sizeof (immbuf), "%" PRIu32, (word >> 15) & 0x1f); + op[0] = immbuf; + } + } + } + if (mne == NULL && (word & 0x3000) != 0) + { + static const char *const mnecsr[8] = + { + NULL, "csrrw", "csrrs", "csrrc", + NULL, "csrrwi", "csrrsi", "csrrci" + }; + static const struct known_csrs known[] = + { + // This list must remain sorted by NR. + { 0x000, "ustatus" }, + { 0x001, "fflags" }, + { 0x002, "fram" }, + { 0x003, "fcsr" }, + { 0x004, "uie" }, + { 0x005, "utvec" }, + { 0x040, "uscratch" }, + { 0x041, "uepc" }, + { 0x042, "ucause" }, + { 0x043, "utval" }, + { 0x044, "uip" }, + { 0x100, "sstatus" }, + { 0x102, "sedeleg" }, + { 0x103, "sideleg" }, + { 0x104, "sie" }, + { 0x105, "stvec" }, + { 0x106, "scounteren" }, + { 0x140, "sscratch" }, + { 0x141, "sepc" }, + { 0x142, "scause" }, + { 0x143, "stval" }, + { 0x144, "sip" }, + { 0x180, "satp" }, + { 0x200, "vsstatus" }, + { 0x204, "vsie" }, + { 0x205, "vstvec" }, + { 0x240, "vsscratch" }, + { 0x241, "vsepc" }, + { 0x242, "vscause" }, + { 0x243, "vstval" }, + { 0x244, "vsip" }, + { 0x280, "vsatp" }, + { 0x600, "hstatus" }, + { 0x602, "hedeleg" }, + { 0x603, "hideleg" }, + { 0x605, "htimedelta" }, + { 0x606, "hcounteren" }, + { 0x615, "htimedeltah" }, + { 0x680, "hgatp" }, + { 0xc00, "cycle" }, + { 0xc01, "time" }, + { 0xc02, "instret" }, + { 0xc03, "hpmcounter3" }, + { 0xc04, "hpmcounter4" }, + { 0xc05, "hpmcounter5" }, + { 0xc06, "hpmcounter6" }, + { 0xc07, "hpmcounter7" }, + { 0xc08, "hpmcounter8" }, + { 0xc09, "hpmcounter9" }, + { 0xc0a, "hpmcounter10" }, + { 0xc0b, "hpmcounter11" }, + { 0xc0c, "hpmcounter12" }, + { 0xc0d, "hpmcounter13" }, + { 0xc0e, "hpmcounter14" }, + { 0xc0f, "hpmcounter15" }, + { 0xc10, "hpmcounter16" }, + { 0xc11, "hpmcounter17" }, + { 0xc12, "hpmcounter18" }, + { 0xc13, "hpmcounter19" }, + { 0xc14, "hpmcounter20" }, + { 0xc15, "hpmcounter21" }, + { 0xc16, "hpmcounter22" }, + { 0xc17, "hpmcounter23" }, + { 0xc18, "hpmcounter24" }, + { 0xc19, "hpmcounter25" }, + { 0xc1a, "hpmcounter26" }, + { 0xc1b, "hpmcounter27" }, + { 0xc1c, "hpmcounter28" }, + { 0xc1d, "hpmcounter29" }, + { 0xc1e, "hpmcounter30" }, + { 0xc1f, "hpmcounter31" }, + { 0xc80, "cycleh" }, + { 0xc81, "timeh" }, + { 0xc82, "instreth" }, + { 0xc83, "hpmcounter3h" }, + { 0xc84, "hpmcounter4h" }, + { 0xc85, "hpmcounter5h" }, + { 0xc86, "hpmcounter6h" }, + { 0xc87, "hpmcounter7h" }, + { 0xc88, "hpmcounter8h" }, + { 0xc89, "hpmcounter9h" }, + { 0xc8a, "hpmcounter10h" }, + { 0xc8b, "hpmcounter11h" }, + { 0xc8c, "hpmcounter12h" }, + { 0xc8d, "hpmcounter13h" }, + { 0xc8e, "hpmcounter14h" }, + { 0xc8f, "hpmcounter15h" }, + { 0xc90, "hpmcounter16h" }, + { 0xc91, "hpmcounter17h" }, + { 0xc92, "hpmcounter18h" }, + { 0xc93, "hpmcounter19h" }, + { 0xc94, "hpmcounter20h" }, + { 0xc95, "hpmcounter21h" }, + { 0xc96, "hpmcounter22h" }, + { 0xc97, "hpmcounter23h" }, + { 0xc98, "hpmcounter24h" }, + { 0xc99, "hpmcounter25h" }, + { 0xc9a, "hpmcounter26h" }, + { 0xc9b, "hpmcounter27h" }, + { 0xc9c, "hpmcounter28h" }, + { 0xc9d, "hpmcounter29h" }, + { 0xc9e, "hpmcounter30h" }, + { 0xc9f, "hpmcounter31h" }, + }; + uint32_t csr = word >> 20; + uint32_t instr = (word >> 12) & 0x7; + size_t last = 0; + if (rd != 0) + op[last++] = REG (rd); + struct known_csrs key = { csr, NULL }; + struct known_csrs *found = bsearch (&key, known, + sizeof (known) / sizeof (known[0]), + sizeof (known[0]), + compare_csr); + if (found) + op[last] = (char *) found->name; + else + { + snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx32, csr); + op[last] = addrbuf; + } + ++last; + if ((word & 0x4000) == 0) + op[last] = REG ((word >> 15) & 0x1f); + else + { + snprintf (immbuf, sizeof (immbuf), "%" PRIu32, (word >> 15) & UINT32_C(0x1f)); + op[last] = immbuf; + } + if (instr == 1 && rd == 0) + mne = "csrw"; + else if (instr == 2 && rd == 0) + mne = "csrs"; + else if (instr == 6 && rd == 0) + mne = "csrsi"; + else if (instr == 2 && rs1 == 0) + mne = "csrr"; + else if (instr == 3 && rd == 0) + mne = "csrc"; + else + mne = (char *) mnecsr[instr]; + } + break; + default: + break; + } + + if (strp == NULL && mne == NULL) + { + len = snprintf (addrbuf, sizeof (addrbuf), "0x%08" PRIx32, word); + strp = addrbuf; + } + } + else + { + // No instruction encodings defined for these sizes yet. + char *cp = stpcpy (mnebuf, "0x"); + assert (length % 2 == 0); + for (size_t i = 0; i < length; i += 2) + cp += snprintf (cp, mnebuf + sizeof (mnebuf) - cp, "%04" PRIx16, + read_2ubyte_unaligned (data + i)); + strp = mnebuf; + len = cp - mnebuf; + } + + if (strp == NULL) + { + + if (0) + { + /* Resize the buffer. */ + char *oldbuf; + enomem: + oldbuf = buf; + if (buf == initbuf) + buf = malloc (2 * bufsize); + else + buf = realloc (buf, 2 * bufsize); + if (buf == NULL) + { + buf = oldbuf; + retval = ENOMEM; + goto do_ret; + } + bufsize *= 2; + + bufcnt = 0; + } + + unsigned long string_end_idx = 0; + fmt = save_fmt; + const char *deferred_start = NULL; + size_t deferred_len = 0; + // XXX Can we get this from color.c? + static const char color_off[] = "\e[0m"; + while (*fmt != '\0') + { + if (*fmt != '%') + { + char ch = *fmt++; + if (ch == '\\') + { + switch ((ch = *fmt++)) + { + case '0' ... '7': + { + int val = ch - '0'; + ch = *fmt; + if (ch >= '0' && ch <= '7') + { + val *= 8; + val += ch - '0'; + ch = *++fmt; + if (ch >= '0' && ch <= '7' && val < 32) + { + val *= 8; + val += ch - '0'; + ++fmt; + } + } + ch = val; + } + break; + + case 'n': + ch = '\n'; + break; + + case 't': + ch = '\t'; + break; + + default: + retval = EINVAL; + goto do_ret; + } + } + else if (ch == '\e' && *fmt == '[') + { + deferred_start = fmt - 1; + do + ++fmt; + while (*fmt != 'm' && *fmt != '\0'); + + if (*fmt == 'm') + { + deferred_len = ++fmt - deferred_start; + continue; + } + + fmt = deferred_start + 1; + deferred_start = NULL; + } + ADD_CHAR (ch); + continue; + } + ++fmt; + + int width = 0; + while (isdigit (*fmt)) + width = width * 10 + (*fmt++ - '0'); + + int prec = 0; + if (*fmt == '.') + while (isdigit (*++fmt)) + prec = prec * 10 + (*fmt - '0'); + + size_t start_idx = bufcnt; + size_t non_printing = 0; + switch (*fmt++) + { + case 'm': + if (deferred_start != NULL) + { + ADD_NSTRING (deferred_start, deferred_len); + non_printing += deferred_len; + } + + ADD_STRING (mne); + + if (deferred_start != NULL) + { + ADD_STRING (color_off); + non_printing += strlen (color_off); + } + + string_end_idx = bufcnt; + break; + + case 'o': + if (op[prec - 1] != NULL) + { + if (deferred_start != NULL) + { + ADD_NSTRING (deferred_start, deferred_len); + non_printing += deferred_len; + } + + ADD_STRING (op[prec - 1]); + + if (deferred_start != NULL) + { + ADD_STRING (color_off); + non_printing += strlen (color_off); + } + + string_end_idx = bufcnt; + } + else + bufcnt = string_end_idx; + break; + + case 'e': + string_end_idx = bufcnt; + break; + + case 'a': + /* Pad to requested column. */ + while (bufcnt - non_printing < (size_t) width) + ADD_CHAR (' '); + width = 0; + break; + + case 'l': + // TODO + break; + + default: + abort(); + } + + /* Pad according to the specified width. */ + while (bufcnt - non_printing < start_idx + width) + ADD_CHAR (' '); + } + + strp = buf; + len = bufcnt; + } + + addr += length; + *startp = data + length; + retval = outcb (strp, len, outcbarg); + if (retval != 0) + break; + } + + do_ret: + if (buf != initbuf) + free (buf); + + return retval; +} |