Formation and destruction of function stack frames (26 pictures to help you understand function stack frames in depth)

  Personal homepage: Welcome everyone --> Populus euphratica under the desert

 Guys, you are so beautiful

 If you find the article helpful

 You can support bloggers with one click

 Every ounce of your concern is the driving force for me to persevere

 

 ☄: The focus of this issue: We will explain the formation and destruction of the function stack frame today

  I hope you all have a happy study and work every day

 ☄: The focus of this issue: We will explain the formation and destruction of the function stack frame today

Related registers:

Relevant assembly commands:

First, let's understand the address space of the C program (VS version)

Next, let's look at the logic of the function call

​ 

 Let's take a simple example to understand:

Create the main function stack frame

Analyze the code in the main function

Preparation before calling the MyAdd function:

Make a function call:

 Open up the stack frame space of MyAdd:

Analyze MyAdd code

ready to return

after ret

Return to the next instruction of call

An interesting proof remains:

Summary of this article:

Next notice:


Function stack frame explanation We explain some analysis through assembly, so let's first understand some registers and assembly instructions to further learn stack frame.

Related registers:

eax: general-purpose register, holding temporary data, often used for return values

ebx: general purpose register, hold temporary data

ebp: stack low register

esp: stack top register

eip: Instruction register, which holds the address of the next instruction of the current instruction.

Relevant assembly commands:

mov: data transfer instruction

push: data is pushed into the stack, and the top register of the esp stack is changed at the same time

pop: The data is popped to the specified location, and the top register of the esp stack will also be changed

sub: Subtraction command

add: addition command

call: function call 1. Push the return value 2. Transfer to the target function

jmp: By modifying eip, transfer to the target function and call

ret : restore the return value address, push eip, similar to the pop eip command

First, let's understand the address space of the C program (VS version)

A diagram of the address space of a C program is as follows:

Different compilers may have different directions, and the growth direction is the same. It must be like the stack goes to the lower address, and the heap area goes to the high address. 

Next, let's look at the logic of the function call

The main function is also a function. Who is calling it?

It is called by the function __tmainCRTStartup(),

Then this function __tmainCRTStartup() is called by the function mainCRTStartup()

This function mainCRTStartup() is called directly by the operating system.

 

 Let's take a simple example to understand:

int MyAdd(int x, int y)
{
	int c = 0;
	c = x + y;

	return c;
}

int main()
{
	int a = 0xA;
	int b = 0xB;
	int z = 0;
	z = MyAdd(a, b);

	printf("z = %x\n", z);
	return 0;
}

A very simple function is the sum of two numbers and then encapsulated as a function to call.

Create the main function stack frame

This is the creation of the stack frame of the main function above, but it doesn't matter. 

Analyze the code in the main function

Code for assembly analysis:

First look at the a variable, which is to assign the value of 0A to the space of ebp - 8, and the same is true for b and z.

See diagram:

Preparation before calling the MyAdd function:

Let's see what the assembly instructions do before calling the function.

The first is to put the value of ebp - 14h (the value of variable b) into eax, which is actually the value of b into the register, then push into eax, and similarly push into ebp - 8 (the value of variable a).

Look at the schematic diagram and observe the pointer of the register at this time. The bottom is the memory layout:

 

The above process proves that:

1. Before the function is called, a temporary variable has been formed.

2. The order of parameter instantiation is from right to left.

Make a function call:

Execute the call command to enter the function body.

What call does is:

1: Push the return value 2. Transfer to the objective function

Why push the return value? Because the function may call the end, then it needs to return.

To push the return value, is to push the address of the next instruction of the call instruction.

Look at the memory and address before pushing:

After pushing into the memory, focus on whether the address of the next instruction of the call is pushed:

As shown in the figure below, it has indeed been pressed

In addition, the value after jmp is the address of the MyAdd function, which is the jump to the function. 

So the value of eip is changed from the original main function value to the value of the MyAdd function.

Look at the schematic:

 Open up the stack frame space of MyAdd:

After we jmp, we should enter the function, that is, execute the function.

Let's look at the assembly first:

These compilations are to open up the warframe space for MyAdd

First analyze step by step, push ebp, push ebp into the stack, ebp is the low position of the stack of the main function, mov moves the address of esp to ebp, ebp is actually the top of the stack of MyAdd, and finally subtract 0CC from the value of esp. In fact, it is to move the current position of esp up, and then enclose a space with ebp for the MyAdd function.

as the picture shows:

The diagram is:

The space enclosed by ebp and esp is the stack frame of the MyAdd function.

This is what esp and ebp both point to, the top of the stack of the main function, so what about the low pointer of the stack?

Don't worry that the low address of the main function stack cannot be found. We just pushed the low pointer of the main function stack into the stack frame space of MyAdd, and we can return directly at that time.

Analyze MyAdd code

Or check the disassembly code first:

Analyze one by one:

mov ebp - 8, 0, in fact, is to assign a value of 0 at the position of ebp -8, then put the value of ebp + 8 into eax, then add the value of ebp + 0C to the value in the original eax, and finally put The value in eax is put into the ebp-8 space .

The translation is:

Open up the space c variable, initialize it to 0, put the value of a in the original parameter instantiation into eax, put the b in the formal parameter instantiation and add it to a, the final result is in eax, put eax The value of is put into the space of variable c. This is the above compilation. Take a look at the diagram below:

ready to return

Speaking of the return value, the return value is actually not placed in the eax register for return , so the value of eax is 0xC

 Then we continue to analyze the assembly:

Analysis: The first is to put the value of the variable c into the eax register , and then we pop up edi, esi, ebx regardless of these, focus on the code mov esp, ebp, this is to assign the address of ebp to esp, It is equivalent to esp and ebp pointing to the lower part of the stack of MyAdd at the same time, that is, to release the stack frame space , and then pop ebp, which is to pop the stack, ebp is the bottom of the stack, and the bottom of the stack is the bottom of the stack of the main function, that is, after the pop , ebp is back to the bottom of the stack of the main function, and esp is at the next instruction of call.

as the picture shows:

after ret

ret is actually similar to pop, that is to move the value of esp down again

As shown in the figure:

Return to the next instruction of call

We return to the next instruction of call after ret

Then look at the assembly, add 8 to the value of esp, in fact, it is to destroy the temporary variable formed.

Moving the value of eax to ebp - 20 is actually moving the value of 0xC in the register to the variable z.

Look at the schematic:

 The rest of the printing is not described.

An interesting proof remains:

When the formal parameters are instantiated, they are arranged continuously from left to right.

int MyAdd(int x, int y)
{
	printf("Before:%d\n", y);
	*(&x + 1) = 100;
	printf("After: %d\n", y);

	return 0;
}

Summary of this article:

First of all, we look at and understand the creation and destruction of function stack frames from the bottom of the assembly. Every detail and every assembly instruction has been explained and a relative diagram. I hope everyone can gain something.

Next notice:

The relevant content of the variable parameter list will be updated in the next issue .

Guess you like

Origin blog.csdn.net/m0_64770095/article/details/124395143