From d0edd666e1dd667336059cec9e08c76c4c2f504e Mon Sep 17 00:00:00 2001 From: Jorge Prendes Date: Mon, 1 Sep 2025 21:56:54 +0100 Subject: [PATCH 1/9] move function type traits to hyperlight_common Signed-off-by: Jorge Prendes --- Cargo.lock | 1 + src/hyperlight_common/Cargo.toml | 4 +- src/hyperlight_common/src/func/error.rs | 45 ++++++++++++ src/hyperlight_common/src/func/functions.rs | 40 +++++++++++ src/hyperlight_common/src/func/mod.rs | 49 +++++++++++++ .../src/func/param_type.rs | 41 +++++------ .../src/func/ret_type.rs | 70 +++++++++---------- .../src/func/utils.rs | 2 + src/hyperlight_common/src/lib.rs | 3 + .../src/func/host_functions.rs | 55 ++++++++++----- src/hyperlight_host/src/func/mod.rs | 13 ++-- .../src/sandbox/initialized_multi_use.rs | 3 +- src/tests/rust_guests/simpleguest/Cargo.lock | 21 ++++++ 13 files changed, 255 insertions(+), 92 deletions(-) create mode 100644 src/hyperlight_common/src/func/error.rs create mode 100644 src/hyperlight_common/src/func/functions.rs create mode 100644 src/hyperlight_common/src/func/mod.rs rename src/{hyperlight_host => hyperlight_common}/src/func/param_type.rs (72%) rename src/{hyperlight_host => hyperlight_common}/src/func/ret_type.rs (60%) rename src/{hyperlight_host => hyperlight_common}/src/func/utils.rs (98%) diff --git a/Cargo.lock b/Cargo.lock index de52f6ae5..9f73bfbf0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1389,6 +1389,7 @@ dependencies = [ "flatbuffers", "log", "spin 0.10.0", + "thiserror 2.0.17", "tracing", ] diff --git a/src/hyperlight_common/Cargo.toml b/src/hyperlight_common/Cargo.toml index 3339e2b0d..a10a991d6 100644 --- a/src/hyperlight_common/Cargo.toml +++ b/src/hyperlight_common/Cargo.toml @@ -21,13 +21,15 @@ log = "0.4.29" tracing = { version = "0.1.43", optional = true } arbitrary = {version = "1.4.2", optional = true, features = ["derive"]} spin = "0.10.0" +thiserror = { version = "2.0.16", default-features = false } [features] default = ["tracing"] +tracing = ["dep:tracing"] fuzzing = ["dep:arbitrary"] trace_guest = [] mem_profile = [] -std = [] +std = ["thiserror/std", "log/std", "tracing/std"] [lib] bench = false # see https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options diff --git a/src/hyperlight_common/src/func/error.rs b/src/hyperlight_common/src/func/error.rs new file mode 100644 index 000000000..26883a7c7 --- /dev/null +++ b/src/hyperlight_common/src/func/error.rs @@ -0,0 +1,45 @@ +/* +Copyright 2025 The Hyperlight Authors. + +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. +*/ + +use alloc::string::String; + +use thiserror::Error; + +use crate::func::{ParameterValue, ReturnValue}; + +/// The error type for Hyperlight operations +#[derive(Error, Debug)] +pub enum Error { + /// Failed to get value from parameter value + #[error("Failed To Convert Parameter Value {0:?} to {1:?}")] + ParameterValueConversionFailure(ParameterValue, &'static str), + + /// Failed to get value from return value + #[error("Failed To Convert Return Value {0:?} to {1:?}")] + ReturnValueConversionFailure(ReturnValue, &'static str), + + /// A function was called with an incorrect number of arguments + #[error("The number of arguments to the function is wrong: got {0:?} expected {1:?}")] + UnexpectedNoOfArguments(usize, usize), + + /// The parameter value type is unexpected + #[error("The parameter value type is unexpected got {0:?} expected {1:?}")] + UnexpectedParameterValueType(ParameterValue, String), + + /// The return value type is unexpected + #[error("The return value type is unexpected got {0:?} expected {1:?}")] + UnexpectedReturnValueType(ReturnValue, String), +} diff --git a/src/hyperlight_common/src/func/functions.rs b/src/hyperlight_common/src/func/functions.rs new file mode 100644 index 000000000..f1468b53a --- /dev/null +++ b/src/hyperlight_common/src/func/functions.rs @@ -0,0 +1,40 @@ +/* +Copyright 2025 The Hyperlight Authors. + +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. +*/ + +use super::utils::for_each_tuple; +use super::{Error, ParameterTuple, ResultType, SupportedReturnType}; + +pub trait Function> { + fn call(&self, args: Args) -> Result; +} + +macro_rules! impl_function { + ([$N:expr] ($($p:ident: $P:ident),*)) => { + impl Function for F + where + F: Fn($($P),*) -> R, + ($($P,)*): ParameterTuple, + R: ResultType, + E: From, + { + fn call(&self, ($($p,)*): ($($P,)*)) -> Result { + (self)($($p),*).into_result() + } + } + }; +} + +for_each_tuple!(impl_function); diff --git a/src/hyperlight_common/src/func/mod.rs b/src/hyperlight_common/src/func/mod.rs new file mode 100644 index 000000000..5556f6fe7 --- /dev/null +++ b/src/hyperlight_common/src/func/mod.rs @@ -0,0 +1,49 @@ +/* +Copyright 2025 The Hyperlight Authors. + +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. +*/ + +/// Error types related to function support +pub(crate) mod error; +/// Definitions and functionality to enable guest-to-host function calling, +/// also called "host functions" +/// +/// This module includes functionality to do the following +/// +/// - Define several prototypes for what a host function must look like, +/// including the number of arguments (arity) they can have, supported argument +/// types, and supported return types +/// - Registering host functions to be callable by the guest +/// - Dynamically dispatching a call from the guest to the appropriate +/// host function +pub(crate) mod functions; +/// Definitions and functionality for supported parameter types +pub(crate) mod param_type; +/// Definitions and functionality for supported return types +pub(crate) mod ret_type; + +pub use error::Error; +/// Re-export for `HostFunction` trait +pub use functions::Function; +pub use param_type::{ParameterTuple, SupportedParameterType}; +pub use ret_type::{ResultType, SupportedReturnType}; + +/// Re-export for `ParameterValue` enum +pub use crate::flatbuffer_wrappers::function_types::ParameterValue; +/// Re-export for `ReturnType` enum +pub use crate::flatbuffer_wrappers::function_types::ReturnType; +/// Re-export for `ReturnType` enum +pub use crate::flatbuffer_wrappers::function_types::ReturnValue; + +mod utils; diff --git a/src/hyperlight_host/src/func/param_type.rs b/src/hyperlight_common/src/func/param_type.rs similarity index 72% rename from src/hyperlight_host/src/func/param_type.rs rename to src/hyperlight_common/src/func/param_type.rs index b86a89d33..b4db004d8 100644 --- a/src/hyperlight_host/src/func/param_type.rs +++ b/src/hyperlight_common/src/func/param_type.rs @@ -14,12 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterType, ParameterValue}; -use tracing::{Span, instrument}; +use alloc::string::String; +use alloc::vec; +use alloc::vec::Vec; +use super::error::Error; use super::utils::for_each_tuple; -use crate::HyperlightError::{ParameterValueConversionFailure, UnexpectedNoOfArguments}; -use crate::{Result, log_then_return}; +use crate::flatbuffer_wrappers::function_types::{ParameterType, ParameterValue}; /// This is a marker trait that is used to indicate that a type is a /// valid Hyperlight parameter type. @@ -34,7 +35,7 @@ pub trait SupportedParameterType: Sized + Clone + Send + Sync + 'static { /// `SupportedParameterType` fn into_value(self) -> ParameterValue; /// Get the actual inner value of this `SupportedParameterType` - fn from_value(value: ParameterValue) -> Result; + fn from_value(value: ParameterValue) -> Result; } // We can then implement these traits for each type that Hyperlight supports as a parameter or return type @@ -57,21 +58,17 @@ macro_rules! impl_supported_param_type { impl SupportedParameterType for $type { const TYPE: ParameterType = ParameterType::$enum; - #[instrument(skip_all, parent = Span::current(), level= "Trace")] fn into_value(self) -> ParameterValue { ParameterValue::$enum(self) } - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - fn from_value(value: ParameterValue) -> Result { + fn from_value(value: ParameterValue) -> Result { match value { ParameterValue::$enum(i) => Ok(i), - other => { - log_then_return!(ParameterValueConversionFailure( - other.clone(), - stringify!($type) - )); - } + other => Err(Error::ParameterValueConversionFailure( + other.clone(), + stringify!($type), + )), } } } @@ -93,7 +90,7 @@ pub trait ParameterTuple: Sized + Clone + Send + Sync + 'static { fn into_value(self) -> Vec; /// Get the actual inner value of this `SupportedParameterType` - fn from_value(value: Vec) -> Result; + fn from_value(value: Vec) -> Result; } impl ParameterTuple for T { @@ -101,18 +98,14 @@ impl ParameterTuple for T { const TYPE: &[ParameterType] = &[T::TYPE]; - #[instrument(skip_all, parent = Span::current(), level= "Trace")] fn into_value(self) -> Vec { vec![self.into_value()] } - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - fn from_value(value: Vec) -> Result { + fn from_value(value: Vec) -> Result { match <[ParameterValue; 1]>::try_from(value) { Ok([val]) => Ok(T::from_value(val)?), - Err(value) => { - log_then_return!(UnexpectedNoOfArguments(value.len(), 1)); - } + Err(value) => Err(Error::UnexpectedNoOfArguments(value.len(), 1)), } } } @@ -126,17 +119,15 @@ macro_rules! impl_param_tuple { $($param::TYPE),* ]; - #[instrument(skip_all, parent = Span::current(), level= "Trace")] fn into_value(self) -> Vec { let ($($name,)*) = self; vec![$($name.into_value()),*] } - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - fn from_value(value: Vec) -> Result { + fn from_value(value: Vec) -> Result { match <[ParameterValue; $N]>::try_from(value) { Ok([$($name,)*]) => Ok(($($param::from_value($name)?,)*)), - Err(value) => { log_then_return!(UnexpectedNoOfArguments(value.len(), $N)); } + Err(value) => Err(Error::UnexpectedNoOfArguments(value.len(), $N)) } } } diff --git a/src/hyperlight_host/src/func/ret_type.rs b/src/hyperlight_common/src/func/ret_type.rs similarity index 60% rename from src/hyperlight_host/src/func/ret_type.rs rename to src/hyperlight_common/src/func/ret_type.rs index 89fe1481f..0e9fd63d5 100644 --- a/src/hyperlight_host/src/func/ret_type.rs +++ b/src/hyperlight_common/src/func/ret_type.rs @@ -14,11 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -use hyperlight_common::flatbuffer_wrappers::function_types::{ReturnType, ReturnValue}; -use tracing::{Span, instrument}; +use alloc::string::String; +use alloc::vec::Vec; -use crate::HyperlightError::ReturnValueConversionFailure; -use crate::{Result, log_then_return}; +use super::error::Error; +use crate::flatbuffer_wrappers::function_types::{ReturnType, ReturnValue}; /// This is a marker trait that is used to indicate that a type is a valid Hyperlight return type. pub trait SupportedReturnType: Sized + Clone + Send + Sync + 'static { @@ -29,16 +29,7 @@ pub trait SupportedReturnType: Sized + Clone + Send + Sync + 'static { fn into_value(self) -> ReturnValue; /// Gets the inner value of the supported return type - fn from_value(value: ReturnValue) -> Result; -} - -/// A trait to handle either a [`SupportedReturnType`] or a [`Result`] -pub trait ResultType { - /// The return type of the supported return value - type ReturnType: SupportedReturnType; - - /// Convert the return type into a `Result` - fn into_result(self) -> Result; + fn from_value(value: ReturnValue) -> Result; } macro_rules! for_each_return_type { @@ -61,43 +52,46 @@ macro_rules! impl_supported_return_type { impl SupportedReturnType for $type { const TYPE: ReturnType = ReturnType::$enum; - #[instrument(skip_all, parent = Span::current(), level= "Trace")] fn into_value(self) -> ReturnValue { ReturnValue::$enum(self) } - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - fn from_value(value: ReturnValue) -> Result { + fn from_value(value: ReturnValue) -> Result { match value { ReturnValue::$enum(i) => Ok(i), - other => { - log_then_return!(ReturnValueConversionFailure( - other.clone(), - stringify!($type) - )); - } + other => Err(Error::ReturnValueConversionFailure( + other.clone(), + stringify!($type), + )), } } } + }; +} + +/// A trait to handle either a [`SupportedReturnType`] or a [`Result`] +pub trait ResultType { + /// The return type of the supported return value + type ReturnType: SupportedReturnType; - impl ResultType for $type { - type ReturnType = $type; + /// Convert the return type into a `Result` + fn into_result(self) -> Result; +} - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - fn into_result(self) -> Result { - Ok(self) - } - } +impl ResultType for T { + type ReturnType = T; - impl ResultType for Result<$type> { - type ReturnType = $type; + fn into_result(self) -> Result { + Ok(self) + } +} - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - fn into_result(self) -> Result { - self - } - } - }; +impl ResultType for Result { + type ReturnType = T; + + fn into_result(self) -> Result { + self + } } for_each_return_type!(impl_supported_return_type); diff --git a/src/hyperlight_host/src/func/utils.rs b/src/hyperlight_common/src/func/utils.rs similarity index 98% rename from src/hyperlight_host/src/func/utils.rs rename to src/hyperlight_common/src/func/utils.rs index 6e92ca041..e7132aef5 100644 --- a/src/hyperlight_host/src/func/utils.rs +++ b/src/hyperlight_common/src/func/utils.rs @@ -31,6 +31,8 @@ limitations under the License. /// /// for_each_tuple!(my_macro); /// ``` +#[doc(hidden)] +#[macro_export] macro_rules! for_each_tuple { (@ $macro:ident diff --git a/src/hyperlight_common/src/lib.rs b/src/hyperlight_common/src/lib.rs index 3ec6aa6fc..962b0ca25 100644 --- a/src/hyperlight_common/src/lib.rs +++ b/src/hyperlight_common/src/lib.rs @@ -35,3 +35,6 @@ pub mod outb; /// cbindgen:ignore pub mod resource; + +/// cbindgen:ignore +pub mod func; diff --git a/src/hyperlight_host/src/func/host_functions.rs b/src/hyperlight_host/src/func/host_functions.rs index 6764a07c2..11a36ff59 100644 --- a/src/hyperlight_host/src/func/host_functions.rs +++ b/src/hyperlight_host/src/func/host_functions.rs @@ -17,12 +17,13 @@ limitations under the License. use std::sync::{Arc, Mutex}; use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterValue, ReturnValue}; +use hyperlight_common::for_each_tuple; +use hyperlight_common::func::{Error as FuncError, Function, ResultType}; -use super::utils::for_each_tuple; -use super::{ParameterTuple, ResultType, SupportedReturnType}; +use super::{ParameterTuple, SupportedReturnType}; use crate::sandbox::UninitializedSandbox; use crate::sandbox::host_funcs::FunctionEntry; -use crate::{Result, new_error}; +use crate::{HyperlightError, Result, new_error}; /// A sandbox on which (primitive) host functions can be registered /// @@ -87,7 +88,7 @@ where // Use Arc in here instead of Box because it's useful in tests and // presumably in other places to be able to clone a HostFunction and // use it across different sandboxes. - func: Arc Result + Send + Sync + 'static>, + func: Arc + Send + Sync + 'static>, } pub(crate) struct TypeErasedHostFunction { @@ -101,7 +102,7 @@ where { /// Call the host function with the given arguments. pub fn call(&self, args: Args) -> Result { - (self.func)(args) + self.func.call(args) } } @@ -111,6 +112,28 @@ impl TypeErasedHostFunction { } } +impl From for HyperlightError { + fn from(e: FuncError) -> Self { + match e { + FuncError::ParameterValueConversionFailure(from, to) => { + HyperlightError::ParameterValueConversionFailure(from, to) + } + FuncError::ReturnValueConversionFailure(from, to) => { + HyperlightError::ReturnValueConversionFailure(from, to) + } + FuncError::UnexpectedNoOfArguments(got, expected) => { + HyperlightError::UnexpectedNoOfArguments(got, expected) + } + FuncError::UnexpectedParameterValueType(got, expected) => { + HyperlightError::UnexpectedParameterValueType(got, expected) + } + FuncError::UnexpectedReturnValueType(got, expected) => { + HyperlightError::UnexpectedReturnValueType(got, expected) + } + } + } +} + impl From> for TypeErasedHostFunction where Args: ParameterTuple, @@ -133,26 +156,22 @@ macro_rules! impl_host_function { // like we do in the case of a `FnMut`. // However, we can't implement `IntoHostFunction` for `Fn` and `FnMut` // because `FnMut` is a supertrait of `Fn`. - */ + */ impl From for HostFunction where F: FnMut($($P),*) -> R + Send + 'static, ($($P,)*): ParameterTuple, - R: ResultType, + R: ResultType, { - fn from(mut func: F) -> HostFunction { - let func = move |($($p,)*): ($($P,)*)| -> Result { - func($($p),*).into_result() - }; + fn from(func: F) -> HostFunction { let func = Mutex::new(func); - HostFunction { - func: Arc::new(move |args: ($($P,)*)| { - func.try_lock() - .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))? - (args) - }) - } + let func = move |$($p: $P,)*| { + let mut func = func.lock().map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?; + (func)($($p),*).into_result() + }; + let func = Arc::new(func); + HostFunction { func } } } }; diff --git a/src/hyperlight_host/src/func/mod.rs b/src/hyperlight_host/src/func/mod.rs index 4290d503d..c627b0de2 100644 --- a/src/hyperlight_host/src/func/mod.rs +++ b/src/hyperlight_host/src/func/mod.rs @@ -26,10 +26,6 @@ limitations under the License. /// - Dynamically dispatching a call from the guest to the appropriate /// host function pub(crate) mod host_functions; -/// Definitions and functionality for supported parameter types -pub(crate) mod param_type; -/// Definitions and functionality for supported return types -pub(crate) mod ret_type; /// Re-export for `HostFunction` trait pub use host_functions::{HostFunction, Registerable}; @@ -37,9 +33,8 @@ pub use host_functions::{HostFunction, Registerable}; pub use hyperlight_common::flatbuffer_wrappers::function_types::ParameterValue; /// Re-export for `ReturnType` enum pub use hyperlight_common::flatbuffer_wrappers::function_types::ReturnType; -/// Re-export for `ReturnType` enum +/// Re-export for `ReturnValue` enum pub use hyperlight_common::flatbuffer_wrappers::function_types::ReturnValue; -pub use param_type::{ParameterTuple, SupportedParameterType}; -pub use ret_type::{ResultType, SupportedReturnType}; - -mod utils; +pub use hyperlight_common::func::{ + ParameterTuple, ResultType, SupportedParameterType, SupportedReturnType, +}; diff --git a/src/hyperlight_host/src/sandbox/initialized_multi_use.rs b/src/hyperlight_host/src/sandbox/initialized_multi_use.rs index f1d71fad9..6cfd2273b 100644 --- a/src/hyperlight_host/src/sandbox/initialized_multi_use.rs +++ b/src/hyperlight_host/src/sandbox/initialized_multi_use.rs @@ -450,7 +450,8 @@ impl MultiUseSandbox { Output::TYPE, args.into_value(), ); - Output::from_value(ret?) + let ret = Output::from_value(ret?)?; + Ok(ret) }) } diff --git a/src/tests/rust_guests/simpleguest/Cargo.lock b/src/tests/rust_guests/simpleguest/Cargo.lock index 1b9611f7b..d621eaa85 100644 --- a/src/tests/rust_guests/simpleguest/Cargo.lock +++ b/src/tests/rust_guests/simpleguest/Cargo.lock @@ -68,6 +68,7 @@ dependencies = [ "flatbuffers", "log", "spin 0.10.0", + "thiserror", ] [[package]] @@ -267,6 +268,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tracing" version = "0.1.43" From 725f5f5c7ffbc9a63673cfab7e226c4a3406cc03 Mon Sep 17 00:00:00 2001 From: Jorge Prendes Date: Tue, 2 Sep 2025 14:24:17 +0100 Subject: [PATCH 2/9] Use function type traits to improve registration ergonomics Signed-off-by: Jorge Prendes --- src/hyperlight_guest/Cargo.toml | 2 +- src/hyperlight_guest/src/error.rs | 30 ++++- .../src/guest_function/definition.rs | 117 ++++++++++++++++++ .../src/guest_function/register.rs | 31 +++++ src/hyperlight_guest_bin/src/host_comm.rs | 8 ++ 5 files changed, 186 insertions(+), 2 deletions(-) diff --git a/src/hyperlight_guest/Cargo.toml b/src/hyperlight_guest/Cargo.toml index edbf7d950..ed6d80421 100644 --- a/src/hyperlight_guest/Cargo.toml +++ b/src/hyperlight_guest/Cargo.toml @@ -14,7 +14,7 @@ Provides only the essential building blocks for interacting with the host enviro [dependencies] anyhow = { version = "1.0.100", default-features = false } serde_json = { version = "1.0", default-features = false, features = ["alloc"] } -hyperlight-common = { workspace = true } +hyperlight-common = { workspace = true, default-features = false } hyperlight-guest-tracing = { workspace = true, default-features = false } flatbuffers = { version= "25.9.23", default-features = false } tracing = { version = "0.1.43", default-features = false, features = ["attributes"] } diff --git a/src/hyperlight_guest/src/error.rs b/src/hyperlight_guest/src/error.rs index e07407f70..463ba6a69 100644 --- a/src/hyperlight_guest/src/error.rs +++ b/src/hyperlight_guest/src/error.rs @@ -15,9 +15,10 @@ limitations under the License. */ use alloc::format; -use alloc::string::String; +use alloc::string::{String, ToString as _}; use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode; +use hyperlight_common::func::Error as FuncError; use {anyhow, serde_json}; pub type Result = core::result::Result; @@ -52,6 +53,33 @@ impl From for HyperlightGuestError { } } +impl From for HyperlightGuestError { + fn from(e: FuncError) -> Self { + match e { + FuncError::ParameterValueConversionFailure(..) => HyperlightGuestError::new( + ErrorCode::GuestFunctionParameterTypeMismatch, + e.to_string(), + ), + FuncError::ReturnValueConversionFailure(..) => HyperlightGuestError::new( + ErrorCode::GuestFunctionParameterTypeMismatch, + e.to_string(), + ), + FuncError::UnexpectedNoOfArguments(..) => HyperlightGuestError::new( + ErrorCode::GuestFunctionIncorrecNoOfParameters, + e.to_string(), + ), + FuncError::UnexpectedParameterValueType(..) => HyperlightGuestError::new( + ErrorCode::GuestFunctionParameterTypeMismatch, + e.to_string(), + ), + FuncError::UnexpectedReturnValueType(..) => HyperlightGuestError::new( + ErrorCode::GuestFunctionParameterTypeMismatch, + e.to_string(), + ), + } + } +} + /// Extension trait to add context to `Option` and `Result` types in guest code, /// converting them to `Result`. /// diff --git a/src/hyperlight_guest_bin/src/guest_function/definition.rs b/src/hyperlight_guest_bin/src/guest_function/definition.rs index a60ca2e14..5fabb8117 100644 --- a/src/hyperlight_guest_bin/src/guest_function/definition.rs +++ b/src/hyperlight_guest_bin/src/guest_function/definition.rs @@ -18,8 +18,14 @@ use alloc::format; use alloc::string::String; use alloc::vec::Vec; +use hyperlight_common::flatbuffer_wrappers::function_call::FunctionCall; use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterType, ReturnType}; use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode; +use hyperlight_common::flatbuffer_wrappers::util::get_flatbuffer_result; +use hyperlight_common::for_each_tuple; +use hyperlight_common::func::{ + Function, ParameterTuple, ResultType, ReturnValue, SupportedReturnType, +}; use hyperlight_guest::error::{HyperlightGuestError, Result}; /// The definition of a function exposed from the guest to the host @@ -35,6 +41,104 @@ pub struct GuestFunctionDefinition { pub function_pointer: usize, } +/// Trait for functions that can be converted to a `fn(&FunctionCall) -> Result>` +#[doc(hidden)] +pub trait IntoGuestFunction +where + Self: Function, + Self: Copy + 'static, + Output: SupportedReturnType, + Args: ParameterTuple, +{ + #[doc(hidden)] + const ASSERT_ZERO_SIZED: (); + + /// Convert the function into a `fn(&FunctionCall) -> Result>` + fn into_guest_function(self) -> fn(&FunctionCall) -> Result>; +} + +/// Trait for functions that can be converted to a `GuestFunctionDefinition` +pub trait AsGuestFunctionDefinition +where + Self: Function, + Self: IntoGuestFunction, + Output: SupportedReturnType, + Args: ParameterTuple, +{ + /// Get the `GuestFunctionDefinition` for this function + fn as_guest_function_definition(&self, name: impl Into) -> GuestFunctionDefinition; +} + +fn into_flatbuffer_result(value: ReturnValue) -> Vec { + match value { + ReturnValue::Void(()) => get_flatbuffer_result(()), + ReturnValue::Int(i) => get_flatbuffer_result(i), + ReturnValue::UInt(u) => get_flatbuffer_result(u), + ReturnValue::Long(l) => get_flatbuffer_result(l), + ReturnValue::ULong(ul) => get_flatbuffer_result(ul), + ReturnValue::Float(f) => get_flatbuffer_result(f), + ReturnValue::Double(d) => get_flatbuffer_result(d), + ReturnValue::Bool(b) => get_flatbuffer_result(b), + ReturnValue::String(s) => get_flatbuffer_result(s.as_str()), + ReturnValue::VecBytes(v) => get_flatbuffer_result(v.as_slice()), + } +} + +macro_rules! impl_host_function { + ([$N:expr] ($($p:ident: $P:ident),*)) => { + impl IntoGuestFunction for F + where + F: Fn($($P),*) -> R, + F: Function, + F: Copy + 'static, // Copy implies that F has no Drop impl + ($($P,)*): ParameterTuple, + R: ResultType, + { + #[doc(hidden)] + const ASSERT_ZERO_SIZED: () = const { + assert!(core::mem::size_of::() == 0) + }; + + fn into_guest_function(self) -> fn(&FunctionCall) -> Result> { + |fc: &FunctionCall| { + // SAFETY: This is safe because: + // 1. F is zero-sized (enforced by the ASSERT_ZERO_SIZED const). + // 2. F has no Drop impl (enforced by the Copy bound). + // Therefore, creating an instance of F is safe. + let this = unsafe { core::mem::zeroed::() }; + let params = fc.parameters.clone().unwrap_or_default(); + let params = <($($P,)*) as ParameterTuple>::from_value(params)?; + let result = Function::::call(&this, params)?; + Ok(into_flatbuffer_result(result.into_value())) + } + } + } + }; +} + +impl AsGuestFunctionDefinition for F +where + F: IntoGuestFunction, + Args: ParameterTuple, + Output: SupportedReturnType, +{ + fn as_guest_function_definition(&self, name: impl Into) -> GuestFunctionDefinition { + let parameter_types = Args::TYPE.to_vec(); + let return_type = Output::TYPE; + let function_pointer = self.into_guest_function(); + let function_pointer = function_pointer as usize; + + GuestFunctionDefinition { + function_name: name.into(), + parameter_types, + return_type, + function_pointer, + } + } +} + +for_each_tuple!(impl_host_function); + impl GuestFunctionDefinition { /// Create a new `GuestFunctionDefinition`. pub fn new( @@ -51,6 +155,19 @@ impl GuestFunctionDefinition { } } + /// Create a new `GuestFunctionDefinition` from a function that implements + /// `AsGuestFunctionDefinition`. + pub fn from_fn( + function_name: String, + function: impl AsGuestFunctionDefinition, + ) -> Self + where + Args: ParameterTuple, + Output: SupportedReturnType, + { + function.as_guest_function_definition(function_name) + } + /// Verify that `self` has same signature as the provided `parameter_types`. pub fn verify_parameters(&self, parameter_types: &[ParameterType]) -> Result<()> { // Verify that the function does not have more than `MAX_PARAMETERS` parameters. diff --git a/src/hyperlight_guest_bin/src/guest_function/register.rs b/src/hyperlight_guest_bin/src/guest_function/register.rs index e68ed6440..ec64bda2e 100644 --- a/src/hyperlight_guest_bin/src/guest_function/register.rs +++ b/src/hyperlight_guest_bin/src/guest_function/register.rs @@ -17,8 +17,11 @@ limitations under the License. use alloc::collections::BTreeMap; use alloc::string::String; +use hyperlight_common::func::{ParameterTuple, SupportedReturnType}; + use super::definition::GuestFunctionDefinition; use crate::REGISTERED_GUEST_FUNCTIONS; +use crate::guest_function::definition::AsGuestFunctionDefinition; /// Represents the functions that the guest exposes to the host. #[derive(Debug, Default, Clone)] @@ -47,6 +50,18 @@ impl GuestFunctionRegister { .insert(guest_function.function_name.clone(), guest_function) } + pub fn register_fn( + &mut self, + name: impl Into, + f: impl AsGuestFunctionDefinition, + ) where + Args: ParameterTuple, + Output: SupportedReturnType, + { + let gfd = f.as_guest_function_definition(name); + self.register(gfd); + } + /// Gets a `GuestFunctionDefinition` by its `name` field. pub fn get(&self, function_name: &str) -> Option<&GuestFunctionDefinition> { self.guest_functions.get(function_name) @@ -62,3 +77,19 @@ pub fn register_function(function_definition: GuestFunctionDefinition) { gfd.register(function_definition); } } + +pub fn register_fn( + name: impl Into, + f: impl AsGuestFunctionDefinition, +) where + Args: ParameterTuple, + Output: SupportedReturnType, +{ + unsafe { + // This is currently safe, because we are single threaded, but we + // should find a better way to do this, see issue #808 + #[allow(static_mut_refs)] + let gfd = &mut REGISTERED_GUEST_FUNCTIONS; + gfd.register_fn(name, f); + } +} diff --git a/src/hyperlight_guest_bin/src/host_comm.rs b/src/hyperlight_guest_bin/src/host_comm.rs index ca64a3612..b6b734f7f 100644 --- a/src/hyperlight_guest_bin/src/host_comm.rs +++ b/src/hyperlight_guest_bin/src/host_comm.rs @@ -26,6 +26,7 @@ use hyperlight_common::flatbuffer_wrappers::function_types::{ use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode; use hyperlight_common::flatbuffer_wrappers::host_function_details::HostFunctionDetails; use hyperlight_common::flatbuffer_wrappers::util::get_flatbuffer_result; +use hyperlight_common::func::{ParameterTuple, SupportedReturnType}; use hyperlight_guest::error::{HyperlightGuestError, Result}; const BUFFER_SIZE: usize = 1000; @@ -45,6 +46,13 @@ where handle.call_host_function::(function_name, parameters, return_type) } +pub fn call_host(function_name: impl AsRef, args: impl ParameterTuple) -> Result +where + T: SupportedReturnType + TryFrom, +{ + call_host_function::(function_name.as_ref(), Some(args.into_value()), T::TYPE) +} + pub fn call_host_function_without_returning_result( function_name: &str, parameters: Option>, From ba27fe124972acdef6a77d904dc3f8eb64d1f117 Mon Sep 17 00:00:00 2001 From: Jorge Prendes Date: Tue, 2 Sep 2025 23:57:22 +0100 Subject: [PATCH 3/9] Add from_result method to ResultType Signed-off-by: Jorge Prendes --- src/hyperlight_common/src/func/functions.rs | 2 +- src/hyperlight_common/src/func/ret_type.rs | 25 ++++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/hyperlight_common/src/func/functions.rs b/src/hyperlight_common/src/func/functions.rs index f1468b53a..a663ef2f6 100644 --- a/src/hyperlight_common/src/func/functions.rs +++ b/src/hyperlight_common/src/func/functions.rs @@ -28,7 +28,7 @@ macro_rules! impl_function { F: Fn($($P),*) -> R, ($($P,)*): ParameterTuple, R: ResultType, - E: From, + E: From + core::fmt::Debug, { fn call(&self, ($($p,)*): ($($P,)*)) -> Result { (self)($($p),*).into_result() diff --git a/src/hyperlight_common/src/func/ret_type.rs b/src/hyperlight_common/src/func/ret_type.rs index 0e9fd63d5..9534b51be 100644 --- a/src/hyperlight_common/src/func/ret_type.rs +++ b/src/hyperlight_common/src/func/ret_type.rs @@ -70,28 +70,47 @@ macro_rules! impl_supported_return_type { } /// A trait to handle either a [`SupportedReturnType`] or a [`Result`] -pub trait ResultType { +pub trait ResultType { /// The return type of the supported return value type ReturnType: SupportedReturnType; /// Convert the return type into a `Result` fn into_result(self) -> Result; + + /// Convert a result into this type, panicking if needed + fn from_result(res: Result) -> Self; } -impl ResultType for T { +impl ResultType for T +where + T: SupportedReturnType, + E: core::fmt::Debug, +{ type ReturnType = T; fn into_result(self) -> Result { Ok(self) } + + fn from_result(res: Result) -> Self { + res.unwrap() + } } -impl ResultType for Result { +impl ResultType for Result +where + T: SupportedReturnType, + E: core::fmt::Debug, +{ type ReturnType = T; fn into_result(self) -> Result { self } + + fn from_result(res: Result) -> Self { + res + } } for_each_return_type!(impl_supported_return_type); From d4d8225610e87152c40fea39ae2ce2cd292eae5d Mon Sep 17 00:00:00 2001 From: Jorge Prendes Date: Wed, 3 Sep 2025 00:00:23 +0100 Subject: [PATCH 4/9] Add guest macro crate Signed-off-by: Jorge Prendes --- Cargo.lock | 32 ++++ Cargo.toml | 4 +- Justfile | 1 + src/hyperlight_common/src/func/ret_type.rs | 1 + src/hyperlight_guest_bin/Cargo.toml | 5 +- src/hyperlight_guest_bin/src/lib.rs | 19 ++ src/hyperlight_guest_macro/Cargo.toml | 24 +++ src/hyperlight_guest_macro/src/lib.rs | 204 +++++++++++++++++++++ 8 files changed, 288 insertions(+), 2 deletions(-) create mode 100644 src/hyperlight_guest_macro/Cargo.toml create mode 100644 src/hyperlight_guest_macro/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 9f73bfbf0..e57cd265f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1453,12 +1453,24 @@ dependencies = [ "glob", "hyperlight-common", "hyperlight-guest", + "hyperlight-guest-macro", "hyperlight-guest-tracing", + "linkme", "log", "spin 0.10.0", "tracing", ] +[[package]] +name = "hyperlight-guest-macro" +version = "0.11.0" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "hyperlight-guest-tracing" version = "0.11.0" @@ -1904,6 +1916,26 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linkme" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1b1703c00b2a6a70738920544aa51652532cacddfec2e162d2e29eae01e665c" +dependencies = [ + "linkme-impl", +] + +[[package]] +name = "linkme-impl" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04d55ca5d5a14363da83bf3c33874b8feaa34653e760d5216d7ef9829c88001a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "linux-raw-sys" version = "0.11.0" diff --git a/Cargo.toml b/Cargo.toml index 55a2a29f8..6c158160a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ members = [ "src/hyperlight_testing", "fuzz", "src/hyperlight_guest_bin", + "src/hyperlight_guest_macro", "src/hyperlight_component_util", "src/hyperlight_component_macro", "src/trace_dump", @@ -28,7 +29,7 @@ exclude = [ [workspace.package] version = "0.11.0" edition = "2024" -rust-version = "1.88" +rust-version = "1.89" license = "Apache-2.0" homepage = "https://github.com/hyperlight-dev/hyperlight" repository = "https://github.com/hyperlight-dev/hyperlight" @@ -39,6 +40,7 @@ hyperlight-common = { path = "src/hyperlight_common", version = "0.11.0", defaul hyperlight-host = { path = "src/hyperlight_host", version = "0.11.0", default-features = false } hyperlight-guest = { path = "src/hyperlight_guest", version = "0.11.0", default-features = false } hyperlight-guest-bin = { path = "src/hyperlight_guest_bin", version = "0.11.0", default-features = false } +hyperlight-guest-macro = { path = "src/hyperlight_guest_macro", version = "0.11.0", default-features = false } hyperlight-testing = { path = "src/hyperlight_testing", default-features = false } hyperlight-guest-tracing = { path = "src/hyperlight_guest_tracing", version = "0.11.0", default-features = false } hyperlight-component-util = { path = "src/hyperlight_component_util", version = "0.11.0", default-features = false } diff --git a/Justfile b/Justfile index 1a0690548..bba11b403 100644 --- a/Justfile +++ b/Justfile @@ -261,6 +261,7 @@ clippy-exhaustive target=default-target: (witguest-wit) ./hack/clippy-package-features.sh hyperlight-host {{ target }} {{ target-triple }} ./hack/clippy-package-features.sh hyperlight-guest {{ target }} ./hack/clippy-package-features.sh hyperlight-guest-bin {{ target }} + ./hack/clippy-package-features.sh hyperlight-guest-macro {{ target }} ./hack/clippy-package-features.sh hyperlight-common {{ target }} {{ target-triple }} ./hack/clippy-package-features.sh hyperlight-testing {{ target }} {{ target-triple }} ./hack/clippy-package-features.sh hyperlight-component-macro {{ target }} {{ target-triple }} diff --git a/src/hyperlight_common/src/func/ret_type.rs b/src/hyperlight_common/src/func/ret_type.rs index 9534b51be..b23efa1ed 100644 --- a/src/hyperlight_common/src/func/ret_type.rs +++ b/src/hyperlight_common/src/func/ret_type.rs @@ -93,6 +93,7 @@ where } fn from_result(res: Result) -> Self { + #![allow(clippy::unwrap_used)] res.unwrap() } } diff --git a/src/hyperlight_guest_bin/Cargo.toml b/src/hyperlight_guest_bin/Cargo.toml index 0f11e3018..fe595eedd 100644 --- a/src/hyperlight_guest_bin/Cargo.toml +++ b/src/hyperlight_guest_bin/Cargo.toml @@ -14,18 +14,21 @@ and third-party code used by our C-API needed to build a native hyperlight-guest """ [features] -default = ["libc", "printf"] +default = ["libc", "printf", "macros"] libc = [] # compile musl libc printf = [ "libc" ] # compile printf trace_guest = ["hyperlight-common/trace_guest", "hyperlight-guest/trace_guest", "hyperlight-guest-tracing/trace"] mem_profile = ["hyperlight-common/mem_profile"] +macros = ["dep:hyperlight-guest-macro", "dep:linkme"] [dependencies] hyperlight-guest = { workspace = true, default-features = false } hyperlight-common = { workspace = true, default-features = false } hyperlight-guest-tracing = { workspace = true, default-features = false } +hyperlight-guest-macro = { workspace = true, default-features = false, optional = true } buddy_system_allocator = "0.11.0" log = { version = "0.4", default-features = false } +linkme = { version = "0.3.33", optional = true } spin = "0.10.0" flatbuffers = { version = "25.2.10", default-features = false } tracing = { version = "0.1.43", default-features = false, features = ["attributes"] } diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index 240e29ac9..ffeb66b19 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -239,9 +239,28 @@ pub extern "C" fn entrypoint(peb_address: u64, seed: u64, ops: u64, max_log_leve hyperlight_guest_tracing::init_guest_tracing(guest_start_tsc); } + #[cfg(feature = "macros")] + for registration in __private::GUEST_FUNCTION_INIT { + registration(); + } + hyperlight_main(); } }); halt(); } + +#[cfg(feature = "macros")] +#[doc(hidden)] +pub mod __private { + pub use hyperlight_common::func::ResultType; + pub use hyperlight_guest::error::HyperlightGuestError; + pub use linkme; + + #[linkme::distributed_slice] + pub static GUEST_FUNCTION_INIT: [fn()]; +} + +#[cfg(feature = "macros")] +pub use hyperlight_guest_macro::{guest_function, host_function}; diff --git a/src/hyperlight_guest_macro/Cargo.toml b/src/hyperlight_guest_macro/Cargo.toml new file mode 100644 index 000000000..d3a7b5cfc --- /dev/null +++ b/src/hyperlight_guest_macro/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "hyperlight-guest-macro" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +readme.workspace = true +description = """ +Macros for registering guest and host functions on hyperlight guests binaries. +""" + +[dependencies] +syn = { version = "2", features = ["full"] } +quote = "1" +proc-macro2 = "1.0" +proc-macro-crate = "3.3.0" + +[lib] +proc-macro = true + +[lints] +workspace = true diff --git a/src/hyperlight_guest_macro/src/lib.rs b/src/hyperlight_guest_macro/src/lib.rs new file mode 100644 index 000000000..ad5de0744 --- /dev/null +++ b/src/hyperlight_guest_macro/src/lib.rs @@ -0,0 +1,204 @@ +/* +Copyright 2025 The Hyperlight Authors. + +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. +*/ + +use proc_macro::TokenStream; +use proc_macro_crate::{FoundCrate, crate_name}; +use quote::quote; +use syn::parse::{Error, Parse, ParseStream, Result}; +use syn::spanned::Spanned as _; +use syn::{ForeignItemFn, ItemFn, LitStr, Pat, parse_macro_input}; + +enum NameArg { + None, + Name(LitStr), +} + +impl Parse for NameArg { + fn parse(input: ParseStream) -> Result { + if input.is_empty() { + return Ok(NameArg::None); + } + let name: LitStr = input.parse()?; + if !input.is_empty() { + return Err(Error::new(input.span(), "expected a single identifier")); + } + Ok(NameArg::Name(name)) + } +} + +#[proc_macro_attribute] +pub fn guest_function(attr: TokenStream, item: TokenStream) -> TokenStream { + let crate_name = + crate_name("hyperlight-guest-bin").expect("hyperlight-guest-bin must be a dependency"); + let crate_name = match crate_name { + FoundCrate::Itself => quote! {crate}, + FoundCrate::Name(name) => { + let ident = syn::Ident::new(&name, proc_macro2::Span::call_site()); + quote! {::#ident} + } + }; + + let fn_declaration = parse_macro_input!(item as ItemFn); + + let ident = fn_declaration.sig.ident.clone(); + + let exported_name = match parse_macro_input!(attr as NameArg) { + NameArg::None => quote! { stringify!(#ident) }, + NameArg::Name(name) => quote! { #name }, + }; + + if let Some(syn::FnArg::Receiver(arg)) = fn_declaration.sig.inputs.first() { + return Error::new( + arg.span(), + "Receiver (self) argument is not allowed in guest functions", + ) + .to_compile_error() + .into(); + } + + if fn_declaration.sig.asyncness.is_some() { + return Error::new( + fn_declaration.sig.asyncness.span(), + "Async functions are not allowed in guest functions", + ) + .to_compile_error() + .into(); + } + + let output = quote! { + #fn_declaration + + const _: () = { + #[#crate_name::__private::linkme::distributed_slice(#crate_name::__private::GUEST_FUNCTION_INIT)] + #[linkme(crate = #crate_name::__private::linkme)] + static REGISTRATION: fn() = || { + hyperlight_guest_bin::guest_function::register::register_fn(#exported_name, #ident); + }; + }; + }; + + output.into() +} + +#[proc_macro_attribute] +pub fn host_function(attr: TokenStream, item: TokenStream) -> TokenStream { + let crate_name = + crate_name("hyperlight-guest-bin").expect("hyperlight-guest-bin must be a dependency"); + let crate_name = match crate_name { + FoundCrate::Itself => quote! {crate}, + FoundCrate::Name(name) => { + let ident = syn::Ident::new(&name, proc_macro2::Span::call_site()); + quote! {::#ident} + } + }; + + let fn_declaration = parse_macro_input!(item as ForeignItemFn); + + let ForeignItemFn { + attrs, + vis, + sig, + semi_token: _, + } = fn_declaration; + + let ident = sig.ident.clone(); + + let exported_name = match parse_macro_input!(attr as NameArg) { + NameArg::None => quote! { stringify!(#ident) }, + NameArg::Name(name) => quote! { #name }, + }; + + let mut args = vec![]; + for arg in sig.inputs.iter() { + match arg { + syn::FnArg::Receiver(_) => { + return Error::new( + arg.span(), + "Receiver (self) argument is not allowed in guest functions", + ) + .to_compile_error() + .into(); + } + syn::FnArg::Typed(arg) => { + let Pat::Ident(pat) = *arg.pat.clone() else { + return Error::new( + arg.span(), + "Only named arguments are allowed in host functions", + ) + .to_compile_error() + .into(); + }; + + if !pat.attrs.is_empty() { + return Error::new( + arg.span(), + "Attributes are not allowed on host function arguments", + ) + .to_compile_error() + .into(); + } + + if pat.by_ref.is_some() { + return Error::new( + arg.span(), + "By-ref arguments are not allowed in host functions", + ) + .to_compile_error() + .into(); + } + + if pat.mutability.is_some() { + return Error::new( + arg.span(), + "Mutable arguments are not allowed in host functions", + ) + .to_compile_error() + .into(); + } + + if pat.subpat.is_some() { + return Error::new( + arg.span(), + "Sub-patterns are not allowed in host functions", + ) + .to_compile_error() + .into(); + } + + let ident = pat.ident.clone(); + + args.push(quote! { #ident }); + } + } + } + + let ret: proc_macro2::TokenStream = match &sig.output { + syn::ReturnType::Default => quote! { quote! { () } }, + syn::ReturnType::Type(_, ty) => { + quote! { #ty } + } + }; + + let output = quote! { + #(#attrs)* #vis #sig { + use #crate_name::__private::{ResultType, HyperlightGuestError}; + use #crate_name::host_comm::call_host; + <#ret as ResultType>::from_result(call_host(#exported_name, (#(#args,)*))) + } + }; + + output.into() +} From aee43a78f61ac76e4b734fbd6a8df25a60c81c2c Mon Sep 17 00:00:00 2001 From: Jorge Prendes Date: Tue, 2 Dec 2025 14:03:31 +0000 Subject: [PATCH 5/9] use guest_function and host_function macros in simpleguest Signed-off-by: Jorge Prendes --- src/tests/rust_guests/simpleguest/src/main.rs | 1704 ++++------------- 1 file changed, 336 insertions(+), 1368 deletions(-) diff --git a/src/tests/rust_guests/simpleguest/src/main.rs b/src/tests/rust_guests/simpleguest/src/main.rs index 9e0a2a08b..7c6534901 100644 --- a/src/tests/rust_guests/simpleguest/src/main.rs +++ b/src/tests/rust_guests/simpleguest/src/main.rs @@ -26,7 +26,7 @@ const MAX_BUFFER_SIZE: usize = 1024; extern crate alloc; use alloc::boxed::Box; -use alloc::string::ToString; +use alloc::string::{String, ToString}; use alloc::vec::Vec; use alloc::{format, vec}; use core::alloc::Layout; @@ -51,7 +51,7 @@ use hyperlight_guest_bin::host_comm::{ print_output_with_host_print, read_n_bytes_from_user_memory, }; use hyperlight_guest_bin::memory::malloc; -use hyperlight_guest_bin::{MIN_STACK_ADDRESS, guest_logger}; +use hyperlight_guest_bin::{MIN_STACK_ADDRESS, guest_function, guest_logger, host_function}; use log::{LevelFilter, error}; use tracing::{Span, instrument}; @@ -59,404 +59,146 @@ extern crate hyperlight_guest; static mut BIGARRAY: [i32; 1024 * 1024] = [0; 1024 * 1024]; -fn set_static(_: &FunctionCall) -> Result> { - unsafe { - #[allow(static_mut_refs)] - for val in BIGARRAY.iter_mut() { - *val = 1; - } - #[allow(static_mut_refs)] - Ok(get_flatbuffer_result(BIGARRAY.len() as i32)) +#[guest_function("SetStatic")] +fn set_static() -> i32 { + #[allow(static_mut_refs)] + let bigarray = unsafe { &mut BIGARRAY }; + for val in bigarray.iter_mut() { + *val = 1; } + bigarray.len() as i32 } -fn echo_double(function_call: &FunctionCall) -> Result> { - if let ParameterValue::Double(value) = function_call.parameters.clone().unwrap()[0].clone() { - Ok(get_flatbuffer_result(value)) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to echo_double".to_string(), - )) - } +#[guest_function("EchoDouble")] +fn echo_double(value: f64) -> f64 { + value } -fn echo_float(function_call: &FunctionCall) -> Result> { - if let ParameterValue::Float(value) = function_call.parameters.clone().unwrap()[0].clone() { - Ok(get_flatbuffer_result(value)) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to echo_float".to_string(), - )) - } +#[guest_function("EchoFloat")] +fn echo_float(value: f32) -> f32 { + value } -#[instrument(skip_all, parent = Span::current(), level= "Trace")] -fn print_output(message: &str) -> Result> { - let res = call_host_function::( - "HostPrint", - Some(Vec::from(&[ParameterValue::String(message.to_string())])), - ReturnType::Int, - )?; - - Ok(get_flatbuffer_result(res)) -} +#[host_function("HostPrint")] +fn host_print(msg: String) -> i32; #[instrument(skip_all, parent = Span::current(), level= "Trace")] -fn simple_print_output(function_call: &FunctionCall) -> Result> { - if let ParameterValue::String(message) = function_call.parameters.clone().unwrap()[0].clone() { - print_output(&message) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to simple_print_output".to_string(), - )) - } +#[guest_function("PrintOutput")] +fn print_output(msg: String) -> i32 { + host_print(msg) } -fn set_byte_array_to_zero(function_call: &FunctionCall) -> Result> { - if let ParameterValue::VecBytes(mut vec) = function_call.parameters.clone().unwrap()[0].clone() - { - vec.fill(0); - Ok(get_flatbuffer_result(&*vec)) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to set_byte_array_to_zero".to_string(), - )) - } +#[guest_function("PrintUsingPrintf")] +fn print_using_printf(msg: String) -> i32 { + print_output(msg) } -fn print_two_args(function_call: &FunctionCall) -> Result> { - if let (ParameterValue::String(arg1), ParameterValue::Int(arg2)) = ( - function_call.parameters.clone().unwrap()[0].clone(), - function_call.parameters.clone().unwrap()[1].clone(), - ) { - let message = format!("Message: arg1:{} arg2:{}.", arg1, arg2); - print_output(&message) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to print_two_args".to_string(), - )) - } +#[guest_function("SetByteArrayToZero")] +fn set_byte_array_to_zero(mut vec: Vec) -> Vec { + vec.fill(0); + vec } -fn print_three_args(function_call: &FunctionCall) -> Result> { - if let (ParameterValue::String(arg1), ParameterValue::Int(arg2), ParameterValue::Long(arg3)) = ( - function_call.parameters.clone().unwrap()[0].clone(), - function_call.parameters.clone().unwrap()[1].clone(), - function_call.parameters.clone().unwrap()[2].clone(), - ) { - let message = format!("Message: arg1:{} arg2:{} arg3:{}.", arg1, arg2, arg3); - print_output(&message) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to print_three_args".to_string(), - )) - } +#[guest_function("PrintTwoArgs")] +fn print_two_args(arg1: String, arg2: i32) -> i32 { + let message = format!("Message: arg1:{arg1} arg2:{arg2}."); + host_print(message) } -fn print_four_args(function_call: &FunctionCall) -> Result> { - if let ( - ParameterValue::String(arg1), - ParameterValue::Int(arg2), - ParameterValue::Long(arg3), - ParameterValue::String(arg4), - ) = ( - function_call.parameters.clone().unwrap()[0].clone(), - function_call.parameters.clone().unwrap()[1].clone(), - function_call.parameters.clone().unwrap()[2].clone(), - function_call.parameters.clone().unwrap()[3].clone(), - ) { - let message = format!( - "Message: arg1:{} arg2:{} arg3:{} arg4:{}.", - arg1, arg2, arg3, arg4 - ); - print_output(&message) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to print_four_args".to_string(), - )) - } +#[guest_function("PrintThreeArgs")] +fn print_three_args(arg1: String, arg2: i32, arg3: i64) -> i32 { + let message = format!("Message: arg1:{arg1} arg2:{arg2} arg3:{arg3}."); + host_print(message) } -fn print_five_args(function_call: &FunctionCall) -> Result> { - if let ( - ParameterValue::String(arg1), - ParameterValue::Int(arg2), - ParameterValue::Long(arg3), - ParameterValue::String(arg4), - ParameterValue::String(arg5), - ) = ( - function_call.parameters.clone().unwrap()[0].clone(), - function_call.parameters.clone().unwrap()[1].clone(), - function_call.parameters.clone().unwrap()[2].clone(), - function_call.parameters.clone().unwrap()[3].clone(), - function_call.parameters.clone().unwrap()[4].clone(), - ) { - let message = format!( - "Message: arg1:{} arg2:{} arg3:{} arg4:{} arg5:{}.", - arg1, arg2, arg3, arg4, arg5 - ); - print_output(&message) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to print_five_args".to_string(), - )) - } +#[guest_function("PrintFourArgs")] +fn print_four_args(arg1: String, arg2: i32, arg3: i64, arg4: String) -> i32 { + let message = format!("Message: arg1:{arg1} arg2:{arg2} arg3:{arg3} arg4:{arg4}."); + host_print(message) } -fn print_six_args(function_call: &FunctionCall) -> Result> { - if let ( - ParameterValue::String(arg1), - ParameterValue::Int(arg2), - ParameterValue::Long(arg3), - ParameterValue::String(arg4), - ParameterValue::String(arg5), - ParameterValue::Bool(arg6), - ) = ( - function_call.parameters.clone().unwrap()[0].clone(), - function_call.parameters.clone().unwrap()[1].clone(), - function_call.parameters.clone().unwrap()[2].clone(), - function_call.parameters.clone().unwrap()[3].clone(), - function_call.parameters.clone().unwrap()[4].clone(), - function_call.parameters.clone().unwrap()[5].clone(), - ) { - let message = format!( - "Message: arg1:{} arg2:{} arg3:{} arg4:{} arg5:{} arg6:{}.", - arg1, arg2, arg3, arg4, arg5, arg6 - ); - print_output(&message) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to print_six_args".to_string(), - )) - } +#[guest_function("PrintFiveArgs")] +fn print_five_args(arg1: String, arg2: i32, arg3: i64, arg4: String, arg5: String) -> i32 { + let message = format!("Message: arg1:{arg1} arg2:{arg2} arg3:{arg3} arg4:{arg4} arg5:{arg5}."); + host_print(message) } -fn print_seven_args(function_call: &FunctionCall) -> Result> { - if let ( - ParameterValue::String(arg1), - ParameterValue::Int(arg2), - ParameterValue::Long(arg3), - ParameterValue::String(arg4), - ParameterValue::String(arg5), - ParameterValue::Bool(arg6), - ParameterValue::Bool(arg7), - ) = ( - function_call.parameters.clone().unwrap()[0].clone(), - function_call.parameters.clone().unwrap()[1].clone(), - function_call.parameters.clone().unwrap()[2].clone(), - function_call.parameters.clone().unwrap()[3].clone(), - function_call.parameters.clone().unwrap()[4].clone(), - function_call.parameters.clone().unwrap()[5].clone(), - function_call.parameters.clone().unwrap()[6].clone(), - ) { - let message = format!( - "Message: arg1:{} arg2:{} arg3:{} arg4:{} arg5:{} arg6:{} arg7:{}.", - arg1, arg2, arg3, arg4, arg5, arg6, arg7 - ); - print_output(&message) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to print_seven_args".to_string(), - )) - } +#[rustfmt::skip] +#[guest_function("PrintSixArgs")] +fn print_six_args(arg1: String, arg2: i32, arg3: i64, arg4: String, arg5: String, arg6: bool) -> i32 { + let message = format!("Message: arg1:{arg1} arg2:{arg2} arg3:{arg3} arg4:{arg4} arg5:{arg5} arg6:{arg6}."); + host_print(message) } -fn print_eight_args(function_call: &FunctionCall) -> Result> { - if let ( - ParameterValue::String(arg1), - ParameterValue::Int(arg2), - ParameterValue::Long(arg3), - ParameterValue::String(arg4), - ParameterValue::String(arg5), - ParameterValue::Bool(arg6), - ParameterValue::Bool(arg7), - ParameterValue::UInt(arg8), - ) = ( - function_call.parameters.clone().unwrap()[0].clone(), - function_call.parameters.clone().unwrap()[1].clone(), - function_call.parameters.clone().unwrap()[2].clone(), - function_call.parameters.clone().unwrap()[3].clone(), - function_call.parameters.clone().unwrap()[4].clone(), - function_call.parameters.clone().unwrap()[5].clone(), - function_call.parameters.clone().unwrap()[6].clone(), - function_call.parameters.clone().unwrap()[7].clone(), - ) { - let message = format!( - "Message: arg1:{} arg2:{} arg3:{} arg4:{} arg5:{} arg6:{} arg7:{} arg8:{}.", - arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 - ); - print_output(&message) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to print_eight_args".to_string(), - )) - } +#[rustfmt::skip] +#[guest_function("PrintSevenArgs")] +fn print_seven_args(arg1: String, arg2: i32, arg3: i64, arg4: String, arg5: String, arg6: bool, arg7: bool) -> i32 { + let message = format!("Message: arg1:{arg1} arg2:{arg2} arg3:{arg3} arg4:{arg4} arg5:{arg5} arg6:{arg6} arg7:{arg7}."); + host_print(message) } -fn print_nine_args(function_call: &FunctionCall) -> Result> { - if let ( - ParameterValue::String(arg1), - ParameterValue::Int(arg2), - ParameterValue::Long(arg3), - ParameterValue::String(arg4), - ParameterValue::String(arg5), - ParameterValue::Bool(arg6), - ParameterValue::Bool(arg7), - ParameterValue::UInt(arg8), - ParameterValue::ULong(arg9), - ) = ( - function_call.parameters.clone().unwrap()[0].clone(), - function_call.parameters.clone().unwrap()[1].clone(), - function_call.parameters.clone().unwrap()[2].clone(), - function_call.parameters.clone().unwrap()[3].clone(), - function_call.parameters.clone().unwrap()[4].clone(), - function_call.parameters.clone().unwrap()[5].clone(), - function_call.parameters.clone().unwrap()[6].clone(), - function_call.parameters.clone().unwrap()[7].clone(), - function_call.parameters.clone().unwrap()[8].clone(), - ) { - let message = format!( - "Message: arg1:{} arg2:{} arg3:{} arg4:{} arg5:{} arg6:{} arg7:{} arg8:{} arg9:{}.", - arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 - ); - print_output(&message) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to print_nine_args".to_string(), - )) - } +#[rustfmt::skip] +#[allow(clippy::too_many_arguments)] +#[guest_function("PrintEightArgs")] +fn print_eight_args(arg1: String, arg2: i32, arg3: i64, arg4: String, arg5: String, arg6: bool, arg7: bool, arg8: u32) -> i32 { + let message = format!("Message: arg1:{arg1} arg2:{arg2} arg3:{arg3} arg4:{arg4} arg5:{arg5} arg6:{arg6} arg7:{arg7} arg8:{arg8}."); + host_print(message) } -fn print_ten_args(function_call: &FunctionCall) -> Result> { - if let ( - ParameterValue::String(arg1), - ParameterValue::Int(arg2), - ParameterValue::Long(arg3), - ParameterValue::String(arg4), - ParameterValue::String(arg5), - ParameterValue::Bool(arg6), - ParameterValue::Bool(arg7), - ParameterValue::UInt(arg8), - ParameterValue::ULong(arg9), - ParameterValue::Int(arg10), - ) = ( - function_call.parameters.clone().unwrap()[0].clone(), - function_call.parameters.clone().unwrap()[1].clone(), - function_call.parameters.clone().unwrap()[2].clone(), - function_call.parameters.clone().unwrap()[3].clone(), - function_call.parameters.clone().unwrap()[4].clone(), - function_call.parameters.clone().unwrap()[5].clone(), - function_call.parameters.clone().unwrap()[6].clone(), - function_call.parameters.clone().unwrap()[7].clone(), - function_call.parameters.clone().unwrap()[8].clone(), - function_call.parameters.clone().unwrap()[9].clone(), - ) { - let message = format!( - "Message: arg1:{} arg2:{} arg3:{} arg4:{} arg5:{} arg6:{} arg7:{} arg8:{} arg9:{} arg10:{}.", - arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 - ); - print_output(&message) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to print_ten_args".to_string(), - )) - } +#[rustfmt::skip] +#[allow(clippy::too_many_arguments)] +#[guest_function("PrintNineArgs")] +fn print_nine_args(arg1: String, arg2: i32, arg3: i64, arg4: String, arg5: String, arg6: bool, arg7: bool, arg8: u32, arg9: u64) -> i32 { + let message = format!("Message: arg1:{arg1} arg2:{arg2} arg3:{arg3} arg4:{arg4} arg5:{arg5} arg6:{arg6} arg7:{arg7} arg8:{arg8} arg9:{arg9}."); + host_print(message) } -fn print_eleven_args(function_call: &FunctionCall) -> Result> { - if let ( - ParameterValue::String(arg1), - ParameterValue::Int(arg2), - ParameterValue::Long(arg3), - ParameterValue::String(arg4), - ParameterValue::String(arg5), - ParameterValue::Bool(arg6), - ParameterValue::Bool(arg7), - ParameterValue::UInt(arg8), - ParameterValue::ULong(arg9), - ParameterValue::Int(arg10), - ParameterValue::Float(arg11), - ) = ( - function_call.parameters.clone().unwrap()[0].clone(), - function_call.parameters.clone().unwrap()[1].clone(), - function_call.parameters.clone().unwrap()[2].clone(), - function_call.parameters.clone().unwrap()[3].clone(), - function_call.parameters.clone().unwrap()[4].clone(), - function_call.parameters.clone().unwrap()[5].clone(), - function_call.parameters.clone().unwrap()[6].clone(), - function_call.parameters.clone().unwrap()[7].clone(), - function_call.parameters.clone().unwrap()[8].clone(), - function_call.parameters.clone().unwrap()[9].clone(), - function_call.parameters.clone().unwrap()[10].clone(), - ) { - let message = format!( - "Message: arg1:{} arg2:{} arg3:{} arg4:{} arg5:{} arg6:{} arg7:{} arg8:{} arg9:{} arg10:{} arg11:{:.3}.", - arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11 - ); - print_output(&message) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to print_eleven_args".to_string(), - )) - } +#[rustfmt::skip] +#[allow(clippy::too_many_arguments)] +#[guest_function("PrintTenArgs")] +fn print_ten_args(arg1: String, arg2: i32, arg3: i64, arg4: String, arg5: String, arg6: bool, arg7: bool, arg8: u32, arg9: u64, arg10: i32) -> i32 { + let message = format!("Message: arg1:{arg1} arg2:{arg2} arg3:{arg3} arg4:{arg4} arg5:{arg5} arg6:{arg6} arg7:{arg7} arg8:{arg8} arg9:{arg9} arg10:{arg10}."); + host_print(message) } -fn buffer_overrun(function_call: &FunctionCall) -> Result> { - if let ParameterValue::String(value) = function_call.parameters.clone().unwrap()[0].clone() { - let c_str = value.as_str(); +#[rustfmt::skip] +#[allow(clippy::too_many_arguments)] +#[guest_function("PrintElevenArgs")] +fn print_eleven_args(arg1: String, arg2: i32, arg3: i64, arg4: String, arg5: String, arg6: bool, arg7: bool, arg8: u32, arg9: u64, arg10: i32, arg11: f32) -> i32 { + let message = format!("Message: arg1:{arg1} arg2:{arg2} arg3:{arg3} arg4:{arg4} arg5:{arg5} arg6:{arg6} arg7:{arg7} arg8:{arg8} arg9:{arg9} arg10:{arg10} arg11:{arg11:.3}."); + host_print(message) +} - let mut buffer: [u8; 17] = [0; 17]; - let length = c_str.len(); +#[guest_function("BufferOverrun")] +fn buffer_overrun(value: String) -> i32 { + let c_str = value.as_str(); - let copy_length = length.min(buffer.len()); - buffer[..copy_length].copy_from_slice(&c_str.as_bytes()[..copy_length]); + let mut buffer: [u8; 17] = [0; 17]; + let length = c_str.len(); - let result = (17i32).saturating_sub(length as i32); + let copy_length = length.min(buffer.len()); + buffer[..copy_length].copy_from_slice(&c_str.as_bytes()[..copy_length]); - Ok(get_flatbuffer_result(result)) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to buffer_overrun".to_string(), - )) - } + (17i32).saturating_sub(length as i32) } +#[guest_function("InfiniteRecursion")] #[allow(unconditional_recursion)] -fn infinite_recursion(_a: &FunctionCall) -> Result> { +fn infinite_recursion() { // blackbox is needed so something //is written to the stack in release mode, //to trigger guard page violation let param = black_box(5); black_box(param); - infinite_recursion(_a) + infinite_recursion() } -fn stack_overflow(function_call: &FunctionCall) -> Result> { - if let ParameterValue::Int(i) = function_call.parameters.clone().unwrap()[0].clone() { - loop_stack_overflow(i); - Ok(get_flatbuffer_result(i)) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to stack_overflow".to_string(), - )) - } +#[guest_function("StackOverflow")] +fn stack_overflow(i: i32) -> i32 { + loop_stack_overflow(i); + i } + // This function will allocate i * (8KiB + 1B) on the stack fn loop_stack_overflow(i: i32) { if i > 0 { @@ -465,36 +207,33 @@ fn loop_stack_overflow(i: i32) { } } -fn large_var(_: &FunctionCall) -> Result> { +#[guest_function("LargeVar")] +fn large_var() -> i32 { let _buffer = black_box([0u8; (DEFAULT_GUEST_STACK_SIZE + 1) as usize]); - Ok(get_flatbuffer_result(DEFAULT_GUEST_STACK_SIZE + 1)) + DEFAULT_GUEST_STACK_SIZE + 1 } -fn small_var(_: &FunctionCall) -> Result> { +#[guest_function("SmallVar")] +fn small_var() -> i32 { let _buffer = black_box([0u8; 1024]); - Ok(get_flatbuffer_result(1024)) + 1024 } -fn call_malloc(function_call: &FunctionCall) -> Result> { - if let ParameterValue::Int(size) = function_call.parameters.clone().unwrap()[0].clone() { - // will panic if OOM, and we need blackbox to avoid optimizing away this test - let buffer = Vec::::with_capacity(size as usize); - black_box(buffer); - Ok(get_flatbuffer_result(size)) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to call_malloc".to_string(), - )) - } +#[guest_function("CallMalloc")] +fn call_malloc(size: i32) -> i32 { + // will panic if OOM, and we need blackbox to avoid optimizing away this test + let buffer = Vec::::with_capacity(size as usize); + black_box(buffer); + size } -unsafe fn exhaust_heap(_: &FunctionCall) -> ! { +#[guest_function("ExhaustHeap")] +fn exhaust_heap() { let layout: Layout = Layout::new::(); - let mut ptr = alloc::alloc::alloc_zeroed(layout); + let mut ptr = unsafe { alloc::alloc::alloc_zeroed(layout) }; while !ptr.is_null() { black_box(ptr); - ptr = alloc::alloc::alloc_zeroed(layout); + ptr = unsafe { alloc::alloc::alloc_zeroed(layout) }; } // after alloc::alloc_zeroed failure (null return when called in loop above) @@ -505,71 +244,43 @@ unsafe fn exhaust_heap(_: &FunctionCall) -> ! { panic!("function should have panicked before due to OOM") } -fn malloc_and_free(function_call: &FunctionCall) -> Result> { - if let ParameterValue::Int(size) = function_call.parameters.clone().unwrap()[0].clone() { - let alloc_length = if size < DEFAULT_GUEST_STACK_SIZE { - size - } else { - size.min(MAX_BUFFER_SIZE as i32) - }; - let allocated_buffer = vec![0; alloc_length as usize]; - drop(allocated_buffer); - - Ok(get_flatbuffer_result(size)) +#[guest_function("MallocAndFree")] +fn malloc_and_free(size: i32) -> i32 { + let alloc_length = if size < DEFAULT_GUEST_STACK_SIZE { + size } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to malloc_and_free".to_string(), - )) - } + size.min(MAX_BUFFER_SIZE as i32) + }; + let allocated_buffer = vec![0; alloc_length as usize]; + drop(allocated_buffer); + + size } -fn echo(function_call: &FunctionCall) -> Result> { - if let ParameterValue::String(value) = function_call.parameters.clone().unwrap()[0].clone() { - Ok(get_flatbuffer_result(&*value)) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to echo".to_string(), - )) - } +#[guest_function("Echo")] +fn echo(value: String) -> String { + value } -fn get_size_prefixed_buffer(function_call: &FunctionCall) -> Result> { - if let ParameterValue::VecBytes(data) = function_call.parameters.clone().unwrap()[0].clone() { - Ok(get_flatbuffer_result(&*data)) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to get_size_prefixed_buffer".to_string(), - )) - } +#[guest_function("GetSizePrefixedBuffer")] +fn get_size_prefixed_buffer(data: Vec) -> Vec { + data } #[expect( clippy::empty_loop, reason = "This function is used to keep the CPU busy" )] -fn spin(_: &FunctionCall) -> Result> { +#[guest_function("Spin")] +fn spin() { loop { // Keep the CPU 100% busy forever } - - #[allow(unreachable_code)] - Ok(get_flatbuffer_result(())) } /// Spins the CPU for approximately the specified number of milliseconds -fn spin_for_ms(fc: &FunctionCall) -> Result> { - let milliseconds = if let ParameterValue::UInt(ms) = fc.parameters.clone().unwrap()[0].clone() { - ms - } else { - return Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Expected UInt parameter".to_string(), - )); - }; - +#[guest_function("SpinForMs")] +fn spin_for_ms(milliseconds: u32) -> u64 { // Simple busy-wait loop - not precise but good enough for testing // Different iteration counts for debug vs release mode to ensure reasonable CPU usage #[cfg(debug_assertions)] @@ -593,69 +304,61 @@ fn spin_for_ms(fc: &FunctionCall) -> Result> { } // Calculate the actual number of milliseconds spun for, based on the counter and iterations per ms - let ms_spun = counter / iterations_per_ms as u64; - Ok(get_flatbuffer_result(ms_spun)) + counter / iterations_per_ms as u64 } -fn test_abort(function_call: &FunctionCall) -> Result> { - if let ParameterValue::Int(code) = function_call.parameters.clone().unwrap()[0].clone() { - abort_with_code(&[code as u8]); - } - Ok(get_flatbuffer_result(())) +#[guest_function("GuestAbortWithCode")] +fn test_abort(code: i32) { + abort_with_code(&[code as u8]); } -fn test_abort_with_code_and_message(function_call: &FunctionCall) -> Result> { - if let (ParameterValue::Int(code), ParameterValue::String(message)) = ( - function_call.parameters.clone().unwrap()[0].clone(), - function_call.parameters.clone().unwrap()[1].clone(), - ) { - unsafe { - abort_with_code_and_message(&[code as u8], message.as_ptr() as *const c_char); - } +#[guest_function("GuestAbortWithMessage")] +fn test_abort_with_code_and_message(code: i32, mut message: String) { + message.push('\0'); // null-terminate the string + unsafe { + abort_with_code_and_message(&[code as u8], message.as_ptr() as *const c_char); } - Ok(get_flatbuffer_result(())) } -fn test_guest_panic(function_call: &FunctionCall) -> Result> { - if let ParameterValue::String(message) = function_call.parameters.clone().unwrap()[0].clone() { - panic!("{}", message); - } - Ok(get_flatbuffer_result(())) -} - -fn test_write_raw_ptr(function_call: &FunctionCall) -> Result> { - if let ParameterValue::Long(offset) = function_call.parameters.clone().unwrap()[0].clone() { - let min_stack_addr = unsafe { MIN_STACK_ADDRESS }; - let page_guard_start = min_stack_addr - PAGE_SIZE; - let addr = { - let abs = u64::try_from(offset.abs()) - .map_err(|_| error!("Invalid offset")) - .unwrap(); - if offset.is_negative() { - page_guard_start - abs - } else { - page_guard_start + abs - } - }; - unsafe { - // print_output(format!("writing to {:#x}\n", addr).as_str()).unwrap(); - write_volatile(addr as *mut u8, 0u8); +#[guest_function("guest_panic")] +fn test_guest_panic(message: String) { + panic!("{}", message); +} + +#[guest_function] +fn test_write_raw_ptr(offset: i64) -> String { + let min_stack_addr = unsafe { MIN_STACK_ADDRESS }; + let page_guard_start = min_stack_addr - PAGE_SIZE; + let addr = { + let abs = u64::try_from(offset.abs()) + .map_err(|_| error!("Invalid offset")) + .unwrap(); + if offset.is_negative() { + page_guard_start - abs + } else { + page_guard_start + abs } - return Ok(get_flatbuffer_result("success")); + }; + unsafe { + // host_print(format!("writing to {:#x}\n", addr).as_str()); + write_volatile(addr as *mut u8, 0u8); } - Ok(get_flatbuffer_result("fail")) + String::from("success") } -fn execute_on_stack(_function_call: &FunctionCall) -> Result> { +#[guest_function("ExecuteOnStack")] +fn execute_on_stack() -> String { unsafe { let mut noop: u8 = 0x90; let stack_fn: fn() = core::mem::transmute(&mut noop as *mut u8); stack_fn(); }; - Ok(get_flatbuffer_result("fail")) + // will only reach this point if stack is executable + String::from("fail") } -fn execute_on_heap(_function_call: &FunctionCall) -> Result> { +#[guest_function("ExecuteOnHeap")] +fn execute_on_heap() -> String { unsafe { // NO-OP followed by RET let heap_memory = Box::new([0x90u8, 0xC3]); @@ -664,355 +367,170 @@ fn execute_on_heap(_function_call: &FunctionCall) -> Result> { black_box(heap_fn); // avoid optimization when running in release mode } // will only reach this point if heap is executable - Ok(get_flatbuffer_result("fail")) + String::from("fail") } -fn test_rust_malloc(function_call: &FunctionCall) -> Result> { - if let ParameterValue::Int(code) = function_call.parameters.clone().unwrap()[0].clone() { - let ptr = unsafe { malloc(code as usize) }; - Ok(get_flatbuffer_result(ptr as i32)) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to test_rust_malloc".to_string(), - )) - } +#[guest_function("TestMalloc")] +fn test_rust_malloc(code: i32) -> i32 { + let ptr = unsafe { malloc(code as usize) }; + ptr as i32 } -fn log_message(function_call: &FunctionCall) -> Result> { - if let (ParameterValue::String(message), ParameterValue::Int(level)) = ( - function_call.parameters.clone().unwrap()[0].clone(), - function_call.parameters.clone().unwrap()[1].clone(), - ) { - let level = LevelFilter::iter().nth(level as usize).unwrap().to_level(); +#[guest_function("LogMessage")] +fn log_message(message: String, level: i32) { + let level = LevelFilter::iter().nth(level as usize).unwrap().to_level(); - match level { - Some(level) => log::log!(level, "{}", &message), - None => { - // was passed LevelFilter::Off, do nothing - } + match level { + Some(level) => log::log!(level, "{}", &message), + None => { + // was passed LevelFilter::Off, do nothing } - Ok(get_flatbuffer_result(())) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to log_message".to_string(), - )) } } -fn trigger_exception(_: &FunctionCall) -> Result> { - unsafe { - core::arch::asm!("ud2"); - } // trigger an undefined instruction exception - Ok(get_flatbuffer_result(())) +#[guest_function("TriggerException")] +fn trigger_exception() { + // trigger an undefined instruction exception + unsafe { core::arch::asm!("ud2") }; } static mut COUNTER: i32 = 0; -fn add_to_static(function_call: &FunctionCall) -> Result> { - if let ParameterValue::Int(i) = function_call.parameters.clone().unwrap()[0].clone() { - let res = unsafe { - COUNTER += i; - COUNTER - }; - Ok(get_flatbuffer_result(res)) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to add_to_static".to_string(), - )) +#[guest_function("AddToStatic")] +fn add_to_static(i: i32) -> i32 { + unsafe { + COUNTER += i; + COUNTER } } -fn get_static(function_call: &FunctionCall) -> Result> { - if function_call.parameters.is_none() { - Ok(get_flatbuffer_result(unsafe { COUNTER })) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to get_static".to_string(), - )) - } +#[guest_function("GetStatic")] +fn get_static() -> i32 { + unsafe { COUNTER } } -fn add_to_static_and_fail(_: &FunctionCall) -> Result> { - unsafe { - COUNTER += 10; - }; +#[guest_function("AddToStaticAndFail")] +fn add_to_static_and_fail() -> Result { + unsafe { COUNTER += 10 }; Err(HyperlightGuestError::new( ErrorCode::GuestError, "Crash on purpose".to_string(), )) } -fn twenty_four_k_in_eight_k_out(function_call: &FunctionCall) -> Result> { - if let ParameterValue::VecBytes(input) = &function_call.parameters.as_ref().unwrap()[0] { - assert!(input.len() == 24 * 1024, "Input must be 24K bytes"); - Ok(get_flatbuffer_result(&input[..8 * 1024])) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to 24K_in_8K_out".to_string(), - )) - } +#[guest_function("24K_in_8K_out")] +fn twenty_four_k_in_eight_k_out(input: Vec) -> Vec { + assert!(input.len() == 24 * 1024, "Input must be 24K bytes"); + input[..8 * 1024].to_vec() } -fn call_given_paramless_hostfunc_that_returns_i64(function_call: &FunctionCall) -> Result> { - if let ParameterValue::String(hostfuncname) = - function_call.parameters.clone().unwrap()[0].clone() - { - let res = call_host_function::(&hostfuncname, None, ReturnType::Long)?; +#[guest_function("CallGivenParamlessHostFuncThatReturnsI64")] +fn call_given_paramless_hostfunc_that_returns_i64(hostfuncname: String) -> Result { + call_host_function::(&hostfuncname, None, ReturnType::Long) +} - Ok(get_flatbuffer_result(res)) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to test_rust_malloc".to_string(), - )) - } +#[guest_function("UseSSE2Registers")] +fn use_sse2_registers() { + let val: f32 = 1.2f32; + unsafe { core::arch::asm!("movss xmm1, DWORD PTR [{0}]", in(reg) &val) }; } -fn use_sse2_registers(_: &FunctionCall) -> Result> { - unsafe { - let val: f32 = 1.2f32; - core::arch::asm!("movss xmm1, DWORD PTR [{0}]", in(reg) &val); - } - Ok(get_flatbuffer_result(())) -} - -fn add(function_call: &FunctionCall) -> Result> { - if let (ParameterValue::Int(a), ParameterValue::Int(b)) = ( - function_call.parameters.clone().unwrap()[0].clone(), - function_call.parameters.clone().unwrap()[1].clone(), - ) { - let res = call_host_function::( - "HostAdd", - Some(Vec::from(&[ParameterValue::Int(a), ParameterValue::Int(b)])), - ReturnType::Int, - )?; - Ok(get_flatbuffer_result(res)) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to add".to_string(), - )) - } +#[guest_function("Add")] +fn add(a: i32, b: i32) -> Result { + #[host_function("HostAdd")] + fn host_add(a: i32, b: i32) -> Result; + + host_add(a, b) } // Does nothing, but used for testing large parameters -fn large_parameters(function_call: &FunctionCall) -> Result> { - if let (ParameterValue::VecBytes(v), ParameterValue::String(s)) = ( - function_call.parameters.clone().unwrap()[0].clone(), - function_call.parameters.clone().unwrap()[1].clone(), - ) { - black_box((v, s)); - Ok(get_flatbuffer_result(())) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to large_parameters".to_string(), - )) - } +#[guest_function("LargeParameters")] +fn large_parameters(v: Vec, s: String) { + black_box((v, s)); } -fn read_from_user_memory(function_call: &FunctionCall) -> Result> { - if let (ParameterValue::ULong(num), ParameterValue::VecBytes(expected)) = ( - function_call.parameters.clone().unwrap()[0].clone(), - function_call.parameters.clone().unwrap()[1].clone(), - ) { - let bytes = read_n_bytes_from_user_memory(num).expect("Failed to read from user memory"); - - // verify that the user memory contains the expected data - if bytes != expected { - error!("User memory does not contain the expected data"); - return Err(HyperlightGuestError::new( - ErrorCode::GuestError, - "User memory does not contain the expected data".to_string(), - )); - } +#[guest_function("ReadFromUserMemory")] +fn read_from_user_memory(num: u64, expected: Vec) -> Result> { + let bytes = read_n_bytes_from_user_memory(num).expect("Failed to read from user memory"); - Ok(get_flatbuffer_result(&*bytes)) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to read_from_user_memory".to_string(), - )) + // verify that the user memory contains the expected data + if bytes != expected { + error!("User memory does not contain the expected data"); + return Err(HyperlightGuestError::new( + ErrorCode::GuestError, + "User memory does not contain the expected data".to_string(), + )); } + + Ok(bytes) } -fn read_mapped_buffer(function_call: &FunctionCall) -> Result> { - if let (ParameterValue::ULong(base), ParameterValue::ULong(len)) = ( - function_call.parameters.clone().unwrap()[0].clone(), - function_call.parameters.clone().unwrap()[1].clone(), - ) { - let base = base as usize as *const u8; - let len = len as usize; +#[guest_function("ReadMappedBuffer")] +fn read_mapped_buffer(base: u64, len: u64) -> Vec { + let base = base as usize as *const u8; + let len = len as usize; - unsafe { - hyperlight_guest_bin::paging::map_region(base as _, base as _, len as u64 + 4096) - }; + unsafe { hyperlight_guest_bin::paging::map_region(base as _, base as _, len as u64 + 4096) }; - let data = unsafe { core::slice::from_raw_parts(base, len) }; + let data = unsafe { core::slice::from_raw_parts(base, len) }; - Ok(get_flatbuffer_result(data)) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to read_mapped_buffer".to_string(), - )) - } + data.to_vec() } -fn write_mapped_buffer(function_call: &FunctionCall) -> Result> { - if let (ParameterValue::ULong(base), ParameterValue::ULong(len)) = ( - function_call.parameters.clone().unwrap()[0].clone(), - function_call.parameters.clone().unwrap()[1].clone(), - ) { - let base = base as usize as *mut u8; - let len = len as usize; +#[guest_function("WriteMappedBuffer")] +fn write_mapped_buffer(base: u64, len: u64) -> bool { + let base = base as usize as *mut u8; + let len = len as usize; - unsafe { - hyperlight_guest_bin::paging::map_region(base as _, base as _, len as u64 + 4096) - }; + unsafe { hyperlight_guest_bin::paging::map_region(base as _, base as _, len as u64 + 4096) }; - let data = unsafe { core::slice::from_raw_parts_mut(base, len) }; + let data = unsafe { core::slice::from_raw_parts_mut(base, len) }; - // should fail - data[0] = 0x42; + // should fail + data[0] = 0x42; - // should never reach this - Ok(get_flatbuffer_result(true)) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to read_mapped_buffer".to_string(), - )) - } + // should never reach this + true } -fn exec_mapped_buffer(function_call: &FunctionCall) -> Result> { - if let (ParameterValue::ULong(base), ParameterValue::ULong(len)) = ( - function_call.parameters.clone().unwrap()[0].clone(), - function_call.parameters.clone().unwrap()[1].clone(), - ) { - let base = base as usize as *mut u8; - let len = len as usize; +#[guest_function("ExecMappedBuffer")] +fn exec_mapped_buffer(base: u64, len: u64) -> bool { + let base = base as usize as *mut u8; + let len = len as usize; - unsafe { - hyperlight_guest_bin::paging::map_region(base as _, base as _, len as u64 + 4096) - }; + unsafe { hyperlight_guest_bin::paging::map_region(base as _, base as _, len as u64 + 4096) }; - let data = unsafe { core::slice::from_raw_parts(base, len) }; + let data = unsafe { core::slice::from_raw_parts(base, len) }; - // Should be safe as long as data is something like a NOOP followed by a RET - let func: fn() = unsafe { core::mem::transmute(data.as_ptr()) }; - func(); + // Should be safe as long as data is something like a NOOP followed by a RET + let func: fn() = unsafe { core::mem::transmute(data.as_ptr()) }; + func(); - Ok(get_flatbuffer_result(true)) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to read_mapped_buffer".to_string(), - )) - } + true } -fn call_host_expect_error(function_call: &FunctionCall) -> Result> { - if let ParameterValue::String(hostfuncname) = - function_call.parameters.clone().unwrap()[0].clone() - { - let res = call_host_function::(&hostfuncname, None, ReturnType::Int); - - match res { - Ok(_) => Err(HyperlightGuestError::new( - ErrorCode::GuestError, - "Expected host function to fail, but it succeeded".to_string(), - )), - Err(e) => { - assert_eq!(e.kind, ErrorCode::HostFunctionError); - assert_eq!( - e.message, - format!("HostFunction {} was not found", hostfuncname) - ); - Ok(get_flatbuffer_result(())) - } - } - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to call_host_expect_error".to_string(), - )) - } +#[guest_function("CallHostExpectError")] +fn call_host_expect_error(hostfuncname: String) -> Result<()> { + let res = call_host_function::(&hostfuncname, None, ReturnType::Int); + + let Err(e) = res else { + return Err(HyperlightGuestError::new( + ErrorCode::GuestError, + "Expected host function to fail, but it succeeded".to_string(), + )); + }; + + assert_eq!(e.kind, ErrorCode::HostFunctionError); + assert_eq!( + e.message, + format!("HostFunction {hostfuncname} was not found") + ); + Ok(()) } #[no_mangle] #[instrument(skip_all, parent = Span::current(), level= "Trace")] pub extern "C" fn hyperlight_main() { - let expect_error_def = GuestFunctionDefinition::new( - "CallHostExpectError".to_string(), - Vec::from(&[ParameterType::String]), - ReturnType::Void, - call_host_expect_error as usize, - ); - register_function(expect_error_def); - - let twenty_four_k_in_def = GuestFunctionDefinition::new( - "24K_in_8K_out".to_string(), - Vec::from(&[ParameterType::VecBytes]), - ReturnType::VecBytes, - twenty_four_k_in_eight_k_out as usize, - ); - register_function(twenty_four_k_in_def); - - let read_from_user_memory_def = GuestFunctionDefinition::new( - "ReadFromUserMemory".to_string(), - Vec::from(&[ParameterType::ULong, ParameterType::VecBytes]), - ReturnType::VecBytes, - read_from_user_memory as usize, - ); - register_function(read_from_user_memory_def); - - let read_mapped_buffer_def = GuestFunctionDefinition::new( - "ReadMappedBuffer".to_string(), - Vec::from(&[ParameterType::ULong, ParameterType::ULong]), - ReturnType::VecBytes, - read_mapped_buffer as usize, - ); - register_function(read_mapped_buffer_def); - - let write_mapped_buffer_def = GuestFunctionDefinition::new( - "WriteMappedBuffer".to_string(), - Vec::from(&[ParameterType::ULong, ParameterType::ULong]), - ReturnType::Bool, - write_mapped_buffer as usize, - ); - register_function(write_mapped_buffer_def); - - let exec_mapped_buffer_def = GuestFunctionDefinition::new( - "ExecMappedBuffer".to_string(), - Vec::from(&[ParameterType::ULong, ParameterType::ULong]), - ReturnType::Bool, - exec_mapped_buffer as usize, - ); - register_function(exec_mapped_buffer_def); - - let set_static_def = GuestFunctionDefinition::new( - "SetStatic".to_string(), - Vec::new(), - ReturnType::Int, - set_static as usize, - ); - register_function(set_static_def); - - let simple_print_output_def = GuestFunctionDefinition::new( - "PrintOutput".to_string(), - Vec::from(&[ParameterType::String]), - ReturnType::Int, - simple_print_output as usize, - ); - register_function(simple_print_output_def); - let print_output_def = GuestFunctionDefinition::new( "PrintOutputWithHostPrint".to_string(), Vec::from(&[ParameterType::String]), @@ -1020,648 +538,98 @@ pub extern "C" fn hyperlight_main() { print_output_with_host_print as usize, ); register_function(print_output_def); +} - let guest_function_def = GuestFunctionDefinition::new( - "GuestMethod".to_string(), - Vec::from(&[ParameterType::String]), - ReturnType::Int, - guest_function as usize, - ); - register_function(guest_function_def); - - let guest_function1_def = GuestFunctionDefinition::new( - "GuestMethod1".to_string(), - Vec::from(&[ParameterType::String]), - ReturnType::Int, - guest_function1 as usize, - ); - register_function(guest_function1_def); - - let guest_function2_def = GuestFunctionDefinition::new( - "GuestMethod2".to_string(), - Vec::from(&[ParameterType::String]), - ReturnType::Int, - guest_function2 as usize, - ); - register_function(guest_function2_def); - - let guest_function3_def = GuestFunctionDefinition::new( - "GuestMethod3".to_string(), - Vec::from(&[ParameterType::String]), - ReturnType::Int, - guest_function3 as usize, - ); - register_function(guest_function3_def); - - let guest_function4_def = GuestFunctionDefinition::new( - "GuestMethod4".to_string(), - Vec::new(), - ReturnType::Int, - guest_function4 as usize, - ); - register_function(guest_function4_def); - - let guest_log_message_def = GuestFunctionDefinition::new( - "LogMessageWithSource".to_string(), - Vec::from(&[ - ParameterType::String, - ParameterType::String, - ParameterType::Int, - ]), - ReturnType::Int, - guest_log_message as usize, - ); - register_function(guest_log_message_def); - - let call_error_method_def = GuestFunctionDefinition::new( - "CallErrorMethod".to_string(), - Vec::from(&[ParameterType::String]), - ReturnType::Int, - call_error_method as usize, - ); - register_function(call_error_method_def); - - let call_host_spin_def = GuestFunctionDefinition::new( - "CallHostSpin".to_string(), - Vec::new(), - ReturnType::Int, - call_host_spin as usize, - ); - register_function(call_host_spin_def); - - let host_call_loop_def = GuestFunctionDefinition::new( - "HostCallLoop".to_string(), - Vec::from(&[ParameterType::String]), - ReturnType::Void, - host_call_loop as usize, - ); - register_function(host_call_loop_def); - - let call_host_then_spin_def = GuestFunctionDefinition::new( - "CallHostThenSpin".to_string(), - Vec::from(&[ParameterType::String]), - ReturnType::Void, - call_host_then_spin as usize, - ); - register_function(call_host_then_spin_def); - - let print_using_printf_def = GuestFunctionDefinition::new( - "PrintUsingPrintf".to_string(), - Vec::from(&[ParameterType::String]), - ReturnType::Int, - simple_print_output as usize, // alias to simple_print_output for now - ); - register_function(print_using_printf_def); - - let stack_overflow_def = GuestFunctionDefinition::new( - "StackOverflow".to_string(), - Vec::from(&[ParameterType::Int]), - ReturnType::Int, - stack_overflow as usize, - ); - register_function(stack_overflow_def); - - let buffer_overrun_def = GuestFunctionDefinition::new( - "BufferOverrun".to_string(), - Vec::from(&[ParameterType::String]), - ReturnType::Int, - buffer_overrun as usize, - ); - register_function(buffer_overrun_def); - - let large_var_def = GuestFunctionDefinition::new( - "LargeVar".to_string(), - Vec::new(), - ReturnType::Int, - large_var as usize, - ); - register_function(large_var_def); - - let small_var_def = GuestFunctionDefinition::new( - "SmallVar".to_string(), - Vec::new(), - ReturnType::Int, - small_var as usize, - ); - register_function(small_var_def); - - let call_malloc_def = GuestFunctionDefinition::new( - "CallMalloc".to_string(), - Vec::from(&[ParameterType::Int]), - ReturnType::Int, - call_malloc as usize, - ); - register_function(call_malloc_def); - - let exhaust_heap_def = GuestFunctionDefinition::new( - "ExhaustHeap".to_string(), - Vec::new(), - ReturnType::Int, - exhaust_heap as usize, - ); - register_function(exhaust_heap_def); - - let malloc_and_free_def = GuestFunctionDefinition::new( - "MallocAndFree".to_string(), - Vec::from(&[ParameterType::Int]), - ReturnType::Int, - malloc_and_free as usize, - ); - register_function(malloc_and_free_def); - - let print_two_args_def = GuestFunctionDefinition::new( - "PrintTwoArgs".to_string(), - Vec::from(&[ParameterType::String, ParameterType::Int]), - ReturnType::Int, - print_two_args as usize, - ); - register_function(print_two_args_def); - - let print_three_args_def = GuestFunctionDefinition::new( - "PrintThreeArgs".to_string(), - Vec::from(&[ - ParameterType::String, - ParameterType::Int, - ParameterType::Long, - ]), - ReturnType::Int, - print_three_args as usize, - ); - register_function(print_three_args_def); - - let print_four_args_def = GuestFunctionDefinition::new( - "PrintFourArgs".to_string(), - Vec::from(&[ - ParameterType::String, - ParameterType::Int, - ParameterType::Long, - ParameterType::String, - ]), - ReturnType::Int, - print_four_args as usize, - ); - register_function(print_four_args_def); - - let print_five_args_def = GuestFunctionDefinition::new( - "PrintFiveArgs".to_string(), - Vec::from(&[ - ParameterType::String, - ParameterType::Int, - ParameterType::Long, - ParameterType::String, - ParameterType::String, - ]), - ReturnType::Int, - print_five_args as usize, - ); - register_function(print_five_args_def); - - let print_six_args_def = GuestFunctionDefinition::new( - "PrintSixArgs".to_string(), - Vec::from(&[ - ParameterType::String, - ParameterType::Int, - ParameterType::Long, - ParameterType::String, - ParameterType::String, - ParameterType::Bool, - ]), - ReturnType::Int, - print_six_args as usize, - ); - register_function(print_six_args_def); - - let print_seven_args_def = GuestFunctionDefinition::new( - "PrintSevenArgs".to_string(), - Vec::from(&[ - ParameterType::String, - ParameterType::Int, - ParameterType::Long, - ParameterType::String, - ParameterType::String, - ParameterType::Bool, - ParameterType::Bool, - ]), - ReturnType::Int, - print_seven_args as usize, - ); - register_function(print_seven_args_def); - - let print_eight_args_def = GuestFunctionDefinition::new( - "PrintEightArgs".to_string(), - Vec::from(&[ - ParameterType::String, - ParameterType::Int, - ParameterType::Long, - ParameterType::String, - ParameterType::String, - ParameterType::Bool, - ParameterType::Bool, - ParameterType::UInt, - ]), - ReturnType::Int, - print_eight_args as usize, - ); - register_function(print_eight_args_def); - - let print_nine_args_def = GuestFunctionDefinition::new( - "PrintNineArgs".to_string(), - Vec::from(&[ - ParameterType::String, - ParameterType::Int, - ParameterType::Long, - ParameterType::String, - ParameterType::String, - ParameterType::Bool, - ParameterType::Bool, - ParameterType::UInt, - ParameterType::ULong, - ]), - ReturnType::Int, - print_nine_args as usize, - ); - register_function(print_nine_args_def); - - let print_ten_args_def = GuestFunctionDefinition::new( - "PrintTenArgs".to_string(), - Vec::from(&[ - ParameterType::String, - ParameterType::Int, - ParameterType::Long, - ParameterType::String, - ParameterType::String, - ParameterType::Bool, - ParameterType::Bool, - ParameterType::UInt, - ParameterType::ULong, - ParameterType::Int, - ]), - ReturnType::Int, - print_ten_args as usize, - ); - register_function(print_ten_args_def); - - let print_eleven_args_def = GuestFunctionDefinition::new( - "PrintElevenArgs".to_string(), - Vec::from(&[ - ParameterType::String, - ParameterType::Int, - ParameterType::Long, - ParameterType::String, - ParameterType::String, - ParameterType::Bool, - ParameterType::Bool, - ParameterType::UInt, - ParameterType::ULong, - ParameterType::Int, - ParameterType::Float, - ]), - ReturnType::Int, - print_eleven_args as usize, - ); - register_function(print_eleven_args_def); - - let set_byte_array_to_zero_def = GuestFunctionDefinition::new( - "SetByteArrayToZero".to_string(), - Vec::from(&[ParameterType::VecBytes]), - ReturnType::VecBytes, - set_byte_array_to_zero as usize, - ); - register_function(set_byte_array_to_zero_def); - - let echo_def = GuestFunctionDefinition::new( - "Echo".to_string(), - Vec::from(&[ParameterType::String]), - ReturnType::String, - echo as usize, - ); - register_function(echo_def); - - let get_size_prefixed_buffer_def = GuestFunctionDefinition::new( - "GetSizePrefixedBuffer".to_string(), - Vec::from(&[ParameterType::VecBytes]), - ReturnType::Int, - get_size_prefixed_buffer as usize, - ); - register_function(get_size_prefixed_buffer_def); - - let spin_def = GuestFunctionDefinition::new( - "Spin".to_string(), - Vec::new(), - ReturnType::Int, - spin as usize, - ); - register_function(spin_def); - - let spin_for_ms_def = GuestFunctionDefinition::new( - "SpinForMs".to_string(), - Vec::from(&[ParameterType::UInt]), - ReturnType::ULong, - spin_for_ms as usize, - ); - register_function(spin_for_ms_def); - - let abort_def = GuestFunctionDefinition::new( - "GuestAbortWithCode".to_string(), - Vec::from(&[ParameterType::Int]), - ReturnType::Void, - test_abort as usize, - ); - register_function(abort_def); - - let abort_with_code_message_def = GuestFunctionDefinition::new( - "GuestAbortWithMessage".to_string(), - Vec::from(&[ParameterType::Int, ParameterType::String]), - ReturnType::Void, - test_abort_with_code_and_message as usize, - ); - register_function(abort_with_code_message_def); - - let guest_panic_def = GuestFunctionDefinition::new( - "guest_panic".to_string(), - Vec::from(&[ParameterType::String]), - ReturnType::Void, - test_guest_panic as usize, - ); - register_function(guest_panic_def); - - let rust_malloc_def = GuestFunctionDefinition::new( - "TestMalloc".to_string(), - Vec::from(&[ParameterType::Int]), - ReturnType::Int, - test_rust_malloc as usize, - ); - register_function(rust_malloc_def); - - let log_message_def = GuestFunctionDefinition::new( - "LogMessage".to_string(), - Vec::from(&[ParameterType::String, ParameterType::Int]), - ReturnType::Void, - log_message as usize, - ); - register_function(log_message_def); - - let infinite_recursion_def = GuestFunctionDefinition::new( - "InfiniteRecursion".to_string(), - Vec::new(), - ReturnType::Void, - infinite_recursion as usize, - ); - register_function(infinite_recursion_def); - - let test_write_raw_ptr_def = GuestFunctionDefinition::new( - "test_write_raw_ptr".to_string(), - Vec::from(&[ParameterType::Long]), - ReturnType::String, - test_write_raw_ptr as usize, - ); - register_function(test_write_raw_ptr_def); - - let execute_on_stack_def = GuestFunctionDefinition::new( - "ExecuteOnStack".to_string(), - Vec::new(), - ReturnType::String, - execute_on_stack as usize, - ); - register_function(execute_on_stack_def); - - let execute_on_heap_def = GuestFunctionDefinition::new( - "ExecuteOnHeap".to_string(), - Vec::new(), - ReturnType::String, - execute_on_heap as usize, - ); - register_function(execute_on_heap_def); - - let add_to_static_def = GuestFunctionDefinition::new( - "AddToStatic".to_string(), - Vec::from(&[ParameterType::Int]), - ReturnType::Int, - add_to_static as usize, - ); - register_function(add_to_static_def); - - let get_static_def = GuestFunctionDefinition::new( - "GetStatic".to_string(), - Vec::new(), - ReturnType::Int, - get_static as usize, - ); - register_function(get_static_def); - - let add_to_static_and_fail_def = GuestFunctionDefinition::new( - "AddToStaticAndFail".to_string(), - Vec::new(), - ReturnType::Int, - add_to_static_and_fail as usize, - ); - register_function(add_to_static_and_fail_def); - - let echo_float_def = GuestFunctionDefinition::new( - "EchoFloat".to_string(), - Vec::from(&[ParameterType::Float]), - ReturnType::Float, - echo_float as usize, - ); - register_function(echo_float_def); - - let echo_double_def = GuestFunctionDefinition::new( - "EchoDouble".to_string(), - Vec::from(&[ParameterType::Double]), - ReturnType::Double, - echo_double as usize, - ); - register_function(echo_double_def); - - let add_def = GuestFunctionDefinition::new( - "Add".to_string(), - Vec::from(&[ParameterType::Int, ParameterType::Int]), - ReturnType::Int, - add as usize, - ); - register_function(add_def); - - let trigger_exception_def = GuestFunctionDefinition::new( - "TriggerException".to_string(), - Vec::new(), - ReturnType::Void, - trigger_exception as usize, - ); - register_function(trigger_exception_def); - - let large_parameters_def = GuestFunctionDefinition::new( - "LargeParameters".to_string(), - Vec::from(&[ParameterType::VecBytes, ParameterType::String]), - ReturnType::Void, - large_parameters as usize, - ); - register_function(large_parameters_def); - - let call_given_hostfunc_def = GuestFunctionDefinition::new( - "CallGivenParamlessHostFuncThatReturnsI64".to_string(), - Vec::from(&[ParameterType::String]), - ReturnType::Long, - call_given_paramless_hostfunc_that_returns_i64 as usize, - ); - register_function(call_given_hostfunc_def); +#[host_function("HostMethod")] +fn host_method(message: String) -> Result; - let use_sse2_registers = GuestFunctionDefinition::new( - "UseSSE2Registers".to_string(), - Vec::new(), - ReturnType::Void, - use_sse2_registers as usize, - ); - register_function(use_sse2_registers); +#[guest_function("GuestMethod")] +fn guest_function(message: String) -> Result { + let message = format!("Hello from GuestFunction, {}", message); + host_method(message) } -fn send_message_to_host_method( - method_name: &str, - guest_message: &str, - message: &str, -) -> Result> { - let message = format!("{}{}", guest_message, message); - let res = call_host_function::( - method_name, - Some(Vec::from(&[ParameterValue::String(message.to_string())])), - ReturnType::Int, - )?; +#[host_function("HostMethod1")] +fn host_method_1(message: String) -> Result; - Ok(get_flatbuffer_result(res)) +#[guest_function("GuestMethod1")] +fn guest_function1(message: String) -> Result { + let message = format!("Hello from GuestFunction1, {}", message); + host_method_1(message) } -fn guest_function(function_call: &FunctionCall) -> Result> { - if let ParameterValue::String(message) = &function_call.parameters.as_ref().unwrap()[0] { - send_message_to_host_method("HostMethod", "Hello from GuestFunction, ", message) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to guest_function".to_string(), - )) - } +#[guest_function("GuestMethod2")] +fn guest_function2(message: String) -> Result { + let message = format!("Hello from GuestFunction2, {}", message); + host_method_1(message) } -fn guest_function1(function_call: &FunctionCall) -> Result> { - if let ParameterValue::String(message) = &function_call.parameters.as_ref().unwrap()[0] { - send_message_to_host_method("HostMethod1", "Hello from GuestFunction1, ", message) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to guest_function1".to_string(), - )) - } +#[guest_function("GuestMethod3")] +fn guest_function3(message: String) -> Result { + let message = format!("Hello from GuestFunction3, {}", message); + host_method_1(message) } -fn guest_function2(function_call: &FunctionCall) -> Result> { - if let ParameterValue::String(message) = &function_call.parameters.as_ref().unwrap()[0] { - send_message_to_host_method("HostMethod1", "Hello from GuestFunction2, ", message) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to guest_function2".to_string(), - )) - } +#[host_function("HostMethod4")] +fn host_method_4(message: String) -> Result<()>; + +#[guest_function("GuestMethod4")] +fn guest_function4() -> Result<()> { + host_method_4("Hello from GuestFunction4".to_string()) } -fn guest_function3(function_call: &FunctionCall) -> Result> { - if let ParameterValue::String(message) = &function_call.parameters.as_ref().unwrap()[0] { - send_message_to_host_method("HostMethod1", "Hello from GuestFunction3, ", message) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to guest_function3".to_string(), - )) +#[guest_function("LogMessageWithSource")] +fn guest_log_message(message: String, source: String, level: i32) -> i32 { + let mut log_level = level; + if !(0..=6).contains(&log_level) { + log_level = 0; } -} -fn guest_function4(_: &FunctionCall) -> Result> { - call_host_function::<()>( - "HostMethod4", - Some(Vec::from(&[ParameterValue::String( - "Hello from GuestFunction4".to_string(), - )])), - ReturnType::Void, - )?; + guest_logger::log_message( + LogLevel::from(log_level as u8), + &message, + &source, + "guest_log_message", + file!(), + line!(), + ); - Ok(get_flatbuffer_result(())) -} - -fn guest_log_message(function_call: &FunctionCall) -> Result> { - if let ( - ParameterValue::String(message), - ParameterValue::String(source), - ParameterValue::Int(level), - ) = ( - &function_call.parameters.as_ref().unwrap()[0], - &function_call.parameters.as_ref().unwrap()[1], - &function_call.parameters.as_ref().unwrap()[2], - ) { - let mut log_level = *level; - if !(0..=6).contains(&log_level) { - log_level = 0; - } + message.len() as i32 +} - guest_logger::log_message( - LogLevel::from(log_level as u8), - message, - source, - "guest_log_message", - file!(), - line!(), - ); +#[guest_function("CallErrorMethod")] +fn call_error_method(message: String) -> Result { + #[host_function("ErrorMethod")] + fn error_method(message: String) -> Result; - Ok(get_flatbuffer_result(message.len() as i32)) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to guest_log_message".to_string(), - )) - } + let message = format!("Error From Host: {}", message); + error_method(message) } -fn call_error_method(function_call: &FunctionCall) -> Result> { - if let ParameterValue::String(message) = &function_call.parameters.as_ref().unwrap()[0] { - send_message_to_host_method("ErrorMethod", "Error From Host: ", message) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to call_error_method".to_string(), - )) - } -} +#[guest_function("CallHostSpin")] +fn call_host_spin() -> Result<()> { + #[host_function("Spin")] + fn host_spin() -> Result<()>; -fn call_host_spin(_: &FunctionCall) -> Result> { - call_host_function::<()>("Spin", None, ReturnType::Void)?; - Ok(get_flatbuffer_result(())) + host_spin() } -fn host_call_loop(function_call: &FunctionCall) -> Result> { - if let ParameterValue::String(message) = &function_call.parameters.as_ref().unwrap()[0] { - loop { - call_host_function::<()>(message, None, ReturnType::Void).unwrap(); - } - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to host_call_loop".to_string(), - )) +#[guest_function("HostCallLoop")] +fn host_call_loop(host_func_name: String) -> Result> { + loop { + call_host_function::<()>(&host_func_name, None, ReturnType::Void).unwrap(); } } // Calls the given host function (no param, no return value) and then spins indefinitely. -fn call_host_then_spin(function_call: &FunctionCall) -> Result> { - if let ParameterValue::String(host_func_name) = &function_call.parameters.as_ref().unwrap()[0] { - call_host_function::<()>(host_func_name, None, ReturnType::Void)?; - #[expect( - clippy::empty_loop, - reason = "This function is used to keep the CPU busy" - )] - loop {} - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to call_host_then_spin".to_string(), - )) - } +#[guest_function("CallHostThenSpin")] +fn call_host_then_spin(host_func_name: String) -> Result<()> { + call_host_function::<()>(&host_func_name, None, ReturnType::Void)?; + #[expect( + clippy::empty_loop, + reason = "This function is used to keep the CPU busy" + )] + loop {} } // Interprets the given guest function call as a host function call and dispatches it to the host. From fa4e81518e22e57508467a5acda235d7b87a99bb Mon Sep 17 00:00:00 2001 From: Jorge Prendes Date: Tue, 2 Dec 2025 14:15:28 +0000 Subject: [PATCH 6/9] update test guests Cargo.lock files Signed-off-by: Jorge Prendes --- .../rust_guests/callbackguest/Cargo.lock | 270 ------------------ src/tests/rust_guests/dummyguest/Cargo.lock | 140 ++++++++- src/tests/rust_guests/simpleguest/Cargo.lock | 119 +++++++- src/tests/rust_guests/witguest/Cargo.lock | 133 ++++++++- 4 files changed, 376 insertions(+), 286 deletions(-) delete mode 100644 src/tests/rust_guests/callbackguest/Cargo.lock diff --git a/src/tests/rust_guests/callbackguest/Cargo.lock b/src/tests/rust_guests/callbackguest/Cargo.lock deleted file mode 100644 index 21ba7c47d..000000000 --- a/src/tests/rust_guests/callbackguest/Cargo.lock +++ /dev/null @@ -1,270 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "anyhow" -version = "1.0.99" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "bitflags" -version = "2.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" - -[[package]] -name = "buddy_system_allocator" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a0108968a3a2dab95b089c0fc3f1afa7759aa5ebe6f1d86d206d6f7ba726eb" -dependencies = [ - "spin 0.9.8", -] - -[[package]] -name = "callbackguest" -version = "0.1.0" -dependencies = [ - "hyperlight-common", - "hyperlight-guest", - "hyperlight-guest-bin", - "hyperlight-guest-tracing", -] - -[[package]] -name = "cc" -version = "1.2.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" -dependencies = [ - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" - -[[package]] -name = "flatbuffers" -version = "25.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1045398c1bfd89168b5fd3f1fc11f6e70b34f6f66300c87d44d3de849463abf1" -dependencies = [ - "bitflags", - "rustc_version", -] - -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - -[[package]] -name = "hyperlight-common" -version = "0.8.0" -dependencies = [ - "anyhow", - "flatbuffers", - "log", - "spin 0.10.0", -] - -[[package]] -name = "hyperlight-guest" -version = "0.8.0" -dependencies = [ - "anyhow", - "hyperlight-common", - "hyperlight-guest-tracing", - "serde_json", -] - -[[package]] -name = "hyperlight-guest-bin" -version = "0.8.0" -dependencies = [ - "buddy_system_allocator", - "cc", - "cfg-if", - "glob", - "hyperlight-common", - "hyperlight-guest", - "hyperlight-guest-tracing", - "log", - "spin 0.10.0", -] - -[[package]] -name = "hyperlight-guest-tracing" -version = "0.8.0" -dependencies = [ - "hyperlight-common", - "hyperlight-guest-tracing-macro", - "spin 0.10.0", -] - -[[package]] -name = "hyperlight-guest-tracing-macro" -version = "0.8.0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "itoa" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" - -[[package]] -name = "lock_api" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" - -[[package]] -name = "memchr" -version = "2.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" - -[[package]] -name = "proc-macro2" -version = "1.0.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "semver" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" - -[[package]] -name = "serde" -version = "1.0.219" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.219" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.141" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spin" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" -dependencies = [ - "lock_api", -] - -[[package]] -name = "syn" -version = "2.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" diff --git a/src/tests/rust_guests/dummyguest/Cargo.lock b/src/tests/rust_guests/dummyguest/Cargo.lock index 725c332ce..e43391031 100644 --- a/src/tests/rust_guests/dummyguest/Cargo.lock +++ b/src/tests/rust_guests/dummyguest/Cargo.lock @@ -52,6 +52,12 @@ dependencies = [ "hyperlight-guest-bin", ] +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "flatbuffers" version = "25.9.23" @@ -68,6 +74,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + [[package]] name = "hyperlight-common" version = "0.11.0" @@ -76,6 +88,7 @@ dependencies = [ "flatbuffers", "log", "spin 0.10.0", + "thiserror", ] [[package]] @@ -101,12 +114,24 @@ dependencies = [ "glob", "hyperlight-common", "hyperlight-guest", + "hyperlight-guest-macro", "hyperlight-guest-tracing", + "linkme", "log", "spin 0.10.0", "tracing", ] +[[package]] +name = "hyperlight-guest-macro" +version = "0.11.0" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "hyperlight-guest-tracing" version = "0.11.0" @@ -117,12 +142,42 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "indexmap" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "linkme" +version = "0.3.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e3283ed2d0e50c06dd8602e0ab319bb048b6325d0bba739db64ed8205179898" +dependencies = [ + "linkme-impl", +] + +[[package]] +name = "linkme-impl" +version = "0.3.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5cec0ec4228b4853bb129c84dbf093a27e6c7a20526da046defc334a1b017f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "lock_api" version = "0.4.13" @@ -151,6 +206,15 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.101" @@ -198,18 +262,27 @@ checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -263,6 +336,56 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml_datetime" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.23.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ + "winnow", +] + [[package]] name = "tracing" version = "0.1.43" @@ -296,3 +419,12 @@ name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] diff --git a/src/tests/rust_guests/simpleguest/Cargo.lock b/src/tests/rust_guests/simpleguest/Cargo.lock index d621eaa85..e915ce41b 100644 --- a/src/tests/rust_guests/simpleguest/Cargo.lock +++ b/src/tests/rust_guests/simpleguest/Cargo.lock @@ -44,6 +44,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "flatbuffers" version = "25.9.23" @@ -60,6 +66,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + [[package]] name = "hyperlight-common" version = "0.11.0" @@ -94,12 +106,24 @@ dependencies = [ "glob", "hyperlight-common", "hyperlight-guest", + "hyperlight-guest-macro", "hyperlight-guest-tracing", + "linkme", "log", "spin 0.10.0", "tracing", ] +[[package]] +name = "hyperlight-guest-macro" +version = "0.11.0" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "hyperlight-guest-tracing" version = "0.11.0" @@ -110,12 +134,42 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "indexmap" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "linkme" +version = "0.3.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e3283ed2d0e50c06dd8602e0ab319bb048b6325d0bba739db64ed8205179898" +dependencies = [ + "linkme-impl", +] + +[[package]] +name = "linkme-impl" +version = "0.3.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5cec0ec4228b4853bb129c84dbf093a27e6c7a20526da046defc334a1b017f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "lock_api" version = "0.4.13" @@ -144,6 +198,15 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.101" @@ -191,18 +254,27 @@ checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -288,6 +360,36 @@ dependencies = [ "syn", ] +[[package]] +name = "toml_datetime" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.23.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ + "winnow", +] + [[package]] name = "tracing" version = "0.1.43" @@ -321,3 +423,12 @@ name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] diff --git a/src/tests/rust_guests/witguest/Cargo.lock b/src/tests/rust_guests/witguest/Cargo.lock index bad77a91b..032aafb5b 100644 --- a/src/tests/rust_guests/witguest/Cargo.lock +++ b/src/tests/rust_guests/witguest/Cargo.lock @@ -176,6 +176,12 @@ dependencies = [ "serde", ] +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + [[package]] name = "hyperlight-common" version = "0.11.0" @@ -184,6 +190,7 @@ dependencies = [ "flatbuffers", "log", "spin 0.10.0", + "thiserror", ] [[package]] @@ -236,12 +243,24 @@ dependencies = [ "glob", "hyperlight-common", "hyperlight-guest", + "hyperlight-guest-macro", "hyperlight-guest-tracing", + "linkme", "log", "spin 0.10.0", "tracing", ] +[[package]] +name = "hyperlight-guest-macro" +version = "0.11.0" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "hyperlight-guest-tracing" version = "0.11.0" @@ -254,13 +273,14 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.16.1", "serde", + "serde_core", ] [[package]] @@ -308,6 +328,26 @@ dependencies = [ "syn", ] +[[package]] +name = "linkme" +version = "0.3.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e3283ed2d0e50c06dd8602e0ab319bb048b6325d0bba739db64ed8205179898" +dependencies = [ + "linkme-impl", +] + +[[package]] +name = "linkme-impl" +version = "0.3.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5cec0ec4228b4853bb129c84dbf093a27e6c7a20526da046defc334a1b017f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "lock_api" version = "0.4.13" @@ -367,6 +407,15 @@ dependencies = [ "syn", ] +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.103" @@ -443,18 +492,27 @@ checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -508,6 +566,56 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml_datetime" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.23.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ + "winnow", +] + [[package]] name = "tracing" version = "0.1.43" @@ -555,7 +663,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed3c6e611f4cd748d85c767815823b777dc56afca793fcda27beae4e85028849" dependencies = [ "bitflags", - "hashbrown", + "hashbrown 0.15.5", "indexmap", "semver", "serde", @@ -641,6 +749,15 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] + [[package]] name = "witguest" version = "0.10.0" From 4bddfe9365abc2505a35ccccf4bcfbabd28f069e Mon Sep 17 00:00:00 2001 From: Jorge Prendes Date: Tue, 2 Dec 2025 23:10:12 +0000 Subject: [PATCH 7/9] Move result unwrap used in host_function macro from hl-common to hl-guest-bin Signed-off-by: Jorge Prendes --- src/hyperlight_common/src/func/ret_type.rs | 14 ++-------- src/hyperlight_guest_bin/src/lib.rs | 30 ++++++++++++++++++++++ src/hyperlight_guest_macro/src/lib.rs | 6 ++--- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/hyperlight_common/src/func/ret_type.rs b/src/hyperlight_common/src/func/ret_type.rs index b23efa1ed..69e0f1a13 100644 --- a/src/hyperlight_common/src/func/ret_type.rs +++ b/src/hyperlight_common/src/func/ret_type.rs @@ -32,6 +32,8 @@ pub trait SupportedReturnType: Sized + Clone + Send + Sync + 'static { fn from_value(value: ReturnValue) -> Result; } +#[macro_export] +#[doc(hidden)] macro_rules! for_each_return_type { ($macro:ident) => { $macro!((), Void); @@ -76,9 +78,6 @@ pub trait ResultType { /// Convert the return type into a `Result` fn into_result(self) -> Result; - - /// Convert a result into this type, panicking if needed - fn from_result(res: Result) -> Self; } impl ResultType for T @@ -91,11 +90,6 @@ where fn into_result(self) -> Result { Ok(self) } - - fn from_result(res: Result) -> Self { - #![allow(clippy::unwrap_used)] - res.unwrap() - } } impl ResultType for Result @@ -108,10 +102,6 @@ where fn into_result(self) -> Result { self } - - fn from_result(res: Result) -> Self { - res - } } for_each_return_type!(impl_supported_return_type); diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index ffeb66b19..bf61b3a62 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -260,6 +260,36 @@ pub mod __private { #[linkme::distributed_slice] pub static GUEST_FUNCTION_INIT: [fn()]; + + pub trait FromResult { + type Output; + fn from_result(res: Result) -> Self; + } + + use alloc::string::String; + use alloc::vec::Vec; + + use hyperlight_common::for_each_return_type; + + macro_rules! impl_maybe_unwrap { + ($ty:ty, $enum:ident) => { + impl FromResult for $ty { + type Output = Self; + fn from_result(res: Result) -> Self { + res.unwrap() + } + } + + impl FromResult for Result<$ty, HyperlightGuestError> { + type Output = $ty; + fn from_result(res: Result) -> Self { + res + } + } + }; + } + + for_each_return_type!(impl_maybe_unwrap); } #[cfg(feature = "macros")] diff --git a/src/hyperlight_guest_macro/src/lib.rs b/src/hyperlight_guest_macro/src/lib.rs index ad5de0744..25af8b17d 100644 --- a/src/hyperlight_guest_macro/src/lib.rs +++ b/src/hyperlight_guest_macro/src/lib.rs @@ -85,7 +85,7 @@ pub fn guest_function(attr: TokenStream, item: TokenStream) -> TokenStream { #[#crate_name::__private::linkme::distributed_slice(#crate_name::__private::GUEST_FUNCTION_INIT)] #[linkme(crate = #crate_name::__private::linkme)] static REGISTRATION: fn() = || { - hyperlight_guest_bin::guest_function::register::register_fn(#exported_name, #ident); + #crate_name::guest_function::register::register_fn(#exported_name, #ident); }; }; }; @@ -194,9 +194,9 @@ pub fn host_function(attr: TokenStream, item: TokenStream) -> TokenStream { let output = quote! { #(#attrs)* #vis #sig { - use #crate_name::__private::{ResultType, HyperlightGuestError}; + use #crate_name::__private::FromResult; use #crate_name::host_comm::call_host; - <#ret as ResultType>::from_result(call_host(#exported_name, (#(#args,)*))) + <#ret as FromResult>::from_result(call_host(#exported_name, (#(#args,)*))) } }; From 31a0249c123ac6fae5609cd362f2bebeccbe9e5d Mon Sep 17 00:00:00 2001 From: Jorge Prendes Date: Tue, 2 Dec 2025 23:11:17 +0000 Subject: [PATCH 8/9] Add comments on tricky areas Signed-off-by: Jorge Prendes --- .../src/guest_function/definition.rs | 25 ++++ src/hyperlight_guest_bin/src/lib.rs | 2 + src/hyperlight_guest_macro/src/lib.rs | 131 ++++++++++++++++++ .../src/func/host_functions.rs | 18 +-- .../src/sandbox/initialized_multi_use.rs | 2 + 5 files changed, 169 insertions(+), 9 deletions(-) diff --git a/src/hyperlight_guest_bin/src/guest_function/definition.rs b/src/hyperlight_guest_bin/src/guest_function/definition.rs index 5fabb8117..f889f4fb6 100644 --- a/src/hyperlight_guest_bin/src/guest_function/definition.rs +++ b/src/hyperlight_guest_bin/src/guest_function/definition.rs @@ -94,6 +94,31 @@ macro_rules! impl_host_function { ($($P,)*): ParameterTuple, R: ResultType, { + // Only functions that can be coerced into a function pointer (i.e., "fn" types) + // can be registered as guest functions. + // + // Note that the "Fn" trait is different from "fn" types in Rust. + // "fn" is a type, while "Fn" is a trait. + // For example, closures that capture environment implement "Fn" but cannot be + // coerced to function pointers. + // This means that the closure returned by `into_guest_function` can not capture + // any environment, not event `self`, and we must only rely on the type system + // to call the correct function. + // + // Ideally we would implement IntoGuestFunction for any F that can be converted + // into a function pointer, but currently there's no way to express that in Rust's + // type system. + // Therefore, to ensure that F is a "fn" type, we enforce that F is zero-sized + // has no Drop impl (the latter is enforced by the Copy bound), and it doesn't + // capture any lifetimes (not even through a marker type like PhantomData). + // + // Note that implementing IntoGuestFunction for "fn($(P),*) -> R" is not an option + // either, "fn($(P),*) -> R" is a type that's shared for all function pointers with + // that signature, e.g., "fn add(a: i32, b: i32) -> i32 { a + b }" and + // "fn sub(a: i32, b: i32) -> i32 { a - b }" both can be coerced to the same + // "fn(i32, i32) -> i32" type, so we would need to capture self (a function pointer) + // to know exactly which function to call. + #[doc(hidden)] const ASSERT_ZERO_SIZED: () = const { assert!(core::mem::size_of::() == 0) diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index bf61b3a62..d692e0b26 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -276,6 +276,8 @@ pub mod __private { impl FromResult for $ty { type Output = Self; fn from_result(res: Result) -> Self { + // Unwrapping here is fine as this would only run in a guest + // and not in the host. res.unwrap() } } diff --git a/src/hyperlight_guest_macro/src/lib.rs b/src/hyperlight_guest_macro/src/lib.rs index 25af8b17d..08e1247ce 100644 --- a/src/hyperlight_guest_macro/src/lib.rs +++ b/src/hyperlight_guest_macro/src/lib.rs @@ -21,6 +21,7 @@ use syn::parse::{Error, Parse, ParseStream, Result}; use syn::spanned::Spanned as _; use syn::{ForeignItemFn, ItemFn, LitStr, Pat, parse_macro_input}; +/// Represents the optional name argument for the guest_function and host_function macros. enum NameArg { None, Name(LitStr), @@ -28,6 +29,8 @@ enum NameArg { impl Parse for NameArg { fn parse(input: ParseStream) -> Result { + // accepts either nothing or a single string literal + // anything else is an error if input.is_empty() { return Ok(NameArg::None); } @@ -39,8 +42,52 @@ impl Parse for NameArg { } } +/// Attribute macro to mark a function as a guest function. +/// This will register the function so that it can be called by the host. +/// +/// If a name is provided as an argument, that name will be used to register the function. +/// Otherwise, the function's identifier will be used. +/// +/// The function arguments must be supported parameter types, and the return type must be +/// a supported return type or a `Result` with T being a supported +/// return type. +/// +/// # Note +/// The function will be registered with the host at program initialization regardless of +/// the visibility modifier used (e.g., `pub`, `pub(crate)`, etc.). +/// This means that a private functions can be called by the host from beyond its normal +/// visibility scope. +/// +/// # Example +/// ```ignore +/// use hyperlight_guest_bin::guest_function; +/// #[guest_function] +/// fn my_guest_function(arg1: i32, arg2: String) -> i32 { +/// arg1 + arg2.len() as i32 +/// } +/// ``` +/// +/// or with a custom name: +/// ```ignore +/// use hyperlight_guest_bin::guest_function; +/// #[guest_function("custom_name")] +/// fn my_guest_function(arg1: i32, arg2: String) -> i32 { +/// arg1 + arg2.len() as i32 +/// } +/// ``` +/// +/// or with a Result return type: +/// ```ignore +/// use hyperlight_guest_bin::guest_function; +/// use hyperlight_guest::bail; +/// #[guest_function] +/// fn my_guest_function(arg1: i32, arg2: String) -> Result { +/// bail!("An error occurred"); +/// } +/// ``` #[proc_macro_attribute] pub fn guest_function(attr: TokenStream, item: TokenStream) -> TokenStream { + // Obtain the crate name for hyperlight-guest-bin let crate_name = crate_name("hyperlight-guest-bin").expect("hyperlight-guest-bin must be a dependency"); let crate_name = match crate_name { @@ -51,15 +98,26 @@ pub fn guest_function(attr: TokenStream, item: TokenStream) -> TokenStream { } }; + // Parse the function definition that we will be working with, and + // early return if parsing as `ItemFn` fails. let fn_declaration = parse_macro_input!(item as ItemFn); + // Obtain the name of the function being decorated. let ident = fn_declaration.sig.ident.clone(); + // Determine the name used to register the function, either + // the provided name or the function's identifier. let exported_name = match parse_macro_input!(attr as NameArg) { NameArg::None => quote! { stringify!(#ident) }, NameArg::Name(name) => quote! { #name }, }; + // Small sanity checks to improve error messages. + // These checks are not strictly necessary, as the generated code + // would fail to compile anyway (due to the trait bounds of `register_fn`), + // but they provide better feedback to the user of the macro. + + // Check that there are no receiver arguments (i.e., `self`, `&self`, `Box`, etc). if let Some(syn::FnArg::Receiver(arg)) = fn_declaration.sig.inputs.first() { return Error::new( arg.span(), @@ -69,6 +127,7 @@ pub fn guest_function(attr: TokenStream, item: TokenStream) -> TokenStream { .into(); } + // Check that the function is not async. if fn_declaration.sig.asyncness.is_some() { return Error::new( fn_declaration.sig.asyncness.span(), @@ -78,10 +137,14 @@ pub fn guest_function(attr: TokenStream, item: TokenStream) -> TokenStream { .into(); } + // The generated code will replace the decorated code, so we need to + // include the original function declaration in the output. let output = quote! { #fn_declaration const _: () = { + // Add the function registration in the GUEST_FUNCTION_INIT distributed slice + // so that it can be registered at program initialization #[#crate_name::__private::linkme::distributed_slice(#crate_name::__private::GUEST_FUNCTION_INIT)] #[linkme(crate = #crate_name::__private::linkme)] static REGISTRATION: fn() = || { @@ -93,8 +156,44 @@ pub fn guest_function(attr: TokenStream, item: TokenStream) -> TokenStream { output.into() } +/// Attribute macro to mark a function as a host function. +/// This will generate a function that calls the host function with the same name. +/// +/// If a name is provided as an argument, that name will be used to call the host function. +/// Otherwise, the function's identifier will be used. +/// +/// The function arguments must be supported parameter types, and the return type must be +/// a supported return type or a `Result` with T being a supported +/// return type. +/// +/// # Panic +/// If the return type is not a Result, the generated function will panic if the host function +/// returns an error. +/// +/// # Example +/// ```ignore +/// use hyperlight_guest_bin::host_function; +/// #[host_function] +/// fn my_host_function(arg1: i32, arg2: String) -> i32; +/// ``` +/// +/// or with a custom name: +/// ```ignore +/// use hyperlight_guest_bin::host_function; +/// #[host_function("custom_name")] +/// fn my_host_function(arg1: i32, arg2: String) -> i32; +/// ``` +/// +/// or with a Result return type: +/// ```ignore +/// use hyperlight_guest_bin::host_function; +/// use hyperlight_guest::error::HyperlightGuestError; +/// #[host_function] +/// fn my_host_function(arg1: i32, arg2: String) -> Result; +/// ``` #[proc_macro_attribute] pub fn host_function(attr: TokenStream, item: TokenStream) -> TokenStream { + // Obtain the crate name for hyperlight-guest-bin let crate_name = crate_name("hyperlight-guest-bin").expect("hyperlight-guest-bin must be a dependency"); let crate_name = match crate_name { @@ -105,8 +204,13 @@ pub fn host_function(attr: TokenStream, item: TokenStream) -> TokenStream { } }; + // Parse the function declaration that we will be working with, and + // early return if parsing as `ForeignItemFn` fails. + // A function declaration without a body is a foreign item function, as that's what + // you would use when declaring an FFI function. let fn_declaration = parse_macro_input!(item as ForeignItemFn); + // Destructure the foreign item function to get its components. let ForeignItemFn { attrs, vis, @@ -114,16 +218,28 @@ pub fn host_function(attr: TokenStream, item: TokenStream) -> TokenStream { semi_token: _, } = fn_declaration; + // Obtain the name of the function being decorated. let ident = sig.ident.clone(); + // Determine the name used to call the host function, either + // the provided name or the function's identifier. let exported_name = match parse_macro_input!(attr as NameArg) { NameArg::None => quote! { stringify!(#ident) }, NameArg::Name(name) => quote! { #name }, }; + // Build the list of argument identifiers to pass to the call_host function. + // While doing that, also do some sanity checks to improve error messages. + // These checks are not strictly necessary, as the generated code would fail + // to compile anyway due to either: + // * the trait bounds of `call_host` + // * the generated code having invalid syntax + // but they provide better feedback to the user of the macro, especially in + // the case of invalid syntax. let mut args = vec![]; for arg in sig.inputs.iter() { match arg { + // Reject receiver arguments (i.e., `self`, `&self`, `Box`, etc). syn::FnArg::Receiver(_) => { return Error::new( arg.span(), @@ -133,6 +249,12 @@ pub fn host_function(attr: TokenStream, item: TokenStream) -> TokenStream { .into(); } syn::FnArg::Typed(arg) => { + // A typed argument: `name: Type` + // Technically, the `name` part can be any pattern, e.g., destructuring patterns + // like `(a, b): (i32, u64)`, but we only allow simple identifiers here + // to keep things simple. + + // Reject anything that is not a simple identifier. let Pat::Ident(pat) = *arg.pat.clone() else { return Error::new( arg.span(), @@ -142,6 +264,7 @@ pub fn host_function(attr: TokenStream, item: TokenStream) -> TokenStream { .into(); }; + // Reject any argument with attributes, e.g., `#[cfg(feature = "gdb")] name: Type` if !pat.attrs.is_empty() { return Error::new( arg.span(), @@ -151,6 +274,7 @@ pub fn host_function(attr: TokenStream, item: TokenStream) -> TokenStream { .into(); } + // Reject any argument passed by reference if pat.by_ref.is_some() { return Error::new( arg.span(), @@ -160,6 +284,7 @@ pub fn host_function(attr: TokenStream, item: TokenStream) -> TokenStream { .into(); } + // Reject any mutable argument, e.g., `mut name: Type` if pat.mutability.is_some() { return Error::new( arg.span(), @@ -169,6 +294,7 @@ pub fn host_function(attr: TokenStream, item: TokenStream) -> TokenStream { .into(); } + // Reject any sub-patterns if pat.subpat.is_some() { return Error::new( arg.span(), @@ -180,11 +306,14 @@ pub fn host_function(attr: TokenStream, item: TokenStream) -> TokenStream { let ident = pat.ident.clone(); + // All checks passed, add the identifier to the argument list. args.push(quote! { #ident }); } } } + // Determine the return type of the function. + // If the return type is not specified, it is `()`. let ret: proc_macro2::TokenStream = match &sig.output { syn::ReturnType::Default => quote! { quote! { () } }, syn::ReturnType::Type(_, ty) => { @@ -192,6 +321,8 @@ pub fn host_function(attr: TokenStream, item: TokenStream) -> TokenStream { } }; + // Take the parts of the function declaration and generate a function definition + // matching the provided declaration, but with a body that calls the host function. let output = quote! { #(#attrs)* #vis #sig { use #crate_name::__private::FromResult; diff --git a/src/hyperlight_host/src/func/host_functions.rs b/src/hyperlight_host/src/func/host_functions.rs index 11a36ff59..4f5e5b892 100644 --- a/src/hyperlight_host/src/func/host_functions.rs +++ b/src/hyperlight_host/src/func/host_functions.rs @@ -64,26 +64,26 @@ where Args: ParameterTuple, Output: SupportedReturnType, { - // This is a thin wrapper around a `Fn(Args) -> Result`. - // But unlike `Fn` which is a trait, this is a concrete type. + // This is a thin wrapper around a `Function`. + // But unlike `Function<..>` which is a trait, this is a concrete type. // This allows us to: // 1. Impose constraints on the function arguments and return type. // 2. Impose a single function signature. // - // This second point is important because the `Fn` trait is generic - // over the function arguments (with an associated return type). - // This means that a given type could implement `Fn` for multiple + // This second point is important because the `Function<..>` trait is generic + // over the function arguments and return type. + // This means that a given type could implement `Function<..>` for multiple // function signatures. // This means we can't do something like: // ```rust,ignore // impl SomeTrait for F // where - // F: Fn(Args) -> Result, + // F: Function, // { ... } // ``` - // because the concrete type F might implement `Fn` for multiple times, - // and that would means implementing `SomeTrait` multiple times for the - // same type. + // because the concrete type F might implement `Function<..>` for multiple + // arguments and return types, and that would means implementing `SomeTrait` + // multiple times for the same type F. // Use Arc in here instead of Box because it's useful in tests and // presumably in other places to be able to clone a HostFunction and diff --git a/src/hyperlight_host/src/sandbox/initialized_multi_use.rs b/src/hyperlight_host/src/sandbox/initialized_multi_use.rs index 6cfd2273b..390667d21 100644 --- a/src/hyperlight_host/src/sandbox/initialized_multi_use.rs +++ b/src/hyperlight_host/src/sandbox/initialized_multi_use.rs @@ -450,6 +450,8 @@ impl MultiUseSandbox { Output::TYPE, args.into_value(), ); + // Use the ? operator to allow converting any hyperlight_common::func::Error + // returned by from_value into a HyperlightError let ret = Output::from_value(ret?)?; Ok(ret) }) From 62895b27bdf38ba4c538af0008cf202da4b8fec0 Mon Sep 17 00:00:00 2001 From: Jorge Prendes Date: Wed, 3 Dec 2025 11:26:56 +0000 Subject: [PATCH 9/9] update README.md to use new macros Signed-off-by: Jorge Prendes --- README.md | 53 ++++++++++++++++------------------------------------- 1 file changed, 16 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 185cc5fdc..56cdda359 100644 --- a/README.md +++ b/README.md @@ -74,54 +74,33 @@ fn main() -> hyperlight_host::Result<()> { #![no_main] extern crate alloc; -use alloc::string::ToString; use alloc::vec::Vec; +use alloc::string::String; use hyperlight_common::flatbuffer_wrappers::function_call::FunctionCall; -use hyperlight_common::flatbuffer_wrappers::function_types::{ - ParameterType, ParameterValue, ReturnType, -}; use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode; -use hyperlight_common::flatbuffer_wrappers::util::get_flatbuffer_result; - -use hyperlight_guest::error::{HyperlightGuestError, Result}; -use hyperlight_guest_bin::guest_function::definition::GuestFunctionDefinition; -use hyperlight_guest_bin::guest_function::register::register_function; -use hyperlight_guest_bin::host_comm::call_host_function; - -fn print_output(function_call: &FunctionCall) -> Result> { - if let ParameterValue::String(message) = function_call.parameters.clone().unwrap()[0].clone() { - let result = call_host_function::( - "HostPrint", - Some(Vec::from(&[ParameterValue::String(message.to_string())])), - ReturnType::Int, - )?; - Ok(get_flatbuffer_result(result)) - } else { - Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionParameterTypeMismatch, - "Invalid parameters passed to simple_print_output".to_string(), - )) - } + +use hyperlight_guest::bail; +use hyperlight_guest::error::Result; +use hyperlight_guest_bin::{guest_function, host_function}; + +#[host_function("HostPrint")] +fn host_print(message: String) -> Result; + +#[guest_function("PrintOutput")] +fn print_output(message: String) -> Result { + let result = host_print(message)?; + Ok(result) } #[no_mangle] pub extern "C" fn hyperlight_main() { - let print_output_def = GuestFunctionDefinition::new( - "PrintOutput".to_string(), - Vec::from(&[ParameterType::String]), - ReturnType::Int, - print_output as usize, - ); - register_function(print_output_def); + // any initialization code goes here } #[no_mangle] pub fn guest_dispatch_function(function_call: FunctionCall) -> Result> { - let function_name = function_call.function_name.clone(); - return Err(HyperlightGuestError::new( - ErrorCode::GuestFunctionNotFound, - function_name, - )); + let function_name = function_call.function_name; + bail!(ErrorCode::GuestFunctionNotFound => "{function_name}"); } ```