When zero is given in eax
, the cpuid
instruction returns two things:
- in
eax
1, the maximum value supported for the input operand in eax
for cpuid
on this processor, and
- in
ebx
, edx
, and eax
, the string “GenuineIntel”, distributed across those registers in that order. The bytes of the string are simply put in the registers, four bytes in each register.
The assembly code you have shown causes the GCC or Clang compiler to copy the latter registers to your reg
array.
To print this array correctly, you could pass to printf
:
- a format string containing
%.12s
to print at most 12 characters, and
- a
char *
that points to the first byte of reg
.
For example:
printf("%.12s\n", (char *) reg);
(Note that this conversion to char *
is specifically allowed by C aliasing rules: The bytes of any object may be accessed using a pointer-to-character type. Other pointer conversions, or uses of their results, are not always defined by the C standard. Pedantically, (char *) ®
may be needed, as it provides a pointer to the first byte of the entire array rather than a pointer to the first byte of reg[0]
. A strict interpretation of the C standard could say that the latter pointer is not reliable for arithmetic beyond reg[0]
.)
Footnote 1: Modifying an input-only operand is a bug; the compiler will assume that EAX is unmodified across the asm
statement. In this case, that could lead to calling printf with al != 0
, even though there are no floating-point values in XMM registers. (x86-64 System V calling convention.) With other callers / surrounding code, the problems could be more serious.
Since you don't care about the value of index
after your asm statement, a read/write "+a"
operand is an easy way in this case to tell the compiler that EAX is also modified:
int index = 0;
int reg[3];
__asm__(
"cpuid"
: "+a"(index), "=b"(reg[0]), "=c"(reg[2]), "=d"(reg[1])
);