WebAssembly Binary Format Source Code
Module Sections Analysis (Type, Function, Code, Memory, etc.)
The WebAssembly binary format uses a segmented structure, with each section serving a specific function and encoding method.
Core Section Types:
- Type Section: Defines function signatures
- Encoding format:
vec(func_type), wherefunc_typeis(param_types) -> (result_types) - Source implementation typically includes type table construction and validation
- Encoding format:
- Function Section: Declares functions in the module
- Encoding format:
vec(type_idx), indices pointing to type definitions in the Type section - Requires mapping function indices to type definitions during implementation
- Encoding format:
- Code Section: Contains function body implementations
- Encoding format:
vec(code), each code structure includes local variable declarations and instruction sequences - Source code needs to implement instruction decoding and local variable management
- Encoding format:
- Memory Section: Defines linear memory
- Encoding format:
vec(memory_type), wherememory_typeislimits(min, max?) - Implementation requires handling memory initialization and growth logic
- Encoding format:
Source Code Example (Simplified):
// Pseudo-code for parsing Type section
void parse_type_section(BinaryReader& reader) {
uint32_t count = reader.read_varuint32();
for (uint32_t i = 0; i < count; i++) {
uint32_t param_count = reader.read_varuint32();
std::vector<ValType> params;
for (uint32_t j = 0; j < param_count; j++) {
params.push_back(read_valtype(reader));
}
uint32_t result_count = reader.read_varuint32();
std::vector<ValType> results;
for (uint32_t j = 0; j < result_count; j++) {
results.push_back(read_valtype(reader));
}
type_table.push_back({params, results});
}
}Binary Encoding and Decoding (LEB128 Encoding)
WebAssembly uses LEB128 (Little Endian Base 128) variable-length integer encoding to compress data.
LEB128 Characteristics:
- Each byte uses 7 bits for data, with the most significant bit as a continuation flag
- Supports signed and unsigned integer encoding
Decoding Implementation Example:
// Pseudo-code for LEB128 decoding
uint32_t decode_u32_leb128(BinaryReader& reader) {
uint32_t result = 0;
uint32_t shift = 0;
uint8_t byte;
do {
byte = reader.read_byte();
result |= (byte & 0x7f) << shift;
shift += 7;
} while (byte & 0x80);
return result;
}Special Handling in Source Code:
- Boundary checks to prevent maliciously crafted overly long encodings
- Performance optimization: Batch reading and decoding
- Error handling: Detection of invalid encodings
Instruction Set Implementation (Numeric Operations, Control Flow, Memory Access)
The WebAssembly instruction set is divided into several categories:
- Numeric Operation Instructions:
- Integer operations:
i32.add,i64.mul, etc. - Floating-point operations:
f32.sqrt,f64.copysign, etc. - Conversion instructions:
i32.wrap/i64,f32.demote/f64, etc.
- Integer operations:
- Control Flow Instructions:
- Unconditional jumps:
br,br_table - Conditional branches:
if,else,end - Loops:
loop,block
- Unconditional jumps:
- Memory Access Instructions:
- Load:
i32.load,f64.load - Store:
i32.store,f64.store - Memory size management:
memory.size,memory.grow
- Load:
Instruction Execution Engine Example:
// Pseudo-code for instruction dispatch
void execute_instruction(VMContext& ctx, Opcode opcode) {
switch (opcode) {
case Opcode::I32_ADD:
{
int32_t a = ctx.pop_i32();
int32_t b = ctx.pop_i32();
ctx.push_i32(a + b);
}
break;
case Opcode::I32_LOAD:
{
uint32_t offset = ctx.pop_u32();
uint32_t align = /* Parsed from instruction */;
uint32_t addr = ctx.pop_u32() + offset;
int32_t value = ctx.memory.read_i32(addr, align);
ctx.push_i32(value);
}
break;
// Other instructions...
}
}Module Validation and Loading Process
Module validation is a critical step to ensure the safety of WASM binaries.
Validation Process:
- Structural Validation: Check section order and basic structure
- Type Validation: Verify function signature consistency
- Instruction Validation: Check operand types and stack balance
- Memory Validation: Ensure memory accesses are within bounds
Source Implementation Key Points:
- Use a state machine to track the validation process
- Maintain type stack and operand stack simulation
- Terminate loading of invalid modules early
Validation Pseudo-code:
bool validate_module(const Module& module) {
// 1. Check if required sections exist
if (!module.has_type_section()) return false;
// 2. Verify function type consistency
for (auto func_idx : module.function_indices()) {
auto expected_type = module.type_at(func_idx);
if (!check_function_type(module, func_idx, expected_type)) {
return false;
}
}
// 3. Validate code section
for (auto& code : module.codes()) {
if (!validate_code(code)) {
return false;
}
}
return true;
}Binary Format Extensions (SIMD, Threads, Exception Handling)
WebAssembly supports new features through extensions.
Main Extensions:
- SIMD (Single Instruction Multiple Data):
- Introduces v128 type
- 128-bit vector operation instructions
- Requires extending the type system and instruction set
- Threads:
- Shared memory support
- Atomic operation instructions
- Requires thread-safe memory access implementation
- Exception Handling:
try/catch/throwinstructions- Exception type system
- Requires modifications to control flow handling logic
Extension Implementation Example:
// Pseudo-code for SIMD instruction handling
void execute_simd_instruction(VMContext& ctx, SimdOpcode opcode) {
switch (opcode) {
case SimdOpcode::V128_LOAD:
{
uint32_t offset = ctx.pop_u32();
uint32_t align = /* Parse alignment */;
uint32_t addr = ctx.pop_u32() + offset;
v128_t value = ctx.memory.read_v128(addr, align);
ctx.push_v128(value);
}
break;
// Other SIMD instructions...
}
}WebAssembly Virtual Machine Source Code
Stack-Based Virtual Machine Implementation (Operand Stack, Control Stack)
WebAssembly uses a stack-based virtual machine design.
Core Data Structures:
- Operand Stack: Stores intermediate computation values
- Control Stack: Manages control flow contexts (blocks, loops, functions)
Stack Frame Structure Example:
struct StackFrame {
uint32_t return_pc; // Return address
uint32_t local_count; // Number of local variables
ValType* local_types; // Local variable types
// May include other frame-specific data
};
class VMContext {
std::vector<Value> operand_stack; // Operand stack
std::vector<StackFrame> call_stack; // Control stack
// Other VM state...
};Stack Operation Implementation:
void push_value(VMContext& ctx, const Value& val) {
ctx.operand_stack.push_back(val);
}
Value pop_value(VMContext& ctx) {
if (ctx.operand_stack.empty()) {
throw RuntimeError("Operand stack underflow");
}
auto val = ctx.operand_stack.back();
ctx.operand_stack.pop_back();
return val;
}Instruction Execution and Scheduling
Instruction execution is the core functionality of the virtual machine.
Execution Loop Pseudo-code:
void run(VMContext& ctx) {
while (ctx.pc < ctx.code_size) {
Opcode opcode = read_opcode(ctx);
// Performance optimization: Use jump table instead of switch
static void (*dispatch_table[])(VMContext&) = {
/* Opcode::UNREACHABLE */ handle_unreachable,
/* Opcode::NOP */ handle_nop,
/* Opcode::BLOCK */ handle_block,
// ...Other instruction handlers
};
dispatch_table[opcode](ctx);
ctx.pc += get_opcode_length(opcode);
}
}Instruction Scheduling Optimizations:
- Use jump tables instead of large switch statements
- Batch prefetch instructions
- Optimize hot paths
Memory Management and Bounds Checking
Memory safety is a core feature of WASM.
Memory Access Implementation:
Value memory_read(VMContext& ctx, uint32_t addr, ValType type, uint32_t align) {
// Bounds checking
if (addr + size_of(type) > ctx.memory.size()) {
throw RuntimeError("Memory access out of bounds");
}
// Alignment checking
if (addr % align != 0) {
throw RuntimeError("Unaligned memory access");
}
// Actual read
switch (type) {
case ValType::I32:
return Value(ctx.memory.read_i32(addr, align));
case ValType::F64:
return Value(ctx.memory.read_f64(addr, align));
// ...Other types
}
}Memory Growth Implementation:
uint32_t memory_grow(VMContext& ctx, uint32_t delta) {
uint32_t old_size = ctx.memory.size() / PAGE_SIZE;
uint32_t new_size = old_size + delta;
if (new_size > MAX_MEMORY_PAGES) {
return UINT32_MAX; // Indicates growth failure
}
if (!ctx.memory.grow(delta * PAGE_SIZE)) {
return UINT32_MAX;
}
return old_size;
}Function Calls and Table Management
Function calls involve parameter passing and return value handling.
Function Call Implementation:
void call_function(VMContext& ctx, uint32_t func_idx) {
// 1. Get function reference
auto& func = ctx.module.functions[func_idx];
// 2. Verify parameter count
if (ctx.operand_stack.size() < func.param_count) {
throw RuntimeError("Insufficient arguments");
}
// 3. Create new stack frame
StackFrame frame;
frame.return_pc = ctx.pc + get_opcode_length(current_opcode);
frame.local_count = func.local_count;
frame.local_types = func.local_types;
ctx.call_stack.push_back(frame);
// 4. Set parameters
for (uint32_t i = 0; i < func.param_count; i++) {
ctx.frame_locals[i] = ctx.pop_value();
}
// 5. Jump to function entry
ctx.pc = func.start_pc;
}Table Management Implementation:
uint32_t table_get(VMContext& ctx, uint32_t idx) {
if (idx >= ctx.module.table.initial) {
throw RuntimeError("Table index out of bounds");
}
return ctx.table.refs[idx];
}
void table_set(VMContext& ctx, uint32_t idx, uint32_t ref) {
if (idx >= ctx.module.table.initial) {
throw RuntimeError("Table index out of bounds");
}
ctx.table.refs[idx] = ref;
}Exception Handling and Error Propagation
The exception handling mechanism ensures errors are propagated correctly.
Exception Handling Implementation:
void handle_throw(VMContext& ctx) {
uint32_t exception_idx = ctx.pop_u32();
auto& exception = ctx.module.exceptions[exception_idx];
// Collect parameters
std::vector<Value> args;
for (uint32_t i = 0; i < exception.param_count; i++) {
args.push_back(ctx.pop_value());
}
std::reverse(args.begin(), args.end());
// Find catch block
for (auto it = ctx.call_stack.rbegin(); it != ctx.call_stack.rend(); ++it) {
if (it->has_catch(exception_idx)) {
// Set exception context and jump to catch block
ctx.current_exception = {exception_idx, args};
ctx.pc = it->catch_pc;
return;
}
}
// No catch block found, terminate execution
throw RuntimeException("Uncaught exception");
}WebAssembly Toolchain Source Code
Emscripten Compiler Source Code (C/C++ to WASM Conversion)
Emscripten compiles C/C++ code into WASM.
Key Components:
- Frontend: Clang/LLVM processes C/C++ code
- Backend: Emscripten-specific LLVM passes
- Runtime: Provides system emulation (file system, network, etc.)
Compilation Process Pseudo-code:
void compile_to_wasm(const std::string& source) {
// 1. Use Clang to generate LLVM IR
auto ir = clang_compile_to_ir(source);
// 2. Apply Emscripten-specific optimizations
apply_emscripten_optimizations(ir);
// 3. Generate WASM
auto wasm = llvm_generate_wasm(ir);
// 4. Add runtime support
add_runtime_support(wasm);
// 5. Output final WASM file
write_wasm_file(wasm);
}System Call Emulation:
// Pseudo-code for file system emulation
void emulate_fs_call(WASMContext& ctx, uint32_t syscall_num, uint32_t* args) {
switch (syscall_num) {
case FS_OPEN:
{
const char* path = ctx.memory.read_string(args[0]);
int flags = args[1];
int fd = fs_open(path, flags);
ctx.set_return_value(fd);
}
break;
// Other system calls...
}
}LLVM Backend Source Code (WASM Target Support)
The LLVM backend converts IR to WASM.
Key Implementations:
- WASM Target Description: Defines instruction set and ABI
- Code Generator: Converts LLVM IR to WASM instructions
- Optimization Passes: WASM-specific optimizations
Instruction Selection Example:
// Pseudo-code for instruction selection
void select_instruction(Instruction* inst) {
switch (inst->opcode()) {
case Instruction::Add:
if (inst->type()->is_integer_ty()) {
// Generate i32.add or i64.add
emit_wasm_insn(inst->is_32bit() ? WASM_I32_ADD : WASM_I64_ADD);
} else if (inst->type()->is_floating_point_ty()) {
// Generate f32.add or f64.add
emit_wasm_insn(inst->is_32bit() ? WASM_F32_ADD : WASM_F64_ADD);
}
break;
// Other instructions...
}
}Memory Model Implementation:
// Pseudo-code for memory access generation
void generate_memory_access(MemoryAccessInst* inst) {
// Calculate address
Value* addr = calculate_address(inst);
// Generate load/store instructions
if (inst->is_load()) {
switch (inst->type()->get_primitive_size_in_bits()) {
case 32:
emit_wasm_insn(WASM_I32_LOAD);
break;
case 64:
emit_wasm_insn(WASM_I64_LOAD);
break;
// Other types...
}
} else {
// Store instructions...
}
}wasm-pack Source Code (Rust to WASM Packaging)
wasm-pack is the official Rust WASM toolchain.
Core Functions:
- Project Initialization: Creates basic project structure
- Build System Integration: Integrates with Cargo
- Packaging: Generates WASM packages for different environments
- JavaScript Glue Code Generation
Build Process Pseudo-code:
// Pseudo-code for build process
fn build_wasm_project(project_dir: &Path) -> Result<()> {
// 1. Read Cargo.toml configuration
let config = read_cargo_toml(project_dir)?;
// 2. Set build target to wasm32-unknown-unknown
set_cargo_target("wasm32-unknown-unknown")?;
// 3. Execute Cargo build
let build_result = cargo_build(project_dir)?;
// 4. Process generated WASM file
let wasm_file = find_wasm_file(build_result.output_dir)?;
// 5. Apply wasm-opt optimization
optimize_wasm(wasm_file)?;
// 6. Generate JavaScript glue code
generate_js_glue(project_dir, wasm_file)?;
// 7. Create pkg directory structure
create_pkg_directory(project_dir)?;
// 8. Copy necessary files to pkg directory
copy_files_to_pkg(project_dir)?;
Ok(())
}JavaScript Glue Code Generation:
// Pseudo-code for JS glue code generation
fn generate_js_glue(project_dir: &Path, wasm_file: &Path) -> Result<()> {
let mut js_code = String::new();
// 1. Generate module loading code
js_code.push_str("// Auto-generated WASM glue code\n");
js_code.push_str("import * as wasm from './");
js_code.push_str(wasm_file.file_name().unwrap().to_str().unwrap());
js_code.push_str("';\n\n");
// 2. Generate export function wrappers
for export in get_wasm_exports(wasm_file)? {
js_code.push_str(&format!("export function {}(", export.name));
// Parameter list...
js_code.push_str(") {\n");
js_code.push_str(&format!(" return wasm.{}(", export.name));
// Parameter passing...
js_code.push_str(");\n");
js_code.push_str("}\n\n");
}
// 3. Write file
let js_path = project_dir.join("pkg").join("index.js");
fs::write(js_path, js_code)?;
Ok(())
}wasm-bindgen Source Code (JavaScript and WASM Interaction)
wasm-bindgen simplifies interactions between Rust and JavaScript.
Core Functions:
- Type Conversion: Converts between Rust and JavaScript types
- Function Binding: Automatically generates function bindings
- DOM Access: Provides safe DOM operation interfaces
Type Conversion Implementation:
// Pseudo-code for type conversion
fn convert_to_js(value: &Value) -> JsValue {
match value {
Value::I32(i) => JsValue::from(*i),
Value::F64(f) => JsValue::from(*f),
Value::String(s) => JsValue::from_str(s),
Value::Array(arr) => {
let js_array = JsValue::new(js_sys::Array::new());
for item in arr {
js_array.push(&convert_to_js(item));
}
js_array
}
// Other types...
}
}Function Binding Generation:
// Pseudo-code for function binding generation
fn generate_function_binding(func: &Function) -> String {
let mut js_code = String::new();
// 1. Generate function signature
js_code.push_str(&format!("export function {}(", func.name));
for param in &func.params {
js_code.push_str(¶m.js_type());
js_code.push_str(", ");
}
if !func.params.is_empty() {
js_code.truncate(js_code.len() - 2); // Remove trailing ", "
}
js_code.push_str(") {\n");
// 2. Generate parameter conversion
for (i, param) in func.params.iter().enumerate() {
js_code.push_str(&format!(" let {} = {};\n",
param.rust_name(),
param.js_to_rust_conversion(i + 1)));
}
// 3. Generate function call
js_code.push_str(&format!(" let result = wasm.{}(", func.name));
for i in 0..func.params.len() {
if i > 0 { js_code.push_str(", "); }
js_code.push_str(&format!("{}{}",
if param.needs_conversion() { "convert_to_wasm(" } else { "" },
param.rust_name(),
if param.needs_conversion() { ")" } else { "" }));
}
js_code.push_str(");\n");
// 4. Generate return value conversion
if let Some(return_type) = &func.return_type {
js_code.push_str(&format!(" return {};\n",
return_type.rust_to_js_conversion("result")));
} else {
js_code.push_str(" return;\n");
}
js_code.push_str("}\n");
js_code
}WABT Tool Source Code (WAT and WASM Conversion)
The WebAssembly Binary Toolkit (WABT) provides tools for converting between WASM binary and text formats.
Core Components:
- WASM Parser: Reads and validates WASM binaries
- WAT Generator: Generates readable text from binaries
- WASM Generator: Generates binaries from text
- Validator: Ensures modules comply with specifications
WASM to WAT Conversion Pseudo-code:
void binary_to_text(const Module& module, TextWriter& writer) {
// 1. Write module start
writer.write("(module\n");
// 2. Process sections
for (const auto& type : module.types) {
writer.write(" (type ");
write_func_type(writer, type);
writer.write(")\n");
}
for (const auto& func : module.functions) {
writer.write(" (func ");
// Write function signature index and local variables
// ...
writer.write("\n");
// Write instructions
for (const auto& instr : func.code) {
writer.write(" ");
write_instruction(writer, instr);
writer.write("\n");
}
writer.write(" )\n");
}
// 3. Write module end
writer.write(")\n");
}WAT to WASM Conversion Pseudo-code:
void text_to_binary(TextParser& parser, ModuleBuilder& builder) {
while (parser.has_more()) {
auto token = parser.next_token();
if (token == "(type") {
// Parse function type
FuncType type = parse_func_type(parser);
builder.add_type(type);
}
else if (token == "(func") {
// Parse function
Function func = parse_function(parser);
builder.add_function(func);
}
// Other section processing...
}
// Validate module
if (!builder.validate()) {
throw ParseError("Invalid module");
}
// Generate binary
builder.build_binary();
}Instruction Parsing Example:
Instruction parse_instruction(TextParser& parser) {
auto opcode_token = parser.next_token();
Opcode opcode = parse_opcode(opcode_token);
switch (opcode) {
case Opcode::I32_ADD:
return Instruction(Opcode::I32_ADD);
case Opcode::I32_LOAD: {
// Parse memory operands
uint32_t align = parse_align(parser);
uint32_t offset = parse_offset(parser);
return Instruction(Opcode::I32_LOAD, align, offset);
}
// Other instructions...
}
}Summary
The WebAssembly source architecture demonstrates its strength as a foundation for modern web technologies:
- Binary Format: A carefully designed compact binary format supporting multiple extensions
- Virtual Machine Implementation: An efficient and secure stack-based execution environment
- Toolchain Ecosystem: A comprehensive toolchain from high-level languages to browsers
Each component is meticulously designed to balance performance, security, and usability, making WebAssembly an ideal choice for web platforms and high-performance computing.



