【问题标题】:How to call cpuid instruction in a Mac framework?如何在 Mac 框架中调用 cpuid 指令?
【发布时间】:2012-08-26 15:26:42
【问题描述】:

我想使用 cpuid 指令来识别 Intel CPU 的特性。我在 Kernel.framework 中找到了 cpuid.h 标头,因此我将 Kernel.framework 添加到我的项目中,并将<Kernel/i386/cpuid.h> 包含在我的源文件中。那产生了

kern/kern_types.h: No such file or directory

我不明白。但是我认为我想要使用的函数do_cpuid 是内联定义的,所以我尝试将其复制到我的源代码中。

static inline void
do_cpuid(uint32_t selector, uint32_t *data)
{
    asm("cpuid"
        : "=a" (data[0]),
          "=b" (data[1]),
          "=c" (data[2]),
          "=d" (data[3])
        : "a"(selector));
}

这给了我错误:

error: can't find a register in class 'BREG' while reloading 'asm'
error: 'asm' operand has impossible constraints

谷歌搜索该错误使我想到了这个问题:Problem on Mac : "Can't find a register in class BREG while reloading asm"

但该问题的解决方案是使用 dynamic-no-pic 选项(GCC_DYNAMIC_NO_PIC 构建设置),Xcode 对构建设置的帮助显示“不适合共享库(需要与位置无关)。 "我正在构建一个框架,我认为它算是一个共享库。那么我怎样才能完成这项工作呢?

【问题讨论】:

    标签: xcode macos assembly x86 cpuid


    【解决方案1】:

    这个相当神秘的错误信息:

    error: can't find a register in class 'BREG' while reloading 'asm'
    error: 'asm' operand has impossible constraints
    

    正在发生,因为其中一个约束是不允许的。在这种情况下,它是EBX。当使用 -fPIC 选项(位置无关代码)编译 32 位代码时,EBX 寄存器用于重定位。它不能用作输出或显示为损坏的寄存器。

    虽然大多数人建议使用特殊的编译器标志进行编译,但可以重写该函数以支持 x86-64/IA32PIC/non-PIC 通过修改汇编代码来保存EBX 寄存器本身并在之后恢复它。这可以通过如下代码完成:

    #include <inttypes.h>
    
    static inline void
    do_cpuid(uint32_t selector, uint32_t *data)
    {
        __asm__ __volatile__ (
            "xchg %%ebx, %k[tempreg]\n\t"
            "cpuid\n\t"
            "xchg %%ebx, %k[tempreg]\n"
            : "=a" (data[0]),
              [tempreg]"=&r" (data[1]),
              "=c" (data[2]),
              "=d" (data[3])
            : "a"(selector),
              "c"(0));
    }
    

    显着的变化是data[1] 值将在编译器选择的可用寄存器中返回。 =&amp;r 约束告诉编译器它选择的任何寄存器都不能是任何其他输入寄存器(我们很早就用xchg 代码破坏了寄存器)。在代码中,我们将EBX 与编译器选择的可用寄存器交换。之后我们再换回来。完成后EBX 将包含其原始值,并且选择的空闲寄存器将包含CPUID 返回的内容。然后,汇编器模板将该空闲寄存器的内容移动到data[1]

    通过允许编译器选择一个空闲寄存器,我们有效地规避了这个问题。编译器足够聪明,不会使用EBX,因为它可能被用于可重定位代码。在 64 位代码中,EBX 不像 32 位代码那样用于重定位,因此它可能可供使用。

    精明的观察者可能已经注意到xor %%ecx,%%ecx。这是我独立于EBX 问题所做的更改。现在认为清除ECX 是一种很好的做法,因为如果ECX 不为零,AMD 制造的某些处理器可能会返回陈旧值。如果您仅针对非 PPC Mac 平台进行开发,则无需进行此更改,因为 Apple 使用的 Intel 处理器不会出现这种行为。

    通常EBX 在 32 位可重定位/PIC 代码中是特殊的,这就是编译器最初抱怨其神秘消息的原因。

    【讨论】:

    • @DavidWohlferd :此时我只考虑使用 cpuid 内在函数。
    • @DavidWohlferd :我投入了零钱。
    【解决方案2】:

    因此,这可能无法回答问题,但应该为至少一些人在这里找到自己的方式提供一个足够有趣的解决方法。


    sysctl 提供了大量有关 macOS 上机器 CPU 的信息。对于初学者,在终端中输入 man sysctl 并查看以 machdep.cpu 为前缀的所有内容。以feature_bits 为例,从cpuid.h 替换uint64_t cpuid_features(void);

    sysctl machdep.cpu.feature_bits 为您提供实际功能位掩码,尽管可读性不强。 sysctl machdep.cpu.features 做得更好一些,并为您提供最容易理解的首字母缩略词。

    现在,以编程方式执行此操作更加有趣并且实际上非常可行:

    #include <sys/sysctl.h>
    
    // ...
    
    int64_t value = 0;
    size_t valueByteSize = sizeof(value);
    int error = sysctlbyname("machdep.cpu.feature_bits", &value, &valueByteSize, NULL, 0);
    if (!error)
    {
       // Check for the next best bit defined in cpuid.h
       // Since you can't build with cpuid.h directly, just copy over the definitions you actually need
       bool hasFPU = value & CPUID_FEATURE_FPU;
    }
    

    【讨论】:

      猜你喜欢
      • 2012-12-25
      • 2018-07-08
      • 2011-06-28
      • 1970-01-01
      • 2021-02-28
      • 2012-12-26
      • 2012-10-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多