CUDA nvJitLink错误:fatbin缺失正确函数的排查与解决咨询
问题:nvJitLink报未定义引用错误(CMake生成fatbin时)
启用CMake的CUDA_FATBIN_COMPILATION标志编译CUDA应用时,nvJitLink出现以下错误:
error : Undefined reference to '_Z7computefff' in 'ltoPtx' error: nvJitLinkComplete(handle) failed with error 6 error: ERROR 9: finish
但直接执行nvcc -arch=lto_86 -rdc=true -fatbin offline.cu编译代码可正常运行。
CMake生成的编译命令
Building CUDA object CMakeFiles/offlineLib.dir/offline.fatbin /usr/local/cuda/bin/nvcc -forward-unknown-to-host-compiler -std=c++17 "--generate-code=arch=compute_86,code=[compute_86,sm_86]" -MD -MT CMakeFiles/offlineLib.dir/offline.fatbin -MF CMakeFiles/offlineLib.dir/offline.fatbin.d -x cu -fatbin /home/Yehonatans/tmp/jitEx/offline.cu -o CMakeFiles/offlineLib.dir/offline.fatbin
相关代码与配置
CMakeLists.txt
cmake_minimum_required(VERSION 3.29) project(TestJitLto CUDA) set(CMAKE_CUDA_ARCHITECTURES 86) set(CMAKE_VERBOSE_MAKEFILE ON) set(CMAKE_CUDA_STANDARD 17) find_package(CUDAToolkit REQUIRED cudadevrt cudart nvJitLink) message(STATUS "nvcc found at: ${CMAKE_CUDA_COMPILER}") add_executable(TestJitLto online.cu) set_target_properties(TestJitLto PROPERTIES CUDA_SEPARABLE_COMPILATION ON) target_link_libraries(TestJitLto PUBLIC CUDA::nvrtc CUDA::nvJitLink cuda CUDA::cudart) add_library(offlineLib OBJECT offline.cu ) set_property(TARGET offlineLib PROPERTY CUDA_FATBIN_COMPILATION ON)
online.cu
#include <nvrtc.h> #include <cuda.h> #include <nvJitLink.h> #include <nvrtc.h> #include <iostream> #define NUM_THREADS 128 #define NUM_BLOCKS 32 #define NVRTC_SAFE_CALL(x) \ do { \ nvrtcResult result = x; \ if (result != NVRTC_SUCCESS) { \ std::cerr << "\nerror: " #x " failed with error " \ << nvrtcGetErrorString(result) << '\n'; \ exit(1); \ } \ } while(0) #define CUDA_SAFE_CALL(x) \ do { \ CUresult result = x; \ if (result != CUDA_SUCCESS) { \ const char *msg; \ cuGetErrorName(result, &msg); \ std::cerr << "\nerror: " #x " failed with error " \ << msg << '\n'; \ exit(1); \ } \ } while(0) #define NVJITLINK_SAFE_CALL(h,x) \ do { \ nvJitLinkResult result = x; \ if (result != NVJITLINK_SUCCESS) { \ std::cerr << "\nerror: " #x " failed with error " \ << result << '\n'; \ size_t lsize; \ result = nvJitLinkGetErrorLogSize(h, &lsize); \ if (result == NVJITLINK_SUCCESS && lsize > 0) { \ char *log = (char*)malloc(lsize); \ result = nvJitLinkGetErrorLog(h, log); \ if (result == NVJITLINK_SUCCESS) { \ std::cerr << "error: " << log << '\n'; \ free(log); \ } \ } \ exit(1); \ } \ } while(0) const char *lto_saxpy = " \n\ extern __device__ float compute(float a, float x, float y); \n\ \n\ extern \"C\" __global__ \n\ void saxpy(float a, float *x, float *y, float *out, size_t n) \n\ { \n\ size_t tid = blockIdx.x * blockDim.x + threadIdx.x; \n\ if (tid < n) { \n\ out[tid] = compute(a, x[tid], y[tid]); \n\ } \n\ } \n"; int main(int argc, char *argv[]) { size_t numBlocks = 32; size_t numThreads = 128; nvrtcProgram prog; NVRTC_SAFE_CALL( nvrtcCreateProgram(&prog, // prog lto_saxpy, // buffer "lto_saxpy.cu", // name 0, // numHeaders NULL, // headers NULL)); // includeNames const char *opts[] = {"-dlto", "--relocatable-device-code=true"}; nvrtcResult compileResult = nvrtcCompileProgram(prog, // prog 2, // numOptions opts); // options size_t logSize; NVRTC_SAFE_CALL(nvrtcGetProgramLogSize(prog, &logSize)); char *log = new char[logSize]; NVRTC_SAFE_CALL(nvrtcGetProgramLog(prog, log)); std::cout << log << '\n'; delete[] log; if (compileResult != NVRTC_SUCCESS) { exit(1); } size_t LTOIRSize; NVRTC_SAFE_CALL(nvrtcGetLTOIRSize(prog, <OIRSize)); char *LTOIR = new char[LTOIRSize]; NVRTC_SAFE_CALL(nvrtcGetLTOIR(prog, LTOIR)); NVRTC_SAFE_CALL(nvrtcDestroyProgram(&prog)); CUdevice cuDevice; CUcontext context; CUmodule module; CUfunction kernel; CUDA_SAFE_CALL(cuInit(0)); CUDA_SAFE_CALL(cuDeviceGet(&cuDevice, 0)); CUDA_SAFE_CALL(cuCtxCreate(&context, 0, cuDevice)); nvJitLinkHandle handle; int major = 0; int minor = 0; CUDA_SAFE_CALL(cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevice)); CUDA_SAFE_CALL(cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevice)); int arch = major*10 + minor; char smbuf[16]; sprintf(smbuf, "-arch=sm_%d", arch); const char *lopts[] = {"-lto", smbuf}; NVJITLINK_SAFE_CALL(handle, nvJitLinkCreate(&handle, 2, lopts)); NVJITLINK_SAFE_CALL(handle, nvJitLinkAddFile(handle, NVJITLINK_INPUT_FATBIN, "offline.fatbin")); NVJITLINK_SAFE_CALL(handle, nvJitLinkAddData(handle, NVJITLINK_INPUT_LTOIR, (void *)LTOIR, LTOIRSize, "lto_online")); NVJITLINK_SAFE_CALL(handle, nvJitLinkComplete(handle)); size_t cubinSize; NVJITLINK_SAFE_CALL(handle, nvJitLinkGetLinkedCubinSize(handle, &cubinSize)); void *cubin = malloc(cubinSize); NVJITLINK_SAFE_CALL(handle, nvJitLinkGetLinkedCubin(handle, cubin)); NVJITLINK_SAFE_CALL(handle, nvJitLinkDestroy(&handle)); CUDA_SAFE_CALL(cuModuleLoadData(&module, cubin)); CUDA_SAFE_CALL(cuModuleGetFunction(&kernel, module, "saxpy")); size_t n = NUM_THREADS * NUM_BLOCKS; size_t bufferSize = n * sizeof(float); float a = 5.1f; float *hX = new float[n], *hY = new float[n], *hOut = new float[n]; for (size_t i = 0; i < n; ++i) { hX[i] = static_cast<float>(i); hY[i] = static_cast<float>(i * 2); } CUdeviceptr dX, dY, dOut; CUDA_SAFE_CALL(cuMemAlloc(&dX, bufferSize)); CUDA_SAFE_CALL(cuMemAlloc(&dY, bufferSize)); CUDA_SAFE_CALL(cuMemAlloc(&dOut, bufferSize)); CUDA_SAFE_CALL(cuMemcpyHtoD(dX, hX, bufferSize)); CUDA_SAFE_CALL(cuMemcpyHtoD(dY, hY, bufferSize)); void *args[] = { &a, &dX, &dY, &dOut, &n }; CUDA_SAFE_CALL( cuLaunchKernel(kernel, NUM_BLOCKS, 1, 1, // grid dim NUM_THREADS, 1, 1, // block dim 0, NULL, // shared mem and stream args, 0)); // arguments CUDA_SAFE_CALL(cuCtxSynchronize()); CUDA_SAFE_CALL(cuMemcpyDtoH(hOut, dOut, bufferSize)); for (size_t i = 0; i < n; ++i) { std::cout << a << " * " << hX[i] << " + " << hY[i] << " = " << hOut[i] << '\n'; } CUDA_SAFE_CALL(cuMemFree(dX)); CUDA_SAFE_CALL(cuMemFree(dY)); CUDA_SAFE_CALL(cuMemFree(dOut)); CUDA_SAFE_CALL(cuModuleUnload(module)); CUDA_SAFE_CALL(cuCtxDestroy(context)); free(cubin); delete[] hX; delete[] hY; delete[] hOut; delete[] LTOIR; return 0; }
offline.cu
__device__ float compute(float a, float x, float y) { return a * x + y; }
问题原因
对比手动nvcc命令和CMake生成的命令,核心差异在于:
- 手动命令添加了
-rdc=true(启用可重定位设备代码),CMake生成的命令缺少该选项,导致设备代码符号无法被跨模块引用。 - 手动命令指定
-arch=lto_86生成LTO IR,而CMake使用默认的compute_86,sm_86生成普通PTX和二进制代码,生成的fatbin中不包含LTO IR,无法与在线生成的LTO IR进行链接。
解决方案
修改CMake配置,给offlineLib目标添加必要的编译选项:
add_library(offlineLib OBJECT offline.cu ) set_property(TARGET offlineLib PROPERTY CUDA_FATBIN_COMPILATION ON) # 启用可重定位设备代码 set_property(TARGET offlineLib PROPERTY CUDA_SEPARABLE_COMPILATION ON) # 指定生成LTO IR的架构 target_compile_options(offlineLib PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:-arch=lto_86>)
修改后,CMake生成的nvcc命令会包含-rdc=true和-arch=lto_86,生成的fatbin将包含LTO IR,nvJitLink即可正确解析compute函数的符号,完成链接。
内容的提问来源于stack exchange,提问作者Yehonatan
相关产品推荐
相关产品推荐

