| /* |
| * 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 "src/binary-reader.h" |
| |
| #include <cassert> |
| #include <cinttypes> |
| #include <cstdarg> |
| #include <cstdint> |
| #include <cstdio> |
| #include <cstring> |
| #include <vector> |
| |
| #include "config.h" |
| |
| #include "src/binary-reader-logging.h" |
| #include "src/binary.h" |
| #include "src/leb128.h" |
| #include "src/stream.h" |
| #include "src/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: |
| BinaryReader(const void* data, |
| size_t size, |
| BinaryReaderDelegate* delegate, |
| const ReadBinaryOptions& options); |
| |
| Result ReadModule(); |
| |
| 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_; |
| }; |
| |
| void WABT_PRINTF_FORMAT(2, 3) PrintError(const char* format, ...); |
| Result ReadOpcode(Opcode* out_value, const char* desc) WABT_WARN_UNUSED; |
| template <typename T> |
| Result ReadT(T* out_value, |
| const char* type_name, |
| const char* desc) WABT_WARN_UNUSED; |
| Result ReadU8(uint8_t* out_value, const char* desc) WABT_WARN_UNUSED; |
| Result ReadU32(uint32_t* out_value, const char* desc) WABT_WARN_UNUSED; |
| Result ReadF32(uint32_t* out_value, const char* desc) WABT_WARN_UNUSED; |
| Result ReadF64(uint64_t* out_value, const char* desc) WABT_WARN_UNUSED; |
| Result ReadV128(v128* out_value, const char* desc) WABT_WARN_UNUSED; |
| Result ReadU32Leb128(uint32_t* out_value, const char* desc) WABT_WARN_UNUSED; |
| Result ReadU64Leb128(uint64_t* out_value, const char* desc) WABT_WARN_UNUSED; |
| Result ReadS32Leb128(uint32_t* out_value, const char* desc) WABT_WARN_UNUSED; |
| Result ReadS64Leb128(uint64_t* out_value, const char* desc) WABT_WARN_UNUSED; |
| Result ReadType(Type* out_value, const char* desc) WABT_WARN_UNUSED; |
| Result ReadRefType(Type* out_value, const char* desc) WABT_WARN_UNUSED; |
| Result ReadExternalKind(ExternalKind* out_value, |
| const char* desc) WABT_WARN_UNUSED; |
| Result ReadStr(string_view* out_str, const char* desc) WABT_WARN_UNUSED; |
| Result ReadBytes(const void** out_data, |
| Address* out_data_size, |
| const char* desc) WABT_WARN_UNUSED; |
| Result ReadIndex(Index* index, const char* desc) WABT_WARN_UNUSED; |
| Result ReadOffset(Offset* offset, const char* desc) WABT_WARN_UNUSED; |
| Result ReadAlignment(Address* align_log2, const char* desc) WABT_WARN_UNUSED; |
| Result ReadMemidx(Index* memidx, const char* desc) WABT_WARN_UNUSED; |
| Result ReadCount(Index* index, const char* desc) WABT_WARN_UNUSED; |
| Result ReadField(TypeMut* out_value) WABT_WARN_UNUSED; |
| |
| bool IsConcreteType(Type); |
| bool IsBlockType(Type); |
| |
| Index NumTotalFuncs(); |
| |
| Result ReadInitExpr(Index index) WABT_WARN_UNUSED; |
| Result ReadTable(Type* out_elem_type, |
| Limits* out_elem_limits) WABT_WARN_UNUSED; |
| Result ReadMemory(Limits* out_page_limits) WABT_WARN_UNUSED; |
| Result ReadGlobalHeader(Type* out_type, bool* out_mutable) WABT_WARN_UNUSED; |
| Result ReadTagType(Index* out_sig_index) WABT_WARN_UNUSED; |
| Result ReadAddress(Address* out_value, |
| Index memory, |
| const char* desc) WABT_WARN_UNUSED; |
| Result ReadFunctionBody(Offset end_offset) WABT_WARN_UNUSED; |
| // ReadInstructions either until and END instruction, or until |
| // the given end_offset. |
| Result ReadInstructions(bool stop_on_end, |
| Offset end_offset, |
| Opcode* final_opcode) WABT_WARN_UNUSED; |
| Result ReadNameSection(Offset section_size) WABT_WARN_UNUSED; |
| Result ReadRelocSection(Offset section_size) WABT_WARN_UNUSED; |
| Result ReadDylinkSection(Offset section_size) WABT_WARN_UNUSED; |
| Result ReadDylink0Section(Offset section_size) WABT_WARN_UNUSED; |
| Result ReadTargetFeaturesSections(Offset section_size) WABT_WARN_UNUSED; |
| Result ReadLinkingSection(Offset section_size) WABT_WARN_UNUSED; |
| Result ReadCustomSection(Index section_index, |
| Offset section_size) WABT_WARN_UNUSED; |
| Result ReadTypeSection(Offset section_size) WABT_WARN_UNUSED; |
| Result ReadImportSection(Offset section_size) WABT_WARN_UNUSED; |
| Result ReadFunctionSection(Offset section_size) WABT_WARN_UNUSED; |
| Result ReadTableSection(Offset section_size) WABT_WARN_UNUSED; |
| Result ReadMemorySection(Offset section_size) WABT_WARN_UNUSED; |
| Result ReadGlobalSection(Offset section_size) WABT_WARN_UNUSED; |
| Result ReadExportSection(Offset section_size) WABT_WARN_UNUSED; |
| Result ReadStartSection(Offset section_size) WABT_WARN_UNUSED; |
| Result ReadElemSection(Offset section_size) WABT_WARN_UNUSED; |
| Result ReadCodeSection(Offset section_size) WABT_WARN_UNUSED; |
| Result ReadDataSection(Offset section_size) WABT_WARN_UNUSED; |
| Result ReadDataCountSection(Offset section_size) WABT_WARN_UNUSED; |
| Result ReadTagSection(Offset section_size) WABT_WARN_UNUSED; |
| Result ReadSections() WABT_WARN_UNUSED; |
| 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(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 = 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::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. |
| return ReadInstructions(/*stop_on_end=*/true, read_end_, NULL); |
| } |
| |
| 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; |
| } |
| |
| Type* result_types = num_results ? result_types_.data() : nullptr; |
| CALLBACK(OnSelectExpr, num_results, result_types); |
| 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; |
| CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment")); |
| Address offset; |
| CHECK_RESULT(ReadAddress(&offset, 0, "load offset")); |
| Index memidx = 0; |
| if (alignment_log2 >> 6) { |
| ERROR_IF(!options_.features.multi_memory_enabled(), |
| "multi_memory not allowed"); |
| CHECK_RESULT(ReadMemidx(&memidx, "store memidx")); |
| alignment_log2 = alignment_log2 & ((1 << 6) - 1); |
| } |
| |
| CALLBACK(OnLoadExpr, opcode, memidx, alignment_log2, offset); |
| if (memidx) { |
| CALLBACK(OnOpcodeUint32Uint32Uint32, alignment_log2, offset, memidx); |
| } else { |
| CALLBACK(OnOpcodeUint32Uint32, alignment_log2, 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; |
| CHECK_RESULT(ReadAlignment(&alignment_log2, "store alignment")); |
| Address offset; |
| CHECK_RESULT(ReadAddress(&offset, 0, "store offset")); |
| Index memidx = 0; |
| if (alignment_log2 >> 6) { |
| ERROR_IF(!options_.features.multi_memory_enabled(), |
| "multi_memory not allowed"); |
| CHECK_RESULT(ReadMemidx(&memidx, "store memidx")); |
| alignment_log2 = alignment_log2 & ((1 << 6) - 1); |
| } |
| |
| CALLBACK(OnStoreExpr, opcode, memidx, alignment_log2, offset); |
| if (memidx) { |
| CALLBACK(OnOpcodeUint32Uint32Uint32, alignment_log2, offset, memidx); |
| } else { |
| CALLBACK(OnOpcodeUint32Uint32, alignment_log2, 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: |
| 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: |
| CALLBACK(OnUnaryExpr, opcode); |
| CALLBACK0(OnOpcodeBare); |
| break; |
| |
| case Opcode::V128BitSelect: |
| 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; |
| CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment")); |
| Address offset; |
| CHECK_RESULT(ReadAddress(&offset, 0, "load offset")); |
| |
| CALLBACK(OnLoadSplatExpr, opcode, alignment_log2, offset); |
| CALLBACK(OnOpcodeUint32Uint32, alignment_log2, offset); |
| break; |
| } |
| case Opcode::V128Load8Lane: |
| case Opcode::V128Load16Lane: |
| case Opcode::V128Load32Lane: |
| case Opcode::V128Load64Lane: { |
| Address alignment_log2; |
| CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment")); |
| Address offset; |
| CHECK_RESULT(ReadAddress(&offset, 0, "load offset")); |
| uint8_t lane_val; |
| CHECK_RESULT(ReadU8(&lane_val, "Lane idx")); |
| |
| CALLBACK(OnSimdLoadLaneExpr, opcode, alignment_log2, offset, lane_val); |
| CALLBACK(OnOpcodeUint32Uint32Uint32, alignment_log2, offset, lane_val); |
| break; |
| } |
| case Opcode::V128Store8Lane: |
| case Opcode::V128Store16Lane: |
| case Opcode::V128Store32Lane: |
| case Opcode::V128Store64Lane: { |
| Address alignment_log2; |
| CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment")); |
| Address offset; |
| CHECK_RESULT(ReadAddress(&offset, 0, "load offset")); |
| uint8_t lane_val; |
| CHECK_RESULT(ReadU8(&lane_val, "Lane idx")); |
| |
| CALLBACK(OnSimdStoreLaneExpr, opcode, alignment_log2, offset, lane_val); |
| CALLBACK(OnOpcodeUint32Uint32Uint32, alignment_log2, offset, lane_val); |
| break; |
| } |
| case Opcode::V128Load32Zero: |
| case Opcode::V128Load64Zero: { |
| Address alignment_log2; |
| CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment")); |
| Address offset; |
| CHECK_RESULT(ReadAddress(&offset, 0, "load offset")); |
| |
| CALLBACK(OnLoadZeroExpr, opcode, alignment_log2, offset); |
| CALLBACK(OnOpcodeUint32Uint32, alignment_log2, 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; |
| CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment")); |
| Address offset; |
| CHECK_RESULT(ReadAddress(&offset, 0, "load offset")); |
| |
| CALLBACK(OnAtomicNotifyExpr, opcode, alignment_log2, offset); |
| CALLBACK(OnOpcodeUint32Uint32, alignment_log2, offset); |
| break; |
| } |
| |
| case Opcode::MemoryAtomicWait32: |
| case Opcode::MemoryAtomicWait64: { |
| Address alignment_log2; |
| CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment")); |
| Address offset; |
| CHECK_RESULT(ReadAddress(&offset, 0, "load offset")); |
| |
| CALLBACK(OnAtomicWaitExpr, opcode, alignment_log2, offset); |
| CALLBACK(OnOpcodeUint32Uint32, alignment_log2, 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; |
| CHECK_RESULT(ReadAlignment(&alignment_log2, "load alignment")); |
| Address offset; |
| CHECK_RESULT(ReadAddress(&offset, 0, "load offset")); |
| |
| CALLBACK(OnAtomicLoadExpr, opcode, alignment_log2, offset); |
| CALLBACK(OnOpcodeUint32Uint32, alignment_log2, 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; |
| CHECK_RESULT(ReadAlignment(&alignment_log2, "store alignment")); |
| Address offset; |
| CHECK_RESULT(ReadAddress(&offset, 0, "store offset")); |
| |
| CALLBACK(OnAtomicStoreExpr, opcode, alignment_log2, offset); |
| CALLBACK(OnOpcodeUint32Uint32, alignment_log2, 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; |
| CHECK_RESULT(ReadAlignment(&alignment_log2, "memory alignment")); |
| Address offset; |
| CHECK_RESULT(ReadAddress(&offset, 0, "memory offset")); |
| |
| CALLBACK(OnAtomicRmwExpr, opcode, alignment_log2, offset); |
| CALLBACK(OnOpcodeUint32Uint32, alignment_log2, 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; |
| CHECK_RESULT(ReadAlignment(&alignment_log2, "memory alignment")); |
| Address offset; |
| CHECK_RESULT(ReadAddress(&offset, 0, "memory offset")); |
| |
| CALLBACK(OnAtomicRmwCmpxchgExpr, opcode, alignment_log2, offset); |
| CALLBACK(OnOpcodeUint32Uint32, alignment_log2, 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) { |
| 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; |
| 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; |
| 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: |
| 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; |
| 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--) { |
| 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; |
| string_view module; |
| 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; |
| 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--) { |
| 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; |
| 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) { |
| 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++) { |
| 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 func; |
| CHECK_RESULT(ReadU32Leb128(&priority, "priority")); |
| CHECK_RESULT(ReadU32Leb128(&func, "function index")); |
| CALLBACK(OnInitFunction, priority, func); |
| } |
| break; |
| case LinkingEntryType::ComdatInfo: |
| CHECK_RESULT(ReadU32Leb128(&count, "count")); |
| CALLBACK(OnComdatCount, count); |
| while (count--) { |
| uint32_t flags; |
| uint32_t entry_count; |
| 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::ReadCustomSection(Index section_index, |
| Offset section_size) { |
| 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 { |
| // 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) { |
| string_view module_name; |
| CHECK_RESULT(ReadStr(&module_name, "import module name")); |
| 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) { |
| 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()); |
| 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 < 0x10000000"); |
| 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); |
| } |
| |
| 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() { |
| 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); |
| return Result::Error; |
| } |
| |
| 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() { |
| 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()); |
| // 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(); |
| } |
| |
| } // namespace wabt |