【ITニュース解説】Notes on Using wabt
2025年09月06日に「Dev.to」が公開したITニュース「Notes on Using wabt」について初心者にもわかりやすいように丁寧に解説しています。
ITニュース概要
wabtは、WebAssemblyプログラムの開発を支援するツールキットだ。人間が書いたコードをWebで動く形式に変換したり、WebAssemblyファイルを分析して中身を調べたり、コードを整理したり、逆アセンブルして理解を深めたりできる。
ITニュース解説
WebAssembly (wasm) は、ウェブブラウザ上で高速に動作するために設計された、低レベルなバイナリ形式の命令セットだ。ウェブアプリケーションのパフォーマンスが求められる部分や、JavaScriptでは処理が重くなるような計算を効率的に実行するために利用される。wabt (WebAssembly Binary Toolkit) は、このWebAssemblyコードの開発を支援するツール群で、コードのコンパイル、解析、デバッグ、検証といった多様な機能を提供する。
記事では、まずwat形式で書かれたフィボナッチ数列のプログラムを例に、WebAssemblyコードのコンパイル過程が紹介されている。watはWebAssembly Text Formatの略で、WebAssemblyの命令をS式と呼ばれる括弧で囲む記法で記述した、人間が読みやすいテキスト形式のコードだ。このfib.watコードでは、WebAssemblyが利用するメモリを1ページ分確保し、$heap_ptrというグローバル変数で次に利用可能なメモリのアドレスを管理している。$allocate関数は指定されたサイズのメモリブロックを確保し、その開始アドレスを返す役割を担う。そして、メインのfib関数がフィボナッチ数列を計算し、その結果をメモリ領域に書き込む。コード中では、i32.constで整数定数を、i32.addやi32.mulで32ビット整数に対する算術演算を行うなど、WebAssemblyの基本的な命令が使われている。
このfib.watファイルをwat2wasm ./fib.wat -o ./fib.wasmというwabtのコマンドでコンパイルすると、WebAssemblyのバイナリ形式であるfib.wasmファイルが生成される。このwasmファイルは、ウェブブラウザやNode.jsなどのWebAssemblyランタイム上で実行可能だ。記事では、main.tsというTypeScriptファイルからwasmを実行する例が示されている。main.tsはfetchとWebAssembly.instantiateStreamingを使ってfib.wasmを非同期にロードし、WebAssemblyモジュールとしてインスタンス化する。インスタンス化の際には、fib.wat内で定義されたenv.log関数(WebAssembly側から呼び出されるログ出力関数)が、JavaScriptのconsole.logと連携するように設定される。モジュールのインスタンス化が成功すると、JavaScript側からfib関数とmemoryオブジェクトにアクセスできるようになり、fib(10)を呼び出すことでWebAssembly側で計算が実行され、結果はWebAssemblyのメモリに格納される。JavaScript側からは、WebAssembly.Memoryオブジェクトのbufferプロパティを介してこのメモリの内容をUint32Arrayとして読み出し、計算結果を確認できる。この一連の流れにより、WebAssemblyがJavaScriptと連携して処理を行い、結果を共有する仕組みが実現されている。
コンパイル後のwasmファイルは、元のwatファイルと比較して大幅にサイズが小さくなる点も注目に値する。これは、wasmが非常に効率的なバイナリ形式であることと、LEB128という圧縮エンコーディング方式が採用されていることによるもので、ウェブアプリケーションの読み込み速度向上に貢献する。
次に、wabtに含まれるwasm-objdumpコマンドを使ったwasmファイルの解析方法が解説されている。このコマンドは、オペレーティングシステムでバイナリファイルを解析するobjdumpコマンドに似ており、wasmファイルの詳細情報を読み解くために非常に有用だ。例えば、他の開発者から渡されたwasmファイルの機能やインターフェースを素早く把握したい場合に役立つ。
wasm-objdump ./fib.wasm -j Export -xコマンドを実行すると、fib.wasmファイルから外部に公開されている(エクスポートされている)要素の一覧が表示される。この記事の例では、memoryとfib関数がエクスポートされていることが確認できる。
続いて、wasm-objdump ./fib.wasm -j Function -xコマンドで、定義されている関数に関する詳細情報が得られる。ここには、各関数がどの型定義(シグネチャ)を使用しているかが示され、fib関数がsig=1の型定義を使用していることがわかる。
さらに、wasm-objdump ./fib.wasm -j Type -xコマンドで、これらの型定義自体を確認できる。type[1] (i32) -> i32という出力は、fib関数が32ビット整数(i32)を一つ引数として受け取り、32ビット整数を一つ戻り値として返すということを明確に示している。
このように、wasm-objdumpを各セクション(Export、Function、Typeなど)を指定して実行することで、wasmファイルが外部にどのような機能を提供し、それらがどのような引数や戻り値を持つかといった情報を効率的に特定できる。ファイルサイズが大きい場合は、全セクションを一括で出力するよりも、このようにセクションごとに絞り込んで分析する方が効果的だ。
wabtのもう一つの便利な機能は、wat-desugarコマンドによるコードのフォーマットだ。watコードは、人間が読みやすいように、命令の後に引数を記述するような「シンタックスシュガー」(糖衣構文)を許容することがある。しかし、WebAssemblyはスタックマシンという仕組みで動作しており、命令を実行する前に、必要な値をスタックにプッシュ(積む)してから命令を実行するという厳密な規約がある。wat-desugarは、このようなシンタックスシュガーを取り除き、スタックマシンが処理する順番に沿って、値を先にスタックにプッシュし、その後に命令を記述する形式に変換する。
記事の例では、$allocate関数が整形前と整形後で比較されている。整形後のコードは、global.get $heap_ptrやlocal.set $startのように、オペランドを先に置いてから命令を実行する形式になっており、WebAssemblyのスタックベースの実行モデルに厳密に従っている。これによりコードはよりコンパクトになるが、元の人間が読みやすい形式とは異なるため、少し慣れが必要になる場合もある。「Desugar」(砂糖抜き)という名前は、糖衣構文を取り除き、より本質的なコード構造を明らかにするというツールの目的をよく表している。
最後に、wabtが提供するwasmファイルの逆アセンブル機能が紹介されている。これは、バイナリ形式のwasmファイルを人間が読める形式に変換する機能だ。主に三つのコマンドがある。
一つ目はwasm2watで、wasmファイルを元のwat形式に近いWebAssembly Text Formatに逆変換する。wasmファイルの具体的な命令の流れを詳細に分析したい場合や、小さな変更を加えたい場合に特に有用だ。wasm2watでwatファイルに戻し、それを編集してから再度wat2wasmでコンパイルするというワークフローも可能だ。
二つ目はwasm2cで、wasmファイルをC言語のソースコードとヘッダファイルに変換する。これは、WebAssemblyモジュールをC言語で書かれたアプリケーションに組み込む際や、wasmの動作をC言語の観点から深く理解したい場合に役立つ。
三つ目はwasm-decompileで、wasmファイルをC言語のような、より高レベルなスタイルの擬似コードに逆コンパイルする。これは、wasmファイルがどのようなアルゴリズムやロジックで実装されているかを、より抽象的で読みやすい形で把握するのに非常に適している。特に、wasmファイル全体の機能や意図を素早く理解したい場合に強力なツールとなる。
実践的な利用では、まずwasm-decompileを使ってwasmの全体的な機能をCスタイルの擬似コードで把握し、より詳細な分析や微調整が必要な場合にはwasm2watでwat形式に戻して作業を進める、という組み合わせが推奨される。これらの逆アセンブルツールは、wasmバイナリの内部構造を理解し、その動作を分析するために不可欠な手段となるだろう。