From cf6706ebd678b4cb91331eae66c5980dfe44ad99 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 23 Sep 2023 15:13:55 +0200 Subject: [PATCH] Make dylib_flag cross-platform --- Cargo.lock | 12 ++++++++- Cargo.toml | 4 +-- src/dylib_flag.rs | 56 +++++++++++++++++----------------------- src/processor/checker.rs | 3 ++- src/processor/reaper.rs | 11 ++++---- 5 files changed, 42 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3bf4cf3..8a93e6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,7 +101,7 @@ dependencies = [ "clap", "ctrlc", "genemichaels", - "libc", + "libloading", "owo-colors", "proc-macro2", "quote", @@ -354,6 +354,16 @@ version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +[[package]] +name = "libloading" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" +dependencies = [ + "cfg-if", + "windows-sys", +] + [[package]] name = "linux-raw-sys" version = "0.4.7" diff --git a/Cargo.toml b/Cargo.toml index 51609ba..0161590 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ anyhow = "1.0.65" clap = { version = "4.0.29", features = ["derive"] } ctrlc = "3.2.5" genemichaels = "0.1.21" +libloading = "0.8.0" owo-colors = "3.5.0" proc-macro2 = { version = "1.0.48", features = ["span-locations"] } quote = "1.0.23" @@ -33,6 +34,3 @@ tracing = "0.1.37" tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } tracing-tree = "0.2.2" walkdir = "2.3.2" - -[target."cfg(unix)".dependencies] -libc = "0.2.138" diff --git a/src/dylib_flag.rs b/src/dylib_flag.rs index 18d9f15..fcc65a8 100644 --- a/src/dylib_flag.rs +++ b/src/dylib_flag.rs @@ -1,9 +1,10 @@ //! Handles the --verify-fn flag. //! It takes in a Rust closure like `|str| true` that takes in a `&str` and returns a bool. -use std::{fmt::Debug, str::FromStr}; +use std::{fmt::Debug, mem::ManuallyDrop, str::FromStr}; use anyhow::{Context, Result}; +use libloading::Symbol; #[repr(C)] pub struct RawOutput { @@ -24,7 +25,7 @@ impl FromStr for RustFunction { type Err = anyhow::Error; fn from_str(s: &str) -> Result { - Self::compile(s) + Self::compile(s).context("compiling and loading rust function") } } @@ -83,17 +84,9 @@ fn wrap_func_body(func: &str) -> Result { } impl RustFunction { - #[cfg(not(target_os = "linux"))] - pub fn compile(body: &str) -> Result { - Err(anyhow::anyhow!("--verify-fn only works on unix")) - } - - #[cfg(target_os = "linux")] pub fn compile(body: &str) -> Result { use anyhow::bail; - use std::io; use std::process::Command; - use std::{ffi::CString, os::unix::prelude::OsStringExt}; let file = tempfile::tempdir()?; @@ -103,9 +96,17 @@ impl RustFunction { std::fs::write(&source_path, full_file).context("writing source")?; + let library_path = file.path().join("libhelper"); + let mut rustc = Command::new("rustc"); rustc.arg(source_path); - rustc.args(["--crate-type=cdylib", "--crate-name=helper", "--emit=link"]); + rustc.args([ + "--crate-type=cdylib", + "--crate-name=helper", + "--emit=link", + "-o", + ]); + rustc.arg(&library_path); rustc.current_dir(file.path()); let output = rustc.output().context("running rustc")?; @@ -114,29 +115,19 @@ impl RustFunction { bail!("Failed to compile code: {stderr}"); } - let dylib_path = file.path().join("libhelper.so"); + // SAFETY: We are loading a simple rust cdylib, which does not do weird things. But we cannot unload Rust dylibs, so we use MD below. + let dylib = unsafe { + libloading::Library::new(&library_path).context("loading helper shared library")? + }; + let dylib = ManuallyDrop::new(dylib); - let os_str = dylib_path.into_os_string(); - let vec = os_str.into_vec(); - let cstr = CString::new(vec)?; + let func: Symbol = unsafe { + dylib + .get(b"cargo_minimize_ffi_function\0") + .context("failed to find entrypoint symbol")? + }; - let dylib = unsafe { libc::dlopen(cstr.as_ptr(), libc::RTLD_LAZY) }; - - if dylib.is_null() { - bail!("failed to open dylib: {}", io::Error::last_os_error()); - } - - let symbol = b"cargo_minimize_ffi_function\0"; - - let func = unsafe { libc::dlsym(dylib, symbol.as_ptr().cast()) }; - - if func.is_null() { - bail!("didn't find entrypoint symbol"); - } - - let func = unsafe { std::mem::transmute::<*mut _, CheckerCFn>(func) }; - - Ok(Self { func }) + Ok(Self { func: *func }) } pub fn call(&self, output: &str, code: Option) -> bool { @@ -169,7 +160,6 @@ mod tests { use super::RustFunction; #[test] - #[cfg_attr(not(target_os = "linux"), ignore)] fn basic_contains_work() { let code = r#"|output| output.out.contains("test")"#; diff --git a/src/processor/checker.rs b/src/processor/checker.rs index 4a52629..0be922f 100644 --- a/src/processor/checker.rs +++ b/src/processor/checker.rs @@ -198,7 +198,8 @@ impl PassController { fn next_in_worklist(&mut self) { let PassControllerState::Bisecting { current, worklist, .. - } = &mut self.state else { + } = &mut self.state + else { unreachable!("next_in_worklist called on non-bisecting state"); }; match worklist.pop() { diff --git a/src/processor/reaper.rs b/src/processor/reaper.rs index d0fa1cc..a21b486 100644 --- a/src/processor/reaper.rs +++ b/src/processor/reaper.rs @@ -62,12 +62,11 @@ impl Minimizer { suggestions: &HashMap<&Path, Vec<&Suggestion>>, ) -> Result<()> { for (sugg_file, suggestions) in suggestions { - let Some(file) = self - .files - .iter() - .find(|source| source.path.ends_with(sugg_file) || sugg_file.ends_with(&source.path)) else { - continue; - }; + let Some(file) = self.files.iter().find(|source| { + source.path.ends_with(sugg_file) || sugg_file.ends_with(&source.path) + }) else { + continue; + }; let changes = &mut Changes::default();