• webassembly学习-动态链接


    一、动态链接技术

    搞过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; 
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    使用相关工具命令编译:

    emcc test1.c test2.c main.c -s SIDE_MODULE=1 -o maintest.wasm
    
    
    • 1
    • 2

    然后使用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))
    )
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    在经过上述的动作后,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>
       
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    本文的例程使用是芒果文档相关例程:
    https://www.imangodoc.com/29060.html

    三、、总结

    webassembly目前的效率已经非常接近于本地运行的代码,一些3D软件和大型的游戏的测试表明,其相对于传统的Javascript有着压倒性的优势。其实认真分析看来,还是解释型的弱语言终究要牺牲效率和速度,wasm不过是掉了一个花枪而已。不过话又说回来,这个花枪掉得好,让开发者明白,没有什么不能够通过底层技术的互相融通来实现更高级的用法。
    或许以后就是大杂烩的时代,没有什么你好他坏的说法,看习惯而已,一家之言,不足一哂。

  • 相关阅读:
    CloudCompare
    【STL常用容器】:string 容器
    女生适不适合干软件测试这一行?“钱”程如何?适合长期发展不?
    androidStudio第一次运行报错无法运行
    I.MX6ull UART
    spring-boot2.6.x兼容swagger2问题
    9.复杂的例子:模块的使用和自定义模块
    肌肉骨骼康复学-习题-单选
    [附源码]Python计算机毕业设计SSM考研信息共享博客系统(程序+LW)
    云原生爱好者周刊:给你的 Kubernetes 集群打分 | 2022-8-15
  • 原文地址:https://blog.csdn.net/fpcc/article/details/125481171