diff --git a/.github/workflows/compile.yml b/.github/workflows/compile.yml index e923f3cc..a374e6a7 100644 --- a/.github/workflows/compile.yml +++ b/.github/workflows/compile.yml @@ -28,7 +28,7 @@ jobs: - uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: nightly - target: x86_64-unknown-none + target: x86_64-unknown-none,wasm32-unknown-unknown components: clippy - name: Setup run: sudo apt update && sudo apt install gcc-multilib just @@ -43,7 +43,7 @@ jobs: - uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: nightly - target: x86_64-unknown-none + target: x86_64-unknown-none,wasm32-unknown-unknown - name: Setup run: | sudo apt update && sudo apt install gcc-multilib just diff --git a/Cargo.lock b/Cargo.lock index 164483f2..de1ac492 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -69,9 +69,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "ar_archive_writer" @@ -669,6 +669,17 @@ dependencies = [ "objc2", ] +[[package]] +name = "dlmalloc" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad5208a115eaba24916f7456929832e310a81518c641f93fee4f89aa93aa3675" +dependencies = [ + "cfg-if", + "libc", + "windows-sys", +] + [[package]] name = "either" version = "1.16.0" @@ -796,6 +807,8 @@ dependencies = [ "crossbeam-queue", "derive_more", "fixhandle", + "fixpostprocessor", + "fixprocedure", "fixshell", "futures", "hex", @@ -819,6 +832,23 @@ dependencies = [ "derive_more", ] +[[package]] +name = "fixpostprocessor" +version = "0.1.0" +dependencies = [ + "anyhow", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "fixprocedure" +version = "0.1.0" +dependencies = [ + "dlmalloc", + "fixutils", +] + [[package]] name = "fixshell" version = "0.1.0" @@ -832,12 +862,27 @@ dependencies = [ "user", ] +[[package]] +name = "fixutils" +version = "0.1.0" +dependencies = [ + "cc", + "fixhandle", + "macros", +] + [[package]] name = "foldhash" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "futures" version = "0.3.32" @@ -974,11 +1019,22 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", "rustc-std-workspace-alloc", "rustc-std-workspace-core", ] +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "foldhash 0.2.0", + "serde", + "serde_core", +] + [[package]] name = "hashbrown" version = "0.17.1" @@ -1047,6 +1103,8 @@ checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", "hashbrown 0.17.1", + "serde", + "serde_core", ] [[package]] @@ -1133,6 +1191,12 @@ dependencies = [ "vmm-sys-util", ] +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" version = "0.2.186" @@ -2046,6 +2110,29 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "wasm-encoder" +version = "0.245.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9dca005e69bf015e45577e415b9af8c67e8ee3c0e38b5b0add5aa92581ed5c" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.245.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f08c9adee0428b7bddf3890fc27e015ac4b761cc608c822667102b8bfd6995e" +dependencies = [ + "bitflags 2.11.1", + "hashbrown 0.16.1", + "indexmap", + "semver", + "serde", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/common/src/lib.rs b/common/src/lib.rs index 66425c37..43a154ac 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -12,21 +12,28 @@ #![feature(unboxed_closures)] #![cfg_attr(feature = "thread_local_cache", feature(thread_local))] -pub mod buddy; -pub mod refcnt; -pub use buddy::BuddyAllocator; -pub mod arrayvec; pub mod bitpack; -pub mod controlreg; -pub mod elfloader; -pub mod ipaddr; -pub mod pipe; -pub mod protocol; -pub mod sendable; -pub mod util; -#[cfg(feature = "std")] -pub mod mmap; +#[cfg(not(target_family = "wasm"))] +#[path = "."] +mod not_wasm { + pub mod arrayvec; + pub mod buddy; + pub mod controlreg; + pub mod elfloader; + pub mod ipaddr; + pub mod pipe; + pub mod protocol; + pub mod refcnt; + pub mod sendable; + pub mod util; + + #[cfg(feature = "std")] + pub mod mmap; +} + +#[cfg(not(target_family = "wasm"))] +pub use not_wasm::{buddy::BuddyAllocator, *}; #[repr(C)] #[derive(Debug)] diff --git a/fix/Cargo.toml b/fix/Cargo.toml index 42f58af4..2d5bc7b9 100644 --- a/fix/Cargo.toml +++ b/fix/Cargo.toml @@ -48,6 +48,8 @@ crossbeam-queue = { [build-dependencies] fixshell = { path = "shell", artifact="staticlib", target = "x86_64-unknown-none" } +fixpostprocessor = { path = "postprocessor" } +fixprocedure = { path = "procedure", artifact = "cdylib", target = "wasm32-unknown-unknown" } anyhow = "1.0.98" bindgen = "0.72.1" cc = "1.2.30" diff --git a/fix/build.rs b/fix/build.rs index ba0b4635..8c230119 100644 --- a/fix/build.rs +++ b/fix/build.rs @@ -203,7 +203,7 @@ fn main() -> Result<()> { ); } let wat = std::fs::read(f.path())?; - let wasm = wat2wasm(&wat)?; + let wasm = fixpostprocessor::process(&wat2wasm(&wat)?)?; let (c, h) = wasm2c(&wasm)?; let elf = c2elf(&c, &h)?; std::fs::write(&dst, elf)?; diff --git a/fix/postprocessor/Cargo.toml b/fix/postprocessor/Cargo.toml new file mode 100644 index 00000000..b3935f0e --- /dev/null +++ b/fix/postprocessor/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "fixpostprocessor" +version = "0.1.0" +edition = "2024" + +[dependencies] +anyhow = "1.0.104" +wasm-encoder = { version = "0.245.1", features = ["wasmparser"] } +wasmparser = "0.245.1" diff --git a/fix/postprocessor/src/lib.rs b/fix/postprocessor/src/lib.rs new file mode 100644 index 00000000..d48f12c0 --- /dev/null +++ b/fix/postprocessor/src/lib.rs @@ -0,0 +1,38 @@ +use anyhow::Result; +use wasm_encoder::{ + MemorySection, MemoryType, Module, RawSection, + reencode::{Reencode, RoundtripReencoder}, +}; +use wasmparser::{Parser, Payload}; + +pub fn process(wasm: &[u8]) -> Result> { + let mut module = Module::new(); + for payload in Parser::new(0).parse_all(wasm) { + match payload? { + Payload::MemorySection(section) => { + let mut memory_section = MemorySection::new(); + RoundtripReencoder.parse_memory_section(&mut memory_section, section)?; + // Inject one hardcoded memory + memory_section.memory(MemoryType { + minimum: 1, + maximum: None, + memory64: false, + shared: false, + page_size_log2: None, + }); + module.section(&memory_section); + } + // Don't change other sections + payload => { + if let Some((id, range)) = payload.as_section() { + module.section(&RawSection { + id, + data: &wasm[range], + }); + } + } + } + } + + Ok(module.finish()) +} diff --git a/fix/procedure/Cargo.toml b/fix/procedure/Cargo.toml new file mode 100644 index 00000000..e0691708 --- /dev/null +++ b/fix/procedure/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "fixprocedure" +version = "0.1.0" +edition = "2024" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +fixutils = { path = "../utils" } +dlmalloc = { version = "0.2.14", features = ["global"] } \ No newline at end of file diff --git a/fix/procedure/src/lib.rs b/fix/procedure/src/lib.rs new file mode 100644 index 00000000..0992608a --- /dev/null +++ b/fix/procedure/src/lib.rs @@ -0,0 +1,25 @@ +#![cfg_attr(target_arch = "wasm32", no_std)] +use dlmalloc::GlobalDlmalloc; +#[global_allocator] +static ALLOCATOR: GlobalDlmalloc = GlobalDlmalloc; +use fixutils::*; + +extern crate alloc; +use alloc::vec::Vec; + +const HELLO: &[u8] = b"hello"; +const WORLD: &[u8] = b" world"; + +#[fix_entrypoint] +pub fn _fixpoint_apply(_combination: RustHandle) -> RustHandle { + unsafe { + let mut blob: Vec = HELLO.to_vec(); + memory_1_write(blob.as_ptr() as u32, blob.len()); + let handle = create_blob(1, blob.len()); + attach_blob(1, &handle); + memory_1_read(blob.as_mut_ptr() as u32, len(&handle)); + blob.append(&mut WORLD.to_vec()); + memory_1_write(blob.as_ptr() as u32, blob.len()); + create_blob(1, blob.len()) + } +} diff --git a/fix/shell/src/fixpoint.rs b/fix/shell/src/fixpoint.rs index 66946837..1bca0429 100644 --- a/fix/shell/src/fixpoint.rs +++ b/fix/shell/src/fixpoint.rs @@ -21,9 +21,9 @@ pub unsafe extern "C" fn w2c_fixpoint_attach_blob( let addr = (1usize << 32) * memory_idx as usize; let len = shell::fixpoint_attach_blob(addr as *mut c_void, handle.bytes); // TODO: this math is wrong - (*memory).pages = (len as u64 / PAGE_SIZE as u64) + 1; + (*memory).pages = len.div_ceil(PAGE_SIZE as usize) as u64; (*memory).max_pages = (1u64 << 32) / PAGE_SIZE as u64; - (*memory).size = len as u64; + (*memory).size = (*memory).pages * PAGE_SIZE as u64; } } @@ -102,6 +102,24 @@ pub unsafe extern "C" fn w2c_fixpoint_create_blob_i32( } } +#[unsafe(no_mangle)] +pub unsafe extern "C" fn w2c_fixpoint_create_blob( + fixpoint: *mut w2c_fixpoint, + memory_index: u32, + length: u32, +) -> wasm_rt_externref_t { + assert!(memory_index < 63); + unsafe { + let memory = crate::rt::MEMORIES[memory_index as usize]; + wasm_rt_externref_t { + bytes: shell::fixpoint_create_blob(core::slice::from_raw_parts( + (*memory).data, + length as usize, + )), + } + } +} + #[unsafe(no_mangle)] pub unsafe extern "C" fn w2c_fixpoint_is_blob_obj( fixpoint: *mut w2c_fixpoint, @@ -162,3 +180,11 @@ pub unsafe extern "C" fn w2c_fixpoint_create_strict_encode( bytes: shell::fixpoint_create_strict_encode(handle.bytes), } } + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn w2c_fixpoint_len( + fixpoint: *mut w2c_fixpoint, + handle: wasm_rt_externref_t, +) -> usize { + shell::fixpoint_len(handle.bytes) +} diff --git a/fix/shell/src/shell.rs b/fix/shell/src/shell.rs index a22f94c5..8d05ff12 100644 --- a/fix/shell/src/shell.rs +++ b/fix/shell/src/shell.rs @@ -255,7 +255,7 @@ pub fn fixpoint_create_strict_encode(handle: [u8; 32]) -> [u8; 32] { encode.pack() } -fn fixpoint_len(handle: [u8; 32]) -> usize { +pub fn fixpoint_len(handle: [u8; 32]) -> usize { let handle = Handle::unpack(handle); handle.len() } diff --git a/fix/utils/Cargo.toml b/fix/utils/Cargo.toml new file mode 100644 index 00000000..c5b50182 --- /dev/null +++ b/fix/utils/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "fixutils" +version = "0.1.0" +edition = "2024" + +[lib] +crate-type = ["rlib"] + +[dependencies] +fixhandle = { path = "../handle", default-features = false } +macros = { path = "../../macros" } + +[build-dependencies] +cc = "1.2.30" \ No newline at end of file diff --git a/fix/utils/build.rs b/fix/utils/build.rs new file mode 100644 index 00000000..f74cce1a --- /dev/null +++ b/fix/utils/build.rs @@ -0,0 +1,9 @@ +fn main() { + println!("cargo::rerun-if-changed=src/fixpoint.c"); + + cc::Build::new() + .file("src/fixpoint.c") + .flag("-mreference-types") + .opt_level(2) + .compile("fixpoint"); +} diff --git a/fix/utils/src/fixpoint.c b/fix/utils/src/fixpoint.c new file mode 100644 index 00000000..36980f2c --- /dev/null +++ b/fix/utils/src/fixpoint.c @@ -0,0 +1,67 @@ +#include + +typedef __externref_t externref; +static externref __attribute__((address_space(1))) combination_global; + +enum producer { + COMBINATION = 0, + TABLE_GET = 1, + CREATE_BLOB = 2, + CREATE_TREE = 3, +}; + +struct RustHandle { + uint8_t name[24]; + union { + uint64_t body; + struct { + uint32_t entry; + uint8_t producer; + uint8_t index; + uint16_t meta; + }; + }; +}; + +// Imports +__attribute__((import_module("fixpoint"), import_name("create_blob"))) +extern externref fixpoint_create_blob(uint32_t memory_index, uint32_t length); + +__attribute__((import_module("fixpoint"), import_name("attach_blob"))) +extern void fixpoint_attach_blob(uint32_t memory_index, externref handle); + +__attribute__((import_module("fixpoint"), import_name("len"))) +extern uint32_t fixpoint_len(externref handle); + +static externref resolve(const struct RustHandle *handle) { + switch (handle->producer) { + case COMBINATION: + return combination_global; + case CREATE_BLOB: + return fixpoint_create_blob(handle->index, handle->entry); + default: + __builtin_unreachable(); + } +} + +void attach_blob(uint32_t memory_index, const struct RustHandle *handle) { + fixpoint_attach_blob(memory_index, resolve(handle)); +} + +uint32_t len(const struct RustHandle *handle) { + return fixpoint_len(resolve(handle)); +} + +extern struct RustHandle _fixpoint_apply_inner(struct RustHandle combination_global); + +static const struct RustHandle combination = { + .producer = COMBINATION, + .meta = 0x0440, // meta bits for Handle::Object(Object::Tree(Tree::Tree(_))) +}; + +__attribute__((export_name("_fixpoint_apply"))) +externref fixpoint_apply(externref input) { + combination_global = input; + struct RustHandle output = _fixpoint_apply_inner(combination); + return resolve(&output); +} \ No newline at end of file diff --git a/fix/utils/src/lib.rs b/fix/utils/src/lib.rs new file mode 100644 index 00000000..eda7d548 --- /dev/null +++ b/fix/utils/src/lib.rs @@ -0,0 +1,78 @@ +#![cfg_attr(target_arch = "wasm32", no_std, feature(asm_experimental_arch))] +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + core::arch::wasm32::unreachable() +} +pub use fixhandle::*; +pub use macros::fix_entrypoint; + +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Producer { + // memory_index, length + CreateBlob = 2, +} + +fn encode_args(producer: Producer, index: u32, entry: u32) -> RawName { + debug_assert!(index < 256); + let mut bytes = [0; 32]; + // 4 bytes for entry/length argument + bytes[24..28].copy_from_slice(&entry.to_le_bytes()); + // One byte for producer type + bytes[28] = producer as u8; + // One byte for table/memory index + bytes[29] = index as u8; + RawName::forge(bytes) +} + +#[repr(C, align(8))] +pub struct RustHandle([u8; 32]); + +impl RustHandle { + fn new(handle: Handle) -> Self { + Self(handle.pack()) + } +} + +core::arch::global_asm!( + r#" + .globl memory_1_read +memory_1_read: + .functype memory_1_read (i32, i32) -> () + local.get 0 + i32.const 0 + local.get 1 + memory.copy 0, 1 + end_function + + .globl memory_1_write +memory_1_write: + .functype memory_1_write (i32, i32) -> () + i32.const 0 + local.get 0 + local.get 1 + memory.copy 1, 0 + end_function + "# +); + +unsafe extern "C" { + // Copies length bytes from memory 1 to destination in program memory 0 + pub fn memory_1_read(destination: u32, length: usize); + + // Copies length bytes from source in program memory 0 to memory 1 + pub fn memory_1_write(source: u32, length: usize); + + pub fn attach_blob(memory_index: u32, handle: *const RustHandle); + pub fn len(handle: *const RustHandle) -> usize; +} + +pub fn create_blob(memory_index: u32, length: usize) -> RustHandle { + RustHandle::new(Handle::Object(Object::Blob(Blob::Blob(unsafe { + BlobName::new(encode_args( + Producer::CreateBlob, + memory_index, + length as u32, + )) + })))) +} diff --git a/fix/wasm/fixprocedure.wasm b/fix/wasm/fixprocedure.wasm new file mode 100644 index 00000000..ad0fb731 Binary files /dev/null and b/fix/wasm/fixprocedure.wasm differ diff --git a/macros/src/fix_utils.rs b/macros/src/fix_utils.rs new file mode 100644 index 00000000..d9afe3f9 --- /dev/null +++ b/macros/src/fix_utils.rs @@ -0,0 +1,18 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, ItemFn}; + +pub fn entrypoint(_attr: TokenStream, item: TokenStream) -> TokenStream { + let item = parse_macro_input!(item as ItemFn); + let ident = &item.sig.ident; + + quote! { + #item + #[unsafe(export_name = "_fixpoint_apply_inner")] + pub extern "C" fn _fixpoint_apply_inner(combination: ::fixutils::RustHandle) -> ::fixutils::RustHandle { + let _fixpoint_apply: fn(::fixutils::RustHandle) -> ::fixutils::RustHandle = #ident; + _fixpoint_apply(combination) + } + } + .into() +} diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 8ce770b1..c36ce0b1 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -2,6 +2,7 @@ use proc_macro::TokenStream; mod bitpack; mod core_local; +mod fix_utils; mod testing; mod util; @@ -39,3 +40,8 @@ pub fn kmain(attr: TokenStream, item: TokenStream) -> TokenStream { pub fn bitpack(input: TokenStream) -> TokenStream { bitpack::bitpack(input) } + +#[proc_macro_attribute] +pub fn fix_entrypoint(attr: TokenStream, item: TokenStream) -> TokenStream { + fix_utils::entrypoint(attr, item) +} diff --git a/procedure.fix b/procedure.fix new file mode 100644 index 00000000..25b04695 --- /dev/null +++ b/procedure.fix @@ -0,0 +1,2 @@ +(let ((procedure @"./target/x86_64-unknown-none/debug/fixprocedure")) + !*(procedure)) \ No newline at end of file