一、动态链接技术
搞过C/C++编程的开发人员都知道,动态库技术是今天几乎大型程序的标配。而动态库技术上,涉及到的一个最基本的问题就是动态加载动态链接技术。做一项更基础的中间语言,WebAssembly 必须提供类似的动态链接技术。其和c/c++的动态库中的动态链接技术一样,通过模块的导入和导出允许N个实例化模块来共享函数、线性内存、 表和常量。(在Iinux中,动态库动态链接使用dlopen函数)。同样在WASM中启用加载和动态运行时的动态链接,尤其是非本地的一些状态都可以通过动态链接来实现对其的共享。因此,对WebAssembly的相关开发编译工具需要支持类似的功能。
一般来说,WebAssembly需要由宿主来提供运行环境,所以宿主环境必须提供这种动态导入实例化模块的方法。
二、WASM如何动态加载
先看一段代码:
int test1(){
return 100;
}
int test2(){
return 200;
}
#include <stdio.h>
int test1();
int test2();
int main() {
int result = test1() + test2();
return result;
}
使用相关工具命令编译:
emcc test1.c test2.c main.c -s SIDE_MODULE=1 -o maintest.wasm
然后使用WasmToWat这个工具(https://webassembly.github.io/wabt/demo/wasm2wat/)来转换成文本:
(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会产生导入和导出两个方向的标记即功能imp和功能exp。如果在实际的应用中使用了libc则会增加对线性内存及相关变量的导入。在C和c++的应用中,前面提到过,使用dloepn,dlsym,dlopen将编译并实例化一个新模块,将编译后的实例存储在主机环境表中,并将索引返回给调用者。dlsym将获得此索引,将实例从表中取出,搜索实例相关的导出,将找到的函数附加到函数表并返回表附加到Caller的元素的索引。
需要说明的是,WebAssembly 中 C 函数指针的表示是函数表的索引,因此上述方案与dlsym函数指针的返回值完全一致。
其实大家都明白,能不能提供一个标准的ABI接口标准,这才是重要的事情,可惜,目前在C/C++是还无法做到。
而要在JS中使用这个例程可以如下操作:
<meta charset="UTF-8">
<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>
本文的例程使用是芒果文档相关例程:
https://www.imangodoc.com/29060.html
三、、总结
webassembly目前的效率已经非常接近于本地运行的代码,一些3D软件和大型的游戏的测试表明,其相对于传统的Javascript有着压倒性的优势。其实认真分析看来,还是解释型的弱语言终究要牺牲效率和速度,wasm不过是掉了一个花枪而已。不过话又说回来,这个花枪掉得好,让开发者明白,没有什么不能够通过底层技术的互相融通来实现更高级的用法。
或许以后就是大杂烩的时代,没有什么你好他坏的说法,看习惯而已,一家之言,不足一哂。