#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 "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);
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
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)
{
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";
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));
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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));
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)
{
char *tmp = op[0];
op[0] = op[1];
op[1] = tmp;
mne = func == 5 ? "ble" : "bleu";
}
break;
case 0x19:
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:
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));
snprintf (addrbuf, sizeof (addrbuf), "0x%" PRIx64, opaddr);
op[rd != 0] = addrbuf;
mne = rd == 0 ? "j" : "jal";
break;
case 0x1c:
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 <= 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 <= 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[] =
{
{ 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
{
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)
{
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;
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':
while (bufcnt - non_printing < (size_t) width)
ADD_CHAR (' ');
width = 0;
break;
case 'l':
break;
default:
abort();
}
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;
}