This crate provides a C standard library implementation for Hyperlight guest binaries based on picolibc. It compiles picolibc from source and generates Rust bindings to the C library types and functions using bindgen.
hyperlight-libc is designed to be used together with hyperlight-guest-bin to build Hyperlight
guest binaries. When the libc feature is enabled on hyperlight-guest-bin (enabled by default),
this crate is automatically included as a dependency.
The crate:
- Builds picolibc from source during compilation
- Generates Rust bindings to libc types (e.g.,
timespec,timeval,clockid_t) and constants (e.g.,EINVAL,EBADF,CLOCK_REALTIME) - Exports the include directory via cargo metadata for downstream C compilation needs
Picolibc is configured for Hyperlight's micro-VM environment with:
- Single-threaded: No locking or TLS support
- Global errno: Uses a single global
errnovariable - Tiny stdio: Minimal stdio implementation
- No malloc: Memory allocation is handled by the Rust global allocator
- IEEE math: Math library without errno side effects
The configuration is defined in include/picolibc.h.
When using hyperlight-guest-bin with the libc feature enabled, the bindings are re-exported as
hyperlight_guest_bin::libc:
use hyperlight_guest_bin::libc::{errno, timespec, EINVAL, CLOCK_REALTIME};Picolibc expects certain POSIX functions to be available at link time. When using this crate,
downstream code must provide implementations for these functions. The hyperlight-guest-bin crate
provides these in src/libc_stubs.rs, which can serve as a reference.
Required stubs include:
| Function | Purpose |
|---|---|
read |
Read from file descriptor (e.g., stdin support) |
write |
Write to file descriptor (e.g., stdout/stderr for printf) |
clock_gettime |
Get current time |
_exit |
Terminate the program |
lseek |
Seek in file (can return ENOSYS for basic stdio support) |
close |
Close file descriptor |
Here's an example of how to implement the write stub that delegates to a host function:
use alloc::string::String;
use core::ffi::{c_int, c_void};
use hyperlight_guest_bin::host_function;
use hyperlight_guest_bin::libc::{errno, EINVAL, EBADF, EIO};
#[host_function("HostPrint")]
fn host_print(message: String) -> i32;
#[unsafe(no_mangle)]
extern "C" fn write(fd: c_int, buf: *const c_void, count: usize) -> isize {
// Validate input buffer
if buf.is_null() && count > 0 {
unsafe { errno = EINVAL as _ };
return -1;
}
// Only support stdout (1) and stderr (2)
if fd != 1 && fd != 2 {
unsafe { errno = EBADF as _ };
return -1;
}
// Read the buffer and convert to a String
let buf = unsafe { core::slice::from_raw_parts(buf as *const u8, count) };
let text = String::from_utf8_lossy(buf).into_owned();
// Delegate to the host function
host_print(text);
count as isize
}The picolibc source is vendored as a git submodule at third_party/picolibc, pointing to
picolibc-bsd - a redistribution of picolibc with
all copyleft-licensed files (GPL/AGPL) removed. Only BSD/MIT/permissive-licensed source files are
present.
See NOTICE.txt in the picolibc-bsd repository root for full licensing details.
For detailed information about the picolibc integration, including how to update picolibc to a new version, see docs/picolibc.md.