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.
Caller Rules 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 Callee Rules 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) Return Rules 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
call.asm |
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' |
Output |
Inside
subroutine1 Inside subroutine2 |