diff --git a/fuzzers/baby_fuzzer_swap_differential/src/bin/libafl_cc.rs b/fuzzers/baby_fuzzer_swap_differential/src/bin/libafl_cc.rs index 4a6519f571..eae59d52db 100644 --- a/fuzzers/baby_fuzzer_swap_differential/src/bin/libafl_cc.rs +++ b/fuzzers/baby_fuzzer_swap_differential/src/bin/libafl_cc.rs @@ -1,6 +1,6 @@ use std::env; -use libafl_cc::{ClangWrapper, CompilerWrapper}; +use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper}; pub fn main() { let args: Vec = env::args().collect(); diff --git a/fuzzers/forkserver_libafl_cc/src/bin/libafl_cc.rs b/fuzzers/forkserver_libafl_cc/src/bin/libafl_cc.rs index 4cfd4af756..1687f30265 100644 --- a/fuzzers/forkserver_libafl_cc/src/bin/libafl_cc.rs +++ b/fuzzers/forkserver_libafl_cc/src/bin/libafl_cc.rs @@ -1,6 +1,6 @@ use std::env; -use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; +use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses, ToolWrapper}; pub fn main() { let args: Vec = env::args().collect(); diff --git a/fuzzers/fuzzbench/src/bin/libafl_cc.rs b/fuzzers/fuzzbench/src/bin/libafl_cc.rs index f0e5b7fd8d..972c99c9a4 100644 --- a/fuzzers/fuzzbench/src/bin/libafl_cc.rs +++ b/fuzzers/fuzzbench/src/bin/libafl_cc.rs @@ -1,6 +1,6 @@ use std::env; -use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; +use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses, ToolWrapper}; pub fn main() { let mut args: Vec = env::args().collect(); diff --git a/fuzzers/fuzzbench_text/src/bin/libafl_cc.rs b/fuzzers/fuzzbench_text/src/bin/libafl_cc.rs index ca1bd02825..38177f7e88 100644 --- a/fuzzers/fuzzbench_text/src/bin/libafl_cc.rs +++ b/fuzzers/fuzzbench_text/src/bin/libafl_cc.rs @@ -1,6 +1,6 @@ use std::env; -use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; +use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses, ToolWrapper}; pub fn main() { let args: Vec = env::args().collect(); diff --git a/fuzzers/libfuzzer_libmozjpeg/src/bin/libafl_cc.rs b/fuzzers/libfuzzer_libmozjpeg/src/bin/libafl_cc.rs index ecf2ef585f..00c81ff847 100644 --- a/fuzzers/libfuzzer_libmozjpeg/src/bin/libafl_cc.rs +++ b/fuzzers/libfuzzer_libmozjpeg/src/bin/libafl_cc.rs @@ -1,6 +1,6 @@ use std::env; -use libafl_cc::{ClangWrapper, CompilerWrapper}; +use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper}; pub fn main() { let args: Vec = env::args().collect(); diff --git a/fuzzers/libfuzzer_libpng/src/bin/libafl_cc.rs b/fuzzers/libfuzzer_libpng/src/bin/libafl_cc.rs index 69f3766586..f542ca070c 100644 --- a/fuzzers/libfuzzer_libpng/src/bin/libafl_cc.rs +++ b/fuzzers/libfuzzer_libpng/src/bin/libafl_cc.rs @@ -1,6 +1,6 @@ use std::env; -use libafl_cc::{ClangWrapper, CompilerWrapper}; +use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper}; pub fn main() { let args: Vec = env::args().collect(); diff --git a/fuzzers/libfuzzer_libpng_accounting/src/bin/libafl_cc.rs b/fuzzers/libfuzzer_libpng_accounting/src/bin/libafl_cc.rs index 1174d76b61..21dcd8fb78 100644 --- a/fuzzers/libfuzzer_libpng_accounting/src/bin/libafl_cc.rs +++ b/fuzzers/libfuzzer_libpng_accounting/src/bin/libafl_cc.rs @@ -1,6 +1,6 @@ use std::env; -use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; +use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses, ToolWrapper}; const GRANULARITY: &str = "FUNC"; diff --git a/fuzzers/libfuzzer_libpng_centralized/src/bin/libafl_cc.rs b/fuzzers/libfuzzer_libpng_centralized/src/bin/libafl_cc.rs index 69f3766586..f542ca070c 100644 --- a/fuzzers/libfuzzer_libpng_centralized/src/bin/libafl_cc.rs +++ b/fuzzers/libfuzzer_libpng_centralized/src/bin/libafl_cc.rs @@ -1,6 +1,6 @@ use std::env; -use libafl_cc::{ClangWrapper, CompilerWrapper}; +use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper}; pub fn main() { let args: Vec = env::args().collect(); diff --git a/fuzzers/libfuzzer_libpng_cmin/src/bin/libafl_cc.rs b/fuzzers/libfuzzer_libpng_cmin/src/bin/libafl_cc.rs index e82a4be918..307fbc1154 100644 --- a/fuzzers/libfuzzer_libpng_cmin/src/bin/libafl_cc.rs +++ b/fuzzers/libfuzzer_libpng_cmin/src/bin/libafl_cc.rs @@ -1,6 +1,6 @@ use std::env; -use libafl_cc::{ClangWrapper, CompilerWrapper}; +use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper}; pub fn main() { let args: Vec = env::args().collect(); diff --git a/fuzzers/libfuzzer_libpng_ctx/src/bin/libafl_cc.rs b/fuzzers/libfuzzer_libpng_ctx/src/bin/libafl_cc.rs index 6f4436f8a9..fc8c6c2800 100644 --- a/fuzzers/libfuzzer_libpng_ctx/src/bin/libafl_cc.rs +++ b/fuzzers/libfuzzer_libpng_ctx/src/bin/libafl_cc.rs @@ -1,6 +1,6 @@ use std::env; -use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses}; +use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses, ToolWrapper}; pub fn main() { let args: Vec = env::args().collect(); diff --git a/fuzzers/libfuzzer_libpng_launcher/Makefile.toml b/fuzzers/libfuzzer_libpng_launcher/Makefile.toml index 7fb21e0c5b..29d462dd9f 100644 --- a/fuzzers/libfuzzer_libpng_launcher/Makefile.toml +++ b/fuzzers/libfuzzer_libpng_launcher/Makefile.toml @@ -4,6 +4,7 @@ FUZZER_NAME='fuzzer_libpng_launcher' CARGO_TARGET_DIR = { value = "${PROJECT_DIR}/target", condition = { env_not_set = ["CARGO_TARGET_DIR"] } } LIBAFL_CC = '${CARGO_TARGET_DIR}/release/libafl_cc' LIBAFL_CXX = '${CARGO_TARGET_DIR}/release/libafl_cxx' +LIBAFL_LIBTOOL = '${CARGO_TARGET_DIR}/release/libafl_libtool' FUZZER = '${CARGO_TARGET_DIR}/release/${FUZZER_NAME}' PROJECT_DIR = { script = ["pwd"] } @@ -57,7 +58,7 @@ script_runner="@shell" script=''' cd libpng-1.6.37 && ./configure --enable-shared=no --with-pic=yes --enable-hardware-optimizations=yes cd "${PROJECT_DIR}" -make -C libpng-1.6.37 CC="${CARGO_TARGET_DIR}/release/libafl_cc" CXX="${CARGO_TARGET_DIR}/release/libafl_cxx" +make -C libpng-1.6.37 CC="${CARGO_TARGET_DIR}/release/libafl_cc" CXX="${CARGO_TARGET_DIR}/release/libafl_cxx" LIBTOOL=${CARGO_TARGET_DIR}/release/libafl_libtool ''' dependencies = [ "libpng", "cxx", "cc" ] @@ -82,7 +83,7 @@ windows_alias = "unsupported" [tasks.run_unix] script_runner = "@shell" script=''' -./${FUZZER_NAME} --cores 0 --input ./corpus +./${FUZZER_NAME}.coverage --broker-port 21337 --cores 0 --input ./corpus ''' dependencies = [ "fuzzer" ] @@ -96,7 +97,7 @@ windows_alias = "unsupported" script_runner = "@shell" script=''' rm -rf libafl_unix_shmem_server || true -timeout 11s ./${FUZZER_NAME} --cores 0 --input ./corpus 2>/dev/null >fuzz_stdout.log || true +timeout 11s ./${FUZZER_NAME}.coverage --broker-port 21337 --cores 0 --input ./corpus 2>/dev/null >fuzz_stdout.log || true if [ -z "$(grep "corpus: 30" fuzz_stdout.log)" ]; then echo "Fuzzer does not generate any testcases or any crashes" exit 1 diff --git a/fuzzers/libfuzzer_libpng_launcher/src/bin/libafl_ar.rs b/fuzzers/libfuzzer_libpng_launcher/src/bin/libafl_ar.rs new file mode 100644 index 0000000000..433f3b66cc --- /dev/null +++ b/fuzzers/libfuzzer_libpng_launcher/src/bin/libafl_ar.rs @@ -0,0 +1,31 @@ +use std::env; + +use libafl_cc::{ArWrapper, Configuration, ToolWrapper}; + +pub fn main() { + let args: Vec = env::args().collect(); + if args.len() > 1 { + let mut cc = ArWrapper::new(); + if let Some(code) = cc + // silence the compiler wrapper output, needed for some configure scripts. + .silence(true) + .parse_args(&args) + .expect("Failed to parse the command line") + .add_configuration(Configuration::GenerateCoverageMap) + .add_configuration(Configuration::Compound(vec![ + Configuration::GenerateCoverageMap, + Configuration::CmpLog, + ])) + .add_configuration(Configuration::UndefinedBehaviorSanitizer) + .add_configuration(Configuration::AddressSanitizer) + // .add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp") + // .add_arg("-fsanitize=address") + .run() + .expect("Failed to run the wrapped libtool") + { + std::process::exit(code); + } + } else { + panic!("LibAFL libtool: No Arguments given"); + } +} diff --git a/fuzzers/libfuzzer_libpng_launcher/src/bin/libafl_cc.rs b/fuzzers/libfuzzer_libpng_launcher/src/bin/libafl_cc.rs index 69f3766586..e5802d4683 100644 --- a/fuzzers/libfuzzer_libpng_launcher/src/bin/libafl_cc.rs +++ b/fuzzers/libfuzzer_libpng_launcher/src/bin/libafl_cc.rs @@ -1,6 +1,6 @@ use std::env; -use libafl_cc::{ClangWrapper, CompilerWrapper}; +use libafl_cc::{ClangWrapper, CompilerWrapper, Configuration, ToolWrapper}; pub fn main() { let args: Vec = env::args().collect(); @@ -24,7 +24,15 @@ pub fn main() { .parse_args(&args) .expect("Failed to parse the command line") .link_staticlib(&dir, "libfuzzer_libpng") - .add_arg("-fsanitize-coverage=trace-pc-guard") + .add_configuration(Configuration::GenerateCoverageMap) + .add_configuration(Configuration::Compound(vec![ + Configuration::GenerateCoverageMap, + Configuration::CmpLog, + ])) + .add_configuration(Configuration::UndefinedBehaviorSanitizer) + .add_configuration(Configuration::AddressSanitizer) + // .add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp") + // .add_arg("-fsanitize=address") .run() .expect("Failed to run the wrapped compiler") { diff --git a/fuzzers/libfuzzer_libpng_launcher/src/bin/libafl_libtool.rs b/fuzzers/libfuzzer_libpng_launcher/src/bin/libafl_libtool.rs new file mode 100644 index 0000000000..3902559afe --- /dev/null +++ b/fuzzers/libfuzzer_libpng_launcher/src/bin/libafl_libtool.rs @@ -0,0 +1,31 @@ +use std::env; + +use libafl_cc::{Configuration, LibtoolWrapper, ToolWrapper}; + +pub fn main() { + let args: Vec = env::args().collect(); + if args.len() > 1 { + let mut cc = LibtoolWrapper::new(); + if let Some(code) = cc + // silence the compiler wrapper output, needed for some configure scripts. + .silence(true) + .parse_args(&args) + .expect("Failed to parse the command line") + .add_configuration(Configuration::GenerateCoverageMap) + .add_configuration(Configuration::Compound(vec![ + Configuration::GenerateCoverageMap, + Configuration::CmpLog, + ])) + .add_configuration(Configuration::UndefinedBehaviorSanitizer) + .add_configuration(Configuration::AddressSanitizer) + // .add_arg("-fsanitize-coverage=trace-pc-guard,trace-cmp") + // .add_arg("-fsanitize=address") + .run() + .expect("Failed to run the wrapped libtool") + { + std::process::exit(code); + } + } else { + panic!("LibAFL libtool: No Arguments given"); + } +} diff --git a/fuzzers/libfuzzer_libpng_norestart/src/bin/libafl_cc.rs b/fuzzers/libfuzzer_libpng_norestart/src/bin/libafl_cc.rs index 69f3766586..f542ca070c 100644 --- a/fuzzers/libfuzzer_libpng_norestart/src/bin/libafl_cc.rs +++ b/fuzzers/libfuzzer_libpng_norestart/src/bin/libafl_cc.rs @@ -1,6 +1,6 @@ use std::env; -use libafl_cc::{ClangWrapper, CompilerWrapper}; +use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper}; pub fn main() { let args: Vec = env::args().collect(); diff --git a/fuzzers/libfuzzer_libpng_tcp_manager/src/bin/libafl_cc.rs b/fuzzers/libfuzzer_libpng_tcp_manager/src/bin/libafl_cc.rs index 69f3766586..f542ca070c 100644 --- a/fuzzers/libfuzzer_libpng_tcp_manager/src/bin/libafl_cc.rs +++ b/fuzzers/libfuzzer_libpng_tcp_manager/src/bin/libafl_cc.rs @@ -1,6 +1,6 @@ use std::env; -use libafl_cc::{ClangWrapper, CompilerWrapper}; +use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper}; pub fn main() { let args: Vec = env::args().collect(); diff --git a/fuzzers/libfuzzer_reachability/src/bin/libafl_cc.rs b/fuzzers/libfuzzer_reachability/src/bin/libafl_cc.rs index b631e484de..36b2acbdf8 100644 --- a/fuzzers/libfuzzer_reachability/src/bin/libafl_cc.rs +++ b/fuzzers/libfuzzer_reachability/src/bin/libafl_cc.rs @@ -1,6 +1,6 @@ use std::env; -use libafl_cc::{ClangWrapper, CompilerWrapper}; +use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper}; pub fn main() { let args: Vec = env::args().collect(); diff --git a/fuzzers/libfuzzer_windows_asan/src/bin/libafl_cc.rs b/fuzzers/libfuzzer_windows_asan/src/bin/libafl_cc.rs index 64c9536d5c..53610b57cb 100644 --- a/fuzzers/libfuzzer_windows_asan/src/bin/libafl_cc.rs +++ b/fuzzers/libfuzzer_windows_asan/src/bin/libafl_cc.rs @@ -1,6 +1,6 @@ use std::env; -use libafl_cc::{ClangWrapper, CompilerWrapper}; +use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper}; pub fn main() { let args: Vec = env::args().collect(); diff --git a/fuzzers/nautilus_sync/src/bin/libafl_cc.rs b/fuzzers/nautilus_sync/src/bin/libafl_cc.rs index 3a11b9b3f3..1f79c67c9f 100644 --- a/fuzzers/nautilus_sync/src/bin/libafl_cc.rs +++ b/fuzzers/nautilus_sync/src/bin/libafl_cc.rs @@ -1,6 +1,6 @@ use std::{env, process::Command, str}; -use libafl_cc::{ClangWrapper, CompilerWrapper}; +use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper}; fn find_libpython() -> Result { match Command::new("python3") diff --git a/fuzzers/nyx_libxml2_parallel/src/bin/libafl_cc.rs b/fuzzers/nyx_libxml2_parallel/src/bin/libafl_cc.rs index 4a6519f571..eae59d52db 100644 --- a/fuzzers/nyx_libxml2_parallel/src/bin/libafl_cc.rs +++ b/fuzzers/nyx_libxml2_parallel/src/bin/libafl_cc.rs @@ -1,6 +1,6 @@ use std::env; -use libafl_cc::{ClangWrapper, CompilerWrapper}; +use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper}; pub fn main() { let args: Vec = env::args().collect(); diff --git a/fuzzers/nyx_libxml2_standalone/src/bin/libafl_cc.rs b/fuzzers/nyx_libxml2_standalone/src/bin/libafl_cc.rs index 4a6519f571..eae59d52db 100644 --- a/fuzzers/nyx_libxml2_standalone/src/bin/libafl_cc.rs +++ b/fuzzers/nyx_libxml2_standalone/src/bin/libafl_cc.rs @@ -1,6 +1,6 @@ use std::env; -use libafl_cc::{ClangWrapper, CompilerWrapper}; +use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper}; pub fn main() { let args: Vec = env::args().collect(); diff --git a/fuzzers/tutorial/src/bin/libafl_cc.rs b/fuzzers/tutorial/src/bin/libafl_cc.rs index 7b2ce8f725..a83f8cb016 100644 --- a/fuzzers/tutorial/src/bin/libafl_cc.rs +++ b/fuzzers/tutorial/src/bin/libafl_cc.rs @@ -1,6 +1,6 @@ use std::env; -use libafl_cc::{ClangWrapper, CompilerWrapper}; +use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper}; pub fn main() { let args: Vec = env::args().collect(); diff --git a/libafl_cc/src/ar.rs b/libafl_cc/src/ar.rs new file mode 100644 index 0000000000..6443f5cb64 --- /dev/null +++ b/libafl_cc/src/ar.rs @@ -0,0 +1,254 @@ +//! Ar Wrapper from `LibAFL` +// pass to e.g. cmake with -DCMAKE_AR=/path/to/fuzzer/target/release/libafl_ar + +use std::{convert::Into, env, path::PathBuf, str::FromStr, string::String, vec::Vec}; + +use crate::{Error, ToolWrapper, LIB_EXT, LIB_PREFIX}; + +/// Wrap Clang +#[allow(clippy::struct_excessive_bools)] +#[derive(Debug)] +pub struct ArWrapper { + is_silent: bool, + + name: String, + linking: bool, + need_libafl_arg: bool, + has_libafl_arg: bool, + + configurations: Vec, + parse_args_called: bool, + base_args: Vec, +} + +#[allow(clippy::match_same_arms)] // for the linking = false wip for "shared" +impl ToolWrapper for ArWrapper { + #[allow(clippy::too_many_lines)] + fn parse_args(&mut self, args: &[S]) -> Result<&'_ mut Self, Error> + where + S: AsRef, + { + let mut new_args: Vec = vec![]; + if args.is_empty() { + return Err(Error::InvalidArguments( + "The number of arguments cannot be 0".to_string(), + )); + } + + if self.parse_args_called { + return Err(Error::Unknown( + "ToolWrapper::parse_args cannot be called twice on the same instance".to_string(), + )); + } + self.parse_args_called = true; + + if args.len() == 1 { + return Err(Error::InvalidArguments( + "LibAFL Tool wrapper - no commands specified. Use me as compiler.".to_string(), + )); + } + + self.name = args[0].as_ref().to_string(); + + let mut linking = true; + // Detect stray -v calls from ./configure scripts. + if args.len() > 1 && args[1].as_ref() == "-v" { + if args.len() == 2 { + self.base_args.push(args[1].as_ref().into()); + return Ok(self); + } + linking = false; + } + + let mut suppress_linking = 0; + let mut i = 1; + while i < args.len() { + match args[i].as_ref() { + "--libafl-no-link" => { + suppress_linking += 1; + self.has_libafl_arg = true; + i += 1; + continue; + } + "--libafl" => { + suppress_linking += 1337; + self.has_libafl_arg = true; + i += 1; + continue; + } + "-fsanitize=fuzzer-no-link" => { + suppress_linking += 1; + self.has_libafl_arg = true; + i += 1; + continue; + } + "-fsanitize=fuzzer" => { + suppress_linking += 1337; + self.has_libafl_arg = true; + i += 1; + continue; + } + "--libafl-configurations" => { + if i + 1 < args.len() { + self.configurations.extend( + args[i + 1] + .as_ref() + .split(',') + .map(|x| crate::Configuration::from_str(x).unwrap()), + ); + i += 2; + continue; + } + } + _ => (), + }; + new_args.push(args[i].as_ref().to_string()); + i += 1; + } + if linking + && (suppress_linking > 0 || (self.has_libafl_arg && suppress_linking == 0)) + && suppress_linking < 1337 + { + linking = false; + new_args.push( + PathBuf::from(env!("OUT_DIR")) + .join(format!("{LIB_PREFIX}no-link-rt.{LIB_EXT}")) + .into_os_string() + .into_string() + .unwrap(), + ); + } + + self.linking = linking; + + // Libraries needed by libafl on Windows + self.base_args.extend(new_args); + Ok(self) + } + + fn add_arg(&mut self, arg: S) -> &'_ mut Self + where + S: AsRef, + { + self.base_args.push(arg.as_ref().to_string()); + self + } + + fn add_configuration(&mut self, configuration: crate::Configuration) -> &'_ mut Self { + self.configurations.push(configuration); + self + } + + fn configurations(&self) -> Result, Error> { + let configs = self.configurations.clone(); + Ok(configs) + } + + fn ignore_configurations(&self) -> Result { + Ok(false) + } + + fn command(&mut self) -> Result, Error> { + self.command_for_configuration(crate::Configuration::Default) + } + + fn command_for_configuration( + &mut self, + configuration: crate::Configuration, + ) -> Result, Error> { + let mut args = vec![]; + + let base_args = self + .base_args + .iter() + .map(|r| { + let arg_as_path = std::path::PathBuf::from(r); + if r.ends_with('.') { + r.to_string() + } else { + if let Some(extension) = arg_as_path.extension() { + let extension = extension.to_str().unwrap(); + let extension_lowercase = extension.to_lowercase(); + match &extension_lowercase[..] { + "o" | "lo" | "a" | "la" | "so" => { + configuration.replace_extension(&arg_as_path) + } + _ => arg_as_path, + } + } else { + arg_as_path + } + .into_os_string() + .into_string() + .unwrap() + } + }) + .collect::>(); + + let Ok(ar_path) = std::env::var("LLVM_AR_PATH") else { + panic!("Couldn't find llvm-ar. Specify the `LLVM_AR_PATH` environment variable"); + }; + + args.push(ar_path); + + args.extend_from_slice(base_args.as_slice()); + + if self.need_libafl_arg && !self.has_libafl_arg { + return Ok(args); + } + + Ok(args) + } + + fn is_linking(&self) -> bool { + self.linking + } + + fn filter(&self, _args: &mut Vec) {} + + fn silence(&mut self, value: bool) -> &'_ mut Self { + self.is_silent = value; + self + } + + fn is_silent(&self) -> bool { + self.is_silent + } +} + +impl Default for ArWrapper { + /// Create a new Clang Wrapper + #[must_use] + fn default() -> Self { + Self::new() + } +} + +impl ArWrapper { + /// Create a new Clang Wrapper + #[must_use] + pub fn new() -> Self { + Self { + name: String::new(), + linking: false, + need_libafl_arg: false, + has_libafl_arg: false, + configurations: vec![crate::Configuration::Default], + parse_args_called: false, + base_args: vec![], + is_silent: false, + } + } + + /// Set if linking + pub fn linking(&mut self, value: bool) -> &'_ mut Self { + self.linking = value; + self + } + + /// Set if it needs the --libafl arg to add the custom arguments to clang + pub fn need_libafl_arg(&mut self, value: bool) -> &'_ mut Self { + self.need_libafl_arg = value; + self + } +} diff --git a/libafl_cc/src/clang.rs b/libafl_cc/src/clang.rs index 26caf4441d..0f07a4a48b 100644 --- a/libafl_cc/src/clang.rs +++ b/libafl_cc/src/clang.rs @@ -4,11 +4,12 @@ use std::{ convert::Into, env, path::{Path, PathBuf}, + str::FromStr, string::String, vec::Vec, }; -use crate::{CompilerWrapper, Error, LIB_EXT, LIB_PREFIX}; +use crate::{CompilerWrapper, Error, ToolWrapper, LIB_EXT, LIB_PREFIX}; /// The `OUT_DIR` for `LLVM` compiler passes pub const OUT_DIR: &str = env!("OUT_DIR"); @@ -83,6 +84,9 @@ pub struct ClangWrapper { has_libafl_arg: bool, use_new_pm: bool, + output: Option, + configurations: Vec, + ignoring_configurations: bool, parse_args_called: bool, base_args: Vec, cc_args: Vec, @@ -93,7 +97,7 @@ pub struct ClangWrapper { } #[allow(clippy::match_same_arms)] // for the linking = false wip for "shared" -impl CompilerWrapper for ClangWrapper { +impl ToolWrapper for ClangWrapper { #[allow(clippy::too_many_lines)] fn parse_args(&mut self, args: &[S]) -> Result<&'_ mut Self, Error> where @@ -108,15 +112,14 @@ impl CompilerWrapper for ClangWrapper { if self.parse_args_called { return Err(Error::Unknown( - "CompilerWrapper::parse_args cannot be called twice on the same instance" - .to_string(), + "ToolWrapper::parse_args cannot be called twice on the same instance".to_string(), )); } self.parse_args_called = true; if args.len() == 1 { return Err(Error::InvalidArguments( - "LibAFL Compiler wrapper - no commands specified. Use me as compiler.".to_string(), + "LibAFL Tool wrapper - no commands specified. Use me as compiler.".to_string(), )); } @@ -145,7 +148,9 @@ impl CompilerWrapper for ClangWrapper { let mut suppress_linking = 0; let mut i = 1; while i < args.len() { - if std::path::Path::new(args[i].as_ref()) + let arg_as_path = std::path::Path::new(args[i].as_ref()); + + if arg_as_path .extension() .map_or(false, |ext| ext.eq_ignore_ascii_case("s")) { @@ -189,6 +194,30 @@ impl CompilerWrapper for ClangWrapper { continue; } } + "--libafl-ignore-configurations" => { + self.ignoring_configurations = true; + i += 1; + continue; + } + "--libafl-configurations" => { + if i + 1 < args.len() { + self.configurations.extend( + args[i + 1] + .as_ref() + .split(',') + .map(|x| crate::Configuration::from_str(x).unwrap()), + ); + i += 2; + continue; + } + } + "-o" => { + if i + 1 < args.len() { + self.output = Some(PathBuf::from(args[i + 1].as_ref())); + i += 2; + continue; + } + } "-x" => self.x_set = true, "-m32" => self.bit_mode = 32, "-m64" => self.bit_mode = 64, @@ -259,49 +288,30 @@ impl CompilerWrapper for ClangWrapper { self } - fn add_cc_arg(&mut self, arg: S) -> &'_ mut Self - where - S: AsRef, - { - self.cc_args.push(arg.as_ref().to_string()); + fn add_configuration(&mut self, configuration: crate::Configuration) -> &'_ mut Self { + self.configurations.push(configuration); self } - fn add_link_arg(&mut self, arg: S) -> &'_ mut Self - where - S: AsRef, - { - self.link_args.push(arg.as_ref().to_string()); - self + fn configurations(&self) -> Result, Error> { + let mut configs = self.configurations.clone(); + configs.reverse(); + Ok(configs) } - fn link_staticlib(&mut self, dir: &Path, name: S) -> &'_ mut Self - where - S: AsRef, - { - let lib_file = dir - .join(format!("{LIB_PREFIX}{}.{LIB_EXT}", name.as_ref())) - .into_os_string() - .into_string() - .unwrap(); - - if cfg!(unix) { - if cfg!(target_vendor = "apple") { - // Same as --whole-archive on linux - // Without this option, the linker picks the first symbols it finds and does not care if it's a weak or a strong symbol - // See: - self.add_link_arg("-Wl,-force_load").add_link_arg(lib_file) - } else { - self.add_link_arg("-Wl,--whole-archive") - .add_link_arg(lib_file) - .add_link_arg("-Wl,--no-whole-archive") - } - } else { - self.add_link_arg(format!("-Wl,-wholearchive:{lib_file}")) - } + fn ignore_configurations(&self) -> Result { + Ok(self.ignoring_configurations) } fn command(&mut self) -> Result, Error> { + self.command_for_configuration(crate::Configuration::Default) + } + + #[allow(clippy::too_many_lines)] + fn command_for_configuration( + &mut self, + configuration: crate::Configuration, + ) -> Result, Error> { let mut args = vec![]; let mut use_pass = false; @@ -310,7 +320,71 @@ impl CompilerWrapper for ClangWrapper { } else { args.push(self.wrapped_cc.clone()); } - args.extend_from_slice(self.base_args.as_slice()); + + let base_args = self + .base_args + .iter() + .map(|r| { + let arg_as_path = std::path::PathBuf::from(r); + if r.ends_with('.') { + r.to_string() + } else { + if let Some(extension) = arg_as_path.extension() { + let extension = extension.to_str().unwrap(); + let extension_lowercase = extension.to_lowercase(); + match &extension_lowercase[..] { + "a" | "la" => configuration.replace_extension(&arg_as_path), + _ => arg_as_path, + } + } else { + arg_as_path + } + .into_os_string() + .into_string() + .unwrap() + } + }) + .collect::>(); + + if let Some(output) = self.output.clone() { + let output = configuration.replace_extension(&output); + let new_filename = output.into_os_string().into_string().unwrap(); + args.push("-o".to_string()); + args.push(new_filename); + args.extend_from_slice(base_args.as_slice()); + } else { + // No output specified, we need to rewrite the single .c file's name. + args.extend( + base_args + .iter() + .map(|r| { + let arg_as_path = std::path::PathBuf::from(r); + if r.ends_with('.') { + r.to_string() + } else { + if let Some(extension) = arg_as_path.extension() { + let extension = extension.to_str().unwrap(); + let extension_lowercase = extension.to_lowercase(); + match &extension_lowercase[..] { + "c" | "cc" | "cxx" | "cpp" => { + configuration.replace_extension(&arg_as_path) + } + _ => arg_as_path, + } + } else { + arg_as_path + } + .into_os_string() + .into_string() + .unwrap() + } + }) + .collect::>(), + ); + } + + args.extend_from_slice(&configuration.to_flags()?); + if self.need_libafl_arg && !self.has_libafl_arg { return Ok(args); } @@ -369,6 +443,7 @@ impl CompilerWrapper for ClangWrapper { if cfg!(unix) { args.push("-pthread".into()); args.push("-ldl".into()); + args.push("-lm".into()); } } else { args.extend_from_slice(self.cc_args.as_slice()); @@ -398,6 +473,49 @@ impl CompilerWrapper for ClangWrapper { } } +impl CompilerWrapper for ClangWrapper { + fn add_cc_arg(&mut self, arg: S) -> &'_ mut Self + where + S: AsRef, + { + self.cc_args.push(arg.as_ref().to_string()); + self + } + + fn add_link_arg(&mut self, arg: S) -> &'_ mut Self + where + S: AsRef, + { + self.link_args.push(arg.as_ref().to_string()); + self + } + + fn link_staticlib(&mut self, dir: &Path, name: S) -> &'_ mut Self + where + S: AsRef, + { + let lib_file = dir + .join(format!("{LIB_PREFIX}{}.{LIB_EXT}", name.as_ref())) + .into_os_string() + .into_string() + .unwrap(); + + if cfg!(unix) { + if cfg!(target_vendor = "apple") { + // Same as --whole-archive on linux + // Without this option, the linker picks the first symbols it finds and does not care if it's a weak or a strong symbol + // See: + self.add_link_arg("-Wl,-force_load").add_link_arg(lib_file) + } else { + self.add_link_arg("-Wl,--whole-archive") + .add_link_arg(lib_file) + .add_link_arg("-Wl,--no-whole-archive") + } + } else { + self.add_link_arg(format!("-Wl,-wholearchive:{lib_file}")) + } + } +} impl Default for ClangWrapper { /// Create a new Clang Wrapper #[must_use] @@ -432,6 +550,9 @@ impl ClangWrapper { need_libafl_arg: false, has_libafl_arg: false, use_new_pm, + output: None, + configurations: vec![crate::Configuration::Default], + ignoring_configurations: false, parse_args_called: false, base_args: vec![], cc_args: vec![], @@ -512,7 +633,7 @@ impl ClangWrapper { #[cfg(test)] mod tests { - use crate::{ClangWrapper, CompilerWrapper}; + use crate::{ClangWrapper, ToolWrapper}; #[test] #[cfg_attr(miri, ignore)] diff --git a/libafl_cc/src/lib.rs b/libafl_cc/src/lib.rs index 63278e1072..499e85aa1c 100644 --- a/libafl_cc/src/lib.rs +++ b/libafl_cc/src/lib.rs @@ -60,10 +60,14 @@ use std::{convert::Into, path::Path, process::Command, string::String, vec::Vec}; +pub mod ar; +pub use ar::ArWrapper; pub mod cfg; pub use cfg::{CfgEdge, ControlFlowGraph, EntryBasicBlockInfo, HasWeight}; pub mod clang; pub use clang::{ClangWrapper, LLVMPasses}; +pub mod libtool; +pub use libtool::LibtoolWrapper; /// `LibAFL` CC Error Type #[derive(Debug)] @@ -76,6 +80,112 @@ pub enum Error { Unknown(String), } +/// `LibAFL` target configuration +#[derive(Debug, Clone)] +pub enum Configuration { + /// Default uninstrumented configurations + Default, + /// Sanitizing addresses + AddressSanitizer, + /// Sanitizing undefined behavior + UndefinedBehaviorSanitizer, + /// Generating a coverage map + GenerateCoverageMap, + /// Generating coverage profile data for `llvm-cov` + GenerateCoverageProfile, + /// Instrumenting for cmplog/redqueen + CmpLog, + /// A compound `Configuration`, made up of a list of other `Configuration`s + Compound(Vec), +} + +impl Configuration { + /// Get compiler flags for this `Configuration` + pub fn to_flags(&self) -> Result, Error> { + Ok(match self { + Configuration::Default => vec![], + Configuration::AddressSanitizer => vec!["-fsanitize=address".to_string()], + Configuration::UndefinedBehaviorSanitizer => vec!["-fsanitize=undefined".to_string()], + Configuration::GenerateCoverageMap => { + vec!["-fsanitize-coverage=trace-pc-guard".to_string()] + } + Configuration::CmpLog => vec!["-fsanitize-coverage=trace-cmp".to_string()], + Configuration::GenerateCoverageProfile => { + vec![ + "-fprofile-instr-generate".to_string(), + "-fcoverage-mapping".to_string(), + ] + } + Configuration::Compound(configurations) => { + let mut result: Vec = vec![]; + for configuration in configurations { + result.extend(configuration.to_flags()?); + } + result + } + }) + } + /// Insert a `Configuration` specific 'tag' in the extension of the given file + #[must_use] + pub fn replace_extension(&self, path: &Path) -> std::path::PathBuf { + let mut parent = if let Some(parent) = path.parent() { + parent.to_path_buf() + } else { + std::path::PathBuf::from("") + }; + let output = path.file_name().unwrap(); + let output = output.to_str().unwrap(); + + let new_filename = if let Some((filename, extension)) = output.split_once('.') { + if let crate::Configuration::Default = self { + format!("{filename}.{extension}") + } else { + format!("{filename}.{self}.{extension}") + } + } else if let crate::Configuration::Default = self { + output.to_string() + } else { + format!("{output}.{self}") + }; + parent.push(new_filename); + parent + } +} + +impl std::str::FromStr for Configuration { + type Err = (); + fn from_str(input: &str) -> Result { + Ok(match input { + "asan" => Configuration::AddressSanitizer, + "ubsan" => Configuration::UndefinedBehaviorSanitizer, + "coverage" => Configuration::GenerateCoverageMap, + "llvm-cov" => Configuration::GenerateCoverageProfile, + "cmplog" => Configuration::CmpLog, + _ => Configuration::Default, + }) + } +} + +impl std::fmt::Display for Configuration { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Configuration::Default => write!(f, ""), + Configuration::AddressSanitizer => write!(f, "asan"), + Configuration::UndefinedBehaviorSanitizer => write!(f, "ubsan"), + Configuration::GenerateCoverageMap => write!(f, "coverage"), + Configuration::GenerateCoverageProfile => write!(f, "llvm-cov"), + Configuration::CmpLog => write!(f, "cmplog"), + Configuration::Compound(configurations) => { + let mut result: Vec = vec![]; + for configuration in configurations { + result.push(format!("{configuration}")); + } + write!(f, "{}", result.join("_")) + } + } + } +} + // TODO macOS /// extension for static libraries #[cfg(windows)] @@ -91,29 +201,19 @@ pub const LIB_PREFIX: &str = ""; #[cfg(not(windows))] pub const LIB_PREFIX: &str = "lib"; -/// Wrap a compiler hijacking its arguments -pub trait CompilerWrapper { +/// Wrap a tool hijacking its arguments +pub trait ToolWrapper { /// Set the wrapper arguments parsing a command line set of arguments fn parse_args(&mut self, args: &[S]) -> Result<&'_ mut Self, Error> where S: AsRef; - /// Add a compiler argument + /// Add an argument fn add_arg(&mut self, arg: S) -> &'_ mut Self where S: AsRef; - /// Add a compiler argument only when compiling - fn add_cc_arg(&mut self, arg: S) -> &'_ mut Self - where - S: AsRef; - - /// Add a compiler argument only when linking - fn add_link_arg(&mut self, arg: S) -> &'_ mut Self - where - S: AsRef; - - /// Add compiler arguments + /// Add arguments fn add_args(&mut self, args: &[S]) -> &'_ mut Self where S: AsRef, @@ -124,6 +224,87 @@ pub trait CompilerWrapper { self } + /// Add a `Configuration` + fn add_configuration(&mut self, configuration: Configuration) -> &'_ mut Self; + + /// Command to run the compiler + fn command(&mut self) -> Result, Error>; + + /// Command to run the compiler for a given `Configuration` + #[allow(clippy::too_many_lines)] + fn command_for_configuration( + &mut self, + configuration: Configuration, + ) -> Result, Error>; + + /// Get the list of requested `Configuration`s + fn configurations(&self) -> Result, Error>; + + /// Whether to ignore the configured `Configurations`. Useful for e.g. nested calls to + /// `libafl_cc` from `libafl_libtool`. + fn ignore_configurations(&self) -> Result; + + /// Get if in linking mode + fn is_linking(&self) -> bool; + + /// Filter out argumets + fn filter(&self, _args: &mut Vec) {} + + /// Silences `libafl_cc` output + fn silence(&mut self, value: bool) -> &'_ mut Self; + + /// Returns `true` if `silence` was called with `true` + fn is_silent(&self) -> bool; + + /// Run the tool + fn run(&mut self) -> Result, Error> { + let mut last_status = Ok(None); + let configurations = if self.ignore_configurations()? { + vec![Configuration::Default] + } else { + self.configurations()? + }; + for configuration in configurations { + let mut args = self.command_for_configuration(configuration)?; + self.filter(&mut args); + + if !self.is_silent() { + dbg!(args.clone()); + } + if args.is_empty() { + last_status = Err(Error::InvalidArguments( + "The number of arguments cannot be 0".into(), + )); + continue; + } + let status = match Command::new(&args[0]).args(&args[1..]).status() { + Ok(s) => s, + Err(e) => { + last_status = Err(Error::Io(e)); + continue; + } + }; + if !self.is_silent() { + dbg!(status); + } + last_status = Ok(status.code()); + } + last_status + } +} + +/// Wrap a compiler hijacking its arguments +pub trait CompilerWrapper: ToolWrapper { + /// Add a compiler argument only when compiling + fn add_cc_arg(&mut self, arg: S) -> &'_ mut Self + where + S: AsRef; + + /// Add a compiler argument only when linking + fn add_link_arg(&mut self, arg: S) -> &'_ mut Self + where + S: AsRef; + /// Add compiler arguments only when compiling fn add_cc_args(&mut self, args: &[S]) -> &'_ mut Self where @@ -150,42 +331,4 @@ pub trait CompilerWrapper { fn link_staticlib(&mut self, dir: &Path, name: S) -> &'_ mut Self where S: AsRef; - - /// Command to run the compiler - fn command(&mut self) -> Result, Error>; - - /// Get if in linking mode - fn is_linking(&self) -> bool; - - /// Filter out argumets - fn filter(&self, _args: &mut Vec) {} - - /// Silences `libafl_cc` output - fn silence(&mut self, value: bool) -> &'_ mut Self; - - /// Returns `true` if `silence` was called with `true` - fn is_silent(&self) -> bool; - - /// Run the compiler - fn run(&mut self) -> Result, Error> { - let mut args = self.command()?; - self.filter(&mut args); - - if !self.is_silent() { - dbg!(args.clone()); - } - if args.is_empty() { - return Err(Error::InvalidArguments( - "The number of arguments cannot be 0".into(), - )); - } - let status = match Command::new(&args[0]).args(&args[1..]).status() { - Ok(s) => s, - Err(e) => return Err(Error::Io(e)), - }; - if !self.is_silent() { - dbg!(status); - } - Ok(status.code()) - } } diff --git a/libafl_cc/src/libtool.rs b/libafl_cc/src/libtool.rs new file mode 100644 index 0000000000..31aea32730 --- /dev/null +++ b/libafl_cc/src/libtool.rs @@ -0,0 +1,291 @@ +//! Libtool Wrapper from `LibAFL` +// call make passing LIBTOOL=/path/to/target/release/libafl_libtool + +use std::{convert::Into, env, path::PathBuf, str::FromStr, string::String, vec::Vec}; + +use crate::{Error, ToolWrapper, LIB_EXT, LIB_PREFIX}; + +/// Wrap Clang +#[allow(clippy::struct_excessive_bools)] +#[derive(Debug)] +pub struct LibtoolWrapper { + is_silent: bool, + + name: String, + linking: bool, + need_libafl_arg: bool, + has_libafl_arg: bool, + + output: Option, + configurations: Vec, + parse_args_called: bool, + base_args: Vec, +} + +#[allow(clippy::match_same_arms)] // for the linking = false wip for "shared" +impl ToolWrapper for LibtoolWrapper { + #[allow(clippy::too_many_lines)] + fn parse_args(&mut self, args: &[S]) -> Result<&'_ mut Self, Error> + where + S: AsRef, + { + let mut new_args: Vec = vec![]; + if args.is_empty() { + return Err(Error::InvalidArguments( + "The number of arguments cannot be 0".to_string(), + )); + } + + if self.parse_args_called { + return Err(Error::Unknown( + "ToolWrapper::parse_args cannot be called twice on the same instance".to_string(), + )); + } + self.parse_args_called = true; + + if args.len() == 1 { + return Err(Error::InvalidArguments( + "LibAFL Tool wrapper - no commands specified. Use me as compiler.".to_string(), + )); + } + + self.name = args[0].as_ref().to_string(); + + let mut linking = true; + // Detect stray -v calls from ./configure scripts. + if args.len() > 1 && args[1].as_ref() == "-v" { + if args.len() == 2 { + self.base_args.push(args[1].as_ref().into()); + return Ok(self); + } + linking = false; + } + + let mut suppress_linking = 0; + let mut i = 1; + while i < args.len() { + match args[i].as_ref() { + "--libafl-no-link" => { + suppress_linking += 1; + self.has_libafl_arg = true; + i += 1; + continue; + } + "--libafl" => { + suppress_linking += 1337; + self.has_libafl_arg = true; + i += 1; + continue; + } + "-fsanitize=fuzzer-no-link" => { + suppress_linking += 1; + self.has_libafl_arg = true; + i += 1; + continue; + } + "-fsanitize=fuzzer" => { + suppress_linking += 1337; + self.has_libafl_arg = true; + i += 1; + continue; + } + "--libafl-configurations" => { + if i + 1 < args.len() { + self.configurations.extend( + args[i + 1] + .as_ref() + .split(',') + .map(|x| crate::Configuration::from_str(x).unwrap()), + ); + i += 2; + continue; + } + } + "-o" => { + if i + 1 < args.len() { + self.output = Some(PathBuf::from(args[i + 1].as_ref())); + i += 2; + continue; + } + } + _ => (), + }; + new_args.push(args[i].as_ref().to_string()); + i += 1; + } + if linking + && (suppress_linking > 0 || (self.has_libafl_arg && suppress_linking == 0)) + && suppress_linking < 1337 + { + linking = false; + new_args.push( + PathBuf::from(env!("OUT_DIR")) + .join(format!("{LIB_PREFIX}no-link-rt.{LIB_EXT}")) + .into_os_string() + .into_string() + .unwrap(), + ); + } + + self.linking = linking; + + // Libraries needed by libafl on Windows + self.base_args.extend(new_args); + Ok(self) + } + + fn add_arg(&mut self, arg: S) -> &'_ mut Self + where + S: AsRef, + { + self.base_args.push(arg.as_ref().to_string()); + self + } + + fn add_configuration(&mut self, configuration: crate::Configuration) -> &'_ mut Self { + self.configurations.push(configuration); + self + } + + fn configurations(&self) -> Result, Error> { + let configs = self.configurations.clone(); + Ok(configs) + } + + fn ignore_configurations(&self) -> Result { + Ok(false) + } + + fn command(&mut self) -> Result, Error> { + self.command_for_configuration(crate::Configuration::Default) + } + + fn command_for_configuration( + &mut self, + configuration: crate::Configuration, + ) -> Result, Error> { + let mut args = vec![]; + + let base_args = self + .base_args + .iter() + .map(|r| { + let arg_as_path = std::path::PathBuf::from(r); + if r.ends_with('.') { + r.to_string() + } else { + if let Some(extension) = arg_as_path.extension() { + let extension = extension.to_str().unwrap(); + let extension_lowercase = extension.to_lowercase(); + match &extension_lowercase[..] { + "o" | "lo" | "a" | "la" | "so" => { + configuration.replace_extension(&arg_as_path) + } + _ => arg_as_path, + } + } else { + arg_as_path + } + .into_os_string() + .into_string() + .unwrap() + } + }) + .collect::>(); + + let libtool_path = if let Ok(libtool_dir) = std::env::var("LIBTOOL_DIR") { + format!("{libtool_dir}/libtool") + } else { + "./libtool".to_string() + }; + + assert!( + std::path::Path::new(&libtool_path).exists(), + "Couldn't find libtool. Specify the `LIBTOOL_DIR` environment variable" + ); + args.push(libtool_path); + + if let Some(output) = self.output.clone() { + let output = configuration.replace_extension(&output); + let new_filename = output.into_os_string().into_string().unwrap(); + let dash_c_position = base_args.iter().position(|x| x == "-c"); + if let Some(dash_c_position) = dash_c_position { + args.extend_from_slice(&base_args[..dash_c_position]); + args.extend_from_slice(&configuration.to_flags()?); + args.push("--libafl-ignore-configurations".to_string()); + args.push("-c".to_string()); + args.push("-o".to_string()); + args.push(new_filename); + args.extend_from_slice(&base_args[(dash_c_position + 1)..]); + } else { + args.extend_from_slice(base_args.as_slice()); + args.extend_from_slice(&configuration.to_flags()?); + args.push("--libafl-ignore-configurations".to_string()); + args.push("-o".to_string()); + args.push(new_filename); + } + } else { + args.extend_from_slice(base_args.as_slice()); + args.extend_from_slice(&configuration.to_flags()?); + } + + if self.need_libafl_arg && !self.has_libafl_arg { + return Ok(args); + } + + Ok(args) + } + + fn is_linking(&self) -> bool { + self.linking + } + + fn filter(&self, _args: &mut Vec) {} + + fn silence(&mut self, value: bool) -> &'_ mut Self { + self.is_silent = value; + self + } + + fn is_silent(&self) -> bool { + self.is_silent + } +} + +impl Default for LibtoolWrapper { + /// Create a new Clang Wrapper + #[must_use] + fn default() -> Self { + Self::new() + } +} + +impl LibtoolWrapper { + /// Create a new Clang Wrapper + #[must_use] + pub fn new() -> Self { + Self { + name: String::new(), + linking: false, + need_libafl_arg: false, + has_libafl_arg: false, + output: None, + configurations: vec![crate::Configuration::Default], + parse_args_called: false, + base_args: vec![], + is_silent: false, + } + } + + /// Set if linking + pub fn linking(&mut self, value: bool) -> &'_ mut Self { + self.linking = value; + self + } + + /// Set if it needs the --libafl arg to add the custom arguments to clang + pub fn need_libafl_arg(&mut self, value: bool) -> &'_ mut Self { + self.need_libafl_arg = value; + self + } +}