| /* |
| * 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 "binary-reader.h" |
| |
| #include <assert.h> |
| #include <inttypes.h> |
| #include <setjmp.h> |
| #include <stdarg.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include <vector> |
| |
| #include "binary.h" |
| #include "binary-reader-logging.h" |
| #include "config.h" |
| #include "stream.h" |
| |
| #if HAVE_ALLOCA |
| #include <alloca.h> |
| #endif |
| |
| namespace wabt { |
| |
| namespace { |
| |
| #define CALLBACK0(member) \ |
| RAISE_ERROR_UNLESS(WABT_SUCCEEDED(ctx->reader->member()), \ |
| #member " callback failed") |
| |
| #define CALLBACK(member, ...) \ |
| RAISE_ERROR_UNLESS(WABT_SUCCEEDED(ctx->reader->member(__VA_ARGS__)), \ |
| #member " callback failed") |
| |
| #define RAISE_ERROR(...) raise_error(ctx, __VA_ARGS__) |
| |
| #define RAISE_ERROR_UNLESS(cond, ...) \ |
| if (!(cond)) \ |
| RAISE_ERROR(__VA_ARGS__); |
| |
| struct Context { |
| size_t read_end = 0; /* Either the section end or data_size. */ |
| BinaryReader* reader = nullptr; |
| BinaryReader::State state; |
| jmp_buf error_jmp_buf; |
| TypeVector param_types; |
| std::vector<uint32_t> target_depths; |
| const ReadBinaryOptions* options = nullptr; |
| BinarySection last_known_section = BinarySection::Invalid; |
| uint32_t num_signatures = 0; |
| uint32_t num_imports = 0; |
| uint32_t num_func_imports = 0; |
| uint32_t num_table_imports = 0; |
| uint32_t num_memory_imports = 0; |
| uint32_t num_global_imports = 0; |
| uint32_t num_function_signatures = 0; |
| uint32_t num_tables = 0; |
| uint32_t num_memories = 0; |
| uint32_t num_globals = 0; |
| uint32_t num_exports = 0; |
| uint32_t num_function_bodies = 0; |
| }; |
| |
| } // namespace |
| |
| static void WABT_PRINTF_FORMAT(2, 3) |
| raise_error(Context* ctx, const char* format, ...) { |
| WABT_SNPRINTF_ALLOCA(buffer, length, format); |
| bool handled = ctx->reader->OnError(buffer); |
| |
| if (!handled) { |
| /* Not great to just print, but we don't want to eat the error either. */ |
| fprintf(stderr, "*ERROR*: @0x%08zx: %s\n", ctx->state.offset, buffer); |
| } |
| longjmp(ctx->error_jmp_buf, 1); |
| } |
| |
| #define IN_SIZE(type) \ |
| if (ctx->state.offset + sizeof(type) > ctx->read_end) { \ |
| RAISE_ERROR("unable to read " #type ": %s", desc); \ |
| } \ |
| memcpy(out_value, ctx->state.data + ctx->state.offset, sizeof(type)); \ |
| ctx->state.offset += sizeof(type) |
| |
| static void in_u8(Context* ctx, uint8_t* out_value, const char* desc) { |
| IN_SIZE(uint8_t); |
| } |
| |
| static void in_u32(Context* ctx, uint32_t* out_value, const char* desc) { |
| IN_SIZE(uint32_t); |
| } |
| |
| static void in_f32(Context* ctx, uint32_t* out_value, const char* desc) { |
| IN_SIZE(float); |
| } |
| |
| static void in_f64(Context* ctx, uint64_t* out_value, const char* desc) { |
| IN_SIZE(double); |
| } |
| |
| #undef IN_SIZE |
| |
| #define BYTE_AT(type, i, shift) ((static_cast<type>(p[i]) & 0x7f) << (shift)) |
| |
| #define LEB128_1(type) (BYTE_AT(type, 0, 0)) |
| #define LEB128_2(type) (BYTE_AT(type, 1, 7) | LEB128_1(type)) |
| #define LEB128_3(type) (BYTE_AT(type, 2, 14) | LEB128_2(type)) |
| #define LEB128_4(type) (BYTE_AT(type, 3, 21) | LEB128_3(type)) |
| #define LEB128_5(type) (BYTE_AT(type, 4, 28) | LEB128_4(type)) |
| #define LEB128_6(type) (BYTE_AT(type, 5, 35) | LEB128_5(type)) |
| #define LEB128_7(type) (BYTE_AT(type, 6, 42) | LEB128_6(type)) |
| #define LEB128_8(type) (BYTE_AT(type, 7, 49) | LEB128_7(type)) |
| #define LEB128_9(type) (BYTE_AT(type, 8, 56) | LEB128_8(type)) |
| #define LEB128_10(type) (BYTE_AT(type, 9, 63) | LEB128_9(type)) |
| |
| #define SHIFT_AMOUNT(type, sign_bit) (sizeof(type) * 8 - 1 - (sign_bit)) |
| #define SIGN_EXTEND(type, value, sign_bit) \ |
| (static_cast<type>((value) << SHIFT_AMOUNT(type, sign_bit)) >> \ |
| SHIFT_AMOUNT(type, sign_bit)) |
| |
| size_t read_u32_leb128(const uint8_t* p, |
| const uint8_t* end, |
| uint32_t* out_value) { |
| if (p < end && (p[0] & 0x80) == 0) { |
| *out_value = LEB128_1(uint32_t); |
| return 1; |
| } else if (p + 1 < end && (p[1] & 0x80) == 0) { |
| *out_value = LEB128_2(uint32_t); |
| return 2; |
| } else if (p + 2 < end && (p[2] & 0x80) == 0) { |
| *out_value = LEB128_3(uint32_t); |
| return 3; |
| } else if (p + 3 < end && (p[3] & 0x80) == 0) { |
| *out_value = LEB128_4(uint32_t); |
| return 4; |
| } else if (p + 4 < end && (p[4] & 0x80) == 0) { |
| /* the top bits set represent values > 32 bits */ |
| if (p[4] & 0xf0) |
| return 0; |
| *out_value = LEB128_5(uint32_t); |
| return 5; |
| } else { |
| /* past the end */ |
| *out_value = 0; |
| return 0; |
| } |
| } |
| |
| static void in_u32_leb128(Context* ctx, uint32_t* out_value, const char* desc) { |
| const uint8_t* p = ctx->state.data + ctx->state.offset; |
| const uint8_t* end = ctx->state.data + ctx->read_end; |
| size_t bytes_read = read_u32_leb128(p, end, out_value); |
| if (!bytes_read) |
| RAISE_ERROR("unable to read u32 leb128: %s", desc); |
| ctx->state.offset += bytes_read; |
| } |
| |
| size_t read_i32_leb128(const uint8_t* p, |
| const uint8_t* end, |
| uint32_t* out_value) { |
| if (p < end && (p[0] & 0x80) == 0) { |
| uint32_t result = LEB128_1(uint32_t); |
| *out_value = SIGN_EXTEND(int32_t, result, 6); |
| return 1; |
| } else if (p + 1 < end && (p[1] & 0x80) == 0) { |
| uint32_t result = LEB128_2(uint32_t); |
| *out_value = SIGN_EXTEND(int32_t, result, 13); |
| return 2; |
| } else if (p + 2 < end && (p[2] & 0x80) == 0) { |
| uint32_t result = LEB128_3(uint32_t); |
| *out_value = SIGN_EXTEND(int32_t, result, 20); |
| return 3; |
| } else if (p + 3 < end && (p[3] & 0x80) == 0) { |
| uint32_t result = LEB128_4(uint32_t); |
| *out_value = SIGN_EXTEND(int32_t, result, 27); |
| return 4; |
| } else if (p + 4 < end && (p[4] & 0x80) == 0) { |
| /* the top bits should be a sign-extension of the sign bit */ |
| bool sign_bit_set = (p[4] & 0x8); |
| int top_bits = p[4] & 0xf0; |
| if ((sign_bit_set && top_bits != 0x70) || |
| (!sign_bit_set && top_bits != 0)) { |
| return 0; |
| } |
| uint32_t result = LEB128_5(uint32_t); |
| *out_value = result; |
| return 5; |
| } else { |
| /* past the end */ |
| return 0; |
| } |
| } |
| |
| static void in_i32_leb128(Context* ctx, uint32_t* out_value, const char* desc) { |
| const uint8_t* p = ctx->state.data + ctx->state.offset; |
| const uint8_t* end = ctx->state.data + ctx->read_end; |
| size_t bytes_read = read_i32_leb128(p, end, out_value); |
| if (!bytes_read) |
| RAISE_ERROR("unable to read i32 leb128: %s", desc); |
| ctx->state.offset += bytes_read; |
| } |
| |
| static void in_i64_leb128(Context* ctx, uint64_t* out_value, const char* desc) { |
| const uint8_t* p = ctx->state.data + ctx->state.offset; |
| const uint8_t* end = ctx->state.data + ctx->read_end; |
| |
| if (p < end && (p[0] & 0x80) == 0) { |
| uint64_t result = LEB128_1(uint64_t); |
| *out_value = SIGN_EXTEND(int64_t, result, 6); |
| ctx->state.offset += 1; |
| } else if (p + 1 < end && (p[1] & 0x80) == 0) { |
| uint64_t result = LEB128_2(uint64_t); |
| *out_value = SIGN_EXTEND(int64_t, result, 13); |
| ctx->state.offset += 2; |
| } else if (p + 2 < end && (p[2] & 0x80) == 0) { |
| uint64_t result = LEB128_3(uint64_t); |
| *out_value = SIGN_EXTEND(int64_t, result, 20); |
| ctx->state.offset += 3; |
| } else if (p + 3 < end && (p[3] & 0x80) == 0) { |
| uint64_t result = LEB128_4(uint64_t); |
| *out_value = SIGN_EXTEND(int64_t, result, 27); |
| ctx->state.offset += 4; |
| } else if (p + 4 < end && (p[4] & 0x80) == 0) { |
| uint64_t result = LEB128_5(uint64_t); |
| *out_value = SIGN_EXTEND(int64_t, result, 34); |
| ctx->state.offset += 5; |
| } else if (p + 5 < end && (p[5] & 0x80) == 0) { |
| uint64_t result = LEB128_6(uint64_t); |
| *out_value = SIGN_EXTEND(int64_t, result, 41); |
| ctx->state.offset += 6; |
| } else if (p + 6 < end && (p[6] & 0x80) == 0) { |
| uint64_t result = LEB128_7(uint64_t); |
| *out_value = SIGN_EXTEND(int64_t, result, 48); |
| ctx->state.offset += 7; |
| } else if (p + 7 < end && (p[7] & 0x80) == 0) { |
| uint64_t result = LEB128_8(uint64_t); |
| *out_value = SIGN_EXTEND(int64_t, result, 55); |
| ctx->state.offset += 8; |
| } else if (p + 8 < end && (p[8] & 0x80) == 0) { |
| uint64_t result = LEB128_9(uint64_t); |
| *out_value = SIGN_EXTEND(int64_t, result, 62); |
| ctx->state.offset += 9; |
| } else if (p + 9 < end && (p[9] & 0x80) == 0) { |
| /* the top bits should be a sign-extension of the sign bit */ |
| bool sign_bit_set = (p[9] & 0x1); |
| int top_bits = p[9] & 0xfe; |
| if ((sign_bit_set && top_bits != 0x7e) || |
| (!sign_bit_set && top_bits != 0)) { |
| RAISE_ERROR("invalid i64 leb128: %s", desc); |
| } |
| uint64_t result = LEB128_10(uint64_t); |
| *out_value = result; |
| ctx->state.offset += 10; |
| } else { |
| /* past the end */ |
| RAISE_ERROR("unable to read i64 leb128: %s", desc); |
| } |
| } |
| |
| #undef BYTE_AT |
| #undef LEB128_1 |
| #undef LEB128_2 |
| #undef LEB128_3 |
| #undef LEB128_4 |
| #undef LEB128_5 |
| #undef LEB128_6 |
| #undef LEB128_7 |
| #undef LEB128_8 |
| #undef LEB128_9 |
| #undef LEB128_10 |
| #undef SHIFT_AMOUNT |
| #undef SIGN_EXTEND |
| |
| static void in_type(Context* ctx, Type* out_value, const char* desc) { |
| uint32_t type = 0; |
| in_i32_leb128(ctx, &type, desc); |
| /* Must be in the vs7 range: [-128, 127). */ |
| if (static_cast<int32_t>(type) < -128 || static_cast<int32_t>(type) > 127) |
| RAISE_ERROR("invalid type: %d", type); |
| *out_value = static_cast<Type>(type); |
| } |
| |
| static void in_str(Context* ctx, StringSlice* out_str, const char* desc) { |
| uint32_t str_len = 0; |
| in_u32_leb128(ctx, &str_len, "string length"); |
| |
| if (ctx->state.offset + str_len > ctx->read_end) |
| RAISE_ERROR("unable to read string: %s", desc); |
| |
| out_str->start = |
| reinterpret_cast<const char*>(ctx->state.data) + ctx->state.offset; |
| out_str->length = str_len; |
| ctx->state.offset += str_len; |
| } |
| |
| static void in_bytes(Context* ctx, |
| const void** out_data, |
| uint32_t* out_data_size, |
| const char* desc) { |
| uint32_t data_size = 0; |
| in_u32_leb128(ctx, &data_size, "data size"); |
| |
| if (ctx->state.offset + data_size > ctx->read_end) |
| RAISE_ERROR("unable to read data: %s", desc); |
| |
| *out_data = |
| static_cast<const uint8_t*>(ctx->state.data) + ctx->state.offset; |
| *out_data_size = data_size; |
| ctx->state.offset += data_size; |
| } |
| |
| static bool is_valid_external_kind(uint8_t kind) { |
| return kind < kExternalKindCount; |
| } |
| |
| static bool is_concrete_type(Type type) { |
| switch (type) { |
| case Type::I32: |
| case Type::I64: |
| case Type::F32: |
| case Type::F64: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| static bool is_inline_sig_type(Type type) { |
| return is_concrete_type(type) || type == Type::Void; |
| } |
| |
| static uint32_t num_total_funcs(Context* ctx) { |
| return ctx->num_func_imports + ctx->num_function_signatures; |
| } |
| |
| static uint32_t num_total_tables(Context* ctx) { |
| return ctx->num_table_imports + ctx->num_tables; |
| } |
| |
| static uint32_t num_total_memories(Context* ctx) { |
| return ctx->num_memory_imports + ctx->num_memories; |
| } |
| |
| static uint32_t num_total_globals(Context* ctx) { |
| return ctx->num_global_imports + ctx->num_globals; |
| } |
| |
| static void read_init_expr(Context* ctx, uint32_t index) { |
| uint8_t opcode; |
| in_u8(ctx, &opcode, "opcode"); |
| switch (static_cast<Opcode>(opcode)) { |
| case Opcode::I32Const: { |
| uint32_t value = 0; |
| in_i32_leb128(ctx, &value, "init_expr i32.const value"); |
| CALLBACK(OnInitExprI32ConstExpr, index, value); |
| break; |
| } |
| |
| case Opcode::I64Const: { |
| uint64_t value = 0; |
| in_i64_leb128(ctx, &value, "init_expr i64.const value"); |
| CALLBACK(OnInitExprI64ConstExpr, index, value); |
| break; |
| } |
| |
| case Opcode::F32Const: { |
| uint32_t value_bits = 0; |
| in_f32(ctx, &value_bits, "init_expr f32.const value"); |
| CALLBACK(OnInitExprF32ConstExpr, index, value_bits); |
| break; |
| } |
| |
| case Opcode::F64Const: { |
| uint64_t value_bits = 0; |
| in_f64(ctx, &value_bits, "init_expr f64.const value"); |
| CALLBACK(OnInitExprF64ConstExpr, index, value_bits); |
| break; |
| } |
| |
| case Opcode::GetGlobal: { |
| uint32_t global_index; |
| in_u32_leb128(ctx, &global_index, "init_expr get_global index"); |
| CALLBACK(OnInitExprGetGlobalExpr, index, global_index); |
| break; |
| } |
| |
| case Opcode::End: |
| return; |
| |
| default: |
| RAISE_ERROR("unexpected opcode in initializer expression: %d (0x%x)", |
| opcode, opcode); |
| break; |
| } |
| |
| in_u8(ctx, &opcode, "opcode"); |
| RAISE_ERROR_UNLESS(static_cast<Opcode>(opcode) == Opcode::End, |
| "expected END opcode after initializer expression"); |
| } |
| |
| static void read_table(Context* ctx, |
| Type* out_elem_type, |
| Limits* out_elem_limits) { |
| in_type(ctx, out_elem_type, "table elem type"); |
| RAISE_ERROR_UNLESS(*out_elem_type == Type::Anyfunc, |
| "table elem type must by anyfunc"); |
| |
| uint32_t flags; |
| uint32_t initial; |
| uint32_t max = 0; |
| in_u32_leb128(ctx, &flags, "table flags"); |
| in_u32_leb128(ctx, &initial, "table initial elem count"); |
| bool has_max = flags & WABT_BINARY_LIMITS_HAS_MAX_FLAG; |
| if (has_max) { |
| in_u32_leb128(ctx, &max, "table max elem count"); |
| RAISE_ERROR_UNLESS(initial <= max, |
| "table initial elem count must be <= max elem count"); |
| } |
| |
| out_elem_limits->has_max = has_max; |
| out_elem_limits->initial = initial; |
| out_elem_limits->max = max; |
| } |
| |
| static void read_memory(Context* ctx, Limits* out_page_limits) { |
| uint32_t flags; |
| uint32_t initial; |
| uint32_t max = 0; |
| in_u32_leb128(ctx, &flags, "memory flags"); |
| in_u32_leb128(ctx, &initial, "memory initial page count"); |
| bool has_max = flags & WABT_BINARY_LIMITS_HAS_MAX_FLAG; |
| RAISE_ERROR_UNLESS(initial <= WABT_MAX_PAGES, "invalid memory initial size"); |
| if (has_max) { |
| in_u32_leb128(ctx, &max, "memory max page count"); |
| RAISE_ERROR_UNLESS(max <= WABT_MAX_PAGES, "invalid memory max size"); |
| RAISE_ERROR_UNLESS(initial <= max, |
| "memory initial size must be <= max size"); |
| } |
| |
| out_page_limits->has_max = has_max; |
| out_page_limits->initial = initial; |
| out_page_limits->max = max; |
| } |
| |
| static void read_global_header(Context* ctx, |
| Type* out_type, |
| bool* out_mutable) { |
| Type global_type; |
| uint8_t mutable_; |
| in_type(ctx, &global_type, "global type"); |
| RAISE_ERROR_UNLESS(is_concrete_type(global_type), |
| "invalid global type: %#x", static_cast<int>(global_type)); |
| |
| in_u8(ctx, &mutable_, "global mutability"); |
| RAISE_ERROR_UNLESS(mutable_ <= 1, "global mutability must be 0 or 1"); |
| |
| *out_type = global_type; |
| *out_mutable = mutable_; |
| } |
| |
| static void read_function_body(Context* ctx, uint32_t end_offset) { |
| bool seen_end_opcode = false; |
| while (ctx->state.offset < end_offset) { |
| uint8_t opcode_u8; |
| in_u8(ctx, &opcode_u8, "opcode"); |
| Opcode opcode = static_cast<Opcode>(opcode_u8); |
| CALLBACK(OnOpcode, opcode); |
| switch (opcode) { |
| case Opcode::Unreachable: |
| CALLBACK0(OnUnreachableExpr); |
| CALLBACK0(OnOpcodeBare); |
| break; |
| |
| case Opcode::Block: { |
| Type sig_type; |
| in_type(ctx, &sig_type, "block signature type"); |
| RAISE_ERROR_UNLESS(is_inline_sig_type(sig_type), |
| "expected valid block signature type"); |
| uint32_t num_types = sig_type == Type::Void ? 0 : 1; |
| CALLBACK(OnBlockExpr, num_types, &sig_type); |
| CALLBACK(OnOpcodeBlockSig, num_types, &sig_type); |
| break; |
| } |
| |
| case Opcode::Loop: { |
| Type sig_type; |
| in_type(ctx, &sig_type, "loop signature type"); |
| RAISE_ERROR_UNLESS(is_inline_sig_type(sig_type), |
| "expected valid block signature type"); |
| uint32_t num_types = sig_type == Type::Void ? 0 : 1; |
| CALLBACK(OnLoopExpr, num_types, &sig_type); |
| CALLBACK(OnOpcodeBlockSig, num_types, &sig_type); |
| break; |
| } |
| |
| case Opcode::If: { |
| Type sig_type; |
| in_type(ctx, &sig_type, "if signature type"); |
| RAISE_ERROR_UNLESS(is_inline_sig_type(sig_type), |
| "expected valid block signature type"); |
| uint32_t num_types = sig_type == Type::Void ? 0 : 1; |
| CALLBACK(OnIfExpr, num_types, &sig_type); |
| CALLBACK(OnOpcodeBlockSig, num_types, &sig_type); |
| break; |
| } |
| |
| case Opcode::Else: |
| CALLBACK0(OnElseExpr); |
| CALLBACK0(OnOpcodeBare); |
| break; |
| |
| case Opcode::Select: |
| CALLBACK0(OnSelectExpr); |
| CALLBACK0(OnOpcodeBare); |
| break; |
| |
| case Opcode::Br: { |
| uint32_t depth; |
| in_u32_leb128(ctx, &depth, "br depth"); |
| CALLBACK(OnBrExpr, depth); |
| CALLBACK(OnOpcodeUint32, depth); |
| break; |
| } |
| |
| case Opcode::BrIf: { |
| uint32_t depth; |
| in_u32_leb128(ctx, &depth, "br_if depth"); |
| CALLBACK(OnBrIfExpr, depth); |
| CALLBACK(OnOpcodeUint32, depth); |
| break; |
| } |
| |
| case Opcode::BrTable: { |
| uint32_t num_targets; |
| in_u32_leb128(ctx, &num_targets, "br_table target count"); |
| ctx->target_depths.resize(num_targets); |
| |
| for (uint32_t i = 0; i < num_targets; ++i) { |
| uint32_t target_depth; |
| in_u32_leb128(ctx, &target_depth, "br_table target depth"); |
| ctx->target_depths[i] = target_depth; |
| } |
| |
| uint32_t default_target_depth; |
| in_u32_leb128(ctx, &default_target_depth, |
| "br_table default target depth"); |
| |
| uint32_t* target_depths = |
| num_targets ? ctx->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: |
| if (ctx->state.offset == end_offset) { |
| seen_end_opcode = true; |
| CALLBACK0(OnEndFunc); |
| } else { |
| CALLBACK0(OnEndExpr); |
| } |
| break; |
| |
| case Opcode::I32Const: { |
| uint32_t value = 0; |
| in_i32_leb128(ctx, &value, "i32.const value"); |
| CALLBACK(OnI32ConstExpr, value); |
| CALLBACK(OnOpcodeUint32, value); |
| break; |
| } |
| |
| case Opcode::I64Const: { |
| uint64_t value = 0; |
| in_i64_leb128(ctx, &value, "i64.const value"); |
| CALLBACK(OnI64ConstExpr, value); |
| CALLBACK(OnOpcodeUint64, value); |
| break; |
| } |
| |
| case Opcode::F32Const: { |
| uint32_t value_bits = 0; |
| in_f32(ctx, &value_bits, "f32.const value"); |
| CALLBACK(OnF32ConstExpr, value_bits); |
| CALLBACK(OnOpcodeF32, value_bits); |
| break; |
| } |
| |
| case Opcode::F64Const: { |
| uint64_t value_bits = 0; |
| in_f64(ctx, &value_bits, "f64.const value"); |
| CALLBACK(OnF64ConstExpr, value_bits); |
| CALLBACK(OnOpcodeF64, value_bits); |
| break; |
| } |
| |
| case Opcode::GetGlobal: { |
| uint32_t global_index; |
| in_u32_leb128(ctx, &global_index, "get_global global index"); |
| CALLBACK(OnGetGlobalExpr, global_index); |
| CALLBACK(OnOpcodeUint32, global_index); |
| break; |
| } |
| |
| case Opcode::GetLocal: { |
| uint32_t local_index; |
| in_u32_leb128(ctx, &local_index, "get_local local index"); |
| CALLBACK(OnGetLocalExpr, local_index); |
| CALLBACK(OnOpcodeUint32, local_index); |
| break; |
| } |
| |
| case Opcode::SetGlobal: { |
| uint32_t global_index; |
| in_u32_leb128(ctx, &global_index, "set_global global index"); |
| CALLBACK(OnSetGlobalExpr, global_index); |
| CALLBACK(OnOpcodeUint32, global_index); |
| break; |
| } |
| |
| case Opcode::SetLocal: { |
| uint32_t local_index; |
| in_u32_leb128(ctx, &local_index, "set_local local index"); |
| CALLBACK(OnSetLocalExpr, local_index); |
| CALLBACK(OnOpcodeUint32, local_index); |
| break; |
| } |
| |
| case Opcode::Call: { |
| uint32_t func_index; |
| in_u32_leb128(ctx, &func_index, "call function index"); |
| RAISE_ERROR_UNLESS(func_index < num_total_funcs(ctx), |
| "invalid call function index"); |
| CALLBACK(OnCallExpr, func_index); |
| CALLBACK(OnOpcodeUint32, func_index); |
| break; |
| } |
| |
| case Opcode::CallIndirect: { |
| uint32_t sig_index; |
| in_u32_leb128(ctx, &sig_index, "call_indirect signature index"); |
| RAISE_ERROR_UNLESS(sig_index < ctx->num_signatures, |
| "invalid call_indirect signature index"); |
| uint32_t reserved; |
| in_u32_leb128(ctx, &reserved, "call_indirect reserved"); |
| RAISE_ERROR_UNLESS(reserved == 0, |
| "call_indirect reserved value must be 0"); |
| CALLBACK(OnCallIndirectExpr, sig_index); |
| CALLBACK(OnOpcodeUint32Uint32, sig_index, reserved); |
| break; |
| } |
| |
| case Opcode::TeeLocal: { |
| uint32_t local_index; |
| in_u32_leb128(ctx, &local_index, "tee_local local index"); |
| CALLBACK(OnTeeLocalExpr, local_index); |
| CALLBACK(OnOpcodeUint32, 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: { |
| uint32_t alignment_log2; |
| in_u32_leb128(ctx, &alignment_log2, "load alignment"); |
| uint32_t offset; |
| in_u32_leb128(ctx, &offset, "load offset"); |
| |
| CALLBACK(OnLoadExpr, opcode, alignment_log2, offset); |
| CALLBACK(OnOpcodeUint32Uint32, alignment_log2, offset); |
| break; |
| } |
| |
| case Opcode::I32Store8: |
| case Opcode::I32Store16: |
| case Opcode::I64Store8: |
| case Opcode::I64Store16: |
| case Opcode::I64Store32: |
| case Opcode::I32Store: |
| case Opcode::I64Store: |
| case Opcode::F32Store: |
| case Opcode::F64Store: { |
| uint32_t alignment_log2; |
| in_u32_leb128(ctx, &alignment_log2, "store alignment"); |
| uint32_t offset; |
| in_u32_leb128(ctx, &offset, "store offset"); |
| |
| CALLBACK(OnStoreExpr, opcode, alignment_log2, offset); |
| CALLBACK(OnOpcodeUint32Uint32, alignment_log2, offset); |
| break; |
| } |
| |
| case Opcode::CurrentMemory: { |
| uint32_t reserved; |
| in_u32_leb128(ctx, &reserved, "current_memory reserved"); |
| RAISE_ERROR_UNLESS(reserved == 0, |
| "current_memory reserved value must be 0"); |
| CALLBACK0(OnCurrentMemoryExpr); |
| CALLBACK(OnOpcodeUint32, reserved); |
| break; |
| } |
| |
| case Opcode::GrowMemory: { |
| uint32_t reserved; |
| in_u32_leb128(ctx, &reserved, "grow_memory reserved"); |
| RAISE_ERROR_UNLESS(reserved == 0, |
| "grow_memory reserved value must be 0"); |
| CALLBACK0(OnGrowMemoryExpr); |
| CALLBACK(OnOpcodeUint32, reserved); |
| 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: |
| 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: |
| 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: |
| CALLBACK(OnUnaryExpr, opcode); |
| CALLBACK0(OnOpcodeBare); |
| break; |
| |
| case Opcode::I32TruncSF32: |
| case Opcode::I32TruncSF64: |
| case Opcode::I32TruncUF32: |
| case Opcode::I32TruncUF64: |
| case Opcode::I32WrapI64: |
| case Opcode::I64TruncSF32: |
| case Opcode::I64TruncSF64: |
| case Opcode::I64TruncUF32: |
| case Opcode::I64TruncUF64: |
| case Opcode::I64ExtendSI32: |
| case Opcode::I64ExtendUI32: |
| case Opcode::F32ConvertSI32: |
| case Opcode::F32ConvertUI32: |
| case Opcode::F32ConvertSI64: |
| case Opcode::F32ConvertUI64: |
| case Opcode::F32DemoteF64: |
| case Opcode::F32ReinterpretI32: |
| case Opcode::F64ConvertSI32: |
| case Opcode::F64ConvertUI32: |
| case Opcode::F64ConvertSI64: |
| case Opcode::F64ConvertUI64: |
| case Opcode::F64PromoteF32: |
| case Opcode::F64ReinterpretI64: |
| case Opcode::I32ReinterpretF32: |
| case Opcode::I64ReinterpretF64: |
| case Opcode::I32Eqz: |
| case Opcode::I64Eqz: |
| CALLBACK(OnConvertExpr, opcode); |
| CALLBACK0(OnOpcodeBare); |
| break; |
| |
| default: |
| RAISE_ERROR("unexpected opcode: %d (0x%x)", static_cast<int>(opcode), |
| static_cast<unsigned>(opcode)); |
| } |
| } |
| RAISE_ERROR_UNLESS(ctx->state.offset == end_offset, |
| "function body longer than given size"); |
| RAISE_ERROR_UNLESS(seen_end_opcode, "function body must end with END opcode"); |
| } |
| |
| static void read_names_section(Context* ctx, uint32_t section_size) { |
| CALLBACK(BeginNamesSection, section_size); |
| uint32_t i = 0; |
| size_t previous_read_end = ctx->read_end; |
| uint32_t previous_subsection_type = 0; |
| while (ctx->state.offset < ctx->read_end) { |
| uint32_t name_type; |
| uint32_t subsection_size; |
| in_u32_leb128(ctx, &name_type, "name type"); |
| if (i != 0) { |
| if (name_type == previous_subsection_type) |
| RAISE_ERROR("duplicate sub-section"); |
| if (name_type < previous_subsection_type) |
| RAISE_ERROR("out-of-order sub-section"); |
| } |
| previous_subsection_type = name_type; |
| in_u32_leb128(ctx, &subsection_size, "subsection size"); |
| size_t subsection_end = ctx->state.offset + subsection_size; |
| if (subsection_end > ctx->read_end) |
| RAISE_ERROR("invalid sub-section size: extends past end"); |
| ctx->read_end = subsection_end; |
| |
| switch (static_cast<NameSectionSubsection>(name_type)) { |
| case NameSectionSubsection::Function: |
| CALLBACK(OnFunctionNameSubsection, i, name_type, subsection_size); |
| if (subsection_size) { |
| uint32_t num_names; |
| in_u32_leb128(ctx, &num_names, "name count"); |
| CALLBACK(OnFunctionNamesCount, num_names); |
| for (uint32_t j = 0; j < num_names; ++j) { |
| uint32_t function_index; |
| StringSlice function_name; |
| |
| in_u32_leb128(ctx, &function_index, "function index"); |
| RAISE_ERROR_UNLESS(function_index < num_total_funcs(ctx), |
| "invalid function index: %u", function_index); |
| in_str(ctx, &function_name, "function name"); |
| CALLBACK(OnFunctionName, function_index, function_name); |
| } |
| } |
| break; |
| case NameSectionSubsection::Local: |
| CALLBACK(OnLocalNameSubsection, i, name_type, subsection_size); |
| if (subsection_size) { |
| uint32_t num_funcs; |
| in_u32_leb128(ctx, &num_funcs, "function count"); |
| CALLBACK(OnLocalNameFunctionCount, num_funcs); |
| for (uint32_t j = 0; j < num_funcs; ++j) { |
| uint32_t function_index; |
| in_u32_leb128(ctx, &function_index, "function index"); |
| uint32_t num_locals; |
| in_u32_leb128(ctx, &num_locals, "local count"); |
| CALLBACK(OnLocalNameLocalCount, function_index, num_locals); |
| for (uint32_t k = 0; k < num_locals; ++k) { |
| uint32_t local_index; |
| StringSlice local_name; |
| |
| in_u32_leb128(ctx, &local_index, "named index"); |
| in_str(ctx, &local_name, "name"); |
| CALLBACK(OnLocalName, function_index, local_index, local_name); |
| } |
| } |
| } |
| break; |
| default: |
| /* unknown subsection, skip it */ |
| ctx->state.offset = subsection_end; |
| break; |
| } |
| ++i; |
| if (ctx->state.offset != subsection_end) { |
| RAISE_ERROR("unfinished sub-section (expected end: 0x%" PRIzx ")", |
| subsection_end); |
| } |
| ctx->read_end = previous_read_end; |
| } |
| CALLBACK0(EndNamesSection); |
| } |
| |
| static void read_reloc_section(Context* ctx, uint32_t section_size) { |
| CALLBACK(BeginRelocSection, section_size); |
| uint32_t num_relocs, section; |
| in_u32_leb128(ctx, §ion, "section"); |
| StringSlice section_name; |
| WABT_ZERO_MEMORY(section_name); |
| if (static_cast<BinarySection>(section) == BinarySection::Custom) |
| in_str(ctx, §ion_name, "section name"); |
| in_u32_leb128(ctx, &num_relocs, "relocation count"); |
| CALLBACK(OnRelocCount, num_relocs, static_cast<BinarySection>(section), |
| section_name); |
| for (uint32_t i = 0; i < num_relocs; ++i) { |
| uint32_t reloc_type, offset, index, addend = 0; |
| in_u32_leb128(ctx, &reloc_type, "relocation type"); |
| in_u32_leb128(ctx, &offset, "offset"); |
| in_u32_leb128(ctx, &index, "index"); |
| RelocType type = static_cast<RelocType>(reloc_type); |
| switch (type) { |
| case RelocType::MemoryAddressLEB: |
| case RelocType::MemoryAddressSLEB: |
| case RelocType::MemoryAddressI32: |
| in_i32_leb128(ctx, &addend, "addend"); |
| break; |
| default: |
| break; |
| } |
| CALLBACK(OnReloc, type, offset, index, addend); |
| } |
| CALLBACK0(EndRelocSection); |
| } |
| |
| static void read_custom_section(Context* ctx, uint32_t section_size) { |
| StringSlice section_name; |
| in_str(ctx, §ion_name, "section name"); |
| CALLBACK(BeginCustomSection, section_size, section_name); |
| |
| bool name_section_ok = ctx->last_known_section >= BinarySection::Import; |
| if (ctx->options->read_debug_names && name_section_ok && |
| strncmp(section_name.start, WABT_BINARY_SECTION_NAME, |
| section_name.length) == 0) { |
| read_names_section(ctx, section_size); |
| } else if (strncmp(section_name.start, WABT_BINARY_SECTION_RELOC, |
| strlen(WABT_BINARY_SECTION_RELOC)) == 0) { |
| read_reloc_section(ctx, section_size); |
| } else { |
| /* This is an unknown custom section, skip it. */ |
| ctx->state.offset = ctx->read_end; |
| } |
| CALLBACK0(EndCustomSection); |
| } |
| |
| static void read_type_section(Context* ctx, uint32_t section_size) { |
| CALLBACK(BeginTypeSection, section_size); |
| in_u32_leb128(ctx, &ctx->num_signatures, "type count"); |
| CALLBACK(OnTypeCount, ctx->num_signatures); |
| |
| for (uint32_t i = 0; i < ctx->num_signatures; ++i) { |
| Type form; |
| in_type(ctx, &form, "type form"); |
| RAISE_ERROR_UNLESS(form == Type::Func, "unexpected type form: %d", |
| static_cast<int>(form)); |
| |
| uint32_t num_params; |
| in_u32_leb128(ctx, &num_params, "function param count"); |
| |
| ctx->param_types.resize(num_params); |
| |
| for (uint32_t j = 0; j < num_params; ++j) { |
| Type param_type; |
| in_type(ctx, ¶m_type, "function param type"); |
| RAISE_ERROR_UNLESS(is_concrete_type(param_type), |
| "expected valid param type (got %d)", |
| static_cast<int>(param_type)); |
| ctx->param_types[j] = param_type; |
| } |
| |
| uint32_t num_results; |
| in_u32_leb128(ctx, &num_results, "function result count"); |
| RAISE_ERROR_UNLESS(num_results <= 1, "result count must be 0 or 1"); |
| |
| Type result_type = Type::Void; |
| if (num_results) { |
| in_type(ctx, &result_type, "function result type"); |
| RAISE_ERROR_UNLESS(is_concrete_type(result_type), |
| "expected valid result type: %d", |
| static_cast<int>(result_type)); |
| } |
| |
| Type* param_types = num_params ? ctx->param_types.data() : nullptr; |
| |
| CALLBACK(OnType, i, num_params, param_types, num_results, &result_type); |
| } |
| CALLBACK0(EndTypeSection); |
| } |
| |
| static void read_import_section(Context* ctx, uint32_t section_size) { |
| CALLBACK(BeginImportSection, section_size); |
| in_u32_leb128(ctx, &ctx->num_imports, "import count"); |
| CALLBACK(OnImportCount, ctx->num_imports); |
| for (uint32_t i = 0; i < ctx->num_imports; ++i) { |
| StringSlice module_name; |
| in_str(ctx, &module_name, "import module name"); |
| StringSlice field_name; |
| in_str(ctx, &field_name, "import field name"); |
| |
| uint32_t kind; |
| in_u32_leb128(ctx, &kind, "import kind"); |
| switch (static_cast<ExternalKind>(kind)) { |
| case ExternalKind::Func: { |
| uint32_t sig_index; |
| in_u32_leb128(ctx, &sig_index, "import signature index"); |
| RAISE_ERROR_UNLESS(sig_index < ctx->num_signatures, |
| "invalid import signature index"); |
| CALLBACK(OnImport, i, module_name, field_name); |
| CALLBACK(OnImportFunc, i, module_name, field_name, |
| ctx->num_func_imports, sig_index); |
| ctx->num_func_imports++; |
| break; |
| } |
| |
| case ExternalKind::Table: { |
| Type elem_type; |
| Limits elem_limits; |
| read_table(ctx, &elem_type, &elem_limits); |
| CALLBACK(OnImport, i, module_name, field_name); |
| CALLBACK(OnImportTable, i, module_name, field_name, |
| ctx->num_table_imports, elem_type, &elem_limits); |
| ctx->num_table_imports++; |
| break; |
| } |
| |
| case ExternalKind::Memory: { |
| Limits page_limits; |
| read_memory(ctx, &page_limits); |
| CALLBACK(OnImport, i, module_name, field_name); |
| CALLBACK(OnImportMemory, i, module_name, field_name, |
| ctx->num_memory_imports, &page_limits); |
| ctx->num_memory_imports++; |
| break; |
| } |
| |
| case ExternalKind::Global: { |
| Type type; |
| bool mutable_; |
| read_global_header(ctx, &type, &mutable_); |
| CALLBACK(OnImport, i, module_name, field_name); |
| CALLBACK(OnImportGlobal, i, module_name, field_name, |
| ctx->num_global_imports, type, mutable_); |
| ctx->num_global_imports++; |
| break; |
| } |
| |
| default: |
| RAISE_ERROR("invalid import kind: %d", kind); |
| } |
| } |
| CALLBACK0(EndImportSection); |
| } |
| |
| static void read_function_section(Context* ctx, uint32_t section_size) { |
| CALLBACK(BeginFunctionSection, section_size); |
| in_u32_leb128(ctx, &ctx->num_function_signatures, "function signature count"); |
| CALLBACK(OnFunctionCount, ctx->num_function_signatures); |
| for (uint32_t i = 0; i < ctx->num_function_signatures; ++i) { |
| uint32_t func_index = ctx->num_func_imports + i; |
| uint32_t sig_index; |
| in_u32_leb128(ctx, &sig_index, "function signature index"); |
| RAISE_ERROR_UNLESS(sig_index < ctx->num_signatures, |
| "invalid function signature index: %d", sig_index); |
| CALLBACK(OnFunction, func_index, sig_index); |
| } |
| CALLBACK0(EndFunctionSection); |
| } |
| |
| static void read_table_section(Context* ctx, uint32_t section_size) { |
| CALLBACK(BeginTableSection, section_size); |
| in_u32_leb128(ctx, &ctx->num_tables, "table count"); |
| RAISE_ERROR_UNLESS(ctx->num_tables <= 1, "table count (%d) must be 0 or 1", |
| ctx->num_tables); |
| CALLBACK(OnTableCount, ctx->num_tables); |
| for (uint32_t i = 0; i < ctx->num_tables; ++i) { |
| uint32_t table_index = ctx->num_table_imports + i; |
| Type elem_type; |
| Limits elem_limits; |
| read_table(ctx, &elem_type, &elem_limits); |
| CALLBACK(OnTable, table_index, elem_type, &elem_limits); |
| } |
| CALLBACK0(EndTableSection); |
| } |
| |
| static void read_memory_section(Context* ctx, uint32_t section_size) { |
| CALLBACK(BeginMemorySection, section_size); |
| in_u32_leb128(ctx, &ctx->num_memories, "memory count"); |
| RAISE_ERROR_UNLESS(ctx->num_memories <= 1, "memory count must be 0 or 1"); |
| CALLBACK(OnMemoryCount, ctx->num_memories); |
| for (uint32_t i = 0; i < ctx->num_memories; ++i) { |
| uint32_t memory_index = ctx->num_memory_imports + i; |
| Limits page_limits; |
| read_memory(ctx, &page_limits); |
| CALLBACK(OnMemory, memory_index, &page_limits); |
| } |
| CALLBACK0(EndMemorySection); |
| } |
| |
| static void read_global_section(Context* ctx, uint32_t section_size) { |
| CALLBACK(BeginGlobalSection, section_size); |
| in_u32_leb128(ctx, &ctx->num_globals, "global count"); |
| CALLBACK(OnGlobalCount, ctx->num_globals); |
| for (uint32_t i = 0; i < ctx->num_globals; ++i) { |
| uint32_t global_index = ctx->num_global_imports + i; |
| Type global_type; |
| bool mutable_; |
| read_global_header(ctx, &global_type, &mutable_); |
| CALLBACK(BeginGlobal, global_index, global_type, mutable_); |
| CALLBACK(BeginGlobalInitExpr, global_index); |
| read_init_expr(ctx, global_index); |
| CALLBACK(EndGlobalInitExpr, global_index); |
| CALLBACK(EndGlobal, global_index); |
| } |
| CALLBACK0(EndGlobalSection); |
| } |
| |
| static void read_export_section(Context* ctx, uint32_t section_size) { |
| CALLBACK(BeginExportSection, section_size); |
| in_u32_leb128(ctx, &ctx->num_exports, "export count"); |
| CALLBACK(OnExportCount, ctx->num_exports); |
| for (uint32_t i = 0; i < ctx->num_exports; ++i) { |
| StringSlice name; |
| in_str(ctx, &name, "export item name"); |
| |
| uint8_t external_kind; |
| in_u8(ctx, &external_kind, "export external kind"); |
| RAISE_ERROR_UNLESS(is_valid_external_kind(external_kind), |
| "invalid export external kind"); |
| |
| uint32_t item_index; |
| in_u32_leb128(ctx, &item_index, "export item index"); |
| switch (static_cast<ExternalKind>(external_kind)) { |
| case ExternalKind::Func: |
| RAISE_ERROR_UNLESS(item_index < num_total_funcs(ctx), |
| "invalid export func index: %d", item_index); |
| break; |
| case ExternalKind::Table: |
| RAISE_ERROR_UNLESS(item_index < num_total_tables(ctx), |
| "invalid export table index"); |
| break; |
| case ExternalKind::Memory: |
| RAISE_ERROR_UNLESS(item_index < num_total_memories(ctx), |
| "invalid export memory index"); |
| break; |
| case ExternalKind::Global: |
| RAISE_ERROR_UNLESS(item_index < num_total_globals(ctx), |
| "invalid export global index"); |
| break; |
| } |
| |
| CALLBACK(OnExport, i, static_cast<ExternalKind>(external_kind), item_index, |
| name); |
| } |
| CALLBACK0(EndExportSection); |
| } |
| |
| static void read_start_section(Context* ctx, uint32_t section_size) { |
| CALLBACK(BeginStartSection, section_size); |
| uint32_t func_index; |
| in_u32_leb128(ctx, &func_index, "start function index"); |
| RAISE_ERROR_UNLESS(func_index < num_total_funcs(ctx), |
| "invalid start function index"); |
| CALLBACK(OnStartFunction, func_index); |
| CALLBACK0(EndStartSection); |
| } |
| |
| static void read_elem_section(Context* ctx, uint32_t section_size) { |
| CALLBACK(BeginElemSection, section_size); |
| uint32_t num_elem_segments; |
| in_u32_leb128(ctx, &num_elem_segments, "elem segment count"); |
| CALLBACK(OnElemSegmentCount, num_elem_segments); |
| RAISE_ERROR_UNLESS(num_elem_segments == 0 || num_total_tables(ctx) > 0, |
| "elem section without table section"); |
| for (uint32_t i = 0; i < num_elem_segments; ++i) { |
| uint32_t table_index; |
| in_u32_leb128(ctx, &table_index, "elem segment table index"); |
| CALLBACK(BeginElemSegment, i, table_index); |
| CALLBACK(BeginElemSegmentInitExpr, i); |
| read_init_expr(ctx, i); |
| CALLBACK(EndElemSegmentInitExpr, i); |
| |
| uint32_t num_function_indexes; |
| in_u32_leb128(ctx, &num_function_indexes, |
| "elem segment function index count"); |
| CALLBACK(OnElemSegmentFunctionIndexCount, i, num_function_indexes); |
| for (uint32_t j = 0; j < num_function_indexes; ++j) { |
| uint32_t func_index; |
| in_u32_leb128(ctx, &func_index, "elem segment function index"); |
| CALLBACK(OnElemSegmentFunctionIndex, i, func_index); |
| } |
| CALLBACK(EndElemSegment, i); |
| } |
| CALLBACK0(EndElemSection); |
| } |
| |
| static void read_code_section(Context* ctx, uint32_t section_size) { |
| CALLBACK(BeginCodeSection, section_size); |
| in_u32_leb128(ctx, &ctx->num_function_bodies, "function body count"); |
| RAISE_ERROR_UNLESS(ctx->num_function_signatures == ctx->num_function_bodies, |
| "function signature count != function body count"); |
| CALLBACK(OnFunctionBodyCount, ctx->num_function_bodies); |
| for (uint32_t i = 0; i < ctx->num_function_bodies; ++i) { |
| uint32_t func_index = ctx->num_func_imports + i; |
| uint32_t func_offset = ctx->state.offset; |
| ctx->state.offset = func_offset; |
| CALLBACK(BeginFunctionBody, func_index); |
| uint32_t body_size; |
| in_u32_leb128(ctx, &body_size, "function body size"); |
| uint32_t body_start_offset = ctx->state.offset; |
| uint32_t end_offset = body_start_offset + body_size; |
| |
| uint32_t num_local_decls; |
| in_u32_leb128(ctx, &num_local_decls, "local declaration count"); |
| CALLBACK(OnLocalDeclCount, num_local_decls); |
| for (uint32_t k = 0; k < num_local_decls; ++k) { |
| uint32_t num_local_types; |
| in_u32_leb128(ctx, &num_local_types, "local type count"); |
| Type local_type; |
| in_type(ctx, &local_type, "local type"); |
| RAISE_ERROR_UNLESS(is_concrete_type(local_type), |
| "expected valid local type"); |
| CALLBACK(OnLocalDecl, k, num_local_types, local_type); |
| } |
| |
| read_function_body(ctx, end_offset); |
| |
| CALLBACK(EndFunctionBody, func_index); |
| } |
| CALLBACK0(EndCodeSection); |
| } |
| |
| static void read_data_section(Context* ctx, uint32_t section_size) { |
| CALLBACK(BeginDataSection, section_size); |
| uint32_t num_data_segments; |
| in_u32_leb128(ctx, &num_data_segments, "data segment count"); |
| CALLBACK(OnDataSegmentCount, num_data_segments); |
| RAISE_ERROR_UNLESS(num_data_segments == 0 || num_total_memories(ctx) > 0, |
| "data section without memory section"); |
| for (uint32_t i = 0; i < num_data_segments; ++i) { |
| uint32_t memory_index; |
| in_u32_leb128(ctx, &memory_index, "data segment memory index"); |
| CALLBACK(BeginDataSegment, i, memory_index); |
| CALLBACK(BeginDataSegmentInitExpr, i); |
| read_init_expr(ctx, i); |
| CALLBACK(EndDataSegmentInitExpr, i); |
| |
| uint32_t data_size; |
| const void* data; |
| in_bytes(ctx, &data, &data_size, "data segment data"); |
| CALLBACK(OnDataSegmentData, i, data, data_size); |
| CALLBACK(EndDataSegment, i); |
| } |
| CALLBACK0(EndDataSection); |
| } |
| |
| static void read_sections(Context* ctx) { |
| while (ctx->state.offset < ctx->state.size) { |
| uint32_t section_code; |
| uint32_t section_size; |
| /* Temporarily reset read_end to the full data size so the next section |
| * can be read. */ |
| ctx->read_end = ctx->state.size; |
| in_u32_leb128(ctx, §ion_code, "section code"); |
| in_u32_leb128(ctx, §ion_size, "section size"); |
| ctx->read_end = ctx->state.offset + section_size; |
| if (section_code >= kBinarySectionCount) { |
| RAISE_ERROR("invalid section code: %u; max is %u", section_code, |
| kBinarySectionCount - 1); |
| } |
| |
| BinarySection section = static_cast<BinarySection>(section_code); |
| |
| if (ctx->read_end > ctx->state.size) |
| RAISE_ERROR("invalid section size: extends past end"); |
| |
| if (ctx->last_known_section != BinarySection::Invalid && |
| section != BinarySection::Custom && |
| section <= ctx->last_known_section) { |
| RAISE_ERROR("section %s out of order", get_section_name(section)); |
| } |
| |
| CALLBACK(BeginSection, section, section_size); |
| |
| #define V(Name, name, code) \ |
| case BinarySection::Name: \ |
| read_##name##_section(ctx, section_size); \ |
| break; |
| |
| switch (section) { |
| WABT_FOREACH_BINARY_SECTION(V) |
| |
| default: |
| assert(0); |
| break; |
| } |
| |
| #undef V |
| |
| if (ctx->state.offset != ctx->read_end) { |
| RAISE_ERROR("unfinished section (expected end: 0x%" PRIzx ")", |
| ctx->read_end); |
| } |
| |
| if (section != BinarySection::Custom) |
| ctx->last_known_section = section; |
| } |
| } |
| |
| Result read_binary(const void* data, |
| size_t size, |
| BinaryReader* reader, |
| const ReadBinaryOptions* options) { |
| BinaryReaderLogging logging_reader(options->log_stream, reader); |
| Context context; |
| /* all the macros assume a Context* named ctx */ |
| Context* ctx = &context; |
| ctx->state.data = static_cast<const uint8_t*>(data); |
| ctx->state.size = ctx->read_end = size; |
| ctx->state.offset = 0; |
| ctx->reader = options->log_stream ? &logging_reader : reader; |
| ctx->options = options; |
| ctx->last_known_section = BinarySection::Invalid; |
| |
| if (setjmp(ctx->error_jmp_buf) == 1) { |
| return Result::Error; |
| } |
| |
| ctx->reader->OnSetState(&ctx->state); |
| |
| uint32_t magic; |
| in_u32(ctx, &magic, "magic"); |
| RAISE_ERROR_UNLESS(magic == WABT_BINARY_MAGIC, "bad magic value"); |
| uint32_t version; |
| in_u32(ctx, &version, "version"); |
| RAISE_ERROR_UNLESS(version == WABT_BINARY_VERSION, |
| "bad wasm file version: %#x (expected %#x)", version, |
| WABT_BINARY_VERSION); |
| |
| CALLBACK(BeginModule, version); |
| read_sections(ctx); |
| CALLBACK0(EndModule); |
| return Result::Ok; |
| } |
| |
| } // namespace wabt |