6

我想使用 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

谷歌搜索该错误使我想到了这个问题:Mac 上的问题:“在重新加载 asm 时找不到 BREG 类中的寄存器”

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

4

2 回答 2

2

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

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-PICEBX ,以保存寄存器本身并在之后恢复它。这可以通过以下代码完成:

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

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

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

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

于 2015-09-13T03:18:11.503 回答
1

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


sysctl在 macOS 上提供了很多关于机器 CPU 的信息。对于初学者,输入man sysctl终端并查看以 . 为前缀的所有内容machdep.cpufeature_bits以替换uint64_t cpuid_features(void);from为例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;
}
于 2019-10-15T16:47:34.683 回答