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.c와 test2.c에 정의 된 test1 ()과 test2 ()를 사용합니다. WebAssembly에서 이러한 모듈을 연결하는 방법을 확인해 보겠습니다.

위 코드를 컴파일하는 명령은 다음과 같습니다. 명령과 같이 동적 연결을 위해 SIDE_MODULE = 1을 사용합니다.

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

사용 가능한 WasmtoWat 사용 https://webassembly.github.io/wabt/demo/wasm2wat/, 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− env.table에서 사용되며 wasm 모듈 용으로 예약 된 변경 불가능한 i32 전역 오프셋입니다. 모듈은 테이블 요소 세그먼트의 이니셜 라이저에서이 전역을 사용할 수 있으므로 올바른 오프셋에서로드됩니다.

  • env.memory − wasm 모듈간에 공유해야하는 메모리 세부 정보가 있습니다.

  • env.table − wasm 모듈간에 공유해야하는 테이블 세부 정보가 있습니다.

수입은 다음과 같이 자바 스크립트로 정의되어야합니다-

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를 사용하는 자바 스크립트 코드입니다.

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

산출

출력은 다음과 같습니다.