diff --git a/.cargo/config.toml b/.cargo/config.toml index 225f5e8a..f557e7be 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,2 +1,18 @@ -[target.wasm32-wasi] -#rustflags = ['-Clink-arg=--import-memory'] +# The adapter module created in this repository can technically be either +# compiled for wasm32-wasi or the unknown-unknown target but the unknown version +# is used to be able to specify custom flags here. That way the wasi tests don't +# use these custom flags but the adapter does. +[target.wasm32-unknown-unknown] +rustflags = [ + # The adapter must import its memory from the main module so pass this for LLD + # to generate the right memory import. + '-Clink-arg=--import-memory', + # The adapter will allocate its own stack and doesn't use the --stack-first + # layout that LLD has by default. Set the stack size from LLD to zero here to + # ensure that the memory imported into the module has a minimum size of 0 as + # opposed to 1MB which might not be compatible with some WASI-using modules. + '-Clink-arg=-zstack-size=0', + # Currently all locations that will run this adapter have this feature enabled + # and this avoid generating a `memcpy` function in the adapter itself. + '-Ctarget-feature=+bulk-memory', +] diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 45d873b3..92a3df45 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,15 +29,15 @@ jobs: steps: - uses: actions/checkout@v3 - run: rustup update stable && rustup default stable - - run: rustup target add wasm32-wasi - - run: cargo build --target wasm32-wasi - env: - RUSTFLAGS: -Clink-args=--import-memory -Ctarget-feature=+bulk-memory -Clink-args=-zstack-size=0 - - run: cargo run -p verify -- ./target/wasm32-wasi/debug/wasi_snapshot_preview1.wasm - - run: cargo build --release --target wasm32-wasi - env: - RUSTFLAGS: -Clink-args=--import-memory -Ctarget-feature=+bulk-memory -Clink-args=-zstack-size=0 - - run: cargo run -p verify -- ./target/wasm32-wasi/release/wasi_snapshot_preview1.wasm + - run: rustup target add wasm32-wasi wasm32-unknown-unknown + - run: cargo build --target wasm32-unknown-unknown + - run: cargo run -p verify -- ./target/wasm32-unknown-unknown/debug/wasi_snapshot_preview1.wasm + - run: cargo build --target wasm32-unknown-unknown --features command + - run: cargo run -p verify -- ./target/wasm32-unknown-unknown/debug/wasi_snapshot_preview1.wasm + - run: cargo build --release --target wasm32-unknown-unknown + - run: cargo run -p verify -- ./target/wasm32-unknown-unknown/release/wasi_snapshot_preview1.wasm + - run: cargo build --release --target wasm32-unknown-unknown --features command + - run: cargo run -p verify -- ./target/wasm32-unknown-unknown/release/wasi_snapshot_preview1.wasm - run: cargo test -p host build: @@ -49,14 +49,18 @@ jobs: steps: - uses: actions/checkout@v3 - run: rustup update stable && rustup default stable - - run: rustup target add wasm32-wasi - - run: cargo build --target wasm32-wasi --release - env: - RUSTFLAGS: -Clink-args=--import-memory -Ctarget-feature=+bulk-memory -Clink-args=-zstack-size=0 + - run: rustup target add wasm32-wasi wasm32-unknown-unknown + - run: cargo build --target wasm32-unknown-unknown --release --features command + - run: mv target/wasm32-unknown-unknown/release/wasi_snapshot_preview1.wasm wasi_snapshot_preview1.command.wasm + - uses: actions/upload-artifact@v3 + with: + name: wasi_snapshot_preview1.command.wasm + path: wasi_snapshot_preview1.command.wasm + - run: cargo build --target wasm32-unknown-unknown --release - uses: actions/upload-artifact@v3 with: name: wasi_snapshot_preview1.wasm - path: target/wasm32-wasi/release/wasi_snapshot_preview1.wasm + path: target/wasm32-unknown-unknown/release/wasi_snapshot_preview1.wasm - uses: marvinpinto/action-automatic-releases@latest if: github.event_name == 'push' && github.ref == 'refs/heads/main' @@ -65,4 +69,6 @@ jobs: automatic_release_tag: latest prerelease: true title: "Latest Build" - files: target/wasm32-wasi/release/wasi_snapshot_preview1.wasm + files: | + target/wasm32-unknown-unknown/release/wasi_snapshot_preview1.wasm + wasi_snapshot_preview1.command.wasm diff --git a/Cargo.toml b/Cargo.toml index c6d86ba8..8961f615 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,12 +22,18 @@ crate-type = ["cdylib"] # that could theoretically change in the future. panic = 'abort' +[profile.release.package.wasi_snapshot_preview1] +opt-level = 's' +strip = 'debuginfo' + # Make dev look like a release build since this adapter module won't work with # a debug build that uses data segments and such. [profile.dev.package.wasi_snapshot_preview1] incremental = false -opt-level = 3 -debug = 0 +opt-level = 's' # Omit integer overflow checks, which include failure messages which require # string initializers. overflow-checks = false + +[features] +command = [] diff --git a/README.md b/README.md new file mode 100644 index 00000000..83dc9212 --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +# `wasi_snapshot_preview1.wasm` + +> **Note**: This repository is a work in progress. This is intended to be an +> internal tool which not everyone has to look at but many might rely on. You +> may need to reach out via issues or +> [Zulip](https://bytecodealliance.zulipchat.com/) to learn more about this +> repository. + +This repository currently contains an implementation of a WebAssembly module: +`wasi_snapshot_preview1.wasm`. This module bridges the `wasi_snapshot_preview1` +ABI to the preview2 ABI of the component model. At this time the preview2 APIs +themselves are not done being specified so a local copy of `wit/*.wit` is used +instead. + +## Building + +This adapter can be built with: + +```sh +$ cargo build --target wasm32-unknown-unknown --release +``` + +And the artifact will be located at +`target/wasm32-unknown-unknown/release/wasi_snapshot_preview1.wasm`. +Alternatively the latest copy of this can be [downloaded from the releases +page][latest-release] + +[latest-release]: https://github.com/bytecodealliance/preview2-prototyping/releases/tag/latest + +## Using + +With a `wasi_snapshot_preview1.wasm` file on-hand you can create a component +from a module that imports WASI functions using the [`wasm-tools` +CLI](https://github.com/bytecodealliance/wasm-tools) + +```sh +$ cat foo.rs +fn main() { + println!("Hello, world!"); +} +$ rustc foo.rs --target wasm32-wasi +$ wasm-tools print foo.wasm | grep '(import' + (import "wasi_snapshot_preview1" "fd_write" (func ... + (import "wasi_snapshot_preview1" "environ_get" (func ... + (import "wasi_snapshot_preview1" "environ_sizes_get" ... + (import "wasi_snapshot_preview1" "proc_exit" (func ... +$ wasm-tools component new foo.wasm --adapt wasi_snapshot_preview1.wasm -o component.wasm + +# Inspect the generated `component.wasm` +$ wasm-tools validate component.wasm --features component-model +$ wasm-tools component wit component.wasm +``` + +Here the `component.wasm` that's generated is a ready-to-run component which +imports wasi preview2 functions and is compatible with the wasi-preview1-using +module internally. diff --git a/src/lib.rs b/src/lib.rs index 98f29937..0a3048b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,13 +22,19 @@ mod bindings { }); } -#[export_name = "command"] -unsafe extern "C" fn command_entrypoint( +#[no_mangle] +pub unsafe extern "C" fn command( stdin: u32, stdout: u32, args_ptr: *const WasmStr, args_len: usize, ) { + // TODO: ideally turning off `command` would remove this import and the + // `*.wit` metadata entirely but doing that ergonomically will likely + // require some form of `use` to avoid duplicating lots of `*.wit` bits. + if !cfg!(feature = "command") { + unreachable(); + } State::with_mut(|state| { state.push_desc(Descriptor::File(File { fd: stdin, @@ -1220,7 +1226,7 @@ struct State { args: Option<&'static [WasmStr]>, } -struct WasmStr { +pub struct WasmStr { ptr: *const u8, len: usize, } diff --git a/test-programs/macros/build.rs b/test-programs/macros/build.rs index 86ebb60c..adc55ce9 100644 --- a/test-programs/macros/build.rs +++ b/test-programs/macros/build.rs @@ -14,17 +14,14 @@ fn main() { cmd.arg("build") .arg("--release") .current_dir("../../") - .arg("--target=wasm32-wasi") + .arg("--target=wasm32-unknown-unknown") + .arg("--features=command") .env("CARGO_TARGET_DIR", &out_dir) - .env( - "RUSTFLAGS", - "-Clink-args=--import-memory -Clink-args=-zstack-size=0", - ) .env_remove("CARGO_ENCODED_RUSTFLAGS"); let status = cmd.status().unwrap(); assert!(status.success()); - let wasi_adapter = out_dir.join("wasm32-wasi/release/wasi_snapshot_preview1.wasm"); + let wasi_adapter = out_dir.join("wasm32-unknown-unknown/release/wasi_snapshot_preview1.wasm"); println!("wasi adapter: {:?}", &wasi_adapter); let wasi_adapter = fs::read(&wasi_adapter).unwrap();