---
title: "WebGL应用框架深入-WebAssembly编译与运行时框架"
author: "Perrin Yong"
author_profile: https://www.pystone.net/profile/
published_by: "Perrin Yong"
canonical: https://www.pystone.net/notes/webgl-wasm-compile-runtime-framework/
type: note
content_role: unspecified
visibility: public
id_stability: rename-stable
source_path: "10-计算机、信息技术与工程/05-游戏图形与运行时/WebGL/WebGL应用框架深入-WebAssembly编译与运行时框架.md"
content_hash: 6e353aa599f9242738f60c951371b17ad4e1061a72018a9d07753151382fb39b
knowledge_version: 224c990773de.5fa8af6e39fa
site_commit: 224c990773de166d23a886306577dd90379529ce
notes_commit: 5fa8af6e39fa3891d1b9b4832bfa6c4e0ecaaf0a
---
# WebGL应用框架深入-WebAssembly编译与运行时框架

```text
微信不支持wasm层面启用多线程。 能否通过详细介绍C++经过Emscripten编译成WebAssembly的机制、微信等宿主平台运行JavaSCript和WebAssembly的运行时框架、多线程的底层原理、微信不支持wasm的技术底层原因，帮我详细解释这个问题
```

## 1. Emscripten 将 C++ 编译为 WebAssembly 的流程

* **前端：Clang/LLVM 转译**
  Emscripten 首先调用 LLVM 的 Clang 前端，将 C++ 源码编译成 LLVM IR（中间表示）。
* **中间处理：Binaryen 优化与转码**
  LLVM IR 经由 Binaryen 工具链进一步优化、转换为 WebAssembly 文本格式（WAT），并最终生成二进制 `.wasm` 模块。
* **生成“胶水”代码（Glue Code）**
  同时，Emscripten 生成一份 JavaScript “胶水”文件（通常为 `module.js`），用于在宿主环境中加载、编译并实例化 `.wasm` 模块，实现 C++ 标准库函数、内存分配、文件系统等运行时支持 ([emscripten.org][1])。
* **运行时配置与参数**

  * 通过命令行参数（例如 `-s USE_PTHREADS=1`、`-s ALLOW_MEMORY_GROWTH=1`）启用或禁用特定功能。
  * Emscripten 提供对 POSIX、SDL、OpenGL（转换为 WebGL）等 API 的兼容支持，便于移植复杂应用 ([emscripten.org][2])。

---

## 2. 宿主平台的 JavaScript 与 WebAssembly 运行时框架

* **浏览器中的标准 WebAssembly 运行时**

  * **WebAssembly 对象**：浏览器内置 `WebAssembly` 命名空间，提供 `instantiate`、`instantiateStreaming`、`Memory`、`Table` 等接口。
  * **JavaScript 引擎集成**：V8、SpiderMonkey 等引擎原生解析 `.wasm`，管理线性内存（`ArrayBuffer`／`SharedArrayBuffer`）并执行字节码指令。
* **微信小程序中的运行时差异**

  * **WXWebAssembly 替代 WebAssembly**
    自微信 8.0 起，原生 `WebAssembly` 对象被替换为 `WXWebAssembly`（或 `WXAssembly`），仅支持最基础的加载与实例化，不包含 SIMD、线程等扩展功能 ([github.com][3])。
  * **无标准 Worker 接口**
    小程序环境中通常不暴露标准的 `Web Worker` API，无法启动独立线程来并行执行 WebAssembly 模块 ([github.com][4])。
  * **安全与资源限制**
    小程序对脚本执行上下文、安全策略（如 CSP）以及单包体积（2 MB 限制）有严格限制，进一步影响运行时能力。

---

## 3. WebAssembly 多线程的底层原理

1. **共享线性内存**

   * WebAssembly 引入可配置为 `shared: true` 的 `Memory` 对象，其底层是 JavaScript 的 `SharedArrayBuffer`。
   * 所有线程（以 Web Worker 形式存在）共享同一块线性内存，无需数据拷贝，即可通过原子操作同步访问 ([web.dev][5])。
2. **线程创建与同步**

   * **Web Worker**：宿主通过 `new Worker()` 创建线程；Emscripten 将 `pthread_create` 映射为生成 Worker 并传递对等上下文。
   * **原子操作与屏障**：利用 `Atomics` 接口完成互斥锁、条件变量等同步原语；必要时调用 `memory.atomic.fence` 保证内存屏障 ([stackoverflow.com][6])。
3. **Emscripten 中的 USE\_PTHREADS 支持**

   * 开启 `-s USE_PTHREADS=1` 后，编译产物中包含对 `pthread` API 的封装：

     1. 启动 Worker
     2. 将主内存转为可共享的 `SharedArrayBuffer`
     3. 自动注入消息通信与同步代码
   * 依赖浏览器对 SharedArrayBuffer 和跨源隔离（COOP/COEP）的完整支持。

---

## 4. 技术底层原因：为何微信小程序不支持 wasm 多线程

1. **不支持 SharedArrayBuffer**

   * 出于安全（Spectre/Meltdown 防护）和资源限制考虑，小程序环境往往禁用 `SharedArrayBuffer`，导致无法构建共享内存模型。
2. **无 Web Worker 接口**

   * 由于不提供标准 `Worker` API，无法从 JavaScript 层面创建独立执行上下文，无法并行运行多个 WebAssembly 实例。
3. **WXWebAssembly 功能裁剪**

   * `WXWebAssembly` 旨在满足基础计算与逻辑移植，舍弃了多线程、SIMD 等高级特性，以保持运行时轻量与安全 ([github.com][3])。
4. **跨源隔离（Cross-Origin Isolation）欠缺**

   * WebAssembly 线程要求启用 COOP/COEP 头部以允许 `SharedArrayBuffer`，但小程序无法控制 HTTP 响应头，无法满足此安全策略。
5. **打包体积与性能考量**

   * 多线程运行时需增加数百 KB 的运行时库（Atomic、同步机制等），与小程序“单包 ≤ 2 MB”的限制相冲突。

---

**结论**：
微信小程序采用了自定义的 `WXWebAssembly` 运行时，并在安全、资源与包体积等多方面做了裁剪和限制，因此暂不支持 WebAssembly 多线程。若需在小程序中并行计算，可考虑基于单线程分时调度或服务器端多线程配合的混合方案。

[1]: https://emscripten.org/?utm_source=chatgpt.com "Main — Emscripten 4.0.11-git (dev) documentation"
[2]: https://emscripten.org/docs/compiling/WebAssembly.html?utm_source=chatgpt.com "Building to WebAssembly — Emscripten 4.0.11-git (dev) documentation"
[3]: https://github.com/tensorflow/tfjs/issues/5021?utm_source=chatgpt.com "WeChat 8.0 has moved WebAssembly to WXWebAssembly so wasm for ... - GitHub"
[4]: https://github.com/emscripten-core/emscripten/issues/16153?utm_source=chatgpt.com "feature help: target to WXAssembly · Issue #16153 - GitHub"
[5]: https://web.dev/articles/webassembly-threads?utm_source=chatgpt.com "Using WebAssembly threads from C, C++ and Rust - web.dev"
[6]: https://stackoverflow.com/questions/59550963/multithreading-in-webassembly?utm_source=chatgpt.com "emscripten - Multithreading in WebAssembly - Stack Overflow"


```text
能否通过具体的代码来进行介绍。我希望理解：Emscripten将C++编译成WebAssembly，在V8运行时上跑的框架是怎样的，宿主（微信）、运行时和WebAssembly、JavaScript之间的架构关系、互动流程是怎样的，Emscripten如何在编译过程中，对C++语言屏蔽硬件和驱动层接口，使得C++无法直接操作系统的网络、磁盘等驱动的？ Emscripten和宿主如何使小程序运行在沙箱中，屏蔽网络、磁盘等接口，使这些接口只能经过宿主的API提供的？
```

## 1. Emscripten 将 C++ 编译为 WebAssembly 的完整流程

### 1.1 C++ 源码示例

```cpp
// hello.cpp
#include <iostream>
int main() {
    std::cout << "Hello from Emscripten!" << std::endl;
    return 0;
}
```

### 1.2 编译命令

```bash
em++ hello.cpp \
    -O2 \
    -s WASM=1 \
    -s MODULARIZE=1 \
    -s EXPORT_NAME="createModule" \
    -o hello.js
```

* `-O2`：开启优化
* `-s WASM=1`：输出 `.wasm` 而非 asm.js
* `-s MODULARIZE=1` `-s EXPORT_NAME`：生成可通过 ES6 `import` 的模块化 Glue Code ([developer.mozilla.org][1])

编译后会生成：

* **`hello.wasm`**：二进制模块
* **`hello.js`**：加载、编译、实例化 Wasm 的 JavaScript “胶水”代码
* （可选）`hello.html`：一份示例 HTML，用于直接在浏览器中打开并运行

---

## 2. V8（浏览器/小程序）运行时加载与执行 WebAssembly 的框架示例

Emscripten 生成的 `hello.js` 内部典型逻辑（摘录）：

```js
// hello.js（节选）
var Module = {
  onRuntimeInitialized: function() {
    // Wasm 模块实例化后回调
    Module._main();  // 调用 C++ 的 main()
  }
};

(function() {
  // 将 Wasm 二进制用 Streaming API 加载并实例化
  WebAssembly.instantiateStreaming(fetch('hello.wasm'), Module)
    .then(function(result) {
      Module.asm = result.instance.exports;
    })
    .catch(function(err) {
      console.error('WASM instantiate failed:', err);
    });
})();
```

* `WebAssembly.instantiateStreaming`：在下载时即时编译，节省启动时间
* `Module.onRuntimeInitialized`：Emscripten 运行时就绪后执行入口函数 ([developer.mozilla.org][1])

在微信小程序环境中，需要用其专属接口 `WXWebAssembly` 替代标准对象：

```js
// 小程序端加载示例
import createModule from './hello.js';
const module = await createModule({
  locateFile: path => path === 'hello.wasm' ? 'hello.wasm' : path
});
module.onRuntimeInitialized = () => {
  module._main();
};
// 内部会调用 WXWebAssembly.instantiate 而非 WebAssembly.instantiate
```

* 微信在基础库中用 `WXWebAssembly` 代替全局 `WebAssembly`，不支持高级扩展如线程、SIMD ([github.com][2])

---

## 3. Emscripten 对 C++ 系统调用与硬件驱动的屏蔽机制

### 3.1 虚拟系统调用与文件系统

* **系统调用映射**
  所有对 `open`、`read`、`write` 等 POSIX 接口的调用，均被编译时替换为对 `library_syscall.js` 中 JS 函数的调用，**不再调用真实操作系统内核** ([stackoverflow.com][3])。

```js
// library_syscall.js（简化示意）
function __syscall_open(pathPtr, flags, mode) {
  var path = UTF8ToString(pathPtr);
  return FS.open(path, flags, mode).fd;  // 委托给 Emscripten 的虚拟 FS
}
```

* **虚拟文件系统（MEMFS）**
  默认使用内存文件系统 `MEMFS`，它模拟了 POSIX API，但所有数据都驻留在浏览器内存中，**C++ 无法直接访问宿主磁盘** ([emscripten.org][4])。

### 3.2 网络接口屏蔽

* 默认环境下，C++ 标准库不包含 BSD Socket 支持，尝试编译 `<sys/socket.h>` 会因缺失实现而报错。
* 如需网络通信，必须显式使用 Emscripten 提供的 Web API 封装（如 `emscripten_fetch`、`EM_ASM` 调用 `fetch`），否则无法链至 OS 网络驱动。

---

## 4. 微信小程序沙箱化：网络与文件接口的宿主侧隔离

### 4.1 网络请求仅能通过宿主 API

微信小程序禁止直接使用 `XMLHttpRequest` 或浏览器原生 `fetch`，所有网络通信必须调用：

```js
wx.request({
  url: 'https://api.example.com/data',
  method: 'GET',
  success(res) { console.log(res.data); }
});
```

* 只能请求在“小程序管理后台→服务器域名”中登记的 HTTPS 接口
* 无法跨域，必须走 `wx.request` 并受次数及域名白名单限制 ([juejin.cn][5])

### 4.2 文件存取通过 FileSystemManager

```js
const fs = wx.getFileSystemManager();
// 保存临时文件
wx.downloadFile({
  url: 'https://example.com/file.bin',
  success(res) {
    fs.saveFile({
      tempFilePath: res.tempFilePath,
      filePath: `${wx.env.USER_DATA_PATH}/file.bin`,
      success() { console.log('保存成功'); }
    });
  }
});
// 读取本地文件
fs.readFile({
  filePath: `${wx.env.USER_DATA_PATH}/file.bin`,
  encoding: 'binary',
  success({ data }) { console.log('文件长度：', data.length); }
});
```

* 小程序提供独立沙箱的用户文件区 `wx.env.USER_DATA_PATH`，与宿主系统彻底隔离
* C++ 在 Wasm 中若调用文件 I/O，会被映射到上述 JS 接口，**无法越过沙箱直接读写磁盘** ([blog.csdn.net][6])

### 4.3 定制 Emscripten 虚拟 FS 驱动（可选）

若需将虚拟 FS 持久化至小程序文件系统，可在 `--post-js` 中注册自定义设备：

```js
// post.js
Module['onRuntimeInitialized'] = () => {
  FS.registerDevice(FS.makedev(89, 0), {
    read: (stream, buffer, offset, length, position) => {
      // 通过 wx.getFileSystemManager().readFileSync 读取真实数据
      // 并填充到 buffer
    },
    write: (stream, buffer, offset, length, position) => {
      // 通过 wx.getFileSystemManager().writeFileSync 持久化
    }
  });
  FS.mkdev('/persist', FS.makedev(89, 0));
};
```

这样，C++ 通过 `open("/persist/data")` 调用时，底层会落到小程序的文件存取 API 上，实现沙箱内持久化。

---

**总结**：

* **编译层面**：Emscripten 利用 LLVM/Clang → Binaryen 生成 `.wasm` + JS Glue，并通过 `library_syscall.js` 实现对所有系统调用的 JS 代理，彻底屏蔽原生驱动。
* **运行时层面**：V8（或微信小程序的 WXWebAssembly）负责字节码解析与执行，Glue Code 注入生命周期回调，组织虚拟内存与调用导出函数。
* **宿主层面**：微信小程序以沙箱模式运行 JS/Wasm，只开放 `wx.request`、`wx.getFileSystemManager` 等 API，保证了网络、磁盘等能力只能通过宿主侧接口使用。

以上示例与原理剖析可帮助您完整掌握从 C++ 代码到微信小程序中 WebAssembly 的编译、加载、执行及沙箱化流程。

[1]: https://developer.mozilla.org/en-US/docs/WebAssembly/Guides/C_to_Wasm?utm_source=chatgpt.com "Compiling a new C/C++ module to WebAssembly - MDN"
[2]: https://github.com/emscripten-core/emscripten/issues/16153?utm_source=chatgpt.com "feature help: target to WXAssembly · Issue #16153 - GitHub"
[3]: https://stackoverflow.com/questions/51256189/standalone-modules-with-emscripten?utm_source=chatgpt.com "javascript - Standalone modules with emscripten - Stack Overflow"
[4]: https://emscripten.org/docs/porting/files/file_systems_overview.html?utm_source=chatgpt.com "File System Overview — Emscripten 4.0.11-git (dev) documentation"
[5]: https://juejin.cn/post/7324559212279824420?utm_source=chatgpt.com "初识小程序-网路请求小程序中网络请求限制，配置request合法域名，发起GET请求，发起POST请求，开发环境忽略合法 - 掘金"
[6]: https://blog.csdn.net/MICHAEL_PRINCE/article/details/123533476?utm_source=chatgpt.com "关于微信小程序使用wx.downloadFile和wx.getFileSystemManager ().saveFile ()保存文件在本机找 ..."


## Emscripten技术发展历史

```text
我想更深入理解WebGL应用，从浏览器发展到小程序的技术历史，以及架构方案。现在想了解的问题是，Emscripten是什么团队创建的，在什么历史情境下，基于什么样的需求创建的，推动该团队创建Emscripten的资源支持来自于哪里
```

以下回答分为三部分，分别介绍 Emscripten 的创建团队与主要开发者、历史情境与初始需求，以及项目所获的主要资源支持来源。

---

## 1. 创建团队与主要开发者

* **主要创建者**：Emscripten 由 Mozilla 研究团队工程师 Alon Zakai（GitHub 名称 “kripken”）于 2010 年发起并创建，并于 2011 年在 OOPSLA 大会上正式对外发布。 ([cs.tufts.edu][1], [hacks.mozilla.org][2])
* **组织背景**：项目最初作为 Alon Zakai 的个人尝试，旨在探索将 LLVM IR 编译为 JavaScript 以便在浏览器中运行非 JavaScript 代码。随后，Mozilla 基金会将其纳入 **Games Program** 和研究团队，并提供人力及基础设施支持。 ([spectrum.ieee.org][3], [hacks.mozilla.org][2])

---

## 2. 历史情境与初始需求

* **WebAssembly 之前的技术局限**
  在 2010 年代初期，浏览器原生并不支持 WebAssembly，JavaScript 是唯一的前端可运行语言，但其在高性能计算与现有 C/C++ 生态方面存在显著差距。
* **核心需求**

  * **跨语言移植**：无需重写，即可将成熟的 C/C++ 应用（如游戏引擎、图形库、科学计算代码）迁移到浏览器环境。
  * **性能优化**：利用 LLVM 的中间优化能力和 asm.js 策略，实现接近原生的执行效率。
  * **生态兼容**：保持对 POSIX、OpenGL（映射为 WebGL）等常用 API 的支持，简化移植成本。 ([cs.tufts.edu][1], [zhuanlan.zhihu.com][4])

---

## 3. 资源支持来源

* **Mozilla 基金会**

  * 提供项目所需的研发人员工时与基础设施，包括代码仓库托管、持续集成、测试平台等。
  * 将 Emscripten 纳入 **Mozilla Games Program**，推动浏览器游戏生态建设。 ([spectrum.ieee.org][3], [hacks.mozilla.org][2])
* **开源社区与行业贡献**

  * 项目托管于 GitHub（kripken/emscripten），吸引来自学术和工业界的贡献者不断完善编译器与运行时支持。
  * 多款行业主流引擎（如 Unity、Defold、Godot）均集成 Emscripten 导出功能，说明其成熟度与生态价值。 ([github.com][5], [en.wikipedia.org][6])
* **学术与工业会议**

  * OOPSLA、LLVM 开发者大会、Mozilla Hacks 等技术会议为 Emscripten 提供交流、展示与反馈渠道，进一步促进项目改进与资源投入。 ([llvm.org][7], [qconnewyork.com][8])

---

以上即 Emscripten 的创始背景、初始需求与资源支持来源概览。如需更深入的技术细节或架构演进，请告知。

[1]: https://www.cs.tufts.edu/comp/150FP/archive/alon-zakai/emscripten.pdf?utm_source=chatgpt.com "Emscripten: An LLVM-to-JavaScript Compiler - Tufts University"
[2]: https://hacks.mozilla.org/2018/01/shrinking-webassembly-and-javascript-code-sizes-in-emscripten/?utm_source=chatgpt.com "Shrinking WebAssembly and JavaScript code sizes in Emscripten - Mozilla ..."
[3]: https://spectrum.ieee.org/webassembly-will-finally-let-you-run-highperformance-applications-in-your-browser?utm_source=chatgpt.com "WebAssembly Will Finally Let You Run High-Performance Applications in ..."
[4]: https://zhuanlan.zhihu.com/p/382519687?utm_source=chatgpt.com "十年磨一剑，WebAssembly是如何诞生的？ - 知乎专栏"
[5]: https://github.com/kripken/emscripten_?utm_source=chatgpt.com "kripken/emscripten_: Emscripten: An LLVM-to-WebAssembly Compiler - GitHub"
[6]: https://en.wikipedia.org/wiki/Emscripten?utm_source=chatgpt.com "Emscripten - Wikipedia"
[7]: https://llvm.org/devmtg/2013-11/slides/Zakai-Emscripten.pdf?utm_source=chatgpt.com "EMSCRIPTEN - COMPILING LLVM BITCODE TO JAVASCRIPT (?!)"
[8]: https://qconnewyork.com/ny2013/user/275.html?utm_source=chatgpt.com "Alon Zakai | QCon New York 2013"


## Emscripten对C++崩溃处理与堆栈信息丢失问题
```text
Emscripten在设计层面，当C++层出现崩溃时，就会将abort抛给JavaScript层对吗？ 从具体的机制上是如何做到的？ 请通过实际的代码进行说明。 从这个角度看，C++崩溃时，JavaScript无法获取C++的崩溃堆栈踪迹，是因为Emscripten在这个问题上的设计本身有缺陷造成的对吗？
```

## 1. C++ 层 `abort()` 如何映射到 JavaScript

当 C++ 代码中执行 `abort()`（或因断言失败、调用 `__builtin_trap()` 等触发致命中断）时，Emscripten 在编译阶段会将该调用重定向为对 WebAssembly 模块中一个导入函数（通常名为 `abort` 或 `_abort`）的调用。

* **导入函数定义**
  在生成的 Glue Code 中，会有类似下面的映射（以 `asmLibraryArg` 为例）：

  ```js
  var asmLibraryArg = {
    // C++ 调用 abort() 会编译为对 env.abort 的调用
    abort: function(what) {
      /* JS 侧实现，见下文 */
    },
    _abort: function() { return asmLibraryArg.abort(); }
    // 其它导入…
  };
  var asm = Module.asm({
    'env': asmLibraryArg,
    // …
  });
  ```

  这一点可见于社区针对“Duplicate functions abort()”问题的讨论，其中明确指出 `abort` 和 `_abort` 都被导入到 Wasm 模块作用域： ([github.com][1])

---

## 2. 在 JavaScript 侧 `abort` 的具体实现及抛出机制

Emscripten 的运行时（`preamble.js` + 各类 JS library）为 `abort` 提供了以下核心实现：

```js
// 伪代码示意，基于 Emscripten 4.x runtime
function abort(what) {
  // 构造错误信息
  what = what ? 'abort(' + what + ')' : 'abort';
  // 打印到 stderr
  Module.printErr(what);
  // 标记已中止，避免后续再运行
  ABORT = true;
  // 抛出 WebAssembly.RuntimeError，使其能穿过所有 wasm 调用帧
  throw new WebAssembly.RuntimeError(what);
}

// C++ 层调用 _abort，会再次调用 abort()
function _abort() {
  abort();
}
```

* 当执行 `abort()` 时，底层直接调用上述 JS `abort`，并最终通过 `throw new WebAssembly.RuntimeError(...)` 在 JavaScript 上抛出一个运行时错误。
* 这使得整个 WebAssembly 实例停止执行，并将控制权交回到最外层的 JS 调用堆栈中。

这一设计由 Emscripten 核心团队在“如何终止执行”问题中确认：“当前实现中，`abort()` 会调用 JS，收集（有限的）堆栈信息，然后抛出一个异常，异常会穿过所有 wasm 帧一直飘到调用者。” ([github.com][2])

---

## 3. 为何 JavaScript 层无法获取 C++ 崩溃时的原生堆栈

1. **异常抛出点在 JS 实现层**
   由于真正的 `throw` 发生在 JS 侧（`new WebAssembly.RuntimeError`），所以捕获到的堆栈信息仅限于 JS 调用栈，不包含 Wasm 内部的 C++ 调用帧。

2. **WebAssembly 无内建跨语言堆栈导出**
   WebAssembly 目前并不提供一种标准机制，让 JS 可以通过异常对象获得完整的 Wasm 调用链。尽管社区已在为 Wasm 异常处理（Wasm EH）推动堆栈跟踪支持，但它目前仅在特定调试模式（`-sASSERTIONS` 或 `-sEXCEPTION_STACK_TRACES`）下为 Wasm EH 生效，而不覆盖 `abort()` 这类“陷阱”（trap）场景 ([github.com][3], [emscripten.org][4])。

3. **设计取舍**

   * 在早期 Web 平台尚未支持 Wasm EH 前，Emscripten 选择用最直接的方式模拟“进程终止”行为：将中止转为 JS 异常并停止执行，而非引入更重的运行时栈展开与解析开销。
   * 本质上，这并非“设计缺陷”，而是在当时技术和性能约束下的一种折中。随着 Wasm EH 与更丰富的调试信息支持成熟，未来可通过开启调试选项来获取更完整的堆栈信息。

---

**总结**：

* **确认**：C++ 层的 `abort()` 最终会被映射为 JS `abort`，并在 JS 侧以 `WebAssembly.RuntimeError` 的形式抛出。
* **机制**：通过预先在 Glue Code 中导入 `abort` 函数，并在运行时实现其打印错误并抛出异常，来完成跨语言的中止传递。
* **堆栈不可见**：由于真正的异常抛出发生在 JS 侧，且当前 Wasm 平台对于“陷阱”并无统一的跨语言堆栈导出机制，导致 JS 捕获时无法获得 C++ 调用链。这是早期设计的权衡结果，而非单一“缺陷”。

若需更丰富的崩溃堆栈，可关注并尝试启用 WebAssembly 异常处理（Wasm EH）及相关调试选项（如 `-sASSERTIONS`、`-sEXCEPTION_STACK_TRACES`），或利用后处理脚本在 JS 层包装 `abort` 来手动捕获及记录调用信息。

[1]: https://github.com/emscripten-core/emscripten/issues/8021?utm_source=chatgpt.com "Duplicate functions abort() and llvm_trap() · Issue #8021 · emscripten ..."
[2]: https://github.com/emscripten-core/emscripten/issues/9715?utm_source=chatgpt.com "How to abort · Issue #9715 · emscripten-core/emscripten - GitHub"
[3]: https://github.com/emscripten-core/emscripten/issues/17466?utm_source=chatgpt.com "[EH] Stack trace support for Wasm EH #17466 - GitHub"
[4]: https://emscripten.org/docs/porting/exceptions.html?utm_source=chatgpt.com "C++ Exceptions Support — Emscripten 4.0.11-git (dev) documentation"
