在我正在写的可执行文件中,我有2个实现相同功能的实现,一个实现用于armhf(快速),另一个实现用于armel(慢速)。在运行时,我想检测CPU类型,如果检测到armhf,则调用armhf实现。如何检测CPU?我需要在C代码中这样的内容:
int is_cpu_armhf(void) {
...
}
代码可能包含内联汇编,但最好不要包含对库函数的调用或系统调用,因为它应与多个库和多个操作系统一起使用。
我已经找到https://github.com/pytorch/cpuinfo/tree/master/src/arm,但是它似乎没有使用任何内联程序集,但是它依赖于操作系统来获取CPU信息。
...我有两种具有相同功能的实现,一种用于armhf(快速),一种用于armel(慢速)。在运行时,我想检测CPU类型,如果检测到armhf,则调用armhf实现。如何检测CPU?我在C代码中需要这样的东西...
正如@Ruslan所指出的那样,cpu功能在ARM上大多具有特权。如果您是root用户,则可以读取功能掩码的MRS寄存器。最新的内核为ARM伪造了cpuid,但仅在最新的内核上可用。
在运行时,您可以在Linux上解析/proc/cpuinfo
以获得cpu架构和功能。您也可以调用getauxval
并从辅助向量中读取位。
我发现最有效的是:
getauxval
的拱形和特征SIGILL
失败,请使用getauxval
探针SIGILL
探针很贵。您设置了SIGILL
处理程序,然后尝试使用ARMv5或ARMv7指令。如果捕获到SIGILL
,则说明该指令不可用。
SIGILL
探针由Crypto ++和OpenSSL使用。例如,在ARMv7中添加了movw
和movt
。这是使用Crypto ++中的movw
and movt
instructions探测ARMv7的代码。 OpenSSL在movw
中执行类似的操作。
movt
探针中需要crypto/armcap.c
。另请参见crypto/armcap.c
在Android上,您应该使用bool CPU_ProbeARMv7()
{
volatile bool result = true;
volatile SigHandler oldHandler = signal(SIGILL, SigIllHandler);
if (oldHandler == SIG_ERR)
return false;
volatile sigset_t oldMask;
if (sigprocmask(0, NULLPTR, (sigset_t*)&oldMask))
return false;
if (setjmp(s_jmpSIGILL))
result = false;
else
{
unsigned int a;
asm volatile (
#if defined(__thumb__)
".inst.n 0xf241, 0x2034 \n\t" // movw r0, 0x1234
".inst.n 0xf2c1, 0x2034 \n\t" // movt r0, 0x1234
"mov %0, r0 \n\t" // mov [a], r0
#else
".inst 0xe3010234 \n\t" // movw r0, 0x1234
".inst 0xe3410234 \n\t" // movt r0, 0x1234
"mov %0, r0 \n\t" // mov [a], r0
#endif
: "=r" (a) : : "r0");
result = (a == 0x12341234);
}
sigprocmask(SIG_SETMASK, (sigset_t*)&oldMask, NULLPTR);
signal(SIGILL, oldHandler);
return result;
}
和volatiles
而不是What sense do these clobbered variable warnings make?。
ARM员工说您应该NOT解析android_getCpuFamily()
。另请参见ARM Blog和android_getCpuFeatures()
。 (非付费墙版本getauxval
)。
DO NOT在iOS设备上执行基于/proc/cpuinfo
的功能探针。苹果设备破坏内存。对于Apple设备,请使用Runtime Detection of CPU Features on an armv8-a CPU之类的东西。
您还需要根据编译器选项启用代码路径。那是蠕虫的全部“另一罐”。对于该问题,请参见here
有关要检查的其他一些源代码,请参阅Crypto ++中的SIGILL
。在这里Crypto ++可以执行诸如How to get device make and model on iOS?,Detect ARM NEON availability in the preprocessor?和cpu.cpp
的调用。
Crypto ++ cpu.cpp
探测发生在特定的源文件中,因为源文件通常需要编译器选项才能启用架构,例如ARM的getauxval
和android_getCpuFamily()
。这就是在android_getCpuFeatures()
中检测到ARMv7和NEON的原因。 (i686和x86_64,Altivec,PowerPC和Aarch64还有其他类似的文件。)>
这是SIGILL
和-march=armv7-a
在Crypto ++中的样子。首先使用-fpu=neon
。如果neon_simd.cpp
失败,则使用neon_simd.cpp
特征探针。
getauxval
android_getCpuFamily()
和CPU_QueryARMv7
的ARM指令已从以下源代码反汇编:
CPU_QueryARMv7
这是阅读MRS的样子。这与在x86上获取cpuid位掩码非常相似。下面的代码可用于获取Aarch64的加密功能,但需要root特权。
该代码要求异常级别1(EL1)及更高,但是用户空间在EL0上运行。尝试从用户区运行代码会导致SIGILL
和终止。
inline bool CPU_QueryARMv7() { #if defined(__ANDROID__) && defined(__arm__) if (((android_getCpuFamily() & ANDROID_CPU_FAMILY_ARM) != 0) && ((android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_ARMv7) != 0)) return true; #elif defined(__linux__) && defined(__arm__) if ((getauxval(AT_HWCAP) & HWCAP_ARMv7) != 0 || (getauxval(AT_HWCAP) & HWCAP_NEON) != 0) return true; #elif defined(__APPLE__) && defined(__arm__) // Apple hardware is ARMv7 or above. return true; #endif return false; }
您自己发布指令的好处是,在编译源文件时不需要拱门选项:
movw
您可以在没有arch选项的情况下编译以上代码:
movt
如果使用
int a; asm volatile("movw %0,%1 \n" "movt %0,%1 \n" : "=r"(a) : "i"(0x1234)); 00000010 <_Z5test2v>: // ARM 10: e3010234 movw r0, #4660 ; 0x1234 14: e3410234 movt r0, #4660 ; 0x1234 18: e12fff1e bx lr 0000001c <_Z5test3v>: // Thumb 1c: f241 2034 movw r0, #4660 ; 0x1234 20: f2c1 2034 movt r0, #4660 ; 0x1234 24: e12fff1e bx lr
和SIGILL
:
#if defined(__arm64__) || defined(__aarch64__) uint64_t caps = 0; // Read ID_AA64ISAR0_EL1 __asm __volatile("mrs %0, " "id_aa64isar0_el1" : "=r" (caps)); #elif defined(__arm__) || defined(__aarch32__) uint32_t caps = 0; // Read ID_ISAR5_EL1 __asm __volatile("mrs %0, " "id_isar5_el1" : "=r" (caps)); #endif
然后您的编译器将需要支持ARMv7,并且您需要使用arch选项:
unsigned int a; asm volatile ( #if defined(__thumb__) ".inst.n 0xf241, 0x2034 \n\t" // movw r0, 0x1234 ".inst.n 0xf2c1, 0x2034 \n\t" // movt r0, 0x1234 "mov %0, r0 \n\t" // mov [a], r0 #else ".inst 0xe3010234 \n\t" // movw r0, 0x1234 ".inst 0xe3410234 \n\t" // movt r0, 0x1234 "mov %0, r0 \n\t" // mov [a], r0 #endif : "=r" (a) : : "r0");
并且GCC可以在整个源文件中使用ARMv7,这可能会在受保护的代码之外导致
gcc cpu-test.c -o cpu-test.o
。
我在x86上使用了错误的指令集,从而遇到了Clang。参见movw
。海湾合作委员会一定会跟随。在Clang情况下,我需要在源文件上使用movt
进行编译,以便可以使用AVX内部函数。 Clang在受保护的块之外生成了AVX代码,并在旧的Core2 Duo机器上崩溃了。 (Clang生成的不安全代码是int a;
asm volatile("movw %0,%1 \n"
"movt %0,%1 \n"
: "=r"(a) : "i"(0x1234));
的初始化。)
[对于ARM,问题是,您需要gcc -march=armv7 cpu-test.c -o cpu-test.o
才能通过SIGILL
和Crypto++ Issue 751启用ISA,并且编译器认为它也可以使用ISA。这是编译器中的设计错误,其中用户的拱门和编译器的拱门被合并。实际上,由于编译器的设计,您需要一个用户体系结构和一个单独的编译器体系结构。