123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520 |
- /* Opcode printing code for the WebAssembly target
- Copyright (C) 2017 Free Software Foundation, Inc.
- This file is part of libopcodes.
- This library is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- It is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
- License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
- MA 02110-1301, USA. */
- #include "sysdep.h"
- #include "disassemble.h"
- #include "opintl.h"
- #include "safe-ctype.h"
- #include "floatformat.h"
- #include "libiberty.h"
- #include "elf-bfd.h"
- #include "elf/internal.h"
- #include "elf/wasm32.h"
- #include <stdint.h>
- /* Type names for blocks and signatures. */
- #define BLOCK_TYPE_NONE 0x40
- #define BLOCK_TYPE_I32 0x7f
- #define BLOCK_TYPE_I64 0x7e
- #define BLOCK_TYPE_F32 0x7d
- #define BLOCK_TYPE_F64 0x7c
- enum wasm_class
- {
- wasm_typed,
- wasm_special,
- wasm_break,
- wasm_break_if,
- wasm_break_table,
- wasm_return,
- wasm_call,
- wasm_call_import,
- wasm_call_indirect,
- wasm_get_local,
- wasm_set_local,
- wasm_tee_local,
- wasm_drop,
- wasm_constant_i32,
- wasm_constant_i64,
- wasm_constant_f32,
- wasm_constant_f64,
- wasm_unary,
- wasm_binary,
- wasm_conv,
- wasm_load,
- wasm_store,
- wasm_select,
- wasm_relational,
- wasm_eqz,
- wasm_current_memory,
- wasm_grow_memory,
- wasm_signature
- };
- struct wasm32_private_data
- {
- bfd_boolean print_registers;
- bfd_boolean print_well_known_globals;
- /* Limit valid symbols to those with a given prefix. */
- const char *section_prefix;
- };
- typedef struct
- {
- const char *name;
- const char *description;
- } wasm32_options_t;
- static const wasm32_options_t options[] =
- {
- { "registers", N_("Disassemble \"register\" names") },
- { "globals", N_("Name well-known globals") },
- };
- #define WASM_OPCODE(opcode, name, intype, outtype, clas, signedness) \
- { name, wasm_ ## clas, opcode },
- struct wasm32_opcode_s
- {
- const char *name;
- enum wasm_class clas;
- unsigned char opcode;
- } wasm32_opcodes[] =
- {
- #include "opcode/wasm.h"
- { NULL, 0, 0 }
- };
- /* Parse the disassembler options in OPTS and initialize INFO. */
- static void
- parse_wasm32_disassembler_options (struct disassemble_info *info,
- const char *opts)
- {
- struct wasm32_private_data *private = info->private_data;
- while (opts != NULL)
- {
- if (CONST_STRNEQ (opts, "registers"))
- private->print_registers = TRUE;
- else if (CONST_STRNEQ (opts, "globals"))
- private->print_well_known_globals = TRUE;
- opts = strchr (opts, ',');
- if (opts)
- opts++;
- }
- }
- /* Check whether SYM is valid. Special-case absolute symbols, which
- are unhelpful to print, and arguments to a "call" insn, which we
- want to be in a section matching a given prefix. */
- static bfd_boolean
- wasm32_symbol_is_valid (asymbol *sym,
- struct disassemble_info *info)
- {
- struct wasm32_private_data *private_data = info->private_data;
- if (sym == NULL)
- return FALSE;
- if (strcmp(sym->section->name, "*ABS*") == 0)
- return FALSE;
- if (private_data && private_data->section_prefix != NULL
- && strncmp (sym->section->name, private_data->section_prefix,
- strlen (private_data->section_prefix)))
- return FALSE;
- return TRUE;
- }
- /* Initialize the disassembler structures for INFO. */
- void
- disassemble_init_wasm32 (struct disassemble_info *info)
- {
- if (info->private_data == NULL)
- {
- static struct wasm32_private_data private;
- private.print_registers = FALSE;
- private.print_well_known_globals = FALSE;
- private.section_prefix = NULL;
- info->private_data = &private;
- }
- if (info->disassembler_options)
- {
- parse_wasm32_disassembler_options (info, info->disassembler_options);
- info->disassembler_options = NULL;
- }
- info->symbol_is_valid = wasm32_symbol_is_valid;
- }
- /* Read an LEB128-encoded integer from INFO at address PC, reading one
- byte at a time. Set ERROR_RETURN if no complete integer could be
- read, LENGTH_RETURN to the number oof bytes read (including bytes
- in incomplete numbers). SIGN means interpret the number as
- SLEB128. Unfortunately, this is a duplicate of wasm-module.c's
- wasm_read_leb128 (). */
- static uint64_t
- wasm_read_leb128 (bfd_vma pc,
- struct disassemble_info * info,
- bfd_boolean * error_return,
- unsigned int * length_return,
- bfd_boolean sign)
- {
- uint64_t result = 0;
- unsigned int num_read = 0;
- unsigned int shift = 0;
- unsigned char byte = 0;
- bfd_boolean success = FALSE;
- while (info->read_memory_func (pc + num_read, &byte, 1, info) == 0)
- {
- num_read++;
- result |= ((bfd_vma) (byte & 0x7f)) << shift;
- shift += 7;
- if ((byte & 0x80) == 0)
- {
- success = TRUE;
- break;
- }
- }
- if (length_return != NULL)
- *length_return = num_read;
- if (error_return != NULL)
- *error_return = ! success;
- if (sign && (shift < 8 * sizeof (result)) && (byte & 0x40))
- result |= -((uint64_t) 1 << shift);
- return result;
- }
- /* Read a 32-bit IEEE float from PC using INFO, convert it to a host
- double, and store it at VALUE. */
- static int
- read_f32 (double *value, bfd_vma pc, struct disassemble_info *info)
- {
- bfd_byte buf[4];
- if (info->read_memory_func (pc, buf, sizeof (buf), info))
- return -1;
- floatformat_to_double (&floatformat_ieee_single_little, buf,
- value);
- return sizeof (buf);
- }
- /* Read a 64-bit IEEE float from PC using INFO, convert it to a host
- double, and store it at VALUE. */
- static int
- read_f64 (double *value, bfd_vma pc, struct disassemble_info *info)
- {
- bfd_byte buf[8];
- if (info->read_memory_func (pc, buf, sizeof (buf), info))
- return -1;
- floatformat_to_double (&floatformat_ieee_double_little, buf,
- value);
- return sizeof (buf);
- }
- /* Main disassembly routine. Disassemble insn at PC using INFO. */
- int
- print_insn_wasm32 (bfd_vma pc, struct disassemble_info *info)
- {
- unsigned char opcode;
- struct wasm32_opcode_s *op;
- bfd_byte buffer[16];
- void *stream = info->stream;
- fprintf_ftype prin = info->fprintf_func;
- struct wasm32_private_data *private_data = info->private_data;
- long long constant = 0;
- double fconstant = 0.0;
- long flags = 0;
- long offset = 0;
- long depth = 0;
- long index = 0;
- long target_count = 0;
- long block_type = 0;
- int len = 1;
- int ret = 0;
- unsigned int bytes_read = 0;
- int i;
- const char *locals[] =
- {
- "$dpc", "$sp1", "$r0", "$r1", "$rpc", "$pc0",
- "$rp", "$fp", "$sp",
- "$r2", "$r3", "$r4", "$r5", "$r6", "$r7",
- "$i0", "$i1", "$i2", "$i3", "$i4", "$i5", "$i6", "$i7",
- "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
- };
- int nlocals = ARRAY_SIZE (locals);
- const char *globals[] =
- {
- "$got", "$plt", "$gpo"
- };
- int nglobals = ARRAY_SIZE (globals);
- bfd_boolean error = FALSE;
- if (info->read_memory_func (pc, buffer, 1, info))
- return -1;
- opcode = buffer[0];
- for (op = wasm32_opcodes; op->name; op++)
- if (op->opcode == opcode)
- break;
- if (!op->name)
- {
- prin (stream, "\t.byte 0x%02x\n", buffer[0]);
- return 1;
- }
- else
- {
- len = 1;
- prin (stream, "\t");
- prin (stream, "%s", op->name);
- if (op->clas == wasm_typed)
- {
- block_type = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, FALSE);
- if (error)
- return -1;
- len += bytes_read;
- switch (block_type)
- {
- case BLOCK_TYPE_NONE:
- prin (stream, "[]");
- break;
- case BLOCK_TYPE_I32:
- prin (stream, "[i]");
- break;
- case BLOCK_TYPE_I64:
- prin (stream, "[l]");
- break;
- case BLOCK_TYPE_F32:
- prin (stream, "[f]");
- break;
- case BLOCK_TYPE_F64:
- prin (stream, "[d]");
- break;
- }
- }
- switch (op->clas)
- {
- case wasm_special:
- case wasm_eqz:
- case wasm_binary:
- case wasm_unary:
- case wasm_conv:
- case wasm_relational:
- case wasm_drop:
- case wasm_signature:
- case wasm_call_import:
- case wasm_typed:
- case wasm_select:
- break;
- case wasm_break_table:
- target_count = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, FALSE);
- if (error)
- return -1;
- len += bytes_read;
- prin (stream, " %ld", target_count);
- for (i = 0; i < target_count + 1; i++)
- {
- long target = 0;
- target = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, FALSE);
- if (error)
- return -1;
- len += bytes_read;
- prin (stream, " %ld", target);
- }
- break;
- case wasm_break:
- case wasm_break_if:
- depth = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, FALSE);
- if (error)
- return -1;
- len += bytes_read;
- prin (stream, " %ld", depth);
- break;
- case wasm_return:
- break;
- case wasm_constant_i32:
- case wasm_constant_i64:
- constant = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, TRUE);
- if (error)
- return -1;
- len += bytes_read;
- prin (stream, " %lld", constant);
- break;
- case wasm_constant_f32:
- /* This appears to be the best we can do, even though we're
- using host doubles for WebAssembly floats. */
- ret = read_f32 (&fconstant, pc + len, info);
- if (ret < 0)
- return -1;
- len += ret;
- prin (stream, " %.9g", fconstant);
- break;
- case wasm_constant_f64:
- ret = read_f64 (&fconstant, pc + len, info);
- if (ret < 0)
- return -1;
- len += ret;
- prin (stream, " %.17g", fconstant);
- break;
- case wasm_call:
- index = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, FALSE);
- if (error)
- return -1;
- len += bytes_read;
- prin (stream, " ");
- private_data->section_prefix = ".space.function_index";
- (*info->print_address_func) ((bfd_vma) index, info);
- private_data->section_prefix = NULL;
- break;
- case wasm_call_indirect:
- constant = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, FALSE);
- if (error)
- return -1;
- len += bytes_read;
- prin (stream, " %lld", constant);
- constant = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, FALSE);
- if (error)
- return -1;
- len += bytes_read;
- prin (stream, " %lld", constant);
- break;
- case wasm_get_local:
- case wasm_set_local:
- case wasm_tee_local:
- constant = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, FALSE);
- if (error)
- return -1;
- len += bytes_read;
- prin (stream, " %lld", constant);
- if (strcmp (op->name + 4, "local") == 0)
- {
- if (private_data->print_registers
- && constant >= 0 && constant < nlocals)
- prin (stream, " <%s>", locals[constant]);
- }
- else
- {
- if (private_data->print_well_known_globals
- && constant >= 0 && constant < nglobals)
- prin (stream, " <%s>", globals[constant]);
- }
- break;
- case wasm_grow_memory:
- case wasm_current_memory:
- constant = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, FALSE);
- if (error)
- return -1;
- len += bytes_read;
- prin (stream, " %lld", constant);
- break;
- case wasm_load:
- case wasm_store:
- flags = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, FALSE);
- if (error)
- return -1;
- len += bytes_read;
- offset = wasm_read_leb128
- (pc + len, info, &error, &bytes_read, FALSE);
- if (error)
- return -1;
- len += bytes_read;
- prin (stream, " a=%ld %ld", flags, offset);
- }
- }
- return len;
- }
- /* Print valid disassembler options to STREAM. */
- void
- print_wasm32_disassembler_options (FILE *stream)
- {
- unsigned int i, max_len = 0;
- fprintf (stream, _("\
- The following WebAssembly-specific disassembler options are supported for use\n\
- with the -M switch:\n"));
- for (i = 0; i < ARRAY_SIZE (options); i++)
- {
- unsigned int len = strlen (options[i].name);
- if (max_len < len)
- max_len = len;
- }
- for (i = 0, max_len++; i < ARRAY_SIZE (options); i++)
- fprintf (stream, " %s%*c %s\n",
- options[i].name,
- (int)(max_len - strlen (options[i].name)), ' ',
- _(options[i].description));
- }
|