头图

最近在开发的过程中,遇到了一些编译问题,有跨平台编译的问题,有动态编译不兼容的问题。经过一番折腾,最后完美解决了,故记录一下这些问题的解决方案。

首先要普及一些基础概念:

Rust 编译的时候会用到两种链接方式:静态链接和动态链接。Rust 一般用静态链接,就是把所有需要的东西都打包到一个文件里,这样程序就能独立运行了。

但是对于一些系统自带的库(比如 Linux 的 libc),Rust 还是会用动态链接。这样做有个小问题:如果你的程序用了动态链接,那它运行的时候就需要系统上有对应的库文件,而且版本还得对得上。要是系统上没有这个库,或者版本不对,程序就跑不起来。

别着急,接下来我们会介绍几个解决办法,告诉你哪些方法更好用。

问题一

在本地 linux 环境中,编译的 rust 程序上传部署到 linux 服务器后,运行出错

可能会看到类似version 'GLIBC_2.33' not found (required by /path/to/your/program)的提示。
这个提示的意思就是编译的时候依赖了服务器上版本比较低或是不兼容的glibc库,从而导致了在服务器上找不到正确的glibc,于是就出错了。

解决方法也并不难:

1. 升级服务器上的 glibc(风险比较高,不推荐)

因为服务器上的很多软件都是依赖glibc的,所以升级glibc,极大可能影响到服务器其他应用的运行,所以不推荐这个方案,此方案仅仅作为一个备用方案。

对于 Debian/Ubuntu 的系统:

sudo apt-get update
sudo apt-get install libc6

验证 glibc 版本:

ldd --version

确保输出的 glibc 版本能对的上编译机器上的版本,这样就没什么问题了。

2. 使用静态编译

静态编译就是把 Rust 程序打包成「自带全家桶」的独立可执行文件,避免在服务器上被 glibc 版本坑到:

步骤一:

1.配置 Cargo 使用静态链接:

编辑或创建 .cargo/config.toml 文件,添加以下内容:

[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "target-feature=+crt-static"]

2.编译程序:

运行以下命令进行静态编译:

cargo build --release

注意:并非所有 Linux 发行版的 glibc 都支持完全静态链接,某些功能可能需要动态链接。

但要注意几个大坑!

1.不是所有 Linux 系统都支持这种操作

glibc 这货有时候会耍小性子,比如处理域名解析之类的功能时,可能还是得动态链接。这就导致即使你静态编译了,在极少数情况下还是可能翻车。

2.终极保底方案:换 musl 编译

如果发现用上面的方法生成的程序还是依赖 glibc,可以试试改用 musl(一个更轻量的 C 库),它能真正做到纯静态:

# 先安装 musl 工具链
rustup target add x86_64-unknown-linux-musl
# 编译时指定 musl
cargo build --release --target x86_64-unknown-linux-musl

这样生成的程序在 target/x86_64-unknown-linux-musl/release/ 里,通常兼容性更强。

验证是否成功

编译后用这个命令检查依赖:

ldd 你的程序名

如果输出显示 not a dynamic executable,恭喜你!这程序已经是完全体了,不依赖系统库了!

然而,静态编译也带来了一个问题:文件体积可能会显著增加。原本小巧的程序(例如 5MB)在静态编译后可能会膨胀到 20MB 甚至更大,尤其是当使用了复杂库(如 OpenSSL)时,这种现象更为明显。别担心,这里有一些方法可以帮助你“瘦身”:

第一式:移除调试符号

调试符号文件包含代码定位信息,虽然方便调试但显著增加体积。建议正式发布时移除。

操作方式:

1.手动移除(通用方法)

strip 目标程序名称

2.自动移除(Rust专属)在 Cargo.toml 添加:

[profile.release]
strip = true# 需 Rust 1.59+ 版本

效果:通常可减少 30%-50% 体积

第二式:编译优化配置

通过编译器优化策略实现深度瘦身

配置方案(Cargo.toml):

[profile.release]
opt-level = "z"# 最高级别体积优化
lto = true# 全局链接优化(显著增加编译时间)
codegen-units = 1# 提升优化密度
panic = "abort"# 禁用栈展开信息(注意:影响错误处理)

注意事项:

建议搭配 RUSTFLAGS="-C target-cpu=native" 提升性能
编译时间可能延长 2-3 倍

第三式:二进制压缩(进阶方案)

使用 UPX 进行可执行文件压缩

操作流程:

1.安装工具

# Debian/Ubuntu
sudo apt install upx
# macOS
brew install upx
# CentOS/RHEL
sudo yum install upx

2.执行压缩

upx --best 目标程序名称

风险提示:

  1. 可能触发杀毒软件误报
  2. 首次启动增加 100-200ms 解压时间
  3. 不推荐用于高频调用的命令行工具

问题二

在rust中使用openssl依赖的时候,会有一大堆的错误场景

1.找不到 OpenSSL 库或头文件

错误信息示例:

“openssl/ssl.h: No such file or directory”
“Could not find OpenSSL installation”

原因:系统上未安装 OpenSSL 的开发包或头文件;或者环境变量(如 OPENSSL_DIR、OPENSSL_LIB_DIR)未正确配置,导致构建脚本无法定位到 OpenSSL 的位置。

2.版本不兼容问题

错误信息示例:
构建失败时提示 OpenSSL 版本不匹配,或者找不到期望的符号(如 undefined reference to 'CRYPTO_num_locks'

原因: 系统中安装的 OpenSSL 版本(例如 1.1 与 3.0)与 openssl-sys 或其它依赖库要求的版本不一致,导致编译或链接时符号不匹配。

对于上面的问题,解决方法也很简单,如果能替换成rustls的话,就优先替换成rustls,因为有测试表明,rustls 的性能表现比openssl更加优越,一般的依赖都可以设置一些feature来改用rustls,如:

tokio-tungstenite = { version = "0.25.0", features = ["rustls"] }

但是如果实在不能更换的话,只能通过编译源码的方式来实现静态编译,可以通过依赖的

vendored的features将第三方依赖的代码直接包含在项目代码仓库中,如

libsqlite3-sys = { version = "0.30.1", features = ["bundled-sqlcipher-vendored-openssl"] }

这会启用openssl-sysvendored特性,自动下载并编译OpenSSL源码。

安装构建工具
确保系统已安装编译OpenSSL所需的工具:

Linux (Ubuntu/Debian):

sudo apt-get update
sudo apt-get install build-essential perl pkg-config

macOS:

brew install make perl

Windows:

安装Strawberry Perl(确保perlPATH中)。

安装NASM并添加到PATH

安装完perl,就可以成功的编译并运行了。

常见问题解决

编译错误(可能是因为缺少工具,如makeperl)。

Windows环境问题:确保Perl和NASM路径正确,且无空格或特殊字符。

最后

在 Rust 的跨平台开发中,动态链接的兼容性问题与第三方库的依赖管理是常见的挑战。通过本文的实践,我们可以总结出以下关键经验:

1.静态编译为王
优先使用 musl 目标进行全静态编译,消除对系统库(如 glibc)的依赖。虽然这会增加应用的体积,但是可以通过 strip 调试符号、优化编译配置(如 opt-level = "z")或者用 UPX 来减少体积膨胀的影响。

2.依赖库的灵活替换
优先选择纯 Rust 实现的库(如 rustls 替代 openssl),避免 C 绑定带来的跨平台问题。大多数依赖库已经支持通过 features 切换具体的实现(例如 tokio-tungstenite rustls 特性)。
对于没得替换的 C 库(如 OpenSSL),建议启用 vendored 特性(如 openssl-sys = { features = ["vendored"] })实现源码级静态编译,避免依赖系统版本。

此文章内容由云梦量化科技Rust开发工程师Rust炼金士创作投稿。


云梦量化科技
1 声望0 粉丝