• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 use std::{
23     fs::{self, DirEntry},
24     io::Write,
25     path::{Path, PathBuf},
26     process::Command,
27 };
28 
29 const X86: &str = "x86";
30 const X86_64: &str = "x86_64";
31 const AARCH64: &str = "aarch64";
32 const ARM: &str = "arm";
33 
34 #[rustfmt::skip]
35 const RING_SRCS: &[(&[&str], &str)] = &[
36     (&[], "crypto/curve25519/curve25519.c"),
37     (&[], "crypto/fipsmodule/aes/aes_nohw.c"),
38     (&[], "crypto/fipsmodule/bn/montgomery.c"),
39     (&[], "crypto/fipsmodule/bn/montgomery_inv.c"),
40     (&[], "crypto/fipsmodule/rsa/padding.c"),
41     (&[], "crypto/limbs/limbs.c"),
42     (&[], "crypto/mem.c"),
43     (&[], "crypto/poly1305/poly1305.c"),
44 
45     (&[AARCH64, ARM, X86_64, X86], "crypto/crypto.c"),
46     (&[AARCH64, ARM, X86_64, X86], "crypto/fipsmodule/ec/ecp_nistz.c"),
47     (&[AARCH64, ARM, X86_64, X86], "crypto/fipsmodule/ec/gfp_p256.c"),
48     (&[AARCH64, ARM, X86_64, X86], "crypto/fipsmodule/ec/gfp_p384.c"),
49     (&[AARCH64, ARM, X86_64, X86], "crypto/fipsmodule/ec/p256.c"),
50 
51     (&[X86_64, X86], "crypto/cpu-intel.c"),
52 
53     (&[X86], "crypto/fipsmodule/aes/asm/aesni-x86.pl"),
54     (&[X86], "crypto/fipsmodule/aes/asm/vpaes-x86.pl"),
55     (&[X86], "crypto/fipsmodule/bn/asm/x86-mont.pl"),
56     (&[X86], "crypto/chacha/asm/chacha-x86.pl"),
57     (&[X86], "crypto/fipsmodule/modes/asm/ghash-x86.pl"),
58 
59     (&[X86_64], "crypto/chacha/asm/chacha-x86_64.pl"),
60     (&[X86_64], "crypto/fipsmodule/aes/asm/aesni-x86_64.pl"),
61     (&[X86_64], "crypto/fipsmodule/aes/asm/vpaes-x86_64.pl"),
62     (&[X86_64], "crypto/fipsmodule/bn/asm/x86_64-mont.pl"),
63     (&[X86_64], "crypto/fipsmodule/bn/asm/x86_64-mont5.pl"),
64     (&[X86_64], "crypto/fipsmodule/ec/p256-x86_64.c"),
65     (&[X86_64], "crypto/fipsmodule/ec/asm/p256-x86_64-asm.pl"),
66     (&[X86_64], "crypto/fipsmodule/modes/asm/aesni-gcm-x86_64.pl"),
67     (&[X86_64], "crypto/fipsmodule/modes/asm/ghash-x86_64.pl"),
68     (&[X86_64], "crypto/poly1305/poly1305_vec.c"),
69     (&[X86_64], SHA512_X86_64),
70     (&[X86_64], "crypto/cipher_extra/asm/chacha20_poly1305_x86_64.pl"),
71 
72     (&[AARCH64, ARM], "crypto/fipsmodule/aes/asm/aesv8-armx.pl"),
73     (&[AARCH64, ARM], "crypto/fipsmodule/modes/asm/ghashv8-armx.pl"),
74 
75     (&[ARM], "crypto/fipsmodule/aes/asm/bsaes-armv7.pl"),
76     (&[ARM], "crypto/fipsmodule/aes/asm/vpaes-armv7.pl"),
77     (&[ARM], "crypto/fipsmodule/bn/asm/armv4-mont.pl"),
78     (&[ARM], "crypto/chacha/asm/chacha-armv4.pl"),
79     (&[ARM], "crypto/curve25519/asm/x25519-asm-arm.S"),
80     (&[ARM], "crypto/fipsmodule/modes/asm/ghash-armv4.pl"),
81     (&[ARM], "crypto/poly1305/poly1305_arm.c"),
82     (&[ARM], "crypto/poly1305/poly1305_arm_asm.S"),
83     (&[ARM], "crypto/fipsmodule/sha/asm/sha256-armv4.pl"),
84     (&[ARM], "crypto/fipsmodule/sha/asm/sha512-armv4.pl"),
85 
86     (&[AARCH64], "crypto/fipsmodule/aes/asm/vpaes-armv8.pl"),
87     (&[AARCH64], "crypto/fipsmodule/bn/asm/armv8-mont.pl"),
88     (&[AARCH64], "crypto/chacha/asm/chacha-armv8.pl"),
89     (&[AARCH64], "crypto/fipsmodule/modes/asm/ghash-neon-armv8.pl"),
90     (&[AARCH64], SHA512_ARMV8),
91 ];
92 
93 const SHA256_X86_64: &str = "crypto/fipsmodule/sha/asm/sha256-x86_64.pl";
94 const SHA512_X86_64: &str = "crypto/fipsmodule/sha/asm/sha512-x86_64.pl";
95 
96 const SHA256_ARMV8: &str = "crypto/fipsmodule/sha/asm/sha256-armv8.pl";
97 const SHA512_ARMV8: &str = "crypto/fipsmodule/sha/asm/sha512-armv8.pl";
98 
99 const RING_TEST_SRCS: &[&str] = &[("crypto/constant_time_test.c")];
100 
101 const PREGENERATED: &str = "pregenerated";
102 
c_flags(target: &Target) -> &'static [&'static str]103 fn c_flags(target: &Target) -> &'static [&'static str] {
104     if target.env != MSVC {
105         static NON_MSVC_FLAGS: &[&str] = &[
106             "-std=c1x", // GCC 4.6 requires "c1x" instead of "c11"
107             "-Wbad-function-cast",
108             "-Wnested-externs",
109             "-Wstrict-prototypes",
110         ];
111         NON_MSVC_FLAGS
112     } else {
113         &[]
114     }
115 }
116 
cpp_flags(target: &Target) -> &'static [&'static str]117 fn cpp_flags(target: &Target) -> &'static [&'static str] {
118     if target.env != MSVC {
119         static NON_MSVC_FLAGS: &[&str] = &[
120             "-pedantic",
121             "-pedantic-errors",
122             "-Wall",
123             "-Wextra",
124             "-Wcast-align",
125             "-Wcast-qual",
126             "-Wconversion",
127             "-Wenum-compare",
128             "-Wfloat-equal",
129             "-Wformat=2",
130             "-Winline",
131             "-Winvalid-pch",
132             "-Wmissing-field-initializers",
133             "-Wmissing-include-dirs",
134             "-Wredundant-decls",
135             "-Wshadow",
136             "-Wsign-compare",
137             "-Wsign-conversion",
138             "-Wundef",
139             "-Wuninitialized",
140             "-Wwrite-strings",
141             "-fno-strict-aliasing",
142             "-fvisibility=hidden",
143         ];
144         NON_MSVC_FLAGS
145     } else {
146         static MSVC_FLAGS: &[&str] = &[
147             "/GS",   // Buffer security checks.
148             "/Gy",   // Enable function-level linking.
149             "/EHsc", // C++ exceptions only, only in C++.
150             "/GR-",  // Disable RTTI.
151             "/Zc:wchar_t",
152             "/Zc:forScope",
153             "/Zc:inline",
154             "/Zc:rvalueCast",
155             // Warnings.
156             "/sdl",
157             "/Wall",
158             "/wd4127", // C4127: conditional expression is constant
159             "/wd4464", // C4464: relative include path contains '..'
160             "/wd4514", // C4514: <name>: unreferenced inline function has be
161             "/wd4710", // C4710: function not inlined
162             "/wd4711", // C4711: function 'function' selected for inline expansion
163             "/wd4820", // C4820: <struct>: <n> bytes padding added after <name>
164             "/wd5045", /* C5045: Compiler will insert Spectre mitigation for memory load if
165                         * /Qspectre switch specified */
166         ];
167         MSVC_FLAGS
168     }
169 }
170 
171 const LD_FLAGS: &[&str] = &[];
172 
173 // None means "any OS" or "any target". The first match in sequence order is
174 // taken.
175 const ASM_TARGETS: &[AsmTarget] = &[
176     AsmTarget {
177         oss: LINUX_ABI,
178         arch: "aarch64",
179         perlasm_format: "linux64",
180         asm_extension: "S",
181         preassemble: false,
182     },
183     AsmTarget {
184         oss: LINUX_ABI,
185         arch: "arm",
186         perlasm_format: "linux32",
187         asm_extension: "S",
188         preassemble: false,
189     },
190     AsmTarget {
191         oss: LINUX_ABI,
192         arch: "x86",
193         perlasm_format: "elf",
194         asm_extension: "S",
195         preassemble: false,
196     },
197     AsmTarget {
198         oss: LINUX_ABI,
199         arch: "x86_64",
200         perlasm_format: "elf",
201         asm_extension: "S",
202         preassemble: false,
203     },
204     AsmTarget {
205         oss: MACOS_ABI,
206         arch: "aarch64",
207         perlasm_format: "ios64",
208         asm_extension: "S",
209         preassemble: false,
210     },
211     AsmTarget {
212         oss: MACOS_ABI,
213         arch: "x86_64",
214         perlasm_format: "macosx",
215         asm_extension: "S",
216         preassemble: false,
217     },
218     AsmTarget {
219         oss: &[WINDOWS],
220         arch: "x86",
221         perlasm_format: "win32n",
222         asm_extension: "asm",
223         preassemble: true,
224     },
225     AsmTarget {
226         oss: &[WINDOWS],
227         arch: "x86_64",
228         perlasm_format: "nasm",
229         asm_extension: "asm",
230         preassemble: true,
231     },
232 ];
233 
234 struct AsmTarget {
235     /// Operating systems.
236     oss: &'static [&'static str],
237 
238     /// Architectures.
239     arch: &'static str,
240 
241     /// The PerlAsm format name.
242     perlasm_format: &'static str,
243 
244     /// The filename extension for assembly files.
245     asm_extension: &'static str,
246 
247     /// Whether pre-assembled object files should be included in the Cargo
248     /// package instead of the asm sources. This way, the user doesn't need
249     /// to install an assembler for the target. This is particularly important
250     /// for x86/x86_64 Windows since an assembler doesn't come with the C
251     /// compiler.
252     preassemble: bool,
253 }
254 
255 /// Operating systems that have the same ABI as Linux on every architecture
256 /// mentioned in `ASM_TARGETS`.
257 const LINUX_ABI: &[&str] = &[
258     "android",
259     "dragonfly",
260     "freebsd",
261     "fuchsia",
262     "illumos",
263     "netbsd",
264     "openbsd",
265     "linux",
266     "solaris",
267 ];
268 
269 /// Operating systems that have the same ABI as macOS on every architecture
270 /// mentioned in `ASM_TARGETS`.
271 const MACOS_ABI: &[&str] = &["ios", "macos"];
272 
273 const WINDOWS: &str = "windows";
274 
275 const MSVC: &str = "msvc";
276 const MSVC_OBJ_OPT: &str = "/Fo";
277 const MSVC_OBJ_EXT: &str = "obj";
278 
279 /// Read an environment variable and tell Cargo that we depend on it.
280 ///
281 /// This needs to be used for any environment variable that isn't a standard
282 /// Cargo-supplied variable.
283 ///
284 /// The name is static since we intend to only read a static set of environment
285 /// variables.
read_env_var(name: &'static str) -> Result<String, std::env::VarError>286 fn read_env_var(name: &'static str) -> Result<String, std::env::VarError> {
287     println!("cargo:rerun-if-env-changed={}", name);
288     std::env::var(name)
289 }
290 
main()291 fn main() {
292     const RING_PREGENERATE_ASM: &str = "RING_PREGENERATE_ASM";
293     match read_env_var(RING_PREGENERATE_ASM).as_deref() {
294         Ok("1") => {
295             pregenerate_asm_main();
296         }
297         Err(std::env::VarError::NotPresent) => ring_build_rs_main(),
298         _ => {
299             panic!("${} has an invalid value", RING_PREGENERATE_ASM);
300         }
301     }
302 }
303 
ring_build_rs_main()304 fn ring_build_rs_main() {
305     use std::env;
306 
307     let out_dir = env::var("OUT_DIR").unwrap();
308     let out_dir = PathBuf::from(out_dir);
309 
310     let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
311     let os = env::var("CARGO_CFG_TARGET_OS").unwrap();
312     let env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
313     let (obj_ext, obj_opt) = if env == MSVC {
314         (MSVC_OBJ_EXT, MSVC_OBJ_OPT)
315     } else {
316         ("o", "-o")
317     };
318 
319     let is_git = std::fs::metadata(".git").is_ok();
320 
321     // Published builds are always release builds.
322     let is_debug = is_git && env::var("DEBUG").unwrap() != "false";
323 
324     let target = Target {
325         arch,
326         os,
327         env,
328         obj_ext,
329         obj_opt,
330         is_git,
331         is_debug,
332     };
333     let pregenerated = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join(PREGENERATED);
334 
335     build_c_code(&target, pregenerated, &out_dir, &ring_core_prefix());
336     emit_rerun_if_changed()
337 }
338 
pregenerate_asm_main()339 fn pregenerate_asm_main() {
340     println!("cargo:rustc-cfg=pregenerate_asm_only");
341 
342     let pregenerated = PathBuf::from(PREGENERATED);
343     std::fs::create_dir(&pregenerated).unwrap();
344     let pregenerated_tmp = pregenerated.join("tmp");
345     std::fs::create_dir(&pregenerated_tmp).unwrap();
346 
347     let mut generated_prefix_headers = false;
348 
349     for asm_target in ASM_TARGETS {
350         // For Windows, package pregenerated object files instead of
351         // pregenerated assembly language source files, so that the user
352         // doesn't need to install the assembler.
353         let asm_dir = if asm_target.preassemble {
354             &pregenerated_tmp
355         } else {
356             &pregenerated
357         };
358 
359         let perlasm_src_dsts = perlasm_src_dsts(&asm_dir, asm_target);
360         perlasm(&perlasm_src_dsts, asm_target);
361 
362         if asm_target.preassemble {
363             if !std::mem::replace(&mut generated_prefix_headers, true) {
364                 generate_prefix_symbols_nasm(&pregenerated, &ring_core_prefix()).unwrap();
365             }
366             let srcs = asm_srcs(perlasm_src_dsts);
367             for src in srcs {
368                 let obj_path = obj_path(&pregenerated, &src, MSVC_OBJ_EXT);
369                 run_command(nasm(&src, asm_target.arch, &obj_path, &pregenerated));
370             }
371         }
372     }
373 }
374 
375 struct Target {
376     arch: String,
377     os: String,
378     env: String,
379     obj_ext: &'static str,
380     obj_opt: &'static str,
381     is_git: bool,
382     is_debug: bool,
383 }
384 
build_c_code(target: &Target, pregenerated: PathBuf, out_dir: &Path, ring_core_prefix: &str)385 fn build_c_code(target: &Target, pregenerated: PathBuf, out_dir: &Path, ring_core_prefix: &str) {
386     println!("cargo:rustc-env=RING_CORE_PREFIX={}", ring_core_prefix);
387 
388     #[cfg(not(feature = "wasm32_c"))]
389     {
390         if &target.arch == "wasm32" {
391             return;
392         }
393     }
394 
395     let asm_target = ASM_TARGETS.iter().find(|asm_target| {
396         asm_target.arch == target.arch && asm_target.oss.contains(&target.os.as_ref())
397     });
398 
399     let use_pregenerated = !target.is_git;
400     let warnings_are_errors = target.is_git;
401 
402     let asm_dir = if use_pregenerated {
403         &pregenerated
404     } else {
405         out_dir
406     };
407 
408     generate_prefix_symbols(target, out_dir, ring_core_prefix).unwrap();
409 
410     let asm_srcs = if let Some(asm_target) = asm_target {
411         let perlasm_src_dsts = perlasm_src_dsts(asm_dir, asm_target);
412 
413         if !use_pregenerated {
414             perlasm(&perlasm_src_dsts[..], asm_target);
415         }
416 
417         let mut asm_srcs = asm_srcs(perlasm_src_dsts);
418 
419         // For Windows we also pregenerate the object files for non-Git builds so
420         // the user doesn't need to install the assembler. On other platforms we
421         // assume the C compiler also assembles.
422         if use_pregenerated && target.os == WINDOWS {
423             // The pregenerated object files always use ".obj" as the extension,
424             // even when the C/C++ compiler outputs files with the ".o" extension.
425             asm_srcs = asm_srcs
426                 .iter()
427                 .map(|src| obj_path(&pregenerated, src.as_path(), "obj"))
428                 .collect::<Vec<_>>();
429         }
430 
431         asm_srcs
432     } else {
433         Vec::new()
434     };
435 
436     let core_srcs = sources_for_arch(&target.arch)
437         .into_iter()
438         .filter(|p| !is_perlasm(&p))
439         .collect::<Vec<_>>();
440 
441     let test_srcs = RING_TEST_SRCS.iter().map(PathBuf::from).collect::<Vec<_>>();
442 
443     let libs = [
444         ("", &core_srcs[..], &asm_srcs[..]),
445         ("test", &test_srcs[..], &[]),
446     ];
447 
448     // XXX: Ideally, ring-test would only be built for `cargo test`, but Cargo
449     // can't do that yet.
450     libs.iter()
451         .for_each(|&(lib_name_suffix, srcs, additional_srcs)| {
452             let lib_name = String::from(ring_core_prefix) + lib_name_suffix;
453             build_library(
454                 &target,
455                 &out_dir,
456                 &lib_name,
457                 srcs,
458                 additional_srcs,
459                 warnings_are_errors,
460             )
461         });
462 
463     println!(
464         "cargo:rustc-link-search=native={}",
465         out_dir.to_str().expect("Invalid path")
466     );
467 }
468 
build_library( target: &Target, out_dir: &Path, lib_name: &str, srcs: &[PathBuf], additional_srcs: &[PathBuf], warnings_are_errors: bool, )469 fn build_library(
470     target: &Target,
471     out_dir: &Path,
472     lib_name: &str,
473     srcs: &[PathBuf],
474     additional_srcs: &[PathBuf],
475     warnings_are_errors: bool,
476 ) {
477     // Compile all the (dirty) source files into object files.
478     let objs = additional_srcs
479         .iter()
480         .chain(srcs.iter())
481         .filter(|f| &target.env != "msvc" || f.extension().unwrap().to_str().unwrap() != "S")
482         .map(|f| compile(f, target, warnings_are_errors, out_dir))
483         .collect::<Vec<_>>();
484 
485     // Rebuild the library if necessary.
486     let lib_path = PathBuf::from(out_dir).join(format!("lib{}.a", lib_name));
487 
488     let mut c = cc::Build::new();
489 
490     for f in LD_FLAGS {
491         let _ = c.flag(&f);
492     }
493     match target.os.as_str() {
494         "macos" => {
495             let _ = c.flag("-fPIC");
496             let _ = c.flag("-Wl,-dead_strip");
497         }
498         _ => {
499             let _ = c.flag("-Wl,--gc-sections");
500         }
501     }
502     for o in objs {
503         let _ = c.object(o);
504     }
505 
506     // Handled below.
507     let _ = c.cargo_metadata(false);
508 
509     c.compile(
510         lib_path
511             .file_name()
512             .and_then(|f| f.to_str())
513             .expect("No filename"),
514     );
515 
516     // Link the library. This works even when the library doesn't need to be
517     // rebuilt.
518     println!("cargo:rustc-link-lib=static={}", lib_name);
519 }
520 
compile(p: &Path, target: &Target, warnings_are_errors: bool, out_dir: &Path) -> String521 fn compile(p: &Path, target: &Target, warnings_are_errors: bool, out_dir: &Path) -> String {
522     let ext = p.extension().unwrap().to_str().unwrap();
523     if ext == "obj" {
524         p.to_str().expect("Invalid path").into()
525     } else {
526         let mut out_path = out_dir.join(p.file_name().unwrap());
527         assert!(out_path.set_extension(target.obj_ext));
528         let cmd = if target.os != WINDOWS || ext != "asm" {
529             cc(p, ext, target, warnings_are_errors, &out_path, out_dir)
530         } else {
531             nasm(p, &target.arch, &out_path, out_dir)
532         };
533 
534         run_command(cmd);
535         out_path.to_str().expect("Invalid path").into()
536     }
537 }
538 
obj_path(out_dir: &Path, src: &Path, obj_ext: &str) -> PathBuf539 fn obj_path(out_dir: &Path, src: &Path, obj_ext: &str) -> PathBuf {
540     let mut out_path = out_dir.join(src.file_name().unwrap());
541     assert!(out_path.set_extension(obj_ext));
542     out_path
543 }
544 
cc( file: &Path, ext: &str, target: &Target, warnings_are_errors: bool, out_path: &Path, include_dir: &Path, ) -> Command545 fn cc(
546     file: &Path,
547     ext: &str,
548     target: &Target,
549     warnings_are_errors: bool,
550     out_path: &Path,
551     include_dir: &Path,
552 ) -> Command {
553     let is_musl = target.env.starts_with("musl");
554 
555     let mut c = cc::Build::new();
556     let _ = c.include("include");
557     let _ = c.include(include_dir);
558     match ext {
559         "c" => {
560             for f in c_flags(target) {
561                 let _ = c.flag(f);
562             }
563         }
564         "S" => (),
565         e => panic!("Unsupported file extension: {:?}", e),
566     };
567     for f in cpp_flags(target) {
568         let _ = c.flag(&f);
569     }
570     if target.os != "none"
571         && target.os != "redox"
572         && target.os != "windows"
573         && target.arch != "wasm32"
574     {
575         let _ = c.flag("-fstack-protector");
576     }
577 
578     match (target.os.as_str(), target.env.as_str()) {
579         // ``-gfull`` is required for Darwin's |-dead_strip|.
580         ("macos", _) => {
581             let _ = c.flag("-gfull");
582         }
583         (_, "msvc") => (),
584         _ => {
585             let _ = c.flag("-g3");
586         }
587     };
588     if !target.is_debug {
589         let _ = c.define("NDEBUG", None);
590     }
591 
592     if &target.env == "msvc" {
593         if std::env::var("OPT_LEVEL").unwrap() == "0" {
594             let _ = c.flag("/Od"); // Disable optimization for debug builds.
595                                    // run-time checking: (s)tack frame, (u)ninitialized variables
596             let _ = c.flag("/RTCsu");
597         } else {
598             let _ = c.flag("/Ox"); // Enable full optimization.
599         }
600     }
601 
602     // Allow cross-compiling without a target sysroot for these targets.
603     //
604     // poly1305_vec.c requires <emmintrin.h> which requires <stdlib.h>.
605     if (target.arch == "wasm32" && target.os == "unknown")
606         || (target.os == "linux" && is_musl && target.arch != "x86_64")
607     {
608         if let Ok(compiler) = c.try_get_compiler() {
609             // TODO: Expand this to non-clang compilers in 0.17.0 if practical.
610             if compiler.is_like_clang() {
611                 let _ = c.flag("-nostdlibinc");
612                 let _ = c.define("RING_CORE_NOSTDLIBINC", "1");
613             }
614         }
615     }
616 
617     if warnings_are_errors {
618         let flag = if &target.env != "msvc" {
619             "-Werror"
620         } else {
621             "/WX"
622         };
623         let _ = c.flag(flag);
624     }
625     if is_musl {
626         // Some platforms enable _FORTIFY_SOURCE by default, but musl
627         // libc doesn't support it yet. See
628         // http://wiki.musl-libc.org/wiki/Future_Ideas#Fortify
629         // http://www.openwall.com/lists/musl/2015/02/04/3
630         // http://www.openwall.com/lists/musl/2015/06/17/1
631         let _ = c.flag("-U_FORTIFY_SOURCE");
632     }
633 
634     let mut c = c.get_compiler().to_command();
635     let _ = c
636         .arg("-c")
637         .arg(format!(
638             "{}{}",
639             target.obj_opt,
640             out_path.to_str().expect("Invalid path")
641         ))
642         .arg(file);
643     c
644 }
645 
nasm(file: &Path, arch: &str, out_file: &Path, include_dir: &Path) -> Command646 fn nasm(file: &Path, arch: &str, out_file: &Path, include_dir: &Path) -> Command {
647     let oformat = match arch {
648         "x86_64" => ("win64"),
649         "x86" => ("win32"),
650         _ => panic!("unsupported arch: {}", arch),
651     };
652 
653     // Nasm requires that the path end in a path separator.
654     let mut include_dir = include_dir.as_os_str().to_os_string();
655     include_dir.push(std::ffi::OsString::from(String::from(
656         std::path::MAIN_SEPARATOR,
657     )));
658 
659     let mut c = Command::new("./target/tools/windows/nasm/nasm");
660     let _ = c
661         .arg("-o")
662         .arg(out_file.to_str().expect("Invalid path"))
663         .arg("-f")
664         .arg(oformat)
665         .arg("-i")
666         .arg("include/")
667         .arg("-i")
668         .arg(include_dir)
669         .arg("-Xgnu")
670         .arg("-gcv8")
671         .arg(file);
672     c
673 }
674 
run_command_with_args<S>(command_name: S, args: &[String]) where S: AsRef<std::ffi::OsStr> + Copy,675 fn run_command_with_args<S>(command_name: S, args: &[String])
676 where
677     S: AsRef<std::ffi::OsStr> + Copy,
678 {
679     let mut cmd = Command::new(command_name);
680     let _ = cmd.args(args);
681     run_command(cmd)
682 }
683 
run_command(mut cmd: Command)684 fn run_command(mut cmd: Command) {
685     eprintln!("running {:?}", cmd);
686     let status = cmd.status().unwrap_or_else(|e| {
687         panic!("failed to execute [{:?}]: {}", cmd, e);
688     });
689     if !status.success() {
690         panic!("execution failed");
691     }
692 }
693 
sources_for_arch(arch: &str) -> Vec<PathBuf>694 fn sources_for_arch(arch: &str) -> Vec<PathBuf> {
695     RING_SRCS
696         .iter()
697         .filter(|&&(archs, _)| archs.is_empty() || archs.contains(&arch))
698         .map(|&(_, p)| PathBuf::from(p))
699         .collect::<Vec<_>>()
700 }
701 
perlasm_src_dsts(out_dir: &Path, asm_target: &AsmTarget) -> Vec<(PathBuf, PathBuf)>702 fn perlasm_src_dsts(out_dir: &Path, asm_target: &AsmTarget) -> Vec<(PathBuf, PathBuf)> {
703     let srcs = sources_for_arch(asm_target.arch);
704     let mut src_dsts = srcs
705         .iter()
706         .filter(|p| is_perlasm(p))
707         .map(|src| (src.clone(), asm_path(out_dir, src, asm_target)))
708         .collect::<Vec<_>>();
709 
710     // Some PerlAsm source files need to be run multiple times with different
711     // output paths.
712     {
713         // Appease the borrow checker.
714         let mut maybe_synthesize = |concrete, synthesized| {
715             let concrete_path = PathBuf::from(concrete);
716             if srcs.contains(&concrete_path) {
717                 let synthesized_path = PathBuf::from(synthesized);
718                 src_dsts.push((
719                     concrete_path,
720                     asm_path(out_dir, &synthesized_path, asm_target),
721                 ))
722             }
723         };
724         maybe_synthesize(SHA512_X86_64, SHA256_X86_64);
725         maybe_synthesize(SHA512_ARMV8, SHA256_ARMV8);
726     }
727 
728     src_dsts
729 }
730 
asm_srcs(perlasm_src_dsts: Vec<(PathBuf, PathBuf)>) -> Vec<PathBuf>731 fn asm_srcs(perlasm_src_dsts: Vec<(PathBuf, PathBuf)>) -> Vec<PathBuf> {
732     perlasm_src_dsts
733         .into_iter()
734         .map(|(_src, dst)| dst)
735         .collect::<Vec<_>>()
736 }
737 
is_perlasm(path: &PathBuf) -> bool738 fn is_perlasm(path: &PathBuf) -> bool {
739     path.extension().unwrap().to_str().unwrap() == "pl"
740 }
741 
asm_path(out_dir: &Path, src: &Path, asm_target: &AsmTarget) -> PathBuf742 fn asm_path(out_dir: &Path, src: &Path, asm_target: &AsmTarget) -> PathBuf {
743     let src_stem = src.file_stem().expect("source file without basename");
744 
745     let dst_stem = src_stem.to_str().unwrap();
746     let dst_filename = format!(
747         "{}-{}.{}",
748         dst_stem, asm_target.perlasm_format, asm_target.asm_extension
749     );
750     out_dir.join(dst_filename)
751 }
752 
perlasm(src_dst: &[(PathBuf, PathBuf)], asm_target: &AsmTarget)753 fn perlasm(src_dst: &[(PathBuf, PathBuf)], asm_target: &AsmTarget) {
754     for (src, dst) in src_dst {
755         let mut args = Vec::<String>::new();
756         args.push(src.to_string_lossy().into_owned());
757         args.push(asm_target.perlasm_format.to_owned());
758         if asm_target.arch == "x86" {
759             args.push("-fPIC".into());
760             args.push("-DOPENSSL_IA32_SSE2".into());
761         }
762         // Work around PerlAsm issue for ARM and AAarch64 targets by replacing
763         // back slashes with forward slashes.
764         let dst = dst
765             .to_str()
766             .expect("Could not convert path")
767             .replace("\\", "/");
768         args.push(dst);
769         run_command_with_args(&get_command("PERL_EXECUTABLE", "perl"), &args);
770     }
771 }
772 
773 fn get_command(var: &'static str, default: &str) -> String {
774     read_env_var(var).unwrap_or_else(|_| default.into())
775 }
776 
777 // TODO: We should emit `cargo:rerun-if-changed-env` for the various
778 // environment variables that affect the build.
779 fn emit_rerun_if_changed() {
780     for path in &["crypto", "include", "third_party/fiat"] {
781         walk_dir(&PathBuf::from(path), &|entry| {
782             let path = entry.path();
783             match path.extension().and_then(|ext| ext.to_str()) {
784                 Some("c") | Some("S") | Some("h") | Some("inl") | Some("pl") | None => {
785                     println!("cargo:rerun-if-changed={}", path.to_str().unwrap());
786                 }
787                 _ => {
788                     // Ignore other types of files.
789                 }
790             }
791         })
792     }
793 }
794 
795 fn walk_dir(dir: &Path, cb: &impl Fn(&DirEntry)) {
796     if dir.is_dir() {
797         for entry in fs::read_dir(dir).unwrap() {
798             let entry = entry.unwrap();
799             let path = entry.path();
800             if path.is_dir() {
801                 walk_dir(&path, cb);
802             } else {
803                 cb(&entry);
804             }
805         }
806     }
807 }
808 
809 fn ring_core_prefix() -> String {
810     let links = std::env::var("CARGO_MANIFEST_LINKS").unwrap();
811 
812     let computed = {
813         let name = std::env::var("CARGO_PKG_NAME").unwrap();
814         let version = std::env::var("CARGO_PKG_VERSION").unwrap();
815         name + "_core_" + &version.replace(&['-', '.'][..], "_")
816     };
817 
818     assert_eq!(links, computed);
819 
820     links + "_"
821 }
822 
823 /// Creates the necessary header file for symbol renaming and returns the path of the
824 /// generated include directory.
825 fn generate_prefix_symbols(
826     target: &Target,
827     out_dir: &Path,
828     prefix: &str,
829 ) -> Result<(), std::io::Error> {
830     generate_prefix_symbols_header(out_dir, "prefix_symbols.h", '#', None, prefix)?;
831 
832     if target.os == "windows" {
833         let _ = generate_prefix_symbols_nasm(out_dir, prefix)?;
834     } else {
835         generate_prefix_symbols_header(
836             out_dir,
837             "prefix_symbols_asm.h",
838             '#',
839             Some("#if defined(__APPLE__)"),
840             prefix,
841         )?;
842     }
843 
844     Ok(())
845 }
846 
847 fn generate_prefix_symbols_nasm(out_dir: &Path, prefix: &str) -> Result<(), std::io::Error> {
848     generate_prefix_symbols_header(
849         out_dir,
850         "prefix_symbols_nasm.inc",
851         '%',
852         Some("%ifidn __OUTPUT_FORMAT__,win32"),
853         prefix,
854     )
855 }
856 
857 fn generate_prefix_symbols_header(
858     out_dir: &Path,
859     filename: &str,
860     pp: char,
861     prefix_condition: Option<&str>,
862     prefix: &str,
863 ) -> Result<(), std::io::Error> {
864     let dir = out_dir.join("ring_core_generated");
865     std::fs::create_dir_all(&dir)?;
866 
867     let path = dir.join(filename);
868     let mut file = std::fs::File::create(&path)?;
869 
870     let filename_ident = filename.replace(".", "_").to_uppercase();
871     writeln!(
872         file,
873         r#"
874 {pp}ifndef ring_core_generated_{filename_ident}
875 {pp}define ring_core_generated_{filename_ident}
876 "#,
877         pp = pp,
878         filename_ident = filename_ident
879     )?;
880 
881     if let Some(prefix_condition) = prefix_condition {
882         writeln!(file, "{}", prefix_condition)?;
883         writeln!(file, "{}", prefix_all_symbols(pp, "_", prefix))?;
884         writeln!(file, "{pp}else", pp = pp)?;
885     };
886     writeln!(file, "{}", prefix_all_symbols(pp, "", prefix))?;
887     if prefix_condition.is_some() {
888         writeln!(file, "{pp}endif", pp = pp)?
889     }
890 
891     writeln!(file, "{pp}endif", pp = pp)?;
892 
893     Ok(())
894 }
895 
896 fn prefix_all_symbols(pp: char, prefix_prefix: &str, prefix: &str) -> String {
897     static SYMBOLS_TO_PREFIX: &[&str] = &[
898         "CRYPTO_poly1305_finish",
899         "CRYPTO_poly1305_finish_neon",
900         "CRYPTO_poly1305_init",
901         "CRYPTO_poly1305_init_neon",
902         "CRYPTO_poly1305_update",
903         "CRYPTO_poly1305_update_neon",
904         "ChaCha20_ctr32",
905         "LIMBS_add_mod",
906         "LIMBS_are_even",
907         "LIMBS_are_zero",
908         "LIMBS_equal",
909         "LIMBS_equal_limb",
910         "LIMBS_less_than",
911         "LIMBS_less_than_limb",
912         "LIMBS_reduce_once",
913         "LIMBS_select_512_32",
914         "LIMBS_shl_mod",
915         "LIMBS_sub_mod",
916         "LIMBS_window5_split_window",
917         "LIMBS_window5_unsplit_window",
918         "LIMB_shr",
919         "OPENSSL_armcap_P",
920         "OPENSSL_cpuid_setup",
921         "OPENSSL_ia32cap_P",
922         "OPENSSL_memcmp",
923         "RSA_padding_check_oaep",
924         "aes_hw_ctr32_encrypt_blocks",
925         "aes_hw_encrypt",
926         "aes_hw_set_encrypt_key",
927         "aes_nohw_ctr32_encrypt_blocks",
928         "aes_nohw_encrypt",
929         "aes_nohw_set_encrypt_key",
930         "aesni_gcm_decrypt",
931         "aesni_gcm_encrypt",
932         "bn_from_montgomery",
933         "bn_from_montgomery_in_place",
934         "bn_gather5",
935         "bn_mul_mont",
936         "bn_mul_mont_gather5",
937         "bn_neg_inv_mod_r_u64",
938         "bn_power5",
939         "bn_scatter5",
940         "bn_sqr8x_internal",
941         "bn_sqrx8x_internal",
942         "bsaes_ctr32_encrypt_blocks",
943         "bssl_constant_time_test_main",
944         "chacha20_poly1305_open",
945         "chacha20_poly1305_seal",
946         "gcm_ghash_avx",
947         "gcm_ghash_clmul",
948         "gcm_ghash_neon",
949         "gcm_gmult_clmul",
950         "gcm_gmult_neon",
951         "gcm_init_avx",
952         "gcm_init_clmul",
953         "gcm_init_neon",
954         "limbs_mul_add_limb",
955         "little_endian_bytes_from_scalar",
956         "nistz256_neg",
957         "nistz256_select_w5",
958         "nistz256_select_w7",
959         "nistz384_point_add",
960         "nistz384_point_double",
961         "nistz384_point_mul",
962         "p256_mul_mont",
963         "p256_point_add",
964         "p256_point_add_affine",
965         "p256_point_double",
966         "p256_point_mul",
967         "p256_point_mul_base",
968         "p256_scalar_mul_mont",
969         "p256_scalar_sqr_rep_mont",
970         "p256_sqr_mont",
971         "p384_elem_div_by_2",
972         "p384_elem_mul_mont",
973         "p384_elem_neg",
974         "p384_elem_sub",
975         "p384_scalar_mul_mont",
976         "poly1305_neon2_addmulmod",
977         "poly1305_neon2_blocks",
978         "sha256_block_data_order",
979         "sha512_block_data_order",
980         "vpaes_ctr32_encrypt_blocks",
981         "vpaes_encrypt",
982         "vpaes_encrypt_key_to_bsaes",
983         "vpaes_set_encrypt_key",
984         "x25519_NEON",
985         "x25519_fe_invert",
986         "x25519_fe_isnegative",
987         "x25519_fe_mul_ttt",
988         "x25519_fe_neg",
989         "x25519_fe_tobytes",
990         "x25519_ge_double_scalarmult_vartime",
991         "x25519_ge_frombytes_vartime",
992         "x25519_ge_scalarmult_base",
993         "x25519_public_from_private_generic_masked",
994         "x25519_sc_mask",
995         "x25519_sc_muladd",
996         "x25519_sc_reduce",
997         "x25519_scalar_mult_generic_masked",
998     ];
999 
1000     let mut out = String::new();
1001 
1002     for symbol in SYMBOLS_TO_PREFIX {
1003         let line = format!(
1004             "{pp}define {prefix_prefix}{symbol} {prefix_prefix}{prefix}{symbol}\n",
1005             pp = pp,
1006             prefix_prefix = prefix_prefix,
1007             prefix = prefix,
1008             symbol = symbol
1009         );
1010         out += &line;
1011     }
1012 
1013     out
1014 }
1015