cwindowsx86drivercl

LNK2019 unresolved external symbol in function defined in static library


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.


Solution

  • 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