mod apple_aarch64;
mod apple_x86_64;
mod linux_aarch64;
mod linux_arm;
mod linux_ppc64le;
mod linux_x86;
mod linux_x86_64;
mod universal;
mod win_aarch64;
mod win_x86;
mod win_x86_64;
use crate::nasm_builder::NasmBuilder;
use crate::{
cargo_env, compiler_is_cl_like, emit_warning, env_var_to_bool, execute_command, find_clang_cl,
get_crate_cc, get_crate_cflags, get_crate_cxx, is_cross_compiling, is_link_whole_archive,
is_no_asm, out_dir, requested_c_std, set_env_for_target, should_build_jitter_entropy, target,
target_arch, target_env, target_is_msvc, target_os, target_vendor, CStdRequested, EnvGuard,
OutputLibType,
};
use std::cell::Cell;
use std::collections::HashMap;
use std::env;
use std::path::PathBuf;
#[non_exhaustive]
#[derive(PartialEq, Eq)]
pub(crate) enum CompilerFeature {
NeonSha3,
}
pub(crate) struct CcBuilder {
manifest_dir: PathBuf,
out_dir: PathBuf,
build_prefix: Option<String>,
output_lib_type: OutputLibType,
compiler_features: Cell<Vec<CompilerFeature>>,
}
use std::fs;
fn identify_sources() -> Vec<&'static str> {
let mut source_files: Vec<&'static str> = vec![];
source_files.append(&mut Vec::from(universal::CRYPTO_LIBRARY));
let mut target_specific_source_found = true;
if target_os() == "windows" {
if target_arch() == "x86_64" {
source_files.append(&mut Vec::from(win_x86_64::CRYPTO_LIBRARY));
} else if target_arch() == "aarch64" {
source_files.append(&mut Vec::from(win_aarch64::CRYPTO_LIBRARY));
} else if target_arch() == "x86" {
source_files.append(&mut Vec::from(win_x86::CRYPTO_LIBRARY));
} else {
target_specific_source_found = false;
}
} else if target_vendor() == "apple" {
if target_arch() == "x86_64" {
source_files.append(&mut Vec::from(apple_x86_64::CRYPTO_LIBRARY));
} else if target_arch() == "aarch64" {
source_files.append(&mut Vec::from(apple_aarch64::CRYPTO_LIBRARY));
} else {
target_specific_source_found = false;
}
} else if target_arch() == "x86_64" {
source_files.append(&mut Vec::from(linux_x86_64::CRYPTO_LIBRARY));
} else if target_arch() == "aarch64" {
source_files.append(&mut Vec::from(linux_aarch64::CRYPTO_LIBRARY));
} else if target_arch() == "arm" {
source_files.append(&mut Vec::from(linux_arm::CRYPTO_LIBRARY));
} else if target_arch() == "x86" {
source_files.append(&mut Vec::from(linux_x86::CRYPTO_LIBRARY));
} else if target_arch() == "powerpc64" {
source_files.append(&mut Vec::from(linux_ppc64le::CRYPTO_LIBRARY));
} else {
target_specific_source_found = false;
}
if !target_specific_source_found {
emit_warning(format!(
"No target-specific source found: {}-{}",
target_os(),
target_arch()
));
}
source_files
}
#[allow(clippy::upper_case_acronyms)]
pub(crate) enum BuildOption {
STD(String),
FLAG(String),
DEFINE(String, String),
INCLUDE(PathBuf),
}
impl BuildOption {
fn std<T: ToString + ?Sized>(val: &T) -> Self {
Self::STD(val.to_string())
}
fn flag<T: ToString + ?Sized>(val: &T) -> Self {
Self::FLAG(val.to_string())
}
fn flag_if_supported<T: ToString + ?Sized>(cc_build: &cc::Build, flag: &T) -> Option<Self> {
if let Ok(true) = cc_build.is_flag_supported(flag.to_string()) {
Some(Self::FLAG(flag.to_string()))
} else {
None
}
}
fn define<K: ToString + ?Sized, V: ToString + ?Sized>(key: &K, val: &V) -> Self {
Self::DEFINE(key.to_string(), val.to_string())
}
fn include<P: Into<PathBuf>>(path: P) -> Self {
Self::INCLUDE(path.into())
}
fn apply_cc<'a>(&self, cc_build: &'a mut cc::Build) -> &'a mut cc::Build {
match self {
BuildOption::STD(val) => cc_build.std(val),
BuildOption::FLAG(val) => cc_build.flag(val),
BuildOption::DEFINE(key, val) => cc_build.define(key, Some(val.as_str())),
BuildOption::INCLUDE(path) => cc_build.include(path.as_path()),
}
}
pub(crate) fn apply_cmake<'a>(
&self,
cmake_cfg: &'a mut cmake::Config,
is_cl_like: bool,
) -> &'a mut cmake::Config {
if is_cl_like {
match self {
BuildOption::STD(val) => cmake_cfg.define(
"CMAKE_C_STANDARD",
val.to_ascii_lowercase().strip_prefix('c').unwrap_or("11"),
),
BuildOption::FLAG(val) => cmake_cfg.cflag(val),
BuildOption::DEFINE(key, val) => cmake_cfg.cflag(format!("/D{key}={val}")),
BuildOption::INCLUDE(path) => cmake_cfg.cflag(format!("/I{}", path.display())),
}
} else {
match self {
BuildOption::STD(val) => cmake_cfg.define(
"CMAKE_C_STANDARD",
val.to_ascii_lowercase().strip_prefix('c').unwrap_or("11"),
),
BuildOption::FLAG(val) => cmake_cfg.cflag(val),
BuildOption::DEFINE(key, val) => cmake_cfg.cflag(format!("-D{key}={val}")),
BuildOption::INCLUDE(path) => cmake_cfg.cflag(format!("-I{}", path.display())),
}
}
}
pub(crate) fn apply_nasm<'a>(&self, nasm_builder: &'a mut NasmBuilder) -> &'a mut NasmBuilder {
match self {
BuildOption::FLAG(val) => nasm_builder.flag(val),
BuildOption::DEFINE(key, val) => nasm_builder.define(key, Some(val.as_str())),
BuildOption::INCLUDE(path) => nasm_builder.include(path.as_path()),
BuildOption::STD(_) => nasm_builder, }
}
}
impl CcBuilder {
pub(crate) fn new(
manifest_dir: PathBuf,
out_dir: PathBuf,
build_prefix: Option<String>,
output_lib_type: OutputLibType,
) -> Self {
Self {
manifest_dir,
out_dir,
build_prefix,
output_lib_type,
compiler_features: Cell::new(vec![]),
}
}
pub(crate) fn collect_universal_build_options(
&self,
cc_build: &cc::Build,
do_quote_paths: bool,
) -> (bool, Vec<BuildOption>) {
let mut build_options: Vec<BuildOption> = Vec::new();
let is_cl_like = compiler_is_cl_like(&cc_build.get_compiler());
match requested_c_std() {
CStdRequested::C99 => {
build_options.push(BuildOption::std("c99"));
}
CStdRequested::C11 => {
build_options.push(BuildOption::std("c11"));
}
CStdRequested::None => {
if !is_cl_like {
if self.compiler_check("c11", Vec::<String>::new()) {
build_options.push(BuildOption::std("c11"));
} else {
build_options.push(BuildOption::std("c99"));
}
}
}
}
if let Some(cc) = get_crate_cc() {
set_env_for_target("CC", &cc);
}
if let Some(cxx) = get_crate_cxx() {
set_env_for_target("CXX", &cxx);
}
if target_arch() == "x86" && !is_cl_like {
if let Some(option) = BuildOption::flag_if_supported(cc_build, "-msse2") {
build_options.push(option);
}
}
if target_os() == "macos" || target_os() == "darwin" {
build_options.push(BuildOption::define("_DARWIN_C_SOURCE", "1"));
}
let opt_level = cargo_env("OPT_LEVEL");
match opt_level.as_str() {
"0" | "1" | "2" => {
if is_no_asm() {
emit_warning("AWS_LC_SYS_NO_ASM found. Disabling assembly code usage.");
build_options.push(BuildOption::define("OPENSSL_NO_ASM", "1"));
}
}
_ => {
assert!(
!is_no_asm(),
"AWS_LC_SYS_NO_ASM only allowed for debug builds!"
);
if !is_cl_like {
let path_str = if do_quote_paths {
format!("\"{}\"", self.manifest_dir.display())
} else {
format!("{}", self.manifest_dir.display())
};
let flag = format!("-ffile-prefix-map={path_str}=");
if let Ok(true) = cc_build.is_flag_supported(&flag) {
emit_warning(format!("Using flag: {flag}"));
build_options.push(BuildOption::flag(&flag));
} else {
emit_warning("NOTICE: Build environment source paths might be visible in release binary.");
let flag = format!("-fdebug-prefix-map={path_str}=");
if let Ok(true) = cc_build.is_flag_supported(&flag) {
emit_warning(format!("Using flag: {flag}"));
build_options.push(BuildOption::flag(&flag));
}
}
}
}
}
if target_os() == "macos" {
if let Some(option) =
BuildOption::flag_if_supported(cc_build, "-Wno-overriding-t-option")
{
build_options.push(option);
}
if let Some(option) = BuildOption::flag_if_supported(cc_build, "-Wno-overriding-option")
{
build_options.push(option);
}
}
(is_cl_like, build_options)
}
pub fn collect_cc_only_build_options(&self) -> Vec<BuildOption> {
let mut build_options: Vec<BuildOption> = Vec::new();
let is_cl_like = compiler_is_cl_like(&cc::Build::new().get_compiler());
if !is_cl_like {
build_options.push(BuildOption::flag("-Wno-unused-parameter"));
if target_os() != "emscripten" {
build_options.push(BuildOption::flag("-pthread"));
}
if target_os() == "linux" || target_os() == "emscripten" {
build_options.push(BuildOption::define("_XOPEN_SOURCE", "700"));
} else if target_vendor() != "apple" {
build_options.push(BuildOption::define("__EXTENSIONS__", "1"));
}
}
if !should_build_jitter_entropy() {
build_options.push(BuildOption::define("DISABLE_CPU_JITTER_ENTROPY", "1"));
}
self.add_includes(&mut build_options);
self.add_defines(&mut build_options, is_cl_like);
build_options
}
fn add_includes(&self, build_options: &mut Vec<BuildOption>) {
if let Some(prefix) = &self.build_prefix {
build_options.push(BuildOption::define("BORINGSSL_IMPLEMENTATION", "1"));
build_options.push(BuildOption::define("BORINGSSL_PREFIX", prefix.as_str()));
build_options.push(BuildOption::include(
self.manifest_dir.join("generated-include"),
));
}
build_options.push(BuildOption::include(self.manifest_dir.join("include")));
build_options.push(BuildOption::include(
self.manifest_dir.join("aws-lc").join("include"),
));
build_options.push(BuildOption::include(
self.manifest_dir
.join("aws-lc")
.join("third_party")
.join("s2n-bignum")
.join("include"),
));
build_options.push(BuildOption::include(
self.manifest_dir
.join("aws-lc")
.join("third_party")
.join("s2n-bignum")
.join("s2n-bignum-imported")
.join("include"),
));
if should_build_jitter_entropy() {
let jitterentropy_path = self
.manifest_dir
.join("aws-lc")
.join("third_party")
.join("jitterentropy")
.join("jitterentropy-library");
build_options.push(BuildOption::include(&jitterentropy_path));
build_options.push(BuildOption::include(jitterentropy_path.join("src")));
}
}
pub fn create_builder(&self) -> cc::Build {
let mut cc_build = cc::Build::new();
let build_options = self.collect_cc_only_build_options();
for option in build_options {
option.apply_cc(&mut cc_build);
}
cc_build
}
pub fn prepare_builder(&self) -> cc::Build {
if let Some(cflags) = get_crate_cflags() {
set_env_for_target("CFLAGS", cflags);
}
let mut cc_build = self.create_builder();
let (is_cl_like, build_options) = self.collect_universal_build_options(&cc_build, false);
for option in build_options {
option.apply_cc(&mut cc_build);
}
if target_os() == "linux" || target_os().ends_with("bsd") {
cc_build.asm_flag("-Wa,--noexecstack");
}
let opt_level = cargo_env("OPT_LEVEL");
if (target_os() == "linux" || target_os().ends_with("bsd"))
&& !is_cl_like
&& !matches!(opt_level.as_str(), "0" | "1" | "2")
&& !self.manifest_dir.to_string_lossy().contains(' ')
{
let path_str = self.manifest_dir.display().to_string();
let asm_flag = format!("-Wa,--debug-prefix-map={path_str}=");
if cc_build.is_flag_supported(&asm_flag).unwrap_or(false) {
cc_build.asm_flag(asm_flag);
}
}
cc_build
}
#[allow(clippy::zero_sized_map_values)]
fn build_s2n_bignum_source_feature_map() -> HashMap<String, CompilerFeature> {
let mut source_feature_map: HashMap<String, CompilerFeature> = HashMap::new();
source_feature_map.insert("sha3_keccak_f1600_alt.S".into(), CompilerFeature::NeonSha3);
source_feature_map.insert("sha3_keccak2_f1600.S".into(), CompilerFeature::NeonSha3);
source_feature_map.insert(
"sha3_keccak4_f1600_alt2.S".into(),
CompilerFeature::NeonSha3,
);
source_feature_map
}
#[allow(clippy::unused_self)]
fn add_defines(&self, build_options: &mut Vec<BuildOption>, is_cl_like: bool) {
if target_os() == "windows" {
build_options.push(BuildOption::define("WIN32_LEAN_AND_MEAN", ""));
build_options.push(BuildOption::define("NOMINMAX", ""));
}
if target_is_msvc() {
build_options.push(BuildOption::define("_CRT_SECURE_NO_WARNINGS", "1"));
}
if is_cl_like {
build_options.push(BuildOption::define("_HAS_EXCEPTIONS", "0"));
build_options.push(BuildOption::define(
"_STL_EXTRA_DISABLED_WARNINGS",
"4774 4987",
));
}
if target().contains("-win7-windows-") {
build_options.push(BuildOption::define("_WIN32_WINNT", "0x0601"));
emit_warning(format!(
"Setting _WIN32_WINNT to _WIN32_WINNT_WIN7 for {} target",
target()
));
if !is_cl_like {
build_options.push(BuildOption::define("AWSLC_WINDOWS_7_COMPAT", ""));
}
}
}
fn prepare_jitter_entropy_builder(&self, is_cl_like: bool) -> cc::Build {
let mut build_options: Vec<BuildOption> = Vec::new();
self.add_includes(&mut build_options);
self.add_defines(&mut build_options, is_cl_like);
let mut je_builder = cc::Build::new();
for option in build_options {
option.apply_cc(&mut je_builder);
}
je_builder.define("AWSLC", "1");
if target_os() == "macos" || target_os() == "darwin" {
je_builder.define("_DARWIN_C_SOURCE", "1");
}
if target_os() != "windows" {
je_builder.pic(true);
}
for &flag in Self::jitter_entropy_dialect_flags(is_cl_like) {
je_builder.flag(flag);
}
for option in self.collect_path_reproducibility_options(&je_builder, is_cl_like) {
option.apply_cc(&mut je_builder);
}
je_builder
}
fn collect_path_reproducibility_options(
&self,
cc_build: &cc::Build,
is_cl_like: bool,
) -> Vec<BuildOption> {
let mut opts: Vec<BuildOption> = Vec::new();
if is_cl_like {
return opts;
}
let path_str = self.manifest_dir.display().to_string();
let file_flag = format!("-ffile-prefix-map={path_str}=");
if cc_build.is_flag_supported(&file_flag).unwrap_or(false) {
opts.push(BuildOption::flag(&file_flag));
} else {
let dbg_flag = format!("-fdebug-prefix-map={path_str}=");
if cc_build.is_flag_supported(&dbg_flag).unwrap_or(false) {
opts.push(BuildOption::flag(&dbg_flag));
}
}
if let Some(opt) = BuildOption::flag_if_supported(
cc_build,
"-fsanitize-undefined-strip-path-components=-1",
) {
opts.push(opt);
}
opts
}
fn jitter_entropy_dialect_flags(is_cl_like: bool) -> &'static [&'static str] {
if is_cl_like {
&["-Od", "-W4", "-DYNAMICBASE"]
} else {
&[
"-fwrapv",
"--param",
"ssp-buffer-size=4",
"-fvisibility=hidden",
"-Wcast-align",
"-Wmissing-field-initializers",
"-Wshadow",
"-Wswitch-enum",
"-Wextra",
"-Wall",
"-pedantic",
"-O0",
"-fwrapv",
"-Wconversion",
]
}
}
fn jitter_entropy_cflags_guards(is_cl_like: bool) -> Vec<EnvGuard> {
let target_u = target().to_lowercase().replace('-', "_");
let cflags_env_names: Vec<String> = vec![
format!("CFLAGS_{target_u}"),
"HOST_CFLAGS".to_string(),
"TARGET_CFLAGS".to_string(),
"CFLAGS".to_string(),
];
let filter_cflags = |value: &str| -> String {
let filtered: String = value
.split_whitespace()
.filter(|flag| !flag.starts_with("-O") && !flag.starts_with("/O"))
.collect::<Vec<_>>()
.join(" ");
if is_cl_like {
format!("{filtered} -Od").trim().to_string()
} else {
format!("{filtered} -O0 -U_FORTIFY_SOURCE")
.trim()
.to_string()
}
};
cflags_env_names
.into_iter()
.filter_map(|name| {
let value = env::var(&name).ok()?;
Some(EnvGuard::new(&name, filter_cflags(&value)))
})
.collect()
}
fn add_all_files(&self, sources: &[&'static str], cc_build: &mut cc::Build) {
let is_cl_like = compiler_is_cl_like(&cc_build.get_compiler());
let force_include_option = if is_cl_like { "/FI" } else { "--include=" };
let mut s2n_bignum_builder = cc_build.clone();
s2n_bignum_builder.flag(format!(
"{}{}",
force_include_option,
self.manifest_dir
.join("generated-include")
.join("openssl")
.join("boringssl_prefix_symbols_asm.h")
.display()
));
s2n_bignum_builder.define("S2N_BN_HIDE_SYMBOLS", "1");
let mut jitter_entropy_builder = should_build_jitter_entropy().then(|| {
let mut jitter_entropy_builder = self.prepare_jitter_entropy_builder(is_cl_like);
jitter_entropy_builder.flag(format!(
"{}{}",
force_include_option,
self.manifest_dir
.join("generated-include")
.join("openssl")
.join("boringssl_prefix_symbols.h")
.display()
));
jitter_entropy_builder
});
let mut build_options = vec![];
self.add_includes(&mut build_options);
let mut nasm_builder = NasmBuilder::new(self.manifest_dir.clone(), self.out_dir.clone());
for option in &build_options {
option.apply_nasm(&mut nasm_builder);
}
let s2n_bignum_source_feature_map = Self::build_s2n_bignum_source_feature_map();
let compiler_features = self.compiler_features.take();
for source in sources {
let source_path = self.manifest_dir.join("aws-lc").join(source);
let is_s2n_bignum = std::path::Path::new(source).starts_with("third_party/s2n-bignum");
let is_jitter_entropy =
std::path::Path::new(source).starts_with("third_party/jitterentropy");
if !source_path.is_file() {
emit_warning(format!("Not a file: {:?}", source_path.as_os_str()));
continue;
}
if is_s2n_bignum {
let filename: String = source_path
.file_name()
.unwrap()
.to_str()
.unwrap()
.to_string();
if let Some(compiler_feature) = s2n_bignum_source_feature_map.get(&filename) {
if compiler_features.contains(compiler_feature) {
s2n_bignum_builder.file(source_path);
} else {
emit_warning(format!(
"Skipping due to missing compiler features: {:?}",
source_path.as_os_str()
));
}
} else {
s2n_bignum_builder.file(source_path);
}
} else if is_jitter_entropy {
if let Some(builder) = jitter_entropy_builder.as_mut() {
builder.file(source_path);
}
} else if source_path.extension() == Some("asm".as_ref()) {
nasm_builder.file(source_path);
} else {
cc_build.file(source_path);
}
}
self.compiler_features.set(compiler_features);
let s2n_bignum_object_files = s2n_bignum_builder.compile_intermediates();
for object in s2n_bignum_object_files {
cc_build.object(object);
}
if let Some(builder) = jitter_entropy_builder {
let _je_cflags_guards = Self::jitter_entropy_cflags_guards(is_cl_like);
let jitter_entropy_object_files = builder.compile_intermediates();
for object in jitter_entropy_object_files {
cc_build.object(object);
}
}
let nasm_object_files = nasm_builder.compile_intermediates();
for object in nasm_object_files {
cc_build.object(object);
}
}
fn build_library(&self, sources: &[&'static str]) {
let mut cc_build = self.prepare_builder();
self.run_compiler_checks(&mut cc_build);
self.add_all_files(sources, &mut cc_build);
let lib_name = if let Some(prefix) = &self.build_prefix {
format!("{}_crypto", prefix.as_str())
} else {
"crypto".to_string()
};
if is_link_whole_archive() {
cc_build.cargo_metadata(false);
}
cc_build.compile(&lib_name);
if is_link_whole_archive() {
emit_warning(format!(
"AWS_LC_SYS_LINK_WHOLE_ARCHIVE set: linking '{lib_name}' with +whole-archive"
));
println!("cargo:rustc-link-search=native={}", self.out_dir.display());
println!(
"cargo:rustc-link-lib={}={lib_name}",
self.output_lib_type.rust_link_lib_kind()
);
}
}
fn compiler_check<T, S>(&self, basename: &str, extra_flags: T) -> bool
where
T: IntoIterator<Item = S>,
S: AsRef<str>,
{
let mut ret_val = false;
let output_dir = self.out_dir.join(format!("out-{basename}"));
let source_file = self
.manifest_dir
.join("aws-lc")
.join("tests")
.join("compiler_features_tests")
.join(format!("{basename}.c"));
if !source_file.exists() {
emit_warning("######");
emit_warning("###### WARNING: MISSING GIT SUBMODULE ######");
emit_warning(format!(
" -- Did you initialize the repo's git submodules? Unable to find source file: {}.",
source_file.display()
));
emit_warning(" -- run 'git submodule update --init --recursive' to initialize.");
emit_warning("######");
emit_warning("######");
}
let mut cc_build = cc::Build::default();
cc_build
.file(source_file)
.warnings_into_errors(true)
.out_dir(&output_dir);
for flag in extra_flags {
let flag = flag.as_ref();
cc_build.flag(flag);
}
let compiler = cc_build.get_compiler();
if compiler.is_like_gnu() || compiler.is_like_clang() {
cc_build.flag("-Wno-unused-parameter");
}
let result = cc_build.try_compile_intermediates();
if result.is_ok() {
ret_val = true;
}
if fs::remove_dir_all(&output_dir).is_err() {
emit_warning(format!("Failed to remove {}", output_dir.display()));
}
emit_warning(format!(
"Compilation of '{basename}.c' {} - {:?}.",
if ret_val { "succeeded" } else { "failed" },
result
));
ret_val
}
fn memcmp_check(&self) {
if is_cross_compiling() {
return;
}
let memcmp_tool = {
let _cflags_guards = cflags_ignore_guards();
let mut probe_build = cc::Build::default();
probe_build.opt_level(3);
probe_build.get_compiler()
};
if !memcmp_tool.is_like_gnu() {
return;
}
if let Some(version) = gcc_version(&memcmp_tool) {
if !gcc_version_may_have_memcmp_bug(&version) {
return;
}
emit_warning(format!(
"GCC {version} may contain a memcmp-related bug \
(https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95189); probing the compiler."
));
}
let basename = "memcmp_invalid_stripped_check";
let exec_path = out_dir().join(basename);
let mut memcmp_compile_args: Vec<std::ffi::OsString> = memcmp_tool.args().to_vec();
if let Ok(ldflags) = std::env::var("LDFLAGS") {
for flag in ldflags.split_whitespace() {
memcmp_compile_args.push(flag.into());
}
}
memcmp_compile_args.push(
self.manifest_dir
.join("aws-lc")
.join("tests")
.join("compiler_features_tests")
.join(format!("{basename}.c"))
.into_os_string(),
);
memcmp_compile_args.push("-Wno-unused-parameter".into());
memcmp_compile_args.push("-o".into());
memcmp_compile_args.push(exec_path.clone().into_os_string());
let memcmp_args: Vec<_> = memcmp_compile_args
.iter()
.map(std::ffi::OsString::as_os_str)
.collect();
let memcmp_compile_result =
execute_command(memcmp_tool.path().as_os_str(), memcmp_args.as_slice());
if !memcmp_compile_result.status {
emit_warning(format!(
"Unable to build the memcmp probe; skipping the GCC memcmp bug check \
(https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95189).\n\
COMPILER: {}\n\
ARGS: {:?}\n\
EXECUTED: {}\n\
ERROR: {}\n\
OUTPUT: {}\n\
",
memcmp_tool.path().display(),
memcmp_args.as_slice(),
memcmp_compile_result.executed,
memcmp_compile_result.stderr,
memcmp_compile_result.stdout
));
return;
}
let result = execute_command(exec_path.as_os_str(), &[]);
if result.executed {
assert!(
result.status,
"### COMPILER BUG DETECTED ###\nYour compiler ({}) is not supported due to a memcmp related bug reported in \
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95189. \
We strongly recommend against using this compiler. \n\
EXIT CODE: {:?}\n\
ERROR: {}\n\
OUTPUT: {}\n\
",
memcmp_tool.path().display(),
result.exit_code,
result.stderr,
result.stdout
);
} else {
emit_warning(format!(
"Unable to execute the memcmp probe; skipping the GCC memcmp bug check \
(https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95189). ERROR: {:?}",
result.spawn_error
));
}
let _ = fs::remove_file(exec_path);
}
fn run_compiler_checks(&self, cc_build: &mut cc::Build) {
if self.compiler_check("stdalign_check", Vec::<&'static str>::new()) {
cc_build.define("AWS_LC_STDALIGN_AVAILABLE", Some("1"));
}
if !target_is_msvc()
&& self.compiler_check("builtin_swap_check", Vec::<&'static str>::new())
{
cc_build.define("AWS_LC_BUILTIN_SWAP_SUPPORTED", Some("1"));
}
if target_arch() == "aarch64"
&& self.compiler_check("neon_sha3_check", vec!["-march=armv8.4-a+sha3"])
{
let mut compiler_features = self.compiler_features.take();
compiler_features.push(CompilerFeature::NeonSha3);
self.compiler_features.set(compiler_features);
cc_build.define("MY_ASSEMBLER_SUPPORTS_NEON_SHA3_EXTENSION", Some("1"));
}
if target_os() == "linux" || target_os() == "android" {
if self.compiler_check("linux_random_h", Vec::<&'static str>::new()) {
cc_build.define("HAVE_LINUX_RANDOM_H", Some("1"));
} else if self.compiler_check("linux_random_h", vec!["-DDEFINE_U32"]) {
cc_build.define("HAVE_LINUX_RANDOM_H", Some("1"));
cc_build.define("AWS_LC_URANDOM_NEEDS_U32", Some("1"));
}
}
self.memcmp_check();
}
}
fn cflags_ignore_guards() -> Vec<EnvGuard> {
let target = target();
let target_u = target.replace(['-', '.'], "_");
[
format!("CFLAGS_{target}"),
format!("CFLAGS_{target_u}"),
"HOST_CFLAGS".to_string(),
"CFLAGS".to_string(),
]
.iter()
.map(|name| EnvGuard::remove(name))
.collect()
}
fn gcc_version(compiler: &cc::Tool) -> Option<String> {
for flag in ["-dumpfullversion", "-dumpversion"] {
let result = execute_command(compiler.path().as_os_str(), &[flag.as_ref()]);
if result.status {
let version = result.stdout.trim();
if !version.is_empty() {
return Some(version.to_string());
}
}
}
None
}
fn gcc_version_may_have_memcmp_bug(version: &str) -> bool {
let mut parts = version.trim().split('.');
let major = parts.next().and_then(|v| v.parse::<u64>().ok());
let minor = parts.next().and_then(|v| v.parse::<u64>().ok());
match (major, minor) {
(Some(9), Some(minor)) => minor < 4,
(Some(10), Some(minor)) => minor < 2,
(Some(9 | 10), None) | (None, _) => true,
(Some(_), _) => false,
}
}
impl crate::Builder for CcBuilder {
fn check_dependencies(&self) -> Result<(), String> {
if OutputLibType::Dynamic == self.output_lib_type {
return Err("CcBuilder only supports static builds".to_string());
}
if target_env() == "ohos" {
return Err("OpenHarmony targets must be built with CMake.".to_string());
}
if Some(true) == env_var_to_bool("CARGO_FEATURE_SSL") {
return Err("cc_builder for libssl not supported".to_string());
}
Ok(())
}
fn build(&self) -> Result<(), String> {
if target_os() == "windows"
&& target_arch() == "aarch64"
&& target_is_msvc()
&& get_crate_cc().is_none()
{
if let Some(clang_cl) = find_clang_cl() {
set_env_for_target("CC", clang_cl);
} else {
emit_warning(
"Windows ARM64 (aarch64-pc-windows-msvc) requires clang-cl. \
Install the 'C++ Clang Compiler for Windows' component in \
Visual Studio Build Tools, or set CC to a working clang-cl. \
See User Guide: https://aws.github.io/aws-lc-rs/index.html",
);
}
}
println!("cargo:root={}", self.out_dir.display());
let sources = crate::cc_builder::identify_sources();
self.build_library(sources.as_slice());
crate::emit_source_build_metadata(&self.manifest_dir);
Ok(())
}
fn name(&self) -> &'static str {
"CC"
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{EnvGuard, ENV_MUTEX};
fn with_target_env<R>(env_val: &str, f: impl FnOnce() -> R) -> R {
let _lock = ENV_MUTEX.lock().unwrap_or_else(|e| e.into_inner());
let _guard = EnvGuard::new("CARGO_CFG_TARGET_ENV", env_val);
f()
}
#[test]
fn test_target_is_msvc_matches_msvc_abi_only() {
assert!(with_target_env("msvc", target_is_msvc));
assert!(!with_target_env("gnu", target_is_msvc));
assert!(!with_target_env("musl", target_is_msvc));
assert!(!with_target_env("", target_is_msvc));
}
#[test]
fn test_gcc_version_may_have_memcmp_bug() {
assert!(gcc_version_may_have_memcmp_bug("9"));
assert!(gcc_version_may_have_memcmp_bug("9.1.0"));
assert!(gcc_version_may_have_memcmp_bug("9.2.0"));
assert!(gcc_version_may_have_memcmp_bug("9.3.1"));
assert!(gcc_version_may_have_memcmp_bug("10"));
assert!(gcc_version_may_have_memcmp_bug("10.1.0"));
assert!(!gcc_version_may_have_memcmp_bug("9.4.0"));
assert!(!gcc_version_may_have_memcmp_bug("9.5.0"));
assert!(!gcc_version_may_have_memcmp_bug("10.2.1"));
assert!(!gcc_version_may_have_memcmp_bug("4.8.5"));
assert!(!gcc_version_may_have_memcmp_bug("8.5.0"));
assert!(!gcc_version_may_have_memcmp_bug("11.4.0"));
assert!(!gcc_version_may_have_memcmp_bug("12"));
assert!(!gcc_version_may_have_memcmp_bug("15.1.0"));
assert!(gcc_version_may_have_memcmp_bug("9.3.0\n"));
assert!(gcc_version_may_have_memcmp_bug(""));
assert!(gcc_version_may_have_memcmp_bug("unknown"));
}
#[test]
fn test_cflags_ignore_guards_suppress_and_restore() {
let _lock = ENV_MUTEX.lock().unwrap_or_else(|e| e.into_inner());
let _target_guard = EnvGuard::new("TARGET", "x86_64-unknown-linux-gnu");
let _g1 = EnvGuard::new("CFLAGS", "-flto=thin");
let _g2 = EnvGuard::new("CFLAGS_x86_64-unknown-linux-gnu", "-fdash-variant");
let _g3 = EnvGuard::new("CFLAGS_x86_64_unknown_linux_gnu", "-funderscore-variant");
let _g4 = EnvGuard::new("HOST_CFLAGS", "-fhost");
let _g5 = EnvGuard::new("TARGET_CFLAGS", "-ftarget");
{
let _guards = cflags_ignore_guards();
assert!(env::var("CFLAGS").is_err());
assert!(env::var("CFLAGS_x86_64-unknown-linux-gnu").is_err());
assert!(env::var("CFLAGS_x86_64_unknown_linux_gnu").is_err());
assert!(env::var("HOST_CFLAGS").is_err());
assert_eq!(env::var("TARGET_CFLAGS").unwrap(), "-ftarget");
}
assert_eq!(env::var("CFLAGS").unwrap(), "-flto=thin");
assert_eq!(
env::var("CFLAGS_x86_64-unknown-linux-gnu").unwrap(),
"-fdash-variant"
);
assert_eq!(
env::var("CFLAGS_x86_64_unknown_linux_gnu").unwrap(),
"-funderscore-variant"
);
assert_eq!(env::var("HOST_CFLAGS").unwrap(), "-fhost");
}
#[test]
fn test_jitter_entropy_flags_cl_dialect_omit_gnu_only() {
let cl = CcBuilder::jitter_entropy_dialect_flags(true);
assert!(
!cl.contains(&"--param") && !cl.contains(&"ssp-buffer-size=4"),
"cl-mode jitter flags must not contain the GNU-only ssp-buffer-size pair: {cl:?}"
);
assert!(cl.contains(&"-Od"), "expected cl-style -Od: {cl:?}");
}
#[test]
fn test_jitter_entropy_gnu_dialect_keeps_hardening_flags() {
let gnu = CcBuilder::jitter_entropy_dialect_flags(false);
assert!(gnu.contains(&"--param"));
assert!(gnu.contains(&"ssp-buffer-size=4"));
assert!(
!gnu.contains(&"-Od"),
"GNU dialect must not use cl-style -Od: {gnu:?}"
);
assert!(gnu.contains(&"-O0"), "expected -O0: {gnu:?}");
}
#[test]
fn test_program_name_is_cl_driver() {
use crate::program_name_is_cl_driver;
use std::path::Path;
for cl in ["clang-cl", "clang-cl.exe", "CLANG-CL.EXE", "cl", "cl.exe"] {
assert!(
program_name_is_cl_driver(Path::new(cl)),
"{cl} should be detected as cl driver mode"
);
}
for gnu in ["clang", "clang.exe", "clang-18", "gcc", "cc"] {
assert!(
!program_name_is_cl_driver(Path::new(gnu)),
"{gnu} should not be detected as cl driver mode"
);
}
}
}