Binaryen - WebAssembly Compiler & Optimizer Toolchain Cheatsheet
Binaryen - WebAssembly Compiler & Optimizer Toolchain Cheatsheet
Binaryen is a compiler and toolchain infrastructure library for WebAssembly, written in C++. Its headline tool, wasm-opt, loads a wasm module into an intermediate representation and runs optimization and transformation passes over it — the same machinery that toolchains like Emscripten use to shrink and speed up wasm. For reverse engineers, Binaryen’s disassembler (wasm-dis) and its analysis/transform passes (including deobfuscation-adjacent simplifications) make it a useful complement to WABT.
Installation
| Platform | Command |
|---|---|
| macOS (Homebrew) | brew install binaryen |
| Debian/Ubuntu | sudo apt install binaryen |
| Arch Linux | sudo pacman -S binaryen |
| npm | npm install -g binaryen (JS bindings) |
| From source | build with CMake from the WebAssembly/binaryen repo |
| Verify | wasm-opt --version |
Core Tools
| Tool | Does |
|---|---|
wasm-opt | Load, optimize/transform, and re-emit a module |
wasm-dis | Disassemble .wasm → text |
wasm-as | Assemble text → .wasm |
wasm-metadce | Dead-code-eliminate across module + JS |
wasm-ctor-eval | Evaluate constructors at build time |
wasm-reduce | Shrink a module while preserving a behavior (bug repro) |
wasm-shell | Run the spec test shell |
Optimizing a Module
# Optimize for size and speed
wasm-opt -O3 module.wasm -o module.opt.wasm
# Optimize aggressively for size
wasm-opt -Oz module.wasm -o module.small.wasm
| Flag | Effect |
|---|---|
-O / -O1..-O4 | Increasing optimization for speed |
-Os | Optimize for size |
-Oz | Optimize aggressively for size |
--enable-FEATURE | Enable a wasm proposal (simd, threads, …) |
-o FILE | Output file |
Running Individual Passes
wasm-opt exposes many named passes you can run selectively — useful for RE (simplifying obfuscated code) as well as builds.
# List all available passes
wasm-opt --help | grep -A2 "passes:"
# Run specific passes (e.g. simplify + remove unused)
wasm-opt module.wasm --precompute --simplify-locals \
--remove-unused-names --vacuum -o clean.wasm
| Pass (examples) | Purpose |
|---|---|
--precompute | Fold constant expressions |
--simplify-locals | Reduce redundant local usage |
--vacuum | Remove obviously dead code |
--remove-unused-module-elements | Strip unused funcs/globals |
--flatten / --rereloop | Restructure control flow |
Disassemble / Assemble
# Disassemble to Binaryen's text form
wasm-dis module.wasm -o module.wat
# Assemble back
wasm-as module.wat -o module.wasm
RE Use: Simplify Then Read
A practical reversing trick is to run Binaryen’s simplification passes to clean up compiler/obfuscator noise, then read the result.
# Simplify a gnarly module, then decompile with WABT
wasm-opt target.wasm --precompute --simplify-locals --vacuum -o simpler.wasm
wasm-decompile simpler.wasm -o simpler.dcmp
Minimizing a Repro (wasm-reduce)
# Shrink a module while a script still reports the behavior of interest
wasm-reduce big.wasm --command "node check.js" -o small.wasm
Great for isolating the minimal wasm that triggers a bug or a specific behavior.
Binaryen vs Related Tools
| Tool | Focus |
|---|---|
| Binaryen | Optimize/transform/analyze wasm (compiler-grade) |
| WABT | Convert/inspect/validate/decompile |
| diswasm | Pseudocode decompiler |
| wasm-tools | Rust low-level manipulation |
For RE: use Binaryen to simplify, WABT/diswasm to read, and wasm-tools to validate/mutate.