commit 73d2f687b4788d902a29b8b245081396507336fd
parent 9d74a5e1d3995809b3d3f14144554f114592d99c
Author: Jared Tobin <jared@jtobin.io>
Date: Sat, 10 Oct 2026 15:23:04 +0400
cbits: detect ARM SHA512 support at runtime
'sha512_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.2-a+sha3'. A
binary built that way executes SHA512 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("+sha3"), and availability is queried from the
OS at runtime (getauxval on Linux, sysctl on Darwin), falling back
to the pure Haskell implementation where it can't be determined.
Diffstat:
2 files changed, 37 insertions(+), 6 deletions(-)
diff --git a/cbits/sha512_arm.c b/cbits/sha512_arm.c
@@ -1,10 +1,19 @@
#include <stdint.h>
#include <string.h>
-#if defined(__aarch64__) && defined(__ARM_FEATURE_SHA512)
+#if defined(__aarch64__)
#include <arm_neon.h>
+#if defined(__APPLE__)
+#include <sys/sysctl.h>
+#elif defined(__linux__)
+#include <sys/auxv.h>
+#ifndef HWCAP_SHA512
+#define HWCAP_SHA512 (1UL << 21)
+#endif
+#endif
+
static const uint64_t K[80] = {
0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
@@ -55,7 +64,11 @@ static const uint64_t K[80] = {
* block: pointer to 16 uint64_t words (already native endian)
*
* The state is updated in place.
+ *
+ * Compiled for the SHA512 extension only; callers must first check
+ * 'sha512_arm_available'.
*/
+__attribute__((target("+sha3")))
void sha512_block_arm(uint64_t *state, const uint64_t *block) {
/* Load current hash state */
uint64x2_t ab = vld1q_u64(&state[0]);
@@ -446,14 +459,34 @@ void sha512_block_arm(uint64_t *state, const uint64_t *block) {
vst1q_u64(&state[6], gh);
}
-/* Return 1 if ARM SHA512 is available, 0 otherwise */
+#if defined(__APPLE__)
+static int sysctl_flag(const char *name) {
+ int val = 0;
+ size_t len = sizeof(val);
+ if (sysctlbyname(name, &val, &len, NULL, 0) != 0) return 0;
+ return val != 0;
+}
+#endif
+
+/*
+ * Return 1 if the CPU implements the ARM SHA512 extension, 0
+ * otherwise. We only know how to ask Darwin and Linux, and report 0
+ * (selecting the pure Haskell fallback) on any other OS.
+ */
int sha512_arm_available(void) {
- return 1;
+#if defined(__APPLE__)
+ return sysctl_flag("hw.optional.arm.FEAT_SHA512")
+ || sysctl_flag("hw.optional.armv8_2_sha512");
+#elif defined(__linux__)
+ return (getauxval(AT_HWCAP) & HWCAP_SHA512) != 0;
+#else
+ return 0;
+#endif
}
#else
-/* Stub implementations when ARM SHA512 is not available */
+/* Stub implementations for non-aarch64 builds */
void sha512_block_arm(uint64_t *state, const uint64_t *block) {
(void)state;
(void)block;
diff --git a/ppad-sha512.cabal b/ppad-sha512.cabal
@@ -47,8 +47,6 @@ library
, bytestring >= 0.9 && < 0.13
c-sources:
cbits/sha512_arm.c
- if arch(aarch64)
- cc-options: -march=armv8.2-a+sha3
if flag(sanitize)
cc-options: -fsanitize=address,undefined -fno-omit-frame-pointer
ghc-options: -optl=-fsanitize=address,undefined