链接器似乎忽略了库并默认为旧版本
Linker seems to ignore library and default to older version
我已经构建了一个共享库 (OpenSSL) 的调试版本,因此我可以使用调试器单步执行某个函数以更好地了解正在发生的事情。
但是,我在实际 link使用我构建的调试版本时遇到了困难。出于某种原因,无论我做什么,linker 总是以预安装的系统版本结束 linking,即使这两个版本都在 usr/lib
目录中,软-links 设置正确(AFAIK),编译时我在命令行上明确指定了调试库。
所以共享库的原始(系统安装)版本是:
>ls /usr/lib/x86_64-linux-gnu/ -lh | grep libssl
lrwxrwxrwx 1 root root 15 Sep 23 2016 libssl.so -> libssl.so.1.0.0
-rw-r--r-- 1 root root 386K Sep 23 2016 libssl.so.1.0.0
我从源代码编译并配置为共享库(对所有目标文件使用 fPIC 标志)的调试版本是:
>ls /usr/lib/ -lh | grep libssl
lrwxrwxrwx 1 root root 29 Oct 19 11:31 libssldebug.so -> /usr/lib/libssldebug.so.1.0.2
-rwxr-xr-x 1 root root 2.3M Oct 19 00:53 libssldebug.so.1.0.2
另外一个 OpenSSL 共享库 libcrypto 也是如此。我在 /usr/lib
.
中有一个 libcryptodebug.so.1.0.2 和相应的软 link
因此,我尝试针对调试共享库构建可执行文件和 link,如下所示:
>g++ test.cpp -o test -std=c++14 -lssldebug -lcryptodebug -I openssl-1.0.2p/include/
它编译并且link没有错误。
然而...当我使用 ldd
检查可执行文件时,我看到:
>ldd test
linux-vdso.so.1 (0x00007ffcaa39b000)
libssl.so.1.0.0 => /usr/lib/x86_64-linux-gnu/libssl.so.1.0.0 (0x00007ff717d37000)
libcrypto.so.1.0.0 => /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0 (0x00007ff71793b000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007ff717630000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007ff71732f000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007ff717119000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff716d6e000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007ff716b6a000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff717f98000)
所以即使我用 -lssldebug
显式地 linked,并且编译和 linked 没有错误,ldd
仍然显示 linker 由于某种原因 link 使用非调试版本 (/usr/lib/x86_64-linux-gnu/libssl.so.1.0.0
)。我也尝试了 运行 ldconfig
然后 recompiling/linking,但它仍然是 link 旧的(非调试)版本。
那么这是怎么回事?我做错了什么导致它默默地忽略命令行 linker 参数并以某种方式默认使用非调试版本(它有一个完全 不同的 共享库名字!)不知何故?
So even though I explicitly linked with -lssldebug, and it compiled and linked with no errors, ldd still shows that the linker for some reason is linking with the non-debug version (/usr/lib/x86_64-linux-gnu/libssl.so.1.0.0
您混淆了静态 linking 和运行时加载(有时也称为动态 linking)。
当你 link 和 g++ test.cpp ... -lssldebug ...
时,你 是 link 和 libssldebug.so
,但是(因为 ldd
输出告诉您)该库 未 在运行时加载期间使用。
发生这种情况是因为 libssldebug.so
有一个特殊的动态标签 DT_SONAME
,其中包含 "libssl.so.1.0.0"
,而静态 linker 记录 名称为要在运行时加载的库。
您可以通过以下方式确认:
readelf -d libssldebug.so | grep SONAME
您可以检查运行时加载程序将尝试为给定可执行文件定位的库:
readelf -d ./test | grep NEEDED
既然你明白了这个问题,你如何解决它?
两种方式之一:
- 您可以更改
libssldebug.so
中编码的 SONAME
,方法是用 -Wl,--soname=libssldebug.so.1.0.2
重新 link,然后重新 link 您的 test
程序。使用上面的 readelf
命令验证 SONAME
和 NEEDED
现在包含 "libssldebug.so.1.0.2"
,然后使用 ldd
验证 libssldebug.so.1.0.2
是运行时加载程序会用。
- 或者,将
libssldebug.so
安装到不同的目录(比如 /tmp/libssldebug
)可能更简单,在该目录中创建一个 symlink libssl.so.1.0.0 -> lissldebug.so
,然后要求运行时加载程序首先使用 g++ test.cpp ... -Wl,--rpath=/tmp/libssldebug
搜索此目录。使用此解决方案,可执行文件仍将搜索 libssl.so.1.0.0
,但它将在 /tmp/libssldebug
目录中搜索 首先 ,然后将找到您的副本。
我已经构建了一个共享库 (OpenSSL) 的调试版本,因此我可以使用调试器单步执行某个函数以更好地了解正在发生的事情。
但是,我在实际 link使用我构建的调试版本时遇到了困难。出于某种原因,无论我做什么,linker 总是以预安装的系统版本结束 linking,即使这两个版本都在 usr/lib
目录中,软-links 设置正确(AFAIK),编译时我在命令行上明确指定了调试库。
所以共享库的原始(系统安装)版本是:
>ls /usr/lib/x86_64-linux-gnu/ -lh | grep libssl
lrwxrwxrwx 1 root root 15 Sep 23 2016 libssl.so -> libssl.so.1.0.0
-rw-r--r-- 1 root root 386K Sep 23 2016 libssl.so.1.0.0
我从源代码编译并配置为共享库(对所有目标文件使用 fPIC 标志)的调试版本是:
>ls /usr/lib/ -lh | grep libssl
lrwxrwxrwx 1 root root 29 Oct 19 11:31 libssldebug.so -> /usr/lib/libssldebug.so.1.0.2
-rwxr-xr-x 1 root root 2.3M Oct 19 00:53 libssldebug.so.1.0.2
另外一个 OpenSSL 共享库 libcrypto 也是如此。我在 /usr/lib
.
因此,我尝试针对调试共享库构建可执行文件和 link,如下所示:
>g++ test.cpp -o test -std=c++14 -lssldebug -lcryptodebug -I openssl-1.0.2p/include/
它编译并且link没有错误。
然而...当我使用 ldd
检查可执行文件时,我看到:
>ldd test
linux-vdso.so.1 (0x00007ffcaa39b000)
libssl.so.1.0.0 => /usr/lib/x86_64-linux-gnu/libssl.so.1.0.0 (0x00007ff717d37000)
libcrypto.so.1.0.0 => /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0 (0x00007ff71793b000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007ff717630000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007ff71732f000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007ff717119000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff716d6e000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007ff716b6a000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff717f98000)
所以即使我用 -lssldebug
显式地 linked,并且编译和 linked 没有错误,ldd
仍然显示 linker 由于某种原因 link 使用非调试版本 (/usr/lib/x86_64-linux-gnu/libssl.so.1.0.0
)。我也尝试了 运行 ldconfig
然后 recompiling/linking,但它仍然是 link 旧的(非调试)版本。
那么这是怎么回事?我做错了什么导致它默默地忽略命令行 linker 参数并以某种方式默认使用非调试版本(它有一个完全 不同的 共享库名字!)不知何故?
So even though I explicitly linked with -lssldebug, and it compiled and linked with no errors, ldd still shows that the linker for some reason is linking with the non-debug version (/usr/lib/x86_64-linux-gnu/libssl.so.1.0.0
您混淆了静态 linking 和运行时加载(有时也称为动态 linking)。
当你 link 和 g++ test.cpp ... -lssldebug ...
时,你 是 link 和 libssldebug.so
,但是(因为 ldd
输出告诉您)该库 未 在运行时加载期间使用。
发生这种情况是因为 libssldebug.so
有一个特殊的动态标签 DT_SONAME
,其中包含 "libssl.so.1.0.0"
,而静态 linker 记录 名称为要在运行时加载的库。
您可以通过以下方式确认:
readelf -d libssldebug.so | grep SONAME
您可以检查运行时加载程序将尝试为给定可执行文件定位的库:
readelf -d ./test | grep NEEDED
既然你明白了这个问题,你如何解决它?
两种方式之一:
- 您可以更改
libssldebug.so
中编码的SONAME
,方法是用-Wl,--soname=libssldebug.so.1.0.2
重新 link,然后重新 link 您的test
程序。使用上面的readelf
命令验证SONAME
和NEEDED
现在包含"libssldebug.so.1.0.2"
,然后使用ldd
验证libssldebug.so.1.0.2
是运行时加载程序会用。 - 或者,将
libssldebug.so
安装到不同的目录(比如/tmp/libssldebug
)可能更简单,在该目录中创建一个 symlinklibssl.so.1.0.0 -> lissldebug.so
,然后要求运行时加载程序首先使用g++ test.cpp ... -Wl,--rpath=/tmp/libssldebug
搜索此目录。使用此解决方案,可执行文件仍将搜索libssl.so.1.0.0
,但它将在/tmp/libssldebug
目录中搜索 首先 ,然后将找到您的副本。