wasm32-dis.c revision 1.1.1.3 1 1.1 christos /* Opcode printing code for the WebAssembly target
2 1.1.1.3 christos Copyright (C) 2017-2020 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.2 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.2 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.1.3 christos int status = 1;
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.1.3 christos if (shift < sizeof (result) * 8)
202 1.1.1.3 christos {
203 1.1.1.3 christos result |= ((uint64_t) (byte & 0x7f)) << shift;
204 1.1.1.3 christos if ((result >> shift) != (byte & 0x7f))
205 1.1.1.3 christos /* Overflow. */
206 1.1.1.3 christos status |= 2;
207 1.1.1.3 christos shift += 7;
208 1.1.1.3 christos }
209 1.1.1.3 christos else if ((byte & 0x7f) != 0)
210 1.1.1.3 christos status |= 2;
211 1.1 christos
212 1.1 christos if ((byte & 0x80) == 0)
213 1.1.1.3 christos {
214 1.1.1.3 christos status &= ~1;
215 1.1.1.3 christos if (sign && (shift < 8 * sizeof (result)) && (byte & 0x40))
216 1.1.1.3 christos result |= -((uint64_t) 1 << shift);
217 1.1.1.3 christos break;
218 1.1.1.3 christos }
219 1.1 christos }
220 1.1 christos
221 1.1 christos if (length_return != NULL)
222 1.1 christos *length_return = num_read;
223 1.1 christos if (error_return != NULL)
224 1.1.1.3 christos *error_return = status != 0;
225 1.1 christos
226 1.1 christos return result;
227 1.1 christos }
228 1.1 christos
229 1.1 christos /* Read a 32-bit IEEE float from PC using INFO, convert it to a host
230 1.1 christos double, and store it at VALUE. */
231 1.1 christos
232 1.1 christos static int
233 1.1 christos read_f32 (double *value, bfd_vma pc, struct disassemble_info *info)
234 1.1 christos {
235 1.1 christos bfd_byte buf[4];
236 1.1 christos
237 1.1 christos if (info->read_memory_func (pc, buf, sizeof (buf), info))
238 1.1 christos return -1;
239 1.1 christos
240 1.1 christos floatformat_to_double (&floatformat_ieee_single_little, buf,
241 1.1 christos value);
242 1.1 christos
243 1.1 christos return sizeof (buf);
244 1.1 christos }
245 1.1 christos
246 1.1 christos /* Read a 64-bit IEEE float from PC using INFO, convert it to a host
247 1.1 christos double, and store it at VALUE. */
248 1.1 christos
249 1.1 christos static int
250 1.1 christos read_f64 (double *value, bfd_vma pc, struct disassemble_info *info)
251 1.1 christos {
252 1.1 christos bfd_byte buf[8];
253 1.1 christos
254 1.1 christos if (info->read_memory_func (pc, buf, sizeof (buf), info))
255 1.1 christos return -1;
256 1.1 christos
257 1.1 christos floatformat_to_double (&floatformat_ieee_double_little, buf,
258 1.1 christos value);
259 1.1 christos
260 1.1 christos return sizeof (buf);
261 1.1 christos }
262 1.1 christos
263 1.1 christos /* Main disassembly routine. Disassemble insn at PC using INFO. */
264 1.1 christos
265 1.1 christos int
266 1.1 christos print_insn_wasm32 (bfd_vma pc, struct disassemble_info *info)
267 1.1 christos {
268 1.1 christos unsigned char opcode;
269 1.1 christos struct wasm32_opcode_s *op;
270 1.1 christos bfd_byte buffer[16];
271 1.1 christos void *stream = info->stream;
272 1.1 christos fprintf_ftype prin = info->fprintf_func;
273 1.1 christos struct wasm32_private_data *private_data = info->private_data;
274 1.1.1.3 christos uint64_t val;
275 1.1.1.3 christos int len;
276 1.1.1.3 christos unsigned int bytes_read;
277 1.1.1.3 christos bfd_boolean error;
278 1.1 christos
279 1.1 christos if (info->read_memory_func (pc, buffer, 1, info))
280 1.1 christos return -1;
281 1.1 christos
282 1.1 christos opcode = buffer[0];
283 1.1 christos
284 1.1 christos for (op = wasm32_opcodes; op->name; op++)
285 1.1 christos if (op->opcode == opcode)
286 1.1 christos break;
287 1.1 christos
288 1.1 christos if (!op->name)
289 1.1 christos {
290 1.1 christos prin (stream, "\t.byte 0x%02x\n", buffer[0]);
291 1.1 christos return 1;
292 1.1 christos }
293 1.1.1.3 christos
294 1.1.1.3 christos len = 1;
295 1.1.1.3 christos
296 1.1.1.3 christos prin (stream, "\t");
297 1.1.1.3 christos prin (stream, "%s", op->name);
298 1.1.1.3 christos
299 1.1.1.3 christos if (op->clas == wasm_typed)
300 1.1 christos {
301 1.1.1.3 christos val = wasm_read_leb128 (pc + len, info, &error, &bytes_read, FALSE);
302 1.1.1.3 christos if (error)
303 1.1.1.3 christos return -1;
304 1.1.1.3 christos len += bytes_read;
305 1.1.1.3 christos switch (val)
306 1.1.1.3 christos {
307 1.1.1.3 christos case BLOCK_TYPE_NONE:
308 1.1.1.3 christos prin (stream, "[]");
309 1.1.1.3 christos break;
310 1.1.1.3 christos case BLOCK_TYPE_I32:
311 1.1.1.3 christos prin (stream, "[i]");
312 1.1.1.3 christos break;
313 1.1.1.3 christos case BLOCK_TYPE_I64:
314 1.1.1.3 christos prin (stream, "[l]");
315 1.1.1.3 christos break;
316 1.1.1.3 christos case BLOCK_TYPE_F32:
317 1.1.1.3 christos prin (stream, "[f]");
318 1.1.1.3 christos break;
319 1.1.1.3 christos case BLOCK_TYPE_F64:
320 1.1.1.3 christos prin (stream, "[d]");
321 1.1.1.3 christos break;
322 1.1.1.3 christos default:
323 1.1.1.3 christos return -1;
324 1.1.1.3 christos }
325 1.1.1.3 christos }
326 1.1.1.3 christos
327 1.1.1.3 christos switch (op->clas)
328 1.1.1.3 christos {
329 1.1.1.3 christos case wasm_special:
330 1.1.1.3 christos case wasm_eqz:
331 1.1.1.3 christos case wasm_binary:
332 1.1.1.3 christos case wasm_unary:
333 1.1.1.3 christos case wasm_conv:
334 1.1.1.3 christos case wasm_relational:
335 1.1.1.3 christos case wasm_drop:
336 1.1.1.3 christos case wasm_signature:
337 1.1.1.3 christos case wasm_call_import:
338 1.1.1.3 christos case wasm_typed:
339 1.1.1.3 christos case wasm_select:
340 1.1.1.3 christos break;
341 1.1.1.3 christos
342 1.1.1.3 christos case wasm_break_table:
343 1.1.1.3 christos {
344 1.1.1.3 christos uint32_t target_count, i;
345 1.1.1.3 christos val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
346 1.1.1.3 christos FALSE);
347 1.1.1.3 christos target_count = val;
348 1.1.1.3 christos if (error || target_count != val || target_count == (uint32_t) -1)
349 1.1.1.3 christos return -1;
350 1.1.1.3 christos len += bytes_read;
351 1.1.1.3 christos prin (stream, " %u", target_count);
352 1.1.1.3 christos for (i = 0; i < target_count + 1; i++)
353 1.1.1.3 christos {
354 1.1.1.3 christos uint32_t target;
355 1.1.1.3 christos val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
356 1.1.1.3 christos FALSE);
357 1.1.1.3 christos target = val;
358 1.1.1.3 christos if (error || target != val)
359 1.1.1.3 christos return -1;
360 1.1.1.3 christos len += bytes_read;
361 1.1.1.3 christos prin (stream, " %u", target);
362 1.1.1.3 christos }
363 1.1.1.3 christos }
364 1.1.1.3 christos break;
365 1.1.1.3 christos
366 1.1.1.3 christos case wasm_break:
367 1.1.1.3 christos case wasm_break_if:
368 1.1.1.3 christos {
369 1.1.1.3 christos uint32_t depth;
370 1.1.1.3 christos val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
371 1.1.1.3 christos FALSE);
372 1.1.1.3 christos depth = val;
373 1.1.1.3 christos if (error || depth != val)
374 1.1.1.3 christos return -1;
375 1.1.1.3 christos len += bytes_read;
376 1.1.1.3 christos prin (stream, " %u", depth);
377 1.1.1.3 christos }
378 1.1.1.3 christos break;
379 1.1.1.3 christos
380 1.1.1.3 christos case wasm_return:
381 1.1.1.3 christos break;
382 1.1.1.3 christos
383 1.1.1.3 christos case wasm_constant_i32:
384 1.1.1.3 christos case wasm_constant_i64:
385 1.1.1.3 christos val = wasm_read_leb128 (pc + len, info, &error, &bytes_read, TRUE);
386 1.1.1.3 christos if (error)
387 1.1.1.3 christos return -1;
388 1.1.1.3 christos len += bytes_read;
389 1.1.1.3 christos prin (stream, " %" PRId64, val);
390 1.1.1.3 christos break;
391 1.1.1.3 christos
392 1.1.1.3 christos case wasm_constant_f32:
393 1.1.1.3 christos {
394 1.1.1.3 christos double fconstant;
395 1.1.1.3 christos int ret;
396 1.1.1.3 christos /* This appears to be the best we can do, even though we're
397 1.1.1.3 christos using host doubles for WebAssembly floats. */
398 1.1.1.3 christos ret = read_f32 (&fconstant, pc + len, info);
399 1.1.1.3 christos if (ret < 0)
400 1.1.1.3 christos return -1;
401 1.1.1.3 christos len += ret;
402 1.1.1.3 christos prin (stream, " %.9g", fconstant);
403 1.1.1.3 christos }
404 1.1.1.3 christos break;
405 1.1.1.3 christos
406 1.1.1.3 christos case wasm_constant_f64:
407 1.1.1.3 christos {
408 1.1.1.3 christos double fconstant;
409 1.1.1.3 christos int ret;
410 1.1.1.3 christos ret = read_f64 (&fconstant, pc + len, info);
411 1.1.1.3 christos if (ret < 0)
412 1.1.1.3 christos return -1;
413 1.1.1.3 christos len += ret;
414 1.1.1.3 christos prin (stream, " %.17g", fconstant);
415 1.1.1.3 christos }
416 1.1.1.3 christos break;
417 1.1 christos
418 1.1.1.3 christos case wasm_call:
419 1.1.1.3 christos {
420 1.1.1.3 christos uint32_t function_index;
421 1.1.1.3 christos val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
422 1.1.1.3 christos FALSE);
423 1.1.1.3 christos function_index = val;
424 1.1.1.3 christos if (error || function_index != val)
425 1.1.1.3 christos return -1;
426 1.1.1.3 christos len += bytes_read;
427 1.1.1.3 christos prin (stream, " ");
428 1.1.1.3 christos private_data->section_prefix = ".space.function_index";
429 1.1.1.3 christos (*info->print_address_func) ((bfd_vma) function_index, info);
430 1.1.1.3 christos private_data->section_prefix = NULL;
431 1.1.1.3 christos }
432 1.1.1.3 christos break;
433 1.1 christos
434 1.1.1.3 christos case wasm_call_indirect:
435 1.1.1.3 christos {
436 1.1.1.3 christos uint32_t type_index, xtra_index;
437 1.1.1.3 christos val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
438 1.1.1.3 christos FALSE);
439 1.1.1.3 christos type_index = val;
440 1.1.1.3 christos if (error || type_index != val)
441 1.1.1.3 christos return -1;
442 1.1.1.3 christos len += bytes_read;
443 1.1.1.3 christos prin (stream, " %u", type_index);
444 1.1.1.3 christos val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
445 1.1.1.3 christos FALSE);
446 1.1.1.3 christos xtra_index = val;
447 1.1.1.3 christos if (error || xtra_index != val)
448 1.1.1.3 christos return -1;
449 1.1.1.3 christos len += bytes_read;
450 1.1.1.3 christos prin (stream, " %u", xtra_index);
451 1.1.1.3 christos }
452 1.1.1.3 christos break;
453 1.1.1.3 christos
454 1.1.1.3 christos case wasm_get_local:
455 1.1.1.3 christos case wasm_set_local:
456 1.1.1.3 christos case wasm_tee_local:
457 1.1.1.3 christos {
458 1.1.1.3 christos uint32_t local_index;
459 1.1.1.3 christos val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
460 1.1.1.3 christos FALSE);
461 1.1.1.3 christos local_index = val;
462 1.1.1.3 christos if (error || local_index != val)
463 1.1.1.3 christos return -1;
464 1.1.1.3 christos len += bytes_read;
465 1.1.1.3 christos prin (stream, " %u", local_index);
466 1.1.1.3 christos if (strcmp (op->name + 4, "local") == 0)
467 1.1.1.3 christos {
468 1.1.1.3 christos static const char *locals[] =
469 1.1.1.3 christos {
470 1.1.1.3 christos "$dpc", "$sp1", "$r0", "$r1", "$rpc", "$pc0",
471 1.1.1.3 christos "$rp", "$fp", "$sp",
472 1.1.1.3 christos "$r2", "$r3", "$r4", "$r5", "$r6", "$r7",
473 1.1.1.3 christos "$i0", "$i1", "$i2", "$i3", "$i4", "$i5", "$i6", "$i7",
474 1.1.1.3 christos "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
475 1.1.1.3 christos };
476 1.1.1.3 christos if (private_data->print_registers
477 1.1.1.3 christos && local_index < ARRAY_SIZE (locals))
478 1.1.1.3 christos prin (stream, " <%s>", locals[local_index]);
479 1.1.1.3 christos }
480 1.1.1.3 christos else
481 1.1.1.3 christos {
482 1.1.1.3 christos static const char *globals[] =
483 1.1.1.3 christos {
484 1.1.1.3 christos "$got", "$plt", "$gpo"
485 1.1.1.3 christos };
486 1.1.1.3 christos if (private_data->print_well_known_globals
487 1.1.1.3 christos && local_index < ARRAY_SIZE (globals))
488 1.1.1.3 christos prin (stream, " <%s>", globals[local_index]);
489 1.1.1.3 christos }
490 1.1.1.3 christos }
491 1.1.1.3 christos break;
492 1.1.1.3 christos
493 1.1.1.3 christos case wasm_grow_memory:
494 1.1.1.3 christos case wasm_current_memory:
495 1.1.1.3 christos {
496 1.1.1.3 christos uint32_t reserved_size;
497 1.1.1.3 christos val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
498 1.1.1.3 christos FALSE);
499 1.1.1.3 christos reserved_size = val;
500 1.1.1.3 christos if (error || reserved_size != val)
501 1.1.1.3 christos return -1;
502 1.1.1.3 christos len += bytes_read;
503 1.1.1.3 christos prin (stream, " %u", reserved_size);
504 1.1.1.3 christos }
505 1.1.1.3 christos break;
506 1.1.1.3 christos
507 1.1.1.3 christos case wasm_load:
508 1.1.1.3 christos case wasm_store:
509 1.1.1.3 christos {
510 1.1.1.3 christos uint32_t flags, offset;
511 1.1.1.3 christos val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
512 1.1.1.3 christos FALSE);
513 1.1.1.3 christos flags = val;
514 1.1.1.3 christos if (error || flags != val)
515 1.1.1.3 christos return -1;
516 1.1.1.3 christos len += bytes_read;
517 1.1.1.3 christos val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
518 1.1.1.3 christos FALSE);
519 1.1.1.3 christos offset = val;
520 1.1.1.3 christos if (error || offset != val)
521 1.1.1.3 christos return -1;
522 1.1.1.3 christos len += bytes_read;
523 1.1.1.3 christos prin (stream, " a=%u %u", flags, offset);
524 1.1.1.3 christos }
525 1.1.1.3 christos break;
526 1.1 christos }
527 1.1 christos return len;
528 1.1 christos }
529 1.1 christos
530 1.1 christos /* Print valid disassembler options to STREAM. */
531 1.1 christos
532 1.1 christos void
533 1.1 christos print_wasm32_disassembler_options (FILE *stream)
534 1.1 christos {
535 1.1 christos unsigned int i, max_len = 0;
536 1.1 christos
537 1.1 christos fprintf (stream, _("\
538 1.1 christos The following WebAssembly-specific disassembler options are supported for use\n\
539 1.1 christos with the -M switch:\n"));
540 1.1 christos
541 1.1 christos for (i = 0; i < ARRAY_SIZE (options); i++)
542 1.1 christos {
543 1.1 christos unsigned int len = strlen (options[i].name);
544 1.1 christos
545 1.1 christos if (max_len < len)
546 1.1 christos max_len = len;
547 1.1 christos }
548 1.1 christos
549 1.1 christos for (i = 0, max_len++; i < ARRAY_SIZE (options); i++)
550 1.1 christos fprintf (stream, " %s%*c %s\n",
551 1.1 christos options[i].name,
552 1.1 christos (int)(max_len - strlen (options[i].name)), ' ',
553 1.1 christos _(options[i].description));
554 1.1 christos }
555