我可以以某种方式构建 webassembly 代码*没有* emscripten“胶水”吗?

Posted

技术标签:

【中文标题】我可以以某种方式构建 webassembly 代码*没有* emscripten“胶水”吗?【英文标题】:Can I somehow build webassembly code *without* the emscripten "glue"? 【发布时间】:2017-12-30 21:50:37 【问题描述】:

我能否以某种方式创建一个 wasm 文件,该文件将按照 in MDN here 的描述自行工作(通过实例化对象并在其上调用函数)?

我能找到的所有指南 (such as this one on MDN) 都推荐使用 emscripten;然而,这也将包括约 70kB 的“胶水代码”(带有约 50kB 的可选文件系统仿真),具有额外的逻辑(如检测节点/浏览器环境和自动获取等),可能还有其他一些仿真。

如果我不想要那个“粘合代码”而只想直接创建 WASM(可能来自 C 代码,但也可能是其他东西)怎么办?现在可以吗?

【问题讨论】:

可能与答案相关的问题 - ***.com/questions/45146099/… 请注意,我在 2017 年问过这个问题,而且进展很快。而且我没有时间在 2019 年(或以后)重新检查答案 【参考方案1】:

您可以使用 emscripten 生成相当少的代码输出。

考虑以下琐碎的文件adder.c

int adder (int a, int b) 
    return a + b;

像这样编译它(需要相当新的 emscripten):

emcc -O2 -s WASM=1 -s SIDE_MODULE=1 -o adder.wasm

要查看它生成了什么,请使用 binaryen 的 wasm-dis 将其反汇编为废文本形式(您也可以使用 wabt 中的 wasm2wast):

wasm-dis adder.wasm -o adder.wast

反汇编后的源代码应如下所示:

(module
 (type $0 (func (param i32 i32) (result i32)))
 (type $1 (func))
 (import "env" "memoryBase" (global $import$0 i32))
 (import "env" "memory" (memory $0 256))
 (import "env" "table" (table 0 anyfunc))
 (import "env" "tableBase" (global $import$3 i32))
 (global $global$0 (mut i32) (i32.const 0))
 (global $global$1 (mut i32) (i32.const 0))
 (export "__post_instantiate" (func $2))
 (export "runPostSets" (func $1))
 (export "_adder" (func $0))
 (func $0 (type $0) (param $var$0 i32) (param $var$1 i32) (result i32)
  (i32.add
   (get_local $var$1)
   (get_local $var$0)
  )
 )
 (func $1 (type $1)
  (nop)
 )
 (func $2 (type $1)
  (block $label$0
   (set_global $global$0
    (get_global $import$0)
   )
   (set_global $global$1
    (i32.add
     (get_global $global$0)
     (i32.const 5242880)
    )
   )
   (call $1)
  )
 )
 ;; custom section "dylink", size 5
)

然后您可以像这样在节点(v8.X 或更高版本)中运行它:

const WA = WebAssembly,
      env = memoryBase: 0,
             tableBase: 0,
             memory: new WA.Memory(initial: 256),
             table: new WA.Table(initial: 0, element: 'anyfunc'),
      code = new Uint8Array(require('fs').readFileSync('adder.wasm'))
WA.compile(code).then(m => 
    return new WA.Instance(m, env: env)
).then(i => 
    console.log(i.exports._adder(7, 8))
)

请注意,如果您想支持使用堆栈和/或堆内存的代码,事情会变得更加复杂。 IE。在调用任何其他导出之前,您至少需要设置 memoryBase 并从主机环境调用 __post_instantiate

如果你想在没有 javascript 环境的情况下解释 WebAssembly 代码,你可以使用 wac/wace 运行它(完全披露:我创建了这个项目)。请注意,wace 假定您定义了“_main”或“main”函数。

【讨论】:

为 wac 加 1!我一直在尝试使用 wabt 解释器构建类似的东西,但发现它不符合我的目的。看起来你为我节省了很多工作:) 我接受你的版本,但是我不喜欢这个问题。希望他们能尽快修复它! github.com/kripken/emscripten/issues/5419【参考方案2】:

你可以使用 ONLY_MY_CODE 标志,这将只生成没有glue.js的wasm模块,例如

emcc -O1 ./src/foo.cpp -o release/foo.wasm -s WASM=1 -s ONLY_MY_CODE=1

来自 Settings.js https://github.com/kripken/emscripten/blob/master/src/settings.js#L583:

var ONLY_MY_CODE = 0; // This disables linking and other causes of adding extra code
                      // automatically, and as a result, your output compiled code
                      // (in the .asm.js file, if you emit with --separate-asm) will
                      //  contain only the functions you provide.

【讨论】:

不错。这和-s SIDE_MODULE=1 有区别吗? github.com/kripken/emscripten/wiki/Linking 据我所知,两者都消除了用于将模块链接到系统库等的胶水代码,但only_my_code=1 更进一步确保只有您的代码被翻译成 wasm跨度> 似乎 ONLY_MY_CODE 标志补充了 SIDE_MODULE 标志,例如: emcc -O1 ./Test.cpp -o ./Test.js -s WASM=1 -s SIDE_MODULE=1 -s ONLY_MY_CODE=1 (检查生成的 Test.wast ) 这并不妨碍生成JS文件。 此设置已不存在github.com/emscripten-core/emscripten/blob/master/src/…【参考方案3】:

LLVM 现在支持使用 WASI 将 C 直接编译为 wasm。不再需要 Emscripten。

如果不需要 libc,您可以直接使用 LLVM。例如,foo.c 文件可以编译为:

clang --target=wasm32 --no-standard-libraries -Wl,--export-all -Wl,--no-entry -o foo.wasm foo.c

否则,WASI-libc 项目有一个独立的 libc 可以使用。

使用 LLVM 将 C 编译为 WebAssembly 并在浏览器中运行的完整过程是 available in this post。

【讨论】:

不错。我没有时间重新检查答案,因为我不再使用 JS/WASM/emscripten。 ://【参考方案4】:

你可以,而且随着时间的推移变得越来越容易!

如果您想完全避免使用 C++,可以创建 WebAssembly 模块,例如在 spec tests 或 WebKit test suite 中所做的那样。

即使使用 C++ 也可以,无需 Emscripten。 wasm-stat.us 这样做是为了例如GCC 酷刑测试。查看它的构建输出,或look at its source。

例如,它会为编译/链接/汇编执行以下操作:

# Get a .o file:
src/work/wasm-install/bin/clang src/work/gcc/gcc/testsuite/gcc.c-torture/execute/20020227-1.c -o src/work/torture-o/20020227-1.c.o --std=gnu89 -DSTACK_SIZE=1044480 -w -Wno-implicit-function-declaration --target=wasm32-unknown-unknown-wasm -c -O2 --sysroot=src/work/wasm-install/sysroot
# Link with libc:
src/work/wasm-install/bin/lld -flavor wasm -entry=main --allow-undefined-file=src/work/wasm-install/sysroot/lib/wasm.syms -o src/work/torture-lld-musl/20020510-1.c.o.wasm src/work/torture-o/20020510-1.c.o src/work/wasm-install/sysroot/lib/libc.a
# Or without a libc (you need to provide one somehow):
src/work/wasm-install/bin/lld -flavor wasm -entry=main --allow-undefined-file=src/work/wasm-install/sysroot/lib/wasm.syms -o src/work/torture-lld/20020510-1.c.o.wasm src/work/torture-o/20020510-1.c.o

# Or, if you want an assembly file instead:
src/work/wasm-install/bin/clang src/work/gcc/gcc/testsuite/gcc.c-torture/execute/20020227-1.c -o src/work/torture-s/20020227-1.c.s --std=gnu89 -DSTACK_SIZE=1044480 -w -Wno-implicit-function-declaration --target=wasm32-unknown-unknown -S -O2 --sysroot=src/work/wasm-install/sysroot
# And get the binary file:
src/work/wasm-install/bin/wast2wasm src/work/torture-s2wasm/loop-6.c.s.wast -o src/work/torture-wast2wasm/loop-6.c.s.wast.wasm

您甚至可以下载瀑布的所有构建工件,包括完整的工具链。只需单击绿色框并找到您要查找的下载。

如果您有胆量,甚至可以write your libc in JavaScript,而不是链接用 C 编写的现有实现。

当您说“独立”时,请记住,WebAssembly 目前无法在不链接到其嵌入器(即 JavaScript)的情况下做任何事情。 Emscripten 遵循的模型(我希望其他人也这样做)是 JavaScript 是微内核并提供系统调用。

【讨论】:

感谢您的回答!也很有帮助,虽然我接受了另一个(但至少赞成你的)【参考方案5】:

2020 年更新

使用 Emscripten,您可以使用 -o my_file.wasm

这样您将只有 WASM 文件,如果您使用标志 STANDALONE_WASM,它将创建 .wasm 和一个可选的 .js 粘合代码(如果需要)。

【讨论】:

如果我有更多时间来实际验证它,我会接受你。由于问题很老,我不再需要这个:(

以上是关于我可以以某种方式构建 webassembly 代码*没有* emscripten“胶水”吗?的主要内容,如果未能解决你的问题,请参考以下文章

Chromium 宣布:再见 PNaCI,你好 WebAssembly!

使用 Django Rest Framework 构建的后端 Json 在前端以某种方式未定义,使用 React 构建

抢先一步,Rust构建版支持直接编译WebAssembly

我可以在 Swift 中通过 NSURLSession 以某种方式执行同步 HTTP 请求吗

通过 WebAssembly 在 JavaScript 中调用 C# 方法

PNaCl 再见,WebAssembly 你好!