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.
This commit is contained in:
parent
0dedf3777d
commit
62ecb94c4a
|
@ -1,3 +1,18 @@
|
|||
2017-04-06 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.
|
||||
|
||||
2017-04-04 H.J. Lu <hongjiu.lu@intel.com>
|
||||
|
||||
* NEWS: Mention support for ELF SHF_GNU_MBIND.
|
||||
|
|
|
@ -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
|
||||
...
|
||||
|
|
|
@ -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>$
|
|
@ -0,0 +1,3 @@
|
|||
.text
|
||||
get_local 0
|
||||
get_global 0
|
|
@ -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>$
|
|
@ -0,0 +1,3 @@
|
|||
.text
|
||||
get_local 0
|
||||
get_global 0
|
|
@ -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
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
i32.const f@plt
|
||||
i32.const f@plt{__sigchar_FiiiiiiiE}
|
||||
i32.const x@got
|
||||
i32.const f@gotcode
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
2017-04-06 Pip Cet <pipcet@gmail.com>
|
||||
|
||||
* dis-asm.h: Add prototypes for wasm32 disassembler.
|
||||
|
||||
2017-04-05 Pedro Alves <palves@redhat.com>
|
||||
|
||||
* dis-asm.h (disassemble_info) <disassembler_options>: Now a
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,3 +1,14 @@
|
|||
2017-04-06 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.
|
||||
* po/opcodes.pot: Regenerate.
|
||||
|
||||
2017-04-05 Pedro Alves <palves@redhat.com>
|
||||
|
||||
* arc-dis.c (parse_option, parse_disassembler_options): Constify.
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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@
|
||||
|
|
|
@ -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" ;;
|
||||
|
|
|
@ -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" ;;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\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 ""
|
||||
|
|
|
@ -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 <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,
|
||||
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));
|
||||
}
|
Loading…
Reference in New Issue