Advanced C++ features and Kernel-mode programming don’t mix


I was just reading recently a whitepaper from the Microsoft web site, describing how you you should use C++ features in the context of kernel-mode programming. The paper is quite interesting, and can be summarized in a few sentences: If you want to use C++ in kernel mode, be aware that a number of C++ features can introduce more trouble instead of solve problems. Although these features are not explicitly prohibited by the paper, it is strongly recommended to avoid them.


Here is the list of features that you should avoid, or at least be aware of:
1) Constructors/destructors.
2) Virtual functions, and class derivation in general.
3) Standard C++ new/delete operators. Use the standard kernel-mode allocation routines instead.
4) Inline functions.
5) Templates.
6) Exceptions.
7) RTTI.


In addition, the paper reminds you about certain coding practices when developing kernel-mode programming:
8) Floating point instructions must be properly protected.
9) Be very aware about your memory usage (paged/non-paged pool, etc)
10) Make sure that you are very conservative with the stack usage (max 12K on x86 systems)
11) Dissasemble your generated code periodically (especially if you still use these features above) to spot eventual problems.
12) Be aware about memory barriers in general. Make sure that you used the volatile keyword in the way you intended.


Certain considerations make a lot of sense, for example #3. Others, like #2, are quite subtle. I remember a friend of mine (which was pretty good at C++ programming in user mode but just started kernel-mode programming) was quite dissapointed that basic C++ features simply didn’t work. One example: the usage of virtual functions under a high IRQL. If the compiler places the VTBL in pageable memory then you might end up executing code under DIRQL for example that attempts to call a virtual function. But since the VTBL is paged out, you get a bugcheck! How would you fix this problem? At least in my opinion, this is not the compiler’s fault since the compiler cannot control the non-paged memory usage for VTBL allocation for a certain class, by putting the VTBL of certain STL classes in non-pageable memory, for example.


So, looking to the list above, it seems that all the juicy C++ features are not very useful in kernel mode, and things like using STL in kernel mode would be probably out of the question. I can’t help wondering – what you have left? If you stripe down C++ by these features, you essentially get the C language more or less. Well, C with C++ style comments pretty much.


Ironically, I remember that I read somewhere that Bjarne Stroustrup started the design of C++ as a language for writing an operating system, or for writing device drivers. So it looks like the language itself failed somewhat at its original goal – running in kernel mode.



But anyway, to end up this post in a positive note, I still believe C++ is an extremely powerful language. And, regardless, of the OS, I like to think that C++ is the language of choice for writing operating system code, even if in kernel mode you have additional limitations. 


Comments (7)

  1. Anonymous says:

    Don’t hang me up on this, but I believe BeOS is written from scratch in C++ (well, as much as possible).

  2. CyrusN says:

    I would just use C#. Let the smart guys who created the runtime deal with this kinda silly crap that developers shouldn’t have to care about 🙂

  3. Anonymous says:

    Maybe a __declspec could solve the vtbl

    problem.

    Anyway even without virtual functions

    i think C++ could be useful even in kernel

    mode. Why pass every KeReleaseSemaphore a

    PKSEMAPHORE and not have semaphore->Release(..) ? It’s true that KeWaitForSingleObject would be a problem without virtual function.

  4. Anonymous says:

    You would use C#… for a kernel?

  5. Anonymous says:

    I read this paper sometime ago. But I can´t agree with "Avoid Templates". Like "plain old functions", templates are processed at compile-time. The snippet belowdoesn´t look like a problem for me:

    template<T1,T2>

    VOID InitString(T1& var, T2 value)

    {

    RtlInitUnicodeString( var, value );

    }

    UNICODE_STRING deviceName;

    InitString( deviceName, L"\Device\MyDrive" );

  6. MGrier says:

    This sounds very dramatic but I think that it’s really all common sense.

    The #1 and #2 constraints to running in kernel mode are availability of heap and stack.

    Anyone who thinks that these resources are free in user mode isn’t much of a engineer.

    The #3 constraint is not calling pagable code from elevated IRQL. Frankly any plan that has you using complex programming constructs while at elevated IRQL is a loser to start with; you should do the least possible and queue something to run at PASSIVE level which is pagable.

    The paging stuff is the only really oddity about restricting use of C++ in kernel mode.