commit d40f9405d179d7902e260227b4c35af51cf6514e
parent dc447b7a2b09425019e2617690bc7adb7b88cd44
Author: Jared Tobin <jared@jtobin.io>
Date: Sat, 10 Oct 2026 15:23:14 +0400
cbits: detect ARM SHA2 support at runtime
'sha256_arm_available' previously reported the result of a
compile-time feature test, and the cabal file forced that test to
pass on every aarch64 build via '-march=armv8-a+sha2'. A binary
built that way executes SHA2 instructions unconditionally, faulting
with SIGILL on aarch64 cores that lack the extension.
The global '-march' flag is dropped. The block kernel alone is now
compiled with target("+sha2"), and availability is queried from the
OS at runtime on Linux (getauxval). Every Apple arm64 core has the
extension; any other OS falls back to the pure Haskell
implementation.
Diffstat:
2 files changed, 25 insertions(+), 5 deletions(-)
diff --git a/cbits/sha256_arm.c b/cbits/sha256_arm.c
@@ -1,10 +1,17 @@
#include <stdint.h>
#include <string.h>
-#if defined(__aarch64__) && defined(__ARM_FEATURE_SHA2)
+#if defined(__aarch64__)
#include <arm_neon.h>
+#if defined(__linux__)
+#include <sys/auxv.h>
+#ifndef HWCAP_SHA2
+#define HWCAP_SHA2 (1UL << 6)
+#endif
+#endif
+
static const uint32_t K[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
@@ -31,7 +38,11 @@ static const uint32_t K[64] = {
* block: pointer to 16 uint32_t words (already native endian)
*
* The state is updated in place.
+ *
+ * Compiled for the SHA2 extension only; callers must first check
+ * 'sha256_arm_available'.
*/
+__attribute__((target("+sha2")))
void sha256_block_arm(uint32_t *state, const uint32_t *block) {
/* Load current hash state */
uint32x4_t abcd = vld1q_u32(&state[0]);
@@ -166,14 +177,25 @@ void sha256_block_arm(uint32_t *state, const uint32_t *block) {
vst1q_u32(&state[4], efgh);
}
-/* Return 1 if ARM SHA2 is available, 0 otherwise */
+/*
+ * Return 1 if the CPU implements the ARM SHA2 extension, 0 otherwise.
+ * Every Apple arm64 core implements it. Elsewhere we only know how to
+ * ask Linux, and report 0 (selecting the pure Haskell fallback) on
+ * any other OS.
+ */
int sha256_arm_available(void) {
+#if defined(__APPLE__)
return 1;
+#elif defined(__linux__)
+ return (getauxval(AT_HWCAP) & HWCAP_SHA2) != 0;
+#else
+ return 0;
+#endif
}
#else
-/* Stub implementations when ARM SHA2 is not available */
+/* Stub implementations for non-aarch64 builds */
void sha256_block_arm(uint32_t *state, const uint32_t *block) {
(void)state;
(void)block;
diff --git a/ppad-sha256.cabal b/ppad-sha256.cabal
@@ -47,8 +47,6 @@ library
, bytestring >= 0.9 && < 0.13
c-sources:
cbits/sha256_arm.c
- if arch(aarch64)
- cc-options: -march=armv8-a+sha2
if flag(sanitize)
cc-options: -fsanitize=address,undefined -fno-omit-frame-pointer
ghc-options: -optl=-fsanitize=address,undefined