c and assembly functions

Posted by burhankhan on Mon, 24 Feb 2020 09:48:52 +0100

Environment: VC++

Effect:
A function is a unit of independent program code that performs a specific task

1. Creating and using functions

  • Function prototype: declare the type of the function, indicating the return value of the function and the parameter type received by the function. There are many types of functions and variables, and any program must declare the type of the function before using the function
  • Function call: it indicates that when the function is executed here and the statement of function call is executed, the program will find the definition of the function and execute its contents. After execution, return to the calling function and continue to execute the next line
  • Function definition: specify what the function does
#include "stdio.h"

int add(int a,int b);	//Function prototype

int main(void)
{
	int a=1,b=1,sum=0;
	sum=add(a,b);		//function call
	printf("sum=%d\n",sum);
	return 0;
}

//Function definition
int add(int a,int b)
{
	return a+b;
}

Let's look at disassembly:
Function prototype:

We can see that the function prototype does not generate machine code here. This is for the compiler to see. It tells the compiler the return value of this function and the parameter type received by the function, and looks at the function type elsewhere. The machine code is for the CPU to execute, so the CPU will not do anything here
Function call:

8:        sum=add(a,b);       //function call
0040104D 8B 45 F8             mov         eax,dword ptr [ebp-8]
00401050 50                   push        eax
00401051 8B 4D FC             mov         ecx,dword ptr [ebp-4]
00401054 51                   push        ecx
00401055 E8 AB FF FF FF       call        @ILT+0(add) (00401005)
0040105A 83 C4 08             add         esp,8
0040105D 89 45 F4             mov         dword ptr [ebp-0Ch],eax

Before the function is called, we can see that the parameters will be stored in the stack, that is, the values of a and b, and then executed at 00401005 address. There is a jmp statement at this address, which will jump to the function definition for execution

Function definition:

13:   //Function definition
14:   int add(int a,int b)
15:   {
004010A0 55                   push        ebp
004010A1 8B EC                mov         ebp,esp
004010A3 83 EC 40             sub         esp,40h
004010A6 53                   push        ebx
004010A7 56                   push        esi
004010A8 57                   push        edi
004010A9 8D 7D C0             lea         edi,[ebp-40h]
004010AC B9 10 00 00 00       mov         ecx,10h
004010B1 B8 CC CC CC CC       mov         eax,0CCCCCCCCh
004010B6 F3 AB                rep stos    dword ptr [edi]
16:       return a+b;
004010B8 8B 45 08             mov         eax,dword ptr [ebp+8]
004010BB 03 45 0C             add         eax,dword ptr [ebp+0Ch]
17:   }
004010BE 5F                   pop         edi
004010BF 5E                   pop         esi
004010C0 5B                   pop         ebx
004010C1 8B E5                mov         esp,ebp
004010C3 5D                   pop         ebp
004010C4 C3                   ret

From the above program, we can see that the function definition will first store the esp in the stack, then give the value of esp to ebp, then open a 40h stack, then store ebx, esi, edi in the stack, and then store 0cccccccccccccccch in some consecutive addresses, and execute the statements in the function definition after these are done.

16:       return a+b;
004010B8 8B 45 08             mov         eax,dword ptr [ebp+8]
004010BB 03 45 0C             add         eax,dword ptr [ebp+0Ch]

Let's look at the end of the function

004010BE 5F                   pop         edi
004010BF 5E                   pop         esi
004010C0 5B                   pop         ebx
004010C1 8B E5                mov         esp,ebp
004010C3 5D                   pop         ebp
004010C4 C3                   ret

At the end of the function, the contents of these registers are given to them. The value of ebp is given to esp. ebp recovers the ebp before the function, and then returns. The function is the same as function call

The function only completes specific tasks, and nothing else changes. From function call to function definition, and finally return, it seems that it only operates on the values of a and b, and nothing else changes

Conclusion:
The function prototype didn't generate machine code. It told the compiler what my parameters were and what the return value was. The function call would first push the parameters into the stack, and then execute the call to an address where there is a jmp command, and then execute the function definition. The function definition would first push some registers into the stack, and then assign values to some memory, and finally put these registers in the stack The function returns to the original value, executes the ret command, and returns the calling function to continue to execute the next line.

2. Difference between value and address

First of all, we need to know some little knowledge

  • &Operator: take the storage address of the variable
  • *Indirect operator: takes the value stored on the pointer point address, and can also be used to declare the pointer
  • Declare pointer variable: type * variable name, declare pointer variable must specify the type of variable the pointer points to, because different variable types occupy different storage space
#include "stdio.h"

int add1(int a,int b);	//Function prototype
int add2(int *a,int *b);	//Function prototype
int main(void)
{
	int a=1,b=1,sum1,sum2;
	sum1=add1(a,b);		//function call
	printf("sum1=%d\n",sum1);
	sum2=add2(&a,&b);		//function call
	printf("sum2=%d\n",sum2);
	return 0;
}

//Function definition
int add1(int a,int b)
{
	return a+b;
}

int add2(int *a,int *b)
{
	return *a+*b;
}

Passing value:

8:        sum1=add1(a,b);     //function call
0040D786 8B 45 F8             mov         eax,dword ptr [ebp-8]
0040D789 50                   push        eax
0040D78A 8B 4D FC             mov         ecx,dword ptr [ebp-4]
0040D78D 51                   push        ecx
0040D78E E8 7C 38 FF FF       call        @ILT+10(add) (0040100f)
0040D793 83 C4 08             add         esp,8
0040D796 89 45 F4             mov         dword ptr [ebp-0Ch],eax

Value is passed to eax register, and then it is pushed into stack

Address:

10:       sum2=add2(&a,&b);       //function call
0040D7AA 8D 45 F8             lea         eax,[ebp-8]
0040D7AD 50                   push        eax
0040D7AE 8D 4D FC             lea         ecx,[ebp-4]
0040D7B1 51                   push        ecx
0040D7B2 E8 5D 38 FF FF       call        @ILT+15(add2) (00401014)
0040D7B7 83 C4 08             add         esp,8
0040D7BA 89 45 F0             mov         dword ptr [ebp-10h],eax

Send the address to eax and stack it
We know that the value of a variable can't be modified by its address, but it can. From the perspective of assembly, we can understand more clearly that the value of a variable is just passed by. The function call is executed at the function definition, and we don't know where the variable is, so we can't modify it. When the address is passed to the function definition, we know where the variable's address is, so we can modify the content of the variable

155 original articles published, 12 praised, 10000 visitors+
Private letter follow