链接Rust静态库与嵌入式C程序时出现`memcmp`重定义错误
我之前在嵌入式Rust+C混合开发场景里碰到过一模一样的multiple definition链接错误,核心原因是Rust的no_std环境默认会内置实现一批标准C库的基础函数(比如memcpy、memset这类内存操作函数),而你的numworks/epsilon项目已经通过自定义libc实现了这些函数,两者符号重复导致冲突。
要解决这个问题,我们的目标是让Rust静态库完全依赖项目的自定义libc,放弃自身内置的标准函数实现,具体步骤如下:
1. 调整Rust项目的Cargo配置
首先修改你的Cargo.toml,添加compiler-builtins依赖并启用no-builtins特性——这个特性会告诉Rust编译器不要编译自己的内置标准函数,转而使用外部提供的实现:
[package] name = "rust_stm32_lib" version = "0.1.0" edition = "2021" [dependencies] compiler_builtins = { version = "0.1", features = ["no-builtins"] }
2. 更新Rust库的源代码
修改你的lib.rs,注意两个关键调整:
- 移除
#![no_builtins]标记:这个标记会强制Rust拒绝使用外部提供的内置函数,和我们的需求完全相反,必须删掉 - 修正
panic_fmt的签名:旧的无参数签名不符合Rust lang item的要求,会导致编译警告甚至错误
修改后的lib.rs如下:
#![crate_type = "staticlib"] #![feature(lang_items)] #![no_std] #[no_mangle] pub extern "C" fn hello_world(a: i32, b: i32) -> i32 { a + b } #[lang = "eh_personality"] extern fn eh_personality() {} #[lang = "panic_fmt"] fn panic_fmt(_args: ::core::fmt::Arguments, _file: &'static str, _line: u32) -> ! { loop {} }
3. 编译Rust库时指定依赖自定义libc
编译Rust库时,通过RUSTFLAGS传递链接参数,让编译器知道要从你的自定义libc中解析符号:
RUSTFLAGS="-C link-arg=-L/path/to/epsilon/custom/libc -C link-arg=-lc" cargo build --release --target=thumbv7em-none-eabihf
把/path/to/epsilon/custom/libc替换成你的numworks/epsilon项目中自定义libc所在的实际路径,-lc表示链接自定义的libc库(如果你的自定义libc库文件名不是libc.a,需要改成对应的参数,比如-lmycustomc)。
4. 调整C项目Makefile的链接顺序
链接器是按顺序解析符号的,所以要确保自定义libc的链接命令放在Rust静态库之前。这样当遇到冲突符号时,链接器会优先使用自定义libc中的实现,而不是Rust库中的。
比如你的Makefile链接部分应该改成这样:
# 先链接自定义libc LD_FLAGS += -L/path/to/epsilon/custom/libc -lc # 再链接Rust静态库 LD_FLAGS += -L/path/to/your/rust/target/lib -lrust
验证是否解决问题
编译完成后,你可以用nm工具重新检查librust.a,那些之前显示为全局定义(T)的冲突符号,现在应该变成未定义(U)状态——这就说明Rust库已经在等待从自定义libc中解析这些符号了,不会再和项目中的实现冲突。
内容的提问来源于stack exchange,提问作者Ferdia McKeogh

