Pen*_*lec 3 compiling dynamic-linking openssl
我正在尝试编译一个程序prog
并将其链接到 OpenSSL 的 1.0.2 测试版,从源代码构建并安装在/usr/local/ssl-1.0.2
. 在使用 0.9.8 的旧系统上,这没有太多麻烦。在安装了 1.0.1 的较新系统上,这需要做更多的工作。我想知道为什么。
以下是我按照 1.0.2 编译和链接的步骤。
$ ./config shared --openssldir=/usr/local/ssl-1.0.2 && make && make install
$ ldconfig
$ ldconfig -p | grep libcrypto
Run Code Online (Sandbox Code Playgroud)
=> 只显示 0.9.8 文件,所以我添加了 1.0.2 文件的路径...
$ ldconfig /usr/local/ssl-1.0.2/lib
$ ldconfig -p | grep libcrypto
Run Code Online (Sandbox Code Playgroud)
=>
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
Run Code Online (Sandbox Code Playgroud)
所以我可以编译prog
...
$ gcc -o prog ... -L/usr/local/ssl-1.0.2/lib -lcrypto
$ ldd prog
Run Code Online (Sandbox Code Playgroud)
=>
libcrypto.so.1.0.0 => /usr/local/ssl-1.0.2/lib/libcrypto.so.1.0.0 (0x0083b000)
Run Code Online (Sandbox Code Playgroud)
...并且它与 1.0.2 正确链接。
同样的步骤,不同的结果。
$ ./config shared --openssldir=/usr/local/ssl-1.0.2 && make && make install
$ ldconfig
$ ldconfig -p | grep libcrypto
Run Code Online (Sandbox Code Playgroud)
=>
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
Run Code Online (Sandbox Code Playgroud)
同样,我将路径添加到 1.0.2 ...
$ ldconfig /usr/local/ssl-1.0.2/lib
$ ldconfig -p | grep libcrypto
Run Code Online (Sandbox Code Playgroud)
=>
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
Run Code Online (Sandbox Code Playgroud)
然后我尝试编译...
$ gcc -o prog ... -L/usr/local/ssl-1.0.2/lib -lcrypto
$ ldd prog
Run Code Online (Sandbox Code Playgroud)
=>
libcrypto.so.1.0.0 => /usr/lib/i386-linux-gnu/i686/cmov/libcrypto.so.1.0.0 (0xb7591000)
Run Code Online (Sandbox Code Playgroud)
但在这里它没有与 1.0.2 相关联。编译时库路径是正确的(用 指定-L
,gcc
否则会失败,因为 中使用的某些函数prog
特定于 1.0.2),但不是运行时的。
运行或不运行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
Run Code Online (Sandbox Code Playgroud)
=>
libcrypto.so.1.0.0 => /usr/local/ssl-1.0.2/lib/libcrypto.so.1.0.0 (0xb7592000)
Run Code Online (Sandbox Code Playgroud)
或者,在运行export LD_LIBRARY_PATH=/usr/local/ssl-1.0.2/lib
之前运行gcc
。
LD_DEBUG=libs ./prog
按照 Mr.spuratic 的建议使用,我发现在/etc/ld.so.cache
. 我打开那个文件,发现 .so 的查找顺序对应于 .so 的输出ldconfig -p
。
所以实际的问题是:
或者换一种说法,
在针对非默认包编译/链接时,您需要注意三件事:
CFLAGS
)LDFLAGS
)LDFLAGS
, LD_RUN_PATH
, LD_LIBRARY_PATH
or ld.so.conf
)你还没有说是什么prog
,所以我不知道它的配置有多好(或者它是否使用 autoconf?),我见过很多只可靠地执行前两个步骤。
在链接阶段,库路径顺序是相关的,假设您使用的是 GNU 工具链(gcc 和 binutils),您可能可以通过CFLAGS
之前设置configure
(或Makefile
直接设置)来查看发生了什么:
export CFLAGS="-Wl,-t"
Run Code Online (Sandbox Code Playgroud)
这会将-t
跟踪选项传递给链接器。如果您在 make 期间仅获得简洁的“CC”和“LD”行输出,则可能需要将V=1
或添加VERBOSE=1
到 make 命令。)
在运行时,您可以ld.so
通过仔细设置来查看尝试的内容LD_DEBUG
,例如
LD_DEBUG=libs ./myprog
Run Code Online (Sandbox Code Playgroud)
(或尝试的值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/
作为最后的手段,您可以在没有“shared”或“noshared”的情况下构建 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
Run Code Online (Sandbox Code Playgroud)
更新
OpenSSL-1.1 进行了一些 API 级别的更改,上面的代码将无法使用 v1.1 头文件和旧库 ( undefined reference to `OpenSSL_version'
)进行编译。
SSLeay_version()
现在已弃用,并且(取决于OPENSSL_API_COMPAT
)可能会被#define
-d 到适当的 API 函数OpenSSL_version()
。