diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 86b25ba990..4ecdc18721 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -143,7 +143,7 @@ jobs: - run: rustup toolchain install --no-self-update --profile=minimal 1.66.0 - - run: echo "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\Llvm\x64\bin" >> $GITHUB_PATH + - run: echo "VSINSTALLDIR=C:\Program Files\Microsoft Visual Studio\2022\Enterprise" >> $GITHUB_ENV shell: bash - run: sh mk/package.sh @@ -362,20 +362,16 @@ jobs: with: persist-credentials: false - # From https://github.com/actions/partner-runner-images/issues/77 - - if: ${{ contains(matrix.host_os, 'windows') && contains(matrix.host_os, 'arm') }} - run: | - curl -LOs https://static.rust-lang.org/rustup/dist/aarch64-pc-windows-msvc/rustup-init.exe - ./rustup-init.exe -y --default-toolchain none --no-modify-path - echo "$USERPROFILE/.cargo/bin" >> "$GITHUB_PATH" - shell: sh - - run: mk/install-build-tools.sh +${{ matrix.rust_channel }} --target=${{ matrix.target }} shell: sh - if: ${{ contains(matrix.host_os, 'windows') && contains(matrix.target, '86') }} run: ./mk/install-build-tools.ps1 + - if: ${{ contains(matrix.host_os, 'windows') }} + run: echo "VSINSTALLDIR=C:\Program Files\Microsoft Visual Studio\2022\Enterprise" >> $GITHUB_ENV + shell: bash + - if: ${{ matrix.xcode_version != '' }} run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode_version }}.app @@ -491,11 +487,6 @@ jobs: - if: ${{ contains(matrix.host_os, 'windows') && contains(matrix.target, '86') }} run: ./mk/install-build-tools.ps1 - - if: ${{ matrix.target == 'aarch64-pc-windows-msvc' && !contains(matrix.host_os, 'arm') }} - run: | - echo "C:\Program Files (x86)\Microsoft Visual Studio\2022\Enterprise\VC\Tools\Llvm\x64\bin" >> $GITHUB_PATH - shell: bash - - if: ${{ !contains(matrix.host_os, 'windows') }} run: | mk/cargo.sh +${{ matrix.rust_channel }} test --lib --tests -vv --target=${{ matrix.target }} ${{ matrix.cargo_options }} ${{ matrix.features }} ${{ matrix.mode }} ${{ matrix.cargo_test_options }} diff --git a/BUILDING.md b/BUILDING.md index d263bdfb68..6db117e1c4 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -24,27 +24,14 @@ version used by default in GitHub Actions is the one that is primarily supported. For Windows ARM64 targets (aarch64-pc-windows-msvc), the Visual Studio Build -Tools “VS 2022 C++ ARM64 build tools” and "clang" components must be installed. -Add Microsoft's provided version of `clang` to `%PATH%`, which will allow the -build to work in GitHub Actions without installing anything: -``` -$env:Path += ";C:\Program Files (x86)\Microsoft Visual Studio\2022\Enterprise\VC\Tools\Llvm\x64\bin" -``` -If you (locally) have “Build Tools for Visual Studio 2022” instead, use: -``` -$env:Path += ";C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\Llvm\x64\bin" -``` -Alternatively, if the host machine is already a Windows ARM64 then use: -``` -$env:Path += ";C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\Llvm\ARM64\bin" -``` +Tools “VS 2022 C++ ARM64 build tools” must be installed. Packaged Builds --------------- The *ring* crate released on crates.io needs fewer tools than are required for building *ring* from Git. Perl isn't required because the output of -the Perl build steps is packaged into the crate. For Windows x86 and x86-64, +the Perl build steps is packaged into the crate. For Windows x86, x86-64 and arm64, the packaged crate contains precompiled object files for the assembly language modules so no macro assembler. @@ -63,12 +50,11 @@ apply when building from crates.io: is used as the assembler. The version to use, how to download it, and where to put it, is all documented in [mk/install-build-tools.ps1](mk/install-build-tools.ps1). +* For Windows arm64 only, Visual Studio's `clang.exe` (which can be installed as a component in Visual Studio 2019 and later) is used as the assembler. Installation instructions can be found [here](https://learn.microsoft.com/en-us/cpp/build/clang-support-msbuild?view=msvc-170). + For Windows hosts, it is easiest to work in a Git Bash prompt as this will make Perl available automatically. You should not need to mess with -VCVARSALL/etc since cc-rs will find MSVC. When targeting -aarch64-pc-windows-msvc, just add the Visual-Studio-supplied LLVM directory -to the PATH by transliterating the relevant PowerShell commands into Bash -syntax; this copy of clang will be able to find all the headers, etc. +VCVARSALL/etc since cc-rs will find MSVC and Clang. Additional Features that are Useful for Development diff --git a/build.rs b/build.rs index ca7a7d4860..5fd5031d15 100644 --- a/build.rs +++ b/build.rs @@ -83,6 +83,7 @@ mod env { define_env! { pub OUT_DIR: SetByCargo } define_env! { pub PERL_EXECUTABLE: RerunIfChanged } define_env! { pub RING_PREGENERATE_ASM: RerunIfChanged } + define_env! { pub VSINSTALLDIR: RerunIfChanged } } const X86: &str = "x86"; @@ -272,6 +273,9 @@ impl AsmTarget { fn use_nasm(&self) -> bool { [WIN32N, NASM].contains(&self.perlasm_format) } + fn use_clang_win(&self) -> bool { + self.perlasm_format == "win64" + } } /// Operating systems that have the same ABI as Linux on every architecture @@ -427,13 +431,17 @@ fn generate_sources_and_preassemble<'a>( 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. + // Package pregenerated object files in addition to pregenerated + // assembly language source files, so that the user doesn't need + // to install the assembler (NASM on x64/86 Windows and MSVC's Clang on arm64 Windows). + if asm_target.use_nasm() || asm_target.use_clang_win() { let srcs = asm_srcs(perlasm_src_dsts); for src in srcs { - nasm(&src, asm_target.arch, out_dir, out_dir, c_root_dir); + if asm_target.use_nasm() { + nasm(&src, asm_target.arch, out_dir, out_dir, c_root_dir); + } else { + clang_win(&src, asm_target.arch, out_dir, out_dir); + } } } } @@ -466,8 +474,8 @@ fn build_c_code( 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 + if asm_target.use_nasm() || asm_target.use_clang_win() { + // Nasm or Clang was already used to generate the object files, so use them instead of // assembling. let obj_srcs = asm_srcs .iter() @@ -489,7 +497,7 @@ fn build_c_code( // 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.arch == X86_64 || target.arch == X86 || target.arch == AARCH64) && target.os == WINDOWS { return false; @@ -593,13 +601,6 @@ fn obj_path(out_dir: &Path, src: &Path) -> PathBuf { 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); @@ -669,6 +670,45 @@ fn nasm(file: &Path, arch: &str, include_dir: &Path, out_dir: &Path, c_root_dir: run_command(c); } +fn clang_win(file: &Path, arch: &str, include_dir: &Path, out_dir: &Path) { + // TODO use the CC API instead of the hardcoded path below. + // Needs https://github.com/rust-lang/cc-rs/pull/1506 to be merged. + // cc::windows_registry::find(asm_target, "clang.exe"); + + let out_file = obj_path(out_dir, file); + // Use Clang that's bundled with MSVC explicitly. + // This is necessary because the `cc` crate uses the system Clang by default, + // which is not compatible with MSVC. + let vs_path = env::var_os(&env::VSINSTALLDIR).unwrap(); + let clang_path = PathBuf::from(vs_path) + .join("VC") + .join("Tools") + .join("Llvm") + .join("bin"); + + // Check if path exists + if !clang_path.exists() { + panic!("Clang path does not exist: {}", clang_path.display()); + } + + let mut c = Command::new(clang_path.join("clang.exe")); + + // Should be called like this: + // clang -target aarch64-windows -c chacha-armv8-win64.S -o chacha-armv8-win64.o -I../include -I./ + let _ = c + .arg("-target") + .arg(format!("{arch}-pc-windows-msvc")) + .arg("-c") + .arg(file) + .arg("-o") + .arg(out_file.to_str().expect("Invalid path")) + .arg("-I") + .arg("include/") + .arg("-I") + .arg(include_dir); + run_command(c); +} + fn run_command_with_args(command_name: &Path, args: &[OsString]) { let mut cmd = Command::new(command_name); let _ = cmd.args(args); diff --git a/crypto/fipsmodule/bn/internal.h b/crypto/fipsmodule/bn/internal.h index 73d52f492d..dd9adab208 100644 --- a/crypto/fipsmodule/bn/internal.h +++ b/crypto/fipsmodule/bn/internal.h @@ -18,11 +18,15 @@ #include -#if defined(OPENSSL_X86_64) && defined(_MSC_VER) && !defined(__clang__) +#if defined(_MSC_VER) && defined(OPENSSL_64_BIT) && !defined(__clang__) #pragma warning(push, 3) #include #pragma warning(pop) +#if defined(OPENSSL_X86_64) #pragma intrinsic(_umul128) +#elif defined(OPENSSL_AARCH64) +#pragma intrinsic(__umulh) +#endif #endif #include "../../internal.h" @@ -132,6 +136,10 @@ static inline void bn_umult_lohi(BN_ULONG *low_out, BN_ULONG *high_out, BN_ULONG a, BN_ULONG b) { #if defined(OPENSSL_X86_64) && defined(_MSC_VER) && !defined(__clang__) *low_out = _umul128(a, b, high_out); +#elif defined(OPENSSL_AARCH64) && defined(_MSC_VER) && !defined(__clang__) + /* Use __umulh intrinsic for 64x64->128 bit multiplication on MSVC ARM64 */ + *low_out = a * b; + *high_out = __umulh(a, b); #else BN_ULLONG result = (BN_ULLONG)a * b; *low_out = (BN_ULONG)result; diff --git a/crypto/limbs/limbs.inl b/crypto/limbs/limbs.inl index 1ca72cbb23..8754c50dd8 100644 --- a/crypto/limbs/limbs.inl +++ b/crypto/limbs/limbs.inl @@ -34,7 +34,7 @@ #error "MSVC 2015 Update 2 or later is required." #endif typedef uint8_t Carry; -#if LIMB_BITS == 64 +#if LIMB_BITS == 64 && defined(OPENSSL_X86_64) #pragma intrinsic(_addcarry_u64, _subborrow_u64) #define RING_CORE_ADDCARRY_INTRINSIC _addcarry_u64 #define RING_CORE_SUBBORROW_INTRINSIC _subborrow_u64 @@ -60,6 +60,13 @@ static inline Carry limb_adc(Limb *r, Limb a, Limb b, Carry carry_in) { Carry ret; #if defined(RING_CORE_ADDCARRY_INTRINSIC) ret = RING_CORE_ADDCARRY_INTRINSIC(carry_in, a, b, r); +#elif defined(OPENSSL_AARCH64) && defined(_MSC_VER) && !defined(__clang__) + /* Manual 64-bit addition for MSVC ARM64 (no __uint128_t) */ + Limb sum = a + carry_in; + Carry carry1 = (sum < a) ? 1 : 0; /* carry from a + carry_in */ + *r = sum + b; + Carry carry2 = (*r < sum) ? 1 : 0; /* carry from sum + b */ + ret = carry1 | carry2; #else DoubleLimb x = (DoubleLimb)a + b + carry_in; *r = (Limb)x; @@ -74,6 +81,10 @@ static inline Carry limb_add(Limb *r, Limb a, Limb b) { Carry ret; #if defined(RING_CORE_ADDCARRY_INTRINSIC) ret = RING_CORE_ADDCARRY_INTRINSIC(0, a, b, r); +#elif defined(OPENSSL_AARCH64) && defined(_MSC_VER) && !defined(__clang__) + /* Manual 64-bit addition for MSVC ARM64 (no __uint128_t) */ + *r = a + b; + ret = (*r < a) ? 1 : 0; /* carry if result overflowed */ #else DoubleLimb x = (DoubleLimb)a + b; *r = (Limb)x; @@ -90,6 +101,13 @@ static inline Carry limb_sbb(Limb *r, Limb a, Limb b, Carry borrow_in) { Carry ret; #if defined(RING_CORE_SUBBORROW_INTRINSIC) ret = RING_CORE_SUBBORROW_INTRINSIC(borrow_in, a, b, r); +#elif defined(OPENSSL_AARCH64) && defined(_MSC_VER) && !defined(__clang__) + /* Manual 64-bit subtraction for MSVC ARM64 (no __uint128_t) */ + Limb temp = a - borrow_in; + Carry borrow1 = (temp > a) ? 1 : 0; /* borrow from a - borrow_in */ + *r = temp - b; + Carry borrow2 = (*r > temp) ? 1 : 0; /* borrow from temp - b */ + ret = borrow1 | borrow2; #else DoubleLimb x = (DoubleLimb)a - b - borrow_in; *r = (Limb)x; @@ -104,6 +122,10 @@ static inline Carry limb_sub(Limb *r, Limb a, Limb b) { Carry ret; #if defined(RING_CORE_SUBBORROW_INTRINSIC) ret = RING_CORE_SUBBORROW_INTRINSIC(0, a, b, r); +#elif defined(OPENSSL_AARCH64) && defined(_MSC_VER) && !defined(__clang__) + /* Manual 64-bit subtraction for MSVC ARM64 (no __uint128_t) */ + *r = a - b; + ret = (*r > a) ? 1 : 0; /* borrow if result underflowed */ #else DoubleLimb x = (DoubleLimb)a - b; *r = (Limb)x; diff --git a/mk/package.sh b/mk/package.sh index 9dd3cc14d1..ba2fb79f65 100644 --- a/mk/package.sh +++ b/mk/package.sh @@ -11,7 +11,7 @@ fi msrv=1.66.0 cargo clean --target-dir=target/pregenerate_asm -RING_PREGENERATE_ASM=1 CC_AARCH64_PC_WINDOWS_MSVC=clang \ +RING_PREGENERATE_ASM=1 \ cargo +${msrv} build -p ring --target-dir=target/pregenerate_asm if [[ -n "$(git status --porcelain -- ':(exclude)pregenerated/')" ]]; then echo Repository is dirty.