pts*_*pts 6 c linux gcc arm cpu-architecture
在我正在编写的可执行文件中,我有 2 个相同功能的实现,一个用于 armhf(快速),另一个用于 armel(慢)。在运行时,我想检测 CPU 类型,并在检测到 armhf 时调用 armhf 实现。如何检测CPU?我在 C 代码中需要这样的东西:
int is_cpu_armhf(void) {
...
}
Run Code Online (Sandbox Code Playgroud)
代码可能包含内联汇编,但最好不要包含对库函数的调用或系统调用,因为它应该适用于多个库和多个操作系统。
我找到了https://github.com/pytorch/cpuinfo/tree/master/src/arm,但它似乎没有使用任何内联程序集,但它依赖于操作系统来获取 CPU 信息。
...我有两种相同功能的实现,一种用于 armhf(快速),一种用于 armel(慢)。在运行时,我想检测 CPU 类型,并在检测到 armhf 时调用 armhf 实现。如何检测CPU?我需要在 C 代码中这样的东西......
正如@Ruslan 指出的那样,CPU 功能主要在 ARM 上享有特权。如果您是 root,那么您可以读取功能掩码的 MRS 寄存器。最新的内核为 ARM 伪造了一个 cpuid,但它仅适用于最新的内核。
在运行时,您可以/proc/cpuinfo在 Linux上解析CPU 架构和功能。您也可以getauxval从辅助向量中调用和读取位。
我发现效果最好的是:
getauxvalarch 和 featureSIGILL如果getauxval失败,请使用探针该SIGILL探测器是昂贵的。您设置了一个SIGILL处理程序并尝试使用 ARMv5 或 ARMv7 指令。如果你抓住了,SIGILL你就知道指令不可用。
SIGILLCrypto++ 和 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;
}
Run Code Online (Sandbox Code Playgroud)
的volatiles需要在探针。另请参阅这些损坏的变量警告有什么意义?
在 Android 上,您应该使用android_getCpuFamily()andandroid_getCpuFeatures()而不是getauxval.
ARM的人说,你应该不解析/proc/cpuinfo。另请参阅 ARM 博客和armv8-a CPU 上 CPU 功能的运行时检测。(此处为非付费专区版本)。
不要SIGILL在 iOS 设备上执行基于特征的探测。苹果设备垃圾内存。对于 Apple 设备,请使用诸如如何在 iOS 上获取设备品牌和型号之类的内容?.
您还需要根据编译器选项启用代码路径。那是一整罐“蠕虫”。对于该问题,请参阅在预处理器中检测 ARM NEON 可用性?
有关要检查的其他一些源代码,请参阅cpu.cppCrypto++。这是其中的Crypto ++确实喜欢召唤物的地方getauxval,android_getCpuFamily()和android_getCpuFeatures()。
的加密++SIGILL探针发生在特定的源文件,因为一个源文件通常需要编译器选项以使拱形,像-march=armv7-a和-fpu=neon用于ARM。这就是为什么在neon_simd.cpp. (对于 i686 和 x86_64、Altivec、PowerPC 和 Aarch64,还有其他类似的文件)。
这是Crypto++ 中agetauxval和的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;
}
Run Code Online (Sandbox Code Playgroud)
用于movw和的 ARM 指令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
Run Code Online (Sandbox Code Playgroud)
这是阅读 MRS 的样子。这与在 x86 上获取 cpuid 位掩码非常相似。下面的代码可用于获取 Aarch64 的加密功能,但它需要 root 权限。
代码需要异常级别 1 (EL1) 及更高级别,但用户空间运行在 EL0。尝试从用户空间运行代码会导致 aSIGILL和终止。
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
Run Code Online (Sandbox Code Playgroud)
自己发出指令的好处是,编译源文件时不需要arch选项:
#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
Run Code Online (Sandbox Code Playgroud)
您可以在没有 arch 选项的情况下编译上述代码:
gcc cpu-test.c -o cpu-test.o
Run Code Online (Sandbox Code Playgroud)
如果您要使用movw和movt:
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");
Run Code Online (Sandbox Code Playgroud)
那么你的编译器需要支持 ARMv7,你需要使用 arch 选项:
gcc -march=armv7 cpu-test.c -o cpu-test.o
Run Code Online (Sandbox Code Playgroud)
并且 GCC 可以在整个源文件中使用 ARMv7,这可能会导致SIGILL受保护代码的外部。
我在 x86 上使用错误的指令集体验过 Clang。请参阅Crypto++ 问题 751。海湾合作委员会肯定会跟进。在 Clang 的情况下,我需要-march=avx在源文件上进行编译,以便我可以使用 AVX 内在函数。Clang 在我的受保护块之外生成了 AVX 代码,它在旧的 Core2 Duo 机器上崩溃了。(Clang 生成的不安全代码是 a 的初始化std::string)。
在 ARM 的情况下,问题是,您需要-march=armv7使用movw和启用 ISA,并且movt编译器认为它也可以使用 ISA。这是编译器中的一个设计错误,其中用户的架构和编译器的架构被混为一谈。实际上,由于编译器设计的原因,您需要一个用户 arch 和一个单独的编译器 arch。