
Dear readers, please don’t be puzzled—because even I was a bit confused when I wrote this title about why I am doing such a seemingly unnecessary thing. As mentioned many times in this blog, my main operating system is Linux. Therefore, if some daily tasks can be accomplished in the native system, it saves the trouble of opening a virtual machine. I believe there are friends with similar needs, so I am sharing this process for everyone to critique (for my seemingly pointless behavior).
Preparing Demonstration Files
First, create a test project called XxDriver. This project has no actual functionality and only meets the minimum source code requirements for a driver. Its structure and content are as follows:
- >> tree
- .
- ├── driver.c
- └── SOURCES
- 1 directory, 2 files
- >> cat SOURCES
- TARGETNAME = XxDriver
- TARGETTYPE = DRIVER
- SOURCES = driver.c
- >> cat driver.c
- #include <ntifs.h>
- VOID Unload(IN PDRIVER_OBJECT DriverObject)
- {
- DbgPrint("Unload\r\n");
- }
- NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
- {
- DriverObject->DriverUnload = Unload;
- return STATUS_SUCCESS;
- }
Since our goal this time is to compile Windows drivers on Linux, we need the compilation toolchain to be as simple as possible. For this reason, we chose the last standalone version of WinDDK—version 7600.
The normal process for compiling Windows drivers using WinDDK is:
- Open the corresponding system’s compilation command line environment.
- Switch to the driver’s project directory (i.e., the directory created above).
- Execute the build command.
If there are no issues, WinDDK will compile the corresponding files based on the selected system and architecture.

Preparing the Compilation Environment on Linux
Next, we start setting up the environment for compiling drivers on Linux.
Installing Wine
First, install the Wine environment according to your distribution. This step should not be difficult.
Configuring the Wine Environment
Then, we need to create several “disks” to allow Wine to quickly find our deployed compilation environment and source code directory. Here, I map ${HOME} to the D drive, below is the configure window of winecfg command:

Finally, copy the WinDDK directory installed in the Windows environment to the disk directory we created above. According to the D drive configuration mentioned above, WinDDK is located in D:\Softwares\WinDDK, which corresponds to ${HOME}/Softwares/WinDDK. The source code of the XxDriver project created at the beginning of this article is located in D:\Codes\XxDriver, which corresponds to ${HOME}/Codes/XxDriver.
After configuring the Wine disks and copying WinDDK to the corresponding directory, we also need to copy two tools to ~/.wine/drive_c/windows. These tools are files used in setenv.bat (mentioned later):
- >> find ~/.wine/drive_c/windows/ -iname 'ulib.dll' -or -iname 'doskey.exe'
- ~/.wine/drive_c/windows/system32/ulib.dll
- ~/.wine/drive_c/windows/system32/doskey.exe
- ~/.wine/drive_c/windows/syswow64/ulib.dll
- ~/.wine/drive_c/windows/syswow64/doskey.exe
上面几个文件分为32位和64位的程序,请从windows环境的对应目录拷贝。另外,根据笔者测试,似乎不拷贝也可以,但是编译环境会少几个命令的ALIAS ;)
These files are divided into 32-bit and 64-bit programs. Please copy them from the corresponding directories in the Windows environment. Additionally, based on my tests, it seems that is OK if not copying them, but the compilation environment will lack a few command aliases.
Starting the WinDDK Compilation Environment
Reviewing the steps for compiling drivers on Windows, we first need to start a build environment, which is opened through the following methods provided by WinDDK:

By checking the information of the above shortcuts, we can get the following four initialization commands for free or checked compilation environments under x86/x64 (we will not focus on the IA64 architecture as it is less commonly used):
- C:\Windows\System32\cmd.exe /k C:\WinDDK\7600.16385.1\bin\setenv.bat C:\WinDDK\7600.16385.1\ chk x64 WIN7
- C:\Windows\System32\cmd.exe /k C:\WinDDK\7600.16385.1\bin\setenv.bat C:\WinDDK\7600.16385.1\ fre x64 WIN7
- C:\Windows\System32\cmd.exe /k C:\WinDDK\7600.16385.1\bin\setenv.bat C:\WinDDK\7600.16385.1\ chk x86 WIN7
- C:\Windows\System32\cmd.exe /k C:\WinDDK\7600.16385.1\bin\setenv.bat C:\WinDDK\7600.16385.1\ fre x86 WIN7
Combining the environment we deployed on Linux, we can initialize the build environment on Linux and use the build command to compile the XxDriver example code created at the beginning of this article with the following command:
- >> wine cmd.exe /k D:/Softwares/WinDDK/7600.16385.1/bin/setenv.bat D:\\Softwares\\WinDDK\\7600.16385.1 chk x64 WIN7
- 002c:fixme:winediag:loader_init wine-staging 9.9 is a testing version containing experimental patches.
- 002c:fixme:winediag:loader_init Please mention your exact version when filing bug reports on winehq.org.
- 0088:fixme:wineusb:query_id Unhandled ID query type 0x5.
- 0088:fixme:wineusb:query_id Unhandled ID query type 0x5.
- 0088:fixme:wineusb:query_id Unhandled ID query type 0x5.
- 0088:fixme:wineusb:query_id Unhandled ID query type 0x5.
- 0128:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!
- 0128:fixme:console:AddConsoleAliasW : (L"..", L"cd ..\\$*", L"cmd.exe") stub!
- D:\Softwares\WinDDK\7600.16385.1>cd \
- D:\>cd Codes\XxDriver
- D:\Codes\XxDriver>build
- BUILD: Compile and Link for AMD64
- BUILD: Loading d:\softwares\winddk\7600.16385.1\build.dat...
- BUILD: Computing Include file dependencies:
- BUILD: Start time: Sat Aug 31 13:58:01 2024
- BUILD: Examining d:\codes\xxdriver directory for files to compile.
- BUILD: Saving d:\softwares\winddk\7600.16385.1\build.dat...
- BUILD: Compiling and Linking d:\codes\xxdriver directory
- 014c:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!
- 0154:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!
- 0154:err:winediag:gnutls_process_attach failed to load libgnutls, no support for encryption
- 0154:err:winediag:process_attach failed to load libgnutls, no support for pfx import/export
- Compiling - driver.c
- 0154:fixme:msvcrt:__clean_type_info_names_internal (5008C9F0) stub
- 0154:fixme:msvcrt:__clean_type_info_names_internal (7A4E3490) stub
- 0154:fixme:msvcrt:__clean_type_info_names_internal (7AA24484) stub
- Linking Executable - objchk_win7_amd64\amd64\xxdriver.sys
- 015c:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!
- 0164:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!
- 0164:fixme:msvcrt:__clean_type_info_names_internal (7A46F190) stub
- 0164:fixme:msvcrt:__clean_type_info_names_internal (7A8D3490) stub
- 0164:fixme:msvcrt:__clean_type_info_names_internal (7AA24484) stub
- 015c:fixme:msvcrt:__clean_type_info_names_internal (7A46F190) stub
- 015c:fixme:msvcrt:__clean_type_info_names_internal (7A8D3490) stub
- 015c:fixme:msvcrt:__clean_type_info_names_internal (7AA24484) stub
- 0134:fixme:file:NtSetInformationFile Unsupported class (19)
- BUILD: Finish time: Sat Aug 31 13:58:02 2024
- BUILD: Done
- 3 files compiled
- 1 executable built
OK, at this point, we can manually compile the driver on the Linux environment.
Compiling Using Makefile
However, typing so many commands every time we compile is still very annoying. Is there a simpler way?
The answer is yes—in Linux world, the common compilation method is to use the Makefile command. Naturally, we can also use this tool to save some time.
Edit the following content into the makefile.linux file:
- .PHONY: x64chk x86chk clean
- WINDDK_PATH := "D:\\\\Softwares\\\\WinDDK\\\\7600.16385.1"
- x64chk:
- wine cmd.exe /c compile.bat x64chk ${WINDDK_PATH} 2 > /dev/null
- x86chk:
- wine cmd.exe /c compile.bat x86chk ${WINDDK_PATH} 2 > /dev/null
- clean:
- rm -f buildchk_*_*.log
- rm -rf objchk_*_*/
Note: The makefile name cannot be any of the following (case insensitive): makefile, makefile.inc, makefile.def, because the WinDDK build tool will check if these filenames exist in the current directory and then read their contents for parsing. Due to differences in syntax format with Linux, this may cause compilation errors.
From the above makefile.linux file,it can be seen that the key lies in the compile.bat file. Here is the content of this file:
- set COMPILE_DIRECTORY=%CD%
- if "%1" == "x64chk" (
- call %~2\bin\setenv.bat %~2 chk x64 WIN7
- ) else if "%1" == "x86chk" (
- call %~2\bin\setenv.bat %~2 chk x86 WIN7
- )
- cd %COMPILE_DIRECTORY%
- build /g
It’s actually not complicated. It just uses a batch script to automatically call setenv.bat to initialize the compilation environment and then use this environment to build.
After editing the above makefile file, you can compile the driver file on Linux using the following command. The -f parameter is a parameter of the makefile command, meaning to read the makefile content from the specified filename. x64chk is a marker defined in the .PHONY parameter in the above makefile content:
- >> make -f makefile.linux x64chk
- wine cmd.exe /c compile.bat x64chk "D:\\\\Softwares\\\\WinDDK\\\\7600.16385.1" 2 > /dev/null
- 0284:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!
- 0284:fixme:console:AddConsoleAliasW : (L"..", L"cd ..\\$*", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"...", L"cd ..\\..\\$*", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"....", L"cd ..\\..\\..\\$*", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"BLD", L"build -cfeg $*", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"BZZ", L"build -ZPg $*", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"BZ", L"build -ZPg $*", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"BCZ", L"build -cZMg $*", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"USE", L"net use $*", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"NUSE", L"net use $* /d", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"QF", L"format a: /q /u /v:\"\"", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"VIEW", L"net view \\\\$*", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"WINDIR", L"cd %windir% $T %homedrive%", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"LINK32", L"link $*", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"COFF", L"link $*", L"cmd.exe") stub!
- 0284:fixme:console:AddConsoleAliasW : (L"up", L"if \"$1\"==\"\" (cd ..) else (for /L %i in (1,1,$1) do cd ..)", L"cmd.exe") stub!
- BUILD: Compile and Link for AMD64
- BUILD: Loading d:\\softwares\\winddk\\7600.16385.1\build.dat...
- BUILD: Computing Include file dependencies:
- BUILD: Start time: Sat Aug 31 14:37:22 2024
- BUILD: Examining d:\codes\xxdriver directory for files to compile.
- BUILD: Saving d:\\softwares\\winddk\\7600.16385.1\build.dat...
- BUILD: Compiling and Linking d:\codes\xxdriver directory
- 0568:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!
- 0574:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!
- 0574:err:winediag:gnutls_process_attach failed to load libgnutls, no support for encryption
- 0574:err:winediag:process_attach failed to load libgnutls, no support for pfx import/export
- Compiling - driver.c
- 0574:fixme:msvcrt:__clean_type_info_names_internal (5008C9F0) stub
- 0574:fixme:msvcrt:__clean_type_info_names_internal (7A4E3490) stub
- 0574:fixme:msvcrt:__clean_type_info_names_internal (7AA24484) stub
- Linking Executable - objchk_win7_amd64\amd64\xxdriver.sys
- 0570:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!
- 0598:fixme:heap:RtlSetHeapInformation HEAP_INFORMATION_CLASS 1 not implemented!
- 0598:fixme:msvcrt:__clean_type_info_names_internal (7A46F190) stub
- 0598:fixme:msvcrt:__clean_type_info_names_internal (7A8D3490) stub
- 0598:fixme:msvcrt:__clean_type_info_names_internal (7AA24484) stub
- 0570:fixme:msvcrt:__clean_type_info_names_internal (7A46F190) stub
- 0570:fixme:msvcrt:__clean_type_info_names_internal (7A8D3490) stub
- 0570:fixme:msvcrt:__clean_type_info_names_internal (7AA24484) stub
- 0330:fixme:file:NtSetInformationFile Unsupported class (19)
- BUILD: Finish time: Sat Aug 31 14:37:23 2024
- BUILD: Done
- 3 files compiled
- 1 executable built
At this point, we can save some more time ;)
Compiling Using VS Code
Although using the command line make command to compile on Linux is a common method, if we can compile directly in an IDE, it would better match everyone’s daily development habits. Next, I will introduce how to use the VS Code integrated development environment to compile drivers.
First, of course, you need to install VS Code.
Then search and install the following two plugins in the marketplace:
Then search and install the following two plugins in the marketplace:
- C/C++: A support plugin for the C language, providing features such as syntax highlighting and auto-completion.
- Makefile Tools: A support plugin for the Makefile compilation system.
After installing the above plugins, open the project directory created at the beginning of this article. The Makefile Tools plugin will automatically load upon recognizing that the project supports makefile compilation.
Since our makefile name is not a common name, we need to manually specify the path of the makefile. Then, after specifying the build target as the target to be compiled by makefile.linux, click the button in the red box in the image below to compile the project:

Here is the compilation result:

The end
In this article, we used Wine and WinDDK to build an environment for compiling Windows drivers on Linux. Finally, we simplified the compilation process using Makefile and ended up with a non-mainstream environment for compiling Windows drivers on Linux using VS Code.