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