From 01ea870ab48a9ab85927453f3ab4862fc2b9842d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 1 Dec 2022 12:46:36 -0800 Subject: [PATCH 1/2] Update a few aspects of the adapter build * Use the `wasm32-unknown-unknown` target for the adapter and specify flags in `.cargo/config.toml` to avoid having to pass the same flags everywhere. This allows using `wasm32-wasi` for tests to ensure the flags only apply to the adapter. * Use `opt-level=s` since speed is not of the utmost concern for this wasm but since it's likely to be included in many places size is likely more important. * Use `strip = 'debuginfo'` for the release build to remove the standard library's debugging information which isn't necessary. * Remove `debug = 0` from the `dev` profile to have debugging information for development. * Add a small `README.md` describing what's here for now. --- .cargo/config.toml | 20 +++++++++++-- .github/workflows/main.yml | 24 ++++++--------- Cargo.toml | 7 +++-- README.md | 56 +++++++++++++++++++++++++++++++++++ test-programs/macros/build.rs | 8 ++--- 5 files changed, 90 insertions(+), 25 deletions(-) create mode 100644 README.md 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..67e55822 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,15 +29,11 @@ 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 --release --target wasm32-unknown-unknown + - run: cargo run -p verify -- ./target/wasm32-unknown-unknown/release/wasi_snapshot_preview1.wasm - run: cargo test -p host build: @@ -49,14 +45,12 @@ 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 - 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 +59,4 @@ 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 diff --git a/Cargo.toml b/Cargo.toml index c6d86ba8..6d3166a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,12 +22,15 @@ 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 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/test-programs/macros/build.rs b/test-programs/macros/build.rs index 86ebb60c..56a08a8f 100644 --- a/test-programs/macros/build.rs +++ b/test-programs/macros/build.rs @@ -14,17 +14,13 @@ fn main() { cmd.arg("build") .arg("--release") .current_dir("../../") - .arg("--target=wasm32-wasi") + .arg("--target=wasm32-unknown-unknown") .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(); From cdaa6870ebd9ab318486f46438dbce66442c0648 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 6 Dec 2022 09:18:59 -0800 Subject: [PATCH 2/2] Move `command` support behind a `command` feature This commit adds a `command` feature to main crate to avoid importing the `_start` function when the `command` feature is disabled, making this adapter useful for non-command WASI programs as well. For now this still emits the `command` export in the final component but with `use` in `*.wit` files it should be easier to avoid that export. --- .github/workflows/main.yml | 14 +++++++++++++- Cargo.toml | 3 +++ src/lib.rs | 12 +++++++++--- test-programs/macros/build.rs | 1 + 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 67e55822..92a3df45 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -32,8 +32,12 @@ jobs: - 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: @@ -46,6 +50,12 @@ jobs: - uses: actions/checkout@v3 - run: rustup update stable && rustup default stable - 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: @@ -59,4 +69,6 @@ jobs: automatic_release_tag: latest prerelease: true title: "Latest Build" - files: target/wasm32-unknown-unknown/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 6d3166a5..8961f615 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,3 +34,6 @@ opt-level = 's' # Omit integer overflow checks, which include failure messages which require # string initializers. overflow-checks = false + +[features] +command = [] 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 56a08a8f..adc55ce9 100644 --- a/test-programs/macros/build.rs +++ b/test-programs/macros/build.rs @@ -15,6 +15,7 @@ fn main() { .arg("--release") .current_dir("../../") .arg("--target=wasm32-unknown-unknown") + .arg("--features=command") .env("CARGO_TARGET_DIR", &out_dir) .env_remove("CARGO_ENCODED_RUSTFLAGS"); let status = cmd.status().unwrap();