
* Move Clippy to extra CI steps * clippy? * Last try with nightly * no clue why but hey * cleanup * more * revert * check ubuntu works * f * debug * clippy_ci * remove tmate * regex --------- Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
266 lines
8.1 KiB
Rust
266 lines
8.1 KiB
Rust
use std::{
|
|
cell::RefCell,
|
|
env,
|
|
fs::File,
|
|
io::Write,
|
|
path::{Path, PathBuf},
|
|
process::exit,
|
|
};
|
|
|
|
use regex::{Regex, RegexBuilder};
|
|
use symcc_libafl::clone_symcc;
|
|
|
|
const SYMCC_RUNTIME_FUNCTION_NAME_PREFIX: &str = "_cpp_";
|
|
|
|
thread_local! {
|
|
static FUNCTION_NAME_REGEX: RefCell<Regex> = RefCell::new(Regex::new(r"pub fn (\w+)").unwrap());
|
|
static EXPORTED_FUNCTION_REGEX: RefCell<Regex> = RefCell::new(RegexBuilder::new(r"(pub fn \w+\s*\([^\)]*\)[^;]*);")
|
|
.multi_line(true)
|
|
.build()
|
|
.unwrap());
|
|
}
|
|
|
|
fn main() {
|
|
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
|
let symcc_src_path = checkout_symcc(&out_path);
|
|
|
|
write_rust_runtime_macro_file(&out_path, &symcc_src_path);
|
|
|
|
if env::var("TARGET").unwrap().contains("linux") {
|
|
let cpp_bindings = bindgen::Builder::default()
|
|
.clang_arg(format!(
|
|
"-I{}",
|
|
symcc_src_path.join("runtime").to_str().unwrap()
|
|
))
|
|
.clang_arg(format!(
|
|
"-I{}",
|
|
symcc_src_path
|
|
.join("runtime")
|
|
.join("rust_backend")
|
|
.to_str()
|
|
.unwrap()
|
|
))
|
|
.clang_args(["-x", "c++", "-std=c++17"].iter())
|
|
.header(
|
|
symcc_src_path
|
|
.join("runtime")
|
|
.join("rust_backend")
|
|
.join("Runtime.h")
|
|
.to_str()
|
|
.unwrap(),
|
|
)
|
|
.header(
|
|
symcc_src_path
|
|
.join("runtime")
|
|
.join("LibcWrappers.cpp")
|
|
.to_str()
|
|
.unwrap(),
|
|
)
|
|
.allowlist_type("SymExpr")
|
|
.allowlist_function("(_sym_.*)|(.*_symbolized)")
|
|
.opaque_type("_.*")
|
|
.size_t_is_usize(true)
|
|
.generate()
|
|
.expect("Unable to generate bindings");
|
|
|
|
write_symcc_runtime_bindings_file(&out_path, &cpp_bindings);
|
|
write_cpp_function_export_macro(&out_path, &cpp_bindings);
|
|
|
|
if std::env::var("CARGO_FEATURE_NO_CPP_RUNTIME").is_err()
|
|
&& std::env::var("DOCS_RS").is_err()
|
|
{
|
|
let rename_header_path = out_path.join("rename.h");
|
|
write_symcc_rename_header(&rename_header_path, &cpp_bindings);
|
|
build_and_link_symcc_runtime(&symcc_src_path, &rename_header_path);
|
|
}
|
|
} else {
|
|
println!("cargo:warning=Building SymCC is only supported on Linux");
|
|
}
|
|
}
|
|
|
|
fn write_cpp_function_export_macro(out_path: &Path, cpp_bindings: &bindgen::Bindings) {
|
|
let mut macro_file = File::create(out_path.join("cpp_exports_macro.rs")).unwrap();
|
|
writeln!(
|
|
macro_file,
|
|
"#[doc(hidden)]
|
|
#[macro_export]
|
|
macro_rules! export_cpp_runtime_functions {{
|
|
() => {{",
|
|
)
|
|
.unwrap();
|
|
EXPORTED_FUNCTION_REGEX.with(|x| {
|
|
x.borrow()
|
|
.captures_iter(&cpp_bindings.to_string())
|
|
.for_each(|captures| {
|
|
writeln!(
|
|
macro_file,
|
|
" symcc_runtime::export_c_symbol!({});",
|
|
&captures[1]
|
|
)
|
|
.unwrap();
|
|
});
|
|
});
|
|
writeln!(
|
|
macro_file,
|
|
" }};
|
|
}}",
|
|
)
|
|
.unwrap();
|
|
}
|
|
|
|
fn checkout_symcc(out_path: &Path) -> PathBuf {
|
|
if std::env::var("DOCS_RS").is_ok() {
|
|
"symcc".into()
|
|
} else {
|
|
let repo_dir = out_path.join("libafl_symcc_src");
|
|
if !repo_dir.exists() {
|
|
clone_symcc(&repo_dir);
|
|
}
|
|
repo_dir
|
|
}
|
|
}
|
|
|
|
fn write_rust_runtime_macro_file(out_path: &Path, symcc_src_path: &Path) {
|
|
let rust_bindings = bindgen::Builder::default()
|
|
.clang_arg(format!(
|
|
"-I{}",
|
|
symcc_src_path.join("runtime").to_str().unwrap()
|
|
))
|
|
.clang_arg(format!(
|
|
"-I{}",
|
|
symcc_src_path
|
|
.join("runtime")
|
|
.join("rust_backend")
|
|
.to_str()
|
|
.unwrap()
|
|
))
|
|
.clang_args(["-x", "c++", "-std=c++17"].iter())
|
|
.header(
|
|
symcc_src_path
|
|
.join("runtime")
|
|
.join("rust_backend")
|
|
.join("RustRuntime.h")
|
|
.to_str()
|
|
.unwrap(),
|
|
)
|
|
.allowlist_type("RSymExpr")
|
|
.allowlist_function("_rsym_.*")
|
|
.opaque_type("_.*")
|
|
.size_t_is_usize(true)
|
|
.generate()
|
|
.expect("Unable to generate bindings");
|
|
let mut rust_runtime_macro = File::create(out_path.join("rust_exports_macro.rs")).unwrap();
|
|
writeln!(
|
|
rust_runtime_macro,
|
|
"#[doc(hidden)]
|
|
#[macro_export]
|
|
macro_rules! invoke_macro_with_rust_runtime_exports {{
|
|
($macro:path; $($extra_ident:path),*) => {{",
|
|
)
|
|
.unwrap();
|
|
EXPORTED_FUNCTION_REGEX.with(|x| {
|
|
x.borrow()
|
|
.captures_iter(&rust_bindings.to_string())
|
|
.for_each(|captures| {
|
|
writeln!(
|
|
rust_runtime_macro,
|
|
" $macro!({},{}; $($extra_ident),*);",
|
|
&captures[1].replace("_rsym_", ""),
|
|
&FUNCTION_NAME_REGEX
|
|
.with(|x| x.borrow().captures(&captures[1]))
|
|
.unwrap()[1]
|
|
)
|
|
.unwrap();
|
|
});
|
|
});
|
|
writeln!(
|
|
rust_runtime_macro,
|
|
" }};
|
|
}}",
|
|
)
|
|
.unwrap();
|
|
}
|
|
|
|
fn write_symcc_runtime_bindings_file(out_path: &Path, cpp_bindings: &bindgen::Bindings) {
|
|
let mut bindings_file = File::create(out_path.join("bindings.rs")).unwrap();
|
|
cpp_bindings.to_string().lines().for_each(|l| {
|
|
if let Some(captures) = FUNCTION_NAME_REGEX.with(|x| x.borrow().captures(l)) {
|
|
let function_name = &captures[1];
|
|
writeln!(
|
|
bindings_file,
|
|
"#[link_name=\"{SYMCC_RUNTIME_FUNCTION_NAME_PREFIX}{function_name}\"]"
|
|
)
|
|
.unwrap();
|
|
}
|
|
writeln!(bindings_file, "{l}").unwrap();
|
|
});
|
|
}
|
|
|
|
fn write_symcc_rename_header(rename_header_path: &Path, cpp_bindings: &bindgen::Bindings) {
|
|
let mut rename_header_file = File::create(rename_header_path).unwrap();
|
|
writeln!(
|
|
rename_header_file,
|
|
"#ifndef PREFIX_EXPORTS_H
|
|
#define PREFIX_EXPORTS_H",
|
|
)
|
|
.unwrap();
|
|
|
|
cpp_bindings
|
|
.to_string()
|
|
.lines()
|
|
.filter_map(|l| FUNCTION_NAME_REGEX.with(|x| x.borrow().captures(l)))
|
|
.map(|captures| captures[1].to_string())
|
|
.for_each(|val| {
|
|
writeln!(
|
|
rename_header_file,
|
|
"#define {} {SYMCC_RUNTIME_FUNCTION_NAME_PREFIX}{}",
|
|
&val, &val
|
|
)
|
|
.unwrap();
|
|
});
|
|
|
|
writeln!(rename_header_file, "#endif").unwrap();
|
|
}
|
|
|
|
fn build_and_link_symcc_runtime(symcc_src_path: &Path, rename_header_path: &Path) {
|
|
build_dep_check(&["cmake"]);
|
|
let cpp_lib = cmake::Config::new(symcc_src_path.join("runtime"))
|
|
.define("RUST_BACKEND", "ON")
|
|
// 2022: Deprecations break -Werror for our symcc build...
|
|
// We want to build it anyway!
|
|
.cxxflag("-Wno-error=deprecated-declarations")
|
|
.cxxflag(format!(
|
|
"-include \"{}\"",
|
|
rename_header_path.to_str().unwrap()
|
|
))
|
|
.build()
|
|
.join("lib");
|
|
link_with_cpp_stdlib();
|
|
println!("cargo:rustc-link-search=native={}", cpp_lib.display());
|
|
println!("cargo:rustc-link-lib=static=SymRuntime");
|
|
}
|
|
|
|
fn link_with_cpp_stdlib() {
|
|
let target = env::var("TARGET").unwrap();
|
|
if target.contains("apple") {
|
|
println!("cargo:rustc-link-lib=dylib=c++");
|
|
} else if target.contains("linux") {
|
|
println!("cargo:rustc-link-lib=dylib=stdc++");
|
|
} else {
|
|
unimplemented!();
|
|
}
|
|
}
|
|
|
|
fn build_dep_check(tools: &[&str]) {
|
|
for tool in tools {
|
|
println!("Checking for build tool {tool}...");
|
|
|
|
if let Ok(path) = which::which(tool) {
|
|
println!("Found build tool {}", path.to_str().unwrap());
|
|
} else {
|
|
println!("ERROR: missing build tool {tool}");
|
|
exit(1);
|
|
};
|
|
}
|
|
}
|