function calling conventions and stacksCategory: Windows programming 2005-09-26 19:48 3825 people read reviews (2) Favorites report Compiler Pascal Delphi Integer compilation VC + +
Directory (?) [+] function calling conventions and stacks 1 What is a stack
The compiler typically uses the stack to implement function calls. The stack is an area of memory, and the embedded environment sometimes requires the programmer 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 stacks to stack information such as parameters, local variables, and so on for each function.
function calls are often nested, and at the same time, there is information about multiple functions in the stack, each of which occupies a contiguous area. The area occupied by a function is called a frame.
The compiler starts using the stack from a high address. Suppose we define an array a[1024] as the stack space, and the first stack-top pointer 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 is returned. The stack-top pointer points to the frame of function A. If you put too much stuff in the stack to spill, it's a[0].
In a multithreaded (task) environment, the memory area that the CPU's stack pointer points to is the stack that is currently in use. One important task of switching threads is to set the stack pointer to the top address of the stack stack of the current thread.
Different CPUs, the stack layout of different compilers, the function call method may be different, but the basic concept of the stack is the same. 2 Function Calling conventions
The function calling convention includes the order in which parameters are passed, who is responsible for cleaning up the stack that the parameters occupy, such as:
|
Parameter Pass Order |
Who is responsible for cleaning up the stack that the parameters occupy |
__pascal |
From left to right |
Called by |
__stdcall |
From right to left |
modulated function |
__cdecl |
From right to left |
Called by |
The code that invokes the function and the called function must take the same function as the calling convention in order for the program to run correctly. On Windows, __cdecl is the default function calling convention for C/s + + programs.
On some CPUs, the compiler passes parameters with registers, and the stack used by the function is allocated and freed by the tuned function. This invocation convention has one thing in common with behavior and __cdecl: The number of arguments and parameters does not cause a stack error.
However, using the Register pass parameter, when the compiler enters the function, the parameters in the register are stored in the stack at the specified position. parameters, like local variables, should have a place in the stack. Parameters can be understood as local variables that are specified as initial values by the calling function. 3 Examples: __cdecl and __stdcall
Different CPUs, different compilers, stack layouts may be different. This article takes the compiler of x86,vc++ as an example.
VC + + Compiler has no longer support __pascal, __fortran, __syscall and other function calling convention. Currently only supports __cdecl and __stdcall.
A program that uses __cdecl or __stdcall calls, when it enters a child function, the stack content is the same. The top of the stack that ESP points to is the return address. This is the call command that is pressed into the stack. Here are the parameters, the left argument on the top, and the right argument down (first into the stack).
As the preceding table shows, the difference between __cdecl and __stdcall is that the __cdecl is the stack that the caller cleans up the parameters, and __stdcall is the stack that the function cleanup parameters occupy.
Since __stdcall's tuned function must know the exact number of incoming parameters at compile time (the called function cleans up the stack), variable-parameter functions, such as printf, cannot be supported. And if the caller uses an incorrect number of arguments, it causes a stack error.
By looking at the assembly code, the __CDECL function call will have a stack adjustment statement after the calling statement, for example: a = 0x1234;
b = 0x5678;
c = Add (A, b);
Corresponding x86 assembly: mov dword ptr [ebp-4],1234h
mov DWORD ptr [ebp-8],5678h
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;
}
Produces the following assembly code (debug version): Push EBP
MOV Ebp,esp
Sub esp,40h
Push EBX
Push ESI
Push EDI
Lea EDI,[EBP-40H]
MOV ecx,10h
MOV eax,0cccccccch
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 indicated by ESP, and will esp+4, so that ESP points to the first parameter when entering the function
Looking at the implementation of the __stdcall function, you will find that only the last line differs from the __CDECL function: RET 8//execute RET and clean up the stack used by parameters
For debug versions, the VC + + compiler will increase the code to check for ESP when calling address directly, 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, it goes to the error-handling code. 004011E0 jne __chkesp+3 (004011E3)
004011E2 ret
004011E3. Error handling code
__chkesp error handling pops up a dialog box that reports that the function call caused the ESP value to be incorrect. The release version of the assembly code is much simpler. Nor does it increase __chkesp. If an ESP error occurs, the program continues to run until the "problem needs to be closed" is encountered.
4 Additional Instructions
The function calling convention is simply the relationship between the calling function's code and the called function.
Assuming function A is __stdcall, function B calls function A. You must tell the compiler through the function declaration that function A is __stdcall. The compiler will naturally produce the correct calling code.
If function A is __stdcall. But in the case where function A is referenced, you tell the compiler that function A is the __cdecl way, and that the compiler produces __cdecl code, which is inconsistent with the calling convention of function A, and an error occurs.
To Delphi call VC function as an example, the function of Delphi default to adopt __pascal Convention, VC function defaults to adopt __CDECL convention. We generally set the VC function to __stdcall, for example: int __stdcall Add (int a, int b);
In Delphi, this function is also declared as __stdcall, it can be called: function Add (A:integer; b:integer): Integer;
stdcall; External ' a.dll ';
Many APIs take __stdcall calling conventions because of the possibility of being called by programs in other languages.