Creation and destruction of function stack frames (detailed! Graphics!)

1. Function creation

1.1 Call of main function

image.png
We assume that this rectangular area is the stack area .
When a function is called, a series of actions will occur in the stack area. Next, let’s study together:
Take a simple addition function as an example:

#include <stdio.h>
int Add(int x, int y)
{
    
    
int z = 0;
z = x + y;
return z;
}
int main()
{
    
    
int a = 3;
int b = 5;
int ret = 0;
ret = Add(a, b);
printf("%d\n", ret);
return 0;
}

When running this function, obviously we need to call the main function first, and then call the Add function.
However, the main function is not called directly, it is called by a function called mainCRTStartup , and this function is called by the function of __tmainCRTStartup , the logic inside is a bit cumbersome.
As shown in the picture:
image.png

1.2 esp and edp registers

Next, we need to understand two registers: esp and ebp , which are pointers to the top of the stack (esp) and the bottom of the stack (ebp) respectively , and are used to maintain the operation of the function . That is to say, the range of activities of the function depends on the distance between the two of them. The larger the space, the larger the space used when calling the function. Every time a function is created, they will open up space one by one. When the function needs to be destroyed or narrowed down , they will also adjust their positions to achieve the purpose of calling and destroying.
image.png

1.3 Open up space for the main function

Next, let's analyze the disassembly code :

int main() {
    
    
002718A0  push        ebp  
002718A1  mov         ebp,esp  
002718A3  sub         esp,0E4h  
002718A9  push        ebx  
002718AA  push        esi  
002718AB  push        edi  
002718AC  lea         edi,[ebp-24h]  
002718AF  mov         ecx,9  
002718B4  mov         eax,0CCCCCCCCh  
002718B9  rep stos    dword ptr es:[edi]  
002718BB  mov         ecx,27C003h  
002718C0  call        0027131B  
	int a = 10;
002718C5  mov         dword ptr [ebp-8],0Ah  
	int b = 20;
002718CC  mov         dword ptr [ebp-14h],14h  
	int c = 0;
002718D3  mov         dword ptr [ebp-20h],0

We analyze sentence by sentence:

002718A0  push        ebp  

Push means to push the stack , that is to say, put ebp on the stack frame of the previous __tmainCRTStartup function

002718A1  mov         ebp,esp  

mov is the abbreviation of move, that is, the value of xx (behind) is assigned to xx (front), which means that the value of esp is assigned to ebp, that is, the stack top pointer of the original __tmainCRTStartup function is changed to the bottom pointer of the main function stack (this is the meaning, in fact, the register is itself, but the number has changed )
image.png

002718A3  sub         esp,0E4h  

sub means subtraction , subtract 0E4h from esp (this is a number) and because in the stack area, it is generally consumed from high addresses to low addresses (that is, address names and house numbers from high to low), so subtracting a number from esp is equivalent to expanding so much space to low addresses , as shown in the figure:image.png

1.4 Initialize the inside of the main function

002718A9  push        ebx  
002718AA  push        esi  
002718AB  push        edi  

The above three are operations of pushing the stack . The three registers with different functions, ebx, esi, and edi, are pushed to the top one by one. The specific functions of these three registers will not be discussed here.

002718AC  lea         edi,[ebp-0E4h]  
002718AF  mov         ecx,9  
002718B4  mov         eax,0CCCCCCCCh  
002718B9  rep stos    dword ptr es:[edi]  
002718BB  mov         ecx,27C003h

The meaning of these five lines is: at ebp-24h, repeat 27C003 times, assign 0CCCCCCCCh, and open up a dword for each assignment, which is 4 bytes.
image.png

1.5 Create variables and assign values ​​(in the main function)

	int a = 10;
002718C5  mov         dword ptr [ebp-8],0Ah  
	int b = 20;
002718CC  mov         dword ptr [ebp-14h],14h  
	int c = 0;
002718D3  mov         dword ptr [ebp-20h],0  

As mentioned earlier, mov means to assign the value of the latter to the former
, so these three lines of assembly language mean that 0Ah, which is 10 in hexadecimal; 14h, which is 20 in hexadecimal; 0, these three numbers are respectively assigned to ebp-8; ebp-14h;

  1. ebp-8, minus 8 is to add 2 double-byte spaces to the lower address
  2. ebp-14h, minus 20 is to add 5 double-byte spaces to the lower address
  3. ebp-20h, minus 32 is to add 8 double-byte spaces to the lower address.
    As shown in the figure:
    image.png
    After the position in Figure 1 is found, the value will also be assigned to the corresponding space: Figure 2
    image.png

1.6 Function parameter passing (Add function)

Then there is parameter passing. Here, a and b are called by value, that is, passing formal parameters . Let's take a look at his logic:

ret = Add(a, b);
00BE1850  mov     eax,dword ptr [ebp-14h] 
00BE1853  push     eax 
00BE1854  mov     ecx,dword ptr [ebp-8] 
00BE1857  push     ecx 
00BE1858  call     00BE10B4 
00BE185D  add     esp,8 
00BE1860  mov     dword ptr [ebp-20h],eax 

First, assign the number stored in the address of ebp-14h (b) to eax
, and then push eax onto the stack.
Similarly, assign the number stored in the address of ebp-8 (a) to ecx
, then push ecx to the stack
, then use call to call the Add function, and push the address of the next line of instruction onto the stack
(the top ebp in the figure is explained in the next paragraph)
as shown in the figure:image.png

1.7 Add function call

Next, let's look at the logic of calling the Add function :

int Add(int x, int y)
{
    
    
00BE1760  push     ebp
00BE1761  mov     ebp,esp
00BE1763  sub     esp,0CCh
00BE1769  push     ebx
00BE176A  push     esi
00BE176B  push     edi

First, push the ebp (bottom pointer of the stack) to the top of the stack, so that the main function can be found when the function is finally destroyed;
then assign the value of esp to ebp, that is, move the bottom pointer of the stack to the same position as the top pointer of the stack ; then
subtract 0CCh from esp, and subtract the number from the top pointer of the stack, obviously to open up so much space for 0CCh ; finally, perform three push operations, esp also -4-4-4 , minus 12 in total to accommodate ebx, The three registers esi and edi. As shown in the picture:

1.8 Definition of Add function

Then let's look at the logic inside Add

int z = 0;   
00BE176C  mov     dword ptr [ebp-8],0 
z = x + y;
00BE1773  mov     eax,dword ptr [ebp+8]  
00BE1776  add     eax,dword ptr [ebp+0Ch]
00BE1779  mov     dword ptr [ebp-8],eax  

The first line assigns a value to z, which is the same as the previous assignment operation, directly find the position of ebp-8, and assign 0 to it; the
key point is how to perform the addition operation:

  • It can be seen that there is a register called eax, and this register is used for accumulation . It can safely bring the data back to the main function when the function is destroyed in the future ~
  • ptr means a pointer, that is, its address
    With this knowledge point, we can easily understand what the following three lines do: first save the number at the address of
    ebp+8 (that is, the original formal parameter b) to the eax register (it was used for assignment before, but now it needs to be accumulated) and then add the number at the address of ebp+0Ch (that is, the original formal parameter a) to eax , and finally put the number stored in eax into ebp-8, and ebp-8 is not where z is located The address~ That is to say, eax puts the addition result stored by itself in z, as shown in the figure :




    image.png

2. Destruction of functions

2.1 Destruction of Add function

Next, let's take a look at how the Add function is destroyed :

return z;
00BE177C  mov     eax,dword ptr [ebp-8]  
}
00BE177F  pop     edi 
00BE1780  pop     esi 
00BE1781  pop     ebx 
00BE1782  mov     esp,ebp 
00BE1784  pop     ebp 
00BE1785  ret 

We know that ebp-8 is the position of z, so after the value stored in it is assigned to eax, because eax is a register, it will not disappear with the destruction of the function, so it can be brought back to the main function.
pop: Pop up , that is, esp+4, destroy the topmost space , and return the topmost space to the memory
. You can see that the three registers edi, esi, and ebx are popped up, and esp+4+4+4
then assigns the value of ebp to esp, that is, pulls down the pointer on the top of the stack and pulls it to the same position as the bottom pointer
of the stack
.
image.png

2.2 How to bring back the value after destruction

After returning to the function, let's take a look again. Since the function has been destroyed, how does it bring back the value of z?

00BE185D  add     esp,8 
00BE1860  mov     dword ptr [ebp-20h],eax  

It can be seen that first esp directly adds 8, skipping the original formal parameters of a and b,
and then puts the value of eax in the address of ebp-20h , that is, in ret. So far, the entire process of calling and destroying the function is completed, and then there is the destruction of the main function. The reason is the same , so I will not explain it here.

write at the end

If this article is helpful to you, can you give me a little like ❤~ Your support is my biggest motivation.

The blogger is Xiaobai, with little talent and learning, and it is inevitable that there will be mistakes. Everyone is welcome to discuss and ask questions, and the blogger will correct it as soon as possible.

Thank you for watching hehe (๑•̀ㅂ•́)و✧~!

Guess you like

Origin blog.csdn.net/weixin_70218204/article/details/131755727