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