The MASM Forum

Miscellaneous => Linux Assembly => Topic started by: jack on August 02, 2024, 09:57:05 PM

Title: need help with Linux asm code
Post by: jack on August 02, 2024, 09:57:05 PM
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
Title: Re: need help with Linux asm code
Post by: jack on August 02, 2024, 10:48:50 PM
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
Title: Re: need help with Linux asm code
Post by: NoCforMe on August 03, 2024, 12:39:30 PM
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.
Title: Re: need help with Linux asm code
Post by: jack on August 04, 2024, 04:10:56 AM
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?
Title: Re: need help with Linux asm code
Post by: NoCforMe on August 04, 2024, 04:26:04 AM
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.
Title: Re: need help with Linux asm code
Post by: jack on August 04, 2024, 04:35:55 AM
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
Title: Re: need help with Linux asm code
Post by: jack on August 04, 2024, 04:42:11 AM
I think I know why the Wiki use these odd offsets, it's so that the string will have terminating 0
Title: Re: need help with Linux asm code
Post by: NoCforMe on August 04, 2024, 07:23:15 AM
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.
Title: Re: need help with Linux asm code
Post by: jj2007 on August 04, 2024, 07:49:03 AM
FreeBasic uses the BSTR format for strings. The DWORD before the string start is the length.
Title: Re: need help with Linux asm code
Post by: NoCforMe on August 04, 2024, 08:05:40 AM
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?
Title: Re: need help with Linux asm code
Post by: jack on August 04, 2024, 08:41:12 AM
@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
Title: Re: need help with Linux asm code
Post by: NoCforMe on August 04, 2024, 08:58:52 AM
Is .string a FreeBasic macro or operator? If so, it may be putting a length word before the string text.
Title: Re: need help with Linux asm code
Post by: jj2007 on August 04, 2024, 10:50:35 AM
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
Title: Re: need help with Linux asm code
Post by: jack on August 04, 2024, 11:01:48 AM
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
Title: Re: need help with Linux asm code
Post by: NoCforMe on August 04, 2024, 11:31:09 AM
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.
Title: Re: need help with Linux asm code
Post by: jack on August 04, 2024, 12:27:59 PM
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
Title: Re: need help with Linux asm code
Post by: sinsi on August 04, 2024, 12:59:32 PM
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.
Title: Re: need help with Linux asm code
Post by: jack on August 04, 2024, 01:23:18 PM
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.
Title: Re: need help with Linux asm code
Post by: sinsi on August 04, 2024, 01:59:22 PM
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
Title: Re: need help with Linux asm code
Post by: jack on August 04, 2024, 07:40:20 PM
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:
Title: Re: need help with Linux asm code
Post by: jack on August 04, 2024, 08:56:18 PM
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
Title: Re: need help with Linux asm code
Post by: NoCforMe on August 05, 2024, 03:29:35 AM
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.