wasm: add WASM 1.0 binary parser and disassembler

Implements a WASM 1.0 binary parser for KolibriOS written in x86 FASM.
Parses all 12 sections of the WASM binary format and prints
disassembly output. Tested against real WASM binaries.

Sections implemented: type, import, function, table, memory, global,
export, start, element, data, custom. Code section parsing is planned
for the next commit.
This commit is contained in:
Daniel Joseph committed 2026-03-30 19:29:52 +01:00
1 parent f305d3425d
commit e16589e95e
10 files changed
+2026

No files matched your search

+7
View File
@@ -0,0 +1,7 @@
*.swp
*.swo
*.swn
*~
*.bin
*.o
*.obj
+178
View File
@@ -0,0 +1,178 @@
# kolibrios-wasm
A WebAssembly 1.0 runtime for [KolibriOS](http://kolibrios.org/), written in x86 FASM assembly.
## Status
Work in progress. Current state:
- Binary parser: WIP, tested on Linux and KolibriOS
- Validator: Not Started
- Interpreter: Not Started
## What it does
Parses WebAssembly 1.0 binary files according to the
[WebAssembly 1.0 spec](https://webassembly.github.io/multi-value/core/_download/WebAssembly.pdf).
The parser currently:
- Validates the magic number and version header
- Fully parses all 12 WASM 1.0 sections
- Decodes LEB128 u32, s32 integers as specified in the spec
- Validates section boundaries — catches malformed binaries
- Prints human-readable disassembly of all decoded data
- Disassembles all 172 WASM 1.0 opcodes in function bodies (code section in progress)
- Tested against real WASM binaries on both Linux and KolibriOS (QEMU)
## Project structure
```
src/
core/
wasm_const.inc # Wasm 1.0 binary format constants
wasm_name.inc # Value type name strings
wasm_func.inc # LEB128 decoder
wasm_parser.inc # Binary parser
kolibrios/
includes/ # KolibriOS standard includes
kolibrios_main.asm # KolibriOS entry point
wasm_kolibrios_interface.inc # Console output macros
linux/
linux_main.asm # Linux entry point
wasm_linux_interface.inc # libc output macros
tests/
simple.wasm
ekun.wasm
```
## Building
### Linux
Requires FASM and GCC (32-bit).
```bash
fasm src/linux/linux_main.asm linux_main.o
gcc -m32 -o parser linux_main.o -nostartfiles -lc -Wl,-e,_start
./parser tests/simple.wasm
```
### KolibriOS
Requires FASM.
```bash
fasm src/kolibrios/kolibrios_main.asm wasm_dis.kex
```
Copy `wasm_dis.kex` to KolibriOS and run from the console:
```
wasm_dis test.wasm
```
## Output
Both platforms produce identical output. Example against a real wasm binary:
```
./src/parser tests/ekun.wasm
Parsing Sections...
Section 1: Type (288 bytes):
Vec of functype (Length: 45):
functype[0]:
Params(0):
Result(0):
functype[1]:
Params(3): i32 i32 i32
Result(1): i32
functype[2]:
Params(1): i32
Result(1): i32
functype[3]:
Params(1): i32
Result(0):
functype[4]:
Params(2): i32 i32
Result(1): i32
functype[5]:
Params(0):
Result(1): i32
functype[6]:
Params(2): i32 i32
Result(0):
functype[7]:
Params(3): i32 i32 i32
Result(0):
functype[8]:
Params(5): i32 i64 i64 i64 i64
Result(0):
functype[9]:
Params(4): i32 i32 i32 i32
Result(1): i32
Section 2: Import (260 bytes)
Vec of Import (Length: 9)
module: env name: exit Import type: typeidx (3)
module: env name: emscripten_asm_const_int Import type: typeidx (1)
module: env name: _emscripten_memcpy_js Import type: typeidx (7)
module: env name: emscripten_date_now Import type: typeidx (21)
module: wasi_snapshot_preview1 name: fd_close Import type: typeidx (2)
module: wasi_snapshot_preview1 name: fd_write Import type: typeidx (9)
module: wasi_snapshot_preview1 name: fd_read Import type: typeidx (9)
module: env name: emscripten_resize_heap Import type: typeidx (2)
module: wasi_snapshot_preview1 name: fd_seek Import type: typeidx (12)
Section 3: Function (249 bytes)
Vector of Typeidx: 0 3 3 3 3 2 1 3 4 4 6 3 3 0 2 3 3 0 0 5 6 5 10 3 3 2 3 2 2 6 5 10 2 5 6 1 7 3 5 4 3 4 3 1 1 9 3 2 4 4 5 2 4 6 1 1 1 2 2 2 6 6 2 0 3 0 3 5 3 6 3 6 0 3 7 6 7 7 7 7 3 0 3 0 4 3 0 5 0 0 1 0 0 7 0 0 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 11 0 0 0 0 0 0 0 0 0 0 0 2 5 1 1 5 1 2 3 2 4 4 4 4 2 2 3 1 9 2 2 9 1 4 1 1 11 22 11 15 15 11 23 24 16 16 11 25 26 27 28 5 5 5 0 2 2 1 13 13 1 4 4 2 1 1 1 29 2 17 8 14 30 8 31 10 2 32 33 34 10 35 4 17 12 36 7 2 10 37 19 19 38 1 18 6 39 1 9 1 2 1 4 5 2 2 1 3 4 4 6 8 14 20 20 8 40 41 6 6 5 5 14 8 8 8 42 3 3 2 5 43 12 44
Section 4: Table (4 bytes)
Vector of Tabletype (Length: 1):
Tabletype with limit min: (44), and unbounded max
Section 5: Memory (6 bytes)
Vector of Memtype (Length: 1):
Memtype: with limit min: (258), and max: (258)
Section 6: Global (8 bytes)
Vector of Global (Length: 1):
Globaltype: i32 (var)
init: i32.const 99936
Section 7: Export (198 bytes)
Vector of Export (Length: 9):
name: memory Export type: memidx (0)
name: __wasm_call_ctors Export type: funcidx (9)
name: __indirect_function_table Export type: tableidx (0)
name: run_from_js Export type: funcidx (71)
name: disassemble_from_js Export type: funcidx (73)
name: _emscripten_stack_restore Export type: funcidx (250)
name: _emscripten_stack_alloc Export type: funcidx (251)
name: emscripten_stack_get_current Export type: funcidx (252)
name: dynCall_jiji Export type: funcidx (254)
Section 9: Element (61 bytes)
Vector of Elem (Length: 1)
tableidx: 0
offset: i32.const 1
vector of funcidx: 109 110 111 112 113 107 108 114 98 117 119 123 131 120 122 125 126 127 128 118 129 130 105 106 116 74 103 104 132 100 101 83 85 86 87 88 178 179 181 182 216 217 221
```
## Dependencies
- [FASM](https://flatassembler.net/)
- GCC with 32-bit support: Linux build only, for linking libc
- KolibriOS with console.obj: KolibriOS build only
## Notes
The Linux build exists purely for development and testing. The KolibriOS build
is the actual target. Both builds share the same core parser in `src/core/` and
produce identical output, only the platform interface differs.
This project is being developed as a GSoC 2026 contribution for KolibriOS.
@@ -0,0 +1,251 @@
; SPDX-License-Identifier: GPL-2.0-only
; Reshare - Shared Resources Daemon
;
; Copyright (C) 2026 KolibriOS Team
;--------------------------------------------------------------------------
; wasm_const.inc - WebAssembly 1.0 Binary Format Constants
; Reference: Chapter 5 (Binary Format) of Wasm 1.0 spec
; Reference Url: https://webassembly.github.io/multi-value/core/_download/WebAssembly.pdf
;---------------------------------------------------------------------------
; GENERAL
WASM_MAGIC = 0x6d736100 ; '\0asm' in little endian
WASM_VERSION = 0x00000001
; SECTIONS
SECTION_CUSTOM = 0x00
SECTION_TYPE = 0x01
SECTION_IMPORT = 0x02
SECTION_FUNCTION = 0x03
SECTION_TABLE = 0x04
SECTION_MEMORY = 0x05
SECTION_GLOBAL = 0x06
SECTION_EXPORT = 0x07
SECTION_START = 0x08
SECTION_ELEMENT = 0x09
SECTION_CODE = 0x0A
SECTION_DATA = 0x0B
; Value Types
VAL_I32 = 0x7F
VAL_I64 = 0x7E
VAL_F32 = 0x7D
VAL_F64 = 0x7C
; Import and Export Sections
FUNC_DESC = 0x00
TABLE_DESC = 0x01
MEM_DESC = 0x02
GLOBAL_DESC = 0x03
; blocktype
BLOCK_EMPTY = 0x40
; function type
FUNC_START = 0x60
; limits
LIMIT_MIN_ONLY = 0x00
LIMIT_MIN_MAX = 0x01
; table type
ELEM_FUNCREF = 0x70
; Global type
GLOBAL_CONST = 0x00
GLOBAL_VAR = 0x01
; Instructions
; Control Instructions
OP_UNREACHABLE = 0x00
OP_NOP = 0x01
OP_BLOCK = 0x02
OP_LOOP = 0x03
OP_IF = 0x04
OP_ELSE = 0x05
OP_BR = 0x0C
OP_BR_IF = 0x0D
OP_BR_TABLE = 0x0E
OP_RETURN = 0x0F
OP_END = 0x0B
OP_CALL = 0x10
OP_CALL_INDIRECT = 0x11
; Parametric instructions
OP_DROP = 0x1A
OP_SELECT = 0x1B
; Variable Instructions
OP_LOCAL_GET = 0x20
OP_LOCAL_SET = 0x21
OP_LOCAL_TEE = 0x22
OP_GLOBAL_GET = 0x23
OP_GLOBAL_SET = 0x24
;memor8y instruction
OP_I32_LOAD = 0x28
OP_I64_LOAD = 0x29
OP_F32_LOAD = 0x2A
OP_F64_LOAD = 0x2B
OP_I32_LOAD8_S = 0x2C
OP_I32_LOAD8_U = 0x2D
OP_I32_LOAD16_S = 0x2E
OP_I32_LOAD16_U = 0x2F
OP_I64_LOAD8_S = 0x30
OP_I64_LOAD8_U = 0x31
OP_I64_LOAD16_S = 0x32
OP_I64_LOAD16_U = 0x33
OP_I64_LOAD32_S = 0x34
OP_I64_LOAD32_U = 0x35
OP_I32_STORE = 0x36
OP_I64_STORE = 0x37
OP_F32_STORE = 0x38
OP_F64_STORE = 0x39
OP_I32_STORE8 = 0x3A
OP_I32_STORE16 = 0x3B
OP_I64_STORE8 = 0x3C
OP_I64_STORE16 = 0x3D
OP_I64_STORE32 = 0x3E
OP_MEMORY_SIZE = 0x3F
OP_MEMORY_GROW = 0x40
; Numeric Instructions
OP_I32_CONST = 0x41
OP_I64_CONST = 0x42
OP_F32_CONST = 0x43
OP_F64_CONST = 0x44
OP_I32_EQZ = 0x45
OP_I32_EQ = 0x46
OP_I32_NE = 0x47
OP_I32_LT_S = 0x48
OP_I32_LT_U = 0x49
OP_I32_GT_S = 0x4A
OP_I32_GT_U = 0x4B
OP_I32_LE_S = 0x4C
OP_I32_LE_U = 0x4D
OP_I32_GE_S = 0x4E
OP_I32_GE_U = 0x4F
OP_I64_EQZ = 0x50
OP_I64_EQ = 0x51
OP_I64_NE = 0x52
OP_I64_LT_S = 0x53
OP_I64_LT_U = 0x54
OP_I64_GT_S = 0x55
OP_I64_GT_U = 0x56
OP_I64_LE_S = 0x57
OP_I64_LE_U = 0x58
OP_I64_GE_S = 0x59
OP_I64_GE_U = 0x5A
OP_F32_EQ = 0x5B
OP_F32_NE = 0x5C
OP_F32_LT = 0x5D
OP_F32_GT = 0x5E
OP_F32_LE = 0x5F
OP_F32_GE = 0x60
OP_F64_EQ = 0x61
OP_F64_NE = 0x62
OP_F64_LT = 0x63
OP_F64_GT = 0x64
OP_F64_LE = 0x65
OP_F64_GE = 0x66
OP_I32_CLZ = 0x67
OP_I32_CTZ = 0x68
OP_I32_POPCNT = 0x69
OP_I32_ADD = 0x6A
OP_I32_SUB = 0x6B
OP_I32_MUL = 0x6C
OP_I32_DIV_S = 0x6D
OP_I32_DIV_U = 0x6E
OP_I32_REM_S = 0x6F
OP_I32_REM_U = 0x70
OP_I32_AND = 0x71
OP_I32_OR = 0x72
OP_I32_XOR = 0x73
OP_I32_SHL = 0x74
OP_I32_SHR_S = 0x75
OP_I32_SHR_U = 0x76
OP_I32_ROTL = 0x77
OP_I32_ROTR = 0x78
OP_I64_CLZ = 0x79
OP_I64_CTZ = 0x7A
OP_I64_POPCNT = 0x7B
OP_I64_ADD = 0x7C
OP_I64_SUB = 0x7D
OP_I64_MUL = 0x7E
OP_I64_DIV_S = 0x7F
OP_I64_DIV_U = 0x80
OP_I64_REM_S = 0x81
OP_I64_REM_U = 0x82
OP_I64_AND = 0x83
OP_I64_OR = 0x84
OP_I64_XOR = 0x85
OP_I64_SHL = 0x86
OP_I64_SHR_S = 0x87
OP_I64_SHR_U = 0x88
OP_I64_ROTL = 0x89
OP_I64_ROTR = 0x8A
OP_F32_ABS = 0x8B
OP_F32_NEG = 0x8C
OP_F32_CEIL = 0x8D
OP_F32_FLOOR = 0x8E
OP_F32_TRUNC = 0x8F
OP_F32_NEAREST = 0x90
OP_F32_SQRT = 0x91
OP_F32_ADD = 0x92
OP_F32_SUB = 0x93
OP_F32_MUL = 0x94
OP_F32_DIV = 0x95
OP_F32_MIN = 0x96
OP_F32_MAX = 0x97
OP_F32_COPYSIGN = 0x98
OP_F64_ABS = 0x99
OP_F64_NEG = 0x9A
OP_F64_CEIL = 0x9B
OP_F64_FLOOR = 0x9C
OP_F64_TRUNC = 0x9D
OP_F64_NEAREST = 0x9E
OP_F64_SQRT = 0x9F
OP_F64_ADD = 0xA0
OP_F64_SUB = 0xA1
OP_F64_MUL = 0xA2
OP_F64_DIV = 0xA3
OP_F64_MIN = 0xA4
OP_F64_MAX = 0xA5
OP_F64_COPYSIGN = 0xA6
; Conversions
OP_I32_WRAP_I64 = 0xA7
OP_I32_TRUNC_F32_S = 0xA8
OP_I32_TRUNC_F32_U = 0xA9
OP_I32_TRUNC_F64_S = 0xAA
OP_I32_TRUNC_F64_U = 0xAB
OP_I64_EXTEND_I32_S = 0xAC
OP_I64_EXTEND_I32_U = 0xAD
OP_I64_TRUNC_F32_S = 0xAE
OP_I64_TRUNC_F32_U = 0xAF
OP_I64_TRUNC_F64_S = 0xB0
OP_I64_TRUNC_F64_U = 0xB1
OP_F32_CONVERT_I32_S = 0xB2
OP_F32_CONVERT_I32_U = 0xB3
OP_F32_CONVERT_I64_S = 0xB4
OP_F32_CONVERT_I64_U = 0xB5
OP_F32_DEMOTE_F64 = 0xB6
OP_F64_CONVERT_I32_S = 0xB7
OP_F64_CONVERT_I32_U = 0xB8
OP_F64_CONVERT_I64_S = 0xB9
OP_F64_CONVERT_I64_U = 0xBA
OP_F64_PROMOTE_F32 = 0xBB
OP_I32_REINTERPRET_F32 = 0xBC
OP_I64_REINTERPRET_F64 = 0xBD
OP_F32_REINTERPRET_I32 = 0xBE
OP_F64_REINTERPRET_I64 = 0xBF
@@ -0,0 +1,100 @@
; SPDX-License-Identifier: GPL-2.0-only
; Reshare - Shared Resources Daemon
;
; Copyright (C) 2026 KolibriOS Team
;----------------------------------------------------
; wasm_func.inc: Utility functions for wasm
;---------------------------------------------------
;---------------------------------------------------
; decode_leb128_u32
;
; decode an unsigned leb128 number
; use the algorithm described by Wikipedia: https://en.wikipedia.org/wiki/LEB128
;
; accept: ebx a pointer to the address to decode
; move ebx past the leb128 number
; wasm allows multiple 0 to follow unsigned leb128, our decoder just discard it
; automatically because the shift would overflow the register
; --------------------------------------------------
leb128_decode_u32:
push ecx
push esi
push edx
xor eax, eax ; accumulator
xor ecx, ecx ; shift amount
.loop:
cmp ecx, 35
jge .error
movzx esi, byte [ebx]
advance
mov edx, esi
and edx, 0x7F
shl edx, cl
or eax, edx
add ecx, 7
cmp ecx, 35
jne .continue
test esi, 0xf0
jnz .terminal_error
.continue:
test esi, 0x80
jnz .loop
pop edx
pop esi
pop ecx
ret
.terminal_error:
println "Error while decoding leb128 integer: u32 terminal bytes can not have upper bits set"
jmp parse_error
.error:
println "Error while decoding leb128 integer: u32 must not exceed 5 bytes"
jmp parse_error
leb128_decode_s32:
push ecx
push esi
push edx
xor eax, eax ; accumulator
xor ecx, ecx ; shift amount
.loop:
cmp ecx, 35
jge .error
movzx esi, byte [ebx]
advance
mov edx, esi
and edx, 0x7F
shl edx, cl
or eax, edx
add ecx, 7
test esi, 0x80
jnz .loop
cmp ecx, 32
jge .done
test esi, 0x40
jz .done
xor esi, esi
not esi
shl esi, cl
or eax, esi
.done:
pop edx
pop esi
pop ecx
ret
.error:
println "Error while decoding leb128 integer: i32 must not exceed 4 bytes"
jmp parse_error
leb128_decode_s64:
push ecx
push esi
push edx
push ebp
push edi
xor eax, eax
.loop:
@@ -0,0 +1,411 @@
; SPDX-License-Identifier: GPL-2.0-only
; Reshare - Shared Resources Daemon
;
; Copyright (C) 2026 KolibriOS Team
; valtype
str_i32 db "i32", 0
str_i64 db "i64", 0
str_f32 db "f32", 0
str_f64 db "f64", 0
valtype:
dd VAL_I32, str_i32
dd VAL_I64, str_i64
dd VAL_F32, str_f32
dd VAL_F64, str_f64
dd 0
str_op_unreachable db "unreachable", 0
str_op_nop db "nop", 0
str_op_block db "block", 0
str_op_loop db "loop", 0
str_op_if db "if", 0
str_op_br db "br", 0
str_op_br_if db "br_if", 0
str_op_br_table db "br_table", 0
str_op_return db "return", 0
str_op_call db "call", 0
str_op_call_indirect db "call_indirect", 0
str_op_drop db "drop", 0
str_op_select db "select", 0
str_op_local_get db "local.get", 0
str_op_local_set db "local.set", 0
str_op_local_tee db "local.tee", 0
str_op_global_get db "global.get", 0
str_op_global_set db "global.set", 0
str_op_i32_load db "i32.load", 0
str_op_i64_load db "i64.load", 0
str_op_f32_load db "f32.load", 0
str_op_f64_load db "f64.load", 0
str_op_i32_load8_s db "i32.load8_s", 0
str_op_i32_load8_u db "i32.load8_u", 0
str_op_i32_load16_s db "i32.load16_s", 0
str_op_i32_load16_u db "i32.load16_u", 0
str_op_i64_load8_s db "i64.load8_s", 0
str_op_i64_load8_u db "i64.load8_u", 0
str_op_i64_load16_s db "i64.load16_s", 0
str_op_i64_load16_u db "i64.load16_u", 0
str_op_i64_load32_s db "i64.load32_s", 0
str_op_i64_load32_u db "i64.load32_u", 0
str_op_i32_store db "i32.store", 0
str_op_i64_store db "i64.store", 0
str_op_f32_store db "f32.store", 0
str_op_f64_store db "f64.store", 0
str_op_i32_store8 db "i32.store8", 0
str_op_i32_store16 db "i32.store16", 0
str_op_i64_store8 db "i64.store8", 0
str_op_i64_store16 db "i64.store16", 0
str_op_i64_store32 db "i64.store32", 0
str_op_memory_size db "memory.size", 0
str_op_memory_grow db "memory.grow", 0
str_op_i32_const db "i32.const", 0
str_op_i64_const db "i64.const", 0
str_op_f32_const db "f32.const", 0
str_op_f64_const db "f64.const", 0
str_op_i32_eqz db "i32.eqz", 0
str_op_i32_eq db "i32.eq", 0
str_op_i32_ne db "i32.ne", 0
str_op_i32_lt_s db "i32.lt_s", 0
str_op_i32_lt_u db "i32.lt_u", 0
str_op_i32_gt_s db "i32.gt_s", 0
str_op_i32_gt_u db "i32.gt_u", 0
str_op_i32_le_s db "i32.le_s", 0
str_op_i32_le_u db "i32.le_u", 0
str_op_i32_ge_s db "i32.ge_s", 0
str_op_i32_ge_u db "i32.ge_u", 0
str_op_i64_eqz db "i64.eqz", 0
str_op_i64_eq db "i64.eq", 0
str_op_i64_ne db "i64.ne", 0
str_op_i64_lt_s db "i64.lt_s", 0
str_op_i64_lt_u db "i64.lt_u", 0
str_op_i64_gt_s db "i64.gt_s", 0
str_op_i64_gt_u db "i64.gt_u", 0
str_op_i64_le_s db "i64.le_s", 0
str_op_i64_le_u db "i64.le_u", 0
str_op_i64_ge_s db "i64.ge_s", 0
str_op_i64_ge_u db "i64.ge_u", 0
str_op_f32_eq db "f32.eq", 0
str_op_f32_ne db "f32.ne", 0
str_op_f32_lt db "f32.lt", 0
str_op_f32_gt db "f32.gt", 0
str_op_f32_le db "f32.le", 0
str_op_f32_ge db "f32.ge", 0
str_op_f64_eq db "f64.eq", 0
str_op_f64_ne db "f64.ne", 0
str_op_f64_lt db "f64.lt", 0
str_op_f64_gt db "f64.gt", 0
str_op_f64_le db "f64.le", 0
str_op_f64_ge db "f64.ge", 0
str_op_i32_clz db "i32.clz", 0
str_op_i32_ctz db "i32.ctz", 0
str_op_i32_popcnt db "i32.popcnt", 0
str_op_i32_add db "i32.add", 0
str_op_i32_sub db "i32.sub", 0
str_op_i32_mul db "i32.mul", 0
str_op_i32_div_s db "i32.div_s", 0
str_op_i32_div_u db "i32.div_u", 0
str_op_i32_rem_s db "i32.rem_s", 0
str_op_i32_rem_u db "i32.rem_u", 0
str_op_i32_and db "i32.and", 0
str_op_i32_or db "i32.or", 0
str_op_i32_xor db "i32.xor", 0
str_op_i32_shl db "i32.shl", 0
str_op_i32_shr_s db "i32.shr_s", 0
str_op_i32_shr_u db "i32.shr_u", 0
str_op_i32_rotl db "i32.rotl", 0
str_op_i32_rotr db "i32.rotr", 0
str_op_i64_clz db "i64.clz", 0
str_op_i64_ctz db "i64.ctz", 0
str_op_i64_popcnt db "i64.popcnt", 0
str_op_i64_add db "i64.add", 0
str_op_i64_sub db "i64.sub", 0
str_op_i64_mul db "i64.mul", 0
str_op_i64_div_s db "i64.div_s", 0
str_op_i64_div_u db "i64.div_u", 0
str_op_i64_rem_s db "i64.rem_s", 0
str_op_i64_rem_u db "i64.rem_u", 0
str_op_i64_and db "i64.and", 0
str_op_i64_or db "i64.or", 0
str_op_i64_xor db "i64.xor", 0
str_op_i64_shl db "i64.shl", 0
str_op_i64_shr_s db "i64.shr_s", 0
str_op_i64_shr_u db "i64.shr_u", 0
str_op_i64_rotl db "i64.rotl", 0
str_op_i64_rotr db "i64.rotr", 0
str_op_f32_abs db "f32.abs", 0
str_op_f32_neg db "f32.neg", 0
str_op_f32_ceil db "f32.ceil", 0
str_op_f32_floor db "f32.floor", 0
str_op_f32_trunc db "f32.trunc", 0
str_op_f32_nearest db "f32.nearest", 0
str_op_f32_sqrt db "f32.sqrt", 0
str_op_f32_add db "f32.add", 0
str_op_f32_sub db "f32.sub", 0
str_op_f32_mul db "f32.mul", 0
str_op_f32_div db "f32.div", 0
str_op_f32_min db "f32.min", 0
str_op_f32_max db "f32.max", 0
str_op_f32_copysign db "f32.copysign", 0
str_op_f64_abs db "f64.abs", 0
str_op_f64_neg db "f64.neg", 0
str_op_f64_ceil db "f64.ceil", 0
str_op_f64_floor db "f64.floor", 0
str_op_f64_trunc db "f64.trunc", 0
str_op_f64_nearest db "f64.nearest", 0
str_op_f64_sqrt db "f64.sqrt", 0
str_op_f64_add db "f64.add", 0
str_op_f64_sub db "f64.sub", 0
str_op_f64_mul db "f64.mul", 0
str_op_f64_div db "f64.div", 0
str_op_f64_min db "f64.min", 0
str_op_f64_max db "f64.max", 0
str_op_f64_copysign db "f64.copysign", 0
str_op_i32_wrap_i64 db "i32.wrap_i64", 0
str_op_i32_trunc_f32_s db "i32.trunc_f32_s", 0
str_op_i32_trunc_f32_u db "i32.trunc_f32_u", 0
str_op_i32_trunc_f64_s db "i32.trunc_f64_s", 0
str_op_i32_trunc_f64_u db "i32.trunc_f64_u", 0
str_op_i64_extend_i32_s db "i64.extend_i32_s", 0
str_op_i64_extend_i32_u db "i64.extend_i32_u", 0
str_op_i64_trunc_f32_s db "i64.trunc_f32_s", 0
str_op_i64_trunc_f32_u db "i64.trunc_f32_u", 0
str_op_i64_trunc_f64_s db "i64.trunc_f64_s", 0
str_op_i64_trunc_f64_u db "i64.trunc_f64_u", 0
str_op_f32_convert_i32_s db "f32.convert_i32_s", 0
str_op_f32_convert_i32_u db "f32.convert_i32_u", 0
str_op_f32_convert_i64_s db "f32.convert_i64_s", 0
str_op_f32_convert_i64_u db "f32.convert_i64_u", 0
str_op_f32_demote_f64 db "f32.demote_f64", 0
str_op_f64_convert_i32_s db "f64.convert_i32_s", 0
str_op_f64_convert_i32_u db "f64.convert_i32_u", 0
str_op_f64_convert_i64_s db "f64.convert_i64_s", 0
str_op_f64_convert_i64_u db "f64.convert_i64_u", 0
str_op_f64_promote_f32 db "f64.promote_f32", 0
str_op_i32_reinterpret_f32 db "i32.reinterpret_f32", 0
str_op_i64_reinterpret_f64 db "i64.reinterpret_f64", 0
str_op_f32_reinterpret_i32 db "f32.reinterpret_i32", 0
str_op_f64_reinterpret_i64 db "f64.reinterpret_i64", 0
str_invalid db "<invalid>", 0
opcode_str_table:
dd str_op_unreachable
dd str_op_nop
dd str_op_block
dd str_op_loop
dd str_op_if
rept 7 { dd str_invalid } ; shouldn't be printed
dd str_op_br
dd str_op_br_if
dd str_op_br_table
dd str_op_return
dd str_op_call
dd str_op_call_indirect
rept 8 { dd str_invalid }
dd str_op_drop
dd str_op_select
rept 4 { dd str_invalid }
dd str_op_local_get
dd str_op_local_set
dd str_op_local_tee
dd str_op_global_get
dd str_op_global_set
rept 3 {dd str_invalid}
dd str_op_i32_load
dd str_op_i64_load
dd str_op_f32_load
dd str_op_f64_load
dd str_op_i32_load8_s
dd str_op_i32_load8_u
dd str_op_i32_load16_s
dd str_op_i32_load16_u
dd str_op_i64_load8_s
dd str_op_i64_load8_u
dd str_op_i64_load16_s
dd str_op_i64_load16_u
dd str_op_i64_load32_s
dd str_op_i64_load32_u
dd str_op_i32_store
dd str_op_i64_store
dd str_op_f32_store
dd str_op_f64_store
dd str_op_i32_store8
dd str_op_i32_store16
dd str_op_i64_store8
dd str_op_i64_store16
dd str_op_i64_store32
dd str_op_memory_size
dd str_op_memory_grow
dd str_op_i32_const
dd str_op_i64_const
dd str_op_f32_const
dd str_op_f64_const
dd str_op_i32_eqz
dd str_op_i32_eq
dd str_op_i32_ne
dd str_op_i32_lt_s
dd str_op_i32_lt_u
dd str_op_i32_gt_s
dd str_op_i32_gt_u
dd str_op_i32_le_s
dd str_op_i32_le_u
dd str_op_i32_ge_s
dd str_op_i32_ge_u
dd str_op_i64_eqz
dd str_op_i64_eq
dd str_op_i64_ne
dd str_op_i64_lt_s
dd str_op_i64_lt_u
dd str_op_i64_gt_s
dd str_op_i64_gt_u
dd str_op_i64_le_s
dd str_op_i64_le_u
dd str_op_i64_ge_s
dd str_op_i64_ge_u
dd str_op_f32_eq
dd str_op_f32_ne
dd str_op_f32_lt
dd str_op_f32_gt
dd str_op_f32_le
dd str_op_f32_ge
dd str_op_f64_eq
dd str_op_f64_ne
dd str_op_f64_lt
dd str_op_f64_gt
dd str_op_f64_le
dd str_op_f64_ge
dd str_op_i32_clz
dd str_op_i32_ctz
dd str_op_i32_popcnt
dd str_op_i32_add
dd str_op_i32_sub
dd str_op_i32_mul
dd str_op_i32_div_s
dd str_op_i32_div_u
dd str_op_i32_rem_s
dd str_op_i32_rem_u
dd str_op_i32_and
dd str_op_i32_or
dd str_op_i32_xor
dd str_op_i32_shl
dd str_op_i32_shr_s
dd str_op_i32_shr_u
dd str_op_i32_rotl
dd str_op_i32_rotr
dd str_op_i64_clz
dd str_op_i64_ctz
dd str_op_i64_popcnt
dd str_op_i64_add
dd str_op_i64_sub
dd str_op_i64_mul
dd str_op_i64_div_s
dd str_op_i64_div_u
dd str_op_i64_rem_s
dd str_op_i64_rem_u
dd str_op_i64_and
dd str_op_i64_or
dd str_op_i64_xor
dd str_op_i64_shl
dd str_op_i64_shr_s
dd str_op_i64_shr_u
dd str_op_i64_rotl
dd str_op_i64_rotr
dd str_op_f32_abs
dd str_op_f32_neg
dd str_op_f32_ceil
dd str_op_f32_floor
dd str_op_f32_trunc
dd str_op_f32_nearest
dd str_op_f32_sqrt
dd str_op_f32_add
dd str_op_f32_sub
dd str_op_f32_mul
dd str_op_f32_div
dd str_op_f32_min
dd str_op_f32_max
dd str_op_f32_copysign
dd str_op_f64_abs
dd str_op_f64_neg
dd str_op_f64_ceil
dd str_op_f64_floor
dd str_op_f64_trunc
dd str_op_f64_nearest
dd str_op_f64_sqrt
dd str_op_f64_add
dd str_op_f64_sub
dd str_op_f64_mul
dd str_op_f64_div
dd str_op_f64_min
dd str_op_f64_max
dd str_op_f64_copysign
dd str_op_i32_wrap_i64
dd str_op_i32_trunc_f32_s
dd str_op_i32_trunc_f32_u
dd str_op_i32_trunc_f64_s
dd str_op_i32_trunc_f64_u
dd str_op_i64_extend_i32_s
dd str_op_i64_extend_i32_u
dd str_op_i64_trunc_f32_s
dd str_op_i64_trunc_f32_u
dd str_op_i64_trunc_f64_s
dd str_op_i64_trunc_f64_u
dd str_op_f32_convert_i32_s
dd str_op_f32_convert_i32_u
dd str_op_f32_convert_i64_s
dd str_op_f32_convert_i64_u
dd str_op_f32_demote_f64
dd str_op_f64_convert_i32_s
dd str_op_f64_convert_i32_u
dd str_op_f64_convert_i64_s
dd str_op_f64_convert_i64_u
dd str_op_f64_promote_f32
dd str_op_i32_reinterpret_f32
dd str_op_i64_reinterpret_f64
dd str_op_f32_reinterpret_i32
dd str_op_f64_reinterpret_i64
assert ($ - opcode_str_table) = (192 * 4)
opcode_parse_table:
dd parse_op_simple ;0x00
dd parse_op_simple ; 0x01
dd parse_op_block ; 0x02
dd parse_op_block ; 0x03
dd parse_op_if ; 0x04
rept 7 { dd parse_error }; no opcode taken or opcode are part of other opcode
dd parse_op_u32 ; 0x0c
dd parse_op_u32 ; 0x0d
dd parse_op_br_table ; 0x0e
dd parse_op_simple ; 0x0f
dd parse_op_u32 ; 0x10
dd parse_call_indirect ; 0x11
rept 8 { dd parse_error} ; 0x12 - 0x
dd parse_op_simple ; 0x1a
dd parse_op_simple ; 0x1b
rept 4 { dd parse_error}
rept 5 { dd parse_op_u32 } ; 0x20 - 0x24
rept 3 { dd parse_error } ; 0x25 - 0x27 Reserved
rept 23 { dd parse_op_memarg } ; 0x28 - 0x3e
dd parse_mem_inst
dd parse_mem_inst
dd parse_op_i32
dd parse_op_i64
dd parse_op_f32
dd parse_op_f64
rept 123 { dd parse_op_simple }
assert ($ - opcode_parse_table) = (192 * 4)
@@ -0,0 +1,897 @@
; SPDX-License-Identifier: GPL-2.0-only
; Reshare - Shared Resources Daemon
;
; Copyright (C) 2026 KolibriOS Team
; ---------------------------------------------------------------------
; wasm_parser.inc
; WASM BINARY PARSER
; Parse the Binary Format described in the Wasm 1.0 Spec
; ---------------------------------------------------------------------
include 'wasm_const.inc'
include 'wasm_name.inc'
buf_ptr dd ?
buf_siz dd ?
buf_end dd ?
func_idx dd 0
parse_saved_esp dd 0
parse_saved_ebp dd 0
macro advance{
inc ebx
}
include 'wasm_func.inc'
;---------------------------------------------
; print a string word for a valtype
; accept ebx - pointer holding valtype
; return eax - 0 on success, 1 on error
; --------------------------------------------
valtype_to_str:
push esi
mov esi, valtype
.loop:
cmp dword [esi], 0
je .error
movzx eax, byte [ebx]
cmp [esi], eax
je .found
add esi, 8
jmp .loop
.error:
println "Unknown byte 0x%x", eax
mov eax, 1
pop esi
ret
.found:
add esi, 4
print " %s ", dword [esi]
xor eax, eax
.exit:
pop esi
ret
section_parse_table:
dd parse_custom_sec
dd parse_type_sec
dd parse_import_sec
dd parse_function_sec
dd parse_table_sec
dd parse_memory_sec
dd parse_global_sec
dd parse_export_sec
dd parse_start_sec
dd parse_element_sec
dd parse_code_sec
dd parse_data_sec
;----------------------------------------------------------
; wasm_parse: Parse a Wasm Binary
; INPUT:
; eax - Buffer address containing the binary
; ebx - Buffer size
; register usage
; ebx - current pointer to buffer
; eax - scratch register
; ---------------------------------------------------------
wasm_parse:
; a wasm binary at least have a 4 bytes magic and 4 bytes header
cmp ebx, 0x08
jb .too_small
mov [buf_ptr], eax
mov [buf_siz], ebx
lea ecx, [ebx + eax]
mov [buf_end], ecx
; println "Buf start: 0x%x, buf_size: %d, buf_end: 0x%x", eax, ebx, ecx
mov ebx, eax
; save for error recovery
mov [parse_saved_esp], esp
mov [parse_saved_ebp], ebp
.parse_module:
call check_header
call parse_sections
.parse_end:
println "Successfully parsed wasm binary"
ret
.too_small:
println "Refusing to parse a file of size: 0x%x without wasm header", ebx
ret
check_header:
cmp dword [ebx], WASM_MAGIC
jne .magic_error
add ebx, 4
cmp dword [ebx], WASM_VERSION
jne .version_error
add ebx, 4
ret
.magic_error:
println "Error, Magic Number 0x%x does not match required 0x%x", dword [ebx], WASM_MAGIC
jmp parse_error
.version_error:
println "Error, Version Number 0x%x does not match required 0x%x", dword [ebx], WASM_VERSION
jmp parse_error
; each binary part is a section
parse_sections:
println "Parsing Sections..."
@@:
movzx eax, byte [ebx]
advance
cmp eax, SECTION_DATA ; biggest section number in SECTION
ja .section_skip
call dword [section_parse_table + eax * 4]
mov eax, [buf_end]
cmp ebx, eax
jge @f
jmp @b
.section_skip:
println "In section skip"
call leb128_decode_u32
add ebx, eax
mov eax, [buf_end]
cmp ebx, eax
jge @f
jmp @b
@@:
ret
parse_custom_sec:
call leb128_decode_u32
println ""
println "Section 0: Custom (%d bytes)", eax
; do nothing but skip section
lea esi, [ebx + eax] ; save end pointer to advance later
; print name
call leb128_decode_u32
println " name: %.*s", eax, ebx ; write ebx of length eax
mov ebx, esi ;skip
ret
parse_type_sec:
call leb128_decode_u32
println ""
println "Section 1: Type (%d bytes): ", eax
add eax, ebx
push eax
; type : vec<functype>
call leb128_decode_u32
println " Vec of functype (Length: %d): ", eax
mov edi, eax
.loop:
test edi, edi
jz .done
call parse_functype
dec edi
jmp .loop
.done:
pop esi
cmp esi, ebx
jne .error
ret
.error:
println "Invalid section size for section: type"
jmp parse_error
parse_functype: ; function start with 0x60
cmp byte [ebx], FUNC_START
je .continue
println "Invalid byte while parsing function. Function should start with %d", FUNC_START
jmp parse_error
.continue:
println " functype[%d]:", dword [func_idx]
inc [func_idx]
advance
call parse_vec_param
call parse_vec_result
println ""
ret
parse_vec_param:
push edi
call leb128_decode_u32
mov edi, eax
print " Params(%d): ", edi
.loop:
test edi, edi
jz .done
call valtype_to_str
test eax, eax
ja parse_error
advance
dec edi
jmp .loop
.done:
println ""
pop edi
ret
parse_vec_result:
push edi
call leb128_decode_u32
mov edi, eax
print " Result(%d): ", edi
.loop:
test edi, edi
jz .done
call valtype_to_str
test eax, eax
ja parse_error
advance
dec edi
jmp .loop
.done:
println ""
pop edi
ret
parse_import_sec:
call leb128_decode_u32
println ""
println "Section 2: Import (%d bytes)", eax
add eax, ebx
push eax
call leb128_decode_u32
println " Vec of Import (Length: %d)", eax
mov edi, eax
.loop:
test edi, edi
jz .done
call parse_import
dec edi
jmp .loop
.done:
pop esi
cmp ebx, esi
jne .error
ret
.error:
println "Invalid section size for import. ebx: (0x%x), esi: (0x%x", ebx, esi
jmp parse_error
parse_import:
; parse modname first
call leb128_decode_u32
print " module: %.*s", eax, ebx
add ebx, eax
; parse nm
call leb128_decode_u32
print " name: %.*s ", eax, ebx
add ebx, eax
parse_importdesc:
movzx eax, byte [ebx]
advance
test eax, eax
je .typeidx
cmp eax, 0x01
je .tabletype
cmp eax, 0x02
je .memtype
cmp eax, 0x03
je .globaltype
.typeidx:
call leb128_decode_u32
println " Import type: typeidx (%d)", eax
ret
.tabletype:
print " Import type: "
jmp parse_table_type
.memtype:
print " Import type: "
jmp parse_memtype
.globaltype:
print " Import type: "
jmp parse_globaltype
parse_table_type:
cmp byte [ebx], 0x70
je @f
movzx eax, byte [ebx]
println "Error: Expecting funcref element type buf found (0x%x)", eax
jmp parse_error
@@:
advance
print "Tabletype "
jmp parse_limits
parse_memtype:
print "Memtype: "
parse_limits:
movzx eax, byte [ebx]
advance
test eax, eax
je .min
cmp eax, LIMIT_MIN_MAX
je .minmax
println "Invalid Limit parsed"
jmp parse_error
.min:
call leb128_decode_u32
println " with limit min: (%d), and unbounded max", eax
ret
.minmax:
call leb128_decode_u32
print " with limit min: (%d)", eax
call leb128_decode_u32
println ", and max: (%d)", eax
ret
parse_globaltype:
print "Globaltype: "
call valtype_to_str
test eax, eax
jne .valtype_error
advance
movzx eax, byte[ebx]
advance
; check if mutable or constant
test eax, eax
jne .check_var
println "(const)"
ret
.check_var:
cmp eax, GLOBAL_VAR
jne .mut_error
println "(var)"
ret
.valtype_error:
movzx eax, byte [ebx]
println " Invalid Valtype (0x%x)", eax
jmp parse_error
.mut_error:
println " Invalid mutable type (%x)", eax
jmp parse_error
parse_function_sec:
call leb128_decode_u32
println ""
println "Section 3: Function (%d bytes)", eax
add eax, ebx
push eax
; function is vec(typeidx)
call leb128_decode_u32
mov edi, eax
print "Vector of Typeidx: "
.loop:
test edi, edi
jz .done
call leb128_decode_u32
print " %d ", eax
dec edi
jmp .loop
.done:
pop esi
cmp esi, ebx
jne .error
println ""
ret
.error:
println "Error while parsing function section. Section size is incorrect"
jmp parse_error
parse_table_sec:
call leb128_decode_u32
println ""
println "Section 4: Table (%d bytes)", eax
add eax, ebx
push eax
; table is a vector of tabletype
call leb128_decode_u32
mov edi, eax
println " Vector of Tabletype (Length: %d): ", edi
.loop:
test edi, edi
jz .done
print " "
call parse_table_type
dec edi
jmp .loop
.done:
pop esi
cmp ebx, esi
jne .error
ret
.error:
println "Error while parsing table section: Section size is incorrect"
jmp parse_error
parse_memory_sec:
call leb128_decode_u32
println ""
println "Section 5: Memory (%d bytes)", eax
add eax, ebx
push eax
; memory section is a vector of memtype
call leb128_decode_u32
println " Vector of Memtype (Length: %d): ", eax
mov edi, eax
.loop:
test edi, edi
jz .done
print " "
call parse_memtype
dec edi
jmp .loop
.done:
pop esi
cmp ebx, esi
jne .error
ret
.error:
println "Error while parsing memory section: Section size is incorrect"
jmp parse_error
parse_global_sec:
call leb128_decode_u32
println ""
println "Section 6: Global (%d bytes)", eax
add eax, ebx
push eax
; global section is a vector of global
call leb128_decode_u32
println " Vector of Global (Length: %d): ", eax
mov edi, eax
.loop:
test edi, edi
jz .done
print " "
call parse_globaltype
print " init: "
call parse_const_expr
dec edi
jmp .loop
.done:
pop esi
cmp esi, ebx
jne .error
ret
.error:
println "Error while parsing global section: Section size is incorrect"
jmp parse_error
parse_export_sec:
call leb128_decode_u32
println ""
println "Section 7: Export (%d bytes)", eax
add eax, ebx
push eax
call leb128_decode_u32
println " Vector of Export (Length: %d): ", eax
mov edi, eax
.loop:
test edi, edi
jz .done
call parse_export
dec edi
jmp .loop
.done:
pop esi
cmp esi, ebx
jne .error
ret
.error:
println "Error while parsing export section: Section size is incorrect"
jmp parse_error
parse_export:
; parse nm
call leb128_decode_u32
print " name: %.*s ", eax, ebx
add ebx, eax
movzx eax, byte [ebx]
advance
test eax, eax
je .funcidx
cmp eax, 0x01
je .tableidx
cmp eax, 0x02
je .memidx
cmp eax, 0x03
je .globalidx
.funcidx:
call leb128_decode_u32
println " Export type: funcidx (%d)", eax
ret
.tableidx:
call leb128_decode_u32
println " Export type: tableidx (%d)", eax
ret
.memidx:
call leb128_decode_u32
println " Export type: memidx (%d)", eax
ret
.globalidx:
call leb128_decode_u32
println " Export type: globalidx (%d)", eax
ret
parse_start_sec:
call leb128_decode_u32
println ""
println "Section 8: Start (%d bytes)", eax
add eax, ebx
push eax
call leb128_decode_u32
println " Function index: %d", eax
pop eax
cmp eax, ebx
jne .error
ret
.error:
println "Error while parsing start section: Section size is incorrect"
jmp parse_error
parse_element_sec:
call leb128_decode_u32
println ""
println "Section 9: Element (%d bytes)", eax
add eax, ebx
push eax
call leb128_decode_u32
println " Vector of Elem (Length: %d)", eax
mov edi, eax
.loop:
test edi, edi
jz .done
call parse_elem
dec edi
jmp .loop
.done:
pop esi
cmp ebx, esi
jne .error
ret
.error:
println "Error while parsing element section: Section size is incorrect, supposed to end at 0x%x but ended at 0x%x", ebx, esi
jmp parse_error
parse_elem:
call leb128_decode_u32
println " tableidx: %d", eax
println " offset"
call parse_const_expr
print " vector of funcidx: "
call leb128_decode_u32
push edi
mov edi, eax
.loop:
test edi, edi
jz .done
call leb128_decode_u32
print " %d ", eax
dec edi
jmp .loop
.done:
pop edi
println ""
ret
parse_code_sec:
call leb128_decode_u32
println ""
println "Section 10: Code (%d bytes) - Skipping", eax
add ebx, eax
ret
parse_data_sec:
call leb128_decode_u32
println ""
println "Section 11: Data (%d bytes)", eax
add eax, ebx
push eax
call leb128_decode_u32
println " Vector of Data (Length: %d)", eax
mov edi, eax
.loop:
test edi, edi
jz .done
call parse_data
dec edi
jmp .loop
.done:
pop esi
cmp ebx, esi
jne .error
ret
.error:
println "Error while parsing Data section: Section size is incorrect"
jmp parse_error
parse_data:
call leb128_decode_u32
println " memidx: %d", eax
call parse_const_expr
print " vector of bytes:"
call leb128_decode_u32
push edi
mov edi, eax
.loop:
test edi, edi
jz .done
movzx esi, byte [ebx]
advance
print " 0x%x", esi
dec edi
jmp .loop
.done:
pop edi
println ""
ret
; -----------------------------------------------------------------
; expression parsing
; parse wasm instructions
; -------------------------------------------------------------------
parse_op_simple:
movzx eax, byte [ebx]
advance
println " %s ", dword [opcode_str_table + eax * 4]
ret
parse_op_u32:
movzx esi, byte [ebx]
advance
call leb128_decode_u32
println " %s %d", dword [opcode_str_table + esi * 4], eax
ret
parse_op_memarg:
push edi
movzx esi, byte [ebx]
advance
call leb128_decode_u32
mov edi, eax
call leb128_decode_u32
println "%s align=%d offset=%d", dword [opcode_str_table + esi * 4], edi, eax
pop edi
ret
parse_blocktype:
cmp byte [ebx], 0x40
je .exit
print " (result "
call valtype_to_str
test eax, eax
jne .error
print ")"
.exit:
advance
println ""
ret
.error:
println "Error: Invalid valtype type while parsing blocktype"
jmp parse_error
parse_op_block:
movzx esi, byte [ebx]
advance
print " %s ", dword [opcode_str_table + esi * 4]
call parse_blocktype
.instr:
jmp parse_expr
.error:
println "An error occurred while parsing %s expression", dword [opcode_str_table + esi * 4]
jmp parse_error
parse_op_if:
push edi
movzx esi, byte [ebx]
advance
print " %s ", dword [opcode_str_table + esi * 4]
call parse_blocktype
mov edi, 0x0b
mov esi, 0x05
.loop:
movzx eax, byte[ebx]
cmp eax ,edi
je .done
cmp eax, esi
je .else
call dword [opcode_parse_table + eax * 4]
jmp .loop
.else:
advance
println ""
println "else"
@@:
movzx eax, byte [ebx]
advance
cmp eax, edi
je .done
call dword [opcode_parse_table + eax * 4]
jmp @b
.done:
advance
pop edi
println "end"
ret
parse_op_br_table:
movzx esi, byte [ebx]
advance
print " %s ", dword [opcode_str_table + esi * 4]
call leb128_decode_u32
push edi
mov edi, eax
.loop:
test edi, edi
jz .done
call leb128_decode_u32
print " %d", eax
dec edi
.done:
call leb128_decode_u32
println " labelidx: %d", eax
pop edi
ret
parse_call_indirect:
movzx esi, byte [ebx]
advance
call leb128_decode_u32
println " %s (%d) " , dword [opcode_str_table + esi * 4], eax
movzx eax, byte [ebx]
advance
cmp eax, 0
jne .error
ret
.error:
println "Error while parsing call_indirect, expected 0x00, but got 0x%x", eax
jmp parse_error
parse_mem_inst:
movzx esi, byte [ebx]
advance
println "%s", dword [opcode_str_table + esi * 4]
movzx eax, byte[ebx]
advance
test eax, eax
jnz .error
ret
.error:
println "Error while parsing memory instruction (%s): expected 0x00, but got 0x%x", dword [opcode_str_table + esi * 4], eax
jmp parse_error
parse_op_f64:
movzx esi, byte [ebx]
advance
mov eax, [buf_end]
sub eax, ebx
cmp eax, 8
jl .error
print " %s ", dword [opcode_str_table + esi * 4]
println "%lf", dword [ebx], dword [ebx + 4] ; argument are pushed in reversed order
add ebx, 8
ret
.error:
println "Error: Incomplete binary for f64"
jmp parse_error
parse_op_f32:
movzx esi, byte [ebx]
advance
mov eax, [buf_end]
sub eax, ebx
cmp eax, 4
jl .error
movss xmm0, dword [ebx]
add ebx, 4
cvtss2sd xmm0, xmm0
sub esp, 8
movsd [esp], xmm0
pop eax
pop edx
print "%s ", dword [opcode_str_table + esi * 4]
println "%lf", edx, eax
ret
.error:
println "Error: Incomplete binary for f32"
jmp parse_error
parse_expr:
movzx esi, byte [ebx] ; read but don't advance so handler can handle their opcode themselves
cmp esi, 0x0B
je .done
call dword [opcode_parse_table + esi * 4]
jmp parse_expr
.done:
advance ; advance the end byte
ret
parse_const_expr:
movzx esi, byte [ebx]
cmp esi, 0x0B
je .error_empty ; empty const expr is invalid
cmp esi, OP_GLOBAL_GET
je .dispatch
cmp esi, OP_I32_CONST
jl .error
cmp esi, OP_F64_CONST
jg .error
.dispatch:
call dword [opcode_parse_table + esi * 4]
movzx esi, byte [ebx]
cmp esi, 0x0B
jne .error_multiple
advance
ret
.error_empty:
println "Error: empty constant expression"
jmp parse_error
.error_multiple:
println "Error: multiple instructions in constant expression"
jmp parse_error
.error:
println "Error: invalid instruction in constant expression (0x%x)", esi
jmp parse_error
parse_op_i32:
movzx esi, byte [ebx]
advance
call leb128_decode_s32
println "%s %d", dword [opcode_str_table + esi * 4], eax
ret
parse_op_i64:
ret
parse_error:
println "Error occured while parsing"
mov ebp, [parse_saved_ebp]
mov esp, [parse_saved_esp]
ret
@@ -0,0 +1,117 @@
; SPDX-License-Identifier: GPL-2.0-only
; Reshare - Shared Resources Daemon
;
; Copyright (C) 2026 KolibriOS Team
format binary as ""
use32
org 0
db 'MENUET01'
dd 1
dd START
dd I_END
dd MEM
dd STACKTOP
dd parameters
dd 00
include 'wasm_kolibrios_interface.inc'
include '../core/wasm_parser.inc'
START:
mcall 68, 11
stdcall dll.Load, @IMPORT
or eax, eax
jnz .exit
cmp byte[parameters], 0
je .no_file
; get file details
mov eax, 70
mov dword [file_info_struct], 5
mov dword [file_info_struct + 16], ddib_block
mov dword [file_info_struct +21], parameters
mov ebx, file_info_struct
mcall
test eax, eax
jnz .file_details_error
; get size (hopefully no wasm file doesn't exceed 4gb)
mov ecx, dword [ddib_block + 32]
mov [file_siz], ecx
; println "Size gotten for file: %s is %d", parameters, ecx
mov ebx, 12
mov eax, 68
mcall
mov [file_ptr], eax
mov eax, 70
mov ebx, [file_ptr]
mov dword [file_info_struct], 0
mov dword [file_info_struct + 4], 0
mov dword [file_info_struct + 8], 0
mov ecx, dword [file_siz]
mov dword [file_info_struct + 12], ecx
mov dword [file_info_struct + 16], ebx
mov dword [file_info_struct + 20], 0
mov dword [file_info_struct + 21], parameters
mov ebx, file_info_struct
mcall
test eax, eax
jne .file_read_error
mov eax, [file_ptr]
mov ebx, dword [file_siz]
; println "Passing ptr: 0x0x, and size: %d to wasm_parse", eax, ebx
call wasm_parse
mov ebx, 13
mov eax, 68
mov ecx, [file_ptr]
mcall
jmp .exit
.no_file:
println "Usage: wasm_dis <filename>"
jmp .exit
.file_details_error:
println "An error occurred while getting details for file: %s", parameters
jmp .exit
.file_read_error:
println "An error occured while reading file: %s", parameters
.exit:
print "Press any key to exit"
invoke con_getc
invoke con_exit, 1
mov eax, -1
mcall
I_END:
file_info_struct:
.fn dd 0
.offset dd 0
.offset2 dd 0
.size dd 0
.ptr dd 0
.ecd db 0
.path dd 0
file_ptr dd ?
file_siz dd ?
rb 131072 ; reserve 128kb
align 16
STACKTOP:
parameters rb 1024
ddib_block rb 560 ; 40 bytes + at most 520 for bytes
MEM:
@@ -0,0 +1,65 @@
; SPDX-License-Identifier: GPL-2.0-only
; Reshare - Shared Resources Daemon
;
; Copyright (C) 2026 KolibriOS Team
include '../../../../proc32.inc'
include '../../../../macros.inc'
include '../../../../dll.inc'
include '../../../../string.inc'
align 16
@IMPORT:
library terminal, 'console.obj'
import terminal, \
con_init, 'con_init', \
con_exit, 'con_exit', \
con_printf, 'con_printf', \
con_getc, 'con_getch'
macro print str, [args]{
common
pusha
local ..s, ..count, ..skip
..count = 0
jmp ..skip
..s db str, 0
..skip:
reverse
match any, args \{
push args
..count = ..count + 1
\}
common
push ..s
call [con_printf]
add esp, 4 + (..count * 4)
popa
}
macro println str, [args]{
common
pusha
local ..s, ..skip, ..count
..count = 0
jmp ..skip
..s db str, 10, 0
..skip:
reverse
match any, args \{
push args
..count = ..count + 1
\}
common
push ..s
call [con_printf]
add esp, 4 + (..count * 4)
popa
}
Binary file not shown.
Binary file not shown.