diff --git a/.gitignore b/.gitignore index 1a4595e..609584b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target *.log +/nyx_data \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index cd2742a..d41c075 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -130,7 +130,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eadd868a2ce9ca38de7eeafdcec9c7065ef89b42b32f0839278d55f35c54d1ff" dependencies = [ "clap", - "heck", + "heck 0.4.1", "indexmap", "log", "proc-macro2", @@ -180,18 +180,19 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.42" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" +checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931" dependencies = [ "clap_builder", + "clap_derive", ] [[package]] name = "clap_builder" -version = "4.5.42" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" +checksum = "2ad9bbf750e73b5884fb8a211a9424a1906c1e156724260fdae972f31d70e1d6" dependencies = [ "anstream", "anstyle", @@ -199,6 +200,18 @@ dependencies = [ "strsim", ] +[[package]] +name = "clap_derive" +version = "4.5.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "clap_lex" version = "0.7.5" @@ -426,6 +439,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "indexmap" version = "2.10.0" @@ -673,7 +692,7 @@ dependencies = [ name = "qemu-nyx-runner" version = "0.1.0" dependencies = [ - "client", + "clap", "csv", "libnyx", "pt-dump-decoder", diff --git a/Cargo.toml b/Cargo.toml index 7812b6e..4f4065f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ version = "0.1.0" edition = "2024" [dependencies] -client = { path = "client" } libnyx = { git = "https://git.cs.tu-dortmund.de/david.venhoff/libnyx-fork" } pt-dump-decoder = { path = "pt-dump-decoder" } -csv = "1.3.1" \ No newline at end of file +csv = "1.3.1" +clap = { version = "4.5.47", features = ["derive"] } diff --git a/build.rs b/build.rs deleted file mode 100644 index e2d5cc7..0000000 --- a/build.rs +++ /dev/null @@ -1,101 +0,0 @@ -use std::env; -use std::path::Path; -use std::process::Command; - -fn main() { - println!("cargo:rerun-if-changed=client"); - let out_dir = env::var("OUT_DIR").unwrap(); - let client_bin = build_client(&out_dir); - - setup_nyx(&out_dir); - - create_nyx_workdir(&out_dir, &client_bin); -} - -#[track_caller] -fn shell(cwd: impl AsRef, command_string: &str) { - println!( - "cargo:warning=Running ({}) {command_string}", - cwd.as_ref().display() - ); - - let cwd = cwd.as_ref(); - let mut parts = command_string.split(" "); - let command_name = parts.next().unwrap(); - - let mut command = Command::new(command_name); - command.current_dir(cwd); - command.stdout(std::process::Stdio::inherit()); - command.stderr(std::process::Stdio::inherit()); - command.args(parts); - - let mut process = command.spawn().unwrap(); - let exit_status = process.wait().unwrap(); - if !exit_status.success() { - panic!( - "Command failed with {exit_status}: {}> {command_string}", - cwd.display() - ); - } -} - -/// Builds the client and returns the path to its binary -fn build_client(out_dir: &str) -> String { - // Change the target dir to avoid a cargo target dir deadlock - shell( - "client", - &format!("cargo build --release --target-dir {out_dir}/client_target"), - ); - format!("{out_dir}/client_target/release/client") -} - -/// Downloads and compiles qemu-nyx and packer and compiles them for the current architecture -/// This means that cross-compilation is not supported -fn setup_nyx(out_dir: &str) { - let packer_path = std::path::PathBuf::from(format!("{out_dir}/packer")); - let qemu_path = std::path::PathBuf::from(format!("{out_dir}/QEMU-Nyx")); - if !packer_path.exists() { - shell(out_dir, "git clone https://github.com/nyx-fuzz/packer/"); - } - if !qemu_path.exists() { - println!("cargo:warning=Cloning and building QEMU-Nyx. This may take a while..."); - shell( - out_dir, - "git clone https://github.com/nyx-fuzz/QEMU-Nyx --depth 1", - ); - - let is_debug_build = match env::var("DEBUG").unwrap().as_str() { - "true" => true, - "false" => false, - other => panic!("Invalid value for DEBUG: {other}"), - }; - let qemu_compile_mode = if is_debug_build { - "debug_static" - } else { - "lto" - }; - shell( - qemu_path, - &format!("bash compile_qemu_nyx.sh {qemu_compile_mode}"), - ); - } -} - -fn create_nyx_workdir(out_dir: &str, client_bin: &str) { - // Create the directory and move required binaries to it - shell( - out_dir, - &format!( - "python3 packer/packer/nyx_packer.py {client_bin} build afl processor_trace --fast_reload_mode --purge" - ), - ); - - // Create the nyx config - shell( - out_dir, - "python3 packer/packer/nyx_config_gen.py build Kernel", - ); - - // Pass the path to the build directory to src/main.rs - println!("cargo:rustc-env=NYX_SHAREDIR={out_dir}/build") -} diff --git a/client/build.rs b/client/build.rs index 46ea92c..8e3dc66 100644 --- a/client/build.rs +++ b/client/build.rs @@ -30,7 +30,7 @@ const BENCHMARKS: &[&str] = &[ ]; fn main() { - println!("cargo:rerun-if-changed=coremark/"); + println!("cargo:rerun-if-changed=build.rs"); let out_dir = env::var("OUT_DIR").unwrap(); Command::new("git") diff --git a/src/benchmark_nyx_pt.rs b/src/benchmark_nyx_pt.rs index a15efc9..e70a3f6 100644 --- a/src/benchmark_nyx_pt.rs +++ b/src/benchmark_nyx_pt.rs @@ -1,21 +1,23 @@ use crate::benchmark::{Benchmark, ToCsv}; +use crate::nyx; use csv::Writer; use libnyx::{NyxConfig, NyxProcess, NyxProcessRole, NyxReturnValue}; use pt_dump_decoder::{AnalyzeData, analyze_dump}; use std::error::Error; use std::io::Write; +use std::path::{Path, PathBuf}; const WORKDIR_PATH: &str = "/tmp/wdir"; -// The sharedir path gets set by the build script -const SHAREDIR_PATH: &str = env!("NYX_SHAREDIR"); pub struct NyxRunner(NyxProcess); impl NyxRunner { - pub fn setup() -> Result> { + pub fn setup(client_bin: &Path) -> Result> { let _ = std::fs::remove_dir_all(WORKDIR_PATH); + let sharedir = nyx::setup_nyx(&PathBuf::from("nyx_data"), client_bin)?; + let sharedir = sharedir.to_str().expect("Expected unicode path"); - let mut nyx_config = NyxConfig::load(SHAREDIR_PATH)?; + let mut nyx_config = NyxConfig::load(sharedir)?; nyx_config.set_workdir_path(WORKDIR_PATH.to_string()); nyx_config.set_input_buffer_size(0x2000); nyx_config.set_nyx_debug_log_path("qemu_nyx.log".to_string()); diff --git a/src/main.rs b/src/main.rs index bad9465..4b87ef2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,19 +1,27 @@ //! Translated from libnyx/test.c mod benchmark; -mod benchmark_baseline; mod benchmark_nyx_pt; +mod nyx; use crate::benchmark::{Benchmark, ToCsv}; -use crate::benchmark_baseline::Baseline; use crate::benchmark_nyx_pt::NyxRunner; use std::error::Error; +use std::path::PathBuf; use std::time::{Duration, Instant}; +use clap::Parser; + +#[derive(Debug, Parser)] +#[command(about="Tool to execute a program with nyx and trace it with intel pt")] +struct Args { + /// Path to the client binary to execute + client: PathBuf +} fn main() -> Result<(), Box> { - let nyx_runner = NyxRunner::setup()?; + let args = Args::parse(); + let nyx_runner = NyxRunner::setup(&args.client)?; benchmark(nyx_runner)?; - benchmark(Baseline)?; Ok(()) } diff --git a/src/nyx.rs b/src/nyx.rs new file mode 100644 index 0000000..4116609 --- /dev/null +++ b/src/nyx.rs @@ -0,0 +1,109 @@ +//! This module contains functionality to download and setup nyx, +//! so that it can be used to execute a binary. + +use std::path::{Path, PathBuf}; +use std::process::Command; +use std::{fs, io}; + +/// Downloads and installs nyx +/// +/// [out_dir] Is the directory to which all tools get downloaded and +/// where the working directory gets created. +/// +/// [client] Is the path to the client binary. +/// +/// Returns the path to the working directory +pub fn setup_nyx(out_dir: &Path, client: &Path) -> Result { + fn inner(out_dir: &Path, client: &Path) -> Result { + if out_dir.exists() { + println!("Nyx is already installed, skipping setup"); + } else { + println!("Installing nxx..."); + fs::create_dir(out_dir)?; + setup_nyx_tools(&out_dir)?; + } + + create_nyx_workdir(&out_dir, &client) + } + + // Clean up the out directory if something went wrong + match inner(out_dir, client) { + Ok(path) => Ok(path), + Err(err) => { + fs::remove_dir_all(out_dir)?; + Err(err) + } + } +} + +#[track_caller] +fn shell(cwd: impl AsRef, command_string: &str) -> Result<(), io::Error> { + println!("Running ({}) {command_string}", cwd.as_ref().display()); + + let cwd = cwd.as_ref(); + let mut parts = command_string.split(" "); + let command_name = parts.next().unwrap(); + + let mut command = Command::new(command_name); + command.current_dir(cwd); + command.stdout(std::process::Stdio::inherit()); + command.stderr(std::process::Stdio::inherit()); + command.args(parts); + + let mut process = command.spawn()?; + let exit_status = process.wait()?; + if !exit_status.success() { + return Err(io::Error::new( + io::ErrorKind::Other, + format!("Command ({command_string}) failed with {exit_status}"), + )); + } + + Ok(()) +} + +/// Downloads and compiles qemu-nyx and packer and compiles them for the current architecture +/// This means that cross-compilation is not supported +fn setup_nyx_tools(out_dir: &Path) -> Result<(), io::Error> { + let packer_path = out_dir.join("packer"); + let qemu_path = out_dir.join("QEMU-Nyx"); + if !packer_path.exists() { + shell(out_dir, "git clone https://github.com/nyx-fuzz/packer/")?; + } + if !qemu_path.exists() { + println!("cargo:warning=Cloning and building QEMU-Nyx. This may take a while..."); + shell( + out_dir, + "git clone https://github.com/nyx-fuzz/QEMU-Nyx --depth 1", + )?; + + // Compile with maximum optimizations + let qemu_compile_mode = "lto"; + shell( + qemu_path, + &format!("bash compile_qemu_nyx.sh {qemu_compile_mode}"), + )?; + } + + Ok(()) +} + +fn create_nyx_workdir(out_dir: &Path, client_bin: &Path) -> Result { + let client_bin = fs::canonicalize(client_bin)?; + let client_bin = client_bin.to_str().expect("Expected unicode path"); + // Create the directory and move required binaries to it + shell( + out_dir, + &format!( + "python3 packer/packer/nyx_packer.py {client_bin} build afl processor_trace --fast_reload_mode --purge" + ), + )?; + + // Create the nyx config + shell( + out_dir, + "python3 packer/packer/nyx_config_gen.py build Kernel", + )?; + + Ok(out_dir.join("build")) +}