libafl_libfuzzer: macOS build support (#1711)

* libafl_libfuzzer: macOS support

* make clippy happy

---------

Co-authored-by: Dominik Maier <domenukk@gmail.com>
This commit is contained in:
Sameer Puri 2024-01-30 17:41:08 -08:00 committed by GitHub
parent a4f753b0f0
commit 13dd1cc4ec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,7 +1,7 @@
use std::{ use std::{
fs::File, fs::File,
io::{BufRead, BufReader, BufWriter, Write}, io::{BufRead, BufReader, BufWriter, Write},
path::PathBuf, path::{Path, PathBuf},
process::{Command, Stdio}, process::{Command, Stdio},
}; };
@ -17,9 +17,9 @@ fn main() {
return; // skip when clippy or docs is running return; // skip when clippy or docs is running
} }
if cfg!(target_os = "windows") { if cfg!(not(any(target_os = "linux", target_os = "macos"))) {
println!( println!(
"cargo:warning=The libafl_libfuzzer runtime may only be built for linux; failing fast." "cargo:warning=The libafl_libfuzzer runtime may only be built for linux or macos; failing fast."
); );
return; return;
} }
@ -29,12 +29,12 @@ fn main() {
println!("cargo:rerun-if-changed=libafl_libfuzzer_runtime/build.rs"); println!("cargo:rerun-if-changed=libafl_libfuzzer_runtime/build.rs");
let custom_lib_dir = let custom_lib_dir =
PathBuf::from(std::env::var_os("OUT_DIR").unwrap()).join("libafl_libfuzzer"); AsRef::<Path>::as_ref(&std::env::var_os("OUT_DIR").unwrap()).join("libafl_libfuzzer");
std::fs::create_dir_all(&custom_lib_dir) std::fs::create_dir_all(&custom_lib_dir)
.expect("Couldn't create the output directory for the fuzzer runtime build"); .expect("Couldn't create the output directory for the fuzzer runtime build");
let mut lib_src = PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap()); let lib_src: PathBuf = AsRef::<Path>::as_ref(&std::env::var_os("CARGO_MANIFEST_DIR").unwrap())
lib_src.push("libafl_libfuzzer_runtime"); .join("libafl_libfuzzer_runtime");
let mut command = Command::new(std::env::var_os("CARGO").unwrap()); let mut command = Command::new(std::env::var_os("CARGO").unwrap());
command command
@ -77,17 +77,15 @@ fn main() {
.arg(std::env::var_os("TARGET").unwrap()); .arg(std::env::var_os("TARGET").unwrap());
assert!( assert!(
!command.status().map(|s| !s.success()).unwrap_or(true), command.status().map_or(false, |s| s.success()),
"Couldn't build runtime crate! Did you remember to use nightly? (`rustup default nightly` to install) Or, did you remember to install ucd-generate? (`cargo install ucd-generate` to install)" "Couldn't build runtime crate! Did you remember to use nightly? (`rustup default nightly` to install) Or, did you remember to install ucd-generate? (`cargo install ucd-generate` to install)"
); );
let mut lib_path = custom_lib_dir.join(std::env::var_os("TARGET").unwrap()); let mut archive_path = custom_lib_dir.join(std::env::var_os("TARGET").unwrap());
lib_path.push("release"); archive_path.push("release");
if cfg!(target_family = "unix") { if cfg!(unix) {
use std::path::Path; archive_path.push("libafl_libfuzzer_runtime.a");
lib_path.push("libafl_libfuzzer_runtime.a");
let target_libdir = Command::new("rustc") let target_libdir = Command::new("rustc")
.args(["--print", "target-libdir"]) .args(["--print", "target-libdir"])
.output() .output()
@ -95,50 +93,52 @@ fn main() {
let target_libdir = String::from_utf8(target_libdir.stdout).unwrap(); let target_libdir = String::from_utf8(target_libdir.stdout).unwrap();
let target_libdir = Path::new(target_libdir.trim()); let target_libdir = Path::new(target_libdir.trim());
let rust_lld = target_libdir.join("../bin/rust-lld");
let rust_ar = target_libdir.join("../bin/llvm-ar"); // NOTE: depends on llvm-tools
let rust_objcopy = target_libdir.join("../bin/llvm-objcopy"); // NOTE: depends on llvm-tools let rust_objcopy = target_libdir.join("../bin/llvm-objcopy"); // NOTE: depends on llvm-tools
let nm = "nm"; // NOTE: we use system nm here because llvm-nm doesn't respect the encoding? let nm = if cfg!(target_os = "macos") {
// NOTE: depends on llvm-tools
target_libdir.join("../bin/llvm-nm")
} else {
// NOTE: we use system nm on linux because llvm-nm doesn't respect the encoding?
PathBuf::from("nm")
};
let redefined_archive_path = custom_lib_dir.join("libFuzzer.a");
let redefined_symbols = custom_lib_dir.join("redefs.txt"); let redefined_symbols = custom_lib_dir.join("redefs.txt");
let objfile_orig = custom_lib_dir.join("libFuzzer.o"); let mut nm_child = Command::new(nm)
let objfile_dest = custom_lib_dir.join("libFuzzer-mimalloc.o"); .arg(&archive_path)
let mut command = Command::new(rust_lld);
command
.args(["-flavor", "gnu"])
.arg("-r")
.arg("--whole-archive")
.arg(lib_path)
.args(["-o", objfile_orig.to_str().expect("Invalid path characters present in your current directory prevent us from linking to the runtime")]);
assert!(
!command.status().map(|s| !s.success()).unwrap_or(true),
"Couldn't link runtime crate! Do you have the llvm-tools component installed? (`rustup component add llvm-tools-preview` to install)"
);
let mut child = Command::new(nm)
.arg(&objfile_orig)
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn() .spawn()
.unwrap(); .unwrap();
let mut redefinitions_file = BufWriter::new(File::create(&redefined_symbols).unwrap()); let mut redefinitions_file = BufWriter::new(File::create(&redefined_symbols).unwrap());
let replacement = format!("_ZN{NAMESPACE_LEN}{NAMESPACE}"); let zn_prefix = if cfg!(target_os = "macos") {
// macOS symbols have an extra `_`
"__ZN"
} else {
"_ZN"
};
let replacement = format!("{zn_prefix}{NAMESPACE_LEN}{NAMESPACE}");
// redefine all the rust-mangled symbols we can // redefine all the rust-mangled symbols we can
// TODO this will break when v0 mangling is stabilised // TODO this will break when v0 mangling is stabilised
for line in BufReader::new(child.stdout.take().unwrap()).lines() { for line in BufReader::new(nm_child.stdout.take().unwrap()).lines() {
let line = line.unwrap(); let line = line.unwrap();
// Skip headers
if line.ends_with(':') || line.is_empty() {
continue;
}
let (_, symbol) = line.rsplit_once(' ').unwrap(); let (_, symbol) = line.rsplit_once(' ').unwrap();
if symbol.starts_with("_ZN") {
if symbol.starts_with(zn_prefix) {
writeln!( writeln!(
redefinitions_file, redefinitions_file,
"{} {}", "{} {}",
symbol, symbol,
symbol.replacen("_ZN", &replacement, 1) symbol.replacen(zn_prefix, &replacement, 1)
) )
.unwrap(); .unwrap();
} }
@ -147,11 +147,11 @@ fn main() {
drop(redefinitions_file); drop(redefinitions_file);
assert!( assert!(
!child.wait().map(|s| !s.success()).unwrap_or(true), nm_child.wait().map_or(false, |s| s.success()),
"Couldn't link runtime crate! Do you have the llvm-tools component installed? (`rustup component add llvm-tools-preview` to install)" "Couldn't link runtime crate! Do you have the llvm-tools component installed? (`rustup component add llvm-tools-preview` to install)"
); );
let mut command = Command::new(rust_objcopy); let mut objcopy_command = Command::new(rust_objcopy);
for symbol in [ for symbol in [
"__rust_drop_panic", "__rust_drop_panic",
@ -173,39 +173,34 @@ fn main() {
"__rust_no_alloc_shim_is_unstable", "__rust_no_alloc_shim_is_unstable",
"__rust_alloc_error_handler_should_panic", "__rust_alloc_error_handler_should_panic",
] { ] {
command let mut symbol = symbol.to_string();
// macOS symbols have an extra `_`
if cfg!(target_os = "macos") {
symbol.insert(0, '_');
}
objcopy_command
.arg("--redefine-sym") .arg("--redefine-sym")
.arg(format!("{symbol}={symbol}_libafl_libfuzzer_runtime")); .arg(format!("{symbol}={symbol}_libafl_libfuzzer_runtime"));
} }
command objcopy_command
.arg("--redefine-syms") .arg("--redefine-syms")
.arg(redefined_symbols) .arg(redefined_symbols)
.args([&objfile_orig, &objfile_dest]); .args([&archive_path, &redefined_archive_path]);
assert!( assert!(
!command.status().map(|s| !s.success()).unwrap_or(true), objcopy_command.status().map_or(false, |s| s.success()),
"Couldn't rename allocators in the runtime crate! Do you have the llvm-tools component installed? (`rustup component add llvm-tools-preview` to install)" "Couldn't rename allocators in the runtime crate! Do you have the llvm-tools component installed? (`rustup component add llvm-tools-preview` to install)"
); );
let mut command = Command::new(rust_ar);
command
.arg("cr")
.arg(custom_lib_dir.join("libFuzzer.a"))
.arg(objfile_dest);
assert!(
!command.status().map(|s| !s.success()).unwrap_or(true),
"Couldn't create runtime archive!"
);
#[cfg(feature = "embed-runtime")] #[cfg(feature = "embed-runtime")]
{ {
// NOTE: lib, .a are added always on unix-like systems as described in: // NOTE: lib, .a are added always on unix-like systems as described in:
// https://gist.github.com/novafacing/1389cbb2f0a362d7eb103e67b4468e2b // https://gist.github.com/novafacing/1389cbb2f0a362d7eb103e67b4468e2b
println!( println!(
"cargo:rustc-env=LIBAFL_LIBFUZZER_RUNTIME_PATH={}", "cargo:rustc-env=LIBAFL_LIBFUZZER_RUNTIME_PATH={}",
custom_lib_dir.join("libFuzzer.a").display() redefined_archive_path.display()
); );
} }
@ -214,13 +209,11 @@ fn main() {
custom_lib_dir.to_str().unwrap() custom_lib_dir.to_str().unwrap()
); );
println!("cargo:rustc-link-lib=static=Fuzzer"); println!("cargo:rustc-link-lib=static=Fuzzer");
} else {
println!(
"cargo:rustc-link-search=native={}",
lib_path.to_str().unwrap()
);
println!("cargo:rustc-link-lib=static=afl_fuzzer_runtime");
}
if cfg!(target_os = "macos") {
println!("cargo:rustc-link-lib=c++");
} else {
println!("cargo:rustc-link-lib=stdc++"); println!("cargo:rustc-link-lib=stdc++");
} }
}
}