Rust 静态编译可执行文件指南
- Rust简介
- Rust默认运行时环境
- 使用MUSL进行静态编译
- 使用预置好的Docker容器进行MUSL编译
Rust简介
Rust是一门强调安全、并发、高效的系统编程语言。无 GC 实现内存安全机制、无数据竞争的并发机制、无运行时开销的抽象机制,是 Rust 独特的优越特性。它声称解决了传统 C 语言和 C++ 语言几十年来饱受责难的内存安全问题,同时还保持了很高的运行效率、很深的底层控制、很广的应用范围,在系统编程领域具有强劲的竞争力和广阔的应用前景。
Rust默认运行时环境
Linux 下 rust 默认使用 gcc 作为链接器,编译后的文件在运行时需要glibc 运行库和其他的一些库。
这就导致在某个Linux版本下编译的执行文件,无法在另一个Linux版本上顺利运行。而且,如果你的程序还使用了OpenSSL动态库,那这样的问题会更加突出。
下面,我们做个最简单的实验。
- 用Cargo创建一个可执行项目。
- 编译这个项目
- 用ldd命令查看编译出来的执行文件依赖了哪些动态链接库
$ cargo -V
cargo 1.31.0 (339d9f9c8 2018-11-16)
$ cargo new --bin hello
$ cd hello
$ cargo build
$ ldd target/debug/hello
linux-vdso.so.1 (0x00007ffdc47e4000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f6db611a000)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f6db5f12000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f6db5cf3000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f6db5adb000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6db56ea000)
/lib64/ld-linux-x86-64.so.2 (0x00007f6db6587000)
可以看到这个执行文件依赖了多个.so的动态链接库。而且这种依赖是基于绝对路径的。一旦运行时环境下没有这些动态库文件,那程序执行的结果就只有一个:报错!
那么,Rust能否像Golang那样编译成独立的静态可执行文件呢?答案是可以的,这需要使用MUSL 静态库。
使用MUSL进行静态编译
使用MUSL编译,首先需要安装musl环境。命令如下:
$ rustup target add x86_64-unknown-linux-musl
$ rustup target add x86_64-unknown-linux-musl --toolchain=nightly
然后,我们编译前面创建的hello工程。
$ cd hello
$ cargo build --release --target=x86_64-unknown-linux-musl
$ ldd target/x86_64-unknown-linux-musl/release/hello
not a dynamic executable
可以看到新的可执行文件已经不再依赖任何动态链接库。我们可以将这个文件放到任何一个Linux操作系统里运行了。
当然,这只是一个最简单的例子。实际工作中,我们可能会遇到更复杂的场景。如:依赖了OpenSSL库。在这样的场景里,我们还需要做很多配置才能获得我们想要的静态编译文件。这里就不再详细介绍了。
笔者想介绍的是如何避免各种繁复的配置,尽可能快捷的进行MUSL编译。就是下面章节的内容。
使用预置好的Docker容器进行MUSL编译
为解决使用MUSL编译配置繁琐的问题,国外的开发者贡献了一个预置好的容器。用这个容器来进行MUSL编译会非常方便快捷。
项目地址是:https://gitlab.com/rust_musl_docker/image
我们直接用这个容器来编译我们前面创建的hello工程。然后依然用ldd来查看编译好的可执行文件。
$ cd hello
$ docker run -it --rm \
> -v $PWD:/workdir \
> -v ~/.cargo/git:/root/.cargo/git \
> -v ~/.cargo/registry:/root/.cargo/registry \
> registry.gitlab.com/rust_musl_docker/image:stable-latest \
> cargo build --release -vv --target=x86_64-unknown-linux-musl
$ ldd target/x86_64-unknown-linux-musl/release/hello
not a dynamic executable
这个容器镜像里已经配置了对OpenSSL库的静态编译。笔者亲测可用。更详细的内容,读者可以去看项目里的注释,已经非常详尽了。