From 2dfe9a1f9dbf84d3f93bd3f0043e16fa2a5215c8 Mon Sep 17 00:00:00 2001 From: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> Date: Tue, 10 Mar 2026 11:29:40 -0700 Subject: [PATCH 01/10] fix: don't canonicalize cargo binary path canonicalize() resolves the cargo -> rustup symlink, causing commands like 'cargo rustc' to be invoked as 'rustup rustc' which doesn't understand cargo-specific arguments. Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> Signed-off-by: Lucy Menon <168595099+syntactically@users.noreply.github.com> --- src/cargo_cmd.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cargo_cmd.rs b/src/cargo_cmd.rs index 6c0b1de..7e4c06c 100644 --- a/src/cargo_cmd.rs +++ b/src/cargo_cmd.rs @@ -48,8 +48,8 @@ impl CargoBinary { pub fn find_cargo() -> Result { let cargo = match env::var_os("CARGO") { - Some(cargo) => Path::new(&cargo).canonicalize()?, - None => which::which("cargo")?.canonicalize()?, + Some(cargo) => PathBuf::from(cargo), + None => which::which("cargo")?, }; let rustup_toolchain = env::var_os("RUSTUP_TOOLCHAIN"); Ok(CargoBinary { From 82205309439e286a014bb04f40510be5c1d15f2a Mon Sep 17 00:00:00 2001 From: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> Date: Tue, 10 Mar 2026 11:30:07 -0700 Subject: [PATCH 02/10] fix: strip --target and --target-dir from forwarded args User CLI args like --target aarch64-unknown-none were passed through to the final cargo build command, overriding the resolved CARGO_BUILD_TARGET env var set by populate_from_args. Filter these out since the resolved hyperlight target is set via environment variable. Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> Signed-off-by: Lucy Menon <168595099+syntactically@users.noreply.github.com> --- src/command.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/command.rs b/src/command.rs index 9beccf4..0660ff9 100644 --- a/src/command.rs +++ b/src/command.rs @@ -529,7 +529,25 @@ impl Command { fn command(&self) -> StdCommand { let mut command = self.cargo.command(); - command.args(self.get_args()); + // Filter out --target and --target-dir from forwarded args since + // populate_from_args sets them via env vars with the resolved values + let args: Vec<_> = self.get_args().map(|a| a.to_owned()).collect(); + let mut skip_next = false; + for arg in args.iter() { + if skip_next { + skip_next = false; + continue; + } + let arg_str = arg.to_string_lossy(); + if arg_str == "--target" || arg_str == "--target-dir" { + skip_next = true; // skip the next arg (the value) + continue; + } + if arg_str.starts_with("--target=") || arg_str.starts_with("--target-dir=") { + continue; + } + command.arg(arg); + } if let Some(cwd) = &self.current_dir { command.current_dir(cwd); } From a548a979f570b3aa55478b13853f14f68e0b5e65 Mon Sep 17 00:00:00 2001 From: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> Date: Tue, 10 Mar 2026 11:30:59 -0700 Subject: [PATCH 03/10] feat: add aarch64-hyperlight-none target support - Add aarch64-hyperlight-none target spec derived from aarch64-unknown-none with hyperlight customizations - Select correct musl arch headers (aarch64 vs x86_64) - Use appropriate clang --target for aarch64 - Make -mno-red-zone x86_64-only (aarch64 has no red zone) - Fix get_spec to pass --target as CLI arg instead of env var Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> Signed-off-by: Lucy Menon <168595099+syntactically@users.noreply.github.com> --- src/sysroot.rs | 26 +++++++++++++++-- src/toolchain.rs | 76 ++++++++++++++++++++++++++---------------------- 2 files changed, 66 insertions(+), 36 deletions(-) diff --git a/src/sysroot.rs b/src/sysroot.rs index f9545d6..8c75c0c 100644 --- a/src/sysroot.rs +++ b/src/sysroot.rs @@ -45,10 +45,31 @@ pub fn build(args: &Args) -> Result<()> { spec.remove("rustc-abi"); spec } + "aarch64-hyperlight-none" => { + let mut spec = get_spec(args, "aarch64-unknown-none")?; + let Value::Object(custom) = json!({ + "code-model": "small", + "linker": "rust-lld", + "linker-flavor": "gnu-lld", + "pre-link-args": { + "gnu-lld": ["-znostart-stop-gc"], + }, + "relocation-model": "pic", + "direct-access-external-data": true, + "position-independent-executables": true, + "features": "+v8.1a,+strict-align,+neon,+fp-armv8" + }) else { + unreachable!() + }; + spec.extend(custom); + spec.remove("rustc-abi"); + spec + } triplet => bail!( "Unsupported target triple: {triplet:?} Supported values are: - * x86_64-hyperlight-none" + * x86_64-hyperlight-none + * aarch64-hyperlight-none" ), }; @@ -180,7 +201,8 @@ fn get_spec(args: &Args, triplet: impl AsRef) -> Result> .envs(args.env.iter()) .current_dir(&args.current_dir) .arg("rustc") - .target(triplet) + .arg("--target") + .arg(triplet.as_ref()) .manifest_path(&args.manifest_path) .arg("-Zunstable-options") .arg("--print=target-spec-json") diff --git a/src/toolchain.rs b/src/toolchain.rs index 0773935..6a2fd96 100644 --- a/src/toolchain.rs +++ b/src/toolchain.rs @@ -74,33 +74,34 @@ pub fn prepare(args: &Args) -> Result<()> { std::fs::create_dir_all(&include_dst_dir) .context("Failed to create sysroot include directory")?; - // Detect which libc variant is present: picolibc or legacy musl - let include_dirs: &[&str] = &[ - // directories for musl - "third_party/printf/", - "third_party/musl/include", - "third_party/musl/arch/generic", - "third_party/musl/arch/x86_64", - "third_party/musl/src/internal", - // directories for picolibc - "third_party/picolibc/libc/include", - "third_party/picolibc/libc/stdio", - "include", - ]; - - for dir in include_dirs { - let include_src_dir = libc_dir.join(dir); - let files = glob::glob(&format!("{}/**/*.h", include_src_dir.display())) - .context("Failed to read include source directory")?; - - for file in files { - let src = file.context("Failed to read include source file")?; - let dst = src.strip_prefix(&include_src_dir).unwrap(); - let dst = include_dst_dir.join(dst); - - std::fs::create_dir_all(dst.parent().unwrap()) - .context("Failed to create include subdirectory")?; - std::fs::copy(&src, &dst).context("Failed to copy include file")?; + if !args.target.starts_with("aarch64") { + // Detect which libc variant is present: picolibc or legacy musl + let include_dirs: &[&str] = &[ + // directories for musl + "third_party/printf/", + "third_party/musl/include", + "third_party/musl/arch/generic", + "third_party/musl/arch/x86_64", + "third_party/musl/src/internal", + // directories for picolibc + "third_party/picolibc/libc/include", + "third_party/picolibc/libc/stdio", + "include", + ]; + + for dir in include_dirs { + let include_src_dir = libc_dir.join(dir); + let files = glob::glob(&format!("{}/**/*.h", include_src_dir.display())) + .context("Failed to read include source directory")?; + for file in files { + let src = file.context("Failed to read include source file")?; + let dst = src.strip_prefix(&include_src_dir).unwrap(); + let dst = include_dst_dir.join(dst); + + std::fs::create_dir_all(dst.parent().unwrap()) + .context("Failed to create include subdirectory")?; + std::fs::copy(&src, &dst).context("Failed to copy include file")?; + } } } @@ -108,19 +109,21 @@ pub fn prepare(args: &Args) -> Result<()> { } pub fn cflags(args: &Args) -> OsString { - const FLAGS: &[&str] = &[ - // terrible hack, see - // https://github.com/hyperlight-dev/hyperlight/blob/main/src/hyperlight_guest_bin/build.rs#L80 - "--target=x86_64-unknown-linux-none", + let clang_target = if args.target.starts_with("aarch64") { + "--target=aarch64-unknown-linux-none" + } else { + "--target=x86_64-unknown-linux-none" + }; + + let common_flags: &[&str] = &[ + clang_target, "-U__linux__", - // Our rust target also has this set since it based off "x86_64-unknown-none" "-fPIC", // We don't support stack protectors at the moment, but Arch Linux clang // auto-enables them for -linux platforms, so explicitly disable them. "-fno-stack-protector", "-fstack-clash-protection", "-mstack-probe-size=4096", - "-mno-red-zone", "-nostdlibinc", // Define HYPERLIGHT as we use this to conditionally enable/disable code // in the libc headers @@ -129,10 +132,15 @@ pub fn cflags(args: &Args) -> OsString { ]; let mut flags = OsString::new(); - for flag in FLAGS { + for flag in common_flags { flags.push(flag); flags.push(" "); } + + // x86_64-specific flags + if args.target.starts_with("x86_64") { + flags.push("-mno-red-zone "); + } flags.push(" "); flags.push("-isystem"); flags.push(" "); From 13b1c46d478cb06c1c6b29eb3d54f33f0ba04557 Mon Sep 17 00:00:00 2001 From: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> Date: Tue, 10 Mar 2026 11:31:20 -0700 Subject: [PATCH 04/10] chore: update CI, docs, and justfile for aarch64 - Add concurrency group to cancel stale CI runs - Gate KVM setup on X64 runners - Gate run-guest on X64 (no host support on ARM yet) - Update README to mention aarch64 target - Use arch() in justfile for target-independent paths Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com> Signed-off-by: Lucy Menon <168595099+syntactically@users.noreply.github.com> --- .github/workflows/ci.yml | 8 +++++++- README.md | 2 +- justfile | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 88635c0..fd5436a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,11 @@ on: branches: [main] workflow_call: +# Cancels old running job if a new one is triggered (e.g. by a push onto the same branch). +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + env: CARGO_TERM_COLOR: always @@ -27,7 +32,7 @@ jobs: - uses: Swatinem/rust-cache@v2 - uses: extractions/setup-just@v4 - name: Enable kvm - if: runner.os == 'Linux' + if: runner.os == 'Linux' && runner.arch == 'X64' run: | echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules sudo udevadm control --reload-rules @@ -39,6 +44,7 @@ jobs: shell: bash run: just build-guest - name: Run example + if: runner.arch == 'X64' shell: bash run: just run-guest - name: Test `new` subcommand diff --git a/README.md b/README.md index a5439f5..dbc9e8b 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ Then to build the hyperlight guest binary, run cargo hyperlight build --release ``` -Your binary will be built for the `x86_64-hyperlight-none` target by default, and placed in `target/x86_64-hyperlight-none/release/guest`. +Your binary will be built for the `x86_64-hyperlight-none` target (or `aarch64-hyperlight-none` on ARM) by default, and placed in `target/-hyperlight-none/release/guest`. There's no need for any extra configuration, the command will take care of everything. diff --git a/justfile b/justfile index c87396f..163dd4e 100644 --- a/justfile +++ b/justfile @@ -26,7 +26,7 @@ build-guest: cargo hyperlight build --manifest-path ./examples/guest/Cargo.toml run-guest: build-guest - cargo run --manifest-path ./examples/host/Cargo.toml -- ./target/x86_64-hyperlight-none/debug/guest + cargo run --manifest-path ./examples/host/Cargo.toml -- ./target/{{arch()}}-hyperlight-none/debug/guest test-new: cargo test --test new From 851f89f2789f902a302f4e75c53c0c776926e8be Mon Sep 17 00:00:00 2001 From: Lucy Menon <168595099+syntactically@users.noreply.github.com> Date: Tue, 9 Jun 2026 20:37:54 +0100 Subject: [PATCH 05/10] Remove some unused code `Command::exec()` is never used anymore, since its use precludes running on Windows. `CargoCmd::resolve_env()` is not used, because the essentially-identical `Command::resolve_env()` is used instead. Signed-off-by: Lucy Menon <168595099+syntactically@users.noreply.github.com> --- src/cargo_cmd.rs | 11 ----- src/command.rs | 126 +---------------------------------------------- 2 files changed, 2 insertions(+), 135 deletions(-) diff --git a/src/cargo_cmd.rs b/src/cargo_cmd.rs index 7e4c06c..920d364 100644 --- a/src/cargo_cmd.rs +++ b/src/cargo_cmd.rs @@ -22,10 +22,6 @@ pub trait CargoCmd { flags: impl AsRef, ) -> &mut Self; fn allow_unstable(&mut self) -> &mut Self; - fn resolve_env( - &self, - base: impl IntoIterator, impl AsRef)>, - ) -> HashMap; fn checked_output(&mut self) -> Result; fn checked_status(&mut self) -> Result<()>; } @@ -195,13 +191,6 @@ impl CargoCmd for Command { self.env("RUSTC_BOOTSTRAP", "1") } - fn resolve_env( - &self, - base: impl IntoIterator, impl AsRef)>, - ) -> HashMap { - merge_env(base, self.get_envs()) - } - fn checked_output(&mut self) -> Result { let output = self.output(); diff --git a/src/command.rs b/src/command.rs index 0660ff9..c3abd13 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1,10 +1,9 @@ use std::collections::{BTreeMap, HashMap}; -use std::convert::Infallible; -use std::ffi::{OsStr, OsString, c_char}; +use std::ffi::{OsStr, OsString}; use std::fmt::Debug; use std::path::{Path, PathBuf}; use std::process::Command as StdCommand; -use std::{env, iter}; +use std::env; use anyhow::{Context, Result}; use os_str_bytes::OsStrBytesExt; @@ -655,127 +654,6 @@ impl Command { .context("Failed to execute cargo")?; Ok(()) } - - /// Executes the cargo command, replacing the current process. - /// - /// This function will never return on success, as it replaces the current process - /// with the cargo process. On error, it will print the error and exit with code 101. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ```no_run - /// use cargo_hyperlight::cargo; - /// - /// cargo() - /// .unwrap() - /// .arg("build") - /// .exec(); // This will never return - /// ``` - /// - /// # Errors - /// - /// This function will exit the process with code 101 if: - /// - The sysroot preparation fails - /// - The process replacement fails - pub fn exec(&self) -> ! { - match self.exec_impl() { - Err(e) => { - eprintln!("{e:?}"); - std::process::exit(101); - } - } - } - - /// Internal implementation of process replacement. - /// - /// This method prepares the sysroot and then calls the low-level `exec` function - /// to replace the current process. - fn exec_impl(&self) -> anyhow::Result { - let args = self.build_args(); - - args.prepare_sysroot() - .context("Failed to prepare sysroot")?; - - let mut command = self.command(); - command.populate_from_args(&args); - - if let Some(cwd) = self.get_current_dir() { - env::set_current_dir(cwd).context("Failed to change current directory")?; - } - - Ok(exec( - command.get_program(), - command.get_args(), - command.resolve_env(self.base_env()), - )?) - } -} - -/// Replaces the current process with the specified program using `execvpe`. -/// -/// This function converts the provided arguments and environment variables into -/// the format expected by the `execvpe` system call and then replaces the current -/// process with the new program. -/// -/// # Arguments -/// -/// * `program` - The path to the program to execute -/// * `args` - The command-line arguments to pass to the program -/// * `envs` - The environment variables to set for the new process -/// -/// # Returns -/// -/// This function should never return on success. On failure, it returns an -/// `std::io::Error` describing what went wrong. -/// -/// # Safety -/// -/// This function uses unsafe code to call `libc::execvpe`. The implementation -/// carefully manages memory to ensure null-terminated strings are properly -/// constructed for the system call. -fn exec( - program: impl AsRef, - args: impl IntoIterator>, - envs: impl IntoIterator, impl AsRef)>, -) -> std::io::Result { - let mut env_bytes = vec![]; - let mut env_offsets = vec![]; - for (k, v) in envs.into_iter() { - env_offsets.push(env_bytes.len()); - env_bytes.extend_from_slice(k.as_ref().as_encoded_bytes()); - env_bytes.push(b'='); - env_bytes.extend_from_slice(v.as_ref().as_encoded_bytes()); - env_bytes.push(0); - } - let env_ptrs = env_offsets - .into_iter() - .map(|offset| env_bytes[offset..].as_ptr() as *const c_char) - .chain(iter::once(std::ptr::null())) - .collect::>(); - - let mut arg_bytes = vec![]; - let mut arg_offsets = vec![]; - - arg_offsets.push(arg_bytes.len()); - arg_bytes.extend_from_slice(program.as_ref().as_encoded_bytes()); - arg_bytes.push(0); - - for arg in args { - arg_offsets.push(arg_bytes.len()); - arg_bytes.extend_from_slice(arg.as_ref().as_encoded_bytes()); - arg_bytes.push(0); - } - let arg_ptrs = arg_offsets - .into_iter() - .map(|offset| arg_bytes[offset..].as_ptr() as *const c_char) - .chain(iter::once(std::ptr::null())) - .collect::>(); - - unsafe { libc::execvpe(arg_ptrs[0], arg_ptrs.as_ptr(), env_ptrs.as_ptr()) }; - - Err(std::io::Error::last_os_error()) } /// Returns `true` if the given environment variable should be preserved From a4a79a2d3b1a2294404573609bfbcddf00dbf8e7 Mon Sep 17 00:00:00 2001 From: Lucy Menon <168595099+syntactically@users.noreply.github.com> Date: Wed, 24 Jun 2026 22:39:05 +0100 Subject: [PATCH 06/10] Update examples to 0.15.0 Signed-off-by: Lucy Menon <168595099+syntactically@users.noreply.github.com> --- examples/guest/Cargo.lock | 226 ++++++++++++++++++----- examples/guest/Cargo.toml | 6 +- examples/guest/src/host_print.c | 6 +- examples/guest/src/main.rs | 4 +- examples/host/Cargo.lock | 305 +++++++++++--------------------- examples/host/Cargo.toml | 2 +- examples/host/src/main.rs | 2 +- 7 files changed, 298 insertions(+), 253 deletions(-) diff --git a/examples/guest/Cargo.lock b/examples/guest/Cargo.lock index 1d1eaf1..ca7c73c 100644 --- a/examples/guest/Cargo.lock +++ b/examples/guest/Cargo.lock @@ -13,9 +13,29 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "bindgen" +version = "0.71.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] [[package]] name = "bindgen" @@ -45,11 +65,11 @@ checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "buddy_system_allocator" -version = "0.11.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a0108968a3a2dab95b089c0fc3f1afa7759aa5ebe6f1d86d206d6f7ba726eb" +checksum = "c1af5a01adeeade54c9f5060300227a8ee64c05b6376f28e9b8ee3b72dd2056f" dependencies = [ - "spin 0.9.8", + "spin", ] [[package]] @@ -94,6 +114,12 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "find-msvc-tools" version = "0.1.4" @@ -102,9 +128,9 @@ checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" [[package]] name = "flatbuffers" -version = "25.9.23" +version = "25.12.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b6620799e7340ebd9968d2e0708eb82cf1971e9a16821e2091b6d6e475eed5" +checksum = "35f6839d7b3b98adde531effaf34f0c2badc6f4735d26fe74709d8e513a96ef3" dependencies = [ "bitflags", "rustc_version", @@ -120,70 +146,111 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" name = "guest" version = "0.0.0" dependencies = [ - "bindgen", + "bindgen 0.72.1", "cc", "hyperlight-common", "hyperlight-guest", "hyperlight-guest-bin", ] +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + [[package]] name = "hyperlight-common" -version = "0.11.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "409ac43d32b148aa71473d5bca0b5ff4cf72ddf4e9cb4550e9a189d807e6c0d4" +checksum = "a29190c35e3883c3e0944007f47a0997f8ebcf784a2a3f35c0311e513b019416" dependencies = [ "anyhow", "flatbuffers", "log", - "spin 0.10.0", + "spin", + "thiserror", + "tracing-core", ] [[package]] name = "hyperlight-guest" -version = "0.11.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27be04c1916c8483d6ec3486bd753b0e0757c30eb94aad53ba1e52da2f7ca00" +checksum = "82a676e88a6640b5cf621171c67f609d1e662713624562597518be6fdb0e0b30" dependencies = [ "anyhow", "flatbuffers", "hyperlight-common", - "hyperlight-guest-tracing", "serde_json", "tracing", ] [[package]] name = "hyperlight-guest-bin" -version = "0.11.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a8193539c50452e90e0c5ee906d251a1b2b6121a6040fc532b2468d9ef2184" +checksum = "0bc349c61dc771769308a2079d398488cd0c52d9bdcad8cf633922b288dd2aed" dependencies = [ "buddy_system_allocator", - "cc", - "cfg-if", "flatbuffers", - "glob", "hyperlight-common", "hyperlight-guest", + "hyperlight-guest-macro", "hyperlight-guest-tracing", + "hyperlight-libc", + "linkme", "log", - "spin 0.10.0", + "spin", "tracing", ] +[[package]] +name = "hyperlight-guest-macro" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bef332d8490f2ebe5b8d52f7d39263f82580c54e9505980fea8186fd6679ea3" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "hyperlight-guest-tracing" -version = "0.11.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670c97963e19dd9e2855ed8ef1db47074d72036beb2ac5e621bd5796be2856b0" +checksum = "ca03b1ccd29c0c277555d8bd737257d0598554004f9667bb196227d5ed8b5b7e" dependencies = [ "hyperlight-common", - "spin 0.10.0", + "spin", "tracing", "tracing-core", ] +[[package]] +name = "hyperlight-libc" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd6f523d9383e9c8b3820bb141b4deb45b5470135ee069cbc387c3a2a3d55c7" +dependencies = [ + "anyhow", + "bindgen 0.71.1", + "cc", + "glob", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "itertools" version = "0.13.0" @@ -215,6 +282,26 @@ dependencies = [ "windows-link", ] +[[package]] +name = "linkme" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83272d46373fb8decca684579ac3e7c8f3d71d4cc3aa693df8759e260ae41cf" +dependencies = [ + "linkme-impl", +] + +[[package]] +name = "linkme-impl" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d59e20403c7d08fe62b4376edfe5c7fb2ef1e6b1465379686d0f21c8df444b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "lock_api" version = "0.4.14" @@ -226,9 +313,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "memchr" @@ -268,6 +355,15 @@ dependencies = [ "syn", ] +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.103" @@ -396,15 +492,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - [[package]] name = "spin" version = "0.10.0" @@ -425,11 +512,61 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -438,9 +575,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", @@ -449,9 +586,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" [[package]] name = "unicode-ident" @@ -464,3 +601,12 @@ name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] diff --git a/examples/guest/Cargo.toml b/examples/guest/Cargo.toml index 5ab53cc..53428e4 100644 --- a/examples/guest/Cargo.toml +++ b/examples/guest/Cargo.toml @@ -5,9 +5,9 @@ edition = "2024" publish = false [dependencies] -hyperlight-common = { version = "0.11.0", default-features = false } -hyperlight-guest = "0.11.0" -hyperlight-guest-bin = "0.11.0" +hyperlight-common = { version = "0.15.0", default-features = false } +hyperlight-guest = "0.15.0" +hyperlight-guest-bin = "0.15.0" [build-dependencies] cc = "1.2" diff --git a/examples/guest/src/host_print.c b/examples/guest/src/host_print.c index eab41ee..9551582 100644 --- a/examples/guest/src/host_print.c +++ b/examples/guest/src/host_print.c @@ -1,7 +1,9 @@ #include "host_print.h" -#include +#include int host_print(const char *s, size_t len) { - return printf("%.*s", (int)len, s); + int n = printf("%.*s", (int)len, s); + fflush(stdout); + return n; } diff --git a/examples/guest/src/main.rs b/examples/guest/src/main.rs index 7b7768d..1083336 100644 --- a/examples/guest/src/main.rs +++ b/examples/guest/src/main.rs @@ -29,7 +29,7 @@ fn host_print(s: impl AsRef<[u8]>) -> i32 { unsafe { ffi::host_print(s.as_ptr() as _, s.len()) } } -pub fn say_hello(func: &FunctionCall) -> Result> { +pub fn say_hello(func: FunctionCall) -> Result> { let params = func.parameters.as_deref().unwrap_or_default(); let Some(ParameterValue::String(name)) = params.first() else { return Err(HyperlightGuestError::new( @@ -48,7 +48,7 @@ pub extern "C" fn hyperlight_main() { "SayHello".into(), [ParameterType::String].into(), ReturnType::Int, - say_hello as usize, + say_hello, )); } diff --git a/examples/host/Cargo.lock b/examples/host/Cargo.lock index 58794df..00bd4ae 100644 --- a/examples/host/Cargo.lock +++ b/examples/host/Cargo.lock @@ -2,18 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "ahash" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy 0.8.27", -] - [[package]] name = "aho-corasick" version = "1.1.4" @@ -34,9 +22,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "arrayref" @@ -50,17 +38,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "autocfg" version = "1.5.0" @@ -75,21 +52,22 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" [[package]] name = "blake3" -version = "1.8.2" +version = "1.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" +checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", + "cpufeatures 0.3.0", ] [[package]] @@ -127,18 +105,6 @@ version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" - [[package]] name = "cc" version = "1.2.44" @@ -163,6 +129,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chacha20" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core", +] + [[package]] name = "chrono" version = "0.4.42" @@ -176,9 +153,9 @@ dependencies = [ [[package]] name = "constant_time_eq" -version = "0.3.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" [[package]] name = "core-foundation-sys" @@ -195,6 +172,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crossbeam-channel" version = "0.5.15" @@ -262,20 +248,6 @@ dependencies = [ "syn", ] -[[package]] -name = "elfcore" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824386967a6a98e7f99d5c15d40cd1534b0ebfc4193a7109689dcf5322e8d744" -dependencies = [ - "libc", - "nix", - "smallvec", - "thiserror 1.0.69", - "tracing", - "zerocopy 0.7.35", -] - [[package]] name = "find-msvc-tools" version = "0.1.4" @@ -284,11 +256,11 @@ checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" [[package]] name = "flatbuffers" -version = "25.9.23" +version = "25.12.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b6620799e7340ebd9968d2e0708eb82cf1971e9a16821e2091b6d6e475eed5" +checksum = "35f6839d7b3b98adde531effaf34f0c2badc6f4735d26fe74709d8e513a96ef3" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.13.0", "rustc_version", ] @@ -330,17 +302,29 @@ checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", ] +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "rand_core", +] + [[package]] name = "git2" version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b88256088d75a56f8ecfa070513a775dd9107f6530ef14919dac831af9cfe2b" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.13.0", "libc", "libgit2-sys", "log", @@ -371,12 +355,6 @@ dependencies = [ "scroll", ] -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - [[package]] name = "host" version = "0.0.0" @@ -387,31 +365,32 @@ dependencies = [ [[package]] name = "hyperlight-common" -version = "0.11.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "409ac43d32b148aa71473d5bca0b5ff4cf72ddf4e9cb4550e9a189d807e6c0d4" +checksum = "a29190c35e3883c3e0944007f47a0997f8ebcf784a2a3f35c0311e513b019416" dependencies = [ "anyhow", "flatbuffers", "log", "spin", + "thiserror", "tracing", + "tracing-core", ] [[package]] name = "hyperlight-host" -version = "0.11.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49984fc8631a27d214550204350e4cc2ca304a37da759ea37407d0d1e4863eb6" +checksum = "634584fae781c9ea278a13f1fbecd25d220c543cfb5bbbbc496b7e30fc97c251" dependencies = [ "anyhow", - "bitflags 2.10.0", + "bitflags 2.13.0", "blake3", "built", "cfg-if", "cfg_aliases", "crossbeam-channel", - "elfcore", "flatbuffers", "goblin", "hyperlight-common", @@ -427,9 +406,8 @@ dependencies = [ "rand", "rust-embed", "serde_json", - "sha256", "termcolor", - "thiserror 2.0.17", + "thiserror", "tracing", "tracing-core", "tracing-log", @@ -608,7 +586,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "333f77a20344a448f3f70664918135fddeb804e938f28a99d685bd92926e0b19" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.13.0", "kvm-bindings", "libc", "vmm-sys-util", @@ -622,9 +600,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.177" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libgit2-sys" @@ -644,7 +622,7 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.13.0", "libc", ] @@ -677,9 +655,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "memchr" @@ -689,12 +667,12 @@ checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "metrics" -version = "0.24.2" +version = "0.24.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dea7ac8057892855ec285c440160265225438c3c45072613c25a4b26e98ef5" +checksum = "89550ee9f79e88fef3119de263694973a8adb26c21d75322164fb8c493039fe2" dependencies = [ - "ahash", "portable-atomic", + "rapidhash", ] [[package]] @@ -706,7 +684,7 @@ dependencies = [ "libc", "num_enum", "vmm-sys-util", - "zerocopy 0.8.27", + "zerocopy", ] [[package]] @@ -717,21 +695,10 @@ checksum = "748f59f22dccd910080a6315fc692bd04bd8c94ae2fc346957ec34b7d985eaa0" dependencies = [ "libc", "mshv-bindings", - "thiserror 2.0.17", + "thiserror", "vmm-sys-util", ] -[[package]] -name = "nix" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -823,15 +790,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy 0.8.27", -] - [[package]] name = "proc-macro2" version = "1.0.103" @@ -857,32 +815,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] -name = "rand" -version = "0.9.4" +name = "r-efi" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" -dependencies = [ - "rand_chacha", - "rand_core", -] +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] -name = "rand_chacha" -version = "0.9.0" +name = "rand" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ - "ppv-lite86", + "chacha20", + "getrandom 0.4.3", "rand_core", ] [[package]] name = "rand_core" -version = "0.9.3" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rapidhash" +version = "4.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e48930979c155e2f33aa36ab3119b5ee81332beb6482199a8ecd6029b80b59" dependencies = [ - "getrandom 0.3.4", + "rustversion", ] [[package]] @@ -893,7 +854,7 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror 2.0.17", + "thiserror", ] [[package]] @@ -915,9 +876,9 @@ checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "rust-embed" -version = "8.9.0" +version = "8.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "947d7f3fad52b283d261c4c99a084937e2fe492248cb9a68a8435a861b8798ca" +checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -1061,23 +1022,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest", ] -[[package]] -name = "sha256" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f880fc8562bdeb709793f00eb42a2ad0e672c4f883bbe59122b926eca935c8f6" -dependencies = [ - "async-trait", - "bytes", - "hex", - "sha2", - "tokio", -] - [[package]] name = "shellexpand" version = "3.1.1" @@ -1147,38 +1095,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.69" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" -dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.69" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", @@ -1195,21 +1123,11 @@ dependencies = [ "zerovec", ] -[[package]] -name = "tokio" -version = "1.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" -dependencies = [ - "bytes", - "pin-project-lite", -] - [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "log", "pin-project-lite", @@ -1219,9 +1137,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", @@ -1230,9 +1148,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", @@ -1281,11 +1199,11 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.18.1" +version = "1.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.3", "js-sys", "wasm-bindgen", ] @@ -1582,34 +1500,13 @@ dependencies = [ "synstructure", ] -[[package]] -name = "zerocopy" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" -dependencies = [ - "byteorder", - "zerocopy-derive 0.7.35", -] - [[package]] name = "zerocopy" version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ - "zerocopy-derive 0.8.27", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "zerocopy-derive", ] [[package]] diff --git a/examples/host/Cargo.toml b/examples/host/Cargo.toml index cdafb59..0ac28e9 100644 --- a/examples/host/Cargo.toml +++ b/examples/host/Cargo.toml @@ -6,4 +6,4 @@ publish = false [dependencies] anyhow = "1.0" -hyperlight-host = "0.11.0" +hyperlight-host = "0.15.0" diff --git a/examples/host/src/main.rs b/examples/host/src/main.rs index e37aee1..7a80fa3 100644 --- a/examples/host/src/main.rs +++ b/examples/host/src/main.rs @@ -10,7 +10,7 @@ fn main() -> anyhow::Result<()> { let mut config = SandboxConfiguration::default(); config.set_heap_size(1024 * 1024); // 1 MiB - config.set_stack_size(1024 * 1024); // 1 MiB + config.set_scratch_size(1024 * 1024); // 1 MiB // create the sandbox let mut sbox = hyperlight_host::UninitializedSandbox::new(guest, Some(config))?.evolve()?; From 603e738d75edc48f7de360828ca5b6d8d50ab3ab Mon Sep 17 00:00:00 2001 From: Lucy Menon <168595099+syntactically@users.noreply.github.com> Date: Mon, 22 Jun 2026 23:24:17 +0100 Subject: [PATCH 07/10] Better support for building C distributables and guests This started with the observation that we didn't actually build libc headers properly on aarch64, leading to the realisation that we weren't using cargo-hyperlight to build C guests (or anything that depended on most of libc), so all that code was essentially untested. cargo-hyperlight now supports building the guest CAPI library, and can directly output the appropriate cflags/ldflags/libs needed to build and link a C guest. On top of that, it can now build a `hyperlight-config` executable into its produced sysroots which provides the same information, as well as a clang wrapper that uses the same logic for ease of use. This is done in order to allow downstream consumers to build C guests using the same logic as cargo-hyperlight without having to build/install the tool themselves, removing opportunities for e.g. target features to get out of sync. Finally, `cargo-hyperlight build-c-sysroot --sysroot-dir ` will now copy just the final build artifacts (header files, libraries, and the aforementioned hyperlight-config and clang wrapper executables) into a new directory tree, which makes it easy to build a redistributable artifact that bundles everything downstreams need to build C guests (including both build logic and libraries/interfaces themselves). Signed-off-by: Lucy Menon <168595099+syntactically@users.noreply.github.com> --- .github/workflows/ci.yml | 12 +- .gitignore | 4 +- Cargo.lock | 2 + Cargo.toml | 2 + build.rs | 5 + examples/c/fetch-capi/Cargo.lock | 859 +++++++++++++++++++++++++++++++ examples/c/fetch-capi/Cargo.toml | 8 + examples/c/fetch-capi/src/lib.rs | 3 + examples/c/guest/main.c | 19 + justfile | 14 + src/cli.rs | 44 ++ src/command.rs | 14 +- src/lib.rs | 24 +- src/main.rs | 122 ++++- src/sysroot.rs | 18 +- src/toolchain.rs | 341 ++++++++---- src/toolchain_flags.rs | 112 ++++ src/util.rs | 31 ++ src/wrapper/_Cargo.toml | 8 + src/wrapper/_clang_parser.rs | 65 +++ src/wrapper/_main.rs | 107 ++++ tests/clang_parser.rs | 61 +++ 22 files changed, 1740 insertions(+), 135 deletions(-) create mode 100644 examples/c/fetch-capi/Cargo.lock create mode 100644 examples/c/fetch-capi/Cargo.toml create mode 100644 examples/c/fetch-capi/src/lib.rs create mode 100644 examples/c/guest/main.c create mode 100644 src/toolchain_flags.rs create mode 100644 src/util.rs create mode 100644 src/wrapper/_Cargo.toml create mode 100644 src/wrapper/_clang_parser.rs create mode 100644 src/wrapper/_main.rs create mode 100644 tests/clang_parser.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fd5436a..930313f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,9 +47,15 @@ jobs: if: runner.arch == 'X64' shell: bash run: just run-guest - - name: Test `new` subcommand + - name: Build C guest example shell: bash - run: just test-new + run: just build-c-guest + - name: Run C guest example + shell: bash + run: just run-c-guest + - name: Run other tests + shell: bash + run: just test spelling: name: Spell check with typos @@ -82,4 +88,4 @@ jobs: run: just fmt - name: Check clippy shell: bash - run: just clippy \ No newline at end of file + run: just clippy diff --git a/.gitignore b/.gitignore index 2dcd3c9..fcdb023 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /target -/examples/*/target +/examples/**/target +/examples/c/sysroot +/examples/c/guest/guest \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index a7036af..37daf8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,6 +85,8 @@ dependencies = [ "libc", "object", "os_str_bytes", + "proc-macro2", + "quote", "regex", "rustc-demangle", "semver", diff --git a/Cargo.toml b/Cargo.toml index f2726ee..338d754 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,3 +24,5 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tempfile = "3" which = { version = "8", features = ["regex"] } +quote = "1.0" +proc-macro2 = "1.0" \ No newline at end of file diff --git a/build.rs b/build.rs index 2246ee5..dec170d 100644 --- a/build.rs +++ b/build.rs @@ -29,6 +29,11 @@ fn main() { println!("cargo:rustc-env=GIT_HASH={}", git_hash); println!("cargo:rustc-env=GIT_DATE={}", git_date); + println!( + "cargo:rustc-env=CARGO_HYPERLIGHT_HOST_TRIPLE={}", + std::env::var("TARGET").unwrap() + ); + // Re-run build script if git HEAD changes println!("cargo:rerun-if-changed=.git/HEAD"); } diff --git a/examples/c/fetch-capi/Cargo.lock b/examples/c/fetch-capi/Cargo.lock new file mode 100644 index 0000000..5e3b1c3 --- /dev/null +++ b/examples/c/fetch-capi/Cargo.lock @@ -0,0 +1,859 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "bindgen" +version = "0.71.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex 1.3.0", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "buddy_system_allocator" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1af5a01adeeade54c9f5060300227a8ee64c05b6376f28e9b8ee3b72dd2056f" +dependencies = [ + "spin", +] + +[[package]] +name = "cbindgen" +version = "0.29.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ecb53484c9c167ba674026b656d8a27d7657a58e6066aa902bfb1a4aa00ae20" +dependencies = [ + "clap", + "heck", + "indexmap", + "log", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn", + "tempfile", + "toml", +] + +[[package]] +name = "cc" +version = "1.2.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +dependencies = [ + "find-msvc-tools", + "shlex 2.0.1", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "fetch_hyperlight_guest_capi" +version = "0.0.0" +dependencies = [ + "hyperlight_guest_capi", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flatbuffers" +version = "25.12.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35f6839d7b3b98adde531effaf34f0c2badc6f4735d26fe74709d8e513a96ef3" +dependencies = [ + "bitflags", + "rustc_version", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hyperlight-common" +version = "0.15.0" +source = "git+https://github.com/hyperlight-dev/hyperlight?rev=v0.15.0#9749e047750a215dea680707433a12f9b33b0397" +dependencies = [ + "anyhow", + "flatbuffers", + "log", + "spin", + "thiserror", + "tracing-core", +] + +[[package]] +name = "hyperlight-guest" +version = "0.15.0" +source = "git+https://github.com/hyperlight-dev/hyperlight?rev=v0.15.0#9749e047750a215dea680707433a12f9b33b0397" +dependencies = [ + "anyhow", + "flatbuffers", + "hyperlight-common", + "serde_json", + "tracing", +] + +[[package]] +name = "hyperlight-guest-bin" +version = "0.15.0" +source = "git+https://github.com/hyperlight-dev/hyperlight?rev=v0.15.0#9749e047750a215dea680707433a12f9b33b0397" +dependencies = [ + "buddy_system_allocator", + "flatbuffers", + "hyperlight-common", + "hyperlight-guest", + "hyperlight-guest-macro", + "hyperlight-guest-tracing", + "hyperlight-libc", + "linkme", + "log", + "spin", + "tracing", +] + +[[package]] +name = "hyperlight-guest-macro" +version = "0.15.0" +source = "git+https://github.com/hyperlight-dev/hyperlight?rev=v0.15.0#9749e047750a215dea680707433a12f9b33b0397" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "hyperlight-guest-tracing" +version = "0.15.0" +source = "git+https://github.com/hyperlight-dev/hyperlight?rev=v0.15.0#9749e047750a215dea680707433a12f9b33b0397" +dependencies = [ + "hyperlight-common", + "spin", + "tracing", + "tracing-core", +] + +[[package]] +name = "hyperlight-libc" +version = "0.15.0" +source = "git+https://github.com/hyperlight-dev/hyperlight?rev=v0.15.0#9749e047750a215dea680707433a12f9b33b0397" +dependencies = [ + "anyhow", + "bindgen", + "cc", + "glob", +] + +[[package]] +name = "hyperlight_guest_capi" +version = "0.15.0" +source = "git+https://github.com/hyperlight-dev/hyperlight?rev=v0.15.0#9749e047750a215dea680707433a12f9b33b0397" +dependencies = [ + "cbindgen", + "flatbuffers", + "hyperlight-common", + "hyperlight-guest", + "hyperlight-guest-bin", + "log", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "linkme" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83272d46373fb8decca684579ac3e7c8f3d71d4cc3aa693df8759e260ae41cf" +dependencies = [ + "linkme-impl", +] + +[[package]] +name = "linkme-impl" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d59e20403c7d08fe62b4376edfe5c7fb2ef1e6b1465379686d0f21c8df444b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" + +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "regex" +version = "1.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +dependencies = [ + "lock_api", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml" +version = "0.9.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 0.7.15", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +dependencies = [ + "indexmap", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.3", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow 1.0.3", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/examples/c/fetch-capi/Cargo.toml b/examples/c/fetch-capi/Cargo.toml new file mode 100644 index 0000000..c4e153c --- /dev/null +++ b/examples/c/fetch-capi/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "fetch_hyperlight_guest_capi" +version = "0.0.0" +edition = "2024" +publish = false + +[dependencies] +hyperlight_guest_capi = { git = "https://github.com/hyperlight-dev/hyperlight", rev = "v0.15.0" } diff --git a/examples/c/fetch-capi/src/lib.rs b/examples/c/fetch-capi/src/lib.rs new file mode 100644 index 0000000..13f323d --- /dev/null +++ b/examples/c/fetch-capi/src/lib.rs @@ -0,0 +1,3 @@ +#![no_std] + +core::compile_error!("This crate is not meant to be built"); diff --git a/examples/c/guest/main.c b/examples/c/guest/main.c new file mode 100644 index 0000000..0126d00 --- /dev/null +++ b/examples/c/guest/main.c @@ -0,0 +1,19 @@ +#include +#include +#include + +int say_hello(const char *string) { + int n = printf("Hello %s\n", string); + fflush(stdout); + return n; +} + +HYPERLIGHT_WRAP_FUNCTION(say_hello, Int, 1, String); +void hyperlight_main(void) +{ + HYPERLIGHT_REGISTER_FUNCTION("SayHello", say_hello); +} + +hl_Vec *c_guest_dispatch_function(const hl_FunctionCall *function_call) { + return NULL; +} diff --git a/justfile b/justfile index 163dd4e..f97432a 100644 --- a/justfile +++ b/justfile @@ -30,3 +30,17 @@ run-guest: build-guest test-new: cargo test --test new + +test-clang-parser: + cargo test --test clang_parser + +test: test-new test-clang-parser + +build-c-sysroot: + cargo hyperlight build-c-sysroot --manifest-path examples/c/fetch-capi/Cargo.toml --c-sysroot-dir examples/c/sysroot + +build-c-guest: build-c-sysroot + examples/c/sysroot/bin/clang examples/c/guest/main.c -o examples/c/guest/guest -lhyperlight_guest_capi -fuse-ld=lld + +run-c-guest: build-c-guest + cargo run --manifest-path ./examples/host/Cargo.toml -- ./examples/c/guest/guest diff --git a/src/cli.rs b/src/cli.rs index a8298a0..17e0df8 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -17,6 +17,9 @@ pub struct Args { pub manifest_path: Option, pub target_dir: PathBuf, pub target: String, + pub host: String, + pub with_guest_capi: bool, + pub c_sysroot_dir: Option, pub env: HashMap, pub current_dir: PathBuf, pub clang: Option, @@ -170,10 +173,17 @@ impl Args { let target_dir = value.current_dir.join(target_dir); + let host = value + .host + .unwrap_or(env!("CARGO_HYPERLIGHT_HOST_TRIPLE").to_string()); + Ok(Args { manifest_path, target_dir, target, + host, + with_guest_capi: value.with_guest_capi, + c_sysroot_dir: value.c_sysroot_dir, env: value.env, current_dir: value.current_dir, clang: toolchain::find_cc().ok(), @@ -196,6 +206,17 @@ struct ArgsImpl { /// Target triple to build for target: Option, + /// Target triple to use for host utilities/wrappers, enabling a + /// building a distributable C sysroot for Canadian cross usecases + host: Option, + + /// Whether to include hyperlight-guest-capi headers and libs in + /// the built sysroot, used for building distributable C sysroots + with_guest_capi: bool, + + /// When building a C sysroot, the target C sysroot directory + c_sysroot_dir: Option, + /// Environment variables to set env: HashMap, @@ -203,6 +224,20 @@ struct ArgsImpl { pub current_dir: PathBuf, } +fn parse_flag(flag: &str, arg: &OsStr) -> Option { + let value = arg.strip_prefix(flag)?; + if value.is_empty() { + Some(true) + } else { + let lower = value.strip_prefix("=")?.to_ascii_lowercase(); + if lower == "false" || lower == "0" { + Some(false) + } else { + Some(true) + } + } +} + fn parse_arg( flag: &str, arg: &OsStr, @@ -237,6 +272,15 @@ impl ArgsImpl { this.target = Some(triplet.to_string_lossy().to_string()); continue; } + if let Some(host) = parse_arg("--host", &arg, &mut args) { + this.host = Some(host.to_string_lossy().to_string()); + } + if let Some(capi) = parse_flag("--with-guest-capi", &arg) { + this.with_guest_capi = capi; + } + if let Some(dir) = parse_arg("--c-sysroot-dir", &arg, &mut args) { + this.c_sysroot_dir = Some(PathBuf::from(dir)); + } } this } diff --git a/src/command.rs b/src/command.rs index c3abd13..cadf3cc 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1,9 +1,9 @@ use std::collections::{BTreeMap, HashMap}; +use std::env; use std::ffi::{OsStr, OsString}; use std::fmt::Debug; use std::path::{Path, PathBuf}; use std::process::Command as StdCommand; -use std::env; use anyhow::{Context, Result}; use os_str_bytes::OsStrBytesExt; @@ -545,6 +545,16 @@ impl Command { if arg_str.starts_with("--target=") || arg_str.starts_with("--target-dir=") { continue; } + if arg_str == "--host" { + skip_next = true; + continue; + } + if arg_str.starts_with("--host=") { + continue; + } + if arg_str.starts_with("--with-guest-capi") { + continue; + } command.arg(arg); } if let Some(cwd) = &self.current_dir { @@ -586,7 +596,7 @@ impl Command { self.cargo.path.as_os_str() } - fn build_args(&self) -> Args { + pub fn build_args(&self) -> Args { // parse the arguments and environment variables match Args::parse( self.get_args(), diff --git a/src/lib.rs b/src/lib.rs index 39a5897..4d0cf04 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,11 +4,13 @@ mod cargo_cmd; mod cli; mod command; mod sysroot; -mod toolchain; +pub mod toolchain; +pub mod toolchain_flags; use cargo_cmd::CargoCmd; -use cli::Args; +pub use cli::Args; pub use command::Command; +mod util; /// Constructs a new `Command` for launching cargo targeting /// [hyperlight](https://github.com/hyperlight-dev/hyperlight) guest code. @@ -58,12 +60,24 @@ impl Args { self.sysroot_dir().join("target") } - pub fn libs_dir(&self) -> std::path::PathBuf { + pub fn c_libs_dir(&self) -> std::path::PathBuf { + self.sysroot_dir().join("lib") + } + + pub fn wrapper_src_dir(&self) -> std::path::PathBuf { + self.sysroot_dir().join("wrapper") + } + + pub fn wrapper_dir(&self) -> std::path::PathBuf { + self.sysroot_dir().join("bin") + } + + pub fn rust_libs_dir(&self) -> std::path::PathBuf { self.triplet_dir().join("lib") } pub fn includes_dir(&self) -> std::path::PathBuf { - self.triplet_dir().join("include") + self.sysroot_dir().join("include") } pub fn crate_dir(&self) -> std::path::PathBuf { @@ -95,7 +109,7 @@ impl CargoCommandExt for std::process::Command { } else { // do nothing, let cc-rs find ar itself } - self.append_cflags(&args.target, toolchain::cflags(args)); + self.append_cflags(&args.target, toolchain::cflags(args).joined()); self } diff --git a/src/main.rs b/src/main.rs index e89babc..7d8617f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,102 @@ use std::env; +use std::ffi::{OsStr, OsString}; -use cargo_hyperlight::cargo; +use anyhow::{Result, anyhow}; +use cargo_hyperlight::{Args, cargo, toolchain, toolchain_flags}; mod new; mod perf; +mod util; const VERSION: &str = env!("CARGO_PKG_VERSION"); const GIT_HASH: &str = env!("GIT_HASH"); const GIT_DATE: &str = env!("GIT_DATE"); +enum FlagKind { + C, + Ld, + Libs, +} +impl FlagKind { + fn parse(x: &OsStr) -> Option { + use FlagKind::*; + match x.to_str()? { + "cflags" => Some(C), + "ldflags" => Some(Ld), + "libs" => Some(Libs), + _ => None, + } + } + fn get_flags(&self, args: &Args) -> toolchain_flags::Flags { + use FlagKind::*; + match self { + C => toolchain::cflags(args), + Ld => toolchain::ldflags(args), + Libs => toolchain::libs(args), + } + } +} +enum SpecialCommand { + Version, + Perf, + New, + BuildCSysroot, + Flags(FlagKind), +} +impl SpecialCommand { + fn parse(x: &OsStr) -> Option { + use SpecialCommand::*; + match x.to_str()? { + "--version" | "-V" => Some(Version), + "perf" => Some(Perf), + "new" => Some(New), + "build-c-sysroot" => Some(BuildCSysroot), + _ => FlagKind::parse(x).map(Flags), + } + } + fn execute(self, args: impl Iterator) -> Result<()> { + use SpecialCommand::*; + match self { + Version => { + println!("cargo-hyperlight {} ({} {})", VERSION, GIT_HASH, GIT_DATE); + } + Perf => perf::run(args)?, + New => new::run(args)?, + BuildCSysroot => { + let built_args = cargo()? + // a C sysroot needs the C API library + .arg("--with-guest-capi") + .args(args) + .build_args(); + let sysroot_dir = built_args + .c_sysroot_dir + .as_ref() + .ok_or(anyhow!("Usage: cargo-hyperlight build-c-sysroot [opts] --c-sysroot-dir "))?; + built_args.prepare_sysroot()?; + util::copy_glob(&built_args.wrapper_dir(), &sysroot_dir.join("bin"), "**/*")?; + util::copy_glob( + &built_args.includes_dir(), + &sysroot_dir.join("include"), + "**/*.h", + )?; + util::copy_glob_with_predicate( + &built_args.c_libs_dir(), + &sysroot_dir.join("lib"), + "**/*", + |x| !x.starts_with("rustlib"), + )?; + } + Flags(k) => { + let built_args = cargo()?.args(args).build_args(); + built_args.prepare_sysroot()?; + let flags = k.get_flags(&built_args).joined(); + println!("{}", flags.to_str().ok_or(anyhow!("flags were not UTF-8"))?); + } + } + Ok(()) + } +} + fn main() { // Skip binary name; when invoked as `cargo hyperlight`, cargo passes // "hyperlight" as argv[1] — skip that too. @@ -17,28 +105,16 @@ fn main() { args.next(); } - match args.peek().map(|a| a.to_os_string()) { - Some(a) if a == "--version" || a == "-V" => { - println!("cargo-hyperlight {} ({} {})", VERSION, GIT_HASH, GIT_DATE); - } - Some(a) if a == "perf" => { - if let Err(e) = perf::run(args) { - eprintln!("{e:?}"); - std::process::exit(1); - } - } - Some(a) if a == "new" => { - if let Err(e) = new::run(args) { - eprintln!("{e:?}"); - std::process::exit(1); - } - } - _ => { - cargo() - .expect("Failed to create cargo command") - .args(args) - .status() - .expect("Failed to execute cargo"); + if let Some(sc) = args.peek().and_then(|x| SpecialCommand::parse(x)) { + if let Err(e) = sc.execute(args) { + eprintln!("{e:?}"); + std::process::exit(1); } + } else { + cargo() + .expect("Failed to create cargo command") + .args(args) + .status() + .expect("Failed to execute cargo"); } } diff --git a/src/sysroot.rs b/src/sysroot.rs index 8c75c0c..0e49de7 100644 --- a/src/sysroot.rs +++ b/src/sysroot.rs @@ -10,18 +10,18 @@ use crate::cli::Args; const CARGO_TOML: &str = include_str!("dummy/_Cargo.toml"); const LIB_RS: &str = include_str!("dummy/_lib.rs"); -#[derive(serde::Deserialize, Default)] -struct CargoBuildMessageTarget { - name: String, +#[derive(serde::Deserialize, Default, Debug)] +pub(crate) struct CargoBuildMessageTarget { + pub(crate) name: String, } -#[derive(serde::Deserialize)] -struct CargoBuildMessage { - reason: String, +#[derive(serde::Deserialize, Debug)] +pub(crate) struct CargoBuildMessage { + pub(crate) reason: String, #[serde(default)] - target: CargoBuildMessageTarget, + pub(crate) target: CargoBuildMessageTarget, #[serde(default)] - filenames: Vec, + pub(crate) filenames: Vec, } pub fn build(args: &Args) -> Result<()> { @@ -77,7 +77,7 @@ Supported values are: let target_dir = args.build_dir(); let triplet_dir = args.triplet_dir(); let crate_dir = args.crate_dir(); - let lib_dir = args.libs_dir(); + let lib_dir = args.rust_libs_dir(); std::fs::create_dir_all(&triplet_dir).context("Failed to create sysroot directories")?; std::fs::write( diff --git a/src/toolchain.rs b/src/toolchain.rs index 6a2fd96..f74b4f1 100644 --- a/src/toolchain.rs +++ b/src/toolchain.rs @@ -1,11 +1,14 @@ -use std::ffi::OsString; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; -use anyhow::{Context, Result, bail}; +use anyhow::{Context, Result, ensure}; +use proc_macro2::TokenStream; +use quote::{TokenStreamExt, quote}; use regex::Regex; use crate::cargo_cmd::{CargoCmd, cargo_cmd}; use crate::cli::Args; +use crate::sysroot::CargoBuildMessage; +use crate::{toolchain_flags, util}; #[derive(serde::Deserialize)] struct CargoMetadata { @@ -21,7 +24,43 @@ struct CargoMetadataPackage { version: semver::Version, } -pub fn find_libc_dir(args: &Args) -> Result { +struct PackageDirectories { + hyperlight_libc: Option, + hyperlight_guest_bin: Option, + hyperlight_guest_capi: Option, +} +impl PackageDirectories { + fn libc(&self) -> Result { + self.hyperlight_libc + .as_ref() + .or(self.hyperlight_guest_bin.as_ref()) + .cloned() + .context( + "Could not find hyperlight-libc or hyperlight-guest-bin package in cargo metadata", + ) + } + fn guest_capi(&self) -> Result { + self.hyperlight_guest_capi + .clone() + .context("Could not find hyperlight-guest-capi package in cargo metadata") + } +} + +fn find_package_dir(metadata: &CargoMetadata, name: &str) -> Result> { + metadata + .packages + .iter() + .find(|x| x.name == name) + .map(|pkg| { + pkg.manifest_path + .parent() + .with_context(|| format!("Failed to get directory for {name}")) + .map(|x| x.to_path_buf()) + }) + .transpose() +} + +fn find_package_dirs(args: &Args) -> Result { let metadata = cargo_cmd()? .env_clear() .envs(args.env.iter()) @@ -37,115 +76,233 @@ pub fn find_libc_dir(args: &Args) -> Result { let metadata = serde_json::from_slice::(&metadata.stdout) .context("Failed to parse cargo metadata")?; - let hyperlight_libc = metadata - .packages - .iter() - .find(|pkg| pkg.name == "hyperlight-libc"); - - if let Some(hyperlight_libc) = hyperlight_libc { - let hyperlight_libc_dir = hyperlight_libc - .manifest_path - .parent() - .context("Failed to get directory for hyperlight-libc")?; - return Ok(hyperlight_libc_dir.to_path_buf()); + Ok(PackageDirectories { + hyperlight_libc: find_package_dir(&metadata, "hyperlight-libc")?, + hyperlight_guest_bin: find_package_dir(&metadata, "hyperlight-guest-bin")?, + hyperlight_guest_capi: if args.with_guest_capi { + find_package_dir(&metadata, "hyperlight_guest_capi")? + } else { + None + }, + }) +} + +fn copy_includes(src_dir: &Path, dst_dir: &Path) -> Result<()> { + util::copy_glob(src_dir, dst_dir, "**/*.h") +} + +fn build_guest_capi(args: &Args, capi_dir: &Path) -> Result<()> { + use crate::CargoCommandExt; + let output = cargo_cmd()? + .env_clear() + .envs(args.env.iter()) + .arg("build") + .manifest_path(&Some(capi_dir.join("Cargo.toml"))) + .target_dir(args.build_dir()) + .arg("--message-format=json") + .env_remove("RUSTC_WORKSPACE_WRAPPER") + .populate_from_args(args) + .output() + .context("Failed to build capi cargo project")?; + ensure!( + output.status.success(), + "Failed to build capi\n{}", + String::from_utf8_lossy(&output.stderr) + ); + + let messages = String::from_utf8_lossy(&output.stdout); + + for message in messages.lines() { + let message = serde_json::from_str::(message) + .context("Failed to parse sysroot build message")?; + if message.reason == "compiler-artifact" { + let name = message.target.name; + if name == "hyperlight_guest_capi" { + for file in message.filenames { + let file_name = file.file_name().with_context(|| { + format!( + "Failed to get filename for capi build artifact {}", + file.display() + ) + })?; + let dst = args.c_libs_dir().join(file_name); + std::fs::copy(&file, &dst)?; + } + } + } } - let hyperlight_guest_bin = metadata - .packages - .iter() - .find(|pkg| pkg.name == "hyperlight-guest-bin"); - - if let Some(hyperlight_guest_bin) = hyperlight_guest_bin { - let hyperlight_guest_bin_dir = hyperlight_guest_bin - .manifest_path - .parent() - .context("Failed to get directory for hyperlight-guest-bin")?; - return Ok(hyperlight_guest_bin_dir.to_path_buf()); + Ok(()) +} + +fn path_to_tokens(p: &Path) -> TokenStream { + let mut tokens = quote! { + let mut x: ::std::path::PathBuf = ::std::path::PathBuf::new(); + }; + for x in p.iter() { + let s = x.to_string_lossy(); + tokens.append_all(quote! { + x.push(#s); + }); } + tokens.append_all(quote! { x }); + quote! { { #tokens } } +} - bail!("Could not find hyperlight-libc or hyperlight-guest-bin package in cargo metadata"); +fn build_wrappers(args: &Args) -> Result<()> { + const CARGO_TOML: &str = include_str!("wrapper/_Cargo.toml"); + const MAIN_RS: &str = include_str!("wrapper/_main.rs"); + const CLANG_PARSER_RS: &str = include_str!("wrapper/_clang_parser.rs"); + const FLAGS_RS: &str = include_str!("toolchain_flags.rs"); + + let wrapper_src_dir = args.wrapper_src_dir(); + std::fs::create_dir_all(&wrapper_src_dir) + .context("Failed to create wrapper source directory")?; + std::fs::write(wrapper_src_dir.join("Cargo.toml"), CARGO_TOML)?; + let wrapper_src_src_dir = wrapper_src_dir.join("src"); + std::fs::create_dir_all(&wrapper_src_src_dir) + .context("Failed to create wrapper source src directory")?; + std::fs::write(wrapper_src_src_dir.join("main.rs"), MAIN_RS)?; + std::fs::write(wrapper_src_src_dir.join("clang_parser.rs"), CLANG_PARSER_RS)?; + std::fs::write(wrapper_src_src_dir.join("toolchain_flags.rs"), FLAGS_RS)?; + let includes_toks = path_to_tokens(args.includes_dir().strip_prefix(args.sysroot_dir())?); + let c_libs_toks = path_to_tokens(args.c_libs_dir().strip_prefix(args.sysroot_dir())?); + let wrapper_toks = path_to_tokens(args.wrapper_dir().strip_prefix(args.sysroot_dir())?); + let target = &args.target; + let with_guest_capi = args.with_guest_capi; + std::fs::write( + wrapper_src_src_dir.join("args.rs"), + (quote! { + pub(crate) fn args(root: &std::path::Path) -> crate::toolchain_flags::Args { + crate::toolchain_flags::Args { + includes_dir: root.join(#includes_toks), + c_libs_dir: root.join(#c_libs_toks), + wrapper_dir: root.join(#wrapper_toks), + target: #target.to_string(), + with_guest_capi: #with_guest_capi, + } + } + }) + .to_string(), + )?; + + let output = cargo_cmd()? + .env_clear() + .envs(args.env.iter()) + .current_dir(&args.current_dir) + .arg("build") + .target(&args.host) + .manifest_path(&Some(wrapper_src_dir.join("Cargo.toml"))) + .target_dir(args.build_dir()) + .arg("--release") + .arg("--message-format=json") + .env_remove("RUSTC_WORKSPACE_WRAPPER") + .output() + .context("Failed to build wrapper cargo project")?; + ensure!( + output.status.success(), + "Failed to build wrapper\n{}", + String::from_utf8_lossy(&output.stderr) + ); + + let messages = String::from_utf8_lossy(&output.stdout); + + for message in messages.lines() { + let message = serde_json::from_str::(message) + .context("Failed to parse wrapper build message")?; + if message.reason == "compiler-artifact" { + let name = message.target.name; + if name == "hyperlight-sysroot-wrappers" { + let files: Vec<_> = message + .filenames + .iter() + .filter(|x| x.extension() != Some("pdb".as_ref())) + .collect(); + ensure!( + files.len() == 1, + "hyperlight-sysroot-wrappers produced wrong number of binaries", + ); + let target_uses_exe = message.filenames[0].extension() == Some("exe".as_ref()); + let dir = args.wrapper_dir(); + let bin_name = |n| { + let mut p = dir.join(n); + if target_uses_exe { + p.set_extension("exe"); + } + p + }; + std::fs::create_dir_all(&dir).context("Failed to create wrapper bin directory")?; + std::fs::copy(&message.filenames[0], bin_name("hyperlight-config"))?; + std::fs::copy(&message.filenames[0], bin_name("clang"))?; + std::fs::copy( + &message.filenames[0], + bin_name(&format!("{}-clang", target)), + )?; + } + } + } + Ok(()) } pub fn prepare(args: &Args) -> Result<()> { - let libc_dir = find_libc_dir(args)?; + let package_dirs = find_package_dirs(args)?; + let libc_dir = package_dirs.libc()?; let include_dst_dir = args.includes_dir(); std::fs::create_dir_all(&include_dst_dir) .context("Failed to create sysroot include directory")?; + // Detect which libc variant is present: picolibc or legacy musl + let mut include_dirs: Vec<&str> = vec![ + // directories for musl + "third_party/printf/", + "third_party/musl/include", + "third_party/musl/arch/generic", + "third_party/musl/src/internal", + // directories for picolibc + "third_party/picolibc/libc/include", + "third_party/picolibc/libc/stdio", + "include", + ]; if !args.target.starts_with("aarch64") { - // Detect which libc variant is present: picolibc or legacy musl - let include_dirs: &[&str] = &[ - // directories for musl - "third_party/printf/", - "third_party/musl/include", - "third_party/musl/arch/generic", - "third_party/musl/arch/x86_64", - "third_party/musl/src/internal", - // directories for picolibc - "third_party/picolibc/libc/include", - "third_party/picolibc/libc/stdio", - "include", - ]; - - for dir in include_dirs { - let include_src_dir = libc_dir.join(dir); - let files = glob::glob(&format!("{}/**/*.h", include_src_dir.display())) - .context("Failed to read include source directory")?; - for file in files { - let src = file.context("Failed to read include source file")?; - let dst = src.strip_prefix(&include_src_dir).unwrap(); - let dst = include_dst_dir.join(dst); - - std::fs::create_dir_all(dst.parent().unwrap()) - .context("Failed to create include subdirectory")?; - std::fs::copy(&src, &dst).context("Failed to copy include file")?; - } - } + include_dirs.push("third_party/musl/arch/x86_64"); } - Ok(()) -} + for dir in include_dirs { + copy_includes(&libc_dir.join(dir), &include_dst_dir)?; + } + if args.with_guest_capi { + let capi_dir = package_dirs.guest_capi()?; + build_guest_capi(args, &capi_dir)?; + copy_includes(&capi_dir.join("include"), &include_dst_dir)?; + } -pub fn cflags(args: &Args) -> OsString { - let clang_target = if args.target.starts_with("aarch64") { - "--target=aarch64-unknown-linux-none" - } else { - "--target=x86_64-unknown-linux-none" - }; + build_wrappers(args)?; - let common_flags: &[&str] = &[ - clang_target, - "-U__linux__", - "-fPIC", - // We don't support stack protectors at the moment, but Arch Linux clang - // auto-enables them for -linux platforms, so explicitly disable them. - "-fno-stack-protector", - "-fstack-clash-protection", - "-mstack-probe-size=4096", - "-nostdlibinc", - // Define HYPERLIGHT as we use this to conditionally enable/disable code - // in the libc headers - "-DHYPERLIGHT=1", - "-D__HYPERLIGHT__=1", - ]; + Ok(()) +} - let mut flags = OsString::new(); - for flag in common_flags { - flags.push(flag); - flags.push(" "); +impl From<&Args> for toolchain_flags::Args { + fn from(args: &Args) -> toolchain_flags::Args { + toolchain_flags::Args { + includes_dir: args.includes_dir(), + c_libs_dir: args.c_libs_dir(), + wrapper_dir: args.wrapper_dir(), + target: args.target.clone(), + with_guest_capi: args.with_guest_capi, + } } +} - // x86_64-specific flags - if args.target.starts_with("x86_64") { - flags.push("-mno-red-zone "); - } - flags.push(" "); - flags.push("-isystem"); - flags.push(" "); - flags.push(args.includes_dir().as_os_str()); - flags +pub fn cflags(args: &Args) -> toolchain_flags::Flags { + toolchain_flags::cflags(&args.into()) +} +pub fn ldflags(args: &Args) -> toolchain_flags::Flags { + toolchain_flags::ldflags(&args.into()) +} +pub fn libs(args: &Args) -> toolchain_flags::Flags { + toolchain_flags::libs(&args.into()) } pub fn find_cc() -> Result { diff --git a/src/toolchain_flags.rs b/src/toolchain_flags.rs new file mode 100644 index 0000000..e931449 --- /dev/null +++ b/src/toolchain_flags.rs @@ -0,0 +1,112 @@ +// this file is used both from the cargo-hyperlight library (via +// `mod`) and from the wrapper and config scripts that are built for +// $HOST in a package specified in wrapper/_Cargo.toml. So, it can +// only use imports available in both those environments. + +use std::borrow::Cow; +use std::ffi::{OsStr, OsString}; +use std::path::PathBuf; + +pub(crate) struct Args { + pub(crate) includes_dir: PathBuf, + pub(crate) c_libs_dir: PathBuf, + #[allow(unused)] // used in the wrapper, but not the host + pub(crate) wrapper_dir: PathBuf, + pub(crate) target: String, + pub(crate) with_guest_capi: bool, +} +impl Args { + fn clang_target(&self) -> &'static OsStr { + if self.target.starts_with("aarch64") { + "--target=aarch64-unknown-linux-none".as_ref() + } else { + "--target=x86_64-unknown-linux-none".as_ref() + } + } +} + +fn os(x: &(impl AsRef + ?Sized)) -> Cow<'_, OsStr> { + x.as_ref().into() +} + +pub struct Flags(pub(crate) Vec>); +impl Flags { + /// This will behave badly with spaces/special characters/etc, + /// which is why it is called `joined` and not + /// `joined_escaped`. That's OK for all the flags we use for now. + pub fn joined(&self) -> OsString { + let mut all = OsString::new(); + all.push(&self.0[0]); + for flag in &self.0[1..] { + all.push(os(" ")); + all.push(flag); + } + all + } +} + +pub(crate) fn cflags(args: &Args) -> Flags { + const COMMON_FLAGS: &[&str] = &[ + "-U__linux__", + "-fPIC", + // We don't support stack protectors at the moment, but Arch Linux clang + // auto-enables them for -linux platforms, so explicitly disable them. + "-fno-stack-protector", + "-fstack-clash-protection", + "-mstack-probe-size=4096", + "-nostdlibinc", + // Define HYPERLIGHT as we use this to conditionally enable/disable code + // in the libc headers + "-DHYPERLIGHT=1", + "-D__HYPERLIGHT__=1", + ]; + + const X86_64_FLAGS: &[&str] = &["-mno-red-zone"]; + + const AARCH64_FLAGS: &[&str] = &["-mstrict-align", "-march=armv8.1-a+fp+simd"]; + + let mut flags: Vec> = Vec::new(); + flags.push(args.clang_target().into()); + COMMON_FLAGS + .iter() + .chain(if args.target.starts_with("x86_64") { + X86_64_FLAGS.iter() + } else if args.target.starts_with("aarch64") { + AARCH64_FLAGS.iter() + } else { + [].iter() + }) + .for_each(|x| flags.push(os(x))); + + flags.push(os("-isystem")); + flags.push(Cow::Owned(args.includes_dir.clone().into())); + Flags(flags) +} + +pub(crate) fn ldflags(args: &Args) -> Flags { + const COMMON_FLAGS: &[&str] = &[ + "-e", + "entrypoint", + "-nostdlib", + "-pie", + "-Wl,--no-dynamic-linker", + ]; + + let mut flags = Vec::new(); + flags.push(args.clang_target().into()); + COMMON_FLAGS.iter().for_each(|x| flags.push(os(x))); + + flags.push(os("-L")); + flags.push(Cow::Owned(args.c_libs_dir.clone().into())); + + Flags(flags) +} + +pub(crate) fn libs(args: &Args) -> Flags { + let mut flags = Vec::new(); + if args.with_guest_capi { + flags.push(os("-l")); + flags.push(os("hyperlight_guest_capi")); + } + Flags(flags) +} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..8848c52 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,31 @@ +use std::fs; +use std::path::Path; + +use anyhow::{Context, Result}; + +pub(crate) fn copy_glob_with_predicate( + src_dir: &Path, + dst_dir: &Path, + glob: &str, + predicate: impl Fn(&Path) -> bool, +) -> Result<()> { + let files = glob::glob(&format!("{}/{}", src_dir.display(), glob)) + .context("Failed to read include source directory")?; + for file in files { + let src = file.context("Failed to read include source file")?; + let dst = src.strip_prefix(src_dir).unwrap(); + if !predicate(dst) { + continue; + } + let dst = dst_dir.join(dst); + + fs::create_dir_all(dst.parent().unwrap()) + .context("Failed to create include subdirectory")?; + fs::copy(&src, &dst).context("Failed to copy include file")?; + } + Ok(()) +} + +pub(crate) fn copy_glob(src_dir: &Path, dst_dir: &Path, glob: &str) -> Result<()> { + copy_glob_with_predicate(src_dir, dst_dir, glob, |_| true) +} diff --git a/src/wrapper/_Cargo.toml b/src/wrapper/_Cargo.toml new file mode 100644 index 0000000..706dfc4 --- /dev/null +++ b/src/wrapper/_Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "hyperlight-sysroot-wrappers" +version = "0.0.0" +edition = "2024" + +[workspace] + +[dependencies] \ No newline at end of file diff --git a/src/wrapper/_clang_parser.rs b/src/wrapper/_clang_parser.rs new file mode 100644 index 0000000..ab799a6 --- /dev/null +++ b/src/wrapper/_clang_parser.rs @@ -0,0 +1,65 @@ +use std::ffi::OsStr; + +#[derive(Copy, Clone)] +pub(crate) struct ClangCmdInfo { + pub(crate) has_non_flag: bool, + pub(crate) has_non_link_flag: bool, +} +impl ClangCmdInfo { + pub(crate) fn will_link(self) -> bool { + !self.has_non_link_flag && self.has_non_flag + } +} + +pub(crate) fn parse_clang_cmd<'a>(cmd: impl Iterator) -> ClangCmdInfo { + // We can add cflags unconditionally, but to know whether or not + // to add ldflags, we need to know whether this compiler driver + // invocation is expected to link or not. + // + // We look through the arguments to produce two pieces of state: + // - `found_non_link_flag`: If true, we found some flag (like -c) + // which means that the compiler driver will not invoke the + // linker, so we should not include the linker + // arguments. Currently, we detect: + // + `-c`, `-S`, `-E`, `-M`, `-MM` + // + `-x -header` + // - `found_non_flag`: If true, there was at least one non-flag + // argument, so the driver will actually be compiling + // something. This is important to catch someone running + // e.g. `clang -v` and avoid inserting the linker arguments, + // which would trigger a great number of warnings. + let mut flags_valid: bool = true; + let mut found_non_link_flag: bool = false; + let mut found_non_flag: bool = false; + let mut iter = cmd.map(OsStr::to_str).map(Option::unwrap); + while let Some(arg) = iter.next() { + if !flags_valid || &arg[0..1] != "-" { + found_non_flag = true; + } + if arg == "--" { + flags_valid = false; + } + if flags_valid && &arg[0..1] == "-" { + let flag = &arg[1..]; + if flag == "c" || flag == "S" || flag == "E" || flag == "M" || flag == "MM" { + found_non_link_flag = true; + } + if &flag[0..1] == "x" { + let lang = if flag.len() > 1 { + Some(&flag[1..]) + } else { + iter.next() + }; + if let Some(lang) = lang + && lang.ends_with("-header") + { + found_non_link_flag = true; + } + } + } + } + ClangCmdInfo { + has_non_link_flag: found_non_link_flag, + has_non_flag: found_non_flag, + } +} diff --git a/src/wrapper/_main.rs b/src/wrapper/_main.rs new file mode 100644 index 0000000..8379661 --- /dev/null +++ b/src/wrapper/_main.rs @@ -0,0 +1,107 @@ +use std::borrow::Cow; +use std::env; +use std::ffi::{OsStr, OsString}; +use std::fs; +use std::path::{self, Path, PathBuf}; +use std::process::exit; + +mod args; +mod toolchain_flags; +mod clang_parser; + +use toolchain_flags::Args; +use clang_parser::parse_clang_cmd; + +fn usage() -> ! { + eprintln!("This multi-call binary should be used as one of the following names: +- hyperlight-config +- clang +"); + exit(1); +} + +fn hyperlight_config(args: &Args, mut cmd: env::ArgsOs) { + while let Some(flag) = cmd.next() { + if flag == "--cflags" { + println!("{}", toolchain_flags::cflags(&args).joined().to_str().unwrap()); + } else if flag == "--ldflags" { + println!("{}", toolchain_flags::ldflags(&args).joined().to_str().unwrap()); + } else if flag == "--libs" { + println!("{}", toolchain_flags::libs(&args).joined().to_str().unwrap()); + } else { + eprintln!("Unknown flag `{}`", flag.display()); + exit(1); + } + } +} + +/// Since path entries may not exist (especially on Windows), +/// use the original if there are any errors. +fn normalize(x: &Path) -> Cow<'_, Path> { + if let Ok(y) = fs::canonicalize(x) { + Cow::Owned(y) + } else { + Cow::Borrowed(x) + } +} + +fn find_next(args: &Args, tool_name: &str) -> PathBuf { + let var_name = format!("HYPERLIGHT_TOOLCHAIN_{}", tool_name.to_lowercase().replace("-", "_")); + if let Some(path) = env::var_os(var_name) { + return path.into(); + } + let wrapper_dir = normalize(&args.wrapper_dir); + let path = env::var_os("PATH").expect("$PATH should exist"); + for path in env::split_paths(&path) { + let abs_path = normalize(&path); + if abs_path == wrapper_dir { + continue; + } + let base_path = path.join(tool_name); + if base_path.exists() { + return base_path; + } + let exe_path = base_path.with_extension("exe"); + if exe_path.exists() { + return exe_path; + } + } + panic!("Could not find base system implementation of {}", tool_name); +} + +fn clang(args: &Args, tool_name: &str, cmd: env::ArgsOs) { + let cmd: Vec<_> = cmd.collect(); + let info = parse_clang_cmd(cmd.iter().map(>::as_ref)); + let mut proc = std::process::Command::new(find_next(args, tool_name)); + proc.args(toolchain_flags::cflags(&args).0); + if info.will_link() { + proc.args(toolchain_flags::ldflags(&args).0); + } + proc.args(cmd); + // TODO: should we auto-add libs? It's probably more flexible to not + proc.status().expect("Failed to run system clang"); +} + +fn main() { + let mut cmd = env::args_os(); + let Some(bin) = cmd.next() else { usage(); }; + let mut bin = PathBuf::from(bin); + if bin.extension() == Some("exe".as_ref()) { + bin.set_extension(""); + } + let bin_name = bin.file_name().unwrap().to_str().unwrap(); + + let sysroot_base = bin.parent().unwrap().parent().unwrap(); + // use absolute instead of fs::canonicalize because Clang does not + // properly support \\?\ paths in include paths. + let sysroot_base = path::absolute(sysroot_base).unwrap(); + let args = args::args(&sysroot_base); + + if bin_name == "hyperlight-config" { + hyperlight_config(&args, cmd); + } else if bin_name == "clang" || bin_name == format!("{0}-clang", args.target) { + clang(&args, &bin_name, cmd); + } else { + usage(); + } +} diff --git a/tests/clang_parser.rs b/tests/clang_parser.rs new file mode 100644 index 0000000..73681b5 --- /dev/null +++ b/tests/clang_parser.rs @@ -0,0 +1,61 @@ +//! Tests for the logic used in the clang frontend wrapper + +#[path = "../src/wrapper/_clang_parser.rs"] +mod clang_parser; + +use std::ffi::OsStr; + +use clang_parser::parse_clang_cmd; + +macro_rules! parse_test { + ($name:ident, $args:tt, $non_flag:tt, $non_link_flag:tt, $will_link:tt) => { + #[test] + fn $name() { + let args: &[&str] = &$args; + let strs = args.iter().map(|x| OsStr::new(x)); + let info = parse_clang_cmd(strs); + assert_eq!(info.has_non_flag, $non_flag); + assert_eq!(info.has_non_link_flag, $non_link_flag); + assert_eq!(info.will_link(), $will_link); + } + }; +} + +parse_test!(empty, [], false, false, false); +parse_test!(no_args, ["-v"], false, false, false); +parse_test!(linking, ["foo.c"], true, false, true); +parse_test!(non_link_flag_c, ["-c", "foo.c"], true, true, false); +parse_test!(non_link_flag_s, ["-S", "foo.c"], true, true, false); +parse_test!(non_link_flag_e, ["-E", "foo.c"], true, true, false); +parse_test!(non_link_flag_m, ["-M", "foo.c"], true, true, false); +parse_test!(non_link_flag_mm, ["-MM", "foo.c"], true, true, false); +parse_test!( + non_link_flag_lang_space, + ["-x", "foo-header", "foo.c"], + true, + true, + false +); +parse_test!( + non_link_flag_lang_nospace, + ["-xfoo-header", "foo.c"], + true, + true, + false +); +parse_test!(end_of_flags_empty, ["--"], false, false, false); +parse_test!(end_of_flags_with_flag_like, ["--", "-v"], true, false, true); +parse_test!( + end_of_flags_with_non_link_flag_like, + ["--", "-c"], + true, + false, + true +); +parse_test!( + end_of_flags_with_non_link_flag, + ["-c", "--", "-v"], + true, + true, + false +); From 47e729079aa0e1f79d848219d8cdbbc3d3a59a8b Mon Sep 17 00:00:00 2001 From: Lucy Menon <168595099+syntactically@users.noreply.github.com> Date: Mon, 22 Jun 2026 23:43:07 +0100 Subject: [PATCH 08/10] Tweak the search logic for `ar` for better macOS compatibility Signed-off-by: Lucy Menon <168595099+syntactically@users.noreply.github.com> --- src/toolchain.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/toolchain.rs b/src/toolchain.rs index f74b4f1..8472f62 100644 --- a/src/toolchain.rs +++ b/src/toolchain.rs @@ -318,12 +318,21 @@ pub fn find_cc() -> Result { } pub fn find_ar() -> Result { - if let Ok(path) = which::which("ar") { - return Ok(path); - } - if let Ok(path) = which::which("llvm-ar") { - return Ok(path); + #[cfg(not(target_os = "macos"))] + let ar = which::which("ar"); + let llvm_ar = which::which("llvm-ar"); + // The system archiver on macOS can't deal with ELFs, so check + // `llvm-ar` first there (but stillfall back to `ar` when it's not + // available, since the correct LLVM ar is named `ar` in some + // environments, like when building in Nix); + #[cfg(target_os = "macos")] + let preferred_ar = llvm_ar.or(ar); + #[cfg(not(target_os = "macos"))] + let preferred_ar = ar.or(llvm_ar); + if let Ok(ar) = preferred_ar { + return Ok(ar); } + // try with postfixed version llvm-ar, e.g., llvm-ar-20 let re = Regex::new(r"llvm-ar-\d+").unwrap(); which::which_re(&re) From 0a7f97418ae478bf9df15ef5a20dd93af938a110 Mon Sep 17 00:00:00 2001 From: Lucy Menon <168595099+syntactically@users.noreply.github.com> Date: Wed, 24 Jun 2026 09:20:18 +0100 Subject: [PATCH 09/10] Run CI on Hyperlight CI images as well as GH images Previously, the CI checks that cargo-hyperlight can actually build a guest ran only on the GH hosted runners, which are poorly-specified and not particularly reflective of the environments where cargo-hyperlight is actually called upon. This changes the workflow to also run on the same runners that we use for Hyperlight CI. Signed-off-by: Lucy Menon <168595099+syntactically@users.noreply.github.com> --- .github/workflows/ci.yml | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 930313f..bed6591 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,18 +21,27 @@ env: jobs: run-tests: - name: Run tests on ${{ matrix.os }} + name: Run tests on ${{ matrix.environment }} ${{ matrix.os }} strategy: matrix: - os: ["ubuntu-latest", "windows-latest"] - runs-on: ${{ matrix.os }} + os: [ "ubuntu-latest", "windows-latest" ] + environment: [ "HL", "GH" ] + runs-on: ${{ fromJson( + matrix.environment == 'HL' && + format('["self-hosted", "{0}", "X64", "1ES.Pool=hld-{1}-amd", "JobId=cargo-hyperlight-{2}-{3}-{4}"]', + matrix.os == 'windows-latest' && 'Windows' || 'Linux', + matrix.os == 'windows-latest' && 'win2025' || 'kvm', + github.run_id, + github.run_number, + github.run_attempt) + || format('["{0}"]', matrix.os) ) }} steps: - uses: actions/checkout@v7 - uses: actions-rust-lang/setup-rust-toolchain@v1 - uses: Swatinem/rust-cache@v2 - uses: extractions/setup-just@v4 - name: Enable kvm - if: runner.os == 'Linux' && runner.arch == 'X64' + if: runner.os == 'Linux' && runner.arch == 'X64' && matrix.environment == 'GH' run: | echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules sudo udevadm control --reload-rules From f883481df73cba9aac5f292b28ddb2a30554e5d8 Mon Sep 17 00:00:00 2001 From: Lucy Menon <168595099+syntactically@users.noreply.github.com> Date: Wed, 24 Jun 2026 21:22:07 +0100 Subject: [PATCH 10/10] Do a better job of cleaning up copied sysroot directories (e.g. includes) Previously, the sysroot build copied include files from several different directories into the final sysroot include dir, but did not do anything to reset the state of the include directory. If the same sysroot dir was used to build sysroots from multiple different versions of hyperlight-libc, since the headers provided are different, downstream code would get a confusing (and likely to fail to compile) mix of the include files from the two libcs. This changes the way that the include directory (and other copied sysroot directories) are constructed, to ensure that stale files are removed as well as new files being copied. In a somewhat-related issue, when building a C sysroot, cargo-hyperlight needs to build hyperlight-libc (as part of hyperlight-guest-capi), and it turns out that that build was itself getting the output include directory, which meant that rebuidls could also result in stale include files being added. This commit changes the logic involved in building hyperlight-guest-capi to ensure that the output sysroot include directory (which is not yet populated) isn't on the include path. Signed-off-by: Lucy Menon <168595099+syntactically@users.noreply.github.com> --- src/command.rs | 4 +- src/lib.rs | 6 +-- src/main.rs | 18 ++++---- src/toolchain.rs | 32 ++++++++------ src/toolchain_flags.rs | 8 ++-- src/util.rs | 94 +++++++++++++++++++++++++++++++++++------- src/wrapper/_main.rs | 4 +- 7 files changed, 121 insertions(+), 45 deletions(-) diff --git a/src/command.rs b/src/command.rs index cadf3cc..86a1155 100644 --- a/src/command.rs +++ b/src/command.rs @@ -64,7 +64,7 @@ impl Debug for Command { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let args = self.build_args_infallible(); let mut cmd = self.command(); - cmd.populate_from_args(&args); + cmd.populate_from_args(&args, false); write!(f, "env ")?; if let Some(current_dir) = &self.current_dir { @@ -659,7 +659,7 @@ impl Command { .context("Failed to prepare sysroot")?; self.command() - .populate_from_args(&args) + .populate_from_args(&args, false) .checked_status() .context("Failed to execute cargo")?; Ok(()) diff --git a/src/lib.rs b/src/lib.rs index 4d0cf04..230492a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,11 +86,11 @@ impl Args { } trait CargoCommandExt { - fn populate_from_args(&mut self, args: &Args) -> &mut Self; + fn populate_from_args(&mut self, args: &Args, bootstrap: bool) -> &mut Self; } impl CargoCommandExt for std::process::Command { - fn populate_from_args(&mut self, args: &Args) -> &mut Self { + fn populate_from_args(&mut self, args: &Args, bootstrap: bool) -> &mut Self { self.target(&args.target); self.sysroot(args.sysroot_dir()); self.append_rustflags("--cfg=hyperlight"); @@ -109,7 +109,7 @@ impl CargoCommandExt for std::process::Command { } else { // do nothing, let cc-rs find ar itself } - self.append_cflags(&args.target, toolchain::cflags(args).joined()); + self.append_cflags(&args.target, toolchain::cflags(args, bootstrap).joined()); self } diff --git a/src/main.rs b/src/main.rs index 7d8617f..b8e152a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ -use std::env; use std::ffi::{OsStr, OsString}; +use std::{env, iter}; use anyhow::{Result, anyhow}; use cargo_hyperlight::{Args, cargo, toolchain, toolchain_flags}; @@ -30,7 +30,7 @@ impl FlagKind { fn get_flags(&self, args: &Args) -> toolchain_flags::Flags { use FlagKind::*; match self { - C => toolchain::cflags(args), + C => toolchain::cflags(args, false), Ld => toolchain::ldflags(args), Libs => toolchain::libs(args), } @@ -73,14 +73,18 @@ impl SpecialCommand { .as_ref() .ok_or(anyhow!("Usage: cargo-hyperlight build-c-sysroot [opts] --c-sysroot-dir "))?; built_args.prepare_sysroot()?; - util::copy_glob(&built_args.wrapper_dir(), &sysroot_dir.join("bin"), "**/*")?; - util::copy_glob( - &built_args.includes_dir(), + util::union_glob( + iter::once(&built_args.wrapper_dir()), + &sysroot_dir.join("bin"), + "**/*", + )?; + util::union_glob( + iter::once(&built_args.includes_dir()), &sysroot_dir.join("include"), "**/*.h", )?; - util::copy_glob_with_predicate( - &built_args.c_libs_dir(), + util::union_glob_with_predicate( + iter::once(&built_args.c_libs_dir()), &sysroot_dir.join("lib"), "**/*", |x| !x.starts_with("rustlib"), diff --git a/src/toolchain.rs b/src/toolchain.rs index 8472f62..e410bd2 100644 --- a/src/toolchain.rs +++ b/src/toolchain.rs @@ -87,8 +87,8 @@ fn find_package_dirs(args: &Args) -> Result { }) } -fn copy_includes(src_dir: &Path, dst_dir: &Path) -> Result<()> { - util::copy_glob(src_dir, dst_dir, "**/*.h") +fn copy_includes(src_dirs: impl Iterator>, dst_dir: &Path) -> Result<()> { + util::union_glob(src_dirs, dst_dir, "**/*.h") } fn build_guest_capi(args: &Args, capi_dir: &Path) -> Result<()> { @@ -101,7 +101,7 @@ fn build_guest_capi(args: &Args, capi_dir: &Path) -> Result<()> { .target_dir(args.build_dir()) .arg("--message-format=json") .env_remove("RUSTC_WORKSPACE_WRAPPER") - .populate_from_args(args) + .populate_from_args(args, true) .output() .context("Failed to build capi cargo project")?; ensure!( @@ -269,14 +269,20 @@ pub fn prepare(args: &Args) -> Result<()> { include_dirs.push("third_party/musl/arch/x86_64"); } - for dir in include_dirs { - copy_includes(&libc_dir.join(dir), &include_dst_dir)?; - } - if args.with_guest_capi { - let capi_dir = package_dirs.guest_capi()?; - build_guest_capi(args, &capi_dir)?; - copy_includes(&capi_dir.join("include"), &include_dst_dir)?; - } + let capi_dir = args + .with_guest_capi + .then(|| { + let d = package_dirs.guest_capi()?; + build_guest_capi(args, &d)?; + Ok::<_, anyhow::Error>(d) + }) + .transpose()?; + + let include_dirs = include_dirs + .into_iter() + .map(|dir| libc_dir.join(dir)) + .chain(capi_dir.map(|x| x.join("include"))); + copy_includes(include_dirs, &include_dst_dir)?; build_wrappers(args)?; @@ -295,8 +301,8 @@ impl From<&Args> for toolchain_flags::Args { } } -pub fn cflags(args: &Args) -> toolchain_flags::Flags { - toolchain_flags::cflags(&args.into()) +pub fn cflags(args: &Args, bootstrap: bool) -> toolchain_flags::Flags { + toolchain_flags::cflags(&args.into(), bootstrap) } pub fn ldflags(args: &Args) -> toolchain_flags::Flags { toolchain_flags::ldflags(&args.into()) diff --git a/src/toolchain_flags.rs b/src/toolchain_flags.rs index e931449..ac9a970 100644 --- a/src/toolchain_flags.rs +++ b/src/toolchain_flags.rs @@ -45,7 +45,7 @@ impl Flags { } } -pub(crate) fn cflags(args: &Args) -> Flags { +pub(crate) fn cflags(args: &Args, bootstrap: bool) -> Flags { const COMMON_FLAGS: &[&str] = &[ "-U__linux__", "-fPIC", @@ -78,8 +78,10 @@ pub(crate) fn cflags(args: &Args) -> Flags { }) .for_each(|x| flags.push(os(x))); - flags.push(os("-isystem")); - flags.push(Cow::Owned(args.includes_dir.clone().into())); + if !bootstrap { + flags.push(os("-isystem")); + flags.push(Cow::Owned(args.includes_dir.clone().into())); + } Flags(flags) } diff --git a/src/util.rs b/src/util.rs index 8848c52..6de08cf 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,31 +1,95 @@ +use std::collections::HashSet; use std::fs; use std::path::Path; use anyhow::{Context, Result}; -pub(crate) fn copy_glob_with_predicate( - src_dir: &Path, +/// Make the `dst_dir` into the union of a subset of the directory +/// trees rooted at `src_dirs`. The files considered to be part of the +/// src directory trees are those for which +/// - the pattern `glob` matches their (relative) path; and +/// - the `predicate` returns true +/// +/// Bug: extraneous empty directories may remain in the output +/// directory +/// +/// For example, with a filesystem layout of: +/// a/ +/// b.h +/// no.y +/// skip.h +/// b/ +/// c.h +/// c/ +/// d.h +/// e.x +/// Calling `union_glob_with_predicate` with `srcs` set to `["a", +/// "b"]` and `dst` to `"c"`, the glob `"*.h"`, and the predicate +/// `|path| path != "skip.h"` would result in `c` containing +/// c/ +/// b.h +/// c.h +pub(crate) fn union_glob_with_predicate( + src_dirs: impl Iterator>, dst_dir: &Path, glob: &str, predicate: impl Fn(&Path) -> bool, ) -> Result<()> { - let files = glob::glob(&format!("{}/{}", src_dir.display(), glob)) - .context("Failed to read include source directory")?; - for file in files { - let src = file.context("Failed to read include source file")?; - let dst = src.strip_prefix(src_dir).unwrap(); - if !predicate(dst) { - continue; + let mut copied = HashSet::new(); + + for src_dir in src_dirs { + let src_dir = src_dir.as_ref(); + let files = glob::glob(&format!("{}/{}", src_dir.display(), glob)) + .context("Failed to read source directory")?; + for file in files { + let src = file.context("Failed to read source file")?; + if !src.is_file() { + // contents will also show up + continue; + } + let suffix = src.strip_prefix(src_dir)?; + if !predicate(suffix) { + continue; + } + let dst = dst_dir.join(suffix); + + let parent_dir = dst.parent().ok_or(anyhow::anyhow!( + "Could not get parent directory of destination file" + ))?; + fs::create_dir_all(parent_dir).context("Failed to create subdirectory")?; + fs::copy(&src, &dst).context("Failed to copy file")?; + let canon = dst + .canonicalize() + .context("Failed to canonicalize copied file")?; + copied.insert(canon); } - let dst = dst_dir.join(dst); + } - fs::create_dir_all(dst.parent().unwrap()) - .context("Failed to create include subdirectory")?; - fs::copy(&src, &dst).context("Failed to copy include file")?; + // This doesn't do a good job of cleaning up empty directories, + // but that shouldn't cause any problems right now, since this is + // only used for header files/libraries/binaries. + let dst_files = glob::glob(&format!("{}/**/*", dst_dir.display())) + .context("Failed to read destination directory")?; + for dst_file in dst_files { + let dst = dst_file.context("Failed to read destination file")?; + let canon = dst + .canonicalize() + .context("Failed to canonicalize existing file")?; + if !copied.contains(&canon) && dst.is_file() { + fs::remove_file(canon).context("Failed to remove stale destination file")?; + } } Ok(()) } -pub(crate) fn copy_glob(src_dir: &Path, dst_dir: &Path, glob: &str) -> Result<()> { - copy_glob_with_predicate(src_dir, dst_dir, glob, |_| true) +/// See the documentation for [`union_glob_with_predicate`] above; +/// this convenience wrapper simply removes the need to explicitly +/// pass a predicate when all files matching the glob should be +/// included. +pub(crate) fn union_glob( + src_dir: impl Iterator>, + dst_dir: &Path, + glob: &str, +) -> Result<()> { + union_glob_with_predicate(src_dir, dst_dir, glob, |_| true) } diff --git a/src/wrapper/_main.rs b/src/wrapper/_main.rs index 8379661..f3d77fe 100644 --- a/src/wrapper/_main.rs +++ b/src/wrapper/_main.rs @@ -23,7 +23,7 @@ fn usage() -> ! { fn hyperlight_config(args: &Args, mut cmd: env::ArgsOs) { while let Some(flag) = cmd.next() { if flag == "--cflags" { - println!("{}", toolchain_flags::cflags(&args).joined().to_str().unwrap()); + println!("{}", toolchain_flags::cflags(&args, false).joined().to_str().unwrap()); } else if flag == "--ldflags" { println!("{}", toolchain_flags::ldflags(&args).joined().to_str().unwrap()); } else if flag == "--libs" { @@ -73,7 +73,7 @@ fn clang(args: &Args, tool_name: &str, cmd: env::ArgsOs) { let cmd: Vec<_> = cmd.collect(); let info = parse_clang_cmd(cmd.iter().map(>::as_ref)); let mut proc = std::process::Command::new(find_next(args, tool_name)); - proc.args(toolchain_flags::cflags(&args).0); + proc.args(toolchain_flags::cflags(&args, false).0); if info.will_link() { proc.args(toolchain_flags::ldflags(&args).0); }