Welcome to MSDN Blogs Sign in | Join | Help
Function Calls and the conventions - Steps towards effective debugging

Hello Guys,

How often has it happened, that we do things without really knowing why we are doing so. I can hear you speaking to yourself... Its not a fault (or a "trap" :P ). Many a times we end up doing that, since most often, the work needs to be completed, period. Learning a certain thing (a big thing), is often like solving puzzles. Its only when you have put all the pieces together, you know the importance of a certain piece.

Debugging is one such puzzle. Function calling conventions - one of those pieces. Today I would talk about function calling conventions that are available to us. Its those basics again(for veterans), nothing new or exiting...but come on, these things never hurt. This time I am covering only Function calling conventions.

N.B - The examples (if any) will reference C/C++ (C# at most). Generally (and I mean GENERALLY) when we use functions we do not use any "specifier" explicitly for calling conventions.

Let's first discuss about the Function Calling Conventions available. We will talk about some that are still alive (__pascal, __fortran,__syscall -> R.I.P).

  • __cdecl
  • __stdcall
  • __fastcall 
  • __thiscall

Calling conventions mainly..

  • decide the way/order things are pushed to the Stack
  • who is going to clean the mess (clear the stack I meant)

The first point is important because, the parameters are accessed by EBP's, in the manner of [EBP + 8], [EBP + 12], etc..(we will talk about stacks later someday, another point to remember here is EBP is used for stack walking when optimization like FPO is not used, else ESP is used directly.)

The second point is also very important because in Static functions (static in terms of number of parameters being constant), the called function (Callee) has complete idea about the number of parameters being passed to it on a particular call, because that is stagnant. Once it has the complete idea, it can do the cleaning stuff.

But in case of dynamic functions (wondering about dynamic functions...hmm...printf(), If you want to write your own, you can use <stdarg.h>/<cstdarg> .)only the calling function knows about the number of parameters that is getting passed. So the onus is on the Calling function. We could have had made the called functions do the cleaning, but we have to pass much more information(infact store them) so that the Callee can use them to cleanup. Which is an obvious overhead that is not economic.

Calling conventions, where the Calling functions do the cleanup, are usually less efficient than the function calls where the Callee function do the cleanup. The executables are also larger in size. Remember that, if we had to pass and store the extra information on Dynamic functions so that the Callee can cleanup, the executable would have been even larger in size.

Function calling convention also decide the way function names are decorated(mangled). I won't talk here much about name mangling because thats another topic. I could have shown some examples but in VS2008, nor in Windbg the proper mangled names are displayed. Just to remember that Name Mangling (Name Decoration as also known) is also dependent on the Calling convention. Those are quite simple in C but quite tricky in C++ (refer to http://en.wikipedia.org/wiki/Name_mangling for the Name Mangling).

Now lets explore each convention mentioned above.

__cdecl
=-=-=-=-

  • Parameters are passed from right to left ( int foo (int _a, int _b, int _c), here they are pushed to stack in the following order _c -> _b -> _a )
  • The calling function has to do the cleanup.
  • Its default calling convention for C/C++ (In case of C++ its default if we are not calling Member Functions or Member Functions have dynamic number of parameters)
  • Larger executables.
  • From compiler, the option is to compile using /Gd

<code>
void __cdecl pf(int a,int b)
{
}

int main()
{
 int a=5,b;
 b=a+1;
 pf(b,a);
 return 0;
}
<\code>

For this convention the mangled name would be ?pf@@YAXHH@Z

If you have VS installed, search for undname.exe (utility for undecorating names), then you can do the following..

D:\VS2008\VC\bin>undname ?pf@@YAXHH@Z
Microsoft (R) C++ Name Undecorator
Copyright (C) Microsoft Corporation. All rights reserved.

Undecoration of :- "?pf@@YAXHH@Z"
is :- "void __cdecl pf(int,int)"

If you view the diassembly, you will see that RET (which is the return command) is without any operands/parameters. This is because in this calling convention the callee dosent cleanup the stack. Look at the last opcode

<DISASSEMBLY>
void __cdecl pf(int a,int b)
{
001113A0 push ebp
001113A1 mov ebp,esp
001113A3 sub esp,0C0h
001113A9 push ebx
001113AA push esi
001113AB push edi
001113AC lea edi,[ebp-0C0h]
001113B2 mov ecx,30h
001113B7 mov eax,0CCCCCCCCh
001113BC rep stos dword ptr es:[edi]
}
001113BE pop edi
001113BF pop esi
001113C0 pop ebx
001113C1 mov esp,ebp
001113C3 pop ebp
001113C4 ret
<DISASSEMBLY>

__stdcall
=-=-=-=-=

  • Windows programmers are so much used to this. This is used everywhere in Windows Programming. Actually the name used there is WINAPI(you can say it as synonym for __stdcall).
  • Parameters pushed into stack from right to left. The this parameter of objects are passed to stack at last.
  • Stack cleared up by Callee function.
  • From compiler, the option is to compile using /Gz

<code>
void __stdcall pf(int a,int b)
{
}

int main()
{
 int a=5,b;
 b=a+1;
 pf(b,a);
 return 0;
}
<\code>

For this convention the mangled name would be ?pf@@YGXHH@Z

D:\VS2008\VC\bin>undname ?pf@@YGXHH@Z
Microsoft (R) C++ Name Undecorator
Copyright (C) Microsoft Corporation. All rights reserved.

Undecoration of :- "?pf@@YGXHH@Z"
is :- "void __stdcall pf(int,int)"

So since in case of __stdcall the stack is cleared up by the callee, you would see in the disassembly that the RET is followed by the size of all the parameters. In this case, it would be 4 bytes of int a + 4bytes of int b. If there was a double c, you would see RET 10h (remember this 10 is in hex so its decimal equivalent would be 12)

<DISASSEMBLY>
void __stdcall pf(int a,int b)
{
003C13A0 push ebp
003C13A1 mov ebp,esp
003C13A3 sub esp,0C0h
003C13A9 push ebx
003C13AA push esi
003C13AB push edi
003C13AC lea edi,[ebp-0C0h]
003C13B2 mov ecx,30h
003C13B7 mov eax,0CCCCCCCCh
003C13BC rep stos dword ptr es:[edi]
}
003C13BE pop edi
003C13BF pop esi
003C13C0 pop ebx
003C13C1 mov esp,ebp
003C13C3 pop ebp
003C13C4 ret 8
<DISASSEMBLY>

__fastcall
=-=-=-=-=

  • The main essense is that , the first two parameters if are equal to or less than 32 bits (like int) are pushed to the ECX and EDX registers. Rest are pushed to stack. Since operations are registers are faster, hence the name __fastcall.
  • Parameters which are not pushed to Registers are pushed from Right to left.
  • The Called function clears the stack, which obviously means that __fastcall becomes incompatible with function with dynamic arguments.
  • From compiler, the option is to compile using /Gr

<code>
void __fastcall pf(int a,int b)
{
}

int main()
{
 int a=5,b;
 b=a+1;
 pf(b,a);
 return 0;
}
<\code>

For this convention the mangled name would be ?pf@@YIXHH@Z

D:\VS2008\VC\bin>undname ?pf@@YIXHH@Z
Microsoft (R) C++ Name Undecorator
Copyright (C) Microsoft Corporation. All rights reserved.

Undecoration of :- "?pf@@YIXHH@Z"
is :- "void __fastcall pf(int,int,int)"

Now lets have a look at the disassembly

<DISASSEMBLY>
void __fastcall pf(int a,int b)
{
000413A0 push ebp
000413A1 mov ebp,esp
000413A3 sub esp,0D8h
000413A9 push ebx
000413AA push esi
000413AB push edi
000413AC push ecx
000413AD lea edi,[ebp-0D8h]
000413B3 mov ecx,36h
000413B8 mov eax,0CCCCCCCCh
000413BD rep stos dword ptr es:[edi]
000413BF pop ecx
000413C0 mov dword ptr [ebp-14h],edx
000413C3 mov dword ptr [ebp-8],ecx
}
000413C6 pop edi
000413C7 pop esi
000413C8 pop ebx
000413C9 mov esp,ebp
000413CB pop ebp
000413CC ret
<DISASSEMBLY>

Ok this is strange, we see simply ret. But isint that in __fastcall, the callee is supposed to clear the stack. Yes you are correct, but lets revisit the first rule once again..

           The main essense is that , the first two parameters, if are equal to or less than 32 bits (like int), are pushed to the ECX and EDX Registers. 
           Rest are pushed to stack.

So it means if the first two parameters (in our case int and int) are equal to or less than 32 bits each (they are as int is 4 byte), then they are pushed to ECX and EDX registers. So basically the parameters are not pushed to the stack, but to the registers itself in this case, so nothing to clear!!!

So following this rule, do we expect to see ret 8, when the function is void fp (int a, int b, double c)? Yes you will see that.

So what would we see when the function is void fp(double a, int b)?
Ans: You would see ret 8, because out of the first two parameters, only second is less than or equal to 32 bits, so only that can be pushed to the register and first (8 bytes) is pushed to stack and hence ret 8.

__thiscall
=-=-=-=-=-

  • Default calling convention, for calling member functions in C++. But if the member function, contains dynamic argument list, then the default calling convention falls back to the dirty __cdecl (please don't fire me saying dirty :P)
  • The parameters are passed from right to left and more importantly the this pointer for objects is passed to the ECX (ofcourse for non-static methods)

Things I didnt tell you - > There is also __clrcall for clr  :P

I guess its enough for the day. I would leave you thinking about whatever you can, on this topic. Hope, I could clear some of the common doubts about calling conventions. Let me know if I left something here, i will give it a shot in part 2 under this topic. Don't foget to provide comments/feedback.

__cdecl bye_bye(see you soon with a new and exiting topic)

Posted: Friday, May 02, 2008 12:45 AM by gouravdas

Comments

No Comments

Leave a Comment

(required) 

(required) 

(optional)

(required) 

  
Enter Code Here: Required

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Page view tracker