I am trying to compile a driver through the use of cl.exe on Windows 10. It is written in pure C. I am using the Enterprise Windows Driver Kit to provide source files and compiling for X86. I understand msbuild.exe exists, but I neither want to make a vs proj file nor do I want the actual compilation process to be obfuscated for me.
Up until this point, I was able to compile and include every function I needed, but, and as I noticed before, link.exe cannot find any function implemented in ntoskrnl.lib.
Specifically, I am unable to resolve the symbols "IoCreateDevice" and "IoDeleteDevice," which are both supposed to be implemented in ntoskrnl.lib.
I have this compilation and link line in a bat file:
cl ^
/c /KERNEL /GS- /Tc ^
filter.c ^
/I"D:\Downloads\EnterpriseWDK_rs1_release_14393_20160715-1616\Program Files\Windows Kits\10\Include\10.0.14393.0\km"
link filter.obj ^
"D:\Downloads\EnterpriseWDK_rs1_release_14393_20160715-1616\Program Files\Windows Kits\10\Lib\10.0.14393.0\km\x86\fwpkclnt.lib" ^
"D:\Downloads\EnterpriseWDK_rs1_release_14393_20160715-1616\Program Files\Windows Kits\10\Lib\10.0.14393.0\km\x86\wdm.lib" ^
"D:\Downloads\EnterpriseWDK_rs1_release_14393_20160715-1616\Program Files\Windows Kits\10\Lib\10.0.14393.0\km\x86\ntoskrnl.lib" ^
/SUBSYSTEM:NATIVE /ENTRY:DriverEntry /DRIVER:WDM /KERNEL /VERBOSE /MACHINE:X86 /BASE:0x1000 /NODEFAULTLIB
Running it produces at the linking stage:
filter.obj : error LNK2019: unresolved external symbol __imp__IoCreateDevice referenced in function _DriverEntry
filter.obj : error LNK2019: unresolved external symbol __imp__IoDeleteDevice referenced in function _unload
Unused libraries:
D:\Downloads\EnterpriseWDK_rs1_release_14393_20160715-1616\Program Files\Windows Kits\10\Lib\10.0.14393.0\km\x86\ntoskrnl.lib
And I know it's searching the library:
...
Searching D:\Downloads\EnterpriseWDK_rs1_release_14393_20160715-1616\Program Files\Windows Kits\10\Lib\10.0.14393.0\km\x86\ntoskrnl.lib:
...
And, to verify that ntoskrnl.lib has the function implementation in it, dumpbin.exe "[ntoskrnl.lib path]" /EXPORTS | grep -i iocreatedevice
returns:
_IoCreateDevice@28
And then, to verify that the definition of the symbol exists within the wpm.h
header file, I modified the source so:
...
#if (NTDDI_VERSION >= NTDDI_WIN2K)
#pragma message("DEFINING IOCREATEDEVICE") //my line
_IRQL_requires_max_(APC_LEVEL)
_Ret_range_(<=, 0)
NTKERNELAPI
NTSTATUS
IoCreateDevice(
_In_ PDRIVER_OBJECT DriverObject,
_In_ ULONG DeviceExtensionSize,
_In_opt_ PUNICODE_STRING DeviceName,
_In_ DEVICE_TYPE DeviceType,
_In_ ULONG DeviceCharacteristics,
_In_ BOOLEAN Exclusive,
_Outptr_result_nullonfailure_
_At_(*DeviceObject,
__drv_allocatesMem(Mem)
_When_((((_In_function_class_(DRIVER_INITIALIZE))
||(_In_function_class_(DRIVER_DISPATCH)))),
__drv_aliasesMem))
PDEVICE_OBJECT *DeviceObject
);
#endif
...
which prints at compile-time:
...
filter.c
DEFINING IOCREATEDEVICE
...
I don't understand why the linker refuses to link to the implemented version of _IoCreateDevice. What does it see that makes it say, "This definition is not applicable?"
Edit 1: In an attempt to force the linker to use a given order when linking the libraries, as per BoP's comment, I added the following statements to the top of my source code file, before any includes:
#pragma comment(lib, "D:\\Downloads\\EnterpriseWDK_rs1_release_14393_20160715-1616\\Program Files\\Windows Kits\\10\\Lib\\10.0.14393.0\\km\\x86\\fwpkclnt.lib")
#pragma comment(lib, "D:\\Downloads\\EnterpriseWDK_rs1_release_14393_20160715-1616\\Program Files\\Windows Kits\\10\\Lib\\10.0.14393.0\\km\\x86\\wdm.lib")
#pragma comment(lib, "D:\\Downloads\\EnterpriseWDK_rs1_release_14393_20160715-1616\\Program Files\\Windows Kits\\10\\Lib\\10.0.14393.0\\km\\x86\\ntoskrnl.lib")
but the LNK2019 error persists.
Edit 2:
Just to clarify exactly what I did with the answer: I added the /Gz
option to the compilation step, which replaced the __cdecl compiler anotation on these functions which are missing one (IoCreateDevice, IoDeleteDevice) with __stdcall compiler annotations; __stdcall is necssary for Win32 API calls, as per the __stdcall documentation on Microsoft's website.
Adding the /Gz
option produced this output:
Finished searching libraries
Finished pass 1
Unused libraries:
D:\Downloads\EnterpriseWDK_rs1_release_14393_20160715-1616\Program Files\Windows Kits\10\Lib\10.0.14393.0\km\x86\ntoskrnl.lib
Discarded _IoCreateDevice@28 from wdm.lib(ntoskrnl.exe)
Discarded _IoDeleteDevice@4 from wdm.lib(ntoskrnl.exe)
Starting pass 2
filter.obj
fwpkclnt.lib(fwpkclnt.sys)
fwpkclnt.lib(fwpkclnt.sys)
fwpkclnt.lib(fwpkclnt.sys)
fwpkclnt.lib(fwpkclnt.sys)
fwpkclnt.lib(fwpkclnt.sys)
wdm.lib(ntoskrnl.exe)
wdm.lib(ntoskrnl.exe)
wdm.lib(ntoskrnl.exe)
wdm.lib(ntoskrnl.exe)
wdm.lib(ntoskrnl.exe)
Finished pass 2
however, the code is properly linked and installs as a driver. Thank you, RbMm.
unfortunatelly IoCreateDevice
and IoDeleteDevice
(and many other kernel api) declared without explicit indication of calling convention.
as example
_IRQL_requires_max_(APC_LEVEL)
NTKERNELAPI
VOID
IoDeleteDevice (
_In_ __drv_freesMem(Mem) PDEVICE_OBJECT DeviceObject
);
when more correct definition will be
_IRQL_requires_max_(APC_LEVEL)
NTKERNELAPI
VOID
NTAPI
IoDeleteDevice (
_In_ __drv_freesMem(Mem) PDEVICE_OBJECT DeviceObject
);
because no explicit NTAPI
(__stdcall
) on api definition, implicit, is used. and CL by default use /Gd
(__cdecl
) when you need use __stdcall
(/Gz
) as default.
how i view this ?
LNK2019: unresolved external symbol __imp__IoDeleteDevice
say for self. this is __cdecl
decoration (in x86). if you use __stdacll
the same symbol will be decorated as __imp__IoDeleteDevice@4
.
and no __imp__IoDeleteDevice
and __imp__IoCreateDevice
symbols in ntoskrnl.lib. as result this lib is unused and you have 2 unresolved symbols. after you add /Gz
option to CL
command line - linker already will be search for __imp__IoDeleteDevice@28
and __imp__IoCreateDevice@4
, found it in ntoskrnl.lib and all will be ok, in this point how minimum