x86: consistently convert to byte registers for TEST w/ imm optimization
[deliverable/binutils-gdb.git] / opcodes / wasm32-dis.c
CommitLineData
62ecb94c 1/* Opcode printing code for the WebAssembly target
b3adc24a 2 Copyright (C) 2017-2020 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
PC
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
39enum 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
71struct 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
80typedef struct
81{
82 const char *name;
83 const char *description;
84} wasm32_options_t;
85
86static 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
95struct 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
108static void
109parse_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
131static bfd_boolean
132wasm32_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
153void
154disassemble_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
184static uint64_t
185wasm_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;
27c1c427 195 int status = 1;
62ecb94c
PC
196
197 while (info->read_memory_func (pc + num_read, &byte, 1, info) == 0)
198 {
199 num_read++;
200
27c1c427
AM
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;
62ecb94c 211
62ecb94c 212 if ((byte & 0x80) == 0)
27c1c427
AM
213 {
214 status &= ~1;
215 if (sign && (shift < 8 * sizeof (result)) && (byte & 0x40))
216 result |= -((uint64_t) 1 << shift);
217 break;
218 }
62ecb94c
PC
219 }
220
221 if (length_return != NULL)
222 *length_return = num_read;
223 if (error_return != NULL)
27c1c427 224 *error_return = status != 0;
62ecb94c
PC
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
232static int
233read_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
249static int
250read_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
265int
266print_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 long long constant = 0;
275 double fconstant = 0.0;
276 long flags = 0;
277 long offset = 0;
278 long depth = 0;
87993319 279 long function_index = 0;
62ecb94c
PC
280 long target_count = 0;
281 long block_type = 0;
282 int len = 1;
283 int ret = 0;
284 unsigned int bytes_read = 0;
285 int i;
286 const char *locals[] =
287 {
288 "$dpc", "$sp1", "$r0", "$r1", "$rpc", "$pc0",
289 "$rp", "$fp", "$sp",
290 "$r2", "$r3", "$r4", "$r5", "$r6", "$r7",
291 "$i0", "$i1", "$i2", "$i3", "$i4", "$i5", "$i6", "$i7",
292 "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
293 };
294 int nlocals = ARRAY_SIZE (locals);
295 const char *globals[] =
296 {
297 "$got", "$plt", "$gpo"
298 };
299 int nglobals = ARRAY_SIZE (globals);
300 bfd_boolean error = FALSE;
301
302 if (info->read_memory_func (pc, buffer, 1, info))
303 return -1;
304
305 opcode = buffer[0];
306
307 for (op = wasm32_opcodes; op->name; op++)
308 if (op->opcode == opcode)
309 break;
310
311 if (!op->name)
312 {
313 prin (stream, "\t.byte 0x%02x\n", buffer[0]);
314 return 1;
315 }
316 else
317 {
318 len = 1;
319
320 prin (stream, "\t");
321 prin (stream, "%s", op->name);
322
323 if (op->clas == wasm_typed)
324 {
325 block_type = wasm_read_leb128
326 (pc + len, info, &error, &bytes_read, FALSE);
327 if (error)
328 return -1;
329 len += bytes_read;
330 switch (block_type)
331 {
332 case BLOCK_TYPE_NONE:
333 prin (stream, "[]");
334 break;
335 case BLOCK_TYPE_I32:
336 prin (stream, "[i]");
337 break;
338 case BLOCK_TYPE_I64:
339 prin (stream, "[l]");
340 break;
341 case BLOCK_TYPE_F32:
342 prin (stream, "[f]");
343 break;
344 case BLOCK_TYPE_F64:
345 prin (stream, "[d]");
346 break;
347 }
348 }
349
350 switch (op->clas)
351 {
352 case wasm_special:
353 case wasm_eqz:
354 case wasm_binary:
355 case wasm_unary:
356 case wasm_conv:
357 case wasm_relational:
358 case wasm_drop:
359 case wasm_signature:
360 case wasm_call_import:
361 case wasm_typed:
362 case wasm_select:
363 break;
364
365 case wasm_break_table:
366 target_count = wasm_read_leb128
367 (pc + len, info, &error, &bytes_read, FALSE);
368 if (error)
369 return -1;
370 len += bytes_read;
371 prin (stream, " %ld", target_count);
372 for (i = 0; i < target_count + 1; i++)
373 {
374 long target = 0;
375 target = wasm_read_leb128
376 (pc + len, info, &error, &bytes_read, FALSE);
377 if (error)
378 return -1;
379 len += bytes_read;
380 prin (stream, " %ld", target);
381 }
382 break;
383
384 case wasm_break:
385 case wasm_break_if:
386 depth = wasm_read_leb128
387 (pc + len, info, &error, &bytes_read, FALSE);
388 if (error)
389 return -1;
390 len += bytes_read;
391 prin (stream, " %ld", depth);
392 break;
393
394 case wasm_return:
395 break;
396
397 case wasm_constant_i32:
398 case wasm_constant_i64:
399 constant = wasm_read_leb128
400 (pc + len, info, &error, &bytes_read, TRUE);
401 if (error)
402 return -1;
403 len += bytes_read;
404 prin (stream, " %lld", constant);
405 break;
406
407 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);
411 if (ret < 0)
412 return -1;
413 len += ret;
aa808707 414 prin (stream, " %.9g", fconstant);
62ecb94c
PC
415 break;
416
417 case wasm_constant_f64:
418 ret = read_f64 (&fconstant, pc + len, info);
419 if (ret < 0)
420 return -1;
421 len += ret;
aa808707 422 prin (stream, " %.17g", fconstant);
62ecb94c
PC
423 break;
424
425 case wasm_call:
87993319 426 function_index = wasm_read_leb128
62ecb94c
PC
427 (pc + len, info, &error, &bytes_read, FALSE);
428 if (error)
429 return -1;
430 len += bytes_read;
431 prin (stream, " ");
432 private_data->section_prefix = ".space.function_index";
87993319 433 (*info->print_address_func) ((bfd_vma) function_index, info);
62ecb94c
PC
434 private_data->section_prefix = NULL;
435 break;
436
437 case wasm_call_indirect:
438 constant = wasm_read_leb128
439 (pc + len, info, &error, &bytes_read, FALSE);
440 if (error)
441 return -1;
442 len += bytes_read;
443 prin (stream, " %lld", constant);
444 constant = wasm_read_leb128
445 (pc + len, info, &error, &bytes_read, FALSE);
446 if (error)
447 return -1;
448 len += bytes_read;
449 prin (stream, " %lld", constant);
450 break;
451
452 case wasm_get_local:
453 case wasm_set_local:
454 case wasm_tee_local:
455 constant = wasm_read_leb128
456 (pc + len, info, &error, &bytes_read, FALSE);
457 if (error)
458 return -1;
459 len += bytes_read;
460 prin (stream, " %lld", constant);
461 if (strcmp (op->name + 4, "local") == 0)
462 {
463 if (private_data->print_registers
464 && constant >= 0 && constant < nlocals)
465 prin (stream, " <%s>", locals[constant]);
466 }
467 else
468 {
469 if (private_data->print_well_known_globals
470 && constant >= 0 && constant < nglobals)
471 prin (stream, " <%s>", globals[constant]);
472 }
473 break;
474
475 case wasm_grow_memory:
476 case wasm_current_memory:
477 constant = wasm_read_leb128
478 (pc + len, info, &error, &bytes_read, FALSE);
479 if (error)
480 return -1;
481 len += bytes_read;
482 prin (stream, " %lld", constant);
483 break;
484
485 case wasm_load:
486 case wasm_store:
487 flags = wasm_read_leb128
488 (pc + len, info, &error, &bytes_read, FALSE);
489 if (error)
490 return -1;
491 len += bytes_read;
492 offset = wasm_read_leb128
493 (pc + len, info, &error, &bytes_read, FALSE);
494 if (error)
495 return -1;
496 len += bytes_read;
497 prin (stream, " a=%ld %ld", flags, offset);
498 }
499 }
500 return len;
501}
502
503/* Print valid disassembler options to STREAM. */
504
505void
506print_wasm32_disassembler_options (FILE *stream)
507{
508 unsigned int i, max_len = 0;
509
510 fprintf (stream, _("\
511The following WebAssembly-specific disassembler options are supported for use\n\
512with the -M switch:\n"));
513
514 for (i = 0; i < ARRAY_SIZE (options); i++)
515 {
516 unsigned int len = strlen (options[i].name);
517
518 if (max_len < len)
519 max_len = len;
520 }
521
522 for (i = 0, max_len++; i < ARRAY_SIZE (options); i++)
523 fprintf (stream, " %s%*c %s\n",
524 options[i].name,
525 (int)(max_len - strlen (options[i].name)), ' ',
526 _(options[i].description));
527}
This page took 0.174793 seconds and 4 git commands to generate.