WebAssembly - 动态链接
动态链接是两个或多个模块在运行时链接在一起的过程。
为了演示动态链接的工作原理,我们将使用 C 程序并使用 Ecmascript sdk 将其编译为 wasm。
所以我们有 −
test1.c
int test1(){ return 100; }
test2.c
int test2(){ return 200; }
main.c
#include <stdio.h> int test1(); int test2(); int main() { int result = test1() + test2(); return result; }
在main.c代码中,它使用了test1()和test2(),它们在test1.c和test2.c中定义。 让我们检查一下如何在 WebAssembly 中链接这些模块。
编译上述代码的命令如下:使用SIDE_MODULE =1进行动态链接,如命令所示。
emcc test1.c test2.c main.c -s SIDE_MODULE=1 -o maintest.wasm
使用 https://webassembly.github.io/wabt/demo/wasm2wat/ 上提供的 WasmtoWat 将获得 maintest.wasm 的 WebAssembly 文本格式。
(module (type $t0 (func (result i32))) (type $t1 (func)) (type $t2 (func (param i32))) (type $t3 (func (param i32 i32) (result i32))) (import "env" "stackSave" (func $env.stackSave (type $t0))) (import "env" "stackRestore" (func $env.stackRestore (type $t2))) (import "env" "__memory_base" (global $env.__memory_base i32)) (import "env" "__table_base" (global $env.__table_base i32)) (import "env" "memory" (memory $env.memory 0)) (import "env" "table" (table $env.table 0 funcref)) (func $f2 (type $t1) (call $__wasm_apply_relocs) ) (func $__wasm_apply_relocs (export "__wasm_apply_relocs") (type $t1)) (func $test1 (export "test1") (type $t0) (result i32) (local $l0 i32) (local.set $l0 (i32.const 100) ) (return (local.get $l0) ) ) (func $test2 (export "test2") (type $t0) (result i32) (local $l0 i32) (local.set $l0 (i32.const 200)) (return (local.get $l0) ) ) (func $__original_main (export "__original_main") (type $t0) (result i32) (local $l0 i32) (local $l1 i32) (local $l2 i32) (local $l3 i32) (local $l4 i32) (local $l5 i32) (local $l6 i32) (local $l7 i32) (local $l8 i32) (local $l9 i32) (local.set $l0(call $env.stackSave)) (local.set $l1 (i32.const 16)) (local.set $l2 (i32.sub (local.get $l0) (local.get $l1))) (call $env.stackRestore (local.get $l2) ) (local.set $l3(i32.const 0)) (i32.store offset=12 (local.get $l2) (local.get $l3)) (local.set $l4 (call $test1)) (local.set $l5 (call $test2)) (local.set $l6 (i32.add (local.get $l4) (local.get $l5))) (i32.store offset=8 (local.get $l2) (local.get $l6)) (local.set $l7 (i32.load offset=8 (local.get $l2))) (local.set $l8 (i32.const 16)) (local.set $l9 (i32.add (local.get $l2) (local.get $l8))) (call $env.stackRestore (local.get $l9)) (return(local.get $l7)) ) (func $main (export "main") (type $t3) (param $p0 i32) (param $p1 i32) (result i32) (local $l2 i32) (local.set $l2 (call $__original_main)) (return (local.get $l2)) ) (func $__post_instantiate (export "__post_instantiate") (type $t1) (call $f2)) (global $__dso_handle (export "__dso_handle") i32 (i32.const 0)) )
WebAssembly 文本格式定义了一些导入,如下所示 −
(import "env" "stackSave" (func $env.stackSave (type $t0))) (import "env" "stackRestore" (func $env.stackRestore (type $t2))) (import "env" "__memory_base" (global $env.__memory_base i32)) (import "env" "__table_base" (global $env.__table_base i32)) (import "env" "memory" (memory $env.memory 0)) (import "env" "table" (table $env.table 0 funcref))
这是在 emcc(emscripten sdk) 编译代码时添加的,它处理 WebAssembly 中的内存管理。
处理导入和导出
现在要查看输出,我们必须定义您可以在 .wat 代码中看到的导入 −
(import "env" "stackSave" (func $env.stackSave (type $t0))) (import "env" "stackRestore" (func $env.stackRestore (type $t2))) (import "env" "__memory_base" (global $env.__memory_base i32)) (import "env" "__table_base" (global $env.__table_base i32)) (import "env" "memory" (memory $env.memory 0)) (import "env" "table" (table $env.table 0 funcref))
以上术语解释如下 −
env.stackSave − 它用于堆栈管理,这是 emscripten 编译代码提供的功能。
env.stackRestore − 它用于堆栈管理,这是 emscripten 编译代码提供的功能。
env.__memory_base − 它是一个不可变的 i32 全局偏移量,在 env.memory 中使用并为 wasm 模块保留。 模块可以在其数据段的初始化程序中使用此全局变量,以便将它们加载到正确的地址。
env.__table_base − 它是一个不可变的 i32 全局偏移量,在 env.table 中使用并为 wasm 模块保留。 模块可以在其表元素段的初始化程序中使用此全局变量,以便将它们加载到正确的偏移量处。
env.memory − 这将包含 wasm 模块之间共享所需的内存详细信息。
env.table − 这将包含 wasm 模块之间需要共享的表详细信息。
导入必须在 JavaScript 中定义如下 −
var wasmMemory = new WebAssembly.Memory({'initial': 256,'maximum': 65536}); const importObj = { env: { stackSave: n => 2, stackRestore: n => 3, //abortStackOverflow: () => { throw new Error('overflow'); }, table: new WebAssembly.Table({ initial: 0, maximum: 65536, element: 'anyfunc' }), __table_base: 0, memory: wasmMemory, __memory_base: 256 } };
示例
以下是在 WebAssembly.instantiate 中使用 importObj 的 javascript 代码。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> </head> <body> <script> var wasmMemory = new WebAssembly.Memory({'initial': 256,'maximum': 65536}); const importObj = { env: { stackSave: n => 2, stackRestore: n => 3, //abortStackOverflow: () => { throw new Error('overflow'); }, table: new WebAssembly.Table({ initial: 0, maximum: 65536, element: 'anyfunc' }), __table_base: 0, memory: wasmMemory, __memory_base: 256 } }; fetch("maintest.wasm") .then(bytes => bytes.arrayBuffer()) .then( module => WebAssembly.instantiate(module, importObj) ) .then(finalcode => { console.log(finalcode); console.log(WebAssembly.Module.imports(finalcode.module)); console.log(finalcode.instance.exports.test1()); console.log(finalcode.instance.exports.test2()); console.log(finalcode.instance.exports.main()); }); </script> </body> </html>
输出
输出结果如下 −