WABT (WebAssembly Binary Toolkit)
Suite of tools for working with WebAssembly binaries — convert between text (.wat) and binary (.wasm) formats, validate, inspect, and transform WebAssembly modules.
✅ Pros
- + Essential for WASM debugging — read and write .wat (text format)
- + wat2wasm: compile text format to binary for learning and testing
- + wasm-objdump: inspect WASM binary contents without decompiling
- + wasm-validate: verify WASM files are well-formed
- + wasm-decompile: generate readable pseudo-code from WASM
⚠️ Cons
- − Low-level tool — not needed for most high-level WASM development
- − Text format (.wat) is verbose and not meant for large codebases
- − Limited compared to full debuggers (WASM debugging in Chrome DevTools preferred)
Overview
WABT (pronounced “wabbit”) is the reference toolkit for working directly with WebAssembly at the binary/text level. It’s invaluable for understanding how WASM works, debugging compiled modules, and learning the WebAssembly specification.
Installation
# macOS
brew install wabt
# Ubuntu
sudo apt-get install wabt
# Build from source
git clone https://github.com/WebAssembly/wabt.git
cd wabt && git submodule update --init
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build . -j$(nproc)
WAT: WebAssembly Text Format
Write WASM directly in text format for learning:
;; add.wat — WebAssembly text format
(module
;; Export a function named "add"
(func $add (export "add") (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add
)
;; Export memory
(memory (export "memory") 1) ;; 1 page = 64KB
;; More complex: factorial
(func $factorial (export "factorial") (param $n i32) (result i32)
(if (result i32)
(i32.le_s (local.get $n) (i32.const 1))
(then (i32.const 1))
(else
(i32.mul
(local.get $n)
(call $factorial
(i32.sub (local.get $n) (i32.const 1))
)
)
)
)
)
)
Key Commands
# Compile .wat to .wasm binary
wat2wasm add.wat -o add.wasm
# Disassemble .wasm back to .wat
wasm2wat add.wasm -o add.wat
# Validate a .wasm file
wasm-validate add.wasm
# → add.wasm: OK
# Inspect binary structure
wasm-objdump -d add.wasm # Disassemble
wasm-objdump -x add.wasm # Show sections (type, import, export, function, etc.)
# Generate pseudo-code (easier to read than .wat)
wasm-decompile add.wasm
# → export function add(a:int, b:int):int { return a + b; }
# Strip debug info for smaller binaries
wasm-strip my-module.wasm
# Show stats
wasm-stats my-module.wasm
Inspecting Compiled WASM
# See what an Emscripten or wasm-pack build actually contains
wasm-objdump -x output.wasm | head -50
# Type section: shows all function signatures
# Import section: shows what's imported from JS
# Export section: shows what's exported to JS
# Code section: number of functions
# Count how many functions are in a WASM binary
wasm-objdump -d output.wasm | grep "^func\[" | wc -l
JavaScript WABT (Browser/Node.js)
// wabt.js — run WABT tools in Node.js
import wabt from 'wabt';
const wabtInstance = await wabt();
const wat = `
(module
(func $add (export "add") (param i32 i32) (result i32)
local.get 0
local.get 1
i32.add))
`;
// Compile WAT to WASM in Node.js (no CLI needed)
const module = wabtInstance.parseWat('test.wat', wat);
const { buffer } = module.toBinary({});
const { instance } = await WebAssembly.instantiate(buffer);
console.log(instance.exports.add(2, 3)); // 5
Concrete Use Case: Debugging a Rust-Compiled WebAssembly Module with WAT Inspection
A team building a browser-based image processing application compiled their Rust library to WebAssembly using wasm-pack. After deploying a new release, users reported that the Gaussian blur filter was producing corrupted output — pixel values were wrapping around to zero instead of clamping at 255. The Rust code looked correct, and the native test suite passed. The problem only manifested when running the compiled .wasm module in the browser, suggesting a compilation or memory issue specific to the WebAssembly target.
The developer used wasm2wat to convert the 1.2MB .wasm binary into its text representation and searched for the blur kernel function. By reading the .wat output, they discovered that the compiler had emitted i32.wrap_i64 followed by i32.and with a bitmask, but the mask value was incorrect — it was truncating to 8 bits before the clamp operation rather than after. The developer also ran wasm-objdump -x to inspect the memory section and confirmed the module was allocating only one page (64KB) of linear memory, which was insufficient for processing images larger than 256x256 pixels. The root cause was a missing #[cfg(target_arch = "wasm32")] attribute on a memory allocation hint that the Rust compiler used for the WASM target. After fixing the Rust source and recompiling, the developer ran wasm-validate to confirm the new binary was well-formed, then used wasm-objdump -d to verify the corrected instruction sequence before deploying.
This debugging workflow — converting binary to text, inspecting specific functions, validating the output, and examining section headers — is exactly what WABT was designed for. Without these tools, the developer would have been limited to black-box debugging in the browser with console.log statements, unable to see the actual WebAssembly instructions the compiler generated. WABT bridged the gap between high-level Rust source code and the low-level WASM binary, making the compilation bug visible and fixable within an hour instead of days of guesswork.
When to Use WABT
Use WABT when:
- You need to debug a compiled
.wasmbinary by inspecting the actual WebAssembly instructions, especially when the bug only appears in the WASM target and not in native builds - You are learning WebAssembly and want to write
.wattext format by hand, compile it to.wasmwithwat2wasm, and understand how the instruction set works at a fundamental level - You need to validate that a
.wasmfile is well-formed before deploying it, particularly when the binary comes from an untrusted source or an experimental compiler toolchain - You want to audit the size and structure of a WASM binary using
wasm-objdumpto understand what sections, imports, and exports it contains before loading it in production - You need to strip debug information from a
.wasmbinary withwasm-stripto reduce file size for production deployment
When NOT to use WABT:
- You are doing standard application-level WebAssembly development with
wasm-pack(Rust), Emscripten (C/C++), or AssemblyScript and do not need to inspect the binary output — the high-level toolchains handle compilation and validation automatically - You need interactive step-through debugging of WebAssembly — use Chrome DevTools or Firefox Developer Tools which provide source-mapped breakpoints for WASM modules
- You are looking for a WebAssembly optimization tool — use
wasm-optfrom the Binaryen toolkit instead, which performs dead code elimination, inlining, and other binary-level optimizations that WABT does not provide