如何在C代码中区分armhf(ARMv7)和armel(ARMv4)?

问题描述 投票:0回答:1

在我正在写的可执行文件中,我有2个实现相同功能的实现,一个实现用于armhf(快速),另一个实现用于armel(慢速)。在运行时,我想检测CPU类型,如果检测到armhf,则调用armhf实现。如何检测CPU?我需要在C代码中这样的内容:

int is_cpu_armhf(void) {
  ...
}

代码可能包含内联汇编,但最好不要包含对库函数的调用或系统调用,因为它应与多个库和多个操作系统一起使用。

我已经找到https://github.com/pytorch/cpuinfo/tree/master/src/arm,但是它似乎没有使用任何内联程序集,但是它依赖于操作系统来获取CPU信息。

c linux gcc arm cpu-architecture
1个回答
6
投票

...我有两种具有相同功能的实现,一种用于armhf(快速),一种用于armel(慢速)。在运行时,我想检测CPU类型,如果检测到armhf,则调用armhf实现。如何检测CPU?我在C代码中需要这样的东西...

正如@Ruslan所指出的那样,cpu功能在ARM上大多具有特权。如果您是root用户,则可以读取功能掩码的MRS寄存器。最新的内核为ARM伪造了cpuid,但仅在最新的内核上可用。

在运行时,您可以在Linux上解析/proc/cpuinfo以获得cpu架构和功能。您也可以调用getauxval并从辅助向量中读取位。

我发现最有效的是:

  1. 尝试阅读getauxval的拱形和特征
  2. 如果SIGILL失败,请使用getauxval探针

SIGILL探针很贵。您设置了SIGILL处理程序,然后尝试使用ARMv5或ARMv7指令。如果捕获到SIGILL,则说明该指令不可用。

SIGILL探针由Crypto ++和OpenSSL使用。例如,在ARMv7中添加了movwmovt。这是使用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的getauxvalandroid_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 才能通过SIGILLCrypto++ Issue 751启用ISA,并且编译器认为它也可以使用ISA。这是编译器中的设计错误,其中用户的拱门和编译器的拱门被合并。实际上,由于编译器的设计,您需要一个用户体系结构和一个单独的编译器体系结构。

© www.soinside.com 2019 - 2024. All rights reserved.