mirror of
https://github.com/fluencelabs/wasmer
synced 2024-12-13 06:15:33 +00:00
Merge #489
489: Su Engine: Decoding and encoding runtime state. r=losfair a=losfair This PR implements a managed runtime that is able to suspend a running WebAssembly program at arbitrary point in time, decode its machine state (registers and stack) into the corresponding WebAssembly abstract state (call frames, locals and the value stack), and encode the abstract state back into machine state for resuming execution later. Features enabled by this PR include: - Tier (compiler backend) switching at runtime. - Debugging with backtraces and local variables. - Suspending to disk. - Live migration. The name "Su" corresponds to 「溯」 in Chinese, originating from 「溯洄从之」 in The Book of Songs. Co-authored-by: losfair <zhy20000919@hotmail.com>
This commit is contained in:
commit
d26cee8bba
82
Cargo.lock
generated
82
Cargo.lock
generated
@ -16,6 +16,11 @@ dependencies = [
|
||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "approx"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.5"
|
||||
@ -65,6 +70,16 @@ dependencies = [
|
||||
"libc 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.46.0"
|
||||
@ -163,6 +178,16 @@ name = "cfg-if"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cgmath"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "0.26.4"
|
||||
@ -203,6 +228,15 @@ dependencies = [
|
||||
"cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winconsole 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.1.3"
|
||||
@ -706,6 +740,14 @@ dependencies = [
|
||||
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.8"
|
||||
@ -814,6 +856,18 @@ dependencies = [
|
||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.57 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.6.5"
|
||||
@ -999,6 +1053,11 @@ dependencies = [
|
||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rgb"
|
||||
version = "0.8.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.15"
|
||||
@ -1496,7 +1555,10 @@ dependencies = [
|
||||
name = "wasmer-runtime-core"
|
||||
version = "0.5.3"
|
||||
dependencies = [
|
||||
"bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"blake2b_simd 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"field-offset 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1523,6 +1585,7 @@ name = "wasmer-singlepass-backend"
|
||||
version = "0.5.3"
|
||||
dependencies = [
|
||||
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dynasm 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dynasmrt 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -1635,15 +1698,28 @@ dependencies = [
|
||||
"winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winconsole"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cgmath 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rgb 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[metadata]
|
||||
"checksum aho-corasick 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e6f484ae0c99fec2e858eb6134949117399f222608d84cadb3f58c1f97c2364c"
|
||||
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
"checksum approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08abcc3b4e9339e33a3d0a5ed15d84a687350c05689d825e0f6655eef9e76a94"
|
||||
"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"
|
||||
"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71"
|
||||
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
|
||||
"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf"
|
||||
"checksum backtrace 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)" = "1a13fc43f04daf08ab4f71e3d27e1fc27fc437d3e95ac0063a796d92fb40f39b"
|
||||
"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6"
|
||||
"checksum bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9f04a5e50dc80b3d5d35320889053637d15011aed5e66b66b37ae798c65da6f7"
|
||||
"checksum bindgen 0.46.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f7f7f0701772b17de73e4f5cbcb1dd6926f4706cba4c1ab62c5367f8bdc94e1"
|
||||
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
|
||||
"checksum blake2b_simd 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce2571a6cd634670daa2977cc894c1cc2ba57c563c498e5a82c35446f34d056e"
|
||||
@ -1655,10 +1731,12 @@ dependencies = [
|
||||
"checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d"
|
||||
"checksum cexpr 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7fa24eb00d5ffab90eaeaf1092ac85c04c64aaf358ea6f84505b8116d24c6af"
|
||||
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
|
||||
"checksum cgmath 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)" = "64a4b57c8f4e3a2e9ac07e0f6abc9c24b6fc9e1b54c3478cfb598f3d0023e51c"
|
||||
"checksum clang-sys 0.26.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6ef0c1bcf2e99c649104bd7a7012d8f8802684400e03db0ec0af48583c6fa0e4"
|
||||
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
|
||||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
"checksum cmake 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "2ca4386c8954b76a8415b63959337d940d724b336cabd3afe189c2b51a7e1ff0"
|
||||
"checksum colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6cdb90b60f2927f8d76139c72dbde7e10c3a2bc47c8594c9c7a66529f2687c03"
|
||||
"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
|
||||
"checksum cranelift-bforest 0.31.0 (git+https://github.com/wasmerio/cranelift.git?rev=2ada531d79b34a9e6c94c81f2615677e22d68780)" = "<none>"
|
||||
"checksum cranelift-codegen 0.31.0 (git+https://github.com/wasmerio/cranelift.git?rev=2ada531d79b34a9e6c94c81f2615677e22d68780)" = "<none>"
|
||||
@ -1715,6 +1793,7 @@ dependencies = [
|
||||
"checksum nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46f0f3210768d796e8fa79ec70ee6af172dacbe7147f5e69be5240a47778302b"
|
||||
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
|
||||
"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
|
||||
"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
|
||||
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
|
||||
"checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba"
|
||||
"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||
@ -1729,6 +1808,7 @@ dependencies = [
|
||||
"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
|
||||
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
|
||||
"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db"
|
||||
"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
|
||||
"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
|
||||
"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
|
||||
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||
@ -1749,6 +1829,7 @@ dependencies = [
|
||||
"checksum regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8f0a0bcab2fd7d1d7c54fa9eae6f43eddeb9ce2e7352f8518a814a4f65d60c58"
|
||||
"checksum regex-syntax 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dcfd8681eebe297b81d98498869d4aae052137651ad7b96822f09ceb690d0a96"
|
||||
"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5"
|
||||
"checksum rgb 0.8.13 (registry+https://github.com/rust-lang/crates.io-index)" = "4f089652ca87f5a82a62935ec6172a534066c7b97be003cc8f702ee9a7a59c92"
|
||||
"checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af"
|
||||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
"checksum ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "b96a9549dc8d48f2c283938303c4b5a77aa29bfbc5b54b084fb1630408899a8f"
|
||||
@ -1804,3 +1885,4 @@ dependencies = [
|
||||
"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9"
|
||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
"checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba"
|
||||
"checksum winconsole 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ef84b96d10db72dd980056666d7f1e7663ce93d82fa33b63e71c966f4cf5032"
|
||||
|
74
examples/iterative_hash/Cargo.lock
generated
Normal file
74
examples/iterative_hash/Cargo.lock
generated
Normal file
@ -0,0 +1,74 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "blake2"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byte-tools"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-mac"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iterative_hash"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum blake2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "91721a6330935673395a0607df4d49a9cb90ae12d259f1b3e0a3f6e1d486872e"
|
||||
"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
|
||||
"checksum crypto-mac 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5"
|
||||
"checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c"
|
||||
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
|
||||
"checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409"
|
||||
"checksum subtle 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
|
||||
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
|
12
examples/iterative_hash/Cargo.toml
Normal file
12
examples/iterative_hash/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "iterative_hash"
|
||||
version = "0.1.0"
|
||||
authors = ["losfair <zhy20000919@hotmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
blake2 = "0.8"
|
16
examples/iterative_hash/src/main.rs
Normal file
16
examples/iterative_hash/src/main.rs
Normal file
@ -0,0 +1,16 @@
|
||||
use blake2::{Blake2b, Digest};
|
||||
|
||||
fn main() {
|
||||
let mut data: Vec<u8> = b"test".to_vec();
|
||||
|
||||
for i in 0.. {
|
||||
let mut hasher = Blake2b::new();
|
||||
hasher.input(&data);
|
||||
let out = hasher.result();
|
||||
data = out.to_vec();
|
||||
|
||||
if i % 1000000 == 0 {
|
||||
println!("Round {}: {:?}", i, data);
|
||||
}
|
||||
}
|
||||
}
|
67
examples/trie_traversal/trie_traversal.rs
Normal file
67
examples/trie_traversal/trie_traversal.rs
Normal file
@ -0,0 +1,67 @@
|
||||
#[link(wasm_import_module = "wasmer_suspend")]
|
||||
extern "C" {
|
||||
fn suspend();
|
||||
}
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Default)]
|
||||
struct Node {
|
||||
count: usize,
|
||||
children: BTreeMap<char, Node>,
|
||||
}
|
||||
|
||||
impl Node {
|
||||
fn insert(&mut self, mut s: impl Iterator<Item = char>) {
|
||||
match s.next() {
|
||||
Some(x) => {
|
||||
self.children.entry(x).or_default().insert(s);
|
||||
}
|
||||
None => {
|
||||
self.count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn for_each_dyn(&self, cb: &dyn Fn(&str, usize), prefix: &mut String) {
|
||||
if self.count > 0 {
|
||||
cb(&prefix, self.count);
|
||||
}
|
||||
|
||||
for (k, v) in self.children.iter() {
|
||||
prefix.push(*k);
|
||||
v.for_each_dyn(cb, prefix);
|
||||
prefix.pop().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut root = Node::default();
|
||||
root.insert("Ava".chars());
|
||||
root.insert("Alexander".chars());
|
||||
root.insert("Aiden".chars());
|
||||
root.insert("Bella".chars());
|
||||
root.insert("Brianna".chars());
|
||||
root.insert("Brielle".chars());
|
||||
root.insert("Charlotte".chars());
|
||||
root.insert("Chloe".chars());
|
||||
root.insert("Camila".chars());
|
||||
|
||||
println!("Tree ready, suspending.");
|
||||
unsafe {
|
||||
suspend();
|
||||
}
|
||||
|
||||
root.for_each_dyn(
|
||||
&|seq, count| {
|
||||
println!("{}: {}", seq, count);
|
||||
unsafe {
|
||||
suspend();
|
||||
}
|
||||
},
|
||||
&mut "".into(),
|
||||
);
|
||||
|
||||
println!("[END]");
|
||||
}
|
@ -164,6 +164,7 @@ impl Intrinsics {
|
||||
let memory_base_ty = i8_ty;
|
||||
let memory_bound_ty = i8_ty;
|
||||
let internals_ty = i64_ty;
|
||||
let interrupt_signal_mem_ty = i8_ty;
|
||||
let local_function_ty = i8_ptr_ty;
|
||||
|
||||
let anyfunc_ty = context.struct_type(
|
||||
@ -222,6 +223,9 @@ impl Intrinsics {
|
||||
internals_ty
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.as_basic_type_enum(),
|
||||
interrupt_signal_mem_ty
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.as_basic_type_enum(),
|
||||
local_function_ty
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.as_basic_type_enum(),
|
||||
|
@ -17,6 +17,7 @@ impl FunctionMiddleware for CallTrace {
|
||||
Event::Internal(InternalEvent::FunctionBegin(id)) => sink.push(Event::Internal(
|
||||
InternalEvent::Breakpoint(Box::new(move |_| {
|
||||
eprintln!("func ({})", id);
|
||||
Ok(())
|
||||
})),
|
||||
)),
|
||||
_ => {}
|
||||
|
@ -94,11 +94,9 @@ impl FunctionMiddleware for Metering {
|
||||
sink.push(Event::WasmOwned(Operator::If {
|
||||
ty: WpTypeOrFuncType::Type(WpType::EmptyBlockType),
|
||||
}));
|
||||
sink.push(Event::Internal(InternalEvent::Breakpoint(Box::new(
|
||||
move |ctx| unsafe {
|
||||
(ctx.throw)(Box::new(ExecutionLimitExceededError));
|
||||
},
|
||||
))));
|
||||
sink.push(Event::Internal(InternalEvent::Breakpoint(Box::new(|_| {
|
||||
Err(Box::new(ExecutionLimitExceededError))
|
||||
}))));
|
||||
sink.push(Event::WasmOwned(Operator::End));
|
||||
}
|
||||
_ => {}
|
||||
|
@ -18,6 +18,8 @@ errno = "0.2.4"
|
||||
libc = "0.2.49"
|
||||
hex = "0.3.2"
|
||||
smallvec = "0.6.9"
|
||||
bincode = "1.1"
|
||||
colored = "1.8"
|
||||
|
||||
# Dependencies for caching.
|
||||
[dependencies.serde]
|
||||
@ -47,6 +49,7 @@ field-offset = "0.1.1"
|
||||
[build-dependencies]
|
||||
blake2b_simd = "0.4.1"
|
||||
rustc_version = "0.2.3"
|
||||
cc = "1.0"
|
||||
|
||||
[features]
|
||||
debug = []
|
||||
|
@ -28,4 +28,16 @@ fn main() {
|
||||
if rustc_version::version_meta().unwrap().channel == rustc_version::Channel::Nightly {
|
||||
println!("cargo:rustc-cfg=nightly");
|
||||
}
|
||||
|
||||
if cfg!(all(target_os = "linux", target_arch = "x86_64")) {
|
||||
cc::Build::new()
|
||||
.file("image-loading-linux-x86-64.s")
|
||||
.compile("image-loading");
|
||||
} else if cfg!(all(target_os = "macos", target_arch = "x86_64")) {
|
||||
cc::Build::new()
|
||||
.file("image-loading-macos-x86-64.s")
|
||||
.compile("image-loading");
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
|
69
lib/runtime-core/image-loading-linux-x86-64.s
Normal file
69
lib/runtime-core/image-loading-linux-x86-64.s
Normal file
@ -0,0 +1,69 @@
|
||||
# NOTE: Keep this consistent with `fault.rs`.
|
||||
|
||||
.globl run_on_alternative_stack
|
||||
run_on_alternative_stack:
|
||||
# (stack_end, stack_begin)
|
||||
# We need to ensure 16-byte alignment here.
|
||||
pushq %r15
|
||||
pushq %r14
|
||||
pushq %r13
|
||||
pushq %r12
|
||||
pushq %rbx
|
||||
pushq %rbp
|
||||
movq %rsp, -16(%rdi)
|
||||
|
||||
leaq run_on_alternative_stack.returning(%rip), %rax
|
||||
movq %rax, -24(%rdi)
|
||||
|
||||
movq %rsi, %rsp
|
||||
|
||||
movq (%rsp), %xmm0
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm1
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm2
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm3
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm4
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm5
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm6
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm7
|
||||
add $8, %rsp
|
||||
|
||||
popq %rbp
|
||||
popq %rax
|
||||
popq %rbx
|
||||
popq %rcx
|
||||
popq %rdx
|
||||
popq %rdi
|
||||
popq %rsi
|
||||
popq %r8
|
||||
popq %r9
|
||||
popq %r10
|
||||
popq %r11
|
||||
popq %r12
|
||||
popq %r13
|
||||
popq %r14
|
||||
popq %r15
|
||||
retq
|
||||
|
||||
run_on_alternative_stack.returning:
|
||||
movq (%rsp), %rsp
|
||||
popq %rbp
|
||||
popq %rbx
|
||||
popq %r12
|
||||
popq %r13
|
||||
popq %r14
|
||||
popq %r15
|
||||
retq
|
69
lib/runtime-core/image-loading-macos-x86-64.s
Normal file
69
lib/runtime-core/image-loading-macos-x86-64.s
Normal file
@ -0,0 +1,69 @@
|
||||
# NOTE: Keep this consistent with `fault.rs`.
|
||||
|
||||
.globl _run_on_alternative_stack
|
||||
_run_on_alternative_stack:
|
||||
# (stack_end, stack_begin)
|
||||
# We need to ensure 16-byte alignment here.
|
||||
pushq %r15
|
||||
pushq %r14
|
||||
pushq %r13
|
||||
pushq %r12
|
||||
pushq %rbx
|
||||
pushq %rbp
|
||||
movq %rsp, -16(%rdi)
|
||||
|
||||
leaq _run_on_alternative_stack.returning(%rip), %rax
|
||||
movq %rax, -24(%rdi)
|
||||
|
||||
movq %rsi, %rsp
|
||||
|
||||
movq (%rsp), %xmm0
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm1
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm2
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm3
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm4
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm5
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm6
|
||||
add $8, %rsp
|
||||
|
||||
movq (%rsp), %xmm7
|
||||
add $8, %rsp
|
||||
|
||||
popq %rbp
|
||||
popq %rax
|
||||
popq %rbx
|
||||
popq %rcx
|
||||
popq %rdx
|
||||
popq %rdi
|
||||
popq %rsi
|
||||
popq %r8
|
||||
popq %r9
|
||||
popq %r10
|
||||
popq %r11
|
||||
popq %r12
|
||||
popq %r13
|
||||
popq %r14
|
||||
popq %r15
|
||||
retq
|
||||
|
||||
_run_on_alternative_stack.returning:
|
||||
movq (%rsp), %rsp
|
||||
popq %rbp
|
||||
popq %rbx
|
||||
popq %r12
|
||||
popq %r13
|
||||
popq %r14
|
||||
popq %r15
|
||||
retq
|
@ -1,6 +1,7 @@
|
||||
use crate::{
|
||||
error::CompileResult,
|
||||
module::ModuleInner,
|
||||
state::ModuleStateMap,
|
||||
typed_func::Wasm,
|
||||
types::{LocalFuncIndex, SigIndex},
|
||||
vm,
|
||||
@ -8,6 +9,7 @@ use crate::{
|
||||
|
||||
use crate::{
|
||||
cache::{Artifact, Error as CacheError},
|
||||
codegen::BreakpointMap,
|
||||
module::ModuleInfo,
|
||||
sys::Memory,
|
||||
};
|
||||
@ -84,6 +86,14 @@ pub trait RunnableModule: Send + Sync {
|
||||
local_func_index: LocalFuncIndex,
|
||||
) -> Option<NonNull<vm::Func>>;
|
||||
|
||||
fn get_module_state_map(&self) -> Option<ModuleStateMap> {
|
||||
None
|
||||
}
|
||||
|
||||
fn get_breakpoints(&self) -> Option<BreakpointMap> {
|
||||
None
|
||||
}
|
||||
|
||||
/// A wasm trampoline contains the necessary data to dynamically call an exported wasm function.
|
||||
/// Given a particular signature index, we are returned a trampoline that is matched with that
|
||||
/// signature and an invoke function that can call the trampoline.
|
||||
|
@ -9,6 +9,7 @@ use crate::{
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::marker::PhantomData;
|
||||
@ -16,6 +17,10 @@ use std::sync::{Arc, RwLock};
|
||||
use wasmparser::{self, WasmDecoder};
|
||||
use wasmparser::{Operator, Type as WpType};
|
||||
|
||||
pub type BreakpointHandler =
|
||||
Box<Fn(BreakpointInfo) -> Result<(), Box<dyn Any>> + Send + Sync + 'static>;
|
||||
pub type BreakpointMap = Arc<HashMap<usize, BreakpointHandler>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Event<'a, 'b> {
|
||||
Internal(InternalEvent),
|
||||
@ -26,7 +31,7 @@ pub enum Event<'a, 'b> {
|
||||
pub enum InternalEvent {
|
||||
FunctionBegin(u32),
|
||||
FunctionEnd,
|
||||
Breakpoint(Box<Fn(BkptInfo) + Send + Sync + 'static>),
|
||||
Breakpoint(BreakpointHandler),
|
||||
SetInternal(u32),
|
||||
GetInternal(u32),
|
||||
}
|
||||
@ -43,8 +48,8 @@ impl fmt::Debug for InternalEvent {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BkptInfo {
|
||||
pub throw: unsafe fn(Box<dyn Any>) -> !,
|
||||
pub struct BreakpointInfo<'a> {
|
||||
pub fault: Option<&'a dyn Any>,
|
||||
}
|
||||
|
||||
pub trait ModuleCodeGenerator<FCG: FunctionCodeGenerator<E>, RM: RunnableModule, E: Debug> {
|
||||
|
464
lib/runtime-core/src/fault.rs
Normal file
464
lib/runtime-core/src/fault.rs
Normal file
@ -0,0 +1,464 @@
|
||||
mod raw {
|
||||
use std::ffi::c_void;
|
||||
|
||||
extern "C" {
|
||||
pub fn run_on_alternative_stack(stack_end: *mut u64, stack_begin: *mut u64) -> u64;
|
||||
pub fn setjmp(env: *mut c_void) -> i32;
|
||||
pub fn longjmp(env: *mut c_void, val: i32) -> !;
|
||||
}
|
||||
}
|
||||
|
||||
use crate::codegen::{BreakpointInfo, BreakpointMap};
|
||||
use crate::state::x64::{build_instance_image, read_stack, X64Register, GPR, XMM};
|
||||
use crate::vm;
|
||||
use libc::{mmap, mprotect, siginfo_t, MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE};
|
||||
use nix::sys::signal::{
|
||||
sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGINT,
|
||||
SIGSEGV, SIGTRAP,
|
||||
};
|
||||
use std::any::Any;
|
||||
use std::cell::UnsafeCell;
|
||||
use std::ffi::c_void;
|
||||
use std::process;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Once;
|
||||
|
||||
pub(crate) unsafe fn run_on_alternative_stack(stack_end: *mut u64, stack_begin: *mut u64) -> u64 {
|
||||
raw::run_on_alternative_stack(stack_end, stack_begin)
|
||||
}
|
||||
|
||||
const TRAP_STACK_SIZE: usize = 1048576; // 1MB
|
||||
|
||||
const SETJMP_BUFFER_LEN: usize = 27;
|
||||
type SetJmpBuffer = [i32; SETJMP_BUFFER_LEN];
|
||||
|
||||
struct UnwindInfo {
|
||||
jmpbuf: SetJmpBuffer, // in
|
||||
breakpoints: Option<BreakpointMap>,
|
||||
payload: Option<Box<Any>>, // out
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static UNWIND: UnsafeCell<Option<UnwindInfo>> = UnsafeCell::new(None);
|
||||
}
|
||||
|
||||
struct InterruptSignalMem(*mut u8);
|
||||
unsafe impl Send for InterruptSignalMem {}
|
||||
unsafe impl Sync for InterruptSignalMem {}
|
||||
|
||||
const INTERRUPT_SIGNAL_MEM_SIZE: usize = 4096;
|
||||
|
||||
lazy_static! {
|
||||
static ref INTERRUPT_SIGNAL_MEM: InterruptSignalMem = {
|
||||
let ptr = unsafe {
|
||||
mmap(
|
||||
::std::ptr::null_mut(),
|
||||
INTERRUPT_SIGNAL_MEM_SIZE,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANON,
|
||||
-1,
|
||||
0,
|
||||
)
|
||||
};
|
||||
if ptr as isize == -1 {
|
||||
panic!("cannot allocate code memory");
|
||||
}
|
||||
InterruptSignalMem(ptr as _)
|
||||
};
|
||||
}
|
||||
static INTERRUPT_SIGNAL_DELIVERED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
pub unsafe fn get_wasm_interrupt_signal_mem() -> *mut u8 {
|
||||
INTERRUPT_SIGNAL_MEM.0
|
||||
}
|
||||
|
||||
pub unsafe fn set_wasm_interrupt() {
|
||||
let mem: *mut u8 = INTERRUPT_SIGNAL_MEM.0;
|
||||
if mprotect(mem as _, INTERRUPT_SIGNAL_MEM_SIZE, PROT_NONE) < 0 {
|
||||
panic!("cannot set PROT_NONE on signal mem");
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn clear_wasm_interrupt() {
|
||||
let mem: *mut u8 = INTERRUPT_SIGNAL_MEM.0;
|
||||
if mprotect(mem as _, INTERRUPT_SIGNAL_MEM_SIZE, PROT_READ | PROT_WRITE) < 0 {
|
||||
panic!("cannot set PROT_READ | PROT_WRITE on signal mem");
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn catch_unsafe_unwind<R, F: FnOnce() -> R>(
|
||||
f: F,
|
||||
breakpoints: Option<BreakpointMap>,
|
||||
) -> Result<R, Box<Any>> {
|
||||
let unwind = UNWIND.with(|x| x.get());
|
||||
let old = (*unwind).take();
|
||||
*unwind = Some(UnwindInfo {
|
||||
jmpbuf: [0; SETJMP_BUFFER_LEN],
|
||||
breakpoints: breakpoints,
|
||||
payload: None,
|
||||
});
|
||||
|
||||
if raw::setjmp(&mut (*unwind).as_mut().unwrap().jmpbuf as *mut SetJmpBuffer as *mut _) != 0 {
|
||||
// error
|
||||
let ret = (*unwind).as_mut().unwrap().payload.take().unwrap();
|
||||
*unwind = old;
|
||||
Err(ret)
|
||||
} else {
|
||||
let ret = f();
|
||||
// implicit control flow to the error case...
|
||||
*unwind = old;
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn begin_unsafe_unwind(e: Box<Any>) -> ! {
|
||||
let unwind = UNWIND.with(|x| x.get());
|
||||
let inner = (*unwind)
|
||||
.as_mut()
|
||||
.expect("not within a catch_unsafe_unwind scope");
|
||||
inner.payload = Some(e);
|
||||
raw::longjmp(&mut inner.jmpbuf as *mut SetJmpBuffer as *mut _, 0xffff);
|
||||
}
|
||||
|
||||
unsafe fn with_breakpoint_map<R, F: FnOnce(Option<&BreakpointMap>) -> R>(f: F) -> R {
|
||||
let unwind = UNWIND.with(|x| x.get());
|
||||
let inner = (*unwind)
|
||||
.as_mut()
|
||||
.expect("not within a catch_unsafe_unwind scope");
|
||||
f(inner.breakpoints.as_ref())
|
||||
}
|
||||
|
||||
pub fn allocate_and_run<R, F: FnOnce() -> R>(size: usize, f: F) -> R {
|
||||
struct Context<F: FnOnce() -> R, R> {
|
||||
f: Option<F>,
|
||||
ret: Option<R>,
|
||||
}
|
||||
|
||||
extern "C" fn invoke<F: FnOnce() -> R, R>(ctx: &mut Context<F, R>) {
|
||||
let f = ctx.f.take().unwrap();
|
||||
ctx.ret = Some(f());
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let mut ctx = Context {
|
||||
f: Some(f),
|
||||
ret: None,
|
||||
};
|
||||
assert!(size % 16 == 0);
|
||||
assert!(size >= 4096);
|
||||
|
||||
let mut stack: Vec<u64> = vec![0; size / 8];
|
||||
let end_offset = stack.len();
|
||||
|
||||
stack[end_offset - 4] = invoke::<F, R> as usize as u64;
|
||||
|
||||
// NOTE: Keep this consistent with `image-loading-*.s`.
|
||||
stack[end_offset - 4 - 10] = &mut ctx as *mut Context<F, R> as usize as u64; // rdi
|
||||
const NUM_SAVED_REGISTERS: usize = 23;
|
||||
let stack_begin = stack
|
||||
.as_mut_ptr()
|
||||
.offset((end_offset - 4 - NUM_SAVED_REGISTERS) as isize);
|
||||
let stack_end = stack.as_mut_ptr().offset(end_offset as isize);
|
||||
|
||||
raw::run_on_alternative_stack(stack_end, stack_begin);
|
||||
ctx.ret.take().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn signal_trap_handler(
|
||||
signum: ::nix::libc::c_int,
|
||||
siginfo: *mut siginfo_t,
|
||||
ucontext: *mut c_void,
|
||||
) {
|
||||
unsafe {
|
||||
let fault = get_fault_info(siginfo as _, ucontext);
|
||||
|
||||
let mut unwind_result: Box<dyn Any> = Box::new(());
|
||||
|
||||
let should_unwind = allocate_and_run(TRAP_STACK_SIZE, || {
|
||||
let mut is_suspend_signal = false;
|
||||
|
||||
match Signal::from_c_int(signum) {
|
||||
Ok(SIGTRAP) => {
|
||||
// breakpoint
|
||||
let out: Option<Result<(), Box<dyn Any>>> = with_breakpoint_map(|bkpt_map| {
|
||||
bkpt_map.and_then(|x| x.get(&(fault.ip as usize))).map(|x| {
|
||||
x(BreakpointInfo {
|
||||
fault: Some(&fault),
|
||||
})
|
||||
})
|
||||
});
|
||||
match out {
|
||||
Some(Ok(())) => {
|
||||
return false;
|
||||
}
|
||||
Some(Err(e)) => {
|
||||
unwind_result = e;
|
||||
return true;
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
Ok(SIGSEGV) | Ok(SIGBUS) => {
|
||||
if fault.faulting_addr as usize == get_wasm_interrupt_signal_mem() as usize {
|
||||
is_suspend_signal = true;
|
||||
clear_wasm_interrupt();
|
||||
INTERRUPT_SIGNAL_DELIVERED.store(false, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// TODO: make this safer
|
||||
let ctx = &mut *(fault.known_registers[X64Register::GPR(GPR::R15).to_index().0].unwrap()
|
||||
as *mut vm::Ctx);
|
||||
let rsp = fault.known_registers[X64Register::GPR(GPR::RSP).to_index().0].unwrap();
|
||||
|
||||
let msm = (*ctx.module)
|
||||
.runnable_module
|
||||
.get_module_state_map()
|
||||
.unwrap();
|
||||
let code_base = (*ctx.module).runnable_module.get_code().unwrap().as_ptr() as usize;
|
||||
let es_image = read_stack(
|
||||
&msm,
|
||||
code_base,
|
||||
rsp as usize as *const u64,
|
||||
fault.known_registers,
|
||||
Some(fault.ip as usize as u64),
|
||||
);
|
||||
|
||||
if is_suspend_signal {
|
||||
let image = build_instance_image(ctx, es_image);
|
||||
unwind_result = Box::new(image);
|
||||
} else {
|
||||
use colored::*;
|
||||
if es_image.frames.len() > 0 {
|
||||
eprintln!(
|
||||
"\n{}",
|
||||
"Wasmer encountered an error while running your WebAssembly program."
|
||||
.bold()
|
||||
.red()
|
||||
);
|
||||
es_image.print_backtrace_if_needed();
|
||||
}
|
||||
// Just let the error propagate otherrwise
|
||||
}
|
||||
|
||||
true
|
||||
});
|
||||
|
||||
if should_unwind {
|
||||
begin_unsafe_unwind(unwind_result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn sigint_handler(
|
||||
_signum: ::nix::libc::c_int,
|
||||
_siginfo: *mut siginfo_t,
|
||||
_ucontext: *mut c_void,
|
||||
) {
|
||||
if INTERRUPT_SIGNAL_DELIVERED.swap(true, Ordering::SeqCst) {
|
||||
eprintln!("Got another SIGINT before trap is triggered on WebAssembly side, aborting");
|
||||
process::abort();
|
||||
}
|
||||
unsafe {
|
||||
set_wasm_interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ensure_sighandler() {
|
||||
INSTALL_SIGHANDLER.call_once(|| unsafe {
|
||||
install_sighandler();
|
||||
});
|
||||
}
|
||||
|
||||
static INSTALL_SIGHANDLER: Once = Once::new();
|
||||
|
||||
unsafe fn install_sighandler() {
|
||||
let sa_trap = SigAction::new(
|
||||
SigHandler::SigAction(signal_trap_handler),
|
||||
SaFlags::SA_ONSTACK,
|
||||
SigSet::empty(),
|
||||
);
|
||||
sigaction(SIGFPE, &sa_trap).unwrap();
|
||||
sigaction(SIGILL, &sa_trap).unwrap();
|
||||
sigaction(SIGSEGV, &sa_trap).unwrap();
|
||||
sigaction(SIGBUS, &sa_trap).unwrap();
|
||||
sigaction(SIGTRAP, &sa_trap).unwrap();
|
||||
|
||||
let sa_interrupt = SigAction::new(
|
||||
SigHandler::SigAction(sigint_handler),
|
||||
SaFlags::SA_ONSTACK,
|
||||
SigSet::empty(),
|
||||
);
|
||||
sigaction(SIGINT, &sa_interrupt).unwrap();
|
||||
}
|
||||
|
||||
pub struct FaultInfo {
|
||||
pub faulting_addr: *const c_void,
|
||||
pub ip: *const c_void,
|
||||
pub known_registers: [Option<u64>; 24],
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
||||
pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *const c_void) -> FaultInfo {
|
||||
use libc::{
|
||||
_libc_xmmreg, ucontext_t, REG_R10, REG_R11, REG_R12, REG_R13, REG_R14, REG_R15, REG_R8,
|
||||
REG_R9, REG_RAX, REG_RBP, REG_RBX, REG_RCX, REG_RDI, REG_RDX, REG_RIP, REG_RSI, REG_RSP,
|
||||
};
|
||||
|
||||
fn read_xmm(reg: &_libc_xmmreg) -> u64 {
|
||||
(reg.element[0] as u64) | ((reg.element[1] as u64) << 32)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(C)]
|
||||
struct siginfo_t {
|
||||
si_signo: i32,
|
||||
si_errno: i32,
|
||||
si_code: i32,
|
||||
si_addr: u64,
|
||||
// ...
|
||||
}
|
||||
|
||||
let siginfo = siginfo as *const siginfo_t;
|
||||
let si_addr = (*siginfo).si_addr;
|
||||
|
||||
let ucontext = ucontext as *const ucontext_t;
|
||||
let gregs = &(*ucontext).uc_mcontext.gregs;
|
||||
let fpregs = &*(*ucontext).uc_mcontext.fpregs;
|
||||
|
||||
let mut known_registers: [Option<u64>; 24] = [None; 24];
|
||||
known_registers[X64Register::GPR(GPR::R15).to_index().0] = Some(gregs[REG_R15 as usize] as _);
|
||||
known_registers[X64Register::GPR(GPR::R14).to_index().0] = Some(gregs[REG_R14 as usize] as _);
|
||||
known_registers[X64Register::GPR(GPR::R13).to_index().0] = Some(gregs[REG_R13 as usize] as _);
|
||||
known_registers[X64Register::GPR(GPR::R12).to_index().0] = Some(gregs[REG_R12 as usize] as _);
|
||||
known_registers[X64Register::GPR(GPR::R11).to_index().0] = Some(gregs[REG_R11 as usize] as _);
|
||||
known_registers[X64Register::GPR(GPR::R10).to_index().0] = Some(gregs[REG_R10 as usize] as _);
|
||||
known_registers[X64Register::GPR(GPR::R9).to_index().0] = Some(gregs[REG_R9 as usize] as _);
|
||||
known_registers[X64Register::GPR(GPR::R8).to_index().0] = Some(gregs[REG_R8 as usize] as _);
|
||||
known_registers[X64Register::GPR(GPR::RSI).to_index().0] = Some(gregs[REG_RSI as usize] as _);
|
||||
known_registers[X64Register::GPR(GPR::RDI).to_index().0] = Some(gregs[REG_RDI as usize] as _);
|
||||
known_registers[X64Register::GPR(GPR::RDX).to_index().0] = Some(gregs[REG_RDX as usize] as _);
|
||||
known_registers[X64Register::GPR(GPR::RCX).to_index().0] = Some(gregs[REG_RCX as usize] as _);
|
||||
known_registers[X64Register::GPR(GPR::RBX).to_index().0] = Some(gregs[REG_RBX as usize] as _);
|
||||
known_registers[X64Register::GPR(GPR::RAX).to_index().0] = Some(gregs[REG_RAX as usize] as _);
|
||||
|
||||
known_registers[X64Register::GPR(GPR::RBP).to_index().0] = Some(gregs[REG_RBP as usize] as _);
|
||||
known_registers[X64Register::GPR(GPR::RSP).to_index().0] = Some(gregs[REG_RSP as usize] as _);
|
||||
|
||||
known_registers[X64Register::XMM(XMM::XMM0).to_index().0] = Some(read_xmm(&fpregs._xmm[0]));
|
||||
known_registers[X64Register::XMM(XMM::XMM1).to_index().0] = Some(read_xmm(&fpregs._xmm[1]));
|
||||
known_registers[X64Register::XMM(XMM::XMM2).to_index().0] = Some(read_xmm(&fpregs._xmm[2]));
|
||||
known_registers[X64Register::XMM(XMM::XMM3).to_index().0] = Some(read_xmm(&fpregs._xmm[3]));
|
||||
known_registers[X64Register::XMM(XMM::XMM4).to_index().0] = Some(read_xmm(&fpregs._xmm[4]));
|
||||
known_registers[X64Register::XMM(XMM::XMM5).to_index().0] = Some(read_xmm(&fpregs._xmm[5]));
|
||||
known_registers[X64Register::XMM(XMM::XMM6).to_index().0] = Some(read_xmm(&fpregs._xmm[6]));
|
||||
known_registers[X64Register::XMM(XMM::XMM7).to_index().0] = Some(read_xmm(&fpregs._xmm[7]));
|
||||
|
||||
FaultInfo {
|
||||
faulting_addr: si_addr as usize as _,
|
||||
ip: gregs[REG_RIP as usize] as _,
|
||||
known_registers,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
|
||||
pub unsafe fn get_fault_info(siginfo: *const c_void, ucontext: *const c_void) -> FaultInfo {
|
||||
#[allow(dead_code)]
|
||||
#[repr(C)]
|
||||
struct ucontext_t {
|
||||
uc_onstack: u32,
|
||||
uc_sigmask: u32,
|
||||
uc_stack: libc::stack_t,
|
||||
uc_link: *const ucontext_t,
|
||||
uc_mcsize: u64,
|
||||
uc_mcontext: *const mcontext_t,
|
||||
}
|
||||
#[repr(C)]
|
||||
struct exception_state {
|
||||
trapno: u16,
|
||||
cpu: u16,
|
||||
err: u32,
|
||||
faultvaddr: u64,
|
||||
}
|
||||
#[repr(C)]
|
||||
struct regs {
|
||||
rax: u64,
|
||||
rbx: u64,
|
||||
rcx: u64,
|
||||
rdx: u64,
|
||||
rdi: u64,
|
||||
rsi: u64,
|
||||
rbp: u64,
|
||||
rsp: u64,
|
||||
r8: u64,
|
||||
r9: u64,
|
||||
r10: u64,
|
||||
r11: u64,
|
||||
r12: u64,
|
||||
r13: u64,
|
||||
r14: u64,
|
||||
r15: u64,
|
||||
rip: u64,
|
||||
rflags: u64,
|
||||
cs: u64,
|
||||
fs: u64,
|
||||
gs: u64,
|
||||
}
|
||||
#[repr(C)]
|
||||
struct fpstate {
|
||||
_unused: [u8; 168],
|
||||
xmm: [[u64; 2]; 8],
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
#[repr(C)]
|
||||
struct mcontext_t {
|
||||
es: exception_state,
|
||||
ss: regs,
|
||||
fs: fpstate,
|
||||
}
|
||||
|
||||
let siginfo = siginfo as *const siginfo_t;
|
||||
let si_addr = (*siginfo).si_addr;
|
||||
|
||||
let ucontext = ucontext as *const ucontext_t;
|
||||
let ss = &(*(*ucontext).uc_mcontext).ss;
|
||||
let fs = &(*(*ucontext).uc_mcontext).fs;
|
||||
|
||||
let mut known_registers: [Option<u64>; 24] = [None; 24];
|
||||
|
||||
known_registers[X64Register::GPR(GPR::R15).to_index().0] = Some(ss.r15);
|
||||
known_registers[X64Register::GPR(GPR::R14).to_index().0] = Some(ss.r14);
|
||||
known_registers[X64Register::GPR(GPR::R13).to_index().0] = Some(ss.r13);
|
||||
known_registers[X64Register::GPR(GPR::R12).to_index().0] = Some(ss.r12);
|
||||
known_registers[X64Register::GPR(GPR::R11).to_index().0] = Some(ss.r11);
|
||||
known_registers[X64Register::GPR(GPR::R10).to_index().0] = Some(ss.r10);
|
||||
known_registers[X64Register::GPR(GPR::R9).to_index().0] = Some(ss.r9);
|
||||
known_registers[X64Register::GPR(GPR::R8).to_index().0] = Some(ss.r8);
|
||||
known_registers[X64Register::GPR(GPR::RSI).to_index().0] = Some(ss.rsi);
|
||||
known_registers[X64Register::GPR(GPR::RDI).to_index().0] = Some(ss.rdi);
|
||||
known_registers[X64Register::GPR(GPR::RDX).to_index().0] = Some(ss.rdx);
|
||||
known_registers[X64Register::GPR(GPR::RCX).to_index().0] = Some(ss.rcx);
|
||||
known_registers[X64Register::GPR(GPR::RBX).to_index().0] = Some(ss.rbx);
|
||||
known_registers[X64Register::GPR(GPR::RAX).to_index().0] = Some(ss.rax);
|
||||
|
||||
known_registers[X64Register::GPR(GPR::RBP).to_index().0] = Some(ss.rbp);
|
||||
known_registers[X64Register::GPR(GPR::RSP).to_index().0] = Some(ss.rsp);
|
||||
|
||||
known_registers[X64Register::XMM(XMM::XMM0).to_index().0] = Some(fs.xmm[0][0]);
|
||||
known_registers[X64Register::XMM(XMM::XMM1).to_index().0] = Some(fs.xmm[1][0]);
|
||||
known_registers[X64Register::XMM(XMM::XMM2).to_index().0] = Some(fs.xmm[2][0]);
|
||||
known_registers[X64Register::XMM(XMM::XMM3).to_index().0] = Some(fs.xmm[3][0]);
|
||||
known_registers[X64Register::XMM(XMM::XMM4).to_index().0] = Some(fs.xmm[4][0]);
|
||||
known_registers[X64Register::XMM(XMM::XMM5).to_index().0] = Some(fs.xmm[5][0]);
|
||||
known_registers[X64Register::XMM(XMM::XMM6).to_index().0] = Some(fs.xmm[6][0]);
|
||||
known_registers[X64Register::XMM(XMM::XMM7).to_index().0] = Some(fs.xmm[7][0]);
|
||||
|
||||
FaultInfo {
|
||||
faulting_addr: si_addr,
|
||||
ip: ss.rip as _,
|
||||
known_registers,
|
||||
}
|
||||
}
|
@ -46,7 +46,7 @@ impl IsExport for Export {
|
||||
/// ```
|
||||
pub struct ImportObject {
|
||||
map: Rc<RefCell<HashMap<String, Box<dyn LikeNamespace>>>>,
|
||||
state_creator: Option<Rc<Fn() -> (*mut c_void, fn(*mut c_void))>>,
|
||||
pub(crate) state_creator: Option<Rc<Fn() -> (*mut c_void, fn(*mut c_void))>>,
|
||||
pub allow_missing_functions: bool,
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,9 @@ pub mod vm;
|
||||
pub mod vmcalls;
|
||||
#[cfg(all(unix, target_arch = "x86_64"))]
|
||||
pub use trampoline_x64 as trampoline;
|
||||
#[cfg(all(unix, target_arch = "x86_64"))]
|
||||
pub mod fault;
|
||||
pub mod state;
|
||||
|
||||
use self::error::CompileResult;
|
||||
#[doc(inline)]
|
||||
|
@ -223,13 +223,6 @@ pub fn read_module<
|
||||
let fcg = mcg
|
||||
.next_function(Arc::clone(&info))
|
||||
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
||||
middlewares
|
||||
.run(
|
||||
Some(fcg),
|
||||
Event::Internal(InternalEvent::FunctionBegin(id as u32)),
|
||||
&info.read().unwrap(),
|
||||
)
|
||||
.map_err(|x| LoadError::Codegen(x))?;
|
||||
|
||||
let info_read = info.read().unwrap();
|
||||
let sig = info_read
|
||||
@ -271,6 +264,13 @@ pub fn read_module<
|
||||
body_begun = true;
|
||||
fcg.begin_body(&info.read().unwrap())
|
||||
.map_err(|x| LoadError::Codegen(format!("{:?}", x)))?;
|
||||
middlewares
|
||||
.run(
|
||||
Some(fcg),
|
||||
Event::Internal(InternalEvent::FunctionBegin(id as u32)),
|
||||
&info.read().unwrap(),
|
||||
)
|
||||
.map_err(|x| LoadError::Codegen(x))?;
|
||||
}
|
||||
middlewares
|
||||
.run(Some(fcg), Event::Wasm(op), &info.read().unwrap())
|
||||
|
840
lib/runtime-core/src/state.rs
Normal file
840
lib/runtime-core/src/state.rs
Normal file
@ -0,0 +1,840 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::Bound::{Included, Unbounded};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct RegisterIndex(pub usize);
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum WasmAbstractValue {
|
||||
Runtime,
|
||||
Const(u64),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MachineState {
|
||||
pub stack_values: Vec<MachineValue>,
|
||||
pub register_values: Vec<MachineValue>,
|
||||
|
||||
pub wasm_stack: Vec<WasmAbstractValue>,
|
||||
pub wasm_stack_private_depth: usize,
|
||||
|
||||
pub wasm_inst_offset: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct MachineStateDiff {
|
||||
pub last: Option<usize>,
|
||||
pub stack_push: Vec<MachineValue>,
|
||||
pub stack_pop: usize,
|
||||
pub reg_diff: Vec<(RegisterIndex, MachineValue)>,
|
||||
|
||||
pub wasm_stack_push: Vec<WasmAbstractValue>,
|
||||
pub wasm_stack_pop: usize,
|
||||
pub wasm_stack_private_depth: usize, // absolute value; not a diff.
|
||||
|
||||
pub wasm_inst_offset: usize, // absolute value; not a diff.
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum MachineValue {
|
||||
Undefined,
|
||||
Vmctx,
|
||||
PreserveRegister(RegisterIndex),
|
||||
CopyStackBPRelative(i32), // relative to Base Pointer, in byte offset
|
||||
ExplicitShadow, // indicates that all values above this are above the shadow region
|
||||
WasmStack(usize),
|
||||
WasmLocal(usize),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FunctionStateMap {
|
||||
pub initial: MachineState,
|
||||
pub local_function_id: usize,
|
||||
pub locals: Vec<WasmAbstractValue>,
|
||||
pub shadow_size: usize, // for single-pass backend, 32 bytes on x86-64
|
||||
pub diffs: Vec<MachineStateDiff>,
|
||||
pub wasm_function_header_target_offset: Option<SuspendOffset>,
|
||||
pub wasm_offset_to_target_offset: BTreeMap<usize, SuspendOffset>,
|
||||
pub loop_offsets: BTreeMap<usize, OffsetInfo>, /* suspend_offset -> info */
|
||||
pub call_offsets: BTreeMap<usize, OffsetInfo>, /* suspend_offset -> info */
|
||||
pub trappable_offsets: BTreeMap<usize, OffsetInfo>, /* suspend_offset -> info */
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum SuspendOffset {
|
||||
Loop(usize),
|
||||
Call(usize),
|
||||
Trappable(usize),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OffsetInfo {
|
||||
pub diff_id: usize,
|
||||
pub activate_offset: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ModuleStateMap {
|
||||
pub local_functions: BTreeMap<usize, FunctionStateMap>,
|
||||
pub total_size: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct WasmFunctionStateDump {
|
||||
pub local_function_id: usize,
|
||||
pub wasm_inst_offset: usize,
|
||||
pub stack: Vec<Option<u64>>,
|
||||
pub locals: Vec<Option<u64>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct ExecutionStateImage {
|
||||
pub frames: Vec<WasmFunctionStateDump>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct InstanceImage {
|
||||
pub memory: Option<Vec<u8>>,
|
||||
pub globals: Vec<u64>,
|
||||
pub execution_state: ExecutionStateImage,
|
||||
}
|
||||
|
||||
impl ModuleStateMap {
|
||||
fn lookup_call_ip(&self, ip: usize, base: usize) -> Option<(&FunctionStateMap, MachineState)> {
|
||||
if ip < base || ip - base >= self.total_size {
|
||||
None
|
||||
} else {
|
||||
let (_, fsm) = self
|
||||
.local_functions
|
||||
.range((Unbounded, Included(&(ip - base))))
|
||||
.last()
|
||||
.unwrap();
|
||||
|
||||
match fsm.call_offsets.get(&(ip - base)) {
|
||||
Some(x) => Some((fsm, fsm.diffs[x.diff_id].build_state(fsm))),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup_trappable_ip(
|
||||
&self,
|
||||
ip: usize,
|
||||
base: usize,
|
||||
) -> Option<(&FunctionStateMap, MachineState)> {
|
||||
if ip < base || ip - base >= self.total_size {
|
||||
None
|
||||
} else {
|
||||
let (_, fsm) = self
|
||||
.local_functions
|
||||
.range((Unbounded, Included(&(ip - base))))
|
||||
.last()
|
||||
.unwrap();
|
||||
|
||||
match fsm.trappable_offsets.get(&(ip - base)) {
|
||||
Some(x) => Some((fsm, fsm.diffs[x.diff_id].build_state(fsm))),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup_loop_ip(&self, ip: usize, base: usize) -> Option<(&FunctionStateMap, MachineState)> {
|
||||
if ip < base || ip - base >= self.total_size {
|
||||
None
|
||||
} else {
|
||||
let (_, fsm) = self
|
||||
.local_functions
|
||||
.range((Unbounded, Included(&(ip - base))))
|
||||
.last()
|
||||
.unwrap();
|
||||
|
||||
match fsm.loop_offsets.get(&(ip - base)) {
|
||||
Some(x) => Some((fsm, fsm.diffs[x.diff_id].build_state(fsm))),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FunctionStateMap {
|
||||
pub fn new(
|
||||
initial: MachineState,
|
||||
local_function_id: usize,
|
||||
shadow_size: usize,
|
||||
locals: Vec<WasmAbstractValue>,
|
||||
) -> FunctionStateMap {
|
||||
FunctionStateMap {
|
||||
initial,
|
||||
local_function_id,
|
||||
shadow_size,
|
||||
locals,
|
||||
diffs: vec![],
|
||||
wasm_function_header_target_offset: None,
|
||||
wasm_offset_to_target_offset: BTreeMap::new(),
|
||||
loop_offsets: BTreeMap::new(),
|
||||
call_offsets: BTreeMap::new(),
|
||||
trappable_offsets: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MachineState {
|
||||
pub fn diff(&self, old: &MachineState) -> MachineStateDiff {
|
||||
let first_diff_stack_depth: usize = self
|
||||
.stack_values
|
||||
.iter()
|
||||
.zip(old.stack_values.iter())
|
||||
.enumerate()
|
||||
.find(|&(_, (&a, &b))| a != b)
|
||||
.map(|x| x.0)
|
||||
.unwrap_or(old.stack_values.len().min(self.stack_values.len()));
|
||||
assert_eq!(self.register_values.len(), old.register_values.len());
|
||||
let reg_diff: Vec<_> = self
|
||||
.register_values
|
||||
.iter()
|
||||
.zip(old.register_values.iter())
|
||||
.enumerate()
|
||||
.filter(|&(_, (&a, &b))| a != b)
|
||||
.map(|(i, (&a, _))| (RegisterIndex(i), a))
|
||||
.collect();
|
||||
let first_diff_wasm_stack_depth: usize = self
|
||||
.wasm_stack
|
||||
.iter()
|
||||
.zip(old.wasm_stack.iter())
|
||||
.enumerate()
|
||||
.find(|&(_, (&a, &b))| a != b)
|
||||
.map(|x| x.0)
|
||||
.unwrap_or(old.wasm_stack.len().min(self.wasm_stack.len()));
|
||||
MachineStateDiff {
|
||||
last: None,
|
||||
stack_push: self.stack_values[first_diff_stack_depth..].to_vec(),
|
||||
stack_pop: old.stack_values.len() - first_diff_stack_depth,
|
||||
reg_diff: reg_diff,
|
||||
|
||||
wasm_stack_push: self.wasm_stack[first_diff_wasm_stack_depth..].to_vec(),
|
||||
wasm_stack_pop: old.wasm_stack.len() - first_diff_wasm_stack_depth,
|
||||
wasm_stack_private_depth: self.wasm_stack_private_depth,
|
||||
|
||||
wasm_inst_offset: self.wasm_inst_offset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MachineStateDiff {
|
||||
pub fn build_state(&self, m: &FunctionStateMap) -> MachineState {
|
||||
let mut chain: Vec<&MachineStateDiff> = vec![];
|
||||
chain.push(self);
|
||||
let mut current = self.last;
|
||||
while let Some(x) = current {
|
||||
let that = &m.diffs[x];
|
||||
current = that.last;
|
||||
chain.push(that);
|
||||
}
|
||||
chain.reverse();
|
||||
let mut state = m.initial.clone();
|
||||
for x in chain {
|
||||
for _ in 0..x.stack_pop {
|
||||
state.stack_values.pop().unwrap();
|
||||
}
|
||||
for v in &x.stack_push {
|
||||
state.stack_values.push(*v);
|
||||
}
|
||||
for &(index, v) in &x.reg_diff {
|
||||
state.register_values[index.0] = v;
|
||||
}
|
||||
for _ in 0..x.wasm_stack_pop {
|
||||
state.wasm_stack.pop().unwrap();
|
||||
}
|
||||
for v in &x.wasm_stack_push {
|
||||
state.wasm_stack.push(*v);
|
||||
}
|
||||
}
|
||||
state.wasm_stack_private_depth = self.wasm_stack_private_depth;
|
||||
state.wasm_inst_offset = self.wasm_inst_offset;
|
||||
state
|
||||
}
|
||||
}
|
||||
|
||||
impl ExecutionStateImage {
|
||||
pub fn print_backtrace_if_needed(&self) {
|
||||
use std::env;
|
||||
|
||||
if let Ok(x) = env::var("WASMER_BACKTRACE") {
|
||||
if x == "1" {
|
||||
eprintln!("{}", self.colored_output());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
eprintln!("Run with `WASMER_BACKTRACE=1` environment variable to display a backtrace.");
|
||||
}
|
||||
|
||||
pub fn colored_output(&self) -> String {
|
||||
use colored::*;
|
||||
|
||||
fn join_strings(x: impl Iterator<Item = String>, sep: &str) -> String {
|
||||
let mut ret = String::new();
|
||||
let mut first = true;
|
||||
|
||||
for s in x {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
ret += sep;
|
||||
}
|
||||
ret += &s;
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn format_optional_u64_sequence(x: &[Option<u64>]) -> String {
|
||||
if x.len() == 0 {
|
||||
"(empty)".into()
|
||||
} else {
|
||||
join_strings(
|
||||
x.iter().enumerate().map(|(i, x)| {
|
||||
format!(
|
||||
"[{}] = {}",
|
||||
i,
|
||||
x.map(|x| format!("{}", x))
|
||||
.unwrap_or_else(|| "?".to_string())
|
||||
.bold()
|
||||
.cyan()
|
||||
)
|
||||
}),
|
||||
", ",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
let mut ret = String::new();
|
||||
|
||||
if self.frames.len() == 0 {
|
||||
ret += &"Unknown fault address, cannot read stack.".yellow();
|
||||
ret += "\n";
|
||||
} else {
|
||||
ret += &"Backtrace:".bold();
|
||||
ret += "\n";
|
||||
for (i, f) in self.frames.iter().enumerate() {
|
||||
ret += &format!("* Frame {} @ Local function {}", i, f.local_function_id).bold();
|
||||
ret += "\n";
|
||||
ret += &format!(
|
||||
" {} {}\n",
|
||||
"Offset:".bold().yellow(),
|
||||
format!("{}", f.wasm_inst_offset).bold().cyan(),
|
||||
);
|
||||
ret += &format!(
|
||||
" {} {}\n",
|
||||
"Locals:".bold().yellow(),
|
||||
format_optional_u64_sequence(&f.locals)
|
||||
);
|
||||
ret += &format!(
|
||||
" {} {}\n\n",
|
||||
"Stack:".bold().yellow(),
|
||||
format_optional_u64_sequence(&f.stack)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl InstanceImage {
|
||||
pub fn from_bytes(input: &[u8]) -> Option<InstanceImage> {
|
||||
use bincode::deserialize;
|
||||
match deserialize(input) {
|
||||
Ok(x) => Some(x),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
use bincode::serialize;
|
||||
serialize(self).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, target_arch = "x86_64"))]
|
||||
pub mod x64 {
|
||||
use super::*;
|
||||
use crate::codegen::BreakpointMap;
|
||||
use crate::fault::{catch_unsafe_unwind, run_on_alternative_stack};
|
||||
use crate::structures::TypedIndex;
|
||||
use crate::types::LocalGlobalIndex;
|
||||
use crate::vm::Ctx;
|
||||
use std::any::Any;
|
||||
|
||||
pub fn new_machine_state() -> MachineState {
|
||||
MachineState {
|
||||
stack_values: vec![],
|
||||
register_values: vec![MachineValue::Undefined; 16 + 8],
|
||||
wasm_stack: vec![],
|
||||
wasm_stack_private_depth: 0,
|
||||
wasm_inst_offset: ::std::usize::MAX,
|
||||
}
|
||||
}
|
||||
|
||||
#[warn(unused_variables)]
|
||||
pub unsafe fn invoke_call_return_on_stack(
|
||||
msm: &ModuleStateMap,
|
||||
code_base: usize,
|
||||
image: InstanceImage,
|
||||
vmctx: &mut Ctx,
|
||||
breakpoints: Option<BreakpointMap>,
|
||||
) -> Result<u64, Box<dyn Any>> {
|
||||
let mut stack: Vec<u64> = vec![0; 1048576 * 8 / 8]; // 8MB stack
|
||||
let mut stack_offset: usize = stack.len();
|
||||
|
||||
stack_offset -= 3; // placeholder for call return
|
||||
|
||||
let mut last_stack_offset: u64 = 0; // rbp
|
||||
|
||||
let mut known_registers: [Option<u64>; 24] = [None; 24];
|
||||
|
||||
let local_functions_vec: Vec<&FunctionStateMap> =
|
||||
msm.local_functions.iter().map(|(_, v)| v).collect();
|
||||
|
||||
// Bottom to top
|
||||
for f in image.execution_state.frames.iter().rev() {
|
||||
let fsm = local_functions_vec[f.local_function_id];
|
||||
let suspend_offset = if f.wasm_inst_offset == ::std::usize::MAX {
|
||||
fsm.wasm_function_header_target_offset
|
||||
} else {
|
||||
fsm.wasm_offset_to_target_offset
|
||||
.get(&f.wasm_inst_offset)
|
||||
.map(|x| *x)
|
||||
}
|
||||
.expect("instruction is not a critical point");
|
||||
|
||||
let (activate_offset, diff_id) = match suspend_offset {
|
||||
SuspendOffset::Loop(x) => fsm.loop_offsets.get(&x),
|
||||
SuspendOffset::Call(x) => fsm.call_offsets.get(&x),
|
||||
SuspendOffset::Trappable(x) => fsm.trappable_offsets.get(&x),
|
||||
}
|
||||
.map(|x| (x.activate_offset, x.diff_id))
|
||||
.expect("offset cannot be found in table");
|
||||
|
||||
let diff = &fsm.diffs[diff_id];
|
||||
let state = diff.build_state(fsm);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] = stack.as_ptr().offset(last_stack_offset as isize) as usize as u64; // push rbp
|
||||
last_stack_offset = stack_offset as _;
|
||||
|
||||
let mut got_explicit_shadow = false;
|
||||
|
||||
for v in state.stack_values.iter() {
|
||||
match *v {
|
||||
MachineValue::Undefined => stack_offset -= 1,
|
||||
MachineValue::Vmctx => {
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] = vmctx as *mut Ctx as usize as u64;
|
||||
}
|
||||
MachineValue::PreserveRegister(index) => {
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] = known_registers[index.0].unwrap_or(0);
|
||||
}
|
||||
MachineValue::CopyStackBPRelative(byte_offset) => {
|
||||
assert!(byte_offset % 8 == 0);
|
||||
let target_offset = (byte_offset / 8) as isize;
|
||||
let v = stack[(last_stack_offset as isize + target_offset) as usize];
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] = v;
|
||||
}
|
||||
MachineValue::ExplicitShadow => {
|
||||
assert!(fsm.shadow_size % 8 == 0);
|
||||
stack_offset -= fsm.shadow_size / 8;
|
||||
got_explicit_shadow = true;
|
||||
}
|
||||
MachineValue::WasmStack(x) => {
|
||||
stack_offset -= 1;
|
||||
match state.wasm_stack[x] {
|
||||
WasmAbstractValue::Const(x) => {
|
||||
stack[stack_offset] = x;
|
||||
}
|
||||
WasmAbstractValue::Runtime => {
|
||||
stack[stack_offset] = f.stack[x].unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
MachineValue::WasmLocal(x) => {
|
||||
stack_offset -= 1;
|
||||
match fsm.locals[x] {
|
||||
WasmAbstractValue::Const(x) => {
|
||||
stack[stack_offset] = x;
|
||||
}
|
||||
WasmAbstractValue::Runtime => {
|
||||
stack[stack_offset] = f.locals[x].unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !got_explicit_shadow {
|
||||
assert!(fsm.shadow_size % 8 == 0);
|
||||
stack_offset -= fsm.shadow_size / 8;
|
||||
}
|
||||
for (i, v) in state.register_values.iter().enumerate() {
|
||||
match *v {
|
||||
MachineValue::Undefined => {}
|
||||
MachineValue::Vmctx => {
|
||||
known_registers[i] = Some(vmctx as *mut Ctx as usize as u64);
|
||||
}
|
||||
MachineValue::WasmStack(x) => match state.wasm_stack[x] {
|
||||
WasmAbstractValue::Const(x) => {
|
||||
known_registers[i] = Some(x);
|
||||
}
|
||||
WasmAbstractValue::Runtime => {
|
||||
known_registers[i] = Some(f.stack[x].unwrap());
|
||||
}
|
||||
},
|
||||
MachineValue::WasmLocal(x) => match fsm.locals[x] {
|
||||
WasmAbstractValue::Const(x) => {
|
||||
known_registers[i] = Some(x);
|
||||
}
|
||||
WasmAbstractValue::Runtime => {
|
||||
known_registers[i] = Some(f.locals[x].unwrap());
|
||||
}
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
// no need to check 16-byte alignment here because it's possible that we're not at a call entry.
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] = (code_base + activate_offset) as u64; // return address
|
||||
}
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] = known_registers[X64Register::GPR(GPR::R15).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] = known_registers[X64Register::GPR(GPR::R14).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] = known_registers[X64Register::GPR(GPR::R13).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] = known_registers[X64Register::GPR(GPR::R12).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] = known_registers[X64Register::GPR(GPR::R11).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] = known_registers[X64Register::GPR(GPR::R10).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] = known_registers[X64Register::GPR(GPR::R9).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] = known_registers[X64Register::GPR(GPR::R8).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] = known_registers[X64Register::GPR(GPR::RSI).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] = known_registers[X64Register::GPR(GPR::RDI).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] = known_registers[X64Register::GPR(GPR::RDX).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] = known_registers[X64Register::GPR(GPR::RCX).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] = known_registers[X64Register::GPR(GPR::RBX).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] = known_registers[X64Register::GPR(GPR::RAX).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] = stack.as_ptr().offset(last_stack_offset as isize) as usize as u64; // rbp
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] =
|
||||
known_registers[X64Register::XMM(XMM::XMM7).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] =
|
||||
known_registers[X64Register::XMM(XMM::XMM6).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] =
|
||||
known_registers[X64Register::XMM(XMM::XMM5).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] =
|
||||
known_registers[X64Register::XMM(XMM::XMM4).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] =
|
||||
known_registers[X64Register::XMM(XMM::XMM3).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] =
|
||||
known_registers[X64Register::XMM(XMM::XMM2).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] =
|
||||
known_registers[X64Register::XMM(XMM::XMM1).to_index().0].unwrap_or(0);
|
||||
|
||||
stack_offset -= 1;
|
||||
stack[stack_offset] =
|
||||
known_registers[X64Register::XMM(XMM::XMM0).to_index().0].unwrap_or(0);
|
||||
|
||||
if let Some(ref memory) = image.memory {
|
||||
assert!(vmctx.internal.memory_bound <= memory.len());
|
||||
|
||||
if vmctx.internal.memory_bound < memory.len() {
|
||||
let grow: unsafe extern "C" fn(ctx: &mut Ctx, memory_index: usize, delta: usize) =
|
||||
::std::mem::transmute((*vmctx.internal.intrinsics).memory_grow);
|
||||
grow(
|
||||
vmctx,
|
||||
0,
|
||||
(memory.len() - vmctx.internal.memory_bound) / 65536,
|
||||
);
|
||||
assert_eq!(vmctx.internal.memory_bound, memory.len());
|
||||
}
|
||||
|
||||
::std::slice::from_raw_parts_mut(
|
||||
vmctx.internal.memory_base,
|
||||
vmctx.internal.memory_bound,
|
||||
)
|
||||
.copy_from_slice(memory);
|
||||
}
|
||||
|
||||
let globals_len = (*vmctx.module).info.globals.len();
|
||||
for i in 0..globals_len {
|
||||
(*(*vmctx.local_backing).globals[LocalGlobalIndex::new(i)].vm_local_global()).data =
|
||||
image.globals[i];
|
||||
}
|
||||
|
||||
drop(image); // free up host memory
|
||||
|
||||
catch_unsafe_unwind(
|
||||
|| {
|
||||
run_on_alternative_stack(
|
||||
stack.as_mut_ptr().offset(stack.len() as isize),
|
||||
stack.as_mut_ptr().offset(stack_offset as isize),
|
||||
)
|
||||
},
|
||||
breakpoints,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn build_instance_image(
|
||||
vmctx: &mut Ctx,
|
||||
execution_state: ExecutionStateImage,
|
||||
) -> InstanceImage {
|
||||
unsafe {
|
||||
let memory = if vmctx.internal.memory_base.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
::std::slice::from_raw_parts(
|
||||
vmctx.internal.memory_base,
|
||||
vmctx.internal.memory_bound,
|
||||
)
|
||||
.to_vec(),
|
||||
)
|
||||
};
|
||||
|
||||
// FIXME: Imported globals
|
||||
let globals_len = (*vmctx.module).info.globals.len();
|
||||
let globals: Vec<u64> = (0..globals_len)
|
||||
.map(|i| {
|
||||
(*vmctx.local_backing).globals[LocalGlobalIndex::new(i)]
|
||||
.get()
|
||||
.to_u64()
|
||||
})
|
||||
.collect();
|
||||
|
||||
InstanceImage {
|
||||
memory: memory,
|
||||
globals: globals,
|
||||
execution_state: execution_state,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[warn(unused_variables)]
|
||||
pub unsafe fn read_stack(
|
||||
msm: &ModuleStateMap,
|
||||
code_base: usize,
|
||||
mut stack: *const u64,
|
||||
initially_known_registers: [Option<u64>; 24],
|
||||
mut initial_address: Option<u64>,
|
||||
) -> ExecutionStateImage {
|
||||
let mut known_registers: [Option<u64>; 24] = initially_known_registers;
|
||||
let mut results: Vec<WasmFunctionStateDump> = vec![];
|
||||
|
||||
for _ in 0.. {
|
||||
let ret_addr = initial_address.take().unwrap_or_else(|| {
|
||||
let x = *stack;
|
||||
stack = stack.offset(1);
|
||||
x
|
||||
});
|
||||
let (fsm, state) = match msm
|
||||
.lookup_call_ip(ret_addr as usize, code_base)
|
||||
.or_else(|| msm.lookup_trappable_ip(ret_addr as usize, code_base))
|
||||
.or_else(|| msm.lookup_loop_ip(ret_addr as usize, code_base))
|
||||
{
|
||||
Some(x) => x,
|
||||
_ => return ExecutionStateImage { frames: results },
|
||||
};
|
||||
|
||||
let mut wasm_stack: Vec<Option<u64>> = state
|
||||
.wasm_stack
|
||||
.iter()
|
||||
.map(|x| match *x {
|
||||
WasmAbstractValue::Const(x) => Some(x),
|
||||
WasmAbstractValue::Runtime => None,
|
||||
})
|
||||
.collect();
|
||||
let mut wasm_locals: Vec<Option<u64>> = fsm
|
||||
.locals
|
||||
.iter()
|
||||
.map(|x| match *x {
|
||||
WasmAbstractValue::Const(x) => Some(x),
|
||||
WasmAbstractValue::Runtime => None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
// This must be before the next loop because that modifies `known_registers`.
|
||||
for (i, v) in state.register_values.iter().enumerate() {
|
||||
match *v {
|
||||
MachineValue::Undefined => {}
|
||||
MachineValue::Vmctx => {}
|
||||
MachineValue::WasmStack(idx) => {
|
||||
if let Some(v) = known_registers[i] {
|
||||
wasm_stack[idx] = Some(v);
|
||||
} else {
|
||||
eprintln!(
|
||||
"BUG: Register {} for WebAssembly stack slot {} has unknown value.",
|
||||
i, idx
|
||||
);
|
||||
}
|
||||
}
|
||||
MachineValue::WasmLocal(idx) => {
|
||||
if let Some(v) = known_registers[i] {
|
||||
wasm_locals[idx] = Some(v);
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
let mut found_shadow = false;
|
||||
for v in state.stack_values.iter() {
|
||||
match *v {
|
||||
MachineValue::ExplicitShadow => {
|
||||
found_shadow = true;
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if !found_shadow {
|
||||
stack = stack.offset((fsm.shadow_size / 8) as isize);
|
||||
}
|
||||
|
||||
for v in state.stack_values.iter().rev() {
|
||||
match *v {
|
||||
MachineValue::ExplicitShadow => {
|
||||
stack = stack.offset((fsm.shadow_size / 8) as isize);
|
||||
}
|
||||
MachineValue::Undefined => {
|
||||
stack = stack.offset(1);
|
||||
}
|
||||
MachineValue::Vmctx => {
|
||||
stack = stack.offset(1);
|
||||
}
|
||||
MachineValue::PreserveRegister(idx) => {
|
||||
known_registers[idx.0] = Some(*stack);
|
||||
stack = stack.offset(1);
|
||||
}
|
||||
MachineValue::CopyStackBPRelative(_) => {
|
||||
stack = stack.offset(1);
|
||||
}
|
||||
MachineValue::WasmStack(idx) => {
|
||||
wasm_stack[idx] = Some(*stack);
|
||||
stack = stack.offset(1);
|
||||
}
|
||||
MachineValue::WasmLocal(idx) => {
|
||||
wasm_locals[idx] = Some(*stack);
|
||||
stack = stack.offset(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
stack = stack.offset(1); // RBP
|
||||
|
||||
wasm_stack.truncate(
|
||||
wasm_stack
|
||||
.len()
|
||||
.checked_sub(state.wasm_stack_private_depth)
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let wfs = WasmFunctionStateDump {
|
||||
local_function_id: fsm.local_function_id,
|
||||
wasm_inst_offset: state.wasm_inst_offset,
|
||||
stack: wasm_stack,
|
||||
locals: wasm_locals,
|
||||
};
|
||||
results.push(wfs);
|
||||
}
|
||||
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub enum GPR {
|
||||
RAX,
|
||||
RCX,
|
||||
RDX,
|
||||
RBX,
|
||||
RSP,
|
||||
RBP,
|
||||
RSI,
|
||||
RDI,
|
||||
R8,
|
||||
R9,
|
||||
R10,
|
||||
R11,
|
||||
R12,
|
||||
R13,
|
||||
R14,
|
||||
R15,
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub enum XMM {
|
||||
XMM0,
|
||||
XMM1,
|
||||
XMM2,
|
||||
XMM3,
|
||||
XMM4,
|
||||
XMM5,
|
||||
XMM6,
|
||||
XMM7,
|
||||
}
|
||||
|
||||
pub enum X64Register {
|
||||
GPR(GPR),
|
||||
XMM(XMM),
|
||||
}
|
||||
|
||||
impl X64Register {
|
||||
pub fn to_index(&self) -> RegisterIndex {
|
||||
match *self {
|
||||
X64Register::GPR(x) => RegisterIndex(x as usize),
|
||||
X64Register::XMM(x) => RegisterIndex(x as usize + 16),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,8 @@
|
||||
//! Variadic functions are not supported because `rax` is used by the trampoline code.
|
||||
|
||||
use crate::loader::CodeMemory;
|
||||
use crate::vm::Ctx;
|
||||
use std::fmt;
|
||||
use std::{mem, slice};
|
||||
|
||||
lazy_static! {
|
||||
@ -98,6 +100,46 @@ impl TrampolineBufferBuilder {
|
||||
idx
|
||||
}
|
||||
|
||||
pub fn add_context_rsp_state_preserving_trampoline(
|
||||
&mut self,
|
||||
target: unsafe extern "C" fn(&mut Ctx, *const CallContext, *const u64),
|
||||
context: *const CallContext,
|
||||
) -> usize {
|
||||
let idx = self.offsets.len();
|
||||
self.offsets.push(self.code.len());
|
||||
|
||||
self.code.extend_from_slice(&[
|
||||
0x53, // push %rbx
|
||||
0x41, 0x54, // push %r12
|
||||
0x41, 0x55, // push %r13
|
||||
0x41, 0x56, // push %r14
|
||||
0x41, 0x57, // push %r15
|
||||
]);
|
||||
self.code.extend_from_slice(&[
|
||||
0x48, 0xbe, // movabsq ?, %rsi
|
||||
]);
|
||||
self.code.extend_from_slice(value_to_bytes(&context));
|
||||
self.code.extend_from_slice(&[
|
||||
0x48, 0x89, 0xe2, // mov %rsp, %rdx
|
||||
]);
|
||||
|
||||
self.code.extend_from_slice(&[
|
||||
0x48, 0xb8, // movabsq ?, %rax
|
||||
]);
|
||||
self.code.extend_from_slice(value_to_bytes(&target));
|
||||
self.code.extend_from_slice(&[
|
||||
0xff, 0xd0, // callq *%rax
|
||||
]);
|
||||
self.code.extend_from_slice(&[
|
||||
0x48, 0x81, 0xc4, // add ?, %rsp
|
||||
]);
|
||||
self.code.extend_from_slice(value_to_bytes(&40i32)); // 5 * 8
|
||||
self.code.extend_from_slice(&[
|
||||
0xc3, //retq
|
||||
]);
|
||||
idx
|
||||
}
|
||||
|
||||
/// Adds a callinfo trampoline.
|
||||
///
|
||||
/// This generates a trampoline function that collects `num_params` parameters into an array
|
||||
@ -196,6 +238,12 @@ impl TrampolineBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for TrampolineBuffer {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "TrampolineBuffer {{}}")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -180,6 +180,10 @@ where
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_vm_func(&self) -> NonNull<vm::Func> {
|
||||
self.f
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Args, Rets> Func<'a, Args, Rets, Host>
|
||||
@ -364,30 +368,35 @@ macro_rules! impl_traits {
|
||||
impl< $( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly<Rets>, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap> ExternalFunction<($( $x ),*), Rets> for FN {
|
||||
#[allow(non_snake_case)]
|
||||
fn to_raw(&self) -> NonNull<vm::Func> {
|
||||
assert_eq!(mem::size_of::<Self>(), 0, "you cannot use a closure that captures state for `Func`.");
|
||||
if mem::size_of::<Self>() == 0 {
|
||||
/// This is required for the llvm backend to be able to unwind through this function.
|
||||
#[cfg_attr(nightly, unwind(allowed))]
|
||||
extern fn wrap<$( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly<Rets>, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap>( ctx: &mut Ctx $( ,$x: <$x as WasmExternType>::Native )* ) -> Rets::CStruct {
|
||||
let f: FN = unsafe { mem::transmute_copy(&()) };
|
||||
|
||||
/// This is required for the llvm backend to be able to unwind through this function.
|
||||
#[cfg_attr(nightly, unwind(allowed))]
|
||||
extern fn wrap<$( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly<Rets>, FN: Fn( &mut Ctx $( ,$x )* ) -> Trap>( ctx: &mut Ctx $( ,$x: <$x as WasmExternType>::Native )* ) -> Rets::CStruct {
|
||||
let f: FN = unsafe { mem::transmute_copy(&()) };
|
||||
let err = match panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
f( ctx $( ,WasmExternType::from_native($x) )* ).report()
|
||||
})) {
|
||||
Ok(Ok(returns)) => return returns.into_c_struct(),
|
||||
Ok(Err(err)) => {
|
||||
let b: Box<_> = err.into();
|
||||
b as Box<dyn Any>
|
||||
},
|
||||
Err(err) => err,
|
||||
};
|
||||
|
||||
let err = match panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
f( ctx $( ,WasmExternType::from_native($x) )* ).report()
|
||||
})) {
|
||||
Ok(Ok(returns)) => return returns.into_c_struct(),
|
||||
Ok(Err(err)) => {
|
||||
let b: Box<_> = err.into();
|
||||
b as Box<dyn Any>
|
||||
},
|
||||
Err(err) => err,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
(&*ctx.module).runnable_module.do_early_trap(err)
|
||||
unsafe {
|
||||
(&*ctx.module).runnable_module.do_early_trap(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NonNull::new(wrap::<$( $x, )* Rets, Trap, Self> as *mut vm::Func).unwrap()
|
||||
NonNull::new(wrap::<$( $x, )* Rets, Trap, Self> as *mut vm::Func).unwrap()
|
||||
} else {
|
||||
assert_eq!(mem::size_of::<Self>(), mem::size_of::<usize>(), "you cannot use a closure that captures state for `Func`.");
|
||||
NonNull::new(unsafe {
|
||||
::std::mem::transmute_copy::<_, *mut vm::Func>(self)
|
||||
}).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,8 +38,8 @@ pub struct Ctx {
|
||||
|
||||
/// These are pointers to things that are known to be owned
|
||||
/// by the owning `Instance`.
|
||||
local_backing: *mut LocalBacking,
|
||||
import_backing: *mut ImportBacking,
|
||||
pub local_backing: *mut LocalBacking,
|
||||
pub import_backing: *mut ImportBacking,
|
||||
pub module: *const ModuleInner,
|
||||
|
||||
//// This is intended to be user-supplied, per-instance
|
||||
@ -100,6 +100,8 @@ pub struct InternalCtx {
|
||||
pub memory_bound: usize,
|
||||
|
||||
pub internals: *mut [u64; INTERNALS_SIZE], // TODO: Make this dynamic?
|
||||
|
||||
pub interrupt_signal_mem: *mut u8,
|
||||
}
|
||||
|
||||
static INTERNAL_FIELDS: AtomicUsize = AtomicUsize::new(0);
|
||||
@ -207,6 +209,17 @@ fn get_intrinsics_for_module(m: &ModuleInfo) -> *const Intrinsics {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, target_arch = "x86_64"))]
|
||||
fn get_interrupt_signal_mem() -> *mut u8 {
|
||||
unsafe { crate::fault::get_wasm_interrupt_signal_mem() }
|
||||
}
|
||||
|
||||
#[cfg(not(all(unix, target_arch = "x86_64")))]
|
||||
fn get_interrupt_signal_mem() -> *mut u8 {
|
||||
static mut REGION: u64 = 0;
|
||||
unsafe { &mut REGION as *mut u64 as *mut u8 }
|
||||
}
|
||||
|
||||
impl Ctx {
|
||||
#[doc(hidden)]
|
||||
pub unsafe fn new(
|
||||
@ -245,6 +258,8 @@ impl Ctx {
|
||||
memory_bound: mem_bound,
|
||||
|
||||
internals: &mut local_backing.internals.0,
|
||||
|
||||
interrupt_signal_mem: get_interrupt_signal_mem(),
|
||||
},
|
||||
local_functions: local_backing.local_functions.as_ptr(),
|
||||
|
||||
@ -296,6 +311,8 @@ impl Ctx {
|
||||
memory_bound: mem_bound,
|
||||
|
||||
internals: &mut local_backing.internals.0,
|
||||
|
||||
interrupt_signal_mem: get_interrupt_signal_mem(),
|
||||
},
|
||||
local_functions: local_backing.local_functions.as_ptr(),
|
||||
|
||||
@ -419,9 +436,13 @@ impl Ctx {
|
||||
12 * (mem::size_of::<usize>() as u8)
|
||||
}
|
||||
|
||||
pub fn offset_local_functions() -> u8 {
|
||||
pub fn offset_interrupt_signal_mem() -> u8 {
|
||||
13 * (mem::size_of::<usize>() as u8)
|
||||
}
|
||||
|
||||
pub fn offset_local_functions() -> u8 {
|
||||
14 * (mem::size_of::<usize>() as u8)
|
||||
}
|
||||
}
|
||||
|
||||
enum InnerFunc {}
|
||||
@ -640,6 +661,11 @@ mod vm_offset_tests {
|
||||
offset_of!(InternalCtx => internals).get_byte_offset(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Ctx::offset_interrupt_signal_mem() as usize,
|
||||
offset_of!(InternalCtx => interrupt_signal_mem).get_byte_offset(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Ctx::offset_local_functions() as usize,
|
||||
offset_of!(Ctx => local_functions).get_byte_offset(),
|
||||
|
@ -18,3 +18,4 @@ nix = "0.13.0"
|
||||
libc = "0.2.49"
|
||||
smallvec = "0.6.9"
|
||||
hashbrown = "0.1"
|
||||
colored = "1.8"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,38 +1,5 @@
|
||||
use dynasmrt::{x64::Assembler, AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi};
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub enum GPR {
|
||||
RAX,
|
||||
RCX,
|
||||
RDX,
|
||||
RBX,
|
||||
RSP,
|
||||
RBP,
|
||||
RSI,
|
||||
RDI,
|
||||
R8,
|
||||
R9,
|
||||
R10,
|
||||
R11,
|
||||
R12,
|
||||
R13,
|
||||
R14,
|
||||
R15,
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub enum XMM {
|
||||
XMM0,
|
||||
XMM1,
|
||||
XMM2,
|
||||
XMM3,
|
||||
XMM4,
|
||||
XMM5,
|
||||
XMM6,
|
||||
XMM7,
|
||||
}
|
||||
pub use wasmer_runtime_core::state::x64::{GPR, XMM};
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub enum Location {
|
||||
@ -87,7 +54,7 @@ pub trait Emitter {
|
||||
type Offset;
|
||||
|
||||
fn get_label(&mut self) -> Self::Label;
|
||||
fn get_offset(&mut self) -> Self::Offset;
|
||||
fn get_offset(&self) -> Self::Offset;
|
||||
|
||||
fn emit_u64(&mut self, x: u64);
|
||||
|
||||
@ -488,7 +455,7 @@ impl Emitter for Assembler {
|
||||
self.new_dynamic_label()
|
||||
}
|
||||
|
||||
fn get_offset(&mut self) -> AssemblyOffset {
|
||||
fn get_offset(&self) -> AssemblyOffset {
|
||||
self.offset()
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ extern crate smallvec;
|
||||
mod codegen_x64;
|
||||
mod emitter_x64;
|
||||
mod machine;
|
||||
mod protect_unix;
|
||||
pub mod protect_unix;
|
||||
|
||||
pub use codegen_x64::X64FunctionCode as FunctionCodeGenerator;
|
||||
pub use codegen_x64::X64ModuleCodeGenerator as ModuleCodeGenerator;
|
||||
|
@ -1,6 +1,8 @@
|
||||
use crate::emitter_x64::*;
|
||||
use smallvec::SmallVec;
|
||||
use std::collections::HashSet;
|
||||
use wasmer_runtime_core::state::x64::X64Register;
|
||||
use wasmer_runtime_core::state::*;
|
||||
use wasmparser::Type as WpType;
|
||||
|
||||
struct MachineStackOffset(usize);
|
||||
@ -10,6 +12,7 @@ pub struct Machine {
|
||||
used_xmms: HashSet<XMM>,
|
||||
stack_offset: MachineStackOffset,
|
||||
save_area_offset: Option<MachineStackOffset>,
|
||||
pub state: MachineState,
|
||||
}
|
||||
|
||||
impl Machine {
|
||||
@ -19,6 +22,7 @@ impl Machine {
|
||||
used_xmms: HashSet::new(),
|
||||
stack_offset: MachineStackOffset(0),
|
||||
save_area_offset: None,
|
||||
state: x64::new_machine_state(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,13 +133,13 @@ impl Machine {
|
||||
pub fn acquire_locations<E: Emitter>(
|
||||
&mut self,
|
||||
assembler: &mut E,
|
||||
tys: &[WpType],
|
||||
tys: &[(WpType, MachineValue)],
|
||||
zeroed: bool,
|
||||
) -> SmallVec<[Location; 1]> {
|
||||
let mut ret = smallvec![];
|
||||
let mut delta_stack_offset: usize = 0;
|
||||
|
||||
for ty in tys {
|
||||
for (ty, mv) in tys {
|
||||
let loc = match *ty {
|
||||
WpType::F32 | WpType::F64 => self.pick_xmm().map(Location::XMM),
|
||||
WpType::I32 | WpType::I64 => self.pick_gpr().map(Location::GPR),
|
||||
@ -151,9 +155,14 @@ impl Machine {
|
||||
};
|
||||
if let Location::GPR(x) = loc {
|
||||
self.used_gprs.insert(x);
|
||||
self.state.register_values[X64Register::GPR(x).to_index().0] = *mv;
|
||||
} else if let Location::XMM(x) = loc {
|
||||
self.used_xmms.insert(x);
|
||||
self.state.register_values[X64Register::XMM(x).to_index().0] = *mv;
|
||||
} else {
|
||||
self.state.stack_values.push(*mv);
|
||||
}
|
||||
self.state.wasm_stack.push(WasmAbstractValue::Runtime);
|
||||
ret.push(loc);
|
||||
}
|
||||
|
||||
@ -180,9 +189,13 @@ impl Machine {
|
||||
match *loc {
|
||||
Location::GPR(ref x) => {
|
||||
assert_eq!(self.used_gprs.remove(x), true);
|
||||
self.state.register_values[X64Register::GPR(*x).to_index().0] =
|
||||
MachineValue::Undefined;
|
||||
}
|
||||
Location::XMM(ref x) => {
|
||||
assert_eq!(self.used_xmms.remove(x), true);
|
||||
self.state.register_values[X64Register::XMM(*x).to_index().0] =
|
||||
MachineValue::Undefined;
|
||||
}
|
||||
Location::Memory(GPR::RBP, x) => {
|
||||
if x >= 0 {
|
||||
@ -194,9 +207,11 @@ impl Machine {
|
||||
}
|
||||
self.stack_offset.0 -= 8;
|
||||
delta_stack_offset += 8;
|
||||
self.state.stack_values.pop().unwrap();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
self.state.wasm_stack.pop().unwrap();
|
||||
}
|
||||
|
||||
if delta_stack_offset != 0 {
|
||||
@ -213,12 +228,17 @@ impl Machine {
|
||||
match *loc {
|
||||
Location::GPR(ref x) => {
|
||||
assert_eq!(self.used_gprs.remove(x), true);
|
||||
self.state.register_values[X64Register::GPR(*x).to_index().0] =
|
||||
MachineValue::Undefined;
|
||||
}
|
||||
Location::XMM(ref x) => {
|
||||
assert_eq!(self.used_xmms.remove(x), true);
|
||||
self.state.register_values[X64Register::XMM(*x).to_index().0] =
|
||||
MachineValue::Undefined;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// Wasm state popping is deferred to `release_locations_only_osr_state`.
|
||||
}
|
||||
}
|
||||
|
||||
@ -241,9 +261,11 @@ impl Machine {
|
||||
}
|
||||
self.stack_offset.0 -= 8;
|
||||
delta_stack_offset += 8;
|
||||
self.state.stack_values.pop().unwrap();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// Wasm state popping is deferred to `release_locations_only_osr_state`.
|
||||
}
|
||||
|
||||
if delta_stack_offset != 0 {
|
||||
@ -255,6 +277,12 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn release_locations_only_osr_state(&mut self, n: usize) {
|
||||
for _ in 0..n {
|
||||
self.state.wasm_stack.pop().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn release_locations_keep_state<E: Emitter>(&self, assembler: &mut E, locs: &[Location]) {
|
||||
let mut delta_stack_offset: usize = 0;
|
||||
let mut stack_offset = self.stack_offset.0;
|
||||
@ -314,7 +342,11 @@ impl Machine {
|
||||
allocated += 1;
|
||||
get_local_location(old_idx)
|
||||
}
|
||||
Location::Memory(_, _) => loc,
|
||||
Location::Memory(_, _) => {
|
||||
let old_idx = allocated;
|
||||
allocated += 1;
|
||||
get_local_location(old_idx)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
});
|
||||
}
|
||||
@ -325,6 +357,19 @@ impl Machine {
|
||||
allocated += 1;
|
||||
}
|
||||
|
||||
for (i, loc) in locations.iter().enumerate() {
|
||||
match *loc {
|
||||
Location::GPR(x) => {
|
||||
self.state.register_values[X64Register::GPR(x).to_index().0] =
|
||||
MachineValue::WasmLocal(i);
|
||||
}
|
||||
Location::Memory(_, _) => {
|
||||
self.state.stack_values.push(MachineValue::WasmLocal(i));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
// How many machine stack slots did all the locals use?
|
||||
let num_mem_slots = locations
|
||||
.iter()
|
||||
@ -346,15 +391,21 @@ impl Machine {
|
||||
|
||||
// Save callee-saved registers.
|
||||
for loc in locations.iter() {
|
||||
if let Location::GPR(_) = *loc {
|
||||
if let Location::GPR(x) = *loc {
|
||||
a.emit_push(Size::S64, *loc);
|
||||
self.stack_offset.0 += 8;
|
||||
self.state.stack_values.push(MachineValue::PreserveRegister(
|
||||
X64Register::GPR(x).to_index(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Save R15 for vmctx use.
|
||||
a.emit_push(Size::S64, Location::GPR(GPR::R15));
|
||||
self.stack_offset.0 += 8;
|
||||
self.state.stack_values.push(MachineValue::PreserveRegister(
|
||||
X64Register::GPR(GPR::R15).to_index(),
|
||||
));
|
||||
|
||||
// Save the offset of static area.
|
||||
self.save_area_offset = Some(MachineStackOffset(self.stack_offset.0));
|
||||
@ -366,7 +417,17 @@ impl Machine {
|
||||
Location::GPR(_) => {
|
||||
a.emit_mov(Size::S64, loc, locations[i]);
|
||||
}
|
||||
_ => break,
|
||||
Location::Memory(_, _) => match locations[i] {
|
||||
Location::GPR(_) => {
|
||||
a.emit_mov(Size::S64, loc, locations[i]);
|
||||
}
|
||||
Location::Memory(_, _) => {
|
||||
a.emit_mov(Size::S64, loc, Location::GPR(GPR::RAX));
|
||||
a.emit_mov(Size::S64, Location::GPR(GPR::RAX), locations[i]);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -429,7 +490,11 @@ mod test {
|
||||
fn test_release_locations_keep_state_nopanic() {
|
||||
let mut machine = Machine::new();
|
||||
let mut assembler = Assembler::new().unwrap();
|
||||
let locs = machine.acquire_locations(&mut assembler, &[WpType::I32; 10], false);
|
||||
let locs = machine.acquire_locations(
|
||||
&mut assembler,
|
||||
&[(WpType::I32, MachineValue::Undefined); 10],
|
||||
false,
|
||||
);
|
||||
|
||||
machine.release_locations_keep_state(&mut assembler, &locs);
|
||||
}
|
||||
|
@ -9,77 +9,18 @@
|
||||
//! are very special, the async signal unsafety of Rust's TLS implementation generally does not affect the correctness here
|
||||
//! unless you have memory unsafety elsewhere in your code.
|
||||
//!
|
||||
use libc::{c_int, c_void, siginfo_t};
|
||||
use nix::sys::signal::{
|
||||
sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV,
|
||||
SIGTRAP,
|
||||
};
|
||||
use std::any::Any;
|
||||
use std::cell::{Cell, RefCell, UnsafeCell};
|
||||
use std::collections::HashMap;
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Once;
|
||||
use wasmer_runtime_core::codegen::BkptInfo;
|
||||
use std::cell::Cell;
|
||||
use wasmer_runtime_core::codegen::BreakpointMap;
|
||||
use wasmer_runtime_core::fault::{begin_unsafe_unwind, catch_unsafe_unwind, ensure_sighandler};
|
||||
use wasmer_runtime_core::typed_func::WasmTrapInfo;
|
||||
|
||||
extern "C" fn signal_trap_handler(
|
||||
signum: ::nix::libc::c_int,
|
||||
siginfo: *mut siginfo_t,
|
||||
ucontext: *mut c_void,
|
||||
) {
|
||||
unsafe {
|
||||
match Signal::from_c_int(signum) {
|
||||
Ok(SIGTRAP) => {
|
||||
let (_, ip) = get_faulting_addr_and_ip(siginfo as _, ucontext);
|
||||
let bkpt_map = BKPT_MAP.with(|x| x.borrow().last().map(|x| x.clone()));
|
||||
if let Some(bkpt_map) = bkpt_map {
|
||||
if let Some(ref x) = bkpt_map.get(&(ip as usize)) {
|
||||
(x)(BkptInfo { throw: throw });
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
do_unwind(signum, siginfo as _, ucontext);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
pub fn setjmp(env: *mut c_void) -> c_int;
|
||||
fn longjmp(env: *mut c_void, val: c_int) -> !;
|
||||
}
|
||||
|
||||
pub unsafe fn install_sighandler() {
|
||||
let sa = SigAction::new(
|
||||
SigHandler::SigAction(signal_trap_handler),
|
||||
SaFlags::SA_ONSTACK,
|
||||
SigSet::empty(),
|
||||
);
|
||||
sigaction(SIGFPE, &sa).unwrap();
|
||||
sigaction(SIGILL, &sa).unwrap();
|
||||
sigaction(SIGSEGV, &sa).unwrap();
|
||||
sigaction(SIGBUS, &sa).unwrap();
|
||||
sigaction(SIGTRAP, &sa).unwrap();
|
||||
}
|
||||
|
||||
const SETJMP_BUFFER_LEN: usize = 27;
|
||||
pub static SIGHANDLER_INIT: Once = Once::new();
|
||||
|
||||
thread_local! {
|
||||
pub static SETJMP_BUFFER: UnsafeCell<[c_int; SETJMP_BUFFER_LEN]> = UnsafeCell::new([0; SETJMP_BUFFER_LEN]);
|
||||
pub static CAUGHT_ADDRESSES: Cell<(*const c_void, *const c_void)> = Cell::new((ptr::null(), ptr::null()));
|
||||
pub static CURRENT_EXECUTABLE_BUFFER: Cell<*const c_void> = Cell::new(ptr::null());
|
||||
pub static TRAP_EARLY_DATA: Cell<Option<Box<dyn Any>>> = Cell::new(None);
|
||||
pub static BKPT_MAP: RefCell<Vec<Arc<HashMap<usize, Box<Fn(BkptInfo) + Send + Sync + 'static>>>>> = RefCell::new(Vec::new());
|
||||
}
|
||||
|
||||
pub unsafe fn trigger_trap() -> ! {
|
||||
let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get());
|
||||
|
||||
longjmp(jmp_buf as *mut c_void, 0)
|
||||
begin_unsafe_unwind(Box::new(()));
|
||||
}
|
||||
|
||||
pub enum CallProtError {
|
||||
@ -87,157 +28,26 @@ pub enum CallProtError {
|
||||
Error(Box<dyn Any>),
|
||||
}
|
||||
|
||||
pub fn call_protected<T>(f: impl FnOnce() -> T) -> Result<T, CallProtError> {
|
||||
pub fn call_protected<T>(
|
||||
f: impl FnOnce() -> T,
|
||||
breakpoints: Option<BreakpointMap>,
|
||||
) -> Result<T, CallProtError> {
|
||||
ensure_sighandler();
|
||||
unsafe {
|
||||
let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get());
|
||||
let prev_jmp_buf = *jmp_buf;
|
||||
|
||||
SIGHANDLER_INIT.call_once(|| {
|
||||
install_sighandler();
|
||||
});
|
||||
|
||||
let signum = setjmp(jmp_buf as *mut _);
|
||||
if signum != 0 {
|
||||
*jmp_buf = prev_jmp_buf;
|
||||
|
||||
if let Some(data) = TRAP_EARLY_DATA.with(|cell| cell.replace(None)) {
|
||||
Err(CallProtError::Error(data))
|
||||
} else {
|
||||
// let (faulting_addr, _inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get());
|
||||
|
||||
// let signal = match Signal::from_c_int(signum) {
|
||||
// Ok(SIGFPE) => "floating-point exception",
|
||||
// Ok(SIGILL) => "illegal instruction",
|
||||
// Ok(SIGSEGV) => "segmentation violation",
|
||||
// Ok(SIGBUS) => "bus error",
|
||||
// Err(_) => "error while getting the Signal",
|
||||
// _ => "unknown trapped signal",
|
||||
// };
|
||||
// // When the trap-handler is fully implemented, this will return more information.
|
||||
// Err(RuntimeError::Trap {
|
||||
// msg: format!("unknown trap at {:p} - {}", faulting_addr, signal).into(),
|
||||
// }
|
||||
// .into())
|
||||
Err(CallProtError::Trap(WasmTrapInfo::Unknown))
|
||||
let ret = catch_unsafe_unwind(|| f(), breakpoints);
|
||||
match ret {
|
||||
Ok(x) => Ok(x),
|
||||
Err(e) => {
|
||||
if let Some(data) = TRAP_EARLY_DATA.with(|cell| cell.replace(None)) {
|
||||
Err(CallProtError::Error(data))
|
||||
} else {
|
||||
Err(CallProtError::Error(e))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let ret = f(); // TODO: Switch stack?
|
||||
*jmp_buf = prev_jmp_buf;
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn throw(payload: Box<dyn Any>) -> ! {
|
||||
let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get());
|
||||
if *jmp_buf == [0; SETJMP_BUFFER_LEN] {
|
||||
::std::process::abort();
|
||||
}
|
||||
TRAP_EARLY_DATA.with(|cell| cell.replace(Some(payload)));
|
||||
longjmp(jmp_buf as *mut ::nix::libc::c_void, 0xffff);
|
||||
}
|
||||
|
||||
/// Unwinds to last protected_call.
|
||||
pub unsafe fn do_unwind(signum: i32, siginfo: *const c_void, ucontext: *const c_void) -> ! {
|
||||
// Since do_unwind is only expected to get called from WebAssembly code which doesn't hold any host resources (locks etc.)
|
||||
// itself, accessing TLS here is safe. In case any other code calls this, it often indicates a memory safety bug and you should
|
||||
// temporarily disable the signal handlers to debug it.
|
||||
|
||||
let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get());
|
||||
if *jmp_buf == [0; SETJMP_BUFFER_LEN] {
|
||||
::std::process::abort();
|
||||
}
|
||||
|
||||
CAUGHT_ADDRESSES.with(|cell| cell.set(get_faulting_addr_and_ip(siginfo, ucontext)));
|
||||
|
||||
longjmp(jmp_buf as *mut ::nix::libc::c_void, signum)
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
||||
unsafe fn get_faulting_addr_and_ip(
|
||||
siginfo: *const c_void,
|
||||
ucontext: *const c_void,
|
||||
) -> (*const c_void, *const c_void) {
|
||||
use libc::{ucontext_t, RIP};
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[repr(C)]
|
||||
struct siginfo_t {
|
||||
si_signo: i32,
|
||||
si_errno: i32,
|
||||
si_code: i32,
|
||||
si_addr: u64,
|
||||
// ...
|
||||
}
|
||||
|
||||
let siginfo = siginfo as *const siginfo_t;
|
||||
let si_addr = (*siginfo).si_addr;
|
||||
|
||||
let ucontext = ucontext as *const ucontext_t;
|
||||
let rip = (*ucontext).uc_mcontext.gregs[RIP as usize];
|
||||
|
||||
(si_addr as _, rip as _)
|
||||
}
|
||||
|
||||
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
|
||||
unsafe fn get_faulting_addr_and_ip(
|
||||
siginfo: *const c_void,
|
||||
ucontext: *const c_void,
|
||||
) -> (*const c_void, *const c_void) {
|
||||
#[allow(dead_code)]
|
||||
#[repr(C)]
|
||||
struct ucontext_t {
|
||||
uc_onstack: u32,
|
||||
uc_sigmask: u32,
|
||||
uc_stack: libc::stack_t,
|
||||
uc_link: *const ucontext_t,
|
||||
uc_mcsize: u64,
|
||||
uc_mcontext: *const mcontext_t,
|
||||
}
|
||||
#[repr(C)]
|
||||
struct exception_state {
|
||||
trapno: u16,
|
||||
cpu: u16,
|
||||
err: u32,
|
||||
faultvaddr: u64,
|
||||
}
|
||||
#[repr(C)]
|
||||
struct regs {
|
||||
rax: u64,
|
||||
rbx: u64,
|
||||
rcx: u64,
|
||||
rdx: u64,
|
||||
rdi: u64,
|
||||
rsi: u64,
|
||||
rbp: u64,
|
||||
rsp: u64,
|
||||
r8: u64,
|
||||
r9: u64,
|
||||
r10: u64,
|
||||
r11: u64,
|
||||
r12: u64,
|
||||
r13: u64,
|
||||
r14: u64,
|
||||
r15: u64,
|
||||
rip: u64,
|
||||
rflags: u64,
|
||||
cs: u64,
|
||||
fs: u64,
|
||||
gs: u64,
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
#[repr(C)]
|
||||
struct mcontext_t {
|
||||
es: exception_state,
|
||||
ss: regs,
|
||||
// ...
|
||||
}
|
||||
|
||||
let siginfo = siginfo as *const siginfo_t;
|
||||
let si_addr = (*siginfo).si_addr;
|
||||
|
||||
let ucontext = ucontext as *const ucontext_t;
|
||||
let rip = (*(*ucontext).uc_mcontext).ss.rip;
|
||||
|
||||
(si_addr, rip as _)
|
||||
begin_unsafe_unwind(payload);
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ use wasmer_clif_backend::CraneliftCompiler;
|
||||
use wasmer_llvm_backend::LLVMCompiler;
|
||||
use wasmer_runtime::{
|
||||
cache::{Cache as BaseCache, FileSystemCache, WasmHash, WASMER_VERSION_HASH},
|
||||
error::RuntimeError,
|
||||
Func, Value,
|
||||
};
|
||||
use wasmer_runtime_core::{
|
||||
@ -112,6 +111,10 @@ struct Run {
|
||||
)]
|
||||
loader: Option<LoaderName>,
|
||||
|
||||
#[cfg(feature = "backend:singlepass")]
|
||||
#[structopt(long = "resume")]
|
||||
resume: Option<String>,
|
||||
|
||||
#[structopt(long = "command-name", hidden = true)]
|
||||
command_name: Option<String>,
|
||||
|
||||
@ -151,7 +154,7 @@ impl FromStr for LoaderName {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
enum Backend {
|
||||
Cranelift,
|
||||
Singlepass,
|
||||
@ -483,7 +486,7 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
} else {
|
||||
if cfg!(feature = "wasi") && wasmer_wasi::is_wasi_module(&module) {
|
||||
let import_object = wasmer_wasi::generate_import_object(
|
||||
let mut import_object = wasmer_wasi::generate_import_object(
|
||||
if let Some(cn) = &options.command_name {
|
||||
[cn.clone()]
|
||||
} else {
|
||||
@ -502,24 +505,95 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
|
||||
mapped_dirs,
|
||||
);
|
||||
|
||||
let instance = module
|
||||
let mut instance = module
|
||||
.instantiate(&import_object)
|
||||
.map_err(|e| format!("Can't instantiate module: {:?}", e))?;
|
||||
|
||||
let start: Func<(), ()> = instance.func("_start").map_err(|e| format!("{:?}", e))?;
|
||||
|
||||
let result = start.call();
|
||||
#[cfg(feature = "backend:singlepass")]
|
||||
unsafe {
|
||||
if options.backend == Backend::Singlepass {
|
||||
use wasmer_runtime_core::fault::{catch_unsafe_unwind, ensure_sighandler};
|
||||
use wasmer_runtime_core::state::{
|
||||
x64::invoke_call_return_on_stack, InstanceImage,
|
||||
};
|
||||
use wasmer_runtime_core::vm::Ctx;
|
||||
|
||||
if let Err(ref err) = result {
|
||||
match err {
|
||||
RuntimeError::Trap { msg } => panic!("wasm trap occured: {}", msg),
|
||||
RuntimeError::Error { data } => {
|
||||
if let Some(error_code) = data.downcast_ref::<wasmer_wasi::ExitCode>() {
|
||||
std::process::exit(error_code.code as i32)
|
||||
ensure_sighandler();
|
||||
|
||||
let start_raw: extern "C" fn(&mut Ctx) =
|
||||
::std::mem::transmute(start.get_vm_func());
|
||||
|
||||
let mut image: Option<InstanceImage> = if let Some(ref path) = options.resume {
|
||||
let mut f = File::open(path).unwrap();
|
||||
let mut out: Vec<u8> = vec![];
|
||||
f.read_to_end(&mut out).unwrap();
|
||||
Some(InstanceImage::from_bytes(&out).expect("failed to decode image"))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let breakpoints = instance.module.runnable_module.get_breakpoints();
|
||||
|
||||
loop {
|
||||
let ret = if let Some(image) = image.take() {
|
||||
let msm = instance
|
||||
.module
|
||||
.runnable_module
|
||||
.get_module_state_map()
|
||||
.unwrap();
|
||||
let code_base =
|
||||
instance.module.runnable_module.get_code().unwrap().as_ptr()
|
||||
as usize;
|
||||
invoke_call_return_on_stack(
|
||||
&msm,
|
||||
code_base,
|
||||
image,
|
||||
instance.context_mut(),
|
||||
breakpoints.clone(),
|
||||
)
|
||||
.map(|_| ())
|
||||
} else {
|
||||
catch_unsafe_unwind(
|
||||
|| start_raw(instance.context_mut()),
|
||||
breakpoints.clone(),
|
||||
)
|
||||
};
|
||||
if let Err(e) = ret {
|
||||
if let Some(new_image) = e.downcast_ref::<InstanceImage>() {
|
||||
let op = interactive_shell(InteractiveShellContext {
|
||||
image: Some(new_image.clone()),
|
||||
});
|
||||
match op {
|
||||
ShellExitOperation::ContinueWith(new_image) => {
|
||||
image = Some(new_image);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err("Error while executing WebAssembly".into());
|
||||
}
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
panic!("error: {:?}", err)
|
||||
}
|
||||
|
||||
{
|
||||
use wasmer_runtime::error::RuntimeError;
|
||||
let result = start.call();
|
||||
|
||||
if let Err(ref err) = result {
|
||||
match err {
|
||||
RuntimeError::Trap { msg } => panic!("wasm trap occured: {}", msg),
|
||||
RuntimeError::Error { data } => {
|
||||
if let Some(error_code) = data.downcast_ref::<wasmer_wasi::ExitCode>() {
|
||||
std::process::exit(error_code.code as i32)
|
||||
}
|
||||
}
|
||||
}
|
||||
panic!("error: {:?}", err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let import_object = wasmer_runtime_core::import::ImportObject::new();
|
||||
@ -544,11 +618,95 @@ fn execute_wasm(options: &Run) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend:singlepass")]
|
||||
struct InteractiveShellContext {
|
||||
image: Option<wasmer_runtime_core::state::InstanceImage>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend:singlepass")]
|
||||
#[derive(Debug)]
|
||||
enum ShellExitOperation {
|
||||
ContinueWith(wasmer_runtime_core::state::InstanceImage),
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend:singlepass")]
|
||||
fn interactive_shell(mut ctx: InteractiveShellContext) -> ShellExitOperation {
|
||||
use std::io::Write;
|
||||
|
||||
let mut stdout = ::std::io::stdout();
|
||||
let stdin = ::std::io::stdin();
|
||||
|
||||
loop {
|
||||
print!("Wasmer> ");
|
||||
stdout.flush().unwrap();
|
||||
let mut line = String::new();
|
||||
stdin.read_line(&mut line).unwrap();
|
||||
let mut parts = line.split(" ").filter(|x| x.len() > 0).map(|x| x.trim());
|
||||
|
||||
let cmd = parts.next();
|
||||
if cmd.is_none() {
|
||||
println!("Command required");
|
||||
continue;
|
||||
}
|
||||
let cmd = cmd.unwrap();
|
||||
|
||||
match cmd {
|
||||
"snapshot" => {
|
||||
let path = parts.next();
|
||||
if path.is_none() {
|
||||
println!("Usage: snapshot [out_path]");
|
||||
continue;
|
||||
}
|
||||
let path = path.unwrap();
|
||||
|
||||
if let Some(ref image) = ctx.image {
|
||||
let buf = image.to_bytes();
|
||||
let mut f = match File::create(path) {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
println!("Cannot open output file at {}: {:?}", path, e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if let Err(e) = f.write_all(&buf) {
|
||||
println!("Cannot write to output file at {}: {:?}", path, e);
|
||||
continue;
|
||||
}
|
||||
println!("Done");
|
||||
} else {
|
||||
println!("Program state not available");
|
||||
}
|
||||
}
|
||||
"continue" | "c" => {
|
||||
if let Some(image) = ctx.image.take() {
|
||||
return ShellExitOperation::ContinueWith(image);
|
||||
} else {
|
||||
println!("Program state not available, cannot continue execution");
|
||||
}
|
||||
}
|
||||
"backtrace" | "bt" => {
|
||||
if let Some(ref image) = ctx.image {
|
||||
println!("{}", image.execution_state.colored_output());
|
||||
} else {
|
||||
println!("State not available");
|
||||
}
|
||||
}
|
||||
"exit" | "quit" => {
|
||||
exit(0);
|
||||
}
|
||||
"" => {}
|
||||
_ => {
|
||||
println!("Unknown command: {}", cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run(options: Run) {
|
||||
match execute_wasm(&options) {
|
||||
Ok(()) => {}
|
||||
Err(message) => {
|
||||
eprintln!("{:?}", message);
|
||||
eprintln!("execute_wasm: {:?}", message);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user