+}
+
+#endif
+
+/*----- General feature probing using auxiliary vectors -------------------*/
+
+/* Try to find the system's definitions for auxiliary vector entries. */
+#ifdef HAVE_SYS_AUXV_H
+# include <sys/auxv.h>
+#endif
+#ifdef HAVE_LINUX_AUXVEC_H
+# include <linux/auxvec.h>
+#endif
+#ifdef HAVE_ASM_HWCAP_H
+# include <asm/hwcap.h>
+#endif
+
+/* The type of entries in the auxiliary vector. I'm assuming that `unsigned
+ * long' matches each platform's word length; if this is false then we'll
+ * need some host-specific tweaking here.
+ */
+union auxval { long i; unsigned long u; const void *p; };
+struct auxentry { unsigned long type; union auxval value; };
+
+/* Register each CPU family's interest in the auxiliary vector. Make sure
+ * that the necessary entry types are defined. This is primarily ordered by
+ * entry type to minimize duplication.
+ */
+#if defined(AT_HWCAP) && CPUFAM_ARMEL
+# define WANT_ANY 1
+# define WANT_AT_HWCAP(_) _(AT_HWCAP, u, hwcap)
+#endif
+
+#if defined(AT_HWCAP) && CPUFAM_ARM64
+# define WANT_ANY 1
+# define WANT_AT_HWCAP(_) _(AT_HWCAP, u, hwcap)
+#endif
+
+#if defined(AT_HWCAP2) && CPUFAM_ARMEL
+# define WANT_ANY 1
+# define WANT_AT_HWCAP2(_) _(AT_HWCAP2, u, hwcap2)
+#endif
+
+/* If we couldn't find any interesting entries then we can switch all of this
+ * machinery off. Also do that if we have no means for atomic updates.
+ */
+#if WANT_ANY && CPU_DISPATCH_P
+
+/* The main output of this section is a bitmask of detected features. The
+ * least significant bit will be set if we've tried to probe. Always access
+ * this using `DISPATCH_LOAD' and `DISPATCH_STORE'.
+ */
+static unsigned hwcaps = 0;
+
+/* For each potentially interesting type which turned out not to exist or be
+ * wanted, define a dummy macro for the sake of the next step.
+ */
+#ifndef WANT_AT_HWCAP
+# define WANT_AT_HWCAP(_)
+#endif
+#ifndef WANT_AT_HWCAP2
+# define WANT_AT_HWCAP2(_)
+#endif
+
+/* For each CPU family, define two lists.
+ *
+ * * `WANTAUX' is a list of the `WANT_AT_MUMBLE' macros which the CPU
+ * family tried to register interest in above. Each entry contains the
+ * interesting auxiliary vector entry type, the name of the union branch
+ * for its value, and the name of the slot in `struct auxprobe' in which
+ * to store the value.
+ *
+ * * `CAPMAP' is a list describing the output features which the CPU family
+ * intends to satisfy from the auxiliary vector. Each entry contains a
+ * feature name suffix, and the token name (for `check_env').
+ */
+#if CPUFAM_ARMEL
+# define WANTAUX(_) \
+ WANT_AT_HWCAP(_) \
+ WANT_AT_HWCAP2(_)
+# define CAPMAP(_) \
+ _(ARM_VFP, "arm:vfp") \
+ _(ARM_NEON, "arm:neon") \
+ _(ARM_V4, "arm:v4") \
+ _(ARM_D32, "arm:d32") \
+ _(ARM_AES, "arm:aes") \
+ _(ARM_PMULL, "arm:pmull")
+#endif
+#if CPUFAM_ARM64
+# define WANTAUX(_) \
+ WANT_AT_HWCAP(_)
+# define CAPMAP(_) \
+ _(ARM_NEON, "arm:neon") \
+ _(ARM_AES, "arm:aes") \
+ _(ARM_PMULL, "arm:pmull")
+#endif
+
+/* Build the bitmask for `hwcaps' from the `CAPMAP' list. */
+enum {
+ HFI_PROBED = 0,
+#define HFI__ENUM(feat, tok) HFI_##feat,
+ CAPMAP(HFI__ENUM)
+#undef HFI__ENUM
+ HFI__END
+};
+enum {
+ HF_PROBED = 1,
+#define HF__FLAG(feat, tok) HF_##feat = 1 << HFI_##feat,
+ CAPMAP(HF__FLAG)
+#undef HF__FLAG
+ HF__END
+};
+
+/* Build a structure in which we can capture the interesting data from the
+ * auxiliary vector.
+ */
+#define AUXUTYPE_i long
+#define AUXUTYPE_u unsigned long
+#define AUXUTYPE_p const void *
+struct auxprobe {
+#define AUXPROBE__SLOT(type, ubranch, slot) AUXUTYPE_##ubranch slot;
+ WANTAUX(AUXPROBE__SLOT)
+#undef AUXPROBE_SLOT
+};
+
+/* --- @probe_hwcaps@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: ---
+ *
+ * Use: Attempt to find the auxiliary vector (which is well hidden)
+ * and discover interesting features from it.
+ */
+
+static void probe_hwcaps(void)
+{
+ unsigned hw = HF_PROBED;
+ struct auxprobe probed = { 0 };
+
+ /* Populate `probed' with the information we manage to retrieve from the
+ * auxiliary vector. Slots we couldn't find are left zero-valued.
+ */
+#if defined(HAVE_GETAUXVAL)
+ /* Shiny new libc lets us request individual entry types. This is almost
+ * too easy.
+ */
+# define CAP__GET(type, ubranch, slot) \
+ probed.slot = (AUXUTYPE_##ubranch)getauxval(type);
+ WANTAUX(CAP__GET)
+#else
+ /* Otherwise we're a bit stuck, really. Modern Linux kernels make a copy
+ * of the vector available in `/procc' so we could try that.
+ *
+ * The usual place is stuck on the end of the environment vector, but that
+ * may well have moved, and we have no way of telling whether it has or
+ * whether there was ever an auxiliary vector there at all; so don't do
+ * that.
+ */
+ {
+ FILE *fp = 0;
+ unsigned char *p = 0, *q = 0;
+ const struct auxentry *a;
+ size_t sz, off, n;
+
+ /* Open the file and read it into a memory chunk. */
+ if ((fp = fopen("/proc/self/auxv", "rb")) == 0) goto clean;
+ sz = 4096; off = 0;
+ if ((p = malloc(sz)) == 0) goto clean;
+ for (;;) {
+ n = fread(p + off, 1, sz - off, fp);
+ off += n;
+ if (off < sz) break;
+ sz *= 2; if ((q = realloc(p, sz)) == 0) break;
+ p = q;
+ }
+
+ /* Work through the vector (or as much of it as we found) and extract the
+ * types we're interested in.
+ */
+ for (a = (const struct auxentry *)p,
+ n = sz/sizeof(struct auxentry);
+ n--; a++) {
+ switch (a->type) {
+#define CAP__SWITCH(type, ubranch, slot) \
+ case type: probed.slot = a->value.ubranch; break;
+ WANTAUX(CAP__SWITCH)
+ case AT_NULL: goto clean;
+ }
+ }
+
+ clean:
+ if (p) free(p);
+ if (fp) fclose(fp);
+ }
+#endif
+
+ /* Each CPU family now has to pick through what was found and stashed in
+ * `probed', and set the appropriate flag bits in `hw'.
+ */
+#if CPUFAM_ARMEL
+ if (probed.hwcap & HWCAP_VFPv3) hw |= HF_ARM_VFP;
+ if (probed.hwcap & HWCAP_NEON) hw |= HF_ARM_NEON;
+ if (probed.hwcap & HWCAP_VFPD32) hw |= HF_ARM_D32;
+ if (probed.hwcap & HWCAP_VFPv4) hw |= HF_ARM_V4;
+# ifdef HWCAP2_AES
+ if (probed.hwcap2 & HWCAP2_AES) hw |= HF_ARM_AES;
+# endif
+# ifdef HWCAP2_PMULL
+ if (probed.hwcap2 & HWCAP2_PMULL) hw |= HF_ARM_PMULL;
+# endif
+#endif
+#if CPUFAM_ARM64
+ if (probed.hwcap & HWCAP_ASIMD) hw |= HF_ARM_NEON;
+ if (probed.hwcap & HWCAP_AES) hw |= HF_ARM_AES;
+ if (probed.hwcap & HWCAP_PMULL) hw |= HF_ARM_PMULL;