如何在gprbuild中配置nvcc编译CUDA代码并解决Ada与CUDA代码链接报错问题
解决Ada调用CUDA代码时的链接未定义错误
看起来你已经走对了大部分路——gprbuild的配置思路完全没问题,链接时出现的undefined reference to « inter_add_two »错误,其实是CUDA编译的一个常见坑:nvcc默认会以C++的规则处理代码,对函数名进行名字修饰(name mangling),而Ada里用Convention => C声明的导入函数,期望的是C风格的未修饰函数名,两者不匹配就导致链接器找不到符号了。
下面是具体的修复步骤,帮你搞定这个问题:
1. 给CUDA函数强制加上C链接约定
你需要在inter_add_two函数的声明和定义处,都加上extern "C"修饰,让nvcc编译时保留C风格的原始函数名,不做修饰。
修改后的kernel.cuh
#ifndef __KERNEL_CUH__ #define __KERNEL_CUH__ #include <cuda.h> __global__ void kernel_add_two(unsigned int *a, unsigned int length); // 用extern "C"包裹,确保C链接约定 #ifdef __cplusplus extern "C" { #endif void inter_add_two(unsigned int *a, unsigned int length); #ifdef __cplusplus } #endif #endif // __KERNEL_CUH__
修改后的kernel.cu
#include "kernel.cuh" #include <math.h> #define THREADS_PER_BLOCK (1024) __global__ void kernel_add_two(unsigned int *a, unsigned int length) { unsigned int tid = threadIdx.x + blockIdx.x * blockDim.x; if (tid < length) a[tid] += 2; } // 同样给函数定义加上extern "C",和头文件保持一致 extern "C" void inter_add_two(unsigned int *a, unsigned int length) { unsigned int block_number = ceil(((float)length) / THREADS_PER_BLOCK); unsigned int *d_a; cudaMalloc((void**)&d_a, sizeof(unsigned int) * length); cudaMemcpy(d_a, a, sizeof(unsigned int) * length, cudaMemcpyHostToDevice); kernel_add_two<<<block_number, THREADS_PER_BLOCK>>>(d_a, length); cudaMemcpy(a, d_a, sizeof(unsigned int) * length, cudaMemcpyDeviceToHost); cudaFree(d_a); }
2. 优化gpr项目文件的编译选项
你的gpr文件已经配置了CUDA语言支持,可以再补充一些选项,让编译更稳妥:
project Cuda_Interface is for Languages use ("Ada", "Cuda"); for Source_Dirs use ("src"); for Object_Dir use "obj"; for Exec_Dir use "."; for Main use ("cuda_interface.adb"); for Create_Missing_Dirs use "True"; package Naming is for Body_Suffix("Cuda") use ".cu"; for Spec_Suffix("Cuda") use ".cuh"; end Naming; package Compiler is for Driver("Cuda") use "nvcc"; -- 加上-std=c++11(或你环境支持的C++标准),以及-fPIC确保生成位置无关代码 for Leading_Required_Switches("Cuda") use ("-c", "-std=c++11", "-fPIC"); -- 可选:添加你的GPU架构参数,比如针对RTX 20系列用-arch=sm_75,根据实际情况调整 for Switches("Cuda") use ("-arch=sm_75"); end Compiler; package Linker is for Default_Switches("Ada") use ("-L/usr/local/cuda/lib64", "-lcuda", "-lcudart", "-lm"); end Linker; end Cuda_Interface;
3. 编译验证
现在执行gprbuild -P Cuda_Interface.gpr,应该就能正常编译和链接了。如果还是遇到问题,可以做两个检查:
- 确认
obj目录下生成了kernel.o文件,说明nvcc正确编译了你的CUDA代码 - 用
nm obj/kernel.o | grep inter_add_two查看符号表,应该能看到T inter_add_two(T表示这是一个全局文本段符号),如果显示的是类似_Z13inter_add_twoPjj这种长名字,说明名字修饰还没解决,需要再检查extern "C"的使用。
最后回答你最初的另一个疑问:是否必须先手动用nvcc生成目标文件再链接? 完全不需要!你当前的gpr配置已经让gprbuild自动识别.cu文件并调用nvcc编译了,只要解决名字修饰的问题,就能让整个流程自动完成。
内容的提问来源于stack exchange,提问作者Louis Etienne




