From ac3d32aa0dcbd5cbe0ce7ce768a8b7fe2d7b69a1 Mon Sep 17 00:00:00 2001 From: nihui Date: Sat, 20 Nov 2021 12:03:32 +0800 Subject: [PATCH] get_elf_hwcap_from_getauxval (#3301) --- src/cpu.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/src/cpu.cpp b/src/cpu.cpp index f95e198ff..a935a1c7b 100644 --- a/src/cpu.cpp +++ b/src/cpu.cpp @@ -38,6 +38,9 @@ #endif #if defined __ANDROID__ || defined __linux__ +#if defined __ANDROID__ +#include +#endif #include #include #include @@ -91,17 +94,63 @@ namespace ncnn { #if defined __ANDROID__ || defined __linux__ +#define AT_HWCAP 16 +#define AT_HWCAP2 26 + +#if defined __ANDROID__ +// Probe the system's C library for a 'getauxval' function and call it if +// it exits, or return 0 for failure. This function is available since API +// level 20. +// +// This code does *NOT* check for '__ANDROID_API__ >= 20' to support the +// edge case where some NDK developers use headers for a platform that is +// newer than the one really targetted by their application. +// This is typically done to use newer native APIs only when running on more +// recent Android versions, and requires careful symbol management. +// +// Note that getauxval() can't really be re-implemented here, because +// its implementation does not parse /proc/self/auxv. Instead it depends +// on values that are passed by the kernel at process-init time to the +// C runtime initialization layer. +static unsigned int get_elf_hwcap_from_getauxval() +{ + typedef unsigned long getauxval_func_t(unsigned long); + + dlerror(); + void* libc_handle = dlopen("libc.so", RTLD_NOW); + if (!libc_handle) + { + NCNN_LOGE("dlopen libc.so failed %s", dlerror()); + return 0; + } + + unsigned int result = 0; + getauxval_func_t* func = (getauxval_func_t*)dlsym(libc_handle, "getauxval"); + if (!func) + { + NCNN_LOGE("dlsym getauxval failed"); + } + else + { + // Note: getauxval() returns 0 on failure. Doesn't touch errno. + result = (unsigned int)(*func)(AT_HWCAP); + } + dlclose(libc_handle); + + return result; +} +#endif // defined __ANDROID__ + // extract the ELF HW capabilities bitmap from /proc/self/auxv static unsigned int get_elf_hwcap_from_proc_self_auxv() { FILE* fp = fopen("/proc/self/auxv", "rb"); if (!fp) { + NCNN_LOGE("fopen /proc/self/auxv failed"); return 0; } -#define AT_HWCAP 16 -#define AT_HWCAP2 26 #if __aarch64__ || __riscv_xlen == 64 struct { @@ -139,7 +188,18 @@ static unsigned int get_elf_hwcap_from_proc_self_auxv() return result; } -static unsigned int g_hwcaps = get_elf_hwcap_from_proc_self_auxv(); +static unsigned int get_elf_hwcap() +{ +#if defined __ANDROID__ + unsigned int hwcap = get_elf_hwcap_from_getauxval(); + if (hwcap) + return hwcap; +#endif + + return get_elf_hwcap_from_proc_self_auxv(); +} + +static unsigned int g_hwcaps = get_elf_hwcap(); #if __aarch64__ // from arch/arm64/include/uapi/asm/hwcap.h