Skip to content

Conversation

@dennisameling
Copy link

@dennisameling dennisameling commented Aug 3, 2025

Closes #2674
Closes #2215

Currently, a workaround is in place to always build with Visual Studio's clang.exe instead of MSVC.

This is causing problems in some cases, because the aarch64-pc-windows-msvc target typically only uses MSVC-provided tooling and has no hard requirement on clang.exe at all. It gets even trickier when it tries to pass MSVC flags like /nologo, /DCOMPILER_MSVC, etc. to clang.exe, which it doesn't support. Sure, clang-cl.exe might work, but that can come with its own set of challenges.

I learned that the ring library basically has two ways of using it:

  1. Using packaged builds, where object files are pre-compiled using Perl, NASM, etc. - the user doesn't need to have these tools installed locally.
  2. Building from Git: the mentioned tools (Perl, NASM, etc.) need to run on the user's machine.

I was thinking:

What if we can use Visual Studio's clang.exe to pre-compile the object files, and then let users build the rest of the project with MSVC, so that it cleanly follows the aarch64-pc-windows-msvc target structure again?

This PR implements exactly that. With some minor changes, I was able to get things to compile using MSVC, using pre-compiled object files generated with Visual Studio's clang.exe.

CC @awakecoding @Alovchin91

- 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.

Comment on lines +63 to +69
#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;
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.

@Alovchin91
Copy link
Contributor

Alovchin91 commented Aug 3, 2025

FYI, the original idea was to use Clang-cl with the MSVC toolchain for 2 main reasons:

  1. Back then, the Nasm assembler was used (I believe, or maybe actually MASM, I don't remember anymore 🤔) which wasn't available for Windows Arm64, but Clang did provide asm support, so we used it.

  2. MSVC didn't have int128_t support (apparently, it's still the case 🤷‍♂️), but Clang did.

I'm not sure what specifically has changed in Ring 0.17 (a lot, from what I know), so maybe today it is easier to build it with MSVC.

@briansmith briansmith closed this Aug 4, 2025
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_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.

#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.

@MarijnS95
Copy link
Contributor

FYI, the original idea was to use Clang-cl with the MSVC toolchain for 2 main reasons:

@Alovchin91 just noting that it doesn't currently use clang-cl.exe but plain clang.exe, overwritten in such a way in cc-rs that it may be receiving CFLAGS that the user initially assumed / set up for cl.exe: #2215. That was proposed to be changed to clang-cl.exe in #2216, but it's all a bit hacky in case the user really has really intentionally set CC=clang.

@Alovchin91
Copy link
Contributor

Alovchin91 commented Aug 4, 2025

@MarijnS95 Right. I can hardly remember much of our discussion (you can find it here and maybe something also in #1167), but I guess maybe back in the v0.16 days the C code was compiled separately, so it didn't really matter whether we used clang or clang-cl. It seems now it matters 😅

I'm not sure if it's a good idea to move forward with MSVC since it apparently still lacks 128-bit support. But as a drop-in replacement, clang-cl should be used of course.

Is it really a problem though that the user could explicitly set CC to something random? 🤔 They could do it with MSVC, too.

Sorry for too many words, pulling this from memory takes a lot of effort 😅

@MarijnS95
Copy link
Contributor

@Alovchin91 reading through those links you seem to mostly mention clang-cl yet the end result uses clang.

the C code was compiled separately, so it didn't really matter whether we used clang or clang-cl

I don't understand exactly what you mean here with "separately". Since the compiler is replaced inside cc-rs, that crate has still taken into account the users' CFLAGS from the environment which the user might have purposefully set up for cl.exe. clang-cl.exe understands those while clang.exe does not.

That is also the reason to check what CC was already set to inside cc-rs. If the user purposefully chose clang.exe and set their CFLAGS to be compatible with that (and ring is currently compiling fine for them on Windows ARM64!), an unconditional switch to clang-cl.exe would break as their arguments are not compatible with the cl.exe-like interface.

@Alovchin91
Copy link
Contributor

Alovchin91 commented Aug 5, 2025

I don't understand exactly what you mean here with "separately".

I mean as a separate step. Indeed, I see the use of cc to get the compiler, but it might have been used only to get the compiler's executable just to run it as a Command later on. I think this is how the assembly code was compiled, since originally it used Nasm. But I didn't look into it much now so I might be confusing it with some other project.

Looking a bit more at that code, it might also have been that we simply relied on cc to tell us whether the compiler is "like MSVC" or "like Clang". Then I'm not sure why that wouldn't work with the environment flags, if cc respects those. Right, so we shouldn't set the compiler unconditionally then.

Probably this check should have covered that case:

if target.os == WINDOWS && target.arch == AARCH64 && !c.get_compiler().is_like_clang() {
    let _ = c.compiler("clang");
}

But then this is an actual mistake, because if it's !is_like_clang(), then it could indeed be MSVC and the CFLAGS might indeed have been set up as for MSVC 🤔

@MarijnS95
Copy link
Contributor

@Alovchin91 yeah, that .compiler() call is just a setter on cc::Build which will configure it to use a different Path/executable, while retaining the original CFLAGS set by the user.

Since this PR is closed, we better jump back to #2216 / #2215 / rust-lang/cc-rs#882 / rust-lang/cc-rs#1367 to discuss possible approaches and solutions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Conflicting C(XX)FLAGS when cross-compiling to aarch64-pc-windows-msvc Fails to build on Windows arm64 (tries to pass MSVC flags to clang.exe)

4 participants