Add feature to build variants/configurations automatically, with libtool/cc/cxx shims (#1322)
* Add feature to build variants/configurations automatically, with libtool/cc/cxx shims * Fixes * Clippy * Add brief comment describing usage * Fix * Fix fuzzers: add ToolWrapper * Clippy * More clippy * More clippy * Add Compound configuration * Clippy * Fix * Clippy * Damn that Clippy * Change names of Configurations * Add ar wrapper * Fix * Clippy * Windows build * Clippy * Clippy * Clippy
This commit is contained in:
parent
07530fea57
commit
71aa0221a0
@ -1,6 +1,6 @@
|
||||
use std::env;
|
||||
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper};
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper};
|
||||
|
||||
pub fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
@ -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<String> = env::args().collect();
|
||||
|
@ -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<String> = env::args().collect();
|
||||
|
@ -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<String> = env::args().collect();
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::env;
|
||||
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper};
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper};
|
||||
|
||||
pub fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::env;
|
||||
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper};
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper};
|
||||
|
||||
pub fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::env;
|
||||
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses};
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses, ToolWrapper};
|
||||
|
||||
const GRANULARITY: &str = "FUNC";
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::env;
|
||||
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper};
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper};
|
||||
|
||||
pub fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::env;
|
||||
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper};
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper};
|
||||
|
||||
pub fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
@ -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<String> = env::args().collect();
|
||||
|
@ -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
|
||||
|
31
fuzzers/libfuzzer_libpng_launcher/src/bin/libafl_ar.rs
Normal file
31
fuzzers/libfuzzer_libpng_launcher/src/bin/libafl_ar.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use std::env;
|
||||
|
||||
use libafl_cc::{ArWrapper, Configuration, ToolWrapper};
|
||||
|
||||
pub fn main() {
|
||||
let args: Vec<String> = 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");
|
||||
}
|
||||
}
|
@ -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<String> = 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")
|
||||
{
|
||||
|
31
fuzzers/libfuzzer_libpng_launcher/src/bin/libafl_libtool.rs
Normal file
31
fuzzers/libfuzzer_libpng_launcher/src/bin/libafl_libtool.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use std::env;
|
||||
|
||||
use libafl_cc::{Configuration, LibtoolWrapper, ToolWrapper};
|
||||
|
||||
pub fn main() {
|
||||
let args: Vec<String> = 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");
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use std::env;
|
||||
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper};
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper};
|
||||
|
||||
pub fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::env;
|
||||
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper};
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper};
|
||||
|
||||
pub fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::env;
|
||||
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper};
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper};
|
||||
|
||||
pub fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::env;
|
||||
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper};
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper};
|
||||
|
||||
pub fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
@ -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<String, String> {
|
||||
match Command::new("python3")
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::env;
|
||||
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper};
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper};
|
||||
|
||||
pub fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::env;
|
||||
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper};
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper};
|
||||
|
||||
pub fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::env;
|
||||
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper};
|
||||
use libafl_cc::{ClangWrapper, CompilerWrapper, ToolWrapper};
|
||||
|
||||
pub fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
254
libafl_cc/src/ar.rs
Normal file
254
libafl_cc/src/ar.rs
Normal file
@ -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<crate::Configuration>,
|
||||
parse_args_called: bool,
|
||||
base_args: Vec<String>,
|
||||
}
|
||||
|
||||
#[allow(clippy::match_same_arms)] // for the linking = false wip for "shared"
|
||||
impl ToolWrapper for ArWrapper {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn parse_args<S>(&mut self, args: &[S]) -> Result<&'_ mut Self, Error>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let mut new_args: Vec<String> = 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<S>(&mut self, arg: S) -> &'_ mut Self
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
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<Vec<crate::Configuration>, Error> {
|
||||
let configs = self.configurations.clone();
|
||||
Ok(configs)
|
||||
}
|
||||
|
||||
fn ignore_configurations(&self) -> Result<bool, Error> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn command(&mut self) -> Result<Vec<String>, Error> {
|
||||
self.command_for_configuration(crate::Configuration::Default)
|
||||
}
|
||||
|
||||
fn command_for_configuration(
|
||||
&mut self,
|
||||
configuration: crate::Configuration,
|
||||
) -> Result<Vec<String>, 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::<Vec<_>>();
|
||||
|
||||
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<String>) {}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
@ -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<PathBuf>,
|
||||
configurations: Vec<crate::Configuration>,
|
||||
ignoring_configurations: bool,
|
||||
parse_args_called: bool,
|
||||
base_args: Vec<String>,
|
||||
cc_args: Vec<String>,
|
||||
@ -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<S>(&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<S>(&mut self, arg: S) -> &'_ mut Self
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
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<S>(&mut self, arg: S) -> &'_ mut Self
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
self.link_args.push(arg.as_ref().to_string());
|
||||
self
|
||||
fn configurations(&self) -> Result<Vec<crate::Configuration>, Error> {
|
||||
let mut configs = self.configurations.clone();
|
||||
configs.reverse();
|
||||
Ok(configs)
|
||||
}
|
||||
|
||||
fn link_staticlib<S>(&mut self, dir: &Path, name: S) -> &'_ mut Self
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
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: <https://stackoverflow.com/questions/13089166/how-to-make-gcc-link-strong-symbol-in-static-library-to-overwrite-weak-symbol>
|
||||
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<bool, Error> {
|
||||
Ok(self.ignoring_configurations)
|
||||
}
|
||||
|
||||
fn command(&mut self) -> Result<Vec<String>, Error> {
|
||||
self.command_for_configuration(crate::Configuration::Default)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn command_for_configuration(
|
||||
&mut self,
|
||||
configuration: crate::Configuration,
|
||||
) -> Result<Vec<String>, 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::<Vec<_>>();
|
||||
|
||||
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::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
|
||||
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<S>(&mut self, arg: S) -> &'_ mut Self
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
self.cc_args.push(arg.as_ref().to_string());
|
||||
self
|
||||
}
|
||||
|
||||
fn add_link_arg<S>(&mut self, arg: S) -> &'_ mut Self
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
self.link_args.push(arg.as_ref().to_string());
|
||||
self
|
||||
}
|
||||
|
||||
fn link_staticlib<S>(&mut self, dir: &Path, name: S) -> &'_ mut Self
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
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: <https://stackoverflow.com/questions/13089166/how-to-make-gcc-link-strong-symbol-in-static-library-to-overwrite-weak-symbol>
|
||||
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)]
|
||||
|
@ -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<Self>),
|
||||
}
|
||||
|
||||
impl Configuration {
|
||||
/// Get compiler flags for this `Configuration`
|
||||
pub fn to_flags(&self) -> Result<Vec<String>, 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<String> = 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<Configuration, Self::Err> {
|
||||
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<String> = 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<S>(&mut self, args: &[S]) -> Result<&'_ mut Self, Error>
|
||||
where
|
||||
S: AsRef<str>;
|
||||
|
||||
/// Add a compiler argument
|
||||
/// Add an argument
|
||||
fn add_arg<S>(&mut self, arg: S) -> &'_ mut Self
|
||||
where
|
||||
S: AsRef<str>;
|
||||
|
||||
/// Add a compiler argument only when compiling
|
||||
fn add_cc_arg<S>(&mut self, arg: S) -> &'_ mut Self
|
||||
where
|
||||
S: AsRef<str>;
|
||||
|
||||
/// Add a compiler argument only when linking
|
||||
fn add_link_arg<S>(&mut self, arg: S) -> &'_ mut Self
|
||||
where
|
||||
S: AsRef<str>;
|
||||
|
||||
/// Add compiler arguments
|
||||
/// Add arguments
|
||||
fn add_args<S>(&mut self, args: &[S]) -> &'_ mut Self
|
||||
where
|
||||
S: AsRef<str>,
|
||||
@ -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<Vec<String>, Error>;
|
||||
|
||||
/// Command to run the compiler for a given `Configuration`
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn command_for_configuration(
|
||||
&mut self,
|
||||
configuration: Configuration,
|
||||
) -> Result<Vec<String>, Error>;
|
||||
|
||||
/// Get the list of requested `Configuration`s
|
||||
fn configurations(&self) -> Result<Vec<Configuration>, Error>;
|
||||
|
||||
/// Whether to ignore the configured `Configurations`. Useful for e.g. nested calls to
|
||||
/// `libafl_cc` from `libafl_libtool`.
|
||||
fn ignore_configurations(&self) -> Result<bool, Error>;
|
||||
|
||||
/// Get if in linking mode
|
||||
fn is_linking(&self) -> bool;
|
||||
|
||||
/// Filter out argumets
|
||||
fn filter(&self, _args: &mut Vec<String>) {}
|
||||
|
||||
/// 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<Option<i32>, 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<S>(&mut self, arg: S) -> &'_ mut Self
|
||||
where
|
||||
S: AsRef<str>;
|
||||
|
||||
/// Add a compiler argument only when linking
|
||||
fn add_link_arg<S>(&mut self, arg: S) -> &'_ mut Self
|
||||
where
|
||||
S: AsRef<str>;
|
||||
|
||||
/// Add compiler arguments only when compiling
|
||||
fn add_cc_args<S>(&mut self, args: &[S]) -> &'_ mut Self
|
||||
where
|
||||
@ -150,42 +331,4 @@ pub trait CompilerWrapper {
|
||||
fn link_staticlib<S>(&mut self, dir: &Path, name: S) -> &'_ mut Self
|
||||
where
|
||||
S: AsRef<str>;
|
||||
|
||||
/// Command to run the compiler
|
||||
fn command(&mut self) -> Result<Vec<String>, Error>;
|
||||
|
||||
/// Get if in linking mode
|
||||
fn is_linking(&self) -> bool;
|
||||
|
||||
/// Filter out argumets
|
||||
fn filter(&self, _args: &mut Vec<String>) {}
|
||||
|
||||
/// 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<Option<i32>, 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())
|
||||
}
|
||||
}
|
||||
|
291
libafl_cc/src/libtool.rs
Normal file
291
libafl_cc/src/libtool.rs
Normal file
@ -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<PathBuf>,
|
||||
configurations: Vec<crate::Configuration>,
|
||||
parse_args_called: bool,
|
||||
base_args: Vec<String>,
|
||||
}
|
||||
|
||||
#[allow(clippy::match_same_arms)] // for the linking = false wip for "shared"
|
||||
impl ToolWrapper for LibtoolWrapper {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn parse_args<S>(&mut self, args: &[S]) -> Result<&'_ mut Self, Error>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let mut new_args: Vec<String> = 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<S>(&mut self, arg: S) -> &'_ mut Self
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
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<Vec<crate::Configuration>, Error> {
|
||||
let configs = self.configurations.clone();
|
||||
Ok(configs)
|
||||
}
|
||||
|
||||
fn ignore_configurations(&self) -> Result<bool, Error> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn command(&mut self) -> Result<Vec<String>, Error> {
|
||||
self.command_for_configuration(crate::Configuration::Default)
|
||||
}
|
||||
|
||||
fn command_for_configuration(
|
||||
&mut self,
|
||||
configuration: crate::Configuration,
|
||||
) -> Result<Vec<String>, 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::<Vec<_>>();
|
||||
|
||||
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<String>) {}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user