public inbox for elfutils@sourceware.org
 help / color / mirror / Atom feed
* RISC-V disassembler
@ 2019-09-07  9:18 Ulrich Drepper
  2019-09-07 20:17 ` Mark Wielaard
  2019-09-09 21:25 ` Jim Wilson
  0 siblings, 2 replies; 4+ messages in thread
From: Ulrich Drepper @ 2019-09-07  9:18 UTC (permalink / raw)
  To: elfutils-devel


[-- Attachment #1.1.1: Type: text/plain, Size: 315 bytes --]

I'll check in the attached patch which implements a disassembler for
RISC-V.  It also fixes a problem in the x86 disassember, exposed through
the additions needed for RISC-V.

Since aside rth, who added the BPF disassembler, no one beside me ever
worked on that code I will push the changes as soon as I can.

[-- Attachment #1.1.2: d-elfutils-riscv-disasm --]
[-- Type: text/plain, Size: 55434 bytes --]

diff --git a/backends/riscv_init.c b/backends/riscv_init.c
index 9aaec9ce..9be5c6f2 100644
--- a/backends/riscv_init.c
+++ b/backends/riscv_init.c
@@ -58,6 +58,7 @@ riscv_init (Elf *elf,
   HOOK (eh, reloc_simple_type);
   HOOK (eh, register_info);
   HOOK (eh, abi_cfi);
+  HOOK (eh, disasm);
   /* gcc/config/ #define DWARF_FRAME_REGISTERS.  */
   eh->frame_nregs = 66;
   HOOK (eh, check_special_symbol);
diff --git a/lib/color.c b/lib/color.c
index 20b9698a..2cb41eba 100644
--- a/lib/color.c
+++ b/lib/color.c
@@ -72,6 +72,8 @@ char *color_operand = NULL;
 char *color_operand1 = "";
 char *color_operand2 = "";
 char *color_operand3 = "";
+char *color_operand4 = "";
+char *color_operand5 = "";
 char *color_label = "";
 char *color_undef = "";
 char *color_undef_tls = "";
@@ -167,8 +169,10 @@ valid arguments are:\n\
 				E (m, mnemonic),
 				E (o, operand),
 				E (o1, operand1),
-				E (o1, operand2),
-				E (o1, operand3),
+				E (o2, operand2),
+				E (o3, operand3),
+				E (o4, operand4),
+				E (o5, operand5),
 				E (l, label),
 				E (u, undef),
 				E (ut, undef_tls),
@@ -205,6 +209,10 @@ valid arguments are:\n\
 		    color_operand2 = color_operand;
 		  if (color_operand3[0] == '\0')
 		    color_operand3 = color_operand;
+		  if (color_operand4[0] == '\0')
+		    color_operand4 = color_operand;
+		  if (color_operand5[0] == '\0')
+		    color_operand5 = color_operand;
 		}
 	    }
 #if 0
@@ -216,7 +224,7 @@ valid arguments are:\n\
 	      color_mnemonic = xstrdup ("\e[38;5;202;1m");
 	      color_operand1 = xstrdup ("\e[38;5;220m");
 	      color_operand2 = xstrdup ("\e[38;5;48m");
-	      color_operand3 = xstrdup ("\e[38;5;112m");
+	      color_operand = xstrdup ("\e[38;5;112m");
 	      color_label = xstrdup ("\e[38;5;21m");
 	    }
 #endif
diff --git a/lib/color.h b/lib/color.h
index 3872eb0a..cb241435 100644
--- a/lib/color.h
+++ b/lib/color.h
@@ -50,6 +50,8 @@ extern char *color_mnemonic;
 extern char *color_operand1;
 extern char *color_operand2;
 extern char *color_operand3;
+extern char *color_operand4;
+extern char *color_operand5;
 extern char *color_label;
 extern char *color_undef;
 extern char *color_undef_tls;
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..bc4e02e5
--- /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;
+}
diff --git a/src/objdump.c b/src/objdump.c
index 6b365d5c..a619674f 100644
--- a/src/objdump.c
+++ b/src/objdump.c
@@ -717,11 +717,13 @@ show_disasm (Ebl *ebl, const char *fname, uint32_t shstrndx)
 	      info.address_color = color_address;
 	      info.bytes_color = color_bytes;
 
-	      if (asprintf (&fmt, "%s%%7m %s%%.1o,%s%%.2o,%s%%.3o%%34a %s%%l",
+	      if (asprintf (&fmt, "%s%%7m %s%%.1o,%s%%.2o,%s%%.3o,,%s%%.4o%s%%.5o%%34a %s%%l",
 			    color_mnemonic ?: "",
 			    color_operand1 ?: "",
 			    color_operand2 ?: "",
 			    color_operand3 ?: "",
+                            color_operand4 ?: "",
+                            color_operand5 ?: "",
 			    color_label ?: "") < 0)
 		error (EXIT_FAILURE, errno, _("cannot allocate memory"));
 	    }
@@ -729,7 +731,7 @@ show_disasm (Ebl *ebl, const char *fname, uint32_t shstrndx)
 	    {
 	      info.address_color = info.bytes_color = NULL;
 
-	      fmt = "%7m %.1o,%.2o,%.3o%34a %l";
+	      fmt = "%7m %.1o,%.2o,%.3o,%.4o,%.5o%34a %l";
 	    }
 
 	  disasm_cb (ctx, &info.cur, info.cur + data->d_size, info.addr,
diff --git a/tests/Makefile.am b/tests/Makefile.am
index f12e48f8..d87d9616 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -165,7 +165,8 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \
 	run-strip-version.sh run-xlate-note.sh \
 	run-readelf-discr.sh \
 	run-dwelf_elf_e_machine_string.sh \
-	run-elfclassify.sh run-elfclassify-self.sh
+	run-elfclassify.sh run-elfclassify-self.sh \
+	run-disasm-riscv64.sh
 
 if !BIARCH
 export ELFUTILS_DISABLE_BIARCH = 1
diff --git a/tests/run-disasm-riscv64.sh b/tests/run-disasm-riscv64.sh
new file mode 100755
index 00000000..5353e818
--- /dev/null
+++ b/tests/run-disasm-riscv64.sh
@@ -0,0 +1,529 @@
+#! /bin/sh
+# Copyright (C) 2019 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# 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 a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/test-subr.sh
+
+testfiles testfile-riscv64-dis1.o testfile-riscv64-dis1.expect
+testrun_compare ${abs_top_builddir}/src/objdump -d testfile-riscv64-dis1.o < testfile-riscv64-dis1.expect
+
+exit $?
+
+# The following code is used to generate the test file.
+cat <<EOF | riscv64-linux-gnu-as -c -o testfile-riscv64-dis1.o -
+.text
+.word 0x80000037
+.word 0x800000b7
+.word 0x40300137
+.word 0x90000017
+.word 0x01003317
+.word 0x000000ef
+.word 0x0000116f
+.word 0x000021ef
+.word 0x0000426f
+.word 0x000082ef
+.word 0x0001036f
+.word 0x000203ef
+.word 0x0004046f
+.word 0x000804ef
+.word 0x0010056f
+.word 0x002005ef
+.word 0x0040066f
+.word 0x008006ef
+.word 0x0100076f
+.word 0x020007ef
+.word 0x0400086f
+.word 0x080008ef
+.word 0x1000096f
+.word 0x200009ef
+.word 0x40000a6f
+.word 0x80000aef
+.word 0x00000067
+.word 0x80008567
+.word 0x050109e7
+.word 0x00d30863
+.word 0x80c41463
+.word 0x40d348e3
+.word 0x20d35263
+.word 0x10d364e3
+.word 0x08d37463
+.word 0x00000003
+.word 0x83050703
+.word 0x00001003
+.word 0x850c1383
+.word 0x04012003
+.word 0xa50c2383
+.word 0x05013003
+.word 0xf50c3383
+.word 0x00004003
+.word 0x83054703
+.word 0x00005003
+.word 0x850c5383
+.word 0x04016003
+.word 0xa50c6383
+.word 0x00000023
+.word 0x5f430323
+.word 0x00001023
+.word 0x5f431323
+.word 0x00002023
+.word 0x5f432323
+.word 0x00003023
+.word 0x5f433323
+.word 0x00000013
+.word 0x00000093
+.word 0x00300093
+.word 0x00310093
+.word 0x00002013
+.word 0x00002093
+.word 0x00302093
+.word 0x00312093
+.word 0x00003013
+.word 0x00003093
+.word 0x00303093
+.word 0x00313093
+.word 0x00004013
+.word 0x00004093
+.word 0x00304093
+.word 0x00314093
+.word 0x00006013
+.word 0x00006093
+.word 0x00306093
+.word 0x00316093
+.word 0x00007013
+.word 0x00007093
+.word 0x00307093
+.word 0x00317093
+.word 0x00311093
+.word 0x00315093
+.word 0x40315093
+.word 0x00000033
+.word 0x010000b3
+.word 0x40000033
+.word 0x410000b3
+.word 0x40010033
+.word 0x410200b3
+.word 0x00001033
+.word 0x010010b3
+.word 0x00002033
+.word 0x010020b3
+.word 0x00012033
+.word 0x010220b3
+.word 0x00003033
+.word 0x010030b3
+.word 0x00043033
+.word 0x010530b3
+.word 0x00004033
+.word 0x010040b3
+.word 0x00005033
+.word 0x010050b3
+.word 0x40005033
+.word 0x410050b3
+.word 0x00006033
+.word 0x010060b3
+.word 0x00007033
+.word 0x010070b3
+.word 0x0000000f
+.word 0x0210000f
+.word 0x00000073
+.word 0x00100073
+.word 0x0000001b
+.word 0x0010001b
+.word 0x0000101b
+.word 0x0010101b
+.word 0x0000501b
+.word 0x0000501b
+.word 0x4010501b
+.word 0x4010501b
+.word 0x0000003b
+.word 0x0000003b
+.word 0x00d0833b
+.word 0x00d0833b
+.word 0x40d0833b
+.word 0x40d0833b
+.word 0x00d0933b
+.word 0x00d0933b
+.word 0x00d0d33b
+.word 0x00d0d33b
+.word 0x40d0d33b
+.word 0x40d0d33b
+.word 0x0000100f
+.word 0x00431073
+.word 0x00431ff3
+.word 0xc0132ff3
+.word 0xc8133ff3
+.word 0x00435ff3
+.word 0xc0136ff3
+.word 0xc8137ff3
+.word 0x02000033
+.word 0x02e40733
+.word 0x02001033
+.word 0x02e41733
+.word 0x02002033
+.word 0x02e42733
+.word 0x02003033
+.word 0x02e43733
+.word 0x02004033
+.word 0x02e44733
+.word 0x02005033
+.word 0x02e45733
+.word 0x02006033
+.word 0x02e46733
+.word 0x02007033
+.word 0x02e47733
+.word 0x0200003b
+.word 0x02e4073b
+.word 0x0200403b
+.word 0x02e4473b
+.word 0x0200503b
+.word 0x02e4573b
+.word 0x0200603b
+.word 0x02e4673b
+.word 0x0200703b
+.word 0x02e4773b
+.word 0x1000202f
+.word 0x1800202f
+.word 0x1000302f
+.word 0x1800302f
+.word 0x0800202f
+.word 0x0800302f
+.word 0x0000202f
+.word 0x0000302f
+.word 0x2000202f
+.word 0x2000302f
+.word 0x6000202f
+.word 0x6000302f
+.word 0x4000202f
+.word 0x4000302f
+.word 0x8000202f
+.word 0x8000302f
+.word 0xa000202f
+.word 0xa000302f
+.word 0xc000202f
+.word 0xc000302f
+.word 0xe000202f
+.word 0xe000302f
+.word 0x00002007
+.word 0x00003007
+.word 0x00004007
+.word 0x00002027
+.word 0x00003027
+.word 0x00004027
+.word 0x00002043
+.word 0x02002043
+.word 0x06002043
+.word 0x00002047
+.word 0x02002047
+.word 0x06002047
+.word 0x0000204b
+.word 0x0200204b
+.word 0x0600204b
+.word 0x0000204f
+.word 0x0200204f
+.word 0x0600204f
+.word 0x00000053
+.word 0x00001053
+.word 0x00002053
+.word 0x00003053
+.word 0x00004053
+.word 0x00007053
+.word 0x02000053
+.word 0x06000053
+.word 0x08000053
+.word 0x0a000053
+.word 0x0e000053
+.word 0x10000053
+.word 0x12000053
+.word 0x16000053
+.word 0x18000053
+.word 0x1a000053
+.word 0x1e000053
+.word 0x58000053
+.word 0x5a000053
+.word 0x5e000053
+.word 0x20000053
+.word 0x20300053
+.word 0x22000053
+.word 0x22300053
+.word 0x26000053
+.word 0x26300053
+.word 0x20001053
+.word 0x20401053
+.word 0x22001053
+.word 0x22401053
+.word 0x26001053
+.word 0x26401053
+.word 0x20002053
+.word 0x20702053
+.word 0x22002053
+.word 0x22702053
+.word 0x26002053
+.word 0x26702053
+.word 0x29700053
+.word 0x2b700053
+.word 0x2f700053
+.word 0x29701053
+.word 0x2b701053
+.word 0x2f701053
+.word 0xc00332d3
+.word 0xc02332d3
+.word 0xc20342d3
+.word 0xc22342d3
+.word 0xc60222d3
+.word 0xc62222d3
+.word 0xc01332d3
+.word 0xc03332d3
+.word 0xc21342d3
+.word 0xc23342d3
+.word 0xc61222d3
+.word 0xc63222d3
+.word 0xe00503d3
+.word 0xe20504d3
+.word 0xe60509d3
+.word 0xa0340753
+.word 0xa0341753
+.word 0xa0342753
+.word 0xa2340753
+.word 0xa2341753
+.word 0xa2342753
+.word 0xa6340753
+.word 0xa6341753
+.word 0xa6342753
+.word 0xe0091d53
+.word 0xe2091d53
+.word 0xe6091d53
+.word 0xd00e2453
+.word 0xd02e2453
+.word 0xd01e1453
+.word 0xd03e1453
+.word 0xd2030553
+.word 0xd2130553
+.word 0xd6030553
+.word 0xd6130553
+.word 0xd22e2453
+.word 0xd23e1453
+.word 0xd62e2453
+.word 0xd63e2453
+.word 0xf00c0753
+.word 0xf20c0753
+.word 0xf60c0753
+.short 0x1000
+.short 0x0800
+.short 0x0400
+.short 0x0200
+.short 0x0100
+.short 0x0080
+.short 0x0040
+.short 0x0020
+.short 0x3100
+.short 0x2900
+.short 0x2500
+.short 0x2140
+.short 0x2120
+.short 0x5100
+.short 0x4900
+.short 0x4500
+.short 0x4140
+.short 0x4120
+.short 0x7100
+.short 0x6900
+.short 0x6500
+.short 0x6140
+.short 0x6120
+.short 0xb100
+.short 0xa900
+.short 0xa500
+.short 0xa140
+.short 0xa120
+.short 0xd100
+.short 0xc900
+.short 0xc500
+.short 0xc140
+.short 0xc120
+.short 0xf100
+.short 0xe900
+.short 0xe500
+.short 0xe140
+.short 0xe120
+.short 0x1001
+.short 0x1301
+.short 0x0341
+.short 0x0321
+.short 0x0311
+.short 0x0309
+.short 0x0305
+.short 0x2081
+.short 0x3081
+.short 0x20c1
+.short 0x20a1
+.short 0x2091
+.short 0x2089
+.short 0x2085
+.short 0x2105
+.short 0x2185
+.short 0x2205
+.short 0x2285
+.short 0x2305
+.short 0x2385
+.short 0x2405
+.short 0x2485
+.short 0x2505
+.short 0x2585
+.short 0x2605
+.short 0x2685
+.short 0x2705
+.short 0x2785
+.short 0x2805
+.short 0x2885
+.short 0x2905
+.short 0x2985
+.short 0x2a05
+.short 0x2a85
+.short 0x2b05
+.short 0x2b85
+.short 0x2c05
+.short 0x2c85
+.short 0x2d05
+.short 0x2d85
+.short 0x2e05
+.short 0x2e85
+.short 0x2f05
+.short 0x2f85
+.short 0x4081
+.short 0x5081
+.short 0x40c1
+.short 0x40a1
+.short 0x4091
+.short 0x4089
+.short 0x4085
+.short 0x7101
+.short 0x6141
+.short 0x6121
+.short 0x6111
+.short 0x6109
+.short 0x6105
+.short 0x7301
+.short 0x6341
+.short 0x6321
+.short 0x6311
+.short 0x6309
+.short 0x6305
+.short 0x9001
+.short 0x8041
+.short 0x8021
+.short 0x8011
+.short 0x8009
+.short 0x8005
+.short 0x8405
+.short 0x8801
+.short 0x9801
+.short 0x8941
+.short 0x8921
+.short 0x8911
+.short 0x8909
+.short 0x8905
+.short 0x8f11
+.short 0x8f31
+.short 0x8f51
+.short 0x8f71
+.short 0x9f11
+.short 0x9f31
+.short 0xa001
+.short 0xb001
+.short 0xa801
+.short 0xa401
+.short 0xa201
+.short 0xa101
+.short 0xa081
+.short 0xa041
+.short 0xa021
+.short 0xa011
+.short 0xa009
+.short 0xa005
+.short 0xc301
+.short 0xd301
+.short 0xcb01
+.short 0xc701
+.short 0xc341
+.short 0xc321
+.short 0xc311
+.short 0xc309
+.short 0xc305
+.short 0xe301
+.short 0xf301
+.short 0xeb01
+.short 0xe701
+.short 0xe341
+.short 0xe321
+.short 0xe311
+.short 0xe309
+.short 0xe305
+.short 0x1302
+.short 0x0342
+.short 0x0322
+.short 0x0312
+.short 0x030a
+.short 0x0306
+.short 0x2702
+.short 0x3702
+.short 0x2742
+.short 0x2722
+.short 0x2712
+.short 0x270a
+.short 0x2706
+.short 0x4702
+.short 0x5702
+.short 0x4742
+.short 0x4722
+.short 0x4712
+.short 0x470a
+.short 0x4706
+.short 0x6702
+.short 0x7702
+.short 0x6742
+.short 0x6722
+.short 0x6712
+.short 0x670a
+.short 0x6706
+.short 0x8302
+.short 0x8342
+.short 0x9002
+.short 0x9502
+.short 0x9572
+.short 0xa062
+.short 0xb062
+.short 0xa862
+.short 0xa462
+.short 0xa262
+.short 0xa162
+.short 0xa0e2
+.short 0xc062
+.short 0xd062
+.short 0xc862
+.short 0xc462
+.short 0xc262
+.short 0xc162
+.short 0xc0e2
+.short 0xe062
+.short 0xf062
+.short 0xe862
+.short 0xe462
+.short 0xe262
+.short 0xe162
+.short 0xe0e2
+.word 0x00153073
+.word 0x0011d073
+.word 0x0011e073
+EOF
diff --git a/tests/testfile-riscv64-dis1.expect.bz2 b/tests/testfile-riscv64-dis1.expect.bz2
new file mode 100644
index 00000000..2740795d
Binary files /dev/null and b/tests/testfile-riscv64-dis1.expect.bz2 differ
diff --git a/tests/testfile-riscv64-dis1.o.bz2 b/tests/testfile-riscv64-dis1.o.bz2
new file mode 100644
index 00000000..50f95f26
Binary files /dev/null and b/tests/testfile-riscv64-dis1.o.bz2 differ
diff --git a/tests/testfile45.expect.bz2 b/tests/testfile45.expect.bz2
index b8b33e9b..e502e15a 100644
Binary files a/tests/testfile45.expect.bz2 and b/tests/testfile45.expect.bz2 differ

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: RISC-V disassembler
  2019-09-07  9:18 RISC-V disassembler Ulrich Drepper
@ 2019-09-07 20:17 ` Mark Wielaard
  2019-09-09 21:25 ` Jim Wilson
  1 sibling, 0 replies; 4+ messages in thread
From: Mark Wielaard @ 2019-09-07 20:17 UTC (permalink / raw)
  To: Ulrich Drepper; +Cc: elfutils-devel

Hi Ulrich,

On Sat, Sep 07, 2019 at 11:18:42AM +0200, Ulrich Drepper wrote:
> I'll check in the attached patch which implements a disassembler for
> RISC-V.  It also fixes a problem in the x86 disassember, exposed through
> the additions needed for RISC-V.

This is awesome. Thanks.

> Since aside rth, who added the BPF disassembler, no one beside me ever
> worked on that code I will push the changes as soon as I can.

You are right that nobody else will probably be able to properly
review these changes. And please feel free to commit changes that
don't see comments where you are the expert. But there is some value
in waiting a little bit after posting a patch. Someone might see some
issue, like the missing EXTRA_DIST files or simple whitespace issues,
that are nice to know, so commits don't break the build on other
systems.

If possible please do split the commits in independent parts. In this
case the x86 disassembler fix and po file updates could have been done
separately.

Normally we have ChangeLog entries for changes. If at all possible,
please add them. They really do help with reviewing and making sure
that what is committed/pushed was what was intended.

Please do add a Signed-off-by to your commits in the future.
See the CONTRIBUTING file for more details.

Thanks,

Mark

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: RISC-V disassembler
  2019-09-07  9:18 RISC-V disassembler Ulrich Drepper
  2019-09-07 20:17 ` Mark Wielaard
@ 2019-09-09 21:25 ` Jim Wilson
  2019-09-10  4:27   ` Ulrich Drepper
  1 sibling, 1 reply; 4+ messages in thread
From: Jim Wilson @ 2019-09-09 21:25 UTC (permalink / raw)
  To: drepper, elfutils-devel

On 9/7/19 2:18 AM, Ulrich Drepper wrote:
> I'll check in the attached patch which implements a disassembler for
> RISC-V.  It also fixes a problem in the x86 disassember, exposed through
> the additions needed for RISC-V.
> 
> Since aside rth, who added the BPF disassembler, no one beside me ever
> worked on that code I will push the changes as soon as I can.

There are some binary bz2 files missing at the end of the patch, but if 
you are committing this yourself that is probably OK.

There is a testfile45.expect.bz2 that doesn't look like it is supposed 
to be part of the patch set.  You probably don't want to commit that one.

There is a testcase for riscv64 but not for riscv32, though the code 
does look like it correctly handles rv32 versus rv64 decodes.  A 
testcase for rv32 would be a nice improvement.

Otherwise, it looks pretty good at a first glance, and I'm not planning 
to do a full review.  Seems to handle the obvious tricky cases 
correctly.  It doesn't support rv128 or the q (quadfloat) extension, but 
then I don't know of anyone using them, and binutils probably doesn't 
handle them correctly either.  We have binutils patches for the draft V 
(vector) and B (bit manipulation) extensions in branches in the 
github.com riscv repos, but these are still changing instruction 
mnemonics and encodings, so not ready for official trees yet.  We just 
need to remember that we have another disassembler to update when the V 
and/or B extensions are finalized.  There are also new CSR registers 
being added regularly, for extensions, and for hardware features like 
the draft CLIC interrupt controller spec, so that is another thing that 
will need occasional updates.

Jim

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: RISC-V disassembler
  2019-09-09 21:25 ` Jim Wilson
@ 2019-09-10  4:27   ` Ulrich Drepper
  0 siblings, 0 replies; 4+ messages in thread
From: Ulrich Drepper @ 2019-09-10  4:27 UTC (permalink / raw)
  To: Jim Wilson, elfutils-devel


[-- Attachment #1.1: Type: text/plain, Size: 1107 bytes --]

On 9/9/19 11:25 PM, Jim Wilson wrote:
> There is a testfile45.expect.bz2 that doesn't look like it is supposed
> to be part of the patch set.  You probably don't want to commit that one.

Yes, I do.  The x86-64 disassembler had a tiny whitespace bug exposed
through the code.


> There is a testcase for riscv64 but not for riscv32, though the code
> does look like it correctly handles rv32 versus rv64 decodes.  A
> testcase for rv32 would be a nice improvement.

Well, if someone has the time.


> Otherwise, it looks pretty good at a first glance, and I'm not planning
> to do a full review.  Seems to handle the obvious tricky cases
> correctly.  It doesn't support rv128

This necessitates the introduction of ELFCLASS128 first.


> or the q (quadfloat) extension,

Yes, it does.


> We have binutils patches for the draft V
> (vector) and B (bit manipulation) extensions in branches in the
> github.com riscv repos, but these are still changing instruction
> mnemonics and encodings, so not ready for official trees yet.

Which is why I didn't add any of that.


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 195 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2019-09-10  4:27 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-07  9:18 RISC-V disassembler Ulrich Drepper
2019-09-07 20:17 ` Mark Wielaard
2019-09-09 21:25 ` Jim Wilson
2019-09-10  4:27   ` Ulrich Drepper

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).