简体   繁体   中英

CUDA *.obj files are not processed by Visual Studio linker

I used CMake 3.13 in order to create a Visual Studio (VS 2017) solution with a single project "cudatest". The project contains two files:

main.cpp
kernel.cu

In order to incorporate support for CUDA I used the script provided by Nvidia, ie FindCUDA.cmake , as opposed to using latest CMake support for CUDA-based applications (I am not using this latest support because it does not allow me to do certain things, so I need to resort to FindCUDA).

CMake successfully generates a project that contains the two aforementioned files. Without going into great detail, file main.cpp contains a declaration of a function:

cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size);

You might recognize it as a function that is created by default when creating a new project in Visual Studio using CUDA Runtime type of project (NVIDIA CUDA integration for Visual Studio).

Whereas file kernel.cu contains the definition of the said function:

__global__ void addKernel(int *c, const int *a, const int *b)
{
    int i = threadIdx.x;
    c[i] = a[i] + b[i];
}

cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size)
{
    int *dev_a = 0;
    int *dev_b = 0;
    int *dev_c = 0;
...
    addKernel<<<1, size>>>(dev_c, dev_a, dev_b);
...
}

Both files are compiled successfully, and the error is fired by the linker:

Error   LNK2019 unresolved external symbol "enum cudaError __cdecl addWithCuda(int *,int const *,int const *,unsigned int)" ...

It should be noted that *.obj file for kernel.cu is generated successfully by NVCC.

However the *.obj files are placed in different directories, which makes me think that this could be the problem, and also happens to reveal my lack of understanding of where visual studio linker looks for *.obj files in order to resolve symbols.

main.obj file ends up in build\\cudatest.dir\\Debug , where build is the folder containing the generated solution.

cudatest_generated_kernel.cu.obj file ends up in build\\CMakeFiles\\cudatest.dir\\Debug

The configuration of output file paths is setup by FindCUDA.cmake script.

I tried placing cudatest_generated_kernel.cu.obj into the same folder as main.obj , but that didn't do anything.

Setting Linker's 'Show Progress' property to /VERBOSE revealed that the linker doesn't even look for cudatest_generated_kernel.cu.obj in order to try and find respective symbols.

=====================

The question is:

Given that I compile a CUDA kernel into *.obj file using NVCC, and a *.cpp file into its own *.obj file using CL.exe, how can I tell the linker to examine cuda kernel *.obj file?

Please let me know If I should expand the question to make it clearer, ie specify NVCC compiler options I used, provide full code listing, etc. Any hints and pointers in the right direction would be greatly appreciated!

EDIT: Per user @talonmies suggestion ( __cdecl addWithCuda might suggest linker is looking for C linkage), I explicitly marked addWithCuda function with extern "C" in both main.cpp and kernel.cu :

extern "C" cudaError_t addWithCuda(int *c, const int *a, const int *b, unsigned int size)

Now the error is as follows:

Error   LNK2019 unresolved external symbol addWithCuda referenced in function main  cudatest    D:\projects\vs2017\TestCUDA_CMake\build\main.obj        

So it turned out that unless visual studio project has CUDA integration(right click project -> properties -> CUDA C++/CUDA linker properties present), CUDA kernels compiled into *.obj files are not automatically considered by the host linker.

Firstly, I added the path to the compiled CUDA *.obj files in Linker->Additional Library Directories.

Secondly, I specified the name of the object file in Linker->Input->Additional Dependencies.

That solved the problem. It should be noted that initially I used a combination of native cmake commands and FindCUDA.cmake macros:

add_executable(cudatest main.cpp kernel.cu)
CUDA_WRAP_SRCS(cudatest OBJ generated_files kernel.cu ${cmake_options} OPTIONS ${options} )

The CUDA_WRAP_SRCS macro adds custom build steps that make Visual Studio invoke nvcc instead of CL to compile CUDA kernels. This macro is also invoked from within CUDA_ADD_EXECUTABLE and CUDA_ADD_LIBRARY macros, which also specify all the paths to generated *.obj files to be already included in the generated visual studio project, thus saving the extra effort I described above.

Furthermore, CUDA_ADD_EXECUTABLE and CUDA_ADD_LIBRARY also facilitates generation of projects for kernels that have relocatable device code enabled, which means that there must be a pre-link step that will device link all the CUDA *.obj files with relocatable device code in them, into intermediate CUDA *.obj file that can be consumed by the host linker (otherwise host linker would not be able to handle *.obj files that contain relocatable device code). The aforementioned macros will also include dependencies to this intermediate generated CUDA *.obj file.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM