关于 wasm-emscripten
为使用 Emscripten 将 C/C++ 编译为 WebAssembly 的 Agent 提供指导,适用于浏览器与 WASI 目标。内容涵盖函数导出、内存配置、异步化、优化和调试。
主要功能
- 01选择 emcc 编译选项
- 02将 C 函数导出到 JavaScript
- 03配置线性内存与堆栈
- 04通过 Asyncify 支持异步操作
- 05优化并调试 WASM
- 06区分浏览器与 WASI 目标
适用场景
- 01将 C/C++ 程序编译为 WebAssembly
- 02从 JavaScript 调用导出的 C 函数
- 03为异步环境适配同步 C 代码
- 04排查 WASM 内存或运行问题
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ mohitmishra786/ low-level-dev-skills --skill wasm-emscripten安装后在 Claude Code 中输入/wasm-emscripten确认已加载。
附带文件references/emscripten-linker-flags.md
详细介绍
使用 Emscripten 编译 WebAssembly
用途
指导 Agent 使用 Emscripten 将 C/C++ 编译为 WebAssembly:选择 emcc 标志、导出函数、配置内存模型、使用 Asyncify 处理异步 C 代码、调试 WASM 二进制文件,以及选择 WASI 或浏览器目标。
触发条件
- “如何使用 Emscripten 将 C 编译为 WebAssembly?”
- “如何将 C 函数导出到 JavaScript?”
- “Emscripten 中的 WebAssembly 内存如何工作?”
- “如何调试 .wasm 文件?”
- “如何使用 Asyncify 让同步 C 代码支持异步?”
- “WASI 与 Emscripten 浏览器目标有什么区别?”
工作流
1. 设置和首次构建
# 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
# Verify
emcc --version
# 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)
# Serve locally (WASM requires HTTP, not file://)
python3 -m http.server 8080
# Open: http://localhost:8080/hello.html
2. 将函数导出到 JavaScript
// 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;
}
# 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
# The leading underscore is required for C functions
// 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
3. 内存模型
Emscripten 提供线性内存堆,C 和 JS 均可访问:
# 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
# Stack size (default 64KB)
emcc prog.c -o prog.js -s STACK_SIZE=1MB
# Shared memory (for SharedArrayBuffer / threads)
emcc prog.c -o prog.js -s SHARED_MEMORY=1 -s USE_PTHREADS=1
// 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——在异步环境中运行同步 C 代码
Asyncify 允许同步 C 代码在进行异步操作(如 fetch()、睡眠等)时暂停和恢复:
// 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");
}
# Enable Asyncify
emcc async.c -o async.js \
-s ASYNCIFY \
-s ASYNCIFY_STACK_SIZE=16384 \
-O2 # Asyncify works better with optimization
5. 优化和 wasm-opt
# 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)
# 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
# Compare sizes
ls -lh prog.wasm prog.opt.wasm
6. 调试 WASM
# Build with debug info
emcc prog.c -g -O0 -o prog.html \
-s ASSERTIONS=1 \
-s SAFE_HEAP=1 # catch misaligned accesses
# In Chrome DevTools:
# Sources → prog.wasm → line-by-line C source debugging
# (requires -g and browser with WASM debugging support)
# LLDB with WASM (wasmtime)
# See skills/runtimes/wasm-wasmtime for CLI WASM debugging
# 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
# Print generated JS
emcc prog.c -o prog.js && cat prog.js | head -100
7. WASI 与浏览器目标
| 特性 | 浏览器(Emscripten) | WASI |
|---|---|---|
| 宿主 API | Web API(fetch、WebGL 等) | POSIX 子集(文件、stdin/stdout) |
| 运行时 | 浏览器 JS 引擎 | wasmtime、wasmer、WAMR、Node.js |
| 线程 | SharedArrayBuffer + pthreads | wasi-threads(有限支持) |
| 网络 | fetch()、WebSocket | wasi-http(preview2) |
| 使用场景 | Web 应用 | 服务端、CLI 工具、边缘计算 |
# Build for WASI (no browser JS, pure WASM)
emcc prog.c -o prog.wasm --target=wasi
# 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
有关 Emscripten 链接器标志参考,请参阅 references/emscripten-linker-flags.md。
相关技能
- 服务端 WASM(使用 wasmtime CLI 和 Rust 嵌入)请使用
skills/runtimes/wasm-wasmtime - 基于 Clang 并使用 WASI SDK 编译 WASM,请使用
skills/compilers/clang - 检查 WASM 二进制结构,请使用
skills/binaries/elf-inspection
评论
支持 Markdown