diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b0adb41..0149aa0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,7 +29,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-24.04, macos-15, windows-2025] + os: [ubuntu-24.04, macos-15, windows-2025, windows-11-arm] runs-on: ${{ matrix.os }} defaults: run: @@ -99,6 +99,7 @@ jobs: - macos-15-intel - windows-2022 - windows-2025 + - windows-11-arm runs-on: ${{ matrix.os }} defaults: run: diff --git a/nss/action.yml b/nss/action.yml index fdc7f33..0a7a544 100644 --- a/nss/action.yml +++ b/nss/action.yml @@ -196,15 +196,27 @@ runs: - name: Install build dependencies (Windows) shell: bash if: ${{ runner.os == 'Windows' && steps.check_build.outputs.build_nss }} - run: | + env: + RUNNER_ARCH: ${{ runner.arch }} + run: | # zizmor: ignore[github-env] # shellcheck disable=SC2028 { echo C:/msys64/usr/bin - echo C:/msys64/mingw64/bin + # mingw64/bin provides x86_64 MinGW toolchain binaries; not used on ARM64 + # where the build uses MSVC exclusively. + [ "$RUNNER_ARCH" != "ARM64" ] && echo C:/msys64/mingw64/bin || true } >> "$GITHUB_PATH" - /c/msys64/usr/bin/pacman -S --noconfirm python3-pip nsinstall + mkdir -p "$HOME/bin" + # renovate: depName=firefox datasource=custom.firefox versioning=loose + curl --proto '=https' --tlsv1.2 -o "$HOME/bin/nsinstall.py" -sSf https://hg-edge.mozilla.org/releases/mozilla-release/raw-file/FIREFOX_149_0_2_RELEASE/config/nsinstall.py + echo "f88df5591ee50d6e86625efd6e5f9c7b0dd4cba61e27ab533b1990115e100caf $HOME/bin/nsinstall.py" | sha256sum -c - + echo "#! /usr/bin/env python3" | cat - "$HOME/bin/nsinstall.py" > "$HOME/bin/nsinstall" + chmod +x "$HOME/bin/nsinstall" + echo "$HOME/bin" >> "$GITHUB_PATH" + # gyp-next>=0.22.0 added Windows ARM64 target support (nodejs/gyp-next#331). # renovate: depName=gyp-next datasource=pypi echo "gyp-next==0.22.2" > req.txt + echo "mozfile" >> req.txt # required by nsinstall.py python3 -m pip install -r req.txt - name: Set up MSVC (Windows) @@ -212,20 +224,13 @@ runs: uses: ilammy/msvc-dev-cmd@v1 # zizmor: ignore[unpinned-uses] # TODO: Would like to pin this, but the Mozilla org allowlist requires "ilammy/msvc-dev-cmd@v1*" # uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0 + with: + arch: ${{ runner.arch == 'ARM64' && 'arm64' || 'amd64' }} - name: Set up build environment (Windows) shell: bash if: ${{ runner.os == 'Windows' && steps.check_build.outputs.build_nss }} run: | - # Work around NSS's coreconf/msvc.sh bug with MSVC 18 (VS 2026): - # Set VSPATH so msvc.sh skips its setup branch, and configure the gyp overrides ourselves. - if [ "${VSCMD_VER%%.*}" = "18" ]; then - { - echo "VSPATH=$VSINSTALLDIR" - echo "GYP_MSVS_OVERRIDE_PATH=$VSINSTALLDIR" - echo "GYP_MSVS_VERSION=2026" - } >> "$GITHUB_ENV" - fi # See https://github.com/ilammy/msvc-dev-cmd#name-conflicts-with-shell-bash rm /usr/bin/link.exe || true @@ -255,6 +260,7 @@ runs: env: TARGET_PLATFORM: ${{ inputs.target }} RUNNER_OS: ${{ runner.os }} + RUNNER_ARCH: ${{ runner.arch }} run: | # We want to do an optimized build for accurate CPU profiling, but # we also want debug symbols and frame pointers for that, which the normal optimized NSS @@ -312,7 +318,25 @@ runs: cp -vaL "$LIB_DIR"/* "dist/Release/lib" else [ "$SCCACHE_CC" ] && [ "$SCCACHE_CXX" ] && export CC="$SCCACHE_CC" CXX="$SCCACHE_CXX" - $NSS_DIR/build.sh -g -Ddisable_tests=1 -Ddisable_dbm=1 -Ddisable_libpkix=1 -Ddisable_ckbi=1 -Ddisable_fips=1 --opt --static + NSS_EXTRA="" + if [ "$RUNNER_OS" == "Windows" ]; then + # nss-msvc-arm64.patch also fixes VS 2026 compatibility on all Windows + # targets, so it is applied unconditionally. + patch -p1 < "$GITHUB_ACTION_PATH/patches/nss-msvc-arm64.patch" + fi + if [ "$RUNNER_OS" == "Windows" ] && [ "$RUNNER_ARCH" == "ARM64" ]; then + # Apply ARM64-specific patches to NSPR and NSS. + # Each patch file can be removed individually as upstream adds support, + # except nss-arm64-gyp-and-aes.patch which bundles two coupled changes. + for p in "$GITHUB_ACTION_PATH/patches/"*.patch; do + [ "$p" = "$GITHUB_ACTION_PATH/patches/nss-msvc-arm64.patch" ] && continue + patch -p1 < "$p" + done + + NSS_EXTRA="--target=arm64" + fi + # shellcheck disable=SC2086 + $NSS_DIR/build.sh -g -Ddisable_tests=1 -Ddisable_dbm=1 -Ddisable_libpkix=1 -Ddisable_ckbi=1 -Ddisable_fips=1 --opt --static $NSS_EXTRA fi - name: Save NSS cache diff --git a/nss/patches/nspr-detect-arm64-cpu.patch b/nss/patches/nspr-detect-arm64-cpu.patch new file mode 100644 index 0000000..d35ef00 --- /dev/null +++ b/nss/patches/nspr-detect-arm64-cpu.patch @@ -0,0 +1,24 @@ +# Fix CPU architecture detection in NSPR configure for Windows ARM64. +# +# When NSS builds NSPR with --target=arm64, nspr.sh passes +# --host=aarch64-pc-mingw32 which activates configure's cross-compile path. +# That path hardcoded CPU_ARCH=x86 for all mingw* targets regardless of +# target_cpu. Use target_cpu instead so aarch64 cross-compile targets are +# handled correctly. +# +# Removable once NSPR configure handles ARM64 in the mingw* cross-compile case. +--- a/nspr/configure ++++ b/nspr/configure +@@ -6647,7 +6647,12 @@ + linux*) OS_ARCH=Linux ;; + solaris*) OS_ARCH=SunOS OS_RELEASE=5 ;; +- mingw*) OS_ARCH=WINNT CPU_ARCH=x86 ;; ++ mingw*) ++ OS_ARCH=WINNT ++ case "$target_cpu" in ++ aarch64) CPU_ARCH=aarch64; USE_64=1 ;; ++ *) CPU_ARCH=x86 ;; ++ esac ;; + cygwin*) OS_ARCH=WINNT ;; + darwin*) OS_ARCH=Darwin ;; + riscos*) OS_ARCH=RISCOS ;; diff --git a/nss/patches/nspr-ntmisc-arm64.patch b/nss/patches/nspr-ntmisc-arm64.patch new file mode 100644 index 0000000..73cad9b --- /dev/null +++ b/nss/patches/nspr-ntmisc-arm64.patch @@ -0,0 +1,49 @@ +# Add MSVC ARM64 implementations for PR_StackPush and PR_StackPop in ntmisc.c. +# +# The existing MSVC code path uses x86 inline assembly (__asm { ... }) which +# the ARM64 MSVC compiler does not support. Add a _M_ARM64 branch using +# _InterlockedExchangePointer (ARM64 compiler intrinsic, available via +# already included by _winnt.h) to implement the same spin-lock +# semantics as the x86 xchg instruction. +# +# Removable once NSPR provides ARM64 implementations upstream. +--- a/nspr/pr/src/md/windows/ntmisc.c ++++ b/nspr/pr/src/md/windows/ntmisc.c +@@ -1075,6 +1075,15 @@ + *(void**)stack_elem = tmp; + __asm__("" : : : "memory"); + *tos = stack_elem; ++# elif defined(_M_ARM64) ++ void* volatile* tos = (void* volatile*)stack; ++ void* tmp; ++retry: ++ if (*tos == (void*)-1) goto retry; ++ tmp = _InterlockedExchangePointer(tos, (void*)-1); ++ if (tmp == (void*)-1) goto retry; ++ *(void**)stack_elem = tmp; ++ _InterlockedExchangePointer(tos, stack_elem); + # else + __asm + { +@@ -1119,6 +1128,21 @@ + *tos = tmp; + } + ++ return tmp; ++# elif defined(_M_ARM64) ++ void* volatile* tos = (void* volatile*)stack; ++ void* tmp; ++retry: ++ if (*tos == (void*)-1) goto retry; ++ tmp = _InterlockedExchangePointer(tos, (void*)-1); ++ if (tmp == (void*)-1) goto retry; ++ if (tmp != (void*)0) { ++ void* next = *(void**)tmp; ++ _InterlockedExchangePointer(tos, next); ++ *(void**)tmp = 0; ++ } else { ++ _InterlockedExchangePointer(tos, tmp); ++ } + return tmp; + # else + __asm diff --git a/nss/patches/nspr-skip-rc-compilation.patch b/nss/patches/nspr-skip-rc-compilation.patch new file mode 100644 index 0000000..a3f44b2 --- /dev/null +++ b/nss/patches/nspr-skip-rc-compilation.patch @@ -0,0 +1,25 @@ +# Skip NSPR resource file compilation on Windows ARM64. +# +# NSPR's Makefile builds nspr.rc into a .res file via rc.exe. On ARM64, +# rc.exe's preprocessor cannot parse ARM64-specific constructs in NSPR's +# headers and fails. The resource file is only needed for shared-library +# version metadata and is not required for the static build we produce. +# Suppressing it on ARM64 only (via CPU_ARCH=aarch64) leaves x64/x86 +# builds unaffected. +# +# Removable once NSPR's Windows Makefile handles ARM64 resource compilation. +--- a/nspr/pr/src/Makefile.in ++++ b/nspr/pr/src/Makefile.in +@@ -245,7 +245,11 @@ + endif + + ifeq ($(OS_ARCH), WINNT) +-RES=$(OBJDIR)/nspr.res ++ifeq ($(CPU_ARCH), aarch64) ++RES= ++else ++RES=$(OBJDIR)/nspr.res ++endif + RESNAME=nspr.rc + endif # WINNT + diff --git a/nss/patches/nspr-winnt-arch-arm64.patch b/nss/patches/nspr-winnt-arch-arm64.patch new file mode 100644 index 0000000..0ae15b4 --- /dev/null +++ b/nss/patches/nspr-winnt-arch-arm64.patch @@ -0,0 +1,17 @@ +# Add ARM64 to _PR_SI_ARCHITECTURE detection in _winnt.h. +# +# _winnt.h defines _PR_SI_ARCHITECTURE for x86, x86-64, and ia64, but +# has a #error fallthrough for any other architecture. Without this patch +# every ARM64 compilation fails with "unknown processor architecture". +# +# Removable once NSPR adds ARM64 support to _winnt.h upstream. +--- a/nspr/pr/include/md/_winnt.h ++++ b/nspr/pr/include/md/_winnt.h +@@ -45,6 +45,8 @@ + #elif defined(_M_IA64) || defined(_IA64_) + #define _PR_SI_ARCHITECTURE "ia64" ++#elif defined(_M_ARM64) || defined(_ARM64_) ++#define _PR_SI_ARCHITECTURE "aarch64" + #else + #error unknown processor architecture + #endif diff --git a/nss/patches/nspr-winnt-cfg-arm64.patch b/nss/patches/nspr-winnt-cfg-arm64.patch new file mode 100644 index 0000000..e03e363 --- /dev/null +++ b/nss/patches/nspr-winnt-cfg-arm64.patch @@ -0,0 +1,18 @@ +# Extend the 64-bit platform branch in _winnt.cfg to cover ARM64. +# +# NSPR's _winnt.cfg uses _M_X64 / _M_AMD64 / _AMD64_ to select the 64-bit +# configuration (IS_64, pointer sizes, etc.). ARM64 uses the same LLP64 ABI +# as x64 (64-bit pointers, 32-bit long) and belongs on the same branch. +# +# Removable once NSPR adds _M_ARM64 / _ARM64_ support upstream. +--- a/nspr/pr/include/md/_winnt.cfg ++++ b/nspr/pr/include/md/_winnt.cfg +@@ -68,7 +68,7 @@ + #define PR_BYTES_PER_WORD_LOG2 2 + #define PR_BYTES_PER_DWORD_LOG2 2 + +-#elif defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_) ++#elif defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_) || defined(_M_ARM64) || defined(_ARM64_) + + #define IS_LITTLE_ENDIAN 1 + #undef IS_BIG_ENDIAN diff --git a/nss/patches/nspr-winnt-interlocked-intrinsics.patch b/nss/patches/nspr-winnt-interlocked-intrinsics.patch new file mode 100644 index 0000000..72ec536 --- /dev/null +++ b/nss/patches/nspr-winnt-interlocked-intrinsics.patch @@ -0,0 +1,28 @@ +# Redirect Win32 Interlocked* exports to _Interlocked* compiler intrinsics. +# +# On ARM64 Windows, kernel32.lib does not export the Win32 Interlocked* +# functions as callable symbols; they are only available as compiler +# intrinsics (underscore-prefixed). Placing redirects immediately after +# rewrites every call site in NSPR via the preprocessor: +# +# - _MD_ATOMIC_* macros in _winnt.h (all builds) +# - _MD_LOCK lock-contention counters in _winnt.h (#ifdef PROFILE_LOCKS) +# - InterlockedCompareExchange called directly in ntio.c and prmwait.c +# +# Removable once NSPR uses compiler intrinsics unconditionally on Windows. +--- a/nspr/pr/include/md/_winnt.h ++++ b/nspr/pr/include/md/_winnt.h +@@ -17,6 +17,13 @@ + #endif /* _WIN32_WINNT */ + + #include ++/* ARM64 kernel32.lib does not export Interlocked* as callable symbols; ++ * redirect all call sites to the equivalent compiler intrinsics. */ ++#define InterlockedCompareExchange _InterlockedCompareExchange ++#define InterlockedIncrement _InterlockedIncrement ++#define InterlockedDecrement _InterlockedDecrement ++#define InterlockedExchange _InterlockedExchange ++#define InterlockedExchangeAdd _InterlockedExchangeAdd + #include + #ifdef __MINGW32__ + #include diff --git a/nss/patches/nss-arm64-gyp-and-aes.patch b/nss/patches/nss-arm64-gyp-and-aes.patch new file mode 100644 index 0000000..91ec05f --- /dev/null +++ b/nss/patches/nss-arm64-gyp-and-aes.patch @@ -0,0 +1,65 @@ +# Add ARM64 MSVC support to NSS's gyp configuration and AES implementation. +# +# These two changes are coupled and must be applied or removed together: +# aes-armv8.c has a `#ifndef __ARM_FEATURE_CRYPTO / #error` guard; that +# macro is only defined via the PreprocessorDefinitions added to config.gypi. +# +# config.gypi: NSS's config.gypi has per-arch MSVS settings for ia32 and x64 +# but not ARM64. Without this block, gyp leaves msvs_configuration_platform +# unset for ARM64 (defaults to x86), causing ninja to use the wrong environment +# and tool paths. gyp-next >= 0.22.0 handles ARM64 toolchain discovery +# (environment.arm64, armasm64.exe), but NSS still needs its own preprocessor +# definitions here. /EHsc matches what the ia32 and x64 blocks already set. +# +# aes-armv8.c: guards its entire body with a preprocessor condition that only +# matches GCC/Clang. MSVC ARM64 supports the same NEON AES intrinsics +# (vaeseq_u8, vaesdq_u8, etc.) but does not define __clang__ or __GNUC__. +# IS_LITTLE_ENDIAN is included for consistency with the ghash patch — Windows +# ARM64 is always little-endian, but the explicit guard matches intent. +# Also adds a no-op __builtin_assume_aligned compat macro. +# +# Removable once NSS adds ARM64 MSVC support to config.gypi and aes-armv8.c. +--- a/nss/coreconf/config.gypi ++++ b/nss/coreconf/config.gypi +@@ -589,6 +589,19 @@ + }, + }, + }], ++ [ 'target_arch=="arm64"', { ++ 'msvs_configuration_platform': 'ARM64', ++ 'msvs_settings': { ++ 'VCCLCompilerTool': { ++ 'PreprocessorDefinitions': [ ++ 'WIN64', ++ '_ARM64_', ++ '__ARM_FEATURE_CRYPTO', ++ ], ++ 'AdditionalOptions': [ '/EHsc' ], ++ }, ++ }, ++ }], + ], + }], + [ 'disable_dbm==1', { +--- a/nss/lib/freebl/aes-armv8.c ++++ b/nss/lib/freebl/aes-armv8.c +@@ -5,10 +5,15 @@ + #include "secerr.h" + #include "rijndael.h" + +-#if ((defined(__clang__) || \ +- (defined(__GNUC__) && defined(__GNUC_MINOR__) && \ +- (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 8)))) && \ +- defined(IS_LITTLE_ENDIAN)) ++#if (((defined(__clang__) || \ ++ (defined(__GNUC__) && defined(__GNUC_MINOR__) && \ ++ (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 8)))) && \ ++ defined(IS_LITTLE_ENDIAN)) || \ ++ (defined(_MSC_VER) && defined(_M_ARM64) && defined(IS_LITTLE_ENDIAN))) ++ ++#ifdef _MSC_VER ++#define __builtin_assume_aligned(ptr, align) (ptr) ++#endif + + #ifndef __ARM_FEATURE_CRYPTO + #error "Compiler option is invalid" diff --git a/nss/patches/nss-blinit-arm64.patch b/nss/patches/nss-blinit-arm64.patch new file mode 100644 index 0000000..9080dbe --- /dev/null +++ b/nss/patches/nss-blinit-arm64.patch @@ -0,0 +1,40 @@ +# Extend __aarch64__ guards in blinit.c to cover MSVC ARM64 (_M_ARM64). +# +# blinit.c uses __aarch64__ in three places where MSVC ARM64 needs to be +# included: +# 1. The _WIN64 + __aarch64__ guard for including (needed for +# IsProcessorFeaturePresent used inside CheckARMSupport on Windows). +# 2. The standalone #if defined(__aarch64__) that gates the ARM64-specific +# CheckARMSupport function definition. +# 3. The FreeblInit() dispatch that calls CheckARMSupport(). +# +# Removable once NSS adds _M_ARM64 checks to blinit.c upstream. +--- a/nss/lib/freebl/blinit.c ++++ b/nss/lib/freebl/blinit.c +@@ -17,7 +17,7 @@ + #include /* for _xgetbv() */ + #endif + +-#if defined(_WIN64) && defined(__aarch64__) ++#if defined(_WIN64) && (defined(__aarch64__) || defined(_M_ARM64)) + #include + #endif + +@@ -186,7 +186,7 @@ + #endif /* defined(__aarch64__) || defined(__arm__) */ + /* clang-format on */ + +-#if defined(__aarch64__) ++#if defined(__aarch64__) || defined(_M_ARM64) + + #if defined(__linux__) + // Defines from hwcap.h in Linux kernel - ARM64 +@@ -557,7 +557,7 @@ + { + #ifdef NSS_X86_OR_X64 + CheckX86CPUSupport(); +-#elif (defined(__aarch64__) || defined(__arm__)) ++#elif (defined(__aarch64__) || defined(__arm__) || defined(_M_ARM64)) + CheckARMSupport(); + #elif (defined(__powerpc__)) + CheckPPCSupport(); diff --git a/nss/patches/nss-gcm-h-arm64.patch b/nss/patches/nss-gcm-h-arm64.patch new file mode 100644 index 0000000..c91cddc --- /dev/null +++ b/nss/patches/nss-gcm-h-arm64.patch @@ -0,0 +1,31 @@ +# Extend __aarch64__ guards in gcm.h to cover MSVC ARM64 (_M_ARM64). +# +# gcm.h uses __aarch64__ in two places: +# 1. To include (needed for uint64x2_t). +# 2. To declare uint64x2_t struct members in gcmHashContextStr. +# +# MSVC ARM64 defines _M_ARM64 instead of __aarch64__. Without this patch +# the struct members are absent and ghash-aarch64.c's accesses (ghash->x, +# ghash->h) corrupt memory or fail to compile. +# +# Removable once NSS adds _M_ARM64 checks to gcm.h upstream. +--- a/nss/lib/freebl/gcm.h ++++ b/nss/lib/freebl/gcm.h +@@ -27,7 +27,7 @@ + #endif /* NSS_DISABLE_SSE2 */ + #endif + +-#ifdef __aarch64__ ++#if defined(__aarch64__) || defined(_M_ARM64) + #include + #endif + +@@ -82,7 +82,7 @@ + pre_align struct gcmHashContextStr { + #ifdef NSS_X86_OR_X64 + __m128i x, h; +-#elif defined(__aarch64__) ++#elif defined(__aarch64__) || defined(_M_ARM64) + uint64x2_t x, h; + #elif defined(USE_PPC_CRYPTO) + vec_u64 x, h; diff --git a/nss/patches/nss-ghash-aarch64-msvc.patch b/nss/patches/nss-ghash-aarch64-msvc.patch new file mode 100644 index 0000000..7f91c20 --- /dev/null +++ b/nss/patches/nss-ghash-aarch64-msvc.patch @@ -0,0 +1,68 @@ +# Enable GHASH-AArch64 code path under MSVC ARM64. +# +# ghash-aarch64.c guards its body with __aarch64__ && (clang||gcc). MSVC +# ARM64 defines _M_ARM64 instead of __aarch64__ and supports the same +# poly64x2_t PMULL intrinsics. +# +# Three changes: +# 1. Extend the top-level guard to match MSVC ARM64. +# 2. Add a __builtin_bswap64 compat: MSVC provides _byteswap_uint64 from +# instead. +# 3. Remove the (poly64_t) C-style casts on vget_low_p64 results. GCC/Clang +# vmull_p64 expects poly64_t scalars; MSVC expects poly64x1_t vectors. +# vget_low_p64 returns poly64x1_t, and MSVC rejects C-style casts to union +# types. Dropping the cast works with both compilers. +# +# Removable once NSS extends ghash-aarch64.c to MSVC upstream. +--- a/nss/lib/freebl/ghash-aarch64.c ++++ b/nss/lib/freebl/ghash-aarch64.c +@@ -9,11 +9,16 @@ + #include "secerr.h" + + /* old gcc doesn't support some poly64x2_t intrinsic */ +-#if defined(__aarch64__) && defined(IS_LITTLE_ENDIAN) && \ +- (defined(__clang__) || defined(__GNUC__) && __GNUC__ > 6) ++#if (defined(__aarch64__) || defined(_M_ARM64)) && defined(IS_LITTLE_ENDIAN) && \ ++ (defined(__clang__) || (defined(__GNUC__) && __GNUC__ > 6) || defined(_MSC_VER)) + + #include + ++#ifdef _MSC_VER ++#include ++#define __builtin_bswap64(x) _byteswap_uint64(x) ++#endif ++ + PRBool + platform_ghash_support() + { +@@ -48,15 +53,15 @@ + + /* Do binary mult ghash->X = Ci * ghash->H. */ + z_low = vreinterpretq_u8_p128( +- vmull_p64((poly64_t)vget_low_p64(vreinterpretq_p64_u64(ci)), +- (poly64_t)vget_low_p64(vreinterpretq_p64_u64(h)))); ++ vmull_p64(vget_low_p64(vreinterpretq_p64_u64(ci)), ++ vget_low_p64(vreinterpretq_p64_u64(h)))); + z_high = vreinterpretq_u8_p128( + vmull_high_p64(vreinterpretq_p64_u64(ci), vreinterpretq_p64_u64(h))); + t1 = vreinterpretq_p64_u8( + vextq_u8(vreinterpretq_u8_u64(h), vreinterpretq_u8_u64(h), 8)); + t_low = vreinterpretq_u8_p128( +- vmull_p64((poly64_t)vget_low_p64(vreinterpretq_p64_u64(ci)), +- (poly64_t)vget_low_p64(t1))); ++ vmull_p64(vget_low_p64(vreinterpretq_p64_u64(ci)), ++ vget_low_p64(t1))); + t_high = vreinterpretq_u8_p128(vmull_high_p64(vreinterpretq_p64_u64(ci), t1)); + t2 = veorq_u8(t_high, t_low); + z_low = veorq_u8(z_low, vextq_u8(zero, t2, 8)); +@@ -68,8 +73,8 @@ + z_low = veorq_u8(z_low, vextq_u8(zero, t2, 8)); + ci = veorq_u64(vreinterpretq_u64_u8(z_low), + vreinterpretq_u64_p128( +- vmull_p64((poly64_t)vget_low_p64(vreinterpretq_p64_u8(z_high)), +- (poly64_t)vget_low_p64(p)))); ++ vmull_p64(vget_low_p64(vreinterpretq_p64_u8(z_high)), ++ vget_low_p64(p)))); + } + + ghash->x = ci; diff --git a/nss/patches/nss-msvc-arm64.patch b/nss/patches/nss-msvc-arm64.patch new file mode 100644 index 0000000..3a423e0 --- /dev/null +++ b/nss/patches/nss-msvc-arm64.patch @@ -0,0 +1,106 @@ +# Add ARM64 support and fix VS 2026 compatibility in NSS's coreconf/msvc.sh. +# +# Three changes: +# +# 1. Add aarch64 to the target_arch case statement so msvc.sh doesn't abort +# with "No support for target" on ARM64 builds. Maps to "arm64" (the +# MSVC/Windows convention) rather than "aarch64" (the Linux/GNU convention). +# +# 2. Fix VS 2026 compatibility: catalog_productLineVersion returned year strings +# ("2017", "2019", "2022") up to VS 2022, but returns the major version number +# ("18") for VS 2026+. Handle the major-number form explicitly: map it to the +# correct CRT redist directory (VC144) and a gyp-next-compatible version string +# ("2022", since gyp-next doesn't yet recognise VS 2026). For pre-2026 +# releases the year string is still used as before. +# +# Also fixes WIN32_REDIST_DIR: the old code hardcoded "Microsoft.VC141.CRT" +# regardless of VS version. The CRT dir encodes the VS generation, so the +# name is now derived from the version query above. +# +# 3. Add PATH entries for the ARM64 host/target compiler bin directories, +# matching the existing pattern for x86 cross-compilation. +# +# As a side effect, the GYP_MSVS_VERSION assignment reuses VS_YEAR from +# change 2, avoiding a second vswhere invocation. +diff --git a/coreconf/msvc.sh b/coreconf/msvc.sh +index a592279c9..5a5cff7ea 100644 +--- a/nss/coreconf/msvc.sh ++++ b/nss/coreconf/msvc.sh +@@ -45,7 +45,9 @@ regquery() { + } + + VSCOMPONENT=Microsoft.VisualStudio.Component.VC.Tools.x86.x64 +-vsinstall=$(vswhere -latest -requires "$VSCOMPONENT" -property installationPath) ++# Use -latest without -requires so the setup block runs on ARM64 runners where ++# only the ARM64 toolchain component is installed (not the x86/x64 one). ++vsinstall=$(vswhere -latest -property installationPath) + + # Attempt to setup paths if vswhere returns something and VSPATH isn't set. + # Otherwise, assume that the env is setup. +@@ -54,6 +56,7 @@ if [[ -n "$vsinstall" && -z "$VSPATH" ]]; then + case "$target_arch" in + ia32) m=x86 ;; + x64) m="$target_arch" ;; ++ aarch64|arm64) m=arm64 ;; + *) + echo "No support for target '$target_arch' with MSVC." 1>&2 + exit 1 +@@ -71,12 +74,28 @@ if [[ -n "$vsinstall" && -z "$VSPATH" ]]; then + + VCVER=$(cat "${VCINSTALLDIR}/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt") + REDISTVER=$(cat "${VCINSTALLDIR}/Auxiliary/Build/Microsoft.VCRedistVersion.default.txt") +- export WIN32_REDIST_DIR="${VCINSTALLDIR}/Redist/MSVC/${REDISTVER}/${m}/Microsoft.VC141.CRT" ++ ++ # Use installationVersion (always populated) to get the VS major version. ++ # catalog_productLineVersion is unreliable: it returns a bare major number ++ # for VS 2026+ and may be empty on some configurations. ++ vs_major=$(vswhere -latest -requires "$VSCOMPONENT" -property installationVersion | cut -d. -f1) ++ if [ "$vs_major" = "18" ]; then ++ # VS 2026: catalog_productLineVersion returns the major version number ++ # rather than a year string; supply the year explicitly. ++ vc_crt=144; gyp_ver=2026 ++ else ++ VS_YEAR=$(vswhere -latest -requires "$VSCOMPONENT" -property catalog_productLineVersion) ++ vc_crt=141; gyp_ver="${VS_YEAR:-2022}" ++ fi ++ export WIN32_REDIST_DIR="${VCINSTALLDIR}/Redist/MSVC/${REDISTVER}/${m}/Microsoft.VC${vc_crt}.CRT" + export WIN_UCRT_REDIST_DIR="${UniversalCRTSdkDir}/Redist/ucrt/DLLs/${m}" + + if [ "$m" == "x86" ]; then + PATH="${PATH}:${VCINSTALLDIR}/Tools/MSVC/${VCVER}/bin/Hostx64/x64" + PATH="${PATH}:${VCINSTALLDIR}/Tools/MSVC/${VCVER}/bin/Hostx64/x86" ++ elif [ "$m" == "arm64" ]; then ++ PATH="${PATH}:${VCINSTALLDIR}/Tools/MSVC/${VCVER}/bin/Hostx64/x64" ++ PATH="${PATH}:${VCINSTALLDIR}/Tools/MSVC/${VCVER}/bin/Hostx64/arm64" + fi + PATH="${PATH}:${VCINSTALLDIR}/Tools/MSVC/${VCVER}/bin/Host${m}/${m}" + PATH="${PATH}:${UniversalCRTSdkDir}/bin/${UCRTVersion}/${m}" +@@ -98,9 +117,26 @@ if [[ -n "$vsinstall" && -z "$VSPATH" ]]; then + LIB="${LIB}:${UniversalCRTSdkDir}/lib/${UCRTVersion}/um/${m}" + export LIB + +- export GYP_MSVS_OVERRIDE_PATH="${VSPATH}" +- export GYP_MSVS_VERSION=$(vswhere -latest -requires "$VSCOMPONENT" -property catalog_productLineVersion) ++ # Use the original Windows-format path from vswhere (not the fixpath-converted ++ # Unix-style VSPATH) so that native ARM64 Python can resolve the directory. ++ export GYP_MSVS_OVERRIDE_PATH="${vsinstall}" ++ export GYP_MSVS_VERSION="${gyp_ver}" + else + echo Assuming env setup is already done. + echo VSPATH=$VSPATH ++ # When setup was done externally (e.g. by vcvarsall.bat), gyp still needs ++ # GYP_MSVS_VERSION. VSCMD_VER is always set by vcvarsall.bat so use it ++ # as the condition; VSINSTALLDIR may be empty on some runner configurations. ++ if [ -n "$VSCMD_VER" ] && [ -z "$GYP_MSVS_VERSION" ]; then ++ # vswhere was unavailable; derive version from VSCMD_VER (set by vcvarsall.bat). ++ # Only VS 2026 needs special handling (returns major "18" not year string). ++ if [ "${VSCMD_VER%%.*}" = "18" ]; then ++ gyp_ver=2026 ++ else ++ gyp_ver=$(vswhere -latest -property catalog_productLineVersion 2>/dev/null || true) ++ gyp_ver="${gyp_ver:-$VSCMD_VER}" ++ fi ++ export GYP_MSVS_VERSION="${gyp_ver}" ++ [ -n "$VSINSTALLDIR" ] && export GYP_MSVS_OVERRIDE_PATH="${VSINSTALLDIR}" ++ fi + fi diff --git a/nss/patches/nss-nspr-arm64.patch b/nss/patches/nss-nspr-arm64.patch new file mode 100644 index 0000000..eff3656 --- /dev/null +++ b/nss/patches/nss-nspr-arm64.patch @@ -0,0 +1,24 @@ +# Pass correct flags to NSPR configure when building for Windows ARM64. +# +# nspr.sh passes --enable-64bit for x64 targets but nothing for aarch64, +# leaving NSPR configure to detect the arch from uname -m — which returns +# "x86_64" on ARM64 Windows runners where the shell is x64 under emulation. +# +# --enable-64bit enables 64-bit pointer/word size. +# --host=aarch64-pc-mingw32 activates configure's cross-compile path so +# that target_cpu=aarch64 is set and the mingw* case selects ARM64 flags. +# +# Removable once NSS's nspr.sh handles ARM64 natively. +diff --git a/coreconf/nspr.sh b/coreconf/nspr.sh +index 28b9c4d47..bb36f0ce0 100644 +--- a/nss/coreconf/nspr.sh ++++ b/nss/coreconf/nspr.sh +@@ -34,6 +34,8 @@ nspr_build() + fi + if [ "$target_arch" = "x64" ]; then + extra_params+=(--enable-64bit) ++ elif [ "$target_arch" = "arm64" ] || [ "$target_arch" = "aarch64" ]; then ++ extra_params+=(--enable-64bit --host=aarch64-pc-mingw32) + fi + + if [[ -n "$CC" && -n "$build_tools_cc" && "$CC" != "$build_tools_cc" ]]; then diff --git a/nss/patches/nss-sha-fast-arm64.patch b/nss/patches/nss-sha-fast-arm64.patch new file mode 100644 index 0000000..99f76d9 --- /dev/null +++ b/nss/patches/nss-sha-fast-arm64.patch @@ -0,0 +1,23 @@ +# Exclude _M_ARM64 (MSVC ARM64) from the 64-bit SHA_HW_t typedef. +# +# sha_fast.h defines SHA_HW_t as PRUint64 on 64-bit platforms, except +# __aarch64__ (GCC/Clang ARM64). The exclusion exists because the ARM HW +# SHA-1 path (sha1-armv8.c) loads hash state with vld1q_u32, expecting four +# contiguous 32-bit values. If SHA_HW_t were PRUint64 the array would have +# 8-byte stride and vld1q_u32 would load {A_lo, A_hi, B_lo, B_hi} instead of +# {A, B, C, D}, corrupting state. +# +# MSVC ARM64 uses _M_ARM64, not __aarch64__, so it must be excluded too. +# +# Removable once NSS adds _M_ARM64 to the IS_64 exclusion upstream. +--- a/nss/lib/freebl/sha_fast.h ++++ b/nss/lib/freebl/sha_fast.h +@@ -10,7 +10,7 @@ + + #define SHA1_INPUT_LEN 64 + +-#if defined(IS_64) && !defined(__sparc) && !defined(__aarch64__) ++#if defined(IS_64) && !defined(__sparc) && !defined(__aarch64__) && !defined(_M_ARM64) + typedef PRUint64 SHA_HW_t; + #define SHA1_USING_64_BIT 1 + #else diff --git a/nss/patches/nss-sha256-armv8-msvc-align.patch b/nss/patches/nss-sha256-armv8-msvc-align.patch new file mode 100644 index 0000000..77428d6 --- /dev/null +++ b/nss/patches/nss-sha256-armv8-msvc-align.patch @@ -0,0 +1,32 @@ +# Replace __attribute__((aligned(16))) with an MSVC-compatible equivalent. +# +# MSVC does not support GCC/Clang's __attribute__ syntax. The K256 constant +# array in sha256-armv8.c uses __attribute__((aligned(16))) which causes a +# compilation error under MSVC. Replace it with a portable macro that expands +# to __declspec(align(16)) on MSVC and __attribute__((aligned(16))) elsewhere. +# +# Removable once NSS adds MSVC alignment compat to sha256-armv8.c upstream. +--- a/nss/lib/freebl/sha256-armv8.c ++++ b/nss/lib/freebl/sha256-armv8.c +@@ -8,6 +8,12 @@ + #error "Compiler option is invalid" + #endif + ++#ifdef _MSC_VER ++#define SHA_ALIGN16 __declspec(align(16)) ++#else ++#define SHA_ALIGN16 __attribute__((aligned(16))) ++#endif ++ + #ifdef FREEBL_NO_DEPEND + #include "stubs.h" + #endif +@@ -21,7 +27,7 @@ + #include + + /* SHA-256 constants, K256. */ +-static const PRUint32 __attribute__((aligned(16))) K256[64] = { ++static const SHA_ALIGN16 PRUint32 K256[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, diff --git a/renovate.json b/renovate.json index 613933e..5ca30ca 100644 --- a/renovate.json +++ b/renovate.json @@ -19,6 +19,12 @@ "transformTemplates": [ "{\"releases\": releases[$match(version, /v\\d+\\.\\d+/)].{\"version\": $replace($replace(version, /^(?:.*\\/)?v/, \"\"), /\\/$/, \"\")}}" ] + }, + "firefox": { + "defaultRegistryUrlTemplate": "https://hg-edge.mozilla.org/releases/mozilla-release/json-tags", + "transformTemplates": [ + "{\"releases\": tags[$match(tag, /^FIREFOX_\\d+_\\d+(_\\d+)?_RELEASE$/)].{\"version\": tag}}" + ] } }, "customManagers": [ @@ -35,16 +41,16 @@ "# renovate: depName=(?\\S+) datasource=(?\\S+)(?: versioning=(?\\S+))?\\n\\s+[A-Z_]+=\"v(?[0-9][0-9.]*)\"", "# renovate: depName=(?\\S+) datasource=(?\\S+)(?: versioning=(?\\S+))?\\n[A-Z_]+=(?[0-9][0-9.]*)", "# renovate: depName=(?\\S+) datasource=(?\\S+)(?: versioning=(?\\S+))?\\n[^\\n]+# v(?[0-9][0-9.]*)", - "# renovate: depName=(?\\S+) datasource=(?\\S+)(?: versioning=(?\\S+))?\\n\\s+[A-Z_]+=(?[0-9][0-9.]*)\\n\\s+[A-Z_]+=(?[0-9a-f]{7,40})", - "# renovate: depName=(?\\S+) datasource=(?\\S+)(?: versioning=(?\\S+))?\\n[^\\n]*==(?[0-9][0-9.]*)" + "# renovate: depName=(?\\S+) datasource=(?\\S+)(?: versioning=(?\\S+))?\\n\\s+[^\\n]+/raw-file/(?FIREFOX_[^/]+)/[^\\n]+" ] } ], "packageRules": [ { - "matchDepNames": ["rhysd/actionlint"], + "description": "SHA256 checksums must be updated manually alongside these version bumps", + "matchDepNames": ["rhysd/actionlint", "nss", "nspr", "firefox"], "prBodyNotes": [ - "⚠️ **Manual step required**: update `SHA256` in `actionlint.yml` from the [release checksums file](https://github.com/rhysd/actionlint/releases/download/v{{newVersion}}/actionlint_{{newVersion}}_checksums.txt)" + "⚠️ **Manual step required**: update the SHA256 checksum(s) that correspond to this version bump:\n- `rhysd/actionlint`: update `SHA256` in `.github/workflows/actionlint.yml` from the [release checksums file](https://github.com/rhysd/actionlint/releases)\n- `nss`/`nspr`: update `NSS_SHA256`/`NSPR_SHA256` in `nss/nss-versions.env` from the Mozilla FTP release\n- `firefox`: update the `sha256sum` checksum for `nsinstall.py` in `nss/action.yml`" ] }, { diff --git a/rust/action.yml b/rust/action.yml index cc98951..4f19403 100644 --- a/rust/action.yml +++ b/rust/action.yml @@ -98,6 +98,8 @@ runs: uses: ilammy/msvc-dev-cmd@v1 # zizmor: ignore[unpinned-uses] # TODO: Would like to pin this, but the Mozilla org allowlist requires "ilammy/msvc-dev-cmd@v1*" # uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0 + with: + arch: ${{ runner.arch == 'ARM64' && 'arm64' || 'amd64' }} # See https://github.com/ilammy/msvc-dev-cmd#name-conflicts-with-shell-bash - name: Set up build environment (Windows) @@ -121,6 +123,14 @@ runs: "https://raw.githubusercontent.com/cargo-bins/cargo-binstall/${BINSTALL_SHA}/install-from-binstall-release.sh" \ | BINSTALL_VERSION="$BINSTALL_VERSION" bash - for tool in $(echo $TOOLS | tr "," " "); do - cargo binstall --no-confirm "$tool" || cargo install --locked "$tool" + for tool in ${TOOLS//,/ }; do + if [ "$tool" == "cargo-fuzz" ]; then + # cargo-fuzz must be compiled with the same nightly toolchain used for fuzzing. + cargo install cargo-fuzz + elif [ "$RUNNER_OS" != "Linux" ] && [ "$tool" == "cargo-careful" ]; then + # TODO: Remove once binary releases work on non-Linux platforms. + cargo install --locked cargo-careful + else + cargo binstall --no-confirm "$tool" || cargo install --locked "$tool" + fi done