kmdf / callback function / c++ compiler = unresolved external symbol link errors

So, I've been writing a KMDF driver, and everythings been rockin-n-rollin'. I decided to use a simple C++ circular buffer class for queuing data coming up from the device (which I've alreay successfully debugged/used in a previous WDM driver). I've read the 'C++ for Kernel Mode Drivers: Pros and Cons', and I think the risk will be small for this simple class. I'm not doing anything fancy with this class, and it's not inheriting from another, nor serving as a base class. I guess you could consider it a 'Plain Old Data' class.

To compile the source file, I changed it's file extension to .cpp (as well as all of the other source files in my project). The class builds fine. However, there are two callbacks that are registered in DriverEntry within the bus.cpp file, which aren't getting linked properly.

Basically, I have the following cleanup callbacks defined in the bus.h header file:

#ifdef __cplusplus
extern "C" {
#endif

EVT_WDF_DEVICE_CONTEXT_CLEANUP BusDeviceCleanup;
EVT_WDF_OBJECT_CONTEXT_CLEANUP BusDriverCleanup;

#ifdef __cplusplus
}
#endif

And in the source file (bus.cpp), they are defined as follows:

VOID BusDeviceCleanup(IN WDFDEVICE Device)
{
...
}

VOID BusDriverCleanup(IN WDFDEVICE Device)
{
...
}

And in DriverEntry() and BusEvtDeviceAdd() within bus.cpp, they are registered as follows:

NTSTATUS DriverEntry(...)
{
...
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.EvtCleanupCallback = BusDeviceCleanup;
...
}

NTSTATUS BusEvtDeviceAdd(...)
{
...
fdoAttributes.EvtCleanupCallback = BusDriverCleanup;
...
}

and I also use the ALLOC_PRAGMA macro in the bus.cpp file:

#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, TsiiBusEvtDeviceAdd)
...
#pragma alloc_text (PAGE, TsiiBusDeviceCleanup)
#pragma alloc_text (PAGE, TsiiBusDriverCleanup)
#endif

With this configuration, I'm getting a linking error as shown below:

1>tsii_bus.obj : error LNK2019: unresolved external symbol xxxxx@4 referenced in function _DriverEntry@8
1>tsii_bus.obj : error LNK2019: unresolved external symbol xxxxx@4 referenced in function _BusEvtDeviceAdd@8

So, it's like the C++ compiler is mangling the names for BusDeviceCleanup and BusDriverCleanup when I register them as callbacks, and not properly linking them to the
EVT_WDF_DEVICE_CONTEXT_CLEANUP and EVT_WDF_OBJECT_CONTEXT_CLEANUP function callbacks defined in bus.h.

What am I doing wrong? How can I fix this? I have attempted to add 'extern "C"' in the appropriate header files to ensure that the filenames aren't mangled, but I must be missing something...

I appreciate any help here! :slight_smile: I would like to use the c++ compiler to build my src code (if nothing else but to have a bit better checking), and especially use my queue class.

Thanks!

Jason

===============
Here's my sources file:

TARGETNAME=ts_ii_bus
TARGETTYPE=DRIVER

KMDF_VERSION_MAJOR=1

MSC_WARNING_LEVEL=/W3

INF_NAME=tsii_bus

NTTARGETFILES=$(OBJ_PATH)$(O)$(INF_NAME).inf

MISCFILES=$(NTTARGETFILES)

PRECOMPILED_INCLUDE=precomp.h

PRECOMPILED_CXX=1

INCLUDES = $(INCLUDES);....\inc;..\shared;C:\svn\TWC\HE\tsiihd\driver\include;

SOURCES= objects.cpp \
data_handlers.cpp \
i2o.cpp \
flash.cpp \
bus.cpp \
queue.cpp \

=================================
Here is a little more detailed link log if it helps:

1>BUILD: Linking for c:\svn\twc\he\tsiihd\driver\bus_driver_project\sys directory
1>_NT_TARGET_VERSION SET TO WINXP
1>Linking Executable - objchk_wxp_x86\i386\bus.sys
1>Microsoft (R) Incremental Linker Version 8.00.50727.278
1>Copyright (C) Microsoft Corporation. All rights reserved.
1>/MERGE:_PAGE=PAGE
1>/MERGE:_TEXT=.text
1>/SECTION:INIT,d
1>/OPT:REF
1>/OPT:ICF
1>/IGNORE:4198,4010,4037,4039,4065,4070,4078,4087,4089,4221
1>/INCREMENTAL:NO
1>/release
1>/NODEFAULTLIB
1>/WX
1>/debug
1>/debugtype:cv,fixup,pdata
1>/version:6.0
1>/osversion:6.0
1>/functionpadmin:5
1>/safeseh
1>/pdbcompress
1>/STACK:0x40000,0x1000
1>/driver
1>/base:0x10000
1>/align:0x80 /stub:c:\win_ddk\6001.18002\lib\wxp\stub512.com
1>/subsystem:native,5.01
1>/entry:FxDriverEntry@8
...
1>c:\win_ddk\6001.18002\lib\wxp\i386\BufferOverflowK.lib
1>c:\win_ddk\6001.18002\lib\wxp\i386\ntoskrnl.lib
1>c:\win_ddk\6001.18002\lib\wxp\i386\hal.lib
1>c:\win_ddk\6001.18002\lib\wxp\i386\wmilib.lib
1>C:\Win_DDK\6001.18002\lib\wdf\kmdf\i386\1.7\WdfLdr.lib
1>C:\Win_DDK\6001.18002\lib\wdf\kmdf\i386\1.7\WdfDriverEntry.lib
1>c:\win_ddk\6001.18002\lib\wxp\i386\sehupd.lib
1>bus.obj : error LNK2019: unresolved external symbol xxxxx@4 referenced in function _DriverEntry@8
1>bus.obj : error LNK2019: unresolved external symbol xxxxx@4 referenced in function _BusEvtDeviceAdd@8
1>BUILD: Finish time: Tue Mar 10 11:07:27 2009
1>c:\svn\twc\he\tsiihd\driver\tsii_bus_driver_project\sys\objchk_wxp_x86\i386\ts_ii_bus.sys : fatal error LNK1120: 2 unresolved externals
1>BUILD: Done

Well I don’t see the extern “C” around the functions, so you have a name
mismatch. If you are having this type of problems with C++ really consider
rethinking your choice of the language for the kernel. It can come back
and bite you in many ways only some of which are in the paper you refer to.


Don Burn (MVP, Windows DDK)
Windows Filesystem and Driver Consulting
Website: http://www.windrvr.com
Blog: http://msmvps.com/blogs/WinDrvr

wrote in message news:xxxxx@ntdev…
> So, I’ve been writing a KMDF driver, and everythings been
> rockin-n-rollin’. I decided to use a simple C++ circular buffer class for
> queuing data coming up from the device (which I’ve alreay successfully
> debugged/used in a previous WDM driver). I’ve read the ‘C++ for Kernel
> Mode Drivers: Pros and Cons’, and I think the risk will be small for this
> simple class. I’m not doing anything fancy with this class, and it’s not
> inheriting from another, nor serving as a base class. I guess you could
> consider it a ‘Plain Old Data’ class.
>
> To compile the source file, I changed it’s file extension to .cpp (as well
> as all of the other source files in my project). The class builds fine.
> However, there are two callbacks that are registered in DriverEntry within
> the bus.cpp file, which aren’t getting linked properly.
>
> Basically, I have the following cleanup callbacks defined in the bus.h
> header file:
>
> #ifdef cplusplus
> extern “C” {
> #endif
>
> EVT_WDF_DEVICE_CONTEXT_CLEANUP BusDeviceCleanup;
> EVT_WDF_OBJECT_CONTEXT_CLEANUP BusDriverCleanup;
>
> #ifdef
cplusplus
> }
> #endif
>
> And in the source file (bus.cpp), they are defined as follows:
>
> VOID BusDeviceCleanup(IN WDFDEVICE Device)
> {
> …
> }
>
> VOID BusDriverCleanup(IN WDFDEVICE Device)
> {
> …
> }
>
> And in DriverEntry() and BusEvtDeviceAdd() within bus.cpp, they are
> registered as follows:
>
> NTSTATUS DriverEntry(…)
> {
> …
> WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
> attributes.EvtCleanupCallback = BusDeviceCleanup;
> …
> }
>
> NTSTATUS BusEvtDeviceAdd(…)
> {
> …
> fdoAttributes.EvtCleanupCallback = BusDriverCleanup;
> …
> }
>
> and I also use the ALLOC_PRAGMA macro in the bus.cpp file:
>
> #ifdef ALLOC_PRAGMA
> #pragma alloc_text (INIT, DriverEntry)
> #pragma alloc_text (PAGE, TsiiBusEvtDeviceAdd)
> …
> #pragma alloc_text (PAGE, TsiiBusDeviceCleanup)
> #pragma alloc_text (PAGE, TsiiBusDriverCleanup)
> #endif
>
> With this configuration, I’m getting a linking error as shown below:
>
> 1>tsii_bus.obj : error LNK2019: unresolved external symbol
> xxxxx@4 referenced in function _DriverEntry@8
> 1>tsii_bus.obj : error LNK2019: unresolved external symbol
> xxxxx@4 referenced in function _BusEvtDeviceAdd@8
>
> So, it’s like the C++ compiler is mangling the names for BusDeviceCleanup
> and BusDriverCleanup when I register them as callbacks, and not properly
> linking them to the
> EVT_WDF_DEVICE_CONTEXT_CLEANUP and EVT_WDF_OBJECT_CONTEXT_CLEANUP function
> callbacks defined in bus.h.
>
> What am I doing wrong? How can I fix this? I have attempted to add
> ‘extern “C”’ in the appropriate header files to ensure that the filenames
> aren’t mangled, but I must be missing something…
>
> I appreciate any help here! :slight_smile: I would like to use the c++ compiler to
> build my src code (if nothing else but to have a bit better checking), and
> especially use my queue class.
>
> Thanks!
>
> Jason
>
> ===============
> Here’s my sources file:
> ===============
> TARGETNAME=ts_ii_bus
> TARGETTYPE=DRIVER
>
> KMDF_VERSION_MAJOR=1
>
> MSC_WARNING_LEVEL=/W3
>
> INF_NAME=tsii_bus
>
> NTTARGETFILES=$(OBJ_PATH)$(O)$(INF_NAME).inf
>
> MISCFILES=$(NTTARGETFILES)
>
> PRECOMPILED_INCLUDE=precomp.h
>
> PRECOMPILED_CXX=1
>
> INCLUDES =
> $(INCLUDES);…..\inc;…\shared;C:\svn\TWC\HE\tsiihd\driver\include;
>
> SOURCES= objects.cpp <br>> data_handlers.cpp <br>> i2o.cpp <br>> flash.cpp <br>> bus.cpp <br>> queue.cpp <br>>
>
> =================================
> Here is a little more detailed link log if it helps:
> =================================
>
> 1>BUILD: Linking for c:\svn\twc\he\tsiihd\driver\bus_driver_project\sys
> directory
> 1>_NT_TARGET_VERSION SET TO WINXP
> 1>Linking Executable - objchk_wxp_x86\i386\bus.sys
> 1>Microsoft (R) Incremental Linker Version 8.00.50727.278
> 1>Copyright (C) Microsoft Corporation. All rights reserved.
> 1>/MERGE:_PAGE=PAGE
> 1>/MERGE:_TEXT=.text
> 1>/SECTION:INIT,d
> 1>/OPT:REF
> 1>/OPT:ICF
> 1>/IGNORE:4198,4010,4037,4039,4065,4070,4078,4087,4089,4221
> 1>/INCREMENTAL:NO
> 1>/release
> 1>/NODEFAULTLIB
> 1>/WX
> 1>/debug
> 1>/debugtype:cv,fixup,pdata
> 1>/version:6.0
> 1>/osversion:6.0
> 1>/functionpadmin:5
> 1>/safeseh
> 1>/pdbcompress
> 1>/STACK:0x40000,0x1000
> 1>/driver
> 1>/base:0x10000
> 1>/align:0x80 /stub:c:\win_ddk\6001.18002\lib\wxp\stub512.com
> 1>/subsystem:native,5.01
> 1>/entry:FxDriverEntry@8
> …
> 1>c:\win_ddk\6001.18002\lib\wxp\i386\BufferOverflowK.lib
> 1>c:\win_ddk\6001.18002\lib\wxp\i386\ntoskrnl.lib
> 1>c:\win_ddk\6001.18002\lib\wxp\i386\hal.lib
> 1>c:\win_ddk\6001.18002\lib\wxp\i386\wmilib.lib
> 1>C:\Win_DDK\6001.18002\lib\wdf\kmdf\i386\1.7\WdfLdr.lib
> 1>C:\Win_DDK\6001.18002\lib\wdf\kmdf\i386\1.7\WdfDriverEntry.lib
> 1>c:\win_ddk\6001.18002\lib\wxp\i386\sehupd.lib
> 1>bus.obj : error LNK2019: unresolved external symbol _BusDeviceCleanup@4
> referenced in function _DriverEntry@8
> 1>bus.obj : error LNK2019: unresolved external symbol _BusDriverCleanup@4
> referenced in function _BusEvtDeviceAdd@8
> 1>BUILD: Finish time: Tue Mar 10 11:07:27 2009
> 1>c:\svn\twc\he\tsiihd\driver\tsii_bus_driver_project\sys\objchk_wxp_x86\i386\ts_ii_bus.sys
> : fatal error LNK1120: 2 unresolved externals
> 1>BUILD: Done
>
>
>
> Information from ESET NOD32 Antivirus, version of virus
> signature database 3923 (20090310)

>
> The message was checked by ESET NOD32 Antivirus.
>
> http://www.eset.com
>
>
>

Information from ESET NOD32 Antivirus, version of virus signature database 3923 (20090310)

The message was checked by ESET NOD32 Antivirus.

http://www.eset.com

One thing right off the bat- I’ve got literally dozens of C++ KMDF drivers (and even more if I add all the “proper” ones I’ve added /TP compiler switches to so I could throw in a class declaration any time I felt the urge;->)- the only thing that has to declared as extern “C” is DriverEntry.

Probably just need to drop that off the declaration in bus.h- I’d be more certain if I’d made that sort of error recently…

xxxxx@gmail.com wrote:

To compile the source file, I changed it’s file extension to .cpp (as well as all of the other source files in my project). The class builds fine. However, there are two callbacks that are registered in DriverEntry within the bus.cpp file, which aren’t getting linked properly.

Basically, I have the following cleanup callbacks defined in the bus.h header file:

#ifdef __cplusplus
extern “C” {
#endif

EVT_WDF_DEVICE_CONTEXT_CLEANUP BusDeviceCleanup;
EVT_WDF_OBJECT_CONTEXT_CLEANUP BusDriverCleanup;

#ifdef __cplusplus
}
#endif

And in the source file (bus.cpp), they are defined as follows:

VOID BusDeviceCleanup(IN WDFDEVICE Device)
{

}

VOID BusDriverCleanup(IN WDFDEVICE Device)
{

}

You have two subtle (but related) problems here.

The root of the problem is that all of the “context cleanup” routines in
KMDF are declared as accepting a WDFOBJECT. That is,
EVT_WDF_DEVICE_CONTEXT_CLEANUP is exactly the same as
EVT_WDF_OBJECT_CONTEXT_CLEANUP, and both are prototyped as
VOID xxxx( IN WDFOBJECT Device );

Because of that, your actual function definitions (which accept a
WDFDEVICE) do not match the prototype, and are seen as overloads.
Hence, they get C++ name decorations.

You need to use an ugly cast here:

VOID BusDeviceCleanup( IN WDFOBJECT Object )
{
WDFDEVICE Device = (WDFDEVICE) Object;

VOID BusDriverCleanup( IN WDFOBJECT Object )
{
WDFDRIVER Driver = (WDFDRIVER) Object;

And you didn’t really declare your “BusDriverCleanup” routine as
accepting a WDFDEVICE did you?

and I also use the ALLOC_PRAGMA macro in the bus.cpp file:

#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, TsiiBusEvtDeviceAdd)

#pragma alloc_text (PAGE, TsiiBusDeviceCleanup)
#pragma alloc_text (PAGE, TsiiBusDriverCleanup)
#endif

Hmm, is your function BusDeviceCleanup, or is it TsiiBusDeviceCleanup?
Some confusion here.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Tim,

You win the award!!! changing the function defs to use WDFObject did the trick!

And you didn’t really declare your “BusDriverCleanup” routine as accepting a WDFDEVICE did you?

That’s a bug! :slight_smile: Also, the ALLOC_PRAGMA entries are incorrect (I was changing a few things around to try to get the project to build).

Now it builds! Thank you, Thank you, Thank you for the input guys!

Jason