/* Opcode printing code for the WebAssembly target
Copyright (C) 2017-2024 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. */
while (opts != NULL)
{
if (startswith (opts, "registers"))
private->print_registers = true;
else if (startswith (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. */
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,
bool *error_return,
unsigned int *length_return,
bool sign)
{
uint64_t result = 0;
unsigned int num_read = 0;
unsigned int shift = 0;
unsigned char byte = 0;
unsigned char lost, mask;
int status = 1;
/* 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;
uint64_t val;
int len;
unsigned int bytes_read;
bool error;
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;
}
len = 1;
prin (stream, "\t");
prin (stream, "%s", op->name);
if (op->clas == wasm_typed)
{
val = wasm_read_leb128 (pc + len, info, &error, &bytes_read, false);
if (error)
return -1;
len += bytes_read;
switch (val)
{
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;
default:
return -1;
}
}
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:
{
uint32_t target_count, i;
val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
false);
target_count = val;
if (error || target_count != val || target_count == (uint32_t) -1)
return -1;
len += bytes_read;
prin (stream, " %u", target_count);
for (i = 0; i < target_count + 1; i++)
{
uint32_t target;
val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
false);
target = val;
if (error || target != val)
return -1;
len += bytes_read;
prin (stream, " %u", target);
}
}
break;
case wasm_break:
case wasm_break_if:
{
uint32_t depth;
val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
false);
depth = val;
if (error || depth != val)
return -1;
len += bytes_read;
prin (stream, " %u", depth);
}
break;
case wasm_return:
break;
case wasm_constant_i32:
case wasm_constant_i64:
val = wasm_read_leb128 (pc + len, info, &error, &bytes_read, true);
if (error)
return -1;
len += bytes_read;
prin (stream, " %" PRId64, val);
break;
case wasm_constant_f32:
{
double fconstant;
int ret;
/* 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:
{
double fconstant;
int ret;
ret = read_f64 (&fconstant, pc + len, info);
if (ret < 0)
return -1;
len += ret;
prin (stream, " %.17g", fconstant);
}
break;
case wasm_call:
{
uint32_t function_index;
val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
false);
function_index = val;
if (error || function_index != val)
return -1;
len += bytes_read;
prin (stream, " ");
private_data->section_prefix = ".space.function_index";
(*info->print_address_func) ((bfd_vma) function_index, info);
private_data->section_prefix = NULL;
}
break;
case wasm_call_indirect:
{
uint32_t type_index, xtra_index;
val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
false);
type_index = val;
if (error || type_index != val)
return -1;
len += bytes_read;
prin (stream, " %u", type_index);
val = wasm_read_leb128 (pc + len, info, &error, &bytes_read,
false);
xtra_index = val;
if (error || xtra_index != val)
return -1;
len += bytes_read;
prin (stream, " %u", xtra_index);
}
break;