1 /* Opcode printing code for the WebAssembly target
2 Copyright (C) 2017 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"
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;
195 bfd_boolean success = FALSE;
197 while (info->read_memory_func (pc + num_read, &byte, 1, info) == 0)
201 result |= ((bfd_vma) (byte & 0x7f)) << shift;
204 if ((byte & 0x80) == 0)
211 if (length_return != NULL)
212 *length_return = num_read;
213 if (error_return != NULL)
214 *error_return = ! success;
216 if (sign && (shift < 8 * sizeof (result)) && (byte & 0x40))
217 result |= -((uint64_t) 1 << shift);
222 /* Read a 32-bit IEEE float from PC using INFO, convert it to a host
223 double, and store it at VALUE. */
226 read_f32 (double *value, bfd_vma pc, struct disassemble_info *info)
230 if (info->read_memory_func (pc, buf, sizeof (buf), info))
233 floatformat_to_double (&floatformat_ieee_single_little, buf,
239 /* Read a 64-bit IEEE float from PC using INFO, convert it to a host
240 double, and store it at VALUE. */
243 read_f64 (double *value, bfd_vma pc, struct disassemble_info *info)
247 if (info->read_memory_func (pc, buf, sizeof (buf), info))
250 floatformat_to_double (&floatformat_ieee_double_little, buf,
256 /* Main disassembly routine. Disassemble insn at PC using INFO. */
259 print_insn_wasm32 (bfd_vma pc, struct disassemble_info *info)
261 unsigned char opcode;
262 struct wasm32_opcode_s *op;
264 void *stream = info->stream;
265 fprintf_ftype prin = info->fprintf_func;
266 struct wasm32_private_data *private_data = info->private_data;
267 long long constant = 0;
268 double fconstant = 0.0;
273 long target_count = 0;
277 unsigned int bytes_read = 0;
279 const char *locals[] =
281 "$dpc", "$sp1", "$r0", "$r1", "$rpc", "$pc0",
283 "$r2", "$r3", "$r4", "$r5", "$r6", "$r7",
284 "$i0", "$i1", "$i2", "$i3", "$i4", "$i5", "$i6", "$i7",
285 "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
287 int nlocals = ARRAY_SIZE (locals);
288 const char *globals[] =
290 "$got", "$plt", "$gpo"
292 int nglobals = ARRAY_SIZE (globals);
293 bfd_boolean error = FALSE;
295 if (info->read_memory_func (pc, buffer, 1, info))
300 for (op = wasm32_opcodes; op->name; op++)
301 if (op->opcode == opcode)
306 prin (stream, "\t.byte 0x%02x\n", buffer[0]);
314 prin (stream, "%s", op->name);
316 if (op->clas == wasm_typed)
318 block_type = wasm_read_leb128
319 (pc + len, info, &error, &bytes_read, FALSE);
325 case BLOCK_TYPE_NONE:
329 prin (stream, "[i]");
332 prin (stream, "[l]");
335 prin (stream, "[f]");
338 prin (stream, "[d]");
350 case wasm_relational:
353 case wasm_call_import:
358 case wasm_break_table:
359 target_count = wasm_read_leb128
360 (pc + len, info, &error, &bytes_read, FALSE);
364 prin (stream, " %ld", target_count);
365 for (i = 0; i < target_count + 1; i++)
368 target = wasm_read_leb128
369 (pc + len, info, &error, &bytes_read, FALSE);
373 prin (stream, " %ld", target);
379 depth = wasm_read_leb128
380 (pc + len, info, &error, &bytes_read, FALSE);
384 prin (stream, " %ld", depth);
390 case wasm_constant_i32:
391 case wasm_constant_i64:
392 constant = wasm_read_leb128
393 (pc + len, info, &error, &bytes_read, TRUE);
397 prin (stream, " %lld", constant);
400 case wasm_constant_f32:
401 /* This appears to be the best we can do, even though we're
402 using host doubles for WebAssembly floats. */
403 ret = read_f32 (&fconstant, pc + len, info);
407 prin (stream, " %.9g", fconstant);
410 case wasm_constant_f64:
411 ret = read_f64 (&fconstant, pc + len, info);
415 prin (stream, " %.17g", fconstant);
419 index = wasm_read_leb128
420 (pc + len, info, &error, &bytes_read, FALSE);
425 private_data->section_prefix = ".space.function_index";
426 (*info->print_address_func) ((bfd_vma) index, info);
427 private_data->section_prefix = NULL;
430 case wasm_call_indirect:
431 constant = wasm_read_leb128
432 (pc + len, info, &error, &bytes_read, FALSE);
436 prin (stream, " %lld", constant);
437 constant = wasm_read_leb128
438 (pc + len, info, &error, &bytes_read, FALSE);
442 prin (stream, " %lld", constant);
448 constant = wasm_read_leb128
449 (pc + len, info, &error, &bytes_read, FALSE);
453 prin (stream, " %lld", constant);
454 if (strcmp (op->name + 4, "local") == 0)
456 if (private_data->print_registers
457 && constant >= 0 && constant < nlocals)
458 prin (stream, " <%s>", locals[constant]);
462 if (private_data->print_well_known_globals
463 && constant >= 0 && constant < nglobals)
464 prin (stream, " <%s>", globals[constant]);
468 case wasm_grow_memory:
469 case wasm_current_memory:
470 constant = wasm_read_leb128
471 (pc + len, info, &error, &bytes_read, FALSE);
475 prin (stream, " %lld", constant);
480 flags = wasm_read_leb128
481 (pc + len, info, &error, &bytes_read, FALSE);
485 offset = wasm_read_leb128
486 (pc + len, info, &error, &bytes_read, FALSE);
490 prin (stream, " a=%ld %ld", flags, offset);
496 /* Print valid disassembler options to STREAM. */
499 print_wasm32_disassembler_options (FILE *stream)
501 unsigned int i, max_len = 0;
503 fprintf (stream, _("\
504 The following WebAssembly-specific disassembler options are supported for use\n\
505 with the -M switch:\n"));
507 for (i = 0; i < ARRAY_SIZE (options); i++)
509 unsigned int len = strlen (options[i].name);
515 for (i = 0, max_len++; i < ARRAY_SIZE (options); i++)
516 fprintf (stream, " %s%*c %s\n",
518 (int)(max_len - strlen (options[i].name)), ' ',
519 _(options[i].description));