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

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.


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.