Can I somehow build webassembly code *without* the emscripten “glue”?

≡放荡痞女 提交于 2020-01-20 14:18:05

问题


Can I somehow create a wasm file, that will work on its own as described in MDN here (by instatiating the objects and calling functions on them)?

All the guides I can find (such as this one on MDN) recommend using emscripten; that will, however, also include ~70kB "glue code" (with ~50 kB optional filesystem emulation), that has additional logic (like detection node/browser environment and automatic fetching etc), and probably some other emulation.

What if I don't want that "glue code" and want to just create WASM directly (probably from C code, but maybe something else)? Is that possible right now?


回答1:


You can use emscripten to generate fairly minimal code output.

Consider the following trivial file adder.c:

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

Compile it like this (requires a fairly recent emscripten):

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

To see what it generated, disassemble it to wast textual form using binaryen's wasm-dis (you can also use wasm2wast from wabt):

wasm-dis adder.wasm -o adder.wast

The disassembled source should look something like this:

(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
)

You can then run this in node (v8.X or later) like this:

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))
})

Note that if you want to support code that uses the stack and/or heap memory things get more complicated. I.e. you'll at least need to set memoryBase and call __post_instantiate from your host environment before you call any other exports.

If you want to interpret WebAssembly code without a JavaScript environment you can run it using wac/wace (full disclosure: I created this project). Note that wace assumes you have a "_main" or "main" function defined.




回答2:


You can, and it's becoming easier over time!

If you want to avoid C++ entirely it's possible to create WebAssembly modules, for example as is done in the spec tests or the WebKit test suite.

Even with C++ you can, without Emscripten. wasm-stat.us does it for e.g. the GCC torture tests. Check out its build output, or look at its source.

For example, it'll do the following for compile / link / assemble:

# 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

You can even download all of the waterfall's build artifacts, including full toolchains. Just click on a green box and find the download you're looking for.

If you're feeling daring you could even write your libc in JavaScript instead of linking an existing implementation written in C.

When you say "on its own", remember than WebAssembly currently can't do anything without linking to its embedder (i.e. JavaScript). The model Emscripten follows (and I expect others to do the same) is that JavaScript is microkernel and provides syscalls.




回答3:


You can use the ONLY_MY_CODE Flag, this will generate only the wasm module with no glue.js e.g.

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

From 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.



回答4:


LLVM now supports direct compilation of C to wasm using WASI. Emscripten is no longer necessary.

If no libc is required, you can use LLVM right out of the box. For example, the file foo.c can be compiled with:

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

Otherwise, the WASI-libc project has a standalone libc that can be used.

A complete procedure for compiling C to WebAssembly with LLVM and running it in a browser is available in this post.



来源:https://stackoverflow.com/questions/45295339/can-i-somehow-build-webassembly-code-without-the-emscripten-glue

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!