CUDA共享库链接:对cudaRegisterLinkedBinary的未定义引用。

14

目标:

  1. 创建一个包含我的CUDA内核的共享库,该库包含一个无需CUDA的封装/头文件。
  2. 为共享库创建一个名为test的可执行文件。

问题:

  1. 共享库MYLIB.so似乎编译良好(没有问题)。
  2. 链接错误:

./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

(同上)


1
CUDA运行时库没有静态版本,因此您不应该期望在对象或共享库中静态包含运行时库符号,因此您最后的两个编辑/添加在这里是无关紧要的。 - talonmies
啊,好的,我不知道那个,说得好。 - cmo
5
从CUDA Toolkit 5.5开始,CUDA Runtime库也有了静态版本。 - RoBiK
2个回答

13

这里是一个例子,按照您指示的步骤创建linux共享对象:

  1. 创建一个包含我的CUDA内核的共享库,并带有无需CUDA的包装器/头文件。
  2. 为该共享库创建一个测试可执行文件。

首先是共享库。其构建命令如下:

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手册的相关部分,并查看示例

编辑:我在cuda 5.5 RC下测试了这个程序,但最终应用程序链接步骤会抱怨找不到cudart库(warning: libcudart.so.5.5., needed by ./libtest.so, not found)。然而,下面的相对简单的修改(例如Makefile)应该适用于cuda 5.0或cuda 5.5。

Makefile:

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

3
问题仍然存在。跟随您的示例,一切都可以顺利编译,直到最后一步创建测试可执行文件时,会抛出“__cudaRegisterLinkedBinary_39_tmpxft ...”错误,如之前所述。 - cmo
我不确定问题可能是什么。对我来说,它似乎完美地工作。您是否按照我的步骤并且完全使用我的文件?您是否使用cuda 5.0? - Robert Crovella
@MatthewParks 我和你一样遇到了 __cudaRegisterLinkedBinary_39_tmpxft 的问题,你解决了吗? - Farzad Salimi Jazi

4
其他答案对我没用(可能是因为我正在使用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++。


这在我的情况下也解决了问题。 - zeno

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接