Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x86: support mapdir on wasi #98

Merged
merged 1 commit into from
Aug 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,15 @@ jobs:
test:
runs-on: ubuntu-22.04
name: Test
strategy:
fail-fast: false
matrix:
target: ["TestWasmtime", "TestWamr", "TestWasmer", "TestWazero", "TestWasmedge"]
steps:
- uses: actions/checkout@v3
- name: Test
env:
GO_TEST_FLAGS: -run ${{ matrix.target }}
run: |
make test

Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ ARG WASI_SDK_VERSION=19
ARG WASI_SDK_VERSION_FULL=${WASI_SDK_VERSION}.0
ARG WASI_VFS_VERSION=v0.3.0
ARG WIZER_VERSION=04e49c989542f2bf3a112d60fbf88a62cce2d0d0
ARG EMSDK_VERSION=3.1.42
ARG EMSDK_VERSION=3.1.40 # TODO: support recent version
ARG BINARYEN_VERSION=113
ARG BUSYBOX_VERSION=1_36_0
ARG RUNC_VERSION=v1.1.8
Expand Down
1 change: 1 addition & 0 deletions Dockerfile.test
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ RUN tar -C /usr/local -xzf go${GOLANG_VERSION}.linux-amd64.tar.gz
ENV PATH=$PATH:/usr/local/go/bin

# install container2wasm
# NOTE: integration test script also depends on this path.
COPY . /test/
WORKDIR /test/
RUN go build -o /usr/local/bin/ ./cmd/c2w
Expand Down
30 changes: 13 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,13 @@ bin dev home lib32 libx32 mnt proc run srv tmp var
boot etc lib lib64 media opt root sbin sys usr
```

> NOTE: Directory mapping is available on non-x86_64 containers (TinyEMU-emulated ones) as of now. Other WASI features untested. Future version will support more WASI features.
Directories mapped to the WASM program is accessible on the container as well.

```
$ mkdir -p /tmp/share/ && echo hi > /tmp/share/from-host
$ wasmtime --mapdir /mnt/share::/tmp/share out.wasm cat /mnt/share/from-host
hi
```

### Container on Browser

Expand Down Expand Up @@ -184,8 +190,6 @@ $ wasmtime --mapdir /test/dir/share::/tmp/share /app/out.wasm ls /test/dir/share
hi
```

> NOTE: Directory mapping is available on non-x86_64 containers (TinyEMU-emulated) as of now. This should be available on all containers in the future.

## Motivation

Though more and more programming languages start to support WASM, it's not easy to run the existing programs on WASM.
Expand All @@ -201,7 +205,7 @@ The following shows the techniqual details:
- Builder: [BuildKit](https://github.com/moby/buildkit) runs the conversion steps written in Dockerfile.
- Emulator: [Bochs](https://bochs.sourceforge.io/) emulates x86_64 CPU on WASM. [TinyEMU](https://bellard.org/tinyemu/) emulates RISC-V CPU on WASM. They're compiled to WASM using [wasi-sdk](https://github.com/WebAssembly/wasi-sdk) (for WASI and on-browser) and [emscripten](https://github.com/emscripten-core/emscripten) (for on-browser).
- Guest OS: Linux runs on the emulated CPU. [runc](https://github.com/opencontainers/runc) starts the container. Non-x86 and non-RISC-V containers runs with additional emulation by QEMU installed via [`tonistiigi/binfmt`](https://github.com/tonistiigi/binfmt).
- Directory Mapping: WASI filesystem API makes host directories visible to the emulator. TinyEMU mounts them to the guest linux via virtio-9p. Unsupported on Bochs (for x86_64 emulation) as of now.
- Directory Mapping: WASI filesystem API makes host directories visible to the emulator. Emulators mount them to the guest linux via virtio-9p.
- Packaging: [wasi-vfs](https://github.com/kateinoigakukun/wasi-vfs) (for WASI and on-browser) and emscripten (for on-browser) are used for packaging the dependencies. The kernel is pre-booted during the build using [wizer](https://github.com/bytecodealliance/wizer/) to minimize the startup latency (for WASI only as of now).
- Security: The converted container runs in the sandboxed WASM (WASI) VM with the limited access to the host system.

Expand All @@ -216,11 +220,11 @@ The following shows the techniqual details:

|runtime |stdio|mapdir|note|
|---|---|---|---|
|wasmtime|:heavy_check_mark:|:construction:||
|wamr(wasm-micro-runtime)|:heavy_check_mark:|:construction:||
|wazero|:heavy_check_mark:|:construction:||
|wasmer|:construction: (stdin unsupported)|:construction:|non-blocking stdin doesn't seem to work|
|wasmedge|:construction: (stdin unsupported)|:construction:|non-blocking stdin doesn't seem to work|
|wasmtime|:heavy_check_mark:|:heavy_check_mark:||
|wamr(wasm-micro-runtime)|:heavy_check_mark:|:heavy_check_mark:||
|wazero|:heavy_check_mark:|:heavy_check_mark:||
|wasmer|:construction: (stdin unsupported)|:heavy_check_mark:|non-blocking stdin doesn't seem to work|
|wasmedge|:construction: (stdin unsupported)|:heavy_check_mark:|non-blocking stdin doesn't seem to work|

### risc-v and other architecutre's containers

Expand All @@ -232,14 +236,6 @@ The following shows the techniqual details:
|wasmer|:construction: (stdin unsupported)|:heavy_check_mark:|non-blocking stdin doesn't seem to work|
|wasmedge|:construction: (stdin unsupported)|:heavy_check_mark:|non-blocking stdin doesn't seem to work|

Example of mapdir with wasmtime:

```console
$ mkdir -p /tmp/share/ && echo hi > /tmp/share/hi
$ wasmtime --mapdir /test/dir/share::/tmp/share -- out.wasm --entrypoint=cat -- /test/dir/share/hi
hi
```

## Similar projects

There are several container runtimes support running WASM applications, but they don't run containers on WASM.
Expand Down
29 changes: 16 additions & 13 deletions cmd/init/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,22 +104,25 @@ func doInit() error {
}

wg.Wait()
if infoSourceRemoteAddr == "" {

// WASI-related filesystems
for _, tag := range []string{rootFSTag, packFSTag} {
dst := filepath.Join("/mnt", tag)
if err := os.Mkdir(dst, 0777); err != nil {
return err
}
log.Printf("mounting %q to %q\n", tag, dst)
if err := syscall.Mount(tag, dst, "9p", 0, "trans=virtio,version=9p2000.L,msize=8192"); err != nil {
log.Printf("failed mounting %q: %v\n", tag, err)
break
}
}
initMounts := []string{rootFSTag, packFSTag}
if infoSourceRemoteAddr != "" {
initMounts = []string{rootFSTag}
}

// WASI-related filesystems
for _, tag := range initMounts {
dst := filepath.Join("/mnt", tag)
if err := os.Mkdir(dst, 0777); err != nil {
return err
}
log.Printf("mounting %q to %q\n", tag, dst)
if err := syscall.Mount(tag, dst, "9p", 0, "trans=virtio,version=9p2000.L,msize=8192"); err != nil {
log.Printf("failed mounting %q: %v\n", tag, err)
break
}
}

specD, err := os.ReadFile(cfg.Container.RuntimeConfigPath)
if err != nil {
return err
Expand Down
5 changes: 5 additions & 0 deletions patches/bochs/Bochs/bochs/Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ NONINLINE_OBJS = \
plugin.o \
crc.o \
bxthread.o \
wasm.o \
@EXTRA_BX_OBJS@

EXTERN_ENVIRONMENT_OBJS = \
Expand Down Expand Up @@ -819,3 +820,7 @@ plugin.o: plugin.@CPP_SUFFIX@ bochs.h config.h osdep.h gui/paramtree.h logio.h \
plugin.h extplugin.h param_names.h pc_system.h bx_debug/debug.h config.h \
osdep.h memory/memory-bochs.h gui/siminterface.h gui/paramtree.h \
gui/gui.h plugin.h
wasm.o: wasm.@CPP_SUFFIX@ iodev/iodev.h bochs.h config.h osdep.h gui/paramtree.h \
logio.h misc/bswap.h plugin.h extplugin.h param_names.h pc_system.h \
bx_debug/debug.h config.h osdep.h memory/memory-bochs.h \
gui/siminterface.h gui/gui.h iodev/pci.h wasm.h
3 changes: 3 additions & 0 deletions patches/bochs/Bochs/bochs/iodev/devices.cc
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,9 @@ void bx_devices_c::init(BX_MEM_C *newmem)
bulkIOQuantumsRequested = 0;
bulkIOQuantumsTransferred = 0;

#if defined(EMSCRIPTEN) || defined(WASI)
PLUG_load_plugin(mapdirVirtio9p, PLUGTYPE_STANDARD);
#endif
bx_init_plugins();

/* now perform checksum of CMOS memory */
Expand Down
119 changes: 7 additions & 112 deletions patches/bochs/Bochs/bochs/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@
#include "iodev/usb/usb_common.h"
#endif

#if defined(EMSCRIPTEN) || defined(WASI)
#include "wasm.h"
#endif

#ifdef WASI
#include <wizer.h>
extern "C" {
Expand Down Expand Up @@ -315,34 +319,6 @@ void print_statistics_tree(bx_param_c *node, int level)

bool vm_init_done = false;

typedef struct {
char *contents;
int len;
int lim;
} FSVirtFile;

int write_info(FSVirtFile *f, int pos, int len, const char *str)
{
if ((pos + len) > f->lim) {
printf("too many write (%d > %d)", pos + len, f->lim);
return -1;
}
for (int i = 0; i < len; i++) {
f->contents[pos + i] = str[i];
}
return len;
}

int putchar_info(FSVirtFile *f, int pos, char c)
{
if ((pos + 1) > f->lim) {
printf("too many write (%d > %d)", pos + 1, f->lim);
return -1;
}
f->contents[pos] = c;
return 1;
}

int write_entrypoint(FSVirtFile *f, int pos1, const char *entrypoint)
{
int p, pos = pos1;
Expand Down Expand Up @@ -490,6 +466,9 @@ int init_wasi_info(int argc, char **argv, FSVirtFile *info)
}

info->len = pos;
#ifdef WASI
info->len += write_preopen_info(info, pos);
#endif
return 0;
}

Expand Down Expand Up @@ -735,90 +714,6 @@ extern "C" {
extern void __wasi_vfs_rt_init(void);
}

#include <wasi/libc.h>

// Populating preopen in the same way as wasi-libc does as well as updating
// the preopens list managed by wasi-libc.
// https://github.com/WebAssembly/wasi-libc/blob/wasi-sdk-19/libc-bottom-half/sources/preopens.c
// We use our list of preopenes for mounting them to the container.

typedef struct preopen {
/// The path prefix associated with the file descriptor.
const char *prefix;

/// The file descriptor.
__wasi_fd_t fd;
} preopen;

preopen *preopens;
size_t num_preopens = 0;
size_t preopen_capacity = 0;

static int populate_preopens() {
for (__wasi_fd_t fd = 3; fd != 0; ++fd) {
__wasi_prestat_t prestat;
__wasi_errno_t ret = __wasi_fd_prestat_get(fd, &prestat);
if (ret == __WASI_ERRNO_BADF) {
break;
}
if (ret != __WASI_ERRNO_SUCCESS)
return -1;

switch (prestat.tag) {
case __WASI_PREOPENTYPE_DIR: {
char *prefix = (char *) malloc(prestat.u.dir.pr_name_len + 1);
if (prefix == NULL)
return -1;
if (__wasi_fd_prestat_dir_name(fd, (uint8_t *)prefix, prestat.u.dir.pr_name_len) != __WASI_ERRNO_SUCCESS)
return -1;
prefix[prestat.u.dir.pr_name_len] = '\0';

int off = 0;
while (1) {
if (prefix[off] == '/') {
off++;
} else if (prefix[off] == '.' && prefix[off + 1] == '/') {
off += 2;
} else if (prefix[off] == '.' && prefix[off + 1] == 0) {
off++;
} else {
break;
}
}

char *p = &(prefix[off]);
if (__wasilibc_register_preopened_fd(fd, strdup(p)) != 0)
return -1;

if (num_preopens == preopen_capacity) {
size_t new_capacity = preopen_capacity == 0 ? 4 : preopen_capacity * 2;
preopen *new_preopens = (preopen *)calloc(sizeof(preopen), new_capacity);
if (new_preopens == NULL) {
return -1;
}
memcpy(new_preopens, preopens, num_preopens * sizeof(preopen));
free(preopens);
preopens = new_preopens;
preopen_capacity = new_capacity;
}

char *prefix2 = strdup(p);
if (prefix2 == NULL)
return -1;

preopens[num_preopens++] = (preopen) { prefix2, fd, };
free(prefix);

break;
}
default:
break;
}
}

return 0;
}

int wasm_start(int (main)()) {
int result;
void *asyncify_buf;
Expand Down
1 change: 1 addition & 0 deletions patches/bochs/Bochs/bochs/plugin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,7 @@ plugin_t bx_builtin_plugins[] = {
#if BX_SUPPORT_USB_XHCI
BUILTIN_OPTPCI_PLUGIN_ENTRY(usb_xhci),
#endif
BUILTIN_OPTPCI_PLUGIN_ENTRY(mapdirVirtio9p),
#if BX_SUPPORT_SOUNDLOW
BUILTIN_SND_PLUGIN_ENTRY(dummy),
BUILTIN_SND_PLUGIN_ENTRY(file),
Expand Down
3 changes: 3 additions & 0 deletions patches/bochs/Bochs/bochs/plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ extern "C" {
#define BX_PLUGIN_HPET "hpet"
#define BX_PLUGIN_VOODOO "voodoo"

#define BX_PLUGIN_MAPDIR_VIRTIO_9P "mapdirVirtio9p"


#define BX_REGISTER_DEVICE_DEVMODEL(a,b,c,d) pluginRegisterDeviceDevmodel(a,b,c,d)
#define BX_UNREGISTER_DEVICE_DEVMODEL(a,b) pluginUnregisterDeviceDevmodel(a,b)
Expand Down Expand Up @@ -489,6 +491,7 @@ PLUGIN_ENTRY_FOR_IMG_MODULE(vbox);
PLUGIN_ENTRY_FOR_IMG_MODULE(vpc);
PLUGIN_ENTRY_FOR_IMG_MODULE(vvfat);

PLUGIN_ENTRY_FOR_MODULE(mapdirVirtio9p);
#endif

#ifdef __cplusplus
Expand Down
Loading