Add missing changelogs for previous commits.
[binutils-gdb.git] / opcodes / wasm32-dis.c
blobb1042793a5e42538d551ae07cd6c496f94f4ea58
1 /* Opcode printing code for the WebAssembly target
2 Copyright (C) 2017-2019 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. */
21 #include "sysdep.h"
22 #include "disassemble.h"
23 #include "opintl.h"
24 #include "safe-ctype.h"
25 #include "floatformat.h"
26 #include "libiberty.h"
27 #include "elf-bfd.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
39 enum wasm_class
41 wasm_typed,
42 wasm_special,
43 wasm_break,
44 wasm_break_if,
45 wasm_break_table,
46 wasm_return,
47 wasm_call,
48 wasm_call_import,
49 wasm_call_indirect,
50 wasm_get_local,
51 wasm_set_local,
52 wasm_tee_local,
53 wasm_drop,
54 wasm_constant_i32,
55 wasm_constant_i64,
56 wasm_constant_f32,
57 wasm_constant_f64,
58 wasm_unary,
59 wasm_binary,
60 wasm_conv,
61 wasm_load,
62 wasm_store,
63 wasm_select,
64 wasm_relational,
65 wasm_eqz,
66 wasm_current_memory,
67 wasm_grow_memory,
68 wasm_signature
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;
80 typedef struct
82 const char *name;
83 const char *description;
84 } wasm32_options_t;
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
97 const char *name;
98 enum wasm_class clas;
99 unsigned char opcode;
100 } wasm32_opcodes[] =
102 #include "opcode/wasm.h"
103 { NULL, 0, 0 }
106 /* Parse the disassembler options in OPTS and initialize INFO. */
108 static void
109 parse_wasm32_disassembler_options (struct disassemble_info *info,
110 const char *opts)
112 struct wasm32_private_data *private = info->private_data;
114 while (opts != NULL)
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, ',');
122 if (opts)
123 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. */
131 static bfd_boolean
132 wasm32_symbol_is_valid (asymbol *sym,
133 struct disassemble_info *info)
135 struct wasm32_private_data *private_data = info->private_data;
137 if (sym == NULL)
138 return FALSE;
140 if (strcmp(sym->section->name, "*ABS*") == 0)
141 return FALSE;
143 if (private_data && private_data->section_prefix != NULL
144 && strncmp (sym->section->name, private_data->section_prefix,
145 strlen (private_data->section_prefix)))
146 return FALSE;
148 return TRUE;
151 /* Initialize the disassembler structures for INFO. */
153 void
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 (). */
184 static uint64_t
185 wasm_read_leb128 (bfd_vma pc,
186 struct disassemble_info * info,
187 bfd_boolean * error_return,
188 unsigned int * length_return,
189 bfd_boolean sign)
191 uint64_t result = 0;
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)
199 num_read++;
201 result |= ((bfd_vma) (byte & 0x7f)) << shift;
203 shift += 7;
204 if ((byte & 0x80) == 0)
206 success = TRUE;
207 break;
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);
219 return result;
222 /* Read a 32-bit IEEE float from PC using INFO, convert it to a host
223 double, and store it at VALUE. */
225 static int
226 read_f32 (double *value, bfd_vma pc, struct disassemble_info *info)
228 bfd_byte buf[4];
230 if (info->read_memory_func (pc, buf, sizeof (buf), info))
231 return -1;
233 floatformat_to_double (&floatformat_ieee_single_little, buf,
234 value);
236 return sizeof (buf);
239 /* Read a 64-bit IEEE float from PC using INFO, convert it to a host
240 double, and store it at VALUE. */
242 static int
243 read_f64 (double *value, bfd_vma pc, struct disassemble_info *info)
245 bfd_byte buf[8];
247 if (info->read_memory_func (pc, buf, sizeof (buf), info))
248 return -1;
250 floatformat_to_double (&floatformat_ieee_double_little, buf,
251 value);
253 return sizeof (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;
263 bfd_byte buffer[16];
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;
269 long flags = 0;
270 long offset = 0;
271 long depth = 0;
272 long function_index = 0;
273 long target_count = 0;
274 long block_type = 0;
275 int len = 1;
276 int ret = 0;
277 unsigned int bytes_read = 0;
278 int i;
279 const char *locals[] =
281 "$dpc", "$sp1", "$r0", "$r1", "$rpc", "$pc0",
282 "$rp", "$fp", "$sp",
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))
296 return -1;
298 opcode = buffer[0];
300 for (op = wasm32_opcodes; op->name; op++)
301 if (op->opcode == opcode)
302 break;
304 if (!op->name)
306 prin (stream, "\t.byte 0x%02x\n", buffer[0]);
307 return 1;
309 else
311 len = 1;
313 prin (stream, "\t");
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);
320 if (error)
321 return -1;
322 len += bytes_read;
323 switch (block_type)
325 case BLOCK_TYPE_NONE:
326 prin (stream, "[]");
327 break;
328 case BLOCK_TYPE_I32:
329 prin (stream, "[i]");
330 break;
331 case BLOCK_TYPE_I64:
332 prin (stream, "[l]");
333 break;
334 case BLOCK_TYPE_F32:
335 prin (stream, "[f]");
336 break;
337 case BLOCK_TYPE_F64:
338 prin (stream, "[d]");
339 break;
343 switch (op->clas)
345 case wasm_special:
346 case wasm_eqz:
347 case wasm_binary:
348 case wasm_unary:
349 case wasm_conv:
350 case wasm_relational:
351 case wasm_drop:
352 case wasm_signature:
353 case wasm_call_import:
354 case wasm_typed:
355 case wasm_select:
356 break;
358 case wasm_break_table:
359 target_count = wasm_read_leb128
360 (pc + len, info, &error, &bytes_read, FALSE);
361 if (error)
362 return -1;
363 len += bytes_read;
364 prin (stream, " %ld", target_count);
365 for (i = 0; i < target_count + 1; i++)
367 long target = 0;
368 target = wasm_read_leb128
369 (pc + len, info, &error, &bytes_read, FALSE);
370 if (error)
371 return -1;
372 len += bytes_read;
373 prin (stream, " %ld", target);
375 break;
377 case wasm_break:
378 case wasm_break_if:
379 depth = wasm_read_leb128
380 (pc + len, info, &error, &bytes_read, FALSE);
381 if (error)
382 return -1;
383 len += bytes_read;
384 prin (stream, " %ld", depth);
385 break;
387 case wasm_return:
388 break;
390 case wasm_constant_i32:
391 case wasm_constant_i64:
392 constant = wasm_read_leb128
393 (pc + len, info, &error, &bytes_read, TRUE);
394 if (error)
395 return -1;
396 len += bytes_read;
397 prin (stream, " %lld", constant);
398 break;
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);
404 if (ret < 0)
405 return -1;
406 len += ret;
407 prin (stream, " %.9g", fconstant);
408 break;
410 case wasm_constant_f64:
411 ret = read_f64 (&fconstant, pc + len, info);
412 if (ret < 0)
413 return -1;
414 len += ret;
415 prin (stream, " %.17g", fconstant);
416 break;
418 case wasm_call:
419 function_index = wasm_read_leb128
420 (pc + len, info, &error, &bytes_read, FALSE);
421 if (error)
422 return -1;
423 len += bytes_read;
424 prin (stream, " ");
425 private_data->section_prefix = ".space.function_index";
426 (*info->print_address_func) ((bfd_vma) function_index, info);
427 private_data->section_prefix = NULL;
428 break;
430 case wasm_call_indirect:
431 constant = wasm_read_leb128
432 (pc + len, info, &error, &bytes_read, FALSE);
433 if (error)
434 return -1;
435 len += bytes_read;
436 prin (stream, " %lld", constant);
437 constant = wasm_read_leb128
438 (pc + len, info, &error, &bytes_read, FALSE);
439 if (error)
440 return -1;
441 len += bytes_read;
442 prin (stream, " %lld", constant);
443 break;
445 case wasm_get_local:
446 case wasm_set_local:
447 case wasm_tee_local:
448 constant = wasm_read_leb128
449 (pc + len, info, &error, &bytes_read, FALSE);
450 if (error)
451 return -1;
452 len += bytes_read;
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]);
460 else
462 if (private_data->print_well_known_globals
463 && constant >= 0 && constant < nglobals)
464 prin (stream, " <%s>", globals[constant]);
466 break;
468 case wasm_grow_memory:
469 case wasm_current_memory:
470 constant = wasm_read_leb128
471 (pc + len, info, &error, &bytes_read, FALSE);
472 if (error)
473 return -1;
474 len += bytes_read;
475 prin (stream, " %lld", constant);
476 break;
478 case wasm_load:
479 case wasm_store:
480 flags = wasm_read_leb128
481 (pc + len, info, &error, &bytes_read, FALSE);
482 if (error)
483 return -1;
484 len += bytes_read;
485 offset = wasm_read_leb128
486 (pc + len, info, &error, &bytes_read, FALSE);
487 if (error)
488 return -1;
489 len += bytes_read;
490 prin (stream, " a=%ld %ld", flags, offset);
493 return len;
496 /* Print valid disassembler options to STREAM. */
498 void
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);
511 if (max_len < len)
512 max_len = len;
515 for (i = 0, max_len++; i < ARRAY_SIZE (options); i++)
516 fprintf (stream, " %s%*c %s\n",
517 options[i].name,
518 (int)(max_len - strlen (options[i].name)), ' ',
519 _(options[i].description));