Chapter 13 - Stack, Call and Return
13.1 Hardware Stack
The x86 CPU supports instructions to manipulate a stack of 32-bit values in memory. You may push and pop from the stack to save and restore values of registers. This allows you to save/restore your registers when you make function calls. The stack pointer (ESP) stores the memory address of the top of the stack which is the last 4-byte value that was pushed. If you remember, the stack grows downwards in memory so the top of the stack will have the smallest memory address. The program below (stack1.asm) pushes 4 numbers and stores the address in the ESP register after each of the pushes. You can see how the memory address stored in ESP decreases by 4 bytes after each number is pushed.
push eax ;push eax to top of stack: decrement esp by 4 then copy the value in eax to the memory address in esp
pop eax ;pop top of stack to eax: copy value (4 bytes) in memory address at esp then increment esp by 4
13.2 Calling Convention and the Stack
A calling convention specifies how arguments/parameters are passed to a function, how return values are sent back to the caller, and how functions manage the stack. There are many different calling conventions. Below is a brief explanation of one of the x86 Calling Conventions and how the stack is used.
1. Caller saves (push) registers EAX, ECX, and ECX (if caller needs to save)
2. Push parameters to stack in reverse order
3. Use the call instruction which places the return address on top of the stack and branches to the function
1. Push EBP onto stack and copy ESP into EBP
2. Allocate local variables onto the stack
3. Save (push) registers EBX, EDI, and ESI (if callee needs to use)
1. Leave the return value in EAX
2. Restore values of EDI and ESI
3. Deallocate local variables by popping them from stack (or move EBP to ESP)
4. Restore caller's EBP
5. Use the ret instruction
Hardware Stack (ESP)
Since we are not using the calling
convention when calling the printf function, you may have noticed we lose the
values in registers EAX, ECX, and EDX.
13.3 Call and Return
The call and return allows your program to call and return from a subroutine. It is used by the calling convention discussed in the previous section.
call Label ;push the return address onto the stack and jump to Label
ret ;pop the return address from the stack and jump
format PE console include 'win32ax.inc' ;======================================= section '.text' code readable executable ;======================================= start: call subroutine1 call subroutine2 invoke Sleep,-1 subroutine1: cinvoke printf,"Inside subroutine1 %c", 10 ret subroutine2: cinvoke printf,"Inside subroutine2 %c", 10 ret ;====================================== ;section '.data' data readable writeable ;====================================== A dd 0 B dd 0 C dd 0 D dd 0 ;==================================== section '.idata' import data readable ;==================================== library msvcrt,'msvcrt.dll',kernel32,'kernel32.dll' import msvcrt,printf,'printf' import kernel32,Sleep,'Sleep'