如何在Mac框架中调用cpuid指令?

时间:2012-08-31 19:46:18

标签: xcode macos assembly intel cpuid

我想使用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对构建设置的帮助说“不适合共享库(需要与位置无关) )“。我正在构建一个框架,我认为它是一个共享库。那么我怎样才能做到这一点呢?

2 个答案:

答案 0 :(得分:0)

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

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

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

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

#include <inttypes.h>

static inline void
do_cpuid(uint32_t selector, uint32_t *data)
{
    __asm__ __volatile__ (
        "xor %%ecx,%%ecx\n\t"
        "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));
}

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

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

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

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

答案 1 :(得分:0)

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


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

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;
}