/* Disassembler for RISC-V. Copyright (C) 2019 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper , 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 . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #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 = (((UINT64_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; }