TR kizaki Tech Memo

WABT install! :Converting WebAssembly text format to wasm

Let's have a go at converting the above .wat text representation example into .wasm assembly format.

ref: wasmのtutorial備忘録。

153927

  1. To start with, make a copy of the above listing inside a text file; call it simple.wat.

simple.wat

(module
  (func $i (import "imports" "imported_func") (param i32))
  (func (export "exported_func")
    i32.const 42
    call $i
  )
)
(module
  ;; imported_func という名前の関数を imports というモジュールからインポートし、$1に格納している。
  ;; このimported_func関数の引数として渡す型は32ビット整数
  (func $i (import "imports" "imported_func") (param i32))
  ;; exported_func という名称で関数をエクスポートする。これをWebアプリ上から利用する形となる
  ;; 以下ネストしている部分はexported_func内の処理
  (func (export "exported_func")
    ;; 32ビット整数を(ここでは42)を定義してスタックにプッシュしている
    i32.const 42
    ;; $1(つまりimported_func関数)を実行する。さきほどpushした42が引数として渡される
    call $i
  )
)
  1. We need to assemble this textual representation into the assembly language the browser actually reads before we can use it. To do this, we can use the wabt tool, which includes compilers to convert between WebAssembly's text representation and wasm, and vice versa, plus more besides. Go to https://github.com/webassembly/wabt — follow the instructions on this page to set up the tool.

WABT: The WebAssembly Binary Toolkit

wabt

Cloning

Clone as normal, but don't forget to get the submodules as well:

$ git clone --recursive https://github.com/WebAssembly/wabt $ cd wabt $ git submodule update --init

This will fetch the testsuite and gtest repos, which are needed for some tests.

Building using CMake directly (Linux and macOS)

You'll need CMake. You can then run CMake, the normal way:

$ mkdir build $ cd build $ cmake .. $ cmake --build .

This will produce build files using CMake's default build generator. Read the CMake documentation for more information.

NOTE: You must create a separate directory for the build artifacts (e.g. build above). Running cmake from the repo root directory will not work since the build produces an executable called wasm2c which conflicts with the wasm2c directory.

cmake .. でerror

┌─(~/dev/wat_test/build)────────┐
└─(21:32:56 on main ✖ ✹ ✭)──> cmake ..                          ──(Tue,May16)─┘
CMake Warning:
  Ignoring extra path from command line:

   ".."

CMake Error: The source directory "/Users/$HOME/dev/wat_test" does not appear to contain CMakeLists.txt.
Specify --help for usage, or press the help button on the CMake GUI.
  • cMakeについて勉強するならここ

fc95f62b68a14ab861dc

cmake Errorあっさり解決!⇒ cloneしたwabtの下(inside)でbuildする必要があったのね…

┌─(~)───────────────────────────┐
└─(22:16:05 on main ✖ ✹ ✭)──> cd devtool                        ──(Tue,May16)─┘
┌─(~/devtool)───────────────────┐
└─(22:16:11 on main ✖ ✹ ✭)──> cd wabt                           ──(Tue,May16)─┘
┌─(~/devtool/wabt)──────────────┐
└─(22:16:16 on main)──> mkdir build                             ──(Tue,May16)─┘
┌─(~/devtool/wabt)──────────────┐
└─(22:16:36 on main)──> cd build                                ──(Tue,May16)─┘
┌─(~/devtool/wabt/build)────────┐
└─(22:16:41 on main)──> cmake ..                                ──(Tue,May16)─┘
-- The C compiler identification is AppleClang 14.0.0.14000029
-- The CXX compiler identification is AppleClang 14.0.0.14000029
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/usr/bin/gcc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/usr/bin/g++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found Git: /usr/local/bin/git (found version "2.40.0") 
-- Looking for alloca.h
-- Looking for alloca.h - found
-- Looking for unistd.h
-- Looking for unistd.h - found
-- Looking for snprintf
-- Looking for snprintf - found
-- Looking for strcasecmp
-- Looking for strcasecmp - found
-- Looking for sys/types.h
-- Looking for sys/types.h - found
-- Looking for stdint.h
-- Looking for stdint.h - found
-- Looking for stddef.h
-- Looking for stddef.h - found
-- Check size of ssize_t
-- Check size of ssize_t - done
-- Check size of size_t
-- Check size of size_t - done
-- Looking for __i386__
-- Looking for __i386__ - not found
-- Looking for __SSE2_MATH__
-- Looking for __SSE2_MATH__ - found
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success
-- Found Threads: TRUE  
-- Found PythonInterp: /usr/local/bin/python3 (found suitable version "3.11.3", minimum required is "3.5") 
-- Configuring done (17.2s)
-- Generating done (0.2s)
-- Build files have been written to: /Users/$HOME/devtool/wabt/build
# wabt  このpathの書き方は今までよりもimprove 
export WABT_BIN="/Users/$HOME/devtool/wabt/bin"
export PATH=$PATH:$WABT_BIN

wabtの各コマンドが実行できるようになった。

┌─(~/devtool/wabt/build)────────┐
└─(22:22:39 on main)──> wasm2wat --version                      ──(Tue,May16)─┘
1.0.32
  1. Once you've got the tool built, add the /wabt/out/clang/Debug directory to your system PATH.

参照blogがやってなかったから省略してしまいました

4.Next, execute the wat2wasm program, passing it the path to the input file, followed by an -o parameter, followed by the path to the output file:

wasm file作れた!わーい

┌─(~/dev/wat_test)──────────────┐
└─(23:12:42 on main ✖ ✹ ✭)──> wat2wasm simple.wat -o simple.wasm

┌─(~/dev/wat_test)──────────────┐
└─(23:14:06 on main ✖ ✹ ✭)──> ls                                ──(Tue,May16)─┘
build       simple.wasm simple.wat

This will convert the wasm into a file called simple.wasm, which contains the .wasm assembly code.

Note: You can also convert the assembly back into the text representation using the wasm2wat tool; for example wasm2wat simple.wasm -o text.wat.

┌─(~/dev/wat_test)──────────────┐
└─(23:14:10 on main ✖ ✹ ✭)──> wasm2wat simple.wasm -o text.wat  ──(Tue,May16)─┘
┌─(~/dev/wat_test)──────────────┐
└─(23:17:54 on main ✖ ✹ ✭)──> ls                                ──(Tue,May16
build       simple.wasm simple.wat  text.wat

Viewing the assembly output

Because the output file is assembly-based, it can't be viewed in a normal text editor. However, you can view it using the wat2wasm tool's -v option. Try this:

wat2wasm simple.wat -v
┌─(~/dev/wat_test)──────────────┐
└─(23:24:56 on main)──> wat2wasm simple.wat -v                  ──(Tue,May16)─┘
0000000: 0061 736d                                 ; WASM_BINARY_MAGIC
0000004: 0100 0000                                 ; WASM_BINARY_VERSION
; section "Type" (1)
0000008: 01                                        ; section code
0000009: 00                                        ; section size (guess)
000000a: 02                                        ; num types
; func type 0
000000b: 60                                        ; func
000000c: 01                                        ; num params
000000d: 7f                                        ; i32
000000e: 00                                        ; num results
; func type 1
000000f: 60                                        ; func
0000010: 00                                        ; num params
0000011: 00                                        ; num results
0000009: 08                                        ; FIXUP section size
; section "Import" (2)
0000012: 02                                        ; section code
0000013: 00                                        ; section size (guess)
0000014: 01                                        ; num imports
; import header 0
0000015: 07                                        ; string length
0000016: 696d 706f 7274 73                        imports  ; import module name
000001d: 0d                                        ; string length
000001e: 696d 706f 7274 6564 5f66 756e 63         imported_func  ; import field name
000002b: 00                                        ; import kind
000002c: 00                                        ; import signature index
0000013: 19                                        ; FIXUP section size
; section "Function" (3)
000002d: 03                                        ; section code
000002e: 00                                        ; section size (guess)
000002f: 01                                        ; num functions
0000030: 01                                        ; function 0 signature index
000002e: 02                                        ; FIXUP section size
; section "Export" (7)
0000031: 07                                        ; section code
0000032: 00                                        ; section size (guess)
0000033: 01                                        ; num exports
0000034: 0d                                        ; string length
0000035: 6578 706f 7274 6564 5f66 756e 63         exported_func  ; export name
0000042: 00                                        ; export kind
0000043: 01                                        ; export func index
0000032: 11                                        ; FIXUP section size
; section "Code" (10)
0000044: 0a                                        ; section code
0000045: 00                                        ; section size (guess)
0000046: 01                                        ; num functions
; function body 0
0000047: 00                                        ; func body size (guess)
0000048: 00                                        ; local decl count
0000049: 41                                        ; i32.const
000004a: 2a                                        ; i32 literal
000004b: 10                                        ; call
000004c: 00                                        ; function index
000004d: 0b                                        ; end
0000047: 06                                        ; FIXUP func body size
0000045: 08                                        ; FIXUP section size
  1. Magic Number and Version:
    • The first four bytes are the magic number 0061736D, indicating the start of a Wasm binary.
    • The next four bytes 01000000 represent the Wasm binary version number.
  2. Section "Type" (1):
    • The section code is 01, indicating that this is the "Type" section.
    • The section size is 00 (guess).
    • This section defines function types.
    • There are two function types defined in this example.
    • Function Type 0:
      • It specifies a function with one parameter of type i32 (signed 32-bit integer) and no results.
    • Function Type 1:
      • It specifies a function with no parameters and no results.
  3. Section "Import" (2):
    • The section code is 02, indicating that this is the "Import" section.
    • The section size is 00 (guess).
    • This section defines imported functions.
    • Import Header 0:
      • The import module name is "imports" (length: 7 characters).
      • The import field name is "imported_func" (length: 13 characters).
      • The import kind is 00, representing a function import.
      • The import signature index is 00, referring to Function Type 0.
  4. Section "Function" (3):
    • The section code is 03, indicating that this is the "Function" section.
    • The section size is 00 (guess).
    • This section defines the functions present in the module.
    • Function 0:
      • It has a signature index of 01, referring to Function Type 1.
  5. Section "Export" (7):
    • The section code is 07, indicating that this is the "Export" section.
    • The section size is 00 (guess).
    • This section defines the exports of the module.
    • Export 0:
      • The export name is "exported_func" (length: 13 characters).
      • The export kind is 00, representing a function export.
      • The export function index is 01, referring to Function 0.
  6. Section "Code" (10):
    • The section code is 0a, indicating that this is the "Code" section.
    • The section size is 00 (guess).
    • This section contains the actual function code.
    • Function Body 0:
      • The function body size is 00 (guess).
      • There are no local declarations.
      • The function body contains the following instructions:
        • i32.const 2a: Pushes the value 42 (hexadecimal 2a) onto the stack.
        • call 00: Calls the function at index 00 (imported_func).
        • end: Ends the function body.

Note that the section sizes are initially marked as 00 and later fixed up based on the actual sizes of the sections.

This is a simplified representation of a Wasm file in a textual format. In its actual binary form, it would consist of bytes representing the instructions and their corresponding values.


WAT上で定義した imported_func 部分は上のコードでは importedFunc として定義している。これを imported_func としてimportし、wasm側に渡している。

ローカルでサーバを立ち上げてsimple.htmlに実行することでブラウザのコンソール上に num: 42 という数値が表示されるのを確認した。

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Simple wasm example</title>
  </head>
  <body>
    <script>
      const importedFunc = (num) => console.log("num: ", + num);
      WebAssembly.instantiateStreaming(fetch("simple.wasm"), {imports : { imported_func: importedFunc }})
      .then(obj => {
        obj.instance.exports.exported_func();
      });
    </script>
  </body>
</html>

これ表示されてないけど保留で!

このwabt install成功したからemscriptenの実行error解決にもう一度tryしてみよう。