When I learned the C language, I heard the teacher say that the function was used to record information through stacks. I also heard of "retaining the site" and "restoring the site, I don't know how to understand it. Recently I am studying Assembly in Linux. Now I will use a simple example to demonstrate assembly-level function calls. This will increase your understanding of the C language. Although not perfect, it is enough to clarify the idea of function calling.
In Linux, you can run the GCC-s functest. C command to generate an assembly.ProgramFunctest. s
// Functest. c
# Include <stdio. h>
Void func (int A, int B)
{
Int C;
Int D;
C =;
D = B;
}
Int main ()
{
Func (2, 3 );
Return 0;
}
In Linux, you can run the GCC-s functest. C command to generate the assembly program functest. S.
First of all, we can see that the compilation of a simple C program is so long that we can think of the advantages of good programming habits.
Let's get down to the point. We don't analyze what each command means. We focus on the part of function calling.
Before calling call func, rows 25, 26, and 27 prepare parameters for func. First, subtract % esp from 8 bytes. This is because the int type in Linux is 4 bytes (think Tan haoqiang's C language programming book that Int Is 2 bytes? This depends on the specific operating system and compiler), that is, manually modifying the stack pointer, you can find that the function parameters in the C language are stored in the stack. Further, we found that GCC first pushed 3 to the stack, followed by 2. It can be noted that the order of the function parameters in C is exactly the opposite of that in function writing. After the three commands are executed, the stack content is as follows:
The second 28 lines call func and use the call command. When executing the Call Command, we not only call the func function, but also do an action that is invisible to our programmers, that is, we press the address of the next instruction into the stack. We will talk about this later, and we can also find that the function name is just an alias of the address, just to help programmers understand it, we can replace these aliases with hexadecimal addresses. Of course, computers do not think that using addresses is more difficult than using aliases.
Now let's go to func. The two commands in lines 6 and 7 can be considered as routines. for specific reasons, see [NOTE 1]. First press the value of % EBP into the stack, and then assign the top stack to % EBP. We can guess it vaguely, because % EBP is modified here, if you do not want to overwrite the original % EBP, you have to store the original % EBP in the stack. If necessary, you can restore the original % EBP in the stack. Row 3: Move the stack to the top of the stack. This operation aims to allocate storage space for the temporary variables in the function in the stack. Some people may wonder why they don't need other memory or registers to store temporary variables? This is because if you write a program for a large project, you need to track which variables use the memory, and which use registers is a nightmare. Therefore, C uses the stack to store temporary variables, because the stack is released or cleared after the function is called, these variables cannot be called by other functions, so this is also the origin of "local variables. This behavior also requires the programmer to be very careful in the function. For example, if we open a large array in the function, when we open a stack space for this array, A segment error may occur. After executing lines 6, 7, and 8, the stack content is as follows:
9,10 Line is 3 Assigned D , 11,12 Yes 2 Assigned C . The stack content is determined as follows:
We can see that two spaces opened for local variables are not actually used.
Some people have doubts about how to use the unit type labeled "XXXX". Now I will tell you that it is actually the return address of the function. Remember what I said before, the Call Command will import the next instruction address after calling the function into the stack, so that the address exists in the XXXX unit, in this way, the function can be returned to the main calling program based on the address in the stack to continue running. The complete stack content is as follows:
After the function is basically executed, leave restores the stack content and % EBP content before calling func. It can be replaced by the following command:
Movl % EBP, % ESP
Popl % EBP
The RET command is returned to the main program.
Line 30 deletes the 2, 3 parameter when calling func from the stack in the master program.
The complete call process of such a function is displayed, and I hope it will be helpful to you. I have just learned the compilation. If there is any error, I hope you can point it out.
Note 1: in this way, function parameters are easily read. At the beginning of the design, considering the use of % ESP and offset to access the parameters in the stack, but because % ESP in the function is very likely to change, maintaining this offset is also complicated, therefore, when % EBP is used to record the position of the stack pointer when entering the function, and % EBP is not changed in this function, it is very easy to locate the function parameters through % EBP, so some people generally refer to % EBP as the bottom pointer of the stack, which has become the habit of assembler programmers.