0

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?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • 2
    Functions using the `stdcall` calling convention (e.g. `WriteConsoleA`) are allowed to modify registers `eax`, `ecx` and `edx` without restoring them before returning. – Michael May 19 '21 at 14:37

1 Answers1

1

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.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • So basically just push and pop when calling a function so I don't have to care what happens to the registers? Also the question was about registers, not push and pop. Don't make me be sad ;( –  May 19 '21 at 21:13
  • 1
    @PupperGump: usually you just let function calls clobber ECX and EDX (and EAX if that's not a return value). Doing otherwise would mean cluttering up your code with huge amounts of push/pop. If you're using Irvine32, note that its functions use a special calling convention where all the registers (except a return value if any) are call-preserved. So pick other registers for values that need to survive across calls, and EAX/ECX/EDX for "temporaries", e.g. preparing args or stuff you store to memory anyway. ([What are callee and caller saved registers?](https://stackoverflow.com/a/56178078)) – Peter Cordes May 19 '21 at 22:09