How to invoke cpuid instruction on Mac?

I want to use the cpuid instruction to determine the functions of an Intel processor. I found the cpuid.h header in Kernel.framework, so I added Kernel.framework to my project and included <Kernel/i386/cpuid.h> in my source file. It produced

 kern/kern_types.h: No such file or directory 

which I do not understand. But the do_cpuid function, which I think I want to use, is defined in the string, so I tried to just copy it to my source code.

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

This gave me errors:

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

As a result of this error, I came across this question: Problem with Mac: "Cannot find case in BREG class when asm reloads"

But the solution to this issue was to use the dynamic-no-pic parameter ( GCC_DYNAMIC_NO_PIC build), and the Xcode help on build settings said: "Not suitable for shared libraries (which should be position-independent)." I am creating a framework that I think is considered a shared library. So how can I do this job?

+5
source share
2 answers

This is a rather cryptic error message:

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

occurs because one of the restrictions is not allowed. In this case, it is EBX . When compiling 32-bit code with the -fPIC option (position-independent code), the EBX register is used to move. It cannot be used as output or displayed as a private register.

Although most people suggest compiling using special compiler flags, the function can be rewritten to support x86-64 / IA32 and PIC / non-PIC by changing the assembler code to save the EBX register itself and restore it after. This can be done using code like:

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

A significant change is that the value of data[1] will be returned to the accessible register selected by the compiler. The constraint =&r tells the compiler that no matter which register it chooses, it cannot be any of the other input registers (we xchg register early with the code xchg ). In the code, we exchange EBX with an accessible register selected by the compiler. Then we exchange it back afterwards. At the end, EBX will contain the original value, and the selected free register will contain what the CPUID was returned. Then the assembler pattern will move the contents of this free register to data[1] .

In fact, we circumvented the problem by letting the compiler select a free register. The compiler is smart enough not to use EBX if it is bundled because it can be used for roaming code. In 64-bit code, EBX not used for moving, as is the case with 32-bit code, so it can be available for use.

An astute observer could notice xor %%ecx,%%ecx . I made this change regardless of the EBX problem. It is now considered good practice to clean ECX because some processors made by AMD may return obsolete values ​​if ECX not zero. If you are developing for non-PPC Mac platforms only, this change is not necessary because Apple uses Intel processors that do not exhibit this behavior.

EBX is usually special in 32-bit roaming / PIC code, so the compiler initially complained about its cryptic message.

+1
source

Thus, this may not give an answer to the question, but should provide a rather interesting workaround, at least for some people who find a way here.


sysctl provides a lot of information about the computer processor in macOS. To get started, type man sysctl in Terminal and look at everything with the prefix machdep.cpu . Take feature_bits , for example, instead of uint64_t cpuid_features(void); from cpuid.h :

sysctl machdep.cpu.feature_bits gives you real bit mask functions, although not very readable. sysctl machdep.cpu.features does the job a little better and provides you mostly understandable shortcuts.

Now to make this programmatically more interesting and actually quite feasible:

 #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; } 
0
source

All Articles