WebAssembly - Liên kết động
Liên kết động là quá trình trong đó hai hoặc nhiều mô-đun sẽ được liên kết với nhau trong thời gian chạy.
Để chứng minh cách hoạt động của liên kết động, chúng tôi sẽ sử dụng chương trình C và biên dịch nó thành wasm bằng Ecmascript sdk.
Vì vậy, ở đây chúng tôi có -
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;
}
Trong mã main.c, nó sử dụng test1 () và test2 (), được định nghĩa bên trong test1.c và test2.c. Hãy để chúng tôi kiểm tra cách liên kết các mô-đun này trong WebAssembly.
Lệnh để biên dịch đoạn mã trên như sau: sử dụng SIDE_MODULE = 1 cho liên kết động như trong lệnh.
emcc test1.c test2.c main.c -s SIDE_MODULE=1 -o maintest.wasm
Sử dụng WasmtoWat, có sẵn tại https://webassembly.github.io/wabt/demo/wasm2wat/, sẽ nhận được định dạng văn bản WebAssembly của 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))
)
Định dạng văn bản WebAssembly có một số nhập được xác định như hình dưới đây -
(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))
Điều này được thêm vào trong khi biên dịch mã bằng emcc (emscripten sdk) và nó liên quan đến quản lý bộ nhớ trong WebAssembly.
Làm việc với Xuất nhập khẩu
Bây giờ để xem đầu ra, chúng tôi sẽ phải xác định các nhập mà bạn có thể thấy trong mã .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))
Các thuật ngữ trên được giải thích như sau:
env.stackSave - Nó được sử dụng để quản lý ngăn xếp, một chức năng được cung cấp bởi mã biên dịch emscripten.
env.stackRestore - Nó được sử dụng để quản lý ngăn xếp, một chức năng được cung cấp bởi mã biên dịch emscripten.
env.__memory_base- Nó là một phần bù toàn cục i32 bất biến, được sử dụng trong env.memory và được dành riêng cho mô-đun wasm. Mô-đun có thể sử dụng toàn cục này trong trình khởi tạo các phân đoạn dữ liệu của nó, để chúng được tải ở địa chỉ chính xác.
env.__table_base- Nó là một phần bù toàn cục i32 không thay đổi được, được sử dụng trong env.table và được dành riêng cho mô-đun wasm. Mô-đun có thể sử dụng toàn cục này trong trình khởi tạo của các phân đoạn phần tử bảng của nó, do đó, chúng được tải ở độ lệch chính xác.
env.memory - Điều này sẽ có chi tiết bộ nhớ được yêu cầu chia sẻ giữa các mô-đun wasm.
env.table - Điều này sẽ có các chi tiết bảng được yêu cầu được chia sẻ giữa các mô-đun wasm.
Nhập phải được định nghĩa trong javascript như sau:
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
}
};
Thí dụ
Sau đây là mã javascript sử dụng importObj bên trong 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>
Đầu ra
Kết quả như sau: