あらすじ
けっこう悩むのでメモる。
wasmバイナリをいじるときは WebAssembly/wabt とか wasi-sdk に同梱されているのとかいろいろあるけど、どれを使うのが最適なんだろう。あとbinaryenとかbytecodealianceとかrustwasmとかにもちょこちょこある。
wabtとwasi-sdkに入ってるやつはだいたいダブってる(wasi-sdkには入ってないやつもあるが)けど、wabt独自実装とllvmの実装なので結構違ってたりする。
環境
- wabt
このgit commit hashでビルドしたやつつこてる。
$ pwd
/home/kubo39/dev/cpp/wabt
$ git rev-parse HEAD
eb4e9509604137f6ac51b529a8540cd0e01f2650
- wasi-sdk
バージョン10?
$ ls bin # wasi-sdk-10
ar@ clang++@ clang-cl@ clang-tidy* ld64.lld@ llvm-ar* llvm-nm* llvm-ranlib@ llvm-strip@ objdump@ strings@
c++filt@ clang-10* clang-cpp@ git-clang-format* lld* llvm-cxxfilt* llvm-objcopy* llvm-size* nm@ ranlib@ strip@
clang@ clang-apply-replacements* clang-format* ld.lld@ lld-link@ llvm-dwarfdump* llvm-objdump* llvm-strings* objcopy@ size@ wasm-ld@
strip
リンカかコンパイラのオプションで --strip-debug
もしくは --strip-all
をつければよい気がする。
というかwabt/wasm-stripってオプション指定できないっぽいんだよな。
--strip-debug
と --strip-all
の違いは、前者がデバッグ情報(.debug_info
とか.debug_line
とか)だけ削ぎ落とすのに対して、後者はシンボル情報まで削ぎ落とす、と書いてる。(少なくとも手元だとシンボルテーブルあるwasm作れんかったぽいのでよくわからんかった)
wabt/wasm-stripはカスタムセクションを削ぎ落とすだけっぽい。
まとめ
- 状況に応じて選ぶ
-
--strip-debug
: カスタムセクションのうち、デバッグ情報を削ぎ落とす -
wabt/wasm-strip
: カスタムセクション全体を削ぎ落とす -
--strip-all
: カスタムセクション全体(とシンボル情報?)を削ぎ落とす
-
wasm-opt
なんかbinaryenにはある。--gc-sections
相当なら wasm-ld
にオプション渡せばよくね?になりそう。
まあこれある程度大きいプロジェクトじゃないと効果みえなさそうなんだよな。
これはbinaryenの最適化パスを単体のツールに切り出したものだった。LLVMバックエンドで-Ozとするとほとんど同じ挙動になるのでその場合はあえてこちらを使う必要はあまりないが、後述のwasm-snip/wasm-gcを使うなどした場合は再度最適化パスをかけるとさらに最適化が行える可能性がある。
まとめ
- LLVMに
-Oz
を渡せば基本的には同じ動作なのであえて使う必要はない - wasm-snip -> wasm-gcをかけたあとに再度最適化パスを通すことでより高度な最適化が行える可能性がある
objdump
wasi-sdkに同梱されている llvm-objdump
と wabt/wasm-objdump
がある。
できることあんま変わらん気がするけど、wasm-objdumpのほうがヘッダの表示が綺麗な感じがするのでこっちを使ってる。
いやでもllvm-objdumpのほうだと -CS
でデバッグ情報がいい感じになるのでこっちのほうがいい気もしてきた。
あとなんかセクション単位でdisassembleできるのもllvm-objdumpだけっぽいのでこちらにするか。
あと、llvm-objdumpはMach-O以外でも-gオプション使えるようになってほしいのだが…
まとめ
好きな方つかって wasi-sdk同梱のobjdumpのほうがいろいろできるのでこっち使う。
nm
wasi-sdk同梱の llvm-nm
とか llvm-objdump
の --syms
オプションでできるみたいなこと書いてるけどこれは嘘。
そんなときでも使えるのは fitzgen/wasm-nm があるのでこっち使ってる。
それでもデバッグ情報を削ぎ落とした場合はシンボル名がわかるのはimport/export sectionだけになってしまうが。。
wasm-nmはカスタムセクションのnameセクションからシンボル情報をとってくる。 --strip-all
や wasm-strip
はカスタムセクションを全部消してしまうので当然だが、 --strip-debug
でもnameセクションを消してしまうみたいなのでstripすると全部だめ。(なぜかproducersセクションは消されず残っている)
まとめ
- とりあえずwasm-nm使う
- デバッグ情報も大事なのでwasm-nm使うときは残しておく
wasm-gc
alexcrichton/wasm-gc) はlldの--gc-sections
がサポートされているので今となってはアーカイブ化されているが、後述のwasm-snipをかけた後に再度かけることでバイナリサイズをより小さくできる可能性がある。
wasm-snip
rustwasm/wasm-snipは指定した関数の中身をunreachable命令に置き換える。wasm-snipを使った後にさらにwasm-gcやwasm-optをかけることでバイナリサイズを極限まで小さくできる
twiggy
twiggy はバイナリでどの関数がでかいとかコールグラフとかデッドコードの情報とか教えてくれる。代替はとくになさそうなので一択?