Skip to content

A "hello world" for **unofficially** building a WASM/WASI module with ZZ

Notifications You must be signed in to change notification settings

zx-project/wasm-lib-hello-world

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

wasm-lib-hello-world

A hello world for unofficially building a WASM/WASI module with ZZ

Prerequisites

zz.toml

Before you can compile a ZZ project library in WASM format, you must set the following compiler and linker flags in your zz.toml project file:

[project]
version = "0.1.0"
name = "whw" # wasm hello world
cincludes = []
cobjects = []
pkgconfig = []

lflags = [
  "-Wl,--export-dynamic", ## export all dynamic symbols, like functions
  "-Wl,--export-all", ## export all symbols so we get access to __heap_base, etc
  "-Wl,--no-entry", ## don't look for a _start function
  "-nostartfiles", ## no startup files
]

cflags = [
  "--target=wasm32-unknown-wasi", ## WASM/WASI clang target
  "--sysroot=/opt/wasi-libc", ## path to installed wasi-libc directory
  "-nostdlib", ## disable stdlib
]

src/lib.zz

Below demonstrates a simple random access API for a tail bound RandomAccess struct, which is allocated on the heap by the caller. This is usual ZZ code.

export struct RandomAccess+ {
  u8 size;
  u8 mem[];
}

export fn read(RandomAccess+L *self, u8 mut *out, usize offset, usize size) -> usize
  where offset + size < L
  where len(out) >= size
  where offset < len(out)
{
  usize mut bytes_read = 0;
  for (usize mut i = 0; i < size; ++i) {
    static_attest(len(self->mem) > offset + i);
    out[i] = self->mem[offset + i];
    bytes_read++;
  }
  return bytes_read;
}

export fn write(RandomAccess+L mut *self, u8 *in, usize offset, usize size) -> usize
  where offset + size < L
  where len(in) >= size
{
  usize mut bytes_written = 0;
  for (usize mut i = 0; i < size; ++i) {
    static_attest(len(self->mem) > offset + i);
    self->mem[offset + i] = in[i];
    bytes_written++;
  }
  return bytes_written;
}

Compiling

To compile the project, you must set the CC environment variable to clang exported by the WASI SDK (CC=/opt/wasi-sdk/bin/clang) and build the project in release (--release) or debug (--debug) mode.

$ CC=/opt/wasi-sdk/bin/clang zz build --release

The release target library ./target/release/lib/libwhw.so will be built in WASM format, NOT as a shared object file. At the time of writing there is no way to control the output extension as .so is hard coded when building a library.

example.js

Below is an example of the compiled WASM module used in Node.js. The example implements both read() and write() methods with tail bound instances of the RandomAccess struct.

To run the example, run the following:

$ node example.js
hello
const assert = require('assert')
const fs = require('fs')

const buffer = new Uint8Array(fs.readFileSync('./target/release/lib/libwhw.so'))
const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 })
const imports = {
  env: { memory }
}

WebAssembly.instantiate(buffer, imports).then(onwasm, onerror)

function onerror(err) {
  console.error(err)
}

function onwasm(wasm) {
  const { instance } = wasm
  const heap = Buffer.from(memory.buffer)
  const ram = instance.exports.__heap_base

  const sizeofRandomAccess = instance.exports.sizeof_whw_RandomAccess()
  const string = "hello"
  const offset = 8
  const L = 32 // tail size

  // write "hello" to the heap
  assert(string.length === Buffer.from(string).copy(heap, ram + sizeofRandomAccess + L))

  // call write on `RandomAccess+L` passing in heap pointer to "hello" to write
  // at offset 8 with buffer size 5
  assert(string.length === instance.exports.whw_write(
    ram, L, // RandomAccess+L
    ram + sizeofRandomAccess + L, // input
    offset, // offset
    string.length // size
  ))

  assert(string.length === instance.exports.whw_read(
    ram, L, // RandomAccess+L
    ram + sizeofRandomAccess + L, // output
    offset, // offset
    string.length // size
  ))

  const output = Buffer.from(heap.slice(
    ram + sizeofRandomAccess + L,
    ram + sizeofRandomAccess + L + string.length
  ))

  assert("hello" == output.toString())

  console.log(output.toString())
}

License

Public Domain

About

A "hello world" for **unofficially** building a WASM/WASI module with ZZ

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published