Skip to content

Commit b45b363

Browse files
authored
feat: Add #[main] and #[dispatch] macros for type-safe guest entry points (#1384)
* make hyperlight_main and guest_dispatch_function optional Signed-off-by: Jorge Prendes <jorge.prendes@gmail.com> * use macros to define hyperlight_main and guest_dispatch_function Signed-off-by: Jorge Prendes <jorge.prendes@gmail.com> --------- Signed-off-by: Jorge Prendes <jorge.prendes@gmail.com>
1 parent 2fca7ae commit b45b363

File tree

4 files changed

+129
-6
lines changed

4 files changed

+129
-6
lines changed

src/hyperlight_guest_bin/src/guest_function/call.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,24 @@ use flatbuffers::FlatBufferBuilder;
2121
use hyperlight_common::flatbuffer_wrappers::function_call::{FunctionCall, FunctionCallType};
2222
use hyperlight_common::flatbuffer_wrappers::function_types::{FunctionCallResult, ParameterType};
2323
use hyperlight_common::flatbuffer_wrappers::guest_error::{ErrorCode, GuestError};
24+
use hyperlight_guest::bail;
2425
use hyperlight_guest::error::{HyperlightGuestError, Result};
2526
use tracing::instrument;
2627

2728
use crate::{GUEST_HANDLE, REGISTERED_GUEST_FUNCTIONS};
2829

30+
core::arch::global_asm!(
31+
".weak guest_dispatch_function",
32+
".set guest_dispatch_function, {}",
33+
sym guest_dispatch_function_default,
34+
);
35+
36+
#[tracing::instrument(skip_all, parent = tracing::Span::current(), level= "Trace")]
37+
fn guest_dispatch_function_default(function_call: FunctionCall) -> Result<Vec<u8>> {
38+
let name = &function_call.function_name;
39+
bail!(ErrorCode::GuestFunctionNotFound => "No handler found for function call: {name:#?}");
40+
}
41+
2942
#[instrument(skip_all, level = "Info")]
3043
pub(crate) fn call_guest_function(function_call: FunctionCall) -> Result<Vec<u8>> {
3144
// Validate this is a Guest Function Call

src/hyperlight_guest_bin/src/lib.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,17 @@ unsafe extern "C" {
197197
fn srand(seed: u32);
198198
}
199199

200+
#[tracing::instrument(skip_all, parent = tracing::Span::current(), level= "Trace")]
201+
extern "C" fn hyperlight_main_default() {
202+
// no-op
203+
}
204+
205+
core::arch::global_asm!(
206+
".weak hyperlight_main",
207+
".set hyperlight_main, {}",
208+
sym hyperlight_main_default,
209+
);
210+
200211
/// Architecture-nonspecific initialisation: set up the heap,
201212
/// coordinate some addresses and configuration with the host, and run
202213
/// user initialisation
@@ -286,6 +297,9 @@ pub(crate) extern "C" fn generic_init(
286297
#[cfg(feature = "macros")]
287298
#[doc(hidden)]
288299
pub mod __private {
300+
pub use alloc::vec::Vec;
301+
302+
pub use hyperlight_common::flatbuffer_wrappers::function_call::FunctionCall;
289303
pub use hyperlight_common::func::ResultType;
290304
pub use hyperlight_guest::error::HyperlightGuestError;
291305
pub use linkme;
@@ -299,7 +313,6 @@ pub mod __private {
299313
}
300314

301315
use alloc::string::String;
302-
use alloc::vec::Vec;
303316

304317
use hyperlight_common::for_each_return_type;
305318

@@ -327,6 +340,6 @@ pub mod __private {
327340
}
328341

329342
#[cfg(feature = "macros")]
330-
pub use hyperlight_guest_macro::{guest_function, host_function};
343+
pub use hyperlight_guest_macro::{dispatch, guest_function, host_function, main};
331344

332345
pub use crate::guest_function::definition::GuestFunc;

src/hyperlight_guest_macro/src/lib.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,103 @@ pub fn guest_function(attr: TokenStream, item: TokenStream) -> TokenStream {
156156
output.into()
157157
}
158158

159+
/// Attribute macro to mark a function as the main entry point for the guest.
160+
/// This will generate a function that is called by the host at program initialization.
161+
///
162+
/// # Example
163+
/// ```ignore
164+
/// use hyperlight_guest_bin::main;
165+
/// #[main]
166+
/// fn main() {
167+
/// // do some initialization work here, e.g., initialize global state, etc.
168+
/// }
169+
/// ```
170+
#[proc_macro_attribute]
171+
pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream {
172+
// Parse the function definition that we will be working with, and
173+
// early return if parsing as `ItemFn` fails.
174+
let fn_declaration = parse_macro_input!(item as ItemFn);
175+
176+
// Obtain the name of the function being decorated.
177+
let ident = fn_declaration.sig.ident.clone();
178+
179+
// The generated code will replace the decorated code, so we need to
180+
// include the original function declaration in the output.
181+
let output = quote! {
182+
#fn_declaration
183+
184+
const _: () = {
185+
mod wrapper {
186+
#[unsafe(no_mangle)]
187+
pub extern "C" fn hyperlight_main() {
188+
super::#ident()
189+
}
190+
}
191+
};
192+
};
193+
194+
output.into()
195+
}
196+
197+
/// Attribute macro to mark a function as the dispatch function for the guest.
198+
/// This is the function that will be called by the host when a function call is made
199+
/// to a function that is not registered with the host.
200+
///
201+
/// # Example
202+
/// ```ignore
203+
/// use hyperlight_guest_bin::dispatch;
204+
/// use hyperlight_guest::error::Result;
205+
/// use hyperlight_guest::bail;
206+
/// use hyperlight_common::flatbuffer_wrappers::function_call::FunctionCall;
207+
/// use hyperlight_common::flatbuffer_wrappers::util::get_flatbuffer_result;
208+
/// #[dispatch]
209+
/// fn dispatch(fc: FunctionCall) -> Result<Vec<u8>> {
210+
/// let name = &fc.function_name;
211+
/// if name == "greet" {
212+
/// return Ok(get_flatbuffer_result("Hello, world!"));
213+
/// }
214+
/// bail!("Unknown function: {name}");
215+
/// }
216+
/// ```
217+
#[proc_macro_attribute]
218+
pub fn dispatch(_attr: TokenStream, item: TokenStream) -> TokenStream {
219+
// Obtain the crate name for hyperlight-guest-bin
220+
let crate_name =
221+
crate_name("hyperlight-guest-bin").expect("hyperlight-guest-bin must be a dependency");
222+
let crate_name = match crate_name {
223+
FoundCrate::Itself => quote! {crate},
224+
FoundCrate::Name(name) => {
225+
let ident = syn::Ident::new(&name, proc_macro2::Span::call_site());
226+
quote! {::#ident}
227+
}
228+
};
229+
230+
// Parse the function definition that we will be working with, and
231+
// early return if parsing as `ItemFn` fails.
232+
let fn_declaration = parse_macro_input!(item as ItemFn);
233+
234+
// Obtain the name of the function being decorated.
235+
let ident = fn_declaration.sig.ident.clone();
236+
237+
// The generated code will replace the decorated code, so we need to
238+
// include the original function declaration in the output.
239+
let output = quote! {
240+
#fn_declaration
241+
242+
const _: () = {
243+
mod wrapper {
244+
use #crate_name::__private::{FunctionCall, HyperlightGuestError, Vec};
245+
#[unsafe(no_mangle)]
246+
pub fn guest_dispatch_function(function_call: FunctionCall) -> ::core::result::Result<Vec<u8>, HyperlightGuestError> {
247+
super::#ident(function_call)
248+
}
249+
}
250+
};
251+
};
252+
253+
output.into()
254+
}
255+
159256
/// Attribute macro to mark a function as a host function.
160257
/// This will generate a function that calls the host function with the same name.
161258
///

src/tests/rust_guests/simpleguest/src/main.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -855,9 +855,9 @@ fn call_host_expect_error(hostfuncname: String) -> Result<()> {
855855
Ok(())
856856
}
857857

858-
#[no_mangle]
858+
#[hyperlight_guest_bin::main]
859859
#[instrument(skip_all, parent = Span::current(), level= "Trace")]
860-
pub extern "C" fn hyperlight_main() {
860+
fn main() {
861861
let print_output_def = GuestFunctionDefinition::<GuestFunc>::new(
862862
"PrintOutputWithHostPrint".to_string(),
863863
Vec::from(&[ParameterType::String]),
@@ -1067,9 +1067,9 @@ fn fuzz_host_function(func: FunctionCall) -> Result<Vec<u8>> {
10671067
}
10681068
}
10691069

1070-
#[no_mangle]
1070+
#[hyperlight_guest_bin::dispatch]
10711071
#[instrument(skip_all, parent = Span::current(), level= "Trace")]
1072-
pub fn guest_dispatch_function(function_call: FunctionCall) -> Result<Vec<u8>> {
1072+
fn dispatch(function_call: FunctionCall) -> Result<Vec<u8>> {
10731073
// This test checks the stack behavior of the input/output buffer
10741074
// by calling the host before serializing the function call.
10751075
// If the stack is not working correctly, the input or output buffer will be

0 commit comments

Comments
 (0)