如何编译一个发行版无关的Linux程序:以rsync为例
Ianus Inferus
2023-03-05
背景
我们经常遇到一个Linux系统上编译的程序复制到另外一个系统上无法使用的问题,甚至同样的发行版,只是系统版本不同也可能遇到这种问题。通常的情况是同一个发行版上编译的文件,老版本上编译的程序可以复制到新版本上运行,而反过来不行。
笔者之前已经写过一篇Linux上的glibc版本兼容性问题,其中提到了三种主要的方法:
- 使用musl进行静态编译
- 使用较老的系统(CentOS 6.9)制作sysroot,然后使用clang、libc++、libc++abi进行交叉编译
- 使用glibc_version_header
最近,笔者遇到需要一台Ubuntu-20.04和一个Synology NAS之间通过rsync传输数据的问题。rsync传输文件时,如果目标文件夹不存在,会报错。在rsync 3.2.3上增加了--mkpath
选项,可用于自动创建目标文件夹,解决了这个问题。根据笔者的尝试,(在使用rsync协议时)需要服务器和客户端的rsync均升级到此版本或更新,才能正常使用--mkpath
选项,否则会报错。
于是,笔者决定使用musl对rsync进行静态编译,并取得了成功,因此有了这篇文章。
正常编译
首先我们找到rsync的源代码,下载下来。
观察源代码,可以发现代码基本上使用C编写而成,不存在C++代码,只有少量python代码,但主要用于文档。
编译系统使用的较为传统的autoconf,依赖项可参考编译说明。
笔者在Ubuntu-20.04上,在源代码文件夹下使用下列命令即成功编译。
sudo apt install libxxhash-dev
./configure --disable-md2man
make
使用musl编译
首先下载musl libc toolchain中的x86_64-linux-musl-native.tgz
解压到任意位置(如/root/x86_64-linux-musl-native
)。
安装clang。
然后在源代码文件夹下执行
./configure --disable-md2man --disable-lz4 --disable-zstd --disable-xxhash --disable-openssl CC=clang CFLAGS='--sysroot=/root/x86_64-linux-musl-native -target x86_64-linux-musl --prefix=/root/x86_64-linux-musl-native/usr/lib/gcc/x86_64-linux-musl/11.2.1 -L/root/x86_64-linux-musl-native/usr/lib/gcc/x86_64-linux-musl/11.2.1' LDFLAGS='-static -Wl,-static -fuse-ld=lld --sysroot=/root/x86_64-linux-musl-native -nostdlib++ -target x86_64-linux-musl --prefix=/root/x86_64-linux-musl-native/usr/lib/gcc/x86_64-linux-musl/11.2.1 -L/root/x86_64-linux-musl-native/usr/lib/gcc/x86_64-linux-musl/11.2.1'
make
其中musl工具链文件夹名称和版本号需要根据实际情况进行修改。
需要注意的是--disable-lz4 --disable-zstd --disable-xxhash --disable-openssl
这些选项关掉了第三方依赖,使得部分功能无法使用,但同时也使得我们不用再单独编译这些第三方库。
执行一些检查
$ nm -D rsync
nm: rsync: no symbols
# ldd rsync
not a dynamic executable
可以发现我们已经成功对rsync进行了静态编译。
展望
musl不是万能的,如在静态链接的时候不支持使用动态链接库(dlopen
),此外功能上也不能覆盖所有的glibc功能,如locale支持。
最近两三年,在llvm中有一个llvm-libc
一直在开发。目标大致上应该是想要正确划分C标准库和操作系统接口,就像Windows上的msvcrt和kernel32.dll。做法上是首先将libc的功能分成单独的几块,然后提供数学(math.h)、字符串(string.h)等跨平台的部分,对和操作系统相关的部分如文件、线程等提供透传。
glibc的符号兼容性问题在一些意想不到的地方也会产生作用,例如Linux游戏(SteamOS / Proton)。但还有其他glibc兼容性问题,参考Win32 Is The Only Stable ABI on Linux。
希望musl、llvm-libc这些努力能够最终解决glibc的兼容性问题,不管是完全还是部分替代glibc。