Analysis of VC ++ function calls

zhaozj2021-02-16  104

Visual C / C compiler provides several function call conventions, understanding these function calls agreed, and the differences between them can help us better debug procedures. In this article, I will discuss some content about function calls.

The compiler of Visual C / C supports the following function call conventions:

Keyword

Clean up stack

Parameter into the stack order

Function name modification (C)

__cdecl

Call functions

Right à left

_Function name

__stdcall

Called function

Right à left

_ Function name @ 数字

__fastcall

Called function

Right à left

@ 名 名 @ 数字

Thiscall (non-key)

Called function

Right à left

/

The above table is only simply listing the characteristics of each function call, since the top two words of this article is "analyzed", how can it be so easy! ? The following is "analyzed" on the four functions of the above four functions:

First, __ cdecl function call conventions

This is the default function call convention for the C and C programs. The parameters are pressed into the stack from the order from right to left. The call function is responsible for cleaning the stack and pops up the parameters. It is also because the stack used to transmit parameters is maintained by the call function, so the function that implements variable parameters can only use this function call convention. Because each of the functions that call it include the code cleaning the stack, the size of the compiled executable is more than the call __stdcall function. When using this function call convention, the modified function name is just a _ (underline) before the original function name, and does not change the case of the function. For __cdecl, we generally do not specifically point out because it is a C and C programs default function call conventions, so only when the compilation option is set to / GZ (STDCALL) or / GR (FastCall), we must be in front of the function Explicitly point out this function call convention. Let's take an example:

INT __CDECL SUMCDECL (int A, int b, int C)

{

INT i = 1000;

Short j = 2000;

INT K = 3000;

INT rebp = 0;

INT value = 0;

// ...

RETURN (A B C);

}

Call: SUMCDECL (10, 20, 30);

The function body and calling statement are as follows, the modified function name is _SumcDecl, the stack, and register status (a line represents 4 bytes):

0

Value

0

REBP

3000

k

2000

j

1000

i

<--------- EBP

10

a

20

b

30

c

[Unused]

ECX

[Unused]

EDX

There is no mouth, the code can explain everything, the following program is Win32 console application (.exe) is also:

#include "iostream.h"

#include "stdio.h"

Extern "C" __declspec (dllexport) INT __CDECL SUMCDECL (Int A, Int B, INT C)

{

// Declaration partial variable

INT i = 1000;

Short J = 2000;

INT K = 3000;

INT rebp = 0;

INT value = 0;

/ / Display the address of the local variable

COUT << "address of local variable:" << endl; cout << & value << "<----------- value << endl;

COUT << & rebp << "<----------- rebp << endl;

COUT << & K << "<----------- K" << Endl;

COUT << & J << "<---------- J" << endl;

COUT << & I << "<----------- i" << endl;

/ / Display the value of the register

Cout << "Register:" << Endl;

__ASM MOV REBP, EBP;

Printf ("0x% 08X <--------- EBP / N", REBP);

// Display the address of the function parameter

Added << "The address of the function parameters:" ENDL;

COUT << & a << "<---------- a" << endl;

COUT << & b << "<----------- B" << Endl;

COUT << & c << "<---------- C" << endl;

// Get the data in the stack through the EBP register and display

Cout << "Gets the data in the stack via EBP: << ENDL;

__ASM MOV EAX, [EBP - 4];

__ASM MOV VALUE, EAX;

COUT << "i:" << value << endl;

__ASM MOV EAX, [EBP - 8];

__ASM MOV VALUE, EAX;

Cout << "J:" << (short) value << Endl;

__ASM MOV EAX, [EBP - 12];

__ASM MOV VALUE, EAX;

COUT << "K:" << value << endl;

__ASM MOV EAX, [EBP 8];

__ASM MOV VALUE, EAX;

Cout << "A:" << value << endl;

__ASM MOV EAX, [EBP 12];

__ASM MOV VALUE, EAX;

Cout << "B:" << value << endl;

__ASM MOV EAX, [EBP 16];

__ASM MOV VALUE, EAX;

Cout << "C:" << value << endl;

// Return

RETURN (A B C);

}

// master function

Int main (int Argc, char * argv [])

{

SumcDecl (10, 20, 30);

Return 0;

}

On my machine, the results are as follows:

Local variable address:

0x0012FF0C <----------- Value

0x0012FF10 <----------- REBP

0x0012FF14 <----------- K

0x0012FF18 <----------- J

0x0012FF1C <----------- I

register:

0x0012FF20 <----------- EBP

The address of the function parameters:

0x0012FF28 <----------- A

0x0012FF2C <----------- B

0x0012FF30 <----------- C

Get the data in the stack by EBP:

I: 1000

J: 2000

K: 3000

A: 10

B: 20

C: 30

The extern "C" of the function declaration section indicates the connection specification (Linkage Specification), instead of C , I will discuss it later in the case of Extern "C". __DECLSPEC (DLLEXPORT) Indicates that the function is exported and will generate .lib file so that we verify how the function name is modified. With regard to the modified function name, we can use the Dumpbin tool under the vc98 / bin directory to verify:

Dumpbin / exports

File name>

The output is as follows:

FILE TYPE: LIBRARY

Exports

Ordinal Name

_SumcDecl

Summary

C9 .debug $ s

14.idata $ 2

14.idata $ 3

4 .idata $ 4

4 .idata $ 5

E .idata $ 6

Second, __ stdcall function call agreement

__stdcall function call conventions are usually used for the Win32 API function, and the parameters are pressed into the stack from the order from right to left. The called function is responsible for cleaning the stack and pops up the parameters. In Windows.h, it contains WINDEF.H, and a WinDef.h defines a WinAPi macro: #define winapi __stdcall, huh, huh, you should know. When using this function call, the modified function name plus a _ (underscore) in front of the original function, and after the original function name, add "@ 数字", of course, does not change the case of the function, @ behind the number Indicates the number of bytes that the parameters, where to note, less than 32-bit (4-bytes) parameters will be extended to 32 bits when the parameter passes. Let's take an example:

INT __STDCALL SUMSTDCALL (Int A, Int B, INT C)

{

INT i = 1000;

Short J = 2000;

INT K = 3000;

INT rebp = 0;

INT value = 0;

// ...

RETURN (A B C);

}

Call: SUMSTDCALL (10, 20, 30);

The function body and the calling statement are as shown above, the modified function name is _SUMSTDCALL @ 12, INT is 32-bit, accounting for 4 bytes, 3 32-bit variables, a total of 12 bytes. The stack and register status are as follows (a line represents 4 bytes):

0

Value

0

REBP

3000

k

2000

j

1000

i

<--------- EBP

10

a

20

b

30

c

[Unused]

ECX

[Unused]

EDX

Still in code:

#include "iostream.h"

#include "stdio.h"

Extern "C" __declspec (dllexport) INT __STDCALL SUMSTDCALL (Int A, Int B, INT C)

{

// Declaration partial variable

INT i = 1000;

Short J = 2000;

INT K = 3000;

INT rebp = 0;

INT value = 0;

/ / Display the address of the local variable

Cout << "address of local variables: << ENDL;

COUT << & value << "<----------- value << Endl;

COUT << & rebp << "<----------- rebp << endl;

COUT << & K << "<----------- K" << Endl;

COUT << & J << "<---------- J" << endl;

COUT << & I << "<----------- i" << endl;

/ / Display the value of the register

Cout << "Register:" << Endl;

__ASM MOV REBP, EBP;

Printf ("0x% 08X <--------- EBP / N", REBP);

// Display the address of the function parameter

Added << "The address of the function parameters:" ENDL;

COUT << & a << "<---------- a" << endl;

COUT << & b << "<----------- B" << Endl;

COUT << & c << "<---------- C" << endl;

// Get the data in the stack through the EBP register and display

Cout << "Gets the data in the stack via EBP: << ENDL;

__ASM MOV EAX, [EBP - 4];

__ASM MOV VALUE, EAX;

COUT << "i:" << value << endl;

__ASM MOV EAX, [EBP - 8];

__ASM MOV VALUE, EAX;

Cout << "J:" << (short) value << Endl;

__ASM MOV EAX, [EBP - 12];

__ASM MOV VALUE, EAX;

COUT << "K:" << value << endl;

__ASM MOV EAX, [EBP 8];

__ASM MOV VALUE, EAX;

Cout << "A:" << value << endl;

__ASM MOV EAX, [EBP 12]; __ ASM MOV Value, EAX;

Cout << "B:" << value << endl;

__ASM MOV EAX, [EBP 16];

__ASM MOV VALUE, EAX;

Cout << "C:" << value << endl;

// Return

RETURN (A B C);

}

// master function

Int main (int Argc, char * argv [])

{

SumstdCall (10, 20, 30);

Return 0;

}

On my machine, the results are as follows:

Local variable address:

0x0012FF0C <----------- Value

0x0012FF10 <----------- REBP

0x0012FF14 <----------- K

0x0012FF18 <----------- J

0x0012FF1C <----------- I

register:

0x0012FF20 <----------- EBP

The address of the function parameters:

0x0012FF28 <----------- A

0x0012FF2C <----------- B

0x0012FF30 <----------- C

Get the data in the stack by EBP:

I: 1000

J: 2000

K: 3000

A: 10

B: 20

C: 30

In fact, it is similar to __cdecl, just change __cdecl to __stdcall, and changed a function name. Analyze the Dumpbin. The result of the LIB file is as follows:

FILE TYPE: LIBRARY

Exports

Ordinal Name

_SUMSTDCALL @ 12

Summary

C9 .debug $ s

14.idata $ 2

14.idata $ 3

4 .idata $ 4

4 .idata $ 5

E .idata $ 6

Third, __ fastcall function call agreement

__fastcall, as the name suggests, the feature is fast because it is a register to pass the parameters. When passing the parameters, the leftmost two two less than or equal to 32-bit (4-bytes) will be stored in the ECX and EDX registers, and the remaining parameters are still pressed into the stack in the order from right to left, which is responsible for cleaning by the called function. Stack and pop the parameters. Here is something that you want to emphasize: The two parameters stored in the register are actually stored in the stack, and the examples and code will prove this. When using this function call, the modified function name plus one @, and after the original function name, add "@ 数字", the same case does not change the case, @ The number of numbers indicates the parameters The number of bytes is actually similar to __stdcall, just replacing the front _ (underline) to @. Let's take an example, and two of the two differences:

INT __FASTCALL SUMFASTCALL (Int A, Double X, INT B, INT C)

{

INT i = 1000;

Short J = 2000;

INT K = 3000;

INT rebp = 0;

INT RECX = 0;

INT redx = 0;

INT value = 0;

// ...

RETURN (A B C);

}

Call: SumfastCall (10, 8.8, 20, 30); function body and calling statement As shown above, the modified function is named @ SumfastCall @ 20, int is 32-bit, accounting for 4 bytes, Double is 64-bit 8 bytes, 3 32-bit variables plus 1 64-bit variable, a total of 20 bytes. The stack and register status are as follows (a line represents 4 bytes):

0

Value

0

REDX

0

RECX

0

REBP

3000

k

2000

j

1000

i

20

b

10

a

<--------- EBP

8.8

X (8 bytes)

30

c

10

ECX

20

EDX

Since __fastcall and the previous two function calls agree, local variables, function parameters have changed in the stack in the stack, and the values ​​in registers (mainly ECX and EDX) have changed, which we must verify, so code Different, but it is the same, please come out below:

#include "iostream.h"

#include "stdio.h"

Extern "C" __DECLSPEC (DLLEXPORT) INT __FASTCALL SUMFASTCALL (Int A, Double X, INT B, INT C)

{

// Declaration partial variable

INT i = 1000;

Short J = 2000;

INT K = 3000;

INT rebp = 0;

INT RECX = 0;

INT redx = 0;

INT value = 0;

// Display the value of ECX and EDX registers

__ASM MOV RECX, ECX;

__ASM MOV REDX, EDX;

Cout << "ECX and EDX registers:" ENDL;

Cout << "ECX:" << RECX << endl;

Cout << "edx:" << REDX << ENDL;

/ / Display the address of the local variable

Cout << "address of local variables: << ENDL;

COUT << & value << "<----------- value << Endl;

COUT << & redx << <----------- redx << endl;

COUT << & Recx << "<---------- RECX << Endl;

COUT << & rebp << "<----------- rebp << endl;

COUT << & K << "<----------- K" << Endl;

COUT << & J << "<---------- J" << endl;

COUT << & I << "<----------- i" << endl;

/ / Displays the address of the parameters deposited in the register, although the variable is stored in the register, but also at cout << "in the stack displays the address of the parameter stored in the register: << Endl;

COUT << & b << "<----------- B" << Endl;

COUT << & a << "<---------- a" << endl;

/ / Display the value of the register

Cout << "Register:" << Endl;

__ASM MOV REBP, EBP;

Printf ("0x% 08X <--------- EBP / N", REBP);

// Display the address of the function parameter

Added << "The address of the function parameters:" ENDL;

COUT << & x << "<---------- x" << endl;

COUT << & c << "<---------- C" << endl;

// Get the data in the stack through the EBP register and display

Cout << "Gets the data in the stack via EBP: << ENDL;

__ASM MOV EAX, [EBP - 12];

__ASM MOV VALUE, EAX;

COUT << "i:" << value << endl;

__ASM MOV EAX, [EBP - 16];

__ASM MOV VALUE, EAX;

Cout << "J:" << (short) value << Endl;

__ASM MOV EAX, [EBP - 20];

__ASM MOV VALUE, EAX;

COUT << "K:" << value << endl;

__ASM MOV EAX, [EBP - 4];

__ASM MOV VALUE, EAX;

Cout << "A:" << value << endl;

__ASM MOV EAX, [EBP - 8];

__ASM MOV VALUE, EAX;

Cout << "B:" << value << endl;

__ASM MOV EAX, [EBP 16];

__ASM MOV VALUE, EAX;

Cout << "C:" << value << endl;

// Return

RETURN (A B C);

}

// master function

Int main (int Argc, char * argv [])

{

SumfastCall (10, 8.8, 20, 30);

Return 0;

}

On my machine, the results are as follows:

The value of the ECX and EDX registers:

ECX: 10

EDX: 20

Local variable address:

0x0012FEFC <----------- Value

0x0012FF00 <----------- REDX

0x0012FF04 <----------- RECX

0x0012FF08 <----------- REBP

0x0012FF0C <----------- K

0x0012FF10 <----------- J0x0012FF14 <----------- i

Show the address of the parameter stored in the register:

0x0012FF18 <----------- B

0x0012FF1C <----------- A

register:

0x0012FF20 <----------- EBP

The address of the function parameters:

0x0012FF28 <----------- x

0x0012FF30 <----------- C

Get the data in the stack by EBP:

I: 1000

J: 2000

K: 3000

A: 10

B: 20

C: 30

The results used Dumpbin / Exports results are as follows:

FILE TYPE: LIBRARY

Exports

Ordinal Name

@ SumfastCall @ 20

Summary

C9 .debug $ s

14.idata $ 2

14.idata $ 3

4 .idata $ 4

4 .idata $ 5

E .idata $ 6

Fourth, thiscall function call agreement

Huh? Why didn't I get a next line in front of thiscall, huh. In fact, thisCall is not a keyword of C , so we cannot explicitly point out in the program to adopt this function call agreement. Some people may ask, how is this thing? In fact, we are often used because it is the default function call convention of the C member function. The parameter is pressed into the stack from the right to left. The called function is responsible for cleaning the stack and pops up the argument. When the Secret Transfer THIS pointer, the member function not only stores the THIS pointer into the ECX register, but also stores in the stack, but is the leftmost of the function that is finally pressed into the stack, position, and using the __fastcall call. Two are the same as equal to 32-bit (4 bytes) parameters. Let's take an example:

Class test

{

PUBLIC:

Int Sumthiscall (int A, int B, int C)

{

INT i = 1000;

Short J = 2000;

INT K = 3000;

INT rebp = 0;

INT value = 0;

// ...

RETURN (A B C);

}

}

transfer:

Test test;

Test.SUMTHISCALL (10, 20, 30);

The definition of the member function and the calling statement are as follows, the stack and register status are as follows (a line represents 4 bytes):

0

Value

0

REBP

3000

k

2000

j

1000

i

Object address

THIS

<--------- EBP

10

a

20

b

30

c

Object address

ECX

[Unused]

EDX

Let's talk about the code, I believe it:

#include "iostream.h"

#include "stdio.h"

Class test

{

PUBLIC:

Int Sumthiscall (int A, int B, int C)

{

// Declaration partial variable

INT i = 1000;

Short J = 2000;

INT K = 3000;

INT rebp = 0;

INT value = 0;

// Get the THIS pointer via the ECX register

__ASM MOV VALUE, ECX;

PRINTF ("" THIS pointer by ECX register: 0x% 08x / n ", value); // Direct output THIS

Printf ("" Direct Output of THIS "Address: 0x% 08x / N", this);

/ / Display the address of the local variable

Cout << "address of local variables: << ENDL;

COUT << & value << "<----------- value << Endl;

COUT << & rebp << "<----------- rebp << endl;

COUT << & K << "<----------- K" << Endl;

COUT << & J << "<---------- J" << endl;

COUT << & I << "<----------- i" << endl;

/ / Display the value of the register

Cout << "Register:" << Endl;

__ASM MOV REBP, EBP;

Printf ("0x% 08X <--------- EBP / N", REBP);

// Display the address of the function parameter

Added << "The address of the function parameters:" ENDL;

COUT << & a << "<---------- a" << endl;

COUT << & b << "<----------- B" << Endl;

COUT << & c << "<---------- C" << endl;

// Get the data in the stack through the EBP register and display

Cout << "Gets the data in the stack via EBP: << ENDL;

__ASM MOV EAX, [EBP - 4];

__ASM MOV VALUE, EAX;

Printf ("this: 0x% 08x / n", value);

__ASM MOV EAX, [EBP - 8];

__ASM MOV VALUE, EAX;

COUT << "i:" << value << endl;

__ASM MOV EAX, [EBP - 12];

__ASM MOV VALUE, EAX;

Cout << "J:" << (short) value << Endl;

__ASM MOV EAX, [EBP - 16];

__ASM MOV VALUE, EAX;

COUT << "K:" << value << endl;

__ASM MOV EAX, [EBP 8];

__ASM MOV VALUE, EAX;

Cout << "A:" << value << endl;

__ASM MOV EAX, [EBP 12];

__ASM MOV VALUE, EAX;

COUT << "B:" << value << Endl; __ ASM MOV EAX, [EBP 16];

__ASM MOV VALUE, EAX;

Cout << "C:" << value << endl;

// Return

RETURN (A B C);

}

}

// master function

Int main (int Argc, char * argv [])

{

Test test;

Test.SUMTHISCALL (10, 20, 30);

Return 0;

}

On my machine, the results are as follows:

Get the THIS pointer through the ECX register: 0x0012FF7C

Direct output THIS referring to the address of the object: 0x0012FF7C

Local variable address:

0x0012FF04 <----------- Value

0x0012FF08 <----------- REBP

0x0012FF0C <----------- K

0x0012FF10 <----------- J

0x0012FF14 <----------- I

register:

0x0012FF1C <----------- EBP

The address of the function parameters:

0x0012FF24 <----------- A

0x0012FF28 <----------- B

0x0012FF2C <----------- C

Get the data in the stack by EBP:

THIS: 0x0012FF7C

I: 1000

J: 2000

K: 3000

A: 10

B: 20

C: 30

The code does not verify whether it is stored in the stack by outputting the THIS pointer variable, but the address of the object referring to the object referring to the object in the ECX register is obtained by the EBP register. . Because the Thiscall function call is only applied to the member function of the C class, there is no C language function name modification mechanism, so I didn't discuss it and did not export the member function.

SumcDecl, SUMSTDCALL and SUMFASTCALL have an extern "C", indicating that the connection specification is c, not C , if not written, the default use C , of course, can also be written into Extern "C ". Put these three functions in a file and remove EXTERN "C" in front of each function, and analyze the .lib file with Dumpbin after compiling. The result is as follows (I only take a key part):

? SumcDecl @@ yahhhh @ z (int __cdecl sucdecl (int, int, int))

? SumfastCall @@ yihnhh @ z (int __fastcall sumfastcall (int, double, int, int))

? SUMSTDCALL @@ yghhhh @ z (int __stdcall sumstdcall (int, int, int))

Wow! ? This is something, it seems that it seems to be a bit messy, and listened to the slow trip:

1. Each function starts with "?", Then the function name, does not change the case.

2, for __cdecl, function name later @@ ya; for __stdcall, function name later @@ yg; for __fastcall, then pick @@ yi. 3, then followed by the code number and parameter type of the function return value type, the rules are as follows:

Generation

Types of

X

Void

Di

charr

E

Unsigned char

Fly

Short

Hide

int

I

Unsigned int

J

Long

K

Unsigned long

M

Float

N

Double

_N

Bool

O

Long Double

PA

Pointer prefix

AA

Reference prefix

V class @@

class

If a parameter is a pointer, add PA before the type code; if it is a reference, add AA before the type code. If the same type of pointer continuously, it is replaced by "0", each "0" represents a repetition; if the same type of reference is continuously appeared, then "1" is replaced, each "1" represents a repetition.

4, the code list, then @z or z to identify the end of the entire function name: If the function is parameter, the @z identification function name is ended; if the function is not parameters, the zigrated function name ends.

5. After the function name ends the flag, pick a space, and the space is a function prototype with brackets. In the resulting final file (.exe or .dll), the function name end flag will not have a function prototype.

For a simple example, assume that the function prototype is as follows (TEST is a custom class):

Void ABC (Int A, Long B, Char * C, Char * D, Bool & E, Test F, Short G)

Then, the modified function name is:

? ABC @@ yaxhjpad0a_nvtest @@ f @ z

In fact, we can set the default function call agreement adopted by the current project in the IDE environment of VC 6.0. Press Alt F7 to open the [Project Settings] dialog box, select [C / C ] tab, and select "Category] drop-down list box in the [Category] drop-down list box. Select function calls agree. As for the command line switch, / gd means __cdecl, / gr represent __fastcall, / gz represents __fastcall.

Finally there is a small episode, and the theme of this article is not too big, but the careful people will definitely find this problem. All functions mentioned above (including the member function of the Test class), local variables j is SHORT type, should only account for 2 bytes, but from memory allocation, it accounts for 4 bytes, why? Because the minimum unit of memory allocation is 4 bytes. Do not believe that CHAR S [10], reuse, and the above similar method analysis, we all believe that 10 bytes of heaven, in fact, there are 12 bytes to be given.

Ok, this trip is over, I hope you have a pleasure. Finally, I will send a small gift: I put the above Test class and the remaining three functions in a file, and integrated all examples together:

http://9cbsgoodname008.51.net/calltest.zip

* ------------------------------------------- *

* Please inform the author and indicate the source, 9CBS welcomes you! ** Author: Lu Peipei (goodname008) *

* Email: GoodName008@163.com *

* Column: http://blog.9cbs.net/goodname008 *

* ------------------------------------------- *

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

New Post(0)