1 // Copyright 2015-2016 Brian Smith.
2 //
3 // Permission to use, copy, modify, and/or distribute this software for any
4 // purpose with or without fee is hereby granted, provided that the above
5 // copyright notice and this permission notice appear in all copies.
6 //
7 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
8 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
10 // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 
15 //! Build the non-Rust components.
16 
17 // It seems like it would be a good idea to use `log!` for logging, but it
18 // isn't worth having the external dependencies (one for the `log` crate, and
19 // another for the concrete logging implementation). Instead we use `eprintln!`
20 // to log everything to stderr.
21 
22 #![forbid(
23     anonymous_parameters,
24     box_pointers,
25     missing_copy_implementations,
26     missing_debug_implementations,
27     missing_docs,
28     trivial_casts,
29     trivial_numeric_casts,
30     unsafe_code,
31     unstable_features,
32     unused_extern_crates,
33     unused_import_braces,
34     unused_qualifications,
35     unused_results,
36     variant_size_differences,
37     warnings
38 )]
39 
40 // In the `pregenerate_asm_main()` case we don't want to access (Cargo)
41 // environment variables at all, so avoid `use std::env` here.
42 
43 use std::{
44     fs::{self, DirEntry},
45     path::{Path, PathBuf},
46     process::Command,
47     time::SystemTime,
48 };
49 
50 const X86: &str = "x86";
51 const X86_64: &str = "x86_64";
52 const AARCH64: &str = "aarch64";
53 const ARM: &str = "arm";
54 
55 #[rustfmt::skip]
56 const RING_SRCS: &[(&[&str], &str)] = &[
57     (&[], "crypto/fipsmodule/aes/aes_nohw.c"),
58     (&[], "crypto/fipsmodule/bn/montgomery.c"),
59     (&[], "crypto/fipsmodule/bn/montgomery_inv.c"),
60     (&[], "crypto/limbs/limbs.c"),
61     (&[], "crypto/mem.c"),
62     (&[], "crypto/poly1305/poly1305.c"),
63 
64     (&[AARCH64, ARM, X86_64, X86], "crypto/crypto.c"),
65     (&[AARCH64, ARM, X86_64, X86], "crypto/curve25519/curve25519.c"),
66     (&[AARCH64, ARM, X86_64, X86], "crypto/fipsmodule/ec/ecp_nistz.c"),
67     (&[AARCH64, ARM, X86_64, X86], "crypto/fipsmodule/ec/ecp_nistz256.c"),
68     (&[AARCH64, ARM, X86_64, X86], "crypto/fipsmodule/ec/gfp_p256.c"),
69     (&[AARCH64, ARM, X86_64, X86], "crypto/fipsmodule/ec/gfp_p384.c"),
70 
71     (&[X86_64, X86], "crypto/cpu-intel.c"),
72 
73     (&[X86], "crypto/fipsmodule/aes/asm/aesni-x86.pl"),
74     (&[X86], "crypto/fipsmodule/aes/asm/vpaes-x86.pl"),
75     (&[X86], "crypto/fipsmodule/bn/asm/x86-mont.pl"),
76     (&[X86], "crypto/chacha/asm/chacha-x86.pl"),
77     (&[X86], "crypto/fipsmodule/ec/asm/ecp_nistz256-x86.pl"),
78     (&[X86], "crypto/fipsmodule/modes/asm/ghash-x86.pl"),
79 
80     (&[X86_64], "crypto/fipsmodule/aes/asm/aesni-x86_64.pl"),
81     (&[X86_64], "crypto/fipsmodule/aes/asm/vpaes-x86_64.pl"),
82     (&[X86_64], "crypto/fipsmodule/bn/asm/x86_64-mont.pl"),
83     (&[X86_64], "crypto/fipsmodule/bn/asm/x86_64-mont5.pl"),
84     (&[X86_64], "crypto/chacha/asm/chacha-x86_64.pl"),
85     (&[X86_64], "crypto/fipsmodule/ec/asm/p256-x86_64-asm.pl"),
86     (&[X86_64], "crypto/fipsmodule/modes/asm/aesni-gcm-x86_64.pl"),
87     (&[X86_64], "crypto/fipsmodule/modes/asm/ghash-x86_64.pl"),
88     (&[X86_64], "crypto/poly1305/poly1305_vec.c"),
89     (&[X86_64], SHA512_X86_64),
90 
91     (&[AARCH64, ARM], "crypto/fipsmodule/aes/asm/aesv8-armx.pl"),
92     (&[AARCH64, ARM], "crypto/fipsmodule/modes/asm/ghashv8-armx.pl"),
93 
94     (&[ARM], "crypto/fipsmodule/aes/asm/bsaes-armv7.pl"),
95     (&[ARM], "crypto/fipsmodule/aes/asm/vpaes-armv7.pl"),
96     (&[ARM], "crypto/fipsmodule/bn/asm/armv4-mont.pl"),
97     (&[ARM], "crypto/chacha/asm/chacha-armv4.pl"),
98     (&[ARM], "crypto/curve25519/asm/x25519-asm-arm.S"),
99     (&[ARM], "crypto/fipsmodule/ec/asm/ecp_nistz256-armv4.pl"),
100     (&[ARM], "crypto/fipsmodule/modes/asm/ghash-armv4.pl"),
101     (&[ARM], "crypto/poly1305/poly1305_arm.c"),
102     (&[ARM], "crypto/poly1305/poly1305_arm_asm.S"),
103     (&[ARM], "crypto/fipsmodule/sha/asm/sha256-armv4.pl"),
104     (&[ARM], "crypto/fipsmodule/sha/asm/sha512-armv4.pl"),
105 
106     (&[AARCH64], "crypto/fipsmodule/aes/asm/vpaes-armv8.pl"),
107     (&[AARCH64], "crypto/fipsmodule/bn/asm/armv8-mont.pl"),
108     (&[AARCH64], "crypto/chacha/asm/chacha-armv8.pl"),
109     (&[AARCH64], "crypto/fipsmodule/ec/asm/ecp_nistz256-armv8.pl"),
110     (&[AARCH64], "crypto/fipsmodule/modes/asm/ghash-neon-armv8.pl"),
111     (&[AARCH64], SHA512_ARMV8),
112 ];
113 
114 const SHA256_X86_64: &str = "crypto/fipsmodule/sha/asm/sha256-x86_64.pl";
115 const SHA512_X86_64: &str = "crypto/fipsmodule/sha/asm/sha512-x86_64.pl";
116 
117 const SHA256_ARMV8: &str = "crypto/fipsmodule/sha/asm/sha256-armv8.pl";
118 const SHA512_ARMV8: &str = "crypto/fipsmodule/sha/asm/sha512-armv8.pl";
119 
120 const RING_TEST_SRCS: &[&str] = &[("crypto/constant_time_test.c")];
121 
122 #[rustfmt::skip]
123 const RING_INCLUDES: &[&str] =
124     &[
125       "crypto/curve25519/curve25519_tables.h",
126       "crypto/curve25519/internal.h",
127       "crypto/fipsmodule/bn/internal.h",
128       "crypto/fipsmodule/ec/ecp_nistz256_table.inl",
129       "crypto/fipsmodule/ec/ecp_nistz384.inl",
130       "crypto/fipsmodule/ec/ecp_nistz.h",
131       "crypto/fipsmodule/ec/ecp_nistz384.h",
132       "crypto/fipsmodule/ec/ecp_nistz256.h",
133       "crypto/internal.h",
134       "crypto/limbs/limbs.h",
135       "crypto/limbs/limbs.inl",
136       "crypto/poly1305/internal.h",
137       "include/GFp/aes.h",
138       "include/GFp/arm_arch.h",
139       "include/GFp/base.h",
140       "include/GFp/check.h",
141       "include/GFp/cpu.h",
142       "include/GFp/mem.h",
143       "include/GFp/poly1305.h",
144       "include/GFp/type_check.h",
145       "third_party/fiat/curve25519_32.h",
146       "third_party/fiat/curve25519_64.h",
147     ];
148 
149 #[rustfmt::skip]
150 const RING_PERL_INCLUDES: &[&str] =
151     &["crypto/perlasm/arm-xlate.pl",
152       "crypto/perlasm/x86gas.pl",
153       "crypto/perlasm/x86nasm.pl",
154       "crypto/perlasm/x86asm.pl",
155       "crypto/perlasm/x86_64-xlate.pl"];
156 
157 const RING_BUILD_FILE: &[&str] = &["build.rs"];
158 
159 const PREGENERATED: &str = "pregenerated";
160 
c_flags(target: &Target) -> &'static [&'static str]161 fn c_flags(target: &Target) -> &'static [&'static str] {
162     if target.env != MSVC {
163         static NON_MSVC_FLAGS: &[&str] = &[
164             "-std=c1x", // GCC 4.6 requires "c1x" instead of "c11"
165             "-Wbad-function-cast",
166             "-Wnested-externs",
167             "-Wstrict-prototypes",
168         ];
169         NON_MSVC_FLAGS
170     } else {
171         &[]
172     }
173 }
174 
cpp_flags(target: &Target) -> &'static [&'static str]175 fn cpp_flags(target: &Target) -> &'static [&'static str] {
176     if target.env != MSVC {
177         static NON_MSVC_FLAGS: &[&str] = &[
178             "-pedantic",
179             "-pedantic-errors",
180             "-Wall",
181             "-Wextra",
182             "-Wcast-align",
183             "-Wcast-qual",
184             "-Wconversion",
185             "-Wenum-compare",
186             "-Wfloat-equal",
187             "-Wformat=2",
188             "-Winline",
189             "-Winvalid-pch",
190             "-Wmissing-field-initializers",
191             "-Wmissing-include-dirs",
192             "-Wredundant-decls",
193             "-Wshadow",
194             "-Wsign-compare",
195             "-Wsign-conversion",
196             "-Wundef",
197             "-Wuninitialized",
198             "-Wwrite-strings",
199             "-fno-strict-aliasing",
200             "-fvisibility=hidden",
201         ];
202         NON_MSVC_FLAGS
203     } else {
204         static MSVC_FLAGS: &[&str] = &[
205             "/GS",   // Buffer security checks.
206             "/Gy",   // Enable function-level linking.
207             "/EHsc", // C++ exceptions only, only in C++.
208             "/GR-",  // Disable RTTI.
209             "/Zc:wchar_t",
210             "/Zc:forScope",
211             "/Zc:inline",
212             "/Zc:rvalueCast",
213             // Warnings.
214             "/sdl",
215             "/Wall",
216             "/wd4127", // C4127: conditional expression is constant
217             "/wd4464", // C4464: relative include path contains '..'
218             "/wd4514", // C4514: <name>: unreferenced inline function has be
219             "/wd4710", // C4710: function not inlined
220             "/wd4711", // C4711: function 'function' selected for inline expansion
221             "/wd4820", // C4820: <struct>: <n> bytes padding added after <name>
222             "/wd5045", /* C5045: Compiler will insert Spectre mitigation for memory load if
223                         * /Qspectre switch specified */
224         ];
225         MSVC_FLAGS
226     }
227 }
228 
229 const LD_FLAGS: &[&str] = &[];
230 
231 // None means "any OS" or "any target". The first match in sequence order is
232 // taken.
233 const ASM_TARGETS: &[(&str, Option<&str>, Option<&str>)] = &[
234     ("x86_64", Some("ios"), Some("macosx")),
235     ("x86_64", Some("macos"), Some("macosx")),
236     ("x86_64", Some(WINDOWS), Some("nasm")),
237     ("x86_64", None, Some("elf")),
238     ("aarch64", Some("ios"), Some("ios64")),
239     ("aarch64", Some("macos"), Some("ios64")),
240     ("aarch64", None, Some("linux64")),
241     ("x86", Some(WINDOWS), Some("win32n")),
242     ("x86", Some("ios"), Some("macosx")),
243     ("x86", None, Some("elf")),
244     ("arm", Some("ios"), Some("ios32")),
245     ("arm", None, Some("linux32")),
246     ("wasm32", None, None),
247 ];
248 
249 const WINDOWS: &str = "windows";
250 const MSVC: &str = "msvc";
251 const MSVC_OBJ_OPT: &str = "/Fo";
252 const MSVC_OBJ_EXT: &str = "obj";
253 
main()254 fn main() {
255     if let Ok(package_name) = std::env::var("CARGO_PKG_NAME") {
256         if package_name == "ring" {
257             ring_build_rs_main();
258             return;
259         }
260     }
261 
262     pregenerate_asm_main();
263 }
264 
ring_build_rs_main()265 fn ring_build_rs_main() {
266     use std::env;
267 
268     let out_dir = env::var("OUT_DIR").unwrap();
269     let out_dir = PathBuf::from(out_dir);
270 
271     let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
272     let os = env::var("CARGO_CFG_TARGET_OS").unwrap();
273     let env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
274     let (obj_ext, obj_opt) = if env == MSVC {
275         (MSVC_OBJ_EXT, MSVC_OBJ_OPT)
276     } else {
277         ("o", "-o")
278     };
279 
280     let is_git = std::fs::metadata(".git").is_ok();
281 
282     // Published builds are always release builds.
283     let is_debug = is_git && env::var("DEBUG").unwrap() != "false";
284 
285     let target = Target {
286         arch,
287         os,
288         env,
289         obj_ext,
290         obj_opt,
291         is_git,
292         is_debug,
293     };
294     let pregenerated = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join(PREGENERATED);
295 
296     build_c_code(&target, pregenerated, &out_dir);
297     check_all_files_tracked()
298 }
299 
pregenerate_asm_main()300 fn pregenerate_asm_main() {
301     let pregenerated = PathBuf::from(PREGENERATED);
302     std::fs::create_dir(&pregenerated).unwrap();
303     let pregenerated_tmp = pregenerated.join("tmp");
304     std::fs::create_dir(&pregenerated_tmp).unwrap();
305 
306     for &(target_arch, target_os, perlasm_format) in ASM_TARGETS {
307         // For Windows, package pregenerated object files instead of
308         // pregenerated assembly language source files, so that the user
309         // doesn't need to install the assembler.
310         let asm_dir = if target_os == Some(WINDOWS) {
311             &pregenerated_tmp
312         } else {
313             &pregenerated
314         };
315 
316         if let Some(perlasm_format) = perlasm_format {
317             let perlasm_src_dsts =
318                 perlasm_src_dsts(&asm_dir, target_arch, target_os, perlasm_format);
319             perlasm(&perlasm_src_dsts, target_arch, perlasm_format, None);
320 
321             if target_os == Some(WINDOWS) {
322                 let srcs = asm_srcs(perlasm_src_dsts);
323                 for src in srcs {
324                     let obj_path = obj_path(&pregenerated, &src, MSVC_OBJ_EXT);
325                     run_command(nasm(&src, target_arch, &obj_path));
326                 }
327             }
328         }
329     }
330 }
331 
332 struct Target {
333     arch: String,
334     os: String,
335     env: String,
336     obj_ext: &'static str,
337     obj_opt: &'static str,
338     is_git: bool,
339     is_debug: bool,
340 }
341 
build_c_code(target: &Target, pregenerated: PathBuf, out_dir: &Path)342 fn build_c_code(target: &Target, pregenerated: PathBuf, out_dir: &Path) {
343     #[cfg(not(feature = "wasm32_c"))]
344     {
345         if &target.arch == "wasm32" {
346             return;
347         }
348     }
349 
350     let includes_modified = RING_INCLUDES
351         .iter()
352         .chain(RING_BUILD_FILE.iter())
353         .chain(RING_PERL_INCLUDES.iter())
354         .map(|f| file_modified(Path::new(*f)))
355         .max()
356         .unwrap();
357 
358     fn is_none_or_equals<T>(opt: Option<T>, other: T) -> bool
359     where
360         T: PartialEq,
361     {
362         if let Some(value) = opt {
363             value == other
364         } else {
365             true
366         }
367     }
368 
369     let (_, _, perlasm_format) = ASM_TARGETS
370         .iter()
371         .find(|entry| {
372             let &(entry_arch, entry_os, _) = *entry;
373             entry_arch == target.arch && is_none_or_equals(entry_os, &target.os)
374         })
375         .unwrap();
376 
377     let use_pregenerated = !target.is_git;
378     let warnings_are_errors = target.is_git;
379 
380     let asm_dir = if use_pregenerated {
381         &pregenerated
382     } else {
383         out_dir
384     };
385 
386     let asm_srcs = if let Some(perlasm_format) = perlasm_format {
387         let perlasm_src_dsts =
388             perlasm_src_dsts(asm_dir, &target.arch, Some(&target.os), perlasm_format);
389 
390         if !use_pregenerated {
391             perlasm(
392                 &perlasm_src_dsts[..],
393                 &target.arch,
394                 perlasm_format,
395                 Some(includes_modified),
396             );
397         }
398 
399         let mut asm_srcs = asm_srcs(perlasm_src_dsts);
400 
401         // For Windows we also pregenerate the object files for non-Git builds so
402         // the user doesn't need to install the assembler. On other platforms we
403         // assume the C compiler also assembles.
404         if use_pregenerated && target.os == WINDOWS {
405             // The pregenerated object files always use ".obj" as the extension,
406             // even when the C/C++ compiler outputs files with the ".o" extension.
407             asm_srcs = asm_srcs
408                 .iter()
409                 .map(|src| obj_path(&pregenerated, src.as_path(), "obj"))
410                 .collect::<Vec<_>>();
411         }
412 
413         asm_srcs
414     } else {
415         Vec::new()
416     };
417 
418     let core_srcs = sources_for_arch(&target.arch)
419         .into_iter()
420         .filter(|p| !is_perlasm(&p))
421         .collect::<Vec<_>>();
422 
423     let test_srcs = RING_TEST_SRCS.iter().map(PathBuf::from).collect::<Vec<_>>();
424 
425     let libs = [
426         ("ring-core", &core_srcs[..], &asm_srcs[..]),
427         ("ring-test", &test_srcs[..], &[]),
428     ];
429 
430     // XXX: Ideally, ring-test would only be built for `cargo test`, but Cargo
431     // can't do that yet.
432     libs.iter().for_each(|&(lib_name, srcs, additional_srcs)| {
433         build_library(
434             &target,
435             &out_dir,
436             lib_name,
437             srcs,
438             additional_srcs,
439             warnings_are_errors,
440             includes_modified,
441         )
442     });
443 
444     println!(
445         "cargo:rustc-link-search=native={}",
446         out_dir.to_str().expect("Invalid path")
447     );
448 }
449 
build_library( target: &Target, out_dir: &Path, lib_name: &str, srcs: &[PathBuf], additional_srcs: &[PathBuf], warnings_are_errors: bool, includes_modified: SystemTime, )450 fn build_library(
451     target: &Target,
452     out_dir: &Path,
453     lib_name: &str,
454     srcs: &[PathBuf],
455     additional_srcs: &[PathBuf],
456     warnings_are_errors: bool,
457     includes_modified: SystemTime,
458 ) {
459     // Compile all the (dirty) source files into object files.
460     let objs = additional_srcs
461         .iter()
462         .chain(srcs.iter())
463         .filter(|f| &target.env != "msvc" || f.extension().unwrap().to_str().unwrap() != "S")
464         .map(|f| compile(f, target, warnings_are_errors, out_dir, includes_modified))
465         .collect::<Vec<_>>();
466 
467     // Rebuild the library if necessary.
468     let lib_path = PathBuf::from(out_dir).join(format!("lib{}.a", lib_name));
469 
470     if objs
471         .iter()
472         .map(Path::new)
473         .any(|p| need_run(&p, &lib_path, includes_modified))
474     {
475         let mut c = cc::Build::new();
476 
477         for f in LD_FLAGS {
478             let _ = c.flag(&f);
479         }
480         match target.os.as_str() {
481             "macos" => {
482                 let _ = c.flag("-fPIC");
483                 let _ = c.flag("-Wl,-dead_strip");
484             }
485             _ => {
486                 let _ = c.flag("-Wl,--gc-sections");
487             }
488         }
489         for o in objs {
490             let _ = c.object(o);
491         }
492 
493         // Handled below.
494         let _ = c.cargo_metadata(false);
495 
496         c.compile(
497             lib_path
498                 .file_name()
499                 .and_then(|f| f.to_str())
500                 .expect("No filename"),
501         );
502     }
503 
504     // Link the library. This works even when the library doesn't need to be
505     // rebuilt.
506     println!("cargo:rustc-link-lib=static={}", lib_name);
507 }
508 
compile( p: &Path, target: &Target, warnings_are_errors: bool, out_dir: &Path, includes_modified: SystemTime, ) -> String509 fn compile(
510     p: &Path,
511     target: &Target,
512     warnings_are_errors: bool,
513     out_dir: &Path,
514     includes_modified: SystemTime,
515 ) -> String {
516     let ext = p.extension().unwrap().to_str().unwrap();
517     if ext == "obj" {
518         p.to_str().expect("Invalid path").into()
519     } else {
520         let mut out_path = out_dir.join(p.file_name().unwrap());
521         assert!(out_path.set_extension(target.obj_ext));
522         if need_run(&p, &out_path, includes_modified) {
523             let cmd = if target.os != WINDOWS || ext != "asm" {
524                 cc(p, ext, target, warnings_are_errors, &out_path)
525             } else {
526                 nasm(p, &target.arch, &out_path)
527             };
528 
529             run_command(cmd);
530         }
531         out_path.to_str().expect("Invalid path").into()
532     }
533 }
534 
obj_path(out_dir: &Path, src: &Path, obj_ext: &str) -> PathBuf535 fn obj_path(out_dir: &Path, src: &Path, obj_ext: &str) -> PathBuf {
536     let mut out_path = out_dir.join(src.file_name().unwrap());
537     assert!(out_path.set_extension(obj_ext));
538     out_path
539 }
540 
cc( file: &Path, ext: &str, target: &Target, warnings_are_errors: bool, out_dir: &Path, ) -> Command541 fn cc(
542     file: &Path,
543     ext: &str,
544     target: &Target,
545     warnings_are_errors: bool,
546     out_dir: &Path,
547 ) -> Command {
548     let is_musl = target.env.starts_with("musl");
549 
550     let mut c = cc::Build::new();
551     let _ = c.include("include");
552     match ext {
553         "c" => {
554             for f in c_flags(target) {
555                 let _ = c.flag(f);
556             }
557         }
558         "S" => (),
559         e => panic!("Unsupported file extension: {:?}", e),
560     };
561     for f in cpp_flags(target) {
562         let _ = c.flag(&f);
563     }
564     if target.os != "none"
565         && target.os != "redox"
566         && target.os != "windows"
567         && target.arch != "wasm32"
568     {
569         let _ = c.flag("-fstack-protector");
570     }
571 
572     match (target.os.as_str(), target.env.as_str()) {
573         // ``-gfull`` is required for Darwin's |-dead_strip|.
574         ("macos", _) => {
575             let _ = c.flag("-gfull");
576         }
577         (_, "msvc") => (),
578         _ => {
579             let _ = c.flag("-g3");
580         }
581     };
582     if !target.is_debug {
583         let _ = c.define("NDEBUG", None);
584     }
585 
586     if &target.env == "msvc" {
587         if std::env::var("OPT_LEVEL").unwrap() == "0" {
588             let _ = c.flag("/Od"); // Disable optimization for debug builds.
589                                    // run-time checking: (s)tack frame, (u)ninitialized variables
590             let _ = c.flag("/RTCsu");
591         } else {
592             let _ = c.flag("/Ox"); // Enable full optimization.
593         }
594     }
595 
596     // Allow cross-compiling without a target sysroot for these targets.
597     //
598     // poly1305_vec.c requires <emmintrin.h> which requires <stdlib.h>.
599     if (target.arch == "wasm32" && target.os == "unknown")
600         || (target.os == "linux" && is_musl && target.arch != "x86_64")
601     {
602         if let Ok(compiler) = c.try_get_compiler() {
603             // TODO: Expand this to non-clang compilers in 0.17.0 if practical.
604             if compiler.is_like_clang() {
605                 let _ = c.flag("-nostdlibinc");
606                 let _ = c.define("GFp_NOSTDLIBINC", "1");
607             }
608         }
609     }
610 
611     if warnings_are_errors {
612         let flag = if &target.env != "msvc" {
613             "-Werror"
614         } else {
615             "/WX"
616         };
617         let _ = c.flag(flag);
618     }
619     if is_musl {
620         // Some platforms enable _FORTIFY_SOURCE by default, but musl
621         // libc doesn't support it yet. See
622         // http://wiki.musl-libc.org/wiki/Future_Ideas#Fortify
623         // http://www.openwall.com/lists/musl/2015/02/04/3
624         // http://www.openwall.com/lists/musl/2015/06/17/1
625         let _ = c.flag("-U_FORTIFY_SOURCE");
626     }
627 
628     let mut c = c.get_compiler().to_command();
629     let _ = c
630         .arg("-c")
631         .arg(format!(
632             "{}{}",
633             target.obj_opt,
634             out_dir.to_str().expect("Invalid path")
635         ))
636         .arg(file);
637     c
638 }
639 
nasm(file: &Path, arch: &str, out_file: &Path) -> Command640 fn nasm(file: &Path, arch: &str, out_file: &Path) -> Command {
641     let oformat = match arch {
642         "x86_64" => ("win64"),
643         "x86" => ("win32"),
644         _ => panic!("unsupported arch: {}", arch),
645     };
646     let mut c = Command::new("./target/tools/nasm");
647     let _ = c
648         .arg("-o")
649         .arg(out_file.to_str().expect("Invalid path"))
650         .arg("-f")
651         .arg(oformat)
652         .arg("-Xgnu")
653         .arg("-gcv8")
654         .arg(file);
655     c
656 }
657 
run_command_with_args<S>(command_name: S, args: &[String]) where S: AsRef<std::ffi::OsStr> + Copy,658 fn run_command_with_args<S>(command_name: S, args: &[String])
659 where
660     S: AsRef<std::ffi::OsStr> + Copy,
661 {
662     let mut cmd = Command::new(command_name);
663     let _ = cmd.args(args);
664     run_command(cmd)
665 }
666 
run_command(mut cmd: Command)667 fn run_command(mut cmd: Command) {
668     eprintln!("running {:?}", cmd);
669     let status = cmd.status().unwrap_or_else(|e| {
670         panic!("failed to execute [{:?}]: {}", cmd, e);
671     });
672     if !status.success() {
673         panic!("execution failed");
674     }
675 }
676 
sources_for_arch(arch: &str) -> Vec<PathBuf>677 fn sources_for_arch(arch: &str) -> Vec<PathBuf> {
678     RING_SRCS
679         .iter()
680         .filter(|&&(archs, _)| archs.is_empty() || archs.contains(&arch))
681         .map(|&(_, p)| PathBuf::from(p))
682         .collect::<Vec<_>>()
683 }
684 
perlasm_src_dsts( out_dir: &Path, arch: &str, os: Option<&str>, perlasm_format: &str, ) -> Vec<(PathBuf, PathBuf)>685 fn perlasm_src_dsts(
686     out_dir: &Path,
687     arch: &str,
688     os: Option<&str>,
689     perlasm_format: &str,
690 ) -> Vec<(PathBuf, PathBuf)> {
691     let srcs = sources_for_arch(arch);
692     let mut src_dsts = srcs
693         .iter()
694         .filter(|p| is_perlasm(p))
695         .map(|src| (src.clone(), asm_path(out_dir, src, os, perlasm_format)))
696         .collect::<Vec<_>>();
697 
698     // Some PerlAsm source files need to be run multiple times with different
699     // output paths.
700     {
701         // Appease the borrow checker.
702         let mut maybe_synthesize = |concrete, synthesized| {
703             let concrete_path = PathBuf::from(concrete);
704             if srcs.contains(&concrete_path) {
705                 let synthesized_path = PathBuf::from(synthesized);
706                 src_dsts.push((
707                     concrete_path,
708                     asm_path(out_dir, &synthesized_path, os, perlasm_format),
709                 ))
710             }
711         };
712         maybe_synthesize(SHA512_X86_64, SHA256_X86_64);
713         maybe_synthesize(SHA512_ARMV8, SHA256_ARMV8);
714     }
715 
716     src_dsts
717 }
718 
asm_srcs(perlasm_src_dsts: Vec<(PathBuf, PathBuf)>) -> Vec<PathBuf>719 fn asm_srcs(perlasm_src_dsts: Vec<(PathBuf, PathBuf)>) -> Vec<PathBuf> {
720     perlasm_src_dsts
721         .into_iter()
722         .map(|(_src, dst)| dst)
723         .collect::<Vec<_>>()
724 }
725 
is_perlasm(path: &PathBuf) -> bool726 fn is_perlasm(path: &PathBuf) -> bool {
727     path.extension().unwrap().to_str().unwrap() == "pl"
728 }
729 
asm_path(out_dir: &Path, src: &Path, os: Option<&str>, perlasm_format: &str) -> PathBuf730 fn asm_path(out_dir: &Path, src: &Path, os: Option<&str>, perlasm_format: &str) -> PathBuf {
731     let src_stem = src.file_stem().expect("source file without basename");
732 
733     let dst_stem = src_stem.to_str().unwrap();
734     let dst_extension = if os == Some("windows") { "asm" } else { "S" };
735     let dst_filename = format!("{}-{}.{}", dst_stem, perlasm_format, dst_extension);
736     out_dir.join(dst_filename)
737 }
738 
perlasm( src_dst: &[(PathBuf, PathBuf)], arch: &str, perlasm_format: &str, includes_modified: Option<SystemTime>, )739 fn perlasm(
740     src_dst: &[(PathBuf, PathBuf)],
741     arch: &str,
742     perlasm_format: &str,
743     includes_modified: Option<SystemTime>,
744 ) {
745     for (src, dst) in src_dst {
746         if let Some(includes_modified) = includes_modified {
747             if !need_run(src, dst, includes_modified) {
748                 continue;
749             }
750         }
751 
752         let mut args = Vec::<String>::new();
753         args.push(src.to_string_lossy().into_owned());
754         args.push(perlasm_format.to_owned());
755         if arch == "x86" {
756             args.push("-fPIC".into());
757             args.push("-DOPENSSL_IA32_SSE2".into());
758         }
759         // Work around PerlAsm issue for ARM and AAarch64 targets by replacing
760         // back slashes with forward slashes.
761         let dst = dst
762             .to_str()
763             .expect("Could not convert path")
764             .replace("\\", "/");
765         args.push(dst);
766         run_command_with_args(&get_command("PERL_EXECUTABLE", "perl"), &args);
767     }
768 }
769 
770 fn need_run(source: &Path, target: &Path, includes_modified: SystemTime) -> bool {
771     let s_modified = file_modified(source);
772     if let Ok(target_metadata) = std::fs::metadata(target) {
773         let target_modified = target_metadata.modified().unwrap();
774         s_modified >= target_modified || includes_modified >= target_modified
775     } else {
776         // On error fetching metadata for the target file, assume the target
777         // doesn't exist.
778         true
779     }
780 }
781 
782 fn file_modified(path: &Path) -> SystemTime {
783     let path = Path::new(path);
784     let path_as_str = format!("{:?}", path);
785     std::fs::metadata(path)
786         .expect(&path_as_str)
787         .modified()
788         .expect("nah")
789 }
790 
791 fn get_command(var: &str, default: &str) -> String {
792     std::env::var(var).unwrap_or_else(|_| default.into())
793 }
794 
795 fn check_all_files_tracked() {
796     for path in &["crypto", "include", "third_party/fiat"] {
797         walk_dir(&PathBuf::from(path), &is_tracked);
798     }
799 }
800 
801 fn is_tracked(file: &DirEntry) {
802     let p = file.path();
803     let cmp = |f| p == PathBuf::from(f);
804     let tracked = match p.extension().and_then(|p| p.to_str()) {
805         Some("h") | Some("inl") => RING_INCLUDES.iter().any(cmp),
806         Some("c") | Some("S") | Some("asm") => {
807             RING_SRCS.iter().any(|(_, f)| cmp(f)) || RING_TEST_SRCS.iter().any(cmp)
808         }
809         Some("pl") => RING_SRCS.iter().any(|(_, f)| cmp(f)) || RING_PERL_INCLUDES.iter().any(cmp),
810         _ => true,
811     };
812     if !tracked {
813         panic!("{:?} is not tracked in build.rs", p);
814     }
815 }
816 
817 fn walk_dir<F>(dir: &Path, cb: &F)
818 where
819     F: Fn(&DirEntry),
820 {
821     if dir.is_dir() {
822         for entry in fs::read_dir(dir).unwrap() {
823             let entry = entry.unwrap();
824             let path = entry.path();
825             if path.is_dir() {
826                 walk_dir(&path, cb);
827             } else {
828                 cb(&entry);
829             }
830         }
831     }
832 }
833