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)交互。 如果需要更复杂的功能,可以扩展模块的结构和逻辑。
NEWBT官方QQ群1: 276678893
可求档连环画,漫画;询问文本处理大师等软件使用技巧;求档softhub软件下载及使用技巧.
但不可"开车",严禁国家敏感话题,不可求档涉及版权的文档软件.
验证问题说明申请入群原因即可.