为何我的Rust Hello-World可执行文件依赖libgcc_s?
问题
在Devuan Excalibur系统(类似无systemd的Debian Trixie,rustc版本1.84)中构建以下Rust程序:
fn main() { println!("Hello, world!\n"); }
使用cargo build --release编译后,执行ldd查看依赖得到:
$ ldd target/release/hello linux-vdso.so.1 (0x00007fff356f5000) libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f0b073c5000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0b071cf000) /lib64/ld-linux-x86-64.so.2 (0x00007f0b0749c000)
而用GCC 14.2(gcc -O3)编译的C版Hello-World程序:
#include <stdio.h> int main() { printf("Hello world\n"); return 0; }
其ldd结果无libgcc_s依赖:
$ ldd hello linux-vdso.so.1 (0x00007fff1d573000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8f1649f000) /lib64/ld-linux-x86-64.so.2 (0x00007f8f166e5000)
请问Rust编译器在可执行文件中加入了什么内容导致该额外依赖,原因是什么?
解答
Rust程序依赖libgcc_s.so.1,核心原因是Rust的标准库或编译器生成的代码引入了GCC运行时支持逻辑,而这些逻辑在简单C程序中不会触发,具体细节如下:
- 栈展开机制的默认启用:Rust默认用栈展开处理panic,这套逻辑依赖
libgcc_s中的_Unwind_*系列函数(如_Unwind_RaiseException)。哪怕是看似简单的println!,其内部的错误处理路径也会包含栈展开相关代码。而GCC编译C程序时,默认不生成栈展开代码(除非用-fexceptions启用C++异常),简单printf调用不会触发这类依赖。 - LLVM后端的代码生成选择:Rust基于LLVM编译,LLVM在生成某些代码时会选择调用
libgcc_s的辅助函数,比如整数溢出检查、特殊浮点数运算处理等。而GCC编译简单C代码时,会直接生成对应机器码,无需依赖外部库实现这些逻辑。 - 标准库的默认链接策略:Rust标准库默认会链接
libgcc_s以支持跨平台的异常处理和通用运行时特性,哪怕程序没有显式用到这些功能。而GCC只有在代码用到需要libgcc_s的特性(如异常、特定数学函数)时才会链接它。
如果想消除该依赖,可以尝试两种方式:
- 用
-C panic=abort编译选项禁用栈展开,改用直接终止程序的panic处理:cargo build --release -C panic=abort - 通过
-C link-args=-static-libgcc静态链接libgcc_s,避免动态依赖
内容的提问来源于Stack Exchange,提问作者einpoklum
相关产品推荐
相关产品推荐

