The Build Script
To pass arguments to the compilation of a specific dependency, we can use the
cargo rustc
subprogram. This lets us pass arguments to rustc
, the Rust compiler, as
well as to LLVM, which rustc
uses for final code generation and
additional optimization. We'll use std::process::Command
, which is similar to using
subprocess.Popen
in Python or system
in C, although much more flexible.
We'll walk through the cargo rustc
arguments that we'll add in addition to the
SanitizerCoverage
flags.
-p first-target-solution
: Tellrustc
to just build the target with these arguments, rather than pass these arguments to all the code we're compiling.--target-dir target/first-target/
: Tellrustc
to build in a subdirectory of the defaulttarget
directory, to avoid conflicting with the currently running build process' lock on thetarget
directory.link-dead-code
: Don't prune any unused code from the compilation result.lto=no
: Don't do link-time optimization, because it might remove some code or instrumentation.--emit=dep-info,link
: Emit linking and dependency information.opt-level={}
: We just pass the opt level we are compiling the fuzzer at to the target as well. This allows us to use--release
.
use std::{env::var, process::Command}; fn main() { let status = Command::new("cargo") .arg("rustc") .arg("-p") .arg("first-target") .arg("--target-dir") .arg("target/first-target/") .arg("--") .arg("-C") .arg(format!( "opt-level={}", var("OPT_LEVEL").expect("No OPT_LEVEL variable set") )) .arg("-C") .arg("prefer-dynamic") .arg("-C") .arg("passes=sancov-module") .arg("-C") .arg("llvm-args=-sanitizer-coverage-level=3") .arg("-C") .arg("llvm-args=-sanitizer-coverage-inline-8bit-counters") .arg("-C") .arg("llvm-args=-sanitizer-coverage-prune-blocks=0") .arg("-C") .arg("link-dead-code") .arg("-C") .arg("lto=no") .arg("--emit=dep-info,link") .status() .expect("Failed to spawn Cargo"); assert!(status.success(), "Target build command failed");
We'll also use some specially formatted print statements in our build script to instruct
cargo
to link with the library. Note the env!
macro, which grabs the value of an
environment variable at compile time (and is a compile error if the variable isn't
present). The CARGO_MANIFEST_DIR
environment variable is set automatically by cargo
during build script compilation and execution. You can check the full list of these
environment variables
here. You can
also check out the full list of special print messages
here.
#![allow(unused)] fn main() { println!( "cargo:rustc-link-search={}/target/first-target/debug/", env!("CARGO_MANIFEST_DIR") ); println!("cargo:rustc-link-lib=static=first_target"); } }