diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/lucet-runtime-wasmsbx | |
parent | Initial commit. (diff) | |
download | firefox-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')
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, ®ion); + 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, ®ion); + 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))); +} |