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

## あらすじ

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

wasmバイナリをいじるときは WebAssembly/wabt とか wasi-sdk に同梱されているのとかいろいろあるけど、どれを使うのが最適なんだろう。あとbinaryenとかbytecodealianceとかrustwasmとかにもちょこちょこある。
wabtとwasi-sdkに入ってるやつはだいたいダブってる(wasi-sdkには入ってないやつもあるが)けど、wabt独自実装とllvmの実装なので結構違ってたりする。

## 環境

- wabt

このgit commit hashでビルドしたやつつこてる。

```console
$ pwd
/home/kubo39/dev/cpp/wabt
$ git rev-parse HEAD
eb4e9509604137f6ac51b529a8540cd0e01f2650
```

- wasi-sdk

バージョン10?

```console
$ 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](https://github.com/fitzgen/wasm-nm) があるのでこっち使ってる。
それでもデバッグ情報を削ぎ落とした場合はシンボル名がわかるのはimport/export sectionだけになってしまうが。。

wasm-nmはカスタムセクションの[name](https://webassembly.github.io/spec/core/appendix/custom.html#name-section)セクションからシンボル情報をとってくる。 `--strip-all` や `wasm-strip` はカスタムセクションを全部消してしまうので当然だが、 `--strip-debug`でもnameセクションを消してしまうみたいなのでstripすると全部だめ。(なぜかproducersセクションは消されず残っている)

### まとめ

- とりあえずwasm-nm使う
- デバッグ情報も大事なのでwasm-nm使うときは残しておく

## wasm-gc

[alexcrichton/wasm-gc)](https://github.com/alexcrichton/wasm-gc) はlldの`--gc-sections`がサポートされているので今となってはアーカイブ化されているが、後述のwasm-snipをかけた後に再度かけることでバイナリサイズをより小さくできる可能性がある。

## wasm-snip

[rustwasm/wasm-snip](https://github.com/rustwasm/wasm-snip)は指定した関数の中身をunreachable命令に置き換える。wasm-snipを使った後にさらにwasm-gcやwasm-optをかけることで[バイナリサイズを極限まで小さくできる](https://hacks.mozilla.org/2018/01/oxidizing-source-maps-with-rust-and-webassembly/)

## twiggy

[twiggy](https://github.com/rustwasm/twiggy) はバイナリでどの関数がでかいとかコールグラフとかデッドコードの情報とか教えてくれる。代替はとくになさそうなので一択？