hyperlight-guest-bin 0.17.0

This crate provides the opinionated bits of the guest library, such as the panic handler, the entry point, the guest logger, the exception handling logic, and third-party code used by our C-API needed to build a native hyperlight-guest binary.
Documentation
// SPDX-License-Identifier: Apache-2.0
// Copyright 2025 The Hyperlight Authors.

use alloc::string::String;
use alloc::vec;
use core::sync::atomic::{AtomicU64, Ordering};

use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterValue, ReturnType};

use crate::host_comm::call_host_function;
use crate::libc::{
    CLOCK_MONOTONIC, CLOCK_REALTIME, EBADF, EINVAL, EIO, ENOSYS, c_int, c_long, c_void, clockid_t,
    errno, timespec,
};

fn set_errno(val: u32) {
    // SAFETY: single-threaded guest, errno is a global int (__GLOBAL_ERRNO)
    // Bindgen uses u32 for the errno definitions, so we convert it to the expected c_int type here
    unsafe { errno = val as _ };
}

static CURRENT_TIME: AtomicU64 = AtomicU64::new(0);

/// Returns a synthetic monotonically-increasing time starting at Unix epoch
/// increasing 1s each call.
fn current_time() -> (u64, u64) {
    let call_count = CURRENT_TIME.fetch_add(1, Ordering::Relaxed) + 1;
    (call_count, 0)
}

#[unsafe(no_mangle)]
extern "C" fn read(fd: c_int, buf: *mut c_void, count: usize) -> isize {
    if buf.is_null() && count > 0 {
        set_errno(EINVAL);
        return -1;
    }

    if fd != 0 {
        set_errno(EBADF);
        return -1;
    }

    0
}

#[unsafe(no_mangle)]
extern "C" fn write(fd: c_int, buf: *const c_void, count: usize) -> isize {
    if buf.is_null() && count > 0 {
        set_errno(EINVAL);
        return -1;
    }

    if fd != 1 && fd != 2 {
        set_errno(EBADF);
        return -1;
    }

    let slice = unsafe { core::slice::from_raw_parts(buf as *const u8, count) };
    let s = String::from_utf8_lossy(slice);
    match call_host_function::<i32>(
        "HostPrint",
        Some(vec![ParameterValue::String(s.into_owned())]),
        ReturnType::Int,
    ) {
        Ok(_) => count as isize,
        Err(_) => {
            set_errno(EIO);
            -1
        }
    }
}

#[unsafe(no_mangle)]
extern "C" fn clock_gettime(clk_id: clockid_t, tp: *mut timespec) -> c_int {
    // The libc bindings generated by bindgen are u32, but we expect them to be clockid_t,
    // so we convert the constants to the expected type here for comparison.
    const CLOCK_ID_REALTIME: clockid_t = CLOCK_REALTIME as _;
    const CLOCK_ID_MONOTONIC: clockid_t = CLOCK_MONOTONIC as _;

    if tp.is_null() {
        set_errno(EINVAL);
        return -1;
    }

    match clk_id {
        CLOCK_ID_REALTIME | CLOCK_ID_MONOTONIC => {
            let (secs, nanos) = current_time();
            unsafe {
                (*tp).tv_sec = secs as c_long;
                (*tp).tv_nsec = nanos as c_long;
            }
            0
        }
        _ => {
            set_errno(EINVAL);
            -1
        }
    }
}

#[unsafe(no_mangle)]
extern "C" fn _exit(ec: c_int) -> ! {
    hyperlight_guest::exit::abort_with_code(&[ec as u8]);
}

#[unsafe(no_mangle)]
extern "C" fn lseek(_fd: c_int, _offset: c_long, _whence: c_int) -> c_long {
    set_errno(ENOSYS);
    -1
}

#[unsafe(no_mangle)]
extern "C" fn close(_fd: c_int) -> c_int {
    0
}