On function call agreement

xiaoxiao2021-03-30  232

On function call agreement

Source: unknown Author: unknown

In the C language, suppose we have such a function:

Int Function (Int A, INT B)

This function can be used as long as you use the result = function (1, 2) in such a way. However, when the senior language is compiled into a computer code that the computer can be identified, there is a problem to conveze: in the CPU, the computer has no way to know how many, what kind of parameters are required, no hardware can save these parameters . That is, the computer does not know how to deliver parameters to this function, and the work of passing the parameters must be coordinated by the function caller and function itself. To this end, the computer provides a data structure called a stack to support parameter transmission.

The stack is an advanced data structure, and the stack has a storage area, a stack top pointer. The stack top pointer points to the first available data item in the stack (called a stack top). The user can add data in the top of the stack top, and this operation is called a press. After the stack, the stack is automatically turned into the location of the newly added data item, and the top pointer is also modified. The user can also take the top of the stack from the stack, called the pop-up (POP), after the pop-up stack, one element under the top of the stack becomes the top of the stack, and the top pointer needs to be modified.

When the function is called, the caller sequentially stacks the parameters, then calls the function, after the function is called, and the data is obtained in the stack and calculates. After the function calculation ends, or the caller, or the function itself modifies the stack to restore the stack to the original.

In parameter delivery, there are two important issues to be clearly stated:

When the number of parameters is more than one, the parameters are pressed into the stack function, and who will restore the stack to the original

In advanced languages, these two issues will be described through function call conventions. Common calls agree:

Stdcall CDECL FastCall thiscall Naked Call

STDCALL call convention

STDCALL is often called the Pascal call agreement, because Pascal is a very common teaching computer programming language, its grammar is rigorous, and the function call convention used is stdcall. In the C / C compiler of the Microsoft C series, this call is often used in Pascal macro, and similar macros has WinAPI and Callback.

The syntax for stdcall call the agreed statement (for the function of the previous article as an example):

INT __STDCALL FUNCTION (Int A, INT B)

Stdcall's call agreement means: 1) Parameter from the right direction left press stack, 2) Function itself Modify the stack 3) Function name Automatically add the debord, followed by a @ symbol, followed by the size of the parameters

Take the above function as an example, the parameter b first is punched, then the parameter A, the function calls Function (1, 2) call to the assembly language will become:

Push 2 second parameter into the stack

Push 1 first parameter

Call function calls the parameters, pay attention to this time automatically put the CS: EIP

For the function itself, you can translate:

PUSH EBP Saves the EBP register that will be used to save the stack top pointer, you can recover when the function exits

MOV EBP, ESP Save Stack Pointer

MOV EAX, [EBP 8H] stacking EBP points before the location of the EBP, CS: EIP, A, B, EBP 8 point to A

Add EAX, [EBP 0CH] Stack EBP 12 is saved in BMOV ESP, EBP Restore ESP

POP EBP

Ret 8

When compiling, the name of this function is translated into _function @ 8

Note that different compilers will insert their assembly code to provide the versatility of compilation, but the general code is. Where the ESP to EBP is retained at the beginning of the function, the end recovery is a common method of compiler.

From the function call, 2 and 1 are sequentially inserted by the PUSH, and in the function, in the function, the offset access parameters are passed through the offset relative to the EBP (i.e., the stack pointer when the function is justified). After the function is over, RET 8 indicates the stack of 8 bytes, and the function restores the stack.

CDECL call convention

CDECL call agreed is also known as C call convention, is a C language default call agreement, and its definition syntax is:

INT Function (int A, int b) // does not add modification is C call convention

INT __CDECL FUNCTION (INT A, INT B) / / Clearly indicates C call conventions

When writing this article, I am unexpected, I found that the parameter stack order of the CDECL call agreed is that the parameter is the same, the parameter first is pressed into the stack to the left. The difference is that the function itself does not clean up the stack, and the caller is responsible for cleaning the stack. Due to this change, the number of C-call conventions allow the number of parameters of the function is not fixed, which is also a major feature of the C language. For the previous FUNCTION function, the assembly code after using CDECL becomes:

Call

Push 1

Push 2

Call function

Add ESP, 8

Note: Here the caller in the restore stack

Called function _function

PUSH EBP Saves the EBP register that will be used to save the stack top pointer, you can recover when the function exits

MOV EBP, ESP Save Stack Pointer

MOV EAX, [EBP 8H] stacking EBP points before the location of the EBP, CS: EIP, A, B, EBP 8 point to A

Add EAX, [EBP 0CH] Stacking in EBP 12 Save B

MOV ESP, EBP Recovery ESP

POP EBP

RET

Note that there is no modification of the stack here.

In MSDN, the modification automatically adds a predecessor before the function name, so the function name is recorded in _function in the symbol table, but I don't see this change when compiling.

Since the parameters are sequenced from the right to left, the first parameter is closest to the top of the stack, so when the number of parameters are used, the position of the first parameter in the stack can certainly know, as long as the parameters are not The number can be determined according to the first latter subsequent parameters, you can use an unproducible parameter, such as the Sprintf function in the CRT, is defined as:

INT Sprintf (Char * Buffer, Const Char * Format, ...)

Since all uncertain parameters can be determined via Format, there is no problem with the parameters of the number of applications.

FastCall

FastCall call agreement and stdcall are similar, which means:

The first and second DWORD parameters of the function (or smaller size) passed through ECX and EDX, other parameters pass the modified function cleaning stack function name modification rules with stdcall by stacking the stack function name

Its declaration syntax is: int FastCall Function (Int A, INT B)

Thiscall

ThisCall is the only function modification that cannot be explicitly indicated because thisCall is not a keyword. It is a C class member function default call agreement. Since the member function call has a THIS pointer, this must be handled, thiscall means: Parameters are passed from right to left into the argument, the THIS pointer passes the caller by ECX; if the number of parameters is uncertain, this The pointer is pressed into the stack after all parameter stacks. Number of parameters, the caller cleaning the stack, otherwise the function clensizes the stack

To illustrate this calling convention, define as follows and using code:

Class A

{

PUBLIC:

Int Function1 (Int A, INT B);

INT Function2 (Int A, ...);

}

Int a :: function1 (int A, int b)

{

RETURN A B;

}

#include

Int A :: function2 (int A, ...)

{

VA_LIST AP;

VA_START (AP, A);

INT I;

Int results = 0;

For (i = 0; i

{

Result = VA_ARG (AP, INT);

}

Return Result;

}

Void calee ()

{

A a a;

A.function1 (1, 2);

A.Function2 (3, 1, 2, 3);

}

After the Callee function is translated into compilation:

// Function function1 call

0401C1D PUSH 2

00401C1F PUSH 1

00401C21 LEA ECX, [EBP-8]

00401C24 Call Function1 Note that this this is not in the stack

// Function function2 call

00401C29 Push 3

00401C2B PUSH 2

00401C2D PUSH 1

00401C2F PUSH 3

00401c31 Lea Eax, [EBP-8] introduced this pointer

00401C34 Push EAX

00401c35 Call Function2

00401C3A Add ESP, 14H

It can be seen that if the number of parameters is fixed, it is similar to stdcall, similar to CDECL.

Naked Call

This is a rare call agreement, general program designer recommends not to use. The compiler does not add initialization and cleaning code to this function. It is more special. You cannot return the return value with Return, you can only return the result with the insert assembly. This is generally used in real mode driver design, assuming that a summary of the addition program is defined, can be defined as:

__Declspec (Naked) Int Add (int A, int b)

{

__ASM MOV EAX, A

__ASM Add Eax, B

__ASM RET

}

Note that this function does not have an explicit RETURN return value, returns to the implementation of the EAX register, and the RET instructions that exit the function must be explicitly inserted. The above code is translated into compilation, it becomes:

MOV EAX, [EBP 8]

Add Eax, [EBP 12]

Ret 8

Note that this modification is used in conjunction with __stdcall, and CDECL, the front is the code used in conjunction with CDECL, the code combined with STDCALL, it becomes:

__DECLSPEC (Naked) INT __STDCALL FUNCTION (Int A, INT B) {

__ASM MOV EAX, A

__ASM Add Eax, B

__ASM RET 8 // Laid to the back 8

}

As for this function being called, the normal CDECL and STDCALL call function are consistent.

Frequently Asked Questions

If the definition agreement is inconsistent, it will cause the stack to be destroyed, resulting in a serious problem, the following is two common problems:

Function prototype declaration and functional body definitions Different functions are declared when DLL import functions

In the future, it is assumed that we declare a function in the DLL species:

__Declspec (DLLEXPORT) INT FUNC (int A, int b); // Note, there is no stdcall here, using CDECL

The time code is:

Typedef int (* WinAPI DLLFUNC) FUNC (Int A, INT B);

HLIB = loadingLibrary (...);

DLLFUNC FUNC = (DLLFUNC) getProcaddress (...) // This modified call agreement

Result = func (1, 2); // causes errors

Since the caller does not understand the meaning of WinAPI's meaning, the above code inevitably causes the stack to be destroyed, and the CHECKESP function inserted by the MFC when compiling will tell you that the stack is destroyed.

转载请注明原文地址:https://www.9cbs.com/read-130755.html

New Post(0)