登录 用户中心() [退出] 后台管理 注册
   
您的位置: 首页 >> 开发 vlog >> 主题: 最简 wasm 教程     [回主站]     [分站链接]
标题
最简 wasm 教程
clq
浏览(8) + 2025-02-24 13:43:25 发表 编辑

关键字:

[2025-02-24 18:18:05 最后更新]

最简 wasm 教程



wasm 的大名想必大家都如雷贯耳,不过网络上的教程一般都是 c/c++/rust 等等为基础的。相信我,最简单的学习方法就是直接看(以及直接写)它的二进制代码 :)

转换器
https://github.com/WebAssembly/wabt

虚拟机列表
https://github.com/appcypher/awesome-wasm-runtimes

--
一篇很好的文章
https://www.cnblogs.com/artech/p/17989165/hello_wasm_3

在线工具
https://wasmdev.cn/wabt-online/wat2wasm/index.html

--------------------------------------------------------
以下来自 deepseek

如何手写 wasm 字节码

手写 WebAssembly (Wasm) 字节码是一项复杂的任务,需要对 Wasm 的二进制格式和指令集有深入的理解。以下是一个简单的步骤指南,帮助你手写 Wasm 字节码:
1. 了解 WebAssembly 二进制格式

WebAssembly 的二进制格式由多个部分组成,包括:

Magic Number: \0asm (4字节)

Version: 1 (4字节)

Sections: 类型、函数、代码等部分

2. 编写一个简单的 Wasm 模块

假设我们要编写一个简单的 Wasm 模块,它包含一个函数,该函数接受两个整数参数并返回它们的和。
3. 手写字节码

以下是一个简单的 Wasm 模块的字节码示例:
plaintext
复制

0000000: 0061 736d ; Magic Number (\0asm)
0000004: 0100 0000 ; Version (1)

; Type Section (1 type)
0000008: 01 ; Section ID (Type)
0000009: 06 ; Section Size (6 bytes)
000000a: 01 ; Number of Types (1)
000000b: 60 ; Function Type
000000c: 02 ; Number of Parameters (2)
000000d: 7f ; i32
000000e: 7f ; i32
000000f: 01 ; Number of Results (1)
0000010: 7f ; i32

; Function Section (1 function)
0000011: 03 ; Section ID (Function)
0000012: 02 ; Section Size (2 bytes)
0000013: 01 ; Number of Functions (1)
0000014: 00 ; Type Index (0)

; Code Section (1 function)
0000015: 0a ; Section ID (Code)
0000016: 07 ; Section Size (7 bytes)
0000017: 01 ; Number of Functions (1)
0000018: 05 ; Function Body Size (5 bytes)
0000019: 00 ; Local Declarations (0)
000001a: 20 ; get_local 0
000001b: 00 ; (index 0)
000001c: 20 ; get_local 1
000001d: 01 ; (index 1)
000001e: 6a ; i32.add
000001f: 0b ; end

4. 解释字节码

Magic Number 和 Version: 这是每个 Wasm 模块的开头。

Type Section: 定义了一个函数类型,该函数接受两个 i32 参数并返回一个 i32 结果。

Function Section: 引用了一个函数类型(索引为 0)。

Code Section: 包含了函数的实际字节码。get_local 0 和 get_local 1 分别获取第一个和第二个参数,i32.add 将它们相加,end 结束函数。

5. 使用工具验证

你可以使用 wat2wasm 工具将文本格式的 Wasm 转换为二进制格式,或者使用 wasm2wat 将二进制格式转换为文本格式进行验证。
sh
复制

wat2wasm -o simple.wasm simple.wat
wasm2wat simple.wasm -o simple.wat

6. 运行 Wasm 模块

你可以使用 JavaScript 或其他支持 Wasm 的环境来加载和运行这个 Wasm 模块。
javascript
复制

WebAssembly.instantiateStreaming(fetch('simple.wasm'))
.then(obj => {
console.log(obj.instance.exports.add(2, 3)); // 输出 5
});

总结

手写 Wasm 字节码需要对 Wasm 的二进制格式和指令集有深入的理解。虽然手写字节码是可行的,但在实际开发中,通常使用高级语言(如 C/C++、Rust)编译为 Wasm,或者使用文本格式(.wat)编写 Wasm 模块。
wasm 中是使用 call_indirect 指令调用外部接口吗

在 WebAssembly (Wasm) 中,call_indirect 指令用于通过函数表(Function Table)间接调用函数,而不是直接调用外部接口。call_indirect 的主要用途是实现动态函数调用,例如在实现函数指针或接口多态性时。
call_indirect 的工作原理

函数表(Function Table):

Wasm 模块可以定义一个函数表,表中存储的是函数的索引。

函数表允许多个函数通过索引动态调用。

call_indirect 指令:

call_indirect 通过函数表中的索引调用函数。

调用时需要指定函数的类型(签名),以确保类型安全。

外部接口调用:

如果要从 Wasm 调用外部接口(例如 JavaScript 函数),通常是通过 导入(Imports) 实现的,而不是 call_indirect。

如何调用外部接口

在 Wasm 中,调用外部接口的步骤如下:

定义导入:

在 Wasm 模块中,通过 import 声明外部函数。

例如,导入一个 JavaScript 函数:
wat
复制

(module
(import "env" "external_func" (func $external_func (param i32) (result i32)))
(func $main (result i32)
i32.const 42
call $external_func
)
(export "main" (func $main))
)

这里,external_func 是一个从 JavaScript 导入的函数。

在 JavaScript 中提供函数:

在加载 Wasm 模块时,提供导入的函数:
javascript
复制

const importObject = {
env: {
external_func: (value) => {
console.log("Called from Wasm with value:", value);
return value + 1;
}
}
};

WebAssembly.instantiateStreaming(fetch('module.wasm'), importObject)
.then(obj => {
console.log(obj.instance.exports.main()); // 输出 43
});

直接调用导入的函数:

在 Wasm 中,直接使用 call 指令调用导入的函数,而不是 call_indirect。

call_indirect 的使用场景

call_indirect 主要用于以下场景:

动态函数调用:

当需要在运行时根据条件调用不同的函数时。

例如,实现函数指针或虚函数表。

多态性:

在实现接口或抽象类时,可以通过函数表动态调用具体的实现。

示例:

以下是一个使用 call_indirect 的简单示例:
wat
复制

(module
(type $func_type (func (param i32) (result i32)))
(table 2 funcref)
(elem (i32.const 0) $func1 $func2)
(func $func1 (param i32) (result i32)
i32.const 1
get_local 0
i32.add
)
(func $func2 (param i32) (result i32)
i32.const 2
get_local 0
i32.add
)
(func $main (param i32) (result i32)
get_local 0
call_indirect (type $func_type)
)
(export "main" (func $main))
)

在这个例子中,call_indirect 根据参数的值从函数表中调用 $func1 或 $func2。

总结

call_indirect: 用于通过函数表间接调用函数,适用于动态函数调用和多态性。

调用外部接口: 通过 import 导入外部函数,并使用 call 指令直接调用。

适用场景: 如果需要调用外部接口(如 JavaScript 函数),应使用导入机制,而不是 call_indirect。

我要一个二进制的完整示例

好的!下面是一个完整的 WebAssembly (Wasm) 二进制模块示例,它实现了一个简单的功能:导出一个函数 add,该函数接受两个 i32 参数并返回它们的和。我们将手写这个二进制模块,并逐步解释其结构。
二进制 Wasm 模块示例

以下是一个完整的 Wasm 二进制模块的十六进制表示:
plaintext
复制

0000000: 0061 736d 0100 0000 0107 0160 027f 7f01 .asm.......`....
0000010: 7f0302 0100 0707 0103 6164 6400 000a09 ..........add...
0000020: 0107 0020 0020 016a 0b ... . .j.

模块结构详解
1. 魔数和版本

Magic Number: 0061 736d (\0asm)

Version: 0100 0000 (版本 1)

plaintext
复制

0061 736d 0100 0000

2. 类型段(Type Section)

定义函数类型。这里定义了一个函数类型,接受两个 i32 参数并返回一个 i32 结果。

plaintext
复制

01 ; Section ID (Type)
07 ; Section Size (7 bytes)
01 ; Number of Types (1)
60 ; Function Type
02 ; Number of Parameters (2)
7f ; i32 (参数 1)
7f ; i32 (参数 2)
01 ; Number of Results (1

不,是调用 js 函数的完整示例

好的!下面是一个完整的 WebAssembly (Wasm) 二进制模块示例,它调用一个从 JavaScript 导入的函数。我们将手写这个二进制模块,并逐步解释其结构。
示例功能

Wasm 模块从 JavaScript 导入一个函数 js_console_log,该函数接受一个 i32 参数并在 JavaScript 中打印它。

Wasm 模块导出一个函数 main,调用 js_console_log 并传入参数 42。

二进制 Wasm 模块

以下是完整的 Wasm 二进制模块的十六进制表示:
plaintext
复制

0000000: 0061 736d 0100 0000 0108 0160 017f 0002 .asm.......`....
0000010: 0a01 0100 076a 730f 6a73 5f63 6f6e 736f .....js.js_conso
0000020: 6c65 5f6c 6f67 0000 0302 0100 0707 0104 le_log..........
0000030: 6d61 696e 0001 0a09 0107 0041 2a10 000b main.......A*...

模块结构详解
1. 魔数和版本

Magic Number: 0061 736d (\0asm)

Version: 0100 0000 (版本 1)

plaintext
复制

0061 736d 0100 0000

2. 类型段(Type Section)

定义函数类型。这里定义了一个函数类型,接受一个 i32 参数并返回空(void)。

plaintext
复制

01 ; Section ID (Type)
08 ; Section Size (8 bytes)
01 ; Number of Types (1)
60 ; Function Type
01 ; Number of Parameters (1)
7f ; i32 (参数)
00 ; Number of Results (0)

3. 导入段(Import Section)

从 JavaScript 导入一个函数 js_console_log,其类型为上面定义的函数类型(索引 0)。

plaintext
复制

02 ; Section ID (Import)
0a ; Section Size (10 bytes)
01 ; Number of Imports (1)
01 ; Module Name Length (1)
6a ; Module Name ("j")
73 ; Module Name ("s")
0f ; Field Name Length (15)
6a73 5f63 6f6e 736f 6c65 5f6c 6f67 ; Field Name ("js_console_log")
00 ; Import Kind (Function)
00 ; Type Index (0)

4. 函数段(Function Section)

定义一个函数,其类型为索引 0(与导入的函数类型相同)。

plaintext
复制

03 ; Section ID (Function)
02 ; Section Size (2 bytes)
01 ; Number of Functions (1)
00 ; Type Index (0)

5. 导出段(Export Section)

导出一个函数 main,其索引为 1(Wasm 函数的索引从 0 开始,导入函数占用索引 0,因此 main 是索引 1)。

plaintext
复制

07 ; Section ID (Export)
07 ; Section Size (7 bytes)
01 ; Number of Exports (1)
04 ; Field Name Length (4)
6d61 696e ; Field Name ("main")
00 ; Export Kind (Function)
01 ; Function Index (1)

6. 代码段(Code Section)

定义 main 函数的字节码:

将常量 42 压入栈。

调用导入的函数 js_console_log(索引 0)。

结束函数。

plaintext
复制

0a ; Section ID (Code)
09 ; Section Size (9 bytes)
01 ; Number of Functions (1)
07 ; Function Body Size (7 bytes)
00 ; Local Declarations (0)
41 2a ; i32.const 42
10 00 ; call 0 (调用导入的函数)
0b ; end

完整的二进制模块

将上述部分组合起来,完整的二进制模块如下:
plaintext
复制

0000000: 0061 736d 0100 0000 0108 0160 017f 0002 .asm.......`....
0000010: 0a01 0100 076a 730f 6a73 5f63 6f6e 736f .....js.js_conso
0000020: 6c65 5f6c 6f67 0000 0302 0100 0707 0104 le_log..........
0000030: 6d61 696e 0001 0a09 0107 0041 2a10 000b main.......A*...

在 JavaScript 中加载和运行

将上述二进制模块保存为 module.wasm。

使用以下 JavaScript 代码加载并运行:

javascript
复制

// 定义导入对象
const importObject = {
js: {
js_console_log: (value) => {
console.log("Called from Wasm with value:", value);
}
}
};

// 加载并实例化 Wasm 模块
WebAssembly.instantiateStreaming(fetch('module.wasm'), importObject)
.then(obj => {
// 调用导出的 main 函数
obj.instance.exports.main();
});

运行结果

运行后,控制台会输出:
复制

Called from Wasm with value: 42

总结

这个示例展示了如何手写一个完整的 Wasm 二进制模块,调用从 JavaScript 导入的函数。

通过 import 机制,Wasm 可以与宿主环境(如 JavaScript)交互。

如果需要更复杂的功能,可以扩展模块的结构和逻辑。





总数:0 页次:1/0 首页 尾页  
总数:0 页次:1/0 首页 尾页  


所在合集/目录



发表评论:
文本/html模式切换 插入图片 文本/html模式切换


附件:



NEWBT官方QQ群1: 276678893
可求档连环画,漫画;询问文本处理大师等软件使用技巧;求档softhub软件下载及使用技巧.
但不可"开车",严禁国家敏感话题,不可求档涉及版权的文档软件.
验证问题说明申请入群原因即可.

Copyright © 2005-2020 clq, All Rights Reserved
版权所有
桂ICP备15002303号-1