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