C Language Function Call Procedure

Keywords: C

Catalog

Function Call Procedure in C Language

First up with a 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 calculates the value of two numbers. Next, we use disassembly to look at the process after the function is decomposed.

Disassembly

#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  

This is the disassembly code. Now let's read it paragraph by paragraph to see how the whole program is executed.

Creation of main() function

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]  
//The partition line......................
int a = 10;
0115173E  mov         dword ptr [a],0Ah  
    int b = 20;
01151745  mov         dword ptr [b],14h 

Here I want to give readers a brief introduction to the meaning of variables. ebp and esp are two general registers. They can store immediate numbers and memory addresses. esi is a source-to-address register. edi is a destination address change register.
They can be used to store addresses.

Before the main() function, there is a function CRTStartUp, ebp is the bottom of the function stack, and esp is the top of the function stack.

Next, move through the first two sentences of code mov assignment, and their states become as follows:

In the third sentence, sub is a subtraction instruction, which makes the esp move backwards by 0E4h units.
The next three sentences are to push ebx,esi,edi into the stack.

In the seventh sentence, lea means load effective address, which is to take out the address of [ebp-0E4h] and assign it to edi.
In the eighth sentence, move the 39h assignment to ecx. In the ninth sentence, move the content assignment to eax.
In the tenth sentence, rep stos are duplicate copies of data.
Be careful:
The ecx register is used to save the number of copies and pastes, and ecx is the counter.
Eax registers are used to transmit information, and eax is an accumulator, usually used to store data.
rep repeats the above instructions.
stos moves the value assignment of eax to the storage unit of the address es:[edi].

Now the blank part is filled by cc cc cc cc. Here cc cc is often encountered "hot".
Dividing line
The next operation is well understood, the purpose of which is to create a and b and push them into the stack.

Calling procedure of 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, and at the same time, it pushes them into the stack in an inverted way with a list of function parameters.

For convenience, the following figure will only intercept the first half.

Next, the call instruction is used, which first moves the instruction to the next sentence, and then manipulates what follows the call. Press F11 at call and we 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 section is very similar to the main function creation process. So it's expressed in two graphs.

    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 pay attention to it.
First, we created the variable ptr[z]. Then, the value of ptr[x] is placed in eax, the value of ptr[y] is added to eax and then placed in eax, and the value assignment of eax is moved to ptr[z]. Finally, we need to return z. This is where the compiler again moves the ptr[z] assignment to eax. The reason for this is that ptr[x], ptr[y], ptr[z] will be destroyed when the function is completed, but the register eax will not be destroyed, so the value of eax is very safe.

The fourth sentence outside brackets is very important. It will destroy all data of the function.

After ejecting ebp, the point has only one pointer esp. Next, ret is executed, that is, return returns. Return to the place below the call we just called.

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 let's jump back to the first sentence of this paragraph. Now esp + 8 goes to the high address. The purpose is to eliminate the parametric instantiation of a,b. In the second sentence, we move the data assignment stored in eax to c. Finally, xor exclusive or, oneself and oneself exclusive or its value becomes zero.


After that, we stacked edi, esi, ebx. Now esp points to our red arrow ebp - 0E4h at the beginning.
Then, because of the add operation, we continue to move to the high address, where we started.


Now we have the cmp instruction, which is the comparison instruction. Its operation is to subtract the first number from the second, and if the subtraction is ZF=0, it means the same. But cmp does not really decrease. At this point, the main() function is actually executed, and then esp, ebp. Back to the original position. As for the subsequent function calls, there is no need to go into them. So far we have seen how functions are implemented in assembly.

Thank you for your reading. If you have any questions, please give me more advice.

Posted by taquitosensei on Fri, 17 May 2019 12:43:28 -0700