wasm-emscripten
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWebAssembly with Emscripten
使用Emscripten开发WebAssembly
Purpose
用途
Guide agents through compiling C/C++ to WebAssembly using Emscripten: emcc flag selection, function exports, memory model configuration, Asyncify for asynchronous C code, debugging WASM binaries, and targeting WASI vs browser.
指导开发者通过Emscripten将C/C++编译为WebAssembly:包括emcc参数选择、函数导出、内存模型配置、使用Asyncify处理异步C代码、调试WASM二进制文件,以及针对WASI与浏览器环境进行适配。
Triggers
触发场景
- "How do I compile C to WebAssembly with Emscripten?"
- "How do I export a C function to JavaScript?"
- "How does WebAssembly memory work with Emscripten?"
- "How do I debug a .wasm file?"
- "How do I use Asyncify to make synchronous C code async?"
- "What's the difference between WASI and Emscripten browser target?"
- "如何使用Emscripten将C编译为WebAssembly?"
- "如何将C函数导出到JavaScript?"
- "WebAssembly内存在Emscripten中如何工作?"
- "如何调试.wasm文件?"
- "如何使用Asyncify将同步C代码转为异步?"
- "WASI与Emscripten浏览器目标有什么区别?"
Workflow
工作流程
1. Setup and first build
1. 环境搭建与首次构建
bash
undefinedbash
undefinedInstall Emscripten SDK
Install Emscripten SDK
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh # add emcc to PATH
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh # add emcc to PATH
Verify
Verify
emcc --version
emcc --version
Compile C to WASM (browser target)
Compile C to WASM (browser target)
emcc hello.c -o hello.html # generates hello.html + hello.js + hello.wasm
emcc hello.c -o hello.js # just JS + WASM (no HTML shell)
emcc hello.c -o hello.html # generates hello.html + hello.js + hello.wasm
emcc hello.c -o hello.js # just JS + WASM (no HTML shell)
Serve locally (WASM requires HTTP, not file://)
Serve locally (WASM requires HTTP, not file://)
python3 -m http.server 8080
python3 -m http.server 8080
undefinedundefined2. Exporting functions to JavaScript
2. 向JavaScript导出函数
c
// math.c
#include <emscripten.h>
// EMSCRIPTEN_KEEPALIVE prevents dead-code elimination
EMSCRIPTEN_KEEPALIVE
int add(int a, int b) {
return a + b;
}
EMSCRIPTEN_KEEPALIVE
double sqrt_approx(double x) {
return x * 0.5 + 1.0;
}bash
undefinedc
// math.c
#include <emscripten.h>
// EMSCRIPTEN_KEEPALIVE prevents dead-code elimination
EMSCRIPTEN_KEEPALIVE
int add(int a, int b) {
return a + b;
}
EMSCRIPTEN_KEEPALIVE
double sqrt_approx(double x) {
return x * 0.5 + 1.0;
}bash
undefinedExport specific functions
Export specific functions
emcc math.c -o math.js
-s EXPORTED_FUNCTIONS='["_add","_sqrt_approx"]'
-s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]'
-s MODULARIZE=1
-s EXPORT_NAME=MathModule
-s EXPORTED_FUNCTIONS='["_add","_sqrt_approx"]'
-s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]'
-s MODULARIZE=1
-s EXPORT_NAME=MathModule
emcc math.c -o math.js
-s EXPORTED_FUNCTIONS='["_add","_sqrt_approx"]'
-s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]'
-s MODULARIZE=1
-s EXPORT_NAME=MathModule
-s EXPORTED_FUNCTIONS='["_add","_sqrt_approx"]'
-s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]'
-s MODULARIZE=1
-s EXPORT_NAME=MathModule
The leading underscore is required for C functions
The leading underscore is required for C functions
```javascript
// Using exported functions in JS
const Module = await MathModule();
// Direct call
const result = Module._add(3, 4);
// Via ccall (type-safe)
const result2 = Module.ccall('add', 'number', ['number', 'number'], [3, 4]);
// Via cwrap (creates a callable JS function)
const add = Module.cwrap('add', 'number', ['number', 'number']);
console.log(add(3, 4)); // 7
```javascript
// Using exported functions in JS
const Module = await MathModule();
// Direct call
const result = Module._add(3, 4);
// Via ccall (type-safe)
const result2 = Module.ccall('add', 'number', ['number', 'number'], [3, 4]);
// Via cwrap (creates a callable JS function)
const add = Module.cwrap('add', 'number', ['number', 'number']);
console.log(add(3, 4)); // 73. Memory model
3. 内存模型
Emscripten provides a linear memory heap accessible from both C and JS:
bash
undefinedEmscripten提供了一块可同时被C和JS访问的线性内存堆:
bash
undefinedConfigure initial and maximum heap
Configure initial and maximum heap
emcc prog.c -o prog.js
-s INITIAL_MEMORY=16MB
-s MAXIMUM_MEMORY=256MB
-s ALLOW_MEMORY_GROWTH=1 # allow dynamic growth
-s INITIAL_MEMORY=16MB
-s MAXIMUM_MEMORY=256MB
-s ALLOW_MEMORY_GROWTH=1 # allow dynamic growth
emcc prog.c -o prog.js
-s INITIAL_MEMORY=16MB
-s MAXIMUM_MEMORY=256MB
-s ALLOW_MEMORY_GROWTH=1 # allow dynamic growth
-s INITIAL_MEMORY=16MB
-s MAXIMUM_MEMORY=256MB
-s ALLOW_MEMORY_GROWTH=1 # allow dynamic growth
Stack size (default 64KB)
Stack size (default 64KB)
emcc prog.c -o prog.js -s STACK_SIZE=1MB
emcc prog.c -o prog.js -s STACK_SIZE=1MB
Shared memory (for SharedArrayBuffer / threads)
Shared memory (for SharedArrayBuffer / threads)
emcc prog.c -o prog.js -s SHARED_MEMORY=1 -s USE_PTHREADS=1
```javascript
// Accessing C memory from JS
const ptr = Module._malloc(1024); // allocate
Module.HEAPU8.set([1, 2, 3], ptr); // write bytes
Module._free(ptr); // free
// Read a C string
const strPtr = Module.ccall('get_message', 'number', [], []);
const str = Module.UTF8ToString(strPtr);
// Write a string to C
const jsStr = "hello";
const cStr = Module.stringToNewUTF8(jsStr); // malloc + copy
Module._process_string(cStr);
Module._free(cStr);emcc prog.c -o prog.js -s SHARED_MEMORY=1 -s USE_PTHREADS=1
```javascript
// Accessing C memory from JS
const ptr = Module._malloc(1024); // allocate
Module.HEAPU8.set([1, 2, 3], ptr); // write bytes
Module._free(ptr); // free
// Read a C string
const strPtr = Module.ccall('get_message', 'number', [], []);
const str = Module.UTF8ToString(strPtr);
// Write a string to C
const jsStr = "hello";
const cStr = Module.stringToNewUTF8(jsStr); // malloc + copy
Module._process_string(cStr);
Module._free(cStr);4. Asyncify — synchronous C in async environments
4. Asyncify — 异步环境中的同步C代码
Asyncify lets synchronous C code suspend and resume for async operations (like , sleep, etc.):
fetch()c
// async.c
#include <emscripten.h>
// Synchronous sleep in C (blocks C, but yields to JS event loop)
EM_JS(void, do_fetch, (const char *url), {
// Emscripten generates wrappers to suspend C while JS runs
Asyncify.handleAsync(async () => {
const resp = await fetch(UTF8ToString(url));
const text = await resp.text();
console.log(text);
});
});
void process_url(const char *url) {
do_fetch(url); // looks synchronous in C
printf("fetch complete\n");
}bash
undefinedAsyncify允许同步C代码暂停和恢复,以处理异步操作(如、sleep等):
fetch()c
// async.c
#include <emscripten.h>
// Synchronous sleep in C (blocks C, but yields to JS event loop)
EM_JS(void, do_fetch, (const char *url), {
// Emscripten generates wrappers to suspend C while JS runs
Asyncify.handleAsync(async () => {
const resp = await fetch(UTF8ToString(url));
const text = await resp.text();
console.log(text);
});
});
void process_url(const char *url) {
do_fetch(url); // looks synchronous in C
printf("fetch complete\n");
}bash
undefinedEnable Asyncify
Enable Asyncify
emcc async.c -o async.js
-s ASYNCIFY
-s ASYNCIFY_STACK_SIZE=16384
-O2 # Asyncify works better with optimization
-s ASYNCIFY
-s ASYNCIFY_STACK_SIZE=16384
-O2 # Asyncify works better with optimization
undefinedemcc async.c -o async.js
-s ASYNCIFY
-s ASYNCIFY_STACK_SIZE=16384
-O2 # Asyncify works better with optimization
-s ASYNCIFY
-s ASYNCIFY_STACK_SIZE=16384
-O2 # Asyncify works better with optimization
undefined5. Optimization and wasm-opt
5. 优化与wasm-opt
bash
undefinedbash
undefinedOptimization levels
Optimization levels
emcc prog.c -O0 -o prog.js # no optimization (fastest build)
emcc prog.c -O2 -o prog.js # balanced
emcc prog.c -O3 -o prog.js # aggressive
emcc prog.c -Os -o prog.js # optimize for size
emcc prog.c -Oz -o prog.js # aggressive size (Emscripten's smallest)
emcc prog.c -O0 -o prog.js # no optimization (fastest build)
emcc prog.c -O2 -o prog.js # balanced
emcc prog.c -O3 -o prog.js # aggressive
emcc prog.c -Os -o prog.js # optimize for size
emcc prog.c -Oz -o prog.js # aggressive size (Emscripten's smallest)
Post-process with wasm-opt (Binaryen)
Post-process with wasm-opt (Binaryen)
wasm-opt -Oz -o prog.opt.wasm prog.wasm # optimize for size
wasm-opt -O4 -o prog.opt.wasm prog.wasm # optimize for speed
wasm-opt -Oz -o prog.opt.wasm prog.wasm # optimize for size
wasm-opt -O4 -o prog.opt.wasm prog.wasm # optimize for speed
Compare sizes
Compare sizes
ls -lh prog.wasm prog.opt.wasm
undefinedls -lh prog.wasm prog.opt.wasm
undefined6. Debugging WASM
6. 调试WASM
bash
undefinedbash
undefinedBuild with debug info
Build with debug info
emcc prog.c -g -O0 -o prog.html
-s ASSERTIONS=1
-s SAFE_HEAP=1 # catch misaligned accesses
-s ASSERTIONS=1
-s SAFE_HEAP=1 # catch misaligned accesses
emcc prog.c -g -O0 -o prog.html
-s ASSERTIONS=1
-s SAFE_HEAP=1 # catch misaligned accesses
-s ASSERTIONS=1
-s SAFE_HEAP=1 # catch misaligned accesses
In Chrome DevTools:
In Chrome DevTools:
Sources → prog.wasm → line-by-line C source debugging
Sources → prog.wasm → line-by-line C source debugging
(requires -g and browser with WASM debugging support)
(requires -g and browser with WASM debugging support)
LLDB with WASM (wasmtime)
LLDB with WASM (wasmtime)
See skills/runtimes/wasm-wasmtime for CLI WASM debugging
See skills/runtimes/wasm-wasmtime for CLI WASM debugging
```bash
```bashEmscripten debug helpers
Emscripten debug helpers
emcc prog.c -o prog.js
-s ASSERTIONS=2 # extensive runtime checks -s SAFE_HEAP=1 # sanitize heap accesses -s STACK_OVERFLOW_CHECK=1
-s ASSERTIONS=2 # extensive runtime checks -s SAFE_HEAP=1 # sanitize heap accesses -s STACK_OVERFLOW_CHECK=1
emcc prog.c -o prog.js
-s ASSERTIONS=2 # extensive runtime checks -s SAFE_HEAP=1 # sanitize heap accesses -s STACK_OVERFLOW_CHECK=1
-s ASSERTIONS=2 # extensive runtime checks -s SAFE_HEAP=1 # sanitize heap accesses -s STACK_OVERFLOW_CHECK=1
Print generated JS
Print generated JS
emcc prog.c -o prog.js && cat prog.js | head -100
undefinedemcc prog.c -o prog.js && cat prog.js | head -100
undefined7. WASI vs browser target
7. WASI vs 浏览器目标
| Feature | Browser (Emscripten) | WASI |
|---|---|---|
| Host APIs | Web APIs (fetch, WebGL, etc.) | POSIX subset (files, stdin/stdout) |
| Runtime | Browser JS engine | wasmtime, wasmer, WAMR, Node.js |
| Threads | SharedArrayBuffer + pthreads | wasi-threads (limited) |
| Networking | fetch(), WebSocket | wasi-http (preview2) |
| Use case | Web applications | Server-side, CLI tools, edge |
bash
undefined| 特性 | 浏览器(Emscripten) | WASI |
|---|---|---|
| 宿主API | Web APIs(fetch、WebGL等) | POSIX子集(文件、标准输入/输出) |
| 运行时 | 浏览器JS引擎 | wasmtime、wasmer、WAMR、Node.js |
| 线程 | SharedArrayBuffer + pthreads | wasi-threads(有限支持) |
| 网络 | fetch()、WebSocket | wasi-http(预览版2) |
| 适用场景 | Web应用 | 服务端、CLI工具、边缘计算 |
bash
undefinedBuild for WASI (no browser JS, pure WASM)
Build for WASI (no browser JS, pure WASM)
emcc prog.c -o prog.wasm --target=wasi
emcc prog.c -o prog.wasm --target=wasi
Or use wasi-sdk (better WASI support than Emscripten)
Or use wasi-sdk (better WASI support than Emscripten)
/opt/wasi-sdk/bin/clang --sysroot=/opt/wasi-sdk/share/wasi-sysroot
prog.c -o prog.wasm wasmtime prog.wasm
prog.c -o prog.wasm wasmtime prog.wasm
For Emscripten linker flags reference, see [references/emscripten-linker-flags.md](references/emscripten-linker-flags.md)./opt/wasi-sdk/bin/clang --sysroot=/opt/wasi-sdk/share/wasi-sysroot
prog.c -o prog.wasm wasmtime prog.wasm
prog.c -o prog.wasm wasmtime prog.wasm
Emscripten链接器参数参考见 [references/emscripten-linker-flags.md](references/emscripten-linker-flags.md)。Related skills
相关技能
- Use for server-side WASM with wasmtime CLI and Rust embedding
skills/runtimes/wasm-wasmtime - Use for Clang-based WASM compilation with WASI SDK
skills/compilers/clang - Use for inspecting WASM binary structure
skills/binaries/elf-inspection
- 使用实现基于wasmtime CLI和Rust嵌入的服务端WASM开发
skills/runtimes/wasm-wasmtime - 使用通过WASI SDK实现基于Clang的WASM编译
skills/compilers/clang - 使用检查WASM二进制结构
skills/binaries/elf-inspection