С++ in kernel

> @“Peter_Viscarola_(OSR)” said: > (Quote) > It’s been done. Google is your friend. > > Peter I asked this question exactly because I used google to figure out Linux kernel + c++ viability. And my impression was that you will have to fight really hard with build system to make it work. However mr Robert’s message sounded to me like you basically need just write c++ code in cpp files - pretty much the same way as I do in drivers for windows. Maybe I just misunderstood.

no different from the situation in Windows

Except (of course) you’ll have no problem with checking something in to Windows that’s written in C++, even into MinKernel.

ETA:

making C++ work with Linux kernel headers does not really seem to be the easiest job

And, of course, the Windows kernel headers have long been C++ compatible.

Peter

In the past I’ve seen C++ code (file filter drivers ), with OO (
class etc). Quite hard to debug, at least for me, when the designers
failed to understand what is/are going on …

Then move on to BSD, then Apple mach based kernel —, almost all is C
code ( except some asm )… Get open source, map correctly ( or almost
), debug thru kernel ( not what I want to do or enjoy ) but debugging
is bit easier, well just the debugger infrastructure is not as solid
as Windbg…

Given that, I always ask one question to any senior new hire — What
is the best code ? I would not answer the question, just in case …
And if I see a simple reasonable answer with some pseudo-codes that
guy is hired, period…

The trend I see, is that lately more candidates learn C++ ( Google ,
FB, Amazon etc., wants C++), so it is natural people want to use that
knowledge in kernel mode…

Now, what is the year when Linux got started ( i.e. porting Xinu or
something after wholesale changes )??? Perhaps early 90s in the lab,
then around 94 or 96 it is out there !!!.. By then lot of people
using C++/ Java is or already coming … So why Linus did not conform
to it… Whatever …

I hear people love to code in Swift/Obj-C/C++, so outside kernel
modules I create interfaces ( like in low level daemons / services )
and open up the bridges, so one can use swift or anything else. Inside
kernel, I use C ( and some asm for tickery).

I see some benefits of using C++, other than that I would suggest (
just like Dejon ) look at the archive, see the concerns, and come up
with solutions ( if you can ) — and enlighten us.

-Pro

OK. Let’s be clear: Coding in C++ these days can mean many things. This is particularly true when we’re talking about coding to Windows kernel-mode C Language interfaces, and under the constraints imposed by the Windows OS environment.

I see primarily three different “levels” of approaches to using C++ in Windows driver development:

  1. C++ as a Better C: In this approach, one typically uses C Language conventions, but with the addition of specific Modern C++ features. This also typically means directly using the OS-supplied C Language interfaces. Most devs who program in C would be happy with the code, and they’d see very few differences. You might see the occasional RAII pattern here, but only rarely (where you can really benefit from some complex custom destructor, for example). This is typically the approach we use in kernel-mode code for Windows at OSR. There’ll be an article in the next issue of The NT Insider about this.

  2. C++ Wrappers: This approach expands the approach above with the addition of C++ classes/wrappers to do a lot of the driver’s work and around some of the more common C Language OS interfaces. This approach is typically where devs bite-off overriding “new” and “delete.” The approach makes the use of RAII patterns for common OS interface tasks like acquiring and automatically releasing locks. This can be done well, but it needs to be done with a lot of restraint. In my experience, it’s quite easy for devs using this approach to “slip into darkness” and accidentally create an impenetrable mess that fall into category 3 below.

  3. Full-On OO C++: This approach is what I think most C Language folks envision when they think of “drivers in C++” … and it makes them shudder. I don’t commonly see this approach anymore, except from folks who are on dev teams where OO is a way of life and they really hate thinking in C Language interface terms. Having seen, and tried to understand and debug, fully class-based, object oriented approaches to writing Windows drivers I can personally attest to how very painful this can be.

C was a terrific language for OS development when compared to assembly language, back in the late 20th Century. It is a shit language for OS development for the 21st Century. What’s better is still an open question, and what we can actually use that’s better… while we’re still using an OS that was designed in the late 1980’s… is a bigger question still.

I think Modern C++ helps.

Peter

Thanks Peter_Viscarola, as always…

Yea, looking forward to reading your NT article(s) on C++ in kernel mode !

I would very much appreciate it if I see what are the things in C++
that can be used with ease ( EASE ) without bending one’s head to
believe in the rituals :slight_smile:

For example — std::, algorithms etc. including others …

Pro

-Pro

BTW, the reason I have interest in this topic —

Two years ago, we were embarking on a project that would be C++ based
cross platform ( Windows, MacOS, Linux being main ones ). Quite a few
kernel modules ( for each platform 5 to 7 ). And lots of codes in
umode.

Back then C++17 is making its place, and I was trying to see how I can
get a message passing module – used BOOST and saw there are quite a
bit of interesting features there ( quite a few of them are now in
C++17 ). Had to move back for some kernel hacks, but there are lot
more works in that … Looks like I would be back next year to take a
shot at it…

Intellectual property protection is what we do, or at least try to do :slight_smile:

So it would help a lot when I see some articles from horses mouth :slight_smile:

-Pro

In my shop, we do C and C# in equal measure. Use the right tool for the right job. In at least the last 20 years, no one has presented any compelling argument to me why any part of C++ represents ‘super C’ or anything except a regression that has all the ways to hang oneself tat the old way has, but provides multitudes more as well.

To be sure C# has its own set of pitfalls as well

Now, what is the year when Linux got started ( i.e. porting Xinu or something after wholesale changes )???

What he actually did was providing his own implementation of UNIX (namely, its SVR2 version as described in Maurice Bach’s
“UNIX Operating System”), and used the MINIX source code in his project. He released it in 1991. You can check it if you wish.
It is available from kernel.org (IIRC , as version 0.01)

Perhaps early 90s in the lab, then around 94 or 96 it is out there !!!.. By then lot of people using C++/ Java is or already coming …
so why Linus did not conform to it… Whatever …

Actually, according to him, he tried to use C++ at some point.

[begin quote]

In fact, in Linux we did try C++ once already, back in 1992. It sucks. Trust me - writing kernel code in C++ is a BLOODY STUPID IDEA. The fact is, C++ compilers are not trustworthy. They were even worse in 1992, but some fundamental facts haven’t changed:

    the whole C++ exception handling thing is fundamentally broken. It's _especially_ broken for kernels.
    any compiler or language that likes to hide things like memory allocations behind your back just isn't a good choice for a kernel.
    you can write object-oriented code (useful for filesystems etc) in C, _without_ the crap that is C++.

In general, I'd say that anybody who designs his kernel modules for C++ is either

    looking for problems
    C++ bigot that can't see what he is writing is really just C anyway
    was given an assignment in CS class to do so.

Feel free to make up (d).

Linus

[end quote]

Anton Bassov

I read this somewhere before, thanks for putting it up here, Anton …
Perhaps things changed in Modern C++, waiting to read the upcoming NT
insider articles to see…
Pretty much everything is tied to std::<excpetion_handling>. And it is
still there, so runtime still think it can generate exceptions…

Exceptional C++, and More exceptional C++ ( little outdated) but a
good reference to understand what could go wrong, how to handle those
situations.

Back in those days, we were writing NDIS based drivers on Windows 98,
we were using all C memory mgmt API. Then RTL*() came and it was a
real joy, seriously. For C++ there are a lot of features that I would
love to use, if they take the excption_handler out or a way to disable
it all together :slight_smile:

-Pro</excpetion_handling>

Mr @MBond2 wrote:

no one has presented any compelling argument to me why any part of C++ represents ‘super C’ or anything except a regression

Really? So, you’re going to argue that strong type checking is a regression? Likewise, #define is better than constexpr? That having to code the number of elements in an array, and keep that consistent, is better than range-based for?

I’d love to hear that.

Peter

Shouldn’t the question be “Rust in kernel”?
We are in the 21st century.

In any case, I agree with Peter on:
“3. Full-On OO C++: This approach is what I think most C Language folks envision when they think of “drivers in C++” … and it makes them shudder. I don’t commonly see this approach anymore, except from folks who are on dev teams where OO is a way of life and they really hate thinking in C Language interface terms. Having seen, and tried to understand and debug, fully class-based, object oriented approaches to writing Windows drivers I can personally attest to how very painful this can be.”
It is great until you need to add more members to the team or pass your code to someone else.
But 1 and 2, make a lot of sense, in my opinion.

Best regards,
Yan.

Shouldn’t the question be “Rust in kernel”?

Well, we _could _have that discussion, but probably in a separate thread.

FYI, it’s been done. Even I managed to write some mess in Rust and got DriverEntry to be called and DbgPrint to output stuff. But it’s a super-big mess right now. And you wind-up with a lot of “unsafe” code.

Peter

I’ve said in the past that the concept of a “Common Language Runtime for Kernel” makes a great deal of sense. Most drivers are largely plumbing around a core set of boilerplate modules. So, embed the boilerplate in a well-tested and proven CLRK, and now one can use any of the .NET languages to write drivers. C#, F#, IronPython all become viable. KMDF is an excellent prototype for the CLRK.

Plus, the acronym is already pronounceable…

the concept of a “Common Language Runtime for Kernel” makes a great deal of sense.

Interesting. I’d like to understand better. Would you say more about what this would do and how it would work?

For example – in MY mind – the primary issue for supporting C# in the kernel is garbage collection. And, while mapping the APIs would be painful we DO already have access to the necessary “goo” to enable it.

Similarly in Rust: To make an interface truly useful and Rusty the OS interface functions need to WORK like Rust functions, and therefore take and return appropriately Rusty parameters. For example, in Rust, the standard way to return status and data from a function is with a Result. So (ignoring the input arguments), we might have:

match WdfDeviceCreate(...) {
    Ok(device) => device,
    Err(err) => {DbgPrint!("WdfDeviceCreateFailed: {:X}", err);  Return(err)},
};

or, more colloquially (and foregoing the DbgPrint for the sake of brevity):

device =WdfDeviceCreate(...)?;

I think that’s right. One thing about Rust is that if you don’t use for it, like, two weeks, you forget everything you learned.

Anyhow, my point really is: How would CLRK help here?

Peter

@“Peter_Viscarola_(OSR)” said:
So, you’re going to argue that strong type checking is a regression? Likewise, #define is better than constexpr? That having to code the number of elements in an array, and keep that consistent, is better than range-based for?

Good points. Not surprising this question was greeted with silence. There are still a few out there that categorize all of c++ as evil even though they don’t know all that much about this ever improving language. You can show them something better than C like the above excellent points and they’ll refuse to think about it. They made a decision long ago not to use c++ and admitting now there is anything good would be an admission they were wrong, their advice is wrong, their code is not as good as it could have been, their productivity is suboptimal, and there were better ways they artificially denied themselves from using their entire career as they railed against it. For these people, c++ has to be worse in every way. Anything else is unthinkable. There seems to be fewer of these people every year and they are not as vocal as before. The tide has turned.

And as far as what linus thought about c++ remember this is the same guy who said “I don’t like debuggers. Never have, probably never will.” How many people ditch kernel debugging as evil because of his statement and refuse to touch them? Just imagine what that handicap would do to your productivity. It’s the same thing when you limit yourself to C.

NOP, this is absolutely false … The post was from a veteran , and
only to indicate that there are concerns, in particular C# pitfalls…
Nothing is silver bullet, unless they design is correct, know the
limitations.

Yes use constant expression instead of #define. Use module import,
instead #include – no problem, they have benefits, and they have use
cases sure … just like inline etc.

No one saying C++ worse here, __ this is what I was trying to say,
don’t try to bend someone’s head into believing ritual(s)__.

It’s a complex language, and kernel debugging is even way way worse,
if design is wrong. No one has to be religious about it. Some of us
object to C++ uses lot more features than C++ stdandards, when the
situation warrants …

-Pro

But type safety is no stronger in C++ than in C. Sure you have more different kinds of casts to screw yourself with, but how does that make it safer? Everything you can do wrong in C, you can still do wrong in C++ along with more stuff? By contrast, C# has actual type safety - yes there are unsafe blocks, but you need special compiler options to even allow them and even when you, the block is explicitly marked as unsafe. For example in C++ you can readily write

const char szABC = “ABC”;

WCHAR* p = (WCHAR*)szABC;
p[42] = ‘Q’;

now you will say, that’s not the right thing to do in C++, and of course it isn’t, but it compiles! Which means that when checking and maintaining code, you have to consider the possibility that someone has written it this way. I spend almost all of my time reviewing and checking code written by others and doubtless this impacts my point of view

And so it goes on. Every bad thing you can do in C, you can also do in C++ plus more. Again, I might just be too dense to take this in, but I see no advantage in C++ versus C unless one goes all the way to C# - where the compiler / runtime provide true type safety and actually safe exception based error handling. Interfaces in place of multiple inheritance and generics in place of template classes. And lots of other productivity and reliability improvements too

but this thread should die. I’m not going to respond again unless we start discussing a proposal for a managed runtime for KM development or something more productive. It could probably be done, but large object garbage collection would be a major issue along with large sections of the system and other namespaces that would have to be restricted

@Tim_Roberts >I’ve said in the past that the concept of a “Common Language Runtime for Kernel” makes a great deal of sense. Have you heard about Singularity? (https://en.m.wikipedia.org/wiki/Singularity_(operating_system)) There was a time that MSFT had so much hype about C# that they tried to write an OS in a managed language. They even published the sources and docs - https://github.com/lastweek/source-singularity/tree/master/docs/Design%20Notes It was discontinued in 2008 though.

@MBond2 said:
const char szABC = “ABC”;

WCHAR* p = (WCHAR*)szABC;
p[42] = ‘Q’;

C++ has 5 types of casts, C has just 1. The reason c++ casts are better is c++ casts describe exactly what they are doing to the reader and the compiler and thus are easier to understand their purpose and get more safety checks from the compiler. A C cast should never be used in c++ because it’s just a quick and dirty blindfold. This really comes through when using your example:

   The C way:  WCHAR* p = (WCHAR*)szABC;             // this compiles and you have a serious bug
   The C++ way: auto p = const_cast<WCHAR*>(szABC);  // this does NOT compile since szABC is 'char' not WCHAR...C++ just saved the day!

const_cast tells the reader and compiler the ‘const’ (or volatile) attribute is being removed, nothing more. const_cast is useful when dealing with libraries that did not properly specify const on function parameters and a number of these cases exist in the WDK. Let’s review the 5 powerful c++ casts:

const_cast
static_cast
reinterpret_cast
dynamic_cast
bit_cast – new in c++20 and excellent for systems level work / protocol

As with anything else in c++ you don’t have to use any of these if you don’t want, but using the right cast for the job makes code more readable, bullet proof, and catching problems at compile time is excellent. Thus casting is just one of thousands of concrete, nuts and bolts ways c++ makes vastly improved device driver code.

Interesting. I’d like to understand better. Would you say more about what this would do and how it would work?

We have already established with KMDF that huge chunks of most drivers are common boilerplate code. That’s exactly the kind of thing that fits in the CLR model. A great deal of user-mode C# programming involves writing plumbing between system-provided CLR modules. At the most basic level, I can imagine KMDF itself as a CLR. Instead of WDFQUEUE and WDFDEVICE, you’d have:

    using System.Kernel.IoQueue;
    using System.Kernel.Device;
    using System.Kernel.Request;
    using System.Kernel.Memory;

    Memory buffer;
    request.RetrieveOutputBuffer( buffer, minsize );

You’re right that the memory model would be an issue. Since it is a common address space, you’d get certain economies of scale, because you wouldn’t need to manage a whole bunch of separate memory pools, but there would have to be some timing guarantees.