@@ -33,14 +33,14 @@ fn main() -> io::Result<ExitCode> {
3333
3434 let manifest_dir =
3535 PathBuf :: from ( env:: var_os ( "CARGO_MANIFEST_DIR" ) . expect ( "CARGO_MANIFEST_DIR unset" ) ) ;
36- let fake_include_dir = manifest_dir. join ( "cxx/include" ) ;
3736 let target_dir = env:: var_os ( "CARGO_TARGET_DIR" )
3837 . map ( PathBuf :: from)
3938 . unwrap_or_else ( || manifest_dir. parent ( ) . unwrap ( ) . join ( "target" ) ) ;
40- let llvm_dir = target_dir. join ( format ! ( "llvm-downloads/llvm-project-{llvm_commit_hash}" ) ) ;
41- if !llvm_dir. try_exists ( ) . is_ok_and ( |val| val) {
39+ let llvm_root = target_dir. join ( format ! ( "llvm-downloads/llvm-project-{llvm_commit_hash}" ) ) ;
40+
41+ if !llvm_root. try_exists ( ) . is_ok_and ( |val| val) {
4242 panic ! (
43- "llvm dir `{llvm_dir :?}` does not exist or cannot be reached. \
43+ "llvm dir `{llvm_root :?}` does not exist or cannot be reached. \
4444 Perhaps you need to run etc/download-llvm.sh?"
4545 )
4646 }
@@ -59,74 +59,93 @@ fn main() -> io::Result<ExitCode> {
5959 }
6060 }
6161
62- let sh_script_exit_status = Command :: new ( "bash" )
63- . args ( [ "-c" , SH_SCRIPT ] )
64- . env ( "llvm" , & llvm_dir. join ( "llvm" ) )
65- . env ( "manifest_dir" , & manifest_dir)
66- . envs ( [
67- ( "llvm_project_git_hash" , llvm_commit_hash) ,
68- ( "cxx_apf_fuzz_exports" , & cxx_exported_symbols. join ( "," ) ) ,
69- (
70- "cxx_apf_fuzz_is_fuzzing" ,
71- if cfg ! ( fuzzing) { "1" } else { "0" } ,
72- ) ,
62+ let llvm_dir = llvm_root. join ( "llvm" ) ;
63+ let bc_out = out_dir. join ( "cxx_apf_fuzz.bc" ) ;
64+ let bc_opt_out = out_dir. join ( "cxx_apf_fuzz.opt.bc" ) ;
65+ let obj_out = out_dir. join ( "cxx_apf_fuzz.o" ) ;
66+ let archive = out_dir. join ( "libcxx_apf_fuzz.a" ) ;
67+
68+ // Flags could probably be split between the frontend and backend.
69+ let clang_codegen_flags = [ "-g" , "-fPIC" , "-fno-exceptions" , "-O3" , "-march=native" ] ;
70+
71+ // HACK(eddyb) first compile all the source files into one `.bc` file:
72+ // - "unity build" (w/ `--include`) lets `-o` specify path (no `--out-dir` sadly)
73+ // - LLVM `.bc` intermediate allows the steps below to reduce dependencies
74+ let mut clang = Command :: new ( "clang++" ) ;
75+ clang
76+ . env_clear ( )
77+ . args ( [ "-xc++" , "-" , "-std=c++17" ] )
78+ . args ( clang_codegen_flags)
79+ . arg ( "-I" )
80+ . arg ( llvm_dir. join ( "include" ) )
81+ . arg ( "-I" )
82+ . arg ( llvm_dir. join ( "lib/Support" ) )
83+ . arg ( "-I" )
84+ . arg ( manifest_dir. join ( "cxx/include" ) )
85+ . args ( [
86+ "-DNDEBUG" ,
87+ "-DHAVE_UNISTD_H" ,
88+ "-DLLVM_ON_UNIX" ,
89+ "-DLLVM_ENABLE_THREADS=0" ,
7390 ] )
74- . status ( ) ?;
75- Ok ( if sh_script_exit_status. success ( ) {
76- ExitCode :: SUCCESS
77- } else {
78- ExitCode :: FAILURE
79- } )
80- }
91+ . arg ( "--include" )
92+ . arg ( manifest_dir. join ( "cxx/fuzz_unity_build.cpp" ) )
93+ . arg ( "--include" )
94+ . arg ( out_dir. join ( "cxx_apf_fuzz.cpp" ) )
95+ . args ( [ "-c" , "-emit-llvm" , "-o" ] )
96+ . arg ( & bc_out) ;
97+ println ! ( "+ {clang:?}" ) ;
98+ assert ! ( clang. status( ) ?. success( ) ) ;
99+
100+ // Use the `internalize` pass (+ O3) to prune everything unexported.
101+ let mut opt = Command :: new ( "opt" ) ;
102+ opt. env_clear ( )
103+ . arg ( "--internalize-public-api-list" )
104+ . arg ( cxx_exported_symbols. join ( "," ) )
105+ . arg ( & bc_out)
106+ . arg ( "-o" )
107+ . arg ( & bc_opt_out) ;
108+
109+ // FIXME this was just the `internalize` hack, but had to move `sancov` here, to
110+ // replicate https://github.com/rust-fuzz/afl.rs/blob/8ece4f9/src/bin/cargo-afl.rs#L370-L372
111+ // *after* `internalize` & optimizations (to avoid instrumenting dead code).
112+ let mut passes = "--passes=internalize,default<O3>" . to_string ( ) ;
113+ if cfg ! ( fuzzing) {
114+ opt. args ( [
115+ "--sanitizer-coverage-level=3" ,
116+ "--sanitizer-coverage-trace-pc-guard" ,
117+ "--sanitizer-coverage-prune-blocks=0" ,
118+ ] ) ;
119+ passes. push_str ( ",sancov-module" ) ;
120+ }
81121
82- // HACK(eddyb) should avoid shelling out, but for now this will suffice.
83- const SH_SCRIPT : & str = r#"
84- set -eux
85-
86- # FIXME(eddyb) maybe split `$clang_codegen_flags` into front-end vs back-end.
87- clang_codegen_flags="-g -fPIC -fno-exceptions -O3 -march=native"
88-
89- # HACK(eddyb) first compile all the source files into one `.bc` file:
90- # - "unity build" (w/ `--include`) lets `-o` specify path (no `--out-dir` sadly)
91- # - LLVM `.bc` intermediate allows the steps below to reduce dependencies
92- echo | clang++ -x c++ - -std=c++17 \
93- $clang_codegen_flags \
94- -I "$llvm"/include \
95- -I "$llvm"/lib/Support \
96- -I "$manifest_dir/cxx/include" \
97- -DNDEBUG -DHAVE_UNISTD_H -DLLVM_ON_UNIX -DLLVM_ENABLE_THREADS=0 \
98- --include="$manifest_dir/cxx/fuzz_unity_build.cpp" \
99- --include="$OUT_DIR"/cxx_apf_fuzz.cpp \
100- -c -emit-llvm -o "$OUT_DIR"/cxx_apf_fuzz.bc
101-
102- # HACK(eddyb) use the `internalize` pass (+ O3) to prune everything unexported.
103- opt_passes='internalize,default<O3>'
104- opt_flags=""
105- # FIXME(eddyb) this was just the `internalize` hack, but had to move `sancov` here, to
106- # replicate https://github.com/rust-fuzz/afl.rs/blob/8ece4f9/src/bin/cargo-afl.rs#L370-L372
107- # *after* `internalize` & optimizations (to avoid instrumenting dead code).
108- if [ "$cxx_apf_fuzz_is_fuzzing" == "1" ]; then
109- opt_passes="$opt_passes,sancov-module"
110- opt_flags="--sanitizer-coverage-level=3 \
111- --sanitizer-coverage-trace-pc-guard \
112- --sanitizer-coverage-prune-blocks=0"
113- fi
114- opt \
115- --internalize-public-api-list="$cxx_apf_fuzz_exports" \
116- --passes="$opt_passes" \
117- $opt_flags \
118- "$OUT_DIR"/cxx_apf_fuzz.bc \
119- -o "$OUT_DIR"/cxx_apf_fuzz.opt.bc
120-
121- # HACK(eddyb) let Clang do the rest of the work, from the pruned `.bc`.
122- # FIXME(eddyb) maybe split `$clang_codegen_flags` into front-end vs back-end.
123- clang++ $clang_codegen_flags \
124- "$OUT_DIR"/cxx_apf_fuzz.opt.bc \
125- -c -o "$OUT_DIR"/cxx_apf_fuzz.o
126-
127- llvm-ar rc "$OUT_DIR"/libcxx_apf_fuzz.a "$OUT_DIR"/cxx_apf_fuzz.o
128-
129- echo cargo:rustc-link-search=native="$OUT_DIR"
130- echo cargo:rustc-link-lib=cxx_apf_fuzz
131- echo cargo:rustc-link-lib=stdc++
132- "# ;
122+ opt. arg ( passes) ;
123+ println ! ( "+ {opt:?}" ) ;
124+ assert ! ( opt. status( ) ?. success( ) ) ;
125+
126+ // Let Clang do the rest of the work, from the pruned `.bc`.
127+ let mut clang_final = Command :: new ( "clang++" ) ;
128+ clang_final
129+ . env_clear ( )
130+ . args ( clang_codegen_flags)
131+ . arg ( bc_opt_out)
132+ . args ( [ "-c" , "-o" ] )
133+ . arg ( & obj_out) ;
134+ eprintln ! ( "+ {clang_final:?}" ) ;
135+ assert ! ( clang_final. status( ) ?. success( ) ) ;
136+
137+ // Construct a linkable archive.
138+ let mut ar = Command :: new ( "ar" ) ;
139+ ar. env_clear ( ) . arg ( "rc" ) . arg ( archive) . arg ( & obj_out) ;
140+ println ! ( "+ {ar:?}" ) ;
141+ assert ! ( ar. status( ) ?. success( ) ) ;
142+
143+ println ! (
144+ "cargo:rustc-link-search=native={}" ,
145+ out_dir. to_str( ) . unwrap( )
146+ ) ;
147+ println ! ( "cargo:rustc-link-lib=cxx_apf_fuzz" ) ;
148+ println ! ( "cargo:rustc-link-lib=stdc++" ) ;
149+
150+ Ok ( ExitCode :: SUCCESS )
151+ }
0 commit comments