C language function call process, function call
Parsing the function call process from the perspective of Assembly
Let's take a look at the following simple function call process:
1 int Add(int x,int y)
2 {
3 int sum = 0;
4 sum = x + y;
5 return sum;
6 }
7
8 int main ()
9 {
10 int a = 10;
11 int b = 12;
12 int ret = 0;
13 ret = Add(a,b);
14 return 0;
15 }
Today, we mainly use assembly code to describe this process. First, we will introduce several registers and the meaning of simple assembly commands.
First, let's look at the registers involved in the function call process:
(1) esp: extended stack pointer with a pointer in its memory, which always points to the top of the stack frame at the top of the system stack.
(2) ebp: extended base pointer, with a pointer in its memory, always pointing to the bottom of a stack frame at the top of the system stack.
(3) eax is the accumulator, which is the default register of many addition multiplication commands.
(4) ebx is the base address register, which stores the base address in memory addressing.
(5) ecx is a counter and an internal counter of the repeated (REP) prefix command and LOOP command.
(6) edx is always used to place the remainder produced by integer division.
(7) esi/edi are called "source/destination index register" (source/destination index), because in many string operation commands, DS: ESI refers to the source string, While ES: EDI points to the target string.
On a 32-bit platform, ESP is reduced by 4 bytes each time.
Let's look at several simple assembly commands:
Mov: the data transmission instruction, also the most basic programming instruction, is used to transmit a data from the source address to the target address (the data transmission between registers is essentially the same)
Sub: subtraction command
Lea: obtains the offset address.
Push: The PUSH command is used to implement the push operation.
Pop: commands for pop-up operations
Call: used to save the next instruction of the current instruction and jump to the target function.
Of course, these commands are the best to understand. They allow you to deeply understand the function call process. If you cannot understand them, you can only understand them through my description.
Before analyzing the memory address space, let's take a look at the distribution of the memory address space:
Stack space increases to a low address, mainly used to save function stack frames. The size of stack space is very limited, with only a few MB of area size
Assembly code implementation:
Main function assembly code:
int main ()
{
011B26E0 push ebp
011B26E1 mov ebp,esp
011B26E3 sub esp,0E4h
011B26E9 push ebx
011B26EA push esi
011B26EB push edi
011B26EC lea edi,[ebp-0E4h]
011B26F2 mov ecx,39h
011B26F7 mov eax,0CCCCCCCCh
011B26FC rep stos dword ptr es:[edi]
int a = 10;
011B26FE mov dword ptr [a],0Ah
int b = 12;
011B2705 mov dword ptr [b],0Ch
int ret = 0;
011B270C mov dword ptr [ret],0
ret = Add(a,b);
011B2713 mov eax,dword ptr [b]
011B2716 push eax
011B2717 mov ecx,dword ptr [a]
011B271A push ecx
011B271B call @ILT+640(_Add) (11B1285h)
011B2720 add esp,8
011B2723 mov dword ptr [ret],eax
return 0;
011B2726 xor eax,eax
}
011B2728 pop edi
011B2729 pop esi
011B272A pop ebx
011B272B add esp,0E4h
011B2731 cmp ebp,esp
011B2733 call @ILT+450(__RTC_CheckEsp) (11B11C7h)
011B2738 mov esp,ebp
011B273A pop ebp
011B273B ret
Add function assembly code:
int Add(int x,int y)
{
011B26A0 push ebp
011B26A1 mov ebp,esp
011B26A3 sub esp,0CCh
011B26A9 push ebx
011B26AA push esi
011B26AB push edi
011B26AC lea edi,[ebp-0CCh]
011B26B2 mov ecx,33h
011B26B7 mov eax,0CCCCCCCCh
011B26BC rep stos dword ptr es:[edi]
int sum = 0;
011B26BE mov dword ptr [sum],0
sum = x + y;
011B26C5 mov eax,dword ptr [x]
011B26C8 add eax,dword ptr [y]
011B26CB mov dword ptr [sum],eax
return sum;
011B26CE mov eax,dword ptr [sum]
}
011B26D1 pop edi
011B26D2 pop esi
011B26D3 pop ebx
011B26D4 mov esp,ebp
011B26D6 pop ebp
011B26D7 ret
The following figure details the call process address changes (all addresses here are taken from the debugging process in the 32-bit windows vs editor .) :
Process description:
1. Copy parameters (parameter instantiation ).
2. Save the next instruction of the current instruction and jump to the called function.
All these operations are performed in the main function.
Next, call the Add function and perform some operations, including:
1. Mobile ebp and esp form a new stack frame structure.
2. Press the stack (push) to form a temporary variable and perform related operations.
3. return a value.
These operations are performed in the Add function.
After the called function completes related operations, return to the original function and execute the following command:
1. pop ).
2. Reply to the stack frame structure of the main function. (Pop)
3. Return the main function.
These operations are also performed in the Add function.So far, the entire process of calling the Add function in the main function has been completed.
To sum up, the entire process involves three steps:
1) Find the function entry based on the called function name;
2) in the stack, please call the parameters in the function and the memory space of the variables defined in the function body.
3) after the function is executed, release the parameter and variable space for review of the function in the stack, and return the value (if any)
If you have learned the microcomputer principle, you will think of the cpu interrupt processing process. Yes, the function call process is exactly the same as the interrupt processing process.
Function call conventions:
Here we will add the basic content of various call regulations.
_ Stdcall call conventions
All parameters are pushed to the stack from right to left, and the stack is cleared by the called subroutine.
_ Cdecl call conventions (The C default calling convention, C call rules)
The parameter is also pushed to the stack from right to left, but the stack is cleared by the caller.
_ Fastcall call Convention
As the name suggests, _ fastcall aims to call functions more quickly. It relies mainly on registers to pass parameters. The remaining parameters are still pushed to the stack in the order from right to left, and the called subroutine clears the stack.
This blog post calls a function according to the call Convention _ stdcall.