- A compact binary format (
.wasm) - A human-readable text format (
.wat)
.wasm binary—examining each section, representative bytecode snippets, and how these modules load and execute in a runtime.
Compiling a C++ Program to WebAssembly
To analyze real-world binaries, let’s compile a simple C++ “Hello, WebAssembly!” program. Ensure you have Emscripten installed:Make sure your Emscripten SDK installation is up to date and the
emcc command is in your PATH.hello.js (loader/JavaScript glue) and hello.wasm (binary module) for inspection.
Module Header: Magic Number & Version
Every WebAssembly binary starts with an 8-byte header:- Magic number
0x00 0x61 0x73 0x6Didentifies the file as WebAssembly. - Version
0x01 0x00 0x00 0x00corresponds to the current WASM spec.
Section Layout
After the header, a.wasm file is organized into sequentially numbered sections. Each section has:
- A one-byte section ID
- A ULEB128-encoded section length
- A payload (raw bytes)
| Section ID | Section | Purpose |
|---|---|---|
| 0 | Custom | Custom metadata, debug information |
| 1 | Type | Function signatures |
| 2 | Import | Imports (functions, memory, tables, globals) |
| 3 | Function | Function declarations (type indices) |
| 4 | Table | Tables of function references (for call_indirect) |
| 5 | Memory | Linear memory definitions (pages and limits) |
| 6 | Global | Module-level global variables |
| 7 | Export | Exports (functions, memory, tables, globals) |
| 8 | Start | Optional start function index |
| 9 | Element | Table initialization entries |
| 10 | Code | Function bodies (locals and opcodes) |
| 11 | Data | Data segment initializers |
1. Type Section (ID 1)
Defines function signatures. Each entry begins with0x60 (function type), followed by parameter and return types:
0x7Fdenotesi32.- The leading
0x60indicates a function signature.
2. Import Section (ID 2)
Imports functions, memory, tables, or globals from the host environment:- Module and field names are length-prefixed UTF-8 strings.
- Import kind
0x00refers to a function.
3. Function Section (ID 3)
Lists the type index for each function defined in this module:4. Table Section (ID 4)
Specifies tables of function references, used bycall_indirect:
0x70=funcref.- Flags = 0 → only an initial size is provided.
5. Memory Section (ID 5)
Defines the module’s linear memory (in 64 KiB pages).
- Flags=0 indicates only an initial limit.
- Limits are ULEB128-encoded page counts (1 page = 64 KiB).
6. Global Section (ID 6)
Declares module-level globals with type, mutability, and initialization:7. Export Section (ID 7)
Exports functions, memory, tables, or globals to the host:- Export kind codes:
0x00=Function,0x02=Memory, etc.
Additional Sections
- Start (ID 8): Designates an entrypoint function.
- Element (ID 9): Table initialization data.
- Code (ID 10): Function bodies (local variables + opcodes).
- Data (ID 11): Memory data segments.
- Custom (ID 0): Arbitrary metadata and debug info.
When to Dive into the Binary Format?
Inspecting raw bytecode isn’t required for everyday WebAssembly development, but it shines in:
- Performance optimization
- Low-level debugging & inspection
- Security auditing & fuzzing
- Legacy system integration
- Custom features via custom sections
- Teaching, research, and compiler comparison
Deep diving into the
.wasm layout can help troubleshoot toolchain issues and squeeze out maximum performance.