在我正在写的可执行文件中,我有2个实现相同功能的实现,一个用于armhf(快速),另一个用于armel(慢速)。在运行时,我想检测CPU类型,如果检测到armhf,则调用armhf实现。如何检测CPU?我需要在C代码中这样的内容:
int is_cpu_armhf(void) {
...
}
该代码可以包含内联汇编,但最好不要包含对库函数的调用或系统调用,因为它应与多个库和多个操作系统一起使用。
我找到了https://github.com/pytorch/cpuinfo/tree/master/src/arm,但是它似乎没有使用任何内联程序集,但是它依赖于操作系统来获取CPU信息。
最佳答案
... I have two implementations of the same function, one for armhf (fast) and one for armel (slow). At runtime I'd like to detect the CPU type, and call the armhf implementation if armhf was detected. How do I detect the CPU? I need something like this in C code ...
正如@Ruslan指出的那样,cpu功能在ARM上大多具有特权。如果您是root用户,则可以读取功能掩码的MRS寄存器。最新的内核为ARM伪造了cpuid,但仅在最新的内核上可用。
在运行时,您可以在Linux上解析
/proc/cpuinfo
以获得cpu架构和功能。您也许还可以调用getauxval
并从辅助 vector 中读取这些位。我发现最有效的是:
getauxval
以获得拱形和特征SIGILL
失败,请使用getauxval
探针SIGILL
探针很昂贵。您设置了SIGILL
处理程序,然后尝试使用ARMv5或ARMv7指令。如果捕获到SIGILL
,则说明该指令不可用。SIGILL
探针由Crypto++和OpenSSL使用。例如,在ARMv7中添加了movw
和movt
。这是使用Crypto++中的 movw
and movt
instructions探测ARMv7的代码。 OpenSSL在 crypto/armcap.c
中执行类似的操作。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?在Android上,您应该使用
android_getCpuFamily()
和android_getCpuFeatures()
而不是getauxval
。ARM人士说您不应该解析
/proc/cpuinfo
。另请参阅ARM Blog和Runtime Detection of CPU Features on an armv8-a CPU。 (非付费墙版本here)。不要在iOS设备上执行基于
SIGILL
的功能探针。苹果设备破坏内存。对于Apple设备,请使用How to get device make and model on iOS?之类的东西。您还需要基于编译器选项启用代码路径。那是蠕虫的全部“另一 jar ”。对于该问题,请参见Detect ARM NEON availability in the preprocessor?
有关其他一些要检查的源代码,请参见Crypto++中的
cpu.cpp
。在这里Crypto++可以执行诸如getauxval
,android_getCpuFamily()
和android_getCpuFeatures()
之类的操作。Crypto++
SIGILL
探针发生在特定的源文件中,因为源文件通常需要使用编译器选项来启用拱形,例如ARM的-march=armv7-a
和-fpu=neon
。这就是在 neon_simd.cpp
中检测到ARMv7和NEON的原因。 (i686和x86_64,Altivec,PowerPC和Aarch64还有其他类似文件)。这是Crypto++中
getauxval
和android_getCpuFamily()
的样子。首先使用CPU_QueryARMv7
。如果CPU_QueryARMv7
失败,则使用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
和movt
的ARM指令: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
这是阅读MRS的样子。这与在x86上获取cpuid位掩码非常相似。以下代码可用于获取Aarch64的加密功能,但需要root特权。
该代码要求异常级别1(EL1)及更高级别,但是用户空间在EL0上运行。尝试从用户区运行代码会导致
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
自己发布指令的好处是,在编译源文件时它不需要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");
您可以在没有arch选项的情况下编译以上代码:
gcc cpu-test.c -o cpu-test.o
如果要使用
movw
和movt
:int a;
asm volatile("movw %0,%1 \n"
"movt %0,%1 \n"
: "=r"(a) : "i"(0x1234));
那么您的编译器将需要支持ARMv7,并且您需要使用arch选项:
gcc -march=armv7 cpu-test.c -o cpu-test.o
并且GCC可以在整个源文件中使用ARMv7,这可能会导致
SIGILL
超出您的 protected 代码。我在x86上使用了错误的指令集而遇到了Clang。参见Crypto++ Issue 751。海湾合作委员会一定会跟随。在Clang情况下,我需要在源文件上使用
-march=avx
进行编译,以便可以使用AVX内部函数。 Clang在 protected 块之外生成了AVX代码,并在旧的Core2 Duo机器上崩溃了。 (Clang生成的不安全代码是std::string
的初始化)。对于ARM,问题是,您需要
-march=armv7
才能通过movw
和movt
启用ISA,并且编译器认为它也可以使用ISA。这是编译器中的一个设计错误,其中用户的拱门和编译器的拱门被混合在一起。实际上,由于编译器的设计,您需要一个用户体系结构和一个单独的编译器体系结构。
关于c - 如何在C代码中区分armhf(ARMv7)和armel(ARMv4)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59478463/