content
Function call process in C language
The previous piece of code
#include<stdio.h>
int Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = Add(a, b);
return 0;
}
This function computes the value of two numbers. Next, let's take a look at the process after the function is disassembled.
disassembled code
#include <stdio.h>
int main()
{
01151720 push ebp
01151721 mov ebp,esp
01151723 sub esp,0E4h
01151729 push ebx
0115172A push esi
0115172B push edi
0115172C lea edi,[ebp-0E4h]
01151732 mov ecx,39h
01151737 mov eax,0CCCCCCCCh
0115173C rep stos dword ptr es:[edi]
int a = 10;
0115173E mov dword ptr [a],0Ah
int b = 20;
01151745 mov dword ptr [b],14h
int c = Add(a, b);
0115174C mov eax,dword ptr [b]
0115174F push eax
01151750 mov ecx,dword ptr [a]
01151753 push ecx
01151754 call _Add (01151104h)
01151759 add esp,8
0115175C mov dword ptr [c],eax
return 0;
0115175F xor eax,eax
}
01151761 pop edi
01151762 pop esi
01151763 pop ebx
01151764 add esp,0E4h
0115176A cmp ebp,esp
0115176C call __RTC_CheckEsp (01151122h)
01151771 mov esp,ebp
01151773 pop ebp
}
//Add()
#include<stdio.h>
int Add(int x, int y)
{
001016D0 push ebp
001016D1 mov ebp,esp
001016D3 sub esp,0CCh
001016D9 push ebx
001016DA push esi
001016DB push edi
001016DC lea edi,[ebp-0CCh]
001016E2 mov ecx,33h
001016E7 mov eax,0CCCCCCCCh
001016EC rep stos dword ptr es:[edi]
int z = 0;
001016EE mov dword ptr [z],0
z = x + y;
001016F5 mov eax,dword ptr [x]
001016F8 add eax,dword ptr [y]
001016FB mov dword ptr [z],eax
return z;
001016FE mov eax,dword ptr [z]
}
00101701 pop edi
00101702 pop esi
00101703 pop ebx
00101704 mov esp,ebp
00101706 pop ebp
00101707 ret
The above is the disassembled code. Now let's interpret it piece by piece to see how the whole program is executed.
Creation of main() function
01.15172 million Push EBP
01,151,721 MOV EBP, ESP
01,151,723 Sub ESP, 0E4h
01,151,729 Push EBX
0115172A Push ESI
0115172B Push EDI
0115172C LEA EDI, [EBP-0E4h]
01,151,732 MOV ECX, 39h
01,151,737 MOV EAX, 0CCCCCCCCh
0115173C REP STOS DWORD PTR ES: [EDI ]
//Separation line…………………….
int a = 10;
0115173E mov dword ptr [a],0Ah
int b = 20;
01151745 mov dword ptr [b],14h
"`
This is for readers and friends Briefly talk about the meaning of variables. ebp and esp are two general-purpose registers. They can store immediate data and memory addresses. esi is the source index register. edi is the destination index register.
They can be used to store addresses.
Before the main() function, there is a function CRTStartUp, ebp is the bottom of the stack of this function, and esp is the top of the stack of this function.
Next, through the first two sentences of code mov assignment move, their state becomes as follows:
In the third sentence, sub is a subtraction instruction, which makes esp move backward by 0E4h units.
The next three sentences are to push ebx, esi, and edi into the stack.
In the seventh sentence, lea means load effective address, that is, the address of [ebp - 0E4h] is taken out and assigned to edi.
Eighth sentence, move the 39h assignment to ecx. In the ninth sentence, move the content assignment to eax.
The tenth sentence, rep stos is to copy the data repeatedly.
Note:
The ecx register is used to save the number of copy-pastes, and ecx is the counter.
The eax register is used to transfer information, and eax is an accumulator, usually used to store data.
rep is to repeat the above instruction.
stos is the storage unit that moves the value of eax to the address of es:[edi].
Now the blanks in it are filled with cc cc cc cc. Here cc cc is often encountered "hot".
The split line
The next operation is well understood, its purpose is to create a and b and push them onto the stack.
The calling process of the Add() function
int c = Add(a, b);
0115174C mov eax,dword ptr [b]
0115174F push eax
01151750 mov ecx,dword ptr [a]
01151753 push ecx
01151754 call _Add (01151104h)
01151759 add esp,8
0115175C mov dword ptr [c],eax
In the first four sentences, it finds a register for a and b to store, and at the same time, pushes it into the stack in a reversed way of the function parameter list.
For convenience, only the top half of the diagram will be taken.
Next, the call instruction is used, which moves the instruction to the next sentence first, and then manipulates the content after the call . Pressing F11 at the call will jump to the Add() function.
Add() function
#include<stdio.h>
int Add(int x, int y)
{
001016D0 push ebp
001016D1 mov ebp,esp
001016D3 sub esp,0CCh
001016D9 push ebx
001016DA push esi
001016DB push edi
001016DC lea edi,[ebp-0CCh]
001016E2 mov ecx,33h
001016E7 mov eax,0CCCCCCCCh
001016EC rep stos dword ptr es:[edi]
This part is very similar to the main function creation process. So it is represented by two pictures.
int z = 0;
001016EE mov dword ptr [z],0
z = x + y;
001016F5 mov eax,dword ptr [x]
001016F8 add eax,dword ptr [y]
001016FB mov dword ptr [z],eax
return z;
001016FE mov eax,dword ptr [z]
}
00101701 pop edi
00101702 pop esi
00101703 pop ebx
00101704 mov esp,ebp
00101706 pop ebp
00101707 ret
This code is very interesting, please readers and friends must pay attention.
First, we create the variable ptr[z]. Then, put the value of ptr[x] in eax, add the value of ptr[y] to eax and put it in eax, and move the value of eax to ptr[z]. Finally, we need to return z. This is where the compiler moves the value assignment of ptr[z] to eax again. The reason for this is: ptr[x], ptr[y], ptr[z] will be destroyed after the function completes, but the register eax will not be destroyed, so the value in eax is very safe.
The fourth sentence outside the brackets is very important. It will destroy all data for that function.
After popping ebp, there is only one pointer esp at that point. Next, execute ret, that is, return returns. Go back to where we just called below.
Destruction of main() function
01151759 add esp,8
0115175C mov dword ptr [c],eax
return 0;
0115175F xor eax,eax
}
01151761 pop edi
01151762 pop esi
01151763 pop ebx
01151764 add esp,0E4h
0115176A cmp ebp,esp
0115176C call __RTC_CheckEsp (01151122h)
01151771 mov esp,ebp
01151773 pop ebp
}
Now we jump back to the first sentence of the paragraph, and now esp+8 is headed for the high address. Its purpose is to eliminate the a, b of the parameter instantiation. In the second sentence, we moved the data assignment saved in eax to c. Finally, xor XOR, self and self XOR or its value becomes 0.
After that, we popped edi, esi, ebx. Now esp points to our red arrow ebp - 0E4h at the beginning .
Then, thanks to the add operation, we continue to the high address, to where we started.
Now, we come to the cmp instruction, cmp is the comparison instruction. Its operation is to take the first number and subtract the second number. If the subtraction is ZF=0, it means equal. But cmp doesn't really decrease. At this point, the main() function is really executed, and then esp, ebp. back to its original position. As for the subsequent function calls, there is no need to delve into it. So far we have seen how the function is implemented in assembly
Thank you for reading, and if you have any questions, please let me know.