CUDA Link Error Lib to Dll

Im trying to create a framework to be able to use CUDA code in several game engine. To be able to communicate with all the wanted engines the frame consists of two main parts. A Lib and a DLL. The Lib contains all the functionality (CUDA and regular C++) and the DLL functions as a bridge to call functions from the Lib.

Now, everything was running oke untill I’ve included a .cu file. When I’m building I’m getting the following error:

Error   3   error LNK2019: unresolved external symbol _Add2And7 referenced in function "public: void __thiscall PerceptorHost::UpdatePerceptorHost(void)" (?UpdatePerceptorHost@PerceptorHost@@QAEXXZ)  D:\_Professional\DAE_Research_Projects\PWO_ePerceptor\Source\build\src\Native_ePerceptor_Dll\Native_ePerceptor_Lib.lib(PerceptorHost.obj) Native_ePerceptor_Dll

When I look at the buildlogs the Lib is building fine (Native_ePerceptor_Lib.lib). It’s the DLL that fails.

Last but not least, the project has to be created using CMake (to be able to tackle different setups). Below you can find snippets of the .cu, Lib .cpp en the Dll .cpp. I looked across the interwebs to be able to find a solution but most of the solutions are regarding issues in one project and not regarding libraries. I’m pretty desperate at the moment so I hope some of you can point out what the issue, and hopefully, what a possible solution is. One more thing, I’m pretty sure all the necessary libraries are being included (cudart, cuda).

Kernel.cu

#include <cuda\cuda_runtime.h>

__global__ void add(int a, int b, int *c) 
{
    *c += a + b;
    printf("%i + %i = %i \n", a, b, *c);
}

extern "C" void Add2And7(int *c)
{
    int *dev_c;

    //Allocate GPU memory
    cudaMalloc((void**)&dev_c, sizeof(int));

    add <<<1, 1>>>(2, 7, dev_c);

    //Copy GPU to CPU
    cudaMemcpy(c, dev_c, sizeof(int),
        cudaMemcpyDeviceToHost);

    //printf("number is %u \n", &c);

    //Free allocated GPU memory
    cudaFree(dev_c);
}

PerceptorHost.cpp

//Forward declaration
extern "C" void Add2And7(int *c);

void PerceptorHost::UpdatePerceptorHost()
{
    if (!g_bIsBooted)
        return;

    if (!m_bTestKernel)
    {
        int output = 0;
        Add2And7(&output);
        printf("2 + 7 = %i \n", output);
        m_bTestKernel = true;
    }
}

DLL.cpp

extern "C" NATIVEDLL_API void __cdecl UpdatePerceptorHost()
{
    PERCEPTORHOST->UpdatePerceptorHost(); //Update the PerceptorHost and all it's managers
}

CMAKE

########################################################################
# Add all source files to variables
# CPU Source Files
FILE(GLOB SRCS *.cpp)
FILE(GLOB HDRS *.h )
FILE(GLOB CUDA_HDRS 
        ${CMAKE_SOURCE_DIR}/include/cuda/*.h)
FILE(GLOB CALIBRATION_FILES 
        RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
        ${CMAKE_CURRENT_SOURCE_DIR}/Calibration/*.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/Calibration/*.h)     
FILE(GLOB CORE_FILES 
        RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
        ${CMAKE_CURRENT_SOURCE_DIR}/Core/*.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/Core/*.h)   
FILE(GLOB DATAPROVIDER_FILES 
        RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
        ${CMAKE_CURRENT_SOURCE_DIR}/Data/DataProvider/*.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/Data/DataProvider/*.h)
FILE(GLOB RESOURCEMANAGER_FILES 
        RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
        ${CMAKE_CURRENT_SOURCE_DIR}/Data/ResourceManager/*.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/Data/ResourceManager/*.h)       
FILE(GLOB DEBUG_FILES 
        RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
        ${CMAKE_CURRENT_SOURCE_DIR}/Debug/*.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/Debug/*.h)  
FILE(GLOB EXCEPTION_FILES 
        RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
        ${CMAKE_CURRENT_SOURCE_DIR}/Exceptions/*.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/Exceptions/*.h)
FILE(GLOB HELPERS_FILES 
        RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
        ${CMAKE_CURRENT_SOURCE_DIR}/Helpers/*.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/Helpers/*.h)        
FILE(GLOB VIDEOWRITER_FILES 
        RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
        ${CMAKE_CURRENT_SOURCE_DIR}/VideoWriter/*.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/VideoWriter/*.h)
# GPU Source Files      
FILE(GLOB GPU_HELPERS_FILES
        ${CMAKE_CURRENT_SOURCE_DIR}/Helpers/*.cuh
        ${CMAKE_CURRENT_SOURCE_DIR}/Helpers/*.cu)       

########################################################################
# Group all source files
SOURCE_GROUP("Calibration" FILES ${CALIBRATION_FILES})
SOURCE_GROUP("Core" FILES ${CORE_FILES})
SOURCE_GROUP("DataProvider" FILES ${DATAPROVIDER_FILES})
SOURCE_GROUP("ResourceManager" FILES ${RESOURCEMANAGER_FILES})
SOURCE_GROUP("Debug" FILES ${DEBUG_FILES})
SOURCE_GROUP("Exceptions" FILES ${EXCEPTION_FILES})
SOURCE_GROUP("Helpers" FILES ${HELPERS_FILES})
SOURCE_GROUP("VideoWriter" FILES ${VIDEOWRITER_FILES})

########################################################################
# Set this part as static lib
IF (D_ENABLE_LIBRARY_CUDA)
CUDA_ADD_LIBRARY(Native_ePerceptor_Lib
    ${CUDA_HDRS}
    ${CALIBRATION_FILES}
    ${CORE_FILES}
    ${DATAPROVIDER_FILES}
    ${RESOURCEMANAGER_FILES}
    ${DEBUG_FILES}
    ${EXCEPTION_FILES}
    ${HELPERS_FILES}
    ${VIDEOWRITER_FILES}
    ${SRCS}
    ${HDRS}
    ${GPU_HELPERS_FILES}
    )
ELSE()
    ADD_LIBRARY(Native_ePerceptor_Lib
    ${CUDA_HDRS}
    ${CALIBRATION_FILES}
    ${CORE_FILES}
    ${DATAPROVIDER_FILES}
    ${RESOURCEMANAGER_FILES}
    ${DEBUG_FILES}
    ${EXCEPTION_FILES}
    ${HELPERS_FILES}
    ${VIDEOWRITER_FILES}
    ${SRCS}
    ${HDRS}
    )
ENDIF()

########################################################################
# Add preprocessor defines
IF (D_ENABLE_LIBRARY_CUDA)
    CMAKE_MINIMUM_REQUIRED(VERSION 2.8.11 FATAL_ERROR)
    FIND_PACKAGE(CUDA REQUIRED)
    INCLUDE_DIRECTORIES(
        ${CUDA_INCLUDE_DIRS}
    )
    set(CUDA_ENABLED ON)
    add_definitions(-DCUDA_ENABLED=1)
ENDIF (D_ENABLE_LIBRARY_CUDA)
IF (D_ENABLE_LIBRARY_OpenCV)
    add_definitions(-DOPENCV_ENABLED=1)
ENDIF (D_ENABLE_LIBRARY_OpenCV)
IF (D_ENABLE_LIBRARY_Glut)
    add_definitions(-DGLUT_ENABLED=1)
ENDIF (D_ENABLE_LIBRARY_Glut)

########################################################################
# Include Directories   
TARGET_LINK_LIBRARIES(Native_ePerceptor_Lib
    ${CUDA_LIBRARIES})

TARGET_LINK_LIBRARIES(Native_ePerceptor_Lib
    ${OpenCV_LIBRARIES})

TARGET_LINK_LIBRARIES(Native_ePerceptor_Lib
    ${Tobii_LIBRARIES})

TARGET_LINK_LIBRARIES(Native_ePerceptor_Lib
    ${Glut_LIBRARIES})

“Now, everything was running oke untill I’ve included a .cu file.”

which file - Kernel.cu?

i suspect that if you paste (the content of) kernel.cu into PerceptorHost.cpp, it would work, which in turn should point to the problem and solution

in postulating, i doubt whether kernel.cu is a library of PerceptorHost.cpp
and i do not know enough about cmake to know how to enable a form of separate compilation - linking in compiled source files

Hi! I’m not able to paste the content of kernel.cu into PerceptorHost.cpp. I guess it isn’t allowed to use global in a cpp file. Or am I mistaken?

I’ve tried to reproduce the issue in a smaller setup (still using the setup - Lib → Dll → CMD for testing). You can download it here: http://we.tl/DITxuAm3Dd

Also the tools and computer setup: Windows 8.1, CMakeGUI 3.2.2, CUDA 7.0, NVCC

Thx in advance everyone!

“I’m not able to paste the content of kernel.cu into PerceptorHost.cpp. I guess it isn’t allowed to use global in a cpp file. Or am I mistaken?”

i would like to think that you should be able to, if you change the extension of perceptorhost.cpp from .cpp to .cu, such that the compiler ‘recognizes’ it correctly

"if you change the extension of perceptorhost.cpp from .cpp to .cu, such that the compiler ‘recognizes’ it correctly "

Then I get more errors :) All relating to the same linking issue (LNK2019). Also, it would mean calling .cu code from .cpp wouldn’t be allowed, which isn’t the case. Thanks for the tip anyway :)

I changed two thing in the project i’ve shared. I’ve changed the ‘Build Customization’ to use CUDA 7.0(.targets, .props), and for the TestKernel.cu I’ve changed it’s type to CUDA C/C++ in the Class Property Page. So, my next question would be, is it possible to set these settings from CMake and how? :) I’m pretty new at CMake myself and it is pretty important :)

Still not pretty sure what caused the issue (don’t have must knowledge on build rules).

I’ve also posted a question on SO with the CMakeLists.txt files
http://stackoverflow.com/questions/30326747/cuda-cmake-settings

section 7.3. NVCC Options for Separate Compilation p39

of the cuda compiler driver document has an exemplary flow diagram of the compile/ link process

i think it is perhaps a diagram to take note of and an excellent reference to use when contemplating compiler/ linker settings

Information that is useful to me (or it doesn’t look that way :) )
I tried to do the two new steps manually in my older project (just added the structure to the Lib part of the project) and I’m getting the following error message from CUDA 7.0.targets:

Error	1	error MSB3191: Unable to create directory "SourceLib.dir\Debug\C:\Users\Matthieu\Desktop\Project\src\SourceLib\Helpers\". The given path's format is not supported.	C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V120\BuildCustomizations\CUDA 7.0.targets	412	9	SourceLib

I’ve searched for a solution but I can’t find any. Everyone is talking about this part “SourceLib.dir\Debug</b>C:\Users…”, but I don’t find an useful solution.

If I don’t set the .targets file it won’t build. I’ll get the older error. Any help?

a> SourceLib.dir\Debug\C:\Users\Matthieu\Desktop\Project\src\SourceLib\Helpers\

is likely not a proper path specification

b> SourceLib.dir\Debug\

may be; and

c> C:\Users\Matthieu\Desktop\Project\src\SourceLib\Helpers\

likely is

b) seems to be a compiler build path; particularly the debug part suggests that it is a debug build

c) seems to be a workspace directory or path

a) seems to be a a superposition - product - of b) and c)

seemingly, somehow you are managing to order a path specification contention - specifying paths in such a way that, when the directives are followed, it creates a conflict and contention, and you end with a path specification of the form of a), instead of b) and c)

Oke, I was able to resolve the issue without the previous mentioned solution (changing Build Customization + setting file to CUDA C/C++)

The problem was that indeed the symbols of my header file that defined my CUDA function was not being exported. So with my project structure (LIB → DLL → CMD) this was an issue. I was able to using it in a LIB → CMD structure. I resolved the issue by defining the header function as follow:

extern "C" __declspec(dllexport) void Add2And7(int *);

instead of:

extern "C" void Add2And7(int *);

I don’t know if there is anything wrong with doing this (it works :))? If I check out MSDN I think it fine using the export keyword.

[i][b]Using _declspec(dllexport)

The dllexport and dllimport storage-class attributes are Microsoft-specific extensions to the C and C++ languages. You can use them to export and import functions, data, and objects to or from a DLL.[/b][/i]