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