#include #include #include #include #include #include #include #include #if CPUINFO_MOCK #include #endif #include #include #include #if CPUINFO_ARCH_ARM64 || CPUINFO_ARCH_ARM && !defined(__ANDROID__) #include #else #define AT_HWCAP 16 #define AT_HWCAP2 26 #endif #if CPUINFO_MOCK static uint32_t mock_hwcap = 0; void cpuinfo_set_hwcap(uint32_t hwcap) { mock_hwcap = hwcap; } #if CPUINFO_ARCH_ARM static uint32_t mock_hwcap2 = 0; void cpuinfo_set_hwcap2(uint32_t hwcap2) { mock_hwcap2 = hwcap2; } #endif #endif #if CPUINFO_ARCH_ARM typedef unsigned long (*getauxval_function_t)(unsigned long); bool cpuinfo_arm_linux_hwcap_from_getauxval( uint32_t hwcap[restrict static 1], uint32_t hwcap2[restrict static 1]) { #if CPUINFO_MOCK *hwcap = mock_hwcap; *hwcap2 = mock_hwcap2; return true; #elif defined(__ANDROID__) /* Android: dynamically check if getauxval is supported */ void* libc = NULL; getauxval_function_t getauxval = NULL; dlerror(); libc = dlopen("libc.so", RTLD_LAZY); if (libc == NULL) { cpuinfo_log_warning("failed to load libc.so: %s", dlerror()); goto cleanup; } getauxval = (getauxval_function_t) dlsym(libc, "getauxval"); if (getauxval == NULL) { cpuinfo_log_info("failed to locate getauxval in libc.so: %s", dlerror()); goto cleanup; } *hwcap = getauxval(AT_HWCAP); *hwcap2 = getauxval(AT_HWCAP2); cleanup: if (libc != NULL) { dlclose(libc); libc = NULL; } return getauxval != NULL; #else /* GNU/Linux: getauxval is always supported */ *hwcap = getauxval(AT_HWCAP); *hwcap2 = getauxval(AT_HWCAP2); return true; #endif } #ifdef __ANDROID__ bool cpuinfo_arm_linux_hwcap_from_procfs( uint32_t hwcap[restrict static 1], uint32_t hwcap2[restrict static 1]) { #if CPUINFO_MOCK *hwcap = mock_hwcap; *hwcap2 = mock_hwcap2; return true; #else uint32_t hwcaps[2] = { 0, 0 }; bool result = false; int file = -1; file = open("/proc/self/auxv", O_RDONLY); if (file == -1) { cpuinfo_log_warning("failed to open /proc/self/auxv: %s", strerror(errno)); goto cleanup; } ssize_t bytes_read; do { Elf32_auxv_t elf_auxv; bytes_read = read(file, &elf_auxv, sizeof(Elf32_auxv_t)); if (bytes_read < 0) { cpuinfo_log_warning("failed to read /proc/self/auxv: %s", strerror(errno)); goto cleanup; } else if (bytes_read > 0) { if (bytes_read == sizeof(elf_auxv)) { switch (elf_auxv.a_type) { case AT_HWCAP: hwcaps[0] = (uint32_t) elf_auxv.a_un.a_val; break; case AT_HWCAP2: hwcaps[1] = (uint32_t) elf_auxv.a_un.a_val; break; } } else { cpuinfo_log_warning( "failed to read %zu bytes from /proc/self/auxv: %zu bytes available", sizeof(elf_auxv), (size_t) bytes_read); goto cleanup; } } } while (bytes_read == sizeof(Elf32_auxv_t)); /* Success, commit results */ *hwcap = hwcaps[0]; *hwcap2 = hwcaps[1]; result = true; cleanup: if (file != -1) { close(file); file = -1; } return result; #endif } #endif /* __ANDROID__ */ #elif CPUINFO_ARCH_ARM64 uint32_t cpuinfo_arm_linux_hwcap_from_getauxval(void) { #if CPUINFO_MOCK return mock_hwcap; #else return (uint32_t) getauxval(AT_HWCAP); #endif } #endif