Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 5 additions & 14 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't be needed anymore once rust-lang/cc-rs#1506 gets merged. It will then be possible to get the path to Visual Studio's clang.exe like this:

cc::windows_registry::find(asm_target, "clang.exe");

Copy link
Owner

@briansmith briansmith Aug 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's wait for the cc-rs changes. Then we can increase our cc-rs version dependency and rely on it to find this for us.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cc-rs change that I think we need is rust-lang/cc-rs#1367.

shell: bash

- run: sh mk/package.sh
Expand Down Expand Up @@ -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
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing this out; see PR #2675.


- 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

Expand Down Expand Up @@ -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 }}
Expand Down
24 changes: 5 additions & 19 deletions BUILDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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
Expand Down
70 changes: 55 additions & 15 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
}
}
}
Expand Down Expand Up @@ -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()
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think instead of this, it would be better to create two separate libraries: one for assembly code, and one for C code. Then configure the cc::Build for the assembly code library to force the use of clang as the C compiler for Windows AArch64. This way, we maximize the use of cc-rs's infrastructure, e.g. handling compiler options and whatnot.

// 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);
Expand Down
10 changes: 9 additions & 1 deletion crypto/fipsmodule/bn/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@

#include <ring-core/base.h>

#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 <intrin.h>
#pragma warning(pop)
#if defined(OPENSSL_X86_64)
#pragma intrinsic(_umul128)
#elif defined(OPENSSL_AARCH64)
#pragma intrinsic(__umulh)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why BoringSSL avoids doing this. My guess is they aren't testing AArch64 Windows builds using MSVC, but instead forcing the use of clang.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why BoringSSL avoids doing this

They actually do use it, looking at the most recent version of internal.h on their main branch: https://boringssl.googlesource.com/boringssl/+/refs/heads/main/crypto/fipsmodule/bn/internal.h#419

My guess is they aren't testing AArch64 Windows builds using MSVC, but instead forcing the use of clang.

They do build arm64 Windows using both MSVC and Clang in their CI. They even seem to prefer MSVC over Clang:

On Windows, MSVC from Visual Studio 2022 or later with Windows 10 SDK 2104 or later are supported, but using the latest versions is recommended. Recent versions of GCC (6.1+) and Clang should work on non-Windows platforms, and maybe on Windows too.

I've just configured an example pipeline in GitHub Actions to build BoringSSL using both MSVC and Clang on arm64. The build succeeds using both compilers. Note that with MSVC, OPENSSL_NO_ASM needs to be defined. They do that in their own CI setup as well.

#endif
#endif

#include "../../internal.h"
Expand Down Expand Up @@ -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;
Expand Down
24 changes: 23 additions & 1 deletion crypto/limbs/limbs.inl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Comment on lines +63 to +69
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the lack of __uint128_t on MSVC as well as not having _addcarry_u64 or _subborrow_u64 on MSVC arm64, I used AI to come up with this workaround and the ones below. I think this looks good, but would very much appreciate anyone with C knowledge to chime in here.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

used AI

I cannot accept PRs of unknown provenance.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upstream BoringSSL has some changes that we should bring in related to this. But, again, it looks like they are just avoiding MSVC on AArch64.

#else
DoubleLimb x = (DoubleLimb)a + b + carry_in;
*r = (Limb)x;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion mk/package.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down