+
+#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;
+#endif
+
+ /* Store the bitmask of features we probed for everyone to see. */
+ DISPATCH_STORE(hwcaps, hw);
+
+ /* Finally, make a report about the things we found. (Doing this earlier
+ * will pointlessly widen the window in which multiple threads will do the
+ * above auxiliary-vector probing.)
+ */
+#define CAP__DEBUG(feat, tok) \
+ dispatch_debug("check auxv for feature `%s': %s", tok, \
+ hw & HF_##feat ? "available" : "absent");
+ CAPMAP(CAP__DEBUG)
+#undef CAP__DEBUG
+}
+
+/* --- @get_hwcaps@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: A mask of hardware capabilities and other features, as probed
+ * from the auxiliary vector.
+ */
+
+static unsigned get_hwcaps(void)
+{
+ unsigned hw;
+
+ DISPATCH_LOAD(hwcaps, hw);
+ if (!(hwcaps & HF_PROBED)) { probe_hwcaps(); DISPATCH_LOAD(hwcaps, hw); }
+ return (hw);