cuda共享库链接:未定义引用cudaRegisterLinkedBinary

时间:2013-06-24 15:13:13

标签: c++ cuda makefile shared-libraries nvcc

目标:

  1. 创建一个包含我的CUDA内核的共享库,该内核具有无CUDA的包装器/标头。
  2. 为共享库创建test可执行文件。
  3. 问题

    1. 共享库MYLIB.so似乎编译得很好。 (没问题)。
    2. 链接错误:
    3. ./libMYLIB.so: undefined reference to __cudaRegisterLinkedBinary_39_tmpxft_000018cf_00000000_6_MYLIB_cpp1_ii_74c599a1

      简化的makefile:

      libMYlib.so :  MYLIB.o
          g++  -shared  -Wl,-soname,libMYLIB.so  -o libMYLIB.so    MYLIB.o  -L/the/cuda/lib/dir  -lcudart
      
      
      MYLIB.o : MYLIB.cu   MYLIB.h
          nvcc  -m64   -arch=sm_20 -dc  -Xcompiler '-fPIC'  MYLIB.cu  -o  MYLIB.o  -L/the/cuda/lib/dir  -lcudart
      
      
      test : test.cpp  libMYlib.so
              g++   test.cpp  -o test  -L.  -ldl -Wl,-rpath,.   -lMYLIB  -L/the/cuda/lib/dir  -lcudart
      

      确实

      nm libMYLIB.so表示所有 CUDA api函数都是“未定义的符号”:

               U __cudaRegisterFunction
               U __cudaRegisterLinkedBinary_39_tmpxft_0000598c_00000000_6_CUPA_cpp1_ii_74c599a1
               U cudaEventRecord
               U cudaFree
               U cudaGetDevice
               U cudaGetDeviceProperties
               U cudaGetErrorString
               U cudaLaunch
               U cudaMalloc
               U cudaMemcpy
      

      所以CUDA不知何故没有链接到共享库MYLIB.so 我错过了什么?


      CUDA甚至没有以某种方式链接到目标文件:

      nm MYLIB.o

               U __cudaRegisterFunction
               U __cudaRegisterLinkedBinary_39_tmpxft_0000598c_00000000_6_CUPA_cpp1_ii_74c599a1
               U cudaEventRecord
               U cudaFree
               U cudaGetDevice
               U cudaGetDeviceProperties
               U cudaGetErrorString
               U cudaLaunch
               U cudaMalloc
               U cudaMemcpy
      

      (与上述相同)

3 个答案:

答案 0 :(得分:11)

这是一个示例linux共享对象的创建示例:

  1. 创建一个包含我的CUDA内核的共享库 CUDA-free wrapper / header。
  2. 为共享库创建测试可执行文件。
  3. 首先是共享库。对此的构建命令如下:

    nvcc -arch=sm_20 -Xcompiler '-fPIC' -dc test1.cu test2.cu
    nvcc -arch=sm_20 -Xcompiler '-fPIC' -dlink test1.o test2.o -o link.o
    g++ -shared -o test.so test1.o test2.o link.o -L/usr/local/cuda/lib64 -lcudart
    

    您的makefile中可能缺少上面的第二步,但我没有分析您的makefile是否存在任何其他问题。

    现在,对于测试可执行文件,构建命令如下:

    g++ -c main.cpp
    g++ -o testmain main.o test.so
    

    要运行它,只需执行testmain可执行文件,但请确保test.so上的LD_LIBRARY_PATH库。

    这些是我用于测试目的的文件:

    test1.h:

    int my_test_func1();
    

    test1.cu:

    #include <stdio.h>
    #include "test1.h"
    
    #define DSIZE 1024
    #define DVAL 10
    #define nTPB 256
    
    #define cudaCheckErrors(msg) \
        do { \
            cudaError_t __err = cudaGetLastError(); \
            if (__err != cudaSuccess) { \
                fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \
                    msg, cudaGetErrorString(__err), \
                    __FILE__, __LINE__); \
                fprintf(stderr, "*** FAILED - ABORTING\n"); \
                exit(1); \
            } \
        } while (0)
    
    __global__ void my_kernel1(int *data){
      int idx = threadIdx.x + (blockDim.x *blockIdx.x);
      if (idx < DSIZE) data[idx] =+ DVAL;
    }
    
    int my_test_func1(){
    
      int *d_data, *h_data;
      h_data = (int *) malloc(DSIZE * sizeof(int));
      if (h_data == 0) {printf("malloc fail\n"); exit(1);}
      cudaMalloc((void **)&d_data, DSIZE * sizeof(int));
      cudaCheckErrors("cudaMalloc fail");
      for (int i = 0; i < DSIZE; i++) h_data[i] = 0;
      cudaMemcpy(d_data, h_data, DSIZE * sizeof(int), cudaMemcpyHostToDevice);
      cudaCheckErrors("cudaMemcpy fail");
      my_kernel1<<<((DSIZE+nTPB-1)/nTPB), nTPB>>>(d_data);
      cudaDeviceSynchronize();
      cudaCheckErrors("kernel");
      cudaMemcpy(h_data, d_data, DSIZE * sizeof(int), cudaMemcpyDeviceToHost);
      cudaCheckErrors("cudaMemcpy 2");
      for (int i = 0; i < DSIZE; i++)
        if (h_data[i] != DVAL) {printf("Results check failed at offset %d, data was: %d, should be %d\n", i, h_data[i], DVAL); exit(1);}
      printf("Results check 1 passed!\n");
      return 0;
    }
    

    test2.h:

    int my_test_func2();
    

    test2.cu:

    #include <stdio.h>
    #include "test2.h"
    
    #define DSIZE 1024
    #define DVAL 20
    #define nTPB 256
    
    #define cudaCheckErrors(msg) \
        do { \
            cudaError_t __err = cudaGetLastError(); \
            if (__err != cudaSuccess) { \
                fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \
                    msg, cudaGetErrorString(__err), \
                    __FILE__, __LINE__); \
                fprintf(stderr, "*** FAILED - ABORTING\n"); \
                exit(1); \
            } \
        } while (0)
    
    __global__ void my_kernel2(int *data){
      int idx = threadIdx.x + (blockDim.x *blockIdx.x);
      if (idx < DSIZE) data[idx] =+ DVAL;
    }
    
    int my_test_func2(){
    
      int *d_data, *h_data;
      h_data = (int *) malloc(DSIZE * sizeof(int));
      if (h_data == 0) {printf("malloc fail\n"); exit(1);}
      cudaMalloc((void **)&d_data, DSIZE * sizeof(int));
      cudaCheckErrors("cudaMalloc fail");
      for (int i = 0; i < DSIZE; i++) h_data[i] = 0;
      cudaMemcpy(d_data, h_data, DSIZE * sizeof(int), cudaMemcpyHostToDevice);
      cudaCheckErrors("cudaMemcpy fail");
      my_kernel2<<<((DSIZE+nTPB-1)/nTPB), nTPB>>>(d_data);
      cudaDeviceSynchronize();
      cudaCheckErrors("kernel");
      cudaMemcpy(h_data, d_data, DSIZE * sizeof(int), cudaMemcpyDeviceToHost);
      cudaCheckErrors("cudaMemcpy 2");
      for (int i = 0; i < DSIZE; i++)
        if (h_data[i] != DVAL) {printf("Results check failed at offset %d, data was: %d, should be %d\n", i, h_data[i], DVAL); exit(1);}
      printf("Results check 2 passed!\n");
      return 0;
    }
    

    main.cpp中:

    #include <stdio.h>
    
    #include "test1.h"
    #include "test2.h"
    
    int main(){
    
      my_test_func1();
      my_test_func2();
      return 0;
    }
    

    当我根据给出的命令编译时,运行./testmain我得到:

    $ ./testmain
    Results check 1 passed!
    Results check 2 passed!
    

    请注意,如果您愿意,可以生成libtest.so而不是test.so,然后您可以为测试可执行文件使用修改后的构建序列:

    g++ -c main.cpp
    g++ -o testmain main.o -L. -ltest
    

    我认为它没有任何区别,但它可能是更熟悉的语法。

    我确信有多种方法可以实现这一目标。这只是一个例子。 您可能还希望查看nvcc manual的相关部分,并查看examples

    编辑:我在cuda 5.5 RC下进行了测试,最后的应用程序链接步骤抱怨找不到cudart lib(warning: libcudart.so.5.5., needed by ./libtest.so, not found)。但是,以下相对简单的修改(例如Makefile)应该在cuda 5.0或cuda 5.5下工作。

    生成文件:

    testmain : main.cpp  libtest.so
            g++ -c main.cpp
            g++ -o testmain  -L.  -ldl -Wl,-rpath,.   -ltest -L/usr/local/cuda/lib64 -lcudart main.o
    
    libtest.so : link.o
            g++  -shared -Wl,-soname,libtest.so -o libtest.so    test1.o test2.o link.o  -L/usr/local/cuda/lib64  -lcudart
    
    link.o : test1.cu test2.cu   test1.h test2.h
            nvcc  -m64   -arch=sm_20 -dc  -Xcompiler '-fPIC'  test1.cu test2.cu
            nvcc  -m64   -arch=sm_20 -Xcompiler '-fPIC' -dlink test1.o test2.o -o link.o
    
    clean :
            rm -f testmain test1.o test2.o link.o libtest.so main.o
    

答案 1 :(得分:0)

您是否尝试过显式禁用可重定位设备代码?即-rdc=false?我用undefined reference to __cudaRegisterLinkedBinaryWhatever获得此-rdc=true,当我删除它时它就消失了。虽然我不足以解释究竟究竟发生了什么。

答案 2 :(得分:0)

其他答案对我不起作用(可能是因为我使用的是cuda 10)。 对我有用的解决方案是将cuda文件编译为:

nvcc -dc -o cuda_file.o cuda_file.cu

比将c ++文件编译为:

g++ -c -o cpp_file.o cpp_file.cpp

最后使用nvcc链接所有内容:

nvcc -o my_prog cpp_file.o cuda_file.o -lcudart -lcuda -L<other stuff>

请勿直接使用此代码。但是,解决该错误的方法的核心是在最后的链接步骤中使用nvcc而不是g ++。