WebAssembly - Liaison dynamique

La liaison dynamique est le processus dans lequel deux modules ou plus seront liés ensemble pendant l'exécution.

Pour démontrer le fonctionnement des liens dynamiques, nous utiliserons le programme C et le compilerons en wasm en utilisant Ecmascript sdk.

Alors nous avons ici -

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; 
}

Dans le code main.c, il utilise test1 () et test2 (), qui sont définis dans test1.c et test2.c. Voyons comment lier ces modules dans WebAssembly.

La commande pour compiler le code ci-dessus est la suivante: utilisez SIDE_MODULE = 1 pour la liaison dynamique comme indiqué dans la commande.

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

Utilisation de WasmtoWat, disponible sur https://webassembly.github.io/wabt/demo/wasm2wat/, obtiendra le format de texte WebAssembly de maintest.wasm.

(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))
)

Le format de texte WebAssembly a des importations définies comme indiqué ci-dessous -

(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))

Ceci est ajouté lors de la compilation du code par emcc (emscripten sdk) et traite de la gestion de la mémoire dans WebAssembly.

Utilisation des importations et des exportations

Maintenant, pour voir la sortie, nous devrons définir les importations que vous pouvez voir dans le code .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))

Les termes ci-dessus sont expliqués comme suit -

  • env.stackSave - Il est utilisé pour la gestion de la pile, une fonctionnalité fournie par le code compilé emscripten.

  • env.stackRestore - Il est utilisé pour la gestion de la pile, une fonctionnalité fournie par le code compilé emscripten.

  • env.__memory_base- C'est un offset global i32 immuable qui est, utilisé dans env.memory et réservé pour le module wasm. Le module peut utiliser ce global dans l'initialiseur de ses segments de données, afin qu'ils soient chargés à la bonne adresse.

  • env.__table_base- C'est un offset global i32 immuable qui est utilisé dans env.table et réservé au module wasm. Le module peut utiliser ce global dans l'initialiseur de ses segments d'élément de table, de sorte qu'ils soient chargés au décalage correct.

  • env.memory - Cela aura les détails de mémoire qui doivent être partagés entre les modules wasm.

  • env.table - Cela aura les détails de la table qui doivent être partagés entre les modules wasm.

Les importations doivent être définies en javascript comme suit -

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 
   } 
};

Exemple

Voici le code javascript qui utilise importObj dans WebAssembly.instantiate.

<!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>

Production

La sortie est la suivante -