From 62ecb94c4a2929c1aace3fb5470d2a5100255811 Mon Sep 17 00:00:00 2001 From: Pip Cet Date: Thu, 6 Apr 2017 17:17:15 +0100 Subject: [PATCH] Add support for disassembling WebAssembly opcodes. include * dis-asm.h: Add prototypes for wasm32 disassembler. opcodes * 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. * po/opcodes.pot: Regenerate. gas * 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. --- gas/ChangeLog | 15 + gas/testsuite/gas/wasm32/allinsn.d | 64 ++-- gas/testsuite/gas/wasm32/disass-2.d | 9 + gas/testsuite/gas/wasm32/disass-2.s | 3 + gas/testsuite/gas/wasm32/disass.d | 9 + gas/testsuite/gas/wasm32/disass.s | 3 + gas/testsuite/gas/wasm32/reloc.d | 7 +- gas/testsuite/gas/wasm32/reloc.s | 2 +- gas/testsuite/gas/wasm32/wasm32.exp | 4 +- include/ChangeLog | 4 + include/dis-asm.h | 3 + opcodes/ChangeLog | 11 + opcodes/Makefile.am | 1 + opcodes/Makefile.in | 2 + opcodes/configure | 2 +- opcodes/configure.ac | 2 +- opcodes/disassemble.c | 14 + opcodes/po/POTFILES.in | 1 + opcodes/po/opcodes.pot | 18 +- opcodes/wasm32-dis.c | 521 ++++++++++++++++++++++++++++ 20 files changed, 654 insertions(+), 41 deletions(-) create mode 100644 gas/testsuite/gas/wasm32/disass-2.d create mode 100644 gas/testsuite/gas/wasm32/disass-2.s create mode 100644 gas/testsuite/gas/wasm32/disass.d create mode 100644 gas/testsuite/gas/wasm32/disass.s create mode 100644 opcodes/wasm32-dis.c diff --git a/gas/ChangeLog b/gas/ChangeLog index cc5f3496c3..0a0d207b86 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,18 @@ +2017-04-06 Pip Cet + + * 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. + 2017-04-04 H.J. Lu * NEWS: Mention support for ELF SHF_GNU_MBIND. 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/ChangeLog b/include/ChangeLog index 956e252612..330a6565a8 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,7 @@ +2017-04-06 Pip Cet + + * dis-asm.h: Add prototypes for wasm32 disassembler. + 2017-04-05 Pedro Alves * dis-asm.h (disassemble_info) : Now a diff --git a/include/dis-asm.h b/include/dis-asm.h index 2ed3d7ec6c..6f1801df98 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/ChangeLog b/opcodes/ChangeLog index 2889f53420..424d7fc12c 100644 --- a/opcodes/ChangeLog +++ b/opcodes/ChangeLog @@ -1,3 +1,14 @@ +2017-04-06 Pip Cet + + * 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. + * po/opcodes.pot: Regenerate. + 2017-04-05 Pedro Alves * arc-dis.c (parse_option, parse_disassembler_options): Constify. 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/Makefile.in b/opcodes/Makefile.in index 84efa6ca84..8c2d71f906 100644 --- a/opcodes/Makefile.in +++ b/opcodes/Makefile.in @@ -561,6 +561,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 \ @@ -969,6 +970,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/visium-dis.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/visium-opc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/w65-dis.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wasm32-dis.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xc16x-asm.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xc16x-desc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xc16x-dis.Plo@am__quote@ diff --git a/opcodes/configure b/opcodes/configure index ea26b91794..27d1472200 100755 --- a/opcodes/configure +++ b/opcodes/configure @@ -12724,7 +12724,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/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/po/POTFILES.in b/opcodes/po/POTFILES.in index a1f48dc3d5..cb1b5131e2 100644 --- a/opcodes/po/POTFILES.in +++ b/opcodes/po/POTFILES.in @@ -217,6 +217,7 @@ visium-dis.c visium-opc.c w65-dis.c w65-opc.h +wasm32-dis.c xc16x-asm.c xc16x-desc.c xc16x-desc.h diff --git a/opcodes/po/opcodes.pot b/opcodes/po/opcodes.pot index b62fc95124..ff22de4934 100644 --- a/opcodes/po/opcodes.pot +++ b/opcodes/po/opcodes.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: bug-binutils@gnu.org\n" -"POT-Creation-Date: 2017-03-29 17:08+0100\n" +"POT-Creation-Date: 2017-04-06 16:22+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -1695,6 +1695,22 @@ msgstr "" msgid "invalid register name" msgstr "" +#: wasm32-dis.c:89 +msgid "Disassemble \"register\" names" +msgstr "" + +#: wasm32-dis.c:90 +msgid "Name well-known globals" +msgstr "" + +#: wasm32-dis.c:489 +#, c-format +msgid "" +"The following WebAssembly-specific disassembler options are supported for " +"use\n" +"with the -M switch:\n" +msgstr "" + #: xc16x-asm.c:65 msgid "Missing '#' prefix" msgstr "" diff --git a/opcodes/wasm32-dis.c b/opcodes/wasm32-dis.c new file mode 100644 index 0000000000..80e4ffe81a --- /dev/null +++ b/opcodes/wasm32-dis.c @@ -0,0 +1,521 @@ +/* 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 +#include "libiberty.h" +#include "elf-bfd.h" +#include "elf/internal.h" +#include "elf/wasm32.h" +#include + +/* 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, + const 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)); +} -- 2.34.1