summaryrefslogtreecommitdiffstats
path: root/third_party/rust/lucet-runtime-wasmsbx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/lucet-runtime-wasmsbx
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/lucet-runtime-wasmsbx')
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/.cargo-checksum.json1
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/Cargo.toml51
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/LICENSE220
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/build.rs12
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/include/lucet.h86
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/include/lucet_types.h199
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/include/lucet_val.h103
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/include/lucet_vmctx.h45
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/src/c_api.rs530
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/src/lib.rs384
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/c_api.c180
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/entrypoint.rs3
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/globals.rs3
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/guest_fault.rs3
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/guests/null.c3
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/guests/yield_resume.c8
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/guests/yield_resume_bindings.json5
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/host.rs3
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting.rs71
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/arithmetic_count.wat11
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/br_table_count.wat25
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/calls.wat22
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/count_after_br.wat21
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/empty_loop.wat9
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/empty_loop_2.wat11
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/if_count.wat21
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/if_not_taken_count.wat21
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/indirect_calls.wat68
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/loops.wat30
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/unreachable_call.wat17
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/memory.rs3
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/stack.rs3
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/start.rs3
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/strcmp.rs3
-rw-r--r--third_party/rust/lucet-runtime-wasmsbx/tests/val.rs61
35 files changed, 2239 insertions, 0 deletions
diff --git a/third_party/rust/lucet-runtime-wasmsbx/.cargo-checksum.json b/third_party/rust/lucet-runtime-wasmsbx/.cargo-checksum.json
new file mode 100644
index 0000000000..1c9f594bc3
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.toml":"60d8bd19066dc4253baa44d665a750a4b831b62f7938b63c0e0d88d439ced9b6","LICENSE":"268872b9816f90fd8e85db5a28d33f8150ebb8dd016653fb39ef1f94f2686bc5","build.rs":"d56fd4e2fe9d0830aee061be443ef0b541ed98dd6a4233220a51745478b3fb77","include/lucet.h":"89ff353483287040e07e3f6eaeed4286cb06f541fb67057f9fdbbf844a7ec928","include/lucet_types.h":"95c7793034c71e123fac04ce61dc89f4930360b6d4af0f40582c84357fd6877d","include/lucet_val.h":"220b862b0280ac4f7962d44520f6cc34a47f3b3a34b8ecf389e6111cd95b8bf0","include/lucet_vmctx.h":"044ca80cf8cca51620de3f113a1c07c00ebecea108a45288f9b87e5e2fbb820c","src/c_api.rs":"97e28537fffeac5d21f09911a3474114997ce0268492edb8010ce8ca178a6c64","src/lib.rs":"48f0ba473b4da3616bddfce3cda00cf836eca3c9e7b055855156c1c167a6f908","tests/c_api.c":"772716a14ceed6913a3670d8ca3fcf0cb0c376493cabae4c8b527fb694554b7b","tests/entrypoint.rs":"1d0080e9f71a16f61e69c21922ac1c2207b2003fb13d3bacafb681be5cceb8b9","tests/globals.rs":"56248c5cefbaad546b08964d9193e5c16a461e974d1c9c520ebba3f1cf32af66","tests/guest_fault.rs":"cb4728d0851148f992c944199f4fc98595070d0879e1ddf5361419e4fc330ad3","tests/guests/null.c":"de2bf0dc98ac1acaef8cb856a300119aa1300d228a1dceb0b7e06b63d3ac712a","tests/guests/yield_resume.c":"b9c19d9c2d540e042d3488f2cea04a7a98cc75910c68faad738ad835b04dde9d","tests/guests/yield_resume_bindings.json":"e124e940038a552d08495d6f99b7dd8495a011e4a082b32ab8d035e40169d663","tests/host.rs":"0cd5f40ed9725c7ef2f497a4e31f09e88833841240dfbfe971d60724075eac00","tests/instruction_counting.rs":"6aa8c2cf4c32adbbb5bf74e8449dd29d4deff77dcb55947a2d15c4eccbb89d6e","tests/instruction_counting/arithmetic_count.wat":"c347c7a78e4159e45aa5ba299f1d03e1efbb7c635f13dd23053907de7f57193b","tests/instruction_counting/br_table_count.wat":"66cdac7595bd69103f5d1b825a58f09a5624db4e35c91e11ae7b30cc2f53eb0c","tests/instruction_counting/calls.wat":"8212736a55a897937c712bab33c22c62ab96f1b32eef44eab1cbee77f34ba641","tests/instruction_counting/count_after_br.wat":"12df2019f3b0c518bd747e485b423f73a82c91571e834bdeed5d7bd6455f84c0","tests/instruction_counting/empty_loop.wat":"3dfa9ab297a7f8fd21bd22db8ded37de0545ac741be132fdcdf1a61c50cf455a","tests/instruction_counting/empty_loop_2.wat":"c65b4516a1a26f9b8b0117bb1f4fe9282e3bc79052b89f834cad6c202a51f68e","tests/instruction_counting/if_count.wat":"620330be046a3b3890646e8fdbbde6d4a8c4763b450d818ee549e103a1c3e3aa","tests/instruction_counting/if_not_taken_count.wat":"12bdc1f0512016e549bd7656386274c7b7b92d32b5d3eeb73b93d6ef18cc4ff2","tests/instruction_counting/indirect_calls.wat":"f56f6a715a733176168a81ca6cbfd22333ab9755029174a3f7950da9b401320e","tests/instruction_counting/loops.wat":"0cd765b1cf694a2c35df69547b30303731512c937cad05e1c052ebcd7840e3b1","tests/instruction_counting/unreachable_call.wat":"c15653c3ff198f321f8b892c363ec7badad75ec703c5b3a215a6dbfb059a15b7","tests/memory.rs":"73b65dc44978181a5d2a3b39a6d0fc60fd6637e16387c2a7b025baedb16a004f","tests/stack.rs":"5e5939991a55c8ecd579d8c509226ed3ac7bae5a847bdcdac4d285df28d54d6b","tests/start.rs":"7b31f8c289c1ad52d05f17c0c46b0856382b0ff85d6e5579c4346c443eb93fb3","tests/strcmp.rs":"7420aa723630af51360a9e9a3763c2fd8d7c57aa8d4011cfd8ca452376036efb","tests/val.rs":"2e38a7aa10bde34d5cf580a958c07eff8593662a86678427a79d8a5f5bc2efac"},"package":null} \ No newline at end of file
diff --git a/third_party/rust/lucet-runtime-wasmsbx/Cargo.toml b/third_party/rust/lucet-runtime-wasmsbx/Cargo.toml
new file mode 100644
index 0000000000..4c114da7d6
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/Cargo.toml
@@ -0,0 +1,51 @@
+[package]
+name = "lucet-runtime-wasmsbx"
+version = "0.1.1"
+description = "Pure Rust runtime for Lucet WebAssembly toolchain"
+homepage = "https://github.com/fastly/lucet"
+repository = "https://github.com/fastly/lucet"
+license = "Apache-2.0 WITH LLVM-exception"
+categories = ["wasm"]
+authors = ["Lucet team <lucet@fastly.com>"]
+edition = "2018"
+
+[dependencies]
+libc = "0.2.65"
+lucet-runtime-internals = { path = "lucet-runtime-internals", version = "0.1.1", package = "lucet-runtime-internals-wasmsbx" }
+lucet-module = { path = "../lucet-module", version = "0.1.1", package="lucet-module-wasmsbx" }
+num-traits = "0.2"
+num-derive = "0.3"
+
+[dev-dependencies]
+byteorder = "1.2"
+failure = "0.1"
+lazy_static = "1.1"
+lucetc = { path = "../lucetc" }
+lucet-runtime-tests = { path = "lucet-runtime-tests", version = "0.1.1" }
+lucet-wasi-sdk = { path = "../lucet-wasi-sdk" }
+nix = "0.13"
+rayon = "1.0"
+tempfile = "3.0"
+
+[build-dependencies]
+# only used for tests
+cc = "1.0"
+
+[features]
+signature_checking = ["lucet-module/signature_checking"]
+
+[lib]
+name = "lucet_runtime"
+crate-type = ["rlib", "staticlib"]
+
+[package.metadata.deb]
+name = "fst-lucet-runtime"
+maintainer = "Adam C. Foltzer <acfoltzer@fastly.com>"
+depends = "$auto"
+priority = "optional"
+assets = [
+ ["target/release/liblucet_runtime.a", "/opt/fst-lucet-runtime/lib/", "644"],
+ ["target/release/liblucet_runtime.rlib", "/opt/fst-lucet-runtime/lib/", "644"],
+ ["target/release/liblucet_runtime.so", "/opt/fst-lucet-runtime/lib/", "755"],
+ ["include/*.h", "/opt/fst-lucet-runtime/include/", "644"],
+]
diff --git a/third_party/rust/lucet-runtime-wasmsbx/LICENSE b/third_party/rust/lucet-runtime-wasmsbx/LICENSE
new file mode 100644
index 0000000000..f9d81955f4
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/LICENSE
@@ -0,0 +1,220 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+--- LLVM Exceptions to the Apache 2.0 License ----
+
+As an exception, if, as a result of your compiling your source code, portions
+of this Software are embedded into an Object form of such source code, you
+may redistribute such embedded portions in such Object form without complying
+with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
+
+In addition, if you combine or link compiled forms of this Software with
+software that is licensed under the GPLv2 ("Combined Software") and if a
+court of competent jurisdiction determines that the patent provision (Section
+3), the indemnity provision (Section 9) or other Section of the License
+conflicts with the conditions of the GPLv2, you may retroactively and
+prospectively choose to deem waived or otherwise exclude such Section(s) of
+the License, but only in their entirety and only with respect to the Combined
+Software.
+
diff --git a/third_party/rust/lucet-runtime-wasmsbx/build.rs b/third_party/rust/lucet-runtime-wasmsbx/build.rs
new file mode 100644
index 0000000000..8abc728f8b
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/build.rs
@@ -0,0 +1,12 @@
+use cc;
+
+fn main() {
+ build_c_api_tests();
+}
+
+fn build_c_api_tests() {
+ cc::Build::new()
+ .file("tests/c_api.c")
+ .include("include")
+ .compile("lucet_runtime_c_api_tests");
+}
diff --git a/third_party/rust/lucet-runtime-wasmsbx/include/lucet.h b/third_party/rust/lucet-runtime-wasmsbx/include/lucet.h
new file mode 100644
index 0000000000..b360784ce7
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/include/lucet.h
@@ -0,0 +1,86 @@
+#ifndef LUCET_H
+#define LUCET_H
+
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "lucet_types.h"
+#include "lucet_val.h"
+#include "lucet_vmctx.h"
+
+#define LUCET_WASM_PAGE_SIZE (64 * 1024)
+
+enum lucet_error lucet_dl_module_load(const char *path, struct lucet_dl_module **mod_out);
+
+void lucet_dl_module_release(const struct lucet_dl_module *module);
+
+const char *lucet_error_name(enum lucet_error e);
+
+bool lucet_instance_check_heap(const struct lucet_instance *inst, const void *ptr, uintptr_t len);
+
+void *lucet_instance_embed_ctx(struct lucet_instance *inst);
+
+enum lucet_error lucet_instance_grow_heap(struct lucet_instance *inst,
+ uint32_t additional_pages,
+ uint32_t * previous_pages_out);
+
+uint8_t *lucet_instance_heap(struct lucet_instance *inst);
+
+uint32_t lucet_instance_heap_len(const struct lucet_instance *inst);
+
+void lucet_instance_release(struct lucet_instance *inst);
+
+enum lucet_error lucet_instance_reset(struct lucet_instance *inst);
+
+enum lucet_error lucet_instance_run(struct lucet_instance * inst,
+ const char * entrypoint,
+ uintptr_t argc,
+ const struct lucet_val *argv,
+ struct lucet_result * result_out);
+
+enum lucet_error lucet_instance_run_func_idx(struct lucet_instance * inst,
+ uint32_t table_idx,
+ uint32_t func_idx,
+ uintptr_t argc,
+ const struct lucet_val *argv,
+ struct lucet_result * result_out);
+
+enum lucet_error
+lucet_instance_resume(struct lucet_instance *inst, void *val, struct lucet_result *result_out);
+
+enum lucet_error lucet_instance_set_fatal_handler(struct lucet_instance *inst,
+ lucet_fatal_handler fatal_handler);
+
+/**
+ * Release or run* must not be called in the body of this function!
+ */
+enum lucet_error lucet_instance_set_signal_handler(struct lucet_instance *inst,
+ lucet_signal_handler signal_handler);
+
+enum lucet_error lucet_mmap_region_create(uint64_t instance_capacity,
+ const struct lucet_alloc_limits *limits,
+ struct lucet_region ** region_out);
+
+enum lucet_error lucet_region_new_instance(const struct lucet_region * region,
+ const struct lucet_dl_module *module,
+ struct lucet_instance ** inst_out);
+
+enum lucet_error lucet_region_new_instance_with_ctx(const struct lucet_region * region,
+ const struct lucet_dl_module *module,
+ void * embed_ctx,
+ struct lucet_instance ** inst_out);
+
+void lucet_region_release(const struct lucet_region *region);
+
+float lucet_retval_f32(const struct lucet_untyped_retval *retval);
+
+double lucet_retval_f64(const struct lucet_untyped_retval *retval);
+
+union lucet_retval_gp lucet_retval_gp(const struct lucet_untyped_retval *retval);
+
+const char *lucet_result_tag_name(enum lucet_result_tag tag);
+
+#endif /* LUCET_H */
diff --git a/third_party/rust/lucet-runtime-wasmsbx/include/lucet_types.h b/third_party/rust/lucet-runtime-wasmsbx/include/lucet_types.h
new file mode 100644
index 0000000000..d419395c7c
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/include/lucet_types.h
@@ -0,0 +1,199 @@
+#ifndef LUCET_TYPES_H
+#define LUCET_TYPES_H
+
+#ifndef _XOPEN_SOURCE
+#define _XOPEN_SOURCE 500
+#endif
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#ifdef __APPLE__
+#include <sys/ucontext.h>
+#else
+#include <ucontext.h>
+#endif
+
+enum lucet_error {
+ lucet_error_ok,
+ lucet_error_invalid_argument,
+ lucet_error_region_full,
+ lucet_error_module,
+ lucet_error_limits_exceeded,
+ lucet_error_no_linear_memory,
+ lucet_error_symbol_not_found,
+ lucet_error_func_not_found,
+ lucet_error_runtime_fault,
+ lucet_error_runtime_terminated,
+ lucet_error_dl,
+ lucet_error_instance_not_returned,
+ lucet_error_instance_not_yielded,
+ lucet_error_start_yielded,
+ lucet_error_internal,
+ lucet_error_unsupported,
+};
+
+enum lucet_signal_behavior {
+ lucet_signal_behavior_default,
+ lucet_signal_behavior_continue,
+ lucet_signal_behavior_terminate,
+};
+
+enum lucet_terminated_reason {
+ lucet_terminated_reason_signal,
+ lucet_terminated_reason_ctx_not_found,
+ lucet_terminated_reason_yield_type_mismatch,
+ lucet_terminated_reason_borrow_error,
+ lucet_terminated_reason_provided,
+};
+
+enum lucet_trapcode {
+ lucet_trapcode_stack_overflow,
+ lucet_trapcode_heap_out_of_bounds,
+ lucet_trapcode_out_of_bounds,
+ lucet_trapcode_indirect_call_to_null,
+ lucet_trapcode_bad_signature,
+ lucet_trapcode_integer_overflow,
+ lucet_trapcode_integer_div_by_zero,
+ lucet_trapcode_bad_conversion_to_integer,
+ lucet_trapcode_interrupt,
+ lucet_trapcode_table_out_of_bounds,
+ lucet_trapcode_user,
+ lucet_trapcode_unknown,
+};
+
+enum lucet_val_type {
+ lucet_val_type_c_ptr,
+ lucet_val_type_guest_ptr,
+ lucet_val_type_u8,
+ lucet_val_type_u16,
+ lucet_val_type_u32,
+ lucet_val_type_u64,
+ lucet_val_type_i8,
+ lucet_val_type_i16,
+ lucet_val_type_i32,
+ lucet_val_type_i64,
+ lucet_val_type_usize,
+ lucet_val_type_isize,
+ lucet_val_type_bool,
+ lucet_val_type_f32,
+ lucet_val_type_f64,
+};
+
+union lucet_val_inner_val {
+ void * as_c_ptr;
+ uint64_t as_u64;
+ int64_t as_i64;
+ float as_f32;
+ double as_f64;
+};
+
+struct lucet_val {
+ enum lucet_val_type ty;
+ union lucet_val_inner_val inner_val;
+};
+
+struct lucet_dl_module;
+
+struct lucet_instance;
+
+struct lucet_region;
+
+/**
+ * Runtime limits for the various memories that back a Lucet instance.
+ * Each value is specified in bytes, and must be evenly divisible by the host page size (4K).
+ */
+struct lucet_alloc_limits {
+ /**
+ * Max size of the heap, which can be backed by real memory. (default 1M)
+ */
+ uint64_t heap_memory_size;
+ /**
+ * Size of total virtual memory. (default 8G)
+ */
+ uint64_t heap_address_space_size;
+ /**
+ * Size of the guest stack. (default 128K)
+ */
+ uint64_t stack_size;
+ /**
+ * Size of the globals region in bytes; each global uses 8 bytes. (default 4K)
+ */
+ uint64_t globals_size;
+ /**
+ * Size of the signal stack region in bytes.
+ *
+ * SIGSTKSZ from <signals.h> is a good default when linking against a Rust release build of
+ * lucet-runtime, but 12K or more is recommended when using a Rust debug build.
+ */
+ uint64_t signal_stack_size;
+};
+
+typedef enum lucet_signal_behavior (*lucet_signal_handler)(struct lucet_instance * inst,
+ const enum lucet_trapcode trap,
+ int signum, const siginfo_t *siginfo,
+ const void *context);
+
+typedef void (*lucet_fatal_handler)(struct lucet_instance *inst);
+
+struct lucet_untyped_retval {
+ char fp[16];
+ char gp[8];
+};
+
+#define LUCET_MODULE_ADDR_DETAILS_NAME_LEN 256
+
+struct lucet_module_addr_details {
+ bool module_code_resolvable;
+ bool in_module_code;
+ char file_name[LUCET_MODULE_ADDR_DETAILS_NAME_LEN];
+ char sym_name[LUCET_MODULE_ADDR_DETAILS_NAME_LEN];
+};
+
+struct lucet_runtime_faulted {
+ bool fatal;
+ enum lucet_trapcode trapcode;
+ uintptr_t rip_addr;
+ struct lucet_module_addr_details rip_addr_details;
+};
+
+struct lucet_terminated {
+ enum lucet_terminated_reason reason;
+ void * provided;
+};
+
+struct lucet_yielded {
+ void *val;
+};
+
+union lucet_result_val {
+ struct lucet_untyped_retval returned;
+ struct lucet_yielded yielded;
+ struct lucet_runtime_faulted faulted;
+ struct lucet_terminated terminated;
+ enum lucet_error errored;
+};
+
+enum lucet_result_tag {
+ lucet_result_tag_returned,
+ lucet_result_tag_yielded,
+ lucet_result_tag_faulted,
+ lucet_result_tag_terminated,
+ lucet_result_tag_errored,
+};
+
+struct lucet_result {
+ enum lucet_result_tag tag;
+ union lucet_result_val val;
+};
+
+union lucet_retval_gp {
+ char as_untyped[8];
+ void * as_c_ptr;
+ uint64_t as_u64;
+ int64_t as_i64;
+};
+
+#endif /* LUCET_TYPES_H */
diff --git a/third_party/rust/lucet-runtime-wasmsbx/include/lucet_val.h b/third_party/rust/lucet-runtime-wasmsbx/include/lucet_val.h
new file mode 100644
index 0000000000..78ea1c17df
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/include/lucet_val.h
@@ -0,0 +1,103 @@
+#ifndef LUCET_VAL_H
+#define LUCET_VAL_H 1
+
+/**
+ * Typed values
+ *
+ * `struct lucet_val` represents a typed value, used in arguments lists.
+ * Such arguments can be built with the `LUCET_VAL_*` convenience macros.
+ *
+ * A guest function call with these arguments eventually returns a
+ * `struct lucet_untyped_retval` value, that can be converted to a
+ * native type with the `LUCET_UNTYPED_RETVAL_TO_*` macros.
+ *
+ * Usage:
+ *
+ * lucet_instance_run(inst, "add_2", 2, (struct lucet_val[]){ LUCET_VAL_U64(123), LUCET_VAL_U64(456)
+ * }); lucet_instance_state(inst, &state); uint64_t res =
+ * LUCET_UNTYPED_RETVAL_TO_U64(state.val.returned);
+ */
+
+#include <sys/types.h>
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+// Creates a lucet_val value from the given type
+
+#define LUCET_VAL_T(T, C, X) \
+ ((struct lucet_val[1]){ { .ty = lucet_val_type_##T, .inner_val.C = (X) } }[0])
+
+#define LUCET_VAL_C_PTR(X) LUCET_VAL_T(c_ptr, as_c_ptr, X)
+#define LUCET_VAL_GUEST_PTR(X) LUCET_VAL_T(guest_ptr, as_u64, X)
+
+#define LUCET_VAL_U8(X) LUCET_VAL_T(u8, as_u64, X)
+#define LUCET_VAL_U16(X) LUCET_VAL_T(u16, as_u64, X)
+#define LUCET_VAL_U32(X) LUCET_VAL_T(u32, as_u64, X)
+#define LUCET_VAL_U64(X) LUCET_VAL_T(u64, as_u64, X)
+
+#define LUCET_VAL_I8(X) LUCET_VAL_T(i8, as_i64, X)
+#define LUCET_VAL_I16(X) LUCET_VAL_T(i16, as_i64, X)
+#define LUCET_VAL_I32(X) LUCET_VAL_T(i32, as_i64, X)
+#define LUCET_VAL_I64(X) LUCET_VAL_T(i64, as_i64, X)
+
+#define LUCET_VAL_USIZE(X) LUCET_VAL_T(usize, as_u64, X)
+#define LUCET_VAL_ISIZE(X) LUCET_VAL_T(isize, as_i64, X)
+
+#define LUCET_VAL_BOOL(X) LUCET_VAL_T(bool, as_u64, X)
+
+#define LUCET_VAL_F32(X) LUCET_VAL_T(f32, as_f32, X)
+#define LUCET_VAL_F64(X) LUCET_VAL_T(f64, as_f64, X)
+
+// Converts a lucet_val value to the given type
+
+#define LUCET_VAL_TO_T(T, C, V) ((T)((V).inner_val.C))
+
+#define LUCET_VAL_TO_C_PTR(X) LUCET_VAL_TO_T(void *, as_c_ptr, X)
+#define LUCET_VAL_TO_GUEST_PTR(X) LUCET_VAL_TO_T(guest_ptr_t, as_u64, X)
+
+#define LUCET_VAL_TO_U8(X) LUCET_VAL_TO_T(uint8_t, as_u64, X)
+#define LUCET_VAL_TO_U16(X) LUCET_VAL_TO_T(uint16_t, as_u64, X)
+#define LUCET_VAL_TO_U32(X) LUCET_VAL_TO_T(uint32_t, as_u64, X)
+#define LUCET_VAL_TO_U64(X) LUCET_VAL_TO_T(uint64_t, as_u64, X)
+
+#define LUCET_VAL_TO_I8(X) LUCET_VAL_TO_T(int8_t, as_i64, X)
+#define LUCET_VAL_TO_I16(X) LUCET_VAL_TO_T(int16_t, as_i64, X)
+#define LUCET_VAL_TO_I32(X) LUCET_VAL_TO_T(int32_t, as_i64, X)
+#define LUCET_VAL_TO_I64(X) LUCET_VAL_TO_T(int64_t, as_i64, X)
+
+#define LUCET_VAL_TO_USIZE(X) LUCET_VAL_TO_T(size_t, as_u64, X)
+#define LUCET_VAL_TO_ISIZE(X) LUCET_VAL_TO_T(ssize_t, as_i64, X)
+
+#define LUCET_VAL_TO_BOOL(X) LUCET_VAL_TO_T(bool, as_u64, X)
+
+#define LUCET_VAL_TO_F32(X) LUCET_VAL_TO_T(float, as_f32, X)
+#define LUCET_VAL_TO_F64(X) LUCET_VAL_TO_T(double, as_f64, X)
+
+// Converts an untyped return value to the given type
+
+#define LUCET_UNTYPED_RETVAL_TO_GP_T(T, C, X) ((T) lucet_retval_gp(&(X)).C)
+#define LUCET_UNTYPED_RETVAL_TO_C_PTR(X) LUCET_UNTYPED_RETVAL_TO_GP_T(void *, as_c_ptr, &(X))
+
+#define LUCET_UNTYPED_RETVAL_TO_GUEST_PTR(X) LUCET_UNTYPED_RETVAL_TO_GP_T(guest_ptr_t, as_u64, X)
+
+#define LUCET_UNTYPED_RETVAL_TO_U8(X) LUCET_UNTYPED_RETVAL_TO_GP_T(uint8_t, as_u64, X)
+#define LUCET_UNTYPED_RETVAL_TO_U16(X) LUCET_UNTYPED_RETVAL_TO_GP_T(uint16_t, as_u64, X)
+#define LUCET_UNTYPED_RETVAL_TO_U32(X) LUCET_UNTYPED_RETVAL_TO_GP_T(uint32_t, as_u64, X)
+#define LUCET_UNTYPED_RETVAL_TO_U64(X) LUCET_UNTYPED_RETVAL_TO_GP_T(uint64_t, as_u64, X)
+
+#define LUCET_UNTYPED_RETVAL_TO_I8(X) LUCET_UNTYPED_RETVAL_TO_GP_T(int8_t, as_i64, X)
+#define LUCET_UNTYPED_RETVAL_TO_I16(X) LUCET_UNTYPED_RETVAL_TO_GP_T(int16_t, as_i64, X)
+#define LUCET_UNTYPED_RETVAL_TO_I32(X) LUCET_UNTYPED_RETVAL_TO_GP_T(int32_t, as_i64, X)
+#define LUCET_UNTYPED_RETVAL_TO_I64(X) LUCET_UNTYPED_RETVAL_TO_GP_T(int64_t, as_i64, X)
+
+#define LUCET_UNTYPED_RETVAL_TO_USIZE(X) LUCET_UNTYPED_RETVAL_TO_GP_T(size_t, as_u64, X)
+#define LUCET_UNTYPED_RETVAL_TO_ISIZE(X) LUCET_UNTYPED_RETVAL_TO_GP_T(ssize_t, as_i64, X)
+
+#define LUCET_UNTYPED_RETVAL_TO_BOOL(X) LUCET_UNTYPED_RETVAL_TO_GP_T(bool, as_u64, X)
+
+#define LUCET_UNTYPED_RETVAL_TO_F32(X) lucet_retval_f32(&(X))
+#define LUCET_UNTYPED_RETVAL_TO_F64(X) lucet_retval_f64(&(X))
+
+#endif
diff --git a/third_party/rust/lucet-runtime-wasmsbx/include/lucet_vmctx.h b/third_party/rust/lucet-runtime-wasmsbx/include/lucet_vmctx.h
new file mode 100644
index 0000000000..76af4afe58
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/include/lucet_vmctx.h
@@ -0,0 +1,45 @@
+#ifndef LUCET_VMCTX_H
+#define LUCET_VMCTX_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+// Typedefs for use in hostcall signatures (host functions called from the
+// guest). Instances always have a 32-bit pointer size into their heap, and
+// 32-bit size_t type.
+typedef uint32_t guest_int;
+typedef uint32_t guest_ptr_t;
+typedef uint32_t guest_size_t;
+
+struct lucet_vmctx;
+
+// Get a pointer to the instance heap.
+char *lucet_vmctx_get_heap(struct lucet_vmctx const *);
+
+// Check if a memory region is inside the instance heap.
+bool lucet_vmctx_check_heap(struct lucet_vmctx const *, void *ptr, size_t len);
+
+// Get the embedding context for a given instance
+// TODO: rename
+void *lucet_vmctx_get_delegate(struct lucet_vmctx const *);
+
+void lucet_vmctx_terminate(struct lucet_vmctx const *, void *info);
+
+void *lucet_vmctx_yield(struct lucet_vmctx const *, void *val);
+
+// returns the current number of wasm pages
+uint32_t lucet_vmctx_current_memory(struct lucet_vmctx const *);
+
+// takes the number of wasm pages to grow by. returns the number of pages before
+// the call on success, or -1 on failure.
+int32_t lucet_vmctx_grow_memory(struct lucet_vmctx const *, uint32_t additional_pages);
+
+// returns the address of a function given its ID
+void *lucet_vmctx_get_func_from_idx(struct lucet_vmctx const *ctx, uint32_t table_id,
+ uint32_t func_id);
+
+// Mostly for tests - this conversion is builtin to lucetc
+int64_t *lucet_vmctx_get_globals(struct lucet_vmctx const *ctx);
+
+#endif // LUCET_VMCTX_H
diff --git a/third_party/rust/lucet-runtime-wasmsbx/src/c_api.rs b/third_party/rust/lucet-runtime-wasmsbx/src/c_api.rs
new file mode 100644
index 0000000000..d8ab481870
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/src/c_api.rs
@@ -0,0 +1,530 @@
+use crate::{DlModule, Instance, Limits, MmapRegion, Module, Region};
+use libc::{c_char, c_int, c_void};
+use lucet_module::TrapCode;
+use lucet_runtime_internals::c_api::*;
+use lucet_runtime_internals::instance::{
+ instance_handle_from_raw, instance_handle_to_raw, InstanceInternal,
+};
+use lucet_runtime_internals::vmctx::VmctxInternal;
+use lucet_runtime_internals::WASM_PAGE_SIZE;
+use lucet_runtime_internals::{
+ assert_nonnull, lucet_hostcall_terminate, lucet_hostcalls, with_ffi_arcs,
+};
+use num_traits::FromPrimitive;
+use std::ffi::CStr;
+use std::ptr;
+use std::sync::{Arc, Once};
+
+macro_rules! with_instance_ptr {
+ ( $name:ident, $body:block ) => {{
+ assert_nonnull!($name);
+ let $name: &mut Instance = &mut *($name as *mut Instance);
+ $body
+ }};
+}
+
+macro_rules! with_instance_ptr_unchecked {
+ ( $name:ident, $body:block ) => {{
+ let $name: &mut Instance = &mut *($name as *mut Instance);
+ $body
+ }};
+}
+
+#[no_mangle]
+pub extern "C" fn lucet_error_name(e: c_int) -> *const c_char {
+ if let Some(e) = lucet_error::from_i32(e) {
+ use self::lucet_error::*;
+ match e {
+ Ok => "lucet_error_ok\0".as_ptr() as _,
+ InvalidArgument => "lucet_error_invalid_argument\0".as_ptr() as _,
+ RegionFull => "lucet_error_region_full\0".as_ptr() as _,
+ Module => "lucet_error_module\0".as_ptr() as _,
+ LimitsExceeded => "lucet_error_limits_exceeded\0".as_ptr() as _,
+ NoLinearMemory => "lucet_error_no_linear_memory\0".as_ptr() as _,
+ SymbolNotFound => "lucet_error_symbol_not_found\0".as_ptr() as _,
+ FuncNotFound => "lucet_error_func_not_found\0".as_ptr() as _,
+ RuntimeFault => "lucet_error_runtime_fault\0".as_ptr() as _,
+ RuntimeTerminated => "lucet_error_runtime_terminated\0".as_ptr() as _,
+ Dl => "lucet_error_dl\0".as_ptr() as _,
+ InstanceNotReturned => "lucet_error_instance_not_returned\0".as_ptr() as _,
+ InstanceNotYielded => "lucet_error_instance_not_yielded\0".as_ptr() as _,
+ StartYielded => "lucet_error_start_yielded\0".as_ptr() as _,
+ Internal => "lucet_error_internal\0".as_ptr() as _,
+ Unsupported => "lucet_error_unsupported\0".as_ptr() as _,
+ }
+ } else {
+ "!!! error: unknown lucet_error variant\0".as_ptr() as _
+ }
+}
+
+#[no_mangle]
+pub extern "C" fn lucet_result_tag_name(tag: libc::c_int) -> *const c_char {
+ if let Some(tag) = lucet_result_tag::from_i32(tag) {
+ use self::lucet_result_tag::*;
+ match tag {
+ Returned => "lucet_result_tag_returned\0".as_ptr() as _,
+ Yielded => "lucet_result_tag_yielded\0".as_ptr() as _,
+ Faulted => "lucet_result_tag_faulted\0".as_ptr() as _,
+ Terminated => "lucet_result_tag_terminated\0".as_ptr() as _,
+ Errored => "lucet_result_tag_errored\0".as_ptr() as _,
+ }
+ } else {
+ "!!! unknown lucet_result_tag variant!\0".as_ptr() as _
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn lucet_mmap_region_create(
+ instance_capacity: u64,
+ limits: *const lucet_alloc_limits,
+ region_out: *mut *mut lucet_region,
+) -> lucet_error {
+ assert_nonnull!(region_out);
+ let limits = limits
+ .as_ref()
+ .map(|l| l.into())
+ .unwrap_or(Limits::default());
+ match MmapRegion::create(instance_capacity as usize, &limits) {
+ Ok(region) => {
+ let region_thin = Arc::into_raw(Arc::new(region as Arc<dyn Region>));
+ region_out.write(region_thin as _);
+ return lucet_error::Ok;
+ }
+ Err(e) => return e.into(),
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn lucet_region_release(region: *const lucet_region) {
+ Arc::from_raw(region as *const Arc<dyn Region>);
+}
+
+// omg this naming convention might not scale
+#[no_mangle]
+pub unsafe extern "C" fn lucet_region_new_instance_with_ctx(
+ region: *const lucet_region,
+ module: *const lucet_dl_module,
+ embed_ctx: *mut c_void,
+ inst_out: *mut *mut lucet_instance,
+) -> lucet_error {
+ assert_nonnull!(inst_out);
+ with_ffi_arcs!([region: dyn Region, module: DlModule], {
+ region
+ .new_instance_builder(module.clone() as Arc<dyn Module>)
+ .with_embed_ctx(embed_ctx)
+ .build()
+ .map(|i| {
+ inst_out.write(instance_handle_to_raw(i) as _);
+ lucet_error::Ok
+ })
+ .unwrap_or_else(|e| e.into())
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn lucet_region_new_instance(
+ region: *const lucet_region,
+ module: *const lucet_dl_module,
+ inst_out: *mut *mut lucet_instance,
+) -> lucet_error {
+ lucet_region_new_instance_with_ctx(region, module, ptr::null_mut(), inst_out)
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn lucet_dl_module_load(
+ path: *const c_char,
+ mod_out: *mut *mut lucet_dl_module,
+) -> lucet_error {
+ assert_nonnull!(mod_out);
+ let path = CStr::from_ptr(path);
+ DlModule::load(path.to_string_lossy().into_owned())
+ .map(|m| {
+ mod_out.write(Arc::into_raw(m) as _);
+ lucet_error::Ok
+ })
+ .unwrap_or_else(|e| e.into())
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn lucet_dl_module_release(module: *const lucet_dl_module) {
+ Arc::from_raw(module as *const DlModule);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn lucet_instance_run(
+ inst: *mut lucet_instance,
+ entrypoint: *const c_char,
+ argc: usize,
+ argv: *const lucet_val::lucet_val,
+ result_out: *mut lucet_result::lucet_result,
+) -> lucet_error {
+ assert_nonnull!(entrypoint);
+ if argc != 0 && argv.is_null() {
+ return lucet_error::InvalidArgument;
+ }
+ let args = if argc == 0 {
+ vec![]
+ } else {
+ std::slice::from_raw_parts(argv, argc)
+ .into_iter()
+ .map(|v| v.into())
+ .collect()
+ };
+ let entrypoint = match CStr::from_ptr(entrypoint).to_str() {
+ Ok(entrypoint_str) => entrypoint_str,
+ Err(_) => {
+ return lucet_error::SymbolNotFound;
+ }
+ };
+
+ with_instance_ptr!(inst, {
+ let res = inst.run(entrypoint, args.as_slice());
+ let ret = res
+ .as_ref()
+ .map(|_| lucet_error::Ok)
+ .unwrap_or_else(|e| e.into());
+ if !result_out.is_null() {
+ std::ptr::write(result_out, res.into());
+ }
+ ret
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn lucet_instance_run_func_idx(
+ inst: *mut lucet_instance,
+ table_idx: u32,
+ func_idx: u32,
+ argc: usize,
+ argv: *const lucet_val::lucet_val,
+ result_out: *mut lucet_result::lucet_result,
+) -> lucet_error {
+ if argc != 0 && argv.is_null() {
+ return lucet_error::InvalidArgument;
+ }
+ let args = if argc == 0 {
+ vec![]
+ } else {
+ std::slice::from_raw_parts(argv, argc)
+ .into_iter()
+ .map(|v| v.into())
+ .collect()
+ };
+ with_instance_ptr!(inst, {
+ let res = inst.run_func_idx(table_idx, func_idx, args.as_slice());
+ let ret = res
+ .as_ref()
+ .map(|_| lucet_error::Ok)
+ .unwrap_or_else(|e| e.into());
+ if !result_out.is_null() {
+ std::ptr::write(result_out, res.into());
+ }
+ ret
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn lucet_instance_resume(
+ inst: *const lucet_instance,
+ val: *mut c_void,
+ result_out: *mut lucet_result::lucet_result,
+) -> lucet_error {
+ with_instance_ptr!(inst, {
+ let res = inst.resume_with_val(CYieldedVal { val });
+ let ret = res
+ .as_ref()
+ .map(|_| lucet_error::Ok)
+ .unwrap_or_else(|e| e.into());
+ if !result_out.is_null() {
+ std::ptr::write(result_out, res.into());
+ }
+ ret
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn lucet_instance_reset(inst: *mut lucet_instance) -> lucet_error {
+ with_instance_ptr!(inst, {
+ inst.reset()
+ .map(|_| lucet_error::Ok)
+ .unwrap_or_else(|e| e.into())
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn lucet_instance_release(inst: *mut lucet_instance) {
+ instance_handle_from_raw(inst as *mut Instance, true);
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn lucet_instance_heap(inst: *mut lucet_instance) -> *mut u8 {
+ with_instance_ptr_unchecked!(inst, { inst.heap_mut().as_mut_ptr() })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn lucet_instance_heap_len(inst: *const lucet_instance) -> u32 {
+ with_instance_ptr_unchecked!(inst, { inst.heap().len() as u32 })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn lucet_instance_check_heap(
+ inst: *const lucet_instance,
+ ptr: *const c_void,
+ len: usize,
+) -> bool {
+ with_instance_ptr_unchecked!(inst, { inst.check_heap(ptr, len) })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn lucet_instance_grow_heap(
+ inst: *mut lucet_instance,
+ additional_pages: u32,
+ previous_pages_out: *mut u32,
+) -> lucet_error {
+ with_instance_ptr!(inst, {
+ match inst.grow_memory(additional_pages) {
+ Ok(previous_pages) => {
+ if !previous_pages_out.is_null() {
+ previous_pages_out.write(previous_pages);
+ }
+ lucet_error::Ok
+ }
+ Err(e) => e.into(),
+ }
+ })
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn lucet_instance_embed_ctx(inst: *mut lucet_instance) -> *mut c_void {
+ with_instance_ptr_unchecked!(inst, {
+ inst.get_embed_ctx::<*mut c_void>()
+ .map(|r| r.map(|p| *p).unwrap_or(ptr::null_mut()))
+ .unwrap_or(ptr::null_mut())
+ })
+}
+
+/// Release or run* must not be called in the body of this function!
+#[no_mangle]
+pub unsafe extern "C" fn lucet_instance_set_signal_handler(
+ inst: *mut lucet_instance,
+ signal_handler: lucet_signal_handler,
+) -> lucet_error {
+ let handler = move |inst: &Instance, trap: &Option<TrapCode>, signum, siginfo, context| {
+ let inst = inst as *const Instance as *mut lucet_instance;
+ signal_handler(inst, trap.into(), signum, siginfo, context).into()
+ };
+ with_instance_ptr!(inst, {
+ inst.set_signal_handler(handler);
+ });
+ lucet_error::Ok
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn lucet_instance_set_fatal_handler(
+ inst: *mut lucet_instance,
+ fatal_handler: lucet_fatal_handler,
+) -> lucet_error {
+ // transmuting is fine here because *mut lucet_instance = *mut Instance
+ let fatal_handler: unsafe extern "C" fn(inst: *mut Instance) =
+ std::mem::transmute(fatal_handler);
+ with_instance_ptr!(inst, {
+ inst.set_c_fatal_handler(fatal_handler);
+ });
+ lucet_error::Ok
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn lucet_retval_gp(retval: *const lucet_untyped_retval) -> lucet_retval_gp {
+ lucet_retval_gp {
+ as_untyped: (*retval).gp,
+ }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn lucet_retval_f32(retval: *const lucet_untyped_retval) -> f32 {
+ let mut v = 0.0f32;
+ core::arch::x86_64::_mm_storeu_ps(
+ &mut v as *mut f32,
+ core::arch::x86_64::_mm_loadu_ps((*retval).fp.as_ptr() as *const f32),
+ );
+ v
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn lucet_retval_f64(retval: *const lucet_untyped_retval) -> f64 {
+ let mut v = 0.0f64;
+ core::arch::x86_64::_mm_storeu_pd(
+ &mut v as *mut f64,
+ core::arch::x86_64::_mm_loadu_pd((*retval).fp.as_ptr() as *const f64),
+ );
+ v
+}
+
+static C_API_INIT: Once = Once::new();
+
+/// Should never actually be called, but should be reachable via a trait method to prevent DCE.
+pub fn ensure_linked() {
+ use std::ptr::read_volatile;
+ C_API_INIT.call_once(|| unsafe {
+ read_volatile(lucet_vmctx_get_heap as *const extern "C" fn());
+ read_volatile(lucet_vmctx_current_memory as *const extern "C" fn());
+ read_volatile(lucet_vmctx_grow_memory as *const extern "C" fn());
+ });
+}
+
+lucet_hostcalls! {
+ #[no_mangle]
+ pub unsafe extern "C" fn lucet_vmctx_get_heap(
+ &mut vmctx,
+ ) -> *mut u8 {
+ vmctx.instance().alloc().slot().heap as *mut u8
+ }
+
+ #[no_mangle]
+ pub unsafe extern "C" fn lucet_vmctx_get_globals(
+ &mut vmctx,
+ ) -> *mut i64 {
+ vmctx.instance().alloc().slot().globals as *mut i64
+ }
+
+ #[no_mangle]
+ /// Get the number of WebAssembly pages currently in the heap.
+ pub unsafe extern "C" fn lucet_vmctx_current_memory(
+ &mut vmctx,
+ ) -> u32 {
+ vmctx.instance().alloc().heap_len() as u32 / WASM_PAGE_SIZE
+ }
+
+ #[no_mangle]
+ /// Grows the guest heap by the given number of WebAssembly pages.
+ ///
+ /// On success, returns the number of pages that existed before the call. On failure, returns `-1`.
+ pub unsafe extern "C" fn lucet_vmctx_grow_memory(
+ &mut vmctx,
+ additional_pages: u32,
+ ) -> i32 {
+ if let Ok(old_pages) = vmctx.instance_mut().grow_memory(additional_pages) {
+ old_pages as i32
+ } else {
+ -1
+ }
+ }
+
+ #[no_mangle]
+ /// Check if a memory region is inside the instance heap.
+ pub unsafe extern "C" fn lucet_vmctx_check_heap(
+ &mut vmctx,
+ ptr: *mut c_void,
+ len: libc::size_t,
+ ) -> bool {
+ vmctx.instance().check_heap(ptr, len)
+ }
+
+ #[no_mangle]
+ pub unsafe extern "C" fn lucet_vmctx_get_func_from_idx(
+ &mut vmctx,
+ table_idx: u32,
+ func_idx: u32,
+ ) -> *const c_void {
+ vmctx.instance()
+ .module()
+ .get_func_from_idx(table_idx, func_idx)
+ .map(|fptr| fptr.ptr.as_usize() as *const c_void)
+ .unwrap_or(std::ptr::null())
+ }
+
+ #[no_mangle]
+ pub unsafe extern "C" fn lucet_vmctx_terminate(
+ &mut _vmctx,
+ details: *mut c_void,
+ ) -> () {
+ lucet_hostcall_terminate!(CTerminationDetails { details });
+ }
+
+ #[no_mangle]
+ /// Get the delegate object for the current instance.
+ ///
+ /// TODO: rename
+ pub unsafe extern "C" fn lucet_vmctx_get_delegate(
+ &mut vmctx,
+ ) -> *mut c_void {
+ vmctx.instance()
+ .get_embed_ctx::<*mut c_void>()
+ .map(|r| r.map(|p| *p).unwrap_or(ptr::null_mut()))
+ .unwrap_or(std::ptr::null_mut())
+ }
+
+ #[no_mangle]
+ pub unsafe extern "C" fn lucet_vmctx_yield(
+ &mut vmctx,
+ val: *mut c_void,
+ ) -> *mut c_void {
+ vmctx
+ .yield_val_try_val(CYieldedVal { val })
+ .map(|CYieldedVal { val }| val)
+ .unwrap_or(std::ptr::null_mut())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::lucet_dl_module;
+ use crate::DlModule;
+ use lucet_module::bindings::Bindings;
+ use lucet_wasi_sdk::{CompileOpts, LinkOpt, LinkOpts, Lucetc};
+ use lucetc::LucetcOpts;
+ use std::sync::Arc;
+ use tempfile::TempDir;
+
+ extern "C" {
+ fn lucet_runtime_test_expand_heap(module: *mut lucet_dl_module) -> bool;
+ fn lucet_runtime_test_yield_resume(module: *mut lucet_dl_module) -> bool;
+ }
+
+ #[test]
+ fn expand_heap() {
+ let workdir = TempDir::new().expect("create working directory");
+
+ let native_build = Lucetc::new(&["tests/guests/null.c"])
+ .with_cflag("-nostartfiles")
+ .with_link_opt(LinkOpt::NoDefaultEntryPoint)
+ .with_link_opt(LinkOpt::AllowUndefinedAll)
+ .with_link_opt(LinkOpt::ExportAll);
+
+ let so_file = workdir.path().join("null.so");
+
+ native_build.build(so_file.clone()).unwrap();
+
+ let dlmodule = DlModule::load(so_file).unwrap();
+
+ unsafe {
+ assert!(lucet_runtime_test_expand_heap(
+ Arc::into_raw(dlmodule) as *mut lucet_dl_module
+ ));
+ }
+ }
+
+ #[test]
+ fn yield_resume() {
+ let workdir = TempDir::new().expect("create working directory");
+
+ let native_build = Lucetc::new(&["tests/guests/yield_resume.c"])
+ .with_cflag("-nostartfiles")
+ .with_link_opt(LinkOpt::NoDefaultEntryPoint)
+ .with_link_opt(LinkOpt::AllowUndefinedAll)
+ .with_link_opt(LinkOpt::ExportAll)
+ .with_bindings(Bindings::from_file("tests/guests/yield_resume_bindings.json").unwrap());
+
+ let so_file = workdir.path().join("yield_resume.so");
+
+ native_build.build(so_file.clone()).unwrap();
+
+ let dlmodule = DlModule::load(so_file).unwrap();
+
+ unsafe {
+ assert!(lucet_runtime_test_yield_resume(
+ Arc::into_raw(dlmodule) as *mut lucet_dl_module
+ ));
+ }
+ }
+}
diff --git a/third_party/rust/lucet-runtime-wasmsbx/src/lib.rs b/third_party/rust/lucet-runtime-wasmsbx/src/lib.rs
new file mode 100644
index 0000000000..60126a9e41
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/src/lib.rs
@@ -0,0 +1,384 @@
+//! # Lucet Runtime for Sandboxed WebAssembly Applications
+//!
+//! This crate runs programs that were compiled with the `lucetc` WebAssembly to native code
+//! compiler. It provides an interface for modules to be loaded from shared object files (see
+//! `DlModule`), and for hosts to provide specialized functionality to guests (see
+//! `Instance::embed_ctx()`).
+//!
+//! The runtime is a critical part of the safety and security story for Lucet. While the semantics
+//! of WebAssembly and the `lucetc` compiler provide many guarantees, the runtime must be correct in
+//! order for the assumptions of those guarantees to hold. For example, the runtime uses guard pages
+//! to ensure that any attempts by guest programs to access memory past the end of the guest heap are
+//! safely caught.
+//!
+//! The runtime is also extensible, and some of the key types are defined as traits for
+//! flexibility. See the `lucet-runtime-internals` crate for details.
+//!
+//! ## Running a Lucet Program
+//!
+//! There are a few essential types for using the runtime:
+//!
+//! - [`Instance`](struct.Instance.html): a Lucet program, together with its dedicated memory and
+//! signal handlers. Users of this API never own an `Instance` directly, but can own the
+//! [`InstanceHandle`](struct.InstanceHandle.html) smart pointer.
+//!
+//! - [`Region`](trait.Region.html): the memory from which instances are created. This crate
+//! includes [`MmapRegion`](struct.MmapRegion.html), an implementation backed by `mmap`.
+//!
+//! - [`Limits`](struct.Limits.html): upper bounds for the resources a Lucet instance may
+//! consume. These may be larger or smaller than the limits described in the WebAssembly module
+//! itself; the smaller limit is always enforced.
+//!
+//! - [`Module`](trait.Module.html): the read-only parts of a Lucet program, including its code and
+//! initial heap configuration. This crate includes [`DlModule`](struct.DlModule.html), an
+//! implementation backed by dynamic loading of shared objects.
+//!
+//! - [`Val`](enum.Val.html): an enum describing values in WebAssembly, used to provide
+//! arguments. These can be created using `From` implementations of primitive types, for example
+//! `5u64.into()` in the example below.
+//!
+//! - [`RunResult`](enum.RunResult.html): the result of running or resuming an instance. These
+//! contain either `UntypedRetVal`s for WebAssembly functions that have returned, or `YieldedVal`s
+//! for WebAssembly programs that have yielded.
+//!
+//! - [`UntypedRetVal`](struct.UntypedRetVal.html): values returned from WebAssembly
+//! functions. These must be interpreted at the correct type by the user via `From` implementations
+//! or `retval.as_T()` methods, for example `u64::from(retval)` in the example below.
+//!
+//! - [`YieldedVal`](struct.YieldedVal.html): dynamically-values yielded by WebAssembly
+//! programs. Not all yield points are given values, so this may be empty. To use the values, if
+//! present, you must first downcast them with the provided methods.
+//!
+//! To run a Lucet program, you start by creating a region, capable of backing a number of
+//! instances. You then load a module and then create a new instance using the region and the
+//! module. You can then run any of the functions that the Lucet program exports, retrieve return
+//! values from those functions, and access the linear memory of the guest.
+//!
+//! ```no_run
+//! use lucet_runtime::{DlModule, Limits, MmapRegion, Region};
+//!
+//! let module = DlModule::load("/my/lucet/module.so").unwrap();
+//! let region = MmapRegion::create(1, &Limits::default()).unwrap();
+//! let mut inst = region.new_instance(module).unwrap();
+//!
+//! let retval = inst.run("factorial", &[5u64.into()]).unwrap().unwrap_returned();
+//! assert_eq!(u64::from(retval), 120u64);
+//! ```
+//!
+//! ## Embedding With Hostcalls
+//!
+//! A "hostcall" is a function called by WebAssembly that is not defined in WebAssembly. Since
+//! WebAssembly is such a minimal language, hostcalls are required for Lucet programs to do anything
+//! interesting with the outside world. For example, in Fastly's [Terrarium
+//! demo](https://wasm.fastly-labs.com/), hostcalls are provided for manipulating HTTP requests,
+//! accessing a key/value store, etc.
+//!
+//! Some simple hostcalls can be implemented by wrapping an externed C function with the
+//! [`lucet_hostcalls!`](macro.lucet_hostcalls.html] macro. The function must take a special `&mut
+//! vmctx` argument for the guest context, similar to `&mut self` on methods. Hostcalls that require
+//! access to some underlying state, such as the key/value store in Terrarium, can access a custom
+//! embedder context through `vmctx`. For example, to make a `u32` available to hostcalls:
+//!
+//! ```no_run
+//! use lucet_runtime::{DlModule, Limits, MmapRegion, Region, lucet_hostcalls};
+//! use lucet_runtime::vmctx::{Vmctx, lucet_vmctx};
+//!
+//! struct MyContext { x: u32 }
+//!
+//! lucet_hostcalls! {
+//! #[no_mangle]
+//! pub unsafe extern "C" fn foo(
+//! &mut vmctx,
+//! ) -> () {
+//! let mut hostcall_context = vmctx.get_embed_ctx_mut::<MyContext>();
+//! hostcall_context.x = 42;
+//! }
+//! }
+//!
+//! let module = DlModule::load("/my/lucet/module.so").unwrap();
+//! let region = MmapRegion::create(1, &Limits::default()).unwrap();
+//! let mut inst = region
+//! .new_instance_builder(module)
+//! .with_embed_ctx(MyContext { x: 0 })
+//! .build()
+//! .unwrap();
+//!
+//! inst.run("call_foo", &[]).unwrap();
+//!
+//! let context_after = inst.get_embed_ctx::<MyContext>().unwrap().unwrap();
+//! assert_eq!(context_after.x, 42);
+//! ```
+//!
+//! The embedder context is backed by a structure that can hold a single value of any type. Rust
+//! embedders should add their own custom state type (like `MyContext` above) for any context they
+//! require, rather than using a common type (such as the `u32`) from the standard library. This
+//! avoids collisions between libraries, and allows for easy composition of embeddings.
+//!
+//! For C-based embedders, the type `*mut libc::c_void` is privileged as the only type that the C
+//! API provides. The following example shows how a Rust embedder can initialize a C-compatible
+//! context:
+//!
+//! ```no_run
+//! use lucet_runtime::{DlModule, Limits, MmapRegion, Region};
+//!
+//! let module = DlModule::load("/my/lucet/module.so").unwrap();
+//! let region = MmapRegion::create(1, &Limits::default()).unwrap();
+//! #[repr(C)]
+//! struct MyForeignContext { x: u32 };
+//! let mut foreign_ctx = Box::into_raw(Box::new(MyForeignContext{ x: 0 }));
+//! let mut inst = region
+//! .new_instance_builder(module)
+//! .with_embed_ctx(foreign_ctx as *mut libc::c_void)
+//! .build()
+//! .unwrap();
+//!
+//! inst.run("main", &[]).unwrap();
+//!
+//! // clean up embedder context
+//! drop(inst);
+//! // foreign_ctx must outlive inst, but then must be turned back into a box
+//! // in order to drop.
+//! unsafe { Box::from_raw(foreign_ctx) };
+//! ```
+//!
+//! ## Yielding and Resuming
+//!
+//! Lucet hostcalls can use the `vmctx` argument to yield, suspending themselves and optionally
+//! returning a value back to the host context. A yielded instance can then be resumed by the host,
+//! and execution will continue from the point of the yield.
+//!
+//! Four yield methods are available for hostcall implementors:
+//!
+//! | | Yields value? | Expects value? |
+//! |-------------------------------------------------------------------------------------|---------------|----------------|
+//! | [`yield_`](vmctx/struct.Vmctx.html#method.yield_) | ❌ | ❌ |
+//! | [`yield_val`](vmctx/struct.Vmctx.html#method.yield_val) | ✅ | ❌ |
+//! | [`yield_expecting_val`](vmctx/struct.Vmctx.html#method.yield_expecting_val) | ❌ | ✅ |
+//! | [`yield_val_expecting_val`](vmctx/struct.Vmctx.html#method.yield_val_expecting_val) | ✅ | ✅ |
+//!
+//! The host is free to ignore values yielded by guests, but a yielded instance may only be resumed
+//! with a value of the correct type using
+//! [`Instance::resume_with_val()`](struct.Instance.html#method.resume_with_val), if one is
+//! expected.
+//!
+//! ### Factorial example
+//!
+//! In this example, we use yielding and resuming to offload multiplication to the host context, and
+//! to incrementally return results to the host. While certainly overkill for computing a factorial
+//! function, this structure mirrors that of many asynchronous workflows.
+//!
+//! Since the focus of this example is on the behavior of hostcalls that yield, our Lucet guest
+//! program just invokes a hostcall:
+//!
+//! ```no_run
+//! // factorials_guest.rs
+//! extern "C" {
+//! fn hostcall_factorials(n: u64) -> u64;
+//! }
+//!
+//! #[no_mangle]
+//! pub extern "C" fn run() -> u64 {
+//! unsafe {
+//! hostcall_factorials(5)
+//! }
+//! }
+//! ```
+//!
+//! In our hostcall, there are two changes from a standard recursive implementation of factorial.
+//!
+//! - Instead of performing the `n * fact(n - 1)` multiplication ourselves, we yield the operands
+//! and expect the product when resumed.
+//!
+//! - Whenever we have computed a factorial, including both intermediate values and the final
+//! answer, we yield it.
+//!
+//! The final answer is returned normally as the result of the guest function.
+//!
+//! To implement this, we introduce a new `enum` type to represent what we want the host to do next,
+//! and yield it when appropriate.
+//!
+//! ```no_run
+//! use lucet_runtime::lucet_hostcalls;
+//! use lucet_runtime::vmctx::Vmctx;
+//!
+//! pub enum FactorialsK {
+//! Mult(u64, u64),
+//! Result(u64),
+//! }
+//!
+//! lucet_hostcalls! {
+//! #[no_mangle]
+//! pub unsafe extern "C" fn hostcall_factorials(
+//! &mut vmctx,
+//! n: u64,
+//! ) -> u64 {
+//! fn fact(vmctx: &mut Vmctx, n: u64) -> u64 {
+//! let result = if n <= 1 {
+//! 1
+//! } else {
+//! let n_rec = fact(vmctx, n - 1);
+//! // yield a request for the host to perform multiplication
+//! vmctx.yield_val_expecting_val(FactorialsK::Mult(n, n_rec))
+//! // once resumed, that yield evaluates to the multiplication result
+//! };
+//! // yield a result
+//! vmctx.yield_val(FactorialsK::Result(result));
+//! result
+//! }
+//! fact(vmctx, n)
+//! }
+//! }
+//! ```
+//!
+//! The host side of the code, then, is an interpreter that repeatedly checks the yielded value and
+//! performs the appropriate operation. The hostcall returns normally with the final answer when it
+//! is finished, so we exit the loop when the run/resume result is `Ok`.
+//!
+//! ```no_run
+//! # pub enum FactorialsK {
+//! # Mult(u64, u64),
+//! # Result(u64),
+//! # }
+//! use lucet_runtime::{DlModule, Error, Limits, MmapRegion, Region};
+//!
+//! let module = DlModule::load("factorials_guest.so").unwrap();
+//! let region = MmapRegion::create(1, &Limits::default()).unwrap();
+//! let mut inst = region.new_instance(module).unwrap();
+//!
+//! let mut factorials = vec![];
+//!
+//! let mut res = inst.run("run", &[]).unwrap();
+//!
+//! while let Ok(val) = res.yielded_ref() {
+//! if let Some(k) = val.downcast_ref::<FactorialsK>() {
+//! match k {
+//! FactorialsK::Mult(n, n_rec) => {
+//! // guest wants us to multiply for it
+//! res = inst.resume_with_val(n * n_rec).unwrap();
+//! }
+//! FactorialsK::Result(n) => {
+//! // guest is returning an answer
+//! factorials.push(*n);
+//! res = inst.resume().unwrap();
+//! }
+//! }
+//! } else {
+//! panic!("didn't yield with expected type");
+//! }
+//! }
+//!
+//! // intermediate values are correct
+//! assert_eq!(factorials.as_slice(), &[1, 2, 6, 24, 120]);
+//! // final value is correct
+//! assert_eq!(u64::from(res.unwrap_returned()), 120u64);
+//! ```
+//!
+//! ## Custom Signal Handlers
+//!
+//! Since Lucet programs are run as native machine code, signals such as `SIGSEGV` and `SIGFPE` can
+//! arise during execution. Rather than letting these signals bring down the entire process, the
+//! Lucet runtime installs alternate signal handlers that limit the effects to just the instance
+//! that raised the signal.
+//!
+//! By default, the signal handler sets the instance state to `State::Fault` and returns early from
+//! the call to `Instance::run()`. You can, however, implement custom error recovery and logging
+//! behavior by defining new signal handlers on a per-instance basis. For example, the following
+//! signal handler increments a counter of signals it has seen before setting the fault state:
+//!
+//! ```no_run
+//! use lucet_runtime::{
+//! DlModule, Error, Instance, Limits, MmapRegion, Region, SignalBehavior, TrapCode,
+//! };
+//! use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
+//!
+//! static SIGNAL_COUNT: AtomicUsize = ATOMIC_USIZE_INIT;
+//!
+//! fn signal_handler_count(
+//! _inst: &Instance,
+//! _trapcode: &Option<TrapCode>,
+//! _signum: libc::c_int,
+//! _siginfo_ptr: *const libc::siginfo_t,
+//! _ucontext_ptr: *const libc::c_void,
+//! ) -> SignalBehavior {
+//! SIGNAL_COUNT.fetch_add(1, Ordering::SeqCst);
+//! SignalBehavior::Default
+//! }
+//!
+//! let module = DlModule::load("/my/lucet/module.so").unwrap();
+//! let region = MmapRegion::create(1, &Limits::default()).unwrap();
+//! let mut inst = region.new_instance(module).unwrap();
+//!
+//! // install the handler
+//! inst.set_signal_handler(signal_handler_count);
+//!
+//! match inst.run("raise_a_signal", &[]) {
+//! Err(Error::RuntimeFault(_)) => {
+//! println!("I've now handled {} signals!", SIGNAL_COUNT.load(Ordering::SeqCst));
+//! }
+//! res => panic!("unexpected result: {:?}", res),
+//! }
+//! ```
+//!
+//! When implementing custom signal handlers for the Lucet runtime, the usual caveats about signal
+//! safety apply: see
+//! [`signal-safety(7)`](http://man7.org/linux/man-pages/man7/signal-safety.7.html).
+//!
+//! ## Interaction With Host Signal Handlers
+//!
+//! Great care must be taken if host application installs or otherwise modifies signal handlers
+//! anywhere in the process. Lucet installs handlers for `SIGBUS`, `SIGFPE`, `SIGILL`, and `SIGSEGV`
+//! when the first Lucet instance begins running, and restores the preëxisting handlers when the
+//! last Lucet instance terminates. During this time, other threads in the host process *must not*
+//! modify those signal handlers, since signal handlers can only be installed on a process-wide
+//! basis.
+//!
+//! Despite this limitation, Lucet is designed to compose with other signal handlers in the host
+//! program. If one of the above signals is caught by the Lucet signal handler, but that thread is
+//! not currently running a Lucet instance, the saved host signal handler is called. This means
+//! that, for example, a `SIGSEGV` on a non-Lucet thread of a host program will still likely abort
+//! the entire process.
+
+#![deny(bare_trait_objects)]
+
+pub mod c_api;
+
+#[cfg(feature = "signature_checking")]
+pub use lucet_module::PublicKey;
+pub use lucet_module::TrapCode;
+pub use lucet_runtime_internals::alloc::Limits;
+pub use lucet_runtime_internals::error::Error;
+pub use lucet_runtime_internals::instance::{
+ FaultDetails, Instance, InstanceHandle, RunResult, SignalBehavior, TerminationDetails,
+ YieldedVal,
+};
+pub use lucet_runtime_internals::module::{DlModule, Module};
+pub use lucet_runtime_internals::region::mmap::MmapRegion;
+pub use lucet_runtime_internals::region::{InstanceBuilder, Region, RegionCreate};
+pub use lucet_runtime_internals::val::{UntypedRetVal, Val};
+pub use lucet_runtime_internals::{lucet_hostcall_terminate, lucet_hostcalls, WASM_PAGE_SIZE};
+
+pub mod vmctx {
+ //! Functions for manipulating instances from hostcalls.
+ //!
+ //! The Lucet compiler inserts an extra `*mut lucet_vmctx` argument to all functions defined and
+ //! called by WebAssembly code. Through this pointer, code running in the guest context can
+ //! access and manipulate the instance and its structures. These functions are intended for use
+ //! in hostcall implementations, and must only be used from within a running guest.
+ //!
+ //! # Panics
+ //!
+ //! All of the `Vmctx` methods will panic if the `Vmctx` was not created from a valid pointer
+ //! associated with a running instance. This should never occur if run in guest code on the
+ //! pointer argument inserted by the compiler.
+ pub use lucet_runtime_internals::vmctx::{lucet_vmctx, Vmctx};
+}
+
+/// Call this if you're having trouble with `lucet_*` symbols not being exported.
+///
+/// This is pretty hackish; we will hopefully be able to avoid this altogether once [this
+/// issue](https://github.com/rust-lang/rust/issues/58037) is addressed.
+#[no_mangle]
+#[doc(hidden)]
+pub extern "C" fn lucet_internal_ensure_linked() {
+ self::c_api::ensure_linked();
+}
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/c_api.c b/third_party/rust/lucet-runtime-wasmsbx/tests/c_api.c
new file mode 100644
index 0000000000..b799d186f0
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/c_api.c
@@ -0,0 +1,180 @@
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "lucet.h"
+
+bool lucet_runtime_test_expand_heap(struct lucet_dl_module *mod)
+{
+ struct lucet_region * region;
+ struct lucet_alloc_limits limits = {
+ .heap_memory_size = 4 * 1024 * 1024,
+ .heap_address_space_size = 8 * 1024 * 1024,
+ .stack_size = 64 * 1024,
+ .globals_size = 4096,
+ .signal_stack_size = 12 * 1024,
+ };
+
+ enum lucet_error err;
+
+ err = lucet_mmap_region_create(1, &limits, &region);
+ if (err != lucet_error_ok) {
+ fprintf(stderr, "failed to create region\n");
+ goto fail1;
+ }
+
+ struct lucet_instance *inst;
+ err = lucet_region_new_instance(region, mod, &inst);
+ if (err != lucet_error_ok) {
+ fprintf(stderr, "failed to create instance\n");
+ goto fail2;
+ }
+
+ uint32_t newpage_start;
+ err = lucet_instance_grow_heap(inst, 1, &newpage_start);
+ if (err != lucet_error_ok) {
+ fprintf(stderr, "failed to grow memory\n");
+ goto fail3;
+ }
+
+ lucet_instance_release(inst);
+ lucet_region_release(region);
+ lucet_dl_module_release(mod);
+
+ return true;
+
+fail3:
+ lucet_instance_release(inst);
+fail2:
+ lucet_region_release(region);
+fail1:
+ lucet_dl_module_release(mod);
+ return false;
+}
+
+enum yield_resume_tag {
+ yield_resume_tag_mult,
+ yield_resume_tag_result,
+};
+
+struct yield_resume_mult {
+ uint64_t x;
+ uint64_t y;
+};
+
+union yield_resume_val_inner {
+ struct yield_resume_mult mult;
+ uint64_t result;
+};
+
+struct yield_resume_val {
+ enum yield_resume_tag tag;
+ union yield_resume_val_inner val;
+};
+
+uint64_t lucet_runtime_test_hostcall_yield_resume(struct lucet_vmctx *vmctx, uint64_t n)
+{
+ if (n <= 1) {
+ struct yield_resume_val result_val = { .tag = yield_resume_tag_result,
+ .val = { .result = 1 } };
+ lucet_vmctx_yield(vmctx, &result_val);
+ return 1;
+ } else {
+ uint64_t n_rec = lucet_runtime_test_hostcall_yield_resume(vmctx, n - 1);
+ struct yield_resume_val mult_val = { .tag = yield_resume_tag_mult,
+ .val = { .mult = { .x = n, .y = n_rec } } };
+ uint64_t n = *(uint64_t *) lucet_vmctx_yield(vmctx, &mult_val);
+ struct yield_resume_val result_val = { .tag = yield_resume_tag_result,
+ .val = { .result = n } };
+ lucet_vmctx_yield(vmctx, &result_val);
+ return n;
+ }
+}
+
+bool lucet_runtime_test_yield_resume(struct lucet_dl_module *mod)
+{
+ struct lucet_region * region;
+ struct lucet_alloc_limits limits = {
+ .heap_memory_size = 4 * 1024 * 1024,
+ .heap_address_space_size = 8 * 1024 * 1024,
+ .stack_size = 64 * 1024,
+ .globals_size = 4096,
+ .signal_stack_size = 12 * 1024,
+ };
+
+ enum lucet_error err;
+
+ err = lucet_mmap_region_create(1, &limits, &region);
+ if (err != lucet_error_ok) {
+ fprintf(stderr, "failed to create region\n");
+ goto fail1;
+ }
+
+ struct lucet_instance *inst;
+ err = lucet_region_new_instance(region, mod, &inst);
+ if (err != lucet_error_ok) {
+ fprintf(stderr, "failed to create instance\n");
+ goto fail2;
+ }
+
+ uint64_t results[5] = { 0 };
+ size_t i = 0;
+
+ struct lucet_result res;
+ lucet_instance_run(inst, "f", 0, (const struct lucet_val[]){}, &res);
+ while (res.tag == lucet_result_tag_yielded) {
+ if (i >= 5) {
+ fprintf(stderr, "hostcall yielded too many results\n");
+ goto fail3;
+ }
+
+ struct yield_resume_val val = *(struct yield_resume_val *) res.val.yielded.val;
+
+ switch (val.tag) {
+ case yield_resume_tag_mult: {
+ uint64_t mult_result = val.val.mult.x * val.val.mult.y;
+ lucet_instance_resume(inst, &mult_result, &res);
+ continue;
+ }
+ case yield_resume_tag_result: {
+ results[i++] = val.val.result;
+ lucet_instance_resume(inst, NULL, &res);
+ continue;
+ }
+ default: {
+ fprintf(stderr, "unexpected yield_resume_tag\n");
+ goto fail3;
+ }
+ }
+ }
+ if (err != lucet_error_ok) {
+ fprintf(stderr, "instance finished with non-ok error: %s\n", lucet_error_name(err));
+ goto fail3;
+ }
+
+ if (res.tag != lucet_result_tag_returned) {
+ fprintf(stderr, "final instance result wasn't returned\n");
+ goto fail3;
+ }
+
+ uint64_t final_result = LUCET_UNTYPED_RETVAL_TO_U64(res.val.returned);
+
+ lucet_instance_release(inst);
+ lucet_region_release(region);
+ lucet_dl_module_release(mod);
+
+ uint64_t expected_results[5] = { 1, 2, 6, 24, 120 };
+ bool results_correct = final_result == 120;
+ for (i = 0; i < 5; i++) {
+ results_correct = results_correct && (results[i] == expected_results[i]);
+ }
+
+ return results_correct;
+
+fail3:
+ lucet_instance_release(inst);
+fail2:
+ lucet_region_release(region);
+fail1:
+ lucet_dl_module_release(mod);
+ return false;
+}
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/entrypoint.rs b/third_party/rust/lucet-runtime-wasmsbx/tests/entrypoint.rs
new file mode 100644
index 0000000000..a4e849cf6f
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/entrypoint.rs
@@ -0,0 +1,3 @@
+use lucet_runtime_tests::entrypoint_tests;
+
+entrypoint_tests!(lucet_runtime::MmapRegion);
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/globals.rs b/third_party/rust/lucet-runtime-wasmsbx/tests/globals.rs
new file mode 100644
index 0000000000..88f82089e6
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/globals.rs
@@ -0,0 +1,3 @@
+use lucet_runtime_tests::globals_tests;
+
+globals_tests!(lucet_runtime::MmapRegion);
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/guest_fault.rs b/third_party/rust/lucet-runtime-wasmsbx/tests/guest_fault.rs
new file mode 100644
index 0000000000..945db4078e
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/guest_fault.rs
@@ -0,0 +1,3 @@
+use lucet_runtime_tests::guest_fault_tests;
+
+guest_fault_tests!(lucet_runtime::MmapRegion);
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/guests/null.c b/third_party/rust/lucet-runtime-wasmsbx/tests/guests/null.c
new file mode 100644
index 0000000000..dbec2355bc
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/guests/null.c
@@ -0,0 +1,3 @@
+void f() {
+ return;
+}
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/guests/yield_resume.c b/third_party/rust/lucet-runtime-wasmsbx/tests/guests/yield_resume.c
new file mode 100644
index 0000000000..22b68f37eb
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/guests/yield_resume.c
@@ -0,0 +1,8 @@
+#include <stdint.h>
+
+extern uint64_t lucet_runtime_test_hostcall_yield_resume(uint64_t n);
+
+uint64_t f()
+{
+ return lucet_runtime_test_hostcall_yield_resume(5);
+}
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/guests/yield_resume_bindings.json b/third_party/rust/lucet-runtime-wasmsbx/tests/guests/yield_resume_bindings.json
new file mode 100644
index 0000000000..fe9ffc6af7
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/guests/yield_resume_bindings.json
@@ -0,0 +1,5 @@
+{
+ "env": {
+ "lucet_runtime_test_hostcall_yield_resume": "lucet_runtime_test_hostcall_yield_resume"
+ }
+}
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/host.rs b/third_party/rust/lucet-runtime-wasmsbx/tests/host.rs
new file mode 100644
index 0000000000..d42cb2bce0
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/host.rs
@@ -0,0 +1,3 @@
+use lucet_runtime_tests::host_tests;
+
+host_tests!(lucet_runtime::MmapRegion);
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting.rs b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting.rs
new file mode 100644
index 0000000000..fd1bf88629
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting.rs
@@ -0,0 +1,71 @@
+use lucet_runtime::{DlModule, Error, Limits, MmapRegion, Region, RunResult};
+use lucetc::{Lucetc, LucetcOpts};
+use rayon::prelude::*;
+use std::fs::DirEntry;
+use std::path::Path;
+use std::sync::Arc;
+use tempfile::TempDir;
+
+pub fn wasm_test<P: AsRef<Path>>(wasm_file: P) -> Result<Arc<DlModule>, Error> {
+ let workdir = TempDir::new().expect("create working directory");
+
+ let native_build = Lucetc::new(wasm_file).with_count_instructions(true);
+
+ let so_file = workdir.path().join("out.so");
+
+ native_build.shared_object_file(so_file.clone())?;
+
+ let dlmodule = DlModule::load(so_file)?;
+
+ Ok(dlmodule)
+}
+
+#[test]
+pub fn check_instruction_counts() {
+ let files: Vec<DirEntry> = std::fs::read_dir("./tests/instruction_counting")
+ .expect("can iterate test files")
+ .map(|ent| {
+ let ent = ent.expect("can get test files");
+ assert!(
+ ent.file_type().unwrap().is_file(),
+ "directories not supported in test/instruction_counting"
+ );
+ ent
+ })
+ .collect();
+
+ assert!(
+ files.len() > 0,
+ "there are no test cases in the `instruction_counting` directory"
+ );
+
+ files.par_iter().for_each(|ent| {
+ let wasm_path = ent.path();
+ let module = wasm_test(&wasm_path).expect("can load module");
+
+ let region = MmapRegion::create(1, &Limits::default()).expect("region can be created");
+
+ let mut inst = region
+ .new_instance(module)
+ .expect("instance can be created");
+
+ inst.run("test_function", &[]).expect("instance runs");
+
+ let instruction_count = inst.get_instruction_count();
+
+ assert_eq!(
+ instruction_count,
+ match inst
+ .run("instruction_count", &[])
+ .expect("instance still runs")
+ {
+ RunResult::Returned(value) => value.as_i64() as u64,
+ RunResult::Yielded(_) => {
+ panic!("instruction counting test runner doesn't support yielding");
+ }
+ },
+ "instruction count for test case {} is incorrect",
+ wasm_path.display()
+ );
+ });
+}
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/arithmetic_count.wat b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/arithmetic_count.wat
new file mode 100644
index 0000000000..90891313a4
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/arithmetic_count.wat
@@ -0,0 +1,11 @@
+(module
+ (func $main (export "test_function")
+ i32.const 4
+ i32.const -5
+ i32.add
+ drop
+ )
+ (func $instruction_count (export "instruction_count") (result i64)
+ i64.const 3
+ )
+)
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/br_table_count.wat b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/br_table_count.wat
new file mode 100644
index 0000000000..29846f083d
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/br_table_count.wat
@@ -0,0 +1,25 @@
+(module
+ (func $main (export "test_function")
+ block $a
+ i64.const 1
+ drop
+
+ block $b
+ i64.const 2
+ drop
+
+ block $c
+ i64.const 3
+ drop
+
+ ;; 3 for above consts, one for i32.const below, 2 for br_table
+ ;; totalling to an expected count of 6
+ (br_table 0 1 2 3 (i32.const 4))
+ end
+ end
+ end
+ )
+ (func $instruction_count (export "instruction_count") (result i64)
+ i64.const 6
+ )
+)
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/calls.wat b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/calls.wat
new file mode 100644
index 0000000000..6fba4c4e6e
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/calls.wat
@@ -0,0 +1,22 @@
+(module
+ (func $mul2 (param $in i64) (result i64)
+ get_local $in
+ get_local $in
+ i64.mul
+ )
+ (func $main (export "test_function")
+ i64.const 1
+ call $mul2 ;; one from the call, three from the called function, one for the return
+ drop
+ i64.const 2
+ call $mul2 ;; and again
+ drop
+ i64.const 3
+ call $mul2 ;; and again
+ ;; for a total of 3 * 6 == 18 instructions
+ drop
+ )
+ (func $instruction_count (export "instruction_count") (result i64)
+ i64.const 18
+ )
+)
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/count_after_br.wat b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/count_after_br.wat
new file mode 100644
index 0000000000..52e180f6f1
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/count_after_br.wat
@@ -0,0 +1,21 @@
+(module
+ (func $main (export "test_function")
+ block $ops
+ i32.const 11
+ i32.const 10
+ i32.add
+
+ br 0 ;; branch to enclosing scope (end of this block)
+ ;; at this point we've counted four operations...
+ end
+
+ i32.const 14
+ i32.const 15
+ i32.add
+ ;; this puts us at 7 operations
+ drop
+ )
+ (func $instruction_count (export "instruction_count") (result i64)
+ i64.const 7
+ )
+)
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/empty_loop.wat b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/empty_loop.wat
new file mode 100644
index 0000000000..1817743134
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/empty_loop.wat
@@ -0,0 +1,9 @@
+(module
+ (func $main (export "test_function") (local $i i32)
+ loop $inner
+ end
+ )
+ (func $instruction_count (export "instruction_count") (result i64)
+ i64.const 0
+ )
+)
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/empty_loop_2.wat b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/empty_loop_2.wat
new file mode 100644
index 0000000000..302e0df8e4
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/empty_loop_2.wat
@@ -0,0 +1,11 @@
+(module
+ (func $main (export "test_function") (local $i i32)
+ block $a
+ loop $inner
+ end
+ end
+ )
+ (func $instruction_count (export "instruction_count") (result i64)
+ i64.const 0
+ )
+)
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/if_count.wat b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/if_count.wat
new file mode 100644
index 0000000000..e4bcb617ee
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/if_count.wat
@@ -0,0 +1,21 @@
+(module
+ (func $main (export "test_function")
+ i32.const 11
+ i32.const 10
+ i32.gt_s
+ ;; this counts up to 3
+ (if ;; this makes 4
+ (then
+ i64.const 5
+ i64.const 10
+ i64.mul
+ ;; and these were another 3
+ drop
+ ;; drop is ignored for a total of 7 operations
+ )
+ )
+ )
+ (func $instruction_count (export "instruction_count") (result i64)
+ i64.const 7
+ )
+)
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/if_not_taken_count.wat b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/if_not_taken_count.wat
new file mode 100644
index 0000000000..af5794d5a9
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/if_not_taken_count.wat
@@ -0,0 +1,21 @@
+(module
+ (func $main (export "test_function")
+ i32.const 10
+ i32.const 11
+ i32.gt_s
+ ;; this counts up to 3
+ (if ;; this makes 4
+ ;; but the `then` branch is not taken
+ (then
+ i64.const 5
+ i64.const 10
+ i64.mul
+ drop
+ )
+ )
+ ;; so we only execute 4 operations
+ )
+ (func $instruction_count (export "instruction_count") (result i64)
+ i64.const 4
+ )
+)
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/indirect_calls.wat b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/indirect_calls.wat
new file mode 100644
index 0000000000..e6a5bbf994
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/indirect_calls.wat
@@ -0,0 +1,68 @@
+(module
+ (type $mulfn (func (param i64) (result i64)))
+
+ ;; mul2 is 3 operations
+ (func $mul2 (param $in i64) (result i64)
+ get_local $in
+ get_local $in
+ i64.add
+ )
+ ;; mul4 is 2 * |mul2| + 2 call + 3 == 13
+ (func $mul4 (param $in i64) (result i64)
+ get_local $in
+ call $mul2
+ get_local $in
+ call $mul2
+ i64.add
+ )
+ ;; mul8 is 2 * |mul4| + 2 call + 3 == 33
+ (func $mul8 (param $in i64) (result i64)
+ get_local $in
+ call $mul4
+ get_local $in
+ call $mul4
+ i64.add
+ )
+ ;; mul16 is 2 * |mul8| + 2 call + 3 == 73
+ ;; by entire accident the number of instructions for
+ ;; subsequent similar functions for higher powers is given by
+ ;; mul(n^2) == 10 * (2 ^ (n - 1)) - 10 + 3
+ (func $mul16 (param $in i64) (result i64)
+ get_local $in
+ call $mul8
+ get_local $in
+ call $mul8
+ i64.add
+ )
+
+ (table anyfunc
+ (elem
+ $mul2 $mul4 $mul8 $mul16
+ )
+ )
+
+ (func $main (export "test_function")
+ ;; call_indirect here is 2, plus 1 for the const, one for the index, and
+ ;; 1 for return
+ ;; the called function (mul2) is 3 instructions, for 8 total.
+ (call_indirect (type $mulfn) (i64.const 0) (i32.const 0))
+ drop
+
+ ;; call_indirect here is 2, plus 1 for the const, one for the index, and
+ ;; 1 for return
+ ;; the called function (mul4) is 13 instructions, for 18 total.
+ (call_indirect (type $mulfn) (i64.const 1) (i32.const 1))
+ drop
+
+ ;; call_indirect here is 2, plus 1 for the const, one for the index, and
+ ;; 1 for return
+ ;; the called function (mul16) is 73 instructions, for 78 total.
+ (call_indirect (type $mulfn) (i64.const 2) (i32.const 3))
+ drop
+
+ ;; for a total of 8 + 18 + 78 == 104 instructions
+ )
+ (func $instruction_count (export "instruction_count") (result i64)
+ i64.const 104
+ )
+)
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/loops.wat b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/loops.wat
new file mode 100644
index 0000000000..3b68a0abd7
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/loops.wat
@@ -0,0 +1,30 @@
+(module
+ (func $main (export "test_function") (local $i i32)
+ i32.const 0
+ set_local $i
+ block $outer
+ loop $inner
+ ;; each loop iteration is:
+ ;; * 4 operations to increment i
+ ;; * 3 operations to test i == 10
+ ;; * 1 branch to break (untaken)
+ ;; * 1 branch to loop
+ get_local $i
+ i32.const 1
+ i32.add
+ set_local $i
+ get_local $i
+ i32.const 10
+ i32.eq
+ br_if $outer
+ br $inner
+ end
+ end
+ ;; iterating i = 0..9, that's 10 * 8 instructions from full executions,
+ ;; plus 9 instructions from the last round.
+ ;; add two for initializing i and that gives 80 + 9 + 2 = 91 instructions
+ )
+ (func $instruction_count (export "instruction_count") (result i64)
+ i64.const 91
+ )
+)
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/unreachable_call.wat b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/unreachable_call.wat
new file mode 100644
index 0000000000..3caf474288
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/instruction_counting/unreachable_call.wat
@@ -0,0 +1,17 @@
+(module
+ (func $main (export "test_function") (result i64)
+ ;; counting the const
+ i64.const 1
+ ;; return is counted by the caller, so we count 1 so far
+ return
+
+ ;; we had a bug where calls in unreachable code would still add
+ ;; one to the instruction counter to track a matching return,
+ ;; but the call would never be made, so the return would never
+ ;; occur, and the count was in error.
+ call $main
+ )
+ (func $instruction_count (export "instruction_count") (result i64)
+ i64.const 1
+ )
+)
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/memory.rs b/third_party/rust/lucet-runtime-wasmsbx/tests/memory.rs
new file mode 100644
index 0000000000..6778ab28d5
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/memory.rs
@@ -0,0 +1,3 @@
+use lucet_runtime_tests::memory_tests;
+
+memory_tests!(lucet_runtime::MmapRegion);
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/stack.rs b/third_party/rust/lucet-runtime-wasmsbx/tests/stack.rs
new file mode 100644
index 0000000000..ab4ce5f626
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/stack.rs
@@ -0,0 +1,3 @@
+use lucet_runtime_tests::stack_tests;
+
+stack_tests!(lucet_runtime::MmapRegion);
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/start.rs b/third_party/rust/lucet-runtime-wasmsbx/tests/start.rs
new file mode 100644
index 0000000000..6b1f2a26c4
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/start.rs
@@ -0,0 +1,3 @@
+use lucet_runtime_tests::start_tests;
+
+start_tests!(lucet_runtime::MmapRegion);
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/strcmp.rs b/third_party/rust/lucet-runtime-wasmsbx/tests/strcmp.rs
new file mode 100644
index 0000000000..85547904c5
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/strcmp.rs
@@ -0,0 +1,3 @@
+use lucet_runtime_tests::strcmp_tests;
+
+strcmp_tests!(lucet_runtime::MmapRegion);
diff --git a/third_party/rust/lucet-runtime-wasmsbx/tests/val.rs b/third_party/rust/lucet-runtime-wasmsbx/tests/val.rs
new file mode 100644
index 0000000000..7fe78e3e22
--- /dev/null
+++ b/third_party/rust/lucet-runtime-wasmsbx/tests/val.rs
@@ -0,0 +1,61 @@
+use lucet_runtime_internals::val::UntypedRetVal;
+
+#[test]
+fn untyped_ret_val_from_f32() {
+ assert_eq!(1.2, f32::from(UntypedRetVal::from(1.2f32)));
+}
+
+#[test]
+fn untyped_ret_val_from_f64() {
+ assert_eq!(1.2, f64::from(UntypedRetVal::from(1.2f64)));
+}
+
+#[test]
+fn untyped_ret_val_from_u8() {
+ assert_eq!(5, u8::from(UntypedRetVal::from(5u8)));
+}
+
+#[test]
+fn untyped_ret_val_from_u16() {
+ assert_eq!(5, u16::from(UntypedRetVal::from(5u16)));
+}
+
+#[test]
+fn untyped_ret_val_from_u32() {
+ assert_eq!(5, u32::from(UntypedRetVal::from(5u32)));
+}
+
+#[test]
+fn untyped_ret_val_from_u64() {
+ assert_eq!(5, u64::from(UntypedRetVal::from(5u64)));
+}
+
+#[test]
+fn untyped_ret_val_from_i8() {
+ assert_eq!(5, i8::from(UntypedRetVal::from(5i8)));
+}
+
+#[test]
+fn untyped_ret_val_from_i16() {
+ assert_eq!(5, i16::from(UntypedRetVal::from(5i16)));
+}
+
+#[test]
+fn untyped_ret_val_from_i32() {
+ assert_eq!(5, i32::from(UntypedRetVal::from(5i32)));
+}
+
+#[test]
+fn untyped_ret_val_from_i64() {
+ assert_eq!(5, i64::from(UntypedRetVal::from(5i64)));
+}
+
+#[test]
+fn untyped_ret_val_from_bool_true() {
+ assert_eq!(true, bool::from(UntypedRetVal::from(true)));
+}
+
+#[test]
+fn untyped_ret_val_from_bool_false() {
+ assert_eq!(false, bool::from(UntypedRetVal::from(false)));
+}