I just finished porting a Windows driver (actually 3) to use the MinGW build environment.
Due to the Windows Device Driver Kit (DDK) being under a somewhat opaque license, we could not get legal clearance to release binaries of our GPLed code compiled with the DDK. This was bad because I, being the open-source kinda guy I am, had based my work on a pretty substantial GPLed codebase. *Surely* the DDK license couldn't be so restrictive to prevent all binaries bsed on GPL source! Well maybe it is and maybe it isn't. When it's not clear, legal says no.
Not good. It looked like we were going to have to somehow pull out the work I had done and rewrite the rest of the code from scratch to be GPL-free so we could release binaries legally following the DDK's EULA.
It was in the process of estimating the work involved that I happened to talk to Jamey Sharp at a local Linux beering. We were each talking about our work and it turned out that he was in the same boat as me, a Linux dev who through some strange quirk was working on Windows drivers. But he wasn't using the DDK, he was using MinGW, which is a port of GCC to compile Windows binaries.
Sometime later he was kind enough to come over and show me his setup and makefile, and sure enough it worked. So, we *could* use the GPLed code if we just distributed DDK-free binaries.
Once on that path, it still did take me a while to get the driver building. One nice thing about trying to get a different build env going is that you have something that works (although you can't distribute it :)
I got it compiling by modifying the driver to have function definitions etc. that MinGW lacked. But it wouldn't link. Sigh. I was using functions that were not in MinGW's import libraries, and could not get around this by fudging it in my driver; I had to fix MinGW. This is a big fear of mine every time I use a piece of open-source software -- if your code has a Foo dependency and Foo is broken, you pretty much have to be willing to start hacking Foo if you want it fixed. (Sure you can file a bug, but obviously nobody cares about your bug except you or it would be fixed, right?)
Once I dived into MinGW (actually w32api) it was straightforward to fix. Many of my changes to get the driver compiling actually belonged in the MinGW header. There were also functions omitted from the import libraries. (Import libs tell the linker how to link to the actual shared libraries that will be present at runtime.) These were defined in .DEF files, and all it needed to know was for each function, how many bytes were its arguments? Easy, once I knew what to do. (Nothing hard, just if one doesn't do this thing regularly, you forget how it all fits together.)
So for anyone who wants to write Windows kernel code without the DDK, it can be done. Some tips:
- Debian/Ubuntu includes mingw cross-compiler
- Don't use KMDF/WDF, MinGW doesn't support it
- Sprinkle all callback functions with DDKAPI, so they use stdcall calling method (the compiler will complain, makes it easy.) including DriverEntry().
- Add -DDBG to enable KdPrint()s
- Inline asm will need to be rewritten
- Recent GCC doesn't have __FUNCTION__, and DDK doesn't have __func__, so plan on wrapping with your own macros for tracing and handling this difference based on #ifdef __MINGW32__. I'm a big fan of gcc's preprocessor, the variable-argument macros are pretty nice.
- WinDBG needs .pdb files to set breakpoints and step through code, MinGW doesn't generate these, so KdPrint() is your friend
- You can't link with libc yet sometimes you need snprintf. I pulled in Linux's snprintf code to fill this gap, yay GPL!
- MinGW is 32-bit only right now. For 64-bit binaries, plan on becoming a serious MinGW contributor for a while!
- Here's how my makefile build cmd ended up: i586-mingw32msvc-gcc $(CFLAGS) -o $@ -shared -Wl,--entry,_DriverEntry@8 -nostartfiles -nostdlib $^ -lntoskrnl -lhal -lndis
- For reference, see the Xen GPLPV repo. More help is always welcome.
Hopefully this blog post will contain enough keywords to be helpful to someone in the future :-)