在有些使用环境中,考虑到库的依赖版本可能与系统中的不同,又或者是编译比较麻烦,耗时,那么就需要使用静态编译。静态编译实际上是将用到的动态链接库提取出来,连接到可执行文件,因此,进行静态编译的关键就是构建静态链接的库。
这次我实验的是 QSS 写的 libqbpasswd。该项目被用于生成与 qBittorrent 4.2.x 兼容的密文,也就是说可以直接通过这个可执行文件生成配置文件中所需的密码字段。该项目在 std 标准库以外仅使用了 openssl 库用于密文的生成,因此有着较为简单的依赖关系。
首先是编译 openssl,这个步骤仅在静态编译时需要,如果是动态编译,那么 libssl-dev 或者 libssl-devel 即可提供所需的库文件和头文件。
这里我使用 openssl 1.1.1d:
Contents
安装 Tool-Chain
apt install -y build-essential pkg-config automake libtool git perl python3 python3-dev wget
初始化环境:
mkdir "$HOME/libqbpasswd" mkdir "$HOME/libqbpasswd/build" mkdir "$HOME/libqbpasswd/src" install_dir="$HOME/libqbpasswd/build" include_dir="$install_dir/include" lib_dir="$install_dir/lib" export PATH="$install_dir/bin:$HOME/bin${PATH:+:${PATH}}" export LD_LIBRARY_PATH="-L$lib_dir" export PKG_CONFIG_PATH="-L$lib_dir/pkgconfig"
下载并编译:
openssl_url="https://www.openssl.org/source/openssl-1.1.1d.tar.gz" openssl_src="$src_dir/openssl.tar.gz" wget -qO "$openssl_src" "$openssl_url" tar xf "$openssl_src" -C "$src_dir" cd "$src_dir/$(tar tf "$openssl_src" | head -1 | cut -f1 -d"/")" ./config --prefix="$install_dir" threads no-shared no-dso no-comp CXXFLAGS="-std=c++14 -ffunction-sections -fdata-sections" LDFLAGS="-Wl,--no-as-needed -ldl -lpthread -pthread" make -j$(nproc) make install_sw install_ssldirs
此时在 install_dir 内就会有静态编译的 openssl 库以及编译 libqbpassword 所需的头文件了。
下载 libqbpassword:
libqbpasswd_url="https://github.com/KozakaiAya/libqbpasswd.git" libqbpasswd_src="$src_dir/libqbpasswd" git clone https://github.com/KozakaiAya/libqbpasswd.git $libqbpasswd_src cd "$libqbpasswd_src"
修改 Makefile 并编译
编译时,还需要为 Makefile 加入刚刚编译的静态 openssl 库:
CPPFLAGS = -Wall -std=c++11 -O2 -I/root/libqbpasswd/build/include -I/usr/include LDFLAGS = -lssl -lcrypto STATIC_LDFLAGS = -L/root/libqbpasswd/build/lib -l:libcrypto.a -lssl -ldl -pthread
在 commit fd87aec68c0230af22c2a844cd8392383d1779fe 中,Makefile 定义的 static_build 确实链接了 crypto 和 ssl 库,但是由于没有链接 libc 库,因此这并不是真正意义上的 static-linked,在使用不同 libc 库版本的系统中,你会看到如下报错:
./qb_password_gen_static: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.25' not found (required by ./qb_password_gen_static)
因此我们需要继续加入 flag:”-static”,手动运行 g++ 进行link:
g++ -o qb_password_gen-a base64.o random.o password.o main.o -L /root/libqbpasswd/build/lib -lssl -lcrypto -ldl -static -lpthread
这样的确可以编译出二进制,而且显示为 static-linked 但运行时依然会有报错:
Segmentation fault
根据搜索,我发现,这是因为 libpthread.a 没有被完整链接进来,因此会出现 Segmentation fault(存储器区段错误)。最终,再次修改 Flag
g++ -o qb_password_gen base64.o random.o password.o main.o -L /root/libqbpasswd/build/lib -lssl -lcrypto -ldl -static -Wl,--whole-archive -lpthread -Wl,--no-whole-archive
这样,最终生成的可执行文件即 Static-Linked:
qb_password_gen_static: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=cdf3e6d75adee659667eda589ab91752c2be5935, not stripped
参考文档:
https://medium.com/@birnbera/static-vs-dynamic-libraries-5912efe9bf52
https://stackoverflow.com/questions/10368671/cannot-find-libcrypto-library-error