From e7fd9c0bf0703f94e0f9e90fbfb96323d44dd1a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Doru=20Bl=C3=A2nzeanu?= Date: Thu, 24 Jul 2025 00:29:32 +0300 Subject: [PATCH 1/2] Enable native debugging for wasm modules/components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Doru Blânzeanu --- .vscode/launch.json | 106 ++++++++++++++---- Cargo.lock | 40 +++++++ Justfile | 18 +-- src/hyperlight_wasm/Cargo.toml | 8 ++ src/hyperlight_wasm/build.rs | 12 +- .../examples/guest-debugging/main.rs | 99 ++++++++++++++++ .../scripts/build-wasm-examples.sh | 77 ++++++++++--- .../src/sandbox/sandbox_builder.rs | 22 ++++ src/hyperlight_wasm_aot/Cargo.toml | 3 + src/hyperlight_wasm_aot/src/main.rs | 12 ++ src/rust_wasm_samples/build.rs | 2 +- src/wasm_runtime/Cargo.lock | 12 ++ src/wasm_runtime/Cargo.toml | 4 + src/wasm_runtime/build.rs | 4 + src/wasm_runtime/src/component.rs | 2 + src/wasm_runtime/src/module.rs | 2 + 16 files changed, 372 insertions(+), 51 deletions(-) create mode 100644 src/hyperlight_wasm/examples/guest-debugging/main.rs diff --git a/.vscode/launch.json b/.vscode/launch.json index 01cb304..cf6632a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,34 +1,96 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - // this can be used to debug tests - "version": "0.2.0", + "inputs": [ + { + "id": "program", + "type": "promptString", + "default": "x64/debug/wasm_runtime", + "description": "Path to the program to debug", + } + ], "configurations": [ { "type": "lldb", "request": "launch", - "name": "Cargo test", + "name": "Debug example 'guest-debugging'", + "cargo": { + "args": [ + "build", + "--example=guest-debugging", + "--package=hyperlight-wasm", + "--features=gdb", + ], + "filter": { + "name": "guest-debugging", + "kind": "example" + } + }, + "env": { + "RUST_DIR_FOR_DEBUGGING_TESTS": "${workspaceFolder}/src/hyperlight_wasm", + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug example 'rust_wasm_examples'", "cargo": { - "args": [ - "test", - "--profile=dev", - "--lib", - "--no-run" - - ], - "filter": { - "name": "hyperlight_wasm", - "kind": "lib" - } + "args": [ + "build", + "--example=rust_wasm_examples", + "--package=hyperlight-wasm", + ], + "filter": { + "name": "rust_wasm_examples", + "kind": "example" + } }, "env": { - "RUST_DIR_FOR_DEBUGGING_TESTS": "${workspaceFolder}/src/hyperlight_wasm" + "RUST_DIR_FOR_DEBUGGING_TESTS": "${workspaceFolder}/src/hyperlight_wasm", + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "name": "Remote GDB attach", + "type": "cppdbg", + "request": "launch", + "program": "${input:program}", + "args": [], + "stopAtEntry": true, + "hardwareBreakpoints": { + "require": false, + "limit": 4 }, - "args": [ - "--exact", - "sandbox::loaded_wasm_sandbox::tests::test_call_host_func_with_vecbytes" + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "miDebuggerServerAddress": "localhost:8080", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + }, ] - } + }, + { + "name": "Remote LLDB attach", + "type": "lldb", + "request": "launch", + "targetCreateCommands": [ + "target create ${input:program}", + ], + "processCreateCommands": [ + "gdb-remote localhost:8080" + ], + }, ] } \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 61fa886..2cb0d29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1011,6 +1011,30 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "gdbstub" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b686b198dfaa4109ebd0443d2841bc521e4b4b2915f1d84b3bb50332a8cdc1ae" +dependencies = [ + "bitflags 2.9.3", + "cfg-if", + "log", + "managed", + "num-traits", + "paste", +] + +[[package]] +name = "gdbstub_arch" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22dde0e1b68787036ccedd0b1ff6f953527a0e807e571fbe898975203027278f" +dependencies = [ + "gdbstub", + "num-traits", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1328,6 +1352,8 @@ dependencies = [ "crossbeam-channel", "elfcore", "flatbuffers", + "gdbstub", + "gdbstub_arch", "goblin", "hyperlight-common", "kvm-bindings", @@ -1367,9 +1393,11 @@ dependencies = [ "blake3", "built", "cfg-if", + "cfg_aliases", "chrono", "criterion", "crossbeam-queue", + "env_logger", "examples_common", "goblin", "hyperlight-component-macro", @@ -1774,6 +1802,12 @@ dependencies = [ "libc", ] +[[package]] +name = "managed" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" + [[package]] name = "memchr" version = "2.7.5" @@ -2052,6 +2086,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "percent-encoding" version = "2.3.1" diff --git a/Justfile b/Justfile index 9d3adf8..7b4a226 100644 --- a/Justfile +++ b/Justfile @@ -19,7 +19,7 @@ ensure-tools: cargo install cargo-component --locked --version 0.21.1 cargo install wit-bindgen-cli --locked --version 0.43.0 -build-all target=default-target: (build target) (build-wasm-examples target) (build-rust-wasm-examples target) (build-rust-component-examples target) (build-wasm-runtime target) +build-all target=default-target features="": (build target features) (build-wasm-examples target features) (build-rust-wasm-examples target features) (build-rust-component-examples target) (build-wasm-runtime target features) build target=default-target features="": (fmt-check) cargo build {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --verbose --profile={{ if target == "debug" {"dev"} else { target } }} @@ -32,23 +32,23 @@ compile-wit: wasm-tools component wit ./src/wasmsamples/components/runcomponent.wit -w -o ./src/wasmsamples/components/runcomponent-world.wasm wasm-tools component wit ./src/component_sample/wit/example.wit -w -o ./src/component_sample/wit/component-world.wasm -build-wasm-runtime target=default-target: - cd ./src/wasm_runtime && cargo build --verbose --profile={{ if target == "debug" {"dev"} else { target } }} && rm -R target +build-wasm-runtime target=default-target features="": + cd ./src/wasm_runtime && cargo build --verbose {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--features " + features } }} --profile={{ if target == "debug" {"dev"} else { target } }} && rm -R target -build-wasm-examples target=default-target: (compile-wit) - {{ build-wasm-examples-command }} {{target}} +build-wasm-examples target=default-target features="": (compile-wit) + {{ build-wasm-examples-command }} {{target}} {{features}} -build-rust-wasm-examples target=default-target: (mkdir-redist target) +build-rust-wasm-examples target=default-target features="": (mkdir-redist target) rustup target add wasm32-unknown-unknown cd ./src/rust_wasm_samples && cargo build --target wasm32-unknown-unknown --profile={{ if target == "debug" {"dev"} else { target } }} - cargo run -p hyperlight-wasm-aot compile ./src/rust_wasm_samples/target/wasm32-unknown-unknown/{{ target }}/rust_wasm_samples.wasm ./x64/{{ target }}/rust_wasm_samples.aot + cargo run {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--features " + features } }} -p hyperlight-wasm-aot compile ./src/rust_wasm_samples/target/wasm32-unknown-unknown/{{ target }}/rust_wasm_samples.wasm ./x64/{{ target }}/rust_wasm_samples.aot -build-rust-component-examples target=default-target: (compile-wit) +build-rust-component-examples target=default-target features="": (compile-wit) # use cargo component so we don't get all the wasi imports https://github.com/bytecodealliance/cargo-component?tab=readme-ov-file#relationship-with-wasm32-wasip2 # we also explicitly target wasm32-unknown-unknown since cargo component might try to pull in wasi imports https://github.com/bytecodealliance/cargo-component/issues/290 rustup target add wasm32-unknown-unknown cd ./src/component_sample && cargo component build --target wasm32-unknown-unknown --profile={{ if target == "debug" {"dev"} else { target } }} - cargo run -p hyperlight-wasm-aot compile --component ./src/component_sample/target/wasm32-unknown-unknown/{{ target }}/component_sample.wasm ./x64/{{ target }}/component_sample.aot + cargo run {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--features " + features } }} -p hyperlight-wasm-aot compile --component ./src/component_sample/target/wasm32-unknown-unknown/{{ target }}/component_sample.wasm ./x64/{{ target }}/component_sample.aot check target=default-target: cargo check --profile={{ if target == "debug" {"dev"} else { target } }} diff --git a/src/hyperlight_wasm/Cargo.toml b/src/hyperlight_wasm/Cargo.toml index 1577130..9dfccc0 100644 --- a/src/hyperlight_wasm/Cargo.toml +++ b/src/hyperlight_wasm/Cargo.toml @@ -29,6 +29,11 @@ name = "helloworld" path = "examples/helloworld/main.rs" test = true +[[example]] +name = "guest-debugging" +path = "examples/guest-debugging/main.rs" +test = true + [[example]] name = "hostfuncs" path = "examples/hostfuncs/main.rs" @@ -52,6 +57,7 @@ tracing = "0.1.27" log = "0.4.28" cfg-if = { version = "1" } metrics = "0.24.2" +env_logger = "0.11.8" [target.'cfg(windows)'.dependencies] windows = { version = "0.62", features = ["Win32_System_Threading"] } @@ -68,6 +74,7 @@ metrics-util = "0.20.0" metrics-exporter-prometheus = "0.17" [build-dependencies] +cfg_aliases = "0.2.1" chrono = "0.4" blake3 = "1.8" built = { version = "0.8.0", features = ["chrono", "git2"] } @@ -81,6 +88,7 @@ function_call_metrics = ["hyperlight-host/function_call_metrics"] seccomp = ["hyperlight-host/seccomp"] print_debug = ["hyperlight-host/print_debug"] crashdump = ["hyperlight-host/crashdump"] +gdb = ["hyperlight-host/gdb"] kvm = ["hyperlight-host/kvm"] mshv2 = ["hyperlight-host/mshv2"] mshv3 = ["hyperlight-host/mshv3"] diff --git a/src/hyperlight_wasm/build.rs b/src/hyperlight_wasm/build.rs index 75ac7f9..5b94212 100644 --- a/src/hyperlight_wasm/build.rs +++ b/src/hyperlight_wasm/build.rs @@ -125,7 +125,7 @@ fn build_wasm_runtime() -> PathBuf { env_vars.retain(|(key, _)| !key.starts_with("CARGO_")); let mut cargo_cmd = std::process::Command::new(&cargo_bin); - let cmd = cargo_cmd + let mut cmd = cargo_cmd .arg("build") .arg("--profile") .arg(cargo_profile) @@ -137,6 +137,12 @@ fn build_wasm_runtime() -> PathBuf { .envs(env_vars) .env("PATH", path_with(&toolchain_dir)) .env("HYPERLIGHT_GUEST_TOOLCHAIN_ROOT", &toolchain_dir); + + // Add --features gdb if the gdb feature is enabled for this build script + if std::env::var("CARGO_FEATURE_GDB").is_ok() { + cmd = cmd.arg("--features").arg("gdb"); + } + let status = cmd .status() .unwrap_or_else(|e| panic!("could not run cargo build wasm_runtime: {}", e)); @@ -239,5 +245,9 @@ fn main() -> Result<()> { println!("cargo:rerun-if-changed=build.rs"); + cfg_aliases::cfg_aliases! { + gdb: { all(feature = "gdb", debug_assertions) }, + } + Ok(()) } diff --git a/src/hyperlight_wasm/examples/guest-debugging/main.rs b/src/hyperlight_wasm/examples/guest-debugging/main.rs new file mode 100644 index 0000000..c313fc8 --- /dev/null +++ b/src/hyperlight_wasm/examples/guest-debugging/main.rs @@ -0,0 +1,99 @@ +/* +Copyright 2025 The Hyperlight Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +use examples_common::get_wasm_module_path; +use hyperlight_host::HyperlightError; +use hyperlight_wasm::{Result, SandboxBuilder}; + +fn get_time_since_boot_microsecond() -> Result { + let res = std::time::SystemTime::now() + .duration_since(std::time::SystemTime::UNIX_EPOCH)? + .as_micros(); + i64::try_from(res).map_err(HyperlightError::IntConversionFailure) +} + +fn builder() -> SandboxBuilder { + #[cfg(gdb)] + { + SandboxBuilder::new() + .with_guest_input_buffer_size(2 * 1024 * 1024) // 2 MiB + .with_guest_heap_size(10 * 1024 * 1024) // 4 MiB + .with_debugging_enabled(8080) // debugging on port 8080 + } + #[cfg(not(gdb))] + SandboxBuilder::new() +} + +fn main() -> Result<()> { + let tests = [ + ( + "HelloWorld.aot", + "HelloWorld", + "Message from Rust Example to Wasm Function".to_string(), + ), + // ( + // "rust_wasm_samples.aot", + // "add", + // (5i32, 3i32), + // ), + ]; + for (idx, case) in tests.iter().enumerate() { + let (mod_path, fn_name, params_opt) = case; + + let mut sandbox = builder().build()?; + + let wasm_sandbox = match mod_path.starts_with("RunWasm") { + true => { + sandbox + .register( + "GetTimeSinceBootMicrosecond", + get_time_since_boot_microsecond, + ) + .unwrap(); + + sandbox.load_runtime()? + } + false => sandbox.load_runtime()?, + }; + + let mod_path = get_wasm_module_path(mod_path)?; + + // Load a Wasm module into the sandbox + let mut loaded_wasm_sandbox = wasm_sandbox.load_module(mod_path)?; + + if *fn_name == "Echo" { + // Call a function in the Wasm module + let result: String = + loaded_wasm_sandbox.call_guest_function(fn_name, params_opt.clone())?; + println!( + "Result from calling Echo Function in Wasm Module \ + test case {idx}) is: {}", + result + ); + } else if *fn_name == "HelloWorld" { + // Call a function in the Wasm module + let result: i32 = + loaded_wasm_sandbox.call_guest_function(fn_name, params_opt.clone())?; + + println!( + "Result from calling HelloWorld Function in Wasm Module \ + test case {idx}) is: {}", + result + ); + } + } + Ok(()) +} diff --git a/src/hyperlight_wasm/scripts/build-wasm-examples.sh b/src/hyperlight_wasm/scripts/build-wasm-examples.sh index ae0e29c..d28932b 100755 --- a/src/hyperlight_wasm/scripts/build-wasm-examples.sh +++ b/src/hyperlight_wasm/scripts/build-wasm-examples.sh @@ -6,9 +6,23 @@ set -o pipefail pushd "$(dirname "${BASH_SOURCE[0]}")/../../wasmsamples" OUTPUT_DIR="../../x64/${1:-"debug"}" +BUILD_TYPE="${1:-"debug"}" +FEATURES="${2:-""}" mkdir -p ${OUTPUT_DIR} OUTPUT_DIR=$(realpath $OUTPUT_DIR) +# Set stripping flags based on whether features are enabled +if [ -n "$FEATURES" ]; then + AOT_FEATURES="--features $FEATURES" + STRIP_FLAGS="" + DEBUG_FLAGS="-g" + OPT_FLAGS="-O0" +else + AOT_FEATURES="" + STRIP_FLAGS="-Wl,--strip-all" + DEBUG_FLAGS="" + OPT_FLAGS="-O3" +fi if [ -f "/.dockerenv" ] || grep -q docker /proc/1/cgroup; then # running in a container so use the installed wasi-sdk as the devcontainer has this installed @@ -16,9 +30,9 @@ if [ -f "/.dockerenv" ] || grep -q docker /proc/1/cgroup; then do echo Building ${FILENAME} # Build the wasm file with wasi-libc for wasmtime - /opt/wasi-sdk/bin/clang -flto -ffunction-sections -mexec-model=reactor -O3 -z stack-size=4096 -Wl,--initial-memory=65536 -Wl,--export=__data_end -Wl,--export=__heap_base,--export=malloc,--export=free,--export=__wasm_call_ctors -Wl,--strip-all,--no-entry -Wl,--allow-undefined -Wl,--gc-sections -o ${OUTPUT_DIR}/${FILENAME%.*}-wasi-libc.wasm ${FILENAME} + /opt/wasi-sdk/bin/clang ${DEBUG_FLAGS} -flto -ffunction-sections -mexec-model=reactor ${OPT_FLAGS} -z stack-size=4096 -Wl,--initial-memory=65536 -Wl,--export=__data_end -Wl,--export=__heap_base,--export=malloc,--export=free,--export=__wasm_call_ctors ${STRIP_FLAGS} -Wl,--no-entry -Wl,--allow-undefined -Wl,--gc-sections -o ${OUTPUT_DIR}/${FILENAME%.*}-wasi-libc.wasm ${FILENAME} - cargo run -p hyperlight-wasm-aot compile ${OUTPUT_DIR}/${FILENAME%.*}-wasi-libc.wasm ${OUTPUT_DIR}/${FILENAME%.*}.aot + cargo run ${AOT_FEATURES} -p hyperlight-wasm-aot compile ${OUTPUT_DIR}/${FILENAME%.*}-wasi-libc.wasm ${OUTPUT_DIR}/${FILENAME%.*}.aot done for WIT_FILE in ${PWD}/components/*.wit; do @@ -30,16 +44,16 @@ if [ -f "/.dockerenv" ] || grep -q docker /proc/1/cgroup; then # Build the wasm file with wasi-libc for wasmtime /opt/wasi-sdk/bin/wasm32-wasip2-clang \ - -ffunction-sections -mexec-model=reactor -O3 -z stack-size=4096 \ + -ffunction-sections -mexec-model=reactor ${OPT_FLAGS} -z stack-size=4096 \ -Wl,--initial-memory=65536 -Wl,--export=__data_end -Wl,--export=__heap_base,--export=malloc,--export=free,--export=__wasm_call_ctors \ - -Wl,--strip-all,--no-entry -Wl,--allow-undefined -Wl,--gc-sections \ + ${STRIP_FLAGS} -Wl,--no-entry -Wl,--allow-undefined -Wl,--gc-sections \ -o ${OUTPUT_DIR}/${COMPONENT_NAME}-p2.wasm \ ${PWD}/components/${COMPONENT_NAME}.c \ ${PWD}/components/bindings/${COMPONENT_NAME}.c \ ${PWD}/components/bindings/${COMPONENT_NAME}_component_type.o # Build AOT for Wasmtime - cargo run -p hyperlight-wasm-aot compile --component ${OUTPUT_DIR}/${COMPONENT_NAME}-p2.wasm ${OUTPUT_DIR}/${COMPONENT_NAME}.aot + cargo run ${AOT_FEATURES} -p hyperlight-wasm-aot compile --component ${OUTPUT_DIR}/${COMPONENT_NAME}-p2.wasm ${OUTPUT_DIR}/${COMPONENT_NAME}.aot done else @@ -55,10 +69,20 @@ else for FILENAME in $(find . -name '*.c' -not -path './components/*') do echo Building ${FILENAME} - # Build the wasm file with wasi-libc for wasmtime - docker run --rm -i -v "${PWD}:/tmp/host" -v "${OUTPUT_DIR}:/tmp/output/" wasm-clang-builder:latest /opt/wasi-sdk/bin/clang -flto -ffunction-sections -mexec-model=reactor -O3 -z stack-size=4096 -Wl,--initial-memory=65536 -Wl,--export=__data_end -Wl,--export=__heap_base,--export=malloc,--export=free,--export=__wasm_call_ctors -Wl,--strip-all,--no-entry -Wl,--allow-undefined -Wl,--gc-sections -o /tmp/output/${FILENAME%.*}-wasi-libc.wasm /tmp/host/${FILENAME} - - cargo run -p hyperlight-wasm-aot compile ${OUTPUT_DIR}/${FILENAME%.*}-wasi-libc.wasm ${OUTPUT_DIR}/${FILENAME%.*}.aot + OUTPUT_WASM="${OUTPUT_DIR}/${FILENAME%.*}-wasi-libc.wasm" + ABS_INPUT="$(realpath ${FILENAME})" + ABS_OUTPUT="$(realpath ${OUTPUT_WASM})" + INPUT_DIR="$(dirname ${ABS_INPUT})" + OUTPUT_DIR_REAL="$(dirname ${ABS_OUTPUT})" + INPUT_BASE="$(basename ${ABS_INPUT})" + OUTPUT_BASE="$(basename ${ABS_OUTPUT})" + # Map parent directories to the same path in the container + docker run --rm -i \ + -v "${INPUT_DIR}:${INPUT_DIR}" \ + -v "${OUTPUT_DIR_REAL}:${OUTPUT_DIR_REAL}" \ + wasm-clang-builder:latest /bin/bash -c "/opt/wasi-sdk/bin/clang ${DEBUG_FLAGS} -flto -ffunction-sections -mexec-model=reactor ${OPT_FLAGS} -z stack-size=4096 -Wl,--initial-memory=65536 -Wl,--export=__data_end -Wl,--export=__heap_base,--export=malloc,--export=free,--export=__wasm_call_ctors ${STRIP_FLAGS} -Wl,--no-entry -Wl,--allow-undefined -Wl,--gc-sections -o ${ABS_OUTPUT} ${ABS_INPUT}" + + cargo run ${AOT_FEATURES} -p hyperlight-wasm-aot compile ${OUTPUT_WASM} ${OUTPUT_DIR}/${FILENAME%.*}.aot done echo Building components @@ -70,18 +94,35 @@ else # Generate bindings for the component wit-bindgen c ${WIT_FILE} --out-dir ${PWD}/components/bindings - # Build the wasm file with wasi-libc for wasmtime - docker run --rm -i -v "${PWD}:/tmp/host" -v "${OUTPUT_DIR}:/tmp/output/" wasm-clang-builder:latest /opt/wasi-sdk/bin/wasm32-wasip2-clang \ - -ffunction-sections -mexec-model=reactor -O3 -z stack-size=4096 \ + COMPONENT_C="${PWD}/components/${COMPONENT_NAME}.c" + BINDINGS_C="${PWD}/components/bindings/${COMPONENT_NAME}.c" + BINDINGS_TYPE_O="${PWD}/components/bindings/${COMPONENT_NAME}_component_type.o" + OUTPUT_WASM="${OUTPUT_DIR}/${COMPONENT_NAME}-p2.wasm" + ABS_COMPONENT_C="$(realpath ${COMPONENT_C})" + ABS_BINDINGS_C="$(realpath ${BINDINGS_C})" + ABS_BINDINGS_TYPE_O="$(realpath ${BINDINGS_TYPE_O})" + ABS_OUTPUT_WASM="$(realpath ${OUTPUT_WASM})" + COMPONENT_C_DIR="$(dirname ${ABS_COMPONENT_C})" + BINDINGS_C_DIR="$(dirname ${ABS_BINDINGS_C})" + BINDINGS_TYPE_O_DIR="$(dirname ${ABS_BINDINGS_TYPE_O})" + OUTPUT_WASM_DIR="$(dirname ${ABS_OUTPUT_WASM})" + # Map all parent directories to the same path in the container + docker run --rm -i \ + -v "${COMPONENT_C_DIR}:${COMPONENT_C_DIR}" \ + -v "${BINDINGS_C_DIR}:${BINDINGS_C_DIR}" \ + -v "${BINDINGS_TYPE_O_DIR}:${BINDINGS_TYPE_O_DIR}" \ + -v "${OUTPUT_WASM_DIR}:${OUTPUT_WASM_DIR}" \ + wasm-clang-builder:latest /bin/bash -c "/opt/wasi-sdk/bin/wasm32-wasip2-clang \ + -ffunction-sections -mexec-model=reactor ${OPT_FLAGS} -z stack-size=4096 \ -Wl,--initial-memory=65536 -Wl,--export=__data_end -Wl,--export=__heap_base,--export=malloc,--export=free,--export=__wasm_call_ctors \ - -Wl,--strip-all,--no-entry -Wl,--allow-undefined -Wl,--gc-sections \ - -o /tmp/output/${COMPONENT_NAME}-p2.wasm \ - /tmp/host/components/${COMPONENT_NAME}.c \ - /tmp/host/components/bindings/${COMPONENT_NAME}.c \ - /tmp/host/components/bindings/${COMPONENT_NAME}_component_type.o + ${STRIP_FLAGS} -Wl,--no-entry -Wl,--allow-undefined -Wl,--gc-sections \ + -o ${ABS_OUTPUT_WASM} \ + ${ABS_COMPONENT_C} \ + ${ABS_BINDINGS_C} \ + ${ABS_BINDINGS_TYPE_O}" # Build AOT for Wasmtime - cargo run -p hyperlight-wasm-aot compile --component ${OUTPUT_DIR}/${COMPONENT_NAME}-p2.wasm ${OUTPUT_DIR}/${COMPONENT_NAME}.aot + cargo run ${AOT_FEATURES} -p hyperlight-wasm-aot compile --component ${OUTPUT_WASM} ${OUTPUT_DIR}/${COMPONENT_NAME}.aot done fi diff --git a/src/hyperlight_wasm/src/sandbox/sandbox_builder.rs b/src/hyperlight_wasm/src/sandbox/sandbox_builder.rs index 7eef21e..63726a4 100644 --- a/src/hyperlight_wasm/src/sandbox/sandbox_builder.rs +++ b/src/hyperlight_wasm/src/sandbox/sandbox_builder.rs @@ -46,6 +46,28 @@ impl SandboxBuilder { } } + /// Enable debugging for the guest + /// This will allow the guest to be natively debugged using GDB or other debugging tools + /// + /// # Example: + /// ```rust + /// use hyperlight_host::sandbox::SandboxBuilder; + /// let sandbox = SandboxBuilder::new() + /// .with_debugging_enabled(8080) // Enable debugging on port 8080 + /// .build() + /// .expect("Failed to build sandbox"); + /// ``` + /// # Note: + /// This feature is only available when the `gdb` feature is enabled. + /// If the `gdb` feature is not enabled, this method will have no effect. + #[cfg(gdb)] + pub fn with_debugging_enabled(mut self, port: u16) -> Self { + let debug_info = hyperlight_host::sandbox::config::DebugInfo { port }; + self.config.set_guest_debug_info(debug_info); + + self + } + /// Set the host print function pub fn with_host_print_fn( mut self, diff --git a/src/hyperlight_wasm_aot/Cargo.toml b/src/hyperlight_wasm_aot/Cargo.toml index ee8ea61..6d198fc 100644 --- a/src/hyperlight_wasm_aot/Cargo.toml +++ b/src/hyperlight_wasm_aot/Cargo.toml @@ -16,3 +16,6 @@ wasmtime = { version = "36.0.2", default-features = false, features = ["cranelif clap = "4.5" cargo_metadata = "0.22" cargo-util-schemas = "0.8.2" + +[features] +gdb = ["wasmtime/debug-builtins"] diff --git a/src/hyperlight_wasm_aot/src/main.rs b/src/hyperlight_wasm_aot/src/main.rs index 6d6dd64..afe34f3 100644 --- a/src/hyperlight_wasm_aot/src/main.rs +++ b/src/hyperlight_wasm_aot/src/main.rs @@ -19,7 +19,10 @@ use std::path::Path; use cargo_metadata::{MetadataCommand, Package}; use cargo_util_schemas::manifest::PackageName; use clap::{Arg, Command}; +#[cfg(all(feature = "gdb", debug_assertions))] +use wasmtime::OptLevel; use wasmtime::{Config, Engine, Module, Precompiled}; + fn main() { let hyperlight_wasm_aot_version = env!("CARGO_PKG_VERSION"); let matches = Command::new("hyperlight-wasm-aot") @@ -149,8 +152,17 @@ fn main() { } } +/// Returns a new `Config` for the Wasmtime engine with additional settings for AOT compilation. fn get_config() -> Config { let mut config = Config::new(); config.target("x86_64-unknown-none").unwrap(); + + // Enable the default features for the Wasmtime engine. + #[cfg(all(feature = "gdb", debug_assertions))] + { + config.debug_info(true); + config.cranelift_opt_level(OptLevel::None); + } + config } diff --git a/src/rust_wasm_samples/build.rs b/src/rust_wasm_samples/build.rs index 93ce36a..e2daa3e 100644 --- a/src/rust_wasm_samples/build.rs +++ b/src/rust_wasm_samples/build.rs @@ -17,6 +17,6 @@ limitations under the License. fn main() { println!("cargo:rustc-link-arg=-zstack-size=4096"); println!("cargo:rustc-link-arg=--initial-memory=65536"); - println!("cargo:rustc-link-arg=--strip-all"); + //println!("cargo:rustc-link-arg=--strip-all"); println!("cargo:rustc-target-feature=+bulk-memory"); } diff --git a/src/wasm_runtime/Cargo.lock b/src/wasm_runtime/Cargo.lock index 02839c6..19c59cb 100644 --- a/src/wasm_runtime/Cargo.lock +++ b/src/wasm_runtime/Cargo.lock @@ -1930,6 +1930,7 @@ dependencies = [ "cargo_metadata", "cc", "cfg-if", + "cfg_aliases", "hyperlight-common", "hyperlight-guest", "hyperlight-guest-bin", @@ -2008,6 +2009,7 @@ dependencies = [ "wasmtime-internal-component-macro", "wasmtime-internal-component-util", "wasmtime-internal-cranelift", + "wasmtime-internal-jit-debug", "wasmtime-internal-math", "wasmtime-internal-slab", "wasmtime-internal-unwinder", @@ -2098,6 +2100,16 @@ dependencies = [ "wasmtime-internal-versioned-export-macros", ] +[[package]] +name = "wasmtime-internal-jit-debug" +version = "36.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e03f0b11f8fe4d456feac11e7e9dc6f02ddb34d4f6a1912775dbc63c5bdd5670" +dependencies = [ + "cc", + "wasmtime-internal-versioned-export-macros", +] + [[package]] name = "wasmtime-internal-math" version = "36.0.2" diff --git a/src/wasm_runtime/Cargo.toml b/src/wasm_runtime/Cargo.toml index 7e3c4fd..cdb3647 100644 --- a/src/wasm_runtime/Cargo.toml +++ b/src/wasm_runtime/Cargo.toml @@ -19,9 +19,13 @@ hyperlight-wasm-macro = { path = "../hyperlight_wasm_macro" } spin = "0.10.0" [build-dependencies] +cfg_aliases = "0.2.1" cc = "1.2" cfg-if = { version = "1" } cargo_metadata = "0.22" reqwest = {version = "0.12", default-features = false, features = ["blocking","rustls-tls"] } [workspace] # indicate that this crate is not part of any workspace + +[features] +gdb = ["wasmtime/debug-builtins"] diff --git a/src/wasm_runtime/build.rs b/src/wasm_runtime/build.rs index d5d8d70..8780d60 100644 --- a/src/wasm_runtime/build.rs +++ b/src/wasm_runtime/build.rs @@ -74,4 +74,8 @@ fn main() { if env::var_os("WIT_WORLD").is_some() { println!("cargo::rustc-cfg=component"); } + + cfg_aliases::cfg_aliases! { + gdb: { all(feature = "gdb", debug_assertions) }, + } } diff --git a/src/wasm_runtime/src/component.rs b/src/wasm_runtime/src/component.rs index c14f8cb..14711dc 100644 --- a/src/wasm_runtime/src/component.rs +++ b/src/wasm_runtime/src/component.rs @@ -103,6 +103,8 @@ pub extern "C" fn hyperlight_main() { let mut config = Config::new(); config.with_custom_code_memory(Some(alloc::sync::Arc::new(platform::WasmtimeCodeMemory {}))); + #[cfg(gdb)] + config.debug_info(true); let engine = Engine::new(&config).unwrap(); let linker = Linker::new(&engine); *CUR_ENGINE.lock() = Some(engine); diff --git a/src/wasm_runtime/src/module.rs b/src/wasm_runtime/src/module.rs index e3fa630..aca5d0e 100644 --- a/src/wasm_runtime/src/module.rs +++ b/src/wasm_runtime/src/module.rs @@ -96,6 +96,8 @@ pub fn guest_dispatch_function(function_call: FunctionCall) -> Result> { fn init_wasm_runtime() -> Result> { let mut config = Config::new(); config.with_custom_code_memory(Some(alloc::sync::Arc::new(platform::WasmtimeCodeMemory {}))); + #[cfg(gdb)] + config.debug_info(true); let engine = Engine::new(&config)?; let mut linker = Linker::new(&engine); wasip1::register_handlers(&mut linker)?; From 966c54bbf0cafb00c9683535405f47204e7acb15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Doru=20Bl=C3=A2nzeanu?= Date: Mon, 29 Sep 2025 17:37:09 +0300 Subject: [PATCH 2/2] Fix build and clippy checks when gdb features is enabled on windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - The toolchain paths and flags are not correctly propagated on windows. When gdb feature is enabled, wasmtime-internal-jit-debug crate tries to compile C files using cc which is not correctly set on Windows Signed-off-by: Doru Blânzeanu --- Justfile | 3 +++ src/hyperlight_wasm/build.rs | 2 ++ 2 files changed, 5 insertions(+) diff --git a/Justfile b/Justfile index 7b4a226..7e86eaf 100644 --- a/Justfile +++ b/Justfile @@ -72,6 +72,9 @@ fmt: cd src/wasm_runtime && cargo +nightly fmt -v --all cd src/hyperlight_wasm_macro && cargo +nightly fmt -v --all +export CC_x86_64_unknown_none:= if os() == "windows" { justfile_directory() / "src/wasm_runtime/guest-toolchain/clang" } else { "" } +export AR_x86_64_unknown_none:= if os() == "windows" { "llvm-ar" } else { "" } + clippy target=default-target: (check target) cargo clippy --profile={{ if target == "debug" {"dev"} else { target } }} --all-targets --all-features -- -D warnings cd src/rust_wasm_samples && cargo clippy --profile={{ if target == "debug" {"dev"} else { target } }} --all-targets --all-features -- -D warnings diff --git a/src/hyperlight_wasm/build.rs b/src/hyperlight_wasm/build.rs index 5b94212..3dc6858 100644 --- a/src/hyperlight_wasm/build.rs +++ b/src/hyperlight_wasm/build.rs @@ -134,6 +134,8 @@ fn build_wasm_runtime() -> PathBuf { .arg(&target_dir) .current_dir(&in_repo_dir) .env_clear() + // On windows when `gdb` features is enabled this is not set correctly + .env("CFLAGS_x86_64_unknown_none", "-fPIC") .envs(env_vars) .env("PATH", path_with(&toolchain_dir)) .env("HYPERLIGHT_GUEST_TOOLCHAIN_ROOT", &toolchain_dir);