[10 Creation and destruction of function stack frames]

Directory

  1. What is a function stack frame
  2. What problems can be solved by understanding function stack frames?
  3. Analysis of the creation and destruction of function stack frames
  4. Analysis of problem point 2

1. What is a function stack frame

Function stack frame (stack frame): It is the space opened up by the call stack during the function call. These spaces are used to store

  • Temporary parameters and function return values
  • Temporary variables (including non-static local variables of functions and other temporary variables automatically generated by the compiler)
  • Save context information (registers that need to be saved unchanged before and after function calls)

2. Understanding what problems function stack frames can solve

The following can be understood:

  • How local variables are created
  • Why local variables are not initialized and the content is random
  • How are parameters passed when calling a function? What is the order in which parameters are passed?
  • How are the formal parameters and actual parameters of a function instantiated?
  • How is the return value of the function brought back?

3. Analysis of creation and destruction of function stack frames

3.1 What is a stack

The stack is one of the most important concepts in today’s computers. Almost every program uses the stack. Without the stack, there would be no functions and no local variables.
The stack is defined as a special container. The user can push data into the stack (push), or pop the data that has been pushed into the stack (pop). The stack must abide by a rule, first put in Pop the data from the stack
In classic operating systems, the stack always grows downward (from high address to low address).

3.2 Understand related registers and assembly instructions

Related registers

eax: general-purpose register, retains temporary data, often used for return values
ebx: general register, retains temporary data
ebp: stack bottom register
esp: register on top of stack
eip: Instruction register, which stores the address of the next instruction after the current instruction.

Assembly instructions

mov: data transfer instruction
push: data is pushed onto the stack, and the top register of the esp stack also changes.
pop: The data pops to the specified location, and the top register of the esp stack also changes.
sub: subtraction instruction
add: addition instruction
call: function call, 1. Push the return address 2. Transfer to the target function
jump: By modifying eip, transfer to the target function and call it
ret: restore return address, push eip, similar to pop eip command

3.3 Creation and destruction of parsing function stack frames

  1. Every time a function is called, space must be opened up for this call, which is the space of the function stack frame.
  2. This space is maintained using two registers, esp and ebp. ebp records the address at the bottom of the stack, and esp records the address at the top of the stack.


Call stack
Demo code:

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\
", ret);
return 0;
}

Add a breakpoint within the function and open the call stack window to reflect the function’s calling logic. Execute to the right bracket of the main function and return to the point where the main function is called. Observe

The main function is also called by the invoke_main function. It was initially called by the mainCRTStartup function, and the add function is called internally in the main function.

Prepare
In order to avoid too much interference, you can right-click the project to open configuration properties, turn off the following options, and remove the code attached by the compiler. At the same time, change the program to 32-bit, that is, x86

Disassembly
Run to the first line of the main function, right-click to go to disassembly

Creation of stack frame
Next, disassemble the code step by step:

00151840 push ebp
//Push the value of the ebp register onto the stack. At this time, ebp stores the ebp that calls the main function.
00151841 mov ebp,esp
//The move instruction stores esp into ebp, which is equivalent to the ebp that generates the main function, which is the esp of invokemain.
00151843 sub esp,0E4h
//Subtract 0xe4h from esp to generate a new esp. This space is prepared for maintaining the main function.
00151849 pushebx
0015184A push esi
0015184B push edit
//Save the values of 3 registers for later restoration

Open the monitoring and memory window, enter the addresses of esp and ebp to observe

  • First execute push ebp, the address of esp will become smaller by 4

  • Execute move ebp, esp, the values of ebp and esp are the same
    * sub esp,0E4h, esp moves up, creating a space for the main function
  • push ebx,esi,edi, esp minus 3 4’s

//The following code is initializing the stack frame space of the main function.
//1. First put the address of ebp-24h in edi
//2. Put 9 in ecx
//3. Put 0xCCCCCCCC in eax
//4. Initialize each byte of memory from edp-0x2h to ebp to 0xCC
0015184C lea edi,[ebp-24h]
0015184F mov ecx,9
00151854 mov eax,0CCCCCCCh
00151859 rep stos dword ptr es:[edi]

  • Load the effective address of the full name of lea, assign the address of ebp-24h, ecx to 9, and eax to 00CCCCCCCh
  • The function of rep stos is to change the values of the ecx content below edi to the value of eax, and open the memory window to observe the effect.

    Regarding 0xcccc, its Chinese character encoding is hot, so the initialized content is displayed as text hot by default.

The next content begins to enter the code of the main function and initializes local variables.
int a = 3;
0015185B mov dword ptr [ebp-8],3
int b = 5;
00151862 mov dword ptr [ebp-14h],5
int ret = 0;
00151869 mov dword ptr [ebp-20h],0

  • Change the value of ebp-8 to 3, assign ebp-14 to 5, and ebp-20 to 0

    There are two empty elements in the middle of the variable. This is based on different compilers.

//Start preparing to call the add function
ret = Add(a, b);
00151870 mov eax,dword ptr [ebp-14h]
00151873 push eax
00151874 mov ecx,dword ptr [ebp-8]
00151877 pushecx
00151878 call 001510B9
0015187D add esp,8
00151880 mov dword ptr [ebp-20h],eax

  • The first step is to pass the parameters. Pass the value stored in ebp-14h to eax, which is the 5 assigned in the previous step. Then push the value 3 of eax and ebp-8 to ecx, and then push it into ecx.

  • Press F11 to enter the add function. At this time, observe the top of the stack and push in the address that saves the next code of the add function, because after the add function is executed, it returns to execute the next code after the add function.

Little endian storage, 0015187d, is exactly the code after the call above. Enter the jump code and continue with F11

00151780 push ebp
00151781 mov ebp,esp
00151783 sub esp,0CCh
00151789 pushebx
0015178A push esi
0015178B push edit

This part is consistent with the process of opening up space in the main function, and the diagram is directly drawn:

Start the calculation process of the add function and return
int z = 0;
0015178C mov dword ptr [ebp-8],0
z = x + y;
00151793 mov eax,dword ptr [ebp + 8]
00151796 add eax,dword ptr [ebp + 0Ch]
00151799 mov dword ptr [ebp-8],eax
return z;
0015179C mov eax,dword ptr [ebp-8]

  • z=0, the position of ebp-8 is assigned to 0
    * z=x + y, first let’s take the value of ebp + 8, marked in blue in the figure below, which is 3 Give eax, then add ebp + c to eax, which is the position of the 5 passed in, and then save the calculation result to ebp-8


  • return z, save the value of the ebp-8 calculation result to the eax register

Destruction of function stack frame

0015179F pop edi
001517A0 pop esi
001517A1popebx
001517A2 mov esp,ebp
001517A4 pop ebp
001517A5ret

  • Pop three times, esp is increased, and edi is popped into the edi register…

  • esp equals ebp

  • Pop ebp to the ebp register. What is saved in ebp is the ebp address of the main function. At the same time, esp + 4

  • Execute the ret instruction. At this time, the top of the esp stack saves the address of the next instruction of the add function. The function of ret is to pop up and jump to this address.
    Back to the next instruction of the add function

  • add esp,8 , destroy the copied formal parameters

  • mov ebp-20, eax, the value of eax is the address of ebp-20. The value of eax saves the calculation result of the add function. It happens that ebp-20 is the address of ret applied for in the main function.

The following principles are similar, but will not be described in detail.

  • When the function return object is a built-in type, it is usually brought back through a register. If the return type is larger, space will be opened up in the calling function, and the address will be implicitly passed to the called function. The called function will find the calling function through the address. Reserve space and save the return value to the calling function

This is the process of destruction and creation of function stack frames

4. Analysis of point 2

  • For local variables, after the function stack space is opened, the space is first initialized to cccc, and then cccc is assigned the value of the local variable. If the value of the local variable is not initialized, it is very likely that the value stored in the local variable is a random value that has been called by cccc or a function.
  • The parameter is a copy of the actual parameter. This new content is used directly during calculation, so modifying the formal parameter will not change the value of the actual parameter. It will be destroyed after use.
  • The return value of the function is saved to the register and then saved to the local variable through the register.