diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/wasm2c/src/binary-reader.cc | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/wasm2c/src/binary-reader.cc')
-rw-r--r-- | third_party/wasm2c/src/binary-reader.cc | 3027 |
1 files changed, 3027 insertions, 0 deletions
diff --git a/third_party/wasm2c/src/binary-reader.cc b/third_party/wasm2c/src/binary-reader.cc new file mode 100644 index 0000000000..8e86383db4 --- /dev/null +++ b/third_party/wasm2c/src/binary-reader.cc @@ -0,0 +1,3027 @@ +/* + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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(§ion_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(§ion_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(¶m_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(§ion_code, "section code")); + CHECK_RESULT(ReadOffset(§ion_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 |