1 /* Opcode printing code for the WebAssembly target
2 Copyright (C) 2017-2020 Free Software Foundation, Inc.
4 This file is part of libopcodes.
6 This library is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 It is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
19 MA 02110-1301, USA. */
22 #include "disassemble.h"
24 #include "safe-ctype.h"
25 #include "floatformat.h"
26 #include "libiberty.h"
28 #include "elf/internal.h"
29 #include "elf/wasm32.h"
30 #include "bfd_stdint.h"
32 /* Type names for blocks and signatures. */
33 #define BLOCK_TYPE_NONE 0x40
34 #define BLOCK_TYPE_I32 0x7f
35 #define BLOCK_TYPE_I64 0x7e
36 #define BLOCK_TYPE_F32 0x7d
37 #define BLOCK_TYPE_F64 0x7c
71 struct wasm32_private_data
73 bfd_boolean print_registers
;
74 bfd_boolean print_well_known_globals
;
76 /* Limit valid symbols to those with a given prefix. */
77 const char *section_prefix
;
83 const char *description
;
86 static const wasm32_options_t options
[] =
88 { "registers", N_("Disassemble \"register\" names") },
89 { "globals", N_("Name well-known globals") },
92 #define WASM_OPCODE(opcode, name, intype, outtype, clas, signedness) \
93 { name, wasm_ ## clas, opcode },
95 struct wasm32_opcode_s
102 #include "opcode/wasm.h"
106 /* Parse the disassembler options in OPTS and initialize INFO. */
109 parse_wasm32_disassembler_options (struct disassemble_info
*info
,
112 struct wasm32_private_data
*private = info
->private_data
;
116 if (CONST_STRNEQ (opts
, "registers"))
117 private->print_registers
= TRUE
;
118 else if (CONST_STRNEQ (opts
, "globals"))
119 private->print_well_known_globals
= TRUE
;
121 opts
= strchr (opts
, ',');
127 /* Check whether SYM is valid. Special-case absolute symbols, which
128 are unhelpful to print, and arguments to a "call" insn, which we
129 want to be in a section matching a given prefix. */
132 wasm32_symbol_is_valid (asymbol
*sym
,
133 struct disassemble_info
*info
)
135 struct wasm32_private_data
*private_data
= info
->private_data
;
140 if (strcmp(sym
->section
->name
, "*ABS*") == 0)
143 if (private_data
&& private_data
->section_prefix
!= NULL
144 && strncmp (sym
->section
->name
, private_data
->section_prefix
,
145 strlen (private_data
->section_prefix
)))
151 /* Initialize the disassembler structures for INFO. */
154 disassemble_init_wasm32 (struct disassemble_info
*info
)
156 if (info
->private_data
== NULL
)
158 static struct wasm32_private_data
private;
160 private.print_registers
= FALSE
;
161 private.print_well_known_globals
= FALSE
;
162 private.section_prefix
= NULL
;
164 info
->private_data
= &private;
167 if (info
->disassembler_options
)
169 parse_wasm32_disassembler_options (info
, info
->disassembler_options
);
171 info
->disassembler_options
= NULL
;
174 info
->symbol_is_valid
= wasm32_symbol_is_valid
;
177 /* Read an LEB128-encoded integer from INFO at address PC, reading one
178 byte at a time. Set ERROR_RETURN if no complete integer could be
179 read, LENGTH_RETURN to the number oof bytes read (including bytes
180 in incomplete numbers). SIGN means interpret the number as
181 SLEB128. Unfortunately, this is a duplicate of wasm-module.c's
182 wasm_read_leb128 (). */
185 wasm_read_leb128 (bfd_vma pc
,
186 struct disassemble_info
* info
,
187 bfd_boolean
* error_return
,
188 unsigned int * length_return
,
192 unsigned int num_read
= 0;
193 unsigned int shift
= 0;
194 unsigned char byte
= 0;
197 while (info
->read_memory_func (pc
+ num_read
, &byte
, 1, info
) == 0)
201 if (shift
< sizeof (result
) * 8)
203 result
|= ((uint64_t) (byte
& 0x7f)) << shift
;
204 if ((result
>> shift
) != (byte
& 0x7f))
209 else if ((byte
& 0x7f) != 0)
212 if ((byte
& 0x80) == 0)
215 if (sign
&& (shift
< 8 * sizeof (result
)) && (byte
& 0x40))
216 result
|= -((uint64_t) 1 << shift
);
221 if (length_return
!= NULL
)
222 *length_return
= num_read
;
223 if (error_return
!= NULL
)
224 *error_return
= status
!= 0;
229 /* Read a 32-bit IEEE float from PC using INFO, convert it to a host
230 double, and store it at VALUE. */
233 read_f32 (double *value
, bfd_vma pc
, struct disassemble_info
*info
)
237 if (info
->read_memory_func (pc
, buf
, sizeof (buf
), info
))
240 floatformat_to_double (&floatformat_ieee_single_little
, buf
,
246 /* Read a 64-bit IEEE float from PC using INFO, convert it to a host
247 double, and store it at VALUE. */
250 read_f64 (double *value
, bfd_vma pc
, struct disassemble_info
*info
)
254 if (info
->read_memory_func (pc
, buf
, sizeof (buf
), info
))
257 floatformat_to_double (&floatformat_ieee_double_little
, buf
,
263 /* Main disassembly routine. Disassemble insn at PC using INFO. */
266 print_insn_wasm32 (bfd_vma pc
, struct disassemble_info
*info
)
268 unsigned char opcode
;
269 struct wasm32_opcode_s
*op
;
271 void *stream
= info
->stream
;
272 fprintf_ftype prin
= info
->fprintf_func
;
273 struct wasm32_private_data
*private_data
= info
->private_data
;
274 long long constant
= 0;
275 double fconstant
= 0.0;
279 long function_index
= 0;
280 long target_count
= 0;
284 unsigned int bytes_read
= 0;
286 const char *locals
[] =
288 "$dpc", "$sp1", "$r0", "$r1", "$rpc", "$pc0",
290 "$r2", "$r3", "$r4", "$r5", "$r6", "$r7",
291 "$i0", "$i1", "$i2", "$i3", "$i4", "$i5", "$i6", "$i7",
292 "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
294 int nlocals
= ARRAY_SIZE (locals
);
295 const char *globals
[] =
297 "$got", "$plt", "$gpo"
299 int nglobals
= ARRAY_SIZE (globals
);
300 bfd_boolean error
= FALSE
;
302 if (info
->read_memory_func (pc
, buffer
, 1, info
))
307 for (op
= wasm32_opcodes
; op
->name
; op
++)
308 if (op
->opcode
== opcode
)
313 prin (stream
, "\t.byte 0x%02x\n", buffer
[0]);
321 prin (stream
, "%s", op
->name
);
323 if (op
->clas
== wasm_typed
)
325 block_type
= wasm_read_leb128
326 (pc
+ len
, info
, &error
, &bytes_read
, FALSE
);
332 case BLOCK_TYPE_NONE
:
336 prin (stream
, "[i]");
339 prin (stream
, "[l]");
342 prin (stream
, "[f]");
345 prin (stream
, "[d]");
357 case wasm_relational
:
360 case wasm_call_import
:
365 case wasm_break_table
:
366 target_count
= wasm_read_leb128
367 (pc
+ len
, info
, &error
, &bytes_read
, FALSE
);
371 prin (stream
, " %ld", target_count
);
372 for (i
= 0; i
< target_count
+ 1; i
++)
375 target
= wasm_read_leb128
376 (pc
+ len
, info
, &error
, &bytes_read
, FALSE
);
380 prin (stream
, " %ld", target
);
386 depth
= wasm_read_leb128
387 (pc
+ len
, info
, &error
, &bytes_read
, FALSE
);
391 prin (stream
, " %ld", depth
);
397 case wasm_constant_i32
:
398 case wasm_constant_i64
:
399 constant
= wasm_read_leb128
400 (pc
+ len
, info
, &error
, &bytes_read
, TRUE
);
404 prin (stream
, " %lld", constant
);
407 case wasm_constant_f32
:
408 /* This appears to be the best we can do, even though we're
409 using host doubles for WebAssembly floats. */
410 ret
= read_f32 (&fconstant
, pc
+ len
, info
);
414 prin (stream
, " %.9g", fconstant
);
417 case wasm_constant_f64
:
418 ret
= read_f64 (&fconstant
, pc
+ len
, info
);
422 prin (stream
, " %.17g", fconstant
);
426 function_index
= wasm_read_leb128
427 (pc
+ len
, info
, &error
, &bytes_read
, FALSE
);
432 private_data
->section_prefix
= ".space.function_index";
433 (*info
->print_address_func
) ((bfd_vma
) function_index
, info
);
434 private_data
->section_prefix
= NULL
;
437 case wasm_call_indirect
:
438 constant
= wasm_read_leb128
439 (pc
+ len
, info
, &error
, &bytes_read
, FALSE
);
443 prin (stream
, " %lld", constant
);
444 constant
= wasm_read_leb128
445 (pc
+ len
, info
, &error
, &bytes_read
, FALSE
);
449 prin (stream
, " %lld", constant
);
455 constant
= wasm_read_leb128
456 (pc
+ len
, info
, &error
, &bytes_read
, FALSE
);
460 prin (stream
, " %lld", constant
);
461 if (strcmp (op
->name
+ 4, "local") == 0)
463 if (private_data
->print_registers
464 && constant
>= 0 && constant
< nlocals
)
465 prin (stream
, " <%s>", locals
[constant
]);
469 if (private_data
->print_well_known_globals
470 && constant
>= 0 && constant
< nglobals
)
471 prin (stream
, " <%s>", globals
[constant
]);
475 case wasm_grow_memory
:
476 case wasm_current_memory
:
477 constant
= wasm_read_leb128
478 (pc
+ len
, info
, &error
, &bytes_read
, FALSE
);
482 prin (stream
, " %lld", constant
);
487 flags
= wasm_read_leb128
488 (pc
+ len
, info
, &error
, &bytes_read
, FALSE
);
492 offset
= wasm_read_leb128
493 (pc
+ len
, info
, &error
, &bytes_read
, FALSE
);
497 prin (stream
, " a=%ld %ld", flags
, offset
);
503 /* Print valid disassembler options to STREAM. */
506 print_wasm32_disassembler_options (FILE *stream
)
508 unsigned int i
, max_len
= 0;
510 fprintf (stream
, _("\
511 The following WebAssembly-specific disassembler options are supported for use\n\
512 with the -M switch:\n"));
514 for (i
= 0; i
< ARRAY_SIZE (options
); i
++)
516 unsigned int len
= strlen (options
[i
].name
);
522 for (i
= 0, max_len
++; i
< ARRAY_SIZE (options
); i
++)
523 fprintf (stream
, " %s%*c %s\n",
525 (int)(max_len
- strlen (options
[i
].name
)), ' ',
526 _(options
[i
].description
));