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