Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions ext/digest/arm_crypto.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/* arm_crypto.h - runtime detection of the ARMv8 Cryptographic Extension */
#ifndef RB_DIGEST_ARM_CRYPTO_H
#define RB_DIGEST_ARM_CRYPTO_H

#if defined(HAVE_ARM_CRYPTO_EXT)

#if defined(__APPLE__)
# include <sys/sysctl.h>
#elif defined(__linux__) || defined(__ANDROID__)
# include <sys/auxv.h>
/* Not every libc/kernel-header combination defines these (e.g. older
* <asm/hwcap.h> layouts), so fall back to the values Linux has used for
* aarch64 since they were introduced. */
# ifndef HWCAP_SHA1
# define HWCAP_SHA1 (1 << 5)
# endif
# ifndef HWCAP_SHA2
# define HWCAP_SHA2 (1 << 6)
# endif
#endif

#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && \
defined(__has_include) && __has_include(<stdatomic.h>) && !defined(_MSC_VER)
# include <stdatomic.h>
# define RB_DIGEST_ARM_ATOMIC_INT _Atomic int
#else
# define RB_DIGEST_ARM_ATOMIC_INT int
#endif

#if defined(__APPLE__)
static inline int
rb_digest_arm_sysctl_enabled(const char *name)
{
int enabled = 0;
size_t size = sizeof(enabled);
if (sysctlbyname(name, &enabled, &size, NULL, 0) != 0) return 0;
return enabled != 0;
}
#endif

static inline int
rb_digest_have_arm_sha1(void)
{
enum { UNKNOWN = -1, NO = 0, YES = 1 };
static RB_DIGEST_ARM_ATOMIC_INT cached = UNKNOWN;
int state = cached;

if (state == UNKNOWN) {
#if defined(__APPLE__)
state = rb_digest_arm_sysctl_enabled("hw.optional.arm.FEAT_SHA1") ? YES : NO;
#elif defined(__linux__) || defined(__ANDROID__)
state = (getauxval(AT_HWCAP) & HWCAP_SHA1) ? YES : NO;
#else
state = NO;
#endif
cached = state;
}
return state == YES;
}

static inline int
rb_digest_have_arm_sha256(void)
{
enum { UNKNOWN = -1, NO = 0, YES = 1 };
static RB_DIGEST_ARM_ATOMIC_INT cached = UNKNOWN;
int state = cached;

if (state == UNKNOWN) {
#if defined(__APPLE__)
state = rb_digest_arm_sysctl_enabled("hw.optional.arm.FEAT_SHA256") ? YES : NO;
#elif defined(__linux__) || defined(__ANDROID__)
state = (getauxval(AT_HWCAP) & HWCAP_SHA2) ? YES : NO;
#else
state = NO;
#endif
cached = state;
}
return state == YES;
}

#else /* !HAVE_ARM_CRYPTO_EXT */

static inline int rb_digest_have_arm_sha1(void) { return 0; }
static inline int rb_digest_have_arm_sha256(void) { return 0; }

#endif /* HAVE_ARM_CRYPTO_EXT */

#endif /* RB_DIGEST_ARM_CRYPTO_H */
18 changes: 18 additions & 0 deletions ext/digest/digest_conf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,21 @@ def digest_conf(name)
$objs << "#{name}.#{$OBJEXT}"
return
end

# Check whether this compiler on an aarch64/arm64 host can build +source+. Will
# try with no additional cflags and also architecture specific flags when
# compiling +source+.
#
# Returns the flag that worked (nil if none was needed), or false if no
# candidate compiled or the host isn't aarch64/arm64.
def arm_crypto_flag(source)
return false unless RbConfig::CONFIG["host_cpu"] =~ /\A(aarch64|arm64)\z/i

[nil, "-march=armv8-a+crypto"].each do |flag|
label = "ARMv8 Crypto Extensions intrinsics" + (flag ? " (#{flag})" : "")
if checking_for(label) { try_compile(%{#include "#{$srcdir}/#{source}"\n}, flag) }
return flag
end
end
false
end
34 changes: 33 additions & 1 deletion ext/digest/sha1/extconf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,42 @@

$objs = [ "sha1init.#{$OBJEXT}" ]

digest_conf("sha1")
bundled = !digest_conf("sha1")

have_header("sys/cdefs.h")

# On aarch64/arm64, build the ARMv8 Crypto Extensions backend and let
# SHA1_Transform() pick it at runtime when the CPU actually has the extension.
#
# $CFLAGS can't carry the extra instruction-set flag for just this one file,
# so give it its own compile rule below when one is needed. This only applies
# when CommonCrypto is not available.
arm_cflags = {}
if bundled && with_config("arm-crypto", true)
case flag = arm_crypto_flag("sha1-arm.c")
when false
# Not aarch64/arm64, or the toolchain can't build the intrinsics: fall
# back to the portable C implementation only.
else
$objs << "sha1-arm.#{$OBJEXT}"
$defs << "-DHAVE_ARM_CRYPTO_EXT"
arm_cflags["sha1-arm"] = flag if flag
end
end

$preload = %w[digest]

create_makefile("digest/sha1")

unless arm_cflags.empty?
File.open("Makefile", "a") do |mf|
mf.puts
mf.puts "# Per-file instruction-set flags for the ARMv8 Crypto Extensions backend."
arm_cflags.each do |obj, cflag|
target = "#{obj}.#{$OBJEXT}"
mf.puts "#{target}: $(srcdir)/#{obj}.c"
mf.puts "\t$(ECHO) compiling #{obj}.c"
mf.puts "\t$(Q) $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) #{cflag} $(COUTFLAG)$@ -c $(CSRCFLAG)$(srcdir)/#{obj}.c"
end
end
end
Loading