gcc编译C/Fortran混合程序 链接静态/动态库报undefined reference错误
C/Fortran混合编程链接静态/动态库报未定义引用错误
参考相关技术帖子,使用gcc编译C、Fortran混合语言程序时,直接链接生成的目标文件流程正常,但替换为静态库或共享库参与链接时,链接器返回如下错误:
/usr/bin/ld: /tmp/ccdqJ6hY.o: in function `MAIN__': hello.f90:(.text+0x76): undefined reference to `fun' collect2: error: ld returned 1 exit status
相关文件内容
C库源文件 fun.c
#include <stdio.h> void fun(void) { printf("Hello from C function.\n"); }
Fortran源文件 hello.f90
program hello print *, "Hello from Fortran program." call fun() end
构建脚本
#! /bin/sh # gcc -c fun.c # 生成C库目标文件 ar r libfun.a fun.o # 生成静态C库 gcc -shared -o libfun.so fun.o # 生成动态链接C库 # gfortran -fno-underscoring -o hello1 fun.o hello.f90 # 该命令可正常编译运行 # gfortran -fno-underscoring -o hello2 -L. -Wl,-rpath=`pwd` -lfun hello.f90 # 该命令链接失败 # gfortran -fno-underscoring -o hello3 -L. libfun.a hello.f90 # 该命令链接失败
已完成排查
通过nm工具检查生成的库文件,fun符号存在且命名符合预期:
$ nm libfun.a fun.o: 0000000000000000 T fun
当前运行环境为Debian GNU/Linux 11 (bullseye),使用gcc版本为gcc (Debian 10.2.1-6) 10.2.1 20210110,初步怀疑本地gcc配置异常但无法定位问题。
故障原因与解决方案
该故障和本地gcc配置无关,由GNU ld链接器的默认库解析顺序规则导致:
- ld从左到右按顺序扫描命令行传入的所有输入文件
- 扫描到普通目标文件(.o)时,会把文件内所有符号纳入链接表,不管后续是否用到
- 扫描到静态库、或
-l指定的动态库时,只会提取当前已经扫描到的未定义符号对应的实现,不会主动查找库内符号去匹配后续才会出现的未定义引用 - 扫描完一个文件就不会再回头重新解析,除非显式指定循环扫描参数
构建命令中-lfun和libfun.a都放在了hello.f90前面:扫描到C库的时候,还没处理Fortran代码生成的临时目标文件,链接器不知道存在fun的未定义引用,不会把fun的实现纳入链接结果;等后续扫描到Fortran目标文件发现缺少fun符号时,链接器已经跳过了C库,自然抛出未定义引用错误。
直接链接fun.o能成功,就是因为目标文件的所有符号都会被无条件纳入链接,不存在库的按需提取逻辑。
修复方法
把所有库相关参数移动到源文件/目标文件参数的最右侧即可,修改后的命令如下:
# 修复动态库链接 gfortran -fno-underscoring -o hello2 -L. -Wl,-rpath=`pwd` hello.f90 -lfun # 修复静态库链接 gfortran -fno-underscoring -o hello3 hello.f90 -L. libfun.a
如果遇到多库循环依赖的复杂场景,可以用-Wl,--start-group和-Wl,--end-group把存在依赖的一组库包裹起来,强制链接器反复扫描组内文件直到所有符号解析完成,当前简单场景不需要该参数。
内容的提问来源于stack exchange,提问作者CKE
相关产品推荐
相关产品推荐

