diff --git a/.github/workflows/RustNightly.yml b/.github/workflows/RustNightly.yml index 8a23ee71e..f3bbcedda 100644 --- a/.github/workflows/RustNightly.yml +++ b/.github/workflows/RustNightly.yml @@ -80,7 +80,7 @@ jobs: - name: Install cargo-hyperlight uses: taiki-e/install-action@5939f3337e40968c39aa70f5ecb1417a92fb25a0 # v2.75.15 with: - tool: cargo-hyperlight@0.1.8 + tool: cargo-hyperlight@0.1.9 - name: Build and move Rust guests run: | diff --git a/.github/workflows/dep_build_guests.yml b/.github/workflows/dep_build_guests.yml index 38c52f7a7..0e62b7ba0 100644 --- a/.github/workflows/dep_build_guests.yml +++ b/.github/workflows/dep_build_guests.yml @@ -70,7 +70,7 @@ jobs: - name: Install cargo-hyperlight uses: taiki-e/install-action@5939f3337e40968c39aa70f5ecb1417a92fb25a0 # v2.75.15 with: - tool: cargo-hyperlight@0.1.8 + tool: cargo-hyperlight@0.1.9 - name: Build Rust guests run: | diff --git a/.github/workflows/dep_code_checks.yml b/.github/workflows/dep_code_checks.yml index 62232fbab..4b14712eb 100644 --- a/.github/workflows/dep_code_checks.yml +++ b/.github/workflows/dep_code_checks.yml @@ -76,7 +76,7 @@ jobs: - name: Install cargo-hyperlight uses: taiki-e/install-action@5939f3337e40968c39aa70f5ecb1417a92fb25a0 # v2.75.15 with: - tool: cargo-hyperlight@0.1.8 + tool: cargo-hyperlight@0.1.9 - name: clippy exhaustive check (debug) run: just clippy-exhaustive debug @@ -149,7 +149,7 @@ jobs: - name: Install cargo-hyperlight uses: taiki-e/install-action@5939f3337e40968c39aa70f5ecb1417a92fb25a0 # v2.75.15 with: - tool: cargo-hyperlight@0.1.8 + tool: cargo-hyperlight@0.1.9 - name: clippy (debug) run: | diff --git a/c.just b/c.just index 502b03c8a..957669952 100644 --- a/c.just +++ b/c.just @@ -3,7 +3,7 @@ mkdir := if os() == "windows" { "mkdir -f -p" } else { "mkdir -p"} # Elf options # We don't support stack protectors at the moment, but Arch Linux clang auto-enables them for -linux platforms, so explicitly disable them. c-compile-options-elf := '-nostdlibinc -H --target=x86_64-unknown-linux-none -fno-stack-protector -fstack-clash-protection -mstack-probe-size=4096 -fPIC' -c-include-flags-elf := "-I " + root / "src/hyperlight_guest_capi/include/" + " -I " + root / "src/hyperlight_guest_bin/third_party/picolibc/libc/include/" + " -I " + root / "src/hyperlight_guest_bin/third_party/picolibc/libc/stdio/" +c-include-flags-elf := "-I " + root / "src/hyperlight_guest_capi/include/" + " -I " + root / "src/hyperlight_guest_bin/third_party/picolibc/libc/include/" + " -I " + root / "src/hyperlight_guest_bin/third_party/picolibc/libc/stdio/" + " -I " + root / "src/hyperlight_guest_bin/include/" c-linker-options-elf := '--entry "entrypoint" --nostdlib -pie --no-dynamic-linker' c-flags-debug-elf := '-O0' c-flags-release-elf := '-O3' diff --git a/src/hyperlight_guest_bin/build.rs b/src/hyperlight_guest_bin/build.rs index ac9e351ed..310f7f6fb 100644 --- a/src/hyperlight_guest_bin/build.rs +++ b/src/hyperlight_guest_bin/build.rs @@ -16,9 +16,8 @@ limitations under the License. mod build_files; -use std::io::Write; use std::path::{Path, PathBuf}; -use std::process::{Command, Stdio}; +use std::process::Command; use std::{env, fs}; use anyhow::{Context, Result, bail}; @@ -51,144 +50,6 @@ fn copy_includes, Q: AsRef + std::fmt::Debug>( Ok(()) } -fn detect_cc_feature(test_code: &str) -> Result { - let cc = std::env::var("HYPERLIGHT_GUEST_clang").unwrap_or_else(|_| "clang".into()); - - let mut cmd = Command::new(&cc) - .arg("-std=c18") - .arg("-x") - .arg("c") - .arg("-") - .arg("-fsyntax-only") - .stdin(Stdio::piped()) - .stderr(Stdio::null()) - .stdout(Stdio::null()) - .spawn() - .with_context(|| "failed to spawn compiler for feature test")?; - - cmd.stdin - .as_mut() - .unwrap() - .write_all(test_code.as_bytes()) - .with_context(|| "failed to write test code")?; - - Ok(cmd - .wait() - .with_context(|| "failed to wait on cc command")? - .success()) -} - -fn gen_config_file(config_dir: &Path) -> Result<()> { - let config_h = config_dir.join("picolibc.h"); - - let mut file = fs::File::create(&config_h) - .with_context(|| format!("Could not create config file {:?}", config_h))?; - - writeln!(file, "/* Generated by hyperlight build script */")?; - writeln!(file, "#pragma once")?; - writeln!(file)?; - - writeln!(file, "#define _NEWLIB_VERSION \"4.3.0\"")?; - writeln!(file, "#define __NEWLIB_VERSION__ \"4.3.0\"")?; - writeln!(file, "#define __NEWLIB__ 4")?; - writeln!(file, "#define __NEWLIB_MINOR__ 3")?; - writeln!(file, "#define __NEWLIB_PATCHLEVEL__ 0")?; - writeln!(file, "#define __PICOLIBC_VERSION__ \"1.8.11\"")?; - writeln!(file, "#define __PICOLIBC__ 1")?; - writeln!(file, "#define __PICOLIBC_MINOR__ 8")?; - writeln!(file, "#define __PICOLIBC_PATCHLEVEL__ 11")?; - writeln!(file)?; - - // Static configuration - writeln!(file, "#define __ASSERT_VERBOSE")?; - writeln!(file, "#define __SINGLE_THREAD")?; // -Dsingle-thread=true - writeln!(file, "#define __GLOBAL_ERRNO")?; // -Dnewlib-global-errno=true - writeln!(file, "#define __INIT_FINI_ARRAY")?; // -Dinitfini-array=true - writeln!(file, "#define __TINY_STDIO")?; // tinystdio is now the only stdio - writeln!(file, "#define __IO_DEFAULT 'd'")?; // -Dformat-default=double - writeln!(file, "#define __IO_FLOAT_EXACT")?; // default - writeln!(file, "#define __IO_WCHAR")?; // -Dio-wchar=true - writeln!(file, "#define __IEEE_LIBM")?; // math library without errno - writeln!(file, "#define __FAST_STRCMP")?; // default optimization - writeln!(file, "#define __FAST_BUFIO")?; // -Dfast-bufio=true - writeln!(file, "#define __IO_SMALL_ULTOA")?; // avoid division in conversion - - for test in &[ - "__builtin_alloca", - "__builtin_ffs", - "__builtin_ffsl", - "__builtin_ffsll", - "__builtin_ctz", - "__builtin_ctzl", - "__builtin_ctzll", - "__builtin_copysign", - "__builtin_copysignl", - "__builtin_isinf", - "__builtin_isinfl", - "__builtin_isnan", - "__builtin_isnanl", - "__builtin_isfinite", - "__builtin_finitel", - "__builtin_issignalingl", - "__builtin_expect", - "__builtin_complex", - "__builtin_add_overflow", - "__builtin_mul_overflow", - ] { - let code = format!( - r#" - #if __has_builtin({test}) - int main() {{ return 0; }} - #else - #error "Builtin not supported" - #endif - "# - ); - - let has = if detect_cc_feature(&code)? { 1 } else { 0 }; - writeln!( - file, - "#define __HAVE_BUILTIN_{} {has}", - test.trim_start_matches("__builtin_").to_uppercase() - )?; - } - - let code = r#"_Complex double x = 1.0 + 2.0*I; int main() { return 0; }"#; - let has = if detect_cc_feature(code)? { 1 } else { 0 }; - writeln!(file, "#define __HAVE_COMPLEX {has}")?; - - // Static undefs - writeln!(file, "#undef __ARM_SEMIHOST")?; // -Dsemihost=false - writeln!(file, "#undef __SEMIHOST")?; // -Dsemihost=false - writeln!(file, "#undef __THREAD_LOCAL_STORAGE")?; // -Dthread-local-storage=false - writeln!(file, "#undef __THREAD_LOCAL_STORAGE_API")?; - writeln!(file, "#undef __THREAD_LOCAL_STORAGE_RP2040")?; - writeln!(file, "#undef __THREAD_LOCAL_STORAGE_STACK_GUARD")?; - writeln!(file, "#undef __ENABLE_MALLOC")?; // -Denable-malloc=false - writeln!(file, "#undef __MALLOC_CLEAR_FREED")?; - writeln!(file, "#undef __MB_CAPABLE")?; // no multibyte support - writeln!(file, "#undef __HAVE_FCNTL")?; // freestanding environment - writeln!(file, "#undef __STDIO_LOCKING")?; // single-thread - writeln!(file, "#undef __IO_C99_FORMATS")?; // -Dio-c99-formats=false - writeln!(file, "#undef __IO_LONG_DOUBLE")?; // not enabled - writeln!(file, "#undef __IO_LONG_LONG")?; // minimal format - writeln!(file, "#undef __IO_MINIMAL_LONG_LONG")?; - writeln!(file, "#undef __IO_PERCENT_B")?; // not enabled - writeln!(file, "#undef __IO_PERCENT_N")?; // not enabled - writeln!(file, "#undef __IO_POS_ARGS")?; // not enabled - writeln!(file, "#undef __MATH_ERRNO")?; // IEEE math only - writeln!(file, "#undef __OBSOLETE_MATH")?; - writeln!(file, "#undef __OBSOLETE_MATH_DOUBLE")?; - writeln!(file, "#undef __OBSOLETE_MATH_FLOAT")?; - writeln!(file, "#undef __PREFER_SIZE_OVER_SPEED")?; // release build - writeln!(file, "#undef __ATOMIC_UNGETC")?; // single-thread - writeln!(file, "#undef __IEEEFP_FUNCS")?; - writeln!(file, "#undef __INIT_FINI_FUNCS")?; // using INIT_FINI_ARRAY instead - writeln!(file, "#undef __HAVE_BITFIELDS_IN_PACKED_STRUCTS")?; - - Ok(()) -} - fn cc_build(picolibc_dir: &PathBuf, target: &str) -> Result { let mut build = cc::Build::new(); let compiler = env::var("HYPERLIGHT_GUEST_clang").unwrap_or("clang".to_string()); @@ -252,11 +113,6 @@ fn cc_build(picolibc_dir: &PathBuf, target: &str) -> Result { .define("_LIBC", None) .define("_FILE_OFFSET_BITS", "64"); - // TODO: this is wrong, it should generate config file to out_dir/include - // but how downstream crates get at it then? And TBH I think having piclibc - // headers (features.h) depend on a build time header is odd - gen_config_file(&picolibc_dir.join("libc/include"))?; - match target { "x86" | "x86_64" => { build.include(picolibc_dir.join("libm/machine/x86")); @@ -323,6 +179,7 @@ fn init_submodule() -> Result<()> { fn cargo_main() -> Result<()> { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=third_party/picolibc"); + println!("cargo:rerun-if-changed=include/picolibc.h"); println!("cargo:rerun-if-env-changed=HYPERLIGHT_GUEST_TOOLCHAIN_ROOT"); let out_dir = env::var("OUT_DIR").expect("cargo OUT_DIR not set"); @@ -332,15 +189,21 @@ fn cargo_main() -> Result<()> { fs::create_dir_all(&include_dir) .with_context(|| format!("Could not create include dir {include_dir:?}"))?; + let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("cargo MANIFEST_DIR not set"); + let manifest_dir = PathBuf::from(manifest_dir); + let picolibc_dir = manifest_dir.join("third_party/picolibc"); + if cfg!(feature = "libc") { - if !Path::new("third_party/picolibc/COPYING.picolibc").exists() { + if !picolibc_dir.join("COPYING.picolibc").exists() { eprintln!("Setting up submodules"); init_submodule().with_context(|| "failed to init picolibc submodule")?; } - let picolibc_dir = PathBuf::from("third_party/picolibc"); let mut build = cc_build(&picolibc_dir, &target)?; + // include for picolibc configuration: picolibc.h + build.include(manifest_dir.join("include")); + add_libc(&mut build, &picolibc_dir, &target)?; add_libm(&mut build, &picolibc_dir, &target)?; @@ -349,7 +212,8 @@ fn cargo_main() -> Result<()> { } build.compile("hyperlight_guest_bin"); - copy_includes(&include_dir, "third_party/picolibc/libc/include")?; + copy_includes(&include_dir, picolibc_dir.join("libc/include"))?; + copy_includes(&include_dir, manifest_dir.join("include"))?; } let include_str = include_dir diff --git a/src/hyperlight_guest_bin/include/picolibc.h b/src/hyperlight_guest_bin/include/picolibc.h new file mode 100644 index 000000000..eab7fe59b --- /dev/null +++ b/src/hyperlight_guest_bin/include/picolibc.h @@ -0,0 +1,145 @@ +/* +Copyright 2026 The Hyperlight Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#pragma once + +// version information +#define _NEWLIB_VERSION "4.3.0" +#define __NEWLIB_VERSION__ "4.3.0" +#define __NEWLIB__ 4 +#define __NEWLIB_MINOR__ 3 +#define __NEWLIB_PATCHLEVEL__ 0 +#define __PICOLIBC_VERSION__ "1.8.11" +#define __PICOLIBC__ 1 +#define __PICOLIBC_MINOR__ 8 +#define __PICOLIBC_PATCHLEVEL__ 11 + +// static configuration - enabled features +#define __ASSERT_VERBOSE +#define __SINGLE_THREAD // -Dsingle-thread=true +#define __GLOBAL_ERRNO // -Dnewlib-global-errno=true +#define __INIT_FINI_ARRAY // -Dinitfini-array=true +#define __TINY_STDIO // tinystdio is now the only stdio +#define __IO_DEFAULT 'd' // -Dformat-default=double +#define __IO_FLOAT_EXACT // default +#define __IO_WCHAR // -Dio-wchar=true +#define __IEEE_LIBM // math library without errno +#define __FAST_STRCMP // default optimization +#define __FAST_BUFIO // -Dfast-bufio=true +#define __IO_SMALL_ULTOA // avoid division in conversion + +// static configuration - disabled features +#undef __ARM_SEMIHOST // -Dsemihost=false +#undef __SEMIHOST // -Dsemihost=false +#undef __THREAD_LOCAL_STORAGE // -Dthread-local-storage=false +#undef __THREAD_LOCAL_STORAGE_API +#undef __THREAD_LOCAL_STORAGE_RP2040 +#undef __THREAD_LOCAL_STORAGE_STACK_GUARD +#undef __ENABLE_MALLOC // -Denable-malloc=false +#undef __MALLOC_CLEAR_FREED +#undef __MB_CAPABLE // no multibyte support +#undef __HAVE_FCNTL // freestanding environment +#undef __STDIO_LOCKING // single-thread +#undef __IO_C99_FORMATS // -Dio-c99-formats=false +#undef __IO_LONG_DOUBLE // not enabled +#undef __IO_LONG_LONG // minimal format +#undef __IO_MINIMAL_LONG_LONG +#undef __IO_PERCENT_B // not enabled +#undef __IO_PERCENT_N // not enabled +#undef __IO_POS_ARGS // not enabled +#undef __MATH_ERRNO // IEEE math only +#undef __OBSOLETE_MATH +#undef __OBSOLETE_MATH_DOUBLE +#undef __OBSOLETE_MATH_FLOAT +#undef __PREFER_SIZE_OVER_SPEED // release build +#undef __ATOMIC_UNGETC // single-thread +#undef __IEEEFP_FUNCS +#undef __INIT_FINI_FUNCS // using INIT_FINI_ARRAY instead +#undef __HAVE_BITFIELDS_IN_PACKED_STRUCTS + +// compiler feature detection +#ifndef __has_builtin +#define picolibc_has_builtin(x) 0 +#else +#define picolibc_has_builtin(x) __has_builtin(x) +#endif + +#if picolibc_has_builtin(__builtin_alloca) +#define __HAVE_BUILTIN_ALLOCA 1 +#endif +#if picolibc_has_builtin(__builtin_ffs) +#define __HAVE_BUILTIN_FFS 1 +#endif +#if picolibc_has_builtin(__builtin_ffsl) +#define __HAVE_BUILTIN_FFSL 1 +#endif +#if picolibc_has_builtin(__builtin_ffsll) +#define __HAVE_BUILTIN_FFSLL 1 +#endif +#if picolibc_has_builtin(__builtin_ctz) +#define __HAVE_BUILTIN_CTZ 1 +#endif +#if picolibc_has_builtin(__builtin_ctzl) +#define __HAVE_BUILTIN_CTZL 1 +#endif +#if picolibc_has_builtin(__builtin_ctzll) +#define __HAVE_BUILTIN_CTZLL 1 +#endif +#if picolibc_has_builtin(__builtin_copysign) +#define __HAVE_BUILTIN_COPYSIGN 1 +#endif +#if picolibc_has_builtin(__builtin_copysignl) +#define __HAVE_BUILTIN_COPYSIGNL 1 +#endif +#if picolibc_has_builtin(__builtin_isinf) +#define __HAVE_BUILTIN_ISINF 1 +#endif +#if picolibc_has_builtin(__builtin_isinfl) +#define __HAVE_BUILTIN_ISINFL 1 +#endif +#if picolibc_has_builtin(__builtin_isnan) +#define __HAVE_BUILTIN_ISNAN 1 +#endif +#if picolibc_has_builtin(__builtin_isnanl) +#define __HAVE_BUILTIN_ISNANL 1 +#endif +#if picolibc_has_builtin(__builtin_isfinite) +#define __HAVE_BUILTIN_ISFINITE 1 +#endif +#if picolibc_has_builtin(__builtin_finitel) +#define __HAVE_BUILTIN_FINITEL 1 +#endif +#if picolibc_has_builtin(__builtin_issignalingl) +#define __HAVE_BUILTIN_ISSIGNALINGL 1 +#endif +#if picolibc_has_builtin(__builtin_expect) +#define __HAVE_BUILTIN_EXPECT 1 +#endif +#if picolibc_has_builtin(__builtin_complex) +#define __HAVE_BUILTIN_COMPLEX 1 +#endif +#if picolibc_has_builtin(__builtin_add_overflow) +#define __HAVE_BUILTIN_ADD_OVERFLOW 1 +#endif +#if picolibc_has_builtin(__builtin_mul_overflow) +#define __HAVE_BUILTIN_MUL_OVERFLOW 1 +#endif + +#if !defined(__STDC_NO_COMPLEX__) +#define __HAVE_COMPLEX 1 +#endif + +#undef picolibc_has_builtin