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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
* `Sandbox` lives in the private `sandbox::initialized` module and is reached
through `hyperlight_host::Sandbox` or `hyperlight_host::sandbox::Sandbox`.
`sandbox::initialized_multi_use` remains as a deprecated public path.
* C guests may omit `c_guest_dispatch_function` when they do not need custom
fallback dispatch.
* Support overriding the guest log level when building or restoring initialized
snapshots.
* `Snapshot::save` now writes the guest memory blob sparsely, skipping all-zero
Expand Down
24 changes: 20 additions & 4 deletions docs/how-to-build-a-hyperlight-guest-binary.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,16 +101,19 @@ The macros are optional. A guest can define the underlying symbols directly,
which is useful for advanced setup work or custom dispatch logic (for example
the WIT-based guests in `src/tests/rust_guests/witguest`).

The host expects two guest symbols:
The runtime provides weak default definitions for two guest symbols. A guest
can override either definition:

* `pub extern "C" fn hyperlight_main()` runs once at guest startup. Use it to
register functions or initialize global state.
* `pub extern "Rust" fn guest_dispatch_function(function_call: FunctionCall) -> Result<Vec<u8>, HyperlightGuestError>`
is invoked when the host calls a function name that is not registered.
* `pub extern "Rust" fn guest_dispatch_function(function_call: FunctionCall) -> Result<Vec<u8>>`
handles calls that do not match a registered guest function. Its default
implementation returns `GuestFunctionNotFound`.

`hyperlight-guest-bin` exposes `#[main]` and `#[dispatch]` macros that generate
these symbols from a regular Rust function, so most "advanced" guests still use
the macros rather than writing the raw `extern "C"` items themselves.
the macros rather than defining the symbols directly. A generated or direct
definition overrides the runtime's weak default.

If you mix the manual form with `#[guest_function]`, registrations from the
macro still happen automatically. Your `hyperlight_main` only needs to do
Expand Down Expand Up @@ -143,6 +146,19 @@ C API library.
The `hyperlight_guest.h` header contains the corresponding APIs to register
guest functions and call host functions from within the guest.

Registered guest functions are checked first. When no registered function
matches, the runtime calls:

```c
hl_ReturnValue *c_guest_dispatch_function(const hl_FunctionCall *function_call);
```

The C API provides a weak default implementation that returns `NULL`, which the
runtime reports as `GuestFunctionNotFound`. Define this function only when the
guest needs custom dynamic dispatch. The guest definition overrides the weak
default and must return either `NULL` or a value created by an
`hl_result_from_*` function.

See [src/tests/c_guests/c_simpleguest/main.c](../src/tests/c_guests/c_simpleguest/main.c)
for a complete example.

Expand Down
13 changes: 13 additions & 0 deletions src/hyperlight_guest_capi/src/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@ static mut LAST_HOST_RESULT: Option<Result<ReturnValue>> = None;

type CGuestFunc = extern "C" fn(&FfiFunctionCall) -> *mut FfiReturnValue;

core::arch::global_asm!(
".weak c_guest_dispatch_function",
".set c_guest_dispatch_function, {}",
sym c_guest_dispatch_function_default,
);

extern "C" fn c_guest_dispatch_function_default(
_function_call: &FfiFunctionCall,
) -> *mut FfiReturnValue {
// Null surfaces as ErrorCode::GuestFunctionNotFound
core::ptr::null_mut()
}

unsafe extern "C" {
// The C guest supplies this fallback. Non-null results come from hl_result_from_*.
fn c_guest_dispatch_function(function_call: &FfiFunctionCall) -> *mut FfiReturnValue;
Expand Down
Loading