RISC-V disassembler

Ulrich Drepper drepper@redhat.com
Sat Sep 7 09:18:00 GMT 2019


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.
-------------- next part --------------
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
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 195 bytes
Desc: OpenPGP digital signature
URL: <http://sourceware.org/pipermail/elfutils-devel/attachments/20190907/aa1ed8cf/attachment.sig>


More information about the Elfutils-devel mailing list