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