Emscripten Wasm 独立编译时不能使用大部分 C++
Emscripten Wasm Can't use most of C++ when compiling standalone
我正在尝试将一个使用标准库的 C++ 小示例编译成 wasm,以便与基本的 javascript 入口点(未生成胶水代码)一起使用。不幸的是,在加载模块时,我收到以下错误:
TypeError: WebAssembly.instantiate(): Import #0 module="wasi_snapshot_preview1" 错误:模块不是对象或函数
我之前曾尝试使用 wasisdk 和独立的 llvm 进行构建,但遇到了类似的问题。似乎没有关于解决这个相当神秘的错误的信息。
首先,在我深入探讨之前,是否有可能将使用标准库中的数据结构的 C++ 代码构建到独立的 wasm 中?鉴于 wasm 仍处于早期阶段,我不确定我是否应该能够完成这项工作,但我可能做错了什么。根据我的经验,几乎每个内置数据结构和字符串都会导致问题,即使我重载 new 和 delete 以排除一些内存分配问题。
详细信息,我的系统是MacOS 10.14,我是运行 Chrome 80。我使用的是github最新版本的emsdk编译。
对于代码块的大量涌入,我深表歉意,但我不确定是否有更好的示例。我已尽可能减少示例。
这是我的 bash 构建脚本:
em++ source.cpp \
--std=c++17 \
-flto \
-fno-exceptions \
-Os \
-o output.wasm \
-s "EXPORTED_FUNCTIONS=['_animate']" \
-s ERROR_ON_UNDEFINED_SYMBOLS=0 \
C++:我一使用标准无序映射等数据结构就收到错误。
#ifdef __cplusplus
#define extern_c_begin() extern "C" {
#define extern_c_end() }
#else
#define extern_c_begin()
#define extern_c_end()
#endif
#include <unordered_map>
std::unordered_map<int, int> map;
int use_map() {
// if I use the map at all, I get the error
map.insert({1, 2});
return (int)map.size();
}
extern_c_begin()
// call into js
void hello_js(void);
int animate(long arg) {
int size = use_map();
hello_js();
return size;
}
extern_c_end()
最后,我的 javascript/index.html:
<!DOCTYPE html><html><head></head><body>
<script type="module">
"use strict";
(async () => {
try {
const wasmInfo = {
instance : null,
memoryHeap : null,
env : {}
};
wasmInfo.env["hello_js"] = () => {
console.log("in js, call from wasm");
};
// error on load
const wasmLoadResult = await WebAssembly.instantiateStreaming(
fetch("./output.wasm"),
{env : wasmInfo.env}
);
wasmInfo.instance = wasmLoadResult.instance;
function animate(t) {
requestAnimationFrame(animate);
try {
console.log(wasmInfo.instance.exports.animate(t));
} catch (e) {
console.error(e);
}
}
requestAnimationFrame(animate);
} catch (err) {
console.error(err);
}
})();
</script></body></html>
几乎所有数据结构似乎都会发生这种情况。我什至尝试了一个名为 robin_hood 的第三方库来替换地图,但它有同样的问题。
有解决办法吗?
您需要实现您的程序所需的所有系统调用。查看 wasm-objdump
的输出以查看程序所需的所有导入的列表。
Firstly, before I go deeper, is it even possible to build C++ code that uses data structures from the standard library into standalone wasm?
是的。证明:
// app.cpp
#include <vector>
using std::vector;
typedef long int i32;
extern "C" {
i32 myFunction(i32 myNumber) {
vector<i32> myVector{ 666, 1337 };
return myVector[0] + myVector[1] + myNumber;
}
}
emcc -Os -s INITIAL_MEMORY=64kb -s MAXIMUM_MEMORY=64kb -s ALLOW_MEMORY_GROWTH=0 -s TOTAL_STACK=0kb -s STANDALONE_WASM -s EXPORTED_FUNCTIONS="['_myFunction']" -Wl,--no-entry "app.cpp" -o "app.wasm"
// javascript
(async () => {
const response = await fetch('app.wasm');
const file = await response.arrayBuffer();
const imports = { wasi_snapshot_preview1: { proc_exit: () => { } } } // dummy placeholder function, a sacrifice to the emscripten god who demands it
const wasm = await WebAssembly.instantiate(file, imports);
const { myFunction } = wasm.instance.exports;
const myNumber = myFunction(123);
console.log(myNumber); // 2126
})();
我正在尝试将一个使用标准库的 C++ 小示例编译成 wasm,以便与基本的 javascript 入口点(未生成胶水代码)一起使用。不幸的是,在加载模块时,我收到以下错误:
TypeError: WebAssembly.instantiate(): Import #0 module="wasi_snapshot_preview1" 错误:模块不是对象或函数
我之前曾尝试使用 wasisdk 和独立的 llvm 进行构建,但遇到了类似的问题。似乎没有关于解决这个相当神秘的错误的信息。
首先,在我深入探讨之前,是否有可能将使用标准库中的数据结构的 C++ 代码构建到独立的 wasm 中?鉴于 wasm 仍处于早期阶段,我不确定我是否应该能够完成这项工作,但我可能做错了什么。根据我的经验,几乎每个内置数据结构和字符串都会导致问题,即使我重载 new 和 delete 以排除一些内存分配问题。
详细信息,我的系统是MacOS 10.14,我是运行 Chrome 80。我使用的是github最新版本的emsdk编译。
对于代码块的大量涌入,我深表歉意,但我不确定是否有更好的示例。我已尽可能减少示例。
这是我的 bash 构建脚本:
em++ source.cpp \
--std=c++17 \
-flto \
-fno-exceptions \
-Os \
-o output.wasm \
-s "EXPORTED_FUNCTIONS=['_animate']" \
-s ERROR_ON_UNDEFINED_SYMBOLS=0 \
C++:我一使用标准无序映射等数据结构就收到错误。
#ifdef __cplusplus
#define extern_c_begin() extern "C" {
#define extern_c_end() }
#else
#define extern_c_begin()
#define extern_c_end()
#endif
#include <unordered_map>
std::unordered_map<int, int> map;
int use_map() {
// if I use the map at all, I get the error
map.insert({1, 2});
return (int)map.size();
}
extern_c_begin()
// call into js
void hello_js(void);
int animate(long arg) {
int size = use_map();
hello_js();
return size;
}
extern_c_end()
最后,我的 javascript/index.html:
<!DOCTYPE html><html><head></head><body>
<script type="module">
"use strict";
(async () => {
try {
const wasmInfo = {
instance : null,
memoryHeap : null,
env : {}
};
wasmInfo.env["hello_js"] = () => {
console.log("in js, call from wasm");
};
// error on load
const wasmLoadResult = await WebAssembly.instantiateStreaming(
fetch("./output.wasm"),
{env : wasmInfo.env}
);
wasmInfo.instance = wasmLoadResult.instance;
function animate(t) {
requestAnimationFrame(animate);
try {
console.log(wasmInfo.instance.exports.animate(t));
} catch (e) {
console.error(e);
}
}
requestAnimationFrame(animate);
} catch (err) {
console.error(err);
}
})();
</script></body></html>
几乎所有数据结构似乎都会发生这种情况。我什至尝试了一个名为 robin_hood 的第三方库来替换地图,但它有同样的问题。
有解决办法吗?
您需要实现您的程序所需的所有系统调用。查看 wasm-objdump
的输出以查看程序所需的所有导入的列表。
Firstly, before I go deeper, is it even possible to build C++ code that uses data structures from the standard library into standalone wasm?
是的。证明:
// app.cpp
#include <vector>
using std::vector;
typedef long int i32;
extern "C" {
i32 myFunction(i32 myNumber) {
vector<i32> myVector{ 666, 1337 };
return myVector[0] + myVector[1] + myNumber;
}
}
emcc -Os -s INITIAL_MEMORY=64kb -s MAXIMUM_MEMORY=64kb -s ALLOW_MEMORY_GROWTH=0 -s TOTAL_STACK=0kb -s STANDALONE_WASM -s EXPORTED_FUNCTIONS="['_myFunction']" -Wl,--no-entry "app.cpp" -o "app.wasm"
// javascript
(async () => {
const response = await fetch('app.wasm');
const file = await response.arrayBuffer();
const imports = { wasi_snapshot_preview1: { proc_exit: () => { } } } // dummy placeholder function, a sacrifice to the emscripten god who demands it
const wasm = await WebAssembly.instantiate(file, imports);
const { myFunction } = wasm.instance.exports;
const myNumber = myFunction(123);
console.log(myNumber); // 2126
})();