Source code

Revision control

Copy as Markdown

Other Tools

/*
* Copyright 2016 WebAssembly Community Group participants
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "wabt/binary-reader.h"
#include <cassert>
#include <cinttypes>
#include <cstdarg>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <vector>
#include "wabt/config.h"
#include "wabt/binary-reader-logging.h"
#include "wabt/binary.h"
#include "wabt/leb128.h"
#include "wabt/stream.h"
#include "wabt/utf8.h"
#if HAVE_ALLOCA
#include <alloca.h>
#endif
#define ERROR_IF(expr, ...) \
do { \
if (expr) { \
PrintError(__VA_ARGS__); \
return Result::Error; \
} \
} while (0)
#define ERROR_UNLESS(expr, ...) ERROR_IF(!(expr), __VA_ARGS__)
#define ERROR_UNLESS_OPCODE_ENABLED(opcode) \
do { \
if (!opcode.IsEnabled(options_.features)) { \
return ReportUnexpectedOpcode(opcode); \
} \
} while (0)
#define CALLBACK0(member) \
ERROR_UNLESS(Succeeded(delegate_->member()), #member " callback failed")
#define CALLBACK(member, ...) \
ERROR_UNLESS(Succeeded(delegate_->member(__VA_ARGS__)), \
#member " callback failed")
namespace wabt {
namespace {
class BinaryReader {
public:
struct ReadModuleOptions {
bool stop_on_first_error;
};
BinaryReader(const void* data,
size_t size,
BinaryReaderDelegate* delegate,
const ReadBinaryOptions& options);
Result ReadModule(const ReadModuleOptions& options);
private:
template <typename T, T BinaryReader::*member>
struct ValueRestoreGuard {
explicit ValueRestoreGuard(BinaryReader* this_)
: this_(this_), previous_value_(this_->*member) {}
~ValueRestoreGuard() { this_->*member = previous_value_; }
BinaryReader* this_;
T previous_value_;
};
struct ReadSectionsOptions {
bool stop_on_first_error;
};
void WABT_PRINTF_FORMAT(2, 3) PrintError(const char* format, ...);
[[nodiscard]] Result ReadOpcode(Opcode* out_value, const char* desc);
template <typename T>
[[nodiscard]] Result ReadT(T* out_value,
const char* type_name,
const char* desc);
[[nodiscard]] Result ReadU8(uint8_t* out_value, const char* desc);
[[nodiscard]] Result ReadU32(uint32_t* out_value, const char* desc);
[[nodiscard]] Result ReadF32(uint32_t* out_value, const char* desc);
[[nodiscard]] Result ReadF64(uint64_t* out_value, const char* desc);
[[nodiscard]] Result ReadV128(v128* out_value, const char* desc);
[[nodiscard]] Result ReadU32Leb128(uint32_t* out_value, const char* desc);
[[nodiscard]] Result ReadU64Leb128(uint64_t* out_value, const char* desc);
[[nodiscard]] Result ReadS32Leb128(uint32_t* out_value, const char* desc);
[[nodiscard]] Result ReadS64Leb128(uint64_t* out_value, const char* desc);
[[nodiscard]] Result ReadType(Type* out_value, const char* desc);
[[nodiscard]] Result ReadRefType(Type* out_value, const char* desc);
[[nodiscard]] Result ReadExternalKind(ExternalKind* out_value,
const char* desc);
[[nodiscard]] Result ReadStr(std::string_view* out_str, const char* desc);
[[nodiscard]] Result ReadBytes(const void** out_data,
Address* out_data_size,
const char* desc);
[[nodiscard]] Result ReadIndex(Index* index, const char* desc);
[[nodiscard]] Result ReadOffset(Offset* offset, const char* desc);
[[nodiscard]] Result ReadAlignment(Address* align_log2, const char* desc);
[[nodiscard]] Result ReadMemidx(Index* memidx, const char* desc);
[[nodiscard]] Result ReadMemLocation(Address* alignment_log2,
Index* memidx,
Address* offset,
const char* desc_align,
const char* desc_memidx,
const char* desc_offset,
uint8_t* lane_val = nullptr);
[[nodiscard]] Result CallbackMemLocation(const Address* alignment_log2,
const Index* memidx,
const Address* offset,
const uint8_t* lane_val = nullptr);
[[nodiscard]] Result ReadCount(Index* index, const char* desc);
[[nodiscard]] Result ReadField(TypeMut* out_value);
bool IsConcreteType(Type);
bool IsBlockType(Type);
Index NumTotalFuncs();
[[nodiscard]] Result ReadInitExpr(Index index);
[[nodiscard]] Result ReadTable(Type* out_elem_type, Limits* out_elem_limits);
[[nodiscard]] Result ReadMemory(Limits* out_page_limits);
[[nodiscard]] Result ReadGlobalHeader(Type* out_type, bool* out_mutable);
[[nodiscard]] Result ReadTagType(Index* out_sig_index);
[[nodiscard]] Result ReadAddress(Address* out_value,
Index memory,
const char* desc);
[[nodiscard]] Result ReadFunctionBody(Offset end_offset);
// ReadInstructions either until and END instruction, or until
// the given end_offset.
[[nodiscard]] Result ReadInstructions(bool stop_on_end,
Offset end_offset,
Opcode* final_opcode);
[[nodiscard]] Result ReadNameSection(Offset section_size);
[[nodiscard]] Result ReadRelocSection(Offset section_size);
[[nodiscard]] Result ReadDylinkSection(Offset section_size);
[[nodiscard]] Result ReadDylink0Section(Offset section_size);
[[nodiscard]] Result ReadTargetFeaturesSections(Offset section_size);
[[nodiscard]] Result ReadLinkingSection(Offset section_size);
[[nodiscard]] Result ReadCodeMetadataSection(std::string_view name,
Offset section_size);
[[nodiscard]] Result ReadCustomSection(Index section_index,
Offset section_size);
[[nodiscard]] Result ReadTypeSection(Offset section_size);
[[nodiscard]] Result ReadImportSection(Offset section_size);
[[nodiscard]] Result ReadFunctionSection(Offset section_size);
[[nodiscard]] Result ReadTableSection(Offset section_size);
[[nodiscard]] Result ReadMemorySection(Offset section_size);
[[nodiscard]] Result ReadGlobalSection(Offset section_size);
[[nodiscard]] Result ReadExportSection(Offset section_size);
[[nodiscard]] Result ReadStartSection(Offset section_size);
[[nodiscard]] Result ReadElemSection(Offset section_size);
[[nodiscard]] Result ReadCodeSection(Offset section_size);
[[nodiscard]] Result ReadDataSection(Offset section_size);
[[nodiscard]] Result ReadDataCountSection(Offset section_size);
[[nodiscard]] Result ReadTagSection(Offset section_size);
[[nodiscard]] Result ReadSections(const ReadSectionsOptions& options);
Result ReportUnexpectedOpcode(Opcode opcode, const char* message = nullptr);
size_t read_end_ = 0; // Either the section end or data_size.
BinaryReaderDelegate::State state_;
BinaryReaderLogging logging_delegate_;
BinaryReaderDelegate* delegate_ = nullptr;
TypeVector param_types_;
TypeVector result_types_;
TypeMutVector fields_;
std::vector<Index> target_depths_;
const ReadBinaryOptions& options_;
BinarySection last_known_section_ = BinarySection::Invalid;
bool did_read_names_section_ = false;
bool reading_custom_section_ = false;
Index num_func_imports_ = 0;
Index num_table_imports_ = 0;
Index num_memory_imports_ = 0;
Index num_global_imports_ = 0;
Index num_tag_imports_ = 0;
Index num_function_signatures_ = 0;
Index num_function_bodies_ = 0;
Index data_count_ = kInvalidIndex;
std::vector<Limits> memories;
using ReadEndRestoreGuard =
ValueRestoreGuard<size_t, &BinaryReader::read_end_>;
};
BinaryReader::BinaryReader(const void* data,
size_t size,
BinaryReaderDelegate* delegate,
const ReadBinaryOptions& options)
: read_end_(size),
state_(static_cast<const uint8_t*>(data), size),
logging_delegate_(options.log_stream, delegate),
delegate_(options.log_stream ? &logging_delegate_ : delegate),
options_(options),
last_known_section_(BinarySection::Invalid) {
delegate->OnSetState(&state_);
}
void WABT_PRINTF_FORMAT(2, 3) BinaryReader::PrintError(const char* format,
...) {
ErrorLevel error_level =
reading_custom_section_ && !options_.fail_on_custom_section_error
? ErrorLevel::Warning
: ErrorLevel::Error;
WABT_SNPRINTF_ALLOCA(buffer, length, format);
Error error(error_level, Location(state_.offset), buffer);
bool handled = delegate_->OnError(error);
if (!handled) {
// Not great to just print, but we don't want to eat the error either.
fprintf(stderr, "%07" PRIzx ": %s: %s\n", state_.offset,
GetErrorLevelName(error_level), buffer);
}
}
Result BinaryReader::ReportUnexpectedOpcode(Opcode opcode, const char* where) {
std::string message = "unexpected opcode";
if (where) {
message += ' ';
message += where;
}
message += ":";
std::vector<uint8_t> bytes = opcode.GetBytes();
assert(bytes.size() > 0);
for (uint8_t byte : bytes) {
message += StringPrintf(" 0x%x", byte);
}
PrintError("%s", message.c_str());
return Result::Error;
}
Result BinaryReader::ReadOpcode(Opcode* out_value, const char* desc) {
uint8_t value = 0;
CHECK_RESULT(ReadU8(&value, desc));
if (Opcode::IsPrefixByte(value)) {
uint32_t code;
CHECK_RESULT(ReadU32Leb128(&code, desc));
*out_value = Opcode::FromCode(value, code);
} else {
*out_value = Opcode::FromCode(value);
}
return Result::Ok;
}
template <typename T>
Result BinaryReader::ReadT(T* out_value,
const char* type_name,
const char* desc) {
if (state_.offset + sizeof(T) > read_end_) {
PrintError("unable to read %s: %s", type_name, desc);
return Result::Error;
}
#if WABT_BIG_ENDIAN
uint8_t tmp[sizeof(T)];
memcpy(tmp, state_.data + state_.offset, sizeof(tmp));
SwapBytesSized(tmp, sizeof(tmp));
memcpy(out_value, tmp, sizeof(T));
#else
memcpy(out_value, state_.data + state_.offset, sizeof(T));
#endif
state_.offset += sizeof(T);
return Result::Ok;
}
Result BinaryReader::ReadU8(uint8_t* out_value, const char* desc) {
return ReadT(out_value, "uint8_t", desc);
}
Result BinaryReader::ReadU32(uint32_t* out_value, const char* desc) {
return ReadT(out_value, "uint32_t", desc);
}
Result BinaryReader::ReadF32(uint32_t* out_value, const char* desc) {
return ReadT(out_value, "float", desc);
}
Result BinaryReader::ReadF64(uint64_t* out_value, const char* desc) {
return ReadT(out_value, "double", desc);
}
Result BinaryReader::ReadV128(v128* out_value, const char* desc) {
return ReadT(out_value, "v128", desc);
}
Result BinaryReader::ReadU32Leb128(uint32_t* out_value, const char* desc) {
const uint8_t* p = state_.data + state_.offset;
const uint8_t* end = state_.data + read_end_;
size_t bytes_read = wabt::ReadU32Leb128(p, end, out_value);
ERROR_UNLESS(bytes_read > 0, "unable to read u32 leb128: %s", desc);
state_.offset += bytes_read;
return Result::Ok;
}
Result BinaryReader::ReadU64Leb128(uint64_t* out_value, const char* desc) {
const uint8_t* p = state_.data + state_.offset;
const uint8_t* end = state_.data + read_end_;
size_t bytes_read = wabt::ReadU64Leb128(p, end, out_value);
ERROR_UNLESS(bytes_read > 0, "unable to read u64 leb128: %s", desc);
state_.offset += bytes_read;
return Result::Ok;
}
Result BinaryReader::ReadS32Leb128(uint32_t* out_value, const char* desc) {
const uint8_t* p = state_.data + state_.offset;
const uint8_t* end = state_.data + read_end_;
size_t bytes_read = wabt::ReadS32Leb128(p, end, out_value);
ERROR_UNLESS(bytes_read > 0, "unable to read i32 leb128: %s", desc);
state_.offset += bytes_read;
return Result::Ok;
}
Result BinaryReader::ReadS64Leb128(uint64_t* out_value, const char* desc) {
const uint8_t* p = state_.data + state_.offset;
const uint8_t* end = state_.data + read_end_;
size_t bytes_read = wabt::ReadS64Leb128(p, end, out_value);
ERROR_UNLESS(bytes_read > 0, "unable to read i64 leb128: %s", desc);
state_.offset += bytes_read;
return Result::Ok;
}
Result BinaryReader::ReadType(Type* out_value, const char* desc) {
uint32_t type = 0;
CHECK_RESULT(ReadS32Leb128(&type, desc));
if (static_cast<Type::Enum>(type) == Type::Reference) {
uint32_t heap_type = 0;
CHECK_RESULT(ReadS32Leb128(&heap_type, desc));
*out_value = Type(Type::Reference, heap_type);
} else {
*out_value = static_cast<Type>(type);
}
return Result::Ok;
}
Result BinaryReader::ReadRefType(Type* out_value, const char* desc) {
uint32_t type = 0;
CHECK_RESULT(ReadS32Leb128(&type, desc));
*out_value = static_cast<Type>(type);
ERROR_UNLESS(out_value->IsRef(), "%s must be a reference type", desc);
return Result::Ok;
}
Result BinaryReader::ReadExternalKind(ExternalKind* out_value,
const char* desc) {
uint8_t value = 0;
CHECK_RESULT(ReadU8(&value, desc));
ERROR_UNLESS(value < kExternalKindCount, "invalid export external kind: %d",
value);
*out_value = static_cast<ExternalKind>(value);
return Result::Ok;
}
Result BinaryReader::ReadStr(std::string_view* out_str, const char* desc) {
uint32_t str_len = 0;
CHECK_RESULT(ReadU32Leb128(&str_len, "string length"));
ERROR_UNLESS(state_.offset + str_len <= read_end_,
"unable to read string: %s", desc);
*out_str = std::string_view(
reinterpret_cast<const char*>(state_.data) + state_.offset, str_len);
state_.offset += str_len;
ERROR_UNLESS(IsValidUtf8(out_str->data(), out_str->length()),
"invalid utf-8 encoding: %s", desc);
return Result::Ok;
}
Result BinaryReader::ReadBytes(const void** out_data,
Address* out_data_size,
const char* desc) {
uint32_t data_size = 0;
CHECK_RESULT(ReadU32Leb128(&data_size, "data size"));
ERROR_UNLESS(state_.offset + data_size <= read_end_,
"unable to read data: %s", desc);
*out_data = static_cast<const uint8_t*>(state_.data) + state_.offset;
*out_data_size = data_size;
state_.offset += data_size;
return Result::Ok;
}
Result BinaryReader::ReadIndex(Index* index, const char* desc) {
uint32_t value;
CHECK_RESULT(ReadU32Leb128(&value, desc));
*index = value;
return Result::Ok;
}
Result BinaryReader::ReadOffset(Offset* offset, const char* desc) {
uint32_t value;
CHECK_RESULT(ReadU32Leb128(&value, desc));
*offset = value;
return Result::Ok;
}
Result BinaryReader::ReadAlignment(Address* alignment_log2, const char* desc) {
uint32_t value;
CHECK_RESULT(ReadU32Leb128(&value, desc));
if (value >= 128 ||
(value >= 32 && !options_.features.multi_memory_enabled())) {
PrintError("invalid %s: %u", desc, value);
return Result::Error;
}
*alignment_log2 = value;
return Result::Ok;
}
Result BinaryReader::ReadMemidx(Index* memidx, const char* desc) {
CHECK_RESULT(ReadIndex(memidx, desc));
ERROR_UNLESS(*memidx < memories.size(), "memory index %u out of range",
*memidx);
return Result::Ok;
}
Result BinaryReader::ReadMemLocation(Address* alignment_log2,
Index* memidx,
Address* offset,
const char* desc_align,
const char* desc_memidx,
const char* desc_offset,
uint8_t* lane_val) {
CHECK_RESULT(ReadAlignment(alignment_log2, desc_align));
*memidx = 0;
if (*alignment_log2 >> 6) {
ERROR_IF(!options_.features.multi_memory_enabled(),
"multi_memory not allowed");
*alignment_log2 = *alignment_log2 & ((1 << 6) - 1);
CHECK_RESULT(ReadMemidx(memidx, desc_memidx));
}
CHECK_RESULT(ReadAddress(offset, 0, desc_offset));
if (lane_val) {
CHECK_RESULT(ReadU8(lane_val, "Lane idx"));
}
return Result::Ok;
}
Result BinaryReader::CallbackMemLocation(const Address* alignment_log2,
const Index* memidx,
const Address* offset,
const uint8_t* lane_val) {
if (lane_val) {
if (*memidx) {
CALLBACK(OnOpcodeUint32Uint32Uint32Uint32, *alignment_log2, *memidx,
*offset, *lane_val);
} else {
CALLBACK(OnOpcodeUint32Uint32Uint32, *alignment_log2, *offset, *lane_val);
}
} else {
if (*memidx) {
CALLBACK(OnOpcodeUint32Uint32Uint32, *alignment_log2, *memidx, *offset);
} else {
CALLBACK(OnOpcodeUint32Uint32, *alignment_log2, *offset);
}
}
return Result::Ok;
}
Result BinaryReader::ReadCount(Index* count, const char* desc) {
CHECK_RESULT(ReadIndex(count, desc));
// This check assumes that each item follows in this section, and takes at
// least 1 byte. It's possible that this check passes but reading fails
// later. It is still useful to check here, though, because it early-outs
// when an erroneous large count is used, before allocating memory for it.
size_t section_remaining = read_end_ - state_.offset;
if (*count > section_remaining) {
PrintError("invalid %s %" PRIindex ", only %" PRIzd
" bytes left in section",
desc, *count, section_remaining);
return Result::Error;
}
return Result::Ok;
}
Result BinaryReader::ReadField(TypeMut* out_value) {
// TODO: Reuse for global header too?
Type field_type;
CHECK_RESULT(ReadType(&field_type, "field type"));
ERROR_UNLESS(IsConcreteType(field_type),
"expected valid field type (got " PRItypecode ")",
WABT_PRINTF_TYPE_CODE(field_type));
uint8_t mutable_ = 0;
CHECK_RESULT(ReadU8(&mutable_, "field mutability"));
ERROR_UNLESS(mutable_ <= 1, "field mutability must be 0 or 1");
out_value->type = field_type;
out_value->mutable_ = mutable_;
return Result::Ok;
}
bool BinaryReader::IsConcreteType(Type type) {
switch (type) {
case Type::I32:
case Type::I64:
case Type::F32:
case Type::F64:
return true;
case Type::V128:
return options_.features.simd_enabled();
case Type::FuncRef:
case Type::ExternRef:
return options_.features.reference_types_enabled();
case Type::Reference:
return options_.features.function_references_enabled();
default:
return false;
}
}
bool BinaryReader::IsBlockType(Type type) {
if (IsConcreteType(type) || type == Type::Void) {
return true;
}
if (!(options_.features.multi_value_enabled() && type.IsIndex())) {
return false;
}
return true;
}
Index BinaryReader::NumTotalFuncs() {
return num_func_imports_ + num_function_signatures_;
}
Result BinaryReader::ReadInitExpr(Index index) {
// Read instructions until END opcode is reached.
Opcode final_opcode(Opcode::Invalid);
CHECK_RESULT(
ReadInstructions(/*stop_on_end=*/true, read_end_, &final_opcode));
ERROR_UNLESS(state_.offset <= read_end_,
"init expression longer than given size");
ERROR_UNLESS(final_opcode == Opcode::End,
"init expression must end with END opcode");
return Result::Ok;
}
Result BinaryReader::ReadTable(Type* out_elem_type, Limits* out_elem_limits) {
CHECK_RESULT(ReadRefType(out_elem_type, "table elem type"));
uint8_t flags;
uint32_t initial;
uint32_t max = 0;
CHECK_RESULT(ReadU8(&flags, "table flags"));
bool has_max = flags & WABT_BINARY_LIMITS_HAS_MAX_FLAG;
bool is_shared = flags & WABT_BINARY_LIMITS_IS_SHARED_FLAG;
bool is_64 = flags & WABT_BINARY_LIMITS_IS_64_FLAG;
const uint8_t unknown_flags = flags & ~WABT_BINARY_LIMITS_ALL_FLAGS;
ERROR_IF(is_shared, "tables may not be shared");
ERROR_IF(is_64, "tables may not be 64-bit");
ERROR_UNLESS(unknown_flags == 0, "malformed table limits flag: %d", flags);
CHECK_RESULT(ReadU32Leb128(&initial, "table initial elem count"));
if (has_max) {
CHECK_RESULT(ReadU32Leb128(&max, "table max elem count"));
}
out_elem_limits->has_max = has_max;
out_elem_limits->initial = initial;
out_elem_limits->max = max;
return Result::Ok;
}
Result BinaryReader::ReadMemory(Limits* out_page_limits) {
uint8_t flags;
uint64_t initial;
uint64_t max = 0;
CHECK_RESULT(ReadU8(&flags, "memory flags"));
bool has_max = flags & WABT_BINARY_LIMITS_HAS_MAX_FLAG;
bool is_shared = flags & WABT_BINARY_LIMITS_IS_SHARED_FLAG;
bool is_64 = flags & WABT_BINARY_LIMITS_IS_64_FLAG;
const uint8_t unknown_flags = flags & ~WABT_BINARY_LIMITS_ALL_FLAGS;
ERROR_UNLESS(unknown_flags == 0, "malformed memory limits flag: %d", flags);
ERROR_IF(is_shared && !options_.features.threads_enabled(),
"memory may not be shared: threads not allowed");
ERROR_IF(is_64 && !options_.features.memory64_enabled(),
"memory64 not allowed");
if (is_64) {
CHECK_RESULT(ReadU64Leb128(&initial, "memory initial page count"));
if (has_max) {
CHECK_RESULT(ReadU64Leb128(&max, "memory max page count"));
}
} else {
uint32_t initial32;
CHECK_RESULT(ReadU32Leb128(&initial32, "memory initial page count"));
initial = initial32;
if (has_max) {
uint32_t max32;
CHECK_RESULT(ReadU32Leb128(&max32, "memory max page count"));
max = max32;
}
}
out_page_limits->has_max = has_max;
out_page_limits->is_shared = is_shared;
out_page_limits->is_64 = is_64;
out_page_limits->initial = initial;
out_page_limits->max = max;
// Have to keep a copy of these, to know how to interpret load/stores.
memories.push_back(*out_page_limits);
return Result::Ok;
}
Result BinaryReader::ReadGlobalHeader(Type* out_type, bool* out_mutable) {
Type global_type = Type::Void;
uint8_t mutable_ = 0;
CHECK_RESULT(ReadType(&global_type, "global type"));
ERROR_UNLESS(IsConcreteType(global_type), "invalid global type: %#x",
static_cast<int>(global_type));
CHECK_RESULT(ReadU8(&mutable_, "global mutability"));
ERROR_UNLESS(mutable_ <= 1, "global mutability must be 0 or 1");
*out_type = global_type;
*out_mutable = mutable_;
return Result::Ok;
}
Result BinaryReader::ReadAddress(Address* out_value,
Index memory,
const char* desc) {
ERROR_UNLESS(memory < memories.size(),
"load/store memory %u out of range %zu", memory,
memories.size());
if (memories[memory].is_64) {
return ReadU64Leb128(out_value, desc);
} else {
uint32_t val;
Result res = ReadU32Leb128(&val, desc);
*out_value = val;
return res;
}
}
Result BinaryReader::ReadFunctionBody(Offset end_offset) {
Opcode final_opcode(Opcode::Invalid);
CHECK_RESULT(
ReadInstructions(/*stop_on_end=*/false, end_offset, &final_opcode));
ERROR_UNLESS(state_.offset == end_offset,
"function body longer than given size");
ERROR_UNLESS(final_opcode == Opcode::End,
"function body must end with END opcode");
return Result::Ok;
}
Result BinaryReader::ReadInstructions(bool stop_on_end,
Offset end_offset,
Opcode* final_opcode) {
while (state_.offset < end_offset) {
Opcode opcode;
CHECK_RESULT(ReadOpcode(&opcode, "opcode"));
CALLBACK(OnOpcode, opcode);
ERROR_UNLESS_OPCODE_ENABLED(opcode);
if (final_opcode) {
*final_opcode = opcode;
}
switch (opcode) {
case Opcode::Unreachable:
CALLBACK0(OnUnreachableExpr);
CALLBACK0(OnOpcodeBare);
break;
case Opcode::Block: {
Type sig_type;
CHECK_RESULT(ReadType(&sig_type, "block signature type"));
ERROR_UNLESS(IsBlockType(sig_type),
"expected valid block signature type");
CALLBACK(OnBlockExpr, sig_type);
CALLBACK(OnOpcodeBlockSig, sig_type);
break;
}
case Opcode::Loop: {
Type sig_type;
CHECK_RESULT(ReadType(&sig_type, "loop signature type"));
ERROR_UNLESS(IsBlockType(sig_type),
"expected valid block signature type");
CALLBACK(OnLoopExpr, sig_type);
CALLBACK(OnOpcodeBlockSig, sig_type);
break;
}
case Opcode::If: {
Type sig_type;
CHECK_RESULT(ReadType(&sig_type, "if signature type"));
ERROR_UNLESS(IsBlockType(sig_type),
"expected valid block signature type");
CALLBACK(OnIfExpr, sig_type);
CALLBACK(OnOpcodeBlockSig, sig_type);
break;
}
case Opcode::Else:
CALLBACK0(OnElseExpr);
CALLBACK0(OnOpcodeBare);
break;
case Opcode::SelectT: {
Index num_results;
CHECK_RESULT(ReadCount(&num_results, "num result types"));
result_types_.resize(num_results);
for (Index i = 0; i < num_results; ++i) {
Type result_type;
CHECK_RESULT(ReadType(&result_type, "select result type"));
ERROR_UNLESS(IsConcreteType(result_type),
"expected valid select result type (got " PRItypecode
")",
WABT_PRINTF_TYPE_CODE(result_type));
result_types_[i] = result_type;
}
if (num_results) {
CALLBACK(OnSelectExpr, num_results, result_types_.data());
CALLBACK(OnOpcodeType, result_types_[0]);
} else {
CALLBACK(OnSelectExpr, 0, NULL);
CALLBACK0(OnOpcodeBare);
}
break;
}
case Opcode::Select:
CALLBACK(OnSelectExpr, 0, nullptr);
CALLBACK0(OnOpcodeBare);
break;
case Opcode::Br: {
Index depth;
CHECK_RESULT(ReadIndex(&depth, "br depth"));
CALLBACK(OnBrExpr, depth);
CALLBACK(OnOpcodeIndex, depth);
break;
}
case Opcode::BrIf: {
Index depth;
CHECK_RESULT(ReadIndex(&depth, "br_if depth"));
CALLBACK(OnBrIfExpr, depth);
CALLBACK(OnOpcodeIndex, depth);
break;
}
case Opcode::BrTable: {
Index num_targets;
CHECK_RESULT(ReadCount(&num_targets, "br_table target count"));
target_depths_.resize(num_targets);
for (Index i = 0; i < num_targets; ++i) {
Index target_depth;
CHECK_RESULT(ReadIndex(&target_depth, "br_table target depth"));
target_depths_[i] = target_depth;
}
Index default_target_depth;
CHECK_RESULT(
ReadIndex(&default_target_depth, "br_table default target depth"));
Index* target_depths = num_targets ? target_depths_.data() : nullptr;
CALLBACK(OnBrTableExpr, num_targets, target_depths,
default_target_depth);
break;
}
case Opcode::Return:
CALLBACK0(OnReturnExpr);
CALLBACK0(OnOpcodeBare);
break;
case Opcode::Nop:
CALLBACK0(OnNopExpr);
CALLBACK0(OnOpcodeBare);
break;
case Opcode::Drop:
CALLBACK0(OnDropExpr);
CALLBACK0(OnOpcodeBare);
break;
case Opcode::End:
CALLBACK0(OnEndExpr);
if (stop_on_end) {
return Result::Ok;
}
break;
case Opcode::I32Const: {
uint32_t value;
CHECK_RESULT(ReadS32Leb128(&value, "i32.const value"));
CALLBACK(OnI32ConstExpr, value);
CALLBACK(OnOpcodeUint32, value);
break;
}
case Opcode::I64Const: {
uint64_t value;
CHECK_RESULT(ReadS64Leb128(&value, "i64.const value"));
CALLBACK(OnI64ConstExpr, value);
CALLBACK(OnOpcodeUint64, value);
break;
}
case Opcode::F32Const: {
uint32_t value_bits = 0;
CHECK_RESULT(ReadF32(&value_bits, "f32.const value"));
CALLBACK(OnF32ConstExpr, value_bits);
CALLBACK(OnOpcodeF32, value_bits);
break;
}
case Opcode::F64Const: {
uint64_t value_bits = 0;
CHECK_RESULT(ReadF64(&value_bits, "f64.const value"));
CALLBACK(OnF64ConstExpr, value_bits);
CALLBACK(OnOpcodeF64, value_bits);
break;
}
case Opcode::V128Const: {
v128 value_bits;
ZeroMemory(value_bits);
CHECK_RESULT(ReadV128(&value_bits, "v128.const value"));
CALLBACK(OnV128ConstExpr, value_bits);
CALLBACK(OnOpcodeV128, value_bits);
break;
}
case Opcode::GlobalGet: {
Index global_index;
CHECK_RESULT(ReadIndex(&global_index, "global.get global index"));
CALLBACK(OnGlobalGetExpr, global_index);
CALLBACK(OnOpcodeIndex, global_index);
break;
}
case Opcode::LocalGet: {
Index local_index;
CHECK_RESULT(ReadIndex(&local_index, "local.get local index"));
CALLBACK(OnLocalGetExpr, local_index);
CALLBACK(OnOpcodeIndex, local_index);
break;
}
case Opcode::GlobalSet: {
Index global_index;
CHECK_RESULT(ReadIndex(&global_index, "global.set global index"));
CALLBACK(OnGlobalSetExpr, global_index);
CALLBACK(OnOpcodeIndex, global_index);
break;
}
case Opcode::LocalSet: {
Index local_index;
CHECK_RESULT(ReadIndex(&local_index, "local.set local index"));
CALLBACK(OnLocalSetExpr, local_index);
CALLBACK(OnOpcodeIndex, local_index);
break;
}
case Opcode::Call: {
Index func_index;
CHECK_RESULT(ReadIndex(&func_index, "call function index"));
CALLBACK(OnCallExpr, func_index);
CALLBACK(OnOpcodeIndex, func_index);
break;
}
case Opcode::CallIndirect: {
Index sig_index;
CHECK_RESULT(ReadIndex(&sig_index, "call_indirect signature index"));
Index table_index = 0;
if (options_.features.reference_types_enabled()) {
CHECK_RESULT(ReadIndex(&table_index, "call_indirect table index"));
} else {
uint8_t reserved;
CHECK_RESULT(ReadU8(&reserved, "call_indirect reserved"));
ERROR_UNLESS(reserved == 0, "call_indirect reserved value must be 0");
}
CALLBACK(OnCallIndirectExpr, sig_index, table_index);
CALLBACK(OnOpcodeUint32Uint32, sig_index, table_index);
break;
}
case Opcode::ReturnCall: {
Index func_index;
CHECK_RESULT(ReadIndex(&func_index, "return_call"));
CALLBACK(OnReturnCallExpr, func_index);
CALLBACK(OnOpcodeIndex, func_index);
break;
}
case Opcode::ReturnCallIndirect: {
Index sig_index;
CHECK_RESULT(ReadIndex(&sig_index, "return_call_indirect"));
Index table_index = 0;
if (options_.features.reference_types_enabled()) {
CHECK_RESULT(
ReadIndex(&table_index, "return_call_indirect table index"));
} else {
uint8_t reserved;
CHECK_RESULT(ReadU8(&reserved, "return_call_indirect reserved"));
ERROR_UNLESS(reserved == 0,
"return_call_indirect reserved value must be 0");
}
CALLBACK(OnReturnCallIndirectExpr, sig_index, table_index);
CALLBACK(OnOpcodeUint32Uint32, sig_index, table_index);
break;
}
case Opcode::LocalTee: {
Index local_index;
CHECK_RESULT(ReadIndex(&local_index, "local.tee local index"));
CALLBACK(OnLocalTeeExpr, local_index);
CALLBACK(OnOpcodeIndex, local_index);
break;
}
case Opcode::I32Load8S:
case Opcode::I32Load8U:
case Opcode::I32Load16S:
case Opcode::I32Load16U:
case Opcode::I64Load8S:
case Opcode::I64Load8U:
case Opcode::I64Load16S:
case Opcode::I64Load16U:
case Opcode::I64Load32S:
case Opcode::I64Load32U:
case Opcode::I32Load:
case Opcode::I64Load:
case Opcode::F32Load:
case Opcode::F64Load:
case Opcode::V128Load:
case Opcode::V128Load8X8S:
case Opcode::V128Load8X8U:
case Opcode::V128Load16X4S:
case Opcode::V128Load16X4U:
case Opcode::V128Load32X2S:
case Opcode::V128Load32X2U: {
Address alignment_log2;
Index memidx;
Address offset;
CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
"load alignment", "load memidx",
"load offset"));
CALLBACK(OnLoadExpr, opcode, memidx, alignment_log2, offset);
CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
break;
}
case Opcode::I32Store8:
case Opcode::I32Store16:
case Opcode::I64Store8:
case Opcode::I64Store16:
case Opcode::I64Store32:
case Opcode::I32Store:
case Opcode::I64Store:
case Opcode::F32Store:
case Opcode::F64Store:
case Opcode::V128Store: {
Address alignment_log2;
Index memidx;
Address offset;
CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
"store alignment", "store memidx",
"store offset"));
CALLBACK(OnStoreExpr, opcode, memidx, alignment_log2, offset);
CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
break;
}
case Opcode::MemorySize: {
Index memidx = 0;
if (!options_.features.multi_memory_enabled()) {
uint8_t reserved;
CHECK_RESULT(ReadU8(&reserved, "memory.size reserved"));
ERROR_UNLESS(reserved == 0, "memory.size reserved value must be 0");
} else {
CHECK_RESULT(ReadMemidx(&memidx, "memory.size memidx"));
}
CALLBACK(OnMemorySizeExpr, memidx);
CALLBACK(OnOpcodeUint32, memidx);
break;
}
case Opcode::MemoryGrow: {
Index memidx = 0;
if (!options_.features.multi_memory_enabled()) {
uint8_t reserved;
CHECK_RESULT(ReadU8(&reserved, "memory.grow reserved"));
ERROR_UNLESS(reserved == 0, "memory.grow reserved value must be 0");
} else {
CHECK_RESULT(ReadMemidx(&memidx, "memory.grow memidx"));
}
CALLBACK(OnMemoryGrowExpr, memidx);
CALLBACK(OnOpcodeUint32, memidx);
break;
}
case Opcode::I32Add:
case Opcode::I32Sub:
case Opcode::I32Mul:
case Opcode::I32DivS:
case Opcode::I32DivU:
case Opcode::I32RemS:
case Opcode::I32RemU:
case Opcode::I32And:
case Opcode::I32Or:
case Opcode::I32Xor:
case Opcode::I32Shl:
case Opcode::I32ShrU:
case Opcode::I32ShrS:
case Opcode::I32Rotr:
case Opcode::I32Rotl:
case Opcode::I64Add:
case Opcode::I64Sub:
case Opcode::I64Mul:
case Opcode::I64DivS:
case Opcode::I64DivU:
case Opcode::I64RemS:
case Opcode::I64RemU:
case Opcode::I64And:
case Opcode::I64Or:
case Opcode::I64Xor:
case Opcode::I64Shl:
case Opcode::I64ShrU:
case Opcode::I64ShrS:
case Opcode::I64Rotr:
case Opcode::I64Rotl:
case Opcode::F32Add:
case Opcode::F32Sub:
case Opcode::F32Mul:
case Opcode::F32Div:
case Opcode::F32Min:
case Opcode::F32Max:
case Opcode::F32Copysign:
case Opcode::F64Add:
case Opcode::F64Sub:
case Opcode::F64Mul:
case Opcode::F64Div:
case Opcode::F64Min:
case Opcode::F64Max:
case Opcode::F64Copysign:
case Opcode::I8X16Add:
case Opcode::I16X8Add:
case Opcode::I32X4Add:
case Opcode::I64X2Add:
case Opcode::I8X16Sub:
case Opcode::I16X8Sub:
case Opcode::I32X4Sub:
case Opcode::I64X2Sub:
case Opcode::I16X8Mul:
case Opcode::I32X4Mul:
case Opcode::I64X2Mul:
case Opcode::I8X16AddSatS:
case Opcode::I8X16AddSatU:
case Opcode::I16X8AddSatS:
case Opcode::I16X8AddSatU:
case Opcode::I8X16SubSatS:
case Opcode::I8X16SubSatU:
case Opcode::I16X8SubSatS:
case Opcode::I16X8SubSatU:
case Opcode::I8X16MinS:
case Opcode::I16X8MinS:
case Opcode::I32X4MinS:
case Opcode::I8X16MinU:
case Opcode::I16X8MinU:
case Opcode::I32X4MinU:
case Opcode::I8X16MaxS:
case Opcode::I16X8MaxS:
case Opcode::I32X4MaxS:
case Opcode::I8X16MaxU:
case Opcode::I16X8MaxU:
case Opcode::I32X4MaxU:
case Opcode::I8X16Shl:
case Opcode::I16X8Shl:
case Opcode::I32X4Shl:
case Opcode::I64X2Shl:
case Opcode::I8X16ShrS:
case Opcode::I8X16ShrU:
case Opcode::I16X8ShrS:
case Opcode::I16X8ShrU:
case Opcode::I32X4ShrS:
case Opcode::I32X4ShrU:
case Opcode::I64X2ShrS:
case Opcode::I64X2ShrU:
case Opcode::V128And:
case Opcode::V128Or:
case Opcode::V128Xor:
case Opcode::F32X4Min:
case Opcode::F32X4PMin:
case Opcode::F64X2Min:
case Opcode::F64X2PMin:
case Opcode::F32X4Max:
case Opcode::F32X4PMax:
case Opcode::F64X2Max:
case Opcode::F64X2PMax:
case Opcode::F32X4Add:
case Opcode::F64X2Add:
case Opcode::F32X4Sub:
case Opcode::F64X2Sub:
case Opcode::F32X4Div:
case Opcode::F64X2Div:
case Opcode::F32X4Mul:
case Opcode::F64X2Mul:
case Opcode::I8X16Swizzle:
case Opcode::I8X16NarrowI16X8S:
case Opcode::I8X16NarrowI16X8U:
case Opcode::I16X8NarrowI32X4S:
case Opcode::I16X8NarrowI32X4U:
case Opcode::V128Andnot:
case Opcode::I8X16AvgrU:
case Opcode::I16X8AvgrU:
case Opcode::I16X8ExtmulLowI8X16S:
case Opcode::I16X8ExtmulHighI8X16S:
case Opcode::I16X8ExtmulLowI8X16U:
case Opcode::I16X8ExtmulHighI8X16U:
case Opcode::I32X4ExtmulLowI16X8S:
case Opcode::I32X4ExtmulHighI16X8S:
case Opcode::I32X4ExtmulLowI16X8U:
case Opcode::I32X4ExtmulHighI16X8U:
case Opcode::I64X2ExtmulLowI32X4S:
case Opcode::I64X2ExtmulHighI32X4S:
case Opcode::I64X2ExtmulLowI32X4U:
case Opcode::I64X2ExtmulHighI32X4U:
case Opcode::I16X8Q15mulrSatS:
case Opcode::I32X4DotI16X8S:
case Opcode::I8X16RelaxedSwizzle:
case Opcode::F32X4RelaxedMin:
case Opcode::F32X4RelaxedMax:
case Opcode::F64X2RelaxedMin:
case Opcode::F64X2RelaxedMax:
case Opcode::I16X8RelaxedQ15mulrS:
case Opcode::I16X8DotI8X16I7X16S:
CALLBACK(OnBinaryExpr, opcode);
CALLBACK0(OnOpcodeBare);
break;
case Opcode::I32Eq:
case Opcode::I32Ne:
case Opcode::I32LtS:
case Opcode::I32LeS:
case Opcode::I32LtU:
case Opcode::I32LeU:
case Opcode::I32GtS:
case Opcode::I32GeS:
case Opcode::I32GtU:
case Opcode::I32GeU:
case Opcode::I64Eq:
case Opcode::I64Ne:
case Opcode::I64LtS:
case Opcode::I64LeS:
case Opcode::I64LtU:
case Opcode::I64LeU:
case Opcode::I64GtS:
case Opcode::I64GeS:
case Opcode::I64GtU:
case Opcode::I64GeU:
case Opcode::F32Eq:
case Opcode::F32Ne:
case Opcode::F32Lt:
case Opcode::F32Le:
case Opcode::F32Gt:
case Opcode::F32Ge:
case Opcode::F64Eq:
case Opcode::F64Ne:
case Opcode::F64Lt:
case Opcode::F64Le:
case Opcode::F64Gt:
case Opcode::F64Ge:
case Opcode::I8X16Eq:
case Opcode::I16X8Eq:
case Opcode::I32X4Eq:
case Opcode::I64X2Eq:
case Opcode::F32X4Eq:
case Opcode::F64X2Eq:
case Opcode::I8X16Ne:
case Opcode::I16X8Ne:
case Opcode::I32X4Ne:
case Opcode::I64X2Ne:
case Opcode::F32X4Ne:
case Opcode::F64X2Ne:
case Opcode::I8X16LtS:
case Opcode::I8X16LtU:
case Opcode::I16X8LtS:
case Opcode::I16X8LtU:
case Opcode::I32X4LtS:
case Opcode::I32X4LtU:
case Opcode::I64X2LtS:
case Opcode::F32X4Lt:
case Opcode::F64X2Lt:
case Opcode::I8X16LeS:
case Opcode::I8X16LeU:
case Opcode::I16X8LeS:
case Opcode::I16X8LeU:
case Opcode::I32X4LeS:
case Opcode::I32X4LeU:
case Opcode::I64X2LeS:
case Opcode::F32X4Le:
case Opcode::F64X2Le:
case Opcode::I8X16GtS:
case Opcode::I8X16GtU:
case Opcode::I16X8GtS:
case Opcode::I16X8GtU:
case Opcode::I32X4GtS:
case Opcode::I32X4GtU:
case Opcode::I64X2GtS:
case Opcode::F32X4Gt:
case Opcode::F64X2Gt:
case Opcode::I8X16GeS:
case Opcode::I8X16GeU:
case Opcode::I16X8GeS:
case Opcode::I16X8GeU:
case Opcode::I32X4GeS:
case Opcode::I32X4GeU:
case Opcode::I64X2GeS:
case Opcode::F32X4Ge:
case Opcode::F64X2Ge:
CALLBACK(OnCompareExpr, opcode);
CALLBACK0(OnOpcodeBare);
break;
case Opcode::I32Clz:
case Opcode::I32Ctz:
case Opcode::I32Popcnt:
case Opcode::I64Clz:
case Opcode::I64Ctz:
case Opcode::I64Popcnt:
case Opcode::F32Abs:
case Opcode::F32Neg:
case Opcode::F32Ceil:
case Opcode::F32Floor:
case Opcode::F32Trunc:
case Opcode::F32Nearest:
case Opcode::F32Sqrt:
case Opcode::F64Abs:
case Opcode::F64Neg:
case Opcode::F64Ceil:
case Opcode::F64Floor:
case Opcode::F64Trunc:
case Opcode::F64Nearest:
case Opcode::F64Sqrt:
case Opcode::I8X16Splat:
case Opcode::I16X8Splat:
case Opcode::I32X4Splat:
case Opcode::I64X2Splat:
case Opcode::F32X4Splat:
case Opcode::F64X2Splat:
case Opcode::I8X16Neg:
case Opcode::I16X8Neg:
case Opcode::I32X4Neg:
case Opcode::I64X2Neg:
case Opcode::V128Not:
case Opcode::V128AnyTrue:
case Opcode::I8X16Bitmask:
case Opcode::I16X8Bitmask:
case Opcode::I32X4Bitmask:
case Opcode::I64X2Bitmask:
case Opcode::I8X16AllTrue:
case Opcode::I16X8AllTrue:
case Opcode::I32X4AllTrue:
case Opcode::I64X2AllTrue:
case Opcode::F32X4Ceil:
case Opcode::F64X2Ceil:
case Opcode::F32X4Floor:
case Opcode::F64X2Floor:
case Opcode::F32X4Trunc:
case Opcode::F64X2Trunc:
case Opcode::F32X4Nearest:
case Opcode::F64X2Nearest:
case Opcode::F32X4Neg:
case Opcode::F64X2Neg:
case Opcode::F32X4Abs:
case Opcode::F64X2Abs:
case Opcode::F32X4Sqrt:
case Opcode::F64X2Sqrt:
case Opcode::I16X8ExtendLowI8X16S:
case Opcode::I16X8ExtendHighI8X16S:
case Opcode::I16X8ExtendLowI8X16U:
case Opcode::I16X8ExtendHighI8X16U:
case Opcode::I32X4ExtendLowI16X8S:
case Opcode::I32X4ExtendHighI16X8S:
case Opcode::I32X4ExtendLowI16X8U:
case Opcode::I32X4ExtendHighI16X8U:
case Opcode::I64X2ExtendLowI32X4S:
case Opcode::I64X2ExtendHighI32X4S:
case Opcode::I64X2ExtendLowI32X4U:
case Opcode::I64X2ExtendHighI32X4U:
case Opcode::I8X16Abs:
case Opcode::I16X8Abs:
case Opcode::I32X4Abs:
case Opcode::I64X2Abs:
case Opcode::I8X16Popcnt:
case Opcode::I16X8ExtaddPairwiseI8X16S:
case Opcode::I16X8ExtaddPairwiseI8X16U:
case Opcode::I32X4ExtaddPairwiseI16X8S:
case Opcode::I32X4ExtaddPairwiseI16X8U:
case Opcode::I32X4RelaxedTruncF32X4S:
case Opcode::I32X4RelaxedTruncF32X4U:
case Opcode::I32X4RelaxedTruncF64X2SZero:
case Opcode::I32X4RelaxedTruncF64X2UZero:
CALLBACK(OnUnaryExpr, opcode);
CALLBACK0(OnOpcodeBare);
break;
case Opcode::V128BitSelect:
case Opcode::F32X4RelaxedMadd:
case Opcode::F32X4RelaxedNmadd:
case Opcode::F64X2RelaxedMadd:
case Opcode::F64X2RelaxedNmadd:
case Opcode::I8X16RelaxedLaneSelect:
case Opcode::I16X8RelaxedLaneSelect:
case Opcode::I32X4RelaxedLaneSelect:
case Opcode::I64X2RelaxedLaneSelect:
case Opcode::I32X4DotI8X16I7X16AddS:
CALLBACK(OnTernaryExpr, opcode);
CALLBACK0(OnOpcodeBare);
break;
case Opcode::I8X16ExtractLaneS:
case Opcode::I8X16ExtractLaneU:
case Opcode::I16X8ExtractLaneS:
case Opcode::I16X8ExtractLaneU:
case Opcode::I32X4ExtractLane:
case Opcode::I64X2ExtractLane:
case Opcode::F32X4ExtractLane:
case Opcode::F64X2ExtractLane:
case Opcode::I8X16ReplaceLane:
case Opcode::I16X8ReplaceLane:
case Opcode::I32X4ReplaceLane:
case Opcode::I64X2ReplaceLane:
case Opcode::F32X4ReplaceLane:
case Opcode::F64X2ReplaceLane: {
uint8_t lane_val;
CHECK_RESULT(ReadU8(&lane_val, "Lane idx"));
CALLBACK(OnSimdLaneOpExpr, opcode, lane_val);
CALLBACK(OnOpcodeUint64, lane_val);
break;
}
case Opcode::I8X16Shuffle: {
v128 value;
CHECK_RESULT(ReadV128(&value, "Lane idx [16]"));
CALLBACK(OnSimdShuffleOpExpr, opcode, value);
CALLBACK(OnOpcodeV128, value);
break;
}
case Opcode::V128Load8Splat:
case Opcode::V128Load16Splat:
case Opcode::V128Load32Splat:
case Opcode::V128Load64Splat: {
Address alignment_log2;
Index memidx;
Address offset;
CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
"load alignment", "load memidx",
"load offset"));
CALLBACK(OnLoadSplatExpr, opcode, memidx, alignment_log2, offset);
CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
break;
}
case Opcode::V128Load8Lane:
case Opcode::V128Load16Lane:
case Opcode::V128Load32Lane:
case Opcode::V128Load64Lane: {
Address alignment_log2;
Index memidx;
Address offset;
uint8_t lane_val;
CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
"load alignment", "load memidx",
"load offset", &lane_val));
CALLBACK(OnSimdLoadLaneExpr, opcode, memidx, alignment_log2, offset,
lane_val);
CHECK_RESULT(
CallbackMemLocation(&alignment_log2, &memidx, &offset, &lane_val));
break;
}
case Opcode::V128Store8Lane:
case Opcode::V128Store16Lane:
case Opcode::V128Store32Lane:
case Opcode::V128Store64Lane: {
Address alignment_log2;
Index memidx;
Address offset;
uint8_t lane_val;
CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
"store alignment", "store memidx",
"store offset", &lane_val));
CALLBACK(OnSimdStoreLaneExpr, opcode, memidx, alignment_log2, offset,
lane_val);
CHECK_RESULT(
CallbackMemLocation(&alignment_log2, &memidx, &offset, &lane_val));
break;
}
case Opcode::V128Load32Zero:
case Opcode::V128Load64Zero: {
Address alignment_log2;
Index memidx;
Address offset;
CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
"load alignment", "load memidx",
"load offset"));
CALLBACK(OnLoadZeroExpr, opcode, memidx, alignment_log2, offset);
CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
break;
}
case Opcode::I32TruncF32S:
case Opcode::I32TruncF64S:
case Opcode::I32TruncF32U:
case Opcode::I32TruncF64U:
case Opcode::I32WrapI64:
case Opcode::I64TruncF32S:
case Opcode::I64TruncF64S:
case Opcode::I64TruncF32U:
case Opcode::I64TruncF64U:
case Opcode::I64ExtendI32S:
case Opcode::I64ExtendI32U:
case Opcode::F32ConvertI32S:
case Opcode::F32ConvertI32U:
case Opcode::F32ConvertI64S:
case Opcode::F32ConvertI64U:
case Opcode::F32DemoteF64:
case Opcode::F32ReinterpretI32:
case Opcode::F64ConvertI32S:
case Opcode::F64ConvertI32U:
case Opcode::F64ConvertI64S:
case Opcode::F64ConvertI64U:
case Opcode::F64PromoteF32:
case Opcode::F64ReinterpretI64:
case Opcode::I32ReinterpretF32:
case Opcode::I64ReinterpretF64:
case Opcode::I32Eqz:
case Opcode::I64Eqz:
case Opcode::F32X4ConvertI32X4S:
case Opcode::F32X4ConvertI32X4U:
case Opcode::I32X4TruncSatF32X4S:
case Opcode::I32X4TruncSatF32X4U:
case Opcode::F32X4DemoteF64X2Zero:
case Opcode::F64X2PromoteLowF32X4:
case Opcode::I32X4TruncSatF64X2SZero:
case Opcode::I32X4TruncSatF64X2UZero:
case Opcode::F64X2ConvertLowI32X4S:
case Opcode::F64X2ConvertLowI32X4U:
CALLBACK(OnConvertExpr, opcode);
CALLBACK0(OnOpcodeBare);
break;
case Opcode::Try: {
Type sig_type;
CHECK_RESULT(ReadType(&sig_type, "try signature type"));
ERROR_UNLESS(IsBlockType(sig_type),
"expected valid block signature type");
CALLBACK(OnTryExpr, sig_type);
CALLBACK(OnOpcodeBlockSig, sig_type);
break;
}
case Opcode::Catch: {
Index index;
CHECK_RESULT(ReadIndex(&index, "tag index"));
CALLBACK(OnCatchExpr, index);
CALLBACK(OnOpcodeIndex, index);
break;
}
case Opcode::CatchAll: {
CALLBACK(OnCatchAllExpr);
CALLBACK(OnOpcodeBare);
break;
}
case Opcode::Delegate: {
Index index;
CHECK_RESULT(ReadIndex(&index, "depth"));
CALLBACK(OnDelegateExpr, index);
CALLBACK(OnOpcodeIndex, index);
break;
}
case Opcode::Rethrow: {
Index depth;
CHECK_RESULT(ReadIndex(&depth, "catch depth"));
CALLBACK(OnRethrowExpr, depth);
CALLBACK(OnOpcodeIndex, depth);
break;
}
case Opcode::Throw: {
Index index;
CHECK_RESULT(ReadIndex(&index, "tag index"));
CALLBACK(OnThrowExpr, index);
CALLBACK(OnOpcodeIndex, index);
break;
}
case Opcode::I32Extend8S:
case Opcode::I32Extend16S:
case Opcode::I64Extend8S:
case Opcode::I64Extend16S:
case Opcode::I64Extend32S:
CALLBACK(OnUnaryExpr, opcode);
CALLBACK0(OnOpcodeBare);
break;
case Opcode::I32TruncSatF32S:
case Opcode::I32TruncSatF32U:
case Opcode::I32TruncSatF64S:
case Opcode::I32TruncSatF64U:
case Opcode::I64TruncSatF32S:
case Opcode::I64TruncSatF32U:
case Opcode::I64TruncSatF64S:
case Opcode::I64TruncSatF64U:
CALLBACK(OnConvertExpr, opcode);
CALLBACK0(OnOpcodeBare);
break;
case Opcode::MemoryAtomicNotify: {
Address alignment_log2;
Index memidx;
Address offset;
CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
"notify alignment", "notify memidx",
"notify offset"));
CALLBACK(OnAtomicNotifyExpr, opcode, memidx, alignment_log2, offset);
CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
break;
}
case Opcode::MemoryAtomicWait32:
case Opcode::MemoryAtomicWait64: {
Address alignment_log2;
Index memidx;
Address offset;
CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
"wait alignment", "wait memidx",
"wait offset"));
CALLBACK(OnAtomicWaitExpr, opcode, memidx, alignment_log2, offset);
CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
break;
}
case Opcode::AtomicFence: {
uint8_t consistency_model;
CHECK_RESULT(ReadU8(&consistency_model, "consistency model"));
ERROR_UNLESS(consistency_model == 0,
"atomic.fence consistency model must be 0");
CALLBACK(OnAtomicFenceExpr, consistency_model);
CALLBACK(OnOpcodeUint32, consistency_model);
break;
}
case Opcode::I32AtomicLoad8U:
case Opcode::I32AtomicLoad16U:
case Opcode::I64AtomicLoad8U:
case Opcode::I64AtomicLoad16U:
case Opcode::I64AtomicLoad32U:
case Opcode::I32AtomicLoad:
case Opcode::I64AtomicLoad: {
Address alignment_log2;
Index memidx;
Address offset;
CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
"load alignment", "load memidx",
"load offset"));
CALLBACK(OnAtomicLoadExpr, opcode, memidx, alignment_log2, offset);
CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
break;
}
case Opcode::I32AtomicStore8:
case Opcode::I32AtomicStore16:
case Opcode::I64AtomicStore8:
case Opcode::I64AtomicStore16:
case Opcode::I64AtomicStore32:
case Opcode::I32AtomicStore:
case Opcode::I64AtomicStore: {
Address alignment_log2;
Index memidx;
Address offset;
CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
"store alignment", "store memidx",
"store offset"));
CALLBACK(OnAtomicStoreExpr, opcode, memidx, alignment_log2, offset);
CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
break;
}
case Opcode::I32AtomicRmwAdd:
case Opcode::I64AtomicRmwAdd:
case Opcode::I32AtomicRmw8AddU:
case Opcode::I32AtomicRmw16AddU:
case Opcode::I64AtomicRmw8AddU:
case Opcode::I64AtomicRmw16AddU:
case Opcode::I64AtomicRmw32AddU:
case Opcode::I32AtomicRmwSub:
case Opcode::I64AtomicRmwSub:
case Opcode::I32AtomicRmw8SubU:
case Opcode::I32AtomicRmw16SubU:
case Opcode::I64AtomicRmw8SubU:
case Opcode::I64AtomicRmw16SubU:
case Opcode::I64AtomicRmw32SubU:
case Opcode::I32AtomicRmwAnd:
case Opcode::I64AtomicRmwAnd:
case Opcode::I32AtomicRmw8AndU:
case Opcode::I32AtomicRmw16AndU:
case Opcode::I64AtomicRmw8AndU:
case Opcode::I64AtomicRmw16AndU:
case Opcode::I64AtomicRmw32AndU:
case Opcode::I32AtomicRmwOr:
case Opcode::I64AtomicRmwOr:
case Opcode::I32AtomicRmw8OrU:
case Opcode::I32AtomicRmw16OrU:
case Opcode::I64AtomicRmw8OrU:
case Opcode::I64AtomicRmw16OrU:
case Opcode::I64AtomicRmw32OrU:
case Opcode::I32AtomicRmwXor:
case Opcode::I64AtomicRmwXor:
case Opcode::I32AtomicRmw8XorU:
case Opcode::I32AtomicRmw16XorU:
case Opcode::I64AtomicRmw8XorU:
case Opcode::I64AtomicRmw16XorU:
case Opcode::I64AtomicRmw32XorU:
case Opcode::I32AtomicRmwXchg:
case Opcode::I64AtomicRmwXchg:
case Opcode::I32AtomicRmw8XchgU:
case Opcode::I32AtomicRmw16XchgU:
case Opcode::I64AtomicRmw8XchgU:
case Opcode::I64AtomicRmw16XchgU:
case Opcode::I64AtomicRmw32XchgU: {
Address alignment_log2;
Index memidx;
Address offset;
CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
"memory alignment", "memory memidx",
"memory offset"));
CALLBACK(OnAtomicRmwExpr, opcode, memidx, alignment_log2, offset);
CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
break;
}
case Opcode::I32AtomicRmwCmpxchg:
case Opcode::I64AtomicRmwCmpxchg:
case Opcode::I32AtomicRmw8CmpxchgU:
case Opcode::I32AtomicRmw16CmpxchgU:
case Opcode::I64AtomicRmw8CmpxchgU:
case Opcode::I64AtomicRmw16CmpxchgU:
case Opcode::I64AtomicRmw32CmpxchgU: {
Address alignment_log2;
Index memidx;
Address offset;
CHECK_RESULT(ReadMemLocation(&alignment_log2, &memidx, &offset,
"memory alignment", "memory memidx",
"memory offset"));
CALLBACK(OnAtomicRmwCmpxchgExpr, opcode, memidx, alignment_log2,
offset);
CHECK_RESULT(CallbackMemLocation(&alignment_log2, &memidx, &offset));
break;
}
case Opcode::TableInit: {
Index segment;
CHECK_RESULT(ReadIndex(&segment, "elem segment index"));
Index table_index;
CHECK_RESULT(ReadIndex(&table_index, "reserved table index"));
CALLBACK(OnTableInitExpr, segment, table_index);
CALLBACK(OnOpcodeUint32Uint32, segment, table_index);
break;
}
case Opcode::MemoryInit: {
Index segment;
ERROR_IF(data_count_ == kInvalidIndex,
"memory.init requires data count section");
CHECK_RESULT(ReadIndex(&segment, "elem segment index"));
Index memidx = 0;
if (!options_.features.multi_memory_enabled()) {
uint8_t reserved;
CHECK_RESULT(ReadU8(&reserved, "reserved memory index"));
ERROR_UNLESS(reserved == 0, "reserved value must be 0");
} else {
CHECK_RESULT(ReadMemidx(&memidx, "memory.init memidx"));
}
CALLBACK(OnMemoryInitExpr, segment, memidx);
CALLBACK(OnOpcodeUint32Uint32, segment, memidx);
break;
}
case Opcode::DataDrop:
ERROR_IF(data_count_ == kInvalidIndex,
"data.drop requires data count section");
[[fallthrough]];
case Opcode::ElemDrop: {
Index segment;
CHECK_RESULT(ReadIndex(&segment, "segment index"));
if (opcode == Opcode::DataDrop) {
CALLBACK(OnDataDropExpr, segment);
} else {
CALLBACK(OnElemDropExpr, segment);
}
CALLBACK(OnOpcodeUint32, segment);
break;
}
case Opcode::MemoryFill: {
Index memidx = 0;
if (!options_.features.multi_memory_enabled()) {
uint8_t reserved;
CHECK_RESULT(ReadU8(&reserved, "memory.fill reserved"));
ERROR_UNLESS(reserved == 0, "memory.fill reserved value must be 0");
} else {
CHECK_RESULT(ReadMemidx(&memidx, "memory.fill memidx"));
}
CALLBACK(OnMemoryFillExpr, memidx);
CALLBACK(OnOpcodeUint32, memidx);
break;
}
case Opcode::MemoryCopy: {
Index srcmemidx = 0;
Index destmemidx = 0;
if (!options_.features.multi_memory_enabled()) {
uint8_t reserved;
CHECK_RESULT(ReadU8(&reserved, "reserved memory index"));
ERROR_UNLESS(reserved == 0, "reserved value must be 0");
CHECK_RESULT(ReadU8(&reserved, "reserved memory index"));
ERROR_UNLESS(reserved == 0, "reserved value must be 0");
} else {
CHECK_RESULT(ReadMemidx(&srcmemidx, "memory.copy srcmemidx"));
CHECK_RESULT(ReadMemidx(&destmemidx, "memory.copy destmemindex"));
}
CALLBACK(OnMemoryCopyExpr, srcmemidx, destmemidx);
CALLBACK(OnOpcodeUint32Uint32, srcmemidx, destmemidx);
break;
}
case Opcode::TableCopy: {
Index table_dst;
Index table_src;
CHECK_RESULT(ReadIndex(&table_dst, "reserved table index"));
CHECK_RESULT(ReadIndex(&table_src, "table src"));
CALLBACK(OnTableCopyExpr, table_dst, table_src);
CALLBACK(OnOpcodeUint32Uint32, table_dst, table_src);
break;
}
case Opcode::TableGet: {
Index table;
CHECK_RESULT(ReadIndex(&table, "table index"));
CALLBACK(OnTableGetExpr, table);
CALLBACK(OnOpcodeUint32, table);
break;
}
case Opcode::TableSet: {
Index table;
CHECK_RESULT(ReadIndex(&table, "table index"));
CALLBACK(OnTableSetExpr, table);
CALLBACK(OnOpcodeUint32, table);
break;
}
case Opcode::TableGrow: {
Index table;
CHECK_RESULT(ReadIndex(&table, "table index"));
CALLBACK(OnTableGrowExpr, table);
CALLBACK(OnOpcodeUint32, table);
break;
}
case Opcode::TableSize: {
Index table;
CHECK_RESULT(ReadIndex(&table, "table index"));
CALLBACK(OnTableSizeExpr, table);
CALLBACK(OnOpcodeUint32, table);
break;
}
case Opcode::TableFill: {
Index table;
CHECK_RESULT(ReadIndex(&table, "table index"));
CALLBACK(OnTableFillExpr, table);
CALLBACK(OnOpcodeUint32, table);
break;
}
case Opcode::RefFunc: {
Index func;
CHECK_RESULT(ReadIndex(&func, "func index"));
CALLBACK(OnRefFuncExpr, func);
CALLBACK(OnOpcodeUint32, func);
break;
}
case Opcode::RefNull: {
Type type;
CHECK_RESULT(ReadRefType(&type, "ref.null type"));
CALLBACK(OnRefNullExpr, type);
CALLBACK(OnOpcodeType, type);
break;
}
case Opcode::RefIsNull:
CALLBACK(OnRefIsNullExpr);
CALLBACK(OnOpcodeBare);
break;
case Opcode::CallRef:
CALLBACK(OnCallRefExpr);
CALLBACK(OnOpcodeBare);
break;
default:
return ReportUnexpectedOpcode(opcode);
}
}
return Result::Ok;
}
Result BinaryReader::ReadNameSection(Offset section_size) {
CALLBACK(BeginNamesSection, section_size);
Index i = 0;
uint32_t previous_subsection_type = 0;
while (state_.offset < read_end_) {
uint32_t name_type;
Offset subsection_size;
CHECK_RESULT(ReadU32Leb128(&name_type, "name type"));
if (i != 0) {
ERROR_UNLESS(name_type != previous_subsection_type,
"duplicate sub-section");
ERROR_UNLESS(name_type >= previous_subsection_type,
"out-of-order sub-section");
}
previous_subsection_type = name_type;
CHECK_RESULT(ReadOffset(&subsection_size, "subsection size"));
size_t subsection_end = state_.offset + subsection_size;
ERROR_UNLESS(subsection_end <= read_end_,
"invalid sub-section size: extends past end");
ReadEndRestoreGuard guard(this);
read_end_ = subsection_end;
NameSectionSubsection type = static_cast<NameSectionSubsection>(name_type);
if (type <= NameSectionSubsection::Last) {
CALLBACK(OnNameSubsection, i, type, subsection_size);
}
switch (type) {
case NameSectionSubsection::Module:
CALLBACK(OnModuleNameSubsection, i, name_type, subsection_size);
if (subsection_size) {
std::string_view name;
CHECK_RESULT(ReadStr(&name, "module name"));
CALLBACK(OnModuleName, name);
}
break;
case NameSectionSubsection::Function:
CALLBACK(OnFunctionNameSubsection, i, name_type, subsection_size);
if (subsection_size) {
Index num_names;
CHECK_RESULT(ReadCount(&num_names, "name count"));
CALLBACK(OnFunctionNamesCount, num_names);
Index last_function_index = kInvalidIndex;
for (Index j = 0; j < num_names; ++j) {
Index function_index;
std::string_view function_name;
CHECK_RESULT(ReadIndex(&function_index, "function index"));
ERROR_UNLESS(function_index != last_function_index,
"duplicate function name: %u", function_index);
ERROR_UNLESS(last_function_index == kInvalidIndex ||
function_index > last_function_index,
"function index out of order: %u", function_index);
last_function_index = function_index;
ERROR_UNLESS(function_index < NumTotalFuncs(),
"invalid function index: %" PRIindex, function_index);
CHECK_RESULT(ReadStr(&function_name, "function name"));
CALLBACK(OnFunctionName, function_index, function_name);
}
}
break;
case NameSectionSubsection::Local:
CALLBACK(OnLocalNameSubsection, i, name_type, subsection_size);
if (subsection_size) {
Index num_funcs;
CHECK_RESULT(ReadCount(&num_funcs, "function count"));
CALLBACK(OnLocalNameFunctionCount, num_funcs);
Index last_function_index = kInvalidIndex;
for (Index j = 0; j < num_funcs; ++j) {
Index function_index;
CHECK_RESULT(ReadIndex(&function_index, "function index"));
ERROR_UNLESS(function_index < NumTotalFuncs(),
"invalid function index: %u", function_index);
ERROR_UNLESS(last_function_index == kInvalidIndex ||
function_index > last_function_index,
"locals function index out of order: %u",
function_index);
last_function_index = function_index;
Index num_locals;
CHECK_RESULT(ReadCount(&num_locals, "local count"));
CALLBACK(OnLocalNameLocalCount, function_index, num_locals);
Index last_local_index = kInvalidIndex;
for (Index k = 0; k < num_locals; ++k) {
Index local_index;
std::string_view local_name;
CHECK_RESULT(ReadIndex(&local_index, "named index"));
ERROR_UNLESS(local_index != last_local_index,
"duplicate local index: %u", local_index);
ERROR_UNLESS(last_local_index == kInvalidIndex ||
local_index > last_local_index,
"local index out of order: %u", local_index);
last_local_index = local_index;
CHECK_RESULT(ReadStr(&local_name, "name"));
CALLBACK(OnLocalName, function_index, local_index, local_name);
}
}
}
break;
case NameSectionSubsection::Label:
// TODO(sbc): Implement label names. These are slightly more complicated
// since they refer to offsets in the code section / instruction stream.
state_.offset = subsection_end;
break;
case NameSectionSubsection::Type:
case NameSectionSubsection::Table:
case NameSectionSubsection::Memory:
case NameSectionSubsection::Global:
case NameSectionSubsection::ElemSegment:
case NameSectionSubsection::DataSegment:
case NameSectionSubsection::Tag:
if (subsection_size) {
Index num_names;
CHECK_RESULT(ReadCount(&num_names, "name count"));
CALLBACK(OnNameCount, num_names);
for (Index j = 0; j < num_names; ++j) {
Index index;
std::string_view name;
CHECK_RESULT(ReadIndex(&index, "index"));
CHECK_RESULT(ReadStr(&name, "name"));
CALLBACK(OnNameEntry, type, index, name);
}
}
state_.offset = subsection_end;
break;
default:
// Unknown subsection, skip it.
state_.offset = subsection_end;
break;
}
++i;
ERROR_UNLESS(state_.offset == subsection_end,
"unfinished sub-section (expected end: 0x%" PRIzx ")",
subsection_end);
}
CALLBACK0(EndNamesSection);
return Result::Ok;
}
Result BinaryReader::ReadRelocSection(Offset section_size) {
CALLBACK(BeginRelocSection, section_size);
uint32_t section_index;
CHECK_RESULT(ReadU32Leb128(&section_index, "section index"));
Index num_relocs;
CHECK_RESULT(ReadCount(&num_relocs, "relocation count"));
CALLBACK(OnRelocCount, num_relocs, section_index);
for (Index i = 0; i < num_relocs; ++i) {
Offset offset;
Index index;
uint32_t reloc_type, addend = 0;
CHECK_RESULT(ReadU32Leb128(&reloc_type, "relocation type"));
CHECK_RESULT(ReadOffset(&offset, "offset"));
CHECK_RESULT(ReadIndex(&index, "index"));
RelocType type = static_cast<RelocType>(reloc_type);
switch (type) {
case RelocType::MemoryAddressLEB:
case RelocType::MemoryAddressLEB64:
case RelocType::MemoryAddressSLEB:
case RelocType::MemoryAddressSLEB64:
case RelocType::MemoryAddressRelSLEB:
case RelocType::MemoryAddressRelSLEB64:
case RelocType::MemoryAddressI32:
case RelocType::MemoryAddressI64:
case RelocType::FunctionOffsetI32:
case RelocType::SectionOffsetI32:
case RelocType::MemoryAddressTLSSLEB:
case RelocType::MemoryAddressTLSI32:
CHECK_RESULT(ReadS32Leb128(&addend, "addend"));
break;
case RelocType::FuncIndexLEB:
case RelocType::TableIndexSLEB:
case RelocType::TableIndexSLEB64:
case RelocType::TableIndexI32:
case RelocType::TableIndexI64:
case RelocType::TypeIndexLEB:
case RelocType::GlobalIndexLEB:
case RelocType::GlobalIndexI32:
case RelocType::TagIndexLEB:
case RelocType::TableIndexRelSLEB:
case RelocType::TableNumberLEB:
break;
default:
PrintError("unknown reloc type: %s", GetRelocTypeName(type));
return Result::Error;
}
CALLBACK(OnReloc, type, offset, index, addend);
}
CALLBACK0(EndRelocSection);
return Result::Ok;
}
Result BinaryReader::ReadDylink0Section(Offset section_size) {
CALLBACK(BeginDylinkSection, section_size);
while (state_.offset < read_end_) {
uint32_t dylink_type;
Offset subsection_size;
CHECK_RESULT(ReadU32Leb128(&dylink_type, "type"));
CHECK_RESULT(ReadOffset(&subsection_size, "subsection size"));
size_t subsection_end = state_.offset + subsection_size;
ERROR_UNLESS(subsection_end <= read_end_,
"invalid sub-section size: extends past end");
ReadEndRestoreGuard guard(this);
read_end_ = subsection_end;
uint32_t count;
switch (static_cast<DylinkEntryType>(dylink_type)) {
case DylinkEntryType::MemInfo: {
uint32_t mem_size;
uint32_t mem_align;
uint32_t table_size;
uint32_t table_align;
CHECK_RESULT(ReadU32Leb128(&mem_size, "mem_size"));
CHECK_RESULT(ReadU32Leb128(&mem_align, "mem_align"));
CHECK_RESULT(ReadU32Leb128(&table_size, "table_size"));
CHECK_RESULT(ReadU32Leb128(&table_align, "table_align"));
CALLBACK(OnDylinkInfo, mem_size, mem_align, table_size, table_align);
break;
}
case DylinkEntryType::Needed:
CHECK_RESULT(ReadU32Leb128(&count, "needed_dynlibs"));
CALLBACK(OnDylinkNeededCount, count);
while (count--) {
std::string_view so_name;
CHECK_RESULT(ReadStr(&so_name, "dylib so_name"));
CALLBACK(OnDylinkNeeded, so_name);
}
break;
case DylinkEntryType::ImportInfo:
CHECK_RESULT(ReadU32Leb128(&count, "count"));
CALLBACK(OnDylinkImportCount, count);
for (Index i = 0; i < count; ++i) {
uint32_t flags = 0;
std::string_view module;
std::string_view field;
CHECK_RESULT(ReadStr(&module, "module"));
CHECK_RESULT(ReadStr(&field, "field"));
CHECK_RESULT(ReadU32Leb128(&flags, "flags"));
CALLBACK(OnDylinkImport, module, field, flags);
}
break;
case DylinkEntryType::ExportInfo:
CHECK_RESULT(ReadU32Leb128(&count, "count"));
CALLBACK(OnDylinkExportCount, count);
for (Index i = 0; i < count; ++i) {
uint32_t flags = 0;
std::string_view name;
CHECK_RESULT(ReadStr(&name, "name"));
CHECK_RESULT(ReadU32Leb128(&flags, "flags"));
CALLBACK(OnDylinkExport, name, flags);
}
break;
default:
// Unknown subsection, skip it.
state_.offset = subsection_end;
break;
}
ERROR_UNLESS(state_.offset == subsection_end,
"unfinished sub-section (expected end: 0x%" PRIzx ")",
subsection_end);
}
CALLBACK0(EndDylinkSection);
return Result::Ok;
}
Result BinaryReader::ReadDylinkSection(Offset section_size) {
CALLBACK(BeginDylinkSection, section_size);
uint32_t mem_size;
uint32_t mem_align;
uint32_t table_size;
uint32_t table_align;
CHECK_RESULT(ReadU32Leb128(&mem_size, "mem_size"));
CHECK_RESULT(ReadU32Leb128(&mem_align, "mem_align"));
CHECK_RESULT(ReadU32Leb128(&table_size, "table_size"));
CHECK_RESULT(ReadU32Leb128(&table_align, "table_align"));
CALLBACK(OnDylinkInfo, mem_size, mem_align, table_size, table_align);
uint32_t count;
CHECK_RESULT(ReadU32Leb128(&count, "needed_dynlibs"));
CALLBACK(OnDylinkNeededCount, count);
while (count--) {
std::string_view so_name;
CHECK_RESULT(ReadStr(&so_name, "dylib so_name"));
CALLBACK(OnDylinkNeeded, so_name);
}
CALLBACK0(EndDylinkSection);
return Result::Ok;
}
Result BinaryReader::ReadTargetFeaturesSections(Offset section_size) {
CALLBACK(BeginTargetFeaturesSection, section_size);
uint32_t count;
CHECK_RESULT(ReadU32Leb128(&count, "sym count"));
CALLBACK(OnFeatureCount, count);
while (count--) {
uint8_t prefix;
std::string_view name;
CHECK_RESULT(ReadU8(&prefix, "prefix"));
CHECK_RESULT(ReadStr(&name, "feature name"));
CALLBACK(OnFeature, prefix, name);
}
CALLBACK0(EndTargetFeaturesSection);
return Result::Ok;
}
Result BinaryReader::ReadLinkingSection(Offset section_size) {
CALLBACK(BeginLinkingSection, section_size);
uint32_t version;
CHECK_RESULT(ReadU32Leb128(&version, "version"));
ERROR_UNLESS(version == 2, "invalid linking metadata version: %u", version);
while (state_.offset < read_end_) {
uint32_t linking_type;
Offset subsection_size;
CHECK_RESULT(ReadU32Leb128(&linking_type, "type"));
CHECK_RESULT(ReadOffset(&subsection_size, "subsection size"));
size_t subsection_end = state_.offset + subsection_size;
ERROR_UNLESS(subsection_end <= read_end_,
"invalid sub-section size: extends past end");
ReadEndRestoreGuard guard(this);
read_end_ = subsection_end;
uint32_t count;
switch (static_cast<LinkingEntryType>(linking_type)) {
case LinkingEntryType::SymbolTable:
CHECK_RESULT(ReadU32Leb128(&count, "sym count"));
CALLBACK(OnSymbolCount, count);
for (Index i = 0; i < count; ++i) {
std::string_view name;
uint32_t flags = 0;
uint32_t kind = 0;
CHECK_RESULT(ReadU32Leb128(&kind, "sym type"));
CHECK_RESULT(ReadU32Leb128(&flags, "sym flags"));
SymbolType sym_type = static_cast<SymbolType>(kind);
switch (sym_type) {
case SymbolType::Function:
case SymbolType::Global:
case SymbolType::Tag:
case SymbolType::Table: {
uint32_t index = 0;
CHECK_RESULT(ReadU32Leb128(&index, "index"));
if ((flags & WABT_SYMBOL_FLAG_UNDEFINED) == 0 ||
(flags & WABT_SYMBOL_FLAG_EXPLICIT_NAME) != 0)
CHECK_RESULT(ReadStr(&name, "symbol name"));
switch (sym_type) {
case SymbolType::Function:
CALLBACK(OnFunctionSymbol, i, flags, name, index);
break;
case SymbolType::Global:
CALLBACK(OnGlobalSymbol, i, flags, name, index);
break;
case SymbolType::Tag:
CALLBACK(OnTagSymbol, i, flags, name, index);
break;
case SymbolType::Table:
CALLBACK(OnTableSymbol, i, flags, name, index);
break;
default:
WABT_UNREACHABLE;
}
break;
}
case SymbolType::Data: {
uint32_t segment = 0;
uint32_t offset = 0;
uint32_t size = 0;
CHECK_RESULT(ReadStr(&name, "symbol name"));
if ((flags & WABT_SYMBOL_FLAG_UNDEFINED) == 0) {
CHECK_RESULT(ReadU32Leb128(&segment, "segment"));
CHECK_RESULT(ReadU32Leb128(&offset, "offset"));
CHECK_RESULT(ReadU32Leb128(&size, "size"));
}
CALLBACK(OnDataSymbol, i, flags, name, segment, offset, size);
break;
}
case SymbolType::Section: {
uint32_t index = 0;
CHECK_RESULT(ReadU32Leb128(&index, "index"));
CALLBACK(OnSectionSymbol, i, flags, index);
break;
}
}
}
break;
case LinkingEntryType::SegmentInfo:
CHECK_RESULT(ReadU32Leb128(&count, "info count"));
CALLBACK(OnSegmentInfoCount, count);
for (Index i = 0; i < count; i++) {
std::string_view name;
Address alignment_log2;
uint32_t flags;
CHECK_RESULT(ReadStr(&name, "segment name"));
CHECK_RESULT(ReadAlignment(&alignment_log2, "segment alignment"));
CHECK_RESULT(ReadU32Leb128(&flags, "segment flags"));
CALLBACK(OnSegmentInfo, i, name, alignment_log2, flags);
}
break;
case LinkingEntryType::InitFunctions:
CHECK_RESULT(ReadU32Leb128(&count, "info count"));
CALLBACK(OnInitFunctionCount, count);
while (count--) {
uint32_t priority;
uint32_t symbol;
CHECK_RESULT(ReadU32Leb128(&priority, "priority"));
CHECK_RESULT(ReadU32Leb128(&symbol, "symbol index"));
CALLBACK(OnInitFunction, priority, symbol);
}
break;
case LinkingEntryType::ComdatInfo:
CHECK_RESULT(ReadU32Leb128(&count, "count"));
CALLBACK(OnComdatCount, count);
while (count--) {
uint32_t flags;
uint32_t entry_count;
std::string_view name;
CHECK_RESULT(ReadStr(&name, "comdat name"));
CHECK_RESULT(ReadU32Leb128(&flags, "flags"));
CHECK_RESULT(ReadU32Leb128(&entry_count, "entry count"));
CALLBACK(OnComdatBegin, name, flags, entry_count);
while (entry_count--) {
uint32_t kind;
uint32_t index;
CHECK_RESULT(ReadU32Leb128(&kind, "kind"));
CHECK_RESULT(ReadU32Leb128(&index, "index"));
ComdatType comdat_type = static_cast<ComdatType>(kind);
CALLBACK(OnComdatEntry, comdat_type, index);
}
}
break;
default:
// Unknown subsection, skip it.
state_.offset = subsection_end;
break;
}
ERROR_UNLESS(state_.offset == subsection_end,
"unfinished sub-section (expected end: 0x%" PRIzx ")",
subsection_end);
}
CALLBACK0(EndLinkingSection);
return Result::Ok;
}
Result BinaryReader::ReadTagType(Index* out_sig_index) {
uint8_t attribute;
CHECK_RESULT(ReadU8(&attribute, "tag attribute"));
ERROR_UNLESS(attribute == 0, "tag attribute must be 0");
CHECK_RESULT(ReadIndex(out_sig_index, "tag signature index"));
return Result::Ok;
}
Result BinaryReader::ReadTagSection(Offset section_size) {
CALLBACK(BeginTagSection, section_size);
Index num_tags;
CHECK_RESULT(ReadCount(&num_tags, "tag count"));
CALLBACK(OnTagCount, num_tags);
for (Index i = 0; i < num_tags; ++i) {
Index tag_index = num_tag_imports_ + i;
Index sig_index;
CHECK_RESULT(ReadTagType(&sig_index));
CALLBACK(OnTagType, tag_index, sig_index);
}
CALLBACK(EndTagSection);
return Result::Ok;
}
Result BinaryReader::ReadCodeMetadataSection(std::string_view name,
Offset section_size) {
CALLBACK(BeginCodeMetadataSection, name, section_size);
Index num_functions;
CHECK_RESULT(ReadCount(&num_functions, "function count"));
CALLBACK(OnCodeMetadataFuncCount, num_functions);
Index last_function_index = kInvalidIndex;
for (Index i = 0; i < num_functions; ++i) {
Index function_index;
CHECK_RESULT(ReadCount(&function_index, "function index"));
ERROR_UNLESS(function_index >= num_func_imports_,
"function import can't have metadata (got %" PRIindex ")",
function_index);
ERROR_UNLESS(function_index < NumTotalFuncs(),
"invalid function index: %" PRIindex, function_index);
ERROR_UNLESS(function_index != last_function_index,
"duplicate function index: %" PRIindex, function_index);
ERROR_UNLESS(last_function_index == kInvalidIndex ||
function_index > last_function_index,
"function index out of order: %" PRIindex, function_index);
last_function_index = function_index;
Index num_metadata;
CHECK_RESULT(ReadCount(&num_metadata, "metadata instances count"));
CALLBACK(OnCodeMetadataCount, function_index, num_metadata);
Offset last_code_offset = kInvalidOffset;
for (Index j = 0; j < num_metadata; ++j) {
Offset code_offset;
CHECK_RESULT(ReadOffset(&code_offset, "code offset"));
ERROR_UNLESS(code_offset != last_code_offset,
"duplicate code offset: %" PRIzx, code_offset);
ERROR_UNLESS(
last_code_offset == kInvalidOffset || code_offset > last_code_offset,
"code offset out of order: %" PRIzx, code_offset);
last_code_offset = code_offset;
Address data_size;
const void* data;
CHECK_RESULT(ReadBytes(&data, &data_size, "instance data"));
CALLBACK(OnCodeMetadata, code_offset, data, data_size);
}
}
CALLBACK(EndCodeMetadataSection);
return Result::Ok;
}
Result BinaryReader::ReadCustomSection(Index section_index,
Offset section_size) {
std::string_view section_name;
CHECK_RESULT(ReadStr(&section_name, "section name"));
CALLBACK(BeginCustomSection, section_index, section_size, section_name);
ValueRestoreGuard<bool, &BinaryReader::reading_custom_section_> guard(this);
reading_custom_section_ = true;
if (options_.read_debug_names && section_name == WABT_BINARY_SECTION_NAME) {
CHECK_RESULT(ReadNameSection(section_size));
did_read_names_section_ = true;
} else if (section_name == WABT_BINARY_SECTION_DYLINK0) {
CHECK_RESULT(ReadDylink0Section(section_size));
} else if (section_name == WABT_BINARY_SECTION_DYLINK) {
CHECK_RESULT(ReadDylinkSection(section_size));
} else if (section_name.rfind(WABT_BINARY_SECTION_RELOC, 0) == 0) {
// Reloc sections always begin with "reloc."
CHECK_RESULT(ReadRelocSection(section_size));
} else if (section_name == WABT_BINARY_SECTION_TARGET_FEATURES) {
CHECK_RESULT(ReadTargetFeaturesSections(section_size));
} else if (section_name == WABT_BINARY_SECTION_LINKING) {
CHECK_RESULT(ReadLinkingSection(section_size));
} else if (options_.features.code_metadata_enabled() &&
section_name.find(WABT_BINARY_SECTION_CODE_METADATA) == 0) {
std::string_view metadata_name = section_name;
metadata_name.remove_prefix(sizeof(WABT_BINARY_SECTION_CODE_METADATA) - 1);
CHECK_RESULT(ReadCodeMetadataSection(metadata_name, section_size));
} else {
// This is an unknown custom section, skip it.
state_.offset = read_end_;
}
CALLBACK0(EndCustomSection);
return Result::Ok;
}
Result BinaryReader::ReadTypeSection(Offset section_size) {
CALLBACK(BeginTypeSection, section_size);
Index num_signatures;
CHECK_RESULT(ReadCount(&num_signatures, "type count"));
CALLBACK(OnTypeCount, num_signatures);
for (Index i = 0; i < num_signatures; ++i) {
Type form;
if (options_.features.gc_enabled()) {
CHECK_RESULT(ReadType(&form, "type form"));
} else {
uint8_t type;
CHECK_RESULT(ReadU8(&type, "type form"));
ERROR_UNLESS(type == 0x60, "unexpected type form (got %#x)", type);
form = Type::Func;
}
switch (form) {
case Type::Func: {
Index num_params;
CHECK_RESULT(ReadCount(&num_params, "function param count"));
param_types_.resize(num_params);
for (Index j = 0; j < num_params; ++j) {
Type param_type;
CHECK_RESULT(ReadType(&param_type, "function param type"));
ERROR_UNLESS(IsConcreteType(param_type),
"expected valid param type (got " PRItypecode ")",
WABT_PRINTF_TYPE_CODE(param_type));
param_types_[j] = param_type;
}
Index num_results;
CHECK_RESULT(ReadCount(&num_results, "function result count"));
result_types_.resize(num_results);
for (Index j = 0; j < num_results; ++j) {
Type result_type;
CHECK_RESULT(ReadType(&result_type, "function result type"));
ERROR_UNLESS(IsConcreteType(result_type),
"expected valid result type (got " PRItypecode ")",
WABT_PRINTF_TYPE_CODE(result_type));
result_types_[j] = result_type;
}
Type* param_types = num_params ? param_types_.data() : nullptr;
Type* result_types = num_results ? result_types_.data() : nullptr;
CALLBACK(OnFuncType, i, num_params, param_types, num_results,
result_types);
break;
}
case Type::Struct: {
ERROR_UNLESS(options_.features.gc_enabled(),
"invalid type form: struct not allowed");
Index num_fields;
CHECK_RESULT(ReadCount(&num_fields, "field count"));
fields_.resize(num_fields);
for (Index j = 0; j < num_fields; ++j) {
CHECK_RESULT(ReadField(&fields_[j]));
}
CALLBACK(OnStructType, i, fields_.size(), fields_.data());
break;
}
case Type::Array: {
ERROR_UNLESS(options_.features.gc_enabled(),
"invalid type form: array not allowed");
TypeMut field;
CHECK_RESULT(ReadField(&field));
CALLBACK(OnArrayType, i, field);
break;
};
default:
PrintError("unexpected type form (got " PRItypecode ")",
WABT_PRINTF_TYPE_CODE(form));
return Result::Error;
}
}
CALLBACK0(EndTypeSection);
return Result::Ok;
}
Result BinaryReader::ReadImportSection(Offset section_size) {
CALLBACK(BeginImportSection, section_size);
Index num_imports;
CHECK_RESULT(ReadCount(&num_imports, "import count"));
CALLBACK(OnImportCount, num_imports);
for (Index i = 0; i < num_imports; ++i) {
std::string_view module_name;
CHECK_RESULT(ReadStr(&module_name, "import module name"));
std::string_view field_name;
CHECK_RESULT(ReadStr(&field_name, "import field name"));
uint8_t kind;
CHECK_RESULT(ReadU8(&kind, "import kind"));
CALLBACK(OnImport, i, static_cast<ExternalKind>(kind), module_name,
field_name);
switch (static_cast<ExternalKind>(kind)) {
case ExternalKind::Func: {
Index sig_index;
CHECK_RESULT(ReadIndex(&sig_index, "import signature index"));
CALLBACK(OnImportFunc, i, module_name, field_name, num_func_imports_,
sig_index);
num_func_imports_++;
break;
}
case ExternalKind::Table: {
Type elem_type;
Limits elem_limits;
CHECK_RESULT(ReadTable(&elem_type, &elem_limits));
CALLBACK(OnImportTable, i, module_name, field_name, num_table_imports_,
elem_type, &elem_limits);
num_table_imports_++;
break;
}
case ExternalKind::Memory: {
Limits page_limits;
CHECK_RESULT(ReadMemory(&page_limits));
CALLBACK(OnImportMemory, i, module_name, field_name,
num_memory_imports_, &page_limits);
num_memory_imports_++;
break;
}
case ExternalKind::Global: {
Type type;
bool mutable_;
CHECK_RESULT(ReadGlobalHeader(&type, &mutable_));
CALLBACK(OnImportGlobal, i, module_name, field_name,
num_global_imports_, type, mutable_);
num_global_imports_++;
break;
}
case ExternalKind::Tag: {
ERROR_UNLESS(options_.features.exceptions_enabled(),
"invalid import tag kind: exceptions not allowed");
Index sig_index;
CHECK_RESULT(ReadTagType(&sig_index));
CALLBACK(OnImportTag, i, module_name, field_name, num_tag_imports_,
sig_index);
num_tag_imports_++;
break;
}
default:
PrintError("malformed import kind: %d", kind);
return Result::Error;
}
}
CALLBACK0(EndImportSection);
return Result::Ok;
}
Result BinaryReader::ReadFunctionSection(Offset section_size) {
CALLBACK(BeginFunctionSection, section_size);
CHECK_RESULT(
ReadCount(&num_function_signatures_, "function signature count"));
CALLBACK(OnFunctionCount, num_function_signatures_);
for (Index i = 0; i < num_function_signatures_; ++i) {
Index func_index = num_func_imports_ + i;
Index sig_index;
CHECK_RESULT(ReadIndex(&sig_index, "function signature index"));
CALLBACK(OnFunction, func_index, sig_index);
}
CALLBACK0(EndFunctionSection);
return Result::Ok;
}
Result BinaryReader::ReadTableSection(Offset section_size) {
CALLBACK(BeginTableSection, section_size);
Index num_tables;
CHECK_RESULT(ReadCount(&num_tables, "table count"));
CALLBACK(OnTableCount, num_tables);
for (Index i = 0; i < num_tables; ++i) {
Index table_index = num_table_imports_ + i;
Type elem_type;
Limits elem_limits;
CHECK_RESULT(ReadTable(&elem_type, &elem_limits));
CALLBACK(OnTable, table_index, elem_type, &elem_limits);
}
CALLBACK0(EndTableSection);
return Result::Ok;
}
Result BinaryReader::ReadMemorySection(Offset section_size) {
CALLBACK(BeginMemorySection, section_size);
Index num_memories;
CHECK_RESULT(ReadCount(&num_memories, "memory count"));
CALLBACK(OnMemoryCount, num_memories);
for (Index i = 0; i < num_memories; ++i) {
Index memory_index = num_memory_imports_ + i;
Limits page_limits;
CHECK_RESULT(ReadMemory(&page_limits));
CALLBACK(OnMemory, memory_index, &page_limits);
}
CALLBACK0(EndMemorySection);
return Result::Ok;
}
Result BinaryReader::ReadGlobalSection(Offset section_size) {
CALLBACK(BeginGlobalSection, section_size);
Index num_globals;
CHECK_RESULT(ReadCount(&num_globals, "global count"));
CALLBACK(OnGlobalCount, num_globals);
for (Index i = 0; i < num_globals; ++i) {
Index global_index = num_global_imports_ + i;
Type global_type;
bool mutable_;
CHECK_RESULT(ReadGlobalHeader(&global_type, &mutable_));
CALLBACK(BeginGlobal, global_index, global_type, mutable_);
CALLBACK(BeginGlobalInitExpr, global_index);
CHECK_RESULT(ReadInitExpr(global_index));
CALLBACK(EndGlobalInitExpr, global_index);
CALLBACK(EndGlobal, global_index);
}
CALLBACK0(EndGlobalSection);
return Result::Ok;
}
Result BinaryReader::ReadExportSection(Offset section_size) {
CALLBACK(BeginExportSection, section_size);
Index num_exports;
CHECK_RESULT(ReadCount(&num_exports, "export count"));
CALLBACK(OnExportCount, num_exports);
for (Index i = 0; i < num_exports; ++i) {
std::string_view name;
CHECK_RESULT(ReadStr(&name, "export item name"));
ExternalKind kind;
CHECK_RESULT(ReadExternalKind(&kind, "export kind"));
Index item_index;
CHECK_RESULT(ReadIndex(&item_index, "export item index"));
if (kind == ExternalKind::Tag) {
ERROR_UNLESS(options_.features.exceptions_enabled(),
"invalid export tag kind: exceptions not allowed");
}
CALLBACK(OnExport, i, static_cast<ExternalKind>(kind), item_index, name);
}
CALLBACK0(EndExportSection);
return Result::Ok;
}
Result BinaryReader::ReadStartSection(Offset section_size) {
CALLBACK(BeginStartSection, section_size);
Index func_index;
CHECK_RESULT(ReadIndex(&func_index, "start function index"));
CALLBACK(OnStartFunction, func_index);
CALLBACK0(EndStartSection);
return Result::Ok;
}
Result BinaryReader::ReadElemSection(Offset section_size) {
CALLBACK(BeginElemSection, section_size);
Index num_elem_segments;
CHECK_RESULT(ReadCount(&num_elem_segments, "elem segment count"));
CALLBACK(OnElemSegmentCount, num_elem_segments);
for (Index i = 0; i < num_elem_segments; ++i) {
uint32_t flags;
CHECK_RESULT(ReadU32Leb128(&flags, "elem segment flags"));
ERROR_IF(flags > SegFlagMax, "invalid elem segment flags: %#x", flags);
Index table_index(0);
if ((flags & (SegPassive | SegExplicitIndex)) == SegExplicitIndex) {
CHECK_RESULT(ReadIndex(&table_index, "elem segment table index"));
}
Type elem_type = Type::FuncRef;
CALLBACK(BeginElemSegment, i, table_index, flags);
if (!(flags & SegPassive)) {
CALLBACK(BeginElemSegmentInitExpr, i);
CHECK_RESULT(ReadInitExpr(i));
CALLBACK(EndElemSegmentInitExpr, i);
}
// For backwards compat we support not declaring the element kind.
if (flags & (SegPassive | SegExplicitIndex)) {
if (flags & SegUseElemExprs) {
CHECK_RESULT(ReadRefType(&elem_type, "table elem type"));
} else {
ExternalKind kind;
CHECK_RESULT(ReadExternalKind(&kind, "export kind"));
ERROR_UNLESS(kind == ExternalKind::Func,
"segment elem type must be func (%s)",
elem_type.GetName().c_str());
elem_type = Type::FuncRef;
}
}
CALLBACK(OnElemSegmentElemType, i, elem_type);
Index num_elem_exprs;
CHECK_RESULT(ReadCount(&num_elem_exprs, "elem count"));
CALLBACK(OnElemSegmentElemExprCount, i, num_elem_exprs);
for (Index j = 0; j < num_elem_exprs; ++j) {
if (flags & SegUseElemExprs) {
Opcode opcode;
CHECK_RESULT(ReadOpcode(&opcode, "elem expr opcode"));
if (opcode == Opcode::RefNull) {
Type type;
CHECK_RESULT(ReadRefType(&type, "elem expr ref.null type"));
CALLBACK(OnElemSegmentElemExpr_RefNull, i, type);
} else if (opcode == Opcode::RefFunc) {
Index func_index;
CHECK_RESULT(ReadIndex(&func_index, "elem expr func index"));
CALLBACK(OnElemSegmentElemExpr_RefFunc, i, func_index);
} else {
PrintError(
"expected ref.null or ref.func in passive element segment");
}
CHECK_RESULT(ReadOpcode(&opcode, "opcode"));
ERROR_UNLESS(opcode == Opcode::End,
"expected END opcode after element expression");
} else {
Index func_index;
CHECK_RESULT(ReadIndex(&func_index, "elem expr func index"));
CALLBACK(OnElemSegmentElemExpr_RefFunc, i, func_index);
}
}
CALLBACK(EndElemSegment, i);
}
CALLBACK0(EndElemSection);
return Result::Ok;
}
Result BinaryReader::ReadCodeSection(Offset section_size) {
CALLBACK(BeginCodeSection, section_size);
CHECK_RESULT(ReadCount(&num_function_bodies_, "function body count"));
ERROR_UNLESS(num_function_signatures_ == num_function_bodies_,
"function signature count != function body count");
CALLBACK(OnFunctionBodyCount, num_function_bodies_);
for (Index i = 0; i < num_function_bodies_; ++i) {
Index func_index = num_func_imports_ + i;
Offset func_offset = state_.offset;
state_.offset = func_offset;
uint32_t body_size;
CHECK_RESULT(ReadU32Leb128(&body_size, "function body size"));
Offset body_start_offset = state_.offset;
Offset end_offset = body_start_offset + body_size;
CALLBACK(BeginFunctionBody, func_index, body_size);
uint64_t total_locals = 0;
Index num_local_decls;
CHECK_RESULT(ReadCount(&num_local_decls, "local declaration count"));
CALLBACK(OnLocalDeclCount, num_local_decls);
for (Index k = 0; k < num_local_decls; ++k) {
Index num_local_types;
CHECK_RESULT(ReadIndex(&num_local_types, "local type count"));
total_locals += num_local_types;
ERROR_UNLESS(total_locals <= UINT32_MAX, "local count must be <= 0x%x",
UINT32_MAX);
Type local_type;
CHECK_RESULT(ReadType(&local_type, "local type"));
ERROR_UNLESS(IsConcreteType(local_type), "expected valid local type");
CALLBACK(OnLocalDecl, k, num_local_types, local_type);
}
if (options_.skip_function_bodies) {
state_.offset = end_offset;
} else {
CHECK_RESULT(ReadFunctionBody(end_offset));
}
CALLBACK(EndFunctionBody, func_index);
}
CALLBACK0(EndCodeSection);
return Result::Ok;
}
Result BinaryReader::ReadDataSection(Offset section_size) {
CALLBACK(BeginDataSection, section_size);
Index num_data_segments;
CHECK_RESULT(ReadCount(&num_data_segments, "data segment count"));
CALLBACK(OnDataSegmentCount, num_data_segments);
// If the DataCount section is not present, then data_count_ will be invalid.
ERROR_UNLESS(data_count_ == kInvalidIndex || data_count_ == num_data_segments,
"data segment count does not equal count in DataCount section");
for (Index i = 0; i < num_data_segments; ++i) {
uint32_t flags;
CHECK_RESULT(ReadU32Leb128(&flags, "data segment flags"));
ERROR_IF(flags != 0 && !options_.features.bulk_memory_enabled(),
"invalid memory index %d: bulk memory not allowed", flags);
ERROR_IF(flags > SegFlagMax, "invalid data segment flags: %#x", flags);
Index memory_index(0);
if (flags & SegExplicitIndex) {
CHECK_RESULT(ReadIndex(&memory_index, "data segment memory index"));
}
CALLBACK(BeginDataSegment, i, memory_index, flags);
if (!(flags & SegPassive)) {
ERROR_UNLESS(memories.size() > 0, "no memory to copy data to");
CALLBACK(BeginDataSegmentInitExpr, i);
CHECK_RESULT(ReadInitExpr(i));
CALLBACK(EndDataSegmentInitExpr, i);
}
Address data_size;
const void* data;
CHECK_RESULT(ReadBytes(&data, &data_size, "data segment data"));
CALLBACK(OnDataSegmentData, i, data, data_size);
CALLBACK(EndDataSegment, i);
}
CALLBACK0(EndDataSection);
return Result::Ok;
}
Result BinaryReader::ReadDataCountSection(Offset section_size) {
CALLBACK(BeginDataCountSection, section_size);
Index data_count;
CHECK_RESULT(ReadIndex(&data_count, "data count"));
CALLBACK(OnDataCount, data_count);
CALLBACK0(EndDataCountSection);
data_count_ = data_count;
return Result::Ok;
}
Result BinaryReader::ReadSections(const ReadSectionsOptions& options) {
Result result = Result::Ok;
Index section_index = 0;
bool seen_section_code[static_cast<int>(BinarySection::Last) + 1] = {false};
for (; state_.offset < state_.size; ++section_index) {
uint8_t section_code;
Offset section_size;
CHECK_RESULT(ReadU8(&section_code, "section code"));
CHECK_RESULT(ReadOffset(&section_size, "section size"));
ReadEndRestoreGuard guard(this);
read_end_ = state_.offset + section_size;
if (section_code >= kBinarySectionCount) {
PrintError("invalid section code: %u", section_code);
if (options.stop_on_first_error) {
return Result::Error;
}
// If we don't have to stop on first error, continue reading
// sections, because although we could not understand the
// current section, we can continue and correctly parse
// subsequent sections, so we can give back as much information
// as we can understand.
result = Result::Error;
state_.offset = read_end_;
continue;
}
BinarySection section = static_cast<BinarySection>(section_code);
if (section != BinarySection::Custom) {
if (seen_section_code[section_code]) {
PrintError("multiple %s sections", GetSectionName(section));
return Result::Error;
}
seen_section_code[section_code] = true;
}
ERROR_UNLESS(read_end_ <= state_.size,
"invalid section size: extends past end");
ERROR_UNLESS(
last_known_section_ == BinarySection::Invalid ||
section == BinarySection::Custom ||
GetSectionOrder(section) > GetSectionOrder(last_known_section_),
"section %s out of order", GetSectionName(section));
ERROR_UNLESS(!did_read_names_section_ || section == BinarySection::Custom,
"%s section can not occur after Name section",
GetSectionName(section));
CALLBACK(BeginSection, section_index, section, section_size);
bool stop_on_first_error = options_.stop_on_first_error;
Result section_result = Result::Error;
switch (section) {
case BinarySection::Custom:
section_result = ReadCustomSection(section_index, section_size);
if (options_.fail_on_custom_section_error) {
result |= section_result;
} else {
stop_on_first_error = false;
}
break;
case BinarySection::Type:
section_result = ReadTypeSection(section_size);
result |= section_result;
break;
case BinarySection::Import:
section_result = ReadImportSection(section_size);
result |= section_result;
break;
case BinarySection::Function:
section_result = ReadFunctionSection(section_size);
result |= section_result;
break;
case BinarySection::Table:
section_result = ReadTableSection(section_size);
result |= section_result;
break;
case BinarySection::Memory:
section_result = ReadMemorySection(section_size);
result |= section_result;
break;
case BinarySection::Global:
section_result = ReadGlobalSection(section_size);
result |= section_result;
break;
case BinarySection::Export:
section_result = ReadExportSection(section_size);
result |= section_result;
break;
case BinarySection::Start:
section_result = ReadStartSection(section_size);
result |= section_result;
break;
case BinarySection::Elem:
section_result = ReadElemSection(section_size);
result |= section_result;
break;
case BinarySection::Code:
section_result = ReadCodeSection(section_size);
result |= section_result;
break;
case BinarySection::Data:
section_result = ReadDataSection(section_size);
result |= section_result;
break;
case BinarySection::Tag:
ERROR_UNLESS(options_.features.exceptions_enabled(),
"invalid section code: %u",
static_cast<unsigned int>(section));
section_result = ReadTagSection(section_size);
result |= section_result;
break;
case BinarySection::DataCount:
ERROR_UNLESS(options_.features.bulk_memory_enabled(),
"invalid section code: %u",
static_cast<unsigned int>(section));
section_result = ReadDataCountSection(section_size);
result |= section_result;
break;
case BinarySection::Invalid:
WABT_UNREACHABLE;
}
if (Succeeded(section_result) && state_.offset != read_end_) {
PrintError("unfinished section (expected end: 0x%" PRIzx ")", read_end_);
section_result = Result::Error;
result |= section_result;
}
if (Failed(section_result)) {
if (stop_on_first_error) {
return Result::Error;
}
// If we're continuing after failing to read this section, move the
// offset to the expected section end. This way we may be able to read
// further sections.
state_.offset = read_end_;
}
if (section != BinarySection::Custom) {
last_known_section_ = section;
}
}
return result;
}
Result BinaryReader::ReadModule(const ReadModuleOptions& options) {
uint32_t magic = 0;
CHECK_RESULT(ReadU32(&magic, "magic"));
ERROR_UNLESS(magic == WABT_BINARY_MAGIC, "bad magic value");
uint32_t version = 0;
CHECK_RESULT(ReadU32(&version, "version"));
ERROR_UNLESS(version == WABT_BINARY_VERSION,
"bad wasm file version: %#x (expected %#x)", version,
WABT_BINARY_VERSION);
CALLBACK(BeginModule, version);
CHECK_RESULT(ReadSections(ReadSectionsOptions{options.stop_on_first_error}));
// This is checked in ReadCodeSection, but it must be checked at the end too,
// in case the code section was omitted.
ERROR_UNLESS(num_function_signatures_ == num_function_bodies_,
"function signature count != function body count");
CALLBACK0(EndModule);
return Result::Ok;
}
} // end anonymous namespace
Result ReadBinary(const void* data,
size_t size,
BinaryReaderDelegate* delegate,
const ReadBinaryOptions& options) {
BinaryReader reader(data, size, delegate, options);
return reader.ReadModule(
BinaryReader::ReadModuleOptions{options.stop_on_first_error});
}
} // namespace wabt