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

JWW*_*ker 5 macos x86 assembly xcode cpuid

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

kern/kern_types.h: No such file or directory
Run Code Online (Sandbox Code Playgroud)

我不明白。但是该函数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));
}
Run Code Online (Sandbox Code Playgroud)

那给了我错误:

error: can't find a register in class 'BREG' while reloading 'asm'
error: 'asm' operand has impossible constraints
Run Code Online (Sandbox Code Playgroud)

谷歌搜索该错误导致我提出以下问题:在Mac上出现问题:“重新加载asm时,无法在BREG类中找到一个寄存器”

但是,该问题的解决方案是使用dynamic-no-pic选项(GCC_DYNAMIC_NO_PIC构建设置),Xcode在构建设置上的帮助显示“不适用于共享库(需要与位置无关)。” 我正在建立一个框架,我认为这是一个共享库。那么我该如何做呢?

Mic*_*tch 2

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

error: can't find a register in class 'BREG' while reloading 'asm'
error: 'asm' operand has impossible constraints
Run Code Online (Sandbox Code Playgroud)

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

尽管大多数人建议使用特殊的编译器标志进行编译,但可以通过修改汇编代码来重写该函数以支持x86-64 / IA32PIC /非 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));
}
Run Code Online (Sandbox Code Playgroud)

显着的变化是该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 代码中是特殊的,这就是编译器最初抱怨其神秘消息的原因。