Search code examples
x86masmcalling-convention

Why is ecx being filled with eax value in MASM?


This is a simple Hello World code I copied from someone.

.386
.MODEL flat, stdcall
 STD_OUTPUT_HANDLE EQU -11  ; std output device

 ;////
; Function prototypes

 GetStdHandle PROTO, nStdHandle: DWORD 
 WriteConsoleA PROTO, handle: DWORD, lpBuffer:PTR BYTE, nNumberOfBytesToWrite:DWORD, lpNumberOfBytesWritten:PTR DWORD, lpReserved:DWORD
 ExitProcess PROTO, dwExitCode: DWORD 

 ;////
 .data

 consoleOutHandle dd ? 
 bytesWritten dd ? 
 message db "Hello darkness my old friend",13,10
 lmessage equ $-message

 ;////
 .code

 main PROC
  INVOKE GetStdHandle, STD_OUTPUT_HANDLE
  mov consoleOutHandle, eax 
  mov edx, offset message 
  pushad    
  mov eax, lmessage
  INVOKE WriteConsoleA, consoleOutHandle, edx, eax, offset bytesWritten, 0
  popad
  INVOKE ExitProcess, 0 
 main ENDP

END main

I wondered what the pushad and popad were for, so I removed them.

This is register data before and after WriteConsoleA is invoked:

Before: EAX = 00000014 EBX = 00B1A000 ECX = 008C1005 EDX = 008C4008 ESI = 008C1005 EDI = 008C1005 EIP = 008C1026 ESP = 0081FCE8 EBP = 0081FCF4 EFL = 00000213 

After:  EAX = 00000001 EBX = 00B1A000 ECX = 00000014 EDX = 008C4004 ESI = 008C1005 EDI = 008C1005 EIP = 008C103A ESP = 0081FCE8 EBP = 0081FCF4 EFL = 00000202 

I looked up the msdn page for the function but couldn't find anything about it.

Is it the function doing this? Do all functions behave like this?


Solution

  • The pushad instruction pushes all the general purpose registers on to the stack so they can later be restored (with, surprisingly enough, popad). Without that, a function which modifies registers without itself saving them first, may well leave you in an undesirable state.

    In the case of Windows API calls, the stdcall calling convention is used (as per the .MODEL directive at the top of your code), which states in part:

    Registers EAX, ECX, and EDX are designated for use within the function. Return values are stored in the EAX register.

    That means those registers may not be preserved in the call. So, if you want to guarantee they retain their original values, you need to do that yourself, either with pushad ... popad or the longer but more targeted:

    push eax
    push ecx
    push edx
    
    ; call function
    
    pop edx
    pop ecx
    pop eax
    

    Whether that's necessary in this very simple example (where you just immediately exit the process after the WriteConsole call), I can't say for sure. However, it's often prudent to protect yourself from the undesirable effects of functions that you call.