我正在尝试编译一个程序prog
并将其链接到 OpenSSL 的 1.0.2 beta,从源代码构建并安装在/usr/local/ssl-1.0.2
.在使用 0.9.8 的旧系统上,这可以顺利完成。在安装了 1.0.1 的较新的系统上,这需要更多的工作。我想知道为什么。
1) 在 Ubuntu 10.04 上,使用 OpenSSL 0.9.8:
以下是我针对 1.0.2 进行编译和链接的步骤。
$ ./config shared --openssldir=/usr/local/ssl-1.0.2 && make && make install
$ ldconfig
$ ldconfig -p | grep libcrypto
=> 仅显示 0.9.8 文件,因此我添加了 1.0.2 文件的路径...
$ ldconfig /usr/local/ssl-1.0.2/lib
$ ldconfig -p | grep libcrypto
=>
libcrypto.so.1.0.0 (libc6) => /usr/local/ssl-1.0.2/lib/libcrypto.so.1.0.0
libcrypto.so.0.9.8 (libc6, hwcap: 0x0008000000008000) => /lib/i686/cmov/libcrypto.so.0.9.8
libcrypto.so.0.9.8 (libc6, hwcap: 0x0004000000000000) => /lib/i586/libcrypto.so.0.9.8
libcrypto.so.0.9.8 (libc6, hwcap: 0x0002000000000000) => /lib/i486/libcrypto.so.0.9.8
libcrypto.so.0.9.8 (libc6) => /lib/libcrypto.so.0.9.8
libcrypto.so.0.9.8 (libc6) => /usr/lib/libcrypto.so.0.9.8
libcrypto.so (libc6) => /usr/local/ssl-1.0.2/lib/libcrypto.so
这样我就可以编译prog
...
$ gcc -o prog ... -L/usr/local/ssl-1.0.2/lib -lcrypto
$ ldd prog
=>
libcrypto.so.1.0.0 => /usr/local/ssl-1.0.2/lib/libcrypto.so.1.0.0 (0x0083b000)
...并且它与 1.0.2 正确链接。
2) 在 Debian Wheezy 上,使用 OpenSSL 1.0.1:
同样的步骤,不同的结果。
$ ./config shared --openssldir=/usr/local/ssl-1.0.2 && make && make install
$ ldconfig
$ ldconfig -p | grep libcrypto
=>
libcrypto.so.1.0.0 (libc6, hwcap: 0x0008000000008000) => /usr/lib/i386-linux-gnu/i686/cmov/libcrypto.so.1.0.0
libcrypto.so.1.0.0 (libc6, hwcap: 0x0004000000000000) => /usr/lib/i386-linux-gnu/i586/libcrypto.so.1.0.0
libcrypto.so.1.0.0 (libc6) => /usr/lib/i386-linux-gnu/libcrypto.so.1.0.0
同样,我添加了 1.0.2 的路径...
$ ldconfig /usr/local/ssl-1.0.2/lib
$ ldconfig -p | grep libcrypto
=>
libcrypto.so.1.0.0 (libc6, hwcap: 0x0008000000008000) => /usr/lib/i386-linux-gnu/i686/cmov/libcrypto.so.1.0.0
libcrypto.so.1.0.0 (libc6, hwcap: 0x0004000000000000) => /usr/lib/i386-linux-gnu/i586/libcrypto.so.1.0.0
libcrypto.so.1.0.0 (libc6) => /usr/local/ssl-1.0.2/lib/libcrypto.so.1.0.0
libcrypto.so.1.0.0 (libc6) => /usr/lib/i386-linux-gnu/libcrypto.so.1.0.0
libcrypto.so (libc6) => /usr/local/ssl-1.0.2/lib/libcrypto.so
然后我尝试编译...
$ gcc -o prog ... -L/usr/local/ssl-1.0.2/lib -lcrypto
$ ldd prog
=>
libcrypto.so.1.0.0 => /usr/lib/i386-linux-gnu/i686/cmov/libcrypto.so.1.0.0 (0xb7591000)
但这里它与 1.0.2 没有链接。编译时库路径是正确的(用 , 指定-L
,gcc
否则会失败,因为 中使用的某些函数prog
特定于 1.0.2),但运行时库路径则不然。
3) 如何让它在 Wheezy 上运行
有或没有跑步ldconfig /usr/local/ssl-1.0.2/lib
:
$ gcc -o prog ... -Wl,--rpath=/usr/local/ssl-1.0.2/lib -L/usr/local/ssl-1.0.2/lib -lcrypto
$ ldd prog
=>
libcrypto.so.1.0.0 => /usr/local/ssl-1.0.2/lib/libcrypto.so.1.0.0 (0xb7592000)
或者,export LD_LIBRARY_PATH=/usr/local/ssl-1.0.2/lib
在运行之前运行gcc
。
我想知道什么
按照 mr.spuratic 的建议使用LD_DEBUG=libs ./prog
,我发现在 中查找了路径/etc/ld.so.cache
。我打开该文件,发现 .so 的查找顺序与 .so 的输出相对应ldconfig -p
。
所以实际的问题是:
- 为什么 1.0.2 文件在 1) 中位于 ldconfig 列表的顶部,但在 2) 中却没有?纯粹的随机性?由于 1.0.1 和 1.0.2 文件具有相同后缀而造成混淆? (“1.0.0”)
或者,换句话说,
- 为什么 3) 中添加的标志在 1) 中不需要?
答案1
针对非默认包进行编译/链接时需要注意三件事:
- 标头(通常
CFLAGS
) - 编译时库路径(通常
LDFLAGS
) - 运行时库路径(路径通过
LDFLAGS
,LD_RUN_PATH
,LD_LIBRARY_PATH
或ld.so.conf
)
你还没有说prog
是什么,所以我不知道它的配置可能有多好(或者如果它使用autoconf?),我见过很多只可靠地执行前两个步骤。
CFLAGS
在链接阶段,库路径顺序是相关的,假设您正在使用 GNU 工具链(gcc 和 binutils),您可以通过之前设置configure
(或直接设置Makefile
)来查看发生了什么:
export CFLAGS="-Wl,-t"
这会将跟踪选项传递给链接器。如果在 make 过程中只得到简洁的“CC”和“LD”行输出,则-t
可能需要在 make 命令中添加V=1
或。)VERBOSE=1
在运行时,您可以ld.so
通过仔细设置来查看尝试的内容LD_DEBUG
,例如
LD_DEBUG=libs ./myprog
files
(或尝试或的值symbols
以获取更多详细信息)
要在构建时正确指定所有三个参数,您应该能够执行以下操作:
export CFLAGS="-I/usr/local/ssl-1.0.2/include"
export LDFLAGS="-L/usr/local/ssl-1.0.2/lib -R/usr/local/ssl-1.0.2/lib"
然后重新配置/重新编译。
您使用的是--openssldir
而不是更传统的--prefix
(我推荐后者,并且make install_sw
如果您不需要默认安装为您提供的 1000 个左右的手册页和符号链接,也可以使用)。这可能是问题的一部分。由于某种原因,您显示的 .so 库已知没有ld.so
版本后缀(例如.so.1.0.2
),正确的“ make install
”应该为您设置它(通过link-shared
main 中的目标Makefile
)。
该-R
选项指示链接器在特定 OpenSSL 库的可执行输出中嵌入 RPATH,以便它不需要依赖运行时链接器 ( ld.so
) 通常提供的默认值。您可以使用以下命令修改现有的二进制文件chrpath
反而。
这或多或少相当于导出LD_LIBRARY_PATH=/usr/local/ssl-1.0.2/lib
。您可以在此处阅读有关 RPATH 和相关 RUNPATH 的更多信息:http://blog.tremily.us/posts/rpath/
作为最后的手段,您可以构建不带“共享”或“不共享”的 OpenSSL,这将为您提供不会出现此问题的静态库(但很可能会出现其他问题,例如在 ELF .so 中使用,导致 PIC /PIE问题)
根据更新的详细信息,我认为问题在于 1.0.1 和 1.0.2beta 都将 .so 版本后缀(SONAME)设置为 1.0.0。在第一个只有 0.9.8 的系统上,这不会造成任何问题;第二个版本为 1.0.1 和 1.0.2 的版本均为 1.0.0,根据顺序,这是“首场比赛获胜” ld.so.{conf,d}
。请记住,ld
编译时链接器是与运行时链接器不同的程序ld.so
,并且可以具有不同的行为(通常会导致符号错误或更糟,如您所见)。
$ cd /usr/local/src/openssl/openssl/1.0.2beta1
$ readelf -a libssl.so | grep SONAME
0x0000000e (SONAME) Library soname: [libssl.so.1.0.0]
$ cat verchk.c
int main(int argc, char *argv[]) {
printf("build: %s\n",OPENSSL_VERSION_TEXT);
printf("run : %s\n",SSLeay_version(SSLEAY_VERSION));
return 0;
}
$ gcc -Wall -I/usr/local/src/openssl/openssl-1.0.2-beta1/include \
-Wl,-rpath /usr/local/src/openssl/openssl-1.0.2-beta1/ \
-o verchk /usr/local/src/openssl/openssl-1.0.2-beta1/libcrypto.so verchk.c
$ ./verchk
build: OpenSSL 1.0.2-beta1 24 Feb 2014
run : OpenSSL 1.0.2-beta1 24 Feb 2014
$ grep SHLIB_M...R= Makefile
SHLIB_MAJOR=1
SHLIB_MINOR=0.0
更新
OpenSSL-1.1 进行了一些 API 级别更改,上述代码将无法使用 v1.1 标头和较旧的库进行编译 ( undefined reference to `OpenSSL_version'
)。
SSLeay_version()
现已弃用,并且(取决于OPENSSL_API_COMPAT
)可能会被#define
-d 为正确的 API 函数OpenSSL_version()
。
答案2
新库的路径是否位于其中的文件之一中/etc/ld.so.conf.d
?下一次运行:-
#ldconfig -v
重建缓存。如果你动作够快,你应该会在打印出来的长列表中看到新的库(或通过管道将其传输到grep
or less
)
也许该路径已经在第一台服务器中,但不在第二台服务器中?