sha256

Pure Haskell SHA-256, HMAC-SHA256 (docs.ppad.tech/sha256).
git clone git://git.ppad.tech/sha256.git
Log | Files | Refs | README | LICENSE

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:
Mcbits/sha256_arm.c | 28+++++++++++++++++++++++++---
Mppad-sha256.cabal | 2--
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