【WebAssembly】WebAssembly 基本格式详解

【WebAssembly】WebAssembly 基本格式详解 WebAssembly 有两种核心格式二进制格式.wasm和文本格式.wat两者完全等价可以无损互转。一、.wasm 二进制格式 文件结构一个.wasm文件由魔数 版本号 多个段Sections组成------------------ 0x00 | 魔数 (4 字节) | \0asm ------------------ 0x04 | 版本号 (4 字节) | 1 ------------------ 0x08 | 段 1 (Type) | ------------------ | 段 2 (Import) | ------------------ | ... | ------------------ | 段 N (Data) | ------------------ 魔数与版本字段十六进制十进制说明魔数00 61 73 6D-\0asm标识版本号01 00 00 001小端序存储 段结构每个段遵循相同的格式------------------ | 段 ID (1 字节) | 0x01 Type, 0x02 Import, ... ------------------ | 段长度 (varint) | 该段的总字节数 ------------------ | 段内容 (N 字节) | 具体数据 ------------------️ 段类型 IDID段名称说明0x00Custom自定义段调试信息、名称等0x01Type函数签名定义0x02Import导入项0x03Function函数索引列表0x04Table函数表定义0x05Memory线性内存定义0x06Global全局变量0x07Export导出项0x08Start启动函数0x09Element表初始化元素0x0ACode函数体实际指令0x0BData内存初始化数据 数据类型编码类型字节值说明i320x7F32位整数i640x7E64位整数f320x7D32位浮点f640x7C64位浮点v1280x7B128位向量SIMDfuncref0x70函数引用externref0x6F外部引用 完整示例最小 .wasm 文件00 61 73 6D ; 魔数 \0asm 01 00 00 00 ; 版本 1 01 ; 段 ID: Type 04 ; 段长度: 4 字节 01 ; 1 个函数类型 60 ; 函数类型标记 00 ; 0 个参数 00 ; 0 个返回值这个 11 字节的文件定义了一个无参数、无返回值的空模块。二、.wat 文本格式 S-表达式语法WAT 使用S-表达式括号嵌套列表来表示结构(module;; 类型定义(type(func(parami32)(resulti32)));; 导入(importenvlog(func$log(parami32)));; 函数定义(func$add(param$a i32)(param$b i32)(resulti32)(i32.add(local.get $a)(local.get $b)));; 导出(exportadd(func$add))) 指令的两种写法折叠式嵌套更接近高级语言(i32.add(local.get0)(local.get1))平铺式线性更接近二进制布局local.get0local.get1i32.add️ 完整模块结构(module;; 类型 (type$t0(func(parami32 i32)(resulti32)));; 导入 (importenvmemory(memory1));; 导入内存(importenvprint(func$print(parami32)));; 导入函数;; 全局变量 (global$counter(muti32)(i32.const0));; 内存 (memory110);; 初始1页最大10页;; 表 (table10funcref);; 10个函数引用槽位;; 函数 (func$add(param$x i32)(param$y i32)(resulti32)(local$temp i32);; 局部变量(local.set $temp(i32.add(local.get $x)(local.get $y)))(local.get $temp))(func$main(exportmain)(resulti32)(call$add(i32.const10)(i32.const20)));; 导出 (exportcounter(global$counter))(exportmemory(memory0));; 数据初始化 (data(i32.const0)Hello, World!\00);; 表元素初始化 (elem(i32.const0)$add $main))三、WASM ↔ WAT 转换 使用 WABT 工具# 安装 WABT (WebAssembly Binary Toolkit)# macOSbrewinstallwabt# Ubuntusudoaptinstallwabt# 二进制 → 文本wasm2wat input.wasm-ooutput.wat# 文本 → 二进制wat2wasm input.wat-ooutput.wasm# 带符号名生成wasm2wat input.wasm --generate-names-ooutput.wat 在线工具工具地址功能WasmExplorerwasm-explorer.orgC/Rust → WASM → WATWebAssembly Studiowebassembly.studio完整的在线IDEWABT Demowebassembly.github.io/wabt/demo/wat2wasm/实时转换四、实战创建一个完整示例步骤 1编写 WAT 文件创建math.wat(module;; 导出函数斐波那契数列(func(exportfibonacci)(param$n i32)(resulti32)(local$a i32)(local$b i32)(local$temp i32)(local.set $a(i32.const0))(local.set $b(i32.const1))(block$exit(loop$continue(br_if$exit(i32.eqz(local.get $n)))(local.set $temp(local.get $b))(local.set $b(i32.add(local.get $a)(local.get $b)))(local.set $a(local.get $temp))(local.set $n(i32.sub(local.get $n)(i32.const1)))(br$continue)))(local.get $a));; 导出函数阶乘(func(exportfactorial)(param$n i32)(resulti32)(local$result i32)(local.set $result(i32.const1))(loop$continue(local.set $n(i32.sub(local.get $n)(i32.const1)))(br_if$continue(i32.gt_u(local.get $n)(i32.const0)))(local.set $result(i32.mul(local.get $result)(local.get $n)))(br$continue))(local.get $result)))步骤 2编译wat2wasm math.wat-omath.wasm步骤 3查看生成的二进制# 查看段信息wasm-objdump-hmath.wasm# 输出# math.wasm: file format wasm 0x1## Sections:# Type start0x0000000a end0x00000012 (size0x00000008)# Function start0x00000012 end0x00000015 (size0x00000003)# Export start0x00000015 end0x0000002d (size0x00000018)# Code start0x0000002d end0x00000096 (size0x00000069)步骤 4在 HTML 中使用!DOCTYPEhtmlhtmlheadtitleWebAssembly Demo/title/headbodyh1WebAssembly 数学函数/h1buttononclickrun()计算/buttonpreidoutput/prescriptasyncfunctionrun(){// 加载 .wasm 文件constresponseawaitfetch(math.wasm);constbytesawaitresponse.arrayBuffer();// 实例化const{instance}awaitWebAssembly.instantiate(bytes);// 调用函数constfib10instance.exports.fibonacci(10);constfact5instance.exports.factorial(5);document.getElementById(output).innerHTMLfibonacci(10) ${fib10}\n factorial(5) ${fact5};}/script/body/html五、对比总结特性.wasm.wat本质二进制字节码文本表示可读性❌ 不可读✅ 人类可读体积小紧凑编码大文本膨胀编写方式编译器生成可手写或生成使用场景分发、执行调试、学习、优化扩展名.wasm.wat/.wast传输效率高低需压缩执行直接由虚拟机执行需先编译为 .wasm六、工具链生态常用工具工具用途命令示例wat2wasmWAT → WASMwat2wasm input.watwasm2watWASM → WATwasm2wat input.wasmwasm-objdump查看段信息wasm-objdump -h file.wasmwasm-strip移除调试信息wasm-strip input.wasmwasm-opt优化体积wasm-opt -Oz input.wasm -o out.wasmwasm2js转为 JavaScriptwasm2js input.wasm -o out.js从 Rust 编译# 安装目标rustup targetaddwasm32-unknown-unknown# 编译为 .wasmcargobuild--targetwasm32-unknown-unknown--release# 使用 wasm-bindgen 生成 JS 绑定wasm-bindgen--targetweb target/wasm32-unknown-unknown/release/myapp.wasm --out-dir ./pkg 学习建议从 WAT 开始手写简单的 WAT 函数用wat2wasm编译测试反向学习用wasm2wat查看 Rust/C 编译结果理解对应关系掌握工具熟悉wasm-objdump查看段结构实战练习创建数学函数库、游戏逻辑等小型项目