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

wasm2c: uvwasi support #2002

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ endif ()
option(BUILD_TESTS "Build GTest-based tests" ON)
option(USE_SYSTEM_GTEST "Use system GTest, instead of building" OFF)
option(BUILD_TOOLS "Build wabt commandline tools" ON)
option(BUILD_UVWASI "Build uvwasi for use by compiled applications" ON)
option(BUILD_FUZZ_TOOLS "Build tools that can repro fuzz bugs" OFF)
option(BUILD_LIBWASM "Build libwasm" ON)
option(USE_ASAN "Use address sanitizer" OFF)
Expand Down Expand Up @@ -353,6 +354,7 @@ set(WABT_LIBRARY_SRC
add_library(wabt STATIC ${WABT_LIBRARY_SRC})
add_library(wabt::wabt ALIAS wabt)


target_compile_features(wabt PUBLIC cxx_std_17)

if (WABT_INSTALL_RULES)
Expand Down Expand Up @@ -395,6 +397,10 @@ IF (NOT WIN32)
endif ()
endif ()

if (BUILD_UVWASI)
add_subdirectory("third_party/uvwasi" )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have a WITH_WASI flag .. is there some reason we can't use use that?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seemed to me like enabling the mostly not working wasi support in the interpreter, and allowing people to build their standalone apps with uvwasi were logically separate things. I'm not attached around the issue of wether this is enabled by default, so whatever makes sense just lmk.

endif ()

if (BUILD_FUZZ_TOOLS)
set(FUZZ_FLAGS "-fsanitize=fuzzer,address")
add_library(wabt-fuzz STATIC ${WABT_LIBRARY_SRC})
Expand Down
27 changes: 27 additions & 0 deletions wasm2c/examples/build-standlone/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
WABT_ROOT=../../..
WASM2C=$(WABT_ROOT)/bin/wasm2c

WASM2C_RUNTIME_PATH=$(WABT_ROOT)/wasm2c/
WASM2C_RUNTIME_FILES=$(addprefix $(WASM2C_RUNTIME_PATH), wasm-rt-impl.c uvwasi-rt.c)

UVWASI_PATH=$(WABT_ROOT)/third_party/uvwasi

DEPS=-I$(UVWASI_PATH)/include -L$(WABT_ROOT)/build/_deps/libuv-build -L$(WABT_ROOT)/build/third_party/uvwasi

LIBS=-luvwasi_a -luv_a -lpthread -ldl -lm

DBG_FLAGS=-g
REL_FLAGS=-O3 -flto -fomit-frame-pointer -fno-stack-protector

CFLAGS=$(REL_FLAGS)

all: input.elf

clean:
rm -rf $(ALL_RESULTS) input.*

input.c:
$(WASM2C) input.wasm -o input.c

input.elf: input.c uvwasi-rt-main.c $(WASM2C_RUNTIME_FILES)
$(CC) $(CFLAGS) input.c uvwasi-rt-main.c -o input.elf -I$(WASM2C_RUNTIME_PATH) $(WASM2C_RUNTIME_FILES) $(DEPS) $(LIBS)
24 changes: 24 additions & 0 deletions wasm2c/examples/build-standlone/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/bash
if [ -z "$1" ]
then
echo "Compiles wasm file into standalone binary"
echo "usage: build.sh foo.wasm"
echo "outputs: foo.elf"
exit
fi

ORIGIN_DIR=$(pwd)
SCRIPT_DIR=$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
output_file=$(basename -- "$1")
output_file="${output_file%%.*}.elf"

set -v

cd $SCRIPT_DIR
make clean

cp ${ORIGIN_DIR}/$1 ./input.wasm
make input.c

make input.elf
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like this build-standalone idea. The mixing of a build.sh script and the Makefile file a find a little confusing, but I think we can iterate on it over time.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For sure, it was just a quick way to get something working, if we want to promote to a first class feature I can just replace it with a stand-alone script.

cp -f input.elf ${ORIGIN_DIR}/${output_file}
Binary file not shown.
76 changes: 76 additions & 0 deletions wasm2c/examples/build-standlone/uvwasi-rt-main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/* Link this with wasm2c output and uvwasi runtime to build a standalone app */
#include <stdio.h>
#include <stdlib.h>
#include "uvwasi.h"
#include "uvwasi-rt.h"

#define MODULE_NAME input
#define MODULE_HEADER "input.h"
#include MODULE_HEADER

//force pre-processor expansion of m_name
#define __module_init(m_name) Z_## m_name ##_init_module()
#define module_init(m_name) __module_init(m_name)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also agree with @keithw that it would be nice to avoid these macros and assume that wasm2c was run with the correct -m flag.

Copy link
Author

@talg talg Oct 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For sure, they have been removed in 9703be9.


#define __module_instantiate(m_name, instance_p, wasi_p) Z_## m_name ##_instantiate(instance_p, wasi_p)
#define module_instantiate(m_name ,instance_p, wasi_p) __module_instantiate(m_name ,instance_p, wasi_p)

#define __module_free(m_name, instance_p) Z_## m_name ##_free(instance_p)
#define module_free(m_name, instance_p) __module_free(m_name, instance_p)

#define __module_start(m_name, instance_p) Z_ ## m_name ## Z__start(instance_p)
#define module_start(m_name, instance_p) __module_start(m_name, instance_p)

int main(int argc, const char** argv)
{
Z_input_instance_t local_instance;
uvwasi_t local_uvwasi_state;

struct Z_wasi_snapshot_preview1_instance_t wasi_state = {
.uvwasi = &local_uvwasi_state,
.instance_memory = &local_instance.w2c_memory
};

uvwasi_options_t init_options;

//pass in standard descriptors
init_options.in = 0;
init_options.out = 1;
init_options.err = 2;
init_options.fd_table_size = 3;

//pass in args and environement
extern const char ** environ;
init_options.argc = argc;
init_options.argv = argv;
init_options.envp = (const char **) environ;

//no sandboxing enforced, binary has access to everything user does
init_options.preopenc = 2;
init_options.preopens = calloc(2, sizeof(uvwasi_preopen_t));

init_options.preopens[0].mapped_path = "/";
init_options.preopens[0].real_path = "/";
init_options.preopens[1].mapped_path = "./";
init_options.preopens[1].real_path = ".";

init_options.allocator = NULL;

wasm_rt_init();
uvwasi_errno_t ret = uvwasi_init(&local_uvwasi_state, &init_options);

if (ret != UVWASI_ESUCCESS) {
printf("uvwasi_init failed with error %d\n", ret);
exit(1);
}

module_init(MODULE_NAME);
module_instantiate(MODULE_NAME, &local_instance, (struct Z_wasi_snapshot_preview1_instance_t *) &wasi_state);
module_start(MODULE_NAME, &local_instance);
module_free(MODULE_NAME, &local_instance);

uvwasi_destroy(&local_uvwasi_state);
wasm_rt_free();

return 0;
}
37 changes: 37 additions & 0 deletions wasm2c/examples/hello-wasi/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#assumes wasi-sdk installed system wide in default location
WASI_SDK_ROOT=/opt/wasi-sdk
WABT_ROOT=../../..

WASI_CLANG=$(WASI_SDK_ROOT)/bin/clang
WASI_SYSROOT=$(WASI_SDK_ROOT)/share/wasi-sysroot

#CFLAGS for compiling files to place nice with wasm2c
WASM_CFLAGS=-Wl,--export-all -Wl,--no-entry -Wl,--growable-table -Wl,--stack-first -Wl,-z,stack-size=1048576


WASM2C=$(WABT_ROOT)/bin/wasm2c

WASM2C_RUNTIME_PATH=$(WABT_ROOT)/wasm2c/
WASM2C_RUNTIME_FILES=$(addprefix $(WASM2C_RUNTIME_PATH), wasm-rt-impl.c uvwasi-rt.c)

UVWASI_PATH=$(WABT_ROOT)/third_party/uvwasi

DEPS=-I$(UVWASI_PATH)/include -L$(WABT_ROOT)/build/_deps/libuv-build -L$(WABT_ROOT)/build/third_party/uvwasi

LIBS=-luvwasi_a -luv_a -lpthread -ldl -lm

ALL_RESULTS=hello.wasm hello.wasm.c hello.elf

all: $(ALL_RESULTS)

clean:
rm -rf $(ALL_RESULTS) hello.wasm.h *.o

hello.wasm: hello.c
$(WASI_CLANG) --sysroot $(WASI_SYSROOT) $(WASM_CFLAGS) hello.c -o hello.wasm

hello.wasm.c: hello.wasm
$(WASM2C) hello.wasm -o hello.wasm.c

hello.elf: hello.wasm.c uvwasi-rt-main.c $(WASM2C_RUNTIME_FILES)
$(CC) -g hello.wasm.c uvwasi-rt-main.c -o hello.elf -I$(WASM2C_RUNTIME_PATH) $(WASM2C_RUNTIME_FILES) $(DEPS) $(LIBS)
2 changes: 2 additions & 0 deletions wasm2c/examples/hello-wasi/TODO
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-address any XXX or TODO lines
-make sure linear memory accesses are bounded checked
34 changes: 34 additions & 0 deletions wasm2c/examples/hello-wasi/hello.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char * argv[]) {

printf("printing args\n");
for (int i = 0; i < argc ; i++) {
printf("[%d] %s \n", i, argv[i]);
}

printf("Writing to stdout\n");
char * hello_str = strdup("hello world!");
puts(hello_str);

printf("writing test.out\n");
FILE * fp = fopen("test.out", "w+");
assert(fp != NULL);
fprintf(fp,"hello filesystem\n");
fclose(fp);
printf("removing test.out\n");
assert(!unlink("test.out"));

printf("writing /tmp/test.out\n");
fp = fopen("/tmp/test.out", "w+");
assert(fp != NULL);
fprintf(fp,"hello filesystem\n");
fclose(fp);
printf("removing /tmp/test.out\n");
assert(!unlink("/tmp/test.out"));

return 0;
}
76 changes: 76 additions & 0 deletions wasm2c/examples/hello-wasi/uvwasi-rt-main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/* Link this with wasm2c output and uvwasi runtime to build a standalone app */
#include <stdio.h>
#include <stdlib.h>
#include "uvwasi.h"
#include "uvwasi-rt.h"

#define MODULE_NAME hello
#define MODULE_HEADER "hello.wasm.h"
#include MODULE_HEADER

//force pre-processor expansion of m_name
#define __module_init(m_name) Z_## m_name ##_init_module()
#define module_init(m_name) __module_init(m_name)

#define __module_instantiate(m_name, instance_p, wasi_p) Z_## m_name ##_instantiate(instance_p, wasi_p)
#define module_instantiate(m_name ,instance_p, wasi_p) __module_instantiate(m_name ,instance_p, wasi_p)

#define __module_free(m_name, instance_p) Z_## m_name ##_free(instance_p)
#define module_free(m_name, instance_p) __module_free(m_name, instance_p)

#define __module_start(m_name, instance_p) Z_ ## m_name ## Z__start(instance_p)
#define module_start(m_name, instance_p) __module_start(m_name, instance_p)

int main(int argc, const char** argv)
{
Z_hello_instance_t local_instance;
uvwasi_t local_uvwasi_state;

struct Z_wasi_snapshot_preview1_instance_t wasi_state = {
.uvwasi = &local_uvwasi_state,
.instance_memory = &local_instance.w2c_memory
};

uvwasi_options_t init_options;

//pass in standard descriptors
init_options.in = 0;
init_options.out = 1;
init_options.err = 2;
init_options.fd_table_size = 3;

//pass in args and environement
extern const char ** environ;
init_options.argc = argc;
init_options.argv = argv;
init_options.envp = (const char **) environ;

//no sandboxing enforced, binary has access to everything user does
init_options.preopenc = 2;
init_options.preopens = calloc(2, sizeof(uvwasi_preopen_t));

init_options.preopens[0].mapped_path = "/";
init_options.preopens[0].real_path = "/";
init_options.preopens[1].mapped_path = "./";
init_options.preopens[1].real_path = ".";

init_options.allocator = NULL;

wasm_rt_init();
uvwasi_errno_t ret = uvwasi_init(&local_uvwasi_state, &init_options);

if (ret != UVWASI_ESUCCESS) {
printf("uvwasi_init failed with error %d\n", ret);
exit(1);
}

module_init(MODULE_NAME);
module_instantiate(MODULE_NAME, &local_instance, (struct Z_wasi_snapshot_preview1_instance_t *) &wasi_state);
module_start(MODULE_NAME, &local_instance);
module_free(MODULE_NAME, &local_instance);

uvwasi_destroy(&local_uvwasi_state);
wasm_rt_free();

return 0;
}
Loading