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