Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Prerelease] - Unreleased

### Changed
* **Breaking:** `GuestFunctionDefinition::new` now takes a typed function pointer instead of `usize` by @ludfjig in https://github.com/hyperlight-dev/hyperlight/pull/1241

## [v0.12.0] - 2025-12-09

### Fixed
Expand Down
4 changes: 2 additions & 2 deletions src/hyperlight_component_util/src/guest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ fn emit_export_extern_decl<'a, 'b, 'c>(
let marshal_result = emit_hl_marshal_result(s, ret.clone(), &ft.result);
let trait_path = s.cur_trait_path();
quote! {
fn #n<T: Guest>(fc: &::hyperlight_common::flatbuffer_wrappers::function_call::FunctionCall) -> ::hyperlight_guest::error::Result<::alloc::vec::Vec<u8>> {
fn #n<T: Guest>(fc: ::hyperlight_common::flatbuffer_wrappers::function_call::FunctionCall) -> ::hyperlight_guest::error::Result<::alloc::vec::Vec<u8>> {
<T as Guest>::with_guest_state(|state| {
#(#pds)*
#(#get_instance)*
Expand All @@ -223,7 +223,7 @@ fn emit_export_extern_decl<'a, 'b, 'c>(
::alloc::string::ToString::to_string(#fname),
::alloc::vec![#(#pts),*],
::hyperlight_common::flatbuffer_wrappers::function_types::ReturnType::VecBytes,
#n::<T> as usize
#n::<T>
)
);
}
Expand Down
12 changes: 3 additions & 9 deletions src/hyperlight_guest_bin/src/guest_function/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ use tracing::{Span, instrument};

use crate::{GUEST_HANDLE, REGISTERED_GUEST_FUNCTIONS};

type GuestFunc = fn(&FunctionCall) -> Result<Vec<u8>>;

#[instrument(skip_all, parent = Span::current(), level= "Trace")]
pub(crate) fn call_guest_function(function_call: FunctionCall) -> Result<Vec<u8>> {
// Validate this is a Guest Function Call
Expand Down Expand Up @@ -59,14 +57,10 @@ pub(crate) fn call_guest_function(function_call: FunctionCall) -> Result<Vec<u8>
// Verify that the function call has the correct parameter types and length.
registered_function_definition.verify_parameters(&function_call_parameter_types)?;

let p_function = unsafe {
let function_pointer = registered_function_definition.function_pointer;
core::mem::transmute::<usize, GuestFunc>(function_pointer)
};

p_function(&function_call)
(registered_function_definition.function_pointer)(function_call)
} else {
// The given function is not registered. The guest should implement a function called guest_dispatch_function to handle this.
// The given function is not registered. The guest should implement a function called
// guest_dispatch_function to handle this.

// TODO: ideally we would define a default implementation of this with weak linkage so the guest is not required
// to implement the function but its seems that weak linkage is an unstable feature so for now its probably better
Expand Down
51 changes: 31 additions & 20 deletions src/hyperlight_guest_bin/src/guest_function/definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,26 @@ use hyperlight_common::func::{
};
use hyperlight_guest::error::{HyperlightGuestError, Result};

/// The definition of a function exposed from the guest to the host
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GuestFunctionDefinition {
/// The function pointer type for Rust guest functions.
pub type GuestFunc = fn(FunctionCall) -> Result<Vec<u8>>;

/// The definition of a function exposed from the guest to the host.
///
/// The type parameter `F` is the function pointer type. For Rust guests this
/// is [`GuestFunc`]; the C API uses its own `CGuestFunc` type.
#[derive(Debug, Clone)]
pub struct GuestFunctionDefinition<F: Copy> {
/// The function name
pub function_name: String,
/// The type of the parameter values for the host function call.
pub parameter_types: Vec<ParameterType>,
/// The type of the return value from the host function call
pub return_type: ReturnType,
/// The function pointer to the guest function
pub function_pointer: usize,
/// The function pointer to the guest function.
pub function_pointer: F,
}

/// Trait for functions that can be converted to a `fn(&FunctionCall) -> Result<Vec<u8>>`
/// Trait for functions that can be converted to a `fn(FunctionCall) -> Result<Vec<u8>>`
#[doc(hidden)]
pub trait IntoGuestFunction<Output, Args>
where
Expand All @@ -53,11 +59,11 @@ where
#[doc(hidden)]
const ASSERT_ZERO_SIZED: ();

/// Convert the function into a `fn(&FunctionCall) -> Result<Vec<u8>>`
fn into_guest_function(self) -> fn(&FunctionCall) -> Result<Vec<u8>>;
/// Convert the function into a `fn(FunctionCall) -> Result<Vec<u8>>`
fn into_guest_function(self) -> fn(FunctionCall) -> Result<Vec<u8>>;
}

/// Trait for functions that can be converted to a `GuestFunctionDefinition`
/// Trait for functions that can be converted to a `GuestFunctionDefinition<GuestFunc>`
pub trait AsGuestFunctionDefinition<Output, Args>
where
Self: Function<Output, Args, HyperlightGuestError>,
Expand All @@ -66,7 +72,10 @@ where
Args: ParameterTuple,
{
/// Get the `GuestFunctionDefinition` for this function
fn as_guest_function_definition(&self, name: impl Into<String>) -> GuestFunctionDefinition;
fn as_guest_function_definition(
&self,
name: impl Into<String>,
) -> GuestFunctionDefinition<GuestFunc>;
}

fn into_flatbuffer_result(value: ReturnValue) -> Vec<u8> {
Expand Down Expand Up @@ -124,14 +133,14 @@ macro_rules! impl_host_function {
assert!(core::mem::size_of::<Self>() == 0)
};

fn into_guest_function(self) -> fn(&FunctionCall) -> Result<Vec<u8>> {
|fc: &FunctionCall| {
fn into_guest_function(self) -> fn(FunctionCall) -> Result<Vec<u8>> {
|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::<F>() };
let params = fc.parameters.clone().unwrap_or_default();
let params = fc.parameters.unwrap_or_default();
let params = <($($P,)*) as ParameterTuple>::from_value(params)?;
let result = Function::<R::ReturnType, ($($P,)*), HyperlightGuestError>::call(&this, params)?;
Ok(into_flatbuffer_result(result.into_value()))
Expand All @@ -147,11 +156,13 @@ where
Args: ParameterTuple,
Output: SupportedReturnType,
{
fn as_guest_function_definition(&self, name: impl Into<String>) -> GuestFunctionDefinition {
fn as_guest_function_definition(
&self,
name: impl Into<String>,
) -> GuestFunctionDefinition<GuestFunc> {
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(),
Expand All @@ -164,13 +175,13 @@ where

for_each_tuple!(impl_host_function);

impl GuestFunctionDefinition {
impl<F: Copy> GuestFunctionDefinition<F> {
/// Create a new `GuestFunctionDefinition`.
pub fn new(
function_name: String,
parameter_types: Vec<ParameterType>,
return_type: ReturnType,
function_pointer: usize,
function_pointer: F,
) -> Self {
Self {
function_name,
Expand All @@ -180,12 +191,12 @@ impl GuestFunctionDefinition {
}
}

/// Create a new `GuestFunctionDefinition` from a function that implements
/// `AsGuestFunctionDefinition`.
/// Create a new `GuestFunctionDefinition<GuestFunc>` from a function that
/// implements `AsGuestFunctionDefinition`.
pub fn from_fn<Output, Args>(
function_name: String,
function: impl AsGuestFunctionDefinition<Output, Args>,
) -> Self
) -> GuestFunctionDefinition<GuestFunc>
where
Args: ParameterTuple,
Output: SupportedReturnType,
Expand Down
38 changes: 24 additions & 14 deletions src/hyperlight_guest_bin/src/guest_function/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,27 @@ use alloc::string::String;

use hyperlight_common::func::{ParameterTuple, SupportedReturnType};

use super::definition::GuestFunctionDefinition;
use super::definition::{GuestFunc, 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)]
pub struct GuestFunctionRegister {
#[derive(Debug, Clone)]
pub struct GuestFunctionRegister<F: Copy> {
/// Currently registered guest functions
guest_functions: BTreeMap<String, GuestFunctionDefinition>,
guest_functions: BTreeMap<String, GuestFunctionDefinition<F>>,
}

impl GuestFunctionRegister {
/// Create a new `GuestFunctionDetails`.
impl<F: Copy> Default for GuestFunctionRegister<F> {
fn default() -> Self {
Self {
guest_functions: BTreeMap::new(),
}
}
}

impl<F: Copy> GuestFunctionRegister<F> {
/// Create a new `GuestFunctionRegister`.
pub const fn new() -> Self {
Self {
guest_functions: BTreeMap::new(),
Expand All @@ -44,12 +52,19 @@ impl GuestFunctionRegister {
/// otherwise the previous `GuestFunctionDefinition` is returned.
pub fn register(
&mut self,
guest_function: GuestFunctionDefinition,
) -> Option<GuestFunctionDefinition> {
guest_function: GuestFunctionDefinition<F>,
) -> Option<GuestFunctionDefinition<F>> {
self.guest_functions
.insert(guest_function.function_name.clone(), guest_function)
}

/// Gets a `GuestFunctionDefinition` by its `name` field.
pub fn get(&self, function_name: &str) -> Option<&GuestFunctionDefinition<F>> {
self.guest_functions.get(function_name)
}
}

impl GuestFunctionRegister<GuestFunc> {
pub fn register_fn<Output, Args>(
&mut self,
name: impl Into<String>,
Expand All @@ -61,14 +76,9 @@ impl GuestFunctionRegister {
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)
}
}

pub fn register_function(function_definition: GuestFunctionDefinition) {
pub fn register_function(function_definition: GuestFunctionDefinition<GuestFunc>) {
unsafe {
// This is currently safe, because we are single threaded, but we
// should find a better way to do this, see issue #808
Expand Down
6 changes: 3 additions & 3 deletions src/hyperlight_guest_bin/src/host_comm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,12 @@ pub fn read_n_bytes_from_user_memory(num: u64) -> Result<Vec<u8>> {
///
/// This function requires memory to be setup to be used. In particular, the
/// existence of the input and output memory regions.
pub fn print_output_with_host_print(function_call: &FunctionCall) -> Result<Vec<u8>> {
pub fn print_output_with_host_print(function_call: FunctionCall) -> Result<Vec<u8>> {
let handle = unsafe { GUEST_HANDLE };
if let ParameterValue::String(message) = function_call.parameters.clone().unwrap()[0].clone() {
if let ParameterValue::String(message) = function_call.parameters.unwrap().remove(0) {
let res = handle.call_host_function::<i32>(
"HostPrint",
Some(Vec::from(&[ParameterValue::String(message.to_string())])),
Some(Vec::from(&[ParameterValue::String(message)])),
ReturnType::Int,
)?;

Expand Down
4 changes: 3 additions & 1 deletion src/hyperlight_guest_bin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ pub(crate) static HEAP_ALLOCATOR: ProfiledLockedHeap<32> =
ProfiledLockedHeap(LockedHeap::<32>::empty());

pub static mut GUEST_HANDLE: GuestHandle = GuestHandle::new();
pub(crate) static mut REGISTERED_GUEST_FUNCTIONS: GuestFunctionRegister =
pub(crate) static mut REGISTERED_GUEST_FUNCTIONS: GuestFunctionRegister<GuestFunc> =
GuestFunctionRegister::new();

/// The size of one page in the host OS, which may have some impacts
Expand Down Expand Up @@ -278,3 +278,5 @@ pub mod __private {

#[cfg(feature = "macros")]
pub use hyperlight_guest_macro::{guest_function, host_function};

pub use crate::guest_function::definition::GuestFunc;
12 changes: 4 additions & 8 deletions src/hyperlight_guest_capi/src/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ use alloc::boxed::Box;
use alloc::slice;
use alloc::vec::Vec;
use core::ffi::{CStr, c_char};
use core::mem;

use hyperlight_common::flatbuffer_wrappers::function_call::FunctionCall;
use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterType, ReturnType};
Expand All @@ -29,7 +28,8 @@ use hyperlight_guest_bin::guest_function::register::GuestFunctionRegister;
use hyperlight_guest_bin::host_comm::call_host_function_without_returning_result;

use crate::types::{FfiFunctionCall, FfiVec};
static mut REGISTERED_C_GUEST_FUNCTIONS: GuestFunctionRegister = GuestFunctionRegister::new();
static mut REGISTERED_C_GUEST_FUNCTIONS: GuestFunctionRegister<CGuestFunc> =
GuestFunctionRegister::new();

type CGuestFunc = extern "C" fn(&FfiFunctionCall) -> Box<FfiVec>;

Expand All @@ -55,10 +55,7 @@ pub fn guest_dispatch_function(function_call: FunctionCall) -> Result<Vec<u8>> {
registered_func.verify_parameters(&function_call_parameter_types)?;

let ffi_func_call = FfiFunctionCall::from_function_call(function_call)?;

let guest_func =
unsafe { mem::transmute::<usize, CGuestFunc>(registered_func.function_pointer) };
let function_result = guest_func(&ffi_func_call);
let function_result = (registered_func.function_pointer)(&ffi_func_call);

unsafe { Ok(FfiVec::into_vec(*function_result)) }
} else {
Expand Down Expand Up @@ -94,8 +91,7 @@ pub extern "C" fn hl_register_function_definition(

let func_params = unsafe { slice::from_raw_parts(params_type, param_no).to_vec() };

let func_def =
GuestFunctionDefinition::new(func_name, func_params, return_type, func_ptr as usize);
let func_def = GuestFunctionDefinition::new(func_name, func_params, return_type, func_ptr);

// Use &raw mut to get a mutable raw pointer, then dereference it
// this is to avoid the clippy warning "shared reference to mutable static"
Expand Down
6 changes: 3 additions & 3 deletions src/tests/rust_guests/simpleguest/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ use hyperlight_common::flatbuffer_wrappers::util::get_flatbuffer_result;
use hyperlight_guest::error::{HyperlightGuestError, Result};
use hyperlight_guest::exit::{abort_with_code, abort_with_code_and_message};
use hyperlight_guest_bin::exception::arch::{Context, ExceptionInfo};
use hyperlight_guest_bin::guest_function::definition::GuestFunctionDefinition;
use hyperlight_guest_bin::guest_function::definition::{GuestFunc, GuestFunctionDefinition};
use hyperlight_guest_bin::guest_function::register::register_function;
use hyperlight_guest_bin::host_comm::{
call_host_function, call_host_function_without_returning_result, get_host_return_value_raw,
Expand Down Expand Up @@ -652,11 +652,11 @@ fn call_host_expect_error(hostfuncname: String) -> Result<()> {
#[no_mangle]
#[instrument(skip_all, parent = Span::current(), level= "Trace")]
pub extern "C" fn hyperlight_main() {
let print_output_def = GuestFunctionDefinition::new(
let print_output_def = GuestFunctionDefinition::<GuestFunc>::new(
"PrintOutputWithHostPrint".to_string(),
Vec::from(&[ParameterType::String]),
ReturnType::Int,
print_output_with_host_print as usize,
print_output_with_host_print,
);
register_function(print_output_def);
}
Expand Down