Search code examples
assemblyx86masmmasm32

Arguments to function not getting passed in correctly MASM


I'm learning MASM and I can't get this simple code to work. I'm not getting the values I pass into invoke, I don't know what's happening. I've tried push 2, push 2, call pow. Same results. EAX and EDX look like garbage or maybe memory addresses.

The thread 0x1544 has exited with code -1073741510 (0xc000013a). The thread 0xd8 has exited with code -1073741510 (0xc000013a). The thread 0x898 has exited with code -1073741510 (0xc000013a). The thread 0x21c4 has exited with code -1073741510 (0xc000013a). The program '[2296] AssemblyTutorial.exe' has exited with code -1073741510 (0xc000013a).

This just means I closed the console window, but why are there 4 threads?

.386
.model flat,stdcall
option casemap:none

include windows.inc

include masm32.inc
includelib masm32.lib

include kernel32.inc
includelib kernel32.lib

include user32.inc
includelib user32.lib


.code

;requires power > 0 for correct answers
pow proc x:DWORD, power:DWORD

    ;THESE TWO MOVES RIGHT HERE ARE NOT WORKING AS EXPECTED
    ;I PUSH 2 ONTO STACK BUT WHEN I LOOK AT REGISTER VALUES
    ;IN DEBUG THEY ARE INCORRECT/GARBAGE
    mov eax, x      ;eax has x
    mov edx, power  ;edx has power
    mov ecx, eax    ;ecx will be my constant base

start_power:
    cmp edx, 1      ;is power 1?
    je end_power    ;then jump to end
    imul eax, ecx   ;else mul result/x with x
    dec edx         ;power--
    jmp start_power ;jump back to beginning of loop

end_power:
    ret             ;return answer eax

pow endp


start:

invoke pow, 2, 2 ;eax should hold 4 after this
invoke ExitProcess, eax ;program should exit with code eax, ez way to print result

end start

Solution

  • Yes, the difference between cdecl and stdcall is that the former is caller-clean and the latter is callee-clean. (See also Raymond Chen's series on calling conventions in Windows.

    The problem is, your pow procedure was not following the stdcall convention because it was not cleaning the stack. You need to specify the number of bytes to pop as part of the ret instruction. In this case, that would be ret 8.

    Or, you can make the function cdecl, in which case the caller becomes responsible for cleaning the stack, and MASM can automatically generate this code as part of the INVOKE directive.

    why are there 4 threads?

    Windows starts background threads for various reasons. These are nothing to worry about. If you investigate them further, you'll probably see that they're started by the thread-pool worker thread (TppWorkerThread in ntdll.dll).


    For what it's worth, the pow function could be more efficiently written as:

    pow PROC x:DWORD, power:DWORD
        ; Load parameters into registers
        mov  eax, x
        mov  edx, power
        mov  ecx, eax
    
        ; Decrement 'power' by 1 and bail out if we're done.
        dec  edx
        jz   Finished
    
        ; The main loop.
    CalculatePow:
        imul eax, ecx
        dec  edx
        jnz  CalculatePow
    
    Finished:
        ret  8        ; assuming this function is STDCALL
    pow ENDP