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