Function calling process (creation and destruction of stack frames)

When a function is called in C language, it will jump to execute the function until it is completed and then execute the next instruction.
This is accomplished by forming a stack frame during the execution of the calling function. A stack frame is a data structure used by the compiler to implement the function calling process.

Take the Add() function as an example to analyze the calling process:

#include<stdio.h>
#include<stdlib.h>
int Add(int x, int y){
    
    
    int sum = 0;
    sum = x + y;
    return sum;
}
int main(){
    
    
    int a = 10;
    int b = 20;
    int ret = 0;
    ret = Add(a, b);
    return 0;
}
  • The stack frame requires two registers, ebp and esp. During the function call, these two registers store the bottom and top pointers of the stack that maintain the stack.
    Note: ebp points to the bottom of the stack frame currently located at the top of the system stack , not the bottom of the system stack. Strictly speaking, "bottom of stack frame" and "bottom of stack" are different concepts; the top of the stack frame referred to by ESP and the top of the system stack are the same position .

esp: Stack pointer register (extended stack pointer), which stores a pointer that always points to the top of the stack frame at the top of the system stack; on a 32-bit platform, ESP decreases by 4 bytes each time ebp: base
address The pointer register (extended base pointer) stores a pointer, which always points to the bottom of the top stack frame of the system stack.
eax is the "accumulator", which is the default register for many addition and multiplication instructions.
ebx is The "base address" (base) register stores the base address when addressing memory.
ecx is a counter, which is the default counter of the repeat (REP) prefix instruction and the LOOP instruction.
edx is always used to store the remainder generated by integer division.
esi/edi are called "source/destination index" respectively, because in many string operation instructions, DS:ESI points to the source string, and ES:EDI points to the destination string. Assembly instruction: mov: data
transfer
instruction , is also the most basic programming instruction, used to transfer a data from the source address to the target address (the data transfer between registers is essentially the same) sub: subtraction instruction lea:
take
the offset address
push: the instruction to implement the push operation It is the PUSH instruction
pop: the instruction that implements the pop operation
call: used to save the next instruction of the current instruction and jump to the target function

Distribution of memory address space:
Insert image description here
The stack space grows toward lower addresses and is mainly used to save function stack frames. The size of the stack space is very limited, only a few MB in size (so the high address in the picture below is at the bottom)

Assembly code implementation:

int main ()
{
    
    
011B26E0  push        ebp  
011B26E1  mov         ebp,esp 
011B26E3  sub         esp,0E4h 
011B26E9  push        ebx  
011B26EA  push        esi  
011B26EB  push        edi  
011B26EC  lea         edi,[ebp-0E4h] 
011B26F2  mov         ecx,39h 
011B26F7  mov         eax,0CCCCCCCCh 
011B26FC  rep stos    dword ptr es:[edi] 
    int a = 10;
011B26FE  mov         dword ptr [a],0Ah 
    int b = 20;
011B2705  mov         dword ptr [b],0Ch 
    int ret = 0;
011B270C  mov         dword ptr [ret],0 
    ret = Add(a,b);
011B2713  mov         eax,dword ptr [b] 
011B2716  push        eax  
011B2717  mov         ecx,dword ptr [a] 
011B271A  push        ecx  
011B271B  call        @ILT+640(_Add) (11B1285h) 
011B2720  add         esp,8 
011B2723  mov         dword ptr [ret],eax 
    return 0;
011B2726  xor         eax,eax 
}
011B2728  pop         edi  
011B2729  pop         esi  
011B272A  pop         ebx  
011B272B  add         esp,0E4h 
011B2731  cmp         ebp,esp 
011B2733  call        @ILT+450(__RTC_CheckEsp) (11B11C7h) 
011B2738  mov         esp,ebp 
011B273A  pop         ebp  
011B273B  ret              
int Add(int x,int y)
{
    
    
011B26A0  push        ebp  
011B26A1  mov         ebp,esp 
011B26A3  sub         esp,0CCh 
011B26A9  push        ebx  
011B26AA  push        esi  
011B26AB  push        edi  
011B26AC  lea         edi,[ebp-0CCh] 
011B26B2  mov         ecx,33h 
011B26B7  mov         eax,0CCCCCCCCh 
011B26BC  rep stos    dword ptr es:[edi] 
    int sum = 0;
011B26BE  mov         dword ptr [sum],0 
    sum = x + y;
011B26C5  mov         eax,dword ptr [x] 
011B26C8  add         eax,dword ptr [y] 
011B26CB  mov         dword ptr [sum],eax 
    return sum;
011B26CE  mov         eax,dword ptr [sum] 
}
011B26D1  pop         edi  
011B26D2  pop         esi  
011B26D3  pop         ebx  
011B26D4  mov         esp,ebp 
011B26D6  pop         ebp  
011B26D7  ret              

1. Call the main() function:

1. push on the stack, put ebp on the top of the stack, and esp always points to the top of the stack
2. mov , pass the esp value to ebp, that is, move esp and ebp together
3. sub (meaning to decrease), that is, esp -0E4h is assigned to esp, and the function call allocation increases from high address to low address , so esp moves upward, which opens up a new space, that is, it opens up space for the main function
4. The next three pushes will ebx, esi, and edi respectively. Push the top of the stack in order, and esp will also point to the top of the stack
5.lea instruction to load the effective address; put the address of ebp-0E4h into edi, that is, edi points to ebp-0E4h (1. Put 39h into ecx ;2. Put 0cccccccch into eax; 3. Start copying from the address pointed by edi to the high address. The number of copies is the ecx content, and the copied content is within eax) 6. Create variables a and b, and initialize
10 and 20
Insert image description here

2. Call of Add() function

Put b into eax, and then push eax onto the stack (formal parameter a).
Put a into eax, and then push eax onto the stack (formal parameter b).
Call: Push the address of the next instruction onto the stack, and then enter the add() function. in
Insert image description here

Note: The call statement pushes the address of the next instruction. In order to know where to continue execution when the function returns,
enter the add() function:

A. First push the main function ebp onto the stack and save the address of the ebp pointing to the bottom of the main() function stack frame. The purpose is to find the bottom of the main function stack when returning. At this time, esp points to the new top of the stack and changes the ebp of the main function
. Pushing the stack is also to find the bottom of the main function stack when returning.
B. Assign the value of esp to ebp to generate a new ebp, that is, the ebp of the Add() function stack frame;
C. Subtract a hexadecimal number 0CCh from esp ( Pre-open space for the Add() function);
D. push ebx, esi, edi;
E. lea instruction, load the effective address;
F. Initialize the pre-opened space to 0xcccccccc;
Insert image description here
G. Create variable z and assign it a value
H. Put Put the formal parameter a into eax, that is, put 10 into eax, add the formal parameter b into eax, that is, add 20 to eax, and then put eax at the position of z, that is, put the sum of the two numbers into z
I .Put the value of z into the register eax and return. Because z is a variable space temporarily opened by the function and will be destroyed after the function is executed, so put it in the register and return K. Next, perform the
pop operation from the stack, and edi esi ebx in sequence from top to bottom When popping the stack, esp will move downward. The characteristics of the stack are: first in last out, last in first out
L. Assign the ebp value to esp, that is, esp moves downward to point to the ebp position. At this time, the stack space opened by add has been destroyed
M .pop pops the element at the top of the stack into ebp, that is to say, puts the ebp of the main function into ebp, that is, ebp now points to the main function ebp
Insert image description here
N. After executing ret, the address of the previous push will be popped out, so It will return to the main function, which is why this address was pushed before, so that the call instruction is completed.
Next, continue executing O from that call instruction.
Insert image description here
Move esp+8, that is, move esp downwards, and destroy the formal parameters.

P. Finally, the main() function stack frame is destroyed, the method is the same as above

Summary: The stack is a necessary space for recording the calling path and parameters when a C language program is running:
function calling frame;
passing parameters;
saving return address;
providing local variable space;

Guess you like

Origin blog.csdn.net/weixin_44280688/article/details/105468514