Document directory
- 1. What is a stack?
- 2. function call conventions
- Example 3: __cdecl and _ stdcall
Posting time: 11:41:00
Function call conventions and stack 1 What is a stack?
The compiler generally uses stacks for function calls. A stack is a region of storage. In an embedded environment, a programmer sometimes needs to define an array as a stack. Windows automatically maintains a stack for each thread, and the stack size can be set. The compiler uses a stack to stack the parameters and local variables of each function.
Function calls are often nested. At the same time, the stack contains information about multiple functions, and each function occupies a continuous zone. The area occupied by a function is called frame ).
The compiler uses the stack from the high address. Suppose we define an array a [1024] As the stack space. At first, the top pointer of the stack points to a [1023]. If there are two functions A and B in the stack, and a calls B, the top pointer of the stack points to the frame of function B. If function B returns. The top pointer of the stack points to the frame of function. If there are too many things in the stack that cause overflow, the damage is the things above a [0.
In a multi-threaded (task) environment, the CPU Stack pointer points to the memory area that is currently used. An important task of switching threads is to set the stack pointer to the top address of the stack of the current thread.
Different CPUs may have different stack la S and function call methods for different compilers, but the basic concepts of stacks are the same.
2. function call conventions
The function call conventions include the order in which parameters are passed and who is responsible for clearing the stacks occupied by parameters, for example:
|
Parameter transfer order |
Who is responsible for clearing the stack occupied by parameters? |
_ Pascal |
Left to right |
Caller |
_ Stdcall |
From right to left |
Called function |
_ Cdecl |
From right to left |
Caller |
The code for calling a function and the called function must use the same function call convention to run the program normally. In Windows, __cdecl is the default function call Convention for C/C ++ programs.
On Some CPUs, the compiler transmits parameters using registers. The stacks used by functions are allocated and released by the called functions. This call Convention has one thing in common with _ cdecl: The inconsistent number of real parameters and form parameters will not cause a stack error.
However, when using registers to pass parameters, the compiler will save the parameters in the registers to the specified position in the stack when entering the function. Parameters should have a place in the stack like local variables. The parameter can be understood as a local variable that calls a function to specify the initial value.
Example 3: __cdecl and _ stdcall
The STACK layout may be different for different CPUs and compilers. This article takes the x86, VC ++ compiler as an example.
The VC ++ compiler no longer supports function call conventions such as _ Pascal, _ Fortran, and _ syscall. Currently, only _ cdecl and _ stdcall are supported.
The stack content of a program that uses the call method of _ cdecl or _ stdcall is the same when it first enters the subfunction. The top of the stack pointed to by ESP is the return address. This is pushed into the stack by the call command. The following are the parameters, the left parameter is on the top, and the right parameter is on the bottom (first on the stack ).
As shown in the previous table, the difference between __cdecl and _ stdcall is: __cdecl is the stack occupied by the caller to clean up the parameter, and __stdcall is the stack occupied by the parameter of the called function to clean up the parameter.
Since the called function of _ stdcall must know the exact number of input parameters during compilation (the called function must clear the stack), variable parameter functions, such as printf, cannot be supported. In addition, if the caller uses an incorrect number of parameters, a stack error occurs.
By viewing the assembly code, the __cdecl function call will have a stack adjustment statement after the call statement, for example:
A = 0x1234;
B = 0x5678;
C = add (A, B );
Corresponding x86 assembly:
MoV dword ptr [ebp-4], 1234 H
MoV dword ptr [ebp-8], 5678 H
MoV eax, dword ptr [ebp-8]
Push eax
MoV ECx, dword ptr [ebp-4]
Push ECx
Call 0040100a
Add ESP, 8
MoV dword ptr [ebp-0Ch], eax
_ Stdcall function calls do not need to adjust the stack:
Call 00401005
MoV dword ptr [ebp-0Ch], eax
Function
Int _ cdecl add (int A, int B)
{
Return A + B;
}
Generate the following Assembly Code (debug version ):
Push EBP
MoV EBP, ESP
Sub ESP, 40 h
Push EBX
Push ESI
Push EDI
Lea EDI, [ebp-40h]
MoV ECx, 10 h
MoV eax, 0 cccccccch
Rep STOs dword ptr [EDI]
MoV eax, dword ptr [EBP + 8]
Add eax, dword ptr [EBP + 0ch]
Pop EDI
Pop ESI
Pop EBX
MoV ESP, EBP
Pop EBP
RET // jump to the address specified by ESP and ESP + 4 to point ESP to the first parameter when it enters the Function
Check the implementation of the _ stdcall function, and you will find that the function is different from the last line of the _ cdecl function:
RET 8 // execute RET and clear the stack occupied by Parameters
For the debug version, the VC ++ compiler adds the code for checking esp when "directly calling the address", for example:
Ta = (Tadd) Add; // Tadd definition: typedef int (_ cdecl * Tadd) (int A, int B );
C = TA (A, B );
Generate the following assembly code:
MoV [ebp-10h], 0040100a
MoV ESI, ESP
MoV ECx, dword ptr [ebp-8]
Push ECx
MoV edX, dword ptr [ebp-4]
Push edX
Call dword ptr [ebp-10h]
Add ESP, 8
Cmp esi, ESP
Call _ chkesp (004011e0)
MoV dword ptr [ebp-0Ch], eax
The _ chkesp code is as follows. If ESP is not equal to the value saved before the function call, the error code is returned.
004011e0 JNE _ chkesp + 3 (004011e3)
004011e2 RET
004011e3; error handling code
The _ chkesp error handling dialog box is displayed. The ESP value is incorrect when a function call is reported. The release version of assembly code is much simpler. Also, _ chkesp is not added. In the event of an ESP error, the program continues to run until "you need to close the problem ".
4. Additional instructions
The function call Convention only describes the relationship between the "Code for calling a function" and the called function.
Assume that function a is _ stdcall, and function B calls function. You must use the function declaration to tell the compiler that function a is _ stdcall. The compiler will naturally generate the correct call code.
If function a is _ stdcall. However, when referencing function A, you tell the compiler that function a is in the _ cdecl mode and the compiler generates code in the _ cdecl mode, which is inconsistent with the call Convention of function, an error occurs.
Using Delphi to call VC functions as an example, Delphi uses the _ Pascal Convention by default, and VC uses the _ cdecl Convention by default. We generally set the VC function to _ stdcall, for example:
Int _ stdcall add (int A, int B );
In Delphi, declare this function as _ stdcall, and you can call it:
Function add (A: integer; B: integer): integer;
Stdcall; External 'a. dll ';
Because it may be called by programs in other languages, many APIs use the _ stdcall call convention.