hi all :smiley:
on this page of the Wikipedia https://en.wikipedia.org/wiki/CPUID#Calling_CPUID (https://en.wikipedia.org/wiki/CPUID#Calling_CPUID) if you scroll down a bit, you will see a small asm program to print 3 pieces of information about your CPU
I know that Linux and Windows use the CPU registers differently, how would you modify the program so that it would run on Windows ?
the reason I post in the Soap Box is because I don't program in masm but prefer high level languages, such as FreeBasic, the code below modified a bit as inline-asm works OK in Linux, it prints
QuotePUID: 906ed
Largest basic function number implemented: 22
Vendor ID: GenuineIntel
here's the FreeBasic code with inline-asm
sub cpuid naked cdecl
asm
.intel_syntax noprefix
' .text
'.m0: .string "CPUID: %x\n"
'.m1: .string "Largest basic function number implemented: %i\n"
'.m2: .string "Vendor ID: %s\n"
'.globl main
'main:
push r12
mov eax, 1
sub rsp, 16
cpuid
lea rdi, .m0[rip]
mov esi, eax
call printf
mov eax, 0
cpuid
lea rdi, .m1[rip]
mov esi, eax
mov r12d, edx
mov ebp, ecx
call printf
mov 3[rsp], ebx
lea rsi, 3[rsp]
lea rdi, .m2[rip]
mov 7[rsp], r12d
mov 11[rsp], ebp
call printf
add rsp, 16
pop r12
ret
'.text
.m0: .string "CPUID: %x\n"
.m1: .string "Largest basic function number implemented: %i\n"
.m2: .string "Vendor ID: %s\n"
'.section .note.GNU-stack,"",@progbits
end asm
end sub
cpuid
print "press return to exit"
Sleep
thanks in advance
I almost got it, it prints
QuoteCPUID: 906ed
Largest basic function number implemented: 22
Vendor ID: -ö⌂
press return to exit
I can't figure out how to get the Vendor ID to work
sub cpuid naked cdecl
asm
.intel_syntax noprefix
' .text
'.m0: .string "CPUID: %x\n"
'.m1: .string "Largest basic function number implemented: %i\n"
'.m2: .string "Vendor ID: %s\n"
'.globl main
'main:
push r8
push r9
push rbx
push rdx
push rsi
mov eax, 1
sub rsp, 16
cpuid
lea rcx, .m0[rip]
mov edx, eax
call printf
mov eax, 0
cpuid
lea rcx, .m1[rip]
mov esi, edx
mov edx, eax
mov r8d, esi
mov r9d, ecx
call printf
mov 3[rsp], ebx
lea rdx, 3[rsp]
lea rcx, .m2[rip]
mov 7[rsp], r8d
mov 11[rsp], r9d
call printf
add rsp, 16
pop rsi
pop rdx
pop rbx
pop r9
pop r8
ret
'.text
.m0: .string "CPUID: %x\n"
.m1: .string "Largest basic function number implemented: %i\n"
.m2: .string "Vendor ID: %s\n"
'.section .note.GNU-stack,"",@progbits
end asm
end sub
cpuid
print "press return to exit"
Sleep
Here's my code for displaying the vendor ID using CUPID:
$CRLF EQU <13, 10>
$tab EQU 9
VendorIDstring DB $CRLF, "Vendor ID:", $tab, $tab, '"'
VendorID1 DB "xxxx"
VendorID2 DB "xxxx"
VendorID3 DB "xxxx"
DB '"', $CRLF, 0
; CPUID w/EAX=0--get vendor ID string:
XOR EAX, EAX
CPUID
MOV DWORD PTR VendorID1, EBX
MOV DWORD PTR VendorID2, EDX
MOV DWORD PTR VendorID3, ECX
; Now you can output the entire string (VendorIDstring) by your chosen method.
The ID string comes back in 3 registers (EBX, ECX, EDX) as 12 bytes of data. It's not a string.
You can adapt this code to yours. Also no need to use 64-bit anything in this.
thank you NoCforMe
the code in opening post works ok in Linux, why doesn't the second post work in Windows ?
if I hard-code the numbers that EBX, EDX and ECX return from CPUID in the data section then it prints "Vendor ID: GenuineIntel"
but if I place the values on the stack [rsp] it prints garbage
here's the hard-coded version, the values placed on the stack are irrelevant in this case
sub cpuid naked cdecl
asm
.intel_syntax noprefix
push r8
push r9
push rbx
push rdx
push rsi
mov eax, 1
sub rsp, 16
cpuid
lea rcx, .m0[rip]
mov edx, eax
call printf
mov eax, 0
cpuid
lea rcx, .m1[rip]
mov esi, edx
mov edx, eax
mov r8d, esi
mov r9d, ecx
call printf
mov dword ptr 3[rsp], 1970169159
mov dword ptr 7[rsp], 1231384169
mov dword ptr 11[rsp], 1818588270
'lea rdx, 3[rsp]
lea rdx, .id[rip]
lea rcx, .m2[rip]
call printf
add rsp, 16
pop rsi
pop rdx
pop rbx
pop r9
pop r8
ret
.id: .long 1970169159, 1231384169, 1818588270, 0
.m0: .string "CPUID: %x\n"
.m1: .string "Largest basic function number implemented: %i\n"
.m2: .string "Vendor ID: %s\n"
end asm
end sub
cpuid
print "press return to exit"
Sleep
but on Windows I could simply call the function __cpuid provided by MS
my beef is, why doesn't it work when the values are placed on the stack?
Quote from: jack on August 04, 2024, 04:10:56 AMmy beef is, why doesn't it work when the values are placed on the stack?
Well, you're using this inside a FreeBasic program, correct? So it's a FreeBasic thing.
One thing I really don't like here are those weird odd-numbered stack offsets:
mov 7[rsp], r8d
mov 11[rsp], r9d
Are those really correct? I know nothing about how FreeBasic handles stack allocation. That seems to be the source of your troubles.
I agree, the odd offsets look wrong, but that is what is on the Wikipedia
anyway, I let this rest, it just bugs me not knowing what the problem is with the stack
I think I know why the Wiki use these odd offsets, it's so that the string will have terminating 0
Hmm, that doesn't make sense to me; the terminating 0 would be at the end, so how would adjusting the offset (the beginning of the string) affect that?
Do you know how FreeBasic sets up its stack frame? That info should be available, and might help you figure out what's actually happening in that code.
FreeBasic uses the BSTR format for strings. The DWORD before the string start is the length.
Aha; so .string inserts that DWORD first, then the string data.
So in order to use such strings with standard (ASCIIZ) string functions, you need to point past the first 4 bytes of each string, correct?
@NoCforMe
in this inline-asm example no FreeBasic strings or functions are used, it simply calls the C printf function which expects a 0 terminated string
Is .string a FreeBasic macro or operator? If so, it may be putting a length word before the string text.
Quote from: NoCforMe on August 04, 2024, 08:05:40 AMyou need to point past the first 4 bytes of each string
IIRC FB returns a ptr to the start of the string, not to the length DWORD
the .string is a gnu assembler directive to include a string into the object file, see .string (https://ftp.gnu.org/old-gnu/Manuals/gas-2.9.1/html_chapter/as_7.html#SEC128)
FreeBasic uses the gnu assembler and linker to produce it's executables
when you use inline asm you can use these directives in your source, there are probably some directives that are not applicable for use in inline asm
Quote from: jack on August 04, 2024, 11:01:48 AMthe .string is a gnu assembler directive to include a string into the object file, see .string (https://ftp.gnu.org/old-gnu/Manuals/gas-2.9.1/html_chapter/as_7.html#SEC128)
OK; it creates an ASCIIZ string, not a BSTR.
for completeness here's my version without using rsp for storing the vendor id on the stack
I am sure that it could be simplified but for this exercise it's good enough
instead of using the stack I use the data at .m3 with 4 longs initialized to 0
sub cpuid naked cdecl
asm
.intel_syntax noprefix
push rbx
push rdx
push rcx
mov eax, 1
sub rsp, 16
cpuid
lea rcx, .m0[rip]
mov edx, eax
call printf
mov eax, 0
cpuid
'place the vendor id in m3
mov dword ptr .m3[rip], ebx
mov dword ptr 4[.m3][rip], edx
mov dword ptr 8[.m3][rip], ecx
mov edx, eax
lea rcx, .m1[rip]
call printf
lea rdx, .m3[rip]
lea rcx, .m2[rip]
call printf
lea rcx, .m3[rip]
add rsp, 16
pop rcx
pop rdx
pop rbx
ret
.m0: .string "CPUID: %x\n"
.m1: .string "Largest basic function number implemented: %i\n"
.m2: .string "Vendor ID: %s\n"
.data
.m3: .fill 4, 4, 0
end asm
end sub
cpuid
print "press return to exit"
Sleep
With the original Windows code, does FreeBasic take care of aligning the stack and allocating spill/shadow space?
The Win64 ABI requires the first 32 bytes of the stack as spill space for the first 4 registers, and the called function "owns" this memory so if printf decides to use it your string gets trashed.
sinsi
good question, I am inclined to think that it takes care of the alignment but am not sure
not reserving 32 bytes for the function call is sloppy/lazy on my part, if I understand it right, instead of sub rsp, 16 I should have used 32
in FreeBasic when writing naked functions you must take care of any memory, stack and registers
you can write regular [non-naked] functions much like you can in PowerBasic.
Looking into FreeBasic's syntax, it seems that "naked" means no prologue/epilogue, so the stack isn't aligned.
Here is an untested proc - I'm unsure of FreeBasic and C calling conventions in Win64, I'm assuming it's fastcall
sub cpuid naked 'cdecl <== not sure about this, Win64 uses fastcall
asm
.intel_syntax noprefix
'on entry the stack is aligned 8, Win64 expects align 16
push rbx 'must save, this is non-volatile in Win64
push rdx 'not needed, this is volatile in Win64
push rcx 'not needed, this is volatile in Win64
'we need a minimum of 32 bytes shadow space, even if a function has fewer arguments
'an odd number of pushes will align the stack to 16
'we want an additional 16 bytes to store the vendor
sub rsp,32+16
'unchanged code
mov eax, 1
'sub rsp, 16
cpuid
lea rcx, .m0[rip]
mov edx, eax
call printf
mov eax, 0
cpuid
'place the vendor id in m3
'mov dword ptr .m3[rip], ebx
'mov dword ptr 4[.m3][rip], edx
'mov dword ptr 8[.m3][rip], ecx
'put the vendor on the stack
mov [rsp+32],ebx
mov [rsp+36],edx
mov [rsp+40],ecx
mov byte ptr [rsp+44],0
'unchanged code
mov edx, eax
lea rcx, .m1[rip]
call printf
'lea rdx, .m3[rip]
'the string is hopefully on the stack :)
lea rdx,[rsp+32]
lea rcx, .m2[rip]
call printf
'lea rcx, .m3[rip]
add rsp, 32+16
pop rcx
pop rdx
pop rbx
ret
.m0: .string "CPUID: %x\n"
.m1: .string "Largest basic function number implemented: %i\n"
.m2: .string "Vendor ID: %s\n"
'.data
'.m3: .fill 4, 4, 0
end asm
end sub
cpuid
print "press return to exit"
Sleep
thank you sinsi :thumbsup:
after I had gone to bed and thinking about this, I realized why it was printing garbage when using the stack, it was being overwritten by printf, you reminded me of something that I had been reminded before but not taken to heart, reserve 32 bytes of stack for the external function being called
Quotean odd number of pushes will align the stack to 16
I didn't know that, thanks
this morning before I had seen your post I made some corrections but your corrections are complete, thank you :biggrin:
I searched the web for x64 stack alignment and I have come to the conclusion that programming in asm is a hassle that is not worth it, gcc and clang are very good at optimizing code
there are times when you need to do bit-fiddling and doing that in a HL is a hassle and ugly, some compilers have intrinsic functions to alleviate this
If you could find a high-level language that uses the 32-bit ABI you'd find it a hell of a lot simpler. No stack hassles like with X64. Don't know what HLLs use that, though.