[C Final Chapter] Creation and Destruction of Function Stack Frames

content

1. The purpose of this paper

2. Basic knowledge

       1. Register

       2. Code case

       3. Overview of the overall stack frame

       4. Overview of the required disassembly code

3. Function stack frame creation and destruction process

       1, _tmainCRTStartup function (calling the main function) stack frame creation

       2. The creation of the main function stack frame

       3. Execute valid code (variables) in the main function

       4. The creation of the Add function stack frame

       5. Execute valid code in the Add function

       6. Destruction of the stack frame of the Add function

       7. Destruction of the main function stack frame

4. Summary


1. The purpose of this paper

  • 1. How are local variables created?
  • 2. Why is the value of the local variable a random value?
  • 3. How do functions pass parameters? What is the order in which the parameters are passed?
  • 4. What is the relationship between formal parameters and actual parameters?
  • 5. How is the function call done?
  • 6. How does the function return after the end of the function call?

When we deeply understand the creation and destruction of function stack frames, the answer is naturally clear. The text begins:

2. Basic knowledge

1. Register

register name Introduction
eax "Accumulator" This is the default register for many add and multiply instructions.
ebx The "base address" register stores the base address when addressing memory.
ecx Counter, which is the default counter for repeat (REP) prefix instructions and LOOP instructions.
edx Always used to put the remainder from integer division.
yes source index register
I know target index register
ebp (stack bottom pointer) "base address pointer", which stores the address and is used to maintain the function stack frame
esp (stack top pointer) is specially used as a stack pointer, which stores the address and is used to maintain the function stack frame

2. Code case

  •  The compiler that this article depends on: VS2013
#include<stdio.h>
int Add(int x, int y)
{
	int z = 0;
	z = x + y;
	return z;
}
int main()
{
	int a = 10;
	int b = 20;
	int c = 0;
	c = Add(a, b);
	printf("%d\n", c);
	return 0;
}

3. Overview of the overall stack frame

Each function call must open up space for it in the stack area. As in the above code, there are the main function and the Add function visible to the naked eye, and corresponding space needs to be opened up for them, but in fact the main function is also called, when we Press F10 for the above code , press it again when it reaches return 0, and the following interface will pop up

 It can be seen from the figure that the main function is called by the __tmainCRTStartup function, and __tmainCRTStartup is called by the mainCRTStartup . Let's take a look at the overall function stack frame development:

  • Two important knowledge points:
  1. Push: put an element on the top of the stack
  2. pop: remove an element from the top of the stack

Next, the development of the function stack frame will be explained in detail: 

4. Overview of the required disassembly code

int main()
{
00031410  push        ebp  
00031411  mov         ebp,esp  
00031413  sub         esp,0E4h  
00031419  push        ebx  
0003141A  push        esi  
0003141B  push        edi  
0003141C  lea         edi,[ebp+FFFFFF1Ch]  
00031422  mov         ecx,39h  
00031427  mov         eax,0CCCCCCCCh  
0003142C  rep stos    dword ptr es:[edi]  
	int a = 10;
0003142E  mov         dword ptr [ebp-8],0Ah  
	int b = 20;
00031435  mov         dword ptr [ebp-14h],14h  
	int c = 0;
0003143C  mov         dword ptr [ebp-20h],0

    c=Add(a,b);  
00031443  mov         eax,dword ptr [ebp-14h]  
00031446  push        eax  
00031447  mov         ecx,dword ptr [ebp-8]  
0003144A  push        ecx  
0003144B  call        00C210E1  
00031440  add         esp,8  
00031443  mov         dword ptr [ebp-20h],eax  
	printf("%d", c);
00241456  mov         esi,esp  
00241458  mov         eax,dword ptr [ebp-20h]  
0024145B  push        eax  
0024145C  push        245858h  
00241461  call        dword ptr ds:[00249114h]  
00241467  add         esp,8  
0024146A  cmp         esi,esp  
0024146C  call        0024113B  
	return 0;
00241471  xor         eax,eax  
}
00031451  pop         edi  
00031452  pop         esi  
00031453  pop         ebx  
00031454  add         esp,0E4h  
0003145A  cmp         ebp,esp  
0003145C  call        __RTC_CheckEsp (03113Bh)  
00031461  mov         esp,ebp  
00031463  pop         ebp  
00031464  ret  
int Add(int x, int y)
{
000313C0  push        ebp  
000313C1  mov         ebp,esp  
000313C3  sub         esp,0CCh  
000313C9  push        ebx  
000313CA  push        esi  
000313CB  push        edi  
000313CC  lea         edi,[ebp-0CCh]  
000313D2  mov         ecx,33h  
000313D7  mov         eax,0CCCCCCCCh  
000313DC  rep stos    dword ptr es:[edi]  
	int z = 0;
000313DE  mov         dword ptr [ebp-8],0  
	z = x + y;
000313E5  mov         eax,dword ptr [ebp+8]  
000313E8  add         eax,dword ptr [ebp+0ch]  
000313EB  mov         dword ptr [ebp-8],eax  
	return z;
000313EE  mov         eax,dword ptr [ebp-8]  
}
000313F1  pop         edi  
000313F2  pop         esi  
000313F3  pop         ebx  
000313F4  mov         esp,ebp  
000313F6  pop         ebp  
000313F7  ret  

3. Function stack frame creation and destruction process

1, _tmainCRTStartup function (calling the main function) stack frame creation

According to the above, we already know that the main function is called by the _tmainCRTStartup function. Naturally, we need to open up a stack frame for it. This space should be maintained by the ebp and sep registers, provided that the lower address is high and the upper address is low. As shown in the figure:

 At this point, you enter the main function. First, you need to push to push the stack:

 Push ebp is to push ebp to the top of the stack. At this time, sep moves to the top of the new stack accordingly, which can be verified by monitoring:

 The diagram is as follows:

 Next perform the mov operation:

 This line of code means to assign sep to ebp, so the position pointed to by ebp is the position pointed to by sep, but the source operation address position remains unchanged, which can be verified by monitoring.

 Then perform the sub operation:

The operation is to subtract 0E4h from the esp. At this time, the position of the esp will go up. Observe by monitoring:

 At this moment, after the sub operation is executed, it has actually entered the development of the main function stack frame below. So far, the development of the _tmainCRTStartup function stack frame has been completed. See diagram below:

2. The creation of the main function stack frame

Continuing from the above, the diagram is as follows:

Next, perform three push operations: Push ebx, sei, and edi into the stack in sequence, and the corresponding esp should also go up.

Take a look at:

The diagram is as follows:

Then perform the following three steps

Operation lea (load effecitve address) loads the effective address. It is equivalent to putting [ebp+FFFFFF1Ch] into edi. After displaying the symbol name, [ebp+FFFFFF1Ch] is [ebp-0E4h]. The -0E4h has been executed before, and it is executed again and put into edi. Then mov puts 39h into ecx, and then mov puts 0CCCCCCCCh in eax. 

 

The purpose of the above operation is to change all dwords (1 word2 bytes, 2dword4 bytes) 39h times down from the edi just now to 0CCCCCCCCch 

Take a look at:

 The diagram is as follows:

At this point, the development of the main stack frame has been completed, and the next step is to execute the officially valid code, see below:

3. Execute valid code (variables) in the main function

Next do the following:

 First mov put 0Ah (10) on the position of ebp-8, similarly put 14h (20) on ebp-14h, and put 0 on ebp-20h, as shown in the figure:

 At this moment, the three variables of a, b, and c have been created, and then the Add function is called: first pass the parameters

First, mov puts ebp-14h (b=20) into eax. Next, push, push the stack to put eax (20) on the top of the stack, and move the corresponding esp. Similarly, mov puts ebp-8 (a=10) into ecx, and then push puts ecx on the top of the stack. as the picture shows:

 Then execute the call operation, call the Add function, press F10 to execute the call, press F11, then jump to the inside of the Add function and push the address of the next instruction of the call instruction to the top of the stack. The purpose of this is to easily return to the address when jumping to the Add function to come back, as shown in the figure:

Press F11 to officially enter the Add function and create a stack frame for it. See below for details:

4. The creation of the Add function stack frame

int Add(int x, int y)
{
000313C0  push        ebp  
000313C1  mov         ebp,esp  
000313C3  sub         esp,0CCh  
000313C9  push        ebx  
000313CA  push        esi  
000313CB  push        edi  
000313CC  lea         edi,[ebp-0CCh]  
000313D2  mov         ecx,33h  
000313D7  mov         eax,0CCCCCCCCh  
000313DC  rep stos    dword ptr es:[edi]  

The previous operations are the same as the internal operations of the previous main function, which are actually preparing our stack frame for the Add function.

First, push ebp pushes ebp to the top of the stack, then mov assigns esp to ebp, then sub, and esp- goes to 0CCh. This step is to open up space for the Add function, and then push three times, the same as the main function. It is still initialized to CCCCCCCC, and the detailed process will not be repeated. It is the same as the main function above, as shown in the figure:

 At this point, the development of the Add stack frame has been basically completed, and the next step is to execute the official and valid code, see below:

5. Execute valid code in the Add function

Continued from above:

	int z = 0;
000313DE  mov         dword ptr [ebp-8],0  
	z = x + y;
000313E5  mov         eax,dword ptr [ebp+8]  
000313E8  add         eax,dword ptr [ebp+0ch]  
000313EB  mov         dword ptr [ebp-8],eax  
	return z;
000313EE  mov         eax,dword ptr [ebp-8]  
}

First, put 0 at the position of ebp-8, then mov puts the value of ebp+8 into eax, and eax is 10 at this time. Then add ebp+0ch to eax, that is to add 20, at this time eax is 30, after adding eax (30) into ebp-8, the final result (30) into z .

At this point, the execution of the valid code inside the Add function is completed, as shown in the figure:

The next step is to return, that is, the destruction of the Add function stack frame, see below: 

6. Destruction of the stack frame of the Add function

	return z;
000313EE  mov         eax,dword ptr [ebp-8]  
}
000313F1  pop         edi  
000313F2  pop         esi  
000313F3  pop         ebx  
000313F4  mov         esp,ebp  
000313F6  pop         ebp  
000313F7  ret 

The above already knows that the value of ebp-8 (30) has been put into eax at this time, and then three pops are executed. Once popped, esp will be added once, as shown in the figure:

 Next, assign ebp to esp, and then pop to pop ebp. At this time, esp also moves. At this time, esp and ebp return to the previous maintenance of the main function stack frame. as the picture shows:

 At this point, the esp points to the address of the next instruction of the call instruction. Press F10 again, and the disassembly will look like this:

0003144B  call        00C210E1  
00031440  add         esp,8  
00031443  mov         dword ptr [ebp-20h],eax  
	printf("%d", c);
00241456  mov         esi,esp  
00241458  mov         eax,dword ptr [ebp-20h]  
0024145B  push        eax  
0024145C  push        245858h  
00241461  call        dword ptr ds:[00249114h]  
00241467  add         esp,8  
0024146A  cmp         esi,esp  
0024146C  call        0024113B  
	return 0;
00241471  xor         eax,eax  
}

At this point, we will understand that the address of the next instruction that previously stored the call instruction is for the convenience of returning, and the position of the esp changes after the previous ret execution:

At this time, the stack frame of the Add function is really destroyed, and then the main function stack frame is destroyed.

7. Destruction of the main function stack frame

0003144B  call        00C210E1  
00031440  add         esp,8  
00031443  mov         dword ptr [ebp-20h],eax  
	printf("%d", c);
00241456  mov         esi,esp  
00241458  mov         eax,dword ptr [ebp-20h]  
0024145B  push        eax  
0024145C  push        245858h  
00241461  call        dword ptr ds:[00249114h]  
00241467  add         esp,8  
0024146A  cmp         esi,esp  
0024146C  call        0024113B  
	return 0;
00241471  xor         eax,eax  
}

Through the disassembly code, we know that the add operation adds 8 to the esp at this time, and the two formal parameters x and y are released at this time, pointing to the position as shown in the figure:

Next, mov puts eax on ebp-20h, and eax is the sum calculated when we issue the Add function. At this time, the sum is brought back by us. Next is the destruction of the main function stack frame, which is the same as the above Add function. The destruction of the stack frame is not much different, so I won't go into details here.

The disassembly code is as follows:

00241471  xor         eax,eax  
}
00031451  pop         edi  
00031452  pop         esi  
00031453  pop         ebx  
00031454  add         esp,0E4h  
0003145A  cmp         ebp,esp  
0003145C  call        __RTC_CheckEsp (03113Bh)  
00031461  mov         esp,ebp  
00031463  pop         ebp  
00031464  ret  

4. Summary

At this point, the creation and destruction of the function stack frame is officially over, and the questions (goals) at the beginning of this article can also be clearly understood:

as follows:

  • 1. How are local variables created?

First, allocate and initialize the stack frame space for the function, and then allocate a little space for the local variables in the stack frame.

  • 2. Why is the value of the local variable a random value?

Because the random value is put in when we open up the stack frame, and when we initialize, the random value is overwritten.

  • 3. How do functions pass parameters? What is the order in which the parameters are passed?

Before I want to call the function, I have already pushed and pushed these two parameters into the stack from right to left. When we actually enter the formal parameter function, we can find it in the stack frame of the Add function through the offset of the pointer. formal parameters.

  • 4. What is the relationship between formal parameters and actual parameters?

The formal parameter is indeed the space opened up when the stack is pushed. The formal parameter and the actual parameter are only the same in value and independent in space. The formal parameter is a temporary copy of the actual parameter, and changing the formal parameter will not affect the actual parameter.

  • 5. How does the function return after the end of the function call?

We have already pushed the address of the next instruction of the call instruction into it before the call. When the function returns after the function call, it will jump to the address of the next instruction of the call instruction, and the return value is brought back through the register.

Guess you like

Origin blog.csdn.net/bit_zyx/article/details/123321243