wasmのbinutils系ってなに使えばいいの?のメモのやつ (2020年6月版)


あらすじ

けっこう悩むのでメモる。

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-objdumpwabt/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-allwasm-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 はバイナリでどの関数がでかいとかコールグラフとかデッドコードの情報とか教えてくれる。代替はとくになさそうなので一択?