OSRLogo
OSRLogoOSRLogoOSRLogo x Subscribe to The NT Insider
OSRLogo
x

Everything Windows Driver Development

x
x
x
GoToHomePage xLoginx
 
 

    Wed, 13 Dec 2017     115626 members

   Login
   Join


 
 
Contents
  Online Dump Analyzer
OSR Dev Blog
The NT Insider
Downloads
ListServer / Forum
Driver Jobs
  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


A
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

nt!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:

 

NTKERNELAPI

ULONG

FASTCALL

ExInterlockedAddUlong (

    IN PULONG Addend,

    IN ULONG Increment,

    IN PKSPIN_LOCK Lock

    );

 

 

and:

 

#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;

http://www.osronline.com/article.cfm?article=500

Rating:
26-Dec-07, Toby Douglass


"seconded"
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.

Rating:
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 .

Rating:
12-Jun-03, Jeremiah Cox


Post Your Comments.
Print this article.
Email this article.

Writing WDF Drivers I: Core Concepts
LAB

Nashua (Amherst), NH
15-19 May 2017

Writing WDF Drivers II: Advanced Implementation Techniques
LAB

Nashua (Amherst), NH
23-26 May 2017

Kernel Debugging and Crash Analysis
LAB

Dulles (Sterling), VA
26-30 Jun 2017

Windows Internals and Software Driver Development
LAB

Nashua (Amherst), NH
24-28 Jul 2017

 
 
 
 
x
LetUsHelp
 

Need to develop a Windows file system solution?

We've got a kit for that.

Need Windows internals or kernel driver expertise?

Bring us your most challenging project - we can help!

System hangs/crashes?

We've got a special diagnostic team that's standing by.

Visit the OSR Corporate Web site for more information about how OSR can help!

 
bottom nav links