我一直在尝试升级旧 Linux 机器(Debian 7.5 wheezy)上的一些程序。我想保持默认系统库不变,并在其旁边添加一些自定义版本。它对大多数库都运行良好,只有 OpenSSL 让我很为难(libssl.so + libcrypt.so)。
这是我的 OpenSSL 配置行:
# ./config --prefix=/usr/local --openssldir=/usr/local/openssl no-gost shared zlib no-ssl2 -fPIC
然后对于其他程序,例如 cURL:
# ./configure LDFLAGS="-L/usr/local/ssl/lib -ldl" --with-ssl --enable-shared
但是我得到了错误的 OpenSSL 版本:
# curl --version
curl 7.44.0 (x86_64-unknown-linux-gnu) libcurl/7.44.0 OpenSSL/1.0.1e
我知道 libssl.so 在该系统的以下文件夹中有副本:
/usr/lib/libssl.so
/usr/lib/x86_64-linux-gnu/libssl.so
/usr/local/lib/libssl.so
/usr/local/ssl/lib/libssl.so
它们都是相同的自定义编译版本,除了/usr/lib/x86_64-linux-gnu/libssl.so这似乎是我的默认系统库(v1.0.5e)。
我想知道如何指示配置脚本以避免查看内部/lib/x86_64-linux-gnu?我可以只更改 /usr/lib/x86_64-linux-gnu/libssl.so 符号链接并将其指向我的构建吗?这样安全吗?
谢谢,
答案1
涉及两种不同的链接器操作,构建时间(用于查找库和“符号”,由ld
或等效执行),以及运行(由 执行ld.so
)将库加载到进程内存中并执行搬迁。
-L
在构建期间使用时,ld
仅确认库和所需符号(通常是组成 API 的函数)可用。在运行时,可以使用具有不同路径的库。LD_LIBRARY_PATH
,LD_PRELOAD
和路径(-R
或-rpath
)是控制这一点的一些方法——最后一种是在构建时指定这一点的方法(因此它是二进制文件的属性,并且不太依赖于用户环境)。
对于任何库,功能、API 和 ABI 都可能发生变化,如果链接器找到或加载“错误”的二进制文件,则会导致错误或不稳定。即使符号可以按预期匹配(否则您会在执行前收到错误),但在进程执行过程中,轮子可能会脱落。对此有解决方案,即对库文件名进行版本控制,以及对库中的符号进行版本控制,尽管这些在这里没有多大用处。(一个复杂的问题是,所有 OpenSSL 1.0.x 系列中的SHLIB_MAJOR
/都是“1.0.0”,因此除非您破解 .,否则无法轻易区分这些库。)SHLIB_MINOR
Makefile
Curl 本身会检查此类问题,它会将构建时libcurl
版本与运行时版本进行比较,并且可能会发出:
警告:curl 和 libcurl 版本不匹配。功能可能会受到影响。
当当代ld
注意到依赖项中可能存在冲突的库时,它会在构建时执行类似操作。
为一个干净的构建,无需任何运行时环境技巧,您可以:
- 构建 OpenSSL 时确保 RPATH 是明确的,这是因为
libssl.so
链接到libcrypto.so
、 和 安装到特定路径 - 使用正确的链接时间和运行时规范构建包(即 curl),以便它找到具体的
libssl.so
/libcrypto.so
修复此问题的一个廉价而又卑鄙的方法是chrpath
在构建后使用用户更改二进制文件中的 ELF RPATH,我一般不建议这样做,对于 curl 绝对不建议这样做,请继续阅读。
现在来看看复杂之处:curl 支持许多协议,其中一些协议是通过其他库提供的,并且当协议支持 TLS(或使用加密函数)时,它往往也会链接到libssl.so
and/or libcrypto.so
,例如 LDAP 和 SSH。你不要想要的是:
curl ↦ libcurl.so → libldap.so → libssl.so
↦ lib...
↦ libcrypto.so
↳ libssl.so
wherecurl
本身会使用一个版本,但libldap.so
可能期望加载不同的版本;如果存在 ABI 不兼容,这也可能在运行时失败。您可能在符号或加载顺序方面运气不佳,最终会加载两个(或更多!)版本的库。
通常有三种方法可以解决这个问题:
- 在构建时关闭所有不需要的功能,对于 curl 来说,至少 LDAP 和 SSH2 必须关闭
- 如果不可能的话,请构建新版本的依赖库,这些库也链接到新的 OpenSSL
- 如果这不可能,您可能能够使用
.a
库进行一些静态链接。对于 curl,这与仅仅使用 不同--enable-static
。它可以工作,我很久以前就这样做过,但它可能在这里不起作用,所以我不会进一步提及它;而且它可能会导致很多麻烦,尤其是使用 PIC/PIE 时。
GnuTLS 可能会让事情变得更加复杂,尽管同时使用这两个库不是问题(至少到目前为止我还没有看到过);但对于其他旨在实现 ABI 兼容性的库(如 LibreSSL)来说,情况并非如此。Curl 确实费了一番功夫才在其构建中只使用了一个 SSL 库(据我所知,它目前支持 8 个),但它不知道例如libssh2
可能链接到什么。
因此,根据版本,您可以尝试分别构建 OpenSSL 和 curl:
# OpenSSL
./config --prefix=/usr/local --openssldir=/usr/local/openssl no-gost \
shared zlib no-ssl2 -fPIC -Wl,-R,/usr/local/openssl
make depend && make && make install_sw
# curl
./configure --with-ssl=/usr/local/openssl --enable-shared \
--enable-ldap=no --without-libssh2 \
CFLAGS=-Wl,-R,/usr/local/openssl/lib
make && make install
(尽管文档表明相反情况,--with-ldap=no
并且--without-ldap
没有任何有用的效果,使用--enable-ldap=no
. libssh2
(对于 scp)通常默认不启用。)
安装后,ldd /usr/local/bin/curl
应该不会有任何意外:对预期libssl.so
和的每个的引用都恰好一个libcrypto.so
。
OpenSSL 构建(非 autoconf)将RPATH
在二进制文件中正确设置openssl
,但默认情况下不会在库中设置。库的定位方式因平台而异(OpenSSL 构建中的大部分 RPATH 处理都是诱饵:它适用于 Linux 以外的平台)。这对于 curl 来说可能不是必需的,但不会引起问题。
当您使用 autoconf 时,configure
无法保证非默认库位置会在二进制文件中产生匹配的 RPATH,CFLAGS
/LDFLAGS
通常可以解决这个问题。但是,在这里,使用 OpenSSL 的两步过程,config
无法很好地处理像 这样的变量LDFLAGS
,因此改为在行末添加config
,并Configure
在创建 时正确处理它Makefile
。
您可以使用以下命令检查 ELF 可执行文件或库运行时路径:
readelf --dynamic elfbinary | grep PATH
您可以使用以下方式调试库加载:
LD_DEBUG=files,libs /usr/local/bin/curl ...
(请注意,原始问题已经过时,OpenSSL 1.0.x 不再受官方支持,尽管发行版可能有所不同。此处的说明通常适用于为特定目的构建 OpenSSL 1.0.x 的特殊版本。构建stunnel
二进制文件以反向代理旧网络设备,以便它可以与当代浏览器一起使用就是一个很好的例子。)
您可以在我对这个问题的回答中找到进一步的解释和说明: 获取 ld 来选择正确的库。
答案2
我发现一个被标记为“不是最好的方法”但对我有用的解决方案是编辑 LD_LIBRARY_PATH 系统变量,例如在 <user>/.bashrc 中:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
答案3
我尝试按照你的建议去做,结果出现了分段错误!所以,不安全!
/usr/lib/i686/cmov$ sudo ln -s /usr/lib/libssl.so.1.0.0 libssl.so.0.9.8
/usr/lib/i686/cmov$ ls -la
total 1684
drwxr-xr-x 2 root root 4096 Jun 16 14:15 .
drwxr-xr-x 3 root root 4096 Oct 22 2012 ..
-rw-r--r-- 1 root root 1393308 Feb 11 2013 libcrypto.so.0.9.8
lrwxrwxrwx 1 root root 24 Jun 16 14:15 libssl.so.0.9.8 -> /usr/lib/libssl.so.1.0.0
-rw-r--r-- 1 root root 310296 Feb 11 2013 libssl.so.0.9.8.bk
:/usr/lib/i686/cmov$ sudo /etc/init.d/nginx restart
Restarting nginx: nginx/usr/sbin/nginx: /usr/lib/i686/cmov/libssl.so.0.9.8: no version information available (required by /usr/sbin/nginx)
Segmentation fault