From e8e9a67a1f82e81e6c3b7fb5b5ee7cf786d31726 Mon Sep 17 00:00:00 2001 From: Alexander Ovchinnikov <8490695+Alovchin91@users.noreply.github.com> Date: Sat, 7 Aug 2021 13:42:36 +0200 Subject: [PATCH] Enable building and running on Windows ARM64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I agree to license my contributions to each file under the terms given at the top of each file I changed. Co-authored-by: Marc-André Moreau --- .github/workflows/ci.yml | 15 ++++++ BUILDING.md | 47 +++++++++++++++--- Cargo.toml | 4 +- build.rs | 70 +++++++++++++-------------- crypto/crypto.c | 2 +- crypto/fipsmodule/ec/ecp_nistz384.inl | 2 +- mk/package.sh | 3 +- src/cpu.rs | 42 ++++++++++++++-- 8 files changed, 134 insertions(+), 51 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2f82e9024c..a9da114c8f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -149,6 +149,9 @@ jobs: toolchain: stable profile: minimal + - run: echo "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\Llvm\x64\bin" >> $GITHUB_PATH + shell: bash + - run: sh mk/package.sh shell: bash @@ -167,6 +170,7 @@ jobs: - aarch64-apple-ios - aarch64-apple-darwin - aarch64-linux-android + - aarch64-pc-windows-msvc - aarch64-unknown-linux-gnu - aarch64-unknown-linux-musl - arm-unknown-linux-gnueabihf @@ -234,6 +238,11 @@ jobs: # TODO: https://github.com/briansmith/ring/issues/486 cargo_options: --no-run + - target: aarch64-pc-windows-msvc + host_os: windows-latest + # GitHub Actions doesn't have a way to run this target yet. + cargo_options: --no-run + - target: aarch64-unknown-linux-gnu host_os: ubuntu-18.04 @@ -303,6 +312,12 @@ jobs: sudo xcode-select -s /Applications/Xcode_12.4.app && sudo rm -Rf /Library/Developer/CommandLineTools/SDKs/* + - if: ${{ matrix.target == 'aarch64-pc-windows-msvc' }} + run: | + echo "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\Llvm\x64\bin" >> $GITHUB_PATH + echo "RUSTFLAGS=-C target-feature=+crt-static" >> $GITHUB_ENV + shell: bash + - if: ${{ !contains(matrix.host_os, 'windows') }} run: | mk/cargo.sh test -vv --all-targets --target=${{ matrix.target }} ${{ matrix.cargo_options }} ${{ matrix.features }} ${{ matrix.mode }} diff --git a/BUILDING.md b/BUILDING.md index ab4e5a6539..8d59abb54d 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -33,9 +33,12 @@ apply when building from crates.io: primitives (32- and 64- bit Intel, and 32- and 64-bit ARM), Perl must be installed and in `$PATH`. -* For Windows targets, `target/tools/windows/nasm/nasm[.exe]` is used as the - assembler. The version to use and how to download it is documented in - [.github/workflows/ci.yml](.github/workflows/ci.yml). +* For Windows targets except ARM64, `target/tools/windows/nasm/nasm[.exe]` + is used as the assembler. The version to use and how to download it is + documented in [.github/workflows/ci.yml](.github/workflows/ci.yml). + +* For Windows ARM64 target, Clang is used as the C compiler and the assembler. + See below "Building for Windows ARM64" section. Cross Compiling --------------- @@ -57,10 +60,12 @@ that the current beta and nightly releases work. On Windows, *ring* supports the x86_64-pc-windows-msvc and i686-pc-windows-msvc targets best. These targets require the “Visual C++ Build Tools -2015” package or Visual Studio 2015 Update 3 or later to be installed. Patches -to get it working on other variants, including in particular Visual Studio 2017 -([#338]), Windows ARM platforms, Windows Universal Platform, Windows XP (the -v140_xp toolchain; [#339]), and the -gnu targets ([#330]) are welcome. +2015” package or Visual Studio 2015 Update 3 or later to be installed. +*ring* now also supports the aarch64-pc-windows-msvc target. For the detailed +instructions please see the next section. +Patches to get it working on other variants, including in particular Visual Studio 2017 +([#338]), Windows Universal Platform, Windows XP (the v140_xp toolchain; [#339]), +and the -gnu targets ([#330]) are welcome. For other platforms, GCC 4.6 or later and Clang 3.5 or later are currently supported best. The build script passes options to the C/C++ compiler that are @@ -78,6 +83,34 @@ defined with a value of at least 21 on 64-bit targets or 18 on 32-bit targets; e.g. export `CFLAGS=-D__ANDROID_API__=21`. +Building for Windows ARM64 +-------------------------- + +Windows ARM64 target requires the “Visual C++ Build Tools 2019” package or +Visual Studio 2019 or later to be installed. “Desktop development with C++” +workflow should be installed, as well as +“MSVC v142 - VS 2019 C++ ARM64 build tools” component. + +To build *ring* for Windows ARM64, you will need to install Clang as it is used +as the C compiler and the assembler for that platform. You can either use +the version of Clang installed by Visual Studio, a standalone version from +llvm.org, or a mingw64 version of Clang, for example, from [llvm-mingw +project](https://github.com/mstorsjo/llvm-mingw). + +If you're buiding *ring* on an ARM64 device like Surface Pro X, please note +that llvm.org and llvm-mingw have native ARM64 versions of Clang available. +Also, if you're building *ring* on an ARM64 device, you might want to use +`aarch64-pc-windows-msvc` Rustup toolchain, which can be installed using +`rustup toolchain add aarch64-pc-windows-msvc`. + +When building on an ARM64 device, due to a bug in the Visual Studio installer, +if you're using `rustc` version < 1.55 you would need to run `cargo build` / +`cargo test` commands from x86_arm64 Developer Command Prompt. You can use +`C:\Program Files (x86)\Microsoft Visual Studio\2019\\VC\Auxiliary\Build\vcvarsx86_arm64.bat` +batch script to configure the environment. If you use `rustc` 1.55 beta or newer, +you can run `cargo` commands without configuring the dev environment beforehand. + + Additional Features that are Useful for Development --------------------------------------------------- The `slow_tests` feature runs additional tests that are too slow to run during diff --git a/Cargo.toml b/Cargo.toml index f22f644635..69a6a638f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -162,7 +162,7 @@ name = "ring" [dependencies] untrusted = { version = "0.9" } -[target.'cfg(any(target_arch = "x86",target_arch = "x86_64", all(any(target_arch = "aarch64", target_arch = "arm"), any(target_os = "android", target_os = "fuchsia", target_os = "linux"))))'.dependencies] +[target.'cfg(any(target_arch = "x86",target_arch = "x86_64", all(any(target_arch = "aarch64", target_arch = "arm"), any(target_os = "android", target_os = "fuchsia", target_os = "linux", target_os = "windows"))))'.dependencies] spin = { version = "0.9.2", default-features = false, features = ["once"] } [target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies] @@ -176,7 +176,7 @@ once_cell = { version = "1.5.2", default-features = false, features=["std"] } web-sys = { version = "0.3.37", default-features = false, features = ["Crypto", "Window"] } [target.'cfg(target_os = "windows")'.dependencies] -winapi = { version = "0.3.8", default-features = false, features = ["ntsecapi", "wtypesbase"] } +winapi = { version = "0.3.9", default-features = false, features = ["ntsecapi", "wtypesbase", "processthreadsapi"] } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen-test = { version = "0.3.18", default-features = false } diff --git a/build.rs b/build.rs index 626db1bfbd..991762b92a 100644 --- a/build.rs +++ b/build.rs @@ -228,6 +228,13 @@ const ASM_TARGETS: &[AsmTarget] = &[ asm_extension: "asm", preassemble: true, }, + AsmTarget { + oss: &[WINDOWS], + arch: "aarch64", + perlasm_format: "win64", + asm_extension: "S", + preassemble: true, + }, ]; struct AsmTarget { @@ -378,7 +385,7 @@ fn pregenerate_asm_main() { let os = WINDOWS; if !std::mem::replace(&mut generated_prefix_headers, true) { - generate_prefix_symbols_nasm(&pregenerated, &ring_core_prefix()).unwrap(); + generate_prefix_symbols(&pregenerated, &ring_core_prefix()).unwrap(); } let srcs = asm_srcs(perlasm_src_dsts); @@ -390,10 +397,8 @@ fn pregenerate_asm_main() { force_warnings_into_errors: true, }; - let compiler = cc::Build::new().get_compiler(); - for src in srcs { - compile(&src, &target, &compiler, &pregenerated); + compile(&src, &target, &pregenerated); } } } @@ -441,7 +446,7 @@ fn build_c_code( out_dir }; - generate_prefix_symbols(target, out_dir, ring_core_prefix).unwrap(); + generate_prefix_symbols(out_dir, ring_core_prefix).unwrap(); let asm_srcs = if let Some(asm_target) = asm_target { let perlasm_src_dsts = perlasm_src_dsts(asm_dir, asm_target); @@ -500,13 +505,11 @@ fn build_library( srcs: &[PathBuf], additional_srcs: &[PathBuf], ) { - let compiler = cc::Build::default().get_compiler(); - // Compile all the (dirty) source files into object files. let objs = additional_srcs .iter() .chain(srcs.iter()) - .map(|f| compile(f, target, &compiler, out_dir)) + .map(|f| compile(f, target, out_dir)) .collect::>(); // Rebuild the library if necessary. @@ -545,14 +548,14 @@ fn build_library( println!("cargo:rustc-link-lib=static={}", lib_name); } -fn compile(p: &Path, target: &Target, compiler: &cc::Tool, out_dir: &Path) -> String { +fn compile(p: &Path, target: &Target, out_dir: &Path) -> String { let ext = p.extension().unwrap().to_str().unwrap(); if ext == "o" { p.to_str().expect("Invalid path").into() } else { let out_path = obj_path(out_dir, p); let cmd = if target.os != WINDOWS || ext != "asm" { - cc(p, ext, target, compiler, &out_path, out_dir) + cc(p, ext, target, &out_path, out_dir) } else { nasm(p, &target.arch, &out_path, out_dir) }; @@ -571,27 +574,28 @@ fn obj_path(out_dir: &Path, src: &Path) -> PathBuf { out_path } -fn cc( - file: &Path, - ext: &str, - target: &Target, - compiler: &cc::Tool, - out_path: &Path, - include_dir: &Path, -) -> Command { +fn cc(file: &Path, ext: &str, target: &Target, out_path: &Path, include_dir: &Path) -> Command { let mut c = cc::Build::new(); + + // FIXME: On Windows AArch64 we currently must use Clang to compile C code + if target.os == WINDOWS && target.arch == AARCH64 && !c.get_compiler().is_like_clang() { + let _ = c.compiler("clang"); + } + + let compiler = c.get_compiler(); + let _ = c.include("include"); let _ = c.include(include_dir); match ext { "c" => { - for f in c_flags(compiler) { + for f in c_flags(&compiler) { let _ = c.flag(f); } } "S" => (), e => panic!("Unsupported file extension: {:?}", e), }; - for f in cpp_flags(compiler) { + for f in cpp_flags(&compiler) { let _ = c.flag(f); } if target.os != "none" @@ -843,24 +847,18 @@ fn ring_core_prefix() -> String { /// Creates the necessary header file for symbol renaming and returns the path of the /// generated include directory. -fn generate_prefix_symbols( - target: &Target, - out_dir: &Path, - prefix: &str, -) -> Result<(), std::io::Error> { +fn generate_prefix_symbols(out_dir: &Path, prefix: &str) -> Result<(), std::io::Error> { generate_prefix_symbols_header(out_dir, "prefix_symbols.h", '#', None, prefix)?; - if target.os == "windows" { - let _ = generate_prefix_symbols_nasm(out_dir, prefix)?; - } else { - generate_prefix_symbols_header( - out_dir, - "prefix_symbols_asm.h", - '#', - Some("#if defined(__APPLE__)"), - prefix, - )?; - } + generate_prefix_symbols_nasm(out_dir, prefix)?; + + generate_prefix_symbols_header( + out_dir, + "prefix_symbols_asm.h", + '#', + Some("#if defined(__APPLE__)"), + prefix, + )?; Ok(()) } diff --git a/crypto/crypto.c b/crypto/crypto.c index 7caecc5150..d0d8467bf7 100644 --- a/crypto/crypto.c +++ b/crypto/crypto.c @@ -15,6 +15,7 @@ #include #include "internal.h" +#if defined(OPENSSL_X86) || defined(OPENSSL_X86_64) // Our assembly does not use the GOT to reference symbols, which means // references to visible symbols will often require a TEXTREL. This is // undesirable, so all assembly-referenced symbols should be hidden. CPU @@ -26,7 +27,6 @@ #define HIDDEN __attribute__((visibility("hidden"))) #endif -#if defined(OPENSSL_X86) || defined(OPENSSL_X86_64) // This value must be explicitly initialised to zero in order to work around a // bug in libtool or the linker on OS X. // diff --git a/crypto/fipsmodule/ec/ecp_nistz384.inl b/crypto/fipsmodule/ec/ecp_nistz384.inl index dbb1cefbe9..dd9e2bfea9 100644 --- a/crypto/fipsmodule/ec/ecp_nistz384.inl +++ b/crypto/fipsmodule/ec/ecp_nistz384.inl @@ -252,6 +252,6 @@ void nistz384_point_mul(P384_POINT *r, const BN_ULONG p_scalar[P384_LIMBS], add_precomputed_w5(r, wvalue, table); } -#if defined(__GNUC__) +#if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic pop #endif diff --git a/mk/package.sh b/mk/package.sh index 3defa304b7..2b24a16239 100644 --- a/mk/package.sh +++ b/mk/package.sh @@ -10,5 +10,6 @@ if [[ $(git status --porcelain | wc -c) -ne 0 ]]; then fi cargo clean --target-dir=target/pregenerate_asm -RING_PREGENERATE_ASM=1 cargo build --target-dir=target/pregenerate_asm +RING_PREGENERATE_ASM=1 CC_AARCH64_PC_WINDOWS_MSVC=clang \ + cargo build --target-dir=target/pregenerate_asm cargo package --allow-dirty diff --git a/src/cpu.rs b/src/cpu.rs index 99a228c25e..37cd6fa95f 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -31,7 +31,12 @@ pub(crate) fn features() -> Features { target_arch = "x86_64", all( any(target_arch = "aarch64", target_arch = "arm"), - any(target_os = "android", target_os = "fuchsia", target_os = "linux") + any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + target_os = "windows" + ) ) ))] { @@ -49,7 +54,12 @@ pub(crate) fn features() -> Features { #[cfg(all( any(target_arch = "aarch64", target_arch = "arm"), - any(target_os = "android", target_os = "fuchsia", target_os = "linux") + any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + target_os = "windows" + ) ))] { arm::setup(); @@ -162,6 +172,27 @@ pub(crate) mod arm { } } + #[cfg(all(target_os = "windows", target_arch = "aarch64"))] + pub fn setup() { + // We do not need to check for the presence of NEON, as Armv8-A always has it + let mut features = NEON.mask; + + let result = unsafe { + winapi::um::processthreadsapi::IsProcessorFeaturePresent( + winapi::um::winnt::PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE, + ) + }; + + if result != 0 { + // These are all covered by one call in Windows + features |= AES.mask; + features |= PMULL.mask; + features |= SHA256.mask; + } + + unsafe { OPENSSL_armcap_P = features }; + } + macro_rules! features { { $( @@ -237,7 +268,12 @@ pub(crate) mod arm { } #[cfg(all( - any(target_os = "android", target_os = "fuchsia", target_os = "linux"), + any( + target_os = "android", + target_os = "fuchsia", + target_os = "linux", + target_os = "windows" + ), any(target_arch = "arm", target_arch = "aarch64") ))] {