emscripten 1.39.16-fastcomp开启EMTERPRETIFY_ASYNC后longjmp崩溃求解决方案
问题原因
该问题是emscripten 1.39.x fastcomp版本emterpreter-async模式的已知实现缺陷:
emterpreter-async模式下调用emscripten_sleep等异步API时,会在执行栈上保存当前的emterpreter执行状态标记,而默认配置下longjmp执行时不会清理这些异步栈标记。跳转完成后第二次调用emscripten_sleep时,emterpreter会检测到栈上存在异常残留的异步上下文,触发断言失败直接崩溃。
可行规避方案
- 方案1(最简便):添加
-s EMTERPRETIFY_ALLOW_LONGJMP=1编译参数
该参数是emterpreter专门为适配longjmp跨异步调用栈跳转设计的开关,开启后编译器会自动插入longjmp执行时的异步上下文清理逻辑。修改后的编译命令为:
emcc -O0 -s EMTERPRETIFY=1 -s EMTERPRETIFY_ASYNC=1 -s EMTERPRETIFY_ALLOW_LONGJMP=1 -s WASM=0 c.cpp -o c.js
该方案可以直接解决你给出的最小复现用例的崩溃问题,运行后会正常输出0到10的计数结果。
- 方案2:调整emterpreter执行白名单
如果开启上述参数后仍存在崩溃,需要确保所有涉及setjmp/longjmp调用、调用异步API的函数全部由emterpreter执行,避免栈上残留非emterpreter代码的上下文。
首先通过nm命令查看编译生成的目标文件的符号,获取对应函数的mangled名称,再通过编译参数指定白名单,示例如下:
emcc -O0 -s EMTERPRETIFY=1 -s EMTERPRETIFY_ASYNC=1 -s EMTERPRETIFY_ALLOW_LONGJMP=1 -s EMTERPRETIFY_WHITELIST='["_main", "_Z3runv", "_Z5checkv"]' -s WASM=0 c.cpp -o c.js
- 方案3:代码层面调整longjmp的触发时机
如果编译参数调整无法满足需求,可以将longjmp的调用调度到当前异步栈完全退出后执行,避免和当前异步上下文冲突。将用例中longjmp(buffer, 1);修改为:
emscripten_async_call([](void*){ longjmp(buffer, 1); }, nullptr, 0);
该修改会让longjmp在下一个事件循环周期执行,此时之前emscripten_sleep产生的异步栈已经完全清理,不会触发上下文校验失败。
内容的提问来源于stack exchange,提问作者teehessar
相关产品推荐
相关产品推荐

