1045 lines
33 KiB
Rust
1045 lines
33 KiB
Rust
// Copyright 2015-2016 Brian Smith.
|
|
//
|
|
// Permission to use, copy, modify, and/or distribute this software for any
|
|
// purpose with or without fee is hereby granted, provided that the above
|
|
// copyright notice and this permission notice appear in all copies.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
|
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
//! Build the non-Rust components.
|
|
|
|
// It seems like it would be a good idea to use `log!` for logging, but it
|
|
// isn't worth having the external dependencies (one for the `log` crate, and
|
|
// another for the concrete logging implementation). Instead we use `eprintln!`
|
|
// to log everything to stderr.
|
|
|
|
use std::{
|
|
ffi::{OsStr, OsString},
|
|
fs::{self, DirEntry},
|
|
io::Write,
|
|
path::{Path, PathBuf},
|
|
process::{Command, Stdio},
|
|
};
|
|
|
|
mod env {
|
|
use std::ffi::OsString;
|
|
|
|
/// Read an environment variable and tell Cargo that we depend on it.
|
|
///
|
|
/// The name is static since we intend to only read a static set of environment
|
|
/// variables.
|
|
pub fn var_os(name: &'static str) -> Option<OsString> {
|
|
println!("cargo:rerun-if-env-changed={}", name);
|
|
std::env::var_os(name)
|
|
}
|
|
|
|
pub fn var(name: &'static str) -> Option<String> {
|
|
var_os(name).and_then(|value| value.into_string().ok())
|
|
}
|
|
}
|
|
|
|
const X86: &str = "x86";
|
|
const X86_64: &str = "x86_64";
|
|
const AARCH64: &str = "aarch64";
|
|
const ARM: &str = "arm";
|
|
const WASM32: &str = "wasm32";
|
|
|
|
#[rustfmt::skip]
|
|
const RING_SRCS: &[(&[&str], &str)] = &[
|
|
(&[], "crypto/curve25519/curve25519.c"),
|
|
(&[], "crypto/fipsmodule/aes/aes_nohw.c"),
|
|
(&[], "crypto/fipsmodule/bn/montgomery.c"),
|
|
(&[], "crypto/fipsmodule/bn/montgomery_inv.c"),
|
|
(&[], "crypto/fipsmodule/ec/ecp_nistz.c"),
|
|
(&[], "crypto/fipsmodule/ec/gfp_p256.c"),
|
|
(&[], "crypto/fipsmodule/ec/gfp_p384.c"),
|
|
(&[], "crypto/fipsmodule/ec/p256.c"),
|
|
(&[], "crypto/limbs/limbs.c"),
|
|
(&[], "crypto/mem.c"),
|
|
(&[], "crypto/poly1305/poly1305.c"),
|
|
|
|
(&[ARM, X86_64, X86], "crypto/crypto.c"),
|
|
|
|
(&[X86_64, X86], "crypto/cpu_intel.c"),
|
|
|
|
(&[X86], "crypto/fipsmodule/aes/asm/aesni-x86.pl"),
|
|
(&[X86], "crypto/fipsmodule/aes/asm/ghash-x86.pl"),
|
|
(&[X86], "crypto/fipsmodule/aes/asm/vpaes-x86.pl"),
|
|
(&[X86], "crypto/fipsmodule/bn/asm/x86-mont.pl"),
|
|
(&[X86], "crypto/chacha/asm/chacha-x86.pl"),
|
|
|
|
(&[X86_64], "crypto/chacha/asm/chacha-x86_64.pl"),
|
|
(&[X86_64], "crypto/curve25519/curve25519_64_adx.c"),
|
|
(&[X86_64], "crypto/fipsmodule/aes/asm/aes-gcm-avx2-x86_64.pl"),
|
|
(&[X86_64], "crypto/fipsmodule/aes/asm/aesni-gcm-x86_64.pl"),
|
|
(&[X86_64], "crypto/fipsmodule/aes/asm/aesni-x86_64.pl"),
|
|
(&[X86_64], "crypto/fipsmodule/aes/asm/ghash-x86_64.pl"),
|
|
(&[X86_64], "crypto/fipsmodule/aes/asm/vpaes-x86_64.pl"),
|
|
(&[X86_64], "crypto/fipsmodule/bn/asm/x86_64-mont.pl"),
|
|
(&[X86_64], "crypto/fipsmodule/bn/asm/x86_64-mont5.pl"),
|
|
(&[X86_64], "crypto/fipsmodule/ec/asm/p256-x86_64-asm.pl"),
|
|
(&[X86_64], SHA512_X86_64),
|
|
(&[X86_64], "crypto/cipher/asm/chacha20_poly1305_x86_64.pl"),
|
|
(&[X86_64], "third_party/fiat/asm/fiat_curve25519_adx_mul.S"),
|
|
(&[X86_64], "third_party/fiat/asm/fiat_curve25519_adx_square.S"),
|
|
|
|
(&[AARCH64, X86_64], "crypto/fipsmodule/ec/p256-nistz.c"),
|
|
|
|
(&[ARM], "crypto/fipsmodule/aes/asm/bsaes-armv7.pl"),
|
|
(&[ARM], "crypto/fipsmodule/aes/asm/ghash-armv4.pl"),
|
|
(&[ARM], "crypto/fipsmodule/aes/asm/vpaes-armv7.pl"),
|
|
(&[ARM], "crypto/fipsmodule/bn/asm/armv4-mont.pl"),
|
|
(&[ARM], "crypto/chacha/asm/chacha-armv4.pl"),
|
|
(&[ARM], "crypto/curve25519/asm/x25519-asm-arm.S"),
|
|
(&[ARM], "crypto/poly1305/poly1305_arm.c"),
|
|
(&[ARM], "crypto/poly1305/poly1305_arm_asm.S"),
|
|
(&[ARM], "crypto/fipsmodule/sha/asm/sha256-armv4.pl"),
|
|
(&[ARM], "crypto/fipsmodule/sha/asm/sha512-armv4.pl"),
|
|
|
|
(&[AARCH64], "crypto/chacha/asm/chacha-armv8.pl"),
|
|
(&[AARCH64], "crypto/cipher/asm/chacha20_poly1305_armv8.pl"),
|
|
(&[AARCH64], "crypto/fipsmodule/aes/asm/aesv8-armx.pl"),
|
|
(&[AARCH64], "crypto/fipsmodule/aes/asm/aesv8-gcm-armv8.pl"),
|
|
(&[AARCH64], "crypto/fipsmodule/aes/asm/ghash-neon-armv8.pl"),
|
|
(&[AARCH64], "crypto/fipsmodule/aes/asm/ghashv8-armx.pl"),
|
|
(&[AARCH64], "crypto/fipsmodule/aes/asm/vpaes-armv8.pl"),
|
|
(&[AARCH64], "crypto/fipsmodule/bn/asm/armv8-mont.pl"),
|
|
(&[AARCH64], "crypto/fipsmodule/ec/asm/p256-armv8-asm.pl"),
|
|
(&[AARCH64], SHA512_ARMV8),
|
|
];
|
|
|
|
const SHA256_X86_64: &str = "crypto/fipsmodule/sha/asm/sha256-x86_64.pl";
|
|
const SHA512_X86_64: &str = "crypto/fipsmodule/sha/asm/sha512-x86_64.pl";
|
|
|
|
const SHA256_ARMV8: &str = "crypto/fipsmodule/sha/asm/sha256-armv8.pl";
|
|
const SHA512_ARMV8: &str = "crypto/fipsmodule/sha/asm/sha512-armv8.pl";
|
|
|
|
const RING_TEST_SRCS: &[&str] = &[("crypto/constant_time_test.c")];
|
|
|
|
const PREGENERATED: &str = "pregenerated";
|
|
|
|
fn cpp_flags(compiler: &cc::Tool) -> &'static [&'static str] {
|
|
if !compiler.is_like_msvc() {
|
|
static NON_MSVC_FLAGS: &[&str] = &[
|
|
"-fvisibility=hidden",
|
|
"-std=c1x", // GCC 4.6 requires "c1x" instead of "c11"
|
|
"-Wall",
|
|
"-Wbad-function-cast",
|
|
"-Wcast-align",
|
|
"-Wcast-qual",
|
|
"-Wconversion",
|
|
"-Wmissing-field-initializers",
|
|
"-Wmissing-include-dirs",
|
|
"-Wnested-externs",
|
|
"-Wredundant-decls",
|
|
"-Wshadow",
|
|
"-Wsign-compare",
|
|
"-Wsign-conversion",
|
|
"-Wstrict-prototypes",
|
|
"-Wundef",
|
|
"-Wuninitialized",
|
|
];
|
|
NON_MSVC_FLAGS
|
|
} else {
|
|
static MSVC_FLAGS: &[&str] = &[
|
|
"/Gy", // Enable function-level linking.
|
|
"/Zc:wchar_t",
|
|
"/Zc:forScope",
|
|
"/Zc:inline",
|
|
// Warnings.
|
|
"/Wall",
|
|
"/wd4127", // C4127: conditional expression is constant
|
|
"/wd4464", // C4464: relative include path contains '..'
|
|
"/wd4514", // C4514: <name>: unreferenced inline function has be
|
|
"/wd4710", // C4710: function not inlined
|
|
"/wd4711", // C4711: function 'function' selected for inline expansion
|
|
"/wd4820", // C4820: <struct>: <n> bytes padding added after <name>
|
|
"/wd5045", /* C5045: Compiler will insert Spectre mitigation for memory load if
|
|
* /Qspectre switch specified */
|
|
];
|
|
MSVC_FLAGS
|
|
}
|
|
}
|
|
|
|
// None means "any OS" or "any target". The first match in sequence order is
|
|
// taken.
|
|
const ASM_TARGETS: &[AsmTarget] = &[
|
|
AsmTarget {
|
|
oss: LINUX_ABI,
|
|
arch: AARCH64,
|
|
perlasm_format: "linux64",
|
|
},
|
|
AsmTarget {
|
|
oss: LINUX_ABI,
|
|
arch: ARM,
|
|
perlasm_format: "linux32",
|
|
},
|
|
AsmTarget {
|
|
oss: LINUX_ABI,
|
|
arch: X86,
|
|
perlasm_format: "elf",
|
|
},
|
|
AsmTarget {
|
|
oss: LINUX_ABI,
|
|
arch: X86_64,
|
|
perlasm_format: "elf",
|
|
},
|
|
AsmTarget {
|
|
oss: &["horizon"],
|
|
arch: ARM,
|
|
perlasm_format: "linux32",
|
|
},
|
|
AsmTarget {
|
|
oss: APPLE_ABI,
|
|
arch: AARCH64,
|
|
perlasm_format: "ios64",
|
|
},
|
|
AsmTarget {
|
|
oss: APPLE_ABI,
|
|
arch: X86_64,
|
|
perlasm_format: "macosx",
|
|
},
|
|
AsmTarget {
|
|
oss: &[WINDOWS],
|
|
arch: X86,
|
|
perlasm_format: WIN32N,
|
|
},
|
|
AsmTarget {
|
|
oss: &[WINDOWS],
|
|
arch: X86_64,
|
|
perlasm_format: NASM,
|
|
},
|
|
AsmTarget {
|
|
oss: &[WINDOWS],
|
|
arch: AARCH64,
|
|
perlasm_format: "win64",
|
|
},
|
|
];
|
|
|
|
struct AsmTarget {
|
|
/// Operating systems.
|
|
oss: &'static [&'static str],
|
|
|
|
/// Architectures.
|
|
arch: &'static str,
|
|
|
|
/// The PerlAsm format name.
|
|
perlasm_format: &'static str,
|
|
}
|
|
|
|
impl AsmTarget {
|
|
fn use_nasm(&self) -> bool {
|
|
[WIN32N, NASM].contains(&self.perlasm_format)
|
|
}
|
|
}
|
|
|
|
/// Operating systems that have the same ABI as Linux on every architecture
|
|
/// mentioned in `ASM_TARGETS`.
|
|
const LINUX_ABI: &[&str] = &[
|
|
"android",
|
|
"dragonfly",
|
|
"freebsd",
|
|
"fuchsia",
|
|
"haiku",
|
|
"hurd",
|
|
"illumos",
|
|
"netbsd",
|
|
"openbsd",
|
|
"linux",
|
|
"redox",
|
|
"solaris",
|
|
];
|
|
|
|
const WIN32N: &str = "win32n";
|
|
const NASM: &str = "nasm";
|
|
|
|
/// Operating systems that have the same ABI as macOS on every architecture
|
|
/// mentioned in `ASM_TARGETS`.
|
|
const APPLE_ABI: &[&str] = &["ios", "macos", "tvos", "visionos", "watchos"];
|
|
|
|
const WINDOWS: &str = "windows";
|
|
|
|
fn main() {
|
|
// Avoid assuming the working directory is the same is the $CARGO_MANIFEST_DIR so that toolchains
|
|
// which may assume other working directories can still build this code.
|
|
let c_root_dir = PathBuf::from(
|
|
env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR should always be set"),
|
|
);
|
|
|
|
// Keep in sync with `core_name_and_version!` in prefixed.rs.
|
|
let core_name_and_version = [
|
|
&env::var("CARGO_PKG_NAME").unwrap(),
|
|
"core",
|
|
&env::var("CARGO_PKG_VERSION_MAJOR").unwrap(),
|
|
&env::var("CARGO_PKG_VERSION_MINOR").unwrap(),
|
|
&env::var("CARGO_PKG_VERSION_PATCH").unwrap(),
|
|
&env::var("CARGO_PKG_VERSION_PRE").unwrap(), // Often empty
|
|
]
|
|
.join("_");
|
|
// Ensure `links` in Cargo.toml is consistent with the version.
|
|
assert_eq!(
|
|
&env::var("CARGO_MANIFEST_LINKS").unwrap(),
|
|
&core_name_and_version
|
|
);
|
|
|
|
const RING_PREGENERATE_ASM: &str = "RING_PREGENERATE_ASM";
|
|
match env::var_os(RING_PREGENERATE_ASM).as_deref() {
|
|
Some(s) if s == "1" => {
|
|
pregenerate_asm_main(&c_root_dir, &core_name_and_version);
|
|
}
|
|
None => ring_build_rs_main(&c_root_dir, &core_name_and_version),
|
|
_ => {
|
|
panic!("${} has an invalid value", RING_PREGENERATE_ASM);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn ring_build_rs_main(c_root_dir: &Path, core_name_and_version: &str) {
|
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
|
let out_dir = PathBuf::from(out_dir);
|
|
|
|
let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
|
let os = env::var("CARGO_CFG_TARGET_OS").unwrap();
|
|
let env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
|
|
let endian = env::var("CARGO_CFG_TARGET_ENDIAN").unwrap();
|
|
let is_little_endian = endian == "little";
|
|
|
|
let is_git = fs::metadata(c_root_dir.join(".git")).is_ok();
|
|
|
|
// Published builds are always built in release mode.
|
|
let is_debug = is_git && env::var("DEBUG").unwrap() != "false";
|
|
|
|
// During local development, force warnings in non-Rust code to be treated
|
|
// as errors. Since warnings are highly compiler-dependent and compilers
|
|
// don't maintain backward compatibility w.r.t. which warnings they issue,
|
|
// don't do this for packaged builds.
|
|
let force_warnings_into_errors = is_git;
|
|
|
|
let target = Target {
|
|
arch,
|
|
os,
|
|
env,
|
|
is_debug,
|
|
force_warnings_into_errors,
|
|
};
|
|
|
|
let asm_target = if is_little_endian {
|
|
ASM_TARGETS.iter().find(|asm_target| {
|
|
asm_target.arch == target.arch && asm_target.oss.contains(&target.os.as_ref())
|
|
})
|
|
} else {
|
|
None
|
|
};
|
|
|
|
// If `.git` exists then assume this is the "local hacking" case where
|
|
// we want to make it easy to build *ring* using `cargo build`/`cargo test`
|
|
// without a prerequisite `package` step, at the cost of needing additional
|
|
// tools like `Perl` and/or `nasm`.
|
|
//
|
|
// If `.git` doesn't exist then assume that this is a packaged build where
|
|
// we want to optimize for minimizing the build tools required: No Perl,
|
|
// no nasm, etc.
|
|
let generated_dir = if !is_git {
|
|
c_root_dir.join(PREGENERATED)
|
|
} else {
|
|
generate_sources_and_preassemble(
|
|
&out_dir,
|
|
asm_target.into_iter(),
|
|
c_root_dir,
|
|
core_name_and_version,
|
|
);
|
|
out_dir.clone()
|
|
};
|
|
|
|
build_c_code(
|
|
asm_target,
|
|
&target,
|
|
&generated_dir,
|
|
c_root_dir,
|
|
&out_dir,
|
|
core_name_and_version,
|
|
);
|
|
emit_rerun_if_changed()
|
|
}
|
|
|
|
fn pregenerate_asm_main(c_root_dir: &Path, core_name_and_version: &str) {
|
|
let pregenerated = c_root_dir.join(PREGENERATED);
|
|
fs::create_dir(&pregenerated).unwrap();
|
|
generate_sources_and_preassemble(
|
|
&pregenerated,
|
|
ASM_TARGETS.iter(),
|
|
c_root_dir,
|
|
core_name_and_version,
|
|
);
|
|
}
|
|
|
|
fn generate_sources_and_preassemble<'a>(
|
|
out_dir: &Path,
|
|
asm_targets: impl Iterator<Item = &'a AsmTarget>,
|
|
c_root_dir: &Path,
|
|
core_name_and_version: &str,
|
|
) {
|
|
generate_prefix_symbols_headers(out_dir, core_name_and_version).unwrap();
|
|
|
|
let perl_exe = get_perl_exe();
|
|
|
|
for asm_target in asm_targets {
|
|
let perlasm_src_dsts = perlasm_src_dsts(out_dir, asm_target);
|
|
perlasm(&perl_exe, &perlasm_src_dsts, asm_target, c_root_dir);
|
|
|
|
if asm_target.use_nasm() {
|
|
// Package pregenerated object files in addition to pregenerated
|
|
// assembly language source files, so that the user doesn't need
|
|
// to install the assembler.
|
|
let srcs = asm_srcs(perlasm_src_dsts);
|
|
for src in srcs {
|
|
nasm(&src, asm_target.arch, out_dir, out_dir, c_root_dir);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct Target {
|
|
arch: String,
|
|
os: String,
|
|
env: String,
|
|
|
|
/// Is this a debug build? This affects whether assertions might be enabled
|
|
/// in the C code. For packaged builds, this should always be `false`.
|
|
is_debug: bool,
|
|
|
|
/// true: Force warnings to be treated as errors.
|
|
/// false: Use the default behavior (perhaps determined by `$CFLAGS`, etc.)
|
|
force_warnings_into_errors: bool,
|
|
}
|
|
|
|
fn build_c_code(
|
|
asm_target: Option<&AsmTarget>,
|
|
target: &Target,
|
|
generated_dir: &Path,
|
|
c_root_dir: &Path,
|
|
out_dir: &Path,
|
|
core_name_and_version: &str,
|
|
) {
|
|
let (asm_srcs, obj_srcs) = if let Some(asm_target) = asm_target {
|
|
let perlasm_src_dsts = perlasm_src_dsts(generated_dir, asm_target);
|
|
|
|
let asm_srcs = asm_srcs(perlasm_src_dsts);
|
|
|
|
if asm_target.use_nasm() {
|
|
// Nasm was already used to generate the object files, so use them instead of
|
|
// assembling.
|
|
let obj_srcs = asm_srcs
|
|
.iter()
|
|
.map(|src| obj_path(generated_dir, src.as_path()))
|
|
.collect::<Vec<_>>();
|
|
(vec![], obj_srcs)
|
|
} else {
|
|
(asm_srcs, vec![])
|
|
}
|
|
} else {
|
|
(vec![], vec![])
|
|
};
|
|
|
|
let core_srcs = sources_for_arch(&target.arch)
|
|
.into_iter()
|
|
.filter(|p| !is_perlasm(p))
|
|
.filter(|p| {
|
|
if let Some(extension) = p.extension() {
|
|
// We don't (and can't) use any .S on Windows since MSVC and NASM can't assemble
|
|
// them.
|
|
if extension == "S"
|
|
&& (target.arch == X86_64 || target.arch == X86)
|
|
&& target.os == WINDOWS
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
true
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
let test_srcs = RING_TEST_SRCS.iter().map(PathBuf::from).collect::<Vec<_>>();
|
|
|
|
let libs = [
|
|
(
|
|
core_name_and_version,
|
|
&core_srcs[..],
|
|
&asm_srcs[..],
|
|
&obj_srcs[..],
|
|
),
|
|
(
|
|
&(String::from(core_name_and_version) + "_test"),
|
|
&test_srcs[..],
|
|
&[],
|
|
&[],
|
|
),
|
|
];
|
|
|
|
// XXX: Ideally, ring-test would only be built for `cargo test`, but Cargo
|
|
// can't do that yet.
|
|
libs.iter()
|
|
.for_each(|&(lib_name, srcs, asm_srcs, obj_srcs)| {
|
|
let srcs = srcs.iter().chain(asm_srcs);
|
|
build_library(
|
|
target,
|
|
c_root_dir,
|
|
out_dir,
|
|
lib_name,
|
|
srcs,
|
|
generated_dir,
|
|
obj_srcs,
|
|
)
|
|
});
|
|
|
|
println!(
|
|
"cargo:rustc-link-search=native={}",
|
|
out_dir.to_str().expect("Invalid path")
|
|
);
|
|
}
|
|
|
|
fn new_build(target: &Target, c_root_dir: &Path, include_dir: &Path) -> cc::Build {
|
|
let mut b = cc::Build::new();
|
|
configure_cc(&mut b, target, c_root_dir, include_dir);
|
|
b
|
|
}
|
|
|
|
fn build_library<'a>(
|
|
target: &Target,
|
|
c_root_dir: &Path,
|
|
out_dir: &Path,
|
|
lib_name: &str,
|
|
srcs: impl Iterator<Item = &'a PathBuf>,
|
|
include_dir: &Path,
|
|
preassembled_objs: &[PathBuf],
|
|
) {
|
|
let mut c = new_build(target, c_root_dir, include_dir);
|
|
|
|
// Compile all the (dirty) source files into object files.
|
|
srcs.for_each(|src| {
|
|
c.file(c_root_dir.join(src));
|
|
});
|
|
|
|
preassembled_objs.iter().for_each(|obj| {
|
|
c.object(obj);
|
|
});
|
|
|
|
// Rebuild the library if necessary.
|
|
let lib_path = PathBuf::from(out_dir).join(format!("lib{}.a", lib_name));
|
|
|
|
// Handled below.
|
|
let _ = c.cargo_metadata(false);
|
|
|
|
c.compile(
|
|
lib_path
|
|
.file_name()
|
|
.and_then(|f| f.to_str())
|
|
.expect("No filename"),
|
|
);
|
|
|
|
// Link the library. This works even when the library doesn't need to be
|
|
// rebuilt.
|
|
println!("cargo:rustc-link-lib=static={}", lib_name);
|
|
}
|
|
|
|
fn obj_path(out_dir: &Path, src: &Path) -> PathBuf {
|
|
let mut out_path = out_dir.join(src.file_name().unwrap());
|
|
// To eliminate unnecessary conditional logic, use ".o" as the extension,
|
|
// even when the compiler (e.g. MSVC) would normally use something else
|
|
// (e.g. ".obj"). cc-rs seems to do the same.
|
|
assert!(out_path.set_extension("o"));
|
|
out_path
|
|
}
|
|
|
|
fn configure_cc(c: &mut cc::Build, target: &Target, c_root_dir: &Path, include_dir: &Path) {
|
|
let compiler = c.get_compiler();
|
|
// FIXME: On Windows AArch64 we currently must use Clang to compile C code
|
|
let compiler = if target.os == WINDOWS && target.arch == AARCH64 && !compiler.is_like_clang() {
|
|
let _ = c.compiler("clang");
|
|
c.get_compiler()
|
|
} else {
|
|
compiler
|
|
};
|
|
|
|
let _ = c.include(c_root_dir.join("include"));
|
|
let _ = c.include(include_dir);
|
|
for f in cpp_flags(&compiler) {
|
|
let _ = c.flag(f);
|
|
}
|
|
|
|
if APPLE_ABI.contains(&target.os.as_str()) {
|
|
// ``-gfull`` is required for Darwin's |-dead_strip|.
|
|
let _ = c.flag("-gfull");
|
|
} else if !compiler.is_like_msvc() {
|
|
let _ = c.flag("-g3");
|
|
};
|
|
|
|
if !target.is_debug {
|
|
let _ = c.define("NDEBUG", None);
|
|
}
|
|
|
|
if target.arch == X86 {
|
|
let is_msvc_not_clang_cl = compiler.is_like_msvc() && !compiler.is_like_clang_cl();
|
|
if !is_msvc_not_clang_cl {
|
|
let _ = c.flag("-msse2");
|
|
}
|
|
}
|
|
|
|
// Allow cross-compiling without a target sysroot for these targets.
|
|
if (target.arch == WASM32)
|
|
|| (target.os == "linux" && target.env == "musl" && target.arch != X86_64)
|
|
{
|
|
// TODO: Expand this to non-clang compilers in 0.17.0 if practical.
|
|
if compiler.is_like_clang() {
|
|
let _ = c.flag("-nostdlibinc");
|
|
let _ = c.define("RING_CORE_NOSTDLIBINC", "1");
|
|
}
|
|
}
|
|
|
|
if target.force_warnings_into_errors {
|
|
c.warnings_into_errors(true);
|
|
}
|
|
}
|
|
|
|
fn nasm(file: &Path, arch: &str, include_dir: &Path, out_dir: &Path, c_root_dir: &Path) {
|
|
let out_file = obj_path(out_dir, file);
|
|
let oformat = match arch {
|
|
x if x == X86_64 => "win64",
|
|
x if x == X86 => "win32",
|
|
_ => panic!("unsupported arch: {}", arch),
|
|
};
|
|
|
|
// Nasm requires that the path end in a path separator.
|
|
let mut include_dir = include_dir.as_os_str().to_os_string();
|
|
include_dir.push(OsString::from(String::from(std::path::MAIN_SEPARATOR)));
|
|
|
|
let mut c = Command::new("./target/tools/windows/nasm/nasm");
|
|
let _ = c
|
|
.arg("-o")
|
|
.arg(out_file.to_str().expect("Invalid path"))
|
|
.arg("-f")
|
|
.arg(oformat)
|
|
.arg("-i")
|
|
.arg("include/")
|
|
.arg("-i")
|
|
.arg(include_dir)
|
|
.arg("-Xgnu")
|
|
.arg("-gcv8")
|
|
.arg(c_root_dir.join(file));
|
|
run_command(c);
|
|
}
|
|
|
|
fn run_command_with_args(command_name: &Path, args: &[OsString]) {
|
|
let mut cmd = Command::new(command_name);
|
|
let _ = cmd.args(args);
|
|
run_command(cmd)
|
|
}
|
|
|
|
fn run_command(mut cmd: Command) {
|
|
eprintln!("running {:?}", cmd);
|
|
cmd.stderr(Stdio::inherit());
|
|
let status = cmd.status().unwrap_or_else(|e| {
|
|
panic!("failed to execute [{:?}]: {}", cmd, e);
|
|
});
|
|
if !status.success() {
|
|
panic!("execution failed");
|
|
}
|
|
}
|
|
|
|
fn sources_for_arch(arch: &str) -> Vec<PathBuf> {
|
|
RING_SRCS
|
|
.iter()
|
|
.filter(|&&(archs, _)| archs.is_empty() || archs.contains(&arch))
|
|
.map(|&(_, p)| PathBuf::from(p))
|
|
.collect::<Vec<_>>()
|
|
}
|
|
|
|
fn perlasm_src_dsts(out_dir: &Path, asm_target: &AsmTarget) -> Vec<(PathBuf, PathBuf)> {
|
|
let srcs = sources_for_arch(asm_target.arch);
|
|
let mut src_dsts = srcs
|
|
.iter()
|
|
.filter(|p| is_perlasm(p))
|
|
.map(|src| (src.clone(), asm_path(out_dir, src, asm_target)))
|
|
.collect::<Vec<_>>();
|
|
|
|
// Some PerlAsm source files need to be run multiple times with different
|
|
// output paths.
|
|
{
|
|
// Appease the borrow checker.
|
|
let mut maybe_synthesize = |concrete, synthesized| {
|
|
let concrete_path = PathBuf::from(concrete);
|
|
if srcs.contains(&concrete_path) {
|
|
let synthesized_path = PathBuf::from(synthesized);
|
|
src_dsts.push((
|
|
concrete_path,
|
|
asm_path(out_dir, &synthesized_path, asm_target),
|
|
))
|
|
}
|
|
};
|
|
maybe_synthesize(SHA512_X86_64, SHA256_X86_64);
|
|
maybe_synthesize(SHA512_ARMV8, SHA256_ARMV8);
|
|
}
|
|
|
|
src_dsts
|
|
}
|
|
|
|
fn asm_srcs(perlasm_src_dsts: Vec<(PathBuf, PathBuf)>) -> Vec<PathBuf> {
|
|
perlasm_src_dsts
|
|
.into_iter()
|
|
.map(|(_src, dst)| dst)
|
|
.collect::<Vec<_>>()
|
|
}
|
|
|
|
fn is_perlasm(path: &Path) -> bool {
|
|
path.extension().unwrap().to_str().unwrap() == "pl"
|
|
}
|
|
|
|
fn asm_path(out_dir: &Path, src: &Path, asm_target: &AsmTarget) -> PathBuf {
|
|
let src_stem = src.file_stem().expect("source file without basename");
|
|
|
|
let dst_stem = src_stem.to_str().unwrap();
|
|
let dst_filename = format!("{}-{}", dst_stem, asm_target.perlasm_format);
|
|
let extension = if asm_target.use_nasm() { "asm" } else { "S" };
|
|
out_dir.join(dst_filename).with_extension(extension)
|
|
}
|
|
|
|
fn perlasm(
|
|
perl_exe: &Path,
|
|
src_dst: &[(PathBuf, PathBuf)],
|
|
asm_target: &AsmTarget,
|
|
c_root_dir: &Path,
|
|
) {
|
|
for (src, dst) in src_dst {
|
|
let mut args = vec![
|
|
join_components_with_forward_slashes(&c_root_dir.join(src)),
|
|
asm_target.perlasm_format.into(),
|
|
];
|
|
if asm_target.arch == X86 {
|
|
args.push("-fPIC".into());
|
|
}
|
|
// Work around PerlAsm issue for ARM and AAarch64 targets by replacing
|
|
// back slashes with forward slashes.
|
|
args.push(join_components_with_forward_slashes(dst));
|
|
run_command_with_args(perl_exe, &args);
|
|
}
|
|
}
|
|
|
|
fn join_components_with_forward_slashes(path: &Path) -> OsString {
|
|
let parts = path.components().map(|c| c.as_os_str()).collect::<Vec<_>>();
|
|
parts.join(OsStr::new("/"))
|
|
}
|
|
|
|
fn get_perl_exe() -> PathBuf {
|
|
get_command("PERL_EXECUTABLE", "perl")
|
|
}
|
|
|
|
fn get_command(var: &'static str, default: &str) -> PathBuf {
|
|
PathBuf::from(env::var_os(var).unwrap_or_else(|| default.into()))
|
|
}
|
|
|
|
// TODO: We should emit `cargo:rerun-if-changed-env` for the various
|
|
// environment variables that affect the build.
|
|
fn emit_rerun_if_changed() {
|
|
for path in &["crypto", "include", "third_party/fiat"] {
|
|
walk_dir(&PathBuf::from(path), &|entry| {
|
|
let path = entry.path();
|
|
match path.extension().and_then(|ext| ext.to_str()) {
|
|
Some("c") | Some("S") | Some("h") | Some("inl") | Some("pl") | None => {
|
|
println!("cargo:rerun-if-changed={}", path.to_str().unwrap());
|
|
}
|
|
_ => {
|
|
// Ignore other types of files.
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
fn walk_dir(dir: &Path, cb: &impl Fn(&DirEntry)) {
|
|
if dir.is_dir() {
|
|
for entry in fs::read_dir(dir).unwrap() {
|
|
let entry = entry.unwrap();
|
|
let path = entry.path();
|
|
if path.is_dir() {
|
|
walk_dir(&path, cb);
|
|
} else {
|
|
cb(&entry);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Creates the necessary header files for symbol renaming.
|
|
///
|
|
/// For simplicity, both non-Nasm- and Nasm- style headers are always
|
|
/// generated, even though local non-packaged builds need only one of them.
|
|
fn generate_prefix_symbols_headers(
|
|
out_dir: &Path,
|
|
core_name_and_version: &str,
|
|
) -> Result<(), std::io::Error> {
|
|
let prefix = &(String::from(core_name_and_version) + "_");
|
|
|
|
generate_prefix_symbols_header(out_dir, "prefix_symbols.h", '#', None, prefix)?;
|
|
|
|
generate_prefix_symbols_header(
|
|
out_dir,
|
|
"prefix_symbols_asm.h",
|
|
'#',
|
|
Some("#if defined(__APPLE__)"),
|
|
prefix,
|
|
)?;
|
|
|
|
generate_prefix_symbols_header(
|
|
out_dir,
|
|
"prefix_symbols_nasm.inc",
|
|
'%',
|
|
Some("%ifidn __OUTPUT_FORMAT__,win32"),
|
|
prefix,
|
|
)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn generate_prefix_symbols_header(
|
|
out_dir: &Path,
|
|
filename: &str,
|
|
pp: char,
|
|
prefix_condition: Option<&str>,
|
|
prefix: &str,
|
|
) -> Result<(), std::io::Error> {
|
|
let dir = out_dir.join("ring_core_generated");
|
|
fs::create_dir_all(&dir)?;
|
|
|
|
let path = dir.join(filename);
|
|
let mut file = fs::File::create(path)?;
|
|
|
|
let filename_ident = filename.replace('.', "_").to_uppercase();
|
|
writeln!(
|
|
file,
|
|
r#"
|
|
{pp}ifndef ring_core_generated_{filename_ident}
|
|
{pp}define ring_core_generated_{filename_ident}
|
|
"#,
|
|
pp = pp,
|
|
filename_ident = filename_ident
|
|
)?;
|
|
|
|
if let Some(prefix_condition) = prefix_condition {
|
|
writeln!(file, "{}", prefix_condition)?;
|
|
writeln!(file, "{}", prefix_all_symbols(pp, "_", prefix))?;
|
|
writeln!(file, "{pp}else", pp = pp)?;
|
|
};
|
|
writeln!(file, "{}", prefix_all_symbols(pp, "", prefix))?;
|
|
if prefix_condition.is_some() {
|
|
writeln!(file, "{pp}endif", pp = pp)?
|
|
}
|
|
|
|
writeln!(file, "{pp}endif", pp = pp)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn prefix_all_symbols(pp: char, prefix_prefix: &str, prefix: &str) -> String {
|
|
// Rename some nistz256 assembly functions to match the names of their
|
|
// polyfills.
|
|
static SYMBOLS_TO_RENAME: &[(&str, &str)] = &[
|
|
("ecp_nistz256_point_double", "p256_point_double"),
|
|
("ecp_nistz256_point_add", "p256_point_add"),
|
|
("ecp_nistz256_point_add_affine", "p256_point_add_affine"),
|
|
("ecp_nistz256_ord_mul_mont", "p256_scalar_mul_mont"),
|
|
("ecp_nistz256_ord_sqr_mont", "p256_scalar_sqr_rep_mont"),
|
|
("ecp_nistz256_mul_mont", "p256_mul_mont"),
|
|
("ecp_nistz256_sqr_mont", "p256_sqr_mont"),
|
|
];
|
|
|
|
static SYMBOLS_TO_PREFIX: &[&str] = &[
|
|
"adx_bmi2_available",
|
|
"avx2_available",
|
|
"CRYPTO_memcmp",
|
|
"CRYPTO_poly1305_finish",
|
|
"CRYPTO_poly1305_finish_neon",
|
|
"CRYPTO_poly1305_init",
|
|
"CRYPTO_poly1305_init_neon",
|
|
"CRYPTO_poly1305_update",
|
|
"CRYPTO_poly1305_update_neon",
|
|
"ChaCha20_ctr32",
|
|
"ChaCha20_ctr32_avx2",
|
|
"ChaCha20_ctr32_neon",
|
|
"ChaCha20_ctr32_nohw",
|
|
"ChaCha20_ctr32_ssse3",
|
|
"ChaCha20_ctr32_ssse3_4x",
|
|
"LIMB_is_zero",
|
|
"LIMBS_add_mod",
|
|
"LIMBS_are_zero",
|
|
"LIMBS_equal",
|
|
"LIMBS_less_than",
|
|
"LIMBS_reduce_once",
|
|
"LIMBS_select_512_32",
|
|
"LIMBS_shl_mod",
|
|
"LIMBS_sub_mod",
|
|
"LIMBS_window5_split_window",
|
|
"LIMBS_window5_unsplit_window",
|
|
"LIMB_shr",
|
|
"OPENSSL_cpuid_setup",
|
|
"aes_gcm_dec_kernel",
|
|
"aes_gcm_dec_update_vaes_avx2",
|
|
"aes_gcm_enc_kernel",
|
|
"aes_gcm_enc_update_vaes_avx2",
|
|
"aes_hw_ctr32_encrypt_blocks",
|
|
"aes_hw_set_encrypt_key",
|
|
"aes_hw_set_encrypt_key_alt",
|
|
"aes_hw_set_encrypt_key_base",
|
|
"aes_nohw_ctr32_encrypt_blocks",
|
|
"aes_nohw_encrypt",
|
|
"aes_nohw_set_encrypt_key",
|
|
"aesni_gcm_decrypt",
|
|
"aesni_gcm_encrypt",
|
|
"bn_from_montgomery_in_place",
|
|
"bn_gather5",
|
|
"bn_mul_mont",
|
|
"bn_mul_mont_nohw",
|
|
"bn_mul4x_mont",
|
|
"bn_mulx4x_mont",
|
|
"bn_mul8x_mont_neon",
|
|
"bn_mul4x_mont_gather5",
|
|
"bn_mulx4x_mont_gather5",
|
|
"bn_neg_inv_mod_r_u64",
|
|
"bn_power5_nohw",
|
|
"bn_powerx5",
|
|
"bn_scatter5",
|
|
"bn_sqr8x_internal",
|
|
"bn_sqr8x_mont",
|
|
"bn_sqrx8x_internal",
|
|
"bsaes_ctr32_encrypt_blocks",
|
|
"bssl_constant_time_test_conditional_memcpy",
|
|
"bssl_constant_time_test_conditional_memxor",
|
|
"bssl_constant_time_test_main",
|
|
"chacha20_poly1305_open",
|
|
"chacha20_poly1305_open_avx2",
|
|
"chacha20_poly1305_open_sse41",
|
|
"chacha20_poly1305_seal",
|
|
"chacha20_poly1305_seal_avx2",
|
|
"chacha20_poly1305_seal_sse41",
|
|
"ecp_nistz256_mul_mont_adx",
|
|
"ecp_nistz256_mul_mont_nohw",
|
|
"ecp_nistz256_ord_mul_mont_adx",
|
|
"ecp_nistz256_ord_mul_mont_nohw",
|
|
"ecp_nistz256_ord_sqr_mont_adx",
|
|
"ecp_nistz256_ord_sqr_mont_nohw",
|
|
"ecp_nistz256_point_add_adx",
|
|
"ecp_nistz256_point_add_nohw",
|
|
"ecp_nistz256_point_add_affine_adx",
|
|
"ecp_nistz256_point_add_affine_nohw",
|
|
"ecp_nistz256_point_double_adx",
|
|
"ecp_nistz256_point_double_nohw",
|
|
"ecp_nistz256_select_w5_avx2",
|
|
"ecp_nistz256_select_w5_nohw",
|
|
"ecp_nistz256_select_w7_avx2",
|
|
"ecp_nistz256_select_w7_nohw",
|
|
"ecp_nistz256_sqr_mont_adx",
|
|
"ecp_nistz256_sqr_mont_nohw",
|
|
"fiat_curve25519_adx_mul",
|
|
"fiat_curve25519_adx_square",
|
|
"gcm_ghash_avx",
|
|
"gcm_ghash_clmul",
|
|
"gcm_ghash_neon",
|
|
"gcm_ghash_vpclmulqdq_avx2_1",
|
|
"gcm_gmult_clmul",
|
|
"gcm_gmult_neon",
|
|
"gcm_init_avx",
|
|
"gcm_init_clmul",
|
|
"gcm_init_neon",
|
|
"gcm_init_vpclmulqdq_avx2",
|
|
"k25519Precomp",
|
|
"limbs_mul_add_limb",
|
|
"little_endian_bytes_from_scalar",
|
|
"ecp_nistz256_neg",
|
|
"ecp_nistz256_select_w5",
|
|
"ecp_nistz256_select_w7",
|
|
"neon_available",
|
|
"p256_mul_mont",
|
|
"p256_point_add",
|
|
"p256_point_add_affine",
|
|
"p256_point_double",
|
|
"p256_point_mul",
|
|
"p256_point_mul_base",
|
|
"p256_point_mul_base_vartime",
|
|
"p256_scalar_mul_mont",
|
|
"p256_scalar_sqr_rep_mont",
|
|
"p256_sqr_mont",
|
|
"p384_elem_div_by_2",
|
|
"p384_elem_mul_mont",
|
|
"p384_elem_neg",
|
|
"p384_elem_sub",
|
|
"p384_point_add",
|
|
"p384_point_double",
|
|
"p384_point_mul",
|
|
"p384_scalar_mul_mont",
|
|
"openssl_poly1305_neon2_addmulmod",
|
|
"openssl_poly1305_neon2_blocks",
|
|
"sha256_block_data_order",
|
|
"sha256_block_data_order_avx",
|
|
"sha256_block_data_order_ssse3",
|
|
"sha256_block_data_order_hw",
|
|
"sha256_block_data_order_neon",
|
|
"sha256_block_data_order_nohw",
|
|
"sha512_block_data_order",
|
|
"sha512_block_data_order_avx",
|
|
"sha512_block_data_order_hw",
|
|
"sha512_block_data_order_neon",
|
|
"sha512_block_data_order_nohw",
|
|
"vpaes_ctr32_encrypt_blocks",
|
|
"vpaes_encrypt",
|
|
"vpaes_encrypt_key_to_bsaes",
|
|
"vpaes_set_encrypt_key",
|
|
"x25519_NEON",
|
|
"x25519_fe_invert",
|
|
"x25519_fe_isnegative",
|
|
"x25519_fe_mul_ttt",
|
|
"x25519_fe_neg",
|
|
"x25519_fe_tobytes",
|
|
"x25519_ge_double_scalarmult_vartime",
|
|
"x25519_ge_frombytes_vartime",
|
|
"x25519_ge_scalarmult_base",
|
|
"x25519_ge_scalarmult_base_adx",
|
|
"x25519_public_from_private_generic_masked",
|
|
"x25519_sc_mask",
|
|
"x25519_sc_muladd",
|
|
"x25519_sc_reduce",
|
|
"x25519_scalar_mult_adx",
|
|
"x25519_scalar_mult_generic_masked",
|
|
];
|
|
|
|
let mut out = String::new();
|
|
|
|
for (old, new) in SYMBOLS_TO_RENAME {
|
|
let line = format!(
|
|
"{pp}define {prefix_prefix}{old} {prefix_prefix}{new}\n",
|
|
pp = pp,
|
|
prefix_prefix = prefix_prefix,
|
|
old = old,
|
|
new = new
|
|
);
|
|
out += &line;
|
|
}
|
|
|
|
for symbol in SYMBOLS_TO_PREFIX {
|
|
let line = format!(
|
|
"{pp}define {prefix_prefix}{symbol} {prefix_prefix}{prefix}{symbol}\n",
|
|
pp = pp,
|
|
prefix_prefix = prefix_prefix,
|
|
prefix = prefix,
|
|
symbol = symbol
|
|
);
|
|
out += &line;
|
|
}
|
|
|
|
out
|
|
}
|