diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8ac002a4cd..1dd398f965 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -71,7 +71,7 @@ jobs: - uses: ./.github/actions/install-rust with: toolchain: nightly - - run: cargo install cargo-fuzz --vers "^0.6" + - run: cargo install cargo-fuzz --vers "^0.7" - run: cargo fetch working-directory: ./fuzz - run: cargo fuzz build --release --debug-assertions @@ -165,6 +165,7 @@ jobs: - run: rustup target add wasm32-wasi - run: cargo fetch --locked + - run: cargo fetch --locked --manifest-path crates/test-programs/wasi-tests/Cargo.toml # Build and test all features except for lightbeam - run: cargo test --features test_programs --all --exclude lightbeam -- --nocapture @@ -302,7 +303,7 @@ jobs: - run: $CENTOS cargo build --release --bin wasmtime --bin wasm2obj shell: bash # Build `libwasmtime.so` - - run: $CENTOS cargo build --release --manifest-path crates/api/Cargo.toml + - run: $CENTOS cargo build --release --manifest-path crates/c-api/Cargo.toml shell: bash # Test what we just built - run: $CENTOS cargo test --features test_programs --release --all --exclude lightbeam --exclude wasmtime --exclude wasmtime-fuzzing @@ -511,4 +512,3 @@ jobs: files: "dist/*" name: ${{ steps.tagname.outputs.val }} token: ${{ secrets.GITHUB_TOKEN }} - diff --git a/.gitmodules b/.gitmodules index 2bb139cd81..59646f986c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,8 +1,8 @@ [submodule "spec_testsuite"] path = tests/spec_testsuite url = https://github.com/WebAssembly/testsuite -[submodule "crates/api/c-examples/wasm-c-api"] - path = crates/api/c-examples/wasm-c-api +[submodule "crates/c-api/examples/wasm-c-api"] + path = crates/c-api/examples/wasm-c-api url = https://github.com/WebAssembly/wasm-c-api [submodule "crates/wasi-common/WASI"] path = crates/wasi-common/wig/WASI diff --git a/Cargo.lock b/Cargo.lock index a79bb8bb96..71ae1c7039 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,6 +30,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64cf76cb6e2222ed0ea86b2b0ee2f71c96ec6edd5af42e84d59160e91b836ec4" +[[package]] +name = "arbitrary" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93b22576af8f14bb2bad6a5dc09c4f80539a801f7ea340c798e222f2ce49859" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "arrayref" version = "0.3.5" @@ -76,9 +85,9 @@ checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "backtrace" -version = "0.3.40" +version = "0.3.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" +checksum = "b4b1549d804b6c73f4817df2ba073709e96e426f12987127c48e6745568c350b" dependencies = [ "backtrace-sys", "cfg-if", @@ -159,8 +168,8 @@ dependencies = [ "lazycell", "log", "peeking_take_while", - "proc-macro2", - "quote", + "proc-macro2 1.0.7", + "quote 1.0.2", "regex", "rustc-hash", "shlex", @@ -341,24 +350,25 @@ dependencies = [ [[package]] name = "cranelift-bforest" -version = "0.54.0" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd3225fff1be118941c5fb66f1fb1f7f3e86468fac0e7364713c4fb99b72632b" +checksum = "40af6e6f7419110906d0f7b4b8084d3216be64d7da77aa12887885ebe0fc2776" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.54.0" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e3e6679892029f76a99b9059d2b74e77ac03637d573bb014bc21579ec1b7da" +checksum = "88583eb22e5cd0fe1ef66f0b80d0063d21d30e84e887d08b3d369def3ea7b4be" dependencies = [ "byteorder", "cranelift-bforest", "cranelift-codegen-meta", "cranelift-codegen-shared", "cranelift-entity", + "gimli", "log", "serde", "smallvec", @@ -368,9 +378,9 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.54.0" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cabe691548e28ca82ebd218f2fe76eec4c5629b64ef3db8b58443b7d9047275" +checksum = "353872c984943b9134d7c835eb9d12932bd90f4992dbe666593771bee920d673" dependencies = [ "cranelift-codegen-shared", "cranelift-entity", @@ -378,24 +388,24 @@ dependencies = [ [[package]] name = "cranelift-codegen-shared" -version = "0.54.0" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d173252ffade4aa6e929090977b9a4cd5ac28e15a42626f878be3844b3000ad9" +checksum = "cd1e96479a56981cce5c8f14d26773195d662ccdbbeca39fb8eba22b5ca8ea6a" [[package]] name = "cranelift-entity" -version = "0.54.0" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3498ad9ba021715716a1c52e2b31d7829a149913fb0d88493e4b07d3b772b656" +checksum = "37bc8dc3d4274ededc687d84821f491a8a03447dbb7481983936220892cf55b4" dependencies = [ "serde", ] [[package]] name = "cranelift-frontend" -version = "0.54.0" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521a30773b8de74345c807a38853f055aca8fecaa39a0fc7698bfebc5a3da515" +checksum = "4ea33e55bda8c425f3f533355b03e3a43cf29b4e228b35b868b6a1c43b6a139e" dependencies = [ "cranelift-codegen", "log", @@ -405,9 +415,9 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.54.0" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624e755cbe984e437308968239736e7f9aa3193e99928fb76eec7a1946627b34" +checksum = "2c22cfaaa5e69eddaddd6cfa7a76233de964b9b2245e81a5f47dae739931ad0d" dependencies = [ "cranelift-codegen", "raw-cpuid", @@ -416,9 +426,9 @@ dependencies = [ [[package]] name = "cranelift-wasm" -version = "0.54.0" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0320733e518ab9e0e2d1a22034d40e2851fb403ed14db5220cf9b86576b9cfd4" +checksum = "038f8fd636abc83ccd6f110e0891766521c3599b52173c0f37e61c684f19a15d" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -426,7 +436,7 @@ dependencies = [ "log", "serde", "thiserror", - "wasmparser 0.45.2", + "wasmparser 0.47.0", ] [[package]] @@ -513,8 +523,8 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd8ce37ad4184ab2ce004c33bf6379185d3b1c95801cab51026bd271bf68eedc" dependencies = [ - "quote", - "syn", + "quote 1.0.2", + "syn 1.0.13", ] [[package]] @@ -526,6 +536,17 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "derive_arbitrary" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b979a9f98d526ac07489ae2879e04d948ceeb195d777742997a30c3d1ab1aad6" +dependencies = [ + "proc-macro2 0.4.30", + "syn 0.14.9", + "synstructure 0.9.0", +] + [[package]] name = "digest" version = "0.8.1" @@ -567,9 +588,9 @@ dependencies = [ "byteorder", "lazy_static", "owning_ref", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", ] [[package]] @@ -667,10 +688,10 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", + "synstructure 0.12.3", ] [[package]] @@ -745,9 +766,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a36606a68532b5640dc86bb1f33c64b45c4682aad4c50f3937b317ea387f3d6" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", ] [[package]] @@ -839,9 +860,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b714fc08d0961716390977cdff1536234415ac37b509e34e5a983def8340fb75" dependencies = [ "proc-macro-hack", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", "unindent", ] @@ -862,9 +883,9 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a8e30575afe28eea36a9a39136b70b2fb6b0dd0a212a5bd1f30a498395c0274" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", ] [[package]] @@ -917,10 +938,11 @@ checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" [[package]] name = "libfuzzer-sys" -version = "0.1.0" -source = "git+https://github.com/rust-fuzz/libfuzzer-sys.git#0c4507533a79e85e1984f59765bdd35fbdaa7f1b" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f9e4f036a9cb9f43c637990c03fe045425a33c1c44abf9bc6f555671be5969" dependencies = [ - "arbitrary", + "arbitrary 0.3.2", "cc", ] @@ -1144,9 +1166,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4214c9e912ef61bf42b81ba9a47e8aad1b2ffaf739ab162bf96d1e011f54e6c5" dependencies = [ "proc-macro-hack", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", ] [[package]] @@ -1185,10 +1207,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53c98547ceaea14eeb26fcadf51dc70d01a2479a7839170eae133721105e4428" dependencies = [ "proc-macro-error-attr", - "proc-macro2", - "quote", + "proc-macro2 1.0.7", + "quote 1.0.2", "rustversion", - "syn", + "syn 1.0.13", ] [[package]] @@ -1197,10 +1219,10 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2bf5d493cf5d3e296beccfd61794e445e830dfc8070a9c248ad3ee071392c6c" dependencies = [ - "proc-macro2", - "quote", + "proc-macro2 1.0.7", + "quote 1.0.2", "rustversion", - "syn", + "syn 1.0.13", "syn-mid", ] @@ -1210,9 +1232,18 @@ version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", +] + +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid 0.1.0", ] [[package]] @@ -1221,7 +1252,7 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0319972dcae462681daf4da1adeeaa066e3ebd29c69be96c6abb1259d2ee2bcc" dependencies = [ - "unicode-xid", + "unicode-xid 0.2.0", ] [[package]] @@ -1251,9 +1282,9 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4882d8237fd8c7373cc25cb802fe0dab9ff70830fd56f47ef6c7f3f287fcc057" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", ] [[package]] @@ -1262,10 +1293,10 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf321cfab555f7411298733c86d21e5136f5ded13f5872fabf9de3337beecda" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.7", "pyo3-derive-backend", - "quote", - "syn", + "quote 1.0.2", + "syn 1.0.13", ] [[package]] @@ -1286,13 +1317,22 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2 0.4.30", +] + [[package]] name = "quote" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.7", ] [[package]] @@ -1517,9 +1557,9 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a0538bd897e17257b0128d2fd95c2ed6df939374073a36166051a79e2eb7986" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", ] [[package]] @@ -1549,9 +1589,9 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8584eea9b9ff42825b46faf46a8c24d2cff13ec152fa2a50df788b87c07ee28" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", ] [[package]] @@ -1584,9 +1624,9 @@ version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", ] [[package]] @@ -1669,9 +1709,20 @@ checksum = "0a97f829a34a0a9d5b353a881025a23b8c9fd09d46be6045df6b22920dbd7a93" dependencies = [ "heck", "proc-macro-error", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", +] + +[[package]] +name = "syn" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid 0.1.0", ] [[package]] @@ -1680,9 +1731,9 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e4ff033220a41d1a57d8125eab57bf5263783dfdcc18688b1dacc6ce9651ef8" dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", + "proc-macro2 1.0.7", + "quote 1.0.2", + "unicode-xid 0.2.0", ] [[package]] @@ -1691,9 +1742,21 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fd3937748a7eccff61ba5b90af1a20dbf610858923a9192ea0ecb0cb77db1d0" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", +] + +[[package]] +name = "synstructure" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bb9b7550d063ea184027c9b8c20ac167cd36d3e06b3a40bceb9d746dc1a7b7" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.14.9", + "unicode-xid 0.1.0", ] [[package]] @@ -1702,10 +1765,10 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", + "unicode-xid 0.2.0", ] [[package]] @@ -1780,9 +1843,9 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb2e25d25307eb8436894f727aba8f65d07adf02e5b35a13cebed48bd282bfef" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", ] [[package]] @@ -1861,6 +1924,12 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + [[package]] name = "unicode-xid" version = "0.2.0" @@ -1921,9 +1990,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8757b0da38353d55a9687f4dee68a8f441f980dd36e16ab07d6e6c673f505f76" dependencies = [ "heck", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", ] [[package]] @@ -1958,9 +2027,9 @@ dependencies = [ name = "wasi-common-cbindgen" version = "0.9.0" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", "trybuild", ] @@ -1988,12 +2057,6 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1527c84a5bd585215f29c06b0e2a5274e478ad4dfc970d26ffad66fdc6cb311d" -[[package]] -name = "wasmparser" -version = "0.45.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b4eab1d9971d0803729cba3617b56eb04fcb4bd25361cb63880ed41a42f20d5" - [[package]] name = "wasmparser" version = "0.47.0" @@ -2015,6 +2078,7 @@ name = "wasmtime" version = "0.9.0" dependencies = [ "anyhow", + "backtrace", "cfg-if", "file-per-thread-logger", "libc", @@ -2031,6 +2095,13 @@ dependencies = [ "winapi", ] +[[package]] +name = "wasmtime-c-api" +version = "0.9.0" +dependencies = [ + "wasmtime", +] + [[package]] name = "wasmtime-cli" version = "0.9.0" @@ -2113,7 +2184,7 @@ dependencies = [ name = "wasmtime-fuzz" version = "0.9.0" dependencies = [ - "arbitrary", + "arbitrary 0.2.0", "env_logger 0.7.1", "libfuzzer-sys", "log", @@ -2127,7 +2198,7 @@ name = "wasmtime-fuzzing" version = "0.9.0" dependencies = [ "anyhow", - "arbitrary", + "arbitrary 0.3.2", "binaryen", "env_logger 0.7.1", "log", @@ -2205,6 +2276,7 @@ dependencies = [ name = "wasmtime-runtime" version = "0.9.0" dependencies = [ + "backtrace", "cc", "cfg-if", "indexmap", @@ -2233,9 +2305,9 @@ dependencies = [ name = "wasmtime-rust-macro" version = "0.9.0" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.7", + "quote 1.0.2", + "syn 1.0.13", ] [[package]] @@ -2323,8 +2395,8 @@ name = "wig" version = "0.9.2" dependencies = [ "heck", - "proc-macro2", - "quote", + "proc-macro2 1.0.7", + "quote 1.0.2", "witx", ] diff --git a/Cargo.toml b/Cargo.toml index 378189e3c2..09bdaa1cfe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ members = [ "crates/fuzzing", "crates/misc/rust", "crates/misc/py", + "crates/c-api", "fuzz", ] diff --git a/ci/build-tarballs.sh b/ci/build-tarballs.sh index b998bb93c9..433450116b 100755 --- a/ci/build-tarballs.sh +++ b/ci/build-tarballs.sh @@ -48,7 +48,7 @@ mkdir tmp/$api_pkgname/lib mkdir tmp/$api_pkgname/include cp LICENSE README.md tmp/$api_pkgname mv bins-$src/* tmp/$api_pkgname/lib -cp crates/api/c-examples/wasm-c-api/include/wasm.h tmp/$api_pkgname/include +cp crates/c-api/examples/wasm-c-api/include/wasm.h tmp/$api_pkgname/include mktarball $api_pkgname # Move wheels to dist folder diff --git a/crates/api/Cargo.toml b/crates/api/Cargo.toml index 67961e0299..66d2e42024 100644 --- a/crates/api/Cargo.toml +++ b/crates/api/Cargo.toml @@ -8,10 +8,6 @@ repository = "https://github.com/bytecodealliance/wasmtime" readme = "README.md" edition = "2018" -[lib] -name = "wasmtime" -crate-type = ["lib", "staticlib", "cdylib"] - [dependencies] wasmtime-runtime = { path = "../runtime", version = "0.9.0" } wasmtime-environ = { path = "../environ", version = "0.9.0" } @@ -22,6 +18,7 @@ anyhow = "1.0.19" region = "2.0.0" libc = "0.2" cfg-if = "0.1.9" +backtrace = "0.3.42" [target.'cfg(target_os = "windows")'.dependencies] winapi = "0.3.7" diff --git a/crates/api/README.md b/crates/api/README.md index b0edded9c2..49ca113c15 100644 --- a/crates/api/README.md +++ b/crates/api/README.md @@ -1,3 +1,8 @@ -# Implementation of wasm-c-api in Rust +## Wasmtime Embedding API -https://github.com/WebAssembly/wasm-c-api +The `wasmtime` crate is an embedding API of the `wasmtime` WebAssembly runtime. +This is intended to be used in Rust projects and provides a high-level API of +working with WebAssembly modules. + +If you're interested in embedding `wasmtime` in other languages, you may wish to +take a look a the [C embedding API](../c-api) instead! diff --git a/crates/api/src/callable.rs b/crates/api/src/callable.rs index 139959f359..732f958981 100644 --- a/crates/api/src/callable.rs +++ b/crates/api/src/callable.rs @@ -141,13 +141,12 @@ impl WrappedCallable for WasmtimeFn { // Get the trampoline to call for this function. let exec_code_buf = self .store - .context() - .compiler() + .compiler_mut() .get_published_trampoline(body, &signature, value_size) .map_err(|e| Trap::new(format!("trampoline error: {:?}", e)))?; // Call the trampoline. - if let Err(message) = unsafe { + if let Err(error) = unsafe { self.instance.with_signals_on(|| { wasmtime_runtime::wasmtime_call_trampoline( vmctx, @@ -156,8 +155,7 @@ impl WrappedCallable for WasmtimeFn { ) }) } { - let trap = - take_api_trap().unwrap_or_else(|| Trap::new(format!("call error: {}", message))); + let trap = take_api_trap().unwrap_or_else(|| Trap::from_jit(error)); return Err(trap); } diff --git a/crates/api/src/context.rs b/crates/api/src/context.rs deleted file mode 100644 index 05f9ecf02f..0000000000 --- a/crates/api/src/context.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crate::Config; -use std::cell::{RefCell, RefMut}; -use std::hash::{Hash, Hasher}; -use std::rc::Rc; -use wasmtime_environ::settings; -use wasmtime_jit::{native, Compiler, Features}; - -#[derive(Clone)] -pub struct Context { - compiler: Rc>, - features: Features, - debug_info: bool, -} - -impl Context { - pub fn new(config: &Config) -> Context { - let isa = native::builder().finish(settings::Flags::new(config.flags.clone())); - Context::new_with_compiler(config, Compiler::new(isa, config.strategy)) - } - - pub fn new_with_compiler(config: &Config, compiler: Compiler) -> Context { - Context { - compiler: Rc::new(RefCell::new(compiler)), - features: config.features.clone(), - debug_info: config.debug_info, - } - } - - pub(crate) fn debug_info(&self) -> bool { - self.debug_info - } - - pub(crate) fn compiler(&self) -> RefMut { - self.compiler.borrow_mut() - } -} - -impl Hash for Context { - fn hash(&self, state: &mut H) - where - H: Hasher, - { - self.compiler.as_ptr().hash(state) - } -} - -impl Eq for Context {} - -impl PartialEq for Context { - fn eq(&self, other: &Context) -> bool { - Rc::ptr_eq(&self.compiler, &other.compiler) - } -} diff --git a/crates/api/src/instance.rs b/crates/api/src/instance.rs index 48b080528b..3299b16382 100644 --- a/crates/api/src/instance.rs +++ b/crates/api/src/instance.rs @@ -1,4 +1,3 @@ -use crate::context::Context; use crate::externals::Extern; use crate::module::Module; use crate::runtime::Store; @@ -6,9 +5,6 @@ use crate::trampoline::take_api_trap; use crate::trap::Trap; use crate::types::{ExportType, ExternType}; use anyhow::{Error, Result}; -use std::cell::RefCell; -use std::collections::{HashMap, HashSet}; -use std::rc::Rc; use wasmtime_jit::{CompiledModule, Resolver}; use wasmtime_runtime::{Export, InstanceHandle, InstantiationError}; @@ -29,19 +25,15 @@ fn instantiate_in_context( data: &[u8], imports: &[Extern], module_name: Option<&str>, - context: Context, - exports: Rc>>>, -) -> Result<(InstanceHandle, HashSet), Error> { - let mut contexts = HashSet::new(); - let debug_info = context.debug_info(); +) -> Result { let mut resolver = SimpleResolver { imports }; let mut compiled_module = CompiledModule::new( - &mut context.compiler(), + &mut store.compiler_mut(), data, module_name, &mut resolver, - exports, - debug_info, + store.global_exports().clone(), + store.engine().config().debug_info, )?; // Register all module signatures @@ -52,40 +44,30 @@ fn instantiate_in_context( let instance = compiled_module.instantiate().map_err(|e| -> Error { if let Some(trap) = take_api_trap() { trap.into() - } else if let InstantiationError::StartTrap(msg) = e { - Trap::new(msg).into() + } else if let InstantiationError::StartTrap(trap) = e { + Trap::from_jit(trap).into() } else { e.into() } })?; - contexts.insert(context); - Ok((instance, contexts)) + Ok(instance) } #[derive(Clone)] pub struct Instance { instance_handle: InstanceHandle, - module: Module, - - // We need to keep CodeMemory alive. - contexts: HashSet, - exports: Box<[Extern]>, } impl Instance { pub fn new(module: &Module, externs: &[Extern]) -> Result { let store = module.store(); - let context = store.context().clone(); - let exports = store.global_exports().clone(); - let (mut instance_handle, contexts) = instantiate_in_context( - module.store(), + let mut instance_handle = instantiate_in_context( + store, module.binary().expect("binary"), externs, module.name(), - context, - exports, )?; let exports = { @@ -104,7 +86,6 @@ impl Instance { Ok(Instance { instance_handle, module: module.clone(), - contexts, exports, }) } @@ -141,8 +122,6 @@ impl Instance { } pub fn from_handle(store: &Store, instance_handle: InstanceHandle) -> Instance { - let contexts = HashSet::new(); - let mut exports = Vec::new(); let mut exports_types = Vec::new(); let mut mutable = instance_handle.clone(); @@ -175,7 +154,6 @@ impl Instance { Instance { instance_handle, module, - contexts, exports: exports.into_boxed_slice(), } } diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs index 7ccf342ca5..7d1dc17588 100644 --- a/crates/api/src/lib.rs +++ b/crates/api/src/lib.rs @@ -7,7 +7,6 @@ //! itself for consumption from other languages. mod callable; -mod context; mod externals; mod instance; mod module; @@ -18,13 +17,11 @@ mod trap; mod types; mod values; -pub mod wasm; - pub use crate::callable::Callable; pub use crate::externals::*; pub use crate::instance::Instance; pub use crate::module::Module; -pub use crate::r#ref::AnyRef; +pub use crate::r#ref::{AnyRef, HostInfo, HostRef}; pub use crate::runtime::{Config, Engine, OptLevel, Store, Strategy}; pub use crate::trap::{FrameInfo, Trap}; pub use crate::types::*; diff --git a/crates/api/src/module.rs b/crates/api/src/module.rs index 3704f11e14..d6fd7f0579 100644 --- a/crates/api/src/module.rs +++ b/crates/api/src/module.rs @@ -196,7 +196,7 @@ impl Module { /// /// [binary]: https://webassembly.github.io/spec/core/binary/index.html pub fn validate(store: &Store, binary: &[u8]) -> Result<()> { - let features = store.engine().config.features.clone(); + let features = store.engine().config().features.clone(); let config = ValidatingParserConfig { operator_config: OperatorValidatorConfig { enable_threads: features.threads, diff --git a/crates/api/src/runtime.rs b/crates/api/src/runtime.rs index 7476bc44c9..5563be02b0 100644 --- a/crates/api/src/runtime.rs +++ b/crates/api/src/runtime.rs @@ -1,4 +1,3 @@ -use crate::context::Context; use anyhow::Result; use std::cell::RefCell; use std::collections::HashMap; @@ -8,7 +7,7 @@ use wasmtime_environ::{ ir, settings::{self, Configurable}, }; -use wasmtime_jit::{CompilationStrategy, Features}; +use wasmtime_jit::{native, CompilationStrategy, Compiler, Features}; // Runtime Environment @@ -297,7 +296,7 @@ pub enum OptLevel { /// default settings. #[derive(Default, Clone)] pub struct Engine { - pub(crate) config: Arc, + config: Arc, } impl Engine { @@ -308,6 +307,11 @@ impl Engine { config: Arc::new(config.clone()), } } + + /// Returns the configuration settings that this engine is using. + pub fn config(&self) -> &Config { + &self.config + } } // Store @@ -337,7 +341,7 @@ pub struct Store { struct StoreInner { engine: Engine, - context: Context, + compiler: RefCell, global_exports: Rc>>>, signature_cache: RefCell>, } @@ -345,10 +349,12 @@ struct StoreInner { impl Store { /// Creates a new store to be associated with the given [`Engine`]. pub fn new(engine: &Engine) -> Store { + let isa = native::builder().finish(settings::Flags::new(engine.config.flags.clone())); + let compiler = Compiler::new(isa, engine.config.strategy); Store { inner: Rc::new(StoreInner { engine: engine.clone(), - context: Context::new(&engine.config), + compiler: RefCell::new(compiler), global_exports: Rc::new(RefCell::new(HashMap::new())), signature_cache: RefCell::new(HashMap::new()), }), @@ -360,8 +366,8 @@ impl Store { &self.inner.engine } - pub(crate) fn context(&self) -> &Context { - &self.inner.context + pub(crate) fn compiler_mut(&self) -> std::cell::RefMut<'_, Compiler> { + self.inner.compiler.borrow_mut() } // Specific to wasmtime: hack to pass memory around to wasi @@ -377,7 +383,7 @@ impl Store { signature: &ir::Signature, ) -> wasmtime_runtime::VMSharedSignatureIndex { use std::collections::hash_map::Entry; - let index = self.context().compiler().signatures().register(signature); + let index = self.compiler_mut().signatures().register(signature); match self.inner.signature_cache.borrow_mut().entry(index) { Entry::Vacant(v) => { v.insert(signature.clone()); @@ -398,7 +404,13 @@ impl Store { .cloned() } - pub(crate) fn ptr_eq(a: &Store, b: &Store) -> bool { + /// Returns whether the stores `a` and `b` refer to the same underlying + /// `Store`. + /// + /// Because the `Store` type is reference counted multiple clones may point + /// to the same underlying storage, and this method can be used to determine + /// whether two stores are indeed the same. + pub fn same(a: &Store, b: &Store) -> bool { Rc::ptr_eq(&a.inner, &b.inner) } } diff --git a/crates/api/src/trampoline/func.rs b/crates/api/src/trampoline/func.rs index 328a325490..f5d7dde5c3 100644 --- a/crates/api/src/trampoline/func.rs +++ b/crates/api/src/trampoline/func.rs @@ -11,7 +11,9 @@ use wasmtime_environ::entity::{EntityRef, PrimaryMap}; use wasmtime_environ::ir::types; use wasmtime_environ::isa::TargetIsa; use wasmtime_environ::wasm::{DefinedFuncIndex, FuncIndex}; -use wasmtime_environ::{ir, settings, CompiledFunction, Export, Module, TrapInformation}; +use wasmtime_environ::{ + ir, settings, CompiledFunction, CompiledFunctionUnwindInfo, Export, Module, TrapInformation, +}; use wasmtime_jit::trampoline::ir::{ ExternalName, Function, InstBuilder, MemFlags, StackSlotData, StackSlotKind, }; @@ -136,6 +138,7 @@ fn make_trampoline( let mut context = Context::new(); context.func = Function::with_name_signature(ExternalName::user(0, 0), signature.clone()); + context.func.collect_frame_layout_info(); let ss = context.func.create_stack_slot(StackSlotData::new( StackSlotKind::ExplicitSlot, @@ -213,8 +216,7 @@ fn make_trampoline( .map_err(|error| pretty_error(&context.func, Some(isa), error)) .expect("compile_and_emit"); - let mut unwind_info = Vec::new(); - context.emit_unwind_info(isa, &mut unwind_info); + let unwind_info = CompiledFunctionUnwindInfo::new(isa, &context); let traps = trap_sink.traps; diff --git a/crates/api/src/trap.rs b/crates/api/src/trap.rs index 5127d1c5c1..0672b661f4 100644 --- a/crates/api/src/trap.rs +++ b/crates/api/src/trap.rs @@ -1,4 +1,5 @@ use crate::instance::Instance; +use backtrace::Backtrace; use std::fmt; use std::sync::Arc; @@ -11,7 +12,8 @@ pub struct Trap { struct TrapInner { message: String, - trace: Vec, + wasm_trace: Vec, + native_trace: Backtrace, } fn _assert_trap_is_sync_and_send(t: &Trap) -> (&dyn Sync, &dyn Send) { @@ -26,10 +28,29 @@ impl Trap { /// assert_eq!("unexpected error", trap.message()); /// ``` pub fn new>(message: I) -> Self { + Trap::new_with_trace(message.into(), Backtrace::new_unresolved()) + } + + pub(crate) fn from_jit(jit: wasmtime_runtime::Trap) -> Self { + Trap::new_with_trace(jit.to_string(), jit.backtrace) + } + + fn new_with_trace(message: String, native_trace: Backtrace) -> Self { + let mut wasm_trace = Vec::new(); + for frame in native_trace.frames() { + let pc = frame.ip() as usize; + if let Some(info) = wasmtime_runtime::jit_function_registry::find(pc) { + wasm_trace.push(FrameInfo { + func_index: info.func_index as u32, + module_name: info.module_id.clone(), + }) + } + } Trap { inner: Arc::new(TrapInner { - message: message.into(), - trace: Vec::new(), + message, + wasm_trace, + native_trace, }), } } @@ -40,7 +61,7 @@ impl Trap { } pub fn trace(&self) -> &[FrameInfo] { - &self.inner.trace + &self.inner.wasm_trace } } @@ -48,7 +69,8 @@ impl fmt::Debug for Trap { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Trap") .field("message", &self.inner.message) - .field("trace", &self.inner.trace) + .field("wasm_trace", &self.inner.wasm_trace) + .field("native_trace", &self.inner.native_trace) .finish() } } @@ -62,22 +84,29 @@ impl fmt::Display for Trap { impl std::error::Error for Trap {} #[derive(Debug)] -pub struct FrameInfo; +pub struct FrameInfo { + module_name: Option, + func_index: u32, +} impl FrameInfo { pub fn instance(&self) -> *const Instance { unimplemented!("FrameInfo::instance"); } - pub fn func_index() -> usize { - unimplemented!("FrameInfo::func_index"); + pub fn func_index(&self) -> u32 { + self.func_index } - pub fn func_offset() -> usize { + pub fn func_offset(&self) -> usize { unimplemented!("FrameInfo::func_offset"); } - pub fn module_offset() -> usize { + pub fn module_offset(&self) -> usize { unimplemented!("FrameInfo::module_offset"); } + + pub fn module_name(&self) -> Option<&str> { + self.module_name.as_deref() + } } diff --git a/crates/api/tests/invoke_func_via_table.rs b/crates/api/tests/invoke_func_via_table.rs index c10d696ef2..9b64e894d8 100644 --- a/crates/api/tests/invoke_func_via_table.rs +++ b/crates/api/tests/invoke_func_via_table.rs @@ -16,7 +16,7 @@ fn test_invoke_func_via_table() -> Result<()> { "#, )?; let module = Module::new(&store, &binary).context("> Error compiling module!")?; - let instance = Instance::new(&store, &module, &[]).context("> Error instantiating module!")?; + let instance = Instance::new(&module, &[]).context("> Error instantiating module!")?; let f = instance .find_export_by_name("table") diff --git a/crates/api/tests/traps.rs b/crates/api/tests/traps.rs index aea6404a9b..6f9cdc582b 100644 --- a/crates/api/tests/traps.rs +++ b/crates/api/tests/traps.rs @@ -41,3 +41,116 @@ fn test_trap_return() -> Result<(), String> { Ok(()) } + +#[test] +fn test_trap_trace() -> Result<(), String> { + let store = Store::default(); + let binary = parse_str( + r#" + (module $hello_mod + (func (export "run") (call $hello)) + (func $hello (unreachable)) + ) + "#, + ) + .map_err(|e| format!("failed to parse WebAssembly text source: {}", e))?; + + let module = + Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?; + let instance = Instance::new(&module, &[]) + .map_err(|e| format!("failed to instantiate module: {:?}", e))?; + let run_func = instance.exports()[0] + .func() + .expect("expected function export"); + + let e = run_func.call(&[]).err().expect("error calling function"); + + let trace = e.trace(); + assert_eq!(trace.len(), 2); + assert_eq!(trace[0].module_name().unwrap(), "hello_mod"); + assert_eq!(trace[0].func_index(), 1); + assert_eq!(trace[1].module_name().unwrap(), "hello_mod"); + assert_eq!(trace[1].func_index(), 0); + assert!(e.message().contains("unreachable")); + + Ok(()) +} + +#[test] +fn test_trap_trace_cb() -> Result<(), String> { + struct ThrowCallback; + + impl Callable for ThrowCallback { + fn call(&self, _params: &[Val], _results: &mut [Val]) -> Result<(), Trap> { + Err(Trap::new("cb throw")) + } + } + + let store = Store::default(); + let binary = parse_str( + r#" + (module $hello_mod + (import "" "throw" (func $throw)) + (func (export "run") (call $hello)) + (func $hello (call $throw)) + ) + "#, + ) + .map_err(|e| format!("failed to parse WebAssembly text source: {}", e))?; + + let fn_type = FuncType::new(Box::new([]), Box::new([])); + let fn_func = Func::new(&store, fn_type, Rc::new(ThrowCallback)); + + let module = + Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?; + let instance = Instance::new(&module, &[fn_func.into()]) + .map_err(|e| format!("failed to instantiate module: {:?}", e))?; + let run_func = instance.exports()[0] + .func() + .expect("expected function export"); + + let e = run_func.call(&[]).err().expect("error calling function"); + + let trace = e.trace(); + assert_eq!(trace.len(), 2); + assert_eq!(trace[0].module_name().unwrap(), "hello_mod"); + assert_eq!(trace[0].func_index(), 1); + assert_eq!(trace[1].module_name().unwrap(), "hello_mod"); + assert_eq!(trace[1].func_index(), 0); + assert_eq!(e.message(), "cb throw"); + + Ok(()) +} + +#[test] +fn test_trap_stack_overflow() -> Result<(), String> { + let store = Store::default(); + let binary = parse_str( + r#" + (module $rec_mod + (func $run (export "run") (call $run)) + ) + "#, + ) + .map_err(|e| format!("failed to parse WebAssembly text source: {}", e))?; + + let module = + Module::new(&store, &binary).map_err(|e| format!("failed to compile module: {}", e))?; + let instance = Instance::new(&module, &[]) + .map_err(|e| format!("failed to instantiate module: {:?}", e))?; + let run_func = instance.exports()[0] + .func() + .expect("expected function export"); + + let e = run_func.call(&[]).err().expect("error calling function"); + + let trace = e.trace(); + assert!(trace.len() >= 32); + for i in 0..trace.len() { + assert_eq!(trace[i].module_name().unwrap(), "rec_mod"); + assert_eq!(trace[i].func_index(), 0); + } + assert!(e.message().contains("call stack exhausted")); + + Ok(()) +} diff --git a/crates/c-api/Cargo.toml b/crates/c-api/Cargo.toml new file mode 100644 index 0000000000..9c1a820cb2 --- /dev/null +++ b/crates/c-api/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "wasmtime-c-api" +version = "0.9.0" +authors = ["The Wasmtime Project Developers"] +description = "C API to expose the Wasmtime runtime" +license = "Apache-2.0 WITH LLVM-exception" +repository = "https://github.com/bytecodealliance/wasmtime" +readme = "README.md" +edition = "2018" +publish = false + +[lib] +name = "wasmtime" +crate-type = ["staticlib", "cdylib"] +doc = false +test = false +doctest = false + +[dependencies] +wasmtime = { path = "../api" } diff --git a/crates/c-api/LICENSE b/crates/c-api/LICENSE new file mode 100644 index 0000000000..f9d81955f4 --- /dev/null +++ b/crates/c-api/LICENSE @@ -0,0 +1,220 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + diff --git a/crates/c-api/README.md b/crates/c-api/README.md new file mode 100644 index 0000000000..b0edded9c2 --- /dev/null +++ b/crates/c-api/README.md @@ -0,0 +1,3 @@ +# Implementation of wasm-c-api in Rust + +https://github.com/WebAssembly/wasm-c-api diff --git a/crates/api/c-examples/Makefile b/crates/c-api/examples/Makefile similarity index 100% rename from crates/api/c-examples/Makefile rename to crates/c-api/examples/Makefile diff --git a/crates/api/c-examples/wasm-c-api b/crates/c-api/examples/wasm-c-api similarity index 100% rename from crates/api/c-examples/wasm-c-api rename to crates/c-api/examples/wasm-c-api diff --git a/crates/api/src/wasm.rs b/crates/c-api/src/lib.rs similarity index 99% rename from crates/api/src/wasm.rs rename to crates/c-api/src/lib.rs index 083671406f..ea647d207d 100644 --- a/crates/api/src/wasm.rs +++ b/crates/c-api/src/lib.rs @@ -5,15 +5,14 @@ // TODO complete the C API -use super::{ - AnyRef, Callable, Engine, ExportType, Extern, ExternType, Func, FuncType, Global, GlobalType, - ImportType, Instance, Limits, Memory, MemoryType, Module, Store, Table, TableType, Trap, Val, - ValType, -}; -use crate::r#ref::{HostInfo, HostRef}; use std::cell::RefCell; use std::rc::Rc; use std::{mem, ptr, slice}; +use wasmtime::{ + AnyRef, Callable, Engine, ExportType, Extern, ExternType, Func, FuncType, Global, GlobalType, + HostInfo, HostRef, ImportType, Instance, Limits, Memory, MemoryType, Module, Store, Table, + TableType, Trap, Val, ValType, +}; macro_rules! declare_vec { ($name:ident, $elem_ty:path) => { @@ -569,14 +568,18 @@ impl wasm_val_t { } } -impl Callable for wasm_func_callback_t { +struct Callback { + callback: wasm_func_callback_t, +} + +impl Callable for Callback { fn call(&self, params: &[Val], results: &mut [Val]) -> Result<(), Trap> { let params = params .iter() .map(|p| wasm_val_t::from_val(p)) .collect::>(); let mut out_results = vec![wasm_val_t::default(); results.len()]; - let func = self.expect("wasm_func_callback_t fn"); + let func = self.callback.expect("wasm_func_callback_t fn"); let out = unsafe { func(params.as_ptr(), out_results.as_mut_ptr()) }; if !out.is_null() { let trap: Box = unsafe { Box::from_raw(out) }; @@ -633,7 +636,7 @@ pub unsafe extern "C" fn wasm_func_new( ) -> *mut wasm_func_t { let store = &(*store).store.borrow(); let ty = (*ty).functype.clone(); - let callback = Rc::new(callback); + let callback = Rc::new(Callback { callback }); let func = Box::new(wasm_func_t { ext: wasm_extern_t { which: ExternHost::Func(HostRef::new(Func::new(store, ty, callback))), @@ -700,7 +703,7 @@ pub unsafe extern "C" fn wasm_instance_new( let module = &(*module).module.borrow(); // FIXME(WebAssembly/wasm-c-api#126) what else can we do with the `store` // argument? - if !Store::ptr_eq(&store, module.store()) { + if !Store::same(&store, module.store()) { if !result.is_null() { let trap = Trap::new("wasm_store_t must match store in wasm_module_t"); let trap = Box::new(wasm_trap_t { @@ -1254,7 +1257,7 @@ pub unsafe extern "C" fn wasm_globaltype_content( pub unsafe extern "C" fn wasm_globaltype_mutability( gt: *const wasm_globaltype_t, ) -> wasm_mutability_t { - use super::Mutability::*; + use wasmtime::Mutability::*; match (*gt).globaltype.mutability() { Const => 0, Var => 1, @@ -1411,7 +1414,7 @@ pub unsafe extern "C" fn wasm_globaltype_new( ty: *mut wasm_valtype_t, mutability: wasm_mutability_t, ) -> *mut wasm_globaltype_t { - use super::Mutability::*; + use wasmtime::Mutability::*; let ty = Box::from_raw(ty); let mutability = match mutability { 0 => Const, diff --git a/crates/environ/Cargo.toml b/crates/environ/Cargo.toml index 0f3d3e52ac..b8a1a0c7ed 100644 --- a/crates/environ/Cargo.toml +++ b/crates/environ/Cargo.toml @@ -13,9 +13,9 @@ edition = "2018" [dependencies] anyhow = "1.0" -cranelift-codegen = { version = "0.54", features = ["enable-serde"] } -cranelift-entity = { version = "0.54", features = ["enable-serde"] } -cranelift-wasm = { version = "0.54", features = ["enable-serde"] } +cranelift-codegen = { version = "0.55", features = ["enable-serde"] } +cranelift-entity = { version = "0.55", features = ["enable-serde"] } +cranelift-wasm = { version = "0.55", features = ["enable-serde"] } wasmparser = "0.47.0" lightbeam = { path = "../lightbeam", optional = true, version = "0.9.0" } indexmap = "1.0.2" @@ -46,7 +46,7 @@ tempfile = "3" target-lexicon = { version = "0.10.0", default-features = false } pretty_env_logger = "0.3.0" rand = { version = "0.7.0", default-features = false, features = ["small_rng"] } -cranelift-codegen = { version = "0.54", features = ["enable-serde", "all-arch"] } +cranelift-codegen = { version = "0.55", features = ["enable-serde", "all-arch"] } filetime = "0.2.7" [badges] diff --git a/crates/environ/src/cache/tests.rs b/crates/environ/src/cache/tests.rs index 55685d6de7..7a9bdc31c6 100644 --- a/crates/environ/src/cache/tests.rs +++ b/crates/environ/src/cache/tests.rs @@ -1,7 +1,9 @@ use super::config::tests::test_prolog; use super::*; use crate::address_map::{FunctionAddressMap, InstructionAddressMap}; -use crate::compilation::{CompiledFunction, Relocation, RelocationTarget, TrapInformation}; +use crate::compilation::{ + CompiledFunction, CompiledFunctionUnwindInfo, Relocation, RelocationTarget, TrapInformation, +}; use crate::module::{MemoryPlan, MemoryStyle, Module}; use cranelift_codegen::{binemit, ir, isa, settings, ValueLocRange}; use cranelift_entity::EntityRef; @@ -259,7 +261,7 @@ fn new_module_cache_data(rng: &mut impl Rng) -> ModuleCacheData { CompiledFunction { body: (0..(i * 3 / 2)).collect(), jt_offsets: sm, - unwind_info: (0..(i * 3 / 2)).collect(), + unwind_info: CompiledFunctionUnwindInfo::Windows((0..(i * 3 / 2)).collect()), } }) .collect(); diff --git a/crates/environ/src/compilation.rs b/crates/environ/src/compilation.rs index 3aea3ba284..ae57584008 100644 --- a/crates/environ/src/compilation.rs +++ b/crates/environ/src/compilation.rs @@ -4,13 +4,136 @@ use crate::cache::ModuleCacheDataTupleType; use crate::module; use crate::module_environ::FunctionBodyData; -use cranelift_codegen::{binemit, ir, isa}; +use cranelift_codegen::{binemit, ir, isa, Context}; use cranelift_entity::PrimaryMap; use cranelift_wasm::{DefinedFuncIndex, FuncIndex, ModuleTranslationState, WasmError}; use serde::{Deserialize, Serialize}; use std::ops::Range; use thiserror::Error; +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct FDERelocEntry(pub i64, pub usize, pub u8); + +/// Relocation entry for unwind info. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct CompiledFunctionUnwindInfoReloc { + /// Entry offest in the code block. + pub offset: u32, + /// Entry addend relative to the code block. + pub addend: u32, +} + +/// Compiled function unwind information. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +pub enum CompiledFunctionUnwindInfo { + /// No info. + None, + /// Windows UNWIND_INFO. + Windows(Vec), + /// Frame layout info. + FrameLayout(Vec, usize, Vec), +} + +impl CompiledFunctionUnwindInfo { + /// Constructs unwind info object. + pub fn new(isa: &dyn isa::TargetIsa, context: &Context) -> Self { + use cranelift_codegen::binemit::{ + FrameUnwindKind, FrameUnwindOffset, FrameUnwindSink, Reloc, + }; + use cranelift_codegen::isa::CallConv; + + struct Sink(Vec, usize, Vec); + impl FrameUnwindSink for Sink { + fn len(&self) -> FrameUnwindOffset { + self.0.len() + } + fn bytes(&mut self, b: &[u8]) { + self.0.extend_from_slice(b); + } + fn reserve(&mut self, len: usize) { + self.0.reserve(len) + } + fn reloc(&mut self, r: Reloc, off: FrameUnwindOffset) { + self.2.push(FDERelocEntry( + 0, + off, + match r { + Reloc::Abs4 => 4, + Reloc::Abs8 => 8, + _ => { + panic!("unexpected reloc type"); + } + }, + )) + } + fn set_entry_offset(&mut self, off: FrameUnwindOffset) { + self.1 = off; + } + } + + let kind = match context.func.signature.call_conv { + CallConv::SystemV | CallConv::Fast | CallConv::Cold => FrameUnwindKind::Libunwind, + CallConv::WindowsFastcall => FrameUnwindKind::Fastcall, + _ => { + return CompiledFunctionUnwindInfo::None; + } + }; + + let mut sink = Sink(Vec::new(), 0, Vec::new()); + context.emit_unwind_info(isa, kind, &mut sink); + + let Sink(data, offset, relocs) = sink; + if data.is_empty() { + return CompiledFunctionUnwindInfo::None; + } + + match kind { + FrameUnwindKind::Fastcall => CompiledFunctionUnwindInfo::Windows(data), + FrameUnwindKind::Libunwind => { + CompiledFunctionUnwindInfo::FrameLayout(data, offset, relocs) + } + } + } + + /// Retuns true is no unwind info data. + pub fn is_empty(&self) -> bool { + match self { + CompiledFunctionUnwindInfo::None => true, + CompiledFunctionUnwindInfo::Windows(d) => d.is_empty(), + CompiledFunctionUnwindInfo::FrameLayout(c, _, _) => c.is_empty(), + } + } + + /// Returns size of serilized unwind info. + pub fn len(&self) -> usize { + match self { + CompiledFunctionUnwindInfo::None => 0, + CompiledFunctionUnwindInfo::Windows(d) => d.len(), + CompiledFunctionUnwindInfo::FrameLayout(c, _, _) => c.len(), + } + } + + /// Serializes data into byte array. + pub fn serialize(&self, dest: &mut [u8], relocs: &mut Vec) { + match self { + CompiledFunctionUnwindInfo::None => (), + CompiledFunctionUnwindInfo::Windows(d) => { + dest.copy_from_slice(d); + } + CompiledFunctionUnwindInfo::FrameLayout(code, _fde_offset, r) => { + dest.copy_from_slice(code); + r.iter().for_each(move |r| { + assert_eq!(r.2, 8); + relocs.push(CompiledFunctionUnwindInfoReloc { + offset: r.1 as u32, + addend: r.0 as u32, + }) + }); + } + } + } +} + /// Compiled function: machine code body, jump table offsets, and unwind information. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub struct CompiledFunction { @@ -21,7 +144,7 @@ pub struct CompiledFunction { pub jt_offsets: ir::JumpTableOffsets, /// The unwind information. - pub unwind_info: Vec, + pub unwind_info: CompiledFunctionUnwindInfo, } type Functions = PrimaryMap; @@ -50,7 +173,7 @@ impl Compilation { .map(|(body_range, jt_offsets, unwind_range)| CompiledFunction { body: buffer[body_range].to_vec(), jt_offsets, - unwind_info: buffer[unwind_range].to_vec(), + unwind_info: CompiledFunctionUnwindInfo::Windows(buffer[unwind_range].to_vec()), }) .collect(), ) diff --git a/crates/environ/src/cranelift.rs b/crates/environ/src/cranelift.rs index 790e6cf642..246838373f 100644 --- a/crates/environ/src/cranelift.rs +++ b/crates/environ/src/cranelift.rs @@ -3,7 +3,8 @@ use crate::address_map::{FunctionAddressMap, InstructionAddressMap}; use crate::cache::{ModuleCacheData, ModuleCacheDataTupleType, ModuleCacheEntry}; use crate::compilation::{ - Compilation, CompileError, CompiledFunction, Relocation, RelocationTarget, TrapInformation, + Compilation, CompileError, CompiledFunction, CompiledFunctionUnwindInfo, Relocation, + RelocationTarget, TrapInformation, }; use crate::func_environ::{ get_func_name, get_imported_memory32_grow_name, get_imported_memory32_size_name, @@ -204,6 +205,7 @@ impl crate::compilation::Compiler for Cranelift { context.func.name = get_func_name(func_index); context.func.signature = module.signatures[module.functions[func_index]].clone(); + context.func.collect_frame_layout_info(); if generate_debug_info { context.func.collect_debug_info(); } @@ -217,7 +219,6 @@ impl crate::compilation::Compiler for Cranelift { )?; let mut code_buf: Vec = Vec::new(); - let mut unwind_info = Vec::new(); let mut reloc_sink = RelocSink::new(func_index); let mut trap_sink = TrapSink::new(); let mut stackmap_sink = binemit::NullStackmapSink {}; @@ -233,7 +234,7 @@ impl crate::compilation::Compiler for Cranelift { CompileError::Codegen(pretty_error(&context.func, Some(isa), error)) })?; - context.emit_unwind_info(isa, &mut unwind_info); + let unwind_info = CompiledFunctionUnwindInfo::new(isa, &context); let address_transform = if generate_debug_info { let body_len = code_buf.len(); diff --git a/crates/environ/src/func_environ.rs b/crates/environ/src/func_environ.rs index b218641877..c33a2d5c15 100644 --- a/crates/environ/src/func_environ.rs +++ b/crates/environ/src/func_environ.rs @@ -362,8 +362,8 @@ impl<'module_environment> TargetEnvironment for FuncEnvironment<'module_environm } impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'module_environment> { - fn is_wasm_parameter(&self, func: &ir::Function, index: usize) -> bool { - func.signature.params[index].purpose == ir::ArgumentPurpose::Normal + fn is_wasm_parameter(&self, signature: &ir::Signature, index: usize) -> bool { + signature.params[index].purpose == ir::ArgumentPurpose::Normal } fn make_table(&mut self, func: &mut ir::Function, index: TableIndex) -> WasmResult { diff --git a/crates/environ/src/lib.rs b/crates/environ/src/lib.rs index fdbea6c870..ec5525fec2 100644 --- a/crates/environ/src/lib.rs +++ b/crates/environ/src/lib.rs @@ -44,8 +44,9 @@ pub use crate::address_map::{ }; pub use crate::cache::{create_new_config as cache_create_new_config, init as cache_init}; pub use crate::compilation::{ - Compilation, CompileError, CompiledFunction, Compiler, Relocation, RelocationTarget, - Relocations, TrapInformation, Traps, + Compilation, CompileError, CompiledFunction, CompiledFunctionUnwindInfo, + CompiledFunctionUnwindInfoReloc, Compiler, Relocation, RelocationTarget, Relocations, + TrapInformation, Traps, }; pub use crate::cranelift::Cranelift; pub use crate::data_structures::*; diff --git a/crates/fuzzing/Cargo.toml b/crates/fuzzing/Cargo.toml index e6eefeafe7..81b1d471b3 100644 --- a/crates/fuzzing/Cargo.toml +++ b/crates/fuzzing/Cargo.toml @@ -10,7 +10,7 @@ version = "0.9.0" [dependencies] anyhow = "1.0.22" -arbitrary = "0.2.0" +arbitrary = { version = "0.3.2", features = ["derive"] } binaryen = "0.8.2" env_logger = { version = "0.7.1", optional = true } log = "0.4.8" diff --git a/crates/fuzzing/src/generators.rs b/crates/fuzzing/src/generators.rs index 1fb6a29c0f..62ed219aef 100644 --- a/crates/fuzzing/src/generators.rs +++ b/crates/fuzzing/src/generators.rs @@ -31,13 +31,17 @@ impl fmt::Debug for WasmOptTtf { } impl Arbitrary for WasmOptTtf { - fn arbitrary(input: &mut U) -> Result - where - U: Unstructured + ?Sized, - { + fn arbitrary(input: &mut Unstructured) -> arbitrary::Result { let seed: Vec = Arbitrary::arbitrary(input)?; let module = binaryen::tools::translate_to_fuzz_mvp(&seed); let wasm = module.write(); Ok(WasmOptTtf { wasm }) } + + fn arbitrary_take_rest(input: Unstructured) -> arbitrary::Result { + let seed: Vec = Arbitrary::arbitrary_take_rest(input)?; + let module = binaryen::tools::translate_to_fuzz_mvp(&seed); + let wasm = module.write(); + Ok(WasmOptTtf { wasm }) + } } diff --git a/crates/fuzzing/src/generators/api.rs b/crates/fuzzing/src/generators/api.rs index debae64f05..1e59fea39f 100644 --- a/crates/fuzzing/src/generators/api.rs +++ b/crates/fuzzing/src/generators/api.rs @@ -15,8 +15,9 @@ //! [swarm testing]: https://www.cs.utah.edu/~regehr/papers/swarm12.pdf use arbitrary::{Arbitrary, Unstructured}; -use std::collections::HashSet; +use std::collections::BTreeSet; +#[derive(Arbitrary, Debug)] struct Swarm { config_debug_info: bool, module_new: bool, @@ -26,24 +27,8 @@ struct Swarm { call_exported_func: bool, } -impl Arbitrary for Swarm { - fn arbitrary(input: &mut U) -> Result - where - U: Unstructured + ?Sized, - { - Ok(Swarm { - config_debug_info: bool::arbitrary(input)?, - module_new: bool::arbitrary(input)?, - module_drop: bool::arbitrary(input)?, - instance_new: bool::arbitrary(input)?, - instance_drop: bool::arbitrary(input)?, - call_exported_func: bool::arbitrary(input)?, - }) - } -} - /// A call to one of Wasmtime's public APIs. -#[derive(Clone, Debug)] +#[derive(Arbitrary, Clone, Debug)] #[allow(missing_docs)] pub enum ApiCall { ConfigNew, @@ -61,8 +46,8 @@ use ApiCall::*; #[derive(Default)] struct Scope { id_counter: usize, - modules: HashSet, - instances: HashSet, + modules: BTreeSet, + instances: BTreeSet, } impl Scope { @@ -81,10 +66,7 @@ pub struct ApiCalls { } impl Arbitrary for ApiCalls { - fn arbitrary(input: &mut U) -> Result - where - U: Unstructured + ?Sized, - { + fn arbitrary(input: &mut Unstructured) -> arbitrary::Result { let swarm = Swarm::arbitrary(input)?; let mut calls = vec![]; @@ -94,8 +76,8 @@ impl Arbitrary for ApiCalls { let mut scope = Scope::default(); - for _ in 0..input.container_size()? { - let mut choices: Vec Result> = vec![]; + for _ in 0..input.arbitrary_len::()? { + let mut choices: Vec arbitrary::Result> = vec![]; if swarm.module_new { choices.push(|input, scope| { @@ -108,7 +90,7 @@ impl Arbitrary for ApiCalls { if swarm.module_drop && !scope.modules.is_empty() { choices.push(|input, scope| { let modules: Vec<_> = scope.modules.iter().cloned().collect(); - let id = arbitrary_choice(input, &modules)?.cloned().unwrap(); + let id = *input.choose(&modules)?; scope.modules.remove(&id); Ok(ModuleDrop { id }) }); @@ -116,7 +98,7 @@ impl Arbitrary for ApiCalls { if swarm.instance_new && !scope.modules.is_empty() { choices.push(|input, scope| { let modules: Vec<_> = scope.modules.iter().cloned().collect(); - let module = arbitrary_choice(input, &modules)?.cloned().unwrap(); + let module = *input.choose(&modules)?; let id = scope.next_id(); scope.instances.insert(id); Ok(InstanceNew { id, module }) @@ -125,7 +107,7 @@ impl Arbitrary for ApiCalls { if swarm.instance_drop && !scope.instances.is_empty() { choices.push(|input, scope| { let instances: Vec<_> = scope.instances.iter().cloned().collect(); - let id = arbitrary_choice(input, &instances)?.cloned().unwrap(); + let id = *input.choose(&instances)?; scope.instances.remove(&id); Ok(InstanceDrop { id }) }); @@ -133,43 +115,28 @@ impl Arbitrary for ApiCalls { if swarm.call_exported_func && !scope.instances.is_empty() { choices.push(|input, scope| { let instances: Vec<_> = scope.instances.iter().cloned().collect(); - let instance = arbitrary_choice(input, &instances)?.cloned().unwrap(); + let instance = *input.choose(&instances)?; let nth = usize::arbitrary(input)?; Ok(CallExportedFunc { instance, nth }) }); } - if let Some(c) = arbitrary_choice(input, &choices)? { - calls.push(c(input, &mut scope)?); - } else { + if choices.is_empty() { break; } + let c = input.choose(&choices)?; + calls.push(c(input, &mut scope)?); } Ok(ApiCalls { calls }) } } -fn arbitrary_choice<'a, T, U>(input: &mut U, choices: &'a [T]) -> Result, U::Error> -where - U: Unstructured + ?Sized, -{ - if choices.is_empty() { - Ok(None) - } else { - let i = usize::arbitrary(input)? % choices.len(); - Ok(Some(&choices[i])) - } -} - -fn arbitrary_config( - input: &mut U, +fn arbitrary_config( + input: &mut Unstructured, swarm: &Swarm, calls: &mut Vec, -) -> Result<(), U::Error> -where - U: Unstructured + ?Sized, -{ +) -> arbitrary::Result<()> { calls.push(ConfigNew); if swarm.config_debug_info && bool::arbitrary(input)? { diff --git a/crates/jit/Cargo.toml b/crates/jit/Cargo.toml index 368b9eb4ad..27490f81e1 100644 --- a/crates/jit/Cargo.toml +++ b/crates/jit/Cargo.toml @@ -11,11 +11,11 @@ readme = "README.md" edition = "2018" [dependencies] -cranelift-codegen = { version = "0.54", features = ["enable-serde"] } -cranelift-entity = { version = "0.54", features = ["enable-serde"] } -cranelift-wasm = { version = "0.54", features = ["enable-serde"] } -cranelift-native = "0.54" -cranelift-frontend = "0.54" +cranelift-codegen = { version = "0.55", features = ["enable-serde"] } +cranelift-entity = { version = "0.55", features = ["enable-serde"] } +cranelift-wasm = { version = "0.55", features = ["enable-serde"] } +cranelift-native = "0.55" +cranelift-frontend = "0.55" wasmtime-environ = { path = "../environ", version = "0.9.0" } wasmtime-runtime = { path = "../runtime", version = "0.9.0" } wasmtime-debug = { path = "../debug", version = "0.9.0" } diff --git a/crates/jit/src/action.rs b/crates/jit/src/action.rs index d3668b0887..c836da8b26 100644 --- a/crates/jit/src/action.rs +++ b/crates/jit/src/action.rs @@ -6,7 +6,7 @@ use std::cmp::max; use std::{fmt, mem, ptr, slice}; use thiserror::Error; use wasmtime_environ::ir; -use wasmtime_runtime::{wasmtime_call_trampoline, Export, InstanceHandle, VMInvokeArgument}; +use wasmtime_runtime::{wasmtime_call_trampoline, Export, InstanceHandle, Trap, VMInvokeArgument}; /// A runtime value. #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -100,10 +100,7 @@ pub enum ActionOutcome { }, /// A trap occurred while the action was executing. - Trapped { - /// The trap message. - message: String, - }, + Trapped(Trap), } /// An error detected while invoking a wasm function or reading a wasm global. @@ -191,7 +188,7 @@ pub fn invoke( compiler.publish_compiled_code(); // Call the trampoline. - if let Err(message) = unsafe { + if let Err(trap) = unsafe { instance.with_signals_on(|| { wasmtime_call_trampoline( callee_vmctx, @@ -200,7 +197,7 @@ pub fn invoke( ) }) } { - return Ok(ActionOutcome::Trapped { message }); + return Ok(ActionOutcome::Trapped(trap)); } // Load the return values out of `values_vec`. diff --git a/crates/jit/src/code_memory.rs b/crates/jit/src/code_memory.rs index 49e6d5ee2d..2eb4806364 100644 --- a/crates/jit/src/code_memory.rs +++ b/crates/jit/src/code_memory.rs @@ -2,14 +2,45 @@ use crate::function_table::FunctionTable; use region; +use std::mem::ManuallyDrop; use std::{cmp, mem}; use wasmtime_environ::{Compilation, CompiledFunction}; use wasmtime_runtime::{Mmap, VMFunctionBody}; +struct CodeMemoryEntry { + mmap: ManuallyDrop, + table: ManuallyDrop, +} + +impl CodeMemoryEntry { + fn new() -> Self { + Self { + mmap: ManuallyDrop::new(Mmap::new()), + table: ManuallyDrop::new(FunctionTable::new()), + } + } + fn with_capacity(cap: usize) -> Result { + Ok(Self { + mmap: ManuallyDrop::new(Mmap::with_at_least(cap)?), + table: ManuallyDrop::new(FunctionTable::new()), + }) + } +} + +impl Drop for CodeMemoryEntry { + fn drop(&mut self) { + unsafe { + // Table needs to be freed before mmap. + ManuallyDrop::drop(&mut self.table); + ManuallyDrop::drop(&mut self.mmap); + } + } +} + /// Memory manager for executable code. pub struct CodeMemory { - current: (Mmap, FunctionTable), - mmaps: Vec<(Mmap, FunctionTable)>, + current: CodeMemoryEntry, + entries: Vec, position: usize, published: usize, } @@ -23,8 +54,8 @@ impl CodeMemory { /// Create a new `CodeMemory` instance. pub fn new() -> Self { Self { - current: (Mmap::new(), FunctionTable::new()), - mmaps: Vec::new(), + current: CodeMemoryEntry::new(), + entries: Vec::new(), position: 0, published: 0, } @@ -81,19 +112,20 @@ impl CodeMemory { self.push_current(0) .expect("failed to push current memory map"); - for (m, t) in &mut self.mmaps[self.published..] { + for CodeMemoryEntry { mmap: m, table: t } in &mut self.entries[self.published..] { + // Remove write access to the pages due to the relocation fixups. + t.publish(m.as_ptr() as u64) + .expect("failed to publish function table"); + if !m.is_empty() { unsafe { region::protect(m.as_mut_ptr(), m.len(), region::Protection::ReadExecute) } .expect("unable to make memory readonly and executable"); } - - t.publish(m.as_ptr() as u64) - .expect("failed to publish function table"); } - self.published = self.mmaps.len(); + self.published = self.entries.len(); } /// Allocate `size` bytes of memory which can be made executable later by @@ -103,7 +135,7 @@ impl CodeMemory { /// /// TODO: Add an alignment flag. fn allocate(&mut self, size: usize) -> Result<(&mut [u8], &mut FunctionTable), String> { - if self.current.0.len() - self.position < size { + if self.current.mmap.len() - self.position < size { self.push_current(cmp::max(0x10000, size))?; } @@ -111,8 +143,8 @@ impl CodeMemory { self.position += size; Ok(( - &mut self.current.0.as_mut_slice()[old_position..self.position], - &mut self.current.1, + &mut self.current.mmap.as_mut_slice()[old_position..self.position], + &mut self.current.table, )) } @@ -153,12 +185,19 @@ impl CodeMemory { // Keep unwind information 32-bit aligned (round up to the nearest 4 byte boundary) let padding = ((func.body.len() + 3) & !3) - func.body.len(); let (unwind, remainder) = remainder.split_at_mut(padding + func.unwind_info.len()); - unwind[padding..].copy_from_slice(&func.unwind_info); + let mut relocs = Vec::new(); + func.unwind_info + .serialize(&mut unwind[padding..], &mut relocs); let unwind_start = func_end + (padding as u32); let unwind_end = unwind_start + (func.unwind_info.len() as u32); - table.add_function(func_start, func_end, unwind_start); + relocs.iter_mut().for_each(move |r| { + r.offset += unwind_start; + r.addend += func_start; + }); + + table.add_function(func_start, func_end, unwind_start, &relocs); (unwind_end, remainder, table, vmfunc) } @@ -174,20 +213,17 @@ impl CodeMemory { fn push_current(&mut self, new_size: usize) -> Result<(), String> { let previous = mem::replace( &mut self.current, - ( - if new_size == 0 { - Mmap::new() - } else { - Mmap::with_at_least(cmp::max(0x10000, new_size))? - }, - FunctionTable::new(), - ), + if new_size == 0 { + CodeMemoryEntry::new() + } else { + CodeMemoryEntry::with_capacity(cmp::max(0x10000, new_size))? + }, ); - if !previous.0.is_empty() { - self.mmaps.push(previous); + if !previous.mmap.is_empty() { + self.entries.push(previous); } else { - assert_eq!(previous.1.len(), 0); + assert_eq!(previous.table.len(), 0); } self.position = 0; diff --git a/crates/jit/src/compiler.rs b/crates/jit/src/compiler.rs index cc4f5b343b..4197a28cb6 100644 --- a/crates/jit/src/compiler.rs +++ b/crates/jit/src/compiler.rs @@ -16,12 +16,12 @@ use wasmtime_environ::entity::{EntityRef, PrimaryMap}; use wasmtime_environ::isa::{TargetFrontendConfig, TargetIsa}; use wasmtime_environ::wasm::{DefinedFuncIndex, DefinedMemoryIndex}; use wasmtime_environ::{ - Compilation, CompileError, CompiledFunction, Compiler as _C, FunctionBodyData, Module, - ModuleVmctxInfo, Relocations, Traps, Tunables, VMOffsets, + Compilation, CompileError, CompiledFunction, CompiledFunctionUnwindInfo, Compiler as _C, + FunctionBodyData, Module, ModuleVmctxInfo, Relocations, Traps, Tunables, VMOffsets, }; use wasmtime_runtime::{ - get_mut_trap_registry, InstantiationError, SignatureRegistry, TrapRegistrationGuard, - VMFunctionBody, + get_mut_trap_registry, jit_function_registry, InstantiationError, SignatureRegistry, + TrapRegistrationGuard, VMFunctionBody, }; /// Select which kind of compilation to use. @@ -51,6 +51,7 @@ pub struct Compiler { code_memory: CodeMemory, trap_registration_guards: Vec, + jit_function_ranges: Vec<(usize, usize)>, trampoline_park: HashMap<*const VMFunctionBody, *const VMFunctionBody>, signatures: SignatureRegistry, strategy: CompilationStrategy, @@ -66,6 +67,7 @@ impl Compiler { isa, code_memory: CodeMemory::new(), trap_registration_guards: Vec::new(), + jit_function_ranges: Vec::new(), trampoline_park: HashMap::new(), signatures: SignatureRegistry::new(), fn_builder_ctx: FunctionBuilderContext::new(), @@ -85,6 +87,10 @@ impl Drop for Compiler { // Having a custom drop implementation we are independent from the field order // in the struct what reduces potential human error. self.trap_registration_guards.clear(); + + for (start, end) in self.jit_function_ranges.iter() { + jit_function_registry::unregister(*start, *end); + } } } @@ -155,6 +161,18 @@ impl Compiler { &mut self.trap_registration_guards, ); + for (i, allocated) in allocated_functions.iter() { + let ptr = (*allocated) as *const VMFunctionBody; + let body_len = compilation.get(i).body.len(); + self.jit_function_ranges + .push((ptr as usize, ptr as usize + body_len)); + let tag = jit_function_registry::JITFunctionTag { + module_id: module.name.clone(), + func_index: i.index(), + }; + jit_function_registry::register(ptr as usize, ptr as usize + body_len, tag); + } + let dbg = if let Some(debug_data) = debug_data { let target_config = self.isa.frontend_config(); let ofs = VMOffsets::new(target_config.pointer_bytes(), &module); @@ -215,6 +233,7 @@ impl Compiler { signature, value_size, )?; + entry.insert(body); body } @@ -266,6 +285,7 @@ fn make_trampoline( let mut context = Context::new(); context.func = ir::Function::with_name_signature(ir::ExternalName::user(0, 0), wrapper_sig); + context.func.collect_frame_layout_info(); { let mut builder = FunctionBuilder::new(&mut context.func, fn_builder_ctx); @@ -326,7 +346,6 @@ fn make_trampoline( } let mut code_buf = Vec::new(); - let mut unwind_info = Vec::new(); let mut reloc_sink = RelocSink {}; let mut trap_sink = binemit::NullTrapSink {}; let mut stackmap_sink = binemit::NullStackmapSink {}; @@ -346,7 +365,7 @@ fn make_trampoline( ))) })?; - context.emit_unwind_info(isa, &mut unwind_info); + let unwind_info = CompiledFunctionUnwindInfo::new(isa, &context); Ok(code_memory .allocate_for_function(&CompiledFunction { diff --git a/crates/jit/src/function_table.rs b/crates/jit/src/function_table.rs index 3e004e0e28..ffa37ce953 100644 --- a/crates/jit/src/function_table.rs +++ b/crates/jit/src/function_table.rs @@ -2,42 +2,7 @@ //! //! This module is primarily used to track JIT functions on Windows for stack walking and unwind. -/// Represents a runtime function table. -/// -/// The runtime function table is not implemented for non-Windows target platforms. -#[cfg(not(target_os = "windows"))] -pub(crate) struct FunctionTable; - -#[cfg(not(target_os = "windows"))] -impl FunctionTable { - /// Creates a new function table. - pub fn new() -> Self { - Self - } - - /// Returns the number of functions in the table, also referred to as its 'length'. - /// - /// For non-Windows platforms, the table will always be empty. - pub fn len(&self) -> usize { - 0 - } - - /// Adds a function to the table based off of the start offset, end offset, and unwind offset. - /// - /// The offsets are from the "module base", which is provided when the table is published. - /// - /// For non-Windows platforms, this is a no-op. - pub fn add_function(&mut self, _start: u32, _end: u32, _unwind: u32) {} - - /// Publishes the function table using the given base address. - /// - /// A published function table will automatically be deleted when it is dropped. - /// - /// For non-Windows platforms, this is a no-op. - pub fn publish(&mut self, _base_address: u64) -> Result<(), String> { - Ok(()) - } -} +type FunctionTableReloc = wasmtime_environ::CompiledFunctionUnwindInfoReloc; /// Represents a runtime function table. /// @@ -66,7 +31,14 @@ impl FunctionTable { /// Adds a function to the table based off of the start offset, end offset, and unwind offset. /// /// The offsets are from the "module base", which is provided when the table is published. - pub fn add_function(&mut self, start: u32, end: u32, unwind: u32) { + pub fn add_function( + &mut self, + start: u32, + end: u32, + unwind: u32, + _relocs: &[FunctionTableReloc], + ) { + assert_eq!(_relocs.len(), 0); use winapi::um::winnt; assert!(!self.published, "table has already been published"); @@ -133,3 +105,106 @@ impl Drop for FunctionTable { } } } + +/// Represents a runtime function table. +/// +/// This is used to register JIT code with the operating system to enable stack walking and unwinding. +#[cfg(any(target_os = "macos", target_os = "linux"))] +pub(crate) struct FunctionTable { + functions: Vec, + relocs: Vec, + published: Option>, +} + +#[cfg(any(target_os = "macos", target_os = "linux"))] +impl FunctionTable { + /// Creates a new function table. + pub fn new() -> Self { + Self { + functions: Vec::new(), + relocs: Vec::new(), + published: None, + } + } + + /// Returns the number of functions in the table, also referred to as its 'length'. + pub fn len(&self) -> usize { + self.functions.len() + } + + /// Adds a function to the table based off of the start offset, end offset, and unwind offset. + /// + /// The offsets are from the "module base", which is provided when the table is published. + pub fn add_function( + &mut self, + _start: u32, + _end: u32, + unwind: u32, + relocs: &[FunctionTableReloc], + ) { + assert!(self.published.is_none(), "table has already been published"); + self.functions.push(unwind); + self.relocs.extend_from_slice(relocs); + } + + /// Publishes the function table using the given base address. + /// + /// A published function table will automatically be deleted when it is dropped. + pub fn publish(&mut self, base_address: u64) -> Result<(), String> { + if self.published.is_some() { + return Err("function table was already published".into()); + } + + if self.functions.is_empty() { + assert_eq!(self.relocs.len(), 0); + self.published = Some(vec![]); + return Ok(()); + } + + extern "C" { + // libunwind import + fn __register_frame(fde: *const u8); + } + + for reloc in self.relocs.iter() { + let addr = base_address + (reloc.offset as u64); + let target = base_address + (reloc.addend as u64); + unsafe { + std::ptr::write(addr as *mut u64, target); + } + } + + let mut fdes = Vec::with_capacity(self.functions.len()); + for unwind_offset in self.functions.iter() { + let addr = base_address + (*unwind_offset as u64); + let off = unsafe { std::ptr::read::(addr as *const u32) } as usize + 4; + + let fde = (addr + off as u64) as usize; + unsafe { + __register_frame(fde as *const _); + } + fdes.push(fde); + } + + self.published = Some(fdes); + Ok(()) + } +} + +#[cfg(any(target_os = "macos", target_os = "linux"))] +impl Drop for FunctionTable { + fn drop(&mut self) { + extern "C" { + // libunwind import + fn __deregister_frame(fde: *const u8); + } + + if self.published.is_some() { + unsafe { + for fde in self.published.as_ref().unwrap() { + __deregister_frame(*fde as *const _); + } + } + } + } +} diff --git a/crates/lightbeam/Cargo.toml b/crates/lightbeam/Cargo.toml index 225a73c311..f3e140f070 100644 --- a/crates/lightbeam/Cargo.toml +++ b/crates/lightbeam/Cargo.toml @@ -19,7 +19,7 @@ memoffset = "0.5.3" itertools = "0.8.2" capstone = "0.6.0" thiserror = "1.0.9" -cranelift-codegen = "0.54" +cranelift-codegen = "0.55" multi_mut = "0.1" either = "1.5" typemap = "0.3" diff --git a/crates/misc/dotnet/Directory.Build.props b/crates/misc/dotnet/Directory.Build.props index b652414ca0..1f08f77261 100644 --- a/crates/misc/dotnet/Directory.Build.props +++ b/crates/misc/dotnet/Directory.Build.props @@ -1,6 +1,7 @@ - 0.8.0 + 0.8.0 wasmtime + wasmtime-c-api diff --git a/crates/misc/dotnet/Directory.Build.targets b/crates/misc/dotnet/Directory.Build.targets index fd577ba0c8..07c6116112 100644 --- a/crates/misc/dotnet/Directory.Build.targets +++ b/crates/misc/dotnet/Directory.Build.targets @@ -6,7 +6,7 @@ .so $(LibraryPrefix)$(WasmtimeLibraryName)$(LibraryExtension) $(MSBuildThisFileDirectory)../../../target/$(Configuration.ToLower()) - cargo build --release -p $(WasmtimeLibraryName) - cargo build -p $(WasmtimeLibraryName) + cargo build --release -p $(WasmtimePackageName) + cargo build -p $(WasmtimePackageName) diff --git a/crates/runtime/Cargo.toml b/crates/runtime/Cargo.toml index 21c62fcf42..d37f6cc9a5 100644 --- a/crates/runtime/Cargo.toml +++ b/crates/runtime/Cargo.toml @@ -20,6 +20,7 @@ indexmap = "1.0.2" thiserror = "1.0.4" more-asserts = "0.2.1" cfg-if = "0.1.9" +backtrace = "0.3.42" [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3.7", features = ["winbase", "memoryapi"] } diff --git a/crates/runtime/src/instance.rs b/crates/runtime/src/instance.rs index 4fe112281d..d6032faec5 100644 --- a/crates/runtime/src/instance.rs +++ b/crates/runtime/src/instance.rs @@ -9,7 +9,7 @@ use crate::memory::LinearMemory; use crate::mmap::Mmap; use crate::signalhandlers::{wasmtime_init_eager, wasmtime_init_finish}; use crate::table::Table; -use crate::traphandlers::wasmtime_call; +use crate::traphandlers::{wasmtime_call, Trap}; use crate::vmcontext::{ VMBuiltinFunctionsArray, VMCallerCheckedAnyfunc, VMContext, VMFunctionBody, VMFunctionImport, VMGlobalDefinition, VMGlobalImport, VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex, @@ -1398,6 +1398,6 @@ pub enum InstantiationError { Link(#[from] LinkError), /// A compilation error occured. - #[error("Trap occurred while invoking start function: {0}")] - StartTrap(String), + #[error("Trap occurred while invoking start function")] + StartTrap(#[source] Trap), } diff --git a/crates/runtime/src/jit_function_registry.rs b/crates/runtime/src/jit_function_registry.rs new file mode 100644 index 0000000000..60d6d73db0 --- /dev/null +++ b/crates/runtime/src/jit_function_registry.rs @@ -0,0 +1,83 @@ +#![allow(missing_docs)] + +use lazy_static::lazy_static; +use std::collections::BTreeMap; +use std::sync::{Arc, RwLock}; + +lazy_static! { + static ref REGISTRY: RwLock = RwLock::new(JITFunctionRegistry::default()); +} + +#[derive(Clone)] +pub struct JITFunctionTag { + pub module_id: Option, + pub func_index: usize, +} + +impl std::fmt::Debug for JITFunctionTag { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(ref module_id) = self.module_id { + write!(f, "{}", module_id)?; + } else { + write!(f, "(module)")?; + } + write!(f, ":{}", self.func_index) + } +} + +struct JITFunctionRegistry { + ranges: BTreeMap)>, +} + +impl Default for JITFunctionRegistry { + fn default() -> Self { + Self { + ranges: Default::default(), + } + } +} + +impl JITFunctionRegistry { + fn register(&mut self, fn_start: usize, fn_end: usize, tag: JITFunctionTag) { + self.ranges.insert(fn_end, (fn_start, Arc::new(tag))); + } + + fn unregister(&mut self, fn_end: usize) { + self.ranges.remove(&fn_end); + } + + fn find(&self, pc: usize) -> Option<&Arc> { + self.ranges + .range(pc..) + .next() + .and_then(|(end, (start, s))| { + if *start <= pc && pc < *end { + Some(s) + } else { + None + } + }) + } +} + +pub fn register(fn_start: usize, fn_end: usize, tag: JITFunctionTag) { + REGISTRY + .write() + .expect("jit function registry lock got poisoned") + .register(fn_start, fn_end, tag); +} + +pub fn unregister(_fn_start: usize, fn_end: usize) { + REGISTRY + .write() + .expect("jit function registry lock got poisoned") + .unregister(fn_end); +} + +pub fn find(pc: usize) -> Option> { + REGISTRY + .read() + .expect("jit function registry lock got poisoned") + .find(pc) + .cloned() +} diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index 5a48273e12..6d5dac0ca7 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -34,6 +34,7 @@ mod trap_registry; mod traphandlers; mod vmcontext; +pub mod jit_function_registry; pub mod libcalls; pub use crate::export::Export; @@ -44,7 +45,7 @@ pub use crate::mmap::Mmap; pub use crate::sig_registry::SignatureRegistry; pub use crate::signalhandlers::{wasmtime_init_eager, wasmtime_init_finish}; pub use crate::trap_registry::{get_mut_trap_registry, get_trap_registry, TrapRegistrationGuard}; -pub use crate::traphandlers::{wasmtime_call, wasmtime_call_trampoline}; +pub use crate::traphandlers::{wasmtime_call, wasmtime_call_trampoline, Trap}; pub use crate::vmcontext::{ VMCallerCheckedAnyfunc, VMContext, VMFunctionBody, VMFunctionImport, VMGlobalDefinition, VMGlobalImport, VMInvokeArgument, VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex, diff --git a/crates/runtime/src/traphandlers.rs b/crates/runtime/src/traphandlers.rs index a2166f20b7..a055ebc722 100644 --- a/crates/runtime/src/traphandlers.rs +++ b/crates/runtime/src/traphandlers.rs @@ -4,7 +4,9 @@ use crate::trap_registry::get_trap_registry; use crate::trap_registry::TrapDescription; use crate::vmcontext::{VMContext, VMFunctionBody}; +use backtrace::Backtrace; use std::cell::Cell; +use std::fmt; use std::ptr; use wasmtime_environ::ir; @@ -18,7 +20,7 @@ extern "C" { } thread_local! { - static RECORDED_TRAP: Cell> = Cell::new(None); + static RECORDED_TRAP: Cell> = Cell::new(None); static JMP_BUF: Cell<*const u8> = Cell::new(ptr::null()); static RESET_GUARD_PAGE: Cell = Cell::new(false); } @@ -43,24 +45,26 @@ pub extern "C" fn CheckIfTrapAtAddress(_pc: *const u8) -> i8 { pub extern "C" fn RecordTrap(pc: *const u8, reset_guard_page: bool) { // TODO: please see explanation in CheckIfTrapAtAddress. let registry = get_trap_registry(); - let trap_desc = registry - .get_trap(pc as usize) - .unwrap_or_else(|| TrapDescription { - source_loc: ir::SourceLoc::default(), - trap_code: ir::TrapCode::StackOverflow, - }); + let trap = Trap { + desc: registry + .get_trap(pc as usize) + .unwrap_or_else(|| TrapDescription { + source_loc: ir::SourceLoc::default(), + trap_code: ir::TrapCode::StackOverflow, + }), + backtrace: Backtrace::new_unresolved(), + }; if reset_guard_page { RESET_GUARD_PAGE.with(|v| v.set(true)); } RECORDED_TRAP.with(|data| { - assert_eq!( - data.get(), - None, + let prev = data.replace(Some(trap)); + assert!( + prev.is_none(), "Only one trap per thread can be recorded at a moment!" ); - data.set(Some(trap_desc)) }); } @@ -108,16 +112,33 @@ fn reset_guard_page() { #[cfg(not(target_os = "windows"))] fn reset_guard_page() {} -fn trap_message() -> String { - let trap_desc = RECORDED_TRAP - .with(|data| data.replace(None)) - .expect("trap_message must be called after trap occurred"); +/// Stores trace message with backtrace. +#[derive(Debug)] +pub struct Trap { + /// What sort of trap happened, as well as where in the original wasm module + /// it happened. + pub desc: TrapDescription, + /// Native stack backtrace at the time the trap occurred + pub backtrace: Backtrace, +} - format!( - "wasm trap: {}, source location: {}", - trap_code_to_expected_string(trap_desc.trap_code), - trap_desc.source_loc, - ) +impl fmt::Display for Trap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "wasm trap: {}, source location: {}", + trap_code_to_expected_string(self.desc.trap_code), + self.desc.source_loc + ) + } +} + +impl std::error::Error for Trap {} + +fn last_trap() -> Trap { + RECORDED_TRAP + .with(|data| data.replace(None)) + .expect("trap_message must be called after trap occurred") } fn trap_code_to_expected_string(trap_code: ir::TrapCode) -> String { @@ -146,9 +167,9 @@ pub unsafe extern "C" fn wasmtime_call_trampoline( vmctx: *mut VMContext, callee: *const VMFunctionBody, values_vec: *mut u8, -) -> Result<(), String> { +) -> Result<(), Trap> { if WasmtimeCallTrampoline(vmctx as *mut u8, callee, values_vec) == 0 { - Err(trap_message()) + Err(last_trap()) } else { Ok(()) } @@ -160,9 +181,9 @@ pub unsafe extern "C" fn wasmtime_call_trampoline( pub unsafe extern "C" fn wasmtime_call( vmctx: *mut VMContext, callee: *const VMFunctionBody, -) -> Result<(), String> { +) -> Result<(), Trap> { if WasmtimeCall(vmctx as *mut u8, callee) == 0 { - Err(trap_message()) + Err(last_trap()) } else { Ok(()) } diff --git a/crates/wasi-c/Cargo.toml b/crates/wasi-c/Cargo.toml index d169491a89..c52e5244db 100644 --- a/crates/wasi-c/Cargo.toml +++ b/crates/wasi-c/Cargo.toml @@ -14,9 +14,9 @@ edition = "2018" wasmtime-runtime = { path = "../runtime", version = "0.9.0" } wasmtime-environ = { path = "../environ", version = "0.9.0" } wasmtime-jit = { path = "../jit", version = "0.9.0" } -cranelift-codegen = { version = "0.54", features = ["enable-serde"] } -cranelift-entity = { version = "0.54", features = ["enable-serde"] } -cranelift-wasm = { version = "0.54", features = ["enable-serde"] } +cranelift-codegen = { version = "0.55", features = ["enable-serde"] } +cranelift-entity = { version = "0.55", features = ["enable-serde"] } +cranelift-wasm = { version = "0.55", features = ["enable-serde"] } target-lexicon = "0.10.0" log = { version = "0.4.8", default-features = false } libc = "0.2.60" diff --git a/crates/wasi-common/src/sandboxed_tty_writer.rs b/crates/wasi-common/src/sandboxed_tty_writer.rs index 2f927d0d2f..3152926157 100644 --- a/crates/wasi-common/src/sandboxed_tty_writer.rs +++ b/crates/wasi-common/src/sandboxed_tty_writer.rs @@ -1,4 +1,4 @@ -use std::io::{Result, Write}; +use std::io::{IoSlice, Result, Write}; /// An adapter around a `Write` stream that guarantees that its output /// is valid UTF-8 and contains no control characters. It does this by @@ -124,6 +124,27 @@ where return Ok(result); } + fn write_vectored(&mut self, bufs: &[IoSlice]) -> Result { + // Terminal output is [not expected to be atomic], so just write all the + // individual buffers in sequence. + // + // [not expected to be atomic]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html#tag_16_474_08 + let mut total_written = 0; + + for buf in bufs { + let written = self.write(buf)?; + + total_written += written; + + // Stop at the first point where the OS writes less than we asked. + if written < buf.len() { + break; + } + } + + Ok(total_written) + } + fn flush(&mut self) -> Result<()> { self.inner.flush() } diff --git a/crates/wasi/Cargo.toml b/crates/wasi/Cargo.toml index 4aaca18c0b..1ea8761523 100644 --- a/crates/wasi/Cargo.toml +++ b/crates/wasi/Cargo.toml @@ -16,9 +16,9 @@ wasmtime-runtime = { path = "../runtime", version = "0.9.0" } wasmtime-environ = { path = "../environ", version = "0.9.0" } wasmtime-jit = { path = "../jit", version = "0.9.0" } wasi-common = { path = "../wasi-common", version = "0.9.0" } -cranelift-codegen = { version = "0.54", features = ["enable-serde"] } -cranelift-entity = { version = "0.54", features = ["enable-serde"] } -cranelift-wasm = { version = "0.54", features = ["enable-serde"] } +cranelift-codegen = { version = "0.55", features = ["enable-serde"] } +cranelift-entity = { version = "0.55", features = ["enable-serde"] } +cranelift-wasm = { version = "0.55", features = ["enable-serde"] } target-lexicon = "0.10.0" log = { version = "0.4.8", default-features = false } wig = { path = "../wasi-common/wig", version = "0.9.2" } diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 996c53b2f3..50dfe42fae 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -15,7 +15,7 @@ log = "0.4.8" wasmtime-fuzzing = { path = "../crates/fuzzing", features = ["env_logger"] } wasmtime-jit = { path = "../crates/jit" } wasmtime = { path = "../crates/api" } -libfuzzer-sys = { git = "https://github.com/rust-fuzz/libfuzzer-sys.git" } +libfuzzer-sys = "0.2.0" [[bin]] name = "compile" diff --git a/scripts/cranelift-version.sh b/scripts/cranelift-version.sh index 7af91d26cd..8bcba364fa 100755 --- a/scripts/cranelift-version.sh +++ b/scripts/cranelift-version.sh @@ -9,7 +9,7 @@ topdir=$(dirname "$0")/.. cd "$topdir" # All the cranelift-* crates have the same version number -version="0.53" +version="0.55" # Update all of the Cargo.toml files. echo "Updating crate versions to $version" diff --git a/src/commands/run.rs b/src/commands/run.rs index 4426245c71..313e837017 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -17,8 +17,7 @@ use wasmtime_interface_types::ModuleData; use wasmtime_wasi::{ create_wasi_instance, old::snapshot_0::create_wasi_instance as create_wasi_instance_snapshot_0, }; -#[cfg(feature = "wasi-c")] -use wasmtime_wasi_c::instantiate_wasi_c; + #[cfg(feature = "wasi-c")] use wasmtime_wasi_c::instantiate_wasi_c; @@ -152,7 +151,8 @@ impl RunCommand { #[cfg(feature = "wasi-c")] { let global_exports = store.global_exports().clone(); - let handle = instantiate_wasi_c(global_exports, &preopen_dirs, &argv, &self.vars)?; + let handle = + instantiate_wasi_c("", global_exports, &preopen_dirs, &argv, &self.vars)?; Instance::from_handle(&store, handle) } #[cfg(not(feature = "wasi-c"))] diff --git a/tests/custom_signal_handler.rs b/tests/custom_signal_handler.rs index 40f9080998..3bbb590424 100644 --- a/tests/custom_signal_handler.rs +++ b/tests/custom_signal_handler.rs @@ -128,9 +128,12 @@ mod tests { { println!("calling read_out_of_bounds..."); let trap = invoke_export(&instance, "read_out_of_bounds").unwrap_err(); - assert!(trap - .message() - .starts_with("call error: wasm trap: out of bounds memory access")); + assert!( + trap.message() + .starts_with("wasm trap: out of bounds memory access"), + "bad trap message: {:?}", + trap.message() + ); } // these invoke wasmtime_call_trampoline from callable.rs @@ -151,7 +154,7 @@ mod tests { let trap = read_out_of_bounds_func.call(&[]).unwrap_err(); assert!(trap .message() - .starts_with("call error: wasm trap: out of bounds memory access")); + .starts_with("wasm trap: out of bounds memory access")); } Ok(()) }