Skip to content
Open
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
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export HYPERLIGHT_CFLAGS := \
export CMAKE_GENERATOR := if os() == "windows" { "Ninja" } else { "" }

ensure-tools:
cargo install cargo-hyperlight --locked --version 0.1.3
cargo install cargo-hyperlight --locked --version 0.1.7

# Check if npm is installed, install automatically if missing (Linux)
[private]
Expand All @@ -39,8 +39,7 @@ check-license-headers:
./dev/check-license-headers.sh

clippy target=default-target features="": (ensure-tools)
cd src/hyperlight-js-runtime && \
cargo hyperlight clippy \
cargo hyperlight clippy -p hyperlight-js-runtime \
--profile={{ if target == "debug" {"dev"} else { target } }} \
-- -D warnings
cargo clippy --all-targets \
Expand Down
5 changes: 3 additions & 2 deletions src/hyperlight-js-runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,18 @@ tracing = { version = "0.1.44", default-features = false, features = ["log","att
hyperlight-common = { workspace = true, default-features = false }
hyperlight-guest = { workspace = true }
hyperlight-guest-bin = { workspace = true }
chrono = { version = "0.4", default-features = false }

[target.'cfg(not(hyperlight))'.dependencies]
clap = { version = "4.5", features = ["derive"] }
libc = "0.2"

[target.'cfg(not(hyperlight))'.dev-dependencies]
escargot = "0.5"
tempfile = "3.26"

[build-dependencies]
cc = "1.2"
bindgen = "0.72"
glob = "0.3"

[features]
default = []
Expand Down
38 changes: 29 additions & 9 deletions src/hyperlight-js-runtime/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,36 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
fn main() {
if std::env::var_os("CARGO_CFG_HYPERLIGHT").is_none() {
return;
}

let files = ["stubs/clock.c", "stubs/localtime.c"];
use std::env;
use std::path::PathBuf;

for file in files {
println!("cargo:rerun-if-changed={}", file);
}
use bindgen::RustEdition::Edition2024;

cc::Build::new().files(files).compile("stubs");
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut bindings = bindgen::builder()
.use_core()
.wrap_unsafe_ops(true)
.rust_edition(Edition2024)
.clang_arg("-D_POSIX_MONOTONIC_CLOCK=1")
.clang_arg("-D_POSIX_C_SOURCE=200809L");

bindings = bindings.header_contents(
"libc_stubs.h",
"
#pragma once
#include <errno.h>
#include <stdio.h>
#include <time.h>
",
);

println!("cargo:rerun-if-changed=stubs/include/stdio.h");
println!("cargo:rerun-if-changed=stubs/include/time.h");

// Write the generated bindings to an output file.
let out_path = PathBuf::from(env::var("OUT_DIR")?).join("libc_stubs.rs");
bindings.generate()?.write_to_file(out_path)?;

Ok(())
}
1 change: 1 addition & 0 deletions src/hyperlight-js-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ extern crate alloc;
mod globals;
pub mod host;
mod host_fn;
mod libc;
mod modules;
pub(crate) mod utils;

Expand Down
33 changes: 33 additions & 0 deletions src/hyperlight-js-runtime/src/libc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
Copyright 2026 The Hyperlight Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
mod bindings {
#![allow(
non_camel_case_types,
non_snake_case,
non_upper_case_globals,
dead_code,
unnecessary_transmutes,
clippy::upper_case_acronyms,
clippy::ptr_offset_with_cast
)]
include!(concat!(env!("OUT_DIR"), "/libc_stubs.rs"));
}

#[allow(unused_imports)] // not used in native.rs
pub(crate) use core::ffi::*;

#[allow(unused_imports)] // not used in native.rs
pub(crate) use bindings::*;
2 changes: 2 additions & 0 deletions src/hyperlight-js-runtime/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ limitations under the License.
#![cfg_attr(hyperlight, no_std)]
#![cfg_attr(hyperlight, no_main)]

mod libc;

#[cfg(hyperlight)]
include!("main/hyperlight.rs");

Expand Down
49 changes: 2 additions & 47 deletions src/hyperlight-js-runtime/src/main/hyperlight.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ limitations under the License.
*/
extern crate alloc;

use core::ffi::*;
use core::time::Duration;

use alloc::format;
use alloc::string::String;
use alloc::vec::Vec;
Expand All @@ -32,6 +29,8 @@ use hyperlight_guest_bin::{guest_function, host_function};
use spin::Mutex;
use tracing::instrument;

mod stubs;

struct Host;

pub trait CatchGuestErrorExt {
Expand Down Expand Up @@ -126,11 +125,6 @@ fn register_host_modules(host_modules_json: String) -> Result<()> {
Ok(())
}

#[unsafe(no_mangle)]
pub extern "C" fn srand(_seed: u32) {
// No-op
}

#[unsafe(no_mangle)]
pub fn guest_dispatch_function(function_call: FunctionCall) -> Result<Vec<u8>> {
let params = function_call.parameters.unwrap_or_default();
Expand All @@ -139,42 +133,3 @@ pub fn guest_dispatch_function(function_call: FunctionCall) -> Result<Vec<u8>> {
let result = RUNTIME.lock().run_handler(function_name, event, run_gc)?;
Ok(get_flatbuffer_result(result.as_str()))
}

/// # Safety
/// This function is used by the C code to get the current time in seconds and nanoseconds.
/// `ts` must be a valid pointer to an array of two `u64` values.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn _current_time(ts: *mut u64) -> c_int {
#[host_function("CurrentTimeMicros")]
fn current_time_micros() -> Result<u64>;

let dur = current_time_micros().unwrap_or(1609459200u64 * 1_000_000u64);
let dur = Duration::from_micros(dur);

let ts = unsafe { core::slice::from_raw_parts_mut(ts, 2) };
ts[0] = dur.as_secs();
ts[1] = dur.subsec_nanos() as u64;

0
}

#[unsafe(no_mangle)]
pub extern "C" fn putchar(c: c_int) -> c_int {
unsafe { hyperlight_guest_bin::host_comm::_putchar(c as c_char) };
if c == '\n' as c_int {
// force a flush of the internal buffer in the hyperlight putchar implementation
unsafe { hyperlight_guest_bin::host_comm::_putchar(0) };
}
(c as c_char) as c_int
}

#[unsafe(no_mangle)]
pub extern "C" fn fflush(f: *mut c_void) -> c_int {
if !f.is_null() {
// we only support flushing all streams, and stdout is our only stream
return -1;
}
// flush stdout
putchar('\0' as _);
0
}
57 changes: 57 additions & 0 deletions src/hyperlight-js-runtime/src/main/stubs/clock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
Copyright 2026 The Hyperlight Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
use hyperlight_guest::error::Result;
use hyperlight_guest_bin::host_function;

use crate::libc;

fn micros_since_epoch() -> u64 {
#[host_function("CurrentTimeMicros")]
fn current_time_micros() -> Result<u64>;

current_time_micros().unwrap_or(1609459200u64 * 1_000_000u64)
}

#[unsafe(no_mangle)]
extern "C" fn clock_gettime(clk_id: libc::clockid_t, ts: *mut libc::timespec) -> libc::c_int {
const CLOCK_REALTIME: libc::clockid_t = libc::CLOCK_REALTIME as libc::clockid_t;
const CLOCK_MONOTONIC: libc::clockid_t = libc::CLOCK_MONOTONIC as libc::clockid_t;

if clk_id != CLOCK_REALTIME && clk_id != CLOCK_MONOTONIC {
unsafe { libc::__errno_location().write(libc::EINVAL as _) };
return -1;
}
let micros = micros_since_epoch();
unsafe {
ts.write(libc::timespec {
tv_sec: (micros / 1_000_000) as _,
tv_nsec: ((micros % 1_000_000) * 1000) as _,
})
};
0
}

#[unsafe(no_mangle)]
extern "C" fn gettimeofday(tp: *mut libc::timeval, _tz: *mut libc::c_void) -> libc::c_int {
let micros = micros_since_epoch();
unsafe {
tp.write(libc::timeval {
tv_sec: (micros / 1_000_000) as _,
tv_usec: (micros % 1_000_000) as _,
});
}
0
}
38 changes: 38 additions & 0 deletions src/hyperlight-js-runtime/src/main/stubs/io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
Copyright 2026 The Hyperlight Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
use crate::libc;

#[unsafe(no_mangle)]
extern "C" fn putchar(c: libc::c_int) -> libc::c_int {
unsafe { libc::_putchar(c as libc::c_char) };
if c == '\n' as libc::c_int {
// force a flush of the internal buffer in the hyperlight putchar implementation
unsafe { libc::_putchar(0) };
}
(c as libc::c_char) as libc::c_int
}

#[unsafe(no_mangle)]
extern "C" fn fflush(f: *mut libc::c_void) -> libc::c_int {
if !f.is_null() {
// we only support flushing all streams, and stdout is our only stream
unsafe { libc::__errno_location().write(libc::EINVAL as _) };
return -1;
}
// flush stdout
unsafe { libc::_putchar(0) };
0
}
50 changes: 50 additions & 0 deletions src/hyperlight-js-runtime/src/main/stubs/localtime.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
Copyright 2026 The Hyperlight Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
use chrono::{DateTime, TimeDelta, Datelike as _, Timelike as _};

use crate::libc;

#[unsafe(no_mangle)]
extern "C" fn localtime_r(time: *const libc::time_t, result: *mut libc::tm) -> *mut libc::tm {
let offset = unsafe { time.read() };

let Some(offset) = TimeDelta::try_seconds(offset) else {
unsafe { libc::__errno_location().write(libc::EOVERFLOW as _) };
return result;
};
let Some(time) = DateTime::UNIX_EPOCH.checked_add_signed(offset) else {
unsafe { libc::__errno_location().write(libc::EOVERFLOW as _) };
return result;
};

unsafe {
result.write(libc::tm {
tm_sec: time.second() as _,
tm_min: time.minute() as _,
tm_hour: time.hour() as _,
tm_mday: time.day() as _,
tm_mon: time.month0() as _,
tm_year: (time.year() - 1900) as _,
tm_wday: time.weekday().num_days_from_sunday() as _,
tm_yday: time.ordinal0() as _,
tm_isdst: 0,
__tm_gmtoff: 0,
__tm_zone: c"GMT".as_ptr() as _,
})
};

result
}
Loading