[英]How to distinguish armhf (ARMv7) and armel (ARMv4) in C code?
在我正在编写的可执行文件中,我有 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 arch 和功能。 您也可以调用getauxval
并从辅助向量中读取位。
我发现效果最好的是:
getauxval
以获得 arch 和 featuregetauxval
失败,请使用SIGILL
探针 SIGILL
探针很昂贵。 您设置了一个SIGILL
处理程序并尝试使用 ARMv5 或 ARMv7 指令。 如果您收到SIGILL
您就知道该指令不可用。
SIGILL
探针由 Crypto++ 和 OpenSSL 使用。 例如, movw
和movt
是在 ARMv7 中添加的。 这是使用 Crypto++ 中的movw
和movt
指令探测 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
。 另请参阅这些损坏的变量警告有什么意义?
在 Android 上,您应该使用android_getCpuFamily()
和android_getCpuFeatures()
而不是getauxval
。
ARM的人说你不应该解析/proc/cpuinfo
。 另请参阅 ARM 博客和armv8-a CPU 上 CPU 功能的运行时检测。 ( 此处为非付费专区版本)。
不要在 iOS 设备上执行基于SIGILL
的特征探测。 苹果设备垃圾内存。 对于 Apple 设备,请使用诸如如何在 iOS 上获取设备品牌和型号之类的内容? .
您还需要根据编译器选项启用代码路径。 那是一整罐“蠕虫”。 对于该问题,请参阅在预处理器中检测 ARM NEON 可用性?
有关要检查的其他源代码,请参阅 Crypto++ 中的cpu.cpp
。 它是 Crypto++ 执行诸如调用getauxval
、 android_getCpuFamily()
和android_getCpuFeatures()
。
Crypto++ SIGILL
探测出现在特定的源文件中,因为源文件通常需要一个编译器选项来启用-march=armv7-a
,例如-march=armv7-a
和-fpu=neon
for ARM。 这就是为什么在neon_simd.cpp
中检测到 ARMv7 和 NEON 的原因。 (对于 i686 和 x86_64、Altivec、PowerPC 和 Aarch64,还有其他类似的文件)。
这是 Crypto++ 中getauxval
和android_getCpuFamily()
样子。 CPU_QueryARMv7
使用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
。
我在 x86 上使用错误的指令集体验过 Clang。 请参阅Crypto++ 问题 751 。 海湾合作委员会肯定会跟进。 在 Clang 的情况下,我需要在源文件上使用-march=avx
进行编译,以便我可以使用 AVX 内在函数。 Clang 在我的受保护块之外生成了 AVX 代码,它在旧的 Core2 Duo 机器上崩溃了。 (Clang 生成的不安全代码是std::string
初始化)。
在 ARM 的情况下,问题是,您需要-march=armv7
才能使用movw
和movt
启用 ISA,并且编译器认为它也可以使用 ISA。 这是编译器中的一个设计错误,其中用户的架构和编译器的架构被混为一谈。 实际上,由于编译器设计的原因,您需要一个用户 arch 和一个单独的编译器 arch。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.