Search code examples
c++cudalinkernvcccusolver

Multiple definition errors of cuSparse functions while linking CUDA files in an executable


My project consists of five CUDA files: main.cu jacobian_kernel.cu hermite_kernel.cu cuSolver_LU.cpp Utilities.cu, the last of which is adapted from this GitHub repo, together with its Utilities.h header file; the three headers are args.h linear_solver.h Utilities.h.

nvcc compiles them succesfully, but while building the trbdf2 executable it yells at me multiple definitions errors like:

nvcc -Xcompiler -fopenmp -o obj/trbdf2  obj/jacobian_kernel.o  obj/hermite_kernel.o  obj/utils.o  obj/cusolver_lu.o  obj/main.o -I/usr/local/cuda-8.0/targets/x86_64-linux/include -I../include -I/usr/local/cuda-8.0/samples/common/inc/ -L/usr/local/cuda-8.0/targets/x86_64-linux/lib -lgomp -lcublas -lcudart -lcusolver -lcusparse
obj/hermite_kernel.o: In function `vec_norminf(int, double const*)':
tmpxft_00006336_00000000-4_hermite_kernel.cudafe1.cpp:(.text+0xba1): multiple definition of `vec_norminf(int, double const*)'
obj/jacobian_kernel.o:tmpxft_00006312_00000000-4_jacobian_kernel.cudafe1.cpp:(.text+0xba1): first defined here
obj/hermite_kernel.o: In function `mat_norminf(int, int, double const*, int)':
tmpxft_00006336_00000000-4_hermite_kernel.cudafe1.cpp:(.text+0xc20): multiple definition of `mat_norminf(int, int, double const*, int)'
obj/jacobian_kernel.o:tmpxft_00006312_00000000-4_jacobian_kernel.cudafe1.cpp:(.text+0xc20): first defined here
...
obj/main.o: In function `second()':
tmpxft_00006385_00000000-4_main.cudafe1.cpp:(.text+0xf32): multiple definition of `second()'
obj/jacobian_kernel.o:tmpxft_00006312_00000000-4_jacobian_kernel.cudafe1.cpp:(.text+0xf32): first defined here
collect2: error: ld returned 1 exit status
Makefile:17: recipe for target 'obj/trbdf2' failed
make: *** [obj/trbdf2] Error 1

Now, I'm quite sure that I'm including multiple times the header helper_cusolver.h which is provided with the CUDA Toolkit and which defines the functions vec_norminf, mat_norminf, and the like. I couldn't guess how to rewrite my headers, which begin as follows:

args.h:

#if !defined(ARGS_H_)
#define ARGS_H_

#include <stdio.h> 
#include <stdlib.h> 
#include <iostream>
#include <math.h>
#include <assert.h>

#include <cuda.h>
#include <cuda_runtime.h>
#include <helper_cuda.h>

#include "Utilities.h"
...
#endif

linear_solver.h:

#ifndef LINEAR_SOLVER_H_
#define LINEAR_SOLVER_H_

#include <cublas_v2.h>
#include <cusparse_v2.h>
#include <cusolverDn.h>

#include <helper_cusolver.h>
...
#endif

Utilities.h:

#ifndef UTILITIES_CUH
#define UTILITIES_CUH

#include "linear_solver.h"
...
#endif

Moreover, the dependencies are:

jacobian_kernel.cu, hermite_kernel.cu -> args.h
cuSolver_LU.cpp -> args.h, linear_solver.h, Utilities.h
main.cu -> args.h, linear_solver.h, Utilities.h
Utilities.cu -> linear_solver.cu

In addition, the Utilities.cu initial directives are a bit different from those at the beginning of the other cuda files, but it is a recent addition to my project, and I got the same error before adding it; anyway, here it is:

#include "cuda_runtime.h"
#include <cuda.h>

#if defined(__CUDACC__) && (CUDA_VERSION >= 7000)
#include <cusolverDn.h>
#endif

#include <cublas_v2.h>

#include "Utilities.h"

Long story short, the issue appears to be that the helper_cusolver.h header is being included more than once, though I've even put some header guard within the linear_solver.h header's first lines; I've tried the #pragma once directive, which is supported by nvcc, and I even checked the guard on the helper_cusolver.

I'm a beginner in C/C++ and I really don't know how to follow from here. I tried to uncomment most of the (apparently multiple) #include directives, once at a time but I keep getting the same error.

Let me know if I should include other piece of info.

EDIT In addition, when I wrap the CUDA and/or cuSolver functions with the cudaCheckErrors of the samples or the GitHub's cusolveSafeCall in Utilities.cu, they appear not to be defined:

cuSolver_LU.cpp: In function ‘void linearSolverLU(cusolverDnHandle_t, int, const double*, int, const double*, double*)’:
cuSolver_LU.cpp:28:51: error: cannot convert ‘cudaError_t {aka cudaError}’ to ‘cusolverStatus_t’ for argument ‘1’ to ‘void cusolveSafeCall(cusolverStatus_t)’
     cusolveSafeCall(cudaMalloc(&info, sizeof(int)));

though the header Utilities.h is correctly included. I thought this could be useful info in order to find the incorrect directive.


Solution

  • It seems like I need to clean up #include directives. As pointed out by Robert Crovella I removed helper_cusolver.h from linear_solver.h and instead I've put it in cuSolver_LU.cpp, as it is the only file that needs it during compilation, and probably including it in more files caused multiple definition errors; now cuSolver_LU.cpp begins like this:

    #include "args.h"
    #include "linear_solver.h"
    #include "Utilities.h"
    #include <helper_cusolver.h>
    
    extern "C" void cusolveSafeCall(cusolverStatus_t err);
    
    ...
    

    Moreover, by declaring cusolveSafeCall() I managed as well to solve definition errors on wrapper functions, which I pointed out in the edit note. Thanks!