[PATCH] [WebAssembly] Disassembler support

Pip Cet pipcet@gmail.com
Fri Mar 31 23:18:00 GMT 2017


This patch adds support for disassembling WebAssembly opcodes.  It
includes some tests and un-xfails the all-instructions test.

Comments and improvements would be most welcome.

Thanks,
Pip

Suggested change log entries:
opcodes/:
2017-03-31  Pip Cet  <pipcet@gmail.com>

    * Makefile.am: Add wasm32-dis.c.
    * configure.ac: Add wasm32-dis.c to wasm32 target.
    * disassemble.c: Add wasm32 disassembler code.
    * wasm32-dis.c: New file.
    * Makefile.in: Regenerate.
    * configure: Regenerate.
    * po/POTFILES.in: Regenerate.

gas/:
2017-03-31  Pip Cet  <pipcet@gmail.com>

    * testsuite/gas/wasm32/allinsn.d: Adjust test for disassembler
    changes.
    * testsuite/gas/wasm32/disass.d: New test.
    * testsuite/gas/wasm32/disass.s: New test.
    * testsuite/gas/wasm32/disass-2.d: New test.
    * testsuite/gas/wasm32/disass-2.s: New test.
    * testsuite/gas/wasm32/reloc.d: Adjust test for changed reloc names.
    * testsuite/gas/wasm32/reloc.s: Update test for changed assembler
    syntax.
    * testsuite/gas/wasm32/wasm32.exp: Run new tests.  Expect allinsn
    test to succeed.

include/:
2017-03-31  Pip Cet  <pipcet@gmail.com>

    * dis-asm.h: Add prototypes for wasm32 disassembler.

----------
diff --git a/gas/testsuite/gas/wasm32/allinsn.d
b/gas/testsuite/gas/wasm32/allinsn.d
index 06124be4d6..c594c72501 100644
--- a/gas/testsuite/gas/wasm32/allinsn.d
+++ b/gas/testsuite/gas/wasm32/allinsn.d
@@ -11,7 +11,7 @@ Disassembly of section .text:
    0:    02 40               block\[\]
    2:    0c 00               br 0
    4:    0d 00               br_if 0
-   6:    0e 01 01 01         br_table 1 1
+   6:    0e 01 01 01         br_table 1 1 1
    a:    10 00               call 0x0
    c:    11 00 00            call_indirect 0 0
    f:    1a                  drop
@@ -22,12 +22,12 @@ Disassembly of section .text:
   14:    8d                  f32.ceil
   15:    43 d0 0f 49         f32.const 3.141590118408203125
   19:    40
-  1a:    b2                  f32.convert_s_i32
-  1b:    b4                  f32.convert_s_i64
-  1c:    b3                  f32.convert_u_i32
-  1d:    b5                  f32.convert_u_i64
+  1a:    b2                  f32.convert_s/i32
+  1b:    b4                  f32.convert_s/i64
+  1c:    b3                  f32.convert_u/i32
+  1d:    b5                  f32.convert_u/i64
   1e:    98                  f32.copysign
-  1f:    b6                  f32.demote_f64
+  1f:    b6                  f32.demote/f64
   20:    95                  f32.div
   21:    5b                  f32.eq
   22:    8e                  f32.floor
@@ -42,7 +42,7 @@ Disassembly of section .text:
   2d:    5c                  f32.ne
   2e:    90                  f32.nearest
   2f:    8c                  f32.neg
-  30:    be                  f32.reinterpret_i32
+  30:    be                  f32.reinterpret/i32
   31:    91                  f32.sqrt
   32:    38 00 00            f32.store a=0 0
   35:    93                  f32.sub
@@ -53,10 +53,10 @@ Disassembly of section .text:
   3a:    44 97 5f 4f         f64.const 3.14158999999999976088e\+200
   3e:    fd bc 6a 90
   42:    69
-  43:    b7                  f64.convert_s_i32
-  44:    b9                  f64.convert_s_i64
-  45:    b8                  f64.convert_u_i32
-  46:    ba                  f64.convert_u_i64
+  43:    b7                  f64.convert_s/i32
+  44:    b9                  f64.convert_s/i64
+  45:    b8                  f64.convert_u/i32
+  46:    ba                  f64.convert_u/i64
   47:    a6                  f64.copysign
   48:    a3                  f64.div
   49:    61                  f64.eq
@@ -72,14 +72,14 @@ Disassembly of section .text:
   55:    62                  f64.ne
   56:    9e                  f64.nearest
   57:    9a                  f64.neg
-  58:    bb                  f64.promote_f32
-  59:    bf                  f64.reinterpret_i64
+  58:    bb                  f64.promote/f32
+  59:    bf                  f64.reinterpret/i64
   5a:    9f                  f64.sqrt
   5b:    39 00 00            f64.store a=0 0
   5e:    a1                  f64.sub
   5f:    9d                  f64.trunc
-  60:    23 00               get_global 0 <\$got>
-  62:    20 00               get_local 0 <\$dpc>
+  60:    23 00               get_global 0
+  62:    20 00               get_local 0
   64:    6a                  i32.add
   65:    71                  i32.and
   66:    67                  i32.clz
@@ -107,7 +107,7 @@ Disassembly of section .text:
   8a:    47                  i32.ne
   8b:    72                  i32.or
   8c:    69                  i32.popcnt
-  8d:    bc                  i32.reinterpret_f32
+  8d:    bc                  i32.reinterpret/f32
   8e:    6f                  i32.rem_s
   8f:    70                  i32.rem_u
   90:    77                  i32.rotl
@@ -119,11 +119,11 @@ Disassembly of section .text:
   98:    3b 00 00            i32.store16 a=0 0
   9b:    3a 00 00            i32.store8 a=0 0
   9e:    6b                  i32.sub
-  9f:    a8                  i32.trunc_s_f32
-  a0:    aa                  i32.trunc_s_f64
-  a1:    a9                  i32.trunc_u_f32
-  a2:    ab                  i32.trunc_u_f64
-  a3:    a7                  i32.wrap_i64
+  9f:    a8                  i32.trunc_s/f32
+  a0:    aa                  i32.trunc_s/f64
+  a1:    a9                  i32.trunc_u/f32
+  a2:    ab                  i32.trunc_u/f64
+  a3:    a7                  i32.wrap/i64
   a4:    73                  i32.xor
   a5:    7c                  i64.add
   a6:    83                  i64.and
@@ -136,8 +136,8 @@ Disassembly of section .text:
   b4:    80                  i64.div_u
   b5:    51                  i64.eq
   b6:    50                  i64.eqz
-  b7:    ac                  i64.extend_s_i32
-  b8:    ad                  i64.extend_u_i32
+  b7:    ac                  i64.extend_s/i32
+  b8:    ad                  i64.extend_u/i32
   b9:    59                  i64.ge_s
   ba:    5a                  i64.ge_u
   bb:    55                  i64.gt_s
@@ -157,7 +157,7 @@ Disassembly of section .text:
   d7:    52                  i64.ne
   d8:    84                  i64.or
   d9:    7b                  i64.popcnt
-  da:    bd                  i64.reinterpret_f64
+  da:    bd                  i64.reinterpret/f64
   db:    81                  i64.rem_s
   dc:    82                  i64.rem_u
   dd:    89                  i64.rotl
@@ -170,20 +170,20 @@ Disassembly of section .text:
   e8:    3e 00 00            i64.store32 a=0 0
   eb:    3c 00 00            i64.store8 a=0 0
   ee:    7d                  i64.sub
-  ef:    ae                  i64.trunc_s_f32
-  f0:    b0                  i64.trunc_s_f64
-  f1:    af                  i64.trunc_u_f32
-  f2:    b1                  i64.trunc_u_f64
+  ef:    ae                  i64.trunc_s/f32
+  f0:    b0                  i64.trunc_s/f64
+  f1:    af                  i64.trunc_u/f32
+  f2:    b1                  i64.trunc_u/f64
   f3:    85                  i64.xor
   f4:    04 7f               if\[i\]
   f6:    03 7e               loop\[l\]
   f8:    01                  nop
   f9:    0f                  return
   fa:    1b                  select
-  fb:    24 00               set_global 0 <\$got>
-  fd:    21 00               set_local 0 <\$dpc>
+  fb:    24 00               set_global 0
+  fd:    21 00               set_local 0
   ff:    60                  f32.ge
- 100:    08                  .byte 08
+ 100:    08                  .byte 0x08

  101:    7f                  i64.div_s
  102:    7e                  i64.mul
@@ -194,5 +194,5 @@ Disassembly of section .text:
  107:    7e                  i64.mul
  108:    7f                  i64.div_s
  109:    00                  unreachable
- 10a:    22 00               tee_local 0 <\$dpc>
+ 10a:    22 00               tee_local 0
     ...
diff --git a/gas/testsuite/gas/wasm32/disass-2.d
b/gas/testsuite/gas/wasm32/disass-2.d
new file mode 100644
index 0000000000..b6aa7954de
--- /dev/null
+++ b/gas/testsuite/gas/wasm32/disass-2.d
@@ -0,0 +1,9 @@
+#as:
+#objdump: -d -Mglobals
+#name: disass-2.d
+^dump.o:     file format elf32-wasm32$
+
+^Disassembly of section .text:$
+^00000000 <.text>:$
+^   0:    20 00               get_local 0$
+^   2:    23 00               get_global 0 <\$got>$
diff --git a/gas/testsuite/gas/wasm32/disass-2.s
b/gas/testsuite/gas/wasm32/disass-2.s
new file mode 100644
index 0000000000..bed9410505
--- /dev/null
+++ b/gas/testsuite/gas/wasm32/disass-2.s
@@ -0,0 +1,3 @@
+        .text
+        get_local 0
+        get_global 0
diff --git a/gas/testsuite/gas/wasm32/disass.d
b/gas/testsuite/gas/wasm32/disass.d
new file mode 100644
index 0000000000..2708137916
--- /dev/null
+++ b/gas/testsuite/gas/wasm32/disass.d
@@ -0,0 +1,9 @@
+#as:
+#objdump: -d -Mregisters,globals
+#name: disass.d
+^dump.o:     file format elf32-wasm32$
+
+^Disassembly of section .text:$
+^00000000 <.text>:$
+^   0:    20 00               get_local 0 <\$dpc>$
+^   2:    23 00               get_global 0 <\$got>$
diff --git a/gas/testsuite/gas/wasm32/disass.s
b/gas/testsuite/gas/wasm32/disass.s
new file mode 100644
index 0000000000..bed9410505
--- /dev/null
+++ b/gas/testsuite/gas/wasm32/disass.s
@@ -0,0 +1,3 @@
+        .text
+        get_local 0
+        get_global 0
diff --git a/gas/testsuite/gas/wasm32/reloc.d b/gas/testsuite/gas/wasm32/reloc.d
index 9317e6e9a3..9dc54e7760 100644
--- a/gas/testsuite/gas/wasm32/reloc.d
+++ b/gas/testsuite/gas/wasm32/reloc.d
@@ -9,10 +9,11 @@ Disassembly of section .text:
 00000000 <.text>:
    0:    41 80 80 80         i32.const 0
    4:    80 00
-            1: R_ASMJS_LEB128_PLT    f
+            1: R_WASM32_PLT_SIG    __sigchar_FiiiiiiiE
+            1: R_WASM32_LEB128_PLT    f
    6:    41 80 80 80         i32.const 0
    a:    80 00
-            7: R_ASMJS_LEB128_GOT    x
+            7: R_WASM32_LEB128_GOT    x
    c:    41 80 80 80         i32.const 0
   10:    80 00
-            d: R_ASMJS_LEB128_GOT_CODE    f
+            d: R_WASM32_LEB128_GOT_CODE    f
diff --git a/gas/testsuite/gas/wasm32/reloc.s b/gas/testsuite/gas/wasm32/reloc.s
index 8cdfd58b96..cd34591b2c 100644
--- a/gas/testsuite/gas/wasm32/reloc.s
+++ b/gas/testsuite/gas/wasm32/reloc.s
@@ -1,3 +1,3 @@
-        i32.const f@plt
+        i32.const f@plt{__sigchar_FiiiiiiiE}
         i32.const x@got
         i32.const f@gotcode
diff --git a/gas/testsuite/gas/wasm32/wasm32.exp
b/gas/testsuite/gas/wasm32/wasm32.exp
index e6d1819677..49c14e45f9 100644
--- a/gas/testsuite/gas/wasm32/wasm32.exp
+++ b/gas/testsuite/gas/wasm32/wasm32.exp
@@ -21,8 +21,6 @@
 # wasm32 assembler testsuite.

 if [istarget wasm32-*-*] {
-    # no disassembler support yet
-    setup_xfail "wasm32-*-*"
     run_dump_test "allinsn"
     # no GOT/PLT relocs yet.
     setup_xfail "wasm32-*-*"
@@ -55,4 +53,6 @@ if [istarget wasm32-*-*] {
     # illegal-23 has become legal
     run_list_test "illegal-24"
     run_list_test "illegal-25"
+    run_dump_test "disass"
+    run_dump_test "disass-2"
 }
diff --git a/include/dis-asm.h b/include/dis-asm.h
index f0544509d0..a6b65431de 100644
--- a/include/dis-asm.h
+++ b/include/dis-asm.h
@@ -318,6 +318,7 @@ extern int print_insn_v850        (bfd_vma,
disassemble_info *);
 extern int print_insn_vax        (bfd_vma, disassemble_info *);
 extern int print_insn_visium        (bfd_vma, disassemble_info *);
 extern int print_insn_w65        (bfd_vma, disassemble_info *);
+extern int print_insn_wasm32        (bfd_vma, disassemble_info *);
 extern int print_insn_xc16x        (bfd_vma, disassemble_info *);
 extern int print_insn_xgate             (bfd_vma, disassemble_info *);
 extern int print_insn_xstormy16        (bfd_vma, disassemble_info *);
@@ -343,10 +344,12 @@ extern void print_riscv_disassembler_options (FILE *);
 extern void print_arm_disassembler_options (FILE *);
 extern void print_arc_disassembler_options (FILE *);
 extern void print_s390_disassembler_options (FILE *);
+extern void print_wasm32_disassembler_options (FILE *);
 extern bfd_boolean aarch64_symbol_is_valid (asymbol *, struct
disassemble_info *);
 extern bfd_boolean arm_symbol_is_valid (asymbol *, struct disassemble_info *);
 extern void disassemble_init_powerpc (struct disassemble_info *);
 extern void disassemble_init_s390 (struct disassemble_info *);
+extern void disassemble_init_wasm32 (struct disassemble_info *);
 extern const disasm_options_t *disassembler_options_powerpc (void);
 extern const disasm_options_t *disassembler_options_arm (void);
 extern const disasm_options_t *disassembler_options_s390 (void);
diff --git a/opcodes/Makefile.am b/opcodes/Makefile.am
index b43c679bff..1ac6bb1128 100644
--- a/opcodes/Makefile.am
+++ b/opcodes/Makefile.am
@@ -259,6 +259,7 @@ TARGET_LIBOPCODES_CFILES = \
     visium-dis.c \
     visium-opc.c \
     w65-dis.c \
+    wasm32-dis.c \
     xc16x-asm.c \
     xc16x-desc.c \
     xc16x-dis.c \
diff --git a/opcodes/configure.ac b/opcodes/configure.ac
index ca982924e6..a9fbfd61f1 100644
--- a/opcodes/configure.ac
+++ b/opcodes/configure.ac
@@ -348,7 +348,7 @@ if test x${all_targets} = xfalse ; then
     bfd_vax_arch)        ta="$ta vax-dis.lo" ;;
     bfd_visium_arch)    ta="$ta visium-dis.lo visium-opc.lo" ;;
     bfd_w65_arch)        ta="$ta w65-dis.lo" ;;
-        bfd_wasm32_arch)        ;;
+        bfd_wasm32_arch)        ta="$ta wasm32-dis.lo" ;;
     bfd_we32k_arch)        ;;
     bfd_xc16x_arch)        ta="$ta xc16x-asm.lo xc16x-desc.lo
xc16x-dis.lo xc16x-ibld.lo xc16x-opc.lo" using_cgen=yes ;;
     bfd_xgate_arch)        ta="$ta xgate-dis.lo xgate-opc.lo" ;;
diff --git a/opcodes/disassemble.c b/opcodes/disassemble.c
index eef06584f4..dd7d3a32b7 100644
--- a/opcodes/disassemble.c
+++ b/opcodes/disassemble.c
@@ -94,6 +94,7 @@
 #define ARCH_vax
 #define ARCH_visium
 #define ARCH_w65
+#define ARCH_wasm32
 #define ARCH_xstormy16
 #define ARCH_xc16x
 #define ARCH_xgate
@@ -474,6 +475,11 @@ disassembler (bfd *abfd)
       disassemble = print_insn_w65;
       break;
 #endif
+#ifdef ARCH_wasm32
+    case bfd_arch_wasm32:
+      disassemble = print_insn_wasm32;
+      break;
+#endif
 #ifdef ARCH_xgate
     case bfd_arch_xgate:
       disassemble = print_insn_xgate;
@@ -580,6 +586,9 @@ disassembler_usage (FILE *stream ATTRIBUTE_UNUSED)
 #ifdef ARCH_s390
   print_s390_disassembler_options (stream);
 #endif
+#ifdef ARCH_wasm32
+  print_wasm32_disassembler_options (stream);
+#endif

   return;
 }
@@ -650,6 +659,11 @@ disassemble_init_for_target (struct
disassemble_info * info)
       disassemble_init_powerpc (info);
       break;
 #endif
+#ifdef ARCH_wasm32
+    case bfd_arch_wasm32:
+      disassemble_init_wasm32 (info);
+      break;
+#endif
 #ifdef ARCH_s390
     case bfd_arch_s390:
       disassemble_init_s390 (info);
diff --git a/opcodes/wasm32-dis.c b/opcodes/wasm32-dis.c
new file mode 100644
index 0000000000..d3858880c0
--- /dev/null
+++ b/opcodes/wasm32-dis.c
@@ -0,0 +1,505 @@
+/* Opcode printing code for the WebAssembly target
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of libopcodes.
+
+   This library 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.
+
+   It 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, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+#include "sysdep.h"
+#include "dis-asm.h"
+#include "opintl.h"
+#include "safe-ctype.h"
+#include "floatformat.h"
+#include <float.h>
+#include "libiberty.h"
+#include "elf-bfd.h"
+#include "elf/internal.h"
+#include "elf/wasm32.h"
+#include <stdint.h>
+
+/* Type names for blocks and signatures.  */
+#define BLOCK_TYPE_NONE              0x40
+#define BLOCK_TYPE_I32               0x7f
+#define BLOCK_TYPE_I64               0x7e
+#define BLOCK_TYPE_F32               0x7d
+#define BLOCK_TYPE_F64               0x7c
+
+enum wasm_class
+  {
+    wasm_typed,
+    wasm_special,
+    wasm_break,
+    wasm_break_if,
+    wasm_break_table,
+    wasm_return,
+    wasm_call,
+    wasm_call_import,
+    wasm_call_indirect,
+    wasm_get_local,
+    wasm_set_local,
+    wasm_tee_local,
+    wasm_drop,
+    wasm_constant_i32,
+    wasm_constant_i64,
+    wasm_constant_f32,
+    wasm_constant_f64,
+    wasm_unary,
+    wasm_binary,
+    wasm_conv,
+    wasm_load,
+    wasm_store,
+    wasm_select,
+    wasm_relational,
+    wasm_eqz,
+    wasm_current_memory,
+    wasm_grow_memory,
+    wasm_signature
+  };
+
+struct wasm32_private_data
+{
+  bfd_boolean print_registers;
+  bfd_boolean print_well_known_globals;
+
+  /* Limit valid symbols to those with a given prefix.  */
+  const char *section_prefix;
+};
+
+typedef struct
+{
+  const char *name;
+  const char *description;
+} wasm32_options_t;
+
+static const wasm32_options_t options[] =
+{
+  { "registers", N_("Disassemble \"register\" names") },
+  { "globals",   N_("Name well-known globals") },
+};
+
+#define WASM_OPCODE(opcode, name, intype, outtype, clas, signedness)     \
+  { name, wasm_ ## clas, opcode },
+
+struct wasm32_opcode_s
+{
+  const char *name;
+  enum wasm_class clas;
+  unsigned char opcode;
+} wasm32_opcodes[] =
+{
+#include "opcode/wasm.h"
+  { NULL, 0, 0 }
+};
+
+/* Parse the disassembler options in OPTS and initialize INFO.  */
+
+static void
+parse_wasm32_disassembler_options (struct disassemble_info *info,
+                                   char *opts)
+{
+  struct wasm32_private_data *private = info->private_data;
+  while (opts != NULL)
+    {
+      if (CONST_STRNEQ (opts, "registers"))
+        private->print_registers = TRUE;
+      else if (CONST_STRNEQ (opts, "globals"))
+        private->print_well_known_globals = TRUE;
+
+      opts = strchr (opts, ',');
+      if (opts)
+        opts++;
+    }
+}
+
+/* Check whether SYM is valid.  Special-case absolute symbols, which
+   are unhelpful to print, and arguments to a "call" insn, which we
+   want to be in a section matching a given prefix.  */
+
+static bfd_boolean
+wasm32_symbol_is_valid (asymbol *sym,
+                        struct disassemble_info *info)
+{
+  struct wasm32_private_data *private_data = info->private_data;
+
+  if (sym == NULL)
+    return FALSE;
+
+  if (strcmp(sym->section->name, "*ABS*") == 0)
+    return FALSE;
+
+  if (private_data && private_data->section_prefix != NULL
+      && strncmp (sym->section->name, private_data->section_prefix,
+                  strlen (private_data->section_prefix)))
+    return FALSE;
+
+  return TRUE;
+}
+
+/* Initialize the disassembler structures for INFO.  */
+
+void
+disassemble_init_wasm32 (struct disassemble_info *info)
+{
+  if (info->private_data == NULL)
+    {
+      static struct wasm32_private_data private;
+
+      private.print_registers = FALSE;
+      private.print_well_known_globals = FALSE;
+      private.section_prefix = NULL;
+
+      info->private_data = &private;
+    }
+
+  if (info->disassembler_options)
+    {
+      parse_wasm32_disassembler_options (info, info->disassembler_options);
+
+      info->disassembler_options = NULL;
+    }
+
+  info->symbol_is_valid = wasm32_symbol_is_valid;
+}
+
+/* Read an LEB128-encoded integer from INFO at address PC, reading one
+   byte at a time.  Set ERROR_RETURN if no complete integer could be
+   read, LENGTH_RETURN to the number oof bytes read (including bytes
+   in incomplete numbers).  SIGN means interpret the number as
+   SLEB128.  Unfortunately, this is a duplicate of wasm-module.c's
+   wasm_read_leb128 ().  */
+
+static uint64_t
+wasm_read_leb128 (bfd_vma                   pc,
+                  struct disassemble_info * info,
+                  bfd_boolean *             error_return,
+                  unsigned int *            length_return,
+                  bfd_boolean               sign)
+{
+  uint64_t result = 0;
+  unsigned int num_read = 0;
+  unsigned int shift = 0;
+  unsigned char byte = 0;
+  bfd_boolean success = FALSE;
+
+  while (info->read_memory_func (pc + num_read, &byte, 1, info) == 0)
+    {
+      num_read++;
+
+      result |= ((bfd_vma) (byte & 0x7f)) << shift;
+
+      shift += 7;
+      if ((byte & 0x80) == 0)
+        {
+          success = TRUE;
+          break;
+        }
+    }
+
+  if (length_return != NULL)
+    *length_return = num_read;
+  if (error_return != NULL)
+    *error_return = ! success;
+
+  if (sign && (shift < 8 * sizeof (result)) && (byte & 0x40))
+    result |= -((uint64_t) 1 << shift);
+
+  return result;
+}
+
+/* Read a 32-bit IEEE float from PC using INFO, convert it to a host
+   double, and store it at VALUE.  */
+
+static int
+read_f32 (double *value, bfd_vma pc, struct disassemble_info *info)
+{
+  bfd_byte buf[4];
+
+  if (info->read_memory_func (pc, buf, sizeof (buf), info))
+    return -1;
+
+  floatformat_to_double (&floatformat_ieee_single_little, buf,
+                         value);
+
+  return sizeof (buf);
+}
+
+/* Read a 64-bit IEEE float from PC using INFO, convert it to a host
+   double, and store it at VALUE.  */
+
+static int
+read_f64 (double *value, bfd_vma pc, struct disassemble_info *info)
+{
+  bfd_byte buf[8];
+
+  if (info->read_memory_func (pc, buf, sizeof (buf), info))
+    return -1;
+
+  floatformat_to_double (&floatformat_ieee_double_little, buf,
+                         value);
+
+  return sizeof (buf);
+}
+
+/* Main disassembly routine.  Disassemble insn at PC using INFO.  */
+
+int
+print_insn_wasm32 (bfd_vma pc, struct disassemble_info *info)
+{
+  unsigned char opcode;
+  struct wasm32_opcode_s *op;
+  bfd_byte buffer[16];
+  void *stream = info->stream;
+  fprintf_ftype prin = info->fprintf_func;
+  struct wasm32_private_data *private_data = info->private_data;
+  long long constant = 0;
+  double fconstant = 0.0;
+  long flags = 0;
+  long offset = 0;
+  long depth = 0;
+  long index = 0;
+  long target_count = 0;
+  long block_type = 0;
+  int len = 1;
+  int ret = 0;
+  unsigned int bytes_read = 0;
+  int i;
+  const char *locals[] = {
+    "$dpc", "$sp1", "$r0", "$r1", "$rpc", "$pc0",
+    "$rp", "$fp", "$sp",
+    "$r2", "$r3", "$r4", "$r5", "$r6", "$r7",
+    "$i0", "$i1", "$i2", "$i3", "$i4", "$i5", "$i6", "$i7",
+    "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
+  };
+  int nlocals = ARRAY_SIZE (locals);
+  const char *globals[] = {
+    "$got", "$plt", "$gpo"
+  };
+  int nglobals = ARRAY_SIZE (globals);
+  bfd_boolean error = FALSE;
+
+  if (info->read_memory_func (pc, buffer, 1, info))
+    return -1;
+
+  opcode = buffer[0];
+
+  for (op = wasm32_opcodes; op->name; op++)
+    if (op->opcode == opcode)
+      break;
+
+  if (!op->name)
+    {
+      prin (stream, "\t.byte 0x%02x\n", buffer[0]);
+      return 1;
+    }
+  else
+    {
+      len = 1;
+
+      prin (stream, "\t");
+      prin (stream, "%s", op->name);
+
+      if (op->clas == wasm_typed)
+        {
+          block_type = wasm_read_leb128
+            (pc + len, info, &error, &bytes_read, FALSE);
+          if (error)
+            return -1;
+          len += bytes_read;
+          switch (block_type)
+            {
+            case BLOCK_TYPE_NONE:
+              prin (stream, "[]");
+              break;
+            case BLOCK_TYPE_I32:
+              prin (stream, "[i]");
+              break;
+            case BLOCK_TYPE_I64:
+              prin (stream, "[l]");
+              break;
+            case BLOCK_TYPE_F32:
+              prin (stream, "[f]");
+              break;
+            case BLOCK_TYPE_F64:
+              prin (stream, "[d]");
+              break;
+            }
+        }
+
+      switch (op->clas)
+        {
+        case wasm_special:
+        case wasm_eqz:
+        case wasm_binary:
+        case wasm_unary:
+        case wasm_conv:
+        case wasm_relational:
+        case wasm_drop:
+        case wasm_signature:
+        case wasm_call_import:
+        case wasm_typed:
+        case wasm_select:
+          break;
+        case wasm_break_table:
+          target_count = wasm_read_leb128
+            (pc + len, info, &error, &bytes_read, FALSE);
+          if (error)
+            return -1;
+          len += bytes_read;
+          prin (stream, " %ld", target_count);
+          for (i = 0; i < target_count + 1; i++)
+            {
+              long target = 0;
+              target = wasm_read_leb128
+                (pc + len, info, &error, &bytes_read, FALSE);
+              if (error)
+                return -1;
+              len += bytes_read;
+              prin (stream, " %ld", target);
+            }
+          break;
+        case wasm_break:
+        case wasm_break_if:
+          depth = wasm_read_leb128
+            (pc + len, info, &error, &bytes_read, FALSE);
+          if (error)
+            return -1;
+          len += bytes_read;
+          prin (stream, " %ld", depth);
+          break;
+        case wasm_return:
+          break;
+        case wasm_constant_i32:
+        case wasm_constant_i64:
+          constant = wasm_read_leb128
+            (pc + len, info, &error, &bytes_read, TRUE);
+          if (error)
+            return -1;
+          len += bytes_read;
+          prin (stream, " %lld", constant);
+          break;
+        case wasm_constant_f32:
+          /* This appears to be the best we can do, even though we're
+             using host doubles for WebAssembly floats.  */
+          ret = read_f32 (&fconstant, pc + len, info);
+          if (ret < 0)
+            return -1;
+          len += ret;
+          prin (stream, " %.*g", DECIMAL_DIG, fconstant);
+          break;
+        case wasm_constant_f64:
+          ret = read_f64 (&fconstant, pc + len, info);
+          if (ret < 0)
+            return -1;
+          len += ret;
+          prin (stream, " %.*g", DECIMAL_DIG, fconstant);
+          break;
+        case wasm_call:
+          index = wasm_read_leb128
+            (pc + len, info, &error, &bytes_read, FALSE);
+          if (error)
+            return -1;
+          len += bytes_read;
+          prin (stream, " ");
+          private_data->section_prefix = ".space.function_index";
+          (*info->print_address_func) ((bfd_vma) index, info);
+          private_data->section_prefix = NULL;
+          break;
+        case wasm_call_indirect:
+          constant = wasm_read_leb128
+            (pc + len, info, &error, &bytes_read, FALSE);
+          if (error)
+            return -1;
+          len += bytes_read;
+          prin (stream, " %lld", constant);
+          constant = wasm_read_leb128
+            (pc + len, info, &error, &bytes_read, FALSE);
+          if (error)
+            return -1;
+          len += bytes_read;
+          prin (stream, " %lld", constant);
+          break;
+        case wasm_get_local:
+        case wasm_set_local:
+        case wasm_tee_local:
+          constant = wasm_read_leb128
+            (pc + len, info, &error, &bytes_read, FALSE);
+          if (error)
+            return -1;
+          len += bytes_read;
+          prin (stream, " %lld", constant);
+          if (strcmp (op->name + 4, "local") == 0)
+            {
+              if (private_data->print_registers
+                  && constant >= 0 && constant < nlocals)
+                prin (stream, " <%s>", locals[constant]);
+            }
+          else
+            {
+              if (private_data->print_well_known_globals
+                  && constant >= 0 && constant < nglobals)
+                prin (stream, " <%s>", globals[constant]);
+            }
+          break;
+        case wasm_grow_memory:
+        case wasm_current_memory:
+          constant = wasm_read_leb128
+            (pc + len, info, &error, &bytes_read, FALSE);
+          if (error)
+            return -1;
+          len += bytes_read;
+          prin (stream, " %lld", constant);
+          break;
+        case wasm_load:
+        case wasm_store:
+          flags = wasm_read_leb128
+            (pc + len, info, &error, &bytes_read, FALSE);
+          if (error)
+            return -1;
+          len += bytes_read;
+          offset = wasm_read_leb128
+            (pc + len, info, &error, &bytes_read, FALSE);
+          if (error)
+            return -1;
+          len += bytes_read;
+          prin (stream, " a=%ld %ld", flags, offset);
+        }
+    }
+  return len;
+}
+
+/* Print valid disassembler options to STREAM.  */
+
+void
+print_wasm32_disassembler_options (FILE *stream)
+{
+  unsigned int i, max_len = 0;
+  fprintf (stream, _("\
+The following WebAssembly-specific disassembler options are supported
for use\n\
+with the -M switch:\n"));
+
+  for (i = 0; i < ARRAY_SIZE (options); i++)
+    {
+      unsigned int len = strlen (options[i].name);
+      if (max_len < len)
+    max_len = len;
+    }
+
+  for (i = 0, max_len++; i < ARRAY_SIZE (options); i++)
+    fprintf (stream, "  %s%*c %s\n",
+         options[i].name,
+         (int)(max_len - strlen (options[i].name)), ' ',
+         _(options[i].description));
+}
-------------- next part --------------
diff --git a/gas/testsuite/gas/wasm32/allinsn.d b/gas/testsuite/gas/wasm32/allinsn.d
index 06124be4d6..c594c72501 100644
--- a/gas/testsuite/gas/wasm32/allinsn.d
+++ b/gas/testsuite/gas/wasm32/allinsn.d
@@ -11,7 +11,7 @@ Disassembly of section .text:
    0:	02 40       		block\[\]
    2:	0c 00       		br 0
    4:	0d 00       		br_if 0
-   6:	0e 01 01 01 		br_table 1 1
+   6:	0e 01 01 01 		br_table 1 1 1
    a:	10 00       		call 0x0
    c:	11 00 00    		call_indirect 0 0
    f:	1a          		drop
@@ -22,12 +22,12 @@ Disassembly of section .text:
   14:	8d          		f32.ceil
   15:	43 d0 0f 49 		f32.const 3.141590118408203125
   19:	40 
-  1a:	b2          		f32.convert_s_i32
-  1b:	b4          		f32.convert_s_i64
-  1c:	b3          		f32.convert_u_i32
-  1d:	b5          		f32.convert_u_i64
+  1a:	b2          		f32.convert_s/i32
+  1b:	b4          		f32.convert_s/i64
+  1c:	b3          		f32.convert_u/i32
+  1d:	b5          		f32.convert_u/i64
   1e:	98          		f32.copysign
-  1f:	b6          		f32.demote_f64
+  1f:	b6          		f32.demote/f64
   20:	95          		f32.div
   21:	5b          		f32.eq
   22:	8e          		f32.floor
@@ -42,7 +42,7 @@ Disassembly of section .text:
   2d:	5c          		f32.ne
   2e:	90          		f32.nearest
   2f:	8c          		f32.neg
-  30:	be          		f32.reinterpret_i32
+  30:	be          		f32.reinterpret/i32
   31:	91          		f32.sqrt
   32:	38 00 00    		f32.store a=0 0
   35:	93          		f32.sub
@@ -53,10 +53,10 @@ Disassembly of section .text:
   3a:	44 97 5f 4f 		f64.const 3.14158999999999976088e\+200
   3e:	fd bc 6a 90 
   42:	69 
-  43:	b7          		f64.convert_s_i32
-  44:	b9          		f64.convert_s_i64
-  45:	b8          		f64.convert_u_i32
-  46:	ba          		f64.convert_u_i64
+  43:	b7          		f64.convert_s/i32
+  44:	b9          		f64.convert_s/i64
+  45:	b8          		f64.convert_u/i32
+  46:	ba          		f64.convert_u/i64
   47:	a6          		f64.copysign
   48:	a3          		f64.div
   49:	61          		f64.eq
@@ -72,14 +72,14 @@ Disassembly of section .text:
   55:	62          		f64.ne
   56:	9e          		f64.nearest
   57:	9a          		f64.neg
-  58:	bb          		f64.promote_f32
-  59:	bf          		f64.reinterpret_i64
+  58:	bb          		f64.promote/f32
+  59:	bf          		f64.reinterpret/i64
   5a:	9f          		f64.sqrt
   5b:	39 00 00    		f64.store a=0 0
   5e:	a1          		f64.sub
   5f:	9d          		f64.trunc
-  60:	23 00       		get_global 0 <\$got>
-  62:	20 00       		get_local 0 <\$dpc>
+  60:	23 00       		get_global 0
+  62:	20 00       		get_local 0
   64:	6a          		i32.add
   65:	71          		i32.and
   66:	67          		i32.clz
@@ -107,7 +107,7 @@ Disassembly of section .text:
   8a:	47          		i32.ne
   8b:	72          		i32.or
   8c:	69          		i32.popcnt
-  8d:	bc          		i32.reinterpret_f32
+  8d:	bc          		i32.reinterpret/f32
   8e:	6f          		i32.rem_s
   8f:	70          		i32.rem_u
   90:	77          		i32.rotl
@@ -119,11 +119,11 @@ Disassembly of section .text:
   98:	3b 00 00    		i32.store16 a=0 0
   9b:	3a 00 00    		i32.store8 a=0 0
   9e:	6b          		i32.sub
-  9f:	a8          		i32.trunc_s_f32
-  a0:	aa          		i32.trunc_s_f64
-  a1:	a9          		i32.trunc_u_f32
-  a2:	ab          		i32.trunc_u_f64
-  a3:	a7          		i32.wrap_i64
+  9f:	a8          		i32.trunc_s/f32
+  a0:	aa          		i32.trunc_s/f64
+  a1:	a9          		i32.trunc_u/f32
+  a2:	ab          		i32.trunc_u/f64
+  a3:	a7          		i32.wrap/i64
   a4:	73          		i32.xor
   a5:	7c          		i64.add
   a6:	83          		i64.and
@@ -136,8 +136,8 @@ Disassembly of section .text:
   b4:	80          		i64.div_u
   b5:	51          		i64.eq
   b6:	50          		i64.eqz
-  b7:	ac          		i64.extend_s_i32
-  b8:	ad          		i64.extend_u_i32
+  b7:	ac          		i64.extend_s/i32
+  b8:	ad          		i64.extend_u/i32
   b9:	59          		i64.ge_s
   ba:	5a          		i64.ge_u
   bb:	55          		i64.gt_s
@@ -157,7 +157,7 @@ Disassembly of section .text:
   d7:	52          		i64.ne
   d8:	84          		i64.or
   d9:	7b          		i64.popcnt
-  da:	bd          		i64.reinterpret_f64
+  da:	bd          		i64.reinterpret/f64
   db:	81          		i64.rem_s
   dc:	82          		i64.rem_u
   dd:	89          		i64.rotl
@@ -170,20 +170,20 @@ Disassembly of section .text:
   e8:	3e 00 00    		i64.store32 a=0 0
   eb:	3c 00 00    		i64.store8 a=0 0
   ee:	7d          		i64.sub
-  ef:	ae          		i64.trunc_s_f32
-  f0:	b0          		i64.trunc_s_f64
-  f1:	af          		i64.trunc_u_f32
-  f2:	b1          		i64.trunc_u_f64
+  ef:	ae          		i64.trunc_s/f32
+  f0:	b0          		i64.trunc_s/f64
+  f1:	af          		i64.trunc_u/f32
+  f2:	b1          		i64.trunc_u/f64
   f3:	85          		i64.xor
   f4:	04 7f       		if\[i\]
   f6:	03 7e       		loop\[l\]
   f8:	01          		nop
   f9:	0f          		return
   fa:	1b          		select
-  fb:	24 00       		set_global 0 <\$got>
-  fd:	21 00       		set_local 0 <\$dpc>
+  fb:	24 00       		set_global 0
+  fd:	21 00       		set_local 0
   ff:	60          		f32.ge
- 100:	08          		.byte 08
+ 100:	08          		.byte 0x08
 
  101:	7f          		i64.div_s
  102:	7e          		i64.mul
@@ -194,5 +194,5 @@ Disassembly of section .text:
  107:	7e          		i64.mul
  108:	7f          		i64.div_s
  109:	00          		unreachable
- 10a:	22 00       		tee_local 0 <\$dpc>
+ 10a:	22 00       		tee_local 0
 	...
diff --git a/gas/testsuite/gas/wasm32/disass-2.d b/gas/testsuite/gas/wasm32/disass-2.d
new file mode 100644
index 0000000000..b6aa7954de
--- /dev/null
+++ b/gas/testsuite/gas/wasm32/disass-2.d
@@ -0,0 +1,9 @@
+#as:
+#objdump: -d -Mglobals
+#name: disass-2.d
+^dump.o:     file format elf32-wasm32$
+
+^Disassembly of section .text:$
+^00000000 <.text>:$
+^   0:	20 00       		get_local 0$
+^   2:	23 00       		get_global 0 <\$got>$
diff --git a/gas/testsuite/gas/wasm32/disass-2.s b/gas/testsuite/gas/wasm32/disass-2.s
new file mode 100644
index 0000000000..bed9410505
--- /dev/null
+++ b/gas/testsuite/gas/wasm32/disass-2.s
@@ -0,0 +1,3 @@
+        .text
+        get_local 0
+        get_global 0
diff --git a/gas/testsuite/gas/wasm32/disass.d b/gas/testsuite/gas/wasm32/disass.d
new file mode 100644
index 0000000000..2708137916
--- /dev/null
+++ b/gas/testsuite/gas/wasm32/disass.d
@@ -0,0 +1,9 @@
+#as:
+#objdump: -d -Mregisters,globals
+#name: disass.d
+^dump.o:     file format elf32-wasm32$
+
+^Disassembly of section .text:$
+^00000000 <.text>:$
+^   0:	20 00       		get_local 0 <\$dpc>$
+^   2:	23 00       		get_global 0 <\$got>$
diff --git a/gas/testsuite/gas/wasm32/disass.s b/gas/testsuite/gas/wasm32/disass.s
new file mode 100644
index 0000000000..bed9410505
--- /dev/null
+++ b/gas/testsuite/gas/wasm32/disass.s
@@ -0,0 +1,3 @@
+        .text
+        get_local 0
+        get_global 0
diff --git a/gas/testsuite/gas/wasm32/reloc.d b/gas/testsuite/gas/wasm32/reloc.d
index 9317e6e9a3..9dc54e7760 100644
--- a/gas/testsuite/gas/wasm32/reloc.d
+++ b/gas/testsuite/gas/wasm32/reloc.d
@@ -9,10 +9,11 @@ Disassembly of section .text:
 00000000 <.text>:
    0:	41 80 80 80 		i32.const 0
    4:	80 00 
-			1: R_ASMJS_LEB128_PLT	f
+			1: R_WASM32_PLT_SIG	__sigchar_FiiiiiiiE
+			1: R_WASM32_LEB128_PLT	f
    6:	41 80 80 80 		i32.const 0
    a:	80 00 
-			7: R_ASMJS_LEB128_GOT	x
+			7: R_WASM32_LEB128_GOT	x
    c:	41 80 80 80 		i32.const 0
   10:	80 00 
-			d: R_ASMJS_LEB128_GOT_CODE	f
+			d: R_WASM32_LEB128_GOT_CODE	f
diff --git a/gas/testsuite/gas/wasm32/reloc.s b/gas/testsuite/gas/wasm32/reloc.s
index 8cdfd58b96..cd34591b2c 100644
--- a/gas/testsuite/gas/wasm32/reloc.s
+++ b/gas/testsuite/gas/wasm32/reloc.s
@@ -1,3 +1,3 @@
-        i32.const f@plt
+        i32.const f@plt{__sigchar_FiiiiiiiE}
         i32.const x@got
         i32.const f@gotcode
diff --git a/gas/testsuite/gas/wasm32/wasm32.exp b/gas/testsuite/gas/wasm32/wasm32.exp
index e6d1819677..49c14e45f9 100644
--- a/gas/testsuite/gas/wasm32/wasm32.exp
+++ b/gas/testsuite/gas/wasm32/wasm32.exp
@@ -21,8 +21,6 @@
 # wasm32 assembler testsuite.
 
 if [istarget wasm32-*-*] {
-    # no disassembler support yet
-    setup_xfail "wasm32-*-*"
     run_dump_test "allinsn"
     # no GOT/PLT relocs yet.
     setup_xfail "wasm32-*-*"
@@ -55,4 +53,6 @@ if [istarget wasm32-*-*] {
     # illegal-23 has become legal
     run_list_test "illegal-24"
     run_list_test "illegal-25"
+    run_dump_test "disass"
+    run_dump_test "disass-2"
 }
diff --git a/include/dis-asm.h b/include/dis-asm.h
index f0544509d0..a6b65431de 100644
--- a/include/dis-asm.h
+++ b/include/dis-asm.h
@@ -318,6 +318,7 @@ extern int print_insn_v850		(bfd_vma, disassemble_info *);
 extern int print_insn_vax		(bfd_vma, disassemble_info *);
 extern int print_insn_visium		(bfd_vma, disassemble_info *);
 extern int print_insn_w65		(bfd_vma, disassemble_info *);
+extern int print_insn_wasm32		(bfd_vma, disassemble_info *);
 extern int print_insn_xc16x		(bfd_vma, disassemble_info *);
 extern int print_insn_xgate             (bfd_vma, disassemble_info *);
 extern int print_insn_xstormy16		(bfd_vma, disassemble_info *);
@@ -343,10 +344,12 @@ extern void print_riscv_disassembler_options (FILE *);
 extern void print_arm_disassembler_options (FILE *);
 extern void print_arc_disassembler_options (FILE *);
 extern void print_s390_disassembler_options (FILE *);
+extern void print_wasm32_disassembler_options (FILE *);
 extern bfd_boolean aarch64_symbol_is_valid (asymbol *, struct disassemble_info *);
 extern bfd_boolean arm_symbol_is_valid (asymbol *, struct disassemble_info *);
 extern void disassemble_init_powerpc (struct disassemble_info *);
 extern void disassemble_init_s390 (struct disassemble_info *);
+extern void disassemble_init_wasm32 (struct disassemble_info *);
 extern const disasm_options_t *disassembler_options_powerpc (void);
 extern const disasm_options_t *disassembler_options_arm (void);
 extern const disasm_options_t *disassembler_options_s390 (void);
diff --git a/opcodes/Makefile.am b/opcodes/Makefile.am
index b43c679bff..1ac6bb1128 100644
--- a/opcodes/Makefile.am
+++ b/opcodes/Makefile.am
@@ -259,6 +259,7 @@ TARGET_LIBOPCODES_CFILES = \
 	visium-dis.c \
 	visium-opc.c \
 	w65-dis.c \
+	wasm32-dis.c \
 	xc16x-asm.c \
 	xc16x-desc.c \
 	xc16x-dis.c \
diff --git a/opcodes/configure.ac b/opcodes/configure.ac
index ca982924e6..a9fbfd61f1 100644
--- a/opcodes/configure.ac
+++ b/opcodes/configure.ac
@@ -348,7 +348,7 @@ if test x${all_targets} = xfalse ; then
 	bfd_vax_arch)		ta="$ta vax-dis.lo" ;;
 	bfd_visium_arch)	ta="$ta visium-dis.lo visium-opc.lo" ;;
 	bfd_w65_arch)		ta="$ta w65-dis.lo" ;;
-        bfd_wasm32_arch)        ;;
+        bfd_wasm32_arch)        ta="$ta wasm32-dis.lo" ;;
 	bfd_we32k_arch)		;;
 	bfd_xc16x_arch)		ta="$ta xc16x-asm.lo xc16x-desc.lo xc16x-dis.lo xc16x-ibld.lo xc16x-opc.lo" using_cgen=yes ;;
 	bfd_xgate_arch)		ta="$ta xgate-dis.lo xgate-opc.lo" ;;
diff --git a/opcodes/disassemble.c b/opcodes/disassemble.c
index eef06584f4..dd7d3a32b7 100644
--- a/opcodes/disassemble.c
+++ b/opcodes/disassemble.c
@@ -94,6 +94,7 @@
 #define ARCH_vax
 #define ARCH_visium
 #define ARCH_w65
+#define ARCH_wasm32
 #define ARCH_xstormy16
 #define ARCH_xc16x
 #define ARCH_xgate
@@ -474,6 +475,11 @@ disassembler (bfd *abfd)
       disassemble = print_insn_w65;
       break;
 #endif
+#ifdef ARCH_wasm32
+    case bfd_arch_wasm32:
+      disassemble = print_insn_wasm32;
+      break;
+#endif
 #ifdef ARCH_xgate
     case bfd_arch_xgate:
       disassemble = print_insn_xgate;
@@ -580,6 +586,9 @@ disassembler_usage (FILE *stream ATTRIBUTE_UNUSED)
 #ifdef ARCH_s390
   print_s390_disassembler_options (stream);
 #endif
+#ifdef ARCH_wasm32
+  print_wasm32_disassembler_options (stream);
+#endif
 
   return;
 }
@@ -650,6 +659,11 @@ disassemble_init_for_target (struct disassemble_info * info)
       disassemble_init_powerpc (info);
       break;
 #endif
+#ifdef ARCH_wasm32
+    case bfd_arch_wasm32:
+      disassemble_init_wasm32 (info);
+      break;
+#endif
 #ifdef ARCH_s390
     case bfd_arch_s390:
       disassemble_init_s390 (info);
diff --git a/opcodes/wasm32-dis.c b/opcodes/wasm32-dis.c
new file mode 100644
index 0000000000..d3858880c0
--- /dev/null
+++ b/opcodes/wasm32-dis.c
@@ -0,0 +1,505 @@
+/* Opcode printing code for the WebAssembly target
+   Copyright (C) 2017 Free Software Foundation, Inc.
+
+   This file is part of libopcodes.
+
+   This library 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.
+
+   It 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, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
+
+#include "sysdep.h"
+#include "dis-asm.h"
+#include "opintl.h"
+#include "safe-ctype.h"
+#include "floatformat.h"
+#include <float.h>
+#include "libiberty.h"
+#include "elf-bfd.h"
+#include "elf/internal.h"
+#include "elf/wasm32.h"
+#include <stdint.h>
+
+/* Type names for blocks and signatures.  */
+#define BLOCK_TYPE_NONE              0x40
+#define BLOCK_TYPE_I32               0x7f
+#define BLOCK_TYPE_I64               0x7e
+#define BLOCK_TYPE_F32               0x7d
+#define BLOCK_TYPE_F64               0x7c
+
+enum wasm_class
+  {
+    wasm_typed,
+    wasm_special,
+    wasm_break,
+    wasm_break_if,
+    wasm_break_table,
+    wasm_return,
+    wasm_call,
+    wasm_call_import,
+    wasm_call_indirect,
+    wasm_get_local,
+    wasm_set_local,
+    wasm_tee_local,
+    wasm_drop,
+    wasm_constant_i32,
+    wasm_constant_i64,
+    wasm_constant_f32,
+    wasm_constant_f64,
+    wasm_unary,
+    wasm_binary,
+    wasm_conv,
+    wasm_load,
+    wasm_store,
+    wasm_select,
+    wasm_relational,
+    wasm_eqz,
+    wasm_current_memory,
+    wasm_grow_memory,
+    wasm_signature
+  };
+
+struct wasm32_private_data
+{
+  bfd_boolean print_registers;
+  bfd_boolean print_well_known_globals;
+
+  /* Limit valid symbols to those with a given prefix.  */
+  const char *section_prefix;
+};
+
+typedef struct
+{
+  const char *name;
+  const char *description;
+} wasm32_options_t;
+
+static const wasm32_options_t options[] =
+{
+  { "registers", N_("Disassemble \"register\" names") },
+  { "globals",   N_("Name well-known globals") },
+};
+
+#define WASM_OPCODE(opcode, name, intype, outtype, clas, signedness)     \
+  { name, wasm_ ## clas, opcode },
+
+struct wasm32_opcode_s
+{
+  const char *name;
+  enum wasm_class clas;
+  unsigned char opcode;
+} wasm32_opcodes[] =
+{
+#include "opcode/wasm.h"
+  { NULL, 0, 0 }
+};
+
+/* Parse the disassembler options in OPTS and initialize INFO.  */
+
+static void
+parse_wasm32_disassembler_options (struct disassemble_info *info,
+                                   char *opts)
+{
+  struct wasm32_private_data *private = info->private_data;
+  while (opts != NULL)
+    {
+      if (CONST_STRNEQ (opts, "registers"))
+        private->print_registers = TRUE;
+      else if (CONST_STRNEQ (opts, "globals"))
+        private->print_well_known_globals = TRUE;
+
+      opts = strchr (opts, ',');
+      if (opts)
+        opts++;
+    }
+}
+
+/* Check whether SYM is valid.  Special-case absolute symbols, which
+   are unhelpful to print, and arguments to a "call" insn, which we
+   want to be in a section matching a given prefix.  */
+
+static bfd_boolean
+wasm32_symbol_is_valid (asymbol *sym,
+                        struct disassemble_info *info)
+{
+  struct wasm32_private_data *private_data = info->private_data;
+
+  if (sym == NULL)
+    return FALSE;
+
+  if (strcmp(sym->section->name, "*ABS*") == 0)
+    return FALSE;
+
+  if (private_data && private_data->section_prefix != NULL
+      && strncmp (sym->section->name, private_data->section_prefix,
+                  strlen (private_data->section_prefix)))
+    return FALSE;
+
+  return TRUE;
+}
+
+/* Initialize the disassembler structures for INFO.  */
+
+void
+disassemble_init_wasm32 (struct disassemble_info *info)
+{
+  if (info->private_data == NULL)
+    {
+      static struct wasm32_private_data private;
+
+      private.print_registers = FALSE;
+      private.print_well_known_globals = FALSE;
+      private.section_prefix = NULL;
+
+      info->private_data = &private;
+    }
+
+  if (info->disassembler_options)
+    {
+      parse_wasm32_disassembler_options (info, info->disassembler_options);
+
+      info->disassembler_options = NULL;
+    }
+
+  info->symbol_is_valid = wasm32_symbol_is_valid;
+}
+
+/* Read an LEB128-encoded integer from INFO at address PC, reading one
+   byte at a time.  Set ERROR_RETURN if no complete integer could be
+   read, LENGTH_RETURN to the number oof bytes read (including bytes
+   in incomplete numbers).  SIGN means interpret the number as
+   SLEB128.  Unfortunately, this is a duplicate of wasm-module.c's
+   wasm_read_leb128 ().  */
+
+static uint64_t
+wasm_read_leb128 (bfd_vma                   pc,
+                  struct disassemble_info * info,
+                  bfd_boolean *             error_return,
+                  unsigned int *            length_return,
+                  bfd_boolean               sign)
+{
+  uint64_t result = 0;
+  unsigned int num_read = 0;
+  unsigned int shift = 0;
+  unsigned char byte = 0;
+  bfd_boolean success = FALSE;
+
+  while (info->read_memory_func (pc + num_read, &byte, 1, info) == 0)
+    {
+      num_read++;
+
+      result |= ((bfd_vma) (byte & 0x7f)) << shift;
+
+      shift += 7;
+      if ((byte & 0x80) == 0)
+        {
+          success = TRUE;
+          break;
+        }
+    }
+
+  if (length_return != NULL)
+    *length_return = num_read;
+  if (error_return != NULL)
+    *error_return = ! success;
+
+  if (sign && (shift < 8 * sizeof (result)) && (byte & 0x40))
+    result |= -((uint64_t) 1 << shift);
+
+  return result;
+}
+
+/* Read a 32-bit IEEE float from PC using INFO, convert it to a host
+   double, and store it at VALUE.  */
+
+static int
+read_f32 (double *value, bfd_vma pc, struct disassemble_info *info)
+{
+  bfd_byte buf[4];
+
+  if (info->read_memory_func (pc, buf, sizeof (buf), info))
+    return -1;
+
+  floatformat_to_double (&floatformat_ieee_single_little, buf,
+                         value);
+
+  return sizeof (buf);
+}
+
+/* Read a 64-bit IEEE float from PC using INFO, convert it to a host
+   double, and store it at VALUE.  */
+
+static int
+read_f64 (double *value, bfd_vma pc, struct disassemble_info *info)
+{
+  bfd_byte buf[8];
+
+  if (info->read_memory_func (pc, buf, sizeof (buf), info))
+    return -1;
+
+  floatformat_to_double (&floatformat_ieee_double_little, buf,
+                         value);
+
+  return sizeof (buf);
+}
+
+/* Main disassembly routine.  Disassemble insn at PC using INFO.  */
+
+int
+print_insn_wasm32 (bfd_vma pc, struct disassemble_info *info)
+{
+  unsigned char opcode;
+  struct wasm32_opcode_s *op;
+  bfd_byte buffer[16];
+  void *stream = info->stream;
+  fprintf_ftype prin = info->fprintf_func;
+  struct wasm32_private_data *private_data = info->private_data;
+  long long constant = 0;
+  double fconstant = 0.0;
+  long flags = 0;
+  long offset = 0;
+  long depth = 0;
+  long index = 0;
+  long target_count = 0;
+  long block_type = 0;
+  int len = 1;
+  int ret = 0;
+  unsigned int bytes_read = 0;
+  int i;
+  const char *locals[] = {
+    "$dpc", "$sp1", "$r0", "$r1", "$rpc", "$pc0",
+    "$rp", "$fp", "$sp",
+    "$r2", "$r3", "$r4", "$r5", "$r6", "$r7",
+    "$i0", "$i1", "$i2", "$i3", "$i4", "$i5", "$i6", "$i7",
+    "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
+  };
+  int nlocals = ARRAY_SIZE (locals);
+  const char *globals[] = {
+    "$got", "$plt", "$gpo"
+  };
+  int nglobals = ARRAY_SIZE (globals);
+  bfd_boolean error = FALSE;
+
+  if (info->read_memory_func (pc, buffer, 1, info))
+    return -1;
+
+  opcode = buffer[0];
+
+  for (op = wasm32_opcodes; op->name; op++)
+    if (op->opcode == opcode)
+      break;
+
+  if (!op->name)
+    {
+      prin (stream, "\t.byte 0x%02x\n", buffer[0]);
+      return 1;
+    }
+  else
+    {
+      len = 1;
+
+      prin (stream, "\t");
+      prin (stream, "%s", op->name);
+
+      if (op->clas == wasm_typed)
+        {
+          block_type = wasm_read_leb128
+            (pc + len, info, &error, &bytes_read, FALSE);
+          if (error)
+            return -1;
+          len += bytes_read;
+          switch (block_type)
+            {
+            case BLOCK_TYPE_NONE:
+              prin (stream, "[]");
+              break;
+            case BLOCK_TYPE_I32:
+              prin (stream, "[i]");
+              break;
+            case BLOCK_TYPE_I64:
+              prin (stream, "[l]");
+              break;
+            case BLOCK_TYPE_F32:
+              prin (stream, "[f]");
+              break;
+            case BLOCK_TYPE_F64:
+              prin (stream, "[d]");
+              break;
+            }
+        }
+
+      switch (op->clas)
+        {
+        case wasm_special:
+        case wasm_eqz:
+        case wasm_binary:
+        case wasm_unary:
+        case wasm_conv:
+        case wasm_relational:
+        case wasm_drop:
+        case wasm_signature:
+        case wasm_call_import:
+        case wasm_typed:
+        case wasm_select:
+          break;
+        case wasm_break_table:
+          target_count = wasm_read_leb128
+            (pc + len, info, &error, &bytes_read, FALSE);
+          if (error)
+            return -1;
+          len += bytes_read;
+          prin (stream, " %ld", target_count);
+          for (i = 0; i < target_count + 1; i++)
+            {
+              long target = 0;
+              target = wasm_read_leb128
+                (pc + len, info, &error, &bytes_read, FALSE);
+              if (error)
+                return -1;
+              len += bytes_read;
+              prin (stream, " %ld", target);
+            }
+          break;
+        case wasm_break:
+        case wasm_break_if:
+          depth = wasm_read_leb128
+            (pc + len, info, &error, &bytes_read, FALSE);
+          if (error)
+            return -1;
+          len += bytes_read;
+          prin (stream, " %ld", depth);
+          break;
+        case wasm_return:
+          break;
+        case wasm_constant_i32:
+        case wasm_constant_i64:
+          constant = wasm_read_leb128
+            (pc + len, info, &error, &bytes_read, TRUE);
+          if (error)
+            return -1;
+          len += bytes_read;
+          prin (stream, " %lld", constant);
+          break;
+        case wasm_constant_f32:
+          /* This appears to be the best we can do, even though we're
+             using host doubles for WebAssembly floats.  */
+          ret = read_f32 (&fconstant, pc + len, info);
+          if (ret < 0)
+            return -1;
+          len += ret;
+          prin (stream, " %.*g", DECIMAL_DIG, fconstant);
+          break;
+        case wasm_constant_f64:
+          ret = read_f64 (&fconstant, pc + len, info);
+          if (ret < 0)
+            return -1;
+          len += ret;
+          prin (stream, " %.*g", DECIMAL_DIG, fconstant);
+          break;
+        case wasm_call:
+          index = wasm_read_leb128
+            (pc + len, info, &error, &bytes_read, FALSE);
+          if (error)
+            return -1;
+          len += bytes_read;
+          prin (stream, " ");
+          private_data->section_prefix = ".space.function_index";
+          (*info->print_address_func) ((bfd_vma) index, info);
+          private_data->section_prefix = NULL;
+          break;
+        case wasm_call_indirect:
+          constant = wasm_read_leb128
+            (pc + len, info, &error, &bytes_read, FALSE);
+          if (error)
+            return -1;
+          len += bytes_read;
+          prin (stream, " %lld", constant);
+          constant = wasm_read_leb128
+            (pc + len, info, &error, &bytes_read, FALSE);
+          if (error)
+            return -1;
+          len += bytes_read;
+          prin (stream, " %lld", constant);
+          break;
+        case wasm_get_local:
+        case wasm_set_local:
+        case wasm_tee_local:
+          constant = wasm_read_leb128
+            (pc + len, info, &error, &bytes_read, FALSE);
+          if (error)
+            return -1;
+          len += bytes_read;
+          prin (stream, " %lld", constant);
+          if (strcmp (op->name + 4, "local") == 0)
+            {
+              if (private_data->print_registers
+                  && constant >= 0 && constant < nlocals)
+                prin (stream, " <%s>", locals[constant]);
+            }
+          else
+            {
+              if (private_data->print_well_known_globals
+                  && constant >= 0 && constant < nglobals)
+                prin (stream, " <%s>", globals[constant]);
+            }
+          break;
+        case wasm_grow_memory:
+        case wasm_current_memory:
+          constant = wasm_read_leb128
+            (pc + len, info, &error, &bytes_read, FALSE);
+          if (error)
+            return -1;
+          len += bytes_read;
+          prin (stream, " %lld", constant);
+          break;
+        case wasm_load:
+        case wasm_store:
+          flags = wasm_read_leb128
+            (pc + len, info, &error, &bytes_read, FALSE);
+          if (error)
+            return -1;
+          len += bytes_read;
+          offset = wasm_read_leb128
+            (pc + len, info, &error, &bytes_read, FALSE);
+          if (error)
+            return -1;
+          len += bytes_read;
+          prin (stream, " a=%ld %ld", flags, offset);
+        }
+    }
+  return len;
+}
+
+/* Print valid disassembler options to STREAM.  */
+
+void
+print_wasm32_disassembler_options (FILE *stream)
+{
+  unsigned int i, max_len = 0;
+  fprintf (stream, _("\
+The following WebAssembly-specific disassembler options are supported for use\n\
+with the -M switch:\n"));
+
+  for (i = 0; i < ARRAY_SIZE (options); i++)
+    {
+      unsigned int len = strlen (options[i].name);
+      if (max_len < len)
+	max_len = len;
+    }
+
+  for (i = 0, max_len++; i < ARRAY_SIZE (options); i++)
+    fprintf (stream, "  %s%*c %s\n",
+	     options[i].name,
+	     (int)(max_len - strlen (options[i].name)), ' ',
+	     _(options[i].description));
+}


More information about the Binutils mailing list