1 /* Opcode printing code for the WebAssembly target
2 Copyright (C) 2017-2024 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"
37 /* Type names for blocks and signatures. */
38 #define BLOCK_TYPE_NONE 0x40
39 #define BLOCK_TYPE_I32 0x7f
40 #define BLOCK_TYPE_I64 0x7e
41 #define BLOCK_TYPE_F32 0x7d
42 #define BLOCK_TYPE_F64 0x7c
76 struct wasm32_private_data
79 bool print_well_known_globals
;
81 /* Limit valid symbols to those with a given prefix. */
82 const char *section_prefix
;
88 const char *description
;
91 static const wasm32_options_t options
[] =
93 { "registers", N_("Disassemble \"register\" names") },
94 { "globals", N_("Name well-known globals") },
97 #define WASM_OPCODE(opcode, name, intype, outtype, clas, signedness) \
98 { name, wasm_ ## clas, opcode },
100 struct wasm32_opcode_s
103 enum wasm_class clas
;
104 unsigned char opcode
;
107 #include "opcode/wasm.h"
111 /* Parse the disassembler options in OPTS and initialize INFO. */
114 parse_wasm32_disassembler_options (struct disassemble_info
*info
,
117 struct wasm32_private_data
*private = info
->private_data
;
121 if (startswith (opts
, "registers"))
122 private->print_registers
= true;
123 else if (startswith (opts
, "globals"))
124 private->print_well_known_globals
= true;
126 opts
= strchr (opts
, ',');
132 /* Check whether SYM is valid. Special-case absolute symbols, which
133 are unhelpful to print, and arguments to a "call" insn, which we
134 want to be in a section matching a given prefix. */
137 wasm32_symbol_is_valid (asymbol
*sym
,
138 struct disassemble_info
*info
)
140 struct wasm32_private_data
*private_data
= info
->private_data
;
145 if (strcmp(sym
->section
->name
, "*ABS*") == 0)
148 if (private_data
&& private_data
->section_prefix
!= NULL
149 && strncmp (sym
->section
->name
, private_data
->section_prefix
,
150 strlen (private_data
->section_prefix
)))
156 /* Initialize the disassembler structures for INFO. */
159 disassemble_init_wasm32 (struct disassemble_info
*info
)
161 if (info
->private_data
== NULL
)
163 static struct wasm32_private_data
private;
165 private.print_registers
= false;
166 private.print_well_known_globals
= false;
167 private.section_prefix
= NULL
;
169 info
->private_data
= &private;
172 if (info
->disassembler_options
)
174 parse_wasm32_disassembler_options (info
, info
->disassembler_options
);
176 info
->disassembler_options
= NULL
;
179 info
->symbol_is_valid
= wasm32_symbol_is_valid
;
182 /* Read an LEB128-encoded integer from INFO at address PC, reading one
183 byte at a time. Set ERROR_RETURN if no complete integer could be
184 read, LENGTH_RETURN to the number oof bytes read (including bytes
185 in incomplete numbers). SIGN means interpret the number as
186 SLEB128. Unfortunately, this is a duplicate of wasm-module.c's
187 wasm_read_leb128 (). */
190 wasm_read_leb128 (bfd_vma pc
,
191 struct disassemble_info
*info
,
193 unsigned int *length_return
,
197 unsigned int num_read
= 0;
198 unsigned int shift
= 0;
199 unsigned char byte
= 0;
200 unsigned char lost
, mask
;
203 while (info
->read_memory_func (pc
+ num_read
, &byte
, 1, info
) == 0)
207 if (shift
< CHAR_BIT
* sizeof (result
))
209 result
|= ((uint64_t) (byte
& 0x7f)) << shift
;
210 /* These bits overflowed. */
211 lost
= byte
^ (result
>> shift
);
212 /* And this is the mask of possible overflow bits. */
213 mask
= 0x7f ^ ((uint64_t) 0x7f << shift
>> shift
);
221 if ((lost
& mask
) != (sign
&& (int64_t) result
< 0 ? mask
: 0))
224 if ((byte
& 0x80) == 0)
227 if (sign
&& shift
< CHAR_BIT
* sizeof (result
) && (byte
& 0x40))
228 result
|= -((uint64_t) 1 << shift
);
233 if (length_return
!= NULL
)
234 *length_return
= num_read
;
235 if (error_return
!= NULL
)
236 *error_return
= status
!= 0;
241 /* Read a 32-bit IEEE float from PC using INFO, convert it to a host
242 double, and store it at VALUE. */
245 read_f32 (double *value
, bfd_vma pc
, struct disassemble_info
*info
)
249 if (info
->read_memory_func (pc
, buf
, sizeof (buf
), info
))
252 floatformat_to_double (&floatformat_ieee_single_little
, buf
,
258 /* Read a 64-bit IEEE float from PC using INFO, convert it to a host
259 double, and store it at VALUE. */
262 read_f64 (double *value
, bfd_vma pc
, struct disassemble_info
*info
)
266 if (info
->read_memory_func (pc
, buf
, sizeof (buf
), info
))
269 floatformat_to_double (&floatformat_ieee_double_little
, buf
,
275 /* Main disassembly routine. Disassemble insn at PC using INFO. */
278 print_insn_wasm32 (bfd_vma pc
, struct disassemble_info
*info
)
280 unsigned char opcode
;
281 struct wasm32_opcode_s
*op
;
283 void *stream
= info
->stream
;
284 fprintf_ftype prin
= info
->fprintf_func
;
285 struct wasm32_private_data
*private_data
= info
->private_data
;
288 unsigned int bytes_read
;
291 if (info
->read_memory_func (pc
, buffer
, 1, info
))
296 for (op
= wasm32_opcodes
; op
->name
; op
++)
297 if (op
->opcode
== opcode
)
302 prin (stream
, "\t.byte 0x%02x\n", buffer
[0]);
309 prin (stream
, "%s", op
->name
);
311 if (op
->clas
== wasm_typed
)
313 val
= wasm_read_leb128 (pc
+ len
, info
, &error
, &bytes_read
, false);
319 case BLOCK_TYPE_NONE
:
323 prin (stream
, "[i]");
326 prin (stream
, "[l]");
329 prin (stream
, "[f]");
332 prin (stream
, "[d]");
346 case wasm_relational
:
349 case wasm_call_import
:
354 case wasm_break_table
:
356 uint32_t target_count
, i
;
357 val
= wasm_read_leb128 (pc
+ len
, info
, &error
, &bytes_read
,
360 if (error
|| target_count
!= val
|| target_count
== (uint32_t) -1)
363 prin (stream
, " %u", target_count
);
364 for (i
= 0; i
< target_count
+ 1; i
++)
367 val
= wasm_read_leb128 (pc
+ len
, info
, &error
, &bytes_read
,
370 if (error
|| target
!= val
)
373 prin (stream
, " %u", target
);
382 val
= wasm_read_leb128 (pc
+ len
, info
, &error
, &bytes_read
,
385 if (error
|| depth
!= val
)
388 prin (stream
, " %u", depth
);
395 case wasm_constant_i32
:
396 case wasm_constant_i64
:
397 val
= wasm_read_leb128 (pc
+ len
, info
, &error
, &bytes_read
, true);
401 prin (stream
, " %" PRId64
, val
);
404 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
);
418 case wasm_constant_f64
:
422 ret
= read_f64 (&fconstant
, pc
+ len
, info
);
426 prin (stream
, " %.17g", fconstant
);
432 uint32_t function_index
;
433 val
= wasm_read_leb128 (pc
+ len
, info
, &error
, &bytes_read
,
435 function_index
= val
;
436 if (error
|| function_index
!= val
)
440 private_data
->section_prefix
= ".space.function_index";
441 (*info
->print_address_func
) ((bfd_vma
) function_index
, info
);
442 private_data
->section_prefix
= NULL
;
446 case wasm_call_indirect
:
448 uint32_t type_index
, xtra_index
;
449 val
= wasm_read_leb128 (pc
+ len
, info
, &error
, &bytes_read
,
452 if (error
|| type_index
!= val
)
455 prin (stream
, " %u", type_index
);
456 val
= wasm_read_leb128 (pc
+ len
, info
, &error
, &bytes_read
,
459 if (error
|| xtra_index
!= val
)
462 prin (stream
, " %u", xtra_index
);
470 uint32_t local_index
;
471 val
= wasm_read_leb128 (pc
+ len
, info
, &error
, &bytes_read
,
474 if (error
|| local_index
!= val
)
477 prin (stream
, " %u", local_index
);
478 if (strcmp (op
->name
+ 4, "local") == 0)
480 static const char *locals
[] =
482 "$dpc", "$sp1", "$r0", "$r1", "$rpc", "$pc0",
484 "$r2", "$r3", "$r4", "$r5", "$r6", "$r7",
485 "$i0", "$i1", "$i2", "$i3", "$i4", "$i5", "$i6", "$i7",
486 "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
488 if (private_data
->print_registers
489 && local_index
< ARRAY_SIZE (locals
))
490 prin (stream
, " <%s>", locals
[local_index
]);
494 static const char *globals
[] =
496 "$got", "$plt", "$gpo"
498 if (private_data
->print_well_known_globals
499 && local_index
< ARRAY_SIZE (globals
))
500 prin (stream
, " <%s>", globals
[local_index
]);
505 case wasm_grow_memory
:
506 case wasm_current_memory
:
508 uint32_t reserved_size
;
509 val
= wasm_read_leb128 (pc
+ len
, info
, &error
, &bytes_read
,
512 if (error
|| reserved_size
!= val
)
515 prin (stream
, " %u", reserved_size
);
522 uint32_t flags
, offset
;
523 val
= wasm_read_leb128 (pc
+ len
, info
, &error
, &bytes_read
,
526 if (error
|| flags
!= val
)
529 val
= wasm_read_leb128 (pc
+ len
, info
, &error
, &bytes_read
,
532 if (error
|| offset
!= val
)
535 prin (stream
, " a=%u %u", flags
, offset
);
542 /* Print valid disassembler options to STREAM. */
545 print_wasm32_disassembler_options (FILE *stream
)
547 unsigned int i
, max_len
= 0;
549 fprintf (stream
, _("\
550 The following WebAssembly-specific disassembler options are supported for use\n\
551 with the -M switch:\n"));
553 for (i
= 0; i
< ARRAY_SIZE (options
); i
++)
555 unsigned int len
= strlen (options
[i
].name
);
561 for (i
= 0, max_len
++; i
< ARRAY_SIZE (options
); i
++)
562 fprintf (stream
, " %s%*c %s\n",
564 (int)(max_len
- strlen (options
[i
].name
)), ' ',
565 _(options
[i
].description
));