在Rust FFI中使用C/Fortran库pfapack时遇未定义符号问题
在Rust中调用Fortran编写的pfapack库时出现未定义符号错误
我想在Rust里使用pfapack库(该库由Fortran编写,提供C接口),于是制作了包含该库源码的Rust crate(pfapack-sys),供其他Rust项目调用,但测试项目编译时出现未定义符号错误。
操作步骤
- 创建包含pfapack源码的新crate
- 编写pfapack-sys的
build.rs:
use std::process::Command; fn main() { // 这个Makefile会调用自定义根Makefile,仅执行c_interface/和fortran/下的两个Makefile Command::new("make").output() .expect("Failed to make"); println!("cargo:rustc-link-search=c_interface"); println!("cargo:rustc-link-search=fortran"); println!("cargo:rustc-link-lib=static=pfapack"); println!("cargo:rustc-link-lib=static=cpfapack"); println!("cargo:rustc-link-lib=gfortran"); }
- 测试项目的
build.rs:
fn main() { println!("cargo:rustc-link-lib=lapack"); println!("cargo:rustc-link-lib=blas"); }
参考信息
- pfapack原始编译示例:
gcc -O3 -I c_interface/ foo.c -o foo.out c_interface/libcpfapack.a fortran/libpfapack.a -lm -lblas -llapack -lgfortran
- 绑定生成命令(参考lapack-sys脚本):
#!/bin/bash set -eux bindgen --allowlist-function='^.*_$' --use-core pfapack.h \ | sed -e 's/::std::os::raw:://g' \ | sed -e '/__darwin_size_t/d' \ > pfapack.rs rustfmt pfapack.rs
- pfapack-sys的
Cargo.toml配置:
[package] name = "pfapack-sys" version = "0.1.0" edition = "2021" links = "pfapack" [dependencies] libc = "0.2" [dependencies.num-complex] version = "0.4" default-features = false [lib] name = "pfapack_sys" [build-dependencies] cc = "1.0.79"
编译错误信息
= note: /usr/bin/ld: /home/dumbo/Documents/test_pfapack/target/debug/deps/test_pfapack-d08bc25fe63b6ef8.ka29pyd46xgunxk.rcgu.o: in function `pfapack_sys::dskpfa': /home/dumbo/Documents/pfapack-sys/src/pfapack-bind.rs:249: undefined reference to `dskpfa' collect2: error: ld returned 1 exit status = note: some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified = note: use the `-l` flag to specify native libraries to link = note: use the `cargo:rustc-link-lib` directive to specify the native libraries to link with Cargo error: could not compile `test_pfapack` due to previous error
问题排查与解决思路
1. 检查符号名的名称修饰问题
Fortran编译后的符号名通常会有修饰规则(比如后缀加下划线、全大写等),bindgen生成的绑定可能匹配错了符号名:
- 用
nm c_interface/libcpfapack.a | grep dskpfa查看库中实际导出的符号,比如可能是dskpfa_(带下划线)。 - 如果符号名带下划线,要么修改bindgen命令匹配正确符号,要么在生成的绑定代码中给对应函数添加
#[link_name = "dskpfa_"]属性指定正确链接名。
2. 调整链接库的顺序
链接器对库的顺序敏感,依赖库要放在被依赖库的后面。参照原始编译命令,修改pfapack-sys的build.rs链接指令顺序:
let root_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); println!("cargo:rustc-link-search={}/c_interface", root_dir); println!("cargo:rustc-link-search={}/fortran", root_dir); // 按依赖顺序排列:cpfapack依赖pfapack,pfapack依赖blas、lapack等 println!("cargo:rustc-link-lib=static=cpfapack"); println!("cargo:rustc-link-lib=static=pfapack"); println!("cargo:rustc-link-lib=blas"); println!("cargo:rustc-link-lib=lapack"); println!("cargo:rustc-link-lib=m"); println!("cargo:rustc-link-lib=gfortran");
3. 确保库文件路径正确
用CARGO_MANIFEST_DIR获取crate根目录的绝对路径,避免相对路径在不同编译场景下出错,如上一步代码所示。
4. 验证Makefile编译结果
手动执行make命令,确认c_interface/libcpfapack.a和fortran/libpfapack.a正常生成,没有编译报错。
5. 移除测试项目的build.rs
测试项目无需自己链接lapack和blas,Cargo会自动传递pfapack-sys的链接指令,移除测试项目的build.rs可避免重复链接或顺序冲突。
内容的提问来源于stack exchange,提问作者Dumbo
相关产品推荐
相关产品推荐

