OSRLogoOSRLogoOSRLogo x OSR Custom Development Services

Everything Windows Driver Development

GoToHomePage xLoginx

    Thu, 14 Mar 2019     118020 members


  Online Dump Analyzer
OSR Dev Blog
The NT Insider
The Basics
File Systems
ListServer / Forum
  Express Links
  · The NT Insider Digital Edition - May-June 2016 Now Available!
  · Windows 8.1 Update: VS Express Now Supported
  · HCK Client install on Windows N versions
  · There's a WDFSTRING?
  · When CAN You Call WdfIoQueueP...ously

Don't Call Us -- Calling Conventions for the x86

reader wrote to us after last month’s article about walking up stack frames suggesting that we should do a write-up describing the basics of how arguments are actually passed on the stack.  This seemed like a useful follow-on and thus this article describes (briefly) the calling conventions used in Windows on the x86 platform (the details are different, of course, for IA-64 and AMD-64, and are not described in this article).

The x86 architecture does not define a “canonical” form for a function call.  Instead, it allows the programmer to pass arguments in whatever fashion is appropriate for the given use.  When a programmer uses a higher level programming language the actual argument passing mechanism is defined by the code generated by the compiler.  There are generally two major calling conventions used by the Microsoft ‘C’ compiler.  The first, is where all arguments are passed on the stack.  In this convention, the return value from the function (if any) is passed in the
EAX register (see Figure 1).


Figure 1 – All Arguments Pass on the Stack


In the second calling convention (the “fastcall” convention) two of the arguments are passed in registers (the ECX and EDX registers) while the remaining arguments (if any) are passed on the stack (noted in Figure 2).



Figure 2 – The “fastcall” Convention


Understanding these calling conventions helps when you are using the debugger and attempting to unravel the implementation of a given piece of code.  For example, the following code is ExfInterlockedAddUlong:


kd> u ExfInterlockedAddUlong


805128f3 9c               pushfd

805128f4 fa               cli

805128f5 8b01             mov     eax,[ecx]

805128f7 0111             add     [ecx],edx

805128f9 9d               popfd

805128fa c20400           ret     0x4


Note that the first thing it does is to use ECX.  Understanding the calling convention, it is straight-forward to move from the function declaration to the corresponding code.  The function is declared as:





ExInterlockedAddUlong (

    IN PULONG Addend,

    IN ULONG Increment,







#define ExInterlockedAddUlong           ExfInterlockedAddUlong


Of course, the #define is why the function name changes in the kernel. Indeed, the kernel team uses a convention for naming their functions, by including an extra “f” in the prefix (hence, Exf represents an Executive function using the fastcall convention).


Understanding this eases debugging because it is now possible to “figure out” what the code is doing.  In the case of this function it becomes clear.  The pushfd saves the current EFLAGS register, with the cli disabling interrupts (roughly equivalent to raising the IRQL on the processor to HIGH_LEVEL).  The mov saves the current value pointed to by ECX in the EAX register (so we return the correct value) and then adds the value in EDX (second parameter) to the value pointed to by ECX.  The popfd then restores the EFLAGS register (and restores interrupt state to what it was when the call was made).  The ret 0x4 then indicates that four bytes should be discarded from the stack upon return – that is the third parameter of the function.  In the uni-processor OS build (which is where we obtained this code) the spinlock parameter is not used.  In the multi-processor OS build it is used and the code is commensurately complex.


An older convention (the “pascal” convention) passes parameters in a different fashion.  In this case, it pushes them on the stack from left to right (as they are presented in the function parentheses).  See Figure 3.





Figure 3 – The “pascal” Convention


This calling convention is unusual (the __pascal keyword is no longer even supported by the C compiler) but is an equally valid way of passing parameters – just one we do not see very often.


Of course, these are still only conventions.  For those writing code in assembler, the calling conventions are not required.  In mixed-language programming, the assembly language writer is responsible for understanding the nuances of the calling conventions and handling it appropriately.  While those writing in higher level languages may have some control (the C compiler provides a mechanism for creating your own prolog and epilog code) it is considerably more cumbersome and still requires a thorough understanding of the conventions.

User Comments
Rate this article and give us feedback. Do you find anything missing? Share your opinion with the community!
Post Your Comment

"info found"
I found the necessary information elsewhere on the OSR site;


26-Dec-07, Toby Douglass

I concur with Jeremiah.

I found this page while searching for an explanation of the difference between __stdcall and __cdecl.

Excellent article within its scope, however.

26-Dec-07, Toby Douglass

"cdecl versus stdcall"
It might be nice to compare the two most popular all-stack calling conventions on Windows, that is stdcall and cdecl. Sure the main difference is who cleans up the stack, but there are other fun tidbits like @ on stdcall symbols and where you find them. Why would someone choose one versus another (variable arguments, consistency with MS API's). And lets not forget about exciting stack overflows when a __cdecl function is called from a stdcall module. Does anyone really care about pascal anymore? Thank goodness your original article covered exception handlers, v-tables, and some of the other fun raw stack excitement, otherwise we would have you sacked .

12-Jun-03, Jeremiah Cox

Post Your Comments.
Print this article.
Email this article.
bottom nav links