The x86 and x86-64 instruction sets have an instruction called
CPUID that tells the program who made the CPU and what features it may have.
We try to get that info using
x86-64 assembly in this tutorial.
When we set
EAX to 0, and call the
CPUID, instruction, the processor
ECX, EBX, EDX registers with parts of the string
containing the name of the processor, e.g.
instruction on the x86-64 instruction set pushes the 64-bit versions of the registers. We need to concatenate
the strings in the 3 registers and print them on screen, using
write() system call.
So what we do is shift the lower 32-bit part of
EDX) to the upper 32-bit part of
RDX and move the lower 32-bit part of
EBX) into the
lower 32-bit part of
RDX, thus placing the two of the parts of the string into the
The remaining part of the string is in the lower 32-bit
RCX, which when gets pushed connects to the
remaining string in the
RDX register. Then we push both the registers on the stack, and then
write() system call.
For doing a
write() system call, we place the number of the system
call as given in
RAX and then place the arguments
RDI, RSI & RDX registers.
RDI is the file descriptor where we want to
RSI holds the address of the string and
RDX holds the
length of the string. If the value of
0x1, the string obtained from the
CPUID call will be printed on screen (
We then do an
exit() system call to exit the program. This program
does not use the C library and uses the kernel interface directly.
Hence we do not have to define a
main() function. We can directly
_start symbol that the operating system uses to invoke every
application. We have to declare it
global so that it is noted as a
symbol in the application’s final binary that has been created and
can be called by the operating system.
section .text global _start _start: xor eax,eax ; place 0x0 in EAX for getting the name of the processor cpuid shl rdx,0x20 ; shifting lower 32-bits into upper 32-bit of RDX xor rdx,rbx ; moving EBX into EDX push rcx ; push the string on the stack push rdx mov rdx, 0x10 ; since we are pushing 2 registers, the length is not more than 16 bytes. mov rsi, rsp ; The address of the string is RSP because the string is on the stack push 0x1 ; The system call write() has the value 0x1 in the sytem call table pop rax mov rdi, rax ; Since we are printing to stdout, the value of the file descriptor is also 0x1 syscall ; make the system call mov rax, 0x3c ; We now make the exit() system call here. xor rdi, rdi ; the argument is 0x0 syscall ; this exits the application and gives control back to the shell or the Operating system
The command to compile the above code is as follows:
$ yasm -f elf64 cpuid.asm $ ld -o cpuid.out cpuid.o