|
| 1 | +use std::process::{Command, ExitCode}; |
| 2 | + |
| 3 | +mod ops; |
| 4 | + |
| 5 | +fn main() -> std::io::Result<ExitCode> { |
| 6 | + // HACK(eddyb) disable the default of re-running the build script on *any* |
| 7 | + // change to *the entire source tree* (i.e. the default is roughly `./`). |
| 8 | + println!("cargo:rerun-if-changed=build.rs"); |
| 9 | + |
| 10 | + let out_dir = std::path::PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); |
| 11 | + std::fs::write(out_dir.join("generated_fuzz_ops.rs"), ops::generate_rust())?; |
| 12 | + |
| 13 | + // FIXME(eddyb) add a way to disable the C++ build below, or automatically |
| 14 | + // disable it if on an unsupported target (e.g. Windows). |
| 15 | + let cxx = true; |
| 16 | + if !cxx { |
| 17 | + return Ok(ExitCode::SUCCESS); |
| 18 | + } |
| 19 | + |
| 20 | + let mut cxx_exported_symbols = vec![]; |
| 21 | + std::fs::write( |
| 22 | + out_dir.join("cxx_apf_fuzz.cpp"), |
| 23 | + ops::generate_cxx(&mut cxx_exported_symbols), |
| 24 | + )?; |
| 25 | + |
| 26 | + // HACK(eddyb) work around https://github.com/rust-lang/cargo/issues/3676, |
| 27 | + // by removing the env vars that Cargo appears to hardcode. |
| 28 | + const CARGO_HARDCODED_ENV_VARS: &[(&str, &str)] = &[ |
| 29 | + ("SSL_CERT_DIR", "/etc/pki/tls/certs"), |
| 30 | + ("SSL_CERT_FILE", "/etc/pki/tls/certs/ca-bundle.crt"), |
| 31 | + ]; |
| 32 | + for &(var_name, cargo_hardcoded_value) in CARGO_HARDCODED_ENV_VARS { |
| 33 | + if let Ok(value) = std::env::var(var_name) { |
| 34 | + if value == cargo_hardcoded_value { |
| 35 | + std::env::remove_var(var_name); |
| 36 | + } |
| 37 | + } |
| 38 | + } |
| 39 | + |
| 40 | + let sh_script_exit_status = Command::new("sh") |
| 41 | + .args(["-c", SH_SCRIPT]) |
| 42 | + .envs([ |
| 43 | + // FIXME(eddyb) ensure this is kept in sync. |
| 44 | + ( |
| 45 | + "llvm_project_git_hash", |
| 46 | + "f3598e8fca83ccfb11f58ec7957c229e349765e3", |
| 47 | + ), |
| 48 | + ("cxx_apf_fuzz_exports", &cxx_exported_symbols.join(",")), |
| 49 | + ( |
| 50 | + "cxx_apf_fuzz_is_fuzzing", |
| 51 | + if cfg!(fuzzing) { "1" } else { "0" }, |
| 52 | + ), |
| 53 | + ]) |
| 54 | + .status()?; |
| 55 | + Ok(if sh_script_exit_status.success() { |
| 56 | + ExitCode::SUCCESS |
| 57 | + } else { |
| 58 | + ExitCode::FAILURE |
| 59 | + }) |
| 60 | +} |
| 61 | + |
| 62 | +// HACK(eddyb) should avoid shelling out, but for now this will suffice. |
| 63 | +const SH_SCRIPT: &str = r#" |
| 64 | +set -e |
| 65 | +
|
| 66 | +llvm_project_tgz_url="https://codeload.github.com/llvm/llvm-project/tar.gz/$llvm_project_git_hash" |
| 67 | +curl -sS "$llvm_project_tgz_url" | tar -C "$OUT_DIR" -xz |
| 68 | +llvm="$OUT_DIR"/llvm-project-"$llvm_project_git_hash"/llvm |
| 69 | +
|
| 70 | +mkdir -p "$OUT_DIR"/fake-config/llvm/Config |
| 71 | +touch "$OUT_DIR"/fake-config/llvm/Config/{abi-breaking,llvm-config}.h |
| 72 | +
|
| 73 | +# HACK(eddyb) we want standard `assert`s to work, but `NDEBUG` also controls |
| 74 | +# unrelated LLVM facilities that are spread all over the place and it's harder |
| 75 | +# to compile all of them, than do this workaround where we shadow `assert.h`. |
| 76 | +echo -e '#undef NDEBUG\n#include_next <assert.h>\n#define NDEBUG' \ |
| 77 | + > "$OUT_DIR"/fake-config/assert.h |
| 78 | +
|
| 79 | +# HACK(eddyb) bypass `$llvm/include/llvm/Support/DataTypes.h.cmake`. |
| 80 | +mkdir -p "$OUT_DIR"/fake-config/llvm/Support |
| 81 | +echo -e '#include <'{math,inttypes,stdint,sys/types}'.h>\n' \ |
| 82 | + > "$OUT_DIR"/fake-config/llvm/Support/DataTypes.h |
| 83 | +
|
| 84 | +# FIXME(eddyb) maybe split `$clang_codegen_flags` into front-end vs back-end. |
| 85 | +clang_codegen_flags="-g -fPIC -fno-exceptions -O3 -march=native" |
| 86 | +
|
| 87 | +# HACK(eddyb) first compile all the source files into one `.bc` file: |
| 88 | +# - "unity build" (w/ `--include`) lets `-o` specify path (no `--out-dir` sadly) |
| 89 | +# - LLVM `.bc` intermediate allows the steps below to reduce dependencies |
| 90 | +echo | clang++ -x c++ - -std=c++17 \ |
| 91 | + $clang_codegen_flags \ |
| 92 | + -I "$llvm"/include \ |
| 93 | + -I "$OUT_DIR"/fake-config \ |
| 94 | + -DNDEBUG \ |
| 95 | + --include="$llvm"/lib/Support/{APInt,APFloat,SmallVector}.cpp \ |
| 96 | + --include="$OUT_DIR"/cxx_apf_fuzz.cpp \ |
| 97 | + -c -emit-llvm -o "$OUT_DIR"/cxx_apf_fuzz.bc |
| 98 | +
|
| 99 | +# HACK(eddyb) use the `internalize` pass (+ O3) to prune everything unexported. |
| 100 | +opt_passes='internalize,default<O3>' |
| 101 | +opt_flags="" |
| 102 | +# FIXME(eddyb) this was just the `internalize` hack, but had to move `sancov` here, to |
| 103 | +# replicate https://github.com/rust-fuzz/afl.rs/blob/8ece4f9/src/bin/cargo-afl.rs#L370-L372 |
| 104 | +# *after* `internalize` & optimizations (to avoid instrumenting dead code). |
| 105 | +if [ "$cxx_apf_fuzz_is_fuzzing" == "1" ]; then |
| 106 | + opt_passes="$opt_passes,sancov-module" |
| 107 | + opt_flags="--sanitizer-coverage-level=3 \ |
| 108 | + --sanitizer-coverage-trace-pc-guard \ |
| 109 | + --sanitizer-coverage-prune-blocks=0" |
| 110 | +fi |
| 111 | +opt \ |
| 112 | + --internalize-public-api-list="$cxx_apf_fuzz_exports" \ |
| 113 | + --passes="$opt_passes" \ |
| 114 | + $opt_flags \ |
| 115 | + "$OUT_DIR"/cxx_apf_fuzz.bc \ |
| 116 | + -o "$OUT_DIR"/cxx_apf_fuzz.opt.bc |
| 117 | +
|
| 118 | +# HACK(eddyb) let Clang do the rest of the work, from the pruned `.bc`. |
| 119 | +# FIXME(eddyb) maybe split `$clang_codegen_flags` into front-end vs back-end. |
| 120 | +clang++ $clang_codegen_flags \ |
| 121 | + "$OUT_DIR"/cxx_apf_fuzz.opt.bc \ |
| 122 | + -c -o "$OUT_DIR"/cxx_apf_fuzz.o |
| 123 | +
|
| 124 | +llvm-ar rc "$OUT_DIR"/libcxx_apf_fuzz.a "$OUT_DIR"/cxx_apf_fuzz.o |
| 125 | +
|
| 126 | +echo cargo:rustc-link-search=native="$OUT_DIR" |
| 127 | +echo cargo:rustc-link-lib=cxx_apf_fuzz |
| 128 | +echo cargo:rustc-link-lib=stdc++ |
| 129 | +"#; |
0 commit comments