Home | History | Annotate | Line # | Download | only in opcodes
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