x86: re-number PREFIX_0X<nn>
[deliverable/binutils-gdb.git] / opcodes / wasm32-dis.c
CommitLineData
62ecb94c 1/* Opcode printing code for the WebAssembly target
250d07de 2 Copyright (C) 2017-2021 Free Software Foundation, Inc.
62ecb94c
PC
3
4 This file is part of libopcodes.
5
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.
10
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.
15
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. */
20
21#include "sysdep.h"
88c1242d 22#include "disassemble.h"
62ecb94c
PC
23#include "opintl.h"
24#include "safe-ctype.h"
25#include "floatformat.h"
62ecb94c
PC
26#include "libiberty.h"
27#include "elf-bfd.h"
28#include "elf/internal.h"
29#include "elf/wasm32.h"
2d5d5a8f 30#include "bfd_stdint.h"
62ecb94c 31
b9b204b3
AM
32#ifdef HAVE_LIMITS_H
33#include <limits.h>
34#endif
35#ifndef CHAR_BIT
36#define CHAR_BIT 8
37#endif
38
62ecb94c
PC
39/* Type names for blocks and signatures. */
40#define BLOCK_TYPE_NONE 0x40
41#define BLOCK_TYPE_I32 0x7f
42#define BLOCK_TYPE_I64 0x7e
43#define BLOCK_TYPE_F32 0x7d
44#define BLOCK_TYPE_F64 0x7c
45
46enum wasm_class
47{
48 wasm_typed,
49 wasm_special,
50 wasm_break,
51 wasm_break_if,
52 wasm_break_table,
53 wasm_return,
54 wasm_call,
55 wasm_call_import,
56 wasm_call_indirect,
57 wasm_get_local,
58 wasm_set_local,
59 wasm_tee_local,
60 wasm_drop,
61 wasm_constant_i32,
62 wasm_constant_i64,
63 wasm_constant_f32,
64 wasm_constant_f64,
65 wasm_unary,
66 wasm_binary,
67 wasm_conv,
68 wasm_load,
69 wasm_store,
70 wasm_select,
71 wasm_relational,
72 wasm_eqz,
73 wasm_current_memory,
74 wasm_grow_memory,
75 wasm_signature
76};
77
78struct wasm32_private_data
79{
80 bfd_boolean print_registers;
81 bfd_boolean print_well_known_globals;
82
83 /* Limit valid symbols to those with a given prefix. */
84 const char *section_prefix;
85};
86
87typedef struct
88{
89 const char *name;
90 const char *description;
91} wasm32_options_t;
92
93static const wasm32_options_t options[] =
94{
95 { "registers", N_("Disassemble \"register\" names") },
96 { "globals", N_("Name well-known globals") },
97};
98
99#define WASM_OPCODE(opcode, name, intype, outtype, clas, signedness) \
100 { name, wasm_ ## clas, opcode },
101
102struct wasm32_opcode_s
103{
104 const char *name;
105 enum wasm_class clas;
106 unsigned char opcode;
107} wasm32_opcodes[] =
108{
109#include "opcode/wasm.h"
110 { NULL, 0, 0 }
111};
112
113/* Parse the disassembler options in OPTS and initialize INFO. */
114
115static void
116parse_wasm32_disassembler_options (struct disassemble_info *info,
117 const char *opts)
118{
119 struct wasm32_private_data *private = info->private_data;
120
121 while (opts != NULL)
122 {
08dedd66 123 if (startswith (opts, "registers"))
62ecb94c 124 private->print_registers = TRUE;
08dedd66 125 else if (startswith (opts, "globals"))
62ecb94c
PC
126 private->print_well_known_globals = TRUE;
127
128 opts = strchr (opts, ',');
129 if (opts)
130 opts++;
131 }
132}
133
134/* Check whether SYM is valid. Special-case absolute symbols, which
135 are unhelpful to print, and arguments to a "call" insn, which we
136 want to be in a section matching a given prefix. */
137
138static bfd_boolean
139wasm32_symbol_is_valid (asymbol *sym,
140 struct disassemble_info *info)
141{
142 struct wasm32_private_data *private_data = info->private_data;
143
144 if (sym == NULL)
145 return FALSE;
146
147 if (strcmp(sym->section->name, "*ABS*") == 0)
148 return FALSE;
149
150 if (private_data && private_data->section_prefix != NULL
151 && strncmp (sym->section->name, private_data->section_prefix,
152 strlen (private_data->section_prefix)))
153 return FALSE;
154
155 return TRUE;
156}
157
158/* Initialize the disassembler structures for INFO. */
159
160void
161disassemble_init_wasm32 (struct disassemble_info *info)
162{
163 if (info->private_data == NULL)
164 {
165 static struct wasm32_private_data private;
166
167 private.print_registers = FALSE;
168 private.print_well_known_globals = FALSE;
169 private.section_prefix = NULL;
170
171 info->private_data = &private;
172 }
173
174 if (info->disassembler_options)
175 {
176 parse_wasm32_disassembler_options (info, info->disassembler_options);
177
178 info->disassembler_options = NULL;
179 }
180
181 info->symbol_is_valid = wasm32_symbol_is_valid;
182}
183
184/* Read an LEB128-encoded integer from INFO at address PC, reading one
185 byte at a time. Set ERROR_RETURN if no complete integer could be
186 read, LENGTH_RETURN to the number oof bytes read (including bytes
187 in incomplete numbers). SIGN means interpret the number as
188 SLEB128. Unfortunately, this is a duplicate of wasm-module.c's
189 wasm_read_leb128 (). */
190
191static uint64_t
192wasm_read_leb128 (bfd_vma pc,
193 struct disassemble_info * info,
194 bfd_boolean * error_return,
195 unsigned int * length_return,
196 bfd_boolean sign)
197{
198 uint64_t result = 0;
199 unsigned int num_read = 0;
200 unsigned int shift = 0;
201 unsigned char byte = 0;
b9b204b3 202 unsigned char lost, mask;
27c1c427 203 int status = 1;
62ecb94c
PC
204
205 while (info->read_memory_func (pc + num_read, &byte, 1, info) == 0)
206 {
207 num_read++;
208
b9b204b3 209 if (shift < CHAR_BIT * sizeof (result))
27c1c427
AM
210 {
211 result |= ((uint64_t) (byte & 0x7f)) << shift;
b9b204b3
AM
212 /* These bits overflowed. */
213 lost = byte ^ (result >> shift);
214 /* And this is the mask of possible overflow bits. */
215 mask = 0x7f ^ ((uint64_t) 0x7f << shift >> shift);
27c1c427
AM
216 shift += 7;
217 }
b9b204b3
AM
218 else
219 {
220 lost = byte;
221 mask = 0x7f;
222 }
223 if ((lost & mask) != (sign && (int64_t) result < 0 ? mask : 0))
27c1c427 224 status |= 2;
62ecb94c 225
62ecb94c 226 if ((byte & 0x80) == 0)
27c1c427
AM
227 {
228 status &= ~1;
b9b204b3 229 if (sign && shift < CHAR_BIT * sizeof (result) && (byte & 0x40))
27c1c427
AM
230 result |= -((uint64_t) 1 << shift);
231 break;
232 }
62ecb94c
PC
233 }
234
235 if (length_return != NULL)
236 *length_return = num_read;
237 if (error_return != NULL)
27c1c427 238 *error_return = status != 0;
62ecb94c
PC
239
240 return result;
241}
242
243/* Read a 32-bit IEEE float from PC using INFO, convert it to a host
244 double, and store it at VALUE. */
245
246static int
247read_f32 (double *value, bfd_vma pc, struct disassemble_info *info)
248{
249 bfd_byte buf[4];
250
251 if (info->read_memory_func (pc, buf, sizeof (buf), info))
252 return -1;
253
254 floatformat_to_double (&floatformat_ieee_single_little, buf,
255 value);
256
257 return sizeof (buf);
258}
259
260/* Read a 64-bit IEEE float from PC using INFO, convert it to a host
261 double, and store it at VALUE. */
262
263static int
264read_f64 (double *value, bfd_vma pc, struct disassemble_info *info)
265{
266 bfd_byte buf[8];
267
268 if (info->read_memory_func (pc, buf, sizeof (buf), info))
269 return -1;
270
271 floatformat_to_double (&floatformat_ieee_double_little, buf,
272 value);
273
274 return sizeof (buf);
275}
276
277/* Main disassembly routine. Disassemble insn at PC using INFO. */
278
279int
280print_insn_wasm32 (bfd_vma pc, struct disassemble_info *info)
281{
282 unsigned char opcode;
283 struct wasm32_opcode_s *op;
284 bfd_byte buffer[16];
285 void *stream = info->stream;
286 fprintf_ftype prin = info->fprintf_func;
287 struct wasm32_private_data *private_data = info->private_data;
febda64f
AM
288 uint64_t val;
289 int len;
290 unsigned int bytes_read;
291 bfd_boolean error;
62ecb94c
PC
292
293 if (info->read_memory_func (pc, buffer, 1, info))
294 return -1;
295
296 opcode = buffer[0];
297
298 for (op = wasm32_opcodes; op->name; op++)
299 if (op->opcode == opcode)
300 break;
301
302 if (!op->name)
303 {
304 prin (stream, "\t.byte 0x%02x\n", buffer[0]);
305 return 1;
306 }
febda64f
AM
307
308 len = 1;
309
310 prin (stream, "\t");
311 prin (stream, "%s", op->name);
312
313 if (op->clas == wasm_typed)
62ecb94c 314 {
febda64f
AM
315 val = wasm_read_leb128 (pc + len, info, &error, &bytes_read, FALSE);
316 if (error)
317 return -1;
318 len += bytes_read;
319 switch (val)
320 {
321 case BLOCK_TYPE_NONE:
322 prin (stream, "[]");
323 break;
324 case BLOCK_TYPE_I32:
325 prin (stream, "[i]");
326 break;
327 case BLOCK_TYPE_I64:
328 prin (stream, "[l]");
329 break;
330 case BLOCK_TYPE_F32:
331 prin (stream, "[f]");
332 break;
333 case BLOCK_TYPE_F64:
334 prin (stream, "[d]");
335 break;
336 default:
337 return -1;
338 }
339 }
340
341 switch (op->clas)
342 {
343 case wasm_special:
344 case wasm_eqz:
345 case wasm_binary:
346 case wasm_unary:
347 case wasm_conv:
348 case wasm_relational:
349 case wasm_drop:
350 case wasm_signature:
351 case wasm_call_import:
352 case wasm_typed:
353 case wasm_select:
354 break;
355
356 case wasm_break_table:
357 {
358 uint32_t target_count, i;
359 val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
360 FALSE);
361 target_count = val;
362 if (error || target_count != val || target_count == (uint32_t) -1)
363 return -1;
364 len += bytes_read;
365 prin (stream, " %u", target_count);
366 for (i = 0; i < target_count + 1; i++)
367 {
368 uint32_t target;
369 val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
370 FALSE);
371 target = val;
372 if (error || target != val)
373 return -1;
374 len += bytes_read;
375 prin (stream, " %u", target);
376 }
377 }
378 break;
379
380 case wasm_break:
381 case wasm_break_if:
382 {
383 uint32_t depth;
384 val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
385 FALSE);
386 depth = val;
387 if (error || depth != val)
388 return -1;
389 len += bytes_read;
390 prin (stream, " %u", depth);
391 }
392 break;
393
394 case wasm_return:
395 break;
396
397 case wasm_constant_i32:
398 case wasm_constant_i64:
399 val = wasm_read_leb128 (pc + len, info, &error, &bytes_read, TRUE);
400 if (error)
401 return -1;
402 len += bytes_read;
403 prin (stream, " %" PRId64, val);
404 break;
405
406 case wasm_constant_f32:
407 {
408 double fconstant;
409 int ret;
410 /* This appears to be the best we can do, even though we're
411 using host doubles for WebAssembly floats. */
412 ret = read_f32 (&fconstant, pc + len, info);
413 if (ret < 0)
414 return -1;
415 len += ret;
416 prin (stream, " %.9g", fconstant);
417 }
418 break;
419
420 case wasm_constant_f64:
421 {
422 double fconstant;
423 int ret;
424 ret = read_f64 (&fconstant, pc + len, info);
425 if (ret < 0)
426 return -1;
427 len += ret;
428 prin (stream, " %.17g", fconstant);
429 }
430 break;
431
432 case wasm_call:
433 {
434 uint32_t function_index;
435 val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
436 FALSE);
437 function_index = val;
438 if (error || function_index != val)
439 return -1;
440 len += bytes_read;
441 prin (stream, " ");
442 private_data->section_prefix = ".space.function_index";
443 (*info->print_address_func) ((bfd_vma) function_index, info);
444 private_data->section_prefix = NULL;
445 }
446 break;
447
448 case wasm_call_indirect:
449 {
450 uint32_t type_index, xtra_index;
451 val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
452 FALSE);
453 type_index = val;
454 if (error || type_index != val)
455 return -1;
456 len += bytes_read;
457 prin (stream, " %u", type_index);
458 val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
459 FALSE);
460 xtra_index = val;
461 if (error || xtra_index != val)
462 return -1;
463 len += bytes_read;
464 prin (stream, " %u", xtra_index);
465 }
466 break;
467
468 case wasm_get_local:
469 case wasm_set_local:
470 case wasm_tee_local:
471 {
472 uint32_t local_index;
473 val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
474 FALSE);
475 local_index = val;
476 if (error || local_index != val)
477 return -1;
478 len += bytes_read;
479 prin (stream, " %u", local_index);
480 if (strcmp (op->name + 4, "local") == 0)
481 {
482 static const char *locals[] =
483 {
484 "$dpc", "$sp1", "$r0", "$r1", "$rpc", "$pc0",
485 "$rp", "$fp", "$sp",
486 "$r2", "$r3", "$r4", "$r5", "$r6", "$r7",
487 "$i0", "$i1", "$i2", "$i3", "$i4", "$i5", "$i6", "$i7",
488 "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
489 };
490 if (private_data->print_registers
491 && local_index < ARRAY_SIZE (locals))
492 prin (stream, " <%s>", locals[local_index]);
493 }
494 else
495 {
496 static const char *globals[] =
497 {
498 "$got", "$plt", "$gpo"
499 };
500 if (private_data->print_well_known_globals
501 && local_index < ARRAY_SIZE (globals))
502 prin (stream, " <%s>", globals[local_index]);
503 }
504 }
505 break;
506
507 case wasm_grow_memory:
508 case wasm_current_memory:
509 {
510 uint32_t reserved_size;
511 val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
512 FALSE);
513 reserved_size = val;
514 if (error || reserved_size != val)
515 return -1;
516 len += bytes_read;
517 prin (stream, " %u", reserved_size);
518 }
519 break;
520
521 case wasm_load:
522 case wasm_store:
523 {
524 uint32_t flags, offset;
525 val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
526 FALSE);
527 flags = val;
528 if (error || flags != val)
529 return -1;
530 len += bytes_read;
531 val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
532 FALSE);
533 offset = val;
534 if (error || offset != val)
535 return -1;
536 len += bytes_read;
537 prin (stream, " a=%u %u", flags, offset);
538 }
539 break;
62ecb94c
PC
540 }
541 return len;
542}
543
544/* Print valid disassembler options to STREAM. */
545
546void
547print_wasm32_disassembler_options (FILE *stream)
548{
549 unsigned int i, max_len = 0;
550
551 fprintf (stream, _("\
552The following WebAssembly-specific disassembler options are supported for use\n\
553with the -M switch:\n"));
554
555 for (i = 0; i < ARRAY_SIZE (options); i++)
556 {
557 unsigned int len = strlen (options[i].name);
558
559 if (max_len < len)
560 max_len = len;
561 }
562
563 for (i = 0, max_len++; i < ARRAY_SIZE (options); i++)
564 fprintf (stream, " %s%*c %s\n",
565 options[i].name,
566 (int)(max_len - strlen (options[i].name)), ' ',
567 _(options[i].description));
568}
This page took 0.251467 seconds and 4 git commands to generate.