Rework of libafl_qemu configuration (#2054)

* LibAFL QEMU can now be dynamically linked
* LibAFL QEMU reconfiguration happens less frequently (now using a signature check)
* Possibility to have custom rpath in QEMU
This commit is contained in:
Romain Malmain 2024-04-16 11:35:15 +02:00 committed by GitHub
parent da6118e61e
commit bc3ef5952b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 369 additions and 194 deletions

View File

@ -12,6 +12,8 @@ classic = [] # The classic way to interact with LibAFL QEMU, with direct calls t
breakpoint = [] # Uses the command system, with breakpoints
sync_exit = [] # Uses the command system, with sync exit.
shared = ["libafl_qemu/shared"]
[profile.release]
incremental = true
debug = true
@ -24,3 +26,6 @@ libafl_bolts = { path = "../../libafl_bolts/" }
libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] }
libafl_qemu_sys = { path = "../../libafl_qemu/libafl_qemu_sys", features = ["arm", "systemmode"] }
env_logger = "*"
[build-dependencies]
libafl_qemu_build = { path = "../../libafl_qemu/libafl_qemu_build" }

View File

@ -45,7 +45,6 @@ args = [
"--no-default-features",
"--features", "std,${FEATURE}",
"--target-dir", "${TARGET_DIR}",
"-vv",
]
dependencies = ["image"]

View File

@ -1,3 +1,5 @@
use libafl_qemu_build::build_libafl_qemu;
#[macro_export]
macro_rules! assert_unique_feature {
() => {};
@ -12,4 +14,6 @@ macro_rules! assert_unique_feature {
fn main() {
assert_unique_feature!("classic", "breakpoint", "sync_exit");
build_libafl_qemu();
}

View File

@ -13,7 +13,7 @@ use libafl::{
inputs::BytesInput,
monitors::MultiMonitor,
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
observers::{HitcountsMapObserver, TimeObserver, TrackingHinted, VariableMapObserver},
observers::{CanTrack, HitcountsMapObserver, TimeObserver, VariableMapObserver},
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
stages::{CalibrationStage, StdMutationalStage},
state::{HasCorpus, StdState},

View File

@ -13,7 +13,7 @@ use libafl::{
inputs::{BytesInput, HasTargetBytes},
monitors::MultiMonitor,
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
observers::{HitcountsMapObserver, TimeObserver, TrackingHinted, VariableMapObserver},
observers::{CanTrack, HitcountsMapObserver, TimeObserver, VariableMapObserver},
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
stages::StdMutationalStage,
state::{HasCorpus, StdState},

View File

@ -13,7 +13,7 @@ use libafl::{
inputs::BytesInput,
monitors::MultiMonitor,
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
observers::{HitcountsMapObserver, TimeObserver, TrackingHinted, VariableMapObserver},
observers::{CanTrack, HitcountsMapObserver, TimeObserver, VariableMapObserver},
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
stages::{CalibrationStage, StdMutationalStage},
state::{HasCorpus, StdState},

View File

@ -58,8 +58,8 @@ serdeany_autoreg = ["libafl_bolts/serdeany_autoreg"]
slirp = [ "systemmode", "libafl_qemu_sys/slirp" ] # build qemu with host libslirp (for user networking)
# disabled atm, enabled when fixed with dynamic list
# shared = [ "libafl_qemu_sys/shared" ]
# Requires the binary's build.rs to call `build_libafl_qemu`
shared = [ "libafl_qemu_sys/shared" ]
[dependencies]
libafl = { path = "../libafl", version = "0.12.0", default-features = false, features = ["std", "derive", "regex"] }

View File

@ -6,9 +6,11 @@ use std::{
use which::which;
use crate::cargo_add_rpath;
const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
const QEMU_REVISION: &str = "50b0c90e0aab07643ccb58cfbbef742bcfb8b7d1";
const QEMU_REVISION: &str = "c9519ee8b6cb1ba54b7df1001f7f39f07218d514";
#[allow(clippy::module_name_repetitions)]
pub struct BuildResult {
@ -22,6 +24,220 @@ fn build_dep_check(tools: &[&str]) {
}
}
fn get_config_signature(config_cmd: &Command) -> String {
let mut signature_string = String::new();
// Command env
let config_env: String = config_cmd
.get_envs()
.map(|(key, value)| {
format!(
"\t{}={}",
key.to_str().expect("Couldn't convert OsStr to str"),
if let Some(v) = value {
v.to_str().expect("Could't convert OsStr to str")
} else {
""
}
)
})
.reduce(|acc, elt| format!("{acc}\n{elt}"))
.into_iter()
.collect();
signature_string += format!("Environment:\n{config_env}").as_str();
// Command args
let config_args: String = config_cmd
.get_args()
.map(|arg| format!("\t{}", arg.to_str().expect("Couldn't convert OsStr to str")))
.reduce(|acc, arg| format!("{acc}\n{arg}"))
.into_iter()
.collect();
signature_string += format!("\n\nArguments:\n{config_args}").as_str();
signature_string
}
#[allow(clippy::too_many_lines)]
fn configure_qemu(
cc_compiler: &cc::Tool,
cpp_compiler: &cc::Tool,
qemu_path: &PathBuf,
build_dir: &Path,
is_usermode: bool,
cpu_target: &String,
target_suffix: &String,
) -> Command {
let mut cmd = Command::new("./configure");
// Set common options for usermode and systemmode
cmd.current_dir(qemu_path)
.env("__LIBAFL_QEMU_CONFIGURE", "")
.env("__LIBAFL_QEMU_BUILD_OUT", build_dir.join("linkinfo.json"))
.env("__LIBAFL_QEMU_BUILD_CC", cc_compiler.path())
.env("__LIBAFL_QEMU_BUILD_CXX", cpp_compiler.path())
.arg(&format!(
"--cc={}",
qemu_path.join("linker_interceptor.py").display()
))
.arg(&format!(
"--cxx={}",
qemu_path.join("linker_interceptor++.py").display()
))
.arg("--as-shared-lib")
.arg(&format!("--target-list={cpu_target}-{target_suffix}"))
.arg("--disable-bsd-user")
.arg("--disable-capstone");
if cfg!(debug_assertions) {
// cmd.arg("--enable-debug");
// .arg("--enable-debug-tcg");
}
if is_usermode {
// Usermode options
cmd.args(["--disable-fdt", "--disable-system"]);
} else {
// Systemmode options
cmd.arg(if cfg!(feature = "slirp") {
"--enable-slirp"
} else {
"--disable-slirp"
})
.arg("--enable-fdt=internal")
.arg("--audio-drv-list=")
.arg("--disable-af-xdp")
.arg("--disable-alsa")
.arg("--disable-attr")
.arg("--disable-auth-pam")
.arg("--disable-dbus-display")
.arg("--disable-bochs")
.arg("--disable-bpf")
.arg("--disable-brlapi")
.arg("--disable-bzip2")
.arg("--disable-cap-ng")
.arg("--disable-canokey")
.arg("--disable-cloop")
.arg("--disable-cocoa")
.arg("--disable-coreaudio")
.arg("--disable-curl")
.arg("--disable-curses")
.arg("--disable-dmg")
.arg("--disable-docs")
.arg("--disable-dsound")
.arg("--disable-fuse")
.arg("--disable-fuse-lseek")
.arg("--disable-gcrypt")
.arg("--disable-gettext")
.arg("--disable-gio")
.arg("--disable-glusterfs")
.arg("--disable-gnutls")
.arg("--disable-gtk")
.arg("--disable-guest-agent")
.arg("--disable-guest-agent-msi")
.arg("--disable-hvf")
.arg("--disable-iconv")
.arg("--disable-jack")
.arg("--disable-keyring")
.arg("--disable-kvm")
.arg("--disable-libdaxctl")
.arg("--disable-libiscsi")
.arg("--disable-libnfs")
.arg("--disable-libpmem")
.arg("--disable-libssh")
.arg("--disable-libudev")
.arg("--disable-libusb")
.arg("--disable-linux-aio")
.arg("--disable-linux-io-uring")
.arg("--disable-linux-user")
.arg("--disable-live-block-migration")
.arg("--disable-lzfse")
.arg("--disable-lzo")
.arg("--disable-l2tpv3")
.arg("--disable-malloc-trim")
.arg("--disable-mpath")
.arg("--disable-multiprocess")
.arg("--disable-netmap")
.arg("--disable-nettle")
.arg("--disable-numa")
.arg("--disable-nvmm")
.arg("--disable-opengl")
.arg("--disable-oss")
.arg("--disable-pa")
.arg("--disable-parallels")
.arg("--disable-png")
.arg("--disable-pvrdma")
.arg("--disable-qcow1")
.arg("--disable-qed")
.arg("--disable-qga-vss")
.arg("--disable-rbd")
.arg("--disable-rdma")
.arg("--disable-replication")
.arg("--disable-sdl")
.arg("--disable-sdl-image")
.arg("--disable-seccomp")
.arg("--disable-selinux")
.arg("--disable-slirp-smbd")
.arg("--disable-smartcard")
.arg("--disable-snappy")
.arg("--disable-sndio")
.arg("--disable-sparse")
.arg("--disable-spice")
.arg("--disable-spice-protocol")
.arg("--disable-tools")
.arg("--disable-tpm")
.arg("--disable-usb-redir")
.arg("--disable-user")
.arg("--disable-u2f")
.arg("--disable-vde")
.arg("--disable-vdi")
.arg("--disable-vduse-blk-export")
.arg("--disable-vhost-crypto")
.arg("--disable-vhost-kernel")
.arg("--disable-vhost-net")
.arg("--disable-vhost-user-blk-server")
.arg("--disable-vhost-vdpa")
.arg("--disable-virglrenderer")
.arg("--disable-virtfs")
.arg("--disable-vmnet")
.arg("--disable-vnc")
.arg("--disable-vnc-jpeg")
.arg("--disable-vnc-sasl")
.arg("--disable-vte")
.arg("--disable-vvfat")
.arg("--disable-whpx")
.arg("--disable-xen")
.arg("--disable-xen-pci-passthrough")
.arg("--disable-xkbcommon")
.arg("--disable-zstd")
.arg("--disable-tests");
}
cmd
}
fn build_qemu(
cc_compiler: &cc::Tool,
cpp_compiler: &cc::Tool,
build_dir: &PathBuf,
jobs: Option<u32>,
) -> Command {
let mut cmd = Command::new("make");
cmd.current_dir(build_dir)
.env("__LIBAFL_QEMU_CONFIGURE", "")
.env("__LIBAFL_QEMU_BUILD_OUT", build_dir.join("linkinfo.json"))
.env("__LIBAFL_QEMU_BUILD_CC", cc_compiler.path())
.env("__LIBAFL_QEMU_BUILD_CXX", cpp_compiler.path())
.arg("-j");
if let Some(j) = jobs {
cmd.arg(&format!("{j}")).env("V", "1");
}
cmd
}
#[allow(clippy::too_many_lines, clippy::missing_panics_doc)]
#[must_use]
pub fn build(
@ -32,7 +248,7 @@ pub fn build(
) -> BuildResult {
let mut cpu_target = cpu_target.to_string();
// qemu-system-arm supports both big and little endian configurations and so
// therefore the "be" feature should ignored in this configuration. Also
// the "be" feature should be ignored in this configuration. Also
// ignore the feature if we are running in clippy which enables all the
// features at once (disabling the check for mutually exclusive options)
// resulting in cpu_target being set to 'x86_64' above which obviously
@ -49,12 +265,13 @@ pub fn build(
cpu_target += "el";
}
let custom_qemu_dir = env::var_os("CUSTOM_QEMU_DIR").map(|x| x.to_string_lossy().to_string());
let custom_qemu_no_build = env::var("CUSTOM_QEMU_NO_BUILD").is_ok();
let custom_qemu_no_configure = env::var("CUSTOM_QEMU_NO_CONFIGURE").is_ok();
println!("cargo:rerun-if-env-changed=CUSTOM_QEMU_DIR");
println!("cargo:rerun-if-env-changed=CUSTOM_QEMU_NO_BUILD");
println!("cargo:rerun-if-env-changed=CUSTOM_QEMU_NO_CONFIGURE");
let libafl_qemu_dir = env::var_os("LIBAFL_QEMU_DIR").map(|x| x.to_string_lossy().to_string());
let libafl_qemu_force_configure = env::var("LIBAFL_QEMU_FORCE_CONFIGURE").is_ok();
let libafl_qemu_no_build = env::var("LIBAFL_QEMU_NO_BUILD").is_ok();
println!("cargo:rerun-if-env-changed=LIBAFL_QEMU_DIR");
println!("cargo:rerun-if-env-changed=LIBAFL_QEMU_FORCE_CONFIGURE");
println!("cargo:rerun-if-env-changed=LIBAFL_QEMU_NO_BUILD");
let out_dir = env::var_os("OUT_DIR").unwrap();
let out_dir = out_dir.to_string_lossy().to_string();
@ -69,7 +286,7 @@ pub fn build(
let cc_compiler = cc::Build::new().cpp(false).get_compiler();
let cpp_compiler = cc::Build::new().cpp(true).get_compiler();
let qemu_path = if let Some(qemu_dir) = custom_qemu_dir.as_ref() {
let qemu_path = if let Some(qemu_dir) = libafl_qemu_dir.as_ref() {
Path::new(&qemu_dir).to_path_buf()
} else {
let qemu_path = target_dir.join(QEMU_DIRNAME);
@ -122,7 +339,8 @@ pub fn build(
qemu_path
};
let build_dir = qemu_path.join("build");
let qemu_build_dir = qemu_path.join("build");
let config_signature_path = qemu_build_dir.join("libafl_config");
let target_suffix = if is_usermode {
"linux-user".to_string()
@ -132,188 +350,71 @@ pub fn build(
let (output_lib, output_lib_link) = if is_usermode {
(
build_dir.join(format!("libqemu-{cpu_target}.so")),
qemu_build_dir.join(format!("libqemu-{cpu_target}.so")),
format!("qemu-{cpu_target}"),
)
} else {
(
build_dir.join(format!("libqemu-system-{cpu_target}.so")),
qemu_build_dir.join(format!("libqemu-system-{cpu_target}.so")),
format!("qemu-system-{cpu_target}"),
)
};
// println!("cargo:rerun-if-changed={}", output_lib.to_string_lossy());
let libafl_config_old_signature = fs::read_to_string(&config_signature_path);
if !output_lib.is_file() || (custom_qemu_dir.is_some() && !custom_qemu_no_build) {
/*drop(
Command::new("make")
.current_dir(&qemu_path)
.arg("distclean")
.status(),
);*/
let mut cmd = Command::new("./configure");
cmd.current_dir(&qemu_path)
//.arg("--as-static-lib")
.env("__LIBAFL_QEMU_BUILD_OUT", build_dir.join("linkinfo.json"))
.env("__LIBAFL_QEMU_BUILD_CC", cc_compiler.path())
.env("__LIBAFL_QEMU_BUILD_CXX", cpp_compiler.path())
.arg(&format!(
"--cc={}",
qemu_path.join("linker_interceptor.py").display()
))
.arg(&format!(
"--cxx={}",
qemu_path.join("linker_interceptor++.py").display()
))
.arg("--as-shared-lib")
.arg(&format!("--target-list={cpu_target}-{target_suffix}"));
if cfg!(feature = "debug_assertions") {
cmd.arg("--enable-debug");
}
if is_usermode && !custom_qemu_no_configure {
cmd.args([
"--disable-bsd-user",
"--disable-fdt",
"--disable-system",
"--disable-capstone",
]);
} else if !custom_qemu_no_configure {
cmd.arg(if cfg!(feature = "slirp") {
"--enable-slirp"
} else {
"--disable-slirp"
})
.arg("--enable-fdt=internal")
.arg("--audio-drv-list=")
.arg("--disable-af-xdp")
.arg("--disable-alsa")
.arg("--disable-attr")
.arg("--disable-auth-pam")
.arg("--disable-dbus-display")
.arg("--disable-bochs")
.arg("--disable-bpf")
.arg("--disable-brlapi")
.arg("--disable-bsd-user")
.arg("--disable-bzip2")
.arg("--disable-capstone")
.arg("--disable-cap-ng")
.arg("--disable-canokey")
.arg("--disable-cloop")
.arg("--disable-cocoa")
.arg("--disable-coreaudio")
.arg("--disable-curl")
.arg("--disable-curses")
.arg("--disable-dmg")
.arg("--disable-docs")
.arg("--disable-dsound")
.arg("--disable-fuse")
.arg("--disable-fuse-lseek")
.arg("--disable-gcrypt")
.arg("--disable-gettext")
.arg("--disable-gio")
.arg("--disable-glusterfs")
.arg("--disable-gnutls")
.arg("--disable-gtk")
.arg("--disable-guest-agent")
.arg("--disable-guest-agent-msi")
.arg("--disable-hvf")
.arg("--disable-iconv")
.arg("--disable-jack")
.arg("--disable-keyring")
.arg("--disable-kvm")
.arg("--disable-libdaxctl")
.arg("--disable-libiscsi")
.arg("--disable-libnfs")
.arg("--disable-libpmem")
.arg("--disable-libssh")
.arg("--disable-libudev")
.arg("--disable-libusb")
.arg("--disable-linux-aio")
.arg("--disable-linux-io-uring")
.arg("--disable-linux-user")
.arg("--disable-live-block-migration")
.arg("--disable-lzfse")
.arg("--disable-lzo")
.arg("--disable-l2tpv3")
.arg("--disable-malloc-trim")
.arg("--disable-mpath")
.arg("--disable-multiprocess")
.arg("--disable-netmap")
.arg("--disable-nettle")
.arg("--disable-numa")
.arg("--disable-nvmm")
.arg("--disable-opengl")
.arg("--disable-oss")
.arg("--disable-pa")
.arg("--disable-parallels")
.arg("--disable-png")
.arg("--disable-pvrdma")
.arg("--disable-qcow1")
.arg("--disable-qed")
.arg("--disable-qga-vss")
.arg("--disable-rbd")
.arg("--disable-rdma")
.arg("--disable-replication")
.arg("--disable-sdl")
.arg("--disable-sdl-image")
.arg("--disable-seccomp")
.arg("--disable-selinux")
.arg("--disable-slirp-smbd")
.arg("--disable-smartcard")
.arg("--disable-snappy")
.arg("--disable-sndio")
.arg("--disable-sparse")
.arg("--disable-spice")
.arg("--disable-spice-protocol")
.arg("--disable-tools")
.arg("--disable-tpm")
.arg("--disable-usb-redir")
.arg("--disable-user")
.arg("--disable-u2f")
.arg("--disable-vde")
.arg("--disable-vdi")
.arg("--disable-vduse-blk-export")
.arg("--disable-vhost-crypto")
.arg("--disable-vhost-kernel")
.arg("--disable-vhost-net")
.arg("--disable-vhost-user-blk-server")
.arg("--disable-vhost-vdpa")
.arg("--disable-virglrenderer")
.arg("--disable-virtfs")
.arg("--disable-vmnet")
.arg("--disable-vnc")
.arg("--disable-vnc-jpeg")
.arg("--disable-vnc-sasl")
.arg("--disable-vte")
.arg("--disable-vvfat")
.arg("--disable-whpx")
.arg("--disable-xen")
.arg("--disable-xen-pci-passthrough")
.arg("--disable-xkbcommon")
.arg("--disable-zstd")
.arg("--disable-tests");
}
let mut config_cmd = configure_qemu(
&cc_compiler,
&cpp_compiler,
&qemu_path,
&qemu_build_dir,
is_usermode,
&cpu_target,
&target_suffix,
);
let current_config_signature = get_config_signature(&config_cmd);
let must_reconfigure = if libafl_qemu_force_configure {
// If the user asked to reconfigure, do so
true
} else if let Ok(libafl_config_old_signature) = libafl_config_old_signature {
if libafl_config_old_signature == current_config_signature {
// Signature match, do not reconfigure
false
} else {
println!("cargo:warning=QEMU configuration is outdated. Reconfiguring...");
true
}
} else {
// In worst scenario, reconfigure
true
};
if must_reconfigure {
assert!(
cmd.status().expect("Invoking Configure failed").success(),
config_cmd
.status()
.expect("Invoking Configure failed")
.success(),
"Configure didn't finish successfully"
);
let mut cmd = Command::new("make");
cmd.current_dir(&build_dir)
.env("__LIBAFL_QEMU_BUILD_OUT", build_dir.join("linkinfo.json"))
.env("__LIBAFL_QEMU_BUILD_CC", cc_compiler.path())
.env("__LIBAFL_QEMU_BUILD_CXX", cpp_compiler.path())
.arg("-j");
if let Some(j) = jobs {
cmd.arg(&format!("{j}")).env("V", "1");
}
// Config succeeded at this point, (over)write the signature file
fs::write(config_signature_path, current_config_signature)
.expect("Couldn't write config signature file.");
}
// Always build by default, make will detect if it is necessary to rebuild qemu
if !libafl_qemu_no_build {
let mut build_cmd = build_qemu(&cc_compiler, &cpp_compiler, &qemu_build_dir, jobs);
assert!(
cmd.status().expect("Invoking Make Failed").success(),
build_cmd.status().expect("Invoking Make Failed").success(),
"Make didn't finish successfully"
);
}
assert!(output_lib.is_file()); // Sanity check
/*
let mut objects = vec![];
for dir in &[
@ -338,13 +439,12 @@ pub fn build(
*/
if cfg!(feature = "shared") {
println!(
"cargo:rustc-link-search=native={}",
build_dir.to_string_lossy()
);
let qemu_build_dir_str = qemu_build_dir.to_str().expect("Could not convert to str");
println!("cargo:rustc-link-search=native={qemu_build_dir_str}");
println!("cargo:rustc-link-lib=dylib={output_lib_link}");
cargo_add_rpath(qemu_build_dir_str);
} else {
let compile_commands_string = &fs::read_to_string(build_dir.join("linkinfo.json"))
let compile_commands_string = &fs::read_to_string(qemu_build_dir.join("linkinfo.json"))
.expect("Failed to read linkinfo.json");
let linkinfo = json::parse(compile_commands_string).expect("Failed to parse linkinfo.json");
@ -357,16 +457,27 @@ pub fn build(
);
}
assert!(cpp_compiler
.to_command()
.current_dir(&build_dir)
let mut link_command = cpp_compiler.to_command();
link_command
.current_dir(&qemu_build_dir)
.arg("-o")
.arg("libqemu-partially-linked.o")
.arg("-r")
.args(cmd)
.status()
.expect("Partial linked failure")
.success());
.args(cmd);
let link_str = format!("{link_command:?}");
let output = link_command.output().expect("Partial linked failure");
if !output.status.success() {
fs::write(qemu_build_dir.join("link.command"), link_str).expect("Link command failed.");
fs::write(qemu_build_dir.join("link.stdout"), &output.stdout)
.expect("Link stdout failed.");
fs::write(qemu_build_dir.join("link.stderr"), &output.stderr)
.expect("Link stderr failed.");
panic!("Linking failed.");
}
/* // Old manual linking, kept here for reference and debugging
if is_usermode {
@ -456,7 +567,7 @@ pub fn build(
.current_dir(out_dir_path)
.arg("crs")
.arg("libqemu-partially-linked.a")
.arg(build_dir.join("libqemu-partially-linked.o"))
.arg(qemu_build_dir.join("libqemu-partially-linked.o"))
.status()
.expect("Ar creation");
@ -476,6 +587,21 @@ pub fn build(
.expect("linkinfo.json `libs` values must be strings");
println!("cargo:rustc-link-lib={val}");
}
for arg in linkinfo["rpath"].members() {
let val = arg
.as_str()
.expect("linkinfo.json `libs` values must be strings")
.to_string()
.replace(
"$ORIGIN",
qemu_build_dir
.as_os_str()
.to_str()
.expect("Could not convert OsStr to str"),
);
cargo_add_rpath(&val);
}
}
/*
@ -497,7 +623,7 @@ pub fn build(
//}
fs::create_dir_all(target_dir.join("pc-bios")).unwrap();
for path in fs::read_dir(build_dir.join("pc-bios")).unwrap() {
for path in fs::read_dir(qemu_build_dir.join("pc-bios")).unwrap() {
let path = path.unwrap().path();
if path.is_file() {
if let Some(name) = path.file_name() {
@ -510,6 +636,6 @@ pub fn build(
BuildResult {
qemu_path,
build_dir,
build_dir: qemu_build_dir,
}
}

View File

@ -3,6 +3,7 @@ use std::{
env, fs,
path::{Path, PathBuf},
process::Command,
ptr::addr_of_mut,
};
use regex::Regex;
@ -15,6 +16,43 @@ pub use build::build;
const LLVM_VERSION_MAX: i32 = 33;
static mut CARGO_RPATH: Option<Vec<String>> = None;
static CARGO_RPATH_SEPARATOR: &str = "|";
pub fn cargo_add_rpath(rpath: &str) {
unsafe {
if let Some(rpaths) = &mut *addr_of_mut!(CARGO_RPATH) {
rpaths.push(rpath.to_string());
} else {
CARGO_RPATH = Some(vec![rpath.to_string()]);
}
}
}
pub fn cargo_propagate_rpath() {
unsafe {
if let Some(cargo_cmds) = &mut *addr_of_mut!(CARGO_RPATH) {
let rpath = cargo_cmds.join(CARGO_RPATH_SEPARATOR);
println!("cargo:rpath={rpath}");
}
}
}
/// Must be called from final binary crates
pub fn build_libafl_qemu() {
// Add rpath if there are some
if let Some(rpaths) = env::var_os("DEP_QEMU_RPATH") {
let rpaths: Vec<&str> = rpaths
.to_str()
.expect("Cannot convert OsString to str")
.split(CARGO_RPATH_SEPARATOR)
.collect();
for rpath in rpaths {
println!("cargo:rustc-link-arg-bins=-Wl,-rpath,{rpath}");
}
}
}
pub fn build_with_bindings(
cpu_target: &str,
is_big_endian: bool,
@ -42,6 +80,8 @@ pub fn build_with_bindings(
let re = Regex::new("(Option<\\s*)unsafe( extern \"C\" fn\\(data: u64)").unwrap();
let replaced = re.replace_all(&contents, "$1$2");
fs::write(bindings_file, replaced.as_bytes()).expect("Unable to write file");
cargo_propagate_rpath();
}
// For bindgen, the llvm version must be >= of the rust llvm version

View File

@ -10,6 +10,7 @@ license = "MIT OR Apache-2.0"
keywords = ["fuzzing", "qemu", "instrumentation"]
edition = "2021"
categories = ["development-tools::testing", "emulators", "embedded", "os", "no-std"]
links = "qemu"
[package.metadata.docs.rs]
features = ["x86_64", "usermode"]