17

I am trying to determine when optional ARMv8 cpu features are available on iOS at runtime. On OS X desktops, like i686, x86_64, PPC and PPC64 we can use sysctl. See, for example, PR 3108, SIGILL-free processor capabilities detection on MacOS X.

The "SIGILL-free processor capabilities" part is important because SIGILL-based feature probes corrupt memory on Apple platforms. At least three projects I follow have had problems with it, including Crypto++ and OpenSSL. Another part of the problem is questions like Does Apple Clang lack CRC32 for ARMv8/Aarch64 under Xcode 10?, where core ARMv8 features that should be present seem to be missing or unsupported.

I've looked for the iOS equivalent of the OS X desktops but I cannot find them (see below).

We really prefer to avoid Apple frameworks like CommonCrypto. We try hard to stay platform agnostic. We don't use CommonCrypto, but I would consider using an Apple functions like below if there's nothing agnostic available (I'm making them up):

BOOL CCHasAes();
BOOL CCHasSHA1();
BOOL CCHasSHA2();

Does Apple publish a method to determine cpu features at runtime?

How do we determine cpu features at runtime on iOS?


These may be related, but I'm not sure if that's the best we have, or if that's the only thing we have, or if there are other ways to approach it.


Below I am looking for CRC-32, CRC-32C, AES, PMULL, SHA1 and SHA2 features. I don't see any hits that resemble what I am looking for. Apple supports ARMv8 since Xcode 7, so 8.2 supports the CPU.

$ grep -IR CTL_ /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.2.sdk | \
  sed 's|/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.2.sdk/usr/include|...|g'
.../c++/4.2.1/bits/c++config.h:#define _GLIBCXX_HAVE_SYS_IOCTL_H 1
.../netinet/in.h:#define        IPCTL_FORWARDING        1       /* act as router */
.../netinet/in.h:#define        IPCTL_SENDREDIRECTS     2       /* may send redirects when forwarding */
.../netinet/in.h:#define        IPCTL_DEFTTL            3       /* default TTL */
.../netinet/in.h:#define        IPCTL_DEFMTU            4       /* default MTU */
.../netinet/in.h:#define IPCTL_RTEXPIRE         5       /* cloned route expiration time */
.../netinet/in.h:#define IPCTL_RTMINEXPIRE      6       /* min value for expiration time */
.../netinet/in.h:#define IPCTL_RTMAXCACHE       7       /* trigger level for dynamic expire */
.../netinet/in.h:#define        IPCTL_SOURCEROUTE       8       /* may perform source routes */
.../netinet/in.h:#define        IPCTL_DIRECTEDBROADCAST 9       /* may re-broadcast received packets */
.../netinet/in.h:#define IPCTL_INTRQMAXLEN      10      /* max length of netisr queue */
.../netinet/in.h:#define        IPCTL_INTRQDROPS        11      /* number of netisr q drops */
.../netinet/in.h:#define        IPCTL_STATS             12      /* ipstat structure */
.../netinet/in.h:#define        IPCTL_ACCEPTSOURCEROUTE 13      /* may accept source routed packets */
.../netinet/in.h:#define        IPCTL_FASTFORWARDING    14      /* use fast IP forwarding code */
.../netinet/in.h:#define        IPCTL_KEEPFAITH         15      /* deprecated */
.../netinet/in.h:#define        IPCTL_GIF_TTL           16      /* default TTL for gif encap packet */
.../netinet/in.h:#define        IPCTL_MAXID             17
.../netinet6/in6.h:#define      IPV6CTL_FORWARDING      1       /* act as router */
.../netinet6/in6.h:#define      IPV6CTL_SENDREDIRECTS   2       /* may send redirects when forwarding */
.../netinet6/in6.h:#define      IPV6CTL_DEFHLIM         3       /* default Hop-Limit */
.../netinet6/in6.h:#define      IPV6CTL_DEFMTU          4       /* default MTU */
.../netinet6/in6.h:#define      IPV6CTL_FORWSRCRT       5       /* forward source-routed dgrams */
.../netinet6/in6.h:#define      IPV6CTL_STATS           6       /* stats */
.../netinet6/in6.h:#define      IPV6CTL_MRTSTATS        7       /* multicast forwarding stats */
.../netinet6/in6.h:#define      IPV6CTL_MRTPROTO        8       /* multicast routing protocol */
.../netinet6/in6.h:#define      IPV6CTL_MAXFRAGPACKETS  9       /* max packets reassembly queue */
.../netinet6/in6.h:#define      IPV6CTL_SOURCECHECK     10      /* verify source route and intf */
.../netinet6/in6.h:#define      IPV6CTL_SOURCECHECK_LOGINT 11   /* minimume logging interval */
.../netinet6/in6.h:#define      IPV6CTL_ACCEPT_RTADV    12
.../netinet6/in6.h:#define      IPV6CTL_KEEPFAITH       13      /* deprecated */
.../netinet6/in6.h:#define      IPV6CTL_LOG_INTERVAL    14
.../netinet6/in6.h:#define      IPV6CTL_HDRNESTLIMIT    15
.../netinet6/in6.h:#define      IPV6CTL_DAD_COUNT       16
.../netinet6/in6.h:#define      IPV6CTL_AUTO_FLOWLABEL  17
.../netinet6/in6.h:#define      IPV6CTL_DEFMCASTHLIM    18
.../netinet6/in6.h:#define      IPV6CTL_GIF_HLIM        19      /* default HLIM for gif encap packet */
.../netinet6/in6.h:#define      IPV6CTL_KAME_VERSION    20
.../netinet6/in6.h:#define      IPV6CTL_USE_DEPRECATED  21      /* use deprec addr (RFC2462 5.5.4) */
.../netinet6/in6.h:#define      IPV6CTL_RR_PRUNE        22      /* walk timer for router renumbering */
.../netinet6/in6.h:#define      IPV6CTL_MAPPED_ADDR     23
.../netinet6/in6.h:#define      IPV6CTL_V6ONLY          24
.../netinet6/in6.h:#define      IPV6CTL_RTEXPIRE        25      /* cloned route expiration time */
.../netinet6/in6.h:#define      IPV6CTL_RTMINEXPIRE     26      /* min value for expiration time */
.../netinet6/in6.h:#define      IPV6CTL_RTMAXCACHE      27      /* trigger level for dynamic expire */
.../netinet6/in6.h:#define      IPV6CTL_USETEMPADDR     32      /* use temporary addresses [RFC 4941] */
.../netinet6/in6.h:#define      IPV6CTL_TEMPPLTIME      33      /* preferred lifetime for tmpaddrs */
.../netinet6/in6.h:#define      IPV6CTL_TEMPVLTIME      34      /* valid lifetime for tmpaddrs */
.../netinet6/in6.h:#define      IPV6CTL_AUTO_LINKLOCAL  35      /* automatic link-local addr assign */
.../netinet6/in6.h:#define      IPV6CTL_RIP6STATS       36      /* raw_ip6 stats */
.../netinet6/in6.h:#define      IPV6CTL_PREFER_TEMPADDR 37      /* prefer temporary addr as src */
.../netinet6/in6.h:#define      IPV6CTL_ADDRCTLPOLICY   38      /* get/set address selection policy */
.../netinet6/in6.h:#define      IPV6CTL_USE_DEFAULTZONE 39      /* use default scope zone */
.../netinet6/in6.h:#define      IPV6CTL_MAXFRAGS        41      /* max fragments */
.../netinet6/in6.h:#define      IPV6CTL_MCAST_PMTU      44      /* enable pMTU discovery for mcast? */
.../netinet6/in6.h:#define      IPV6CTL_NEIGHBORGCTHRESH 46
.../netinet6/in6.h:#define      IPV6CTL_MAXIFPREFIXES   47
.../netinet6/in6.h:#define      IPV6CTL_MAXIFDEFROUTERS 48
.../netinet6/in6.h:#define      IPV6CTL_MAXDYNROUTES    49
.../netinet6/in6.h:#define      ICMPV6CTL_ND6_ONLINKNSRFC4861   50
.../netinet6/in6.h:/* New entries should be added here from current IPV6CTL_MAXID value. */
.../netinet6/in6.h:#define      IPV6CTL_MAXID           51
.../sys/ioctl.h:#ifndef _SYS_IOCTL_H_
.../sys/ioctl.h:#define _SYS_IOCTL_H_
.../sys/ioctl.h:#endif /* !_SYS_IOCTL_H_ */
.../sys/ioctl.h: * Keep outside _SYS_IOCTL_H_
.../sys/ioctl.h:#endif /* !_SYS_IOCTL_H_ */
.../sys/mount.h: * Sysctl CTL_VFS definitions.
.../sys/mount.h:        int             vc_vers;        /* should be VFSIDCTL_VERS1 (below) */
.../sys/mount.h:#define VFS_CTL_VERS1   0x01
.../sys/mount.h:#define VFS_CTL_STATFS  0x00010001      /* statfs */
.../sys/mount.h:#define VFS_CTL_UMOUNT  0x00010002      /* unmount */
.../sys/mount.h:#define VFS_CTL_QUERY   0x00010003      /* anything wrong? (vfsquery) */
.../sys/mount.h:#define VFS_CTL_NEWADDR 0x00010004      /* reconnect to new address */
.../sys/mount.h:#define VFS_CTL_TIMEO   0x00010005      /* set timeout for vfs notification */
.../sys/mount.h:#define VFS_CTL_NOLOCKS 0x00010006      /* disable file locking */
.../sys/mount.h:#define VFS_CTL_SADDR   0x00010007      /* get server address */
.../sys/mount.h:#define VFS_CTL_DISC    0x00010008      /* server disconnected */
.../sys/mount.h:#define VFS_CTL_SERVERINFO  0x00010009  /* information about fs server */
.../sys/mount.h:#define VFS_CTL_NSTATUS 0x0001000A      /* netfs mount status */
.../sys/mount.h: * NetFS mount status - returned by VFS_CTL_NSTATUS
.../sys/socket.h: * Definitions for network related sysctl, CTL_NET.
.../sys/sysctl.h:#ifndef _SYS_SYSCTL_H_
.../sys/sysctl.h:#define        _SYS_SYSCTL_H_
.../sys/sysctl.h:#define CTL_MAXNAME    12      /* largest number of components supported */
.../sys/sysctl.h: *                     SYSCTL_OUT(rey, local buffer, length)
.../sys/sysctl.h: * e.g. SYSCTL_INT(_parent, OID_AUTO, name, CTLFLAG_RW, &variable, 0, "");
.../sys/sysctl.h:#define SYSCTL_DEF_ENABLED
.../sys/sysctl.h:#ifdef SYSCTL_DEF_ENABLED
.../sys/sysctl.h:#define        CTL_UNSPEC      0               /* unused */
.../sys/sysctl.h:#define        CTL_KERN        1               /* "high kernel": proc, limits */
.../sys/sysctl.h:#define        CTL_VM          2               /* virtual memory */
.../sys/sysctl.h:#define        CTL_VFS         3               /* file system, mount type is next */
.../sys/sysctl.h:#define        CTL_NET         4               /* network, see socket.h */
.../sys/sysctl.h:#define        CTL_DEBUG       5               /* debugging parameters */
.../sys/sysctl.h:#define        CTL_HW          6               /* generic cpu/io */
.../sys/sysctl.h:#define        CTL_MACHDEP     7               /* machine dependent */
.../sys/sysctl.h:#define        CTL_USER        8               /* user-level */
.../sys/sysctl.h:#define        CTL_MAXID       9               /* number of valid top-level ids */
.../sys/sysctl.h:#define CTL_NAMES { \
.../sys/sysctl.h: * CTL_KERN identifiers
.../sys/sysctl.h: * Don't add any more sysctls like this.  Instead, use the SYSCTL_*() macros
.../sys/sysctl.h:#define CTL_KERN_NAMES { \
.../sys/sysctl.h: * CTL_VFS identifiers
.../sys/sysctl.h:#define CTL_VFS_NAMES { \
.../sys/sysctl.h: * CTL_VM identifiers
.../sys/sysctl.h:#define        CTL_VM_NAMES { \
.../sys/sysctl.h: * CTL_HW identifiers
.../sys/sysctl.h:#define CTL_HW_NAMES { \
.../sys/sysctl.h: * CTL_USER definitions
.../sys/sysctl.h:#define        CTL_USER_NAMES { \
.../sys/sysctl.h: * CTL_DEBUG definitions
.../sys/sysctl.h:#define        CTL_DEBUG_NAME          0       /* string: variable name */
.../sys/sysctl.h:#define        CTL_DEBUG_VALUE         1       /* int: variable value */
.../sys/sysctl.h:#define        CTL_DEBUG_MAXID         20
.../sys/sysctl.h:#if (CTL_MAXID != 9) || (KERN_MAXID != 72) || (VM_MAXID != 6) || (HW_MAXID != 26) || (USER_MAXID != 21) || (CTL_DEBUG_MAXID != 20)
.../sys/sysctl.h:#error Use the SYSCTL_*() macros and OID_AUTO instead!
.../sys/sysctl.h:#endif /* SYSCTL_DEF_ENABLED */
.../sys/sysctl.h:#endif /* !_SYS_SYSCTL_H_ */
.../sys/termios.h:#if !defined(_SYS_IOCTL_COMPAT_H_) || __DARWIN_UNIX03
.../sys/termios.h:#endif        /* !_SYS_IOCTL_COMPAT_H_ */
jww
  • 97,681
  • 90
  • 411
  • 885
  • *How do we determine cpu features at runtime on iOS?* - why do you want to know this? What are you trying to achieve? – mag_zbc Aug 11 '17 at 15:01
  • 4
    @mag_zbc - *" why do you want to know this?"* - If a feature is available, then we will use a different code path at runtime. For example, C++ CRC-32 code runs at 7 or 8 cpb. The same CRC-32 code using ARMv8 instructions runs at 0.7 cpb. That's about 10x faster. SHA1 runs about 8-9x faster, and SHA2 runs about 12-14x faster, and AES runs 12-14x faster. – jww Aug 11 '17 at 15:03
  • @jww Have you looked at [Main ID Register](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0301h/Bgbiddeb.html) and other CPUID Feature registers. You can query for existence of individual features or just query for the exact Variant and Revision and maintain a list offline of features corresponding to each variant. – Ajay Brahmakshatriya Aug 22 '17 at 04:19
  • 2
    @AjayBrahmakshatriya It's not an acceptable solution: That register is, key quote, _"accessible in privileged modes only."_. As a matter of fact, the x86 architecture is unusual in permitting unprivileged, usermode access to the processor ID and feature flags. For instance, the ARM and OpenPOWER architectures _(and I expect many others as well)_ make this a privileged operation for security reasons; – Iwillnotexist Idonotexist Aug 22 '17 at 06:24
  • @jww *The "SIGILL-free processor capabilities" part is important because SIGILL-based feature probes corrupt memory on Apple platforms.* - regarding this, does it corrupt application memory only? If so, the feature probe can be done as a separate process with just a exception handler installed. – Ajay Brahmakshatriya Aug 22 '17 at 06:32
  • @AjayBrahmakshatriya - Thanks. The code is part of a library C/C++, and it could run at library startup. That could happen a DLL/shared object load or program load. I doubt forking a process is viable. – jww Aug 22 '17 at 22:45
  • 1
    @jww since this information will remain constant *more or less* during a boot, you can cache the result. Only the first process has to fork. I understand there are some restrictions regarding spawning processes on iOS. Also, if the corruption of memory is limited to say stack or some limited area, it could also be done in just a thread. – Ajay Brahmakshatriya Aug 23 '17 at 04:06
  • @AjayBrahmakshatriya - Cache it from where? We don't have it; and we are trying to gather it. That's point of this question. – jww Aug 23 '17 at 04:15
  • @jww Yes, I meant that the first process that tries to use your library could fork and gather the data. Then store it in some file. So that future uses of the library don't have to fork. The first instance can be a controlled one. – Ajay Brahmakshatriya Aug 23 '17 at 04:17
  • Can you please link to or describe this "memory corruption on SIGILL"? I can find no reference to it elsewhere than here. Like @Ajay said, if the corruption is controllable in some sense, you might be able to write a short chunk of ARM assembler to set the conditions right for the test. – Iwillnotexist Idonotexist Aug 23 '17 at 06:36
  • @IwillnotexistIdonotexist - See OpenSSL's [PR 3108, SIGILL-free processor capabilities detection on MacOS X](https://github.com/openssl/openssl/pull/3108/files). We experienced the same on IA-32 and believed it was due to the SIGILL probes. Once we moved away from the probes the problems disappeared. "We" are the [Crypto++ project](https://www.cryptopp.com/). – jww Aug 23 '17 at 06:47
  • @AjayBrahmakshatriya - I don't believe Apple allows you to fork processes on iOS. Also see [Will fork() in iOS app likely be rejected by Apple's vetting process?](https://stackoverflow.com/q/15893849/608639) – jww Aug 23 '17 at 06:57
  • 1
    EDIT: I think I might know what causes the SIGILLs to not be caught... Could you please check whether or not `sigprocmask(SIG_SETMASK, 0, &currset)` reports that SIGILL is _blocked_ in `currset` immediately after `sigsetjmp()` returns a non-zero value in the probe code? If SIGILL is left blocked, then another synchronous SIGILL would result in UB such as termination of the process. The solution would then be to specifically unblock SIGILL, even if it's supposed to have been unblocked by `siglongjmp()`. – Iwillnotexist Idonotexist Aug 23 '17 at 09:10
  • 1
    Yet another possibility, aside from forcibly unblocking `SIGILL`, is to use `sigaction()`'s support for alternate stacks for signal handling (See `sa_flags = SA_ONSTACK` and `sigaltstack()`). Does that avoid the memory corruption? – Iwillnotexist Idonotexist Aug 23 '17 at 21:12

1 Answers1

3

As far as I know Apple is going to remove the support to 32-bit chips this year. Since Apple A7 processors Apple uses ArmV8-A cores for its SoCs...so maybe you cannot choose them with the new APIs because you have nothing to choose...Apple usually is cryptic about those kind of things and uses automatic switching for certain features on mobile platforms. However basically every iOS 11 SoC will have the same basic hardware features except for the number of cores and the energy management (which is different for SoC >= A10 Fusion). I was searching for the same thing some month ago but apparently on iOS you have to be in the hands of the OS.

  • Thanks Andrea. We provide richer support than Apple. That's a nice way of saying, *we don't embrace the abandon-ware model of development*, where last year's Christmas present is obsolete. We still support back to 2012 or so; and Xcode 6.0 is fair game for us. – jww Aug 21 '17 at 16:42
  • I clearly understand your problem. I can take a look around to see if I can help you find some way, even if apparently seems that they've closed ways for doing this... – Andrea Vultaggio Aug 23 '17 at 12:11