2012-08-31 52 views
4

我想使用cpuid指令来识别Intel CPU的功能。我在Kernel.framework中找到了cpuid.h头文件,所以我将Kernel.framework添加到了我的项目中,并在我的源文件中包含了<Kernel/i386/cpuid.h>。那生产如何在Mac框架中调用cpuid指令?

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"

但是解决这个问题是使用动态无pic选项(编译设置),Xcode对编译设置的帮助表示“不适合共享库(它们需要与位置无关)”。我正在构建一个框架,我认为这是一个共享库。那我该如何做这项工作?

回答

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 /IA32PIC通过修改汇编代码保存EBX寄存器/ 非PIC本身并在恢复之后。这可以用类似的代码来完成:

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

的显著变化是值将在可用时返回注册编译器选择。 =&r约束告诉编译器,它选择的任何寄存器都不能是任何其他输入寄存器(我们提前用xchg代码摧毁了寄存器)。在代码中,我们使用编译器选择的可用寄存器来交换EBX。然后我们将之交换回去。完成时EBX将包含其原始值,并且所选的免费寄存器将包含由CPUID返回的内容。然后汇编程序模板将把该空闲寄存器的内容移动到。

实际上,我们通过允许编译器选择一个空闲寄存器来避免了这个问题。编译器足够智能,因为它可能被用于可重定位代码,所以不要使用EBX。在64位代码中,EBX不用于重定位,就像使用32位代码一样,因此它可以用于使用。

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

一般EBX在32位可重定位/ PIC代码中是特殊的,这就是为什么编译器最初抱怨它的神秘消息。